78625d6415
- 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>
101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useRef, useCallback } from "react";
|
|
import dynamic from "next/dynamic";
|
|
import { LayerPanel, getDefaultVisibility } from "./layer-panel";
|
|
import { BasemapSwitcher } from "./basemap-switcher";
|
|
import { SearchBar } from "./search-bar";
|
|
import { SelectionToolbar } from "./selection-toolbar";
|
|
import { FeatureInfoPanel } from "./feature-info-panel";
|
|
import type { MapViewerHandle } from "./map-viewer";
|
|
import type {
|
|
BasemapId, ClickedFeature, LayerVisibility, SearchResult, SelectedFeature,
|
|
} from "../types";
|
|
|
|
const MapViewer = dynamic(
|
|
() => import("./map-viewer").then((m) => ({ default: m.MapViewer })),
|
|
{
|
|
ssr: false,
|
|
loading: () => (
|
|
<div className="flex items-center justify-center h-full bg-muted/30">
|
|
<p className="text-sm text-muted-foreground">Se incarca harta...</p>
|
|
</div>
|
|
),
|
|
}
|
|
);
|
|
|
|
export function GeoportalModule() {
|
|
const mapHandleRef = useRef<MapViewerHandle>(null);
|
|
const [basemap, setBasemap] = useState<BasemapId>("liberty");
|
|
const [layerVisibility, setLayerVisibility] = useState<LayerVisibility>(getDefaultVisibility);
|
|
const [clickedFeature, setClickedFeature] = useState<ClickedFeature | null>(null);
|
|
const [selectionMode, setSelectionMode] = useState(false);
|
|
const [selectedFeatures, setSelectedFeatures] = useState<SelectedFeature[]>([]);
|
|
const [flyTarget, setFlyTarget] = useState<{ center: [number, number]; zoom?: number } | undefined>();
|
|
|
|
const handleFeatureClick = useCallback((feature: ClickedFeature | null) => {
|
|
// null = clicked on empty space, close panel
|
|
if (!feature || !feature.properties) {
|
|
setClickedFeature(null);
|
|
return;
|
|
}
|
|
setClickedFeature(feature);
|
|
}, []);
|
|
|
|
const handleSearchResult = useCallback((result: SearchResult) => {
|
|
if (result.coordinates) {
|
|
setFlyTarget({ center: result.coordinates, zoom: result.type === "uat" ? 12 : 17 });
|
|
}
|
|
}, []);
|
|
|
|
const handleToggleSelectionMode = useCallback(() => {
|
|
setSelectionMode((prev) => {
|
|
if (prev) {
|
|
mapHandleRef.current?.clearSelection();
|
|
setSelectedFeatures([]);
|
|
}
|
|
return !prev;
|
|
});
|
|
}, []);
|
|
|
|
return (
|
|
<div className="absolute inset-0">
|
|
<MapViewer
|
|
ref={mapHandleRef}
|
|
className="h-full w-full"
|
|
basemap={basemap}
|
|
selectionMode={selectionMode}
|
|
onFeatureClick={handleFeatureClick}
|
|
onSelectionChange={setSelectedFeatures}
|
|
layerVisibility={layerVisibility}
|
|
center={flyTarget?.center}
|
|
zoom={flyTarget?.zoom}
|
|
/>
|
|
|
|
{/* 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={setLayerVisibility} />
|
|
</div>
|
|
|
|
{/* 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 */}
|
|
<div className="absolute bottom-8 left-3 z-10">
|
|
<SelectionToolbar
|
|
selectedFeatures={selectedFeatures}
|
|
selectionMode={selectionMode}
|
|
onToggleSelectionMode={handleToggleSelectionMode}
|
|
onClearSelection={() => { mapHandleRef.current?.clearSelection(); setSelectedFeatures([]); }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|