Files
frontend/apps/sn/app/(map-layout)/map/lib/use-gis-edit-selection.ts

237 lines
7.7 KiB
TypeScript

"use client";
import type * as React from "react";
import { type VertexHandleMeta } from "@/features/gis-edit/model/vertex-handles";
import { getClosestGeometryVertexMeta } from "@/features/gis-edit/lib/geometry-mutations";
import { useEffect } from "react";
import VectorLayer from "ol/layer/Vector.js";
import { unByKey } from "ol/Observable.js";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import { useGisEdit } from "../components/gis-edit/use-gis-edit";
import { getSelectedFeatureLayerTypeName } from "./right-panel-features";
/** 편집 중 지도 클릭 우선순위와 공통 선택 interaction 활성 상태를 연결한다. */
type Options = {
mapReady: boolean;
gisMaps: React.RefObject<any>;
gisEdit: ReturnType<typeof useGisEdit>;
gisEditVertexLayerRef: React.RefObject<VectorLayer<any> | null>;
clearSelectedVertexHandle: () => void;
gisEditSelectedVertexHandleKeyRef: React.RefObject<string>;
gisEditSelectedVertexMetaListRef: React.RefObject<VertexHandleMeta[] | null>;
applySelectedVertexHandleStyle: () => void;
setGisEditDetailChangeId: React.Dispatch<React.SetStateAction<string | null>>;
setFeatureData: React.Dispatch<any>;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
setIsSearchModalOpen: (open: boolean) => void;
gisEditMoveGripMetaRef: React.RefObject<{
changeId: string;
meta: VertexHandleMeta | null;
} | null>;
openGisEditPipeDetail: (changeId: string | null) => void;
};
/** 편집 중 지도 클릭 우선순위와 공통 선택 interaction 활성 상태를 연결한다. */
export function useGisEditSelection({
mapReady,
gisMaps,
gisEdit,
gisEditVertexLayerRef,
clearSelectedVertexHandle,
gisEditSelectedVertexHandleKeyRef,
gisEditSelectedVertexMetaListRef,
applySelectedVertexHandleStyle,
setGisEditDetailChangeId,
setFeatureData,
setIsOpen,
setIsSearchModalOpen,
gisEditMoveGripMetaRef,
openGisEditPipeDetail,
}: Options) {
// 편집 세션 전체 객체 대신 실제 사용하는 상태와 명령만 의존성으로 추적한다.
const {
isEditMode: editIsEditMode,
pendingInteraction: editPendingInteraction,
announceStatus: editAnnounceStatus,
activeTool: editActiveTool,
selectedLayer: editSelectedLayer,
registerFeatureSelection: editRegisterFeatureSelection,
changes: editChanges,
} = gisEdit;
useEffect(() => {
if (!mapReady || !gisMaps.current || !editIsEditMode) return;
const map = gisMaps.current;
const clickKey = map.on("singleclick", (event: any) => {
const vertexHandleFeature =
editPendingInteraction?.editType === "VERTEX_EDIT"
? map.forEachFeatureAtPixel(
event.pixel,
(feature: any, layer: any) =>
layer === gisEditVertexLayerRef.current ? feature : null,
{ hitTolerance: 14 },
)
: null;
if (vertexHandleFeature) {
event.preventDefault?.();
const handleKind = String(
vertexHandleFeature.get?.("handleKind") ?? "",
);
if (handleKind !== "vertex") {
clearSelectedVertexHandle();
editAnnounceStatus(
"흰색 midpoint는 드래그해서 새 꼭지점을 추가할 수 있습니다. Delete 삭제 대상은 주황색 실꼭지점입니다.",
);
return;
}
gisEditSelectedVertexHandleKeyRef.current = String(
vertexHandleFeature.get?.("handleSelectionKey") ?? "",
);
gisEditSelectedVertexMetaListRef.current =
(vertexHandleFeature.get?.("handleMetaList") as
| VertexHandleMeta[]
| undefined) ?? null;
applySelectedVertexHandleStyle();
editAnnounceStatus(
"주황색 실꼭지점을 선택했습니다. Delete 키를 누르면 해당 꼭지점을 삭제합니다.",
);
return;
}
const previewFeature = map.forEachFeatureAtPixel(
event.pixel,
(feature: any) =>
String(feature?.get?.("changeId") ?? "").trim() ? feature : null,
{ hitTolerance: 12 },
);
// 편집 도구가 켜진 동안에는 공통 Select를 우회하므로 원본 WFS 피처도 여기서 직접 판정한다.
const editableSourceFeature = previewFeature
? null
: editActiveTool && editActiveTool !== "ADD"
? map.forEachFeatureAtPixel(
event.pixel,
(feature: any) => {
if (String(feature?.get?.("changeId") ?? "").trim())
return null;
return getSelectedFeatureLayerTypeName(feature) ===
editSelectedLayer?.layerId
? feature
: null;
},
{ hitTolerance: 12 },
)
: null;
const selectedFeature = previewFeature ?? editableSourceFeature;
if (!selectedFeature) return;
clearSelectedVertexHandle();
const editSelection = editRegisterFeatureSelection(selectedFeature);
if (!editSelection.handled) return;
event.preventDefault?.();
if (editSelection.editType === "DELETE") {
const select = GisApp.Module.select as any;
select?.resetSelectedFeature?.();
setGisEditDetailChangeId(null);
setFeatureData(null);
setIsOpen(false);
setIsSearchModalOpen(false);
return;
}
if (editActiveTool === "MOVE" || editActiveTool === "VERTEX_EDIT") {
if (
editActiveTool === "MOVE" &&
typeof editSelection.changeId === "string" &&
editSelection.changeId
) {
gisEditMoveGripMetaRef.current = {
changeId: String(editSelection.changeId),
meta: getClosestGeometryVertexMeta(
selectedFeature.getGeometry?.(),
map,
event.coordinate,
),
};
}
setGisEditDetailChangeId(null);
setFeatureData(null);
setIsOpen(false);
setIsSearchModalOpen(false);
return;
}
const selectedChangeId =
typeof editSelection.changeId === "string"
? editSelection.changeId
: "";
const selectedChange = editChanges.find(
(change) => change.changeId === selectedChangeId,
);
const canOpenPipeDetail =
selectedChangeId && editSelection.editType !== "DELETE";
if (canOpenPipeDetail) {
openGisEditPipeDetail(selectedChangeId);
setFeatureData(
selectedChange?.editType === "ADD"
? {
lyrTypeNm: "tb_ms_swage_ppl",
fcltMngNo: "",
mngInstCd: "",
fclt_mng_no: "",
inst_mng_no: "",
}
: null,
);
window.setTimeout(() => {
setGisEditDetailChangeId(selectedChangeId);
setIsOpen(true);
}, 0);
setIsSearchModalOpen(false);
}
});
return () => {
unByKey(clickKey);
};
}, [
applySelectedVertexHandleStyle,
clearSelectedVertexHandle,
editActiveTool,
editChanges,
editIsEditMode,
editPendingInteraction,
editAnnounceStatus,
editRegisterFeatureSelection,
editSelectedLayer,
mapReady,
openGisEditPipeDetail,
gisMaps,
gisEditVertexLayerRef,
gisEditSelectedVertexHandleKeyRef,
gisEditSelectedVertexMetaListRef,
setGisEditDetailChangeId,
setFeatureData,
setIsOpen,
setIsSearchModalOpen,
gisEditMoveGripMetaRef,
]);
useEffect(() => {
const select = GisApp.Module.select as any;
if (!select?.setActive) return;
if (editIsEditMode && editActiveTool) {
select.resetSelectedFeature?.();
select.setActive(false);
return;
}
select.setActive(true);
}, [editActiveTool, editIsEditMode, mapReady]);
return {};
}