Files
ArchiTools/src/modules/geoportal/components/geoportal-module.tsx
T
AI Assistant 78625d6415 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>
2026-03-24 07:43:11 +02:00

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>
);
}