Skip to content

Instantly share code, notes, and snippets.

@mapsense-examples
Last active August 29, 2015 14:23
Show Gist options
  • Save mapsense-examples/27e9f86aaf66519ec128 to your computer and use it in GitHub Desktop.
Save mapsense-examples/27e9f86aaf66519ec128 to your computer and use it in GitHub Desktop.
query OSM points 1.1

Here we compare the geospatial distribution between two terms in OpenStreetMap points of interest. Click "Next" to load a new example comparison. Or put any words you want in the input boxes to see where they occur in OSM.

html, body, #myMap {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
font: 11px 'Droid Sans', sans-serif;
color: #666;
}
* { box-sizing: border-box }
.ms {
stroke-width: 1;
stroke: #ccc;
stroke-opacity: 0.8;
fill-opacity: 0.5;
mix-blend-mode: multiply;
/*
*/
}
circle {
vector-effect: non-scaling-stroke;
}
.A {
stroke: cyan;
fill: cyan;
/*
stroke-width: 0;
*/
}
.B {
stroke: magenta;
fill: magenta;
/*
stroke-width: 0;
*/
}
a.button {
display: inline-block;
padding: 5px 10px;
border: 2px solid #777;
font: 16px bold 'Droid Sans', sans-serif;
line-height: 37px;
color: #777;
height: 50px;
min-width: 50px;
vertical-align: top;
margin-left: 4px;
text-align: center;
cursor: pointer;
}
a.button:hover {
border: 2px solid #000;
color: #000;
}
input {
border: 0s;
font: 16px 'Droid Sans', sans-serif;
}
input:focus {
outline-offset: 0px;
}
#refresh { margin-top: 5px;}
#selector_a {
outline: 13px solid rgba(0,255,255,0.6);
mix-blend-mode: multiply;
margin: 13px;
}
#selector_b {
outline: 13px solid rgba(255,0,255,0.6);
mix-blend-mode: multiply;
margin: 13px;
margin-left: 0px;
}
#ui {
position: absolute;
z-index: 99;
margin: 0;
top: 0;
left: 0;
clear: both;
}
#selector {
font: 20px 'Droid Sans', sans-serif;
}
#legend {
background: rgba(255,255,255,0.8);
overflow: auto;
display: inline-block;
}
#color-chips, #color-values {
float: left;
margin-left: 5px;
}
#color-values{
white-space: pre;
padding-right: 5px;
}
#color-values textarea {
overflow:hidden;
margin: 0;
border: none;
resize: none;
height: 100%;
}
.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 #ddd;
padding: 0 2px;
}
.detailKey {
background: #eee;
opacity: .8;
text-transform: uppercase;
font-weight: 600;
}
.detailVal {
background: rgba(255,255,255,0.8);
}
<!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="index.css" rel="stylesheet"/>
<style>
circle {
vector-effect: non-scaling-stroke;
/*We leave them unpainted until they match a regex later*/
stroke: none;
fill: none;
}
</style>
</head>
<body>
<div id="myMap"></div>
<div id="ui">
<div id="control"></div>
<div id="legend">
</div>
</div>
<script>
// TODO: read inputs from hash
mapsense.hash = function() { // overriding the hash function to include input variables
var hash = {},
s0, // cached location.hash
lat = 90 - 1e-8, // allowable latitude range
map;
var parser = function(map, s) {
var args = s.split("/").map(Number);
if (args.length < 3 || args.some(isNaN)) return true; // replace bogus hash
else {
var size = map.size();
map.zoomBy(args[0] - map.zoom(),
{x: size.x / 2, y: size.y / 2},
{lat: Math.min(lat, Math.max(-lat, args[1])), lon: args[2]});
}
};
var formatter = function(map) {
var center = map.center(),
zoom = map.zoom(),
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
return "#" + zoom.toFixed(2) +
"/" + center.lat.toFixed(precision) +
"/" + center.lon.toFixed(precision) +
"/" + d3.select('#selector_a').node().value +
"/" + d3.select('#selector_b').node().value
};
function move() {
var s1 = formatter(map);
if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
}
function hashchange() {
//if (location.hash === s0) return; // ignore spurious hashchange events
if (parser(map, (s0 = location.hash).substring(1)))
move(); // replace bogus hash
}
hash.map = function(x) {
if (!arguments.length) return map;
if (map) {
map.off("move", move);
window.removeEventListener("hashchange", hashchange, false);
}
if (map = x) {
map.on("move", move);
window.addEventListener("hashchange", hashchange, false);
d3.selectAll('.selector').on('change.hash',function() {
hashchange();
});
if (location.hash)
hashchange();
else
move();
}
return hash;
};
hash.parser = function(x) {
if (!arguments.length) return parser;
parser = x;
return hash;
};
hash.formatter = function(x) {
if (!arguments.length) return formatter;
formatter = x;
return hash;
};
return hash;
};
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 = 'sketch';
G.input_A = 'kitten';
G.input_B = 'puppy';
G.samples = [
['kitten','puppy'],
['bar','pub'],
['soda','pop'],
['cafe','caffe'],
['church','mosque'],
['ice cream','gelato'],
['Martin Luther King','Jefferson Davis'],
['bicycle','motorcycle'],
['east','west']
];
G.samples_i = 0;
G.layers = {
'base': { 'url': '', 'default': false},
'overlay_a': {
'url': 'https://{S}-api.mapsense.co/universes/mapsense.planet_osm_points/{Z}/{X}/{Y}.topojson?api-key=' + G.key,
'where': "&where=name=='"+G.input_A+"' OR name=='"+G.input_B+"'",
'params': '&density=200',
'class': '',
'default': true
},
'labels': { 'url': '', 'default': false}
};
G.selector_options = ['place_of_worship','school','bench','restaurant','fast_food','cafe','bicycle_parking','pub','bar','swimming_pool','university','college'];
G.selector_options.sort();
initSelect(); // initialize the selector UI
initMap(); // initialize the map
// Add a div to display info mouseover info
var mouseinfo = d3.select('body')
.append("div")
.attr("class","mouseinfo");
function initMap() {
var param_selection_function = (function(show_list) {
return function(s) {
s.attr("r", 3);
s.attr("class", function(f){
var classes = ['ms'];
classes.push(G.layers['overlay_a']['class']);
//var show_list = ['amenity','tourism','name'];
var show_list = ['name'];
for (var i = 0; i < show_list.length; i++) {
if (f.properties && f.properties[show_list[i]]) {
classes.push(f.properties[show_list[i]]);
/*if ( f.properties[show_list[i]] == d3.select('#selector_a').node().value ) {
classes.push('A');
} else if ( f.properties[show_list[i]] == d3.select('#selector_b').node().value ) {
classes.push('B');
}*/
var feature_value = f.properties[show_list[i]];
var A_value = d3.select('#selector_a').node().value;
var B_value = d3.select('#selector_b').node().value;
var A_regex = new RegExp(escapeRegExp(A_value), "gi");
var B_regex = new RegExp(escapeRegExp(B_value), "gi");
// at this point, the line above is the same as: var regex = /#abc#/g;
if ( A_regex.test(feature_value) ) {
classes.push('A');
} else if ( B_regex.test(feature_value) ) {
classes.push('B');
}
}
};
return classes.join(' ');
})
s.on("mouseover", function(d) { // Bind a function to mouseover
// This will build a table to display the field names and values
var text = "";
var value;
var no_list = ['DND_UUID'];
text += '<div class="detailCard"><table><tbody>';
if (show_list.length > 0) { // if there's a show_list, only show those fields
for (var i = 0; i < show_list.length; i++) {
key = show_list[i];
if (d.properties && d.properties[key]) {
value = d.properties[key];
value = formatValue(value);
text += '<tr><td class="detailKey">' + key + '</td><td class="detailVal">' + value + '</td></tr>';
}
}
} else { // if no show_list, show all fields
for (var key in d.properties) {
// except if it's in the no_list
if ( no_list.indexOf(key.toUpperCase()) == -1 ) {
text += '<tr><td class="detailKey">' + key + '</td><td class="detailVal">' + d.properties[key] + '</td></tr>';
}
}
}
mouseinfo.html(text); // Update the mouseinfo div with the dynamic info
G.layers['overlay_a'].ms_layer.selection(param_selection_function([]));
});
};
});
map = mapsense.map("#myMap"); // init the map
map.extent(G.home);
if (G.basemap) {
map.add(mapsense.basemap().apiKey(G.key).style(G.basemap));
}
/*
We want to use one overlay layer for both the queries
so we'll see their relative proportions.
We need to class them either A or B.
*/
G.layers['overlay_a'].ms_layer = mapsense.topoJson()
.url(mapsense.url(
G.layers['overlay_a'].url + G.layers['overlay_a'].where + G.layers['overlay_a'].params
)
.hosts(['a', 'b', 'c', 'd']))
.clip(false)
.scale('fixed')
.selection(
param_selection_function([])
);
map.add(G.layers['overlay_a'].ms_layer);
// 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
//$("#selector").val(G.FIELD).change(); // trigger the selector
}
function updateQuery() {
// update the query
//var new_where = "&where=amenity=='" + d3.select('#selector_a').node().value + "' OR amenity=='" + d3.select('#selector_b').node().value + "'";
var valu_A = d3.select('#selector_a').node().value;
var valu_B = d3.select('#selector_b').node().value;
var new_where = "&where=name=='" + valu_A + "' OR name=='" + valu_B + "'";
var new_url = G.layers['overlay_a'].url + new_where + G.layers['overlay_a'].params;
// update the layer
G.layers['overlay_a'].ms_layer.url(
mapsense.url(new_url)
.hosts(['a','b','c','d'])
);
}
function cycleQuery() {
// update the query
//var new_where = "&where=amenity=='" + d3.select('#selector_a').node().value + "' OR amenity=='" + d3.select('#selector_b').node().value + "'";
if (G.samples_i < G.samples.length-1) {
G.samples_i += 1;
} else {
G.samples_i = 0;
}
var valu_A = G.samples[G.samples_i][0];
var valu_B = G.samples[G.samples_i][1];
d3.select('#selector_a').node().value = valu_A;
d3.select('#selector_b').node().value = valu_B;
d3.select('#enter').on('click')();
/* var new_where = "&where=name=='" + valu_A + "' OR name=='" + valu_B + "'";
var new_url = G.layers['overlay_a'].url + new_where + G.layers['overlay_a'].params;
// update the layer
G.layers['overlay_a'].ms_layer.url(
mapsense.url(new_url)
.hosts(['a','b','c','d'])
);*/
}
function initSelect() {
// add a select element to the page
d3.select('#control').append('input')
.attr('id','selector_a')
.attr('class','selector A')
;
d3.select('#control').append('input')
.attr('id','selector_b')
.attr('class','selector B')
;
d3.select('#control').append('a')
.attr('id','enter')
.attr('class','button')
.text('Search')
;
d3.select('#control').append('br');
d3.select('#control').append('a')
.attr('id','refresh')
.attr('class','button')
.html('Next')
;
// When the user selects an option, update the class for those features
d3.selectAll('.selector').on('change.query', updateQuery);
d3.selectAll('#enter').on('click', updateQuery);
d3.selectAll('#refresh').on('click', cycleQuery);
d3.select('#selector_a').node().value = G.input_A;
d3.select('#selector_b').node().value = G.input_B;
}
function escapeRegExp(stringToGoIntoTheRegex) {
return stringToGoIntoTheRegex.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment