Amélioration avec des fonctions d'un vrai dashboard automotive et containerisation du projet pour faciliter le déploiement
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
export function distance(a, b) {
|
||||
const rad = Math.PI / 180, lat = (b.lat - a.lat) * rad, lon = (b.lon - a.lon) * rad;
|
||||
const h = Math.sin(lat / 2) ** 2 + Math.cos(a.lat * rad) * Math.cos(b.lat * rad) * Math.sin(lon / 2) ** 2;
|
||||
return 6371000 * 2 * Math.asin(Math.min(1, Math.sqrt(h)));
|
||||
}
|
||||
export function validateZones(data) {
|
||||
if (data?.type !== 'FeatureCollection' || !Array.isArray(data.features) || data.features.length > 1000) throw new Error('Fichier GeoJSON attendu, 1 000 zones maximum.');
|
||||
const ids = new Set();
|
||||
return data.features.map((f, i) => {
|
||||
const p = f.properties || {}, c = f.geometry?.coordinates;
|
||||
if (f.geometry?.type !== 'Point' || !Array.isArray(c) || c.length !== 2 || !c.every(Number.isFinite) || Math.abs(c[0]) > 180 || Math.abs(c[1]) > 85 || !Number.isFinite(p.radiusM) || p.radiusM < 300 || p.radiusM > 10000 || !/^[A-Z]{2}$/.test(p.country || '') || typeof p.source !== 'string' || !p.source.trim() || !Number.isFinite(Date.parse(p.updatedAt)) || !Number.isFinite(Date.parse(p.expiresAt)) || Date.parse(p.expiresAt) <= Date.parse(p.updatedAt)) throw new Error(`Zone ${i + 1} invalide : coordonnées, pays, rayon (300–10 000 m), source et dates requis.`);
|
||||
const id = String(p.id ?? i).slice(0, 100);
|
||||
if (ids.has(id)) throw new Error('Identifiants de zones en double.'); ids.add(id);
|
||||
return { id, lat: c[1], lon: c[0], radiusM: p.radiusM, country: p.country, name: String(p.name || 'Zone de vigilance').slice(0, 120), source: p.source.slice(0, 150), updatedAt: p.updatedAt, expiresAt: p.expiresAt };
|
||||
});
|
||||
}
|
||||
export function nearbyZones(zones, position, country, now = Date.now()) {
|
||||
if (!position || position.source !== 'gps' || !Number.isFinite(position.accuracy) || position.accuracy > 100 || !Number.isFinite(position.timestamp) || Math.abs(now - position.timestamp) > 15000) return [];
|
||||
return zones.filter(z => z.country === country && Date.parse(z.updatedAt) <= now && Date.parse(z.expiresAt) > now)
|
||||
.map(z => ({ ...z, remaining: Math.max(0, distance(position, z) - z.radiusM) }))
|
||||
.filter(z => z.remaining <= 400).sort((a, b) => a.remaining - b.remaining);
|
||||
}
|
||||
export function instruction(step) {
|
||||
const m = step.maneuver || {}, ways = { left: 'à gauche', right: 'à droite', 'slight left': 'légèrement à gauche', 'slight right': 'légèrement à droite', 'sharp left': 'franchement à gauche', 'sharp right': 'franchement à droite', straight: 'tout droit', uturn: 'en faisant demi-tour' };
|
||||
if (m.type === 'arrive') return 'Vous arrivez à destination';
|
||||
if (m.type === 'depart') return `Départ${step.name ? ` sur ${step.name}` : ''}`;
|
||||
if (['roundabout', 'rotary'].includes(m.type)) return `Au rond-point, ${m.exit ? `prenez la sortie ${m.exit}` : 'suivez l’itinéraire'}`;
|
||||
return `${m.type === 'merge' ? 'Insérez-vous' : m.type === 'fork' ? 'Restez' : 'Continuez'} ${ways[m.modifier] || 'sur l’itinéraire'}${step.name ? ` sur ${step.name}` : ''}`;
|
||||
}
|
||||
// Nearest segment projection gives progress even on long rural road segments.
|
||||
export function routeProgress(coords, position) {
|
||||
let best = { away: Infinity, travelled: 0 }, total = 0;
|
||||
const scale = Math.cos(position.lat * Math.PI / 180);
|
||||
for (let i = 1; i < coords.length; i++) {
|
||||
const a = { lon: coords[i - 1][0], lat: coords[i - 1][1] }, b = { lon: coords[i][0], lat: coords[i][1] };
|
||||
const dx = (b.lon - a.lon) * scale, dy = b.lat - a.lat;
|
||||
const fraction = Math.max(0, Math.min(1, (((position.lon - a.lon) * scale) * dx + (position.lat - a.lat) * dy) / (dx * dx + dy * dy || 1)));
|
||||
const point = { lon: a.lon + fraction * (b.lon - a.lon), lat: a.lat + fraction * (b.lat - a.lat) };
|
||||
const length = distance(a, b), away = distance(position, point);
|
||||
if (away < best.away) best = { away, travelled: total + fraction * length };
|
||||
total += length;
|
||||
}
|
||||
return { ...best, total, fraction: total ? best.travelled / total : 0 };
|
||||
}
|
||||
Reference in New Issue
Block a user