diff --git a/packages/ui/src/modules/places/gadm-picker/GadmPicker.tsx b/packages/ui/src/modules/places/gadm-picker/GadmPicker.tsx index 38c11757..37bbbc1c 100644 --- a/packages/ui/src/modules/places/gadm-picker/GadmPicker.tsx +++ b/packages/ui/src/modules/places/gadm-picker/GadmPicker.tsx @@ -426,10 +426,12 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className if (processingGids.current.has(gid)) return; processingGids.current.add(gid); + const targetLevel = (forceLevel !== undefined) ? forceLevel : (resolutionOption > gadmLevel ? resolutionOption : gadmLevel); + let isDuplicate = false; setSelectedRegions(prev => { if (prev.some(r => r.gid === gid)) { isDuplicate = true; return prev; } - return [...prev, { gid, gadmName: name, level: gadmLevel, raw: region }]; + return [...prev, { gid, gadmName: name, level: targetLevel, raw: region }]; }); if (isDuplicate) { @@ -441,12 +443,17 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className setLoadingGid(gid, true); try { - const targetLevel = (forceLevel !== undefined) ? forceLevel : (resolutionOption > gadmLevel ? resolutionOption : gadmLevel); - - const geojson = await fetchRegionBoundary( + let geojson = await fetchRegionBoundary( gid, name || '', targetLevel, enrich ); + if (!geojson.features || geojson.features.length === 0) { + console.warn(`No subdivisions found for ${gid} at level ${targetLevel}. Snapping back to level ${gadmLevel}`); + + setSelectedRegions(prev => prev.map(r => r.gid === gid ? { ...r, level: gadmLevel } : r)); + geojson = await fetchRegionBoundary(gid, name || '', gadmLevel, enrich); + } + let activeStats: Record | undefined; if (enrich && geojson.features && geojson.features.length > 0) { const props = geojson.features[0].properties; @@ -477,6 +484,27 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className }); }; + const handleFlyToRegion = async (gid: string) => { + if (!map) return; + const gj = geojsons[gid]; + if (!gj || !gj.features || gj.features.length === 0) return; + + try { + const turf = await import('@turf/turf'); + const bbox = turf.bbox(gj as any); + map.fitBounds(bbox as any, { padding: 50, duration: 800 }); + } catch (e) { + console.error("Failed to fit bounds", e); + } + }; + + const handleReResolveRegion = (region: any, targetLevel: number) => { + if (loadingBoundaryIds.has(region.gid)) return; + if (targetLevel === region.level) return; + handleRemoveRegion(region.gid); + handleSelectRegion({ gid: region.gid, gadmName: region.gadmName, level: region.level }, targetLevel, true); + }; + const handleCopyToClipboard = async () => { const data = selectedRegions.map(r => ({ gid: r.gid, name: r.gadmName, level: r.level, raw: r.raw })); await navigator.clipboard.writeText(JSON.stringify(data, null, 2)); @@ -567,15 +595,7 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className }, [updateMapFeatures]); const sidebarContent = active ? ( -
-
-

GADM Area Selector

- {onClose && ( - - )} -
+

Click anywhere on the map to inspect administrative areas, or use the search below. @@ -677,7 +697,14 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className {selectedRegions.map((region) => { const loading = loadingBoundaryIds.has(region.gid); return ( -

+
{ + if ((e.target as HTMLElement).closest('button')) return; + handleFlyToRegion(region.gid); + }} + >
{loading && } @@ -686,8 +713,8 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className ({region.gid})
@@ -698,6 +725,28 @@ export function GadmPicker({ map, active, onClose, onSelectionChange, className Pop: {region.stats.ghsPopulation ? Math.round(region.stats.ghsPopulation).toLocaleString() : 'N/A'}
)} +
+ Change Level +
+ {[0, 1, 2, 3, 4, 5].map((lvl) => { + const isSelected = lvl === region.level; + return ( + + ); + })} +
+
); })}