From 57d851b3bad13db30d233ac70a651a110fa04d3f Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Thu, 7 Feb 2013 00:11:36 +0000 Subject: 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... --- resources/data.js | 9 - 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 += ' Hospital
'; - 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) + + '
' + + self.catchmentAreaTemplate(catchmentAreaProperties(catchmentArea, feature)) + + '
'); - 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 += ' Hospital
'; + 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('\ + self.editorTemplate = _.template('\ \ \ -\ -

Hospital

\ +'); + + self.healthPostTemplate = _.template('

Hospital

\ \ -\ -\ -
Name<%= properties["name"] %>
Emergency<%= properties["emergency"] %>
\ -

Catchment Area

\ +Name<%= name %>\ +Emergency<%= emergency %>\ +'); + + self.catchmentAreaTemplate = _.template('

Catchment Area

\ \ -\ -\ -\ -\ -\ +\ +\ +\ +\ +\
Surface Area<%= properties["area"] %>
Number of Settlements<%= properties["number_of_settlements"] %>
Population<%= properties["population"] %>
Furthest distance from settlement to health structure<%= properties["greatest_settlement_dist"] %>
Average distance of all settlements from health structure<%= properties["average_settlement_dist"] %>
Surface Area<%= area %>
Number of Settlements<%= number_of_settlements %>
Population<%= population %>
Furthest distance from settlement to health structure<%= greatest_settlement_dist %>
Average distance of all settlements from health structure<%= average_settlement_dist %>
'); - self.settlementPopupTemplate= _.template('\ -\ -\ -\ -

Settlement

\ + self.settlementTemplate = _.template('

Settlement

\ \ -\ -\ +\ +\
Name<%= properties["name"] %>
Population<%= properties["population"] %>
Name<%= name %>
Population<%= population %>
'); var map = initMap(self); -- cgit v1.2.3