Choose the map colors based on a range from the data in the spreadsheet.
This map is generated from data in this Google Docs Spreasheet:
https://docs.google.com/spreadsheet/pub?key=0AhLWjwzZgPNGdEFib2pYeVg1VEU5U0w0MXV5Y254NlE&output=html
<p>
This map is generated from data in this Google Docs Spreasheet:<br />
<a href="https://docs.google.com/spreadsheet/pub?key=0AhLWjwzZgPNGdEFib2pYeVg1VEU5U0w0MXV5Y254NlE&output=html">https://docs.google.com/spreadsheet/pub?key=0AhLWjwzZgPNGdEFib2pYeVg1VEU5U0w0MXV5Y254NlE&output=html</a>
</p>
<!-- These are shims for IE6,7,8 and the like "lesser-browsers" -->
<script src="/choropleth/js/es5-shim.min.js"></script>
<script src="/choropleth/js/json2.js"></script>
<!-- used for javascript mustache templating -->
<script src="/choropleth/js/mustache.js"></script>
<!-- You must set the height of the div for the map, yes this should be in an external file -->
<div id="map_test_1" style="height: 400px; margin: 3em 0;"></div>
<script src="/choropleth/js/tabletop.js"></script>
<script type="text/javascript" src="/choropleth/js/us-states.js"></script>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.css" />
<!--[if lte IE 8]>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.5/leaflet.ie.css" />
<![endif]-->
<script type="text/javascript" src="http://cdn.leafletjs.com/leaflet-0.5/leaflet.js"></script>
<script type="text/javascript">
// Once all tests are complete this version is complete.
// [Done] include all required .js files
// [Done] use tabletop.js to load data table from google spreadsheet and turn geoJSON into real objects
// [Done] make array to associate machine headers to human headers from spreadsheet
// [Done] forEach spreadsheet-data-row display geoJSON object to map
// [Done] forEach spreadsheet-data-row update the map control layer
// [TODO] Add feedback to the control layer to show loading of map.
// This should probably change to a jQuery on.doc.load thing...
window.onload = function() { init() };
// Initialize the different constants that this uses to get information from a Google-Drive spreadsheet
var mapKey = "0AhLWjwzZgPNGdEFib2pYeVg1VEU5U0w0MXV5Y254NlE";
var sheetName = 'Hunger Data'
var geoJSONHeaderName = "ubergeojson";
var headersSheetName = "uberMap_meta-data";
var choroplethRangeHeaderName = "foodinsecurityrate";
var choroplethRangeHigh = 0;
var choroplethRangeLow = 0;
var choroplethRangeSteps = [];
// Initialize map specific variables
var defaultMapCenter = [41.05, -77.37]; // A good way to get this number is go to maps.google.com zoom to where you want to center the map - right click and "Drop LatLng marker" copy and paste that little set of numbers in here just the way it is.
var defaultZoomLevel = 7; // Self explanitory: Sets the zoom level from 0 to 18 usually of how close you want to be to the earth when the map loads. For Pennsylvania it's ~7
// Constants that are needed in multiple places in the app below.
var sheetHeaders = [];
var masterGeoJSON = {
"type": "FeatureCollection",
"features": []
};
var geojson;
var controlPanelTemplate = "{{={u{ }u}=}}";
// init() is where we actually go to the Google-Drive spreadsheet and load the data in.
function init() {
Tabletop.init( { key: mapKey,
callback: processSpreadsheetData,
wanted: [sheetName, headersSheetName]
});
}
// This is where we actually process all of the data from the Google-Drive spreadsheet
// and populate the masterGeoJSON object to get it ready for the map display function below.
function processSpreadsheetData(data, tabletop) {
var choroplethRangeCandidate = [];
// I leave these here to toggle the ability to check out these variables in the browser
// - so I can see what we're actually pulling from the Google-Drive spreadsheet.
// --- Before data processing variables --- //
//console.log(data);
//console.log(tabletop.sheets(headersSheetName));
//console.log(tabletop.sheets(sheetName));
//console.log(tabletop.sheets(sheetName).elements);
// ---
// this creates a sheetHeaders[] object that holds two different
// - properties: humanReadable and machineReadable
// - sheetHeaders[] is an array that we can step through using forEach (thank you ES5)
// - to display all of the map properties for each map object when we get to updating the map control layer.
tabletop.sheets(headersSheetName).elements.forEach( function(element, index) {
sheetHeaders[index] = {
machineReadable: tabletop.sheets(sheetName).column_names[index],
humanReadable: element.uberheaderlabel
};
});
// browser-view toggle for debugging:
console.log("Headers:");
console.log(sheetHeaders);
tabletop.sheets(sheetName).elements.forEach(function(element, rowIndex) {
masterGeoJSON.features[rowIndex] = JSON.parse(element[geoJSONHeaderName]).features[0];
tabletop.sheets(sheetName).column_names.forEach(function(headerName, headerIndex) {
if (headerName !== geoJSONHeaderName) {
masterGeoJSON.features[rowIndex].properties[headerName] = element[headerName];
} else {
// Do nothing
}
});
//console.log("County Name Check: " + masterGeoJSON.features[rowIndex].properties['countyname'] + " - " + masterGeoJSON.features[rowIndex].properties['name']);
choroplethRangeCandidate[rowIndex] = parseFloat(masterGeoJSON.features[rowIndex].properties[choroplethRangeHeaderName]);
});
choroplethRangeHigh = Math.max.apply(null, choroplethRangeCandidate);
choroplethRangeLow = Math.min.apply(null, choroplethRangeCandidate);
for (i=0;i<6;i++) {
choroplethRangeSteps[i] = parseFloat(choroplethRangeLow + (((choroplethRangeHigh - choroplethRangeLow) / 6) * i));
}
console.log("Max Choropleth Range: " + choroplethRangeHigh);
console.log("Min Choropleth Range: " + choroplethRangeLow);
console.log("Steps = " + choroplethRangeSteps);
// More browser-view toggles for debugging:
// --- After data processing variables --- //
//console.log(data);
console.log("masterGeoJSON:");
console.log(masterGeoJSON);
//console.log(statesData);
//Build the mustache template
controlPanelTemplate += "<ul>";
sheetHeaders.forEach( function(sheetHeader, sheetHeaderIndex){
if (sheetHeader.machineReadable !== geoJSONHeaderName) {
controlPanelTemplate += "<li>" +
'<span class="map-data-property label">' + sheetHeader.humanReadable + "</span>" +
": " +
'<span class="map-data-property value">' + "{u{" + sheetHeader.machineReadable + "}u}" + "</span>" +
"</li>";
}
});
controlPanelTemplate += "</ul>";
console.log(controlPanelTemplate);
// ---
// Now that we have actually loaded all of the data from the Google-Drive spreadsheet
// - go ahead and load masterGeoSJON up to the map.
loadMapData(masterGeoJSON);
}
// Setup the map to center where you would like it to. You can always to go maps.google.com and right click anywhere on the map and "Drop LatLng Marker".
var map = L.map('map_test_1').setView(defaultMapCenter, defaultZoomLevel);
// This is where you get your map tiles. You can get your own free API key from cloudmade.com
// - please replace my key with yours if you're using this code in your own project.
var cloudmade = L.tileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', {
attribution: 'Map data © 2011 OpenStreetMap contributors, Imagery © 2011 CloudMade',
key: 'c5007019bb4e4787afb0135c36690912',
styleId: 22677
}).addTo(map);
// This is where we decide which colors the polygons we draw on the map will be.
// - this needs to be re-written before version 1.0 to calculate the proper range
// - from the data provided automatically to provide a 5 to 10 color change range.
// [TODO] re-write getColor() to calculate the 5 to 10 color step automatically based on the data from the spreadsheet.
function getColor(d) {
return d > choroplethRangeSteps[5] ? '#BD0026' :
d > choroplethRangeSteps[4] ? '#F03B20' :
d > choroplethRangeSteps[3] ? '#FD8D3C' :
d > choroplethRangeSteps[2] ? '#FEB24C' :
d > choroplethRangeSteps[1] ? '#FED976' :
'#FFFFB2';
}
// [TODO] re-write this to get the styles from a stylesheet instead of hard-coding them here.
// ------ Maybe I won't actually do this since some of these don't really match a CSS standard.
// ------ Maybe the we will css standard the colors? Some of these things can be set as
// ------ variables at the top of this document... perhaps we'll just set it there due to the
// ------ styleing constraints set forth from our leaflet.js buddies
function style(feature) {
return {
fillColor: getColor(feature.properties.foodinsecurityrate),
weight: 1,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
};
}
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
};
// method that we will use to update the control based on feature properties passed
info.update = function (props) {
var grades = [0, 10, 20, 50, 100, 200, 500, 1000];
if (props) {
// This is called with when the mouse over an element.
this._div.innerHTML = Mustache.render(controlPanelTemplate, props);
} else {
// [TODO] Display Map-Legend when not hovering
}
};
info.addTo(map);
// This is what happens when your mouse hovers over a map element.
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
fillColor: '#78A700',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
// This is what happens when your mouse goes away from an element.
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
// This is where we assign the behavior to each map element we draw.
// [TODO] Add on click event
// - zoom in when first clicked - then update map control layer
// - zoom out when clicked again to pan back to the overall map view
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight
});
}
// This is the bit where we load all the geoJSON information into the map.
function loadMapData(geoJSONData) {
geojson = L.geoJson(geoJSONData, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
}
</script>
<style type="text/css">
.info {
padding: 6px 8px;
font: 14px/16px Arial, Helvetica, sans-serif;
background: white;
background: rgba(255,255,255,0.8);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
}
.info h4 {
margin: 0 0 5px;
color: #777;
}
.legend-i {
clear: both;
width: 18px;
height: 18px;
float: left;
margin-right: 8px;
opacity: 0.7;
}
.legend-label {
line-height: 18px;
}
</style>