feat: 항적분석(구역 검색) 기능 구현
구역 기반 선박 항적 검색 기능 추가. 사용자가 지도에 최대 3개 구역을
그리고 ANY/ALL/SEQUENTIAL 조건으로 해당 구역을 통과한 선박의 항적을
조회·재생할 수 있다.
신규 패키지 (src/areaSearch/):
- stores: areaSearchStore, areaSearchAnimationStore (재생 제어)
- services: areaSearchApi (REST API + hitDetails 타임스탬프/위치 보간)
- components: AreaSearchPage, ZoneDrawPanel, AreaSearchTimeline, AreaSearchTooltip
- hooks: useAreaSearchLayer (Deck.gl 레이어), useZoneDraw (OL Draw)
- utils: areaSearchLayerRegistry, csvExport (BOM+UTF-8 엑셀 호환)
- types: areaSearch.types (상수, 색상, 모드)
주요 기능:
- 폴리곤/사각형/원 구역 그리기 + 드래그 순서 변경
- 구역별 색상 구분 (빨강/청록/황색)
- 시간 기반 애니메이션 재생 (TripsLayer 궤적 + 가상선박 이동)
- 선종/개별 선박 필터링, 항적 표시/궤적 표시 토글
- 호버 툴팁 (국기 SVG, 구역별 진입/진출 시각·위치)
- CSV 내보내기 (신호원, 식별번호, 국적 ISO 변환, 구역 통과 정보)
기존 파일 수정:
- SideNav/Sidebar: gnb8 '항적분석' 메뉴 활성화
- useShipLayer: areaSearch 레이어 병합
- MapContainer: useAreaSearchLayer 훅 + 호버 핸들러 + 타임라인 렌더링
- trackLayer: layerIds 파라미터 추가 (area search/track query 레이어 ID 분리)
- ShipLegend: 항적분석 모드 선종 카운트 지원
- countryCodeUtils: MMSI MID→ISO alpha-2 매핑 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 12:29:31 +09:00
|
|
|
/**
|
|
|
|
|
* 항적분석 검색 결과 CSV 내보내기
|
|
|
|
|
* BOM + UTF-8 인코딩 (한글 엑셀 호환)
|
|
|
|
|
*/
|
|
|
|
|
import { getShipKindName, getSignalSourceName } from '../../tracking/types/trackQuery.types';
|
|
|
|
|
import { getCountryIsoCode } from '../../replay/components/VesselListManager/utils/countryCodeUtils';
|
|
|
|
|
|
|
|
|
|
function formatTimestamp(ms) {
|
|
|
|
|
if (!ms) return '';
|
|
|
|
|
const d = new Date(ms);
|
|
|
|
|
const pad = (n) => String(n).padStart(2, '0');
|
|
|
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatPosition(pos) {
|
|
|
|
|
if (!pos || pos.length < 2) return '';
|
|
|
|
|
const lon = pos[0];
|
|
|
|
|
const lat = pos[1];
|
|
|
|
|
const latDir = lat >= 0 ? 'N' : 'S';
|
|
|
|
|
const lonDir = lon >= 0 ? 'E' : 'W';
|
|
|
|
|
return `${Math.abs(lat).toFixed(4)}\u00B0${latDir} ${Math.abs(lon).toFixed(4)}\u00B0${lonDir}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function escapeCsvField(value) {
|
|
|
|
|
const str = String(value ?? '');
|
|
|
|
|
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
|
|
|
|
|
return `"${str.replace(/"/g, '""')}"`;
|
|
|
|
|
}
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
feat: STS 분석 기능 구현 및 항적분석 고도화
- STS(Ship-to-Ship) 접촉 분석 기능 전체 구현
- API 연동 (vessel-contacts), 스토어, 레이어 훅, 레이어 레지스트리
- 접촉 쌍 그룹핑, 그룹 카드 목록, 상세 모달 (그리드 레이아웃)
- ScatterplotLayer 접촉 포인트 + 위험도 색상
- 항적분석 탭 UI 분리 (구역분석 / STS분석)
- AreaSearchPage → AreaSearchTab, StsAnalysisTab 추출
- 탭 전환 시 결과 초기화 확인, 구역 클리어
- 지도 호버 하이라이트 구현 (구역분석 + STS)
- MapContainer pointermove에 STS 레이어 ID 핸들러 추가
- STS 쌍 항적 동시 하이라이트 (vesselId → groupIndex 매핑)
- 목록↔지도 호버 연동 자동 스크롤
- pickingRadius 12→20 확대
- 재생 컨트롤러(AreaSearchTimeline) STS 지원
- 항적/궤적 토글 activeTab 기반 스토어 분기
- 닫기 시 양쪽 스토어 + 레이어 정리
- 패널 닫기 초기화 수정 (isOpen 감지, clearResults로 탭 보존)
- 조회 중 로딩 오버레이 (LoadingOverlay 공통 컴포넌트)
- 항적분석 다중 방문 대응, 선박 상세 모달, 구역 편집 기능
- trackLayer updateTriggers Set 직렬화, highlightedVesselIds 지원
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 06:20:46 +09:00
|
|
|
* 검색 결과를 CSV로 내보내기 (다중 방문 동적 컬럼 지원)
|
feat: 항적분석(구역 검색) 기능 구현
구역 기반 선박 항적 검색 기능 추가. 사용자가 지도에 최대 3개 구역을
그리고 ANY/ALL/SEQUENTIAL 조건으로 해당 구역을 통과한 선박의 항적을
조회·재생할 수 있다.
신규 패키지 (src/areaSearch/):
- stores: areaSearchStore, areaSearchAnimationStore (재생 제어)
- services: areaSearchApi (REST API + hitDetails 타임스탬프/위치 보간)
- components: AreaSearchPage, ZoneDrawPanel, AreaSearchTimeline, AreaSearchTooltip
- hooks: useAreaSearchLayer (Deck.gl 레이어), useZoneDraw (OL Draw)
- utils: areaSearchLayerRegistry, csvExport (BOM+UTF-8 엑셀 호환)
- types: areaSearch.types (상수, 색상, 모드)
주요 기능:
- 폴리곤/사각형/원 구역 그리기 + 드래그 순서 변경
- 구역별 색상 구분 (빨강/청록/황색)
- 시간 기반 애니메이션 재생 (TripsLayer 궤적 + 가상선박 이동)
- 선종/개별 선박 필터링, 항적 표시/궤적 표시 토글
- 호버 툴팁 (국기 SVG, 구역별 진입/진출 시각·위치)
- CSV 내보내기 (신호원, 식별번호, 국적 ISO 변환, 구역 통과 정보)
기존 파일 수정:
- SideNav/Sidebar: gnb8 '항적분석' 메뉴 활성화
- useShipLayer: areaSearch 레이어 병합
- MapContainer: useAreaSearchLayer 훅 + 호버 핸들러 + 타임라인 렌더링
- trackLayer: layerIds 파라미터 추가 (area search/track query 레이어 ID 분리)
- ShipLegend: 항적분석 모드 선종 카운트 지원
- countryCodeUtils: MMSI MID→ISO alpha-2 매핑 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 12:29:31 +09:00
|
|
|
*
|
|
|
|
|
* @param {Array} tracks ProcessedTrack 배열
|
feat: STS 분석 기능 구현 및 항적분석 고도화
- STS(Ship-to-Ship) 접촉 분석 기능 전체 구현
- API 연동 (vessel-contacts), 스토어, 레이어 훅, 레이어 레지스트리
- 접촉 쌍 그룹핑, 그룹 카드 목록, 상세 모달 (그리드 레이아웃)
- ScatterplotLayer 접촉 포인트 + 위험도 색상
- 항적분석 탭 UI 분리 (구역분석 / STS분석)
- AreaSearchPage → AreaSearchTab, StsAnalysisTab 추출
- 탭 전환 시 결과 초기화 확인, 구역 클리어
- 지도 호버 하이라이트 구현 (구역분석 + STS)
- MapContainer pointermove에 STS 레이어 ID 핸들러 추가
- STS 쌍 항적 동시 하이라이트 (vesselId → groupIndex 매핑)
- 목록↔지도 호버 연동 자동 스크롤
- pickingRadius 12→20 확대
- 재생 컨트롤러(AreaSearchTimeline) STS 지원
- 항적/궤적 토글 activeTab 기반 스토어 분기
- 닫기 시 양쪽 스토어 + 레이어 정리
- 패널 닫기 초기화 수정 (isOpen 감지, clearResults로 탭 보존)
- 조회 중 로딩 오버레이 (LoadingOverlay 공통 컴포넌트)
- 항적분석 다중 방문 대응, 선박 상세 모달, 구역 편집 기능
- trackLayer updateTriggers Set 직렬화, highlightedVesselIds 지원
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 06:20:46 +09:00
|
|
|
* @param {Object} hitDetails { vesselId: [{ polygonId, visitIndex, entryTimestamp, exitTimestamp }] }
|
feat: 항적분석(구역 검색) 기능 구현
구역 기반 선박 항적 검색 기능 추가. 사용자가 지도에 최대 3개 구역을
그리고 ANY/ALL/SEQUENTIAL 조건으로 해당 구역을 통과한 선박의 항적을
조회·재생할 수 있다.
신규 패키지 (src/areaSearch/):
- stores: areaSearchStore, areaSearchAnimationStore (재생 제어)
- services: areaSearchApi (REST API + hitDetails 타임스탬프/위치 보간)
- components: AreaSearchPage, ZoneDrawPanel, AreaSearchTimeline, AreaSearchTooltip
- hooks: useAreaSearchLayer (Deck.gl 레이어), useZoneDraw (OL Draw)
- utils: areaSearchLayerRegistry, csvExport (BOM+UTF-8 엑셀 호환)
- types: areaSearch.types (상수, 색상, 모드)
주요 기능:
- 폴리곤/사각형/원 구역 그리기 + 드래그 순서 변경
- 구역별 색상 구분 (빨강/청록/황색)
- 시간 기반 애니메이션 재생 (TripsLayer 궤적 + 가상선박 이동)
- 선종/개별 선박 필터링, 항적 표시/궤적 표시 토글
- 호버 툴팁 (국기 SVG, 구역별 진입/진출 시각·위치)
- CSV 내보내기 (신호원, 식별번호, 국적 ISO 변환, 구역 통과 정보)
기존 파일 수정:
- SideNav/Sidebar: gnb8 '항적분석' 메뉴 활성화
- useShipLayer: areaSearch 레이어 병합
- MapContainer: useAreaSearchLayer 훅 + 호버 핸들러 + 타임라인 렌더링
- trackLayer: layerIds 파라미터 추가 (area search/track query 레이어 ID 분리)
- ShipLegend: 항적분석 모드 선종 카운트 지원
- countryCodeUtils: MMSI MID→ISO alpha-2 매핑 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 12:29:31 +09:00
|
|
|
* @param {Array} zones 구역 배열
|
|
|
|
|
*/
|
|
|
|
|
export function exportSearchResultToCSV(tracks, hitDetails, zones) {
|
feat: STS 분석 기능 구현 및 항적분석 고도화
- STS(Ship-to-Ship) 접촉 분석 기능 전체 구현
- API 연동 (vessel-contacts), 스토어, 레이어 훅, 레이어 레지스트리
- 접촉 쌍 그룹핑, 그룹 카드 목록, 상세 모달 (그리드 레이아웃)
- ScatterplotLayer 접촉 포인트 + 위험도 색상
- 항적분석 탭 UI 분리 (구역분석 / STS분석)
- AreaSearchPage → AreaSearchTab, StsAnalysisTab 추출
- 탭 전환 시 결과 초기화 확인, 구역 클리어
- 지도 호버 하이라이트 구현 (구역분석 + STS)
- MapContainer pointermove에 STS 레이어 ID 핸들러 추가
- STS 쌍 항적 동시 하이라이트 (vesselId → groupIndex 매핑)
- 목록↔지도 호버 연동 자동 스크롤
- pickingRadius 12→20 확대
- 재생 컨트롤러(AreaSearchTimeline) STS 지원
- 항적/궤적 토글 activeTab 기반 스토어 분기
- 닫기 시 양쪽 스토어 + 레이어 정리
- 패널 닫기 초기화 수정 (isOpen 감지, clearResults로 탭 보존)
- 조회 중 로딩 오버레이 (LoadingOverlay 공통 컴포넌트)
- 항적분석 다중 방문 대응, 선박 상세 모달, 구역 편집 기능
- trackLayer updateTriggers Set 직렬화, highlightedVesselIds 지원
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 06:20:46 +09:00
|
|
|
// 구역별 최대 방문 횟수 계산
|
|
|
|
|
const maxVisitsPerZone = {};
|
|
|
|
|
zones.forEach((z) => { maxVisitsPerZone[z.id] = 1; });
|
|
|
|
|
Object.values(hitDetails).forEach((hits) => {
|
|
|
|
|
const countByZone = {};
|
|
|
|
|
(Array.isArray(hits) ? hits : []).forEach((h) => {
|
|
|
|
|
countByZone[h.polygonId] = (countByZone[h.polygonId] || 0) + 1;
|
|
|
|
|
});
|
|
|
|
|
for (const [zoneId, count] of Object.entries(countByZone)) {
|
|
|
|
|
maxVisitsPerZone[zoneId] = Math.max(maxVisitsPerZone[zoneId] || 0, count);
|
|
|
|
|
}
|
|
|
|
|
});
|
feat: 항적분석(구역 검색) 기능 구현
구역 기반 선박 항적 검색 기능 추가. 사용자가 지도에 최대 3개 구역을
그리고 ANY/ALL/SEQUENTIAL 조건으로 해당 구역을 통과한 선박의 항적을
조회·재생할 수 있다.
신규 패키지 (src/areaSearch/):
- stores: areaSearchStore, areaSearchAnimationStore (재생 제어)
- services: areaSearchApi (REST API + hitDetails 타임스탬프/위치 보간)
- components: AreaSearchPage, ZoneDrawPanel, AreaSearchTimeline, AreaSearchTooltip
- hooks: useAreaSearchLayer (Deck.gl 레이어), useZoneDraw (OL Draw)
- utils: areaSearchLayerRegistry, csvExport (BOM+UTF-8 엑셀 호환)
- types: areaSearch.types (상수, 색상, 모드)
주요 기능:
- 폴리곤/사각형/원 구역 그리기 + 드래그 순서 변경
- 구역별 색상 구분 (빨강/청록/황색)
- 시간 기반 애니메이션 재생 (TripsLayer 궤적 + 가상선박 이동)
- 선종/개별 선박 필터링, 항적 표시/궤적 표시 토글
- 호버 툴팁 (국기 SVG, 구역별 진입/진출 시각·위치)
- CSV 내보내기 (신호원, 식별번호, 국적 ISO 변환, 구역 통과 정보)
기존 파일 수정:
- SideNav/Sidebar: gnb8 '항적분석' 메뉴 활성화
- useShipLayer: areaSearch 레이어 병합
- MapContainer: useAreaSearchLayer 훅 + 호버 핸들러 + 타임라인 렌더링
- trackLayer: layerIds 파라미터 추가 (area search/track query 레이어 ID 분리)
- ShipLegend: 항적분석 모드 선종 카운트 지원
- countryCodeUtils: MMSI MID→ISO alpha-2 매핑 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 12:29:31 +09:00
|
|
|
|
|
|
|
|
// 헤더 구성
|
|
|
|
|
const baseHeaders = [
|
|
|
|
|
'신호원', '식별번호', '선박명', '선종', '국적',
|
|
|
|
|
'포인트수', '이동거리(NM)', '평균속도(kn)', '최대속도(kn)',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const zoneHeaders = [];
|
feat: STS 분석 기능 구현 및 항적분석 고도화
- STS(Ship-to-Ship) 접촉 분석 기능 전체 구현
- API 연동 (vessel-contacts), 스토어, 레이어 훅, 레이어 레지스트리
- 접촉 쌍 그룹핑, 그룹 카드 목록, 상세 모달 (그리드 레이아웃)
- ScatterplotLayer 접촉 포인트 + 위험도 색상
- 항적분석 탭 UI 분리 (구역분석 / STS분석)
- AreaSearchPage → AreaSearchTab, StsAnalysisTab 추출
- 탭 전환 시 결과 초기화 확인, 구역 클리어
- 지도 호버 하이라이트 구현 (구역분석 + STS)
- MapContainer pointermove에 STS 레이어 ID 핸들러 추가
- STS 쌍 항적 동시 하이라이트 (vesselId → groupIndex 매핑)
- 목록↔지도 호버 연동 자동 스크롤
- pickingRadius 12→20 확대
- 재생 컨트롤러(AreaSearchTimeline) STS 지원
- 항적/궤적 토글 activeTab 기반 스토어 분기
- 닫기 시 양쪽 스토어 + 레이어 정리
- 패널 닫기 초기화 수정 (isOpen 감지, clearResults로 탭 보존)
- 조회 중 로딩 오버레이 (LoadingOverlay 공통 컴포넌트)
- 항적분석 다중 방문 대응, 선박 상세 모달, 구역 편집 기능
- trackLayer updateTriggers Set 직렬화, highlightedVesselIds 지원
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 06:20:46 +09:00
|
|
|
zones.forEach((zone) => {
|
|
|
|
|
const max = maxVisitsPerZone[zone.id] || 1;
|
|
|
|
|
if (max === 1) {
|
|
|
|
|
zoneHeaders.push(
|
|
|
|
|
`구역${zone.name}_진입시각`, `구역${zone.name}_진입위치`,
|
|
|
|
|
`구역${zone.name}_진출시각`, `구역${zone.name}_진출위치`,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
for (let v = 1; v <= max; v++) {
|
|
|
|
|
zoneHeaders.push(
|
|
|
|
|
`구역${zone.name}_${v}차_진입시각`, `구역${zone.name}_${v}차_진입위치`,
|
|
|
|
|
`구역${zone.name}_${v}차_진출시각`, `구역${zone.name}_${v}차_진출위치`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
feat: 항적분석(구역 검색) 기능 구현
구역 기반 선박 항적 검색 기능 추가. 사용자가 지도에 최대 3개 구역을
그리고 ANY/ALL/SEQUENTIAL 조건으로 해당 구역을 통과한 선박의 항적을
조회·재생할 수 있다.
신규 패키지 (src/areaSearch/):
- stores: areaSearchStore, areaSearchAnimationStore (재생 제어)
- services: areaSearchApi (REST API + hitDetails 타임스탬프/위치 보간)
- components: AreaSearchPage, ZoneDrawPanel, AreaSearchTimeline, AreaSearchTooltip
- hooks: useAreaSearchLayer (Deck.gl 레이어), useZoneDraw (OL Draw)
- utils: areaSearchLayerRegistry, csvExport (BOM+UTF-8 엑셀 호환)
- types: areaSearch.types (상수, 색상, 모드)
주요 기능:
- 폴리곤/사각형/원 구역 그리기 + 드래그 순서 변경
- 구역별 색상 구분 (빨강/청록/황색)
- 시간 기반 애니메이션 재생 (TripsLayer 궤적 + 가상선박 이동)
- 선종/개별 선박 필터링, 항적 표시/궤적 표시 토글
- 호버 툴팁 (국기 SVG, 구역별 진입/진출 시각·위치)
- CSV 내보내기 (신호원, 식별번호, 국적 ISO 변환, 구역 통과 정보)
기존 파일 수정:
- SideNav/Sidebar: gnb8 '항적분석' 메뉴 활성화
- useShipLayer: areaSearch 레이어 병합
- MapContainer: useAreaSearchLayer 훅 + 호버 핸들러 + 타임라인 렌더링
- trackLayer: layerIds 파라미터 추가 (area search/track query 레이어 ID 분리)
- ShipLegend: 항적분석 모드 선종 카운트 지원
- countryCodeUtils: MMSI MID→ISO alpha-2 매핑 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 12:29:31 +09:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const headers = [...baseHeaders, ...zoneHeaders];
|
|
|
|
|
|
|
|
|
|
// 데이터 행 생성
|
|
|
|
|
const rows = tracks.map((track) => {
|
|
|
|
|
const baseRow = [
|
|
|
|
|
getSignalSourceName(track.sigSrcCd),
|
|
|
|
|
track.targetId || '',
|
|
|
|
|
track.shipName || '',
|
|
|
|
|
getShipKindName(track.shipKindCode),
|
|
|
|
|
track.nationalCode ? getCountryIsoCode(track.nationalCode) : '',
|
|
|
|
|
track.stats?.pointCount ?? track.geometry.length,
|
|
|
|
|
track.stats?.totalDistance != null ? track.stats.totalDistance.toFixed(2) : '',
|
|
|
|
|
track.stats?.avgSpeed != null ? track.stats.avgSpeed.toFixed(1) : '',
|
|
|
|
|
track.stats?.maxSpeed != null ? track.stats.maxSpeed.toFixed(1) : '',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const hits = hitDetails[track.vesselId] || [];
|
|
|
|
|
const zoneData = [];
|
|
|
|
|
zones.forEach((zone) => {
|
feat: STS 분석 기능 구현 및 항적분석 고도화
- STS(Ship-to-Ship) 접촉 분석 기능 전체 구현
- API 연동 (vessel-contacts), 스토어, 레이어 훅, 레이어 레지스트리
- 접촉 쌍 그룹핑, 그룹 카드 목록, 상세 모달 (그리드 레이아웃)
- ScatterplotLayer 접촉 포인트 + 위험도 색상
- 항적분석 탭 UI 분리 (구역분석 / STS분석)
- AreaSearchPage → AreaSearchTab, StsAnalysisTab 추출
- 탭 전환 시 결과 초기화 확인, 구역 클리어
- 지도 호버 하이라이트 구현 (구역분석 + STS)
- MapContainer pointermove에 STS 레이어 ID 핸들러 추가
- STS 쌍 항적 동시 하이라이트 (vesselId → groupIndex 매핑)
- 목록↔지도 호버 연동 자동 스크롤
- pickingRadius 12→20 확대
- 재생 컨트롤러(AreaSearchTimeline) STS 지원
- 항적/궤적 토글 activeTab 기반 스토어 분기
- 닫기 시 양쪽 스토어 + 레이어 정리
- 패널 닫기 초기화 수정 (isOpen 감지, clearResults로 탭 보존)
- 조회 중 로딩 오버레이 (LoadingOverlay 공통 컴포넌트)
- 항적분석 다중 방문 대응, 선박 상세 모달, 구역 편집 기능
- trackLayer updateTriggers Set 직렬화, highlightedVesselIds 지원
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 06:20:46 +09:00
|
|
|
const max = maxVisitsPerZone[zone.id] || 1;
|
|
|
|
|
const zoneHits = hits
|
|
|
|
|
.filter((h) => h.polygonId === zone.id)
|
|
|
|
|
.sort((a, b) => (a.visitIndex || 1) - (b.visitIndex || 1));
|
|
|
|
|
|
|
|
|
|
for (let v = 0; v < max; v++) {
|
|
|
|
|
const hit = zoneHits[v];
|
|
|
|
|
if (hit) {
|
|
|
|
|
zoneData.push(
|
|
|
|
|
formatTimestamp(hit.entryTimestamp),
|
|
|
|
|
formatPosition(hit.entryPosition),
|
|
|
|
|
formatTimestamp(hit.exitTimestamp),
|
|
|
|
|
formatPosition(hit.exitPosition),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
zoneData.push('', '', '', '');
|
|
|
|
|
}
|
feat: 항적분석(구역 검색) 기능 구현
구역 기반 선박 항적 검색 기능 추가. 사용자가 지도에 최대 3개 구역을
그리고 ANY/ALL/SEQUENTIAL 조건으로 해당 구역을 통과한 선박의 항적을
조회·재생할 수 있다.
신규 패키지 (src/areaSearch/):
- stores: areaSearchStore, areaSearchAnimationStore (재생 제어)
- services: areaSearchApi (REST API + hitDetails 타임스탬프/위치 보간)
- components: AreaSearchPage, ZoneDrawPanel, AreaSearchTimeline, AreaSearchTooltip
- hooks: useAreaSearchLayer (Deck.gl 레이어), useZoneDraw (OL Draw)
- utils: areaSearchLayerRegistry, csvExport (BOM+UTF-8 엑셀 호환)
- types: areaSearch.types (상수, 색상, 모드)
주요 기능:
- 폴리곤/사각형/원 구역 그리기 + 드래그 순서 변경
- 구역별 색상 구분 (빨강/청록/황색)
- 시간 기반 애니메이션 재생 (TripsLayer 궤적 + 가상선박 이동)
- 선종/개별 선박 필터링, 항적 표시/궤적 표시 토글
- 호버 툴팁 (국기 SVG, 구역별 진입/진출 시각·위치)
- CSV 내보내기 (신호원, 식별번호, 국적 ISO 변환, 구역 통과 정보)
기존 파일 수정:
- SideNav/Sidebar: gnb8 '항적분석' 메뉴 활성화
- useShipLayer: areaSearch 레이어 병합
- MapContainer: useAreaSearchLayer 훅 + 호버 핸들러 + 타임라인 렌더링
- trackLayer: layerIds 파라미터 추가 (area search/track query 레이어 ID 분리)
- ShipLegend: 항적분석 모드 선종 카운트 지원
- countryCodeUtils: MMSI MID→ISO alpha-2 매핑 추가
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 12:29:31 +09:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return [...baseRow, ...zoneData];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// CSV 문자열 생성
|
|
|
|
|
const csvLines = [
|
|
|
|
|
headers.map(escapeCsvField).join(','),
|
|
|
|
|
...rows.map((row) => row.map(escapeCsvField).join(',')),
|
|
|
|
|
];
|
|
|
|
|
const csvContent = csvLines.join('\n');
|
|
|
|
|
|
|
|
|
|
// BOM + UTF-8 Blob 생성 및 다운로드
|
|
|
|
|
const BOM = '\uFEFF';
|
|
|
|
|
const blob = new Blob([BOM + csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
|
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const pad = (n) => String(n).padStart(2, '0');
|
|
|
|
|
const filename = `항적분석_${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}.csv`;
|
|
|
|
|
|
|
|
|
|
const link = document.createElement('a');
|
|
|
|
|
link.href = url;
|
|
|
|
|
link.download = filename;
|
|
|
|
|
link.click();
|
|
|
|
|
URL.revokeObjectURL(url);
|
|
|
|
|
}
|