@mint-ui/map 1.2.0-test.34 → 1.2.0-test.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/mint-map/core/advanced/shared/context.d.ts +19 -12
- package/dist/components/mint-map/core/advanced/shared/context.js +54 -75
- package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +20 -0
- package/dist/components/mint-map/core/advanced/shared/helpers.js +40 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +74 -0
- package/dist/components/mint-map/core/advanced/shared/hooks.js +189 -0
- package/dist/components/mint-map/core/advanced/shared/index.d.ts +3 -0
- package/dist/components/mint-map/core/advanced/shared/performance.d.ts +12 -110
- package/dist/components/mint-map/core/advanced/shared/performance.js +56 -151
- package/dist/components/mint-map/core/advanced/shared/types.d.ts +18 -153
- package/dist/components/mint-map/core/advanced/shared/types.js +0 -1
- package/dist/components/mint-map/core/advanced/shared/utils.d.ts +36 -27
- package/dist/components/mint-map/core/advanced/shared/utils.js +58 -52
- package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +42 -0
- package/dist/components/mint-map/core/advanced/shared/viewport.js +51 -0
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.d.ts +22 -74
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.js +156 -617
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.d.ts +26 -76
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.js +152 -551
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.d.ts +67 -8
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.js +81 -20
- package/dist/index.es.js +917 -1575
- package/dist/index.js +11 -0
- package/dist/index.umd.js +923 -1573
- package/package.json +1 -1
|
@@ -2,24 +2,22 @@ import Konva from "konva";
|
|
|
2
2
|
import { Position, Offset } from "../../../types";
|
|
3
3
|
/**
|
|
4
4
|
* 캔버스 데이터 타입 Enum
|
|
5
|
-
* 마커인지 폴리곤인지 구분하는 상수
|
|
6
5
|
*/
|
|
7
6
|
export declare enum CanvasDataType {
|
|
8
7
|
MARKER = "MARKER",
|
|
9
8
|
POLYGON = "POLYGON"
|
|
10
9
|
}
|
|
11
10
|
/**
|
|
12
|
-
* 폴리곤 경로 정의
|
|
11
|
+
* 폴리곤 경로 정의 (GeoJSON MultiPolygon 형식)
|
|
13
12
|
*/
|
|
14
13
|
export interface Paths {
|
|
15
14
|
type: string;
|
|
16
15
|
coordinates: number[][][][];
|
|
17
16
|
}
|
|
18
17
|
/**
|
|
19
|
-
*
|
|
20
|
-
* (렌더링에 필요한 최소 정보)
|
|
18
|
+
* 캔버스 마커/폴리곤의 기본 필수 속성
|
|
21
19
|
*/
|
|
22
|
-
export interface
|
|
20
|
+
export interface CanvasOption {
|
|
23
21
|
id: string;
|
|
24
22
|
position: Position;
|
|
25
23
|
boxWidth?: number;
|
|
@@ -30,196 +28,63 @@ export interface KonvaCanvasOption {
|
|
|
30
28
|
}
|
|
31
29
|
/**
|
|
32
30
|
* 서버 데이터와 캔버스 옵션을 결합한 타입
|
|
33
|
-
*
|
|
34
|
-
* @
|
|
35
|
-
* // API에서 받은 Marker 타입을 그대로 유지하면서 캔버스 렌더링 정보 추가
|
|
36
|
-
* type MarkerWithCanvas = KonvaCanvasData<Marker>
|
|
37
|
-
* // { raId, lat, lng, buildingName, totalArea } + { id, position, boxWidth, ... }
|
|
31
|
+
*
|
|
32
|
+
* @template T 서버에서 받은 원본 데이터 타입
|
|
38
33
|
*/
|
|
39
|
-
export declare type
|
|
34
|
+
export declare type CanvasData<T = {}> = T & CanvasOption;
|
|
40
35
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* WoongCanvasMarker/WoongCanvasPolygon이 제공하는 헬퍼 함수 모음입니다.
|
|
44
|
-
* 커스텀 렌더링 함수 내에서 좌표 변환 시 사용하세요.
|
|
45
|
-
*
|
|
46
|
-
* ## 주요 기능
|
|
47
|
-
* - **자동 캐싱**: 좌표 변환 결과를 LRU 캐시에 저장 (성능 최적화)
|
|
48
|
-
* - **지도 좌표 → 화면 좌표**: 위경도를 픽셀 좌표로 자동 변환
|
|
49
|
-
* - **null 안전성**: 변환 실패 시 null 반환 (안전한 예외 처리)
|
|
36
|
+
* 렌더링 유틸리티 함수들 (좌표 변환)
|
|
50
37
|
*
|
|
51
38
|
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* // 마커 렌더링 예시
|
|
55
|
-
* const renderBase = ({ ctx, items, utils }) => {
|
|
56
|
-
* for (const item of items) {
|
|
57
|
-
* const offset = utils.getOrComputeMarkerOffset(item);
|
|
58
|
-
* if (!offset) continue; // 변환 실패 시 스킵
|
|
59
|
-
*
|
|
60
|
-
* ctx.fillRect(offset.x, offset.y, 50, 50);
|
|
61
|
-
* }
|
|
62
|
-
* };
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* // 폴리곤 렌더링 예시
|
|
66
|
-
* const renderBase = ({ ctx, items, utils }) => {
|
|
67
|
-
* for (const item of items) {
|
|
68
|
-
* const offsets = utils.getOrComputePolygonOffsets(item);
|
|
69
|
-
* if (!offsets) continue;
|
|
70
|
-
*
|
|
71
|
-
* for (const multiPolygon of offsets) {
|
|
72
|
-
* for (const polygon of multiPolygon) {
|
|
73
|
-
* ctx.beginPath();
|
|
74
|
-
* ctx.moveTo(polygon[0][0], polygon[0][1]);
|
|
75
|
-
* for (let i = 1; i < polygon.length; i++) {
|
|
76
|
-
* ctx.lineTo(polygon[i][0], polygon[i][1]);
|
|
77
|
-
* }
|
|
78
|
-
* ctx.closePath();
|
|
79
|
-
* ctx.fill();
|
|
80
|
-
* }
|
|
81
|
-
* }
|
|
82
|
-
* }
|
|
83
|
-
* };
|
|
84
39
|
*/
|
|
85
40
|
export interface RenderUtils<T> {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
* - **자동 캐싱**: 동일한 폴리곤은 캐시에서 즉시 반환 (성능 최적화)
|
|
90
|
-
* - **MultiPolygon 지원**: GeoJSON MultiPolygon 형식 지원
|
|
91
|
-
* - **Donut Polygon 지원**: 구멍이 있는 폴리곤 지원
|
|
92
|
-
*
|
|
93
|
-
* @param polygonData 폴리곤 데이터 (paths 필드 필수)
|
|
94
|
-
* @returns 변환된 픽셀 좌표 배열 (4차원 배열) 또는 null (변환 실패 시)
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* const offsets = utils.getOrComputePolygonOffsets(polygonItem);
|
|
98
|
-
* if (!offsets) return; // 변환 실패
|
|
99
|
-
*
|
|
100
|
-
* // offsets 구조: [MultiPolygon][Polygon][Point][x/y]
|
|
101
|
-
* for (const multiPolygon of offsets) {
|
|
102
|
-
* for (const polygon of multiPolygon) {
|
|
103
|
-
* ctx.beginPath();
|
|
104
|
-
* ctx.moveTo(polygon[0][0], polygon[0][1]);
|
|
105
|
-
* for (let i = 1; i < polygon.length; i++) {
|
|
106
|
-
* ctx.lineTo(polygon[i][0], polygon[i][1]);
|
|
107
|
-
* }
|
|
108
|
-
* ctx.closePath();
|
|
109
|
-
* ctx.fill();
|
|
110
|
-
* }
|
|
111
|
-
* }
|
|
112
|
-
*/
|
|
113
|
-
getOrComputePolygonOffsets: (polygonData: KonvaCanvasData<T>) => number[][][][] | null;
|
|
114
|
-
/**
|
|
115
|
-
* 마커의 위경도 좌표를 화면 픽셀 좌표로 변환합니다.
|
|
116
|
-
*
|
|
117
|
-
* - **자동 캐싱**: 동일한 마커는 캐시에서 즉시 반환 (성능 최적화)
|
|
118
|
-
* - **중심점 기준**: 반환된 좌표는 마커의 중심점 (x, y)
|
|
119
|
-
*
|
|
120
|
-
* @param markerData 마커 데이터 (position 필드 필수)
|
|
121
|
-
* @returns 변환된 픽셀 좌표 { x, y } 또는 null (변환 실패 시)
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* const offset = utils.getOrComputeMarkerOffset(markerItem);
|
|
125
|
-
* if (!offset) return; // 변환 실패
|
|
126
|
-
*
|
|
127
|
-
* // offset.x, offset.y는 화면 픽셀 좌표
|
|
128
|
-
* const boxWidth = markerItem.boxWidth || 60;
|
|
129
|
-
* const boxHeight = markerItem.boxHeight || 75;
|
|
130
|
-
*
|
|
131
|
-
* // 중앙 정렬, 하단 기준으로 그리기
|
|
132
|
-
* ctx.fillRect(
|
|
133
|
-
* offset.x - boxWidth / 2,
|
|
134
|
-
* offset.y - boxHeight,
|
|
135
|
-
* boxWidth,
|
|
136
|
-
* boxHeight
|
|
137
|
-
* );
|
|
138
|
-
*/
|
|
139
|
-
getOrComputeMarkerOffset: (markerData: KonvaCanvasData<T>) => Offset | null;
|
|
41
|
+
getOrComputePolygonOffsets: (polygonData: CanvasData<T>) => number[][][][] | null;
|
|
42
|
+
getOrComputeMarkerOffset: (markerData: CanvasData<T>) => Offset | null;
|
|
140
43
|
}
|
|
141
44
|
/**
|
|
142
45
|
* 커스텀 렌더링 함수 파라미터 - Base Layer
|
|
143
46
|
*/
|
|
144
47
|
export interface RenderBaseParams<T> {
|
|
145
|
-
/** Canvas 2D 렌더링 컨텍스트 (순수 Canvas API) */
|
|
146
48
|
ctx: CanvasRenderingContext2D;
|
|
147
|
-
|
|
148
|
-
items: KonvaCanvasData<T>[];
|
|
149
|
-
/** 현재 선택된 마커/폴리곤 ID Set */
|
|
49
|
+
items: CanvasData<T>[];
|
|
150
50
|
selectedIds: Set<string>;
|
|
151
|
-
|
|
152
|
-
hoveredItem?: KonvaCanvasData<T> | null;
|
|
153
|
-
/** 렌더링 유틸리티 함수들 */
|
|
51
|
+
hoveredItem?: CanvasData<T> | null;
|
|
154
52
|
utils: RenderUtils<T>;
|
|
155
53
|
}
|
|
156
54
|
/**
|
|
157
55
|
* 커스텀 렌더링 함수 타입 - Base Layer
|
|
158
56
|
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* const renderBase = ({ ctx, items, selectedIds, utils }) => {
|
|
163
|
-
* for (const item of items) {
|
|
164
|
-
* if (selectedIds.has(item.id)) continue;
|
|
165
|
-
* const pos = utils.getOrComputeMarkerOffset(item);
|
|
166
|
-
* ctx.fillRect(pos.x, pos.y, 50, 50); // 순수 Canvas API!
|
|
167
|
-
* }
|
|
168
|
-
* };
|
|
57
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
169
58
|
*/
|
|
170
59
|
export declare type CustomRenderBase<T> = (params: RenderBaseParams<T>) => void;
|
|
171
60
|
/**
|
|
172
61
|
* 커스텀 렌더링 함수 파라미터 - Animation Layer
|
|
173
62
|
*/
|
|
174
63
|
export interface RenderAnimationParams<T> {
|
|
175
|
-
/** Konva Layer 인스턴스 */
|
|
176
64
|
layer: Konva.Layer;
|
|
177
|
-
/** 현재 선택된 마커/폴리곤 ID Set */
|
|
178
65
|
selectedIds: Set<string>;
|
|
179
|
-
|
|
180
|
-
items: KonvaCanvasData<T>[];
|
|
181
|
-
/** 렌더링 유틸리티 함수들 */
|
|
66
|
+
items: CanvasData<T>[];
|
|
182
67
|
utils: RenderUtils<T>;
|
|
183
68
|
}
|
|
184
69
|
/**
|
|
185
70
|
* 커스텀 렌더링 함수 타입 - Animation Layer (선택)
|
|
186
71
|
*
|
|
187
|
-
* @
|
|
188
|
-
* const renderAnimation = ({ layer, selectedIds, items, utils }) => {
|
|
189
|
-
* for (const id of selectedIds) {
|
|
190
|
-
* const item = items.find(i => i.id === id);
|
|
191
|
-
* // Konva 애니메이션 구현
|
|
192
|
-
* }
|
|
193
|
-
* };
|
|
72
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
194
73
|
*/
|
|
195
74
|
export declare type CustomRenderAnimation<T> = (params: RenderAnimationParams<T>) => void;
|
|
196
75
|
/**
|
|
197
76
|
* 커스텀 렌더링 함수 파라미터 - Event Layer
|
|
198
77
|
*/
|
|
199
78
|
export interface RenderEventParams<T> {
|
|
200
|
-
/** Canvas 2D 렌더링 컨텍스트 (순수 Canvas API) */
|
|
201
79
|
ctx: CanvasRenderingContext2D;
|
|
202
|
-
|
|
203
|
-
hoveredItem: KonvaCanvasData<T> | null;
|
|
204
|
-
/** 렌더링 유틸리티 함수들 */
|
|
80
|
+
hoveredItem: CanvasData<T> | null;
|
|
205
81
|
utils: RenderUtils<T>;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
/** 외부에서 전달된 단일 선택 아이템 (특별한 효과용) */
|
|
209
|
-
selectedItem?: KonvaCanvasData<T> | null;
|
|
82
|
+
selectedItems?: CanvasData<T>[];
|
|
83
|
+
selectedItem?: CanvasData<T> | null;
|
|
210
84
|
}
|
|
211
85
|
/**
|
|
212
86
|
* 커스텀 렌더링 함수 타입 - Event Layer
|
|
213
87
|
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* @example
|
|
217
|
-
* const renderEvent = ({ ctx, hoveredItem, utils, selectedItems }) => {
|
|
218
|
-
* if (hoveredItem) {
|
|
219
|
-
* const pos = utils.getOrComputeMarkerOffset(hoveredItem);
|
|
220
|
-
* ctx.fillStyle = 'red';
|
|
221
|
-
* ctx.fillRect(pos.x, pos.y, 50, 50); // 순수 Canvas API!
|
|
222
|
-
* }
|
|
223
|
-
* };
|
|
88
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
224
89
|
*/
|
|
225
90
|
export declare type CustomRenderEvent<T> = (params: RenderEventParams<T>) => void;
|
|
@@ -1,49 +1,58 @@
|
|
|
1
1
|
import { Offset } from "../../../types";
|
|
2
2
|
import { MintMapController } from "../../MintMapController";
|
|
3
|
-
import {
|
|
3
|
+
import { CanvasData } from "./types";
|
|
4
4
|
/**
|
|
5
|
-
* 폴리곤
|
|
5
|
+
* 폴리곤 좌표 변환 (위경도 → 화면 좌표)
|
|
6
|
+
*
|
|
7
|
+
* @param polygonData 폴리곤 데이터
|
|
8
|
+
* @param controller MintMapController 인스턴스
|
|
9
|
+
* @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
|
|
6
10
|
*/
|
|
7
|
-
export declare const computePolygonOffsets: (polygonData:
|
|
11
|
+
export declare const computePolygonOffsets: (polygonData: CanvasData<any>, controller: MintMapController) => number[][][][] | null;
|
|
8
12
|
/**
|
|
9
|
-
* 마커
|
|
13
|
+
* 마커 좌표 변환 (위경도 → 화면 좌표)
|
|
14
|
+
*
|
|
15
|
+
* @param markerData 마커 데이터
|
|
16
|
+
* @param controller MintMapController 인스턴스
|
|
17
|
+
* @returns 변환된 화면 좌표 또는 null
|
|
10
18
|
*/
|
|
11
|
-
export declare const computeMarkerOffset: (markerData:
|
|
19
|
+
export declare const computeMarkerOffset: (markerData: CanvasData<any>, controller: MintMapController) => Offset | null;
|
|
12
20
|
/**
|
|
13
|
-
* Point-in-Polygon 알고리즘
|
|
21
|
+
* Point-in-Polygon 알고리즘 (Ray Casting)
|
|
22
|
+
*
|
|
23
|
+
* @param point 확인할 점의 좌표
|
|
24
|
+
* @param polygon 폴리곤 좌표 배열
|
|
25
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
14
26
|
*/
|
|
15
27
|
export declare const isPointInPolygon: (point: Offset, polygon: number[][]) => boolean;
|
|
16
28
|
/**
|
|
17
29
|
* 폴리곤 히트 테스트 (도넛 폴리곤 지원)
|
|
18
30
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* 중요: 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됨
|
|
24
|
-
* - 도넛 폴리곤 A: isDonutPolygon=true
|
|
25
|
-
* - 내부 폴리곤 B: isDonutPolygon=false (별도 데이터)
|
|
31
|
+
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
32
|
+
* @param polygonData 폴리곤 데이터
|
|
33
|
+
* @param getPolygonOffsets 폴리곤 좌표 변환 함수
|
|
34
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
26
35
|
*/
|
|
27
|
-
export declare const isPointInPolygonData: (clickedOffset: Offset, polygonData:
|
|
36
|
+
export declare const isPointInPolygonData: (clickedOffset: Offset, polygonData: CanvasData<any>, getPolygonOffsets: (data: CanvasData<any>) => number[][][][] | null) => boolean;
|
|
28
37
|
/**
|
|
29
|
-
* 마커 히트 테스트 (
|
|
38
|
+
* 마커 히트 테스트 (꼬리 제외)
|
|
30
39
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
40
|
+
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
41
|
+
* @param markerData 마커 데이터
|
|
42
|
+
* @param getMarkerOffset 마커 좌표 변환 함수
|
|
43
|
+
* @returns 점이 마커 영역 내부에 있으면 true
|
|
35
44
|
*/
|
|
36
|
-
export declare const isPointInMarkerData: (clickedOffset: Offset, markerData:
|
|
45
|
+
export declare const isPointInMarkerData: (clickedOffset: Offset, markerData: CanvasData<any>, getMarkerOffset: (data: CanvasData<any>) => Offset | null) => boolean;
|
|
37
46
|
export declare const hexToRgba: (hexColor: string, alpha?: number) => string;
|
|
38
47
|
/**
|
|
39
|
-
* 텍스트
|
|
48
|
+
* 텍스트 박스 너비 계산
|
|
40
49
|
*
|
|
41
|
-
* @param
|
|
42
|
-
* @param
|
|
43
|
-
* @param
|
|
44
|
-
* @param
|
|
45
|
-
* @param
|
|
46
|
-
* @returns
|
|
50
|
+
* @param params 파라미터 객체
|
|
51
|
+
* @param params.text 측정할 텍스트
|
|
52
|
+
* @param params.fontConfig 폰트 설정
|
|
53
|
+
* @param params.padding 패딩 값 (px)
|
|
54
|
+
* @param params.minWidth 최소 너비 (px)
|
|
55
|
+
* @returns 계산된 텍스트 박스 너비 (px)
|
|
47
56
|
*/
|
|
48
57
|
export declare const calculateTextBoxWidth: ({ text, fontConfig, padding, minWidth, }: {
|
|
49
58
|
text: string;
|
|
@@ -7,7 +7,11 @@ var MapTypes = require('../../../types/MapTypes.js');
|
|
|
7
7
|
require('../../../types/MapEventTypes.js');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* 폴리곤
|
|
10
|
+
* 폴리곤 좌표 변환 (위경도 → 화면 좌표)
|
|
11
|
+
*
|
|
12
|
+
* @param polygonData 폴리곤 데이터
|
|
13
|
+
* @param controller MintMapController 인스턴스
|
|
14
|
+
* @returns 변환된 화면 좌표 배열 (4차원 배열) 또는 null
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
var computePolygonOffsets = function (polygonData, controller) {
|
|
@@ -17,7 +21,7 @@ var computePolygonOffsets = function (polygonData, controller) {
|
|
|
17
21
|
return null;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
var result = [];
|
|
24
|
+
var result = []; // GeoJSON MultiPolygon 구조: [MultiPolygon][PolygonGroup][Coordinate][lng, lat]
|
|
21
25
|
|
|
22
26
|
for (var _i = 0, _a = paths.coordinates; _i < _a.length; _i++) {
|
|
23
27
|
var multiPolygon = _a[_i];
|
|
@@ -28,7 +32,8 @@ var computePolygonOffsets = function (polygonData, controller) {
|
|
|
28
32
|
var polygonOffsets = [];
|
|
29
33
|
|
|
30
34
|
for (var _c = 0, polygonGroup_1 = polygonGroup; _c < polygonGroup_1.length; _c++) {
|
|
31
|
-
var coord = polygonGroup_1[_c];
|
|
35
|
+
var coord = polygonGroup_1[_c]; // GeoJSON은 [lng, lat] 순서이지만 Position은 [lat, lng] 순서
|
|
36
|
+
|
|
32
37
|
var pos = new MapTypes.Position(coord[1], coord[0]);
|
|
33
38
|
var offset = controller.positionToOffset(pos);
|
|
34
39
|
polygonOffsets.push([offset.x, offset.y]);
|
|
@@ -43,30 +48,37 @@ var computePolygonOffsets = function (polygonData, controller) {
|
|
|
43
48
|
return result;
|
|
44
49
|
};
|
|
45
50
|
/**
|
|
46
|
-
* 마커
|
|
51
|
+
* 마커 좌표 변환 (위경도 → 화면 좌표)
|
|
52
|
+
*
|
|
53
|
+
* @param markerData 마커 데이터
|
|
54
|
+
* @param controller MintMapController 인스턴스
|
|
55
|
+
* @returns 변환된 화면 좌표 또는 null
|
|
47
56
|
*/
|
|
48
57
|
|
|
49
58
|
var computeMarkerOffset = function (markerData, controller) {
|
|
50
|
-
if (!markerData.position)
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
59
|
+
if (!markerData.position) return null;
|
|
54
60
|
return controller.positionToOffset(markerData.position);
|
|
55
61
|
};
|
|
56
62
|
/**
|
|
57
|
-
* Point-in-Polygon 알고리즘
|
|
63
|
+
* Point-in-Polygon 알고리즘 (Ray Casting)
|
|
64
|
+
*
|
|
65
|
+
* @param point 확인할 점의 좌표
|
|
66
|
+
* @param polygon 폴리곤 좌표 배열
|
|
67
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
58
68
|
*/
|
|
59
69
|
|
|
60
70
|
var isPointInPolygon = function (point, polygon) {
|
|
71
|
+
// Ray Casting 알고리즘: 점에서 오른쪽으로 무한히 뻗은 선과 폴리곤 변의 교차 횟수로 판단
|
|
61
72
|
var inside = false;
|
|
62
73
|
|
|
63
74
|
for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
64
75
|
var xi = polygon[i][0],
|
|
65
76
|
yi = polygon[i][1];
|
|
66
77
|
var xj = polygon[j][0],
|
|
67
|
-
yj = polygon[j][1];
|
|
78
|
+
yj = polygon[j][1]; // 점의 y 좌표가 변의 양 끝점 사이에 있고, 교차점의 x 좌표가 점의 x 좌표보다 큰지 확인
|
|
79
|
+
|
|
68
80
|
var intersect = yi > point.y !== yj > point.y && point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi;
|
|
69
|
-
if (intersect) inside = !inside;
|
|
81
|
+
if (intersect) inside = !inside; // 교차할 때마다 inside 상태 토글
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
return inside;
|
|
@@ -74,23 +86,20 @@ var isPointInPolygon = function (point, polygon) {
|
|
|
74
86
|
/**
|
|
75
87
|
* 폴리곤 히트 테스트 (도넛 폴리곤 지원)
|
|
76
88
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* 중요: 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됨
|
|
82
|
-
* - 도넛 폴리곤 A: isDonutPolygon=true
|
|
83
|
-
* - 내부 폴리곤 B: isDonutPolygon=false (별도 데이터)
|
|
89
|
+
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
90
|
+
* @param polygonData 폴리곤 데이터
|
|
91
|
+
* @param getPolygonOffsets 폴리곤 좌표 변환 함수
|
|
92
|
+
* @returns 점이 폴리곤 내부에 있으면 true
|
|
84
93
|
*/
|
|
85
94
|
|
|
86
95
|
var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffsets) {
|
|
87
96
|
var polygonOffsets = getPolygonOffsets(polygonData);
|
|
88
|
-
if (!polygonOffsets) return false; //
|
|
97
|
+
if (!polygonOffsets) return false; // 도넛 폴리곤 처리: 외부 폴리곤 내부에 있으면서 구멍(hole) 내부에 있지 않아야 함
|
|
89
98
|
|
|
90
99
|
if (polygonData.isDonutPolygon) {
|
|
91
100
|
for (var _i = 0, polygonOffsets_1 = polygonOffsets; _i < polygonOffsets_1.length; _i++) {
|
|
92
101
|
var multiPolygon = polygonOffsets_1[_i];
|
|
93
|
-
if (multiPolygon.length === 0) continue; //
|
|
102
|
+
if (multiPolygon.length === 0) continue; // 구멍이 없는 경우 일반 폴리곤과 동일
|
|
94
103
|
|
|
95
104
|
if (multiPolygon.length === 1) {
|
|
96
105
|
if (isPointInPolygon(clickedOffset, multiPolygon[0])) {
|
|
@@ -98,33 +107,30 @@ var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffse
|
|
|
98
107
|
}
|
|
99
108
|
|
|
100
109
|
continue;
|
|
101
|
-
} //
|
|
110
|
+
} // 외부 폴리곤 내부에 있는지 확인
|
|
102
111
|
|
|
103
112
|
|
|
104
113
|
var outerPolygon = multiPolygon[0];
|
|
105
114
|
|
|
106
115
|
if (!isPointInPolygon(clickedOffset, outerPolygon)) {
|
|
107
|
-
continue;
|
|
108
|
-
} //
|
|
116
|
+
continue;
|
|
117
|
+
} // 구멍 내부에 있으면 false (도넛의 빈 공간)
|
|
109
118
|
|
|
110
119
|
|
|
111
120
|
for (var i = 1; i < multiPolygon.length; i++) {
|
|
112
121
|
var hole = multiPolygon[i];
|
|
113
122
|
|
|
114
123
|
if (isPointInPolygon(clickedOffset, hole)) {
|
|
115
|
-
// ❌ 구멍 안에 있음 → 이 도넛 폴리곤은 히트 안 됨
|
|
116
|
-
// 다른 multiPolygon 체크하지 않고 바로 false 반환
|
|
117
|
-
// (도넛 폴리곤의 구멍 안은 무조건 클릭 불가)
|
|
118
124
|
return false;
|
|
119
125
|
}
|
|
120
|
-
} //
|
|
126
|
+
} // 외부 폴리곤 내부에 있으면서 모든 구멍 밖에 있으면 true
|
|
121
127
|
|
|
122
128
|
|
|
123
129
|
return true;
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
return false;
|
|
127
|
-
} // 일반 폴리곤 처리
|
|
133
|
+
} // 일반 폴리곤 처리
|
|
128
134
|
|
|
129
135
|
|
|
130
136
|
for (var _a = 0, polygonOffsets_2 = polygonOffsets; _a < polygonOffsets_2.length; _a++) {
|
|
@@ -143,12 +149,12 @@ var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffse
|
|
|
143
149
|
return false;
|
|
144
150
|
};
|
|
145
151
|
/**
|
|
146
|
-
* 마커 히트 테스트 (
|
|
152
|
+
* 마커 히트 테스트 (꼬리 제외)
|
|
147
153
|
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
154
|
+
* @param clickedOffset 클릭/마우스 위치 좌표
|
|
155
|
+
* @param markerData 마커 데이터
|
|
156
|
+
* @param getMarkerOffset 마커 좌표 변환 함수
|
|
157
|
+
* @returns 점이 마커 영역 내부에 있으면 true
|
|
152
158
|
*/
|
|
153
159
|
|
|
154
160
|
var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset) {
|
|
@@ -156,41 +162,41 @@ var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset)
|
|
|
156
162
|
if (!markerOffset) return false;
|
|
157
163
|
var boxWidth = markerData.boxWidth || 50;
|
|
158
164
|
var boxHeight = markerData.boxHeight || 28;
|
|
159
|
-
var tailHeight = markerData.tailHeight || 0; //
|
|
165
|
+
var tailHeight = markerData.tailHeight || 0; // 마커 중심점 기준으로 박스 영역 계산 (꼬리는 제외)
|
|
160
166
|
|
|
161
167
|
var x = markerOffset.x - boxWidth / 2;
|
|
162
|
-
var y = markerOffset.y - boxHeight - tailHeight; //
|
|
168
|
+
var y = markerOffset.y - boxHeight - tailHeight; // 클릭 위치가 박스 영역 내부에 있는지 확인
|
|
163
169
|
|
|
164
170
|
return clickedOffset.x >= x && clickedOffset.x <= x + boxWidth && clickedOffset.y >= y && clickedOffset.y <= y + boxHeight;
|
|
165
|
-
};
|
|
171
|
+
}; // Hex 색상을 RGBA로 변환
|
|
172
|
+
|
|
166
173
|
var hexToRgba = function (hexColor, alpha) {
|
|
167
174
|
if (alpha === void 0) {
|
|
168
175
|
alpha = 1;
|
|
169
|
-
}
|
|
170
|
-
|
|
176
|
+
}
|
|
171
177
|
|
|
172
|
-
var hex = hexColor.replace('#', '');
|
|
178
|
+
var hex = hexColor.replace('#', '');
|
|
173
179
|
|
|
174
|
-
if (hex.length
|
|
175
|
-
|
|
176
|
-
var g = parseInt(hex.substring(2, 4), 16);
|
|
177
|
-
var b = parseInt(hex.substring(4, 6), 16);
|
|
178
|
-
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
|
|
180
|
+
if (hex.length !== 6) {
|
|
181
|
+
throw new Error('Invalid hex color format');
|
|
179
182
|
}
|
|
180
183
|
|
|
181
|
-
|
|
184
|
+
var r = parseInt(hex.substring(0, 2), 16);
|
|
185
|
+
var g = parseInt(hex.substring(2, 4), 16);
|
|
186
|
+
var b = parseInt(hex.substring(4, 6), 16);
|
|
187
|
+
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
|
|
182
188
|
};
|
|
183
189
|
var tempCanvas = document.createElement('canvas');
|
|
184
190
|
var tempCtx = tempCanvas.getContext('2d');
|
|
185
191
|
/**
|
|
186
|
-
* 텍스트
|
|
192
|
+
* 텍스트 박스 너비 계산
|
|
187
193
|
*
|
|
188
|
-
* @param
|
|
189
|
-
* @param
|
|
190
|
-
* @param
|
|
191
|
-
* @param
|
|
192
|
-
* @param
|
|
193
|
-
* @returns
|
|
194
|
+
* @param params 파라미터 객체
|
|
195
|
+
* @param params.text 측정할 텍스트
|
|
196
|
+
* @param params.fontConfig 폰트 설정
|
|
197
|
+
* @param params.padding 패딩 값 (px)
|
|
198
|
+
* @param params.minWidth 최소 너비 (px)
|
|
199
|
+
* @returns 계산된 텍스트 박스 너비 (px)
|
|
194
200
|
*/
|
|
195
201
|
|
|
196
202
|
var calculateTextBoxWidth = function (_a) {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { MutableRefObject } from "react";
|
|
2
|
+
import { CanvasData } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* 뷰포트 영역 (화면에 보이는 영역)
|
|
5
|
+
*/
|
|
6
|
+
export interface ViewportBounds {
|
|
7
|
+
minX: number;
|
|
8
|
+
maxX: number;
|
|
9
|
+
minY: number;
|
|
10
|
+
maxY: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 바운딩 박스 (마커/폴리곤의 최소/최대 좌표)
|
|
14
|
+
*/
|
|
15
|
+
export interface BoundingBox {
|
|
16
|
+
minX: number;
|
|
17
|
+
minY: number;
|
|
18
|
+
maxX: number;
|
|
19
|
+
maxY: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 현재 뷰포트 영역 계산
|
|
23
|
+
*
|
|
24
|
+
* @param stage Konva Stage 인스턴스
|
|
25
|
+
* @param cullingMargin 컬링 여유 공간 (px)
|
|
26
|
+
* @param viewportRef 뷰포트 경계를 저장할 ref
|
|
27
|
+
*/
|
|
28
|
+
export declare const updateViewport: (stage: {
|
|
29
|
+
width: () => number;
|
|
30
|
+
height: () => number;
|
|
31
|
+
} | null, cullingMargin: number, viewportRef: MutableRefObject<ViewportBounds | null>) => void;
|
|
32
|
+
/**
|
|
33
|
+
* 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
|
|
34
|
+
*
|
|
35
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
36
|
+
* @param item 확인할 아이템
|
|
37
|
+
* @param viewportRef 뷰포트 경계 ref
|
|
38
|
+
* @param boundingBoxCacheRef 바운딩 박스 캐시 ref
|
|
39
|
+
* @param computeBoundingBox 바운딩 박스 계산 함수
|
|
40
|
+
* @returns 뷰포트 안에 있으면 true
|
|
41
|
+
*/
|
|
42
|
+
export declare const isInViewport: <T>(item: CanvasData<T>, viewportRef: MutableRefObject<ViewportBounds | null>, boundingBoxCacheRef: MutableRefObject<Map<string, BoundingBox>>, computeBoundingBox: (item: CanvasData<T>) => BoundingBox | null) => boolean;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 현재 뷰포트 영역 계산
|
|
7
|
+
*
|
|
8
|
+
* @param stage Konva Stage 인스턴스
|
|
9
|
+
* @param cullingMargin 컬링 여유 공간 (px)
|
|
10
|
+
* @param viewportRef 뷰포트 경계를 저장할 ref
|
|
11
|
+
*/
|
|
12
|
+
var updateViewport = function (stage, cullingMargin, viewportRef) {
|
|
13
|
+
if (!stage) return;
|
|
14
|
+
viewportRef.current = {
|
|
15
|
+
minX: -cullingMargin,
|
|
16
|
+
maxX: stage.width() + cullingMargin,
|
|
17
|
+
minY: -cullingMargin,
|
|
18
|
+
maxY: stage.height() + cullingMargin
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* 아이템이 현재 뷰포트 안에 있는지 확인 (바운딩 박스 캐싱)
|
|
23
|
+
*
|
|
24
|
+
* @template T 마커/폴리곤 데이터의 추가 속성 타입
|
|
25
|
+
* @param item 확인할 아이템
|
|
26
|
+
* @param viewportRef 뷰포트 경계 ref
|
|
27
|
+
* @param boundingBoxCacheRef 바운딩 박스 캐시 ref
|
|
28
|
+
* @param computeBoundingBox 바운딩 박스 계산 함수
|
|
29
|
+
* @returns 뷰포트 안에 있으면 true
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
var isInViewport = function (item, viewportRef, boundingBoxCacheRef, computeBoundingBox) {
|
|
33
|
+
if (!viewportRef.current) return true;
|
|
34
|
+
var viewport = viewportRef.current; // 캐시된 바운딩 박스 확인
|
|
35
|
+
|
|
36
|
+
var bbox = boundingBoxCacheRef.current.get(item.id);
|
|
37
|
+
|
|
38
|
+
if (!bbox) {
|
|
39
|
+
// 바운딩 박스 계산 (공통 함수 사용)
|
|
40
|
+
var computed = computeBoundingBox(item);
|
|
41
|
+
if (!computed) return false;
|
|
42
|
+
bbox = computed;
|
|
43
|
+
boundingBoxCacheRef.current.set(item.id, bbox);
|
|
44
|
+
} // 바운딩 박스와 viewport 교차 체크
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
return !(bbox.maxX < viewport.minX || bbox.minX > viewport.maxX || bbox.maxY < viewport.minY || bbox.minY > viewport.maxY);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
exports.isInViewport = isInViewport;
|
|
51
|
+
exports.updateViewport = updateViewport;
|