Files
SPipe/src/MapHeader.tsx
T
bjkim1 21ebb16fb7 SPipe 데모 v2 (2026-10-30) 서버 배포본
- SPipe_server_2026-10-30.zip 내용을 풀어서 등록 (압축 파일은 올리지 않음)
- README에 데모 v2 서버 업데이트 순서 추가, 저장소에 없는 문서 목차 정리
- 패치내역을 docs/로 이동, _worklog.md 추가
- 임시 파일(backend/temp.*)과 API 키가 든 파일은 제외
2026-10-30 11:00:46 +09:00

133 lines
5.8 KiB
TypeScript

import React from 'react';
import { BasemapType, DisplayMode, type SimulationResult } from './types';
import { AddressSearch } from './AddressSearch';
import { CRS } from './crs';
import { type MapFocusRequest } from './Map';
export interface MapHeaderProps {
crs: CRS;
basemap: BasemapType;
displayMode: DisplayMode;
setCrs: React.Dispatch<React.SetStateAction<CRS>>;
crsSelectRef: React.RefObject<HTMLSelectElement>;
setBasemap: React.Dispatch<React.SetStateAction<BasemapType>>;
setDisplayMode: React.Dispatch<React.SetStateAction<DisplayMode>>;
requestMapFocus: (request: Omit<MapFocusRequest, 'id'>) => void;
showToast: (msg: string) => void;
wasmResult: SimulationResult | null;
}
/** Map pane toolbar: CRS, basemap and result display selectors. */
export const MapHeader: React.FC<MapHeaderProps> = ({
crs,
basemap,
displayMode,
setCrs,
crsSelectRef,
setBasemap,
setDisplayMode,
requestMapFocus,
showToast,
wasmResult,
}) => (
<div className="map-header">
{/* Right toolbar: Basemap, CRS, Data */}
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
{/* CRS Selector */}
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
<select
ref={crsSelectRef}
value={crs}
onChange={(e) => setCrs(e.target.value as CRS)}
style={{ fontSize: '12px', padding: '3px 6px', border: '1px solid #cbd5e1', borderRadius: '4px', maxWidth: '175px' }}
aria-label="좌표계 선택"
>
<optgroup label="기본 및 글로벌">
<option value="LOCAL">로컬 평면 (Local)</option>
<option value="EPSG:4326">WGS84 경위도 (4326)</option>
<option value="EPSG:3857">Web Mercator (3857)</option>
</optgroup>
<optgroup label="국가표준 GRS80 (현행 법정)">
<option value="EPSG:5179">한국 UTM-K (5179)</option>
<option value="EPSG:5186">중부원점 GRS80 (5186)</option>
<option value="EPSG:5185">서부원점 GRS80 (5185)</option>
<option value="EPSG:5187">동부원점 GRS80 (5187)</option>
<option value="EPSG:5188">동해원점 GRS80 (5188)</option>
<option value="EPSG:5180">제주원점 GRS80 (5180)</option>
</optgroup>
<optgroup label="포털 지도">
<option value="EPSG:5181">카카오맵 중부 (5181)</option>
</optgroup>
<optgroup label="구 지적 / Bessel (상수도 구 GIS)">
<option value="EPSG:5174">구 지적 중부 (5174)</option>
<option value="EPSG:5176">구 기준점 중부 (5176)</option>
<option value="EPSG:5173">구 지적 서부 (5173)</option>
<option value="EPSG:5177">구 지적 동부 (5177)</option>
<option value="EPSG:5175">구 지적 제주 (5175)</option>
</optgroup>
<optgroup label="KGD2002 과도기 (Y+500,000)">
<option value="EPSG:102080">KGD2002 중부 (102080)</option>
<option value="EPSG:102081">KGD2002 서부 (102081)</option>
<option value="EPSG:102082">KGD2002 동부 (102082)</option>
<option value="EPSG:102083">KGD2002 동해 (102083)</option>
</optgroup>
</select>
</div>
{/* Basemap Selector */}
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
<span style={{ fontSize: '12px', color: 'var(--text-secondary)' }}>지도:</span>
<select
value={basemap}
onChange={(e) => setBasemap(e.target.value as BasemapType)}
style={{ fontSize: '12px', padding: '3px 6px', border: '1px solid #cbd5e1', borderRadius: '4px' }}
>
<option value="blank">단색 배경</option>
<option value="osm">OpenStreetMap</option>
<optgroup label="브이월드 (VWorld)">
<option value="vworld">일반 지도</option>
<option value="vworld-white">백지도</option>
<option value="vworld-midnight">야간 지도</option>
<option value="vworld-satellite">위성 영상</option>
<option value="vworld-satellite-hybrid">위성 + 도로·지명</option>
</optgroup>
</select>
</div>
{/* Data display mode */}
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
<span style={{ fontSize: '12px', color: 'var(--text-secondary)' }}>표시:</span>
<select
value={displayMode}
onChange={(e) =>
setDisplayMode(e.target.value as DisplayMode)
}
style={{ fontSize: '12px', padding: '3px 6px', border: '1px solid #cbd5e1', borderRadius: '4px' }}
>
<option value="pressure">압력 (Pressure)</option>
<option value="head">수두 (Head)</option>
<option value="flow">유량 (Flow)</option>
<option value="velocity">유속 (Velocity)</option>
<option value="headloss">수두손실 (Headloss)</option>
<option value="elevation">지형 표고 (Elevation)</option>
<option value="diff-pressure">수압 변화량 (Diff ΔP)</option>
<option value="diff-flow">유량 변화량 (Diff ΔQ)</option>
<option value="quality">
수질 (Quality - {wasmResult?.qualityInfo?.chemName || '농도/수령'})
</option>
</select>
</div>
</div>
<AddressSearch
onSelect={(result) => {
requestMapFocus({
lngLat: [result.longitude, result.latitude],
zoom: 17,
});
setBasemap('vworld');
showToast(`주소로 이동: ${result.roadAddressName || result.parcelAddressName || result.addressName}`);
}}
/>
</div>
);