Skip to content

Instantly share code, notes, and snippets.

@jhnklly
Created July 15, 2015 21:45
Show Gist options
  • Save jhnklly/3b2f0e150c3316636c3a to your computer and use it in GitHub Desktop.
Save jhnklly/3b2f0e150c3316636c3a to your computer and use it in GitHub Desktop.
Point-in-Polygon + labels
<!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);
}
.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;
}
.title-text p {
text-align: left;
vertical-align: middle;
}
.control {
padding: 10px;
font-size: 1.5em;
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; }
.left { position: absolute; left: 0; }
.right { position: absolute; right: 0; }
#selector {
font: 20px 'Droid Sans', sans-serif;
}
#info-B, #info-A {
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;
}
</style>
</head>
<body>
<div id="myMap"></div>
<div class="wrapper">
<div id="results" class="ui top right">
<div id="coordinates"></div>
<div id="info-A">
</div>
<div id="info-B">
</div>
</div>
<div class="title">
<div class="title-text">
<div class="h1sim">Gimme Some Context!</div>
<p class="col2">Click the map to get polygon information for Mapsense Earth and Mapsense Demographics.</p>
<p class="col2">You send the <a target="_blank" href="http://developer.mapsense.co">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>
<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);
}
labels_url = "http://stamen-tiles-{S}.a.ssl.fastly.net/toner-labels/{Z}/{X}/{Y}.png";
labels_layer = mapsense.image()
.url(mapsense.url(labels_url)
.hosts(["a", "b", "c", "d"]))
;
map.add(labels_layer.visible(true).id("labels_layer"));
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);
//window.select(map).addEventListener("mousedown", mousedowning, false);
d3.select(".mapsense-map").on("mousedown", function(){
mousedowning(d3.event);
});
// 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", "0")
;
// Append to parent
console.log(d.node().parentNode);
if (d && d.node() && d.node().parentNode) {
/*var pin = document.createElement('path');
d.node().parentNode.insertBefore(pin, d.nextSibling);
console.log( d3.select(d.node().parentNode) );
*/
var tform = d.attr('transform');
var regex = /(.*)scale.*/;
tform = tform.replace(regex, "$1 scale(0.3) translate(-52,-92)");
console.log(tform);
d3.select(d.node().parentNode).append('path')
.attr('d','m 49.324,4.814 c -14.332,0 -25.954,11.622 -25.954,25.954 0,5.836 1.929,11.223 5.174,15.556 0.011,0.006 0.016,0.017 0.016,0.022 0.617,0.907 20.496,30.058 20.763,49.422 0.268,-19.364 20.146,-48.515 20.764,-49.422 0.011,-0.016 0.016,-0.022 0.016,-0.022 3.247,-4.333 5.174,-9.72 5.174,-15.556 0,-14.331 -11.62,-25.954 -25.953,-25.954 z')
//.attr('style','fill:#FFA6A6;stroke:#ff0000;stroke-width:6; ' + ' transform: ' + tform + '; webkit-transform: ' + tform + ';')
.attr('style','fill:#FFA6A6;stroke:#ff0000;stroke-width:2; stroke-antialiasing:true;')
.attr('class', 'pin' )
.attr('transform', tform )
.attr('shape-rendering', 'auto' )
;
}
})
.scale("fixed");
map.add(G.pt_layer);
if (query = window.location.search.substring(1)) {
readParams(query);
}
}
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 url = 'https://api.mapsense.co/universes/mapsense.osm_pip/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 +')';
url += '&api-key=key-32173abbaf4d488d9288161859596096';
url += '&select=name,way_area,osm_id,boundary,admin_level,leisure,wikipedia';
//var url = 'https://staging-api.mapsense.co/public/retrieve';
d3.json(url)
.get(function(error,data){
//data = d3.select(data).filter(function(d, i) { return i & 1; });
var layers, landuse;
var layers_arr = [], landuse_arr =[];
var info_html;
var text = '<div class="control ">Mapsense.Earth</div>';
var link = paramsToUrl(lon,lat);
text += '<div class="detailCard"><table><tbody>';
//text += '<tr><td class="detailKey">Link</td><td class="detailVal"><span class="clickselect" contenteditable spellcheck=false">' + link + '</span></td></tr>';
/* text += '<tr><td class="detailKey">Latitude</td><td class="detailVal">' + lat + '</td></tr>';
text += '<tr><td class="detailKey">Longitude</td><td class="detailVal">' + lon + '</td></tr>';
*/
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) {
polygons = data.layers.polygons;
for (var luid in polygons) {
//layers_arr.push(layers_key);
var a_poly = polygons[luid];
var keyvals = []
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']);
if (puid !== 'name' && puid !== 'way_area') 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 = '<tr><td class="detailKey">&nbsp;</td><td class="detailVal">&nbsp;</td></tr>';
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]);
}
}
// 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('#info-B').html( text );
//mouseinfo.html( JSON.stringify(data) );
//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 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 +')';
url += '&api-key=key-32173abbaf4d488d9288161859596096';
url += '&where=layer=="census_block"';
url += '&select=' + select_arr.join(',');
//var url = 'https://api.mapsense.co/public/retrieve';
d3.json(url)
.get(function(error,data){
//data = d3.select(data).filter(function(d, i) { return i & 1; });
var layers, landuse;
var layers_arr = [], landuse_arr =[];
var info_html;
var text = '<div class="control">Mapsense.Demographics</div>';
text += '<div class="detailCard"><table><tbody>';
var polys_arr = [];
if (data && data.layers ) {
polygons = data.layers;
for (var luid in polygons) {
//layers_arr.push(layers_key);
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 = '<tr><td class="detailKey">&nbsp;</td><td class="detailVal">&nbsp;</td></tr>';
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>';
};
//var table_html = keyvals.join();
//table_html += '<tr><td class="detailKey">&nbsp;</td><td class="detailVal">&nbsp;</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);
//d3.select('#results').classed('outline',true);
})
}
function mousing(e) {
var lonlat = map.pointLocation(map.mouse(e));
var lon = lonlat.lon;
if (lon > 180) lon = lon % 180; // 190 -> 10
if (lon < -180) { // -190 -> 170
mod = lon % 180; // 10
lon = 180 + mod;
}
lon = lon.toFixed(4);
var lat = lonlat.lat;
lat = lat.toFixed(4);
mouseinfo.html( lon + ', ' + lat );
}
function mousedowning(e) {
var lonlat = map.pointLocation(map.mouse(e));
var lon = lonlat.lon;
console.log(e.clientX, "rawlon", lon);
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);
var lat = lonlat.lat;
lat = lat.toFixed(5);
//mouseinfo.html( lon + ', ' + lat );
getPIPs(lon, lat);
paramsToUrl(lon, lat);
G.point = [markLatLon(lat, lon)]; // a geojson features array
G.pt_layer.features(G.point);
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment