-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
376 lines (335 loc) · 13.9 KB
/
index.html
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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Preston Candy Map</title>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
}
#map {
height: 100vh;
width: 100vw;
}
#toggleMode, #filterCandy, #routeOptions, #endRoute {
position: absolute;
top: 10px;
left: 10px;
z-index: 1000;
padding: 10px;
background-color: white;
border: 1px solid #333;
cursor: pointer;
}
#filterCandy {
top: 50px;
}
#routeOptions {
top: 90px;
}
#endRoute {
top: 130px;
display: none;
}
/* Admin popup styling */
#adminPopup {
display: none;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 20px;
background-color: white;
border: 2px solid #333;
z-index: 2000;
}
</style>
</head>
<body>
<div id="toggleMode">Switch to Dev Mode</div>
<select id="filterCandy">
<option value="">Show All Candies</option>
</select>
<div id="routeOptions">
<label for="routeCandy">Route Candy:</label>
<select id="routeCandy">
<option value="">Choose Candy</option>
</select>
<label for="houseCount">Number of Houses:</label>
<input type="number" id="houseCount" min="1" value="3" style="width: 60px;">
<button id="createRoute">Create Route</button>
</div>
<button id="endRoute">End Route</button>
<!-- Admin login popup -->
<div id="adminPopup">
<label for="adminPassword">Enter Admin Password:</label>
<input type="password" id="adminPassword" />
<button id="submitAdmin">Submit</button>
</div>
<div id="map"></div>
<!-- Leaflet library -->
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<!-- Leaflet Routing Machine for the route creation -->
<script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
<script>
// Initialize the map centered on Preston, Idaho
var map = L.map('map', {
minZoom: 8, // Minimum zoom level to ensure markers stay visible
maxZoom: 19 // Maximum zoom level for details
}).setView([42.0963, -111.8766], 14); // Preston coordinates
// Set up the tile layer from OpenStreetMap
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Data for candies
var candyOptions = [
'Snickers', 'Peanut Butter Cups', 'Hershey\'s Kisses',
'Kit Kat', 'M&Ms', 'Hershey Bar', 'Twix', 'Starbursts'
];
// Add candy options to the filter and route dropdowns
var filterDropdown = document.getElementById('filterCandy');
var routeCandyDropdown = document.getElementById('routeCandy');
candyOptions.forEach(function(candy) {
var option = document.createElement('option');
option.value = candy;
option.textContent = candy;
filterDropdown.appendChild(option);
routeCandyDropdown.appendChild(option.cloneNode(true));
});
// Mode and marker control
var devMode = false;
var loadedBuildings = {}; // Stores buildings and candy data
var markers = {}; // Store markers by ID for easy management
var currentCandyFilter = ''; // Track the current candy filter
var routingControl = null; // For managing routes
var routingMode = false; // Track if routing mode is active
// Function to query OpenStreetMap buildings data via Overpass API (2-mile radius)
function fetchBuildings(bbox) {
var overpassApiUrl = 'https://overpass-api.de/api/interpreter';
var query = `
[out:json];
(
way["building"](around:3220,42.0963,-111.8766); // 2-mile radius (3.22 km)
relation["building"](around:3220,42.0963,-111.8766);
);
out center;
`;
fetch(overpassApiUrl, {
method: 'POST',
body: query,
})
.then(response => response.json())
.then(data => {
data.elements.forEach((element) => {
var id = element.id;
if (!loadedBuildings[id]) { // Only create markers for new buildings
var lat = element.center ? element.center.lat : element.lat;
var lon = element.center ? element.center.lon : element.lon;
// Assign random candy or use existing candy data
var candy = candyOptions[Math.floor(Math.random() * candyOptions.length)];
if (loadedBuildings[id]) candy = loadedBuildings[id].candy;
// Store building data
var building = { id, lat, lon, candy };
loadedBuildings[id] = building;
createMarker(building);
}
});
})
.catch(err => console.error('Error fetching building data:', err));
}
// Function to create markers with candy labels for buildings
function createMarker(building) {
var marker = L.marker([building.lat, building.lon]).addTo(map);
markers[building.id] = marker; // Store marker reference by building ID
// Create a popup with the candy name
var popupContent = `<b>Candy: ${building.candy}</b>`;
// Candy dropdown for dev mode
var candySelector = document.createElement('select');
candyOptions.forEach(function (candy) {
var option = document.createElement('option');
option.value = candy;
option.textContent = candy;
candySelector.appendChild(option);
});
candySelector.value = building.candy;
candySelector.addEventListener('change', function () {
building.candy = this.value;
marker.setPopupContent(`<b>Candy: ${this.value}</b>`);
applyCandyFilter(); // Reapply the filter when candy changes
});
// Bind popup and candy selector based on mode
marker.bindPopup(devMode ? candySelector : popupContent);
// Show or hide marker based on current candy filter
if (currentCandyFilter && building.candy !== currentCandyFilter) {
map.removeLayer(marker); // Hide marker if not matching filter
}
}
// Admin login logic for Dev Mode
document.getElementById('toggleMode').addEventListener('click', function () {
var adminPopup = document.getElementById('adminPopup');
var adminPasswordInput = document.getElementById('adminPassword');
if (devMode) {
// Already in dev mode, switch back to normal mode
devMode = false;
this.innerHTML = 'Switch to Dev Mode';
refreshMarkers();
} else {
// Show the admin popup to prompt for password
adminPopup.style.display = 'block';
adminPasswordInput.value = ''; // Clear any previous input
}
});
// Handle the admin login popup submission
document.getElementById('submitAdmin').addEventListener('click', function () {
var adminPassword = document.getElementById('adminPassword').value;
var adminPopup = document.getElementById('adminPopup');
if (adminPassword === 'admin') {
devMode = true;
document.getElementById('toggleMode').innerHTML = 'Switch to Normal Mode';
adminPopup.style.display = 'none'; // Hide popup
refreshMarkers();
} else {
alert('Incorrect password. Access denied.');
}
});
// Function to refresh markers based on mode (dev or normal)
function refreshMarkers() {
for (var id in markers) {
if (markers[id]) {
map.removeLayer(markers[id]); // Remove existing markers
createMarker(loadedBuildings[id]); // Recreate with updated mode
}
}
}
// Function to filter markers by selected candy
filterDropdown.addEventListener('change', function () {
currentCandyFilter = this.value;
applyCandyFilter();
});
// Function to apply the candy filter
function applyCandyFilter() {
for (var id in markers) {
if (markers[id]) {
var building = loadedBuildings[id];
if (currentCandyFilter === '' || building.candy === currentCandyFilter) {
if (!map.hasLayer(markers[id])) {
map.addLayer(markers[id]); // Show marker if it matches filter
}
} else {
map.removeLayer(markers[id]); // Hide marker if it doesn't match filter
}
}
}
}
// Function to create a route between selected houses and follow the streets
document.getElementById('createRoute').addEventListener('click', function () {
var candyType = document.getElementById('routeCandy').value;
var houseCount = parseInt(document.getElementById('houseCount').value, 10);
if (!candyType || houseCount < 1) {
alert('Please select a candy and set a valid number of houses.');
return;
}
if (routingControl) {
map.removeControl(routingControl); // Remove existing route
}
// Enter routing mode and hide all markers except for the route
routingMode = true;
document.getElementById('endRoute').style.display = 'block'; // Show end route button
for (var id in markers) {
if (markers[id]) {
map.removeLayer(markers[id]); // Hide all markers temporarily
}
}
// Collect all buildings with the selected candy
var waypoints = [];
for (var id in loadedBuildings) {
if (loadedBuildings[id].candy === candyType) {
var building = loadedBuildings[id];
waypoints.push(L.latLng(building.lat, building.lon));
if (waypoints.length <= houseCount) {
markers[id].addTo(map); // Add marker back to map for relevant houses
}
}
}
// Limit the number of houses to the requested count
waypoints = waypoints.slice(0, houseCount);
// Add a route between the waypoints if there are enough houses
if (waypoints.length > 1) {
routingControl = L.Routing.control({
waypoints: waypoints,
routeWhileDragging: true,
lineOptions: {
styles: [{ color: 'red', opacity: 1, weight: 5 }] // Red route for visibility
},
createMarker: function(i, waypoint, n) {
return L.marker(waypoint.latLng, {
icon: L.divIcon({
className: 'route-marker',
html: i === 0 ? 'Start' : i === n - 1 ? 'End' : ''
})
});
},
show: false // Disable turn-by-turn directions panel
}).addTo(map);
} else {
alert('Not enough houses found for the selected candy.');
}
});
// Function to end the route and return to normal mode
document.getElementById('endRoute').addEventListener('click', function () {
routingMode = false;
document.getElementById('endRoute').style.display = 'none'; // Hide the end route button
if (routingControl) {
map.removeControl(routingControl); // Remove the current route
}
// Show all markers again
for (var id in markers) {
if (!map.hasLayer(markers[id])) {
markers[id].addTo(map);
}
}
});
// Function to get the current map bounds in a format suitable for Overpass API
function getBoundingBox() {
var bounds = map.getBounds();
return {
north: bounds.getNorth(),
south: bounds.getSouth(),
east: bounds.getEast(),
west: bounds.getWest()
};
}
// Function to show/hide markers based on current bounds
function updateVisibleMarkers() {
var bounds = map.getBounds();
// Loop through all markers and only show ones inside bounds
for (var id in loadedBuildings) {
var building = loadedBuildings[id];
var latLng = L.latLng(building.lat, building.lon);
if (bounds.contains(latLng)) {
if (!markers[id]) {
createMarker(building); // Create new marker if not already shown
} else if (!map.hasLayer(markers[id])) {
markers[id].addTo(map); // Add marker back to map if hidden
}
} else {
if (markers[id] && map.hasLayer(markers[id])) {
map.removeLayer(markers[id]); // Remove marker if out of bounds
}
}
}
}
// Event: When zooming or panning, update the markers based on visibility
map.on('moveend', function () {
if (!routingMode) {
updateVisibleMarkers(); // Dynamically update which markers are shown
}
});
// Load buildings when the page is first loaded
fetchBuildings(getBoundingBox());
</script>
</body>
</html>