diff options
author | Christopher Baines <cbaines8@gmail.com> | 2013-02-07 00:11:36 +0000 |
---|---|---|
committer | Christopher Baines <cbaines8@gmail.com> | 2013-02-07 00:11:36 +0000 |
commit | 57d851b3bad13db30d233ac70a651a110fa04d3f (patch) | |
tree | 2b440d75172346a59a6bf3cd19bddc5ad8da5056 | |
parent | ecebbf676e8be8945058e013b91a2c023b474b0b (diff) | |
download | health-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.js | 9 | ||||
-rw-r--r-- | resources/map.js | 526 |
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); |