Skip to content

Instantly share code, notes, and snippets.

@jhnklly
Created June 18, 2015 23:02
Show Gist options
  • Save jhnklly/32588f7da3806f152c20 to your computer and use it in GitHub Desktop.
Save jhnklly/32588f7da3806f152c20 to your computer and use it in GitHub Desktop.
external data joins--test of concept
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 home = [ // we'll set the map extent to these bounds (nyc)
{lon: -74, lat: 40.68},
{lon: -73.7, lat: 40.83}
];
var home = [ // we'll set the map extent to these bounds (sf)
{lon: -122.52, lat: 37.70},
{lon: -122.35, lat: 37.81}
];
var G = {}; // A global object to store the field variables
G.field_stats = {};
G.FIELD = "94110";
G.fields_arr = [];
G.zips = {
'94110': {
'94104': 0.1,
'94124': 0.15,
'94121': 0.2,
'94122': 0.25,
'94116': 0.3,
},
'94104': {
'94110': 0.7,
'94124': 0.8,
'94121': 0.9,
'94122': 0.95,
'94116': 0.99,
},
'94116': {
'94110': 0.2,
'94124': 0.4,
'94121': 0.5,
'94122': 0.6,
'94104': 0.9,
},
'94121': {
'94110': 0.2,
'94124': 0.4,
'94104': 0.5,
'94122': 0.6,
'94116': 0.9,
},
'94124': {
'94104': 0.2,
'94110': 0.4,
'94121': 0.5,
'94122': 0.6,
'94116': 0.9,
}
};
G.zips_arr = [];
for (var key in G.zips) {
G.zips_arr.push(key);
}
var colorQuantile,
schema_url,
demographics_url,
demographics_layer,
selectedScheme = 'Blues',
numClasses = 9,
F_COMMA = d3.format("0,000");
// 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 += "&select=layer,fips_code,zip_code";
demographics_url += "&where=name!='Puerto Rico' AND layer=='zip_code'";
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 joinData() {
d3.selectAll('.ms')
.data(G.zips, function(d) {
console.log( d.properties.fips_code, G.zips[d.properties.fips_code] );
return G.zips[d.properties.fips_code];
});
}
function initMap() {
var param_selection_function = (function(whitelist) {
return function(s) {
applyColors(); // Update the colors based on the current global FIELD
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(home);
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) {
d3.selectAll('.ms')
.style("fill", function(d,i) {
colorQuantile = d3.scale.quantile()
.domain([0,1])
.range(colorbrewer[selectedScheme][numClasses])
;
var colr = '#bbb';
if (d.properties.fips_code) {
// the polygon's own zip:
//console.log(d);
var own_zip = d.properties.fips_code;
if (G.zips[own_zip] && own_zip !== G.FIELD) {
var value_from_selected_zip = G.zips[own_zip][G.FIELD];
console.log(G.FIELD, own_zip, value_from_selected_zip, colorQuantile(value_from_selected_zip));
colr = colorQuantile(value_from_selected_zip);
} else if (own_zip == G.FIELD) {
console.log(G.FIELD, own_zip, 'magenta');
colr = '#ff00ff';
}
return colr;
}
});
}
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...
.data(G.zips_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