Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Disaster Risk and Impact Analysis</title> | |
| <script src="static/js/deckgl@9.0.33.min.js"></script> | |
| <script src="static/js/maplibre-gl@3.0.0.js"></script> | |
| <link href="static/css/maplibre-gl@3.0.0.css" rel="stylesheet" /> | |
| <script src="static/js/tailwind@3.4.5.js"></script> | |
| <script src="static/js/chart.js"></script> | |
| <script src="static/js/popper.min.js"></script> | |
| <script src="static/js/tippy-bundle.umd.min.js"></script> | |
| <link rel="stylesheet" href="static/css/font-awesome@all.min.css" /> | |
| <style> | |
| html, | |
| body { | |
| height: 100%; | |
| margin: 0; | |
| } | |
| #map { | |
| width: 100%; | |
| height: 100%; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Floating Box for Charts --> | |
| <div | |
| class="absolute left-4 top-4 h-9/10 bottom-4 w-2/5 p-6 bg-white backdrop-blur-sm bg-opacity-60 shadow-lg overflow-y-auto z-10 rounded-2xl" | |
| > | |
| <h1 class="text-2xl font-bold mb-4 text-center">Location Insight</h1> | |
| <!-- STATISTICS --> | |
| <h1 class="text-l font-bold mb-4">Statistic</h1> | |
| <div class="flex flex-col space-y-4 w-full"> | |
| <div | |
| class="bg-white text-black p-6 rounded-xl shadow-md flex items-center justify-between" | |
| > | |
| <div class="flex items-center space-x-4"> | |
| <div class="icon bg-gray-200 p-2 rounded-full mr-4"> | |
| <img | |
| src="static/icon/pin.png" | |
| alt="Icon 2" | |
| class="h-6 w-6 rounded-full" | |
| /> | |
| </div> | |
| <h2 class="text-xl font-bold">Locations</h2> | |
| </div> | |
| <span class="text-2xl font-bold">104</span> | |
| </div> | |
| </div> | |
| <!-- LAYER CONTROL --> | |
| <h2 class="text-l font-bold mt-8 mb-4">Show Disaster Intensity</h2> | |
| <div id="boxContainer" class="grid grid-cols-3 gap-4 mt-4"> | |
| <div | |
| class="box flex items-center p-4 border rounded-lg cursor-pointer transition-all duration-300 bg-white" | |
| data-id="wind" | |
| > | |
| <div class="icon bg-gray-200 p-2 rounded-full mr-4"> | |
| <img | |
| src="static/icon/wind.png" | |
| alt="Icon 2" | |
| class="h-6 w-6 rounded-full" | |
| /> | |
| </div> | |
| <h1 class="text-lg font-semibold">Wind</h1> | |
| </div> | |
| <div | |
| class="box flex items-center p-4 bg-white border rounded-lg cursor-pointer transition-all duration-300" | |
| data-id="flood" | |
| > | |
| <div class="icon bg-gray-200 p-2 rounded-full mr-4"> | |
| <img | |
| src="static/icon/flood.png" | |
| alt="Icon 2" | |
| class="h-6 w-6 rounded-full" | |
| /> | |
| </div> | |
| <h1 class="text-lg font-semibold">Flood</h1> | |
| </div> | |
| <div | |
| class="box flex items-center p-4 bg-white border rounded-lg cursor-pointer transition-all duration-300" | |
| data-id="pm" | |
| > | |
| <div class="icon bg-gray-200 p-2 rounded-full mr-4"> | |
| <img | |
| src="static/icon/pm.png" | |
| alt="Icon 2" | |
| class="h-6 w-6 rounded-full" | |
| /> | |
| </div> | |
| <h1 class="text-lg font-semibold">Air Pollution</h1> | |
| </div> | |
| </div> | |
| <!-- Top 5 Location Impact to Wind --> | |
| <h2 class="text-l font-bold mt-8 mb-4"> | |
| Top 5 Locations Most Affected by Wind | |
| </h2> | |
| <div | |
| class="w-full max-w-4xl backdrop-blur-sm shadow-lg rounded-xl p-4 overflow-x-auto" | |
| > | |
| <table class="min-w-full table-auto"> | |
| <thead> | |
| <tr class="bg-gray-200 text-left"> | |
| <th class="p-4 font-bold text-gray-600">Top</th> | |
| <th class="p-4 font-bold text-gray-600">Branch</th> | |
| <th class="p-4 font-bold text-gray-600">Score</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr class="border-b cursor-pointer hover:bg-red-400 bg-red-300"> | |
| <td class="p-4">1</td> | |
| <td class="p-4">HomePro Phetchabun Branch</td> | |
| <td class="p-4">14.04</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-red-400 bg-red-200"> | |
| <td class="p-4">2</td> | |
| <td class="p-4">Home Pro Chiang Rai</td> | |
| <td class="p-4">13.52</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-red-400 bg-red-100"> | |
| <td class="p-4">3</td> | |
| <td class="p-4">HomePro Nakhon Si Thammarat</td> | |
| <td class="p-4">8.687</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-red-400"> | |
| <td class="p-4">4</td> | |
| <td class="p-4">Homepro Chaiyaphum</td> | |
| <td class="p-4">7.845</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-red-400"> | |
| <td class="p-4">5</td> | |
| <td class="p-4">HomePro (San Sai)</td> | |
| <td class="p-4">6.311</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- Top 5 Location Impact to Flood --> | |
| <h2 class="text-md font-bold mt-8 mb-4"> | |
| Top 5 Locations Most Affected by Flood | |
| </h2> | |
| <div | |
| class="w-full max-w-4xl backdrop-blur-sm shadow-lg rounded-xl p-4 overflow-x-auto" | |
| > | |
| <table class="min-w-full table-auto"> | |
| <thead> | |
| <tr class="bg-gray-200 text-left"> | |
| <th class="p-4 font-bold text-gray-600">Top</th> | |
| <th class="p-4 font-bold text-gray-600">Branch</th> | |
| <th class="p-4 font-bold text-gray-600">Score</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr class="border-b cursor-pointer hover:bg-blue-400 bg-blue-300"> | |
| <td class="p-4">1</td> | |
| <td class="p-4">HomePro Nakhon Si Thammarat</td> | |
| <td class="p-4">8.687</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-blue-400 bg-blue-200"> | |
| <td class="p-4">2</td> | |
| <td class="p-4">HomePro (San Sai)</td> | |
| <td class="p-4">6.311</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-blue-400 bg-blue-100"> | |
| <td class="p-4">3</td> | |
| <td class="p-4">Home Pro (Chiang Mai)</td> | |
| <td class="p-4">8.472</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-blue-400"> | |
| <td class="p-4">4</td> | |
| <td class="p-4">Home Pro (Chiang Mai Hangdong)</td> | |
| <td class="p-4">7.006</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-blue-400"> | |
| <td class="p-4">5</td> | |
| <td class="p-4">HomePro </td> | |
| <td class="p-4">4.392</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- Top 5 Location Impact to PM --> | |
| <h2 class="text-l font-bold mt-8 mb-4"> | |
| Top 5 Locations Most Affected by Air Pollution | |
| </h2> | |
| <div | |
| class="w-full max-w-4xl backdrop-blur-sm shadow-lg rounded-xl p-4 overflow-x-auto" | |
| > | |
| <table class="min-w-full table-auto"> | |
| <thead> | |
| <tr class="bg-gray-200 text-left"> | |
| <th class="p-4 font-bold text-gray-600">Top</th> | |
| <th class="p-4 font-bold text-gray-600">Branch</th> | |
| <th class="p-4 font-bold text-gray-600">Score</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| <tr class="border-b cursor-pointer hover:bg-gray-400 bg-gray-300"> | |
| <td class="p-4">1</td> | |
| <td class="p-4">HomePro </td> | |
| <td class="p-4">4.392</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-gray-400 bg-gray-200"> | |
| <td class="p-4">2</td> | |
| <td class="p-4">Home Pro (Ayutthaya)</td> | |
| <td class="p-4">1.467</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-gray-400 bg-gray-100"> | |
| <td class="p-4">3</td> | |
| <td class="p-4">Home Pro the distribution center</td> | |
| <td class="p-4">1.130</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-gray-400"> | |
| <td class="p-4">4</td> | |
| <td class="p-4">HomePro Rangsit Klong Si</td> | |
| <td class="p-4">1.277</td> | |
| </tr> | |
| <tr class="border-b cursor-pointer hover:bg-gray-400"> | |
| <td class="p-4">5</td> | |
| <td class="p-4">Home Pro (Future Park Rangsit)</td> | |
| <td class="p-4">1.379</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Fullscreen Map --> | |
| <div id="map"></div> | |
| <!--img | |
| src="static/icon/logo.png" | |
| alt="Top-right image" | |
| class="absolute top-0 right-0 w-48 p-4" | |
| /--> | |
| <a href="index.html"> | |
| <img | |
| src="static/icon/home.png" | |
| id="goHome" | |
| class="absolute bg-white backdrop-blur-sm bg-opacity-60 rounded-xl shadow-lg top-4 right-4 w-12 p-4 hover:bg-opacity-100 hover:cursor-pointer" | |
| /> | |
| </a> | |
| <a href="analytics.html"> | |
| <img | |
| src="static/icon/radar.png" | |
| id="goOverView" | |
| class="absolute bg-white backdrop-blur-sm bg-opacity-60 rounded-xl shadow-lg top-20 right-4 w-12 p-4 hover:bg-opacity-100 hover:cursor-pointer" | |
| /> | |
| </a> | |
| <a href="locations.html"> | |
| <img | |
| src="static/icon/location.png" | |
| id="goLocation" | |
| class="absolute bg-white backdrop-blur-sm bg-opacity-60 rounded-xl shadow-lg top-36 right-4 w-12 p-4 hover:bg-opacity-100 hover:cursor-pointer" | |
| /> | |
| </a> | |
| <a href="forecast.html"> | |
| <img | |
| src="static/icon/forecast.png" | |
| id="goForecast" | |
| class="absolute bg-white backdrop-blur-sm bg-opacity-60 rounded-xl shadow-lg top-52 right-4 w-12 p-4 hover:bg-opacity-100 hover:cursor-pointer" | |
| /> | |
| </a> | |
| <img | |
| src="static/icon/extent.png" | |
| id="resetViewButton" | |
| class="absolute bg-white backdrop-blur-sm bg-opacity-60 rounded-xl shadow-lg bottom-12 right-4 w-12 p-4 hover:bg-opacity-100 hover:cursor-pointer" | |
| /> | |
| <img | |
| src="static/icon/play.png" | |
| id="animateButton" | |
| class="absolute bg-white backdrop-blur-sm bg-opacity-60 rounded-xl shadow-lg bottom-28 right-4 w-12 p-4 hover:bg-opacity-100 hover:cursor-pointer" | |
| /> | |
| <script> | |
| let INIT_VIEW_STATE = { | |
| longitude: 98.1339, | |
| latitude: 10.863, | |
| zoom: 5.594, | |
| pitch: 48.3549, | |
| bearing: -17.579, | |
| }; | |
| var viewState = INIT_VIEW_STATE; | |
| const firm_pin = new deck.IconLayer({ | |
| id: "IconLayerFIRM", | |
| data: "data/firm/location_homepro_do_2018-2024.json", | |
| getColor: (d) => [Math.sqrt(d.exits), 140, 0], | |
| getIcon: (d) => "marker", | |
| getPosition: (d) => [d.lon, d.lat], | |
| getSize: 24, | |
| iconAtlas: | |
| "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/icon-atlas.png", | |
| iconMapping: | |
| "https://raw.githubusercontent.com/visgl/deck.gl-data/master/website/icon-atlas.json", | |
| pickable: false, | |
| }); | |
| // LAYER | |
| /* | |
| const firm_loc = new deck.HexagonLayer({ | |
| id: "HexagonLayer", | |
| data: "data/firm/homeprto_disaster_firm_location_forecast.json", | |
| //extruded: true, | |
| getPosition: (d) => [d.lon, d.lat], | |
| getColorWeight: (d) => d.wind_2021, | |
| getElevationWeight: (d) => d.wind_2021, | |
| elevationScale: 10, | |
| colorRange: [ | |
| [255, 255, 178, 200], | |
| [254, 217, 118, 200], | |
| [254, 178, 76, 200], | |
| [253, 141, 60, 200], | |
| [240, 59, 32, 200], | |
| [189, 0, 38, 200], | |
| ], | |
| colorScaleType: "quantile", | |
| radius: 5000, | |
| pickable: true, | |
| elevationAggregation: "MEAN", | |
| colorAggregation: "MEAN", | |
| });*/ | |
| const province_boundary = new deck.GeoJsonLayer({ | |
| id: "GeoJsonLayer", | |
| data: "data/common/province_admin_diva_sim.geojson", | |
| pickable: false, | |
| getLineColor: [0, 0, 0], | |
| getLineWidth: 200, | |
| getFillColor: [255, 255, 255, 0], | |
| }); | |
| // Initialize Deck.GL | |
| const deckInstance = new deck.DeckGL({ | |
| initialViewState: INIT_VIEW_STATE, | |
| container: "map", | |
| mapStyle: | |
| "https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json", | |
| controller: { doubleClickZoom: true, inertia: true }, | |
| onViewStateChange: (e) => { | |
| viewState = e.viewState; | |
| //console.log(viewState); | |
| }, | |
| getTooltip, | |
| layers: [province_boundary, firm_pin], | |
| }); | |
| function getTooltip({ object }) { | |
| return ( | |
| object && { | |
| html: `<h2 class="text-black">Disaster Index: ${object.colorValue}</h2>`, | |
| className: "rounded-lg shadow-lg", | |
| style: { | |
| backgroundColor: "#fff", | |
| fontSize: "0.8em", | |
| }, | |
| } | |
| ); | |
| } | |
| function rotateCameraRight() { | |
| initialViewState = { | |
| ...viewState, | |
| bearing: viewState.bearing - 20, | |
| transitionDuration: 10000, | |
| transitionInterpolator: new deck.LinearInterpolator(["bearing"]), | |
| onTransitionEnd: rotateCameraLeft, | |
| }; | |
| deckInstance.setProps({ initialViewState }); | |
| } | |
| function rotateCameraLeft() { | |
| initialViewState = { | |
| ...viewState, | |
| bearing: viewState.bearing + 20, | |
| transitionDuration: 10000, | |
| transitionInterpolator: new deck.LinearInterpolator(["bearing"]), | |
| onTransitionEnd: rotateCameraRight, | |
| }; | |
| deckInstance.setProps({ initialViewState }); | |
| } | |
| function resetCameraPosition() { | |
| deckInstance.setProps({ | |
| initialViewState: { | |
| ...INIT_VIEW_STATE, | |
| transitionInterpolator: new deck.FlyToInterpolator({ speed: 2 }), | |
| transitionDuration: "auto", | |
| }, | |
| }); | |
| } | |
| async function updateMap() { | |
| console.log("update Map"); | |
| const selectedBoxId = getSelectedBox(); | |
| //console.log('Currently Selected Box ID:', selectedBoxId); | |
| if (selectedBoxId == "wind") { | |
| console.log(" Do something to Wind"); | |
| deckInstance.setProps({ | |
| layers: [ | |
| province_boundary, | |
| new deck.HexagonLayer({ | |
| id: "HexagonLayer", | |
| data: "data/firm/homeprto_disaster_firm_location_forecast.json", | |
| //extruded: true, | |
| getPosition: (d) => [d.lon, d.lat], | |
| getColorWeight: (d) => d.wind_2023, | |
| getElevationWeight: (d) => d.wind_2023, | |
| elevationScale: 10, | |
| colorRange: [ | |
| [255, 255, 178, 200], | |
| [254, 217, 118, 200], | |
| [254, 178, 76, 200], | |
| [253, 141, 60, 200], | |
| [240, 59, 32, 200], | |
| [189, 0, 38, 200], | |
| ], | |
| colorScaleType: "quantile", | |
| radius: 5000, | |
| pickable: true, | |
| elevationAggregation: "MEAN", | |
| colorAggregation: "MEAN", | |
| }), | |
| firm_pin, | |
| ], | |
| getTooltip, | |
| }); | |
| } else if (selectedBoxId == "flood") { | |
| console.log(" Do something to Flood"); | |
| deckInstance.setProps({ | |
| layers: [ | |
| province_boundary, | |
| new deck.HexagonLayer({ | |
| id: "HexagonLayer", | |
| data: "data/firm/homeprto_disaster_firm_location_forecast.json", | |
| //extruded: true, | |
| getPosition: (d) => [d.lon, d.lat], | |
| getColorWeight: (d) => d.flood_2023, | |
| getElevationWeight: (d) => d.flood_2023, | |
| elevationScale: 10, | |
| colorRange: [ | |
| [242, 240, 247, 200], | |
| [218, 218, 235, 200], | |
| [188, 189, 220, 200], | |
| [158, 154, 200, 200], | |
| [117, 107, 177, 200], | |
| [84, 39, 143, 200], | |
| , | |
| ], | |
| colorScaleType: "quantile", | |
| radius: 5000, | |
| pickable: true, | |
| elevationAggregation: "MEAN", | |
| colorAggregation: "MEAN", | |
| }), | |
| firm_pin, | |
| ], | |
| getTooltip, | |
| }); | |
| } else if (selectedBoxId == "pm") { | |
| console.log(" Do something to PM"); | |
| deckInstance.setProps({ | |
| layers: [ | |
| province_boundary, | |
| new deck.HexagonLayer({ | |
| id: "HexagonLayer", | |
| data: "data/firm/homeprto_disaster_firm_location_forecast.json", | |
| //extruded: true, | |
| getPosition: (d) => [d.lon, d.lat], | |
| getColorWeight: (d) => d.pm_2023, | |
| getElevationWeight: (d) => d.pm_2023, | |
| elevationScale: 10, | |
| colorRange: [ | |
| [247, 247, 247, 200], | |
| [217, 217, 217, 200], | |
| [189, 189, 189, 200], | |
| [150, 150, 150, 200], | |
| [99, 99, 99, 200], | |
| [37, 37, 37, 200], | |
| ], | |
| colorScaleType: "quantile", | |
| radius: 5000, | |
| pickable: true, | |
| elevationAggregation: "MEAN", | |
| colorAggregation: "MEAN", | |
| }), | |
| firm_pin, | |
| ], | |
| getTooltip, | |
| }); | |
| } | |
| // refresh again after 60 seconds | |
| setTimeout(updateMap, 60000); | |
| } | |
| const resetButton = document.querySelector("#resetViewButton"); | |
| resetButton.addEventListener("click", () => { | |
| resetCameraPosition(); | |
| }); | |
| tippy("#resetViewButton", { | |
| content: "Reset View", | |
| placement: "left", | |
| }); | |
| const animateButton = document.querySelector("#animateButton"); | |
| animateButton.addEventListener("click", () => { | |
| rotateCameraLeft(); | |
| }); | |
| tippy("#animateButton", { | |
| content: "Animate View", | |
| placement: "left", | |
| }); | |
| tippy("#goOverView", { | |
| content: "FIRM Overview", | |
| placement: "left", | |
| }); | |
| tippy("#goLocation", { | |
| content: "Location-Based", | |
| placement: "left", | |
| }); | |
| tippy("#goForecast", { | |
| content: "Forecasting", | |
| placement: "left", | |
| }); | |
| const boxes = document.querySelectorAll(".box"); | |
| boxes.forEach((box) => { | |
| box.addEventListener("click", function () { | |
| // Remove 'selected' class from all boxes | |
| boxes.forEach((b) => | |
| b.classList.remove("selected", "bg-sky-500", "text-white") | |
| ); | |
| // Add 'selected' class to the clicked box | |
| this.classList.add("selected", "bg-sky-500", "text-white"); | |
| updateMap(); | |
| }); | |
| }); | |
| function getSelectedBox() { | |
| const selectedBox = document.querySelector(".box.selected"); | |
| if (selectedBox) { | |
| return selectedBox.getAttribute("data-id"); // Return the selected box's data-id | |
| } | |
| return null; | |
| } | |
| </script> | |
| <script> | |
| // First Chart.js Configuration | |
| </script> | |
| </body> | |
| </html> | |