sn: 침수발생이력 변수명 수정
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user