sn: 침수흔적위선 심선/위선 모두 나오도록
This commit is contained in:
+172
-85
@@ -186,10 +186,17 @@ const FeatureClickEventPopup = ({
|
||||
const [floodDepthRows, setFloodDepthRows] = useState<
|
||||
Record<string, unknown>[]
|
||||
>([]);
|
||||
const [selectedFloodDepthYear, setSelectedFloodDepthYear] = useState<
|
||||
const [floodWtlvRows, setFloodWtlvRows] = useState<Record<string, unknown>[]>(
|
||||
[],
|
||||
);
|
||||
const [selectedFloodDepthFid, setSelectedFloodDepthFid] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const [selectedFloodWtlvFid, setSelectedFloodWtlvFid] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
const floodDepthYearListRef = useRef<HTMLDivElement>(null);
|
||||
const floodWtlvListRef = useRef<HTMLDivElement>(null);
|
||||
const { ctpv, sgg, fetchCtpv, fetchSgg } = useStore();
|
||||
|
||||
// 🎯 관로관리번호 추출
|
||||
@@ -850,7 +857,9 @@ const FeatureClickEventPopup = ({
|
||||
if (selectedLayerName === "침수흔적위선" && featureId) {
|
||||
const depthLookupKey = normalizeFldngWtlvMapApiFid(featureId);
|
||||
setFloodDepthRows([]);
|
||||
setSelectedFloodDepthYear(null);
|
||||
setFloodWtlvRows([]);
|
||||
setSelectedFloodDepthFid(null);
|
||||
setSelectedFloodWtlvFid(null);
|
||||
try {
|
||||
const normalizedFloodMapFid =
|
||||
normalizeFldngWtlvMapApiFid(featureId);
|
||||
@@ -877,18 +886,25 @@ const FeatureClickEventPopup = ({
|
||||
`v1/fldng/hstry/depth-map/${encodeURIComponent(depthLookupKey)}`,
|
||||
);
|
||||
const depthPayload = depthRows?.data ?? depthRows;
|
||||
const normalizedDepthRows = Array.isArray(depthPayload)
|
||||
? depthPayload.filter((row) => row && typeof row === "object")
|
||||
const depthItems = Array.isArray(depthPayload)
|
||||
? depthPayload
|
||||
: depthPayload?.depthMaps;
|
||||
const waterLevelItems = Array.isArray(
|
||||
depthPayload?.waterLevelMaps,
|
||||
)
|
||||
? depthPayload.waterLevelMaps
|
||||
: [];
|
||||
const normalizedDepthRows = Array.isArray(depthItems)
|
||||
? depthItems.filter((row) => row && typeof row === "object")
|
||||
: [];
|
||||
const normalizedWaterLevelRows = waterLevelItems.filter(
|
||||
(row: unknown) => row && typeof row === "object",
|
||||
);
|
||||
setFloodDepthRows(normalizedDepthRows);
|
||||
const years = normalizedDepthRows
|
||||
.map((row) =>
|
||||
String(row.fldng_yr ?? row.fldngYr ?? row.yr ?? "").trim(),
|
||||
)
|
||||
.filter(Boolean);
|
||||
console.log("depth years:", years);
|
||||
// 최초에는 연도를 선택하지 않고 목록만 표시한다.
|
||||
setSelectedFloodDepthYear(null);
|
||||
setFloodWtlvRows(normalizedWaterLevelRows);
|
||||
// 최초에는 연계 항목을 선택하지 않고 버튼 목록만 표시한다.
|
||||
setSelectedFloodDepthFid(null);
|
||||
setSelectedFloodWtlvFid(null);
|
||||
}
|
||||
} catch (depthError) {
|
||||
console.warn("침수깊이지도 조회 실패:", depthError);
|
||||
@@ -1097,82 +1113,153 @@ const FeatureClickEventPopup = ({
|
||||
>
|
||||
<VerticalTable
|
||||
headName={
|
||||
layerNm === "침수흔적위선" && floodDepthRows.length > 0 ? (
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
layerNm === "침수흔적위선" &&
|
||||
(floodDepthRows.length > 0 || floodWtlvRows.length > 0) ? (
|
||||
<div className="flex w-full min-w-0 flex-col items-start gap-2">
|
||||
<span>기본정보</span>
|
||||
<div className="flex w-full items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="이전 연도"
|
||||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||||
onClick={() =>
|
||||
floodDepthYearListRef.current?.scrollBy({
|
||||
left: -120,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<div
|
||||
ref={floodDepthYearListRef}
|
||||
className="flex min-w-0 flex-1 gap-2 overflow-x-auto whitespace-nowrap"
|
||||
>
|
||||
{Array.from(
|
||||
new Set(
|
||||
floodDepthRows
|
||||
.map((row) =>
|
||||
String(
|
||||
row.fldng_yr ?? row.fldngYr ?? row.yr ?? "",
|
||||
).trim(),
|
||||
)
|
||||
.filter(Boolean),
|
||||
),
|
||||
).map((year) => (
|
||||
<button
|
||||
key={year}
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
setSelectedFloodDepthYear(year);
|
||||
const selectedDepthRow = floodDepthRows.find(
|
||||
(row) =>
|
||||
String(
|
||||
row.fldng_yr ?? row.fldngYr ?? row.yr ?? "",
|
||||
).trim() === year,
|
||||
{floodDepthRows.length > 0 && (
|
||||
<div className="flex w-full items-center gap-1">
|
||||
<span className="w-9 shrink-0 text-[1.1rem] font-semibold">
|
||||
심선
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="이전 연도"
|
||||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||||
onClick={() =>
|
||||
floodDepthYearListRef.current?.scrollBy({
|
||||
left: -120,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<div
|
||||
ref={floodDepthYearListRef}
|
||||
className="flex min-w-0 flex-1 gap-2 overflow-x-auto whitespace-nowrap [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
>
|
||||
{[...floodDepthRows]
|
||||
.map((row, index) => ({
|
||||
row,
|
||||
index,
|
||||
year: String(
|
||||
row.fldng_yr ?? row.fldngYr ?? row.yr ?? "",
|
||||
).trim(),
|
||||
}))
|
||||
.filter(({ year }) => Boolean(year))
|
||||
.sort((left, right) => {
|
||||
const yearOrder = left.year.localeCompare(
|
||||
right.year,
|
||||
undefined,
|
||||
{ numeric: true },
|
||||
);
|
||||
const depthFid = String(
|
||||
selectedDepthRow?.fldng_dpth_map_sn ??
|
||||
selectedDepthRow?.fid ??
|
||||
"",
|
||||
).trim();
|
||||
if (depthFid) {
|
||||
const core = GisApp.Module.layer as any;
|
||||
await core.showSearchResultsFromGeoJSON?.(
|
||||
"tb_ms_fldng_dpth_map",
|
||||
`tb_ms_fldng_dpth_map.${depthFid.replace(/^tb_ms_fldng_dpth_map\./, "")}`,
|
||||
);
|
||||
}
|
||||
}}
|
||||
className={`rounded border px-3 py-1 text-[1.2rem] font-medium leading-5 ${selectedFloodDepthYear === year ? "border-blue-600 bg-white text-blue-700" : "border-border bg-muted text-muted-foreground"}`}
|
||||
>
|
||||
{year}년
|
||||
</button>
|
||||
))}
|
||||
return yearOrder !== 0
|
||||
? yearOrder
|
||||
: left.index - right.index;
|
||||
})
|
||||
.map(({ row, index, year }) => (
|
||||
<button
|
||||
key={`${String(row.fldng_dpth_map_sn ?? row.fid ?? "row")}-${index}`}
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
const depthFid = String(
|
||||
row.fldng_dpth_map_sn ?? row.fid ?? "",
|
||||
).trim();
|
||||
if (depthFid) {
|
||||
setSelectedFloodDepthFid(depthFid);
|
||||
setSelectedFloodWtlvFid(null);
|
||||
const core = GisApp.Module.layer as any;
|
||||
await core.showSearchResultsFromGeoJSON?.(
|
||||
"tb_ms_fldng_dpth_map",
|
||||
`tb_ms_fldng_dpth_map.${depthFid.replace(/^tb_ms_fldng_dpth_map\./, "")}`,
|
||||
);
|
||||
}
|
||||
}}
|
||||
className={`shrink-0 rounded border px-3 py-1 text-[1.2rem] font-medium leading-5 ${selectedFloodDepthFid === String(row.fldng_dpth_map_sn ?? row.fid ?? "").trim() ? "border-blue-600 bg-white text-blue-700" : "border-border bg-muted text-muted-foreground"}`}
|
||||
>
|
||||
{year}년
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="다음 연도"
|
||||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||||
onClick={() =>
|
||||
floodDepthYearListRef.current?.scrollBy({
|
||||
left: 120,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="다음 연도"
|
||||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||||
onClick={() =>
|
||||
floodDepthYearListRef.current?.scrollBy({
|
||||
left: 120,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{floodWtlvRows.length > 0 && (
|
||||
<div className="flex w-full items-center gap-1">
|
||||
<span className="w-9 shrink-0 text-[1.1rem] font-semibold">
|
||||
위선
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="이전 위선"
|
||||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||||
onClick={() =>
|
||||
floodWtlvListRef.current?.scrollBy({
|
||||
left: -120,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<div
|
||||
ref={floodWtlvListRef}
|
||||
className="flex min-w-0 flex-1 gap-2 overflow-x-auto whitespace-nowrap [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
>
|
||||
{floodWtlvRows.map((row, index) => {
|
||||
const waterLevelFid = String(row.fid ?? "").trim();
|
||||
const disasterName = String(
|
||||
row.fldng_dstr_nm ?? row.fldngDstrNm ?? "",
|
||||
).trim();
|
||||
return (
|
||||
<button
|
||||
key={`${waterLevelFid || "wtlv"}-${index}`}
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
if (!waterLevelFid) return;
|
||||
setSelectedFloodWtlvFid(waterLevelFid);
|
||||
setSelectedFloodDepthFid(null);
|
||||
const core = GisApp.Module.layer as any;
|
||||
await core.showSearchResultsFromGeoJSON?.(
|
||||
"tb_ms_fldng_wtlv_map",
|
||||
`tb_ms_fldng_wtlv_map.${waterLevelFid.replace(/^tb_ms_fldng_wtlv_map\./, "")}`,
|
||||
);
|
||||
}}
|
||||
className={`shrink-0 rounded border px-3 py-1 text-[1.2rem] font-medium leading-5 ${selectedFloodWtlvFid === waterLevelFid ? "border-blue-600 bg-white text-blue-700" : "border-border bg-muted text-muted-foreground"}`}
|
||||
>
|
||||
{disasterName || "재해명 없음"}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
aria-label="다음 위선"
|
||||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||||
onClick={() =>
|
||||
floodWtlvListRef.current?.scrollBy({
|
||||
left: 120,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : layerNm === "하수관로" && tableData?.elapsedYears ? (
|
||||
<>
|
||||
|
||||
@@ -5208,6 +5208,30 @@ const Map = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 연계 심선/위선 조회는 공용 search-layer를 교체한다. 이때 탭에 보관된
|
||||
// 기존 침수흔적 feature는 더 이상 지도 레이어에 속하지 않으므로, 단순히
|
||||
// Select 컬렉션에 다시 넣어도 강조가 렌더링되지 않는다. 탭으로 돌아올
|
||||
// 때는 FID로 search-layer를 복원한 뒤 로드된 feature를 선택하게 한다.
|
||||
if (
|
||||
layerTypeName === FLOOD_HISTORY_LAYER_TYPE ||
|
||||
layerTypeName === "tb_ms_fldng_dpth_map"
|
||||
) {
|
||||
clearSinkholeFocusedPipeLayer();
|
||||
const featureIdentifier = getRightPanelFeatureIdentifier(data).replace(
|
||||
new RegExp(`^${layerTypeName}\\.`),
|
||||
"",
|
||||
);
|
||||
if (featureIdentifier) {
|
||||
const core = GisApp.Module.layer as any;
|
||||
void core?.showSearchResultsFromGeoJSON?.(
|
||||
layerTypeName,
|
||||
`${layerTypeName}.${featureIdentifier}`,
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data || typeof data.getGeometry !== "function") {
|
||||
clearSinkholeFocusedPipeLayer();
|
||||
|
||||
|
||||
@@ -165,7 +165,9 @@ export function VerticalTable({
|
||||
<>
|
||||
{headName ? (
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="mx-3 text-2xl font-medium text-label">{headName}</h3>
|
||||
<h3 className="mx-3 min-w-0 flex-1 text-2xl font-medium text-label">
|
||||
{headName}
|
||||
</h3>
|
||||
{showSaveButton && onSaveClick && (
|
||||
<Button type="button" color="primary" onClick={onSaveClick}>
|
||||
저장
|
||||
|
||||
Reference in New Issue
Block a user