fix: détection pays fiable via nom de ville Cesium+ + reorder bounding boxes

- Extrait le pays depuis le champ city Cesium+ en priorité (ex: "Heusy, 4800, Belgique" → BE)
- Bounding boxes réordonnées : petits pays (LU, BE, CH, NL) avant FR pour éviter les faux positifs
- Affiche l'heure du dernier refresh sur le badge live

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
syoul
2026-03-22 19:08:22 +01:00
parent d99ad3707d
commit 94474fc007
2 changed files with 36 additions and 8 deletions

View File

@@ -12,6 +12,7 @@ export default function App() {
const [stats, setStats] = useState<PeriodStats | null>(null);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [lastUpdate, setLastUpdate] = useState<Date | null>(null);
const [source, setSource] = useState<'live' | 'mock'>('mock');
useEffect(() => {
@@ -26,6 +27,7 @@ export default function App() {
setTransactions(transactions);
setStats(stats);
setSource(source);
setLastUpdate(new Date());
}
})
.catch((err) => console.warn('Ğ1Flux refresh error:', err))
@@ -66,7 +68,7 @@ export default function App() {
: 'bg-[#0a0b0f]/80 border-[#2e2f3a] text-[#4b5563]'
}`}>
{source === 'live'
? <>{refreshing ? <span className="animate-spin inline-block"></span> : '●'} live Ğ1v2</>
? <>{refreshing ? <span className="animate-spin inline-block"></span> : '●'} live Ğ1v2{lastUpdate && <span className="ml-1 opacity-60">{lastUpdate.toLocaleTimeString('fr-FR')}</span>}</>
: '○ mock'}
</div>
</div>

View File

@@ -25,16 +25,17 @@ export interface GeoProfile {
// Détection de pays par bounding box (pays présents dans la communauté Ğ1)
const COUNTRY_BOXES: { code: string; latMin: number; latMax: number; lngMin: number; lngMax: number }[] = [
{ code: 'FR', latMin: 41.3, latMax: 51.1, lngMin: -5.1, lngMax: 9.6 },
// Petits pays d'abord : leurs bounding boxes chevauchent celle de la France
{ code: 'LU', latMin: 49.4, latMax: 50.2, lngMin: 5.7, lngMax: 6.5 },
{ code: 'BE', latMin: 49.5, latMax: 51.5, lngMin: 2.5, lngMax: 6.4 },
{ code: 'CH', latMin: 45.8, latMax: 47.8, lngMin: 5.9, lngMax: 10.5 },
{ code: 'LU', latMin: 49.4, latMax: 50.2, lngMin: 5.7, lngMax: 6.5 },
{ code: 'NL', latMin: 50.7, latMax: 53.6, lngMin: 3.3, lngMax: 7.2 },
{ code: 'DE', latMin: 47.3, latMax: 55.1, lngMin: 6.0, lngMax: 15.0 },
{ code: 'FR', latMin: 41.3, latMax: 51.1, lngMin: -5.1, lngMax: 9.6 },
{ code: 'ES', latMin: 35.9, latMax: 43.8, lngMin: -9.3, lngMax: 4.3 },
{ code: 'PT', latMin: 36.8, latMax: 42.2, lngMin: -9.5, lngMax: -6.2 },
{ code: 'IT', latMin: 36.6, latMax: 47.1, lngMin: 6.6, lngMax: 18.5 },
{ code: 'GB', latMin: 49.9, latMax: 60.9, lngMin: -8.2, lngMax: 1.8 },
{ code: 'NL', latMin: 50.7, latMax: 53.6, lngMin: 3.3, lngMax: 7.2 },
{ code: 'MA', latMin: 27.6, latMax: 35.9, lngMin: -13.2, lngMax: -1.0 },
{ code: 'TN', latMin: 30.2, latMax: 37.5, lngMin: 7.5, lngMax: 11.6 },
{ code: 'SN', latMin: 12.3, latMax: 16.7, lngMin: -17.5, lngMax: -11.4 },
@@ -55,6 +56,29 @@ export function cleanCityName(city: string): string {
return city.split(',')[0].trim();
}
// Noms de pays en français/anglais → code ISO (Cesium+ utilise le français)
const COUNTRY_NAME_TO_CODE: Record<string, string> = {
'france': 'FR', 'belgique': 'BE', 'belgium': 'BE',
'suisse': 'CH', 'switzerland': 'CH', 'schweiz': 'CH',
'luxembourg': 'LU', 'allemagne': 'DE', 'germany': 'DE',
'espagne': 'ES', 'spain': 'ES', 'portugal': 'PT',
'italie': 'IT', 'italy': 'IT', 'pays-bas': 'NL',
'netherlands': 'NL', 'royaume-uni': 'GB', 'united kingdom': 'GB',
'maroc': 'MA', 'morocco': 'MA', 'tunisie': 'TN', 'tunisia': 'TN',
'sénégal': 'SN', 'senegal': 'SN', 'canada': 'CA', 'brésil': 'BR', 'brazil': 'BR',
};
/** Extrait le pays depuis le champ city Cesium+ (ex: "Heusy, 4800, Belgique" → "BE") */
function countryCodeFromCity(city: string): string {
const parts = city.split(',');
for (let i = parts.length - 1; i >= 0; i--) {
const token = parts[i].trim().toLowerCase();
const code = COUNTRY_NAME_TO_CODE[token];
if (code) return code;
}
return '';
}
// geoPoint accepte n'importe quel type — Cesium+ utilise plusieurs formats ES geo_point
const HitSchema = z.object({
_id: z.string(),
@@ -127,10 +151,11 @@ export async function resolveGeoByKeys(
const src = hit._source;
const geo = parseGeoPoint(src.geoPoint);
if (!geo) continue;
const city = src.city ?? 'Inconnue';
result.set(hit._id, {
name: src.title ?? '',
city: src.city ?? 'Inconnue',
countryCode: latLngToCountryCode(geo.lat, geo.lng),
city,
countryCode: countryCodeFromCity(city) || latLngToCountryCode(geo.lat, geo.lng),
lat: geo.lat,
lng: geo.lng,
});
@@ -186,10 +211,11 @@ export async function resolveGeoByNames(
const src = hit._source;
const geo = parseGeoPoint(src.geoPoint);
if (geo && src.title) {
const city = src.city ?? 'Inconnue';
result.set(src.title.toLowerCase(), {
name: src.title,
city: src.city ?? 'Inconnue',
countryCode: latLngToCountryCode(geo.lat, geo.lng),
city,
countryCode: countryCodeFromCity(city) || latLngToCountryCode(geo.lat, geo.lng),
lat: geo.lat,
lng: geo.lng,
});