Last active
August 29, 2015 14:06
-
-
Save dshook/5abcc10d330a2b6f6caf to your computer and use it in GitHub Desktop.
Leaflet map javascript
This file contains hidden or 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
var map = null; | |
var zipLayer = null; | |
var resultStats = { | |
min: 0, | |
max: 0, | |
mean: 0, | |
variance: 0, | |
deviation: 0 | |
}; | |
var colorFunc = null; //the current function based on the dropdown | |
var apiResults = null; //last results we received | |
//load in the map if not already loaded, center the map on a selected point | |
//and set up the events/layers | |
initMap = function () { | |
clearError(); | |
//get the center point of the map based on external parameters | |
$.ajax({ | |
url: '/api/GetMapCenter' | |
}).done(function (results) { | |
if (map === null) { | |
map = L.map('map'); | |
//add drag event that only happens every so often | |
var throttledDrag = _.throttle(dragEnd, 1500, { leading: false }); | |
map.on('dragend', throttledDrag); | |
map.on('zoomend', throttledDrag); | |
L.tileLayer( | |
//'http://{s}.tile.osm.org/{z}/{x}/{y}.png', //another b&w tile server that stopped working for a bit | |
'http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png', | |
{ | |
attribution: '© <a href="http://openstreetmap.org">OpenStreetMap</a> Contributors', | |
maxZoom: 18, | |
minZoom: 8 | |
} | |
).addTo(map); | |
} | |
map.setView([results.latitude, results.longitude], 11); | |
//add center marker | |
L.marker([results.latitude, results.longitude], { title: 'Tunnel' }) | |
.addTo(map); | |
fetchData(); | |
}).fail(function (jqXHR, textStatus) { | |
tunnelData.errorLoading(jqXHR, textStatus); | |
}); | |
} | |
var loading = false; | |
function showLoader() { | |
var html = '<h5>Loading</h5>' | |
+ '<div class="control-group span12 pagination-centered" style="font-weight:300; padding:1em 0 1em 0;">' | |
+ '<i class="icon-spinner icon-spin orange"></i> Loading Lots of Zip Data, please be patient...' | |
+ '</div>'; | |
$('#map-loader').html(html); | |
loading = true; | |
} | |
function killLoader() { | |
$('#map-loader').empty(); | |
loading = false; | |
} | |
//create the popup loaded with info on hover over a zip | |
function popup(feature, layer) { | |
if (feature.properties && feature.properties.zip) { | |
(function (layer, feature) { | |
var properties = feature.properties; | |
// Create a mouseover event | |
layer.on("mouseover", function (e) { | |
// Change the style to the highlighted version | |
layer.setStyle(zipHighlightStyle(feature)); | |
// Create a popup with a unique ID linked to this record | |
var popup = $("<div></div>", { | |
id: "popup-" + properties.zip, | |
class: 'map-popup', | |
css: { | |
position: "absolute", | |
bottom: "10px", | |
left: "10px", | |
zIndex: 1002, | |
backgroundColor: "white", | |
padding: "8px", | |
border: "1px solid #ccc" | |
} | |
}); | |
var content = '<div>'; | |
content += '<h5><b>' + properties.zip + '</b></h5>'; | |
content += '<p>Revenue ' + numeral(properties.rev).format('$0,0') + '</p>'; | |
content += '<p>Quantity ' + numeral(properties.qty).format('0') + '</p>'; | |
content += '<p>Population ' + numeral(properties.pop).format('0,0') + '</p>'; | |
content += '<p>' + numeral(properties.home_pct_own / 100).format('0.0%') + ' own, ' + | |
numeral(properties.home_pct_rent / 100).format('0.0%') + ' rent </p>'; | |
content += '<p>Median Age ' + properties.mAge + '</p>'; | |
content += '<p>Median Income ' + numeral(properties.mInc).format('$0,0') + '</p>'; | |
content += '<p>Color Function ' + numeral(colorFunc(properties)).format('0,0.00') + '</p>'; | |
content += '<p>Age Breakdown</p>'; | |
//histogram | |
content += '<div id="age-graph"><ul>'; | |
content += '<li>' + properties.age_pct_0_19 + ':0-19</li>'; | |
content += '<li>' + properties.age_pct_20_39 + ':20-39</li>'; | |
content += '<li>' + properties.age_pct_40_59 + ':40-59</li>'; | |
content += '<li>' + properties.age_pct_60_79 + ':60-79</li>'; | |
content += '<li>' + properties.age_pct_80_over + ':>=80</li>'; | |
content += '</ul></div>'; | |
content += '</div><div id="age-labels"></div>'; | |
var hed = $(content, { css: { fontSize: "16px", marginBottom: "3px" } }) | |
.appendTo(popup); | |
popup.appendTo("#map"); | |
makeGraph(); | |
}); | |
// Create a mouseout event that undoes the mouseover changes | |
layer.on("mouseout", function (e) { | |
layer.setStyle(zipStyle(feature)); | |
$(".map-popup").remove(); | |
}); | |
})(layer, feature); | |
} | |
} | |
function makeGraph() { | |
var container = document.getElementById("age-graph"); | |
var labels = document.getElementById("age-labels"); | |
var dnl = container.getElementsByTagName("li"); | |
for (var i = 0; i < dnl.length; i++) { | |
var item = dnl.item(i); | |
var innerHtml = item.innerHTML; | |
var content = innerHtml.split(":"); | |
var value = content[0]; | |
var label = content[1]; | |
item.style.height = value + "%"; | |
var leftOffset = (i * 40 + 20) + "px"; | |
labels.innerHTML = labels.innerHTML + | |
'<span style="position:absolute;top:-16px;left:' + leftOffset + '">' + label + '</span>'; | |
item.style.left = leftOffset; | |
item.innerHTML = value; | |
item.style.visibility = "visible"; | |
} | |
} | |
function dragEnd(distance) { | |
fetchData(); | |
} | |
var colorFunctions = { | |
rev: function (properties) { | |
return properties.rev; | |
}, | |
revPerPop: function (properties) { | |
return properties.pop == 0 ? | |
0 : | |
properties.rev / properties.pop; | |
}, | |
popPerRev: function (properties) { | |
return properties.rev == 0 ? | |
0 : | |
properties.pop / properties.rev; | |
}, | |
medInc: function (properties) { | |
return properties.mInc; | |
}, | |
medIncPerRev: function (properties) { | |
return properties.rev == 0 ? | |
0 : | |
properties.mInc / properties.rev; | |
}, | |
qty: function (properties) { | |
return properties.qty; | |
}, | |
totMoney: function (properties) { | |
return properties.mInc * properties.pop; | |
} | |
}; | |
//determine the right color for a feature given its properties | |
function zipStyle(feature) { | |
var colorArray = [ | |
'#5D16DA', | |
'#2F14D9', | |
'#1325D8', | |
'#1250D7', | |
'#117CD6', | |
'#0FA7D5', | |
'#0ED3D4', | |
'#0DD3A6', | |
'#0CD278', | |
'#0BD14A', | |
'#09D01C', | |
'#22CF08', | |
'#4ECE07', | |
'#7ACD06', | |
'#A6CC05', | |
'#CBC304', | |
'#CA9503', | |
'#C96702', | |
'#C83901', | |
'#C70A00', | |
]; | |
//color values that want it | |
var colorRange = []; | |
for (var i = 1; i <= colorArray.length; i++) { | |
colorRange.push(i); | |
} | |
var colorFunc = colorFunctions[$('#map-color').val()]; | |
var val = colorFunc(feature.properties); | |
var domainStart = Math.max(resultStats.mean - (2 * resultStats.deviation), 0); | |
var domainEnd = resultStats.mean + (2 * resultStats.deviation); | |
var bucketFunc = d3.scale.quantize().domain([domainStart, domainEnd]).range(colorRange); | |
//no color for you 0's | |
if (val == 0) { | |
return { | |
'color': '#888888', | |
'weight': 2, | |
'opacity': 0.8 | |
}; | |
} | |
var assignedBucket = bucketFunc(val); | |
return { | |
'color': colorArray[assignedBucket - 1], | |
'weight': 2, | |
'opacity': 0.8 | |
}; | |
} | |
function zipHighlightStyle(feature) { | |
var defStyle = zipStyle(feature); | |
defStyle.opacity = 1; | |
defStyle.weight = 5; | |
return defStyle; | |
} | |
//get the zip geoJSON data from the server | |
function fetchData() { | |
//don't load data with an outstanding request | |
if (loading) { | |
return; | |
} | |
clearError(); | |
showLoader(); | |
var mapCenter = map.getCenter(); | |
var mapBounds = map.getBounds(); | |
var params = { | |
latitude: mapCenter.lat, | |
longitude: mapCenter.lng, | |
NELat: mapBounds._northEast.lat, | |
NELng: mapBounds._northEast.lng, | |
SWLat: mapBounds._southWest.lat, | |
SWLng: mapBounds._southWest.lng, | |
extraFilterParametersYouShoulPass: null | |
}; | |
$.ajax({ | |
url: '/api/Map?' + $.param(params) | |
}).done(function (results) { | |
killLoader(); | |
apiResults = results; | |
displayResults(apiResults); | |
}).fail(function (jqXHR, textStatus) { | |
errorLoading(jqXHR, textStatus); | |
killLoader(); | |
}); | |
} | |
//update the map once we have geoJSON data | |
function displayResults(results) { | |
//remove last zips | |
if (zipLayer !== null) { | |
map.removeLayer(zipLayer); | |
} | |
colorFuncChange(); | |
calcMinMax(results.geoData.features); | |
zipLayer = L.geoJson(results.geoData, { | |
onEachFeature: popup, | |
style: zipStyle | |
}); | |
zipLayer.addTo(map); | |
} | |
//calculate statistics about the data given our coloring function | |
function calcMinMax(features) { | |
var colorFuncVals = _.map(features, function (feature) { | |
return colorFunc(feature.properties); | |
}); | |
var stats = calcStats(colorFuncVals); | |
stats.min = _.min(colorFuncVals); | |
stats.max = _.max(colorFuncVals); | |
resultStats = stats; | |
} | |
function calcStats(a) { | |
var r = { mean: 0, variance: 0, deviation: 0 }, t = a.length; | |
for (var m, s = 0, l = t; l--; s += a[l]); | |
for (m = r.mean = s / t, l = t, s = 0; l--; s += Math.pow(a[l] - m, 2)); | |
return r.deviation = Math.sqrt(r.variance = s / t), r; | |
} | |
function colorFuncChange() { | |
colorFunc = colorFunctions[$('#map-color').val()]; | |
} | |
$(function () { | |
$('#map-color').on('change', function (el) { | |
displayResults(apiResults); | |
}); | |
initMap(); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment