Skip to content

Instantly share code, notes, and snippets.

@mapsense-examples
Last active August 29, 2015 14:24
Show Gist options
  • Save mapsense-examples/3b2430bc38cee41fb882 to your computer and use it in GitHub Desktop.
Save mapsense-examples/3b2430bc38cee41fb882 to your computer and use it in GitHub Desktop.
Point-in-polygon info
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://d3js.org/topojson.v1.min.js" charset="utf-8"></script>
<script src="https://developer.mapsense.co/mapsense.js" charset="utf-8"></script>
<link type="text/css" href="https://developer.mapsense.co/mapsense.css" rel="stylesheet"/>
<link type="text/css" href="simple_basemap.css" rel="stylesheet"/>
<style>
html, body, #myMap {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
font: 11px 'Droid Sans', sans-serif;
color: #666;
}
body {font-size: 12px; }
.ui, .mouseinfo {
font: 12px monospace !important;
color: black;
}
.top { position: absolute; top: 4px; }
.right { position: absolute; right: 4px; }
.float-left { float: left; }
.float-right { float: right; }
.col2 { width: 49%; display: inline-block;}
.bold { font-weight: bold; }
* { box-sizing: border-box }
circle {
vector-effect: non-scaling-stroke;
fill-opacity: 0.35;
}
.wrapper {
position: absolute;
top: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 99;
margin: 0;
clear: both;
}
.ui {
/*position: absolute;
*/
overflow: auto;
max-width: 280px;
max-height: 100%;
background: rgba(255,255,255,1);
pointer-events: all;
}
.outline {
outline: 1px solid #aaa;
}
.title {
/*position: absolute;
top: 0;
margin: 0 auto;
*/
text-align: left;
padding: 20px 2px 2px 20px;
background: rgba(255,255,255,0.75);
pointer-events: all;
}
.title-text {
width: calc(100% - 280px);
/*width: 280px;
margin: 0 auto;
*/
color: #3c4662;
max-width: 600px;
}
.title-text p {
text-align: left;
vertical-align: top;
}
.control {
padding: 10px;
font-size: 1.3em;
font-weight: bold;
}
.h1sim {
/*padding: 10px 20px;
*/
font-size: 1.9em;
font-weight: bold;
}
#coordinates { font-size: 1.5em; font-weight: bold; padding: 10px; padding-bottom: 0px; }
.two-col {
-moz-column-count: 2;
-moz-column-gap: 20px;
-webkit-column-count: 2;
-webkit-column-gap: 20px;
margin-bottom: 0;
}
.top { position: absolute; top: 0; }
.right { position: absolute; right: 0; }
.bottom { position: absolute; bottom: 0; }
.left { position: absolute; left: 0; }
#selector {
font: 20px 'Droid Sans', sans-serif;
}
.infos {
background: rgba(255,255,255,0.8);
overflow: auto;
display: inline-block;
max-height: 100%;
pointer-events: all;
overflow: auto;
}
.mouseinfo {
position: absolute;
bottom: 0;
left: 0;
pointer-events: none;
max-width: 300px;
/*font: 20px 'Droid Sans', sans-serif;*/
}
table {
border-collapse: collapse;
}
table, th, td {
border: 1px solid #eee;
padding: 2px;
padding-left: 10px;
padding-right: 10px
}
.detailKey {
background: #eee;
color: #666;
opacity: .8;
text-transform: uppercase;
font-size: 11px;
font-weight: 400;
}
.detailVal {
background: rgba(255,255,255,0.8);
text-align: left;
}
.point {
fill: rgba(255,0,0, 0.6);
stroke: rgba(255,0,0, 1);
/*stroke: rgba(68, 167, 228, 1); blue
*/
stroke-width: 2;
}
.clickselect {
pointer-events: all;
}
.mapsense-attribution a {
color: #999;
}
.mapsense-attribution {
color: #999;
background: rgba(255,255,255,0.5);
padding: 7px;
}
label {
pointer-events: all;
}
#labels_control {
pointer-events: all;
padding: 3px;
}
.land {
stroke: #a2d3f2;
stroke-width: 1;
}
</style>
</head>
<body>
<div id="myMap"></div>
<div class="wrapper">
<div id="results" class="ui top right">
<div id="coordinates"></div>
<div id="info-D" class="infos"></div>
<div id="info-C" class="infos"></div>
<div id="info-A" class="infos"></div>
<div id="info-B" class="infos"></div>
</div>
<div class="title">
<div class="title-text">
<div class="h1sim">Mapsense Context API</div>
<p class="">Click the map to get polygon information for <a target="_blank" href="https://developer.mapsense.co/tileViewer/?tileset=mapsense.earth">Mapsense Neighborhoods</a>, <a target="_blank" href="https://developer.mapsense.co/tileViewer/?tileset=mapsense.earth">Mapsense Density</a>, <a target="_blank" href="https://developer.mapsense.co/tileViewer/?tileset=mapsense.earth">Mapsense Earth</a>, and <a target="_blank" href="https://developer.mapsense.co/tileViewer/?tileset=mapsense.demographics">Mapsense Demographics</a>.You send the <a target="_blank" href="https://developer.mapsense.co/documentation/contextAPI">Mapsense Context API</a> a latitude & longitude, it returns information for all the polygons that contain the point. Works with any Mapsense dataset, or your own.</p>
</div>
</div>
<!--
<div class="ui top right">
<div class="control">
Mapsense.Demographics
</div>
</div>
-->
<div class="bottom left">
<label>
<input id="labels_control" type="checkbox"> Labels
</label>
</div>
</div>
<script>
var G = {}; // A global object to store variables
G.key = 'key-2d5eacd8b924489c8ed5e8418bd883bc';
G.simplify = '&ringSpan=10&lineSpan=10&s=10';
G.home = [ // we'll set the map extent to these bounds
{lon: -125, lat: -60},
{lon: 160, lat: 75}
];
//G.basemap = {'style': 'sketch', 'blayer': null };
G.basemap = {'style': 'simple', 'blayer': null };
G.params = {};
window.onload = function(){
initMap(); // initialize the map
}
// Add a div to display info mouseover info
var mouseinfo = d3.select('body')
.append("div")
.attr("class","mouseinfo");
function initMap() {
map = mapsense.map("#myMap"); // init the map
map.tileSize({x:256,y:256});
//map.extent(G.home);
if (G.basemap) {
G.basemap.blayer = mapsense.basemap().apiKey(G.key).style(G.basemap.style)
G.basemap.blayer.selection();
map.add(G.basemap.blayer);
}
// change map interaction so users can see the map update when they scroll through the selector fields
map.interact(false);
map.add(mapsense.drag());
map.add(mapsense.wheel());
map.add(mapsense.dblclick());
map.add(mapsense.touch());
map.add(mapsense.hash());
mapsense.compass().map(map); //enable shift zoom
d3.select('.compass').attr('style','display: none;') // but hide the compass graphic
G.pt_layer = mapsense.geoJson()
//.features(G.point)
.selection(function(d){
d.attr("class", "point")
.attr("r", "7")
;
})
.scale("fixed");
map.add(G.pt_layer);
if (query = window.location.search.substring(1)) {
readParams(query);
}
//window.select(map).addEventListener("mousedown", mousedowning, false);
d3.select(".mapsense-map").on("mousedown", function(){
mousedowning(d3.event);
});
G.labels_url = "http://stamen-tiles-{S}.a.ssl.fastly.net/toner-labels/{Z}/{X}/{Y}.png";
G.labels_layer = mapsense.image()
.url(mapsense.url(G.labels_url)
.hosts(["a", "b", "c", "d"]))
;
map.add(G.labels_layer.visible(false).id("labels_layer"));
d3.select('#labels_control').on('click',function(){ toggleLabels(); });
}
function toggleLabels() {
var checkit = d3.select('#labels_control').property('checked');
if (checkit) {
G.labels_layer.visible(true);
d3.select("#labels_layer").attr("style","opacity: 0.35;");
var credit = d3.select('.mapsense-attribution').html();
credit += ' <a target="_blank" href="http://stamen.com">©Stamen Design</a>';
d3.select('.mapsense-attribution').html(credit);
} else {
G.labels_layer.visible(false)
var credit = '<a target="_blank" href="https://developer.mapsense.co/tileViewer/?tileset=mapsense.earth">©Mapsense ©OpenStreetMap</a>';
d3.select('.mapsense-attribution').html(credit);
}
}
function urlToParams() {
var query = window.location.search.substring(1);
var vars = query.split("&");
vars.forEach(function(v) {
var p = v.split("=");
G.params[p[0]] = p[1]
})
getPIPs(G.params.lon, G.params.lat);
}
function paramsToUrl(lon,lat) {
var base_url = window.location.origin + window.location.pathname;
var param_obj = {
'lon': lon,
'lat': lat
};
var param_str = Object.keys(param_obj).map(function(k) {
return encodeURIComponent(k) + '=' + encodeURIComponent(param_obj[k])
}).join('&');
var url = base_url + '?' + param_str;
return url;
}
var temp;
function getPIPs(lon, lat) {
lon = parseFloat(lon),
lat = parseFloat(lat);
var osm_url = 'https://api.mapsense.co/universes/mapsense.osm_pip/intersects?geometry=POINT('+lon + '%20' + lat +')';
osm_url += '&version=1';
osm_url += '&api-key=key-32173abbaf4d488d9288161859596096';
osm_url += '&select=name,way_area,osm_id,boundary,admin_level,amenity,aeroway,leisure,wikipedia,building,natural,water,waterway,landuse,intermittent';
d3.json(osm_url)
.get(function(error,response){
//data = d3.select(data).filter(function(d, i) { return i & 1; });
var data = response.data;
var layers, landuse;
var layers_arr = [], landuse_arr =[];
var info_html;
d3.select('#coordinates').text(lat + ', ' + lon);
//d3.select('#link').html('<span class="clickselect" contenteditable spellcheck=false">' + link + '</span>');
var polys_arr = [];
if (data && data.layers && data.layers.polygons) {
var text = '<div class="control ">mapsense.osm_pip</div>';
var link = paramsToUrl(lon,lat);
text += '<div class="detailCard"><table><tbody>';
polygons = data.layers.polygons;
for (var luid in polygons) {
//layers_arr.push(layers_key);
var a_poly = polygons[luid];
var keyvals = [], wikilink, temp_str, temp_arr, osmlink;
for (var puid in a_poly) {
//text += '<tr><td class="detailKey">' + puid + '</td><td class="detailVal">' + a_poly[puid] + '</td></tr>';
if (puid == 'way_area') {
keyvals.push([puid,parseInt(a_poly[puid]/1000000),'normal']);
} else if (puid == 'wikipedia') {
temp_str = a_poly[puid];
temp_arr = temp_str.split(':');
wikilink = '<a target="_blank" href="https://' + temp_arr[0] + '.wikipedia.org/wiki/' + temp_arr[1] + '">' + a_poly[puid] + '</a>';
keyvals.push([puid, wikilink, 'normal']);
} else if (puid == 'osm_id') {
temp_str = a_poly[puid];
if (temp_str < 0) {
temp_str = Math.abs(temp_str);
osmlink = '<a target="_blank" href="https://www.openstreetmap.org/relation/' + temp_str + '/">' + temp_str + '</a>';
} else {
osmlink = '<a target="_blank" href="https://www.openstreetmap.org/way/' + temp_str + '/">' + temp_str + '</a>';
}
keyvals.push([puid, osmlink, 'normal']);
} else if (puid !== 'name') {
keyvals.push([puid,a_poly[puid],'normal']);
}
}
// Sort within by attr, but put name at top in bold
keyvals.sort();
keyvals.reverse();
if (a_poly.name) keyvals.push(['name',a_poly.name,'bold']);
temp = keyvals;
var way_size = a_poly.way_area/1000000;
var table_html = '';
for (var i = keyvals.length - 1; i >= 0; i--) {
table_html += '<tr><td class="detailKey">' + keyvals[i][0] + '</td><td class="detailVal '+ keyvals[i][2] +'">' + keyvals[i][1] + '</td></tr>';
};
table_html += '<tr><td class="detailKey">&nbsp;</td><td class="detailVal">&nbsp;</td></tr>';
//var table_html = keyvals.join();
polys_arr.push([way_size, table_html]);
}
} else {
var text = '<div class="control ">No Data</div>';
}
// Then sort all by way_area / 1000000 desc
polys_arr = polys_arr.sort(function(a,b) {
return a[0] - b[0];
});
for (var i = polys_arr.length - 1; i >= 0; i--) {
text += polys_arr[i][1];
};
// Now convert to text
d3.select('#info-B').html( text );
//d3.select('.clickselect').on('click', this.setSelectionRange(0, this.value.length));
d3.select('.clickselect').on('click', function(){
d3.event.stopPropagation();
var range, selection;
if (window.getSelection && document.createRange) {
selection = window.getSelection();
range = document.createRange();
range.selectNodeContents(this);
selection.removeAllRanges();
selection.addRange(range);
} else if (document.selection && document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(this);
range.select();
}
/* Click to copy not available in chrome
var copyEvent = new ClipboardEvent('copy', { dataType: 'text/plain', data: 'My string' } );
document.dispatchEvent(copyEvent);*/
});
})
/*
DEMOGRAPHICS
*/
var d3_percent = d3.format(",%");
var d3_comma = d3.format(",.0f");
var census_fields = {
age: { show: true, eg: 67.3, format: d3.format(".1f") },
area: { show: false, eg: 76742, format: d3.format("") },
asian: { show: true, eg: 0.38403755, format: d3_percent},
bachelors: { show: true, eg: 0.102649, format: d3_percent},
black: { show: true, eg: 0.03192488, format: d3_percent},
children: { show: true, eg: 0.03380282, format: d3_percent},
female: { show: true, eg: 0.42535213, format: d3_percent},
fips_code: { show: false, eg: "060750123011", format: d3.format("") },
food_stamps: { show: true, eg: 0.07073509, format: d3_percent},
hhi: { show: true, eg: 11733, format: d3_comma},
high_school: { show: true, eg: 0.4370861, format: d3_percent},
hispanic: { show: true, eg: 0.27511737, format: d3_percent},
households: { show: false, eg: 721, format: d3_percent},
male: { show: true, eg: 0.5746479, format: d3_percent},
minz: { show: false, eg: 11, format: ''},
name: { show: false, eg: "Block Group 1", format: d3.format("") },
no_high_school: { show: true, eg: 0.5629139, format: d3_percent},
population: { show: false, eg: 1065, format: d3.format("") },
poverty: { show: true, eg: 0.49014086, format: d3_percent},
school_enrollment: { show: true, eg: 0.11924883, format: d3_percent},
seniors: { show: true, eg: 0.5380282, format: d3_percent},
unemployment: { show: true, eg: 0.04950495, format: d3_percent},
uninsured: { show: true, eg: 0.2, format: d3_percent},
white: { show: true, eg: 0.33333334, format: d3_percent}
};
var select_arr = [];
for (var key in census_fields) {
if (census_fields[key].show) select_arr.push(key);
}
var census_url = 'https://api.mapsense.co/universes/mapsense.demographics/intersects?geometry=POINT('+lon + '%20' + lat +')';
//url += '&select=name,admin_level,leisure,ele';
//https://api.mapsense.co/universes/mapsense.earth/intersects?geometry=POINT('+lon + '%20' + lat +')';
census_url += '&api-key=key-32173abbaf4d488d9288161859596096';
census_url += '&where=layer=="census_block"';
census_url += '&select=' + select_arr.join(',');
//var url = 'https://api.mapsense.co/public/retrieve';
d3.json(census_url)
.get(function(error,response){
var data = response.data;
var layers, landuse;
var layers_arr = [], landuse_arr =[];
var info_html;
var polys_arr = [];
if (data && data.layers && Object.getOwnPropertyNames(data.layers).length > 0) {
var text = '<div class="control">mapsense.demographics</div>';
text += '<div class="detailCard"><table><tbody>';
polygons = data.layers;
for (var luid in polygons) {
var a_poly = polygons[luid];
var keyvals = []
for (var puid in a_poly) {
for (var attr in a_poly[puid]) {
var attr_val = a_poly[puid][attr];
var val_form = census_fields[attr].format(attr_val);
if (attr == 'area') keyvals.push([attr,parseInt(a_poly[puid][attr]/1000000),'normal']);
if (attr !== 'name' && attr !== 'area') keyvals.push([attr, val_form,'normal']);
}
}
// Sort within by attr, but put name at top in bold
keyvals.sort();
keyvals.reverse();
if (a_poly[puid].name) keyvals.push(['name',a_poly[puid].name,'bold']);
temp = keyvals;
var way_size = a_poly[puid].area/1000000;
var table_html = '';
for (var i = keyvals.length - 1; i >= 0; i--) {
table_html += '<tr><td class="detailKey">' + keyvals[i][0] + '</td><td class="detailVal '+ keyvals[i][2] +'">' + keyvals[i][1] + '</td></tr>';
};
polys_arr.push([way_size, table_html]);
}
}
// Then sort all by way_area / 1000000 desc
polys_arr = polys_arr.sort(function(a,b) {
return a[0] - b[0];
});
for (var i = polys_arr.length - 1; i >= 0; i--) {
text += polys_arr[i][1];
};
// Now convert to text
d3.select('#info-A').html(text);
})
var pop_url = 'https://api.mapsense.co/universes/mapsense.density/intersects?geometry=POINT('+lon + '%20' + lat +')';
pop_url += '&api-key=key-32173abbaf4d488d9288161859596096';
d3.json(pop_url)
.get(function(error,response){
var data = response.data;
var layers, landuse;
var layers_arr = [], landuse_arr =[];
var info_html;
var polys_arr = [];
var text = '<div class="control">mapsense.population</div>';
if (data && data.layers && data.layers._undefined) {
var pop_obj = data.layers._undefined;
/*
4405121: Object
area: 14.02716
density: 8201.604
minz: 7
*/
text += '<div class="detailCard"><table><tbody>';
for (var key in pop_obj) { // should only be one, but we don't know the key
var density = parseInt(pop_obj[key]['density']);
density = d3_comma(density);
text += '<tr><td class="detailKey">Population density</td><td class="detailVal">' + density + '</td></tr>';
}
text += '</table></tbody>';
}
// Now convert to text
d3.select('#info-C').html(text);
})
var hood_url = 'https://api.mapsense.co/universes/mapsense.neighborhoods/intersects?geometry=POINT('+lon + '%20' + lat +')';
hood_url += '&api-key=key-32173abbaf4d488d9288161859596096';
d3.json(hood_url)
.get(function(error,response){
var data = response.data;
var text = '<div class="control">mapsense.neighborhoods</div>';
if (data && data.layers && data.layers._undefined) {
var inner_obj = data.layers._undefined;
text += '<div class="detailCard"><table><tbody>';
for (var key in inner_obj) { // should only be one, but we don't know the key
var hood = inner_obj[key]['name'];
text += '<tr><td class="detailKey">Neighborhood</td><td class="detailVal">' + hood + '</td></tr>';
}
text += '</table></tbody>';
}
// Now convert to text
d3.select('#info-D').html(text);
})
}
G.mousex = 0;
G.mousey = 0;
G.mouses = 0;
function mousing(e) {
G.mouses +=
G.mousex = e.clientX;
G.mousey = e.clientY;
if (G.mouses > 10 ) {
G.mouses = 0;
}
}
function mousedowning(e) {
var lonlat = map.pointLocation(map.mouse(e));
var lon = lonlat.lon;
var lat = lonlat.lat;
lat = lat.toFixed(5);
// To work around multi-world lat-lon problem:
// Make them at +360
var features = [], point;
for (var i = -3; i < 4; i++) {
ilon = lon + 360*i;
point = markLatLon(lat, ilon); // a geojson features array
features.push(point);
};
//G.pt_layer.features(G.point);
G.pt_layer.features(features);
var mod;
if (lon > 360) {
lon = lon % 360;
//console.log('lon mod360',lon);
}
if (lon > 180) {
lon = lon % 180; // 190 -> 10
}
if (lon < -360) {
lon = lon % 360;
//console.log('lon mod360',lon);
}
if (lon < -180) { // -190 -> 170
mod = lon % 180; // 10
lon = 180 + mod;
}
//console.log(e.clientX, "FINAL", lon);
lon = lon.toFixed(5);
//mouseinfo.html( lon + ', ' + lat );
getPIPs(lon, lat);
paramsToUrl(lon, lat);
G.params.lat = lat;
G.params.lon = lon;
}
function markLatLon(lat,lon,name){
name = name || "";
var feature = {
type: "Feature",
geometry: {type: "Point", "coordinates": [ +lon, +lat ]},
};
return feature;
}
</script>
</body>
</html>
.mapsense-simple.labels {
font-size: 16;
fill: #4c83b2;
font-weight: 400;
text-transform: uppercase;
stroke-width: .3;
stroke: grey;
font-stretch: expanded;
letter-spacing: 1.5;
font-family: "Josefin Sans";
}
.mapsense-simple.tile-background {
fill: #CBE6F3;
}
.mapsense-simple.land {
/*fill: #fffaf2;*/
fill: #fcfcfc;
}
.mapsense-simple.water_polygon {
fill: #CBE6F3;
}
.mapsense-simple.country_border,
.mapsense-simple.disputed_border {
stroke: #aaa;
}
.mapsense-simple.state_border {
stroke: #aaa;
}
.mapsense-simple.water_line {
stroke: #CBE6F3;
}
.mapsense-simple.park {
fill: #c6f3bd;
stroke: none;
}
.mapsense-simple.building {
fill: #f9ece2;
stroke: none;
}
.mapsense-simple.school {
fill: #f0eced;
stroke: none;
}
.mapsense-simple.urban {
fill: rgba(243, 210, 191, 0.19);
stroke: none;
}
.mapsense-simple._0.roads,
.mapsense-simple._1.roads,
.mapsense-simple._2.roads,
.mapsense-simple._3.roads,
.mapsense-simple._4.roads,
.mapsense-simple._5.roads,
.mapsense-simple._6.roads,
{
stroke: none;
}
.mapsense-simple.ne_10m_roads {
stroke: #ddd;
}
.mapsense-simple.motorway {
stroke: #ddd;
}
.mapsense-simple.arterial_major {
stroke: #ddd;
}
.mapsense-simple.arterial_minor {
stroke: #cfdddb;
}
.mapsense-simple.road_med {
stroke: #dae9ea;
}
.mapsense-simple.road_minor {
stroke: #ededed;
}
.mapsense-simple.rail_major {
stroke: #c7c4c4;
}
.mapsense-simple.rail_minor {
stroke: #c7c4c4;
}
.mapsense-simple.runway {
stroke: #e1dede;
}
.mapsense-simple.path {
stroke: #e1dede;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment