diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 74e28f14..3a22c32c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,4 +1,4 @@ -name: Build, Test, and Push Docker Image +name: Docker on: push: diff --git a/README.md b/README.md index 4b816ea6..50ebc158 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,18 @@ --- + +[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![DOI](https://img.shields.io/badge/DOI-10.14195/978--989--26--0884--6_29-blue)](https://www.researchgate.net/publication/278769168_ForeFire_open-source_code_for_wildland_fire_spread_models) + [![linuxCI](https://github.com/forefireAPI/forefire/actions/workflows/main.yml/badge.svg)](https://github.com/forefireAPI/forefire/actions/workflows/main.yml) [![macOSCI](https://github.com/forefireAPI/forefire/actions/workflows/macos.yml/badge.svg)](https://github.com/forefireAPI/forefire/actions/workflows/macos.yml) -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![Docker CI/CD](https://github.com/forefireAPI/forefire/actions/workflows/docker.yml/badge.svg)](https://github.com/forefireAPI/forefire/actions/workflows/docker.yml) +[![Documentation Status](https://readthedocs.org/projects/forefire/badge/?version=latest)](https://forefire.readthedocs.io/en/latest/?badge=latest) + +[![Docker Package](https://img.shields.io/badge/Docker-Package-blue?logo=docker&logoColor=white)](https://github.com/forefireAPI/forefire/pkgs/container/forefire) ![Language](https://img.shields.io/badge/C++-00599C?logo=c%2B%2B&logoColor=white) ![Language](https://img.shields.io/badge/Python-3776AB?logo=python&logoColor=white) -[![Documentation Status](https://readthedocs.org/projects/forefire/badge/?version=latest)](https://forefire.readthedocs.io/en/latest/?badge=latest) -[![DOI](https://img.shields.io/badge/DOI-10.14195/978--989--26--0884--6_29-blue)](https://www.researchgate.net/publication/278769168_ForeFire_open-source_code_for_wildland_fire_spread_models) **ForeFire** is an open-source **wildfire simulation engine** written in C++. Developed by CNRS at the [Université de Corse Pascal Paoli](https://www.univ-corse.fr/), it is used for research and operational forecasting. The engine implements various fire behavior models and enables high-fidelity coupled fire-atmosphere simulations, aiming to improve wildfire prediction and understanding for complex environments. @@ -80,9 +85,11 @@ The easiest way to get started is often using Docker and the interactive console This server provides a graphical user interface that you can access on your browser at http://localhost:8000/ 6. Run your first simulation - - Run the command `include[real_case.ff]` - - Then press Refresh Map - + + In ForeFire, running a simulation and viewing the result are separate commands. The UI guides you through this process. + - **Step 1: Run the simulation script.** In the command input box, type `include[real_case.ff]` and click the **`Send`** button. The simulation will run on the server. + - **Step 2: View the result.** After the command finishes, click the **`Refresh Map`** button to load the simulation results onto the map. + You should see a simulation running in the Aullène region of Corsica. **This confirms your Docker setup is working!** Check the full documentation for more details on this example ## Build from source diff --git a/docs/source/_static/images/gui_real_case_ff.jpg b/docs/source/_static/images/gui_real_case_ff.jpg index 47628313..b2442405 100644 Binary files a/docs/source/_static/images/gui_real_case_ff.jpg and b/docs/source/_static/images/gui_real_case_ff.jpg differ diff --git a/docs/source/getting_started/quickstart.rst b/docs/source/getting_started/quickstart.rst index 6c57a130..2a575b13 100644 --- a/docs/source/getting_started/quickstart.rst +++ b/docs/source/getting_started/quickstart.rst @@ -66,9 +66,11 @@ Steps 8. **Run a Simulation:** - * In the web console's command input box, type: `include[real_case.ff]` and press Enter or click Send. - * Click the "Refresh Map" button. + In ForeFire, running a simulation and viewing the result are separate commands. The UI guides you through this. + * **Step 1: Run the simulation script.** In the command input box, type `include[real_case.ff]` and click the **`Send`** button. The simulation will run on the server. + * **Step 2: View the result.** After the command finishes, click the **`Refresh Map`** button to load the simulation results onto the map. + You should see a simulation running in the Aullène region of Corsica. .. image:: /_static/images/gui_real_case_ff.jpg diff --git a/src/HttpCommandServer.hpp b/src/HttpCommandServer.hpp index 29a4b97f..5708462a 100644 --- a/src/HttpCommandServer.hpp +++ b/src/HttpCommandServer.hpp @@ -291,12 +291,14 @@ namespace http_command { return buildResponse("200 OK", "text/html; charset=UTF-8", body); } else { if (!fileExists(path)) { - cout << "File not found: " << path << std::endl; if (const char* ffHome = std::getenv("FOREFIREHOME")) { std::string altPath = std::string(ffHome) +"/tools/htdocs/"+ path; - if (fileExists(altPath)) + if (fileExists(altPath)){ path = altPath; + } + } else { + cout << "FOREFIREHOME not set. File not found: " << path << std::endl; } } if (fileExists(path)) { diff --git a/tools/htdocs/index.html b/tools/htdocs/index.html index c26ce5fd..0b16b192 100644 --- a/tools/htdocs/index.html +++ b/tools/htdocs/index.html @@ -7,165 +7,17 @@ -
@@ -213,33 +65,5 @@
- - - - - - - - - - \ No newline at end of file diff --git a/tools/htdocs/js/forefireGUI.js b/tools/htdocs/js/forefireGUI.js index 356709c0..c336ab2e 100644 --- a/tools/htdocs/js/forefireGUI.js +++ b/tools/htdocs/js/forefireGUI.js @@ -1,5 +1,6 @@ // Updated list of commands. const commands = { + "include": "include[real_case.ff]", "FireDomain": "FireDomain[sw=(0.0,0.0,0.0);ne=(100.0,100.0,0.0);t=0.0]", "FireNode": "FireNode[loc=(0.0,0.0,0.0);vel=(0.0,0.0,0.0);t=0.]", "FireFront": "FireFront[]", @@ -15,7 +16,6 @@ const commands = { "setParameter": "setParameter[param=value]", "setParameters": "setParameters[param1=val1;param2=val2]", "getParameter": "getParameter[paramNames]", - "include": "include[run.ff]", "loadData": "loadData[data.nc;2024-12-13T15:41:33Z]", "clear": "clear[]", "systemExec": "systemExec[ls]", @@ -256,19 +256,6 @@ function updateMapWithGeoJSON(geojson, refit = true) { } } - document.getElementById('refreshMap').addEventListener('click', async () => { - await sendCommand("setParameter[dumpMode=geojson]"); - const geojsonText = await sendCommand("print[]"); - if (geojsonText) { - try { - const geojson = JSON.parse(geojsonText); - updateMapWithGeoJSON(geojson); - } catch (e) { - console.error("Failed to parse GeoJSON:", e); - } - } - }); - document.getElementById('sendCommand').addEventListener('click', () => { const command = document.getElementById('commandInput').value.trim(); if (command !== "") { @@ -453,14 +440,14 @@ function updateMapWithGeoJSON(geojson, refit = true) { let autoRefreshInterval = null; // 1) Factor out the refresh logic into a separate function: -async function refreshMap() { +async function refreshMap(refit = true) { // The same logic you had in the 'refreshMap' button’s click handler await sendCommand("setParameter[dumpMode=geojson]"); const geojsonText = await sendCommand("print[]"); if (geojsonText) { try { const geojson = JSON.parse(geojsonText); - updateMapWithGeoJSON(geojson, false); + updateMapWithGeoJSON(geojson, refit); } catch (e) { console.error("Failed to parse GeoJSON:", e); } @@ -510,8 +497,10 @@ document.getElementById('refreshMap').addEventListener('click', refreshMap); // 3) Handle the auto-refresh checkbox changes: document.getElementById('autoRefreshCheckbox').addEventListener('change', (e) => { if (e.target.checked) { - // Start auto-refresh every 10 seconds (adjust as needed) - autoRefreshInterval = setInterval(refreshMap, 500); + // Start auto-refresh (adjust as needed) + autoRefreshInterval = setInterval(() => { + refreshMap(false); + }, 500); } else { // Stop auto-refresh clearInterval(autoRefreshInterval); @@ -521,4 +510,22 @@ document.getElementById('autoRefreshCheckbox').addEventListener('change', (e) => document.getElementById('WindOrParts').addEventListener('change', (e) => { setWindMapUseVector(e.target.checked) +}); + +const toggleLogs = document.getElementById('toggleLogs'); +const responseConsole = document.getElementById('responseConsole'); +const commandHistory = document.getElementById('commandHistory'); + +toggleLogs.addEventListener('change', function() { + const displayStyle = this.checked ? 'block' : 'none'; + responseConsole.style.display = displayStyle; + commandHistory.style.display = displayStyle; + + // Optional: Adjust map height if needed when logs are hidden + // For example, increase the map height when logs are hidden + if (!this.checked) { + document.getElementById('map').style.height = '700px'; + } else { + document.getElementById('map').style.height = '500px'; + } }); \ No newline at end of file diff --git a/tools/htdocs/style.css b/tools/htdocs/style.css new file mode 100644 index 00000000..65f4a53a --- /dev/null +++ b/tools/htdocs/style.css @@ -0,0 +1,173 @@ +/* Global Styles */ +html, body { + margin: 0; + padding: 0; + height: 100%; + font-family: monospace; + background-color: #111; + color: #eee; +} +/* Main container as a flex row: sidebar + main area */ +#container { + display: flex; + height: 100vh; +} +/* Sidebar: left bar with layers list */ +#sidebar { + width: 200px; + background-color: #222; + padding: 10px; + display: flex; + flex-direction: column; + justify-content: space-between; +} +/* Logo container */ +#logo { + text-align: center; + margin-bottom: 10px; +} + +/* Logo image sizing */ +#logo img { + max-width: 100%; + height: auto; + display: block; +} + +#layerList { + list-style: none; + padding: 0; + margin: 0; + flex-grow: 1; + overflow-y: auto; +} +#layerList li { + margin: 5px 0; + padding: 5px; + cursor: pointer; + background-color: #333; +} +#layerList li:hover { + background-color: #444; +} +#updateLayers { + background-color: #444; + border: none; + color: #eee; + padding: 5px; + cursor: pointer; + margin-top: 10px; +} +/* Right side: main area */ +#mainArea { + flex-grow: 1; + display: flex; + flex-direction: column; +} +/* Header: big orange date with toggle checkbox */ +#dateHeader { + background: #111; + text-align: center; + font-size: 2em; + color: orange; + padding: 10px; +} +#dateHeader label { + font-size: 0.5em; + color: #ccc; + margin-left: 10px; + cursor: pointer; +} +/* Response Console: server responses */ +#responseConsole { + background: #222; + padding: 10px; + height: 200px; + overflow-y: auto; +} +/* Command History: sent commands in green */ +#commandHistory { + background: #222; + padding: 10px; + height: 100px; + overflow-y: auto; + margin: 5px; + color: lightgreen; +} +.commandText { + margin: 2px 0; +} +/* Input Area: command input and buttons */ +#inputArea { + display: flex; + align-items: center; + height: 40px; + padding: 5px; + background-color: #111; +} +#inputArea input { + flex: 1; + padding: 5px; + font-size: 1em; + background-color: #333; + color: #eee; + border: 1px solid #444; +} +#inputArea button { + padding: 5px 10px; + background-color: #444; + color: #eee; + border: none; + cursor: pointer; + margin-left: 5px; +} +/* Predefined Command Buttons */ +#commandButtons { + margin: 5px; +} +#commandButtons button { + padding: 5px 10px; + margin: 2px; + background-color: #444; + color: #eee; + border: none; + cursor: pointer; + font-size: 0.9em; +} +#commandButtons button:hover { + background-color: #555; +} +/* Map container: fixed height to avoid layout issues with big images */ +#map { + height: 500px; + width: calc(100% - 10px); + margin: 5px; +} +/* Bounding box handle style */ +.bbox-handle { + background: red; + border-radius: 50%; + width: 8px; + height: 8px; + border: 1px solid #fff; +} +/* Container for the wind canvas */ +#windContainer { + margin: 5px; +} + +#refreshMap { + background-color: #5a995a !important; +} + +#refreshMap:hover { + background-color: #72be72 !important; +} + +#sendCommand { + background-color: darkorange !important; /* A clear, action-oriented orange */ +} + +#sendCommand:hover { + background-color: orange !important; /* A slightly darker orange for hover */ +} \ No newline at end of file