Skip to content

Instantly share code, notes, and snippets.

@mapsense-examples
Last active August 29, 2015 14:22
Show Gist options
  • Save mapsense-examples/52732b29ec244f470777 to your computer and use it in GitHub Desktop.
Save mapsense-examples/52732b29ec244f470777 to your computer and use it in GitHub Desktop.
Interactive census map
html, body, #myMap {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
font: 14px 'Droid Sans', sans-serif;
color: #666;
}
.ms {
stroke-width: 1;
stroke: #ccc;
}
#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;
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"/>
</head>
<body>
<div id="myMap"></div>
<div id="ui">
<div id="control"></div>
<div id="legend">
<div id="color-chips"></div>
<div id="color-values"></div>
</div>
</div>
<script src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js' type="text/javascript"></script>
<script src='http://d3js.org/colorbrewer.v1.min.js' type="text/javascript"></script>
<script>
var map;
var the_key = "key-2d5eacd8b924489c8ed5e8418bd883bc";
var us50 = [ // we'll set the map extent to these bounds
{lon: -156, lat: 19.1},
{lon: -66.4, lat: 55.9}
];
var G = {}; // A global object to store the field variables
G.field_stats = {};
G.FIELD = "age";
G.fields_arr = [];
var colorQuantile,
schema_url,
demographics_url,
demographics_layer,
selectedScheme = 'Blues',
numClasses = 9,
F_COMMA = d3.format(",.0f");
// We use the schema_url to get the list of fields available and add them to the UI selector
schema_url = 'https://explore.mapsense.co/explore/api/schema?api-key=' + the_key;
schema_url += '&universe=mapsense.demographics';
demographics_url = "https://{S}-api.mapsense.co/universes/mapsense.demographics/{Z}/{X}/{Y}.topojson?api-key=" + the_key;
demographics_url += "&where=name!='Puerto Rico' AND layer=='state'";
demographics_url += "&ringSpan=10&lineSpan=10&s=10"; // params to simplify geoms (https://developer.mapsense.co/documentation/tileQueries)
// create a color ramp
colorQuantile = d3.scale.quantile()
.domain([0,1])
.range(colorbrewer[selectedScheme][numClasses])
;
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(whitelist) {
return function(s) {
// Update the colors and legend based on the max and min of the current global FIELD
// max & min expand to include values for new tiles (but don't contract)
updateAll();
s.attr("class", "ms"); // Assign a class (ms = mapsense)
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;
text += '<div class="detailCard"><table><tbody>';
if (whitelist.length > 0) { // if there's a whitelist, only show those fields
for (var i = 0; i < whitelist.length; i++) {
key = whitelist[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 whitelist, show all fields
for (var key in d.properties) {
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
// Finally, assign this function to the selection
// and request the values for name, population, and the currently selected field option
demographics_layer.selection(param_selection_function(['name',G.FIELD,'population']));
});
};
});
map = mapsense.map("#myMap"); // init the map
//map.zoom(3).center({lon: -99, lat: 38});
map.extent(us50);
demographics_layer = mapsense.topoJson()
.url(mapsense.url(demographics_url).hosts(['a', 'b', 'c', 'd']))
.clip(true)
.selection( param_selection_function(['name',G.FIELD,'population']) )
.on("load", applyColors)
;
map.add(demographics_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.attribution('<a target="_blank" href="https://developer.mapsense.co/tileViewer/?tileset=mapsense.demographics">Mapsense Demographics</a>'));
$("#selector").val(G.FIELD).change(); // trigger the selector
}
function updateAll() {
applyColors(); // for the map
drawColorChips(); // for the legend
updateChipValues(); // for the legend
}
function getStats(selxn) {
selxn.each(function(d,i){
if (d.properties && d.properties[G.FIELD]) {
var field_value = d.properties[G.FIELD];
if ( parseFloat(field_value) > G.field_stats[G.FIELD].max) {
G.field_stats[G.FIELD].max = +field_value;
}
if ( +field_value < G.field_stats[G.FIELD].min) {
G.field_stats[G.FIELD].min = +field_value;
}
}
});
}
function applyColors(e) {
if (G.FIELD && G.field_stats[G.FIELD]) {
d3.selectAll('.ms').call(getStats);
d3.selectAll('.ms')
.style("fill", function(d,i) {
colorQuantile = d3.scale.quantile()
.domain([G.field_stats[G.FIELD].min, G.field_stats[G.FIELD].max])
.range(colorbrewer[selectedScheme][numClasses])
;
return colorQuantile(d.properties[G.FIELD]);
});
}
}
function initSelect() {
d3.json(schema_url, function(json) {
var skip_fields = ['layer','area','name','id','minz'];
// the names of fields are embed in the requested json
// here we map them to a simpler array
G.fields_arr = json.schema.fields
.map(function(d){
// include field if its values are numeric
// otherwise will return undefined
if (d.type !== 'string') return d.name;
})
;
// only include field if it's truthy (removes undefined values)
G.fields_arr = G.fields_arr.filter(function (el) {
return el;
});
// if the field is in our list of skip_fields, then skip it
G.fields_arr = G.fields_arr.filter(function (el) {
return skip_fields.indexOf(el) == -1;
});
// add a select element to the page
d3.select('#control').append('select')
.attr('id','selector')
.selectAll('option') // there aren't any option elements yet, so we...
.data(G.fields_arr) // bind the list of fields...
.enter() // and when we enter the selection...
.append('option') // we append an option
.text(function(d){return d;}) // and add the field name (d being an element of fields_arr)
;
// Insert the fields_arr into our global object, so it can also store max & min...
// needed for scaling the colors for each unique domain of values.
for (var i = 0; i < G.fields_arr.length; i++) {
G.field_stats[G.fields_arr[i]] = {max: -Infinity, min: Infinity};
}
// When the user selects an option, set the FIELD variable
// and update the colors and legend
d3.select('#selector')
.on('change', function() {
G.FIELD = this.value;
updateAll();
});
});
}
function formatValue(value) {
// Formatting:
if (isNaN(value)) { ret = value; } // if not a number, no need to format
else if ( +value >= 1000 ) ret = F_COMMA(value); // Big numbers get commas
else if ( +value < 1000 ) ret = value.toFixed(2); // Small numbers get decimal places (but only 2)
return ret;
}
function drawColorChips() {
//var svg = "<svg width='24' height='270'>";
var svg = "<svg width='24' height='"+numClasses*25+"'>";
for ( var i = 0; i < numClasses; i++ ){
svg += "<rect fill="+colorbrewer[selectedScheme][numClasses][i]+" width='24' height='"+Math.min(24,parseInt(265/numClasses))+"' y='"+i*Math.min(24,parseInt(265/numClasses))+"'/>";
}
$("#color-chips").empty().append(svg);
updateChipValues();
}
function updateChipValues() {
$("#color-values").empty();
var str = "";
$("#color-chips rect").each(function(i){
chip_bounds = colorQuantile.invertExtent( colorbrewer[selectedScheme][numClasses][i] );
str += formatValue(chip_bounds[0]) + ' &ndash; ' + formatValue(chip_bounds[1]);
str += "\n";
});
str = str.replace( /\n$/, "" );
$("#color-values").append("<div class='textyarea' readonly style='line-height:"+Math.min(24,parseInt(265/numClasses))+"px; height:"+Math.min(24,parseInt(265/numClasses))*numClasses+"px'>"+str+"\n</div>");
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment