This commit is contained in:
Felix Delattre 2026-04-11 19:25:42 +02:00
parent ec3aac3ec3
commit db14a71228
7 changed files with 374 additions and 108 deletions

View file

@ -5,6 +5,7 @@
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdn.jsdelivr.net/npm/geotiff@2.0.7/dist-browser/geotiff.js"></script>
<script src="common.js"></script>
<script src="https://cdn.jsdelivr.net/npm/proj4@2.9.0/dist/proj4.js"></script>
<style>
body { margin: 0; font-family: sans-serif; }
@ -42,6 +43,7 @@
<a href="prepared.html">Prepared</a>
<a href="fusion.html">Fusion</a>
<a href="postprocessed.html" class="active">Postprocessed</a>
<a href="metrics.html">Metrics</a>
</div>
<h1 id="siteName">Innsbruck</h1>
<div class="season-row"><h2 id="season">2024</h2><span class="download-links" id="downloadLinks"></span></div>
@ -66,6 +68,11 @@
<option value="fusion">Fusion</option>
<option value="s3">S3</option>
</select>
<label>Mode:</label>
<select id="fusionModeSelect" title="BtI vs ItB processed paths">
<option value="bti">BtI</option>
<option value="itb">ItB</option>
</select>
</div>
<input type="range" id="dateSlider" min="0" max="365" value="0">
<div id="dateDisplay">2024-01-01</div>
@ -87,7 +94,7 @@
proj4.defs("EPSG:4326", "+proj=longlat +datum=WGS84 +no_defs");
let siteName = "innsbruck", season = "2024";
let strategy = "aggressive", sigma = "20", source = "s2";
let strategy = "aggressive", sigma = "20", source = "s2", fusionMode = "bti";
let sitePosition = [47.116171, 11.320308];
let start = new Date(2024, 0, 1);
let availableSiteSeasons = {};
@ -105,18 +112,26 @@
};
function getProcessedPath() {
return `data/${siteName}/${season}/processed_${strategy}_sigma${sigma}`;
const mid = fusionMode === "itb" ? `processed_${strategy}_itb_sigma${sigma}` : `processed_${strategy}_sigma${sigma}`;
return `data/${siteName}/${season}/${mid}`;
}
async function loadTimeseries() {
const base = getProcessedPath();
try {
const [n, g, b] = await Promise.all([
fetch(`${getProcessedPath()}/ndvi/${source}/timeseries.json`).then(r => r.ok ? r.json() : []),
fetch(`${getProcessedPath()}/gcc/${source}/timeseries.json`).then(r => r.ok ? r.json() : []),
fetch(`${getProcessedPath()}/bands/${source}/timeseries.json`).then(r => r.ok ? r.json() : [])
fetch(`${base}/ndvi/${source}/timeseries.json`).then((r) => (r.ok ? r.json() : [])),
fetch(`${base}/gcc/${source}/timeseries.json`).then((r) => (r.ok ? r.json() : [])),
fetch(`${base}/bands/${source}/timeseries.json`).then((r) => (r.ok ? r.json() : [])),
]);
ndviTs = n; gccTs = g; bandsTs = b;
} catch { ndviTs = []; gccTs = []; bandsTs = []; }
ndviTs = n;
gccTs = g;
bandsTs = b;
} catch {
ndviTs = [];
gccTs = [];
bandsTs = [];
}
drawPlots();
updateDownloadLinks();
}
@ -167,7 +182,12 @@
function updateDownloadLinks() {
const el = document.getElementById("downloadLinks");
if (!el) return;
const base = `data/${siteName}/${season}/processed_${strategy}_sigma${sigma}/export/${source}`;
const root = getProcessedPath();
if (fusionMode === "itb") {
el.innerHTML = `<a href="${root}/gcc/${source}/timeseries.json">[GCC JSON]</a>`;
return;
}
const base = `${root}/export/${source}`;
const name = `${siteName}_${season}_postprocessed_${strategy}_sigma${sigma}_${source}`;
el.innerHTML = `<a href="${base}/timeseries.json" download="${name}.json">[JSON]</a><a href="${base}/timeseries.csv" download="${name}.csv">[CSV]</a>`;
}
@ -200,34 +220,11 @@
async function loadGeotiff(filename) {
const path = `${getProcessedPath()}/${source}/${filename}`;
const tiff = await GeoTIFF.fromArrayBuffer(await (await fetch(path)).arrayBuffer());
const image = await tiff.getImage();
const rasters = await image.readRasters();
const width = image.getWidth(), height = image.getHeight();
const bbox = image.getBoundingBox();
const geoKeys = image.getGeoKeys();
const crsCode = geoKeys.ProjectedCSTypeGeoKey ? `EPSG:${geoKeys.ProjectedCSTypeGeoKey}` :
(geoKeys.GeographicTypeGeoKey !== 4326 ? `EPSG:${geoKeys.GeographicTypeGeoKey}` : "EPSG:4326");
const [blue, green, red] = [0, 1, 2].map(i => Array.from(rasters[i]));
const normalize = (arr) => {
let min = Infinity, max = -Infinity;
for (const v of arr) if (!isNaN(v) && v > 0) { min = Math.min(min, v); max = Math.max(max, v); }
return arr.map(v => Math.max(0, Math.min(255, ((v - min) / (max - min || 1)) * 255)));
};
const [rN, gN, bN] = [red, green, blue].map(normalize);
const canvas = Object.assign(document.createElement("canvas"), { width, height });
const ctx = canvas.getContext("2d");
ctx.imageSmoothingEnabled = false;
const imgData = ctx.createImageData(width, height);
for (let i = 0; i < rN.length; i++) {
const idx = i * 4;
if (rN[i] === 0 && gN[i] === 0 && bN[i] === 0) imgData.data[idx + 3] = 0;
else { imgData.data[idx] = rN[i]; imgData.data[idx + 1] = gN[i]; imgData.data[idx + 2] = bN[i]; imgData.data[idx + 3] = 255; }
}
ctx.putImageData(imgData, 0, 0);
const buf = await (await fetch(path)).arrayBuffer();
const { dataUrl, bbox, crsCode } = await geotiffToCanvasDataUrl(buf);
const bounds = crsCode === "EPSG:4326" ? [[bbox[1], bbox[0]], [bbox[3], bbox[2]]] : transformBounds(bbox, crsCode);
const dateStr = filename.replace("_0.geotiff", "");
return { dataUrl: canvas.toDataURL(), bounds, dateStr };
return { dataUrl, bounds, dateStr };
}
async function updateMap() {
@ -278,6 +275,7 @@
const params = new URLSearchParams(location.search);
params.set("site", siteName);
params.set("season", season);
params.set("mode", fusionMode);
history.replaceState({}, "", `?${params}`);
const urlDate = params.get("date");
if (urlDate) document.getElementById("dateSlider").value = daysFromDate(urlDate);
@ -326,9 +324,11 @@
strategy = urlParams.get("strategy") || "aggressive";
sigma = urlParams.get("sigma") || "20";
source = urlParams.get("source") || "s2";
fusionMode = urlParams.get("mode") === "itb" ? "itb" : "bti";
document.getElementById("strategySelect").value = strategy;
document.getElementById("sigmaSelect").value = sigma;
document.getElementById("sourceSelect").value = source;
document.getElementById("fusionModeSelect").value = fusionMode;
const initSite = getSiteBySitename(initialSite);
if (initSite?.geometry?.coordinates) {
@ -367,6 +367,12 @@
history.replaceState({}, "", `?${urlParams}`);
loadTimeseries(); updateMap();
});
document.getElementById("fusionModeSelect").addEventListener("change", function() {
fusionMode = this.value;
urlParams.set("mode", fusionMode);
history.replaceState({}, "", `?${urlParams}`);
loadTimeseries(); updateMap();
});
await setSiteSeason(initialSite, initialSeason);
}