-
Notifications
You must be signed in to change notification settings - Fork 0
/
us-energy-viz.js
308 lines (250 loc) · 11.2 KB
/
us-energy-viz.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
// Create a Leaflet map object
var map = L.map('map', {
center: [36, -94],
zoom: 4,
});
// Create a tile layer object
var tiles = L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png', {
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> © <a href="http://cartodb.com/attributions">CartoDB</a>',
subdomains: 'abcd',
maxZoom: 19
});
// Add the tile layer to the map object
tiles.addTo(map);
// Get references to HTML elements for displaying statistics
var statsList = document.getElementById("stats");
var statsHeader = document.getElementById("stats-header")
var statsSvg;
// Define common styles for GeoJSON layers
var commonStyles = {
weight: 1,
stroke: 1,
fillOpacity: .8
}
// Define an object of layer information for each energy source
var layerInfo = {
"Hydro": { color: '#1f78b4' },
"Wind": { color: '#6baed6' },
"Solar": { color: '#fd8d3c' },
"Pumped Storage": { color: '#4daf4a' },
"Geothermal": { color: '#8c6bb1' },
"Biomass": { color: '#bd9e39' },
"Wood": { color: '#a1d99b' },
"Coal": { color: '#bdbdbd' },
"Natural Gas": { color: '#e6550d' },
"Petroleum": { color: '#de2d26' },
"Nuclear": { color: '#6a3d9a' },
"Other Fossil Gasses": { color: '#fdae6b' },
"Other": { color: '#636363' }
};
// Create an empty object to store GeoJSON layers for each energy source
var geoJsonLayers = {};
// Iterate through each energy source in the layerInfo object and create a GeoJSON layer for each one
for (var layer in layerInfo) {
geoJsonLayers[layer] = L.geoJson(plants, {
pointToLayer: function (feature, latlng) {
// Create a circular marker for each feature (of each layer) with the common styles
return L.circleMarker(latlng, commonStyles);
},
filter: function (feature) {
// Filter out features that do not have the current for loop's iteration energy source in their fuel sources
if (feature.properties.fuel_source[layer]) {
return feature;
}
},
style: function (feature) {
// Set the style for each feature based on the current energy source's color and the feature's fuel source value for that energy source
return {
color: layerInfo[layer].color,
fillColor: layerInfo[layer].color,
radius: getRadius(feature.properties.fuel_source[layer])
}
},
onEachFeature: function (feature, layer) {
// Initialize tooltips for each feature
content = initTooltips(feature, layer);
layer.bindTooltip(content);
}
}).addTo(map);
}
// Create an empty dictionary to store key-value pairs for Leaflet's layer control
var sourcesLayers = {};
// Iterate through each energy source in the layerInfo object and add a key-value pair to the sourcesLayers object
for (var key in layerInfo) {
// The key is an HTML string with the energy source name colored with the corresponding color, and the value is the corresponding GeoJSON layer from the geoJsonLayers object
sourcesLayers[`<span style='color:${layerInfo[key].color}'><b>${key}</b></span>`] = geoJsonLayers[key];
}
// Add a layer control to the map object with the sourcesLayers object as the overlay layers
var layerControl = L.control.layers(null, sourcesLayers, { collapsed: true }).addTo(map);
// <input type="checkbox" id="layer1" name="Layer1" oninput="toggleLayer(this)"><label for="layer1"> LAYER 1 </label>
// function toggleLayer(element) {
// if (element.checked) {
// if (!map.hasLayer(OpenRailwayMap)) map.addLayer(OpenRailwayMap);
// }
// else {
// if (map.hasLayer(OpenRailwayMap)) map.removeLayer(OpenRailwayMap);
// }
// }
// document.getElementById('layer1').checked = map.hasLayer(OpenRailwayMap);
// Uncheck the checkbox for an unchecked layer
// for (var i = 0; i < layerControl._layers, i++) {
// layerControl._layers[0][L.stamp(geoJsonLayers[key])].layer._input.checked = false;
// }
// Add an event listener to the map that triggers when the map is clicked
map.on('click', function (e) {
var stats = {}; // Create object to sum MW values
var statsContent = ''; // Assign variable to hold popup content
var total = 0; // Assign variable to hold count of total MW in area of interest
var proxPlants = []; //Create array to hold latlng coordinates of all plants in the area of interest
// Set the center of the radiusCircle to the current click point and add it to the map
radiusCircle.setLatLng(e.latlng)
.addTo(map);
// Loop through each energy source in the layerInfo object
for (var gsLayer in layerInfo) {
// Loop through each layer in the corresponding geoJsonLayers object
geoJsonLayers[gsLayer].eachLayer(function (layer) {
// Calculate the distance between the current layer and the click point, in kilometers
var distance = e.latlng.distanceTo(layer.getLatLng()) / 1000;
// If the distance is greater than 500 km, hide the layer
if (distance > 500) {
layer.setStyle({
stroke: false,
fill: false
});
} else {
// Otherwise, add the layer's coordinates to the proxPlants array and show the layer
proxPlants.push(layer.getLatLng());
layer.setStyle({
stroke: true,
fill: true
});
// Generate content for the tooltip popup
content = initTooltips(layer.feature, layer);
// Add additional content to the tooltip popup
content += `${Math.ceil(distance)} km from the click point.<br>`
// Set the tooltip content for the layer to the generated content
layer.setTooltipContent(content);
// Get the properties for each geojson feature
var props = layer.feature.properties;
// Loop through all fuel sources for the current layer and update the stats object with their output values
for (var source in props.fuel_source) {
total += props.fuel_source[source]
if (stats[source]) {
stats[source].output += props.fuel_source[source];
} else {
stats[source] = {
output: props.fuel_source[source],
color: layerInfo[source].color
}
}
}
// Add a "Total" key-value pair to the stats object
stats["Total"] = Math.ceil(total);
}
});
}
// Create array to hold objects to generate piechart svg
var svgData = [];
// Loop through each energy source in the stats object and generate content for the stats section
for (var stat in stats) {
// Make sure not to include the "Total" key
if (stat != "Total") {
statsContent += `<b style='color:${stats[stat].color}'>${stat}:</b><br>
${Math.ceil(stats[stat].output).toLocaleString()} MW<br>
<b>(${(Math.ceil((stats[stat].output / stats.Total) * 100)).toLocaleString()}%)</b> <br><br>`;
// Populate svgData array with objects to generate piechart svg
// Add an object to the svgData array for each fuel source in the stats object
svgData.push(
{
label: stat,
value: stats[stat].output,
color: stats[stat].color
}
)
}
}
// Add a piechart svg to the stats section for the plants within the radius
statsSvg = document.getElementById('stats-svg');
statsSvg.innerHTML = createPieChart(svgData).outerHTML;
// Add the "Total" line to the stats section
statsContent += `<b>TOTAL: </b> ${stats.Total.toLocaleString()} MW`
// Set the stats section content to the generated content
// Add stats for plants in radius, to the stats section
statsList.innerHTML = statsContent;
//Update stats header with coords of click
// Update the stats header with the latitude and longitude of the click point
statsHeader.innerHTML = `<span>Within 500km of Latitude: ${Math.ceil(e.latlng.lat)} and Longitude: ${Math.ceil(e.latlng.lng)}`;
// Fly to bounds defined by coords of plants in radius
map.flyToBounds(proxPlants);
});
// Create a circle with a 500 km radius, initially centered at [0, 0], with yellow outline
var radiusCircle = L.circle([0, 0], 500000, {
fillColor: 'white',
fillOpacity: .1,
color: 'yellow',
opacity: .3,
stroke: false,
weight: 3,
interactive: false // This allows users to click through the circle
});
// Reset Layers
// Get the reset button element and add a click event listener
const resetButton = document.getElementById('reset');
L.DomEvent.on(resetButton, 'click', function (ev) {
// Stops the click event from propogating to the reset button's parent element (the map).
L.DomEvent.stopPropagation(ev);
// Loop through each layer and reset the styles and tooltips
for (var gsLayer in layerInfo) {
geoJsonLayers[gsLayer].eachLayer(function (layer) {
// displays all features
layer.setStyle({
stroke: true,
fill: true
});
// resets tooltips
content = initTooltips(layer.feature, layer);
layer.bindTooltip(content);
});
}
// Remove the radius circle and reset the stats section content
radiusCircle.removeFrom(map);
statsHeader.innerHTML = `Click Any Location on the Map to Discover Energy Source Statistics`;
statsSvg.innerHTML = '';
stats.innerHTML = '';
statsList.innerHTML = '';
// Set the map view to the default location and zoom level
map.setView([36, -94], 4);
}); // End reset layers
// Function to calculate the radius of a circle based on a given value
function getRadius(val) {
var radius = Math.sqrt(val / Math.PI);
// Adjusts for map values and returns radius
return radius * .8;
}
// Function to initialize tooltips for a given feature and layer
function initTooltips(feature, layer) {
// Get the properties for each geojson feature
var props = layer.feature.properties;
// Assign powr plant name and energy output to content variable.
var content = `<h3>${props.plant_name}</h3>`;
for (var type in props.fuel_source) {
content += `<span style='color:${layerInfo[type].color}'>${type}:</span>
${props.fuel_source[type].toLocaleString()} MW<br>`;
}
// Some plants have more than one fuel source
// If so...
if (Object.keys(props.fuel_source).length > 1) {
// Find the most dominant...
var compare = 0
var dominant = ''
for (var type in props.fuel_source) {
if (props.fuel_source[type] > compare) {
compare = props.fuel_source[type]
dominant = type;
}
}
// And add it to the popup
content += `Primarily a ${dominant} plant.</span><br>`;
}
return content;
} // end initTooltips