fix(geoportal): simplified info panel, preserve basemap zoom, DXF export, intravilan outline
- Feature panel: simplified (NR_CAD/NR_CF/SIRUTA/Suprafata/Proprietari), aligned top-right under basemap switcher, click empty space to close - Basemap switch: preserves zoom+center via viewStateRef + moveend listener - DXF export: use -s_srs + -t_srs (not -a_srs + -t_srs which ogr2ogr rejects) - Intravilan: double line (black outer + orange inner), z13+, no fill - Parcel labels: cadastral_ref shown at z16+ - UAT z12: original geometry (no simplification) - Removed MapLibre popup (only side panel) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,19 +9,11 @@ import { SelectionToolbar } from "./selection-toolbar";
|
||||
import { FeatureInfoPanel } from "./feature-info-panel";
|
||||
import type { MapViewerHandle } from "./map-viewer";
|
||||
import type {
|
||||
BasemapId,
|
||||
ClickedFeature,
|
||||
LayerVisibility,
|
||||
SearchResult,
|
||||
SelectedFeature,
|
||||
BasemapId, ClickedFeature, LayerVisibility, SearchResult, SelectedFeature,
|
||||
} from "../types";
|
||||
|
||||
/* MapLibre uses WebGL — must disable SSR */
|
||||
const MapViewer = dynamic(
|
||||
() =>
|
||||
import("./map-viewer").then((m) => ({
|
||||
default: m.MapViewer,
|
||||
})),
|
||||
() => import("./map-viewer").then((m) => ({ default: m.MapViewer })),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
@@ -32,54 +24,33 @@ const MapViewer = dynamic(
|
||||
}
|
||||
);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Component */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
export function GeoportalModule() {
|
||||
const mapHandleRef = useRef<MapViewerHandle>(null);
|
||||
|
||||
// Map state
|
||||
const [basemap, setBasemap] = useState<BasemapId>("liberty");
|
||||
const [layerVisibility, setLayerVisibility] = useState<LayerVisibility>(
|
||||
getDefaultVisibility
|
||||
);
|
||||
|
||||
// Feature info
|
||||
const [layerVisibility, setLayerVisibility] = useState<LayerVisibility>(getDefaultVisibility);
|
||||
const [clickedFeature, setClickedFeature] = useState<ClickedFeature | null>(null);
|
||||
|
||||
// Selection
|
||||
const [selectionMode, setSelectionMode] = useState(false);
|
||||
const [selectedFeatures, setSelectedFeatures] = useState<SelectedFeature[]>([]);
|
||||
|
||||
// Fly-to target (from search)
|
||||
const [flyTarget, setFlyTarget] = useState<{ center: [number, number]; zoom?: number } | undefined>();
|
||||
|
||||
const handleFeatureClick = useCallback((feature: ClickedFeature) => {
|
||||
const handleFeatureClick = useCallback((feature: ClickedFeature | null) => {
|
||||
// null = clicked on empty space, close panel
|
||||
if (!feature || !feature.properties) {
|
||||
setClickedFeature(null);
|
||||
return;
|
||||
}
|
||||
setClickedFeature(feature);
|
||||
}, []);
|
||||
|
||||
const handleVisibilityChange = useCallback((vis: LayerVisibility) => {
|
||||
setLayerVisibility(vis);
|
||||
}, []);
|
||||
|
||||
const handleSearchResult = useCallback((result: SearchResult) => {
|
||||
if (result.coordinates) {
|
||||
setFlyTarget({
|
||||
center: result.coordinates,
|
||||
zoom: result.type === "uat" ? 12 : 17,
|
||||
});
|
||||
setFlyTarget({ center: result.coordinates, zoom: result.type === "uat" ? 12 : 17 });
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleSelectionChange = useCallback((features: SelectedFeature[]) => {
|
||||
setSelectedFeatures(features);
|
||||
}, []);
|
||||
|
||||
const handleToggleSelectionMode = useCallback(() => {
|
||||
setSelectionMode((prev) => {
|
||||
if (prev) {
|
||||
// Turning off: clear selection
|
||||
mapHandleRef.current?.clearSelection();
|
||||
setSelectedFeatures([]);
|
||||
}
|
||||
@@ -87,38 +58,32 @@ export function GeoportalModule() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleClearSelection = useCallback(() => {
|
||||
mapHandleRef.current?.clearSelection();
|
||||
setSelectedFeatures([]);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="absolute inset-0">
|
||||
{/* Full-bleed map */}
|
||||
<MapViewer
|
||||
ref={mapHandleRef}
|
||||
className="h-full w-full"
|
||||
basemap={basemap}
|
||||
selectionMode={selectionMode}
|
||||
onFeatureClick={handleFeatureClick}
|
||||
onSelectionChange={handleSelectionChange}
|
||||
onSelectionChange={setSelectedFeatures}
|
||||
layerVisibility={layerVisibility}
|
||||
center={flyTarget?.center}
|
||||
zoom={flyTarget?.zoom}
|
||||
/>
|
||||
|
||||
{/* Top-left controls: search + layers */}
|
||||
{/* Top-left: search + layers */}
|
||||
<div className="absolute top-3 left-3 z-10 flex flex-col gap-2 max-w-xs">
|
||||
<SearchBar onResultSelect={handleSearchResult} />
|
||||
<LayerPanel
|
||||
visibility={layerVisibility}
|
||||
onVisibilityChange={handleVisibilityChange}
|
||||
/>
|
||||
<LayerPanel visibility={layerVisibility} onVisibilityChange={setLayerVisibility} />
|
||||
</div>
|
||||
|
||||
{/* Top-right: basemap switcher (offset to avoid nav controls) */}
|
||||
<div className="absolute top-3 right-14 z-10">
|
||||
{/* Top-right: basemap switcher + feature panel (aligned) */}
|
||||
<div className="absolute top-3 right-14 z-10 flex flex-col items-end gap-2">
|
||||
<BasemapSwitcher value={basemap} onChange={setBasemap} />
|
||||
{clickedFeature && !selectionMode && (
|
||||
<FeatureInfoPanel feature={clickedFeature} onClose={() => setClickedFeature(null)} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bottom-left: selection toolbar */}
|
||||
@@ -127,19 +92,9 @@ export function GeoportalModule() {
|
||||
selectedFeatures={selectedFeatures}
|
||||
selectionMode={selectionMode}
|
||||
onToggleSelectionMode={handleToggleSelectionMode}
|
||||
onClearSelection={handleClearSelection}
|
||||
onClearSelection={() => { mapHandleRef.current?.clearSelection(); setSelectedFeatures([]); }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Right side: feature info panel */}
|
||||
{clickedFeature && !selectionMode && (
|
||||
<div className="absolute top-16 right-3 z-10">
|
||||
<FeatureInfoPanel
|
||||
feature={clickedFeature}
|
||||
onClose={() => setClickedFeature(null)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user