@mint-ui/map 1.2.0-test.35 → 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 +9 -71
- package/dist/components/mint-map/core/advanced/shared/context.js +43 -137
- package/dist/components/mint-map/core/advanced/shared/helpers.d.ts +5 -13
- package/dist/components/mint-map/core/advanced/shared/helpers.js +8 -20
- package/dist/components/mint-map/core/advanced/shared/hooks.d.ts +6 -76
- package/dist/components/mint-map/core/advanced/shared/hooks.js +18 -112
- package/dist/components/mint-map/core/advanced/shared/performance.d.ts +9 -188
- package/dist/components/mint-map/core/advanced/shared/performance.js +53 -229
- 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 +21 -126
- package/dist/components/mint-map/core/advanced/shared/utils.js +43 -151
- package/dist/components/mint-map/core/advanced/shared/viewport.d.ts +4 -34
- package/dist/components/mint-map/core/advanced/shared/viewport.js +4 -34
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.d.ts +22 -74
- package/dist/components/mint-map/core/advanced/woongCanvasMarker/WoongCanvasMarker.js +122 -516
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.d.ts +26 -76
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/WoongCanvasPolygon.js +118 -432
- package/dist/components/mint-map/core/advanced/woongCanvasPolygon/renderer.d.ts +3 -3
- package/dist/index.es.js +409 -1632
- package/dist/index.umd.js +409 -1632
- package/package.json +1 -1
|
@@ -2,80 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
// ============================================================================
|
|
6
|
-
// 성능 최적화 상수 (30,000개 마커/폴리곤 기준 최적화)
|
|
7
|
-
// ============================================================================
|
|
8
|
-
|
|
9
5
|
/**
|
|
10
|
-
* 공간 인덱스 그리드 셀 크기 (
|
|
11
|
-
*
|
|
12
|
-
* 최적값 계산:
|
|
13
|
-
* - 목표: 클릭 시 셀당 10~30개 항목만 체크 (빠른 Hit Test)
|
|
14
|
-
* - 화면 크기: 1920×1080 기준
|
|
15
|
-
* - 30,000개 항목 → 50px 셀 크기 = 약 800개 셀 = 셀당 ~37개
|
|
16
|
-
*
|
|
17
|
-
* 성능 비교 (30,000개 기준):
|
|
18
|
-
* - 200px: 셀당 ~577개 → Hit Test O(577) ❌ 느림
|
|
19
|
-
* - 50px: 셀당 ~37개 → Hit Test O(37) ✅ 15배 빠름!
|
|
6
|
+
* 공간 인덱스 그리드 셀 크기 (픽셀 단위)
|
|
20
7
|
*
|
|
21
|
-
*
|
|
22
|
-
* - 작을수록: Hit Test 빠름, 메모리 사용량 증가
|
|
23
|
-
* - 클수록: 메모리 효율적, Hit Test 느림
|
|
8
|
+
* @default 50
|
|
24
9
|
*/
|
|
25
10
|
var SPATIAL_GRID_CELL_SIZE = 50;
|
|
26
11
|
/**
|
|
27
|
-
* 뷰포트 컬링 여유 공간 (
|
|
12
|
+
* 뷰포트 컬링 여유 공간 (픽셀 단위)
|
|
28
13
|
*
|
|
29
|
-
*
|
|
30
|
-
* 30,000개 중 실제 렌더링: 화면에 보이는 1,000~3,000개만
|
|
14
|
+
* @default 100
|
|
31
15
|
*/
|
|
32
16
|
|
|
33
17
|
var DEFAULT_CULLING_MARGIN = 100;
|
|
34
18
|
/**
|
|
35
19
|
* LRU 캐시 최대 항목 수
|
|
36
20
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* 최적값 계산:
|
|
40
|
-
* - 전체 항목: 30,000개
|
|
41
|
-
* - 캐시 크기: 30,000개 → 100% 히트율 (메모리: ~2.4MB)
|
|
42
|
-
*
|
|
43
|
-
* 메모리 사용량 (항목당 ~80 bytes):
|
|
44
|
-
* - 10,000개: ~800KB → 캐시 히트율 33% ❌
|
|
45
|
-
* - 30,000개: ~2.4MB → 캐시 히트율 100% ✅
|
|
46
|
-
*
|
|
47
|
-
* zoom/pan 시 어차피 clear() 호출되므로 메모리 누적 없음
|
|
21
|
+
* @default 30000
|
|
48
22
|
*/
|
|
49
23
|
|
|
50
24
|
var DEFAULT_MAX_CACHE_SIZE = 30000;
|
|
51
25
|
/**
|
|
52
|
-
* LRU (Least Recently Used)
|
|
26
|
+
* LRU Cache (Least Recently Used)
|
|
53
27
|
*
|
|
54
|
-
*
|
|
28
|
+
* 좌표 변환 결과를 캐싱하기 위한 캐시 구현
|
|
55
29
|
*
|
|
56
30
|
* @template K 캐시 키 타입
|
|
57
31
|
* @template V 캐시 값 타입
|
|
58
|
-
*
|
|
59
|
-
* @remarks
|
|
60
|
-
* **개선 사항**:
|
|
61
|
-
* 1. get() 성능 향상: 접근 빈도 추적 없이 단순 조회만 수행 (delete+set 제거)
|
|
62
|
-
* 2. set() 버그 수정: 기존 키 업데이트 시 maxSize 체크 로직 개선
|
|
63
|
-
* 3. 메모리 효율: 단순 FIFO 캐시로 동작하여 오버헤드 최소화
|
|
64
|
-
*
|
|
65
|
-
* **트레이드오프**:
|
|
66
|
-
* - 장점: 읽기 성능 대폭 향상 (10,000번 get → 이전보다 2배 빠름)
|
|
67
|
-
* - 단점: 접근 빈도가 아닌 삽입 순서 기반 eviction (FIFO)
|
|
68
|
-
*
|
|
69
|
-
* WoongCanvasMarker 사용 사례에 최적:
|
|
70
|
-
* - 좌표 변환 결과는 zoom/pan 시 어차피 전체 초기화
|
|
71
|
-
* - 접근 빈도 추적보다 빠른 조회가 더 중요
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```typescript
|
|
75
|
-
* const cache = new LRUCache<string, Offset>(30000);
|
|
76
|
-
* cache.set(item.id, offset);
|
|
77
|
-
* const cached = cache.get(item.id);
|
|
78
|
-
* ```
|
|
79
32
|
*/
|
|
80
33
|
|
|
81
34
|
var LRUCache =
|
|
@@ -88,63 +41,43 @@ function () {
|
|
|
88
41
|
|
|
89
42
|
this.cache = new Map();
|
|
90
43
|
this.maxSize = maxSize;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* 캐시에서 값 조회
|
|
94
|
-
*
|
|
95
|
-
* @param key 조회할 키
|
|
96
|
-
* @returns 캐시된 값 또는 undefined (캐시 미스 시)
|
|
97
|
-
*
|
|
98
|
-
* @remarks
|
|
99
|
-
* - 성능: O(1) 해시 조회
|
|
100
|
-
* - 최적화: delete+set 제거로 읽기 성능 대폭 향상
|
|
101
|
-
*/
|
|
44
|
+
} // 캐시에서 값 조회
|
|
102
45
|
|
|
103
46
|
|
|
104
47
|
LRUCache.prototype.get = function (key) {
|
|
105
48
|
return this.cache.get(key);
|
|
106
|
-
};
|
|
107
|
-
/**
|
|
108
|
-
* 캐시에 값 저장
|
|
109
|
-
*
|
|
110
|
-
* @param key 저장할 키
|
|
111
|
-
* @param value 저장할 값
|
|
112
|
-
*
|
|
113
|
-
* @remarks
|
|
114
|
-
* - 기존 키 업데이트: 단순 덮어쓰기 (크기 변화 없음)
|
|
115
|
-
* - 신규 키 추가: 크기 체크 후 필요시 가장 오래된 항목 제거 (FIFO)
|
|
116
|
-
* - 성능: O(1) 평균 시간복잡도
|
|
117
|
-
*/
|
|
49
|
+
}; // 캐시에 값 저장 (FIFO eviction)
|
|
118
50
|
|
|
119
51
|
|
|
120
52
|
LRUCache.prototype.set = function (key, value) {
|
|
121
53
|
var exists = this.cache.has(key);
|
|
122
54
|
|
|
123
55
|
if (exists) {
|
|
124
|
-
// 기존 항목 업데이트: 단순 덮어쓰기 (크기 변화 없음)
|
|
125
56
|
this.cache.set(key, value);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (this.cache.size >= this.maxSize) {
|
|
129
|
-
// 가장 오래된 항목 제거 (Map의 첫 번째 항목)
|
|
130
|
-
var firstKey = this.cache.keys().next().value;
|
|
131
|
-
|
|
132
|
-
if (firstKey !== undefined) {
|
|
133
|
-
this.cache.delete(firstKey);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
136
59
|
|
|
137
|
-
|
|
60
|
+
if (this.cache.size >= this.maxSize) {
|
|
61
|
+
var firstKey = this.cache.keys().next().value;
|
|
62
|
+
|
|
63
|
+
if (firstKey !== undefined) {
|
|
64
|
+
this.cache.delete(firstKey);
|
|
65
|
+
}
|
|
138
66
|
}
|
|
139
|
-
|
|
67
|
+
|
|
68
|
+
this.cache.set(key, value);
|
|
69
|
+
}; // 캐시 초기화
|
|
70
|
+
|
|
140
71
|
|
|
141
72
|
LRUCache.prototype.clear = function () {
|
|
142
73
|
this.cache.clear();
|
|
143
|
-
};
|
|
74
|
+
}; // 캐시 크기 반환
|
|
75
|
+
|
|
144
76
|
|
|
145
77
|
LRUCache.prototype.size = function () {
|
|
146
78
|
return this.cache.size;
|
|
147
|
-
};
|
|
79
|
+
}; // 키 존재 여부 확인
|
|
80
|
+
|
|
148
81
|
|
|
149
82
|
LRUCache.prototype.has = function (key) {
|
|
150
83
|
return this.cache.has(key);
|
|
@@ -154,16 +87,10 @@ function () {
|
|
|
154
87
|
}();
|
|
155
88
|
/**
|
|
156
89
|
* Spatial Hash Grid (공간 해시 그리드)
|
|
157
|
-
* 공간 인덱싱을 위한 그리드 기반 자료구조 (개선 버전)
|
|
158
90
|
*
|
|
159
|
-
*
|
|
160
|
-
* 1. 중복 삽입 방지: 같은 항목을 여러 번 insert 해도 안전
|
|
161
|
-
* 2. 메모리 누수 방지: 기존 항목 자동 제거
|
|
162
|
-
* 3. 성능 최적화: 불필요한 배열 생성 최소화
|
|
91
|
+
* 빠른 Hit Test를 위한 그리드 기반 공간 인덱싱 자료구조
|
|
163
92
|
*
|
|
164
|
-
*
|
|
165
|
-
* - 빠른 Hit Test (마우스 클릭 시 어떤 마커/폴리곤인지 찾기)
|
|
166
|
-
* - 30,000개 항목 → 클릭 위치 주변 ~10개만 체크 (3,000배 빠름)
|
|
93
|
+
* @template T 인덱싱할 항목 타입
|
|
167
94
|
*/
|
|
168
95
|
|
|
169
96
|
var SpatialHashGrid =
|
|
@@ -177,28 +104,24 @@ function () {
|
|
|
177
104
|
this.cellSize = cellSize;
|
|
178
105
|
this.grid = new Map();
|
|
179
106
|
this.itemToCells = new Map();
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* 셀 키 생성 (x, y 좌표 → 그리드 셀 ID)
|
|
183
|
-
*/
|
|
107
|
+
} // 셀 키 생성 (x, y 좌표 → 그리드 셀 ID)
|
|
184
108
|
|
|
185
109
|
|
|
186
110
|
SpatialHashGrid.prototype.getCellKey = function (x, y) {
|
|
111
|
+
// 좌표를 셀 크기로 나눈 몫으로 셀 인덱스 계산
|
|
187
112
|
var cellX = Math.floor(x / this.cellSize);
|
|
188
113
|
var cellY = Math.floor(y / this.cellSize);
|
|
189
114
|
return "".concat(cellX, ",").concat(cellY);
|
|
190
|
-
};
|
|
191
|
-
/**
|
|
192
|
-
* 바운딩 박스가 걸치는 모든 셀 키 배열 반환
|
|
193
|
-
*/
|
|
115
|
+
}; // 바운딩 박스가 걸치는 모든 셀 키 배열 반환
|
|
194
116
|
|
|
195
117
|
|
|
196
118
|
SpatialHashGrid.prototype.getCellsForBounds = function (minX, minY, maxX, maxY) {
|
|
197
|
-
var cells = [];
|
|
119
|
+
var cells = []; // 바운딩 박스가 걸치는 셀 범위 계산
|
|
120
|
+
|
|
198
121
|
var startCellX = Math.floor(minX / this.cellSize);
|
|
199
122
|
var startCellY = Math.floor(minY / this.cellSize);
|
|
200
123
|
var endCellX = Math.floor(maxX / this.cellSize);
|
|
201
|
-
var endCellY = Math.floor(maxY / this.cellSize);
|
|
124
|
+
var endCellY = Math.floor(maxY / this.cellSize); // 바운딩 박스가 걸치는 모든 셀을 배열에 추가
|
|
202
125
|
|
|
203
126
|
for (var x = startCellX; x <= endCellX; x++) {
|
|
204
127
|
for (var y = startCellY; y <= endCellY; y++) {
|
|
@@ -207,31 +130,15 @@ function () {
|
|
|
207
130
|
}
|
|
208
131
|
|
|
209
132
|
return cells;
|
|
210
|
-
};
|
|
211
|
-
/**
|
|
212
|
-
* 항목 추가 (바운딩 박스 기반)
|
|
213
|
-
*
|
|
214
|
-
* 항목을 공간 인덱스에 추가합니다. 바운딩 박스가 걸치는 모든 셀에 삽입됩니다.
|
|
215
|
-
*
|
|
216
|
-
* @param item 추가할 항목
|
|
217
|
-
* @param minX 바운딩 박스 최소 X 좌표
|
|
218
|
-
* @param minY 바운딩 박스 최소 Y 좌표
|
|
219
|
-
* @param maxX 바운딩 박스 최대 X 좌표
|
|
220
|
-
* @param maxY 바운딩 박스 최대 Y 좌표
|
|
221
|
-
*
|
|
222
|
-
* @remarks
|
|
223
|
-
* - 중복 삽입 방지: 기존 항목이 있으면 먼저 제거 후 재삽입
|
|
224
|
-
* - 메모리 누수 방지: 이전 셀 참조 완전 제거
|
|
225
|
-
* - 성능: O(1) 평균 시간복잡도
|
|
226
|
-
*/
|
|
133
|
+
}; // 항목 추가 (바운딩 박스 기반, 중복 삽입 방지)
|
|
227
134
|
|
|
228
135
|
|
|
229
136
|
SpatialHashGrid.prototype.insert = function (item, minX, minY, maxX, maxY) {
|
|
230
|
-
//
|
|
231
|
-
this.remove(item); //
|
|
137
|
+
// 기존 항목 제거 (중복 삽입 방지: 같은 항목을 여러 번 insert 해도 안전)
|
|
138
|
+
this.remove(item); // 바운딩 박스가 걸치는 모든 셀에 항목 등록
|
|
232
139
|
|
|
233
140
|
var cells = this.getCellsForBounds(minX, minY, maxX, maxY);
|
|
234
|
-
this.itemToCells.set(item, cells);
|
|
141
|
+
this.itemToCells.set(item, cells); // 항목과 셀의 매핑 저장 (제거 시 필요)
|
|
235
142
|
|
|
236
143
|
for (var _i = 0, cells_1 = cells; _i < cells_1.length; _i++) {
|
|
237
144
|
var cell = cells_1[_i];
|
|
@@ -242,24 +149,12 @@ function () {
|
|
|
242
149
|
|
|
243
150
|
this.grid.get(cell).push(item);
|
|
244
151
|
}
|
|
245
|
-
};
|
|
246
|
-
/**
|
|
247
|
-
* 항목 제거
|
|
248
|
-
*
|
|
249
|
-
* 공간 인덱스에서 항목을 제거합니다.
|
|
250
|
-
*
|
|
251
|
-
* @param item 제거할 항목
|
|
252
|
-
*
|
|
253
|
-
* @remarks
|
|
254
|
-
* - 메모리 누수 방지: 모든 셀에서 참조 완전 제거
|
|
255
|
-
* - 빈 셀 정리: 항목이 없어진 셀은 자동으로 정리됨
|
|
256
|
-
* - 성능: O(셀 개수), 보통 O(1)
|
|
257
|
-
*/
|
|
152
|
+
}; // 항목 제거 (모든 셀에서 참조 제거)
|
|
258
153
|
|
|
259
154
|
|
|
260
155
|
SpatialHashGrid.prototype.remove = function (item) {
|
|
261
156
|
var prevCells = this.itemToCells.get(item);
|
|
262
|
-
if (!prevCells) return; //
|
|
157
|
+
if (!prevCells) return; // 항목이 등록된 모든 셀에서 참조 제거 (메모리 누수 방지)
|
|
263
158
|
|
|
264
159
|
for (var _i = 0, prevCells_1 = prevCells; _i < prevCells_1.length; _i++) {
|
|
265
160
|
var cell = prevCells_1[_i];
|
|
@@ -270,86 +165,39 @@ function () {
|
|
|
270
165
|
|
|
271
166
|
if (index !== -1) {
|
|
272
167
|
cellItems.splice(index, 1);
|
|
273
|
-
} // 빈 셀 정리 (메모리
|
|
168
|
+
} // 빈 셀 정리 (메모리 효율: 사용하지 않는 셀 제거)
|
|
274
169
|
|
|
275
170
|
|
|
276
171
|
if (cellItems.length === 0) {
|
|
277
172
|
this.grid.delete(cell);
|
|
278
173
|
}
|
|
279
174
|
}
|
|
280
|
-
}
|
|
175
|
+
} // 항목과 셀의 매핑 제거
|
|
176
|
+
|
|
281
177
|
|
|
282
178
|
this.itemToCells.delete(item);
|
|
283
|
-
};
|
|
284
|
-
/**
|
|
285
|
-
* 항목 위치 업데이트
|
|
286
|
-
*
|
|
287
|
-
* 항목의 위치를 업데이트합니다. remove + insert의 편의 함수입니다.
|
|
288
|
-
*
|
|
289
|
-
* @param item 업데이트할 항목
|
|
290
|
-
* @param minX 새로운 바운딩 박스 최소 X 좌표
|
|
291
|
-
* @param minY 새로운 바운딩 박스 최소 Y 좌표
|
|
292
|
-
* @param maxX 새로운 바운딩 박스 최대 X 좌표
|
|
293
|
-
* @param maxY 새로운 바운딩 박스 최대 Y 좌표
|
|
294
|
-
*/
|
|
179
|
+
}; // 항목 위치 업데이트 (remove + insert)
|
|
295
180
|
|
|
296
181
|
|
|
297
182
|
SpatialHashGrid.prototype.update = function (item, minX, minY, maxX, maxY) {
|
|
298
183
|
this.insert(item, minX, minY, maxX, maxY);
|
|
299
|
-
};
|
|
300
|
-
/**
|
|
301
|
-
* 점 주변의 항목 조회 (1개 셀만)
|
|
302
|
-
*
|
|
303
|
-
* 특정 좌표가 속한 셀의 모든 항목을 반환합니다.
|
|
304
|
-
*
|
|
305
|
-
* @param x 조회할 X 좌표
|
|
306
|
-
* @param y 조회할 Y 좌표
|
|
307
|
-
* @returns 해당 셀의 항목 배열 (없으면 빈 배열)
|
|
308
|
-
*
|
|
309
|
-
* @remarks
|
|
310
|
-
* - 성능: O(해당 셀의 항목 수) - 보통 ~10개 (30,000개 전체를 체크하지 않음)
|
|
311
|
-
* - Hit Test에 최적화된 메서드
|
|
312
|
-
* - 빈 배열 재사용으로 메모리 할당 최소화
|
|
313
|
-
*
|
|
314
|
-
* @example
|
|
315
|
-
* ```typescript
|
|
316
|
-
* const candidates = grid.queryPoint(mouseX, mouseY);
|
|
317
|
-
* for (const item of candidates) {
|
|
318
|
-
* if (isPointInItem(item, mouseX, mouseY)) {
|
|
319
|
-
* return item;
|
|
320
|
-
* }
|
|
321
|
-
* }
|
|
322
|
-
* ```
|
|
323
|
-
*/
|
|
184
|
+
}; // 점 주변의 항목 조회 (Hit Test용)
|
|
324
185
|
|
|
325
186
|
|
|
326
187
|
SpatialHashGrid.prototype.queryPoint = function (x, y) {
|
|
188
|
+
// 클릭 위치가 속한 셀의 모든 항목 조회 (O(1) 수준의 빠른 조회)
|
|
327
189
|
var cellKey = this.getCellKey(x, y);
|
|
328
190
|
var items = this.grid.get(cellKey); // 빈 배열 재사용 (메모리 할당 최소화)
|
|
329
191
|
|
|
330
192
|
return items || [];
|
|
331
|
-
};
|
|
332
|
-
/**
|
|
333
|
-
* 영역 내 항목 조회
|
|
334
|
-
*
|
|
335
|
-
* 특정 영역(바운딩 박스)과 교차하는 모든 항목을 반환합니다.
|
|
336
|
-
*
|
|
337
|
-
* @param minX 영역 최소 X 좌표
|
|
338
|
-
* @param minY 영역 최소 Y 좌표
|
|
339
|
-
* @param maxX 영역 최대 X 좌표
|
|
340
|
-
* @param maxY 영역 최대 Y 좌표
|
|
341
|
-
* @returns 영역과 교차하는 항목 배열 (중복 제거됨)
|
|
342
|
-
*
|
|
343
|
-
* @remarks
|
|
344
|
-
* - 성능: O(셀 개수 × 셀당 평균 항목 수)
|
|
345
|
-
* - Set으로 중복 제거 보장 (항목이 여러 셀에 걸쳐 있어도 한 번만 반환)
|
|
346
|
-
* - Viewport Culling에 유용
|
|
347
|
-
*/
|
|
193
|
+
}; // 영역 내 항목 조회 (Viewport Culling용)
|
|
348
194
|
|
|
349
195
|
|
|
350
196
|
SpatialHashGrid.prototype.queryBounds = function (minX, minY, maxX, maxY) {
|
|
197
|
+
// 영역이 걸치는 모든 셀 찾기
|
|
351
198
|
var cells = this.getCellsForBounds(minX, minY, maxX, maxY);
|
|
352
|
-
var results = new Set();
|
|
199
|
+
var results = new Set(); // 중복 제거를 위해 Set 사용
|
|
200
|
+
// 각 셀의 모든 항목을 결과에 추가
|
|
353
201
|
|
|
354
202
|
for (var _i = 0, cells_2 = cells; _i < cells_2.length; _i++) {
|
|
355
203
|
var cell = cells_2[_i];
|
|
@@ -358,48 +206,24 @@ function () {
|
|
|
358
206
|
if (items) {
|
|
359
207
|
for (var _a = 0, items_1 = items; _a < items_1.length; _a++) {
|
|
360
208
|
var item = items_1[_a];
|
|
361
|
-
results.add(item);
|
|
209
|
+
results.add(item); // Set이므로 중복 자동 제거
|
|
362
210
|
}
|
|
363
211
|
}
|
|
364
212
|
}
|
|
365
213
|
|
|
366
214
|
return Array.from(results);
|
|
367
|
-
};
|
|
368
|
-
/**
|
|
369
|
-
* 항목 존재 여부 확인
|
|
370
|
-
*
|
|
371
|
-
* @param item 확인할 항목
|
|
372
|
-
* @returns 항목이 인덱스에 있으면 true, 아니면 false
|
|
373
|
-
*
|
|
374
|
-
* @remarks
|
|
375
|
-
* - 성능: O(1) 해시 조회
|
|
376
|
-
*/
|
|
215
|
+
}; // 항목 존재 여부 확인
|
|
377
216
|
|
|
378
217
|
|
|
379
218
|
SpatialHashGrid.prototype.has = function (item) {
|
|
380
219
|
return this.itemToCells.has(item);
|
|
381
|
-
};
|
|
382
|
-
/**
|
|
383
|
-
* 전체 초기화
|
|
384
|
-
*/
|
|
220
|
+
}; // 전체 초기화
|
|
385
221
|
|
|
386
222
|
|
|
387
223
|
SpatialHashGrid.prototype.clear = function () {
|
|
388
224
|
this.grid.clear();
|
|
389
225
|
this.itemToCells.clear();
|
|
390
|
-
};
|
|
391
|
-
/**
|
|
392
|
-
* 통계 정보
|
|
393
|
-
*
|
|
394
|
-
* 공간 인덱스의 현재 상태를 반환합니다. 디버깅 및 성능 분석에 유용합니다.
|
|
395
|
-
*
|
|
396
|
-
* @returns 통계 정보 객체
|
|
397
|
-
*
|
|
398
|
-
* @remarks
|
|
399
|
-
* - totalCells: 현재 사용 중인 셀 개수
|
|
400
|
-
* - totalItems: 인덱스에 등록된 고유 항목 수 (정확)
|
|
401
|
-
* - avgItemsPerCell: 셀당 평균 항목 수
|
|
402
|
-
*/
|
|
226
|
+
}; // 통계 정보 반환
|
|
403
227
|
|
|
404
228
|
|
|
405
229
|
SpatialHashGrid.prototype.stats = function () {
|
|
@@ -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;
|