sn: 침수발생이력 변수명 수정

This commit is contained in:
2026-07-24 15:23:35 +09:00
parent 8a35d232ef
commit c13cab394a
4 changed files with 159 additions and 151 deletions
@@ -1,10 +1,10 @@
"use client";
import { useMemo, useState } from "react";
import { formatFloodArea, formatFloodDepth, type FloodHistoryItem } from "./fldngHstryTypes";
import { formatFloodArea, formatFloodDepth, type FldngHstryItem } from "./fldngHstryTypes";
type Props = {
detail: FloodHistoryItem;
detail: FldngHstryItem;
onClose: () => void;
position: {
left: number;
@@ -1,3 +1,6 @@
/**
* 침수발생이력 popup
*/
"use client";
import { useCallback, useEffect, useMemo, useState } from "react";
@@ -8,20 +11,20 @@ import { getRawJson } from "@/components/lib/api";
import useStore from "@/store/useStore";
import LoadingOverlay from "@/components/ui/LoadingBar";
import {
attachFloodHistoryGeometry,
buildFloodHistoryListQueryString,
buildFloodHistoryYearOptions,
FLOOD_HISTORY_DEFAULT_FILTERS,
FLOOD_HISTORY_DOWNLOAD_BATCH_SIZE,
FLOOD_HISTORY_DOWNLOAD_HEADERS,
FLOOD_HISTORY_LIST_HEADLIST,
FLOOD_HISTORY_PAGE_SIZE,
FLOOD_HISTORY_ROW_SELECT_MESSAGE,
normalizeFloodHistoryFilters,
toFloodHistoryTableRows,
type FloodHistoryApiItem,
type FloodHistoryFilters,
type FloodHistoryTableRow,
FLDNG_HSTRY_DEFAULT_FILTERS,
FLDNG_HSTRY_DOWNLOAD_BATCH_SIZE,
FLDNG_HSTRY_DOWNLOAD_HEADERS,
FLDNG_HSTRY_LIST_HEADLIST,
FLDNG_HSTRY_PAGE_SIZE,
FLDNG_HSTRY_ROW_SELECT_MESSAGE,
attachFldngHstryGeometry,
buildFldngHstryListQueryString,
buildFldngHstryYearOptions,
normalizeFldngHstryFilters,
toFldngHstryTableRows,
type FldngHstryApiItem,
type FldngHstryFilters,
type FldngHstryTableRow,
} from "./fldngHstryTypes";
const unwrapPayload = <T,>(response: any): T =>
@@ -38,12 +41,12 @@ const resolveOptionText = (
return options.find((item) => String(item.val).trim() === lookup)?.text ?? "";
};
const resolveFloodHistoryApiSort = (sort?: UiTableSort) => {
const resolveFldngHstryApiSort = (sort?: UiTableSort) => {
if (!sort?.key || !sort.direction) return undefined;
return { key: sort.key, direction: sort.direction } as UiTableSort;
};
const buildDownloadRows = (rows: FloodHistoryTableRow[]) =>
const buildDownloadRows = (rows: FldngHstryTableRow[]) =>
rows.map((row) => ({
rowNo: row.rowNo,
floodYearDisplay: row.floodYearDisplay,
@@ -65,10 +68,10 @@ const buildDownloadRows = (rows: FloodHistoryTableRow[]) =>
}));
const resolveRegionNames = (
row: FloodHistoryTableRow,
row: FldngHstryTableRow,
ctpvOptions: Array<{ text: string; val: string | number }>,
sggOptions: Array<{ text: string; val: string | number }>,
): FloodHistoryTableRow => ({
): FldngHstryTableRow => ({
...row,
ctpvNm: resolveOptionText(row.ctpvCd ?? "", ctpvOptions, 2) || row.ctpvNm,
sggNm: resolveOptionText(row.sggCd ?? "", sggOptions, 5) || row.sggNm,
@@ -76,9 +79,9 @@ const resolveRegionNames = (
export default function FldngHstryPopupPage() {
const { ctpv, sgg, fetchCtpv, fetchSgg } = useStore();
const [filters, setFilters] = useState<FloodHistoryFilters>(FLOOD_HISTORY_DEFAULT_FILTERS);
const [appliedFilters, setAppliedFilters] = useState<FloodHistoryFilters>(FLOOD_HISTORY_DEFAULT_FILTERS);
const [rows, setRows] = useState<FloodHistoryTableRow[]>([]);
const [filters, setFilters] = useState<FldngHstryFilters>(FLDNG_HSTRY_DEFAULT_FILTERS);
const [appliedFilters, setAppliedFilters] = useState<FldngHstryFilters>(FLDNG_HSTRY_DEFAULT_FILTERS);
const [rows, setRows] = useState<FldngHstryTableRow[]>([]);
const [total, setTotal] = useState(0);
const [currentPage, setCurrentPage] = useState(1);
const [selectedIndex, setSelectedIndex] = useState(-1);
@@ -100,7 +103,7 @@ export default function FldngHstryPopupPage() {
);
}, [ctpv, rows.length, sgg]);
const floodYearOptions = useMemo(() => buildFloodHistoryYearOptions(), []);
const fldngYearOptions = useMemo(() => buildFldngHstryYearOptions(), []);
const sggOptions = useMemo(
() =>
@@ -111,20 +114,20 @@ export default function FldngHstryPopupPage() {
);
const fetchRows = useCallback(
async (page: number, nextFilters: FloodHistoryFilters, size: number, nextSort?: UiTableSort) => {
const normalized = normalizeFloodHistoryFilters(nextFilters);
const queryString = buildFloodHistoryListQueryString(
async (page: number, nextFilters: FldngHstryFilters, size: number, nextSort?: UiTableSort) => {
const normalized = normalizeFldngHstryFilters(nextFilters);
const queryString = buildFldngHstryListQueryString(
normalized,
page,
size,
resolveFloodHistoryApiSort(nextSort),
resolveFldngHstryApiSort(nextSort),
);
const response = await getRawJson(`/sewerage-service/api/v1/fldng/hstry?${queryString}`);
const payload = unwrapPayload<{ items?: FloodHistoryApiItem[]; total?: number }>(response);
const payload = unwrapPayload<{ items?: FldngHstryApiItem[]; total?: number }>(response);
const items = Array.isArray(payload?.items) ? payload.items : [];
const displayItems = items.map((item) => {
const hydrated = attachFloodHistoryGeometry(item);
const hydrated = attachFldngHstryGeometry(item);
return {
...hydrated,
ctpvNm: resolveOptionText(hydrated.ctpvCd ?? "", ctpv as any, 2),
@@ -133,7 +136,7 @@ export default function FldngHstryPopupPage() {
});
return {
rows: toFloodHistoryTableRows(displayItems, page, size),
rows: toFldngHstryTableRows(displayItems, page, size),
total: Number(payload?.total ?? 0),
};
},
@@ -141,11 +144,11 @@ export default function FldngHstryPopupPage() {
);
const loadRows = useCallback(
async (page: number, nextFilters: FloodHistoryFilters, nextSort?: UiTableSort) => {
async (page: number, nextFilters: FldngHstryFilters, nextSort?: UiTableSort) => {
setIsLoading(true);
try {
const normalized = normalizeFloodHistoryFilters(nextFilters);
const response = await fetchRows(page, normalized, FLOOD_HISTORY_PAGE_SIZE, nextSort);
const normalized = normalizeFldngHstryFilters(nextFilters);
const response = await fetchRows(page, normalized, FLDNG_HSTRY_PAGE_SIZE, nextSort);
setRows(response.rows);
setTotal(response.total);
setCurrentPage(page);
@@ -166,13 +169,13 @@ export default function FldngHstryPopupPage() {
);
useEffect(() => {
void loadRows(1, FLOOD_HISTORY_DEFAULT_FILTERS, undefined);
void loadRows(1, FLDNG_HSTRY_DEFAULT_FILTERS, undefined);
// 최초 로딩은 한 번만 수행한다.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleSearch = async (page = 1) => {
const normalized = normalizeFloodHistoryFilters(filters);
const normalized = normalizeFldngHstryFilters(filters);
if (normalized.sggCd !== "0" && normalized.ctpvCd === "0") {
alert("시도를 먼저 선택하세요.");
@@ -199,7 +202,7 @@ export default function FldngHstryPopupPage() {
setSelectedIndex(index);
window.opener?.postMessage(
{
type: FLOOD_HISTORY_ROW_SELECT_MESSAGE,
type: FLDNG_HSTRY_ROW_SELECT_MESSAGE,
row,
openDetail: false,
},
@@ -218,7 +221,7 @@ export default function FldngHstryPopupPage() {
const response = await fetchRows(
1,
appliedFilters,
Math.max(total, FLOOD_HISTORY_DOWNLOAD_BATCH_SIZE),
Math.max(total, FLDNG_HSTRY_DOWNLOAD_BATCH_SIZE),
appliedSort,
);
await downloadExcelWithKoreanHeaders({
@@ -226,7 +229,7 @@ export default function FldngHstryPopupPage() {
fileName: `침수발생이력_${new Date().toISOString().slice(0, 10)}.xlsx`,
sheetNameArray: ["침수발생이력"],
headerRange: "A1:Q1",
headerMapping: FLOOD_HISTORY_DOWNLOAD_HEADERS,
headerMapping: FLDNG_HSTRY_DOWNLOAD_HEADERS,
});
} catch (error) {
console.error("침수발생이력 다운로드 중 오류:", error);
@@ -302,7 +305,7 @@ export default function FldngHstryPopupPage() {
}
>
<option value="">전체</option>
{floodYearOptions.map((year) => (
{fldngYearOptions.map((year) => (
<option key={year} value={year}>
{year}년
</option>
@@ -352,11 +355,11 @@ export default function FldngHstryPopupPage() {
</div>
<div className="min-h-0 min-w-0 flex-1 overflow-x-hidden overflow-y-auto pt-2">
<UiTable<FloodHistoryTableRow>
<UiTable<FldngHstryTableRow>
headName={<>침수발생이력 조회 결과 총 {total.toLocaleString()}건</>}
tableData={rows}
headlist={FLOOD_HISTORY_LIST_HEADLIST}
pageSize={FLOOD_HISTORY_PAGE_SIZE}
headlist={FLDNG_HSTRY_LIST_HEADLIST}
pageSize={FLDNG_HSTRY_PAGE_SIZE}
total={total}
cellClick={(index) => handleRowClick(index)}
onPageChange={(page) => void handlePageChange(page)}
@@ -3,29 +3,29 @@
import type { UiHeadProps, UiTableSort } from "@common/business_components/ui";
export const FLOOD_HISTORY_POPUP_KEY = "fldngHstryPopup";
export const FLOOD_HISTORY_ROW_SELECT_MESSAGE = "SN_FLOOD_HISTORY_ROW_SELECT";
export const FLDNG_HSTRY_ROW_SELECT_MESSAGE = "SN_FLOOD_HISTORY_ROW_SELECT";
export const FLOOD_HISTORY_LAYER_TYPE = "tb_sn_flood_trace_mock";
export const FLOOD_HISTORY_LAYER_NAME = "침수흔적도";
export const FLOOD_HISTORY_AREA_LAYER_ID = "SN_FLOOD_HISTORY_AREA";
export const FLOOD_HISTORY_DEPTH_LAYER_ID = "SN_FLOOD_HISTORY_DEPTH";
export const FLOOD_HISTORY_PAGE_SIZE = 10;
export const FLOOD_HISTORY_DOWNLOAD_BATCH_SIZE = 2000;
export const FLDNG_HSTRY_PAGE_SIZE = 10;
export const FLDNG_HSTRY_DOWNLOAD_BATCH_SIZE = 2000;
export type FloodHistoryFilters = {
export type FldngHstryFilters = {
ctpvCd: string;
sggCd: string;
floodYear: string;
disasterName: string;
};
export type FloodHistoryDepthZone = {
export type FldngHstryDepthZone = {
zoneId: string;
depthLabel: string;
color: string;
ring: [number, number][];
};
export type FloodHistoryApiItem = {
export type FldngHstryApiItem = {
fid: string;
fldngMapFid?: string;
fldngWtlvMapSn?: number | null;
@@ -58,7 +58,7 @@ export type FloodHistoryApiItem = {
rainStr?: number | null;
};
export type FloodHistoryItem = FloodHistoryApiItem & {
export type FldngHstryItem = FldngHstryApiItem & {
historyId: string;
ctpvNm: string;
sggNm: string;
@@ -85,10 +85,10 @@ export type FloodHistoryItem = FloodHistoryApiItem & {
totalRainfallMm: number;
rainfallIntensityMmHr: number;
areaRing: [number, number][];
depthZones: FloodHistoryDepthZone[];
depthZones: FldngHstryDepthZone[];
};
export type FloodHistoryTableRow = FloodHistoryItem & {
export type FldngHstryTableRow = FldngHstryItem & {
rowNo: number;
floodYearDisplay: string;
floodHourDisplay: string;
@@ -102,14 +102,14 @@ export type FloodHistoryTableRow = FloodHistoryItem & {
rainStrDisplay: string;
};
export const FLOOD_HISTORY_DEFAULT_FILTERS: FloodHistoryFilters = {
export const FLDNG_HSTRY_DEFAULT_FILTERS: FldngHstryFilters = {
ctpvCd: "0",
sggCd: "0",
floodYear: "",
disasterName: "",
};
export const FLOOD_HISTORY_DEPTH_LEGEND_ITEMS = [
export const FLDNG_HSTRY_DEPTH_LEGEND_ITEMS = [
{ color: "#fff27a", label: "0.0~0.5m" },
{ color: "#c2f13d", label: "0.5~1.0m" },
{ color: "#67d8ff", label: "1.0~2.0m" },
@@ -117,7 +117,7 @@ export const FLOOD_HISTORY_DEPTH_LEGEND_ITEMS = [
{ color: "#f2528b", label: "5.0m 이상" },
] as const;
export const FLOOD_HISTORY_LIST_HEADLIST: UiHeadProps[] = [
export const FLDNG_HSTRY_LIST_HEADLIST: UiHeadProps[] = [
{ id: "rowNo", title: "NO", widthPercent: 5, sortable: false },
{ id: "floodYearDisplay", title: "침수년도", widthPercent: 6, sortable: true, sortKey: "fldngYr" },
{ id: "ctpvNm", title: "시도", widthPercent: 7, sortable: true, sortKey: "ctpvCd" },
@@ -137,7 +137,7 @@ export const FLOOD_HISTORY_LIST_HEADLIST: UiHeadProps[] = [
{ id: "damageAmountDisplay", title: "피해금액", widthPercent: 9, sortable: true, sortKey: "damAmt" },
];
export const FLOOD_HISTORY_DOWNLOAD_HEADERS: Record<string, string> = {
export const FLDNG_HSTRY_DOWNLOAD_HEADERS: Record<string, string> = {
rowNo: "NO",
floodYearDisplay: "침수년도",
ctpvNm: "시도",
@@ -198,11 +198,11 @@ const buildGeometrySeed = (fid: string) => {
};
};
const createDepthZones = (
const createFldngHstryDepthZones = (
historyId: string,
centerX: number,
centerY: number,
): FloodHistoryDepthZone[] => {
): FldngHstryDepthZone[] => {
return [
{
zoneId: `${historyId}-depth-1`,
@@ -312,7 +312,7 @@ const formatInteger = (value?: number | null, suffix = "") => {
return `${numeric.toLocaleString("ko-KR")}${suffix}`;
};
const buildCauseDetailName = (item: FloodHistoryApiItem) => {
const buildCauseDetailName = (item: FldngHstryApiItem) => {
const direct = String(item.dtlOcrnCsCn ?? "").trim();
if (direct) return direct;
const code = String(item.fldngCsDtlCd ?? "").trim();
@@ -325,27 +325,27 @@ const buildGradeLabel = (avgFloodDepthM: number) => {
return "1등급";
};
export const normalizeFloodHistoryFilters = (
filters: FloodHistoryFilters,
): FloodHistoryFilters => ({
export const normalizeFldngHstryFilters = (
filters: FldngHstryFilters,
): FldngHstryFilters => ({
ctpvCd: String(filters.ctpvCd ?? "0") || "0",
sggCd: String(filters.sggCd ?? "0") || "0",
floodYear: String(filters.floodYear ?? "").replace(/[^0-9]/g, "").slice(0, 4),
disasterName: String(filters.disasterName ?? "").trim(),
});
export const buildFloodHistoryYearOptions = () => {
export const buildFldngHstryYearOptions = () => {
const currentYear = new Date().getFullYear();
return Array.from({ length: 30 }, (_, index) => String(currentYear - index));
};
export const buildFloodHistoryListQueryString = (
filters: FloodHistoryFilters,
export const buildFldngHstryListQueryString = (
filters: FldngHstryFilters,
page: number,
size: number,
sort?: UiTableSort,
) => {
const normalized = normalizeFloodHistoryFilters(filters);
const normalized = normalizeFldngHstryFilters(filters);
const params = new URLSearchParams({
page: String(page - 1),
size: String(size),
@@ -364,7 +364,7 @@ export const formatFloodArea = (value: number) => `${value.toFixed(1)}ha`;
export const formatFloodDepth = (value: number) => `${value.toFixed(2)}m`;
export const attachFloodHistoryGeometry = (item: FloodHistoryApiItem): FloodHistoryItem => {
export const attachFldngHstryGeometry = (item: FldngHstryApiItem): FldngHstryItem => {
const historyId = String(item.fid ?? "");
const geometrySeed = buildGeometrySeed(historyId);
const startDate = normalizeDate(item.bgngYmd);
@@ -409,15 +409,15 @@ export const attachFloodHistoryGeometry = (item: FloodHistoryApiItem): FloodHist
rotationDeg: 9,
scales: [1.08, 0.9, 1.04, 0.82, 0.98, 1.12, 0.88, 0.96, 1.03, 0.86],
}),
depthZones: createDepthZones(historyId, geometrySeed.centerX, geometrySeed.centerY),
depthZones: createFldngHstryDepthZones(historyId, geometrySeed.centerX, geometrySeed.centerY),
};
};
export const toFloodHistoryTableRows = (
items: FloodHistoryItem[],
export const toFldngHstryTableRows = (
items: FldngHstryItem[],
page: number,
pageSize = FLOOD_HISTORY_PAGE_SIZE,
): FloodHistoryTableRow[] =>
pageSize = FLDNG_HSTRY_PAGE_SIZE,
): FldngHstryTableRow[] =>
items.map((item, index) => ({
...item,
rowNo: (page - 1) * pageSize + index + 1,
@@ -433,14 +433,19 @@ export const toFloodHistoryTableRows = (
rainStrDisplay: formatNumber(item.rainfallIntensityMmHr, 1),
}));
const resolveFloodHistoryNodeName = (node: any) =>
const resolveFldngHstryNodeName = (node: any) =>
String(node?.text ?? node?.data?.lyrNm ?? node?.data?.name ?? node?.lyrNm ?? node?.name ?? "").trim();
const resolveFloodHistoryLayerType = (node: any) =>
const resolveFldngHstryLayerType = (node: any) =>
String(node?.data?.lyrTyNm ?? node?.data?.lyrTypeNm ?? node?.lyrTyNm ?? node?.lyrTypeNm ?? "").trim();
export const isFloodHistoryNode = (node: any) => {
const name = resolveFloodHistoryNodeName(node);
const layerType = resolveFloodHistoryLayerType(node);
export const isFldngHstryNode = (node: any) => {
const name = resolveFldngHstryNodeName(node);
const layerType = resolveFldngHstryLayerType(node);
return name === FLOOD_HISTORY_LAYER_NAME || layerType === FLOOD_HISTORY_LAYER_TYPE;
};
export const isFloodHistoryNode = isFldngHstryNode;
export const FLOOD_HISTORY_ROW_SELECT_MESSAGE = FLDNG_HSTRY_ROW_SELECT_MESSAGE;
export const FLOOD_HISTORY_PAGE_SIZE = FLDNG_HSTRY_PAGE_SIZE;
export const FLOOD_HISTORY_DOWNLOAD_BATCH_SIZE = FLDNG_HSTRY_DOWNLOAD_BATCH_SIZE;
+74 -74
View File
@@ -114,10 +114,10 @@ import {
FLOOD_HISTORY_AREA_LAYER_ID,
FLOOD_HISTORY_DEPTH_LAYER_ID,
FLOOD_HISTORY_LAYER_TYPE,
FLOOD_HISTORY_ROW_SELECT_MESSAGE,
FLOOD_HISTORY_POPUP_KEY,
isFloodHistoryNode,
type FloodHistoryItem,
FLDNG_HSTRY_ROW_SELECT_MESSAGE,
isFldngHstryNode,
type FldngHstryItem,
} from "./components/fldngHstry/fldngHstryTypes";
import FeatureClickEventPopup from "./components/popupComponent/featureClickEventPopup";
import SearchModal from "./searchModal/searchModal";
@@ -973,7 +973,7 @@ const getRightPanelFeatureDisplayLabel = (data: any, category: RightPanelTabCate
return getRightPanelFeatureTitle(data);
};
const createFloodHistoryAreaStyle = (label: string) =>
const createFldngHstryAreaStyle = (label: string) =>
new Style({
fill: new Fill({
color: "rgba(30, 136, 229, 0.16)",
@@ -993,7 +993,7 @@ const createFloodHistoryAreaStyle = (label: string) =>
zIndex: 9994,
});
const createFloodHistoryDepthStyle = (label: string, color: string) =>
const createFldngHstryDepthStyle = (label: string, color: string) =>
new Style({
fill: new Fill({
color,
@@ -2544,7 +2544,7 @@ const Map = () => {
const [checkedLayerLegendVersion, setCheckedLayerLegendVersion] = useState(0);
const [isAgedPipeChecked, setIsAgedPipeChecked] = useState(false);
const [isSinkholeChecked, setIsSinkholeChecked] = useState(false);
const [isFloodHistoryChecked, setIsFloodHistoryChecked] = useState(false);
const [isFldngHstryChecked, setIsFldngHstryChecked] = useState(false);
const [swagePrcsFcltLayerMode, setSwagePrcsFcltLayerMode] = useState<SwagePrcsFcltLayerMode>("all");
const [activeLayerSettingOrder, setActiveLayerSettingOrder] = useState<string[]>([]);
const [draftAgedPipeFilters, setDraftAgedPipeFilters] =
@@ -2559,8 +2559,8 @@ const Map = () => {
);
const [isSinkholePopupFilterActive, setIsSinkholePopupFilterActive] = useState(false);
const [sinkholeRadiusMeters, setSinkholeRadiusMeters] = useState(SINKHOLE_DEFAULT_RADIUS);
const [selectedFloodHistoryDetail, setSelectedFloodHistoryDetail] = useState<FloodHistoryItem | null>(null);
const [floodHistoryDetailPopupPosition, setFloodHistoryDetailPopupPosition] = useState<{ left: number; top: number } | null>(null);
const [selectedFldngHstryDetail, setSelectedFldngHstryDetail] = useState<FldngHstryItem | null>(null);
const [fldngHstryDetailPopupPosition, setFldngHstryDetailPopupPosition] = useState<{ left: number; top: number } | null>(null);
const [selectedSinkholeDetail, setSelectedSinkholeDetail] = useState<SinkholeDetail | null>(null);
const [selectedSinkholeNearbyPipes, setSelectedSinkholeNearbyPipes] = useState<SinkholeNearbyPipe[]>([]);
const [lastSinkholeSyncSummary, setLastSinkholeSyncSummary] = useState<SinkholeSyncSummary | null>(null);
@@ -2593,8 +2593,8 @@ const Map = () => {
const agedPipePopupWindowRef = useRef<Window | null>(null);
const sinkholePopupWindowRef = useRef<Window | null>(null);
// 침수발생이력 overlay layer는 지도 드래그 중에도 유지돼야 하므로 React state 대신 ref로 보관한다.
const floodHistoryAreaLayerRef = useRef<VectorLayer<VectorSource> | null>(null);
const floodHistoryDepthLayerRef = useRef<VectorLayer<VectorSource> | null>(null);
const fldngHstryAreaLayerRef = useRef<VectorLayer<VectorSource> | null>(null);
const fldngHstryDepthLayerRef = useRef<VectorLayer<VectorSource> | null>(null);
const sinkholePopupCloseWatcherRef = useRef<number | null>(null);
const pipeAnalysisPopupWindowRef = useRef<Window | null>(null);
const gisEditSessionPopupWindowRef = useRef<Window | null>(null);
@@ -4289,10 +4289,10 @@ const Map = () => {
[clearSinkholeFocusedPipeLayer, ensureSinkholeFocusedPipeLayer],
);
const ensureFloodHistoryAreaSource = useCallback(() => {
const ensureFldngHstryAreaSource = useCallback(() => {
if (!mapReady || !gisMaps.current) return null;
if (!floodHistoryAreaLayerRef.current) {
if (!fldngHstryAreaLayerRef.current) {
const source = new VectorSource();
const layer = new VectorLayer({
source,
@@ -4303,16 +4303,16 @@ const Map = () => {
layer.set("lyrTyNm", FLOOD_HISTORY_LAYER_TYPE);
layer.set("clickPsblYn", "N");
gisMaps.current.addLayer(layer);
floodHistoryAreaLayerRef.current = layer;
fldngHstryAreaLayerRef.current = layer;
}
return floodHistoryAreaLayerRef.current.getSource();
return fldngHstryAreaLayerRef.current.getSource();
}, [mapReady]);
const ensureFloodHistoryDepthSource = useCallback(() => {
const ensureFldngHstryDepthSource = useCallback(() => {
if (!mapReady || !gisMaps.current) return null;
if (!floodHistoryDepthLayerRef.current) {
if (!fldngHstryDepthLayerRef.current) {
const source = new VectorSource();
const layer = new VectorLayer({
source,
@@ -4323,21 +4323,21 @@ const Map = () => {
layer.set("lyrTyNm", FLOOD_HISTORY_LAYER_TYPE);
layer.set("clickPsblYn", "N");
gisMaps.current.addLayer(layer);
floodHistoryDepthLayerRef.current = layer;
fldngHstryDepthLayerRef.current = layer;
}
return floodHistoryDepthLayerRef.current.getSource();
return fldngHstryDepthLayerRef.current.getSource();
}, [mapReady]);
const clearFloodHistoryLayers = useCallback(() => {
floodHistoryAreaLayerRef.current?.getSource?.()?.clear?.();
floodHistoryDepthLayerRef.current?.getSource?.()?.clear?.();
const clearFldngHstryLayers = useCallback(() => {
fldngHstryAreaLayerRef.current?.getSource?.()?.clear?.();
fldngHstryDepthLayerRef.current?.getSource?.()?.clear?.();
}, []);
const renderSelectedFloodHistory = useCallback(
(detail: FloodHistoryItem | null) => {
const areaSource = ensureFloodHistoryAreaSource();
const depthSource = ensureFloodHistoryDepthSource();
const renderSelectedFldngHstry = useCallback(
(detail: FldngHstryItem | null) => {
const areaSource = ensureFldngHstryAreaSource();
const depthSource = ensureFldngHstryDepthSource();
if (!areaSource || !depthSource) return;
areaSource.clear();
@@ -4348,21 +4348,21 @@ const Map = () => {
const areaFeature = new Feature({
geometry: new Polygon([detail.areaRing]),
});
areaFeature.setStyle(createFloodHistoryAreaStyle(`${detail.floodYear}년 ${detail.districtName}`));
areaFeature.setStyle(createFldngHstryAreaStyle(`${detail.floodYear}년 ${detail.districtName}`));
areaSource.addFeature(areaFeature);
detail.depthZones.forEach((zone) => {
const zoneFeature = new Feature({
geometry: new Polygon([zone.ring]),
});
zoneFeature.setStyle(createFloodHistoryDepthStyle(zone.depthLabel, zone.color));
zoneFeature.setStyle(createFldngHstryDepthStyle(zone.depthLabel, zone.color));
depthSource.addFeature(zoneFeature);
});
},
[ensureFloodHistoryAreaSource, ensureFloodHistoryDepthSource],
[ensureFldngHstryAreaSource, ensureFldngHstryDepthSource],
);
const focusFloodHistoryOnMap = useCallback((detail: FloodHistoryItem) => {
const focusFldngHstryOnMap = useCallback((detail: FldngHstryItem) => {
if (!gisMaps.current) return;
const geometry = new Polygon([detail.areaRing]);
@@ -4375,27 +4375,27 @@ const Map = () => {
});
}, []);
const clearFloodHistorySelection = useCallback(() => {
setSelectedFloodHistoryDetail(null);
setFloodHistoryDetailPopupPosition(null);
clearFloodHistoryLayers();
}, [clearFloodHistoryLayers]);
const clearFldngHstrySelection = useCallback(() => {
setSelectedFldngHstryDetail(null);
setFldngHstryDetailPopupPosition(null);
clearFldngHstryLayers();
}, [clearFldngHstryLayers]);
const closeFloodHistoryDetail = useCallback(() => {
setFloodHistoryDetailPopupPosition(null);
const closeFldngHstryDetail = useCallback(() => {
setFldngHstryDetailPopupPosition(null);
}, []);
const handleFloodHistoryRowSelect = useCallback(
(detail: FloodHistoryItem) => {
setSelectedFloodHistoryDetail(detail);
renderSelectedFloodHistory(detail);
focusFloodHistoryOnMap(detail);
const handleFldngHstryRowSelect = useCallback(
(detail: FldngHstryItem) => {
setSelectedFldngHstryDetail(detail);
renderSelectedFldngHstry(detail);
focusFldngHstryOnMap(detail);
setIsSearchModalOpen(false);
},
[focusFloodHistoryOnMap, renderSelectedFloodHistory, setIsSearchModalOpen],
[focusFldngHstryOnMap, renderSelectedFldngHstry, setIsSearchModalOpen],
);
const openFloodHistoryPopupWindow = useCallback(() => {
const openFldngHstryPopupWindow = useCallback(() => {
const popup = openWindowPopup(FLOOD_HISTORY_POPUP_KEY as WindowPopupKey);
if (!popup) {
alert("브라우저 팝업이 차단되었습니다. 팝업 허용 후 다시 시도해주세요.");
@@ -5105,16 +5105,16 @@ const Map = () => {
}, [loadSinkholeDetail, sinkholeScope]);
useEffect(() => {
const handleFloodHistoryPopupMessage = (event: MessageEvent) => {
const handleFldngHstryPopupMessage = (event: MessageEvent) => {
if (event.origin !== window.location.origin) return;
if (event.data?.type !== FLOOD_HISTORY_ROW_SELECT_MESSAGE || !event.data?.row) return;
if (event.data?.type !== FLDNG_HSTRY_ROW_SELECT_MESSAGE || !event.data?.row) return;
handleFloodHistoryRowSelect(event.data.row as FloodHistoryItem);
handleFldngHstryRowSelect(event.data.row as FldngHstryItem);
};
window.addEventListener("message", handleFloodHistoryPopupMessage);
return () => window.removeEventListener("message", handleFloodHistoryPopupMessage);
}, [handleFloodHistoryRowSelect]);
window.addEventListener("message", handleFldngHstryPopupMessage);
return () => window.removeEventListener("message", handleFldngHstryPopupMessage);
}, [handleFldngHstryRowSelect]);
// WaterDepth 표시 여부를 결정하는 함수
const handleWaterDepthVisibility = useCallback((checkedIds: string[], checkedNodesData: any[]) => {
@@ -5171,10 +5171,10 @@ const Map = () => {
WATER_DEPTH_LAYER_TYPES.includes(String(node?.data?.lyrTyNm ?? "")),
);
setIsAgedPipeChecked(agedPipeChecked);
const floodHistoryChecked = checkedNodesData.some((node) => isFloodHistoryNode(node));
setIsFloodHistoryChecked(floodHistoryChecked);
if (!floodHistoryChecked) {
clearFloodHistorySelection();
const fldngHstryChecked = checkedNodesData.some((node) => isFldngHstryNode(node));
setIsFldngHstryChecked(fldngHstryChecked);
if (!fldngHstryChecked) {
clearFldngHstrySelection();
}
const sinkholeChecked = checkedNodesData.some((node) => isSinkholeLayerNode(node));
setIsSinkholeChecked(sinkholeChecked);
@@ -5197,7 +5197,7 @@ const Map = () => {
removeSinkholeLayer();
closeSinkholeDetail();
}
}, [clearFloodHistorySelection, closeSinkholeDetail, handleWaterDepthVisibility, removeFlowDirectionLayer, removeSinkholeLayer, retainFlowDirectionUsageCodes]);
}, [clearFldngHstrySelection, closeSinkholeDetail, handleWaterDepthVisibility, removeFlowDirectionLayer, removeSinkholeLayer, retainFlowDirectionUsageCodes]);
const waterDepthLegendItems = useMemo(
() => extractWaterDepthLegendItems(checkedNodesDataRef.current ?? []),
@@ -5228,14 +5228,14 @@ const Map = () => {
openSinkholePopupWindow();
return false;
}
if (isFloodHistoryNode(node)) {
openFloodHistoryPopupWindow();
if (isFldngHstryNode(node)) {
openFldngHstryPopupWindow();
return false;
}
return true;
},
[openAgedPipeList, openFloodHistoryPopupWindow, openSinkholePopupWindow],
[openAgedPipeList, openFldngHstryPopupWindow, openSinkholePopupWindow],
);
// 현재 체크된 특수 레이어만 하단 설정영역에 모아 렌더링하기 위한 패널 목록이다.
@@ -7110,25 +7110,25 @@ const Map = () => {
}, [renderSelectedSinkhole, selectedSinkholeDetail, sinkholeRadiusMeters]);
useEffect(() => {
renderSelectedFloodHistory(selectedFloodHistoryDetail);
}, [renderSelectedFloodHistory, selectedFloodHistoryDetail]);
renderSelectedFldngHstry(selectedFldngHstryDetail);
}, [renderSelectedFldngHstry, selectedFldngHstryDetail]);
useEffect(() => {
if (!mapReady || !gisMaps.current) return;
const map = gisMaps.current;
const clickKey = map.on("singleclick", (event: any) => {
if (!selectedFloodHistoryDetail || !isFloodHistoryChecked) return;
if (!selectedFldngHstryDetail || !isFldngHstryChecked) return;
let clickedFloodHistory = false;
let clickedFldngHstry = false;
map.forEachFeatureAtPixel(
event.pixel,
(feature: any, layer: any) => {
if (
layer === floodHistoryAreaLayerRef.current ||
layer === floodHistoryDepthLayerRef.current
layer === fldngHstryAreaLayerRef.current ||
layer === fldngHstryDepthLayerRef.current
) {
clickedFloodHistory = true;
clickedFldngHstry = true;
return feature;
}
return undefined;
@@ -7136,14 +7136,14 @@ const Map = () => {
{ hitTolerance: 4 },
);
if (!clickedFloodHistory) return;
if (!clickedFldngHstry) return;
const size = map.getSize?.() ?? [1200, 800];
const popupWidth = 220;
const popupHeight = 210;
const nextLeft = Math.min(Math.max(event.pixel[0] + 12, 12), size[0] - popupWidth - 12);
const nextTop = Math.min(Math.max(event.pixel[1] - 12, 12), size[1] - popupHeight - 12);
setFloodHistoryDetailPopupPosition({
setFldngHstryDetailPopupPosition({
left: nextLeft,
top: nextTop,
});
@@ -7153,13 +7153,13 @@ const Map = () => {
return () => {
unByKey(clickKey);
};
}, [isFloodHistoryChecked, mapReady, selectedFloodHistoryDetail, setIsSearchModalOpen]);
}, [isFldngHstryChecked, mapReady, selectedFldngHstryDetail, setIsSearchModalOpen]);
useEffect(() => {
return () => {
clearFloodHistoryLayers();
clearFldngHstryLayers();
};
}, [clearFloodHistoryLayers]);
}, [clearFldngHstryLayers]);
return (
@@ -7287,11 +7287,11 @@ const Map = () => {
</div>
</div>
) : null}
{selectedFloodHistoryDetail && floodHistoryDetailPopupPosition ? (
{selectedFldngHstryDetail && fldngHstryDetailPopupPosition ? (
<FldngHstryDetailPanel
detail={selectedFloodHistoryDetail}
position={floodHistoryDetailPopupPosition}
onClose={closeFloodHistoryDetail}
detail={selectedFldngHstryDetail}
position={fldngHstryDetailPopupPosition}
onClose={closeFldngHstryDetail}
/>
) : null}
<AnalysisPopupMenu
@@ -7375,7 +7375,7 @@ const Map = () => {
treeTogglePosition="center"
zoomGuide={zoomGuide}
layerIconVisible={(node: any) =>
isAgedPipeLayerNode(node) || isSinkholeRootNode(node) || isFloodHistoryNode(node)
isAgedPipeLayerNode(node) || isSinkholeRootNode(node) || isFldngHstryNode(node)
}
defaultZoom={7.5}