Astro 5 SSR + ioredis cache Valkey, déployable sur shared-net. - Vigilance temps réel via Opendatasoft (no-auth, LOv2) - Carte SVG des 96 départements (gregoiredavid/france-geojson) - Climato T° 30j par dept (CSV.GZ Météo France, cache 24h) - Conseils officiels par phénomène (7 types Vigilance) - /api/health (UptimeRobot) + /api/vigilance (JSON public CORS *) - Dockerfile multi-stage, CI Forgejo deploy.yml (pattern Reteno) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
3.4 KiB
JavaScript
111 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
// Génère un SVG paths data pour la carte des départements français.
|
|
// Source GeoJSON : github.com/gregoiredavid/france-geojson (Licence Ouverte v2.0).
|
|
// Projection : Mercator approchée. Suffisant pour visualisation, pas pour cartographie précise.
|
|
//
|
|
// Usage : node scripts/build-france-map.mjs
|
|
// Output : src/data/france-map.json (paths par code INSEE, viewBox)
|
|
|
|
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const INPUT = resolve(__dirname, '../data-sources/departements-simplifie.geojson');
|
|
const OUTPUT = resolve(__dirname, '../src/data/france-map.json');
|
|
|
|
const W = 1000;
|
|
const H = 1000;
|
|
|
|
function project(lng, lat) {
|
|
// Mercator (lng/lat en degrés → coordonnées planes en radians)
|
|
const x = (lng * Math.PI) / 180;
|
|
const y = Math.log(Math.tan(Math.PI / 4 + (lat * Math.PI) / 180 / 2));
|
|
return [x, y];
|
|
}
|
|
|
|
function ringToPath(ring) {
|
|
return ring
|
|
.map(([lng, lat], i) => {
|
|
const [x, y] = project(lng, lat);
|
|
return `${i === 0 ? 'M' : 'L'}${x.toFixed(5)},${y.toFixed(5)}`;
|
|
})
|
|
.join('') + 'Z';
|
|
}
|
|
|
|
function geometryToPath(geom) {
|
|
if (geom.type === 'Polygon') {
|
|
return geom.coordinates.map(ringToPath).join('');
|
|
}
|
|
if (geom.type === 'MultiPolygon') {
|
|
return geom.coordinates.map((poly) => poly.map(ringToPath).join('')).join('');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
const geojson = JSON.parse(readFileSync(INPUT, 'utf-8'));
|
|
|
|
// First pass: compute bounding box on projected coords.
|
|
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
for (const f of geojson.features) {
|
|
const polys = f.geometry.type === 'Polygon' ? [f.geometry.coordinates] : f.geometry.coordinates;
|
|
for (const poly of polys) {
|
|
for (const ring of poly) {
|
|
for (const [lng, lat] of ring) {
|
|
const [x, y] = project(lng, lat);
|
|
if (x < minX) minX = x;
|
|
if (x > maxX) maxX = x;
|
|
if (y < minY) minY = y;
|
|
if (y > maxY) maxY = y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const scale = Math.min(W / (maxX - minX), H / (maxY - minY));
|
|
const offsetX = -minX * scale;
|
|
const offsetY = maxY * scale; // invert Y (SVG top-down)
|
|
|
|
function scaledRingToPath(ring) {
|
|
return ring
|
|
.map(([lng, lat], i) => {
|
|
const [x, y] = project(lng, lat);
|
|
const sx = (x * scale + offsetX).toFixed(2);
|
|
const sy = (offsetY - y * scale).toFixed(2);
|
|
return `${i === 0 ? 'M' : 'L'}${sx},${sy}`;
|
|
})
|
|
.join('') + 'Z';
|
|
}
|
|
|
|
function scaledGeometryToPath(geom) {
|
|
if (geom.type === 'Polygon') {
|
|
return geom.coordinates.map(scaledRingToPath).join('');
|
|
}
|
|
if (geom.type === 'MultiPolygon') {
|
|
return geom.coordinates.map((poly) => poly.map(scaledRingToPath).join('')).join('');
|
|
}
|
|
return '';
|
|
}
|
|
|
|
const paths = {};
|
|
for (const f of geojson.features) {
|
|
const code = String(f.properties.code);
|
|
paths[code] = {
|
|
d: scaledGeometryToPath(f.geometry),
|
|
name: f.properties.nom,
|
|
};
|
|
}
|
|
|
|
const finalW = (maxX - minX) * scale;
|
|
const finalH = (maxY - minY) * scale;
|
|
|
|
const output = {
|
|
viewBox: `0 0 ${finalW.toFixed(2)} ${finalH.toFixed(2)}`,
|
|
width: finalW.toFixed(0),
|
|
height: finalH.toFixed(0),
|
|
paths,
|
|
};
|
|
|
|
mkdirSync(dirname(OUTPUT), { recursive: true });
|
|
writeFileSync(OUTPUT, JSON.stringify(output));
|
|
console.log(`Wrote ${OUTPUT} — ${Object.keys(paths).length} départements, viewBox ${output.viewBox}`);
|