|
| 1 | + <!DOCTYPE html> |
| 2 | + <html> |
| 3 | + <head> |
| 4 | + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
| 5 | + <title>Final Project BG Hospitals</title> |
| 6 | + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.2.0/leaflet.css" type="text/css" crossorigin=""> |
| 7 | + |
| 8 | + <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.2.0/leaflet.js" crossorigin=""></script> |
| 9 | + <script src=" https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity=" sha512-WXoSHqw/t26DszhdMhOXOkI7qCiv5QWXhH9R7CgvgZMHz1ImlkVQ3uNsiQKu5wwbbxtPzFXd1hK4tzno2VqhpA==" crossorigin="" ></script> |
| 10 | + |
| 11 | + <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> |
| 12 | + |
| 13 | + <link rel="stylesheet" href="style2_April26th_6.30pm.css" type="text/css"> |
| 14 | + <link rel="stylesheet" href="style.css" type="text/css"> |
| 15 | + |
| 16 | + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/2.2.1/js/bootstrap.min.js"></script> |
| 17 | + |
| 18 | + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/2.2.1/css/bootstrap.min.css"> |
| 19 | + |
| 20 | + <!-- <script src="cities2.js"></script> --> |
| 21 | + <script src="cities_dist2.js"></script> |
| 22 | + |
| 23 | + |
| 24 | + <script src="bg_hostpitals_gisco_geojson_4326.js"></script> |
| 25 | + <script src="vw_provinces_nsi_hospitals_4326.js"></script> |
| 26 | + |
| 27 | + |
| 28 | + |
| 29 | + <link rel="stylesheet" href="MarkerCluster.css" /> |
| 30 | + <link rel="stylesheet" href="MarkerCluster.Default.css" /> |
| 31 | + <script src="leaflet.markercluster-src.js"></script> |
| 32 | + |
| 33 | + <link rel="stylesheet" href="leaflet-search-master\dist\leaflet-search.src.css"> |
| 34 | + <script src="leaflet-search-master\dist\leaflet-search.src.js"></script> |
| 35 | + |
| 36 | + |
| 37 | + <script type="text/javascript"> |
| 38 | + |
| 39 | + // Variables |
| 40 | + var map; |
| 41 | + var hospitalLayer; |
| 42 | + |
| 43 | + // Selection variables |
| 44 | + var selection; |
| 45 | + var selectedLayer; |
| 46 | + //var selectedFeature; |
| 47 | + |
| 48 | + function init() { |
| 49 | + |
| 50 | + // create map and set center and zoom level |
| 51 | + map = new L.map('mapid'); |
| 52 | + map.setView([42.7339, 25.4858],7); |
| 53 | + |
| 54 | + // create carto basemap |
| 55 | + var cartoGrey = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', { |
| 56 | + maxZoom: 19, |
| 57 | + attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors' |
| 58 | + }); |
| 59 | + cartoGrey.addTo(map); |
| 60 | + |
| 61 | + // create and add osm tile layer |
| 62 | + var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { |
| 63 | + maxZoom: 19, |
| 64 | + attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' |
| 65 | + }); |
| 66 | + |
| 67 | + // create osm humanitarian layer (not adding it to map) |
| 68 | + var osmHumanitarian = L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', { |
| 69 | + maxZoom: 19, |
| 70 | + attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' |
| 71 | + }); |
| 72 | + |
| 73 | + |
| 74 | + function polystyle(feature) { |
| 75 | + return { |
| 76 | + // fillColor: 'beige', // polygon color - now left blank |
| 77 | + fillOpacity: 0, //polygon opacity |
| 78 | + weight: 4, |
| 79 | + opacity: .1, //Outline opacity |
| 80 | + color: 'green' //Outline color |
| 81 | + |
| 82 | + }; |
| 83 | + } |
| 84 | + var provinceLayer = L.geoJSON(vw_provincesData, { |
| 85 | + style:polystyle, // from style function defined above |
| 86 | + onEachFeature: function(feature, layer) { |
| 87 | + layer.bindPopup('<b>' + feature.properties.provinces_en + ' Province </b><br> Public Hospitals in Province: ' + feature.properties.state_hospitalstate_hospital + '<br> Public Hospital Beds in Province: ' + feature.properties.beds_state_hospital)} |
| 88 | + }).addTo(map); |
| 89 | + |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | + // create icons for hospitals (selected and unselected) |
| 94 | + var hospitalIcon = L.icon({ |
| 95 | + iconUrl: 'hospital_unselected.svg', //default orange hospital icon |
| 96 | + iconSize: [30,30] //[20,20] //[40,40] |
| 97 | + }); |
| 98 | + |
| 99 | + var selectedHospitalIcon = L.icon({ |
| 100 | + iconUrl: 'hospital_selected.svg', //selected blue hospital icon |
| 101 | + iconSize: [30,30] //[20,20] //[40,40] |
| 102 | + }); |
| 103 | + |
| 104 | + // handle click events on hospital features |
| 105 | + function hospitalsOnEachFeature(feature, layer){ |
| 106 | + layer.on({ |
| 107 | + click: function(e) { |
| 108 | + if (selection) { |
| 109 | + resetStyles(); |
| 110 | + } |
| 111 | + e.target.setIcon(selectedHospitalIcon); //selected hospital based on click event |
| 112 | + selection = e.target; |
| 113 | + selectedLayer = hospitalLayer; |
| 114 | + |
| 115 | + // Insert HTML with the feature name |
| 116 | + buildSummaryLabel(feature); |
| 117 | + |
| 118 | + L.DomEvent.stopPropagation(e); // stop click event from being propagated further |
| 119 | + } |
| 120 | + }); |
| 121 | + } |
| 122 | + |
| 123 | + |
| 124 | + |
| 125 | + |
| 126 | + |
| 127 | + // Hospital Custom radius and icon create function |
| 128 | + var markersHospital = L.markerClusterGroup({ |
| 129 | + maxClusterRadius: 120, |
| 130 | + iconCreateFunction: function (cluster) { |
| 131 | + var childCount = cluster.getChildCount(); |
| 132 | + |
| 133 | + var c = ' marker-hospital-'; |
| 134 | + if (childCount < 10) { |
| 135 | + c += 'small'; |
| 136 | + } else if (childCount < 100) { |
| 137 | + c += 'medium'; |
| 138 | + } else { |
| 139 | + c += 'large'; |
| 140 | + } |
| 141 | + return new L.DivIcon({ html: '<div><span>' + childCount + '</span></div>', className: 'marker marker-hospital' + c, iconSize: new L.Point(40, 40) }); |
| 142 | + }, |
| 143 | + //Disable all of the defaults: |
| 144 | + disableClusteringAtZoom:14, spiderfyOnMaxZoom: false, showCoverageOnHover: false, zoomToBoundsOnClick: true |
| 145 | + }); |
| 146 | + |
| 147 | + var hospitalLayer = new L.geoJSON(hospitalData,{ |
| 148 | + pointToLayer: function (feature, latlng) { |
| 149 | + return L.marker(latlng, {icon: hospitalIcon}); |
| 150 | + }, |
| 151 | + onEachFeature: hospitalsOnEachFeature |
| 152 | + } |
| 153 | + ); |
| 154 | + |
| 155 | + //hospitalLayer.addTo(map) |
| 156 | + markersHospital.addLayer(hospitalLayer).addTo(map); |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | + // handle clicks on the map that didn't hit a feature |
| 161 | + map.addEventListener('click', function(e) { |
| 162 | + if (selection) { |
| 163 | + resetStyles(); |
| 164 | + selection = null; |
| 165 | + document.getElementById('summaryLabel').innerHTML = '<p>Click a hospital on the map to get more information.</p>'; |
| 166 | + } |
| 167 | + }); |
| 168 | + |
| 169 | + // function to set the old selected feature back to its original symbol. Used when the map or a feature is clicked. |
| 170 | + function resetStyles(){ |
| 171 | + if (selectedLayer === hospitalLayer) selection.setIcon(hospitalIcon); |
| 172 | + else if (selectedLayer === otherLayer) selectedLayer.resetStyle(selection); |
| 173 | + } |
| 174 | + |
| 175 | + // function to build the HTML for the summary label using the selected feature's "name" property |
| 176 | + function buildSummaryLabel(currentFeature){ |
| 177 | + var featureName = currentFeature.properties.hospital_name || "Unnamed feature"; |
| 178 | + var featureCity = currentFeature.properties.city || ""; // Hospital City |
| 179 | + var bedCapacity = currentFeature.properties.cap_beds || ""; // Bed Capacity |
| 180 | + |
| 181 | + var summaryHTML = '<p style="font-size:18px"><b>Hospital:</b> ' + featureName + '</p>'; |
| 182 | + |
| 183 | + // Concatenate the additional label if it exists |
| 184 | + if (featureCity !== "") {summaryHTML += '<p><b>City:</b> ' + featureCity + '</p>';} |
| 185 | + if (bedCapacity !== "") {summaryHTML += '<p><b>Bed Capacity:</b> ' + bedCapacity + '</p>';} |
| 186 | + |
| 187 | + document.getElementById('summaryLabel').innerHTML = summaryHTML; |
| 188 | + } |
| 189 | + |
| 190 | + |
| 191 | + |
| 192 | + |
| 193 | + //City cluster variables |
| 194 | + var markers = L.markerClusterGroup({ |
| 195 | + maxClusterRadius: 90, |
| 196 | + iconCreateFunction: function (cluster) { |
| 197 | + var childCount = cluster.getChildCount(); |
| 198 | + |
| 199 | + var c = ' marker-city-'; |
| 200 | + if (childCount < 10) { |
| 201 | + c += 'small'; |
| 202 | + } else if (childCount < 100) { |
| 203 | + c += 'medium'; |
| 204 | + } else { |
| 205 | + c += 'large'; |
| 206 | + } |
| 207 | + return new L.DivIcon({ html: '<div><span>' + childCount + '</span></div>', className: 'marker marker-city' + c, iconSize: new L.Point(40, 40) }); |
| 208 | + }, |
| 209 | + //Disable all of the defaults: |
| 210 | + spiderfyOnMaxZoom: true, showCoverageOnHover: false, zoomToBoundsOnClick: true |
| 211 | + }); |
| 212 | + |
| 213 | + // city styling |
| 214 | + var geojsonMarkerOptions = { |
| 215 | + radius: 8, |
| 216 | + fillColor: "#8dd3c7", //"#ff7800", |
| 217 | + color: "#000", |
| 218 | + weight: .8, |
| 219 | + opacity: .7, |
| 220 | + fillOpacity: 0.7 |
| 221 | + }; |
| 222 | + |
| 223 | + // cities |
| 224 | + var geojsonLayer = L.geoJSON(citiesData, { |
| 225 | + pointToLayer: function (feature, latlng) { |
| 226 | + return L.circleMarker(latlng, geojsonMarkerOptions); |
| 227 | + }, |
| 228 | + onEachFeature: function(feature, layer) { |
| 229 | + var cityName = feature.properties.city_name_en || feature.properties.city_name_bg; |
| 230 | + var distance = (feature.properties.dist / 1000).toFixed(1); // Rounds to one decimal place |
| 231 | + layer.bindPopup('City: <b>' + cityName + ' </b> <br>Distance to nearest hospital: <b>' + distance + ' km</b> <br>Hospital: <b> '+ feature.properties.hospital_name +' </b>')} |
| 232 | + }); |
| 233 | + //.addTo(map); |
| 234 | + |
| 235 | + // add cities clusters to map |
| 236 | + markers.addLayer(geojsonLayer) |
| 237 | + map.addLayer(markers) |
| 238 | + |
| 239 | + // Unclustered city2 styling |
| 240 | + function geojsonMarkerOptionsColor (feature) { |
| 241 | + var distance_km = feature.properties.dist; // Accessing distance from feature.properties |
| 242 | + if (distance_km > 62200) { |
| 243 | + return { fillColor: "#d73027", weight: 0, fillOpacity: 0.5 }; // Style for distance > 62.2 km |
| 244 | + } else if (distance_km > 34600) { |
| 245 | + return { fillColor: "#d73027", weight: 0, fillOpacity: 0.5 }; // Style for 34.6 km - 62.2 km |
| 246 | + } else if (distance_km > 23400) { |
| 247 | + return { fillColor: "#fc8d59", weight: 0, fillOpacity: 0.5 }; // Style for 23.4 km - 34.6 km |
| 248 | + } else if (distance_km > 15500) { |
| 249 | + return { fillColor: "#fee090", weight: 0, fillOpacity: 0.5 }; // Style for 15.5 km - 23.4 km |
| 250 | + } else if (distance_km > 8700) { |
| 251 | + return { fillColor: "#b5e8f5", weight: 0, fillOpacity: 0.5 }; // Style for 8.7 km - 15.5 km |
| 252 | + } else { |
| 253 | + return { fillColor: "#91bfdb", weight: 0, fillOpacity: 0.5 }; // Default style for less than 8.7 km |
| 254 | + } |
| 255 | + } |
| 256 | + |
| 257 | + // Unclustered city2 geoJSON and pop-up |
| 258 | + var geojsonColorLayer = L.geoJSON(citiesData, { |
| 259 | + pointToLayer: function (feature, latlng) { |
| 260 | + return L.circleMarker(latlng, geojsonMarkerOptionsColor(feature)); // Pass feature to the function |
| 261 | + }, |
| 262 | + onEachFeature: function(feature, layer) { |
| 263 | + var cityName = feature.properties.city_name_en || feature.properties.city_name_bg; |
| 264 | + var distance = (feature.properties.dist / 1000).toFixed(1); // Rounds to one decimal place |
| 265 | + layer.bindPopup('City: <b>' + cityName + ' </b> <br>Distance to nearest hospital: <b>' + distance + ' km</b> <br>Hospital: <b> '+ feature.properties.hospital_name +' </b>'); |
| 266 | + }, |
| 267 | + }); |
| 268 | + |
| 269 | + |
| 270 | + |
| 271 | + |
| 272 | + |
| 273 | + |
| 274 | + // define basemap and thematic layers and add layer switcher control |
| 275 | + var basemaps = { |
| 276 | + "OSM": osm, |
| 277 | + "OSM Humanitarian": osmHumanitarian, |
| 278 | + "Carto Grey": cartoGrey |
| 279 | + }; |
| 280 | + |
| 281 | + |
| 282 | + var overlays = { |
| 283 | + "Hospitals Clustered": markersHospital, |
| 284 | + "Cities Clustered": markers, |
| 285 | + "Most Vunerable Places": geojsonColorLayer, |
| 286 | + "Provinces": provinceLayer |
| 287 | + }; |
| 288 | + L.control.layers(basemaps,overlays).addTo(map); |
| 289 | + |
| 290 | + |
| 291 | + |
| 292 | + //Search |
| 293 | + |
| 294 | + // city search |
| 295 | + const citySearchControl = new L.Control.Search({ |
| 296 | + position: 'topright', |
| 297 | + autoCollapse: true, |
| 298 | + layer: markers, |
| 299 | + zoom: 13, |
| 300 | + propertyName: 'city_name_en' |
| 301 | + }); |
| 302 | + |
| 303 | + map.addControl(citySearchControl); |
| 304 | + |
| 305 | + // province search |
| 306 | + const provinceSearchControl = new L.Control.Search({ |
| 307 | + position: 'topright', |
| 308 | + autoCollapse: true, |
| 309 | + layer: provinceLayer, |
| 310 | + zoom: 9, |
| 311 | + propertyName: 'provinces_en' |
| 312 | + }); |
| 313 | + |
| 314 | + map.addControl(provinceSearchControl); |
| 315 | + |
| 316 | + } |
| 317 | + |
| 318 | + </script> |
| 319 | + </head> |
| 320 | +<div class="general"> |
| 321 | +<body onload="init()"> |
| 322 | + |
| 323 | + <div class="left"> |
| 324 | + <div class="title"><h1>Hospital Accessibility in Bulgaria</h1></div> |
| 325 | + <div id="mapid"></div> |
| 326 | + <img src="Distance from Hospital Distribution.png" alt="Distance From Hospital Histogram"> |
| 327 | + <img src="ScatterPlot_best_fit.png" alt="Scatter Plot Population and Distance to Hospital"> |
| 328 | + <img src="Hospital Beds per 1000 People by Province.png" alt="hospital beds per 1000 by province"> |
| 329 | + <img src="Top_20_Nearest Cities_final.png" alt="Top 20 Hospitals with Highest Number of Nearest Cities"> |
| 330 | + </div> |
| 331 | + <div class="right"> |
| 332 | + <div class="layers"><b>Click here for layers</b></div> |
| 333 | + <div class="city-search"> <b>Search cities</b></div> |
| 334 | + <div class="province-search"><b>Search provinces</b></div> |
| 335 | + |
| 336 | + <div id = 'summaryLabel'> |
| 337 | + <h3>Legend</h3> |
| 338 | + <img src="legend_icon_green.png" alt="Hospital Clusters"> Hospital Clusters<br> |
| 339 | + <img src="legend_icon_blue.png" alt="Hospital Clusters"> City Clusters<br> |
| 340 | + <br><p>Click on the <b>hospital icon</b> to get <br> more information about a particular hospital. <br><br> |
| 341 | + Click on a <b>province</b> to find the total number of <br> state hospital beds in the province. </p></div> |
| 342 | + </div> |
| 343 | + </div> |
| 344 | + </body> |
| 345 | +</html> |
0 commit comments