Created
July 15, 2015 21:45
-
-
Save jhnklly/3b2f0e150c3316636c3a to your computer and use it in GitHub Desktop.
Point-in-Polygon + labels
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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"> </td><td class="detailVal"> </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"> </td><td class="detailVal"> </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"> </td><td class="detailVal"> </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"> </td><td class="detailVal"> </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