노후관로 분석 - 시도 선택시 지도 줌인 기능 구현
This commit is contained in:
@@ -24,6 +24,7 @@ import { downloadAgedPipeCsv } from "./agedPipeExport";
|
||||
const POPUP_ROW_SELECT_MESSAGE = "SN_AGED_PIPE_ROW_SELECT";
|
||||
const POPUP_MINIMIZE_MESSAGE = "SN_AGED_PIPE_POPUP_MINIMIZE";
|
||||
const POPUP_FILTER_SYNC_MESSAGE = "SN_AGED_PIPE_FILTER_SYNC";
|
||||
const POPUP_CTPV_FOCUS_MESSAGE = "SN_AGED_PIPE_CTPV_FOCUS";
|
||||
|
||||
export default function AgedPipePopupPage() {
|
||||
const searchParams = useSearchParams();
|
||||
@@ -123,7 +124,7 @@ export default function AgedPipePopupPage() {
|
||||
return () => window.removeEventListener("message", handleFilterSyncMessage);
|
||||
}, [loadRows]);
|
||||
|
||||
const handleSearch = (page = 1) => {
|
||||
const handleSearch = async (page = 1, shouldFocusCtpv = false) => {
|
||||
if (!validateElapsedYears(Number(filters.elapsedYears))) {
|
||||
alert("경과년도는 1년~100년 범위 내에서 입력해주세요.");
|
||||
return;
|
||||
@@ -131,7 +132,17 @@ export default function AgedPipePopupPage() {
|
||||
|
||||
const normalized = normalizeAgedPipeFilters(filters);
|
||||
setFilters(normalized);
|
||||
loadRows(page, normalized);
|
||||
await loadRows(page, normalized);
|
||||
|
||||
if (shouldFocusCtpv && normalized.ctpvCd !== "0") {
|
||||
window.opener?.postMessage(
|
||||
{
|
||||
type: POPUP_CTPV_FOCUS_MESSAGE,
|
||||
ctpvCd: normalized.ctpvCd,
|
||||
},
|
||||
window.location.origin,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRowClick = (index?: number) => {
|
||||
@@ -239,7 +250,7 @@ export default function AgedPipePopupPage() {
|
||||
});
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter") handleSearch(1);
|
||||
if (event.key === "Enter") void handleSearch(1, true);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
@@ -288,7 +299,7 @@ export default function AgedPipePopupPage() {
|
||||
size="sm"
|
||||
color="primary"
|
||||
className="h-10 px-5 !text-[1.1rem]"
|
||||
onClick={() => handleSearch(1)}
|
||||
onClick={() => void handleSearch(1, true)}
|
||||
>
|
||||
조회
|
||||
</Button>
|
||||
@@ -311,7 +322,7 @@ export default function AgedPipePopupPage() {
|
||||
pageSize={10}
|
||||
total={total}
|
||||
cellClick={(index) => handleRowClick(index)}
|
||||
onPageChange={(page) => handleSearch(page)}
|
||||
onPageChange={(page) => void handleSearch(page)}
|
||||
curPage={currentPage}
|
||||
index={selectedIndex}
|
||||
widthFixed={true}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
export const AGED_PIPE_LAYER_TYPE = "tb_ms_swage_ppl";
|
||||
export const AGED_PIPE_DEFAULT_ELAPSED_YEARS = 30;
|
||||
export const AGED_PIPE_PAGE_SIZE = 10;
|
||||
export const AGED_PIPE_MIN_ZOOM = 14;
|
||||
export const AGED_PIPE_MIN_ZOOM = 13;
|
||||
export const AGED_PIPE_INVALID_INSTALL_YMD_PREFIXES = ["19000101"];
|
||||
|
||||
export type AgedPipeFilters = {
|
||||
|
||||
@@ -68,6 +68,10 @@ const AGED_PIPE_HIGHLIGHT_LAYER_ID = "SN_AGED_PIPE_HIGHLIGHT";
|
||||
const AGED_PIPE_ROW_SELECT_MESSAGE = "SN_AGED_PIPE_ROW_SELECT";
|
||||
const AGED_PIPE_POPUP_MINIMIZE_MESSAGE = "SN_AGED_PIPE_POPUP_MINIMIZE";
|
||||
const AGED_PIPE_FILTER_SYNC_MESSAGE = "SN_AGED_PIPE_FILTER_SYNC";
|
||||
// 노후관로 팝업에서 조회 버튼 클릭 시, 부모 지도를 선택 시도 범위로 이동시키는 메시지이다.
|
||||
const AGED_PIPE_CTPV_FOCUS_MESSAGE = "SN_AGED_PIPE_CTPV_FOCUS";
|
||||
// 시도 기준 자동 이동은 조회 직후 노후관로가 바로 보이는 수준까지 한 단계 더 당겨 고정한다.
|
||||
const AGED_PIPE_CTPV_FOCUS_ZOOM = 14;
|
||||
const SOUTH_KOREA_VIEW_EXTENT: [number, number, number, number] = [
|
||||
720000,
|
||||
1445000,
|
||||
@@ -952,6 +956,72 @@ const Map = () => {
|
||||
}
|
||||
}, [draftAgedPipeFilters, isAgedPipeListOpen, loadAgedPipeList, syncAgedPipePopupFilters]);
|
||||
|
||||
// 노후관로 팝업에서 시도 기준 조회를 실행하면, 부모 지도창을 해당 시도 extent로 이동시킨다.
|
||||
const focusMapToAgedPipeCtpv = useCallback(async (ctpvCd: string) => {
|
||||
const normalizedCtpvCd = String(ctpvCd ?? "").replace(/[^0-9]/g, "").slice(0, 2);
|
||||
if (!normalizedCtpvCd || normalizedCtpvCd === "0") return;
|
||||
|
||||
const map = gisMaps.current;
|
||||
if (!map) return;
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
service: "WFS",
|
||||
version: "1.0.0",
|
||||
request: "GetFeature",
|
||||
typeName: "hasudo:tb_ms_ctpv",
|
||||
outputFormat: "application/json",
|
||||
CQL_FILTER: `mctpv_cd='${normalizedCtpvCd}'`,
|
||||
});
|
||||
const payload = await getRawJson(`/geoserver/hasudo/ows?${params.toString()}`);
|
||||
// 시도 WFS는 환경마다 3857/4326 등 다른 CRS로 내려올 수 있으므로 응답 crs를 우선 신뢰한다.
|
||||
const responseCrsName = String(payload?.crs?.properties?.name ?? "");
|
||||
const resolvedDataProjection = responseCrsName.includes("EPSG::")
|
||||
? `EPSG:${responseCrsName.split("EPSG::")[1]}`
|
||||
: responseCrsName.includes("EPSG:")
|
||||
? responseCrsName.slice(responseCrsName.lastIndexOf("EPSG:"))
|
||||
: GisApp.FROM_PROJ;
|
||||
const features = new GeoJSON().readFeatures(payload, {
|
||||
dataProjection: resolvedDataProjection,
|
||||
featureProjection: map.getView().getProjection(),
|
||||
});
|
||||
|
||||
let extent: [number, number, number, number] | null = null;
|
||||
features.forEach((feature) => {
|
||||
const geometry = feature.getGeometry();
|
||||
if (!geometry) return;
|
||||
const geometryExtent = geometry.getExtent();
|
||||
if (!extent) {
|
||||
extent = [...geometryExtent] as [number, number, number, number];
|
||||
return;
|
||||
}
|
||||
extent = [
|
||||
Math.min(extent[0], geometryExtent[0]),
|
||||
Math.min(extent[1], geometryExtent[1]),
|
||||
Math.max(extent[2], geometryExtent[2]),
|
||||
Math.max(extent[3], geometryExtent[3]),
|
||||
];
|
||||
});
|
||||
|
||||
if (!extent) return;
|
||||
|
||||
const view = map.getView();
|
||||
view.fit(extent, {
|
||||
padding: AGED_PIPE_FOCUS_PADDING,
|
||||
maxZoom: 18,
|
||||
constrainResolution: true,
|
||||
nearest: true,
|
||||
});
|
||||
|
||||
const fittedZoom = view.getZoom() ?? 0;
|
||||
if (fittedZoom < AGED_PIPE_CTPV_FOCUS_ZOOM) {
|
||||
view.setZoom(AGED_PIPE_CTPV_FOCUS_ZOOM);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("노후관로 시도 이동 중 오류:", error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const openAgedPipePopupWindow = useCallback(() => {
|
||||
const popup = openWindowPopup("agedPipePopup", {
|
||||
elapsedYears: String(appliedAgedPipeFilters.elapsedYears),
|
||||
@@ -1067,6 +1137,12 @@ const Map = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data?.type === AGED_PIPE_CTPV_FOCUS_MESSAGE) {
|
||||
if (!event.data?.ctpvCd) return;
|
||||
void focusMapToAgedPipeCtpv(String(event.data.ctpvCd));
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data?.type === PIPE_ANALYSIS_DETAIL_ROW_SELECT_MESSAGE) {
|
||||
if (!event.data?.row) return;
|
||||
handlePipeAnalysisRowSelect(event.data.row as PipeAnalysisDetailRow);
|
||||
@@ -1075,7 +1151,7 @@ const Map = () => {
|
||||
|
||||
window.addEventListener("message", handleAgedPipePopupMessage);
|
||||
return () => window.removeEventListener("message", handleAgedPipePopupMessage);
|
||||
}, [agedPipeTotalCount, handleAgedPipeRowDoubleClick, handlePipeAnalysisRowSelect]);
|
||||
}, [agedPipeTotalCount, focusMapToAgedPipeCtpv, handleAgedPipeRowDoubleClick, handlePipeAnalysisRowSelect]);
|
||||
|
||||
// WaterDepth 표시 여부를 결정하는 함수
|
||||
const handleWaterDepthVisibility = useCallback((checkedIds: string[], checkedNodesData: any[]) => {
|
||||
|
||||
Reference in New Issue
Block a user