aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Baines <cbaines8@gmail.com>2013-02-07 00:11:36 +0000
committerChristopher Baines <cbaines8@gmail.com>2013-02-07 00:11:36 +0000
commit57d851b3bad13db30d233ac70a651a110fa04d3f (patch)
tree2b440d75172346a59a6bf3cd19bddc5ad8da5056
parentecebbf676e8be8945058e013b91a2c023b474b0b (diff)
downloadhealth-map-57d851b3bad13db30d233ac70a651a110fa04d3f.tar
health-map-57d851b3bad13db30d233ac70a651a110fa04d3f.tar.gz
Big post Hack Weekend refactor.
Updating the popups after the data on the settlements works, but only for the popup open at the time the data loads (if any). This needs more thinking about...
-rw-r--r--resources/data.js9
-rw-r--r--resources/map.js526
2 files changed, 275 insertions, 260 deletions
diff --git a/resources/data.js b/resources/data.js
index 85a77c2..e69de29 100644
--- a/resources/data.js
+++ b/resources/data.js
@@ -1,9 +0,0 @@
-function getVillages(area, zoom) {
- var poly = "12.3 -12.3 12.4 -12.3 12.48 -12.25 12.35 -12.19";
- var query = 'data=[out:json];(node(poly:"' + poly + '");<;);out;';
-
- converter = new op2geojson();
- converter.fetch("http://overpass-api.de/api/interpreter", query, zoom, function(data) {
-
- });
-}
diff --git a/resources/map.js b/resources/map.js
index 3369656..8159514 100644
--- a/resources/map.js
+++ b/resources/map.js
@@ -48,6 +48,78 @@ function getDataURIForRegion(self, region) {
return dataURI;
}
+function catchmentAreaProperties(catchmentArea, healthPost) {
+ var settlements = catchmentArea.settlements;
+
+ var format = new OpenLayers.Format.GeoJSON;
+ var openLayersGeo = format.parseGeometry(catchmentArea.geometry);
+
+ var sumOfDistances = 0;
+ var maxDistance = 0;
+
+ var Geographic = new OpenLayers.Projection("EPSG:4326");
+ var Mercator = new OpenLayers.Projection("EPSG:900913");
+
+ var healthPostPoint = format.parseGeometry(healthPost.geometry);
+ if (healthPost.geometry.type == "Point") {
+ //amenityPoint = new OpenLayers.Geometry.Point(amenity.geometry.coordinates[0], amenity.geometry.coordinates[1]).transform(Geographic, Mercator);
+ } else { // Its a polygon
+ healthPostPoint = healthPostPoint.getCentroid();
+ }
+
+ _.each(catchmentArea.settlements, function (settlement) {
+ var settlementGeo = format.parseGeometry(settlement.geometry);
+
+ var distance = settlementGeo.distanceTo(healthPostPoint);
+ sumOfDistances += distance;
+ if (distance > maxDistance)
+ maxDistance = distance;
+ });
+
+ var areaInSquareMeters = openLayersGeo.getGeodesicArea();
+ var areaString = areaInSquareMeters.toFixed(2) + "m" + "2".sup();
+ if (areaInSquareMeters > 1000000) {
+ areaString = (areaInSquareMeters / 1000000).toFixed(2) + "km" + "2".sup();
+ }
+
+ var areaProperties;
+ if (typeof settlements == "undefined") {
+ areaProperties = { area: areaString,
+ number_of_settlements: "Unknown",
+ population: "Unknown",
+ greatest_settlement_dist: "Unknown",
+ average_settlement_dist: "Unknown"
+ }
+ } else {
+ var population = 0;
+ var numberOfSettlementsWithoutPopulation = 0;
+
+ _.each(settlements, function(settlement) {
+ if (typeof settlement.properties.population != "undefined") {
+ population += parseInt(settlement.properties.population);
+ } else {
+ numberOfSettlementsWithoutPopulation++;
+ }});
+
+ if (numberOfSettlementsWithoutPopulation != 0) {
+ if (numberOfSettlementsWithoutPopulation == 1) {
+ population = population + " (but " + numberOfSettlementsWithoutPopulation + " settlement has no population set)";
+ } else {
+ population = population + " (but " + numberOfSettlementsWithoutPopulation + " settlements have no population set)";
+ }
+ }
+
+ areaProperties = { area: areaString,
+ number_of_settlements: settlements.length,
+ population: population,
+ greatest_settlement_dist: maxDistance,
+ average_settlement_dist: (sumOfDistances/catchmentArea.settlements.length)
+ }
+ }
+
+ return areaProperties;
+}
+
function initMap(self) {
self.tileLayer = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
@@ -60,87 +132,23 @@ function initMap(self) {
layers: [self.tileLayer],
}).setView([12.4822, -11.9463], 11);
- self.amenitiesShown = ["hospital", "doctors", "dentist"];
+ self.amenitiesShown = ["hospital"];
self.amenities = {};
self.amenityLayers = {}; // contains the layers for each amenity type
+ self.catchmentAreaSettlementsLayers = {}; // Settlement layers, addressed by catchmentArea.id
+ self.settlementsLayerGroup = L.layerGroup(); // A layer group for all the settlement layers, such that they can be treated as one layer in the control
self.catchmentAreas = {};
-
- // Create the settlement layer
- self.settlementLayer = L.geoJson({ type: "FeatureCollection", features: [] }, {
- style: function(feature) {
- return {fillColor: 'green',
- weight: 2,
- opacity: 1,
- color: 'black',
- dashArray: '3',
- fillOpacity: 0.1};
- },
- onEachFeature: function(feature, layer) {
- var center;
- if (feature.geometry.type === "Point") {
- center = feature.geometry.coordinates;
- } else {
- center = feature.geometry.coordinates[0];
- }
-
- var displayProperties = { name: feature.properties["name"], population: feature.properties["population"] };
- if (typeof displayProperties["name"] == "undefined")
- displayProperties["name"] = "Unknown";
- if (typeof displayProperties["population"] == "undefined")
- displayProperties["population"] = "Unknown";
-
- layer.bindPopup(self.settlementPopupTemplate({ properties: displayProperties, coordinate: center }));
- }
- });
- map.addLayer(self.settlementLayer);
+ self.healthPosts = {};
+ self.markers = {};
+ self.converter = new op2geojson();
// Layer controller
self.layersControl = L.control.layers().addTo(map);
- self.layersControl.addOverlay(self.settlementLayer, "Settlements");
-
- L.control.locate().addTo(map);
-
- // Legend
- var legend = L.control({position: 'bottomright'});
- legend.onAdd = function (map) {
- var div = L.DomUtil.create('div', 'info legend');
- div.innerHTML += '<img src="resources/img/hospital.png"> Hospital<br>';
- return div;
- };
- legend.addTo(map);
-
- return map;
-}
-
-function displayMap(self, map) {
-
- function createQueryData(bbox) {
- return "data=[out:json];(" +
- "(node[amenity=hospital]("+ bbox +");way[amenity=hospital]("+ bbox +");node(w););" +
- "(node[amenity=doctors]("+ bbox +");way[amenity=doctors]("+ bbox +");node(w););" +
- "(node[amenity=dentist]("+ bbox +");way[amenity=dentist]("+ bbox +");node(w););" +
- "(node(" + bbox + ");relation[type=boundary][boundary=catchment_area];way(r);node(w););" +
- ");out;";
- }
- function highlightFeature(e) {
- var layer = e.target;
+ map.addLayer(self.settlementsLayerGroup);
+ self.layersControl.addOverlay(self.settlementsLayerGroup, "Settlements");
- layer.setStyle({
- weight: 5,
- color: '#666',
- dashArray: '',
- fillOpacity: 0.1
- });
-
- if (!L.Browser.ie && !L.Browser.opera) {
- layer.bringToFront();
- }
- }
-
- function resetHighlight(e) {
- self.catchmentAreaLayer.resetStyle(e.target);
- }
+ var emptyFeatureCollection = { type: "FeatureCollection", features: [] };
var hospitalIcon = L.icon({
iconUrl: 'resources/img/hospital.png',
@@ -150,47 +158,48 @@ function displayMap(self, map) {
popupAnchor: [2, -9] // point to open the popup from relative to iconAnchor
});
- var zoom = map.getZoom();
- if (zoom < 10) {
- return;
- }
-
- // Make the bounding box string
- var bounds = map.getBounds();
- var sw = bounds.getSouthWest();
- var ne = bounds.getNorthEast();
- bbox = [sw.lat, sw.lng, ne.lat, ne.lng].join(',');
-
- self.converter = new op2geojson();
- var markers = {};
-
- function createCatchmentAreaLayer(data) {
- return L.geoJson(data, {
- style: function(feature) {
- return {fillColor: 'green',
- weight: 2,
- opacity: 1,
- color: 'black',
- dashArray: '3',
- fillOpacity: 0.1};
- },
- onEachFeature: function(feature, layer) {
- layer.on({
- mouseover: highlightFeature,
- mouseout: resetHighlight,
- click: function() {
- markers[feature.properties.subject].openPopup();
+ self.catchmentAreaLayer = L.geoJson(emptyFeatureCollection, {
+ style: function(feature) {
+ return {fillColor: 'green',
+ weight: 2,
+ opacity: 1,
+ color: 'black',
+ dashArray: '3',
+ fillOpacity: 0.1};
+ },
+ onEachFeature: function(feature, layer) {
+ layer.on({
+ mouseover: function(e) {
+ var layer = e.target;
+
+ layer.setStyle({
+ weight: 5,
+ color: '#666',
+ dashArray: '',
+ fillOpacity: 0.1
+ });
+
+ if (!L.Browser.ie && !L.Browser.opera) {
+ layer.bringToFront();
}
- });
- },
- filter: function(feature, layer) {
- return _.contains(_.values(feature.properties), "catchment_area");
- }
- });
- }
+ },
+ mouseout: function(e) {
+ self.catchmentAreaLayer.resetStyle(e.target);
+ },
+ click: function() {
+ self.markers[feature.properties.subject].openPopup();
+ }
+ });
+ },
+ filter: function(feature, layer) {
+ return _.contains(_.values(feature.properties), "catchment_area");
+ }
+ });
+ map.addLayer(self.catchmentAreaLayer);
+ self.layersControl.addOverlay(self.catchmentAreaLayer, "Catchment Areas");
- function createAmenityLayer(data, amenity) {
- return L.geoJson(data, {
+ function createAmenityLayer(amenity) {
+ return L.geoJson(emptyFeatureCollection, {
style: function(feature) {
return {color: 'red',
fillColor: 'red',
@@ -209,90 +218,69 @@ function displayMap(self, map) {
}
var catchmentArea = self.catchmentAreas[feature.id];
- var settlements = catchmentArea.settlements;
-
- var format = new OpenLayers.Format.GeoJSON;
- var openLayersGeo = format.parseGeometry(catchmentArea.geometry);
- var sumOfDistances = 0;
- var maxDistance = 0;
+ layer.bindPopup(self.editorTemplate({coordinate: center}) +
+ self.healthPostTemplate(feature.properties) +
+ '<div id="' + catchmentArea.id + '">' +
+ self.catchmentAreaTemplate(catchmentAreaProperties(catchmentArea, feature)) +
+ '</div>');
- var Geographic = new OpenLayers.Projection("EPSG:4326");
- var Mercator = new OpenLayers.Projection("EPSG:900913");
+ self.markers[feature.id] = layer;
+ },
+ filter: function(feature, layer) {
+ // TODO: Fix and make more efficient
+ return _.contains(_.values(feature.properties), amenity);
+ }
+ });
+ }
- var featurePoint = format.parseGeometry(feature.geometry);
- if (feature.geometry.type == "Point") {
- //amenityPoint = new OpenLayers.Geometry.Point(amenity.geometry.coordinates[0], amenity.geometry.coordinates[1]).transform(Geographic, Mercator);
- } else { // Its a polygon
- featurePoint = featurePoint.getCentroid();
- }
+ _.each(self.amenitiesShown, function(amenity, i) {
+ self.amenityLayers[amenity] = createAmenityLayer(amenity);
- _.each(catchmentArea.settlements, function (settlement) {
- var settlementGeo = format.parseGeometry(settlement.geometry);
+ map.addLayer(self.amenityLayers[amenity]);
+ self.layersControl.addOverlay(self.amenityLayers[amenity],
+ self.amenitiesShown[i].charAt(0).toUpperCase() + self.amenitiesShown[i].slice(1));
+ });
- var distance = settlementGeo.distanceTo(featurePoint);
- sumOfDistances += distance;
- if (distance > maxDistance)
- maxDistance = distance;
- });
+ L.control.locate().addTo(map);
- var areaInSquareMeters = openLayersGeo.getGeodesicArea();
- var areaString = areaInSquareMeters.toFixed(2) + "m" + "2".sup();
- if (areaInSquareMeters > 1000000) {
- areaString = (areaInSquareMeters / 1000000).toFixed(2) + "km" + "2".sup();
- }
+ // Legend
+ var legend = L.control({position: 'bottomright'});
+ legend.onAdd = function (map) {
+ var div = L.DomUtil.create('div', 'info legend');
+ div.innerHTML += '<img src="resources/img/hospital.png"> Hospital<br>';
+ return div;
+ };
+ legend.addTo(map);
- var areaProperties;
- if (typeof settlements == "undefined") {
- areaProperties = { area: areaString,
- number_of_settlements: "Unknown",
- population: "Unknown",
- greatest_settlement_dist: "Unknown",
- average_settlement_dist: "Unknown"
- }
- } else {
- var population = 0;
- var numberOfSettlementsWithoutPopulation = 0;
+ return map;
+}
- _.each(settlements, function(settlement) {
- if (typeof settlement.properties.population != "undefined") {
- population += parseInt(settlement.properties.population);
- } else {
- numberOfSettlementsWithoutPopulation++;
- }});
+function displayMap(self, map) {
- if (numberOfSettlementsWithoutPopulation != 0) {
- if (numberOfSettlementsWithoutPopulation == 1) {
- population = population + " (but " + numberOfSettlementsWithoutPopulation + " settlement has no population set)";
- } else {
- population = population + " (but " + numberOfSettlementsWithoutPopulation + " settlements have no population set)";
- }
- }
+ function createQueryData(bbox) {
+ return "data=[out:json];(" +
+ "(node[amenity=hospital]("+ bbox +");way[amenity=hospital]("+ bbox +");node(w););" +
+ //"(node[amenity=doctors]("+ bbox +");way[amenity=doctors]("+ bbox +");node(w););" +
+ //"(node[amenity=dentist]("+ bbox +");way[amenity=dentist]("+ bbox +");node(w););" +
+ "(node(" + bbox + ");relation[type=boundary][boundary=catchment_area];way(r);node(w););" +
+ ");out;";
+ }
- areaProperties = { area: areaString,
- number_of_settlements: settlements.length,
- population: population,
- greatest_settlement_dist: maxDistance,
- average_settlement_dist: (sumOfDistances/catchmentArea.settlements.length)
- }
- }
+ var zoom = map.getZoom();
+ if (zoom < 10) {
+ return;
+ }
- layer.bindPopup(self.popupTemplate({ properties: $.extend(feature.properties, areaProperties), coordinate: center }));
+ // Make the bounding box string
+ var bounds = map.getBounds();
+ var sw = bounds.getSouthWest();
+ var ne = bounds.getNorthEast();
+ bbox = [sw.lat, sw.lng, ne.lat, ne.lng].join(',');
- markers[feature.id] = layer;
- },
- filter: function(feature, layer) {
- // TODO: Fix and make more efficient
- return _.contains(_.values(feature.properties), amenity);
- }
- });
- }
+ self.converter = new op2geojson();
function addSettlementsForArea(catchmentArea) {
- if (typeof catchmentArea.settlements != 'undefined') {
- return;
- }
-
// Create the bounding polygon for the query
var poly = "";
_.each(catchmentArea.geometry.coordinates[0], function(coordinatePair) {
@@ -302,73 +290,111 @@ function displayMap(self, map) {
var query = 'data=[out:json];(node(poly:"' + poly + '");<;node(w););out;';
- // Fetch settlement data
- self.converter.fetch("http://overpass-api.de/api/interpreter", query, zoom, function(data) {
+ function processSettlements(data, catchmentArea) {
data.features = _.filter(data.features, function(feature) {
return _.contains(_.keys(feature.properties), "place") ||
feature.properties["landuse"] == "residential";
});
catchmentArea.settlements = data.features;
- self.settlementLayer.addData(data);
- });
+
+ if (catchmentArea.id in self.catchmentAreaSettlementsLayers) {
+ self.catchmentAreaSettlementsLayers[catchmentArea.id].clearLayers();
+ self.catchmentAreaSettlementsLayers[catchmentArea.id].addData(data);
+ } else {
+ self.catchmentAreaSettlementsLayers[catchmentArea.id] = L.geoJson(data, {
+ style: function(feature) {
+ return {fillColor: 'green',
+ weight: 2,
+ opacity: 1,
+ color: 'black',
+ dashArray: '3',
+ fillOpacity: 0.1};
+ },
+ onEachFeature: function(feature, layer) {
+ var center;
+ if (feature.geometry.type === "Point") {
+ center = feature.geometry.coordinates;
+ } else {
+ center = feature.geometry.coordinates[0];
+ }
+
+ var displayProperties = { name: feature.properties["name"], population: feature.properties["population"] };
+ if (typeof displayProperties["name"] == "undefined")
+ displayProperties["name"] = "Unknown";
+ if (typeof displayProperties["population"] == "undefined")
+ displayProperties["population"] = "Unknown";
+
+ layer.bindPopup(self.editorTemplate({coordinate: center}) + self.settlementTemplate(displayProperties));
+ }
+ });
+ self.settlementsLayerGroup.addLayer(self.catchmentAreaSettlementsLayers[catchmentArea.id]);
+
+ $('#' + catchmentArea.id).html(self.catchmentAreaTemplate(catchmentAreaProperties(catchmentArea, self.healthPosts[catchmentArea.properties["subject"]])));
+ }
+ }
+
+ // Fetch settlement data
+ self.converter.fetch("http://overpass-api.de/api/interpreter", query, zoom,
+ (function(catchmentArea) { return function (data) { processSettlements(data, catchmentArea); }; })(catchmentArea));
}
var query = createQueryData(bbox);
- if (typeof self.amenities != "undefined") {
- console.log(getDataURIForRegion(self, "region"));
- }
- self.amenities = {};
_.each(self.amenitiesShown, function(amenity) {
self.amenities[amenity] = [];
});
// Convert the data to GeoJSON
self.converter.fetch("http://overpass-api.de/api/interpreter", query, zoom, function(data) {
- if (jQuery.isEmptyObject(self.amenityLayers)) {
-
- // For each catchment area polygon
- _.each(
- _.filter(data.features,
- function(feature) {
- return _.contains(_.values(feature.properties), "catchment_area");
- }),
- function(catchmentArea) {
- // Add it to the associative array
- self.catchmentAreas[catchmentArea.properties["subject"]] = catchmentArea;
+ self.catchmentAreaLayer.clearLayers();
+ var oldCatchmentAreas = self.catchmentAreas;
+ self.catchmentAreas = {};
+ self.healthPosts = {};
+
+ // For each health post
+ _.each(
+ _.filter(data.features,
+ function(feature) {
+ return feature.properties["amenity"] == "hospital";
+ }),
+ function(healthPost) {
+ // Add it to the associative array
+ self.healthPosts[healthPost.id] = healthPost;
+ }
+ );
+
+ // For each catchment area polygon
+ _.each(
+ _.filter(data.features,
+ function(feature) {
+ return _.contains(_.values(feature.properties), "catchment_area");
+ }),
+ function(catchmentArea) {
+ // Add it to the associative array
+ var subjectId = catchmentArea.properties["subject"];
+
+ if (subjectId in oldCatchmentAreas) {
+ catchmentArea = oldCatchmentAreas[subjectId];
+ }
+
+ self.catchmentAreas[subjectId] = catchmentArea;
+
+ // If the settlements for this catchment area have already been fetched
+ if (typeof catchmentArea.settlements == 'undefined') {
addSettlementsForArea(catchmentArea);
- }
- );
-
- // Now deal with the catchment areas
- self.catchmentAreaLayer = createCatchmentAreaLayer(data);
-
- _.each(self.amenitiesShown, function(amenity, i) {
- self.amenityLayers[amenity] = createAmenityLayer(data, amenity);
-
- map.addLayer(self.amenityLayers[amenity]);
- self.layersControl.addOverlay(self.amenityLayers[amenity],
- self.amenitiesShown[i].charAt(0).toUpperCase() + self.amenitiesShown[i].slice(1));
- });
-
- map.addLayer(self.catchmentAreaLayer);
- self.layersControl.addOverlay(self.catchmentAreaLayer, "Catchment Areas");
- } else {
- self.catchmentAreaLayer.clearLayers();
- self.catchmentAreaLayer.addData(data);
-
- _.each(self.amenitiesShown, function(amenity, i) {
- // Update the data for each amenity layer
- self.amenityLayers[amenity].clearLayers();
- self.amenityLayers[amenity].addData(data);
- });
-
- _.each(self.catchmentAreas, function(catchmentArea, i) {
- // Update the data for each CatchmentAreaVillageLayer
- addSettlementsForArea(catchmentArea);
- });
- }
+ }
+ }
+ );
+
+ self.catchmentAreaLayer.clearLayers();
+ self.catchmentAreaLayer.addData(data);
+
+ _.each(self.amenitiesShown, function(amenity, i) {
+ // Update the data for each amenity layer
+ self.amenityLayers[amenity].clearLayers();
+ self.amenityLayers[amenity].addData(data);
+ });
});
}
@@ -376,32 +402,30 @@ $(document).ready(function() {
var self = this; // the HTMLDocument
- self.popupTemplate = _.template('<a href="http://www.openstreetmap.org/edit?editor=potlatch2&lat=<%= coordinate[1] %>&lon=<%= coordinate[0] %>&zoom=18">\
+ self.editorTemplate = _.template('<a href="http://www.openstreetmap.org/edit?editor=potlatch2&lat=<%= coordinate[1] %>&lon=<%= coordinate[0] %>&zoom=18">\
<img src="resources/img/potlatch.png"></a>\
<a href="http://www.openstreetmap.org/edit?editor=remote&lat=<%= coordinate[1] %>&lon=<%= coordinate[0] %>&zoom=18">\
-<img src="resources/img/josm.png"></a>\
-<h2>Hospital</h2>\
+<img src="resources/img/josm.png"></a>');
+
+ self.healthPostTemplate = _.template('<h2>Hospital</h2>\
<table width="100%">\
-<tr><td>Name</td><td align="right"><%= properties["name"] %></td></tr>\
-<tr><td>Emergency</td><td align="right" style="text-transform:capitalize;"><%= properties["emergency"] %></td></tr>\
-</table>\
-<h2>Catchment Area</h2>\
+<tr><td>Name</td><td align="right"><%= name %></td></tr>\
+<tr><td>Emergency</td><td align="right" style="text-transform:capitalize;"><%= emergency %></td></tr>\
+</table>');
+
+ self.catchmentAreaTemplate = _.template('<h2>Catchment Area</h2>\
<table>\
-<tr><td>Surface Area</td><td align="right"><%= properties["area"] %></td></tr>\
-<tr><td>Number of Settlements</td><td align="right"><%= properties["number_of_settlements"] %></td></tr>\
-<tr><td>Population</td><td align="right"><%= properties["population"] %></td></tr>\
-<tr><td>Furthest distance from settlement to health structure</td><td align="right"><%= properties["greatest_settlement_dist"] %></td></tr>\
-<tr><td>Average distance of all settlements from health structure</td><td align="right"><%= properties["average_settlement_dist"] %></td></tr>\
+<tr><td>Surface Area</td><td align="right"><%= area %></td></tr>\
+<tr><td>Number of Settlements</td><td align="right"><%= number_of_settlements %></td></tr>\
+<tr><td>Population</td><td align="right"><%= population %></td></tr>\
+<tr><td>Furthest distance from settlement to health structure</td><td align="right"><%= greatest_settlement_dist %></td></tr>\
+<tr><td>Average distance of all settlements from health structure</td><td align="right"><%= average_settlement_dist %></td></tr>\
</table>');
- self.settlementPopupTemplate= _.template('<a href="http://www.openstreetmap.org/edit?editor=potlatch2&lat=<%= coordinate[1] %>&lon=<%= coordinate[0] %>&zoom=18">\
-<img src="resources/img/potlatch.png"></a>\
-<a href="http://www.openstreetmap.org/edit?editor=remote&lat=<%= coordinate[1] %>&lon=<%= coordinate[0] %>&zoom=18">\
-<img src="resources/img/josm.png"></a>\
-<h3>Settlement</h3>\
+ self.settlementTemplate = _.template('<h3>Settlement</h3>\
<table width="100%">\
-<tr><td>Name</td><td align="right"><%= properties["name"] %></td></tr>\
-<tr><td>Population</td><td align="right"><%= properties["population"] %></td></tr>\
+<tr><td>Name</td><td align="right"><%= name %></td></tr>\
+<tr><td>Population</td><td align="right"><%= population %></td></tr>\
</table>');
var map = initMap(self);