@mint-ui/map 1.2.0-test.2 → 1.2.0-test.21
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/README.md +7 -7
- package/dist/components/mint-map/core/MintMapController.d.ts +1 -0
- package/dist/components/mint-map/core/MintMapCore.js +2 -2
- package/dist/components/mint-map/core/advanced/index.d.ts +1 -4
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongCanvasLayer.d.ts +227 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/{WoongKonvaMarker.js → WoongCanvasLayer.js} +338 -237
- package/dist/components/mint-map/core/advanced/woongCanvas/index.d.ts +12 -1
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.d.ts +81 -12
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/context.js +85 -14
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/index.d.ts +11 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.d.ts +62 -13
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/performance.js +62 -13
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/renderer.d.ts +115 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/renderer.js +288 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.d.ts +138 -16
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/types.js +20 -0
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.d.ts +162 -10
- package/dist/components/mint-map/core/advanced/woongCanvas/shared/utils.js +235 -14
- package/dist/components/mint-map/core/wrapper/MapMarkerWrapper.js +22 -1
- package/dist/components/mint-map/google/GoogleMintMapController.d.ts +1 -0
- package/dist/components/mint-map/google/GoogleMintMapController.js +7 -1
- package/dist/components/mint-map/kakao/KakaoMintMapController.d.ts +1 -0
- package/dist/components/mint-map/kakao/KakaoMintMapController.js +7 -1
- package/dist/components/mint-map/naver/NaverMintMapController.d.ts +3 -0
- package/dist/components/mint-map/naver/NaverMintMapController.js +40 -4
- package/dist/index.es.js +1078 -283
- package/dist/index.js +35 -15
- package/dist/index.umd.js +1091 -283
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -16
- package/dist/components/mint-map/core/advanced/woongCanvas/ClusterMarker.d.ts +0 -11
- package/dist/components/mint-map/core/advanced/woongCanvas/WoongKonvaMarker.d.ts +0 -48
|
@@ -1,23 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview WoongCanvasLayer를 위한 유틸리티 함수들
|
|
3
|
+
*
|
|
4
|
+
* 이 파일은 좌표 변환, 히트 테스트, 색상 변환 등 WoongCanvasLayer에서
|
|
5
|
+
* 사용하는 다양한 유틸리티 함수들을 제공합니다.
|
|
6
|
+
*
|
|
7
|
+
* ## 주요 기능
|
|
8
|
+
* - **좌표 변환**: 지도 좌표(위경도) ↔ 화면 좌표(픽셀) 변환
|
|
9
|
+
* - **히트 테스트**: 마우스 클릭/호버 시 어떤 마커/폴리곤인지 찾기
|
|
10
|
+
* - **색상 변환**: 다양한 색상 형식 간 변환
|
|
11
|
+
* - **바운딩 박스**: 폴리곤의 경계 영역 계산
|
|
12
|
+
*
|
|
13
|
+
* @version 2.2.1
|
|
14
|
+
* @since 2.0.0
|
|
15
|
+
* @author 박건웅
|
|
16
|
+
*/
|
|
1
17
|
import { Offset } from "../../../../types";
|
|
2
18
|
import { MintMapController } from "../../../MintMapController";
|
|
3
|
-
import {
|
|
19
|
+
import { KonvaCanvasData } from "./types";
|
|
4
20
|
/**
|
|
5
|
-
* 폴리곤
|
|
21
|
+
* 폴리곤 좌표를 화면 좌표로 변환
|
|
22
|
+
*
|
|
23
|
+
* GeoJSON MultiPolygon 형식의 폴리곤 데이터를 Canvas에서 그릴 수 있는
|
|
24
|
+
* 화면 좌표 배열로 변환합니다.
|
|
25
|
+
*
|
|
26
|
+
* @param polygonData - 변환할 폴리곤 데이터
|
|
27
|
+
* @param controller - 지도 컨트롤러 (좌표 변환용)
|
|
28
|
+
* @returns 변환된 좌표 배열 또는 null (변환 실패 시)
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const offsets = computePolygonOffsets(polygonData, controller);
|
|
33
|
+
* if (offsets) {
|
|
34
|
+
* // offsets[0][0][0] = [x, y] - 첫 번째 MultiPolygon의 첫 번째 Polygon의 첫 번째 점
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
6
37
|
*/
|
|
7
|
-
export declare const computePolygonOffsets: (polygonData:
|
|
38
|
+
export declare const computePolygonOffsets: (polygonData: KonvaCanvasData<any>, controller: MintMapController) => number[][][][] | null;
|
|
8
39
|
/**
|
|
9
|
-
* 마커
|
|
40
|
+
* 마커 좌표를 화면 좌표로 변환
|
|
41
|
+
*
|
|
42
|
+
* 마커의 지도 좌표(위경도)를 Canvas에서 그릴 수 있는 화면 좌표로 변환합니다.
|
|
43
|
+
*
|
|
44
|
+
* @param markerData - 변환할 마커 데이터
|
|
45
|
+
* @param controller - 지도 컨트롤러 (좌표 변환용)
|
|
46
|
+
* @returns 변환된 화면 좌표 또는 null (변환 실패 시)
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const offset = computeMarkerOffset(markerData, controller);
|
|
51
|
+
* if (offset) {
|
|
52
|
+
* console.log(`마커 위치: (${offset.x}, ${offset.y})`);
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
10
55
|
*/
|
|
11
|
-
export declare const computeMarkerOffset: (markerData:
|
|
56
|
+
export declare const computeMarkerOffset: (markerData: KonvaCanvasData<any>, controller: MintMapController) => Offset | null;
|
|
12
57
|
/**
|
|
13
|
-
* Point-in-Polygon 알고리즘
|
|
58
|
+
* Point-in-Polygon 알고리즘 (Ray Casting)
|
|
59
|
+
*
|
|
60
|
+
* 주어진 점이 폴리곤 내부에 있는지 확인하는 알고리즘입니다.
|
|
61
|
+
* Ray Casting 방식을 사용하여 홀수 번 교차하면 내부, 짝수 번 교차하면 외부로 판단합니다.
|
|
62
|
+
*
|
|
63
|
+
* @param point - 확인할 점의 좌표
|
|
64
|
+
* @param polygon - 폴리곤의 좌표 배열 (순서대로 연결된 점들)
|
|
65
|
+
* @returns 점이 폴리곤 내부에 있으면 true, 외부에 있으면 false
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const point = { x: 100, y: 100 };
|
|
70
|
+
* const polygon = [[0, 0], [200, 0], [200, 200], [0, 200]];
|
|
71
|
+
* const isInside = isPointInPolygon(point, polygon); // true
|
|
72
|
+
* ```
|
|
14
73
|
*/
|
|
15
74
|
export declare const isPointInPolygon: (point: Offset, polygon: number[][]) => boolean;
|
|
16
75
|
/**
|
|
17
|
-
* 폴리곤 히트 테스트
|
|
76
|
+
* 폴리곤 히트 테스트 (도넛 폴리곤 지원)
|
|
77
|
+
*
|
|
78
|
+
* 클릭된 좌표가 폴리곤 내부에 있는지 확인합니다.
|
|
79
|
+
* 도넛 폴리곤(구멍이 있는 폴리곤)도 지원합니다.
|
|
80
|
+
*
|
|
81
|
+
* ## 도넛 폴리곤 처리 로직
|
|
82
|
+
* 1. **외부 폴리곤(첫 번째)**: 내부에 있어야 함
|
|
83
|
+
* 2. **내부 구멍들(나머지)**: 내부에 있으면 안 됨 (evenodd 규칙)
|
|
84
|
+
*
|
|
85
|
+
* ## 중요 사항
|
|
86
|
+
* 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됩니다:
|
|
87
|
+
* - 도넛 폴리곤 A: `isDonutPolygon=true`
|
|
88
|
+
* - 내부 폴리곤 B: `isDonutPolygon=false` (별도 데이터)
|
|
89
|
+
*
|
|
90
|
+
* @param clickedOffset - 클릭된 좌표
|
|
91
|
+
* @param polygonData - 확인할 폴리곤 데이터
|
|
92
|
+
* @param getPolygonOffsets - 폴리곤 좌표를 가져오는 함수
|
|
93
|
+
* @returns 클릭된 좌표가 폴리곤 내부에 있으면 true, 외부에 있으면 false
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* const isHit = isPointInPolygonData(
|
|
98
|
+
* { x: 100, y: 100 },
|
|
99
|
+
* polygonData,
|
|
100
|
+
* (data) => computePolygonOffsets(data, controller)
|
|
101
|
+
* );
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare const isPointInPolygonData: (clickedOffset: Offset, polygonData: KonvaCanvasData<any>, getPolygonOffsets: (data: KonvaCanvasData<any>) => number[][][][] | null) => boolean;
|
|
105
|
+
/**
|
|
106
|
+
* 마커 히트 테스트 (클릭/hover 영역 체크)
|
|
107
|
+
*
|
|
108
|
+
* 클릭된 좌표가 마커의 클릭 가능한 영역 내부에 있는지 확인합니다.
|
|
109
|
+
*
|
|
110
|
+
* ## 중요 사항
|
|
111
|
+
* **꼬리(tail)는 Hit Test 영역에서 제외됩니다!**
|
|
112
|
+
* - `markerOffset.y`는 마커 최하단(꼬리 끝) 좌표
|
|
113
|
+
* - `boxHeight`는 마커 본체만 포함 (꼬리 제외)
|
|
114
|
+
* - `tailHeight`만큼 위로 올려서 본체만 Hit Test 영역으로 사용
|
|
115
|
+
*
|
|
116
|
+
* @param clickedOffset - 클릭된 좌표
|
|
117
|
+
* @param markerData - 확인할 마커 데이터
|
|
118
|
+
* @param getMarkerOffset - 마커 좌표를 가져오는 함수
|
|
119
|
+
* @returns 클릭된 좌표가 마커 내부에 있으면 true, 외부에 있으면 false
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const isHit = isPointInMarkerData(
|
|
124
|
+
* { x: 100, y: 100 },
|
|
125
|
+
* markerData,
|
|
126
|
+
* (data) => computeMarkerOffset(data, controller)
|
|
127
|
+
* );
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare const isPointInMarkerData: (clickedOffset: Offset, markerData: KonvaCanvasData<any>, getMarkerOffset: (data: KonvaCanvasData<any>) => Offset | null) => boolean;
|
|
131
|
+
/**
|
|
132
|
+
* HEX 색상을 RGBA 색상으로 변환
|
|
133
|
+
*
|
|
134
|
+
* 16진수 색상 코드를 RGBA 형식으로 변환합니다.
|
|
135
|
+
*
|
|
136
|
+
* @param hexColor - 변환할 HEX 색상 (예: "#FF0000" 또는 "FF0000")
|
|
137
|
+
* @param alpha - 알파 값 (0~1, 기본값: 1)
|
|
138
|
+
* @returns RGBA 형식의 색상 문자열
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* const rgba = hexToRgba('#FF0000', 0.5); // "rgba(255, 0, 0, 0.5)"
|
|
143
|
+
* const rgba2 = hexToRgba('FF0000', 1); // "rgba(255, 0, 0, 1)"
|
|
144
|
+
* ```
|
|
18
145
|
*/
|
|
19
|
-
export declare const
|
|
146
|
+
export declare const hexToRgba: (hexColor: string, alpha?: number) => string;
|
|
20
147
|
/**
|
|
21
|
-
*
|
|
148
|
+
* 텍스트 박스의 너비를 계산
|
|
149
|
+
*
|
|
150
|
+
* Canvas 2D API의 measureText()를 사용하여 텍스트의 실제 너비를 측정하고,
|
|
151
|
+
* 패딩과 최소 너비를 고려하여 최종 박스 너비를 계산합니다.
|
|
152
|
+
*
|
|
153
|
+
* @param params - 파라미터 객체
|
|
154
|
+
* @param params.text - 측정할 텍스트
|
|
155
|
+
* @param params.fontConfig - 폰트 설정 (예: 'bold 16px Arial')
|
|
156
|
+
* @param params.padding - 텍스트 박스에 적용할 패딩 값
|
|
157
|
+
* @param params.minWidth - 최소 너비
|
|
158
|
+
* @returns 계산된 텍스트 박스의 너비
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const width = calculateTextBoxWidth({
|
|
163
|
+
* text: 'Hello World',
|
|
164
|
+
* fontConfig: 'bold 16px Arial',
|
|
165
|
+
* padding: 10,
|
|
166
|
+
* minWidth: 50
|
|
167
|
+
* });
|
|
168
|
+
* ```
|
|
22
169
|
*/
|
|
23
|
-
export declare const
|
|
170
|
+
export declare const calculateTextBoxWidth: ({ text, fontConfig, padding, minWidth, }: {
|
|
171
|
+
text: string;
|
|
172
|
+
fontConfig: string;
|
|
173
|
+
padding: number;
|
|
174
|
+
minWidth: number;
|
|
175
|
+
}) => number;
|
|
@@ -7,7 +7,38 @@ var MapTypes = require('../../../../types/MapTypes.js');
|
|
|
7
7
|
require('../../../../types/MapEventTypes.js');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* @fileoverview WoongCanvasLayer를 위한 유틸리티 함수들
|
|
11
|
+
*
|
|
12
|
+
* 이 파일은 좌표 변환, 히트 테스트, 색상 변환 등 WoongCanvasLayer에서
|
|
13
|
+
* 사용하는 다양한 유틸리티 함수들을 제공합니다.
|
|
14
|
+
*
|
|
15
|
+
* ## 주요 기능
|
|
16
|
+
* - **좌표 변환**: 지도 좌표(위경도) ↔ 화면 좌표(픽셀) 변환
|
|
17
|
+
* - **히트 테스트**: 마우스 클릭/호버 시 어떤 마커/폴리곤인지 찾기
|
|
18
|
+
* - **색상 변환**: 다양한 색상 형식 간 변환
|
|
19
|
+
* - **바운딩 박스**: 폴리곤의 경계 영역 계산
|
|
20
|
+
*
|
|
21
|
+
* @version 2.2.1
|
|
22
|
+
* @since 2.0.0
|
|
23
|
+
* @author 박건웅
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* 폴리곤 좌표를 화면 좌표로 변환
|
|
27
|
+
*
|
|
28
|
+
* GeoJSON MultiPolygon 형식의 폴리곤 데이터를 Canvas에서 그릴 수 있는
|
|
29
|
+
* 화면 좌표 배열로 변환합니다.
|
|
30
|
+
*
|
|
31
|
+
* @param polygonData - 변환할 폴리곤 데이터
|
|
32
|
+
* @param controller - 지도 컨트롤러 (좌표 변환용)
|
|
33
|
+
* @returns 변환된 좌표 배열 또는 null (변환 실패 시)
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const offsets = computePolygonOffsets(polygonData, controller);
|
|
38
|
+
* if (offsets) {
|
|
39
|
+
* // offsets[0][0][0] = [x, y] - 첫 번째 MultiPolygon의 첫 번째 Polygon의 첫 번째 점
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
11
42
|
*/
|
|
12
43
|
|
|
13
44
|
var computePolygonOffsets = function (polygonData, controller) {
|
|
@@ -43,18 +74,46 @@ var computePolygonOffsets = function (polygonData, controller) {
|
|
|
43
74
|
return result;
|
|
44
75
|
};
|
|
45
76
|
/**
|
|
46
|
-
* 마커
|
|
77
|
+
* 마커 좌표를 화면 좌표로 변환
|
|
78
|
+
*
|
|
79
|
+
* 마커의 지도 좌표(위경도)를 Canvas에서 그릴 수 있는 화면 좌표로 변환합니다.
|
|
80
|
+
*
|
|
81
|
+
* @param markerData - 변환할 마커 데이터
|
|
82
|
+
* @param controller - 지도 컨트롤러 (좌표 변환용)
|
|
83
|
+
* @returns 변환된 화면 좌표 또는 null (변환 실패 시)
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const offset = computeMarkerOffset(markerData, controller);
|
|
88
|
+
* if (offset) {
|
|
89
|
+
* console.log(`마커 위치: (${offset.x}, ${offset.y})`);
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
47
92
|
*/
|
|
48
93
|
|
|
49
94
|
var computeMarkerOffset = function (markerData, controller) {
|
|
50
|
-
if (!markerData.position
|
|
95
|
+
if (!markerData.position) {
|
|
51
96
|
return null;
|
|
52
97
|
}
|
|
53
98
|
|
|
54
|
-
return controller.positionToOffset(markerData.position
|
|
99
|
+
return controller.positionToOffset(markerData.position);
|
|
55
100
|
};
|
|
56
101
|
/**
|
|
57
|
-
* Point-in-Polygon 알고리즘
|
|
102
|
+
* Point-in-Polygon 알고리즘 (Ray Casting)
|
|
103
|
+
*
|
|
104
|
+
* 주어진 점이 폴리곤 내부에 있는지 확인하는 알고리즘입니다.
|
|
105
|
+
* Ray Casting 방식을 사용하여 홀수 번 교차하면 내부, 짝수 번 교차하면 외부로 판단합니다.
|
|
106
|
+
*
|
|
107
|
+
* @param point - 확인할 점의 좌표
|
|
108
|
+
* @param polygon - 폴리곤의 좌표 배열 (순서대로 연결된 점들)
|
|
109
|
+
* @returns 점이 폴리곤 내부에 있으면 true, 외부에 있으면 false
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const point = { x: 100, y: 100 };
|
|
114
|
+
* const polygon = [[0, 0], [200, 0], [200, 200], [0, 200]];
|
|
115
|
+
* const isInside = isPointInPolygon(point, polygon); // true
|
|
116
|
+
* ```
|
|
58
117
|
*/
|
|
59
118
|
|
|
60
119
|
var isPointInPolygon = function (point, polygon) {
|
|
@@ -72,18 +131,85 @@ var isPointInPolygon = function (point, polygon) {
|
|
|
72
131
|
return inside;
|
|
73
132
|
};
|
|
74
133
|
/**
|
|
75
|
-
* 폴리곤 히트 테스트
|
|
134
|
+
* 폴리곤 히트 테스트 (도넛 폴리곤 지원)
|
|
135
|
+
*
|
|
136
|
+
* 클릭된 좌표가 폴리곤 내부에 있는지 확인합니다.
|
|
137
|
+
* 도넛 폴리곤(구멍이 있는 폴리곤)도 지원합니다.
|
|
138
|
+
*
|
|
139
|
+
* ## 도넛 폴리곤 처리 로직
|
|
140
|
+
* 1. **외부 폴리곤(첫 번째)**: 내부에 있어야 함
|
|
141
|
+
* 2. **내부 구멍들(나머지)**: 내부에 있으면 안 됨 (evenodd 규칙)
|
|
142
|
+
*
|
|
143
|
+
* ## 중요 사항
|
|
144
|
+
* 도넛 폴리곤과 내부 폴리곤은 별개의 polygonData로 처리됩니다:
|
|
145
|
+
* - 도넛 폴리곤 A: `isDonutPolygon=true`
|
|
146
|
+
* - 내부 폴리곤 B: `isDonutPolygon=false` (별도 데이터)
|
|
147
|
+
*
|
|
148
|
+
* @param clickedOffset - 클릭된 좌표
|
|
149
|
+
* @param polygonData - 확인할 폴리곤 데이터
|
|
150
|
+
* @param getPolygonOffsets - 폴리곤 좌표를 가져오는 함수
|
|
151
|
+
* @returns 클릭된 좌표가 폴리곤 내부에 있으면 true, 외부에 있으면 false
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const isHit = isPointInPolygonData(
|
|
156
|
+
* { x: 100, y: 100 },
|
|
157
|
+
* polygonData,
|
|
158
|
+
* (data) => computePolygonOffsets(data, controller)
|
|
159
|
+
* );
|
|
160
|
+
* ```
|
|
76
161
|
*/
|
|
77
162
|
|
|
78
163
|
var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffsets) {
|
|
79
164
|
var polygonOffsets = getPolygonOffsets(polygonData);
|
|
80
|
-
if (!polygonOffsets) return false;
|
|
165
|
+
if (!polygonOffsets) return false; // 🍩 도넛 폴리곤 처리 (isDonutPolygon === true)
|
|
166
|
+
|
|
167
|
+
if (polygonData.isDonutPolygon) {
|
|
168
|
+
for (var _i = 0, polygonOffsets_1 = polygonOffsets; _i < polygonOffsets_1.length; _i++) {
|
|
169
|
+
var multiPolygon = polygonOffsets_1[_i];
|
|
170
|
+
if (multiPolygon.length === 0) continue; // 외부 폴리곤만 있는 경우 (구멍 없음) - 일반 폴리곤처럼 처리
|
|
171
|
+
|
|
172
|
+
if (multiPolygon.length === 1) {
|
|
173
|
+
if (isPointInPolygon(clickedOffset, multiPolygon[0])) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
continue;
|
|
178
|
+
} // 1. 외부 폴리곤(첫 번째)에 포함되는지 확인
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
var outerPolygon = multiPolygon[0];
|
|
182
|
+
|
|
183
|
+
if (!isPointInPolygon(clickedOffset, outerPolygon)) {
|
|
184
|
+
continue; // 외부 폴리곤 밖이면 다음 multiPolygon 확인
|
|
185
|
+
} // 2. 내부 구멍들(나머지)에 포함되는지 확인
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
for (var i = 1; i < multiPolygon.length; i++) {
|
|
189
|
+
var hole = multiPolygon[i];
|
|
190
|
+
|
|
191
|
+
if (isPointInPolygon(clickedOffset, hole)) {
|
|
192
|
+
// ❌ 구멍 안에 있음 → 이 도넛 폴리곤은 히트 안 됨
|
|
193
|
+
// 다른 multiPolygon 체크하지 않고 바로 false 반환
|
|
194
|
+
// (도넛 폴리곤의 구멍 안은 무조건 클릭 불가)
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
} // ✅ 외부 폴리곤 안 + 구멍 밖 = 히트!
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return false;
|
|
204
|
+
} // 일반 폴리곤 처리 (isDonutPolygon === false 또는 undefined)
|
|
205
|
+
|
|
81
206
|
|
|
82
|
-
for (var
|
|
83
|
-
var multiPolygon =
|
|
207
|
+
for (var _a = 0, polygonOffsets_2 = polygonOffsets; _a < polygonOffsets_2.length; _a++) {
|
|
208
|
+
var multiPolygon = polygonOffsets_2[_a];
|
|
84
209
|
|
|
85
|
-
for (var
|
|
86
|
-
var polygonGroup = multiPolygon_2[
|
|
210
|
+
for (var _b = 0, multiPolygon_2 = multiPolygon; _b < multiPolygon_2.length; _b++) {
|
|
211
|
+
var polygonGroup = multiPolygon_2[_b];
|
|
212
|
+
if (polygonGroup.length === 0) continue;
|
|
87
213
|
|
|
88
214
|
if (isPointInPolygon(clickedOffset, polygonGroup)) {
|
|
89
215
|
return true;
|
|
@@ -94,7 +220,29 @@ var isPointInPolygonData = function (clickedOffset, polygonData, getPolygonOffse
|
|
|
94
220
|
return false;
|
|
95
221
|
};
|
|
96
222
|
/**
|
|
97
|
-
* 마커 히트 테스트
|
|
223
|
+
* 마커 히트 테스트 (클릭/hover 영역 체크)
|
|
224
|
+
*
|
|
225
|
+
* 클릭된 좌표가 마커의 클릭 가능한 영역 내부에 있는지 확인합니다.
|
|
226
|
+
*
|
|
227
|
+
* ## 중요 사항
|
|
228
|
+
* **꼬리(tail)는 Hit Test 영역에서 제외됩니다!**
|
|
229
|
+
* - `markerOffset.y`는 마커 최하단(꼬리 끝) 좌표
|
|
230
|
+
* - `boxHeight`는 마커 본체만 포함 (꼬리 제외)
|
|
231
|
+
* - `tailHeight`만큼 위로 올려서 본체만 Hit Test 영역으로 사용
|
|
232
|
+
*
|
|
233
|
+
* @param clickedOffset - 클릭된 좌표
|
|
234
|
+
* @param markerData - 확인할 마커 데이터
|
|
235
|
+
* @param getMarkerOffset - 마커 좌표를 가져오는 함수
|
|
236
|
+
* @returns 클릭된 좌표가 마커 내부에 있으면 true, 외부에 있으면 false
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```typescript
|
|
240
|
+
* const isHit = isPointInMarkerData(
|
|
241
|
+
* { x: 100, y: 100 },
|
|
242
|
+
* markerData,
|
|
243
|
+
* (data) => computeMarkerOffset(data, controller)
|
|
244
|
+
* );
|
|
245
|
+
* ```
|
|
98
246
|
*/
|
|
99
247
|
|
|
100
248
|
var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset) {
|
|
@@ -102,14 +250,87 @@ var isPointInMarkerData = function (clickedOffset, markerData, getMarkerOffset)
|
|
|
102
250
|
if (!markerOffset) return false;
|
|
103
251
|
var boxWidth = markerData.boxWidth || 50;
|
|
104
252
|
var boxHeight = markerData.boxHeight || 28;
|
|
105
|
-
var tailHeight =
|
|
253
|
+
var tailHeight = markerData.tailHeight || 0; // 🎯 tailHeight 사용!
|
|
254
|
+
|
|
106
255
|
var x = markerOffset.x - boxWidth / 2;
|
|
107
|
-
var y = markerOffset.y - boxHeight - tailHeight;
|
|
256
|
+
var y = markerOffset.y - boxHeight - tailHeight; // 🔥 꼬리만큼 위로!
|
|
257
|
+
|
|
108
258
|
return clickedOffset.x >= x && clickedOffset.x <= x + boxWidth && clickedOffset.y >= y && clickedOffset.y <= y + boxHeight;
|
|
109
259
|
};
|
|
260
|
+
/**
|
|
261
|
+
* HEX 색상을 RGBA 색상으로 변환
|
|
262
|
+
*
|
|
263
|
+
* 16진수 색상 코드를 RGBA 형식으로 변환합니다.
|
|
264
|
+
*
|
|
265
|
+
* @param hexColor - 변환할 HEX 색상 (예: "#FF0000" 또는 "FF0000")
|
|
266
|
+
* @param alpha - 알파 값 (0~1, 기본값: 1)
|
|
267
|
+
* @returns RGBA 형식의 색상 문자열
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* const rgba = hexToRgba('#FF0000', 0.5); // "rgba(255, 0, 0, 0.5)"
|
|
272
|
+
* const rgba2 = hexToRgba('FF0000', 1); // "rgba(255, 0, 0, 1)"
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
|
|
276
|
+
var hexToRgba = function (hexColor, alpha) {
|
|
277
|
+
if (alpha === void 0) {
|
|
278
|
+
alpha = 1;
|
|
279
|
+
} // 입력된 hexColor에서 "#" 제거
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
var hex = hexColor.replace('#', ''); // 6자리일 경우 알파 값은 사용자가 제공한 alpha 값으로 설정
|
|
283
|
+
|
|
284
|
+
if (hex.length === 6) {
|
|
285
|
+
var r = parseInt(hex.substring(0, 2), 16);
|
|
286
|
+
var g = parseInt(hex.substring(2, 4), 16);
|
|
287
|
+
var b = parseInt(hex.substring(4, 6), 16);
|
|
288
|
+
return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
throw new Error('Invalid hex color format');
|
|
292
|
+
};
|
|
293
|
+
var tempCanvas = document.createElement('canvas');
|
|
294
|
+
var tempCtx = tempCanvas.getContext('2d');
|
|
295
|
+
/**
|
|
296
|
+
* 텍스트 박스의 너비를 계산
|
|
297
|
+
*
|
|
298
|
+
* Canvas 2D API의 measureText()를 사용하여 텍스트의 실제 너비를 측정하고,
|
|
299
|
+
* 패딩과 최소 너비를 고려하여 최종 박스 너비를 계산합니다.
|
|
300
|
+
*
|
|
301
|
+
* @param params - 파라미터 객체
|
|
302
|
+
* @param params.text - 측정할 텍스트
|
|
303
|
+
* @param params.fontConfig - 폰트 설정 (예: 'bold 16px Arial')
|
|
304
|
+
* @param params.padding - 텍스트 박스에 적용할 패딩 값
|
|
305
|
+
* @param params.minWidth - 최소 너비
|
|
306
|
+
* @returns 계산된 텍스트 박스의 너비
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```typescript
|
|
310
|
+
* const width = calculateTextBoxWidth({
|
|
311
|
+
* text: 'Hello World',
|
|
312
|
+
* fontConfig: 'bold 16px Arial',
|
|
313
|
+
* padding: 10,
|
|
314
|
+
* minWidth: 50
|
|
315
|
+
* });
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
var calculateTextBoxWidth = function (_a) {
|
|
320
|
+
var text = _a.text,
|
|
321
|
+
fontConfig = _a.fontConfig,
|
|
322
|
+
padding = _a.padding,
|
|
323
|
+
minWidth = _a.minWidth;
|
|
324
|
+
if (!tempCtx) return 0;
|
|
325
|
+
tempCtx.font = fontConfig;
|
|
326
|
+
var textWidth = tempCtx.measureText(text).width;
|
|
327
|
+
return Math.max(minWidth, textWidth + padding);
|
|
328
|
+
};
|
|
110
329
|
|
|
330
|
+
exports.calculateTextBoxWidth = calculateTextBoxWidth;
|
|
111
331
|
exports.computeMarkerOffset = computeMarkerOffset;
|
|
112
332
|
exports.computePolygonOffsets = computePolygonOffsets;
|
|
333
|
+
exports.hexToRgba = hexToRgba;
|
|
113
334
|
exports.isPointInMarkerData = isPointInMarkerData;
|
|
114
335
|
exports.isPointInPolygon = isPointInPolygon;
|
|
115
336
|
exports.isPointInPolygonData = isPointInPolygonData;
|
|
@@ -172,7 +172,7 @@ function MapMarkerWrapper(_a) {
|
|
|
172
172
|
var onMouseOverHandler = function (e) {
|
|
173
173
|
var _a;
|
|
174
174
|
|
|
175
|
-
var marker = markerRef.current;
|
|
175
|
+
var marker = markerRef.current;
|
|
176
176
|
|
|
177
177
|
if (marker) {
|
|
178
178
|
var mouseOverHandler = (_a = options === null || options === void 0 ? void 0 : options.event) === null || _a === void 0 ? void 0 : _a.get('mouseover');
|
|
@@ -185,6 +185,25 @@ function MapMarkerWrapper(_a) {
|
|
|
185
185
|
|
|
186
186
|
next && topOnHover && controller.markerToTheTop(marker);
|
|
187
187
|
}
|
|
188
|
+
}; // 20251014 | 장한별 | mouseleave 이벤트 추가, 마우스가 마커 위에서 떠날 때 원래 zindex 를 복구하기 위함
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
var onMouseLeaveHandler = function (e) {
|
|
192
|
+
var _a;
|
|
193
|
+
|
|
194
|
+
var marker = markerRef.current;
|
|
195
|
+
|
|
196
|
+
if (marker) {
|
|
197
|
+
var mouseOutHandler = (_a = options === null || options === void 0 ? void 0 : options.event) === null || _a === void 0 ? void 0 : _a.get('mouseout');
|
|
198
|
+
var next = true;
|
|
199
|
+
|
|
200
|
+
if (mouseOutHandler) {
|
|
201
|
+
var hasNext = mouseOutHandler(e);
|
|
202
|
+
hasNext !== undefined && (next = hasNext);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
next && topOnHover && controller.restoreMarkerZIndex(marker);
|
|
206
|
+
}
|
|
188
207
|
}; //create object
|
|
189
208
|
|
|
190
209
|
|
|
@@ -202,10 +221,12 @@ function MapMarkerWrapper(_a) {
|
|
|
202
221
|
}); //드래그 여부 초기화를 먼저 수행하기 위해 capture : true 처리
|
|
203
222
|
|
|
204
223
|
divElement.addEventListener('mouseover', onMouseOverHandler);
|
|
224
|
+
divElement.addEventListener('mouseleave', onMouseLeaveHandler);
|
|
205
225
|
return function () {
|
|
206
226
|
divElement.removeEventListener('click', onClickHandler);
|
|
207
227
|
divElement.removeEventListener('mousedown', onMousedownHandler);
|
|
208
228
|
divElement.removeEventListener('mouseover', onMouseOverHandler);
|
|
229
|
+
divElement.removeEventListener('mouseleave', onMouseLeaveHandler);
|
|
209
230
|
|
|
210
231
|
if (markerRef.current) {
|
|
211
232
|
controller.clearDrawable(markerRef.current);
|
|
@@ -28,6 +28,7 @@ export declare class GoogleMintMapController extends MintMapController {
|
|
|
28
28
|
private getMaxZIndex;
|
|
29
29
|
setMarkerZIndex(marker: Marker, zIndex: number): void;
|
|
30
30
|
markerToTheTop(marker: Marker): void;
|
|
31
|
+
restoreMarkerZIndex(marker: Marker): void;
|
|
31
32
|
clearDrawable(drawable: Drawable): boolean;
|
|
32
33
|
private dragged;
|
|
33
34
|
isMapDragged(): boolean;
|
|
@@ -20,7 +20,9 @@ var polygon = require('../core/util/polygon.js');
|
|
|
20
20
|
require('../naver/NaverMintMapController.js');
|
|
21
21
|
require('../core/advanced/canvas/CanvasMarkerClaude.js');
|
|
22
22
|
require('../core/advanced/MapLoadingComponents.js');
|
|
23
|
-
require('
|
|
23
|
+
require('konva');
|
|
24
|
+
require('../core/advanced/woongCanvas/shared/types.js');
|
|
25
|
+
require('../core/advanced/woongCanvas/shared/utils.js');
|
|
24
26
|
require('../core/advanced/woongCanvas/shared/context.js');
|
|
25
27
|
require('../core/advanced/woongCanvas/shared/performance.js');
|
|
26
28
|
require('../core/wrapper/MapControlWrapper.js');
|
|
@@ -339,6 +341,10 @@ function (_super) {
|
|
|
339
341
|
}
|
|
340
342
|
};
|
|
341
343
|
|
|
344
|
+
GoogleMintMapController.prototype.restoreMarkerZIndex = function (marker) {// Google Maps에서는 restoreMarkerZIndex 기능을 지원하지 않습니다.
|
|
345
|
+
// 이 기능은 Naver Maps에서만 사용 가능합니다.
|
|
346
|
+
};
|
|
347
|
+
|
|
342
348
|
GoogleMintMapController.prototype.clearDrawable = function (drawable) {
|
|
343
349
|
if (drawable && drawable.native) {
|
|
344
350
|
if (drawable.native instanceof google.maps.Marker || drawable.native instanceof google.maps.Polygon || drawable.native instanceof google.maps.Polyline) {
|
|
@@ -31,6 +31,7 @@ export declare class KakaoMintMapController extends MintMapController {
|
|
|
31
31
|
private getMaxZIndex;
|
|
32
32
|
setMarkerZIndex(marker: Marker, zIndex: number): void;
|
|
33
33
|
markerToTheTop(marker: Marker): void;
|
|
34
|
+
restoreMarkerZIndex(marker: Marker): void;
|
|
34
35
|
clearDrawable(drawable: Drawable): boolean;
|
|
35
36
|
private dragged;
|
|
36
37
|
isMapDragged(): boolean;
|
|
@@ -21,7 +21,9 @@ var polygon = require('../core/util/polygon.js');
|
|
|
21
21
|
require('../naver/NaverMintMapController.js');
|
|
22
22
|
require('../core/advanced/canvas/CanvasMarkerClaude.js');
|
|
23
23
|
require('../core/advanced/MapLoadingComponents.js');
|
|
24
|
-
require('
|
|
24
|
+
require('konva');
|
|
25
|
+
require('../core/advanced/woongCanvas/shared/types.js');
|
|
26
|
+
require('../core/advanced/woongCanvas/shared/utils.js');
|
|
25
27
|
require('../core/advanced/woongCanvas/shared/context.js');
|
|
26
28
|
require('../core/advanced/woongCanvas/shared/performance.js');
|
|
27
29
|
require('../core/wrapper/MapControlWrapper.js');
|
|
@@ -347,6 +349,10 @@ function (_super) {
|
|
|
347
349
|
}
|
|
348
350
|
};
|
|
349
351
|
|
|
352
|
+
KakaoMintMapController.prototype.restoreMarkerZIndex = function (marker) {// Kakao Maps에서는 restoreMarkerZIndex 기능을 지원하지 않습니다.
|
|
353
|
+
// 이 기능은 Naver Maps에서만 사용 가능합니다.
|
|
354
|
+
};
|
|
355
|
+
|
|
350
356
|
KakaoMintMapController.prototype.clearDrawable = function (drawable) {
|
|
351
357
|
var _this = this;
|
|
352
358
|
|
|
@@ -27,9 +27,12 @@ export declare class NaverMintMapController extends MintMapController {
|
|
|
27
27
|
createMarker(marker: Marker): void;
|
|
28
28
|
updateMarker(marker: Marker, options: MarkerOptions): void;
|
|
29
29
|
private markerMaxZIndex;
|
|
30
|
+
private markerOriginalZIndex;
|
|
30
31
|
private getMaxZIndex;
|
|
32
|
+
private getCurrentZIndex;
|
|
31
33
|
setMarkerZIndex(marker: Marker, zIndex: number): void;
|
|
32
34
|
markerToTheTop(marker: Marker): void;
|
|
35
|
+
restoreMarkerZIndex(marker: Marker): void;
|
|
33
36
|
clearDrawable(drawable: Drawable): boolean;
|
|
34
37
|
private dragged;
|
|
35
38
|
isMapDragged(): boolean;
|
|
@@ -20,7 +20,9 @@ require('../core/util/geo.js');
|
|
|
20
20
|
var polygon = require('../core/util/polygon.js');
|
|
21
21
|
require('../core/advanced/canvas/CanvasMarkerClaude.js');
|
|
22
22
|
require('../core/advanced/MapLoadingComponents.js');
|
|
23
|
-
require('
|
|
23
|
+
require('konva');
|
|
24
|
+
require('../core/advanced/woongCanvas/shared/types.js');
|
|
25
|
+
require('../core/advanced/woongCanvas/shared/utils.js');
|
|
24
26
|
require('../core/advanced/woongCanvas/shared/context.js');
|
|
25
27
|
require('../core/advanced/woongCanvas/shared/performance.js');
|
|
26
28
|
require('../core/wrapper/MapControlWrapper.js');
|
|
@@ -339,20 +341,54 @@ function (_super) {
|
|
|
339
341
|
}
|
|
340
342
|
};
|
|
341
343
|
|
|
342
|
-
NaverMintMapController.prototype.
|
|
344
|
+
NaverMintMapController.prototype.getCurrentZIndex = function (marker) {
|
|
343
345
|
if (this.map && marker.element && marker.element instanceof HTMLElement) {
|
|
344
346
|
var parent_1 = marker.element.parentElement;
|
|
345
347
|
|
|
346
|
-
if (parent_1) {
|
|
347
|
-
|
|
348
|
+
if (parent_1 && parent_1.style.zIndex) {
|
|
349
|
+
var zIndex = Number(parent_1.style.zIndex);
|
|
350
|
+
return isNaN(zIndex) ? undefined : zIndex;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return undefined;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
NaverMintMapController.prototype.setMarkerZIndex = function (marker, zIndex) {
|
|
358
|
+
if (this.map && marker.element && marker.element instanceof HTMLElement) {
|
|
359
|
+
var parent_2 = marker.element.parentElement;
|
|
360
|
+
|
|
361
|
+
if (parent_2) {
|
|
362
|
+
parent_2.style.zIndex = String(zIndex);
|
|
348
363
|
}
|
|
349
364
|
}
|
|
350
365
|
};
|
|
351
366
|
|
|
352
367
|
NaverMintMapController.prototype.markerToTheTop = function (marker) {
|
|
368
|
+
// 이미 최상위로 올라간 상태면 (원래 zIndex가 이미 저장됨) 중복 실행 방지
|
|
369
|
+
if (this.markerOriginalZIndex !== undefined) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
var currentZIndex = this.getCurrentZIndex(marker); // undefined면 null로 저장 (원래 zIndex가 없었음을 표시)
|
|
374
|
+
|
|
375
|
+
this.markerOriginalZIndex = currentZIndex !== undefined ? currentZIndex : null;
|
|
353
376
|
this.setMarkerZIndex(marker, this.getMaxZIndex(1));
|
|
354
377
|
};
|
|
355
378
|
|
|
379
|
+
NaverMintMapController.prototype.restoreMarkerZIndex = function (marker) {
|
|
380
|
+
if (this.markerOriginalZIndex !== undefined) {
|
|
381
|
+
if (this.markerOriginalZIndex === null) {
|
|
382
|
+
// 원래 zIndex가 없었으면 제거 (또는 초기값 0으로)
|
|
383
|
+
this.setMarkerZIndex(marker, 0);
|
|
384
|
+
} else {
|
|
385
|
+
this.setMarkerZIndex(marker, this.markerOriginalZIndex);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
this.markerOriginalZIndex = undefined;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
356
392
|
NaverMintMapController.prototype.clearDrawable = function (drawable) {
|
|
357
393
|
var _a;
|
|
358
394
|
|