Skip to content

Commit e2a53d0

Browse files
Initial commit
0 parents  commit e2a53d0

File tree

118 files changed

+47603
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+47603
-0
lines changed

.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto
6.44 KB
Loading
+345
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,345 @@
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 &copy; <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: '&copy; <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: '&copy; <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>
6.12 KB
Loading
9.13 KB
Loading

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 geospatial-geek
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2+
bin
3+
obj
4+
5+
# mstest test results
6+
TestResults
7+
node_modules

0 commit comments

Comments
 (0)