thanthamky commited on
Commit
f324446
·
verified ·
1 Parent(s): 19091d3

Upload index.html

Browse files
Files changed (1) hide show
  1. index.html +445 -14
index.html CHANGED
@@ -1,15 +1,446 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>My Website</h1>
12
- <p>This is my first webpage</p>
13
- </div>
14
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  </html>
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Leaflet GeoJSON Styler</title>
7
+
8
+ <!-- 1. Leaflet CSS from CDN -->
9
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
10
+ integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
11
+ crossorigin="" />
12
+
13
+ <!-- 2. Google Font (Inter) -->
14
+ <link rel="preconnect" href="https://fonts.googleapis.com">
15
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
16
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" rel="stylesheet">
17
+
18
+ <!-- 3. Embedded CSS -->
19
+ <style>
20
+ /* Basic page reset and font application */
21
+ html, body {
22
+ height: 100%;
23
+ width: 100%;
24
+ margin: 0;
25
+ padding: 0;
26
+ font-family: 'Inter', sans-serif;
27
+ background-color: #f0f0f0;
28
+ }
29
+
30
+ /* Full-page map container */
31
+ #map {
32
+ height: 100%;
33
+ width: 100%;
34
+ /* Fixes potential z-index issue with Leaflet controls */
35
+ z-index: 1;
36
+ }
37
+
38
+ /* Styling for the top-left controls widget */
39
+ #controls {
40
+ position: absolute;
41
+ top: 10px;
42
+ left: 50px; /* Aligned with Leaflet's zoom control */
43
+ z-index: 1000; /* Ensures it's above the map */
44
+ background: white;
45
+ padding: 12px 15px;
46
+ border-radius: 8px;
47
+ box-shadow: 0 2px 6px rgba(0,0,0,0.2);
48
+ display: flex;
49
+ flex-direction: column;
50
+ gap: 10px;
51
+ max-width: 200px;
52
+ }
53
+
54
+ #controls h4 {
55
+ margin: 0 0 5px 0;
56
+ font-size: 14px;
57
+ font-weight: 700;
58
+ color: #333;
59
+ }
60
+
61
+ #controls label {
62
+ font-size: 12px;
63
+ font-weight: 500;
64
+ color: #555;
65
+ display: block;
66
+ margin-bottom: 3px;
67
+ }
68
+
69
+ /* Styling for inputs and select */
70
+ #controls input[type="text"],
71
+ #controls input[type="color"],
72
+ #controls select {
73
+ width: 100%;
74
+ box-sizing: border-box; /* Ensures padding doesn't affect width */
75
+ border: 1px solid #ccc;
76
+ border-radius: 4px;
77
+ padding: 6px;
78
+ font-size: 12px;
79
+ font-family: 'Inter', sans-serif;
80
+ }
81
+
82
+ #controls input[type="color"] {
83
+ padding: 2px; /* Color inputs are weird */
84
+ height: 30px;
85
+ }
86
+
87
+ /* Styling for the bottom-right legend */
88
+ .legend {
89
+ position: absolute;
90
+ bottom: 30px; /* Aligned with Leaflet's attribution */
91
+ right: 10px;
92
+ z-index: 1000;
93
+ background: white;
94
+ padding: 10px 12px;
95
+ border-radius: 8px;
96
+ box-shadow: 0 2px 6px rgba(0,0,0,0.2);
97
+ line-height: 1.5;
98
+ font-size: 13px;
99
+ color: #333;
100
+ }
101
+
102
+ .legend h4 {
103
+ margin-top: 0;
104
+ margin-bottom: 5px;
105
+ font-size: 14px;
106
+ font-weight: 700;
107
+ }
108
+
109
+ /* Style for the color box in the legend */
110
+ .legend i {
111
+ width: 18px;
112
+ height: 18px;
113
+ float: left;
114
+ margin-right: 8px;
115
+ opacity: 0.9;
116
+ border: 1px solid #777;
117
+ }
118
+ </style>
119
+ </head>
120
+ <body>
121
+
122
+ <!-- 4. HTML Map Container -->
123
+ <div id="map"></div>
124
+
125
+ <!-- 5. HTML Controls Widget -->
126
+ <div id="controls">
127
+ <h4>GeoJSON Style</h4>
128
+
129
+ <div>
130
+ <label for="colorizeProperty">Colorize Property</label>
131
+ <input type="text" id="colorizeProperty" value="pop_max">
132
+ </div>
133
+
134
+ <div>
135
+ <label for="colormap">Colormap</label>
136
+ <select id="colormap">
137
+ <option value="Reds">Reds</option>
138
+ <option value="Greens">Greens</option>
139
+ <option value="Blues">Blues</option>
140
+ </select>
141
+ </div>
142
+
143
+ <div>
144
+ <label for="strokeColor">Stroke Color</label>
145
+ <input type="color" id="strokeColor" value="#000000">
146
+ </div>
147
+
148
+ <div>
149
+ <label for="tooltipProperty">Tooltip Property</label>
150
+ <input type="text" id="tooltipProperty" value="name">
151
+ </div>
152
+ </div>
153
+
154
+ <!-- 6. Leaflet JS from CDN -->
155
+ <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
156
+ integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
157
+ crossorigin=""></script>
158
+
159
+ <!-- 7. Embedded Application JavaScript -->
160
+ <script>
161
+ // Wait for the DOM to be fully loaded before running script
162
+ document.addEventListener('DOMContentLoaded', () => {
163
+
164
+ // --- 1. GLOBAL VARIABLES ---
165
+ let map;
166
+ let geoJsonLayer;
167
+ let legend;
168
+ const geoJsonUrl = 'https://huggingface.co/spaces/thanthamky/tu-econmap/resolve/main/hhincome.geojson';
169
+
170
+ // DOM elements for controls
171
+ const controls = {
172
+ colorizeProperty: document.getElementById('colorizeProperty'),
173
+ colormap: document.getElementById('colormap'),
174
+ strokeColor: document.getElementById('strokeColor'),
175
+ tooltipProperty: document.getElementById('tooltipProperty')
176
+ };
177
+
178
+ // --- 2. MAP INITIALIZATION ---
179
+
180
+ // Define base map layers
181
+ const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
182
+ attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
183
+ });
184
+
185
+ const googleSat = L.tileLayer('http://www.google.cn/maps/vt?lyrs=s&x={x}&y={y}&z={z}', {
186
+ attribution: 'Map data &copy; Google'
187
+ });
188
+
189
+ // Initialize the map
190
+ map = L.map('map', {
191
+ center: [13.7563, 100.5018], // Centered on Bangkok
192
+ zoom: 6,
193
+ layers: [osm] // Default layer
194
+ });
195
+
196
+ // Layer control objects
197
+ const baseMaps = {
198
+ "OpenStreetMap": osm,
199
+ "Google Satellite": googleSat
200
+ };
201
+ const overlayMaps = {}; // Will be populated by fetch
202
+
203
+ // Add layer control to map
204
+ const layerControl = L.control.layers(baseMaps, overlayMaps, {
205
+ position: 'topright',
206
+ collapsed: true
207
+ }).addTo(map);
208
+
209
+
210
+ // --- 3. LEGEND CONTROL ---
211
+
212
+ // Create a custom control for the legend
213
+ const LegendControl = L.Control.extend({
214
+ options: {
215
+ position: 'bottomright'
216
+ },
217
+
218
+ onAdd: function (map) {
219
+ const container = L.DomUtil.create('div', 'legend');
220
+ container.innerHTML = `
221
+ <h4>Legend</h4>
222
+ <div><i style="background: #a50f15"></i> Legends</div>
223
+ <!-- This is a static placeholder as requested -->
224
+ `;
225
+ return container;
226
+ }
227
+ });
228
+
229
+ // Add the legend to the map
230
+ legend = new LegendControl().addTo(map);
231
+
232
+
233
+ // --- 4. HELPER FUNCTIONS ---
234
+
235
+ /**
236
+ * Gets the min and max values of a property from GeoJSON features.
237
+ * @param {string} property - The name of the property to analyze.
238
+ * @param {Array} features - An array of GeoJSON features.
239
+ * @returns {object} - An object { min, max }.
240
+ */
241
+ function getMinMax(property, features) {
242
+ const values = features
243
+ .map(f => f.properties[property])
244
+ .filter(v => typeof v === 'number' && isFinite(v));
245
+
246
+ if (values.length === 0) {
247
+ return { min: 0, max: 1 }; // Default if no valid data
248
+ }
249
+
250
+ return {
251
+ min: Math.min(...values),
252
+ max: Math.max(...values)
253
+ };
254
+ }
255
+
256
+ /**
257
+ * Simple linear interpolation for a single color channel.
258
+ * @param {number} val - Normalized value (0 to 1).
259
+ * @param {number} start - Start channel value (0-255).
260
+ * @param {number} end - End channel value (0-255).
261
+ * @returns {number} - Interpolated channel value.
262
+ */
263
+ function lerp(val, start, end) {
264
+ return Math.round(start + (end - start) * val);
265
+ }
266
+
267
+ /**
268
+ * Converts RGB values to a hex string.
269
+ * @param {number} r - Red (0-255).
270
+ * @param {number} g - Green (0-255).
271
+ * @param {number} b - Blue (0-255).
272
+ * @returns {string} - Hex color code (e.g., "#ff0000").
273
+ */
274
+ function toHex(r, g, b) {
275
+ return "#" + [r, g, b]
276
+ .map(x => {
277
+ const hex = x.toString(16);
278
+ return hex.length === 1 ? '0' + hex : hex;
279
+ })
280
+ .join('');
281
+ }
282
+
283
+ /**
284
+ * Generates a color from a colormap based on a value.
285
+ * @param {number} value - The feature's value.
286
+ * @param {number} min - The minimum value in the dataset.
287
+ * @param {number} max - The maximum value in the dataset.
288
+ * @param {string} colormapName - "Reds", "Greens", or "Blues".
289
+ * @returns {string} - Hex color code.
290
+ */
291
+ function getColor(value, min, max, colormapName) {
292
+ if (typeof value !== 'number' || !isFinite(value)) {
293
+ return '#808080'; // Default gray for non-numeric data
294
+ }
295
+
296
+ // Normalize the value
297
+ const range = max - min;
298
+ let normValue = (range === 0) ? 0.5 : (value - min) / range;
299
+ normValue = Math.max(0, Math.min(1, normValue)); // Clamp 0-1
300
+
301
+ let r, g, b;
302
+ const start = 255; // White
303
+
304
+ switch (colormapName) {
305
+ case 'Reds':
306
+ r = start;
307
+ g = lerp(normValue, start, 0); // White to Red
308
+ b = lerp(normValue, start, 0);
309
+ break;
310
+ case 'Greens':
311
+ r = lerp(normValue, start, 0);
312
+ g = start;
313
+ b = lerp(normValue, start, 0); // White to Green
314
+ break;
315
+ case 'Blues':
316
+ default:
317
+ r = lerp(normValue, start, 0);
318
+ g = lerp(normValue, start, 0);
319
+ b = start; // White to Blue
320
+ break;
321
+ }
322
+
323
+ return toHex(r, g, b);
324
+ }
325
+
326
+ /**
327
+ * Dynamically styles a single GeoJSON feature based on current controls.
328
+ * @param {object} feature - The GeoJSON feature.
329
+ * @returns {object} - A Leaflet path style object.
330
+ */
331
+ function styleFeature(feature) {
332
+ const colorizeProp = controls.colorizeProperty.value;
333
+ const colormap = controls.colormap.value;
334
+ const stroke = controls.strokeColor.value;
335
+
336
+ // Get min/max from the full dataset (stored on the layer)
337
+ const { min, max } = geoJsonLayer.minMax[colorizeProp];
338
+ const value = feature.properties[colorizeProp];
339
+
340
+ const fillColor = getColor(value, min, max, colormap);
341
+
342
+ return {
343
+ fillColor: fillColor,
344
+ color: stroke,
345
+ weight: 1,
346
+ opacity: 1,
347
+ fillOpacity: 0.7
348
+ };
349
+ }
350
+
351
+
352
+ // --- 5. STYLING AND TOOLTIP UPDATE FUNCTIONS ---
353
+
354
+ /**
355
+ * Re-applies the style to the entire GeoJSON layer.
356
+ */
357
+ function updateStyle() {
358
+ if (!geoJsonLayer) return; // Guard clause
359
+
360
+ // Re-calculate min/max for the new property if needed
361
+ const prop = controls.colorizeProperty.value;
362
+ if (!geoJsonLayer.minMax[prop]) {
363
+ geoJsonLayer.minMax[prop] = getMinMax(prop, geoJsonLayer.toGeoJSON().features);
364
+ }
365
+
366
+ // Re-style the layer
367
+ geoJsonLayer.setStyle(styleFeature);
368
+ }
369
+
370
+ /**
371
+ * Updates the tooltips for all features on the layer.
372
+ */
373
+ function updateTooltips() {
374
+ if (!geoJsonLayer) return; // Guard clause
375
+
376
+ const prop = controls.tooltipProperty.value;
377
+
378
+ geoJsonLayer.eachLayer((layer) => {
379
+ const content = layer.feature.properties[prop];
380
+ const tooltipContent = (content !== null && content !== undefined) ? String(content) : 'N/A';
381
+
382
+ layer.unbindTooltip();
383
+ layer.bindTooltip(tooltipContent);
384
+ });
385
+ }
386
+
387
+
388
+ // --- 6. DATA FETCHING AND LAYER CREATION ---
389
+
390
+ fetch(geoJsonUrl)
391
+ .then(response => {
392
+ if (!response.ok) {
393
+ throw new Error(`HTTP error! status: ${response.status}`);
394
+ }
395
+ return response.json();
396
+ })
397
+ .then(data => {
398
+ // Create the GeoJSON layer
399
+ geoJsonLayer = L.geoJSON(data, {
400
+ // style: styleFeature, // <-- REMOVED: Don't style until minMax is calculated
401
+ onEachFeature: (feature, layer) => {
402
+ // Bind initial tooltip
403
+ const prop = controls.tooltipProperty.value;
404
+ const content = feature.properties[prop];
405
+ const tooltipContent = (content !== null && content !== undefined) ? String(content) : 'N/A';
406
+ layer.bindTooltip(tooltipContent);
407
+ }
408
+ });
409
+
410
+ // Pre-calculate min/max for the default property
411
+ // Store minMax on the layer object for access in styleFeature
412
+ geoJsonLayer.minMax = {};
413
+ const defaultProp = controls.colorizeProperty.value;
414
+ geoJsonLayer.minMax[defaultProp] = getMinMax(defaultProp, data.features);
415
+
416
+ // NOW that the layer and minMax exist, apply the style
417
+ geoJsonLayer.setStyle(styleFeature);
418
+
419
+ // Add layer to the map and layer control
420
+ geoJsonLayer.addTo(map);
421
+ layerControl.addOverlay(geoJsonLayer, "Household Income layer");
422
+
423
+ // Now that the layer exists, apply the initial style
424
+ // updateStyle(); // <-- REMOVED: This is now redundant
425
+ })
426
+ .catch(error => {
427
+ console.error("Error fetching or processing GeoJSON:", error);
428
+ alert("Could not load GeoJSON data. See console for details.");
429
+ });
430
+
431
+
432
+ // --- 7. EVENT LISTENERS ---
433
+
434
+ // Add listeners to update style
435
+ controls.colorizeProperty.addEventListener('change', updateStyle);
436
+ controls.colormap.addEventListener('change', updateStyle);
437
+ controls.strokeColor.addEventListener('change', updateStyle);
438
+
439
+ // Add listener to update tooltips
440
+ controls.tooltipProperty.addEventListener('change', updateTooltips);
441
+
442
+ });
443
+ </script>
444
+ </body>
445
  </html>
446
+