react-naver-maps-kit 1.0.1 → 1.1.0

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/index.cjs CHANGED
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  let react = require("react");
3
3
  let react_jsx_runtime = require("react/jsx-runtime");
4
4
  let react_dom = require("react-dom");
5
+ let react_dom_client = require("react-dom/client");
5
6
 
6
7
  //#region src/core/loader/loadNaverMapsScript.ts
7
8
  const NAVER_MAPS_SCRIPT_BASE_URL = "https://oapi.map.naver.com/openapi/v3/maps.js";
@@ -872,6 +873,19 @@ function useNaverMapInstance(options = {}) {
872
873
  }).map;
873
874
  }
874
875
 
876
+ //#endregion
877
+ //#region src/overlays/marker-clusterer/ClustererContext.ts
878
+ /**
879
+ * `<MarkerClusterer>`가 제공하는 React Context.
880
+ *
881
+ * `<Marker>`는 이 context를 통해 클러스터러에 자신을 등록합니다.
882
+ * `null`이면 클러스터러 밖에 있는 것이므로 일반 마커로 동작합니다.
883
+ *
884
+ * @internal 라이브러리 내부 전용. 직접 사용하지 마세요.
885
+ */
886
+ const ClustererContext = (0, react.createContext)(null);
887
+ ClustererContext.displayName = "ClustererContext";
888
+
875
889
  //#endregion
876
890
  //#region src/overlays/marker/Marker.tsx
877
891
  function pickHtmlIconAnchor(icon) {
@@ -905,87 +919,87 @@ function toMarkerOptions(props, targetMap, icon) {
905
919
  if (props.zIndex !== void 0) options.zIndex = props.zIndex;
906
920
  return options;
907
921
  }
908
- function buildMarkerEventBindings(props) {
922
+ function buildMarkerEventBindings(propsRef) {
909
923
  return [
910
924
  {
911
925
  eventName: "click",
912
- invoke: props.onClick ? (event) => props.onClick?.(event) : void 0
926
+ invoke: (event) => propsRef.current.onClick?.(event)
913
927
  },
914
928
  {
915
929
  eventName: "dblclick",
916
- invoke: props.onDblClick ? (event) => props.onDblClick?.(event) : void 0
930
+ invoke: (event) => propsRef.current.onDblClick?.(event)
917
931
  },
918
932
  {
919
933
  eventName: "rightclick",
920
- invoke: props.onRightClick ? (event) => props.onRightClick?.(event) : void 0
934
+ invoke: (event) => propsRef.current.onRightClick?.(event)
921
935
  },
922
936
  {
923
937
  eventName: "mousedown",
924
- invoke: props.onMouseDown ? (event) => props.onMouseDown?.(event) : void 0
938
+ invoke: (event) => propsRef.current.onMouseDown?.(event)
925
939
  },
926
940
  {
927
941
  eventName: "mouseup",
928
- invoke: props.onMouseUp ? (event) => props.onMouseUp?.(event) : void 0
942
+ invoke: (event) => propsRef.current.onMouseUp?.(event)
929
943
  },
930
944
  {
931
945
  eventName: "touchstart",
932
- invoke: props.onTouchStart ? (event) => props.onTouchStart?.(event) : void 0
946
+ invoke: (event) => propsRef.current.onTouchStart?.(event)
933
947
  },
934
948
  {
935
949
  eventName: "touchend",
936
- invoke: props.onTouchEnd ? (event) => props.onTouchEnd?.(event) : void 0
950
+ invoke: (event) => propsRef.current.onTouchEnd?.(event)
937
951
  },
938
952
  {
939
953
  eventName: "dragstart",
940
- invoke: props.onDragStart ? (event) => props.onDragStart?.(event) : void 0
954
+ invoke: (event) => propsRef.current.onDragStart?.(event)
941
955
  },
942
956
  {
943
957
  eventName: "drag",
944
- invoke: props.onDrag ? (event) => props.onDrag?.(event) : void 0
958
+ invoke: (event) => propsRef.current.onDrag?.(event)
945
959
  },
946
960
  {
947
961
  eventName: "dragend",
948
- invoke: props.onDragEnd ? (event) => props.onDragEnd?.(event) : void 0
962
+ invoke: (event) => propsRef.current.onDragEnd?.(event)
949
963
  },
950
964
  {
951
965
  eventName: "clickable_changed",
952
- invoke: props.onClickableChanged ? (event) => props.onClickableChanged?.(event) : void 0
966
+ invoke: (event) => propsRef.current.onClickableChanged?.(event)
953
967
  },
954
968
  {
955
969
  eventName: "cursor_changed",
956
- invoke: props.onCursorChanged ? (event) => props.onCursorChanged?.(event) : void 0
970
+ invoke: (event) => propsRef.current.onCursorChanged?.(event)
957
971
  },
958
972
  {
959
973
  eventName: "draggable_changed",
960
- invoke: props.onDraggableChanged ? (event) => props.onDraggableChanged?.(event) : void 0
974
+ invoke: (event) => propsRef.current.onDraggableChanged?.(event)
961
975
  },
962
976
  {
963
977
  eventName: "icon_changed",
964
- invoke: props.onIconChanged ? (event) => props.onIconChanged?.(event) : void 0
978
+ invoke: (event) => propsRef.current.onIconChanged?.(event)
965
979
  },
966
980
  {
967
981
  eventName: "icon_loaded",
968
- invoke: props.onIconLoaded ? (event) => props.onIconLoaded?.(event) : void 0
982
+ invoke: (event) => propsRef.current.onIconLoaded?.(event)
969
983
  },
970
984
  {
971
985
  eventName: "position_changed",
972
- invoke: props.onPositionChanged ? (event) => props.onPositionChanged?.(event) : void 0
986
+ invoke: (event) => propsRef.current.onPositionChanged?.(event)
973
987
  },
974
988
  {
975
989
  eventName: "shape_changed",
976
- invoke: props.onShapeChanged ? (event) => props.onShapeChanged?.(event) : void 0
990
+ invoke: (event) => propsRef.current.onShapeChanged?.(event)
977
991
  },
978
992
  {
979
993
  eventName: "title_changed",
980
- invoke: props.onTitleChanged ? (event) => props.onTitleChanged?.(event) : void 0
994
+ invoke: (event) => propsRef.current.onTitleChanged?.(event)
981
995
  },
982
996
  {
983
997
  eventName: "visible_changed",
984
- invoke: props.onVisibleChanged ? (event) => props.onVisibleChanged?.(event) : void 0
998
+ invoke: (event) => propsRef.current.onVisibleChanged?.(event)
985
999
  },
986
1000
  {
987
1001
  eventName: "zIndex_changed",
988
- invoke: props.onZIndexChanged ? (event) => props.onZIndexChanged?.(event) : void 0
1002
+ invoke: (event) => propsRef.current.onZIndexChanged?.(event)
989
1003
  }
990
1004
  ];
991
1005
  }
@@ -998,8 +1012,31 @@ function bindMarkerEventListeners(marker, listenersRef, bindings) {
998
1012
  binding.invoke?.(event);
999
1013
  }));
1000
1014
  }
1015
+ function toLatLngLiteral(position) {
1016
+ if (!position) return null;
1017
+ if (typeof position === "object" && "lat" in position && "lng" in position) {
1018
+ if (typeof position.lat === "number" && typeof position.lng === "number") return {
1019
+ lat: position.lat,
1020
+ lng: position.lng
1021
+ };
1022
+ if (typeof position.lat === "function" && typeof position.lng === "function") return {
1023
+ lat: position.lat(),
1024
+ lng: position.lng()
1025
+ };
1026
+ }
1027
+ if (typeof position === "object" && "x" in position && "y" in position) {
1028
+ const p = position;
1029
+ if (typeof p.x === "number" && typeof p.y === "number") return {
1030
+ lat: p.y,
1031
+ lng: p.x
1032
+ };
1033
+ }
1034
+ return null;
1035
+ }
1001
1036
  const Marker = (0, react.forwardRef)(function MarkerInner(props, ref) {
1002
1037
  const { map: contextMap, sdkStatus } = useNaverMap();
1038
+ const clustererRegistry = (0, react.useContext)(ClustererContext);
1039
+ const isInsideClusterer = clustererRegistry !== null && clustererRegistry.enabled;
1003
1040
  const markerRef = (0, react.useRef)(null);
1004
1041
  const markerEventListenersRef = (0, react.useRef)([]);
1005
1042
  const onMarkerDestroyRef = (0, react.useRef)(props.onMarkerDestroy);
@@ -1007,14 +1044,43 @@ const Marker = (0, react.forwardRef)(function MarkerInner(props, ref) {
1007
1044
  const [portalReady, setPortalReady] = (0, react.useState)(false);
1008
1045
  const hasChildren = props.children !== void 0 && props.children !== null;
1009
1046
  const targetMap = props.map ?? contextMap;
1047
+ const propsRef = (0, react.useRef)(props);
1048
+ (0, react.useEffect)(() => {
1049
+ propsRef.current = props;
1050
+ });
1010
1051
  (0, react.useEffect)(() => {
1052
+ if (!clustererRegistry) return;
1053
+ const id = props.clustererItemId;
1054
+ if (id === void 0 || id === null) return;
1055
+ const latLng = toLatLngLiteral(props.position);
1056
+ if (!latLng) return;
1057
+ clustererRegistry.register({
1058
+ id,
1059
+ position: latLng,
1060
+ data: props.item ?? null,
1061
+ markerOptions: props.icon !== void 0 ? { icon: props.icon } : void 0
1062
+ });
1063
+ return () => {
1064
+ clustererRegistry.unregister(id);
1065
+ };
1066
+ }, [
1067
+ clustererRegistry,
1068
+ props.clustererItemId,
1069
+ props.position,
1070
+ props.item,
1071
+ props.icon
1072
+ ]);
1073
+ (0, react.useEffect)(() => {
1074
+ if (isInsideClusterer) return;
1011
1075
  onMarkerDestroyRef.current = props.onMarkerDestroy;
1012
- }, [props.onMarkerDestroy]);
1076
+ }, [isInsideClusterer, props.onMarkerDestroy]);
1013
1077
  (0, react.useEffect)(() => {
1078
+ if (isInsideClusterer) return;
1014
1079
  if (typeof document === "undefined") return;
1015
1080
  setMarkerDiv(document.createElement("div"));
1016
- }, []);
1081
+ }, [isInsideClusterer]);
1017
1082
  (0, react.useEffect)(() => {
1083
+ if (isInsideClusterer) return;
1018
1084
  if (!hasChildren || !markerDiv) {
1019
1085
  setPortalReady(false);
1020
1086
  return;
@@ -1031,7 +1097,11 @@ const Marker = (0, react.forwardRef)(function MarkerInner(props, ref) {
1031
1097
  return () => {
1032
1098
  observer.disconnect();
1033
1099
  };
1034
- }, [hasChildren, markerDiv]);
1100
+ }, [
1101
+ isInsideClusterer,
1102
+ hasChildren,
1103
+ markerDiv
1104
+ ]);
1035
1105
  const invokeMarkerMethod = (0, react.useCallback)((methodName, ...args) => {
1036
1106
  const marker = markerRef.current;
1037
1107
  if (!marker) return;
@@ -1087,68 +1157,1332 @@ const Marker = (0, react.forwardRef)(function MarkerInner(props, ref) {
1087
1157
  setZIndex: (...args) => invokeMarkerMethod("setZIndex", ...args)
1088
1158
  }), [invokeMarkerMethod]);
1089
1159
  (0, react.useEffect)(() => {
1160
+ if (isInsideClusterer) return;
1090
1161
  if (sdkStatus !== "ready" || !targetMap || markerRef.current) return;
1091
1162
  if (hasChildren && !portalReady) return;
1092
1163
  try {
1093
- const resolvedIcon = resolveMarkerIcon(props.icon, markerDiv, hasChildren);
1094
- const marker = new naver.maps.Marker(toMarkerOptions(props, targetMap, resolvedIcon));
1164
+ const resolvedIcon = resolveMarkerIcon(propsRef.current.icon, markerDiv, hasChildren);
1165
+ const marker = new naver.maps.Marker(toMarkerOptions(propsRef.current, targetMap, resolvedIcon));
1095
1166
  markerRef.current = marker;
1096
- if (props.collisionBehavior !== void 0) marker.setOptions("collisionBehavior", props.collisionBehavior);
1097
- if (props.collisionBoxSize !== void 0) marker.setOptions("collisionBoxSize", props.collisionBoxSize);
1098
- bindMarkerEventListeners(marker, markerEventListenersRef, buildMarkerEventBindings(props));
1099
- props.onMarkerReady?.(marker);
1167
+ if (propsRef.current.collisionBehavior !== void 0) marker.setOptions("collisionBehavior", propsRef.current.collisionBehavior);
1168
+ if (propsRef.current.collisionBoxSize !== void 0) marker.setOptions("collisionBoxSize", propsRef.current.collisionBoxSize);
1169
+ bindMarkerEventListeners(marker, markerEventListenersRef, buildMarkerEventBindings(propsRef));
1170
+ propsRef.current.onMarkerReady?.(marker);
1100
1171
  } catch (error) {
1101
1172
  const normalizedError = error instanceof Error ? error : /* @__PURE__ */ new Error("Failed to create naver.maps.Marker instance.");
1102
- props.onMarkerError?.(normalizedError);
1173
+ propsRef.current.onMarkerError?.(normalizedError);
1103
1174
  }
1104
1175
  }, [
1176
+ isInsideClusterer,
1105
1177
  hasChildren,
1106
1178
  markerDiv,
1107
1179
  portalReady,
1108
- props,
1109
1180
  sdkStatus,
1110
1181
  targetMap
1111
1182
  ]);
1112
1183
  (0, react.useLayoutEffect)(() => {
1184
+ if (isInsideClusterer) return;
1113
1185
  const marker = markerRef.current;
1114
1186
  if (!marker || !targetMap) return;
1115
1187
  const rafId = requestAnimationFrame(() => {
1116
- const nextOptions = toMarkerOptions(props, targetMap, hasChildren && portalReady ? resolveMarkerIcon(props.icon, markerDiv, hasChildren) : props.icon);
1188
+ const currentProps = propsRef.current;
1189
+ const nextOptions = toMarkerOptions(currentProps, targetMap, hasChildren && portalReady ? resolveMarkerIcon(currentProps.icon, markerDiv, hasChildren) : currentProps.icon);
1117
1190
  marker.setOptions(nextOptions);
1118
- if (props.collisionBehavior !== void 0) marker.setOptions("collisionBehavior", props.collisionBehavior);
1119
- if (props.collisionBoxSize !== void 0) marker.setOptions("collisionBoxSize", props.collisionBoxSize);
1191
+ if (currentProps.collisionBehavior !== void 0) marker.setOptions("collisionBehavior", currentProps.collisionBehavior);
1192
+ if (currentProps.collisionBoxSize !== void 0) marker.setOptions("collisionBoxSize", currentProps.collisionBoxSize);
1120
1193
  });
1121
1194
  return () => {
1122
1195
  cancelAnimationFrame(rafId);
1123
1196
  };
1124
1197
  }, [
1198
+ isInsideClusterer,
1125
1199
  hasChildren,
1126
1200
  markerDiv,
1127
1201
  portalReady,
1128
- props,
1129
- targetMap
1202
+ targetMap,
1203
+ props.position,
1204
+ props.icon,
1205
+ props.animation,
1206
+ props.shape,
1207
+ props.title,
1208
+ props.cursor,
1209
+ props.clickable,
1210
+ props.draggable,
1211
+ props.visible,
1212
+ props.zIndex,
1213
+ props.collisionBehavior,
1214
+ props.collisionBoxSize
1130
1215
  ]);
1131
1216
  (0, react.useEffect)(() => {
1217
+ if (isInsideClusterer) return;
1132
1218
  const marker = markerRef.current;
1133
1219
  if (!marker) return;
1134
- bindMarkerEventListeners(marker, markerEventListenersRef, buildMarkerEventBindings(props));
1220
+ bindMarkerEventListeners(marker, markerEventListenersRef, buildMarkerEventBindings(propsRef));
1135
1221
  return () => {
1136
1222
  if (markerEventListenersRef.current.length > 0) {
1137
1223
  naver.maps.Event.removeListener(markerEventListenersRef.current);
1138
1224
  markerEventListenersRef.current = [];
1139
1225
  }
1140
1226
  };
1141
- }, [props]);
1227
+ }, [isInsideClusterer]);
1142
1228
  (0, react.useEffect)(() => {
1229
+ if (isInsideClusterer) return;
1143
1230
  return () => {
1144
1231
  teardownMarker();
1145
1232
  };
1146
- }, [teardownMarker]);
1233
+ }, [isInsideClusterer, teardownMarker]);
1234
+ if (isInsideClusterer) return null;
1147
1235
  if (!hasChildren || !markerDiv) return null;
1148
1236
  return (0, react_dom.createPortal)(props.children, markerDiv);
1149
1237
  });
1150
1238
  Marker.displayName = "Marker";
1151
1239
 
1240
+ //#endregion
1241
+ //#region src/overlays/marker-clusterer/algorithms/grid.ts
1242
+ const DEFAULT_OPTIONS$2 = {
1243
+ gridSize: 60,
1244
+ minClusterSize: 2,
1245
+ maxZoom: 21
1246
+ };
1247
+ var GridAlgorithm = class {
1248
+ options;
1249
+ constructor(options) {
1250
+ this.options = {
1251
+ ...DEFAULT_OPTIONS$2,
1252
+ ...options
1253
+ };
1254
+ }
1255
+ cluster(items, ctx) {
1256
+ if (ctx.zoom >= this.options.maxZoom) return {
1257
+ clusters: [],
1258
+ points: items
1259
+ };
1260
+ const { gridSize } = this.options;
1261
+ const scale = Math.pow(2, ctx.zoom);
1262
+ const cellMap = /* @__PURE__ */ new Map();
1263
+ for (const item of items) {
1264
+ const worldX = (item.position.lng + 180) / 360 * scale;
1265
+ const latRad = item.position.lat * Math.PI / 180;
1266
+ const worldY = (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * scale;
1267
+ const key = `${Math.floor(worldX * 256 / gridSize)}:${Math.floor(worldY * 256 / gridSize)}`;
1268
+ let cell = cellMap.get(key);
1269
+ if (!cell) {
1270
+ cell = [];
1271
+ cellMap.set(key, cell);
1272
+ }
1273
+ cell.push(item);
1274
+ }
1275
+ const clusters = [];
1276
+ const points = [];
1277
+ for (const [key, cellItems] of cellMap) {
1278
+ if (cellItems.length < this.options.minClusterSize) {
1279
+ points.push(...cellItems);
1280
+ continue;
1281
+ }
1282
+ let sumLat = 0;
1283
+ let sumLng = 0;
1284
+ let minLat = Infinity;
1285
+ let maxLat = -Infinity;
1286
+ let minLng = Infinity;
1287
+ let maxLng = -Infinity;
1288
+ for (const item of cellItems) {
1289
+ sumLat += item.position.lat;
1290
+ sumLng += item.position.lng;
1291
+ minLat = Math.min(minLat, item.position.lat);
1292
+ maxLat = Math.max(maxLat, item.position.lat);
1293
+ minLng = Math.min(minLng, item.position.lng);
1294
+ maxLng = Math.max(maxLng, item.position.lng);
1295
+ }
1296
+ clusters.push({
1297
+ id: `grid-${key}`,
1298
+ position: {
1299
+ lat: sumLat / cellItems.length,
1300
+ lng: sumLng / cellItems.length
1301
+ },
1302
+ count: cellItems.length,
1303
+ bounds: {
1304
+ south: minLat,
1305
+ north: maxLat,
1306
+ west: minLng,
1307
+ east: maxLng
1308
+ },
1309
+ items: cellItems
1310
+ });
1311
+ }
1312
+ return {
1313
+ clusters,
1314
+ points
1315
+ };
1316
+ }
1317
+ setOptions(options) {
1318
+ if (typeof options === "object" && options !== null) this.options = {
1319
+ ...this.options,
1320
+ ...options
1321
+ };
1322
+ }
1323
+ };
1324
+
1325
+ //#endregion
1326
+ //#region src/overlays/marker-clusterer/algorithms/radius.ts
1327
+ const DEFAULT_OPTIONS$1 = {
1328
+ radius: 60,
1329
+ minClusterSize: 2,
1330
+ maxZoom: 21
1331
+ };
1332
+ function toWorldPixel(lat, lng, zoom) {
1333
+ const scale = Math.pow(2, zoom) * 256;
1334
+ const x = (lng + 180) / 360 * scale;
1335
+ const latRad = lat * Math.PI / 180;
1336
+ return {
1337
+ x,
1338
+ y: (1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) / 2 * scale
1339
+ };
1340
+ }
1341
+ function pixelDistance(a, b) {
1342
+ const dx = a.x - b.x;
1343
+ const dy = a.y - b.y;
1344
+ return Math.sqrt(dx * dx + dy * dy);
1345
+ }
1346
+ var RadiusAlgorithm = class {
1347
+ options;
1348
+ constructor(options) {
1349
+ this.options = {
1350
+ ...DEFAULT_OPTIONS$1,
1351
+ ...options
1352
+ };
1353
+ }
1354
+ cluster(items, ctx) {
1355
+ if (ctx.zoom >= this.options.maxZoom) return {
1356
+ clusters: [],
1357
+ points: items
1358
+ };
1359
+ const { radius } = this.options;
1360
+ const used = /* @__PURE__ */ new Set();
1361
+ const clusters = [];
1362
+ const points = [];
1363
+ const pixelPositions = items.map((item) => toWorldPixel(item.position.lat, item.position.lng, ctx.zoom));
1364
+ for (let i = 0; i < items.length; i++) {
1365
+ if (used.has(items[i].id)) continue;
1366
+ const center = pixelPositions[i];
1367
+ const group = [items[i]];
1368
+ used.add(items[i].id);
1369
+ for (let j = i + 1; j < items.length; j++) {
1370
+ if (used.has(items[j].id)) continue;
1371
+ if (pixelDistance(center, pixelPositions[j]) <= radius) {
1372
+ group.push(items[j]);
1373
+ used.add(items[j].id);
1374
+ }
1375
+ }
1376
+ if (group.length < this.options.minClusterSize) {
1377
+ points.push(...group);
1378
+ continue;
1379
+ }
1380
+ let sumLat = 0;
1381
+ let sumLng = 0;
1382
+ let minLat = Infinity;
1383
+ let maxLat = -Infinity;
1384
+ let minLng = Infinity;
1385
+ let maxLng = -Infinity;
1386
+ for (const item of group) {
1387
+ sumLat += item.position.lat;
1388
+ sumLng += item.position.lng;
1389
+ minLat = Math.min(minLat, item.position.lat);
1390
+ maxLat = Math.max(maxLat, item.position.lat);
1391
+ minLng = Math.min(minLng, item.position.lng);
1392
+ maxLng = Math.max(maxLng, item.position.lng);
1393
+ }
1394
+ const avgLat = sumLat / group.length;
1395
+ const avgLng = sumLng / group.length;
1396
+ clusters.push({
1397
+ id: `radius-${avgLat.toFixed(6)}-${avgLng.toFixed(6)}-${group.length}`,
1398
+ position: {
1399
+ lat: avgLat,
1400
+ lng: avgLng
1401
+ },
1402
+ count: group.length,
1403
+ bounds: {
1404
+ south: minLat,
1405
+ north: maxLat,
1406
+ west: minLng,
1407
+ east: maxLng
1408
+ },
1409
+ items: group
1410
+ });
1411
+ }
1412
+ return {
1413
+ clusters,
1414
+ points
1415
+ };
1416
+ }
1417
+ setOptions(options) {
1418
+ if (typeof options === "object" && options !== null) this.options = {
1419
+ ...this.options,
1420
+ ...options
1421
+ };
1422
+ }
1423
+ };
1424
+
1425
+ //#endregion
1426
+ //#region ../../node_modules/.pnpm/kdbush@4.0.2/node_modules/kdbush/index.js
1427
+ const ARRAY_TYPES = [
1428
+ Int8Array,
1429
+ Uint8Array,
1430
+ Uint8ClampedArray,
1431
+ Int16Array,
1432
+ Uint16Array,
1433
+ Int32Array,
1434
+ Uint32Array,
1435
+ Float32Array,
1436
+ Float64Array
1437
+ ];
1438
+ /** @typedef {Int8ArrayConstructor | Uint8ArrayConstructor | Uint8ClampedArrayConstructor | Int16ArrayConstructor | Uint16ArrayConstructor | Int32ArrayConstructor | Uint32ArrayConstructor | Float32ArrayConstructor | Float64ArrayConstructor} TypedArrayConstructor */
1439
+ const VERSION = 1;
1440
+ const HEADER_SIZE = 8;
1441
+ var KDBush = class KDBush {
1442
+ /**
1443
+ * Creates an index from raw `ArrayBuffer` data.
1444
+ * @param {ArrayBuffer} data
1445
+ */
1446
+ static from(data) {
1447
+ if (!(data instanceof ArrayBuffer)) throw new Error("Data must be an instance of ArrayBuffer.");
1448
+ const [magic, versionAndType] = new Uint8Array(data, 0, 2);
1449
+ if (magic !== 219) throw new Error("Data does not appear to be in a KDBush format.");
1450
+ const version = versionAndType >> 4;
1451
+ if (version !== VERSION) throw new Error(`Got v${version} data when expected v${VERSION}.`);
1452
+ const ArrayType = ARRAY_TYPES[versionAndType & 15];
1453
+ if (!ArrayType) throw new Error("Unrecognized array type.");
1454
+ const [nodeSize] = new Uint16Array(data, 2, 1);
1455
+ const [numItems] = new Uint32Array(data, 4, 1);
1456
+ return new KDBush(numItems, nodeSize, ArrayType, data);
1457
+ }
1458
+ /**
1459
+ * Creates an index that will hold a given number of items.
1460
+ * @param {number} numItems
1461
+ * @param {number} [nodeSize=64] Size of the KD-tree node (64 by default).
1462
+ * @param {TypedArrayConstructor} [ArrayType=Float64Array] The array type used for coordinates storage (`Float64Array` by default).
1463
+ * @param {ArrayBuffer} [data] (For internal use only)
1464
+ */
1465
+ constructor(numItems, nodeSize = 64, ArrayType = Float64Array, data) {
1466
+ if (isNaN(numItems) || numItems < 0) throw new Error(`Unpexpected numItems value: ${numItems}.`);
1467
+ this.numItems = +numItems;
1468
+ this.nodeSize = Math.min(Math.max(+nodeSize, 2), 65535);
1469
+ this.ArrayType = ArrayType;
1470
+ this.IndexArrayType = numItems < 65536 ? Uint16Array : Uint32Array;
1471
+ const arrayTypeIndex = ARRAY_TYPES.indexOf(this.ArrayType);
1472
+ const coordsByteSize = numItems * 2 * this.ArrayType.BYTES_PER_ELEMENT;
1473
+ const idsByteSize = numItems * this.IndexArrayType.BYTES_PER_ELEMENT;
1474
+ const padCoords = (8 - idsByteSize % 8) % 8;
1475
+ if (arrayTypeIndex < 0) throw new Error(`Unexpected typed array class: ${ArrayType}.`);
1476
+ if (data && data instanceof ArrayBuffer) {
1477
+ this.data = data;
1478
+ this.ids = new this.IndexArrayType(this.data, HEADER_SIZE, numItems);
1479
+ this.coords = new this.ArrayType(this.data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
1480
+ this._pos = numItems * 2;
1481
+ this._finished = true;
1482
+ } else {
1483
+ this.data = new ArrayBuffer(HEADER_SIZE + coordsByteSize + idsByteSize + padCoords);
1484
+ this.ids = new this.IndexArrayType(this.data, HEADER_SIZE, numItems);
1485
+ this.coords = new this.ArrayType(this.data, HEADER_SIZE + idsByteSize + padCoords, numItems * 2);
1486
+ this._pos = 0;
1487
+ this._finished = false;
1488
+ new Uint8Array(this.data, 0, 2).set([219, (VERSION << 4) + arrayTypeIndex]);
1489
+ new Uint16Array(this.data, 2, 1)[0] = nodeSize;
1490
+ new Uint32Array(this.data, 4, 1)[0] = numItems;
1491
+ }
1492
+ }
1493
+ /**
1494
+ * Add a point to the index.
1495
+ * @param {number} x
1496
+ * @param {number} y
1497
+ * @returns {number} An incremental index associated with the added item (starting from `0`).
1498
+ */
1499
+ add(x, y) {
1500
+ const index = this._pos >> 1;
1501
+ this.ids[index] = index;
1502
+ this.coords[this._pos++] = x;
1503
+ this.coords[this._pos++] = y;
1504
+ return index;
1505
+ }
1506
+ /**
1507
+ * Perform indexing of the added points.
1508
+ */
1509
+ finish() {
1510
+ const numAdded = this._pos >> 1;
1511
+ if (numAdded !== this.numItems) throw new Error(`Added ${numAdded} items when expected ${this.numItems}.`);
1512
+ sort(this.ids, this.coords, this.nodeSize, 0, this.numItems - 1, 0);
1513
+ this._finished = true;
1514
+ return this;
1515
+ }
1516
+ /**
1517
+ * Search the index for items within a given bounding box.
1518
+ * @param {number} minX
1519
+ * @param {number} minY
1520
+ * @param {number} maxX
1521
+ * @param {number} maxY
1522
+ * @returns {number[]} An array of indices correponding to the found items.
1523
+ */
1524
+ range(minX, minY, maxX, maxY) {
1525
+ if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
1526
+ const { ids, coords, nodeSize } = this;
1527
+ const stack = [
1528
+ 0,
1529
+ ids.length - 1,
1530
+ 0
1531
+ ];
1532
+ const result = [];
1533
+ while (stack.length) {
1534
+ const axis = stack.pop() || 0;
1535
+ const right = stack.pop() || 0;
1536
+ const left = stack.pop() || 0;
1537
+ if (right - left <= nodeSize) {
1538
+ for (let i = left; i <= right; i++) {
1539
+ const x = coords[2 * i];
1540
+ const y = coords[2 * i + 1];
1541
+ if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[i]);
1542
+ }
1543
+ continue;
1544
+ }
1545
+ const m = left + right >> 1;
1546
+ const x = coords[2 * m];
1547
+ const y = coords[2 * m + 1];
1548
+ if (x >= minX && x <= maxX && y >= minY && y <= maxY) result.push(ids[m]);
1549
+ if (axis === 0 ? minX <= x : minY <= y) {
1550
+ stack.push(left);
1551
+ stack.push(m - 1);
1552
+ stack.push(1 - axis);
1553
+ }
1554
+ if (axis === 0 ? maxX >= x : maxY >= y) {
1555
+ stack.push(m + 1);
1556
+ stack.push(right);
1557
+ stack.push(1 - axis);
1558
+ }
1559
+ }
1560
+ return result;
1561
+ }
1562
+ /**
1563
+ * Search the index for items within a given radius.
1564
+ * @param {number} qx
1565
+ * @param {number} qy
1566
+ * @param {number} r Query radius.
1567
+ * @returns {number[]} An array of indices correponding to the found items.
1568
+ */
1569
+ within(qx, qy, r) {
1570
+ if (!this._finished) throw new Error("Data not yet indexed - call index.finish().");
1571
+ const { ids, coords, nodeSize } = this;
1572
+ const stack = [
1573
+ 0,
1574
+ ids.length - 1,
1575
+ 0
1576
+ ];
1577
+ const result = [];
1578
+ const r2 = r * r;
1579
+ while (stack.length) {
1580
+ const axis = stack.pop() || 0;
1581
+ const right = stack.pop() || 0;
1582
+ const left = stack.pop() || 0;
1583
+ if (right - left <= nodeSize) {
1584
+ for (let i = left; i <= right; i++) if (sqDist(coords[2 * i], coords[2 * i + 1], qx, qy) <= r2) result.push(ids[i]);
1585
+ continue;
1586
+ }
1587
+ const m = left + right >> 1;
1588
+ const x = coords[2 * m];
1589
+ const y = coords[2 * m + 1];
1590
+ if (sqDist(x, y, qx, qy) <= r2) result.push(ids[m]);
1591
+ if (axis === 0 ? qx - r <= x : qy - r <= y) {
1592
+ stack.push(left);
1593
+ stack.push(m - 1);
1594
+ stack.push(1 - axis);
1595
+ }
1596
+ if (axis === 0 ? qx + r >= x : qy + r >= y) {
1597
+ stack.push(m + 1);
1598
+ stack.push(right);
1599
+ stack.push(1 - axis);
1600
+ }
1601
+ }
1602
+ return result;
1603
+ }
1604
+ };
1605
+ /**
1606
+ * @param {Uint16Array | Uint32Array} ids
1607
+ * @param {InstanceType<TypedArrayConstructor>} coords
1608
+ * @param {number} nodeSize
1609
+ * @param {number} left
1610
+ * @param {number} right
1611
+ * @param {number} axis
1612
+ */
1613
+ function sort(ids, coords, nodeSize, left, right, axis) {
1614
+ if (right - left <= nodeSize) return;
1615
+ const m = left + right >> 1;
1616
+ select(ids, coords, m, left, right, axis);
1617
+ sort(ids, coords, nodeSize, left, m - 1, 1 - axis);
1618
+ sort(ids, coords, nodeSize, m + 1, right, 1 - axis);
1619
+ }
1620
+ /**
1621
+ * Custom Floyd-Rivest selection algorithm: sort ids and coords so that
1622
+ * [left..k-1] items are smaller than k-th item (on either x or y axis)
1623
+ * @param {Uint16Array | Uint32Array} ids
1624
+ * @param {InstanceType<TypedArrayConstructor>} coords
1625
+ * @param {number} k
1626
+ * @param {number} left
1627
+ * @param {number} right
1628
+ * @param {number} axis
1629
+ */
1630
+ function select(ids, coords, k, left, right, axis) {
1631
+ while (right > left) {
1632
+ if (right - left > 600) {
1633
+ const n = right - left + 1;
1634
+ const m = k - left + 1;
1635
+ const z = Math.log(n);
1636
+ const s = .5 * Math.exp(2 * z / 3);
1637
+ const sd = .5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
1638
+ select(ids, coords, k, Math.max(left, Math.floor(k - m * s / n + sd)), Math.min(right, Math.floor(k + (n - m) * s / n + sd)), axis);
1639
+ }
1640
+ const t = coords[2 * k + axis];
1641
+ let i = left;
1642
+ let j = right;
1643
+ swapItem(ids, coords, left, k);
1644
+ if (coords[2 * right + axis] > t) swapItem(ids, coords, left, right);
1645
+ while (i < j) {
1646
+ swapItem(ids, coords, i, j);
1647
+ i++;
1648
+ j--;
1649
+ while (coords[2 * i + axis] < t) i++;
1650
+ while (coords[2 * j + axis] > t) j--;
1651
+ }
1652
+ if (coords[2 * left + axis] === t) swapItem(ids, coords, left, j);
1653
+ else {
1654
+ j++;
1655
+ swapItem(ids, coords, j, right);
1656
+ }
1657
+ if (j <= k) left = j + 1;
1658
+ if (k <= j) right = j - 1;
1659
+ }
1660
+ }
1661
+ /**
1662
+ * @param {Uint16Array | Uint32Array} ids
1663
+ * @param {InstanceType<TypedArrayConstructor>} coords
1664
+ * @param {number} i
1665
+ * @param {number} j
1666
+ */
1667
+ function swapItem(ids, coords, i, j) {
1668
+ swap(ids, i, j);
1669
+ swap(coords, 2 * i, 2 * j);
1670
+ swap(coords, 2 * i + 1, 2 * j + 1);
1671
+ }
1672
+ /**
1673
+ * @param {InstanceType<TypedArrayConstructor>} arr
1674
+ * @param {number} i
1675
+ * @param {number} j
1676
+ */
1677
+ function swap(arr, i, j) {
1678
+ const tmp = arr[i];
1679
+ arr[i] = arr[j];
1680
+ arr[j] = tmp;
1681
+ }
1682
+ /**
1683
+ * @param {number} ax
1684
+ * @param {number} ay
1685
+ * @param {number} bx
1686
+ * @param {number} by
1687
+ */
1688
+ function sqDist(ax, ay, bx, by) {
1689
+ const dx = ax - bx;
1690
+ const dy = ay - by;
1691
+ return dx * dx + dy * dy;
1692
+ }
1693
+
1694
+ //#endregion
1695
+ //#region ../../node_modules/.pnpm/supercluster@8.0.1/node_modules/supercluster/index.js
1696
+ const defaultOptions = {
1697
+ minZoom: 0,
1698
+ maxZoom: 16,
1699
+ minPoints: 2,
1700
+ radius: 40,
1701
+ extent: 512,
1702
+ nodeSize: 64,
1703
+ log: false,
1704
+ generateId: false,
1705
+ reduce: null,
1706
+ map: (props) => props
1707
+ };
1708
+ const fround = Math.fround || ((tmp) => ((x) => {
1709
+ tmp[0] = +x;
1710
+ return tmp[0];
1711
+ }))(new Float32Array(1));
1712
+ const OFFSET_ZOOM = 2;
1713
+ const OFFSET_ID = 3;
1714
+ const OFFSET_PARENT = 4;
1715
+ const OFFSET_NUM = 5;
1716
+ const OFFSET_PROP = 6;
1717
+ var Supercluster = class {
1718
+ constructor(options) {
1719
+ this.options = Object.assign(Object.create(defaultOptions), options);
1720
+ this.trees = new Array(this.options.maxZoom + 1);
1721
+ this.stride = this.options.reduce ? 7 : 6;
1722
+ this.clusterProps = [];
1723
+ }
1724
+ load(points) {
1725
+ const { log, minZoom, maxZoom } = this.options;
1726
+ if (log) console.time("total time");
1727
+ const timerId = `prepare ${points.length} points`;
1728
+ if (log) console.time(timerId);
1729
+ this.points = points;
1730
+ const data = [];
1731
+ for (let i = 0; i < points.length; i++) {
1732
+ const p = points[i];
1733
+ if (!p.geometry) continue;
1734
+ const [lng, lat] = p.geometry.coordinates;
1735
+ const x = fround(lngX(lng));
1736
+ const y = fround(latY(lat));
1737
+ data.push(x, y, Infinity, i, -1, 1);
1738
+ if (this.options.reduce) data.push(0);
1739
+ }
1740
+ let tree = this.trees[maxZoom + 1] = this._createTree(data);
1741
+ if (log) console.timeEnd(timerId);
1742
+ for (let z = maxZoom; z >= minZoom; z--) {
1743
+ const now = +Date.now();
1744
+ tree = this.trees[z] = this._createTree(this._cluster(tree, z));
1745
+ if (log) console.log("z%d: %d clusters in %dms", z, tree.numItems, +Date.now() - now);
1746
+ }
1747
+ if (log) console.timeEnd("total time");
1748
+ return this;
1749
+ }
1750
+ getClusters(bbox, zoom) {
1751
+ let minLng = ((bbox[0] + 180) % 360 + 360) % 360 - 180;
1752
+ const minLat = Math.max(-90, Math.min(90, bbox[1]));
1753
+ let maxLng = bbox[2] === 180 ? 180 : ((bbox[2] + 180) % 360 + 360) % 360 - 180;
1754
+ const maxLat = Math.max(-90, Math.min(90, bbox[3]));
1755
+ if (bbox[2] - bbox[0] >= 360) {
1756
+ minLng = -180;
1757
+ maxLng = 180;
1758
+ } else if (minLng > maxLng) {
1759
+ const easternHem = this.getClusters([
1760
+ minLng,
1761
+ minLat,
1762
+ 180,
1763
+ maxLat
1764
+ ], zoom);
1765
+ const westernHem = this.getClusters([
1766
+ -180,
1767
+ minLat,
1768
+ maxLng,
1769
+ maxLat
1770
+ ], zoom);
1771
+ return easternHem.concat(westernHem);
1772
+ }
1773
+ const tree = this.trees[this._limitZoom(zoom)];
1774
+ const ids = tree.range(lngX(minLng), latY(maxLat), lngX(maxLng), latY(minLat));
1775
+ const data = tree.data;
1776
+ const clusters = [];
1777
+ for (const id of ids) {
1778
+ const k = this.stride * id;
1779
+ clusters.push(data[k + OFFSET_NUM] > 1 ? getClusterJSON(data, k, this.clusterProps) : this.points[data[k + OFFSET_ID]]);
1780
+ }
1781
+ return clusters;
1782
+ }
1783
+ getChildren(clusterId) {
1784
+ const originId = this._getOriginId(clusterId);
1785
+ const originZoom = this._getOriginZoom(clusterId);
1786
+ const errorMsg = "No cluster with the specified id.";
1787
+ const tree = this.trees[originZoom];
1788
+ if (!tree) throw new Error(errorMsg);
1789
+ const data = tree.data;
1790
+ if (originId * this.stride >= data.length) throw new Error(errorMsg);
1791
+ const r = this.options.radius / (this.options.extent * Math.pow(2, originZoom - 1));
1792
+ const x = data[originId * this.stride];
1793
+ const y = data[originId * this.stride + 1];
1794
+ const ids = tree.within(x, y, r);
1795
+ const children = [];
1796
+ for (const id of ids) {
1797
+ const k = id * this.stride;
1798
+ if (data[k + OFFSET_PARENT] === clusterId) children.push(data[k + OFFSET_NUM] > 1 ? getClusterJSON(data, k, this.clusterProps) : this.points[data[k + OFFSET_ID]]);
1799
+ }
1800
+ if (children.length === 0) throw new Error(errorMsg);
1801
+ return children;
1802
+ }
1803
+ getLeaves(clusterId, limit, offset) {
1804
+ limit = limit || 10;
1805
+ offset = offset || 0;
1806
+ const leaves = [];
1807
+ this._appendLeaves(leaves, clusterId, limit, offset, 0);
1808
+ return leaves;
1809
+ }
1810
+ getTile(z, x, y) {
1811
+ const tree = this.trees[this._limitZoom(z)];
1812
+ const z2 = Math.pow(2, z);
1813
+ const { extent, radius } = this.options;
1814
+ const p = radius / extent;
1815
+ const top = (y - p) / z2;
1816
+ const bottom = (y + 1 + p) / z2;
1817
+ const tile = { features: [] };
1818
+ this._addTileFeatures(tree.range((x - p) / z2, top, (x + 1 + p) / z2, bottom), tree.data, x, y, z2, tile);
1819
+ if (x === 0) this._addTileFeatures(tree.range(1 - p / z2, top, 1, bottom), tree.data, z2, y, z2, tile);
1820
+ if (x === z2 - 1) this._addTileFeatures(tree.range(0, top, p / z2, bottom), tree.data, -1, y, z2, tile);
1821
+ return tile.features.length ? tile : null;
1822
+ }
1823
+ getClusterExpansionZoom(clusterId) {
1824
+ let expansionZoom = this._getOriginZoom(clusterId) - 1;
1825
+ while (expansionZoom <= this.options.maxZoom) {
1826
+ const children = this.getChildren(clusterId);
1827
+ expansionZoom++;
1828
+ if (children.length !== 1) break;
1829
+ clusterId = children[0].properties.cluster_id;
1830
+ }
1831
+ return expansionZoom;
1832
+ }
1833
+ _appendLeaves(result, clusterId, limit, offset, skipped) {
1834
+ const children = this.getChildren(clusterId);
1835
+ for (const child of children) {
1836
+ const props = child.properties;
1837
+ if (props && props.cluster) if (skipped + props.point_count <= offset) skipped += props.point_count;
1838
+ else skipped = this._appendLeaves(result, props.cluster_id, limit, offset, skipped);
1839
+ else if (skipped < offset) skipped++;
1840
+ else result.push(child);
1841
+ if (result.length === limit) break;
1842
+ }
1843
+ return skipped;
1844
+ }
1845
+ _createTree(data) {
1846
+ const tree = new KDBush(data.length / this.stride | 0, this.options.nodeSize, Float32Array);
1847
+ for (let i = 0; i < data.length; i += this.stride) tree.add(data[i], data[i + 1]);
1848
+ tree.finish();
1849
+ tree.data = data;
1850
+ return tree;
1851
+ }
1852
+ _addTileFeatures(ids, data, x, y, z2, tile) {
1853
+ for (const i of ids) {
1854
+ const k = i * this.stride;
1855
+ const isCluster = data[k + OFFSET_NUM] > 1;
1856
+ let tags, px, py;
1857
+ if (isCluster) {
1858
+ tags = getClusterProperties(data, k, this.clusterProps);
1859
+ px = data[k];
1860
+ py = data[k + 1];
1861
+ } else {
1862
+ const p = this.points[data[k + OFFSET_ID]];
1863
+ tags = p.properties;
1864
+ const [lng, lat] = p.geometry.coordinates;
1865
+ px = lngX(lng);
1866
+ py = latY(lat);
1867
+ }
1868
+ const f = {
1869
+ type: 1,
1870
+ geometry: [[Math.round(this.options.extent * (px * z2 - x)), Math.round(this.options.extent * (py * z2 - y))]],
1871
+ tags
1872
+ };
1873
+ let id;
1874
+ if (isCluster || this.options.generateId) id = data[k + OFFSET_ID];
1875
+ else id = this.points[data[k + OFFSET_ID]].id;
1876
+ if (id !== void 0) f.id = id;
1877
+ tile.features.push(f);
1878
+ }
1879
+ }
1880
+ _limitZoom(z) {
1881
+ return Math.max(this.options.minZoom, Math.min(Math.floor(+z), this.options.maxZoom + 1));
1882
+ }
1883
+ _cluster(tree, zoom) {
1884
+ const { radius, extent, reduce, minPoints } = this.options;
1885
+ const r = radius / (extent * Math.pow(2, zoom));
1886
+ const data = tree.data;
1887
+ const nextData = [];
1888
+ const stride = this.stride;
1889
+ for (let i = 0; i < data.length; i += stride) {
1890
+ if (data[i + OFFSET_ZOOM] <= zoom) continue;
1891
+ data[i + OFFSET_ZOOM] = zoom;
1892
+ const x = data[i];
1893
+ const y = data[i + 1];
1894
+ const neighborIds = tree.within(data[i], data[i + 1], r);
1895
+ const numPointsOrigin = data[i + OFFSET_NUM];
1896
+ let numPoints = numPointsOrigin;
1897
+ for (const neighborId of neighborIds) {
1898
+ const k = neighborId * stride;
1899
+ if (data[k + OFFSET_ZOOM] > zoom) numPoints += data[k + OFFSET_NUM];
1900
+ }
1901
+ if (numPoints > numPointsOrigin && numPoints >= minPoints) {
1902
+ let wx = x * numPointsOrigin;
1903
+ let wy = y * numPointsOrigin;
1904
+ let clusterProperties;
1905
+ let clusterPropIndex = -1;
1906
+ const id = ((i / stride | 0) << 5) + (zoom + 1) + this.points.length;
1907
+ for (const neighborId of neighborIds) {
1908
+ const k = neighborId * stride;
1909
+ if (data[k + OFFSET_ZOOM] <= zoom) continue;
1910
+ data[k + OFFSET_ZOOM] = zoom;
1911
+ const numPoints2 = data[k + OFFSET_NUM];
1912
+ wx += data[k] * numPoints2;
1913
+ wy += data[k + 1] * numPoints2;
1914
+ data[k + OFFSET_PARENT] = id;
1915
+ if (reduce) {
1916
+ if (!clusterProperties) {
1917
+ clusterProperties = this._map(data, i, true);
1918
+ clusterPropIndex = this.clusterProps.length;
1919
+ this.clusterProps.push(clusterProperties);
1920
+ }
1921
+ reduce(clusterProperties, this._map(data, k));
1922
+ }
1923
+ }
1924
+ data[i + OFFSET_PARENT] = id;
1925
+ nextData.push(wx / numPoints, wy / numPoints, Infinity, id, -1, numPoints);
1926
+ if (reduce) nextData.push(clusterPropIndex);
1927
+ } else {
1928
+ for (let j = 0; j < stride; j++) nextData.push(data[i + j]);
1929
+ if (numPoints > 1) for (const neighborId of neighborIds) {
1930
+ const k = neighborId * stride;
1931
+ if (data[k + OFFSET_ZOOM] <= zoom) continue;
1932
+ data[k + OFFSET_ZOOM] = zoom;
1933
+ for (let j = 0; j < stride; j++) nextData.push(data[k + j]);
1934
+ }
1935
+ }
1936
+ }
1937
+ return nextData;
1938
+ }
1939
+ _getOriginId(clusterId) {
1940
+ return clusterId - this.points.length >> 5;
1941
+ }
1942
+ _getOriginZoom(clusterId) {
1943
+ return (clusterId - this.points.length) % 32;
1944
+ }
1945
+ _map(data, i, clone) {
1946
+ if (data[i + OFFSET_NUM] > 1) {
1947
+ const props = this.clusterProps[data[i + OFFSET_PROP]];
1948
+ return clone ? Object.assign({}, props) : props;
1949
+ }
1950
+ const original = this.points[data[i + OFFSET_ID]].properties;
1951
+ const result = this.options.map(original);
1952
+ return clone && result === original ? Object.assign({}, result) : result;
1953
+ }
1954
+ };
1955
+ function getClusterJSON(data, i, clusterProps) {
1956
+ return {
1957
+ type: "Feature",
1958
+ id: data[i + OFFSET_ID],
1959
+ properties: getClusterProperties(data, i, clusterProps),
1960
+ geometry: {
1961
+ type: "Point",
1962
+ coordinates: [xLng(data[i]), yLat(data[i + 1])]
1963
+ }
1964
+ };
1965
+ }
1966
+ function getClusterProperties(data, i, clusterProps) {
1967
+ const count = data[i + OFFSET_NUM];
1968
+ const abbrev = count >= 1e4 ? `${Math.round(count / 1e3)}k` : count >= 1e3 ? `${Math.round(count / 100) / 10}k` : count;
1969
+ const propIndex = data[i + OFFSET_PROP];
1970
+ const properties = propIndex === -1 ? {} : Object.assign({}, clusterProps[propIndex]);
1971
+ return Object.assign(properties, {
1972
+ cluster: true,
1973
+ cluster_id: data[i + OFFSET_ID],
1974
+ point_count: count,
1975
+ point_count_abbreviated: abbrev
1976
+ });
1977
+ }
1978
+ function lngX(lng) {
1979
+ return lng / 360 + .5;
1980
+ }
1981
+ function latY(lat) {
1982
+ const sin = Math.sin(lat * Math.PI / 180);
1983
+ const y = .5 - .25 * Math.log((1 + sin) / (1 - sin)) / Math.PI;
1984
+ return y < 0 ? 0 : y > 1 ? 1 : y;
1985
+ }
1986
+ function xLng(x) {
1987
+ return (x - .5) * 360;
1988
+ }
1989
+ function yLat(y) {
1990
+ const y2 = (180 - y * 360) * Math.PI / 180;
1991
+ return 360 * Math.atan(Math.exp(y2)) / Math.PI - 90;
1992
+ }
1993
+
1994
+ //#endregion
1995
+ //#region src/overlays/marker-clusterer/algorithms/supercluster.ts
1996
+ const DEFAULT_OPTIONS = {
1997
+ radius: 60,
1998
+ minZoom: 0,
1999
+ maxZoom: 16,
2000
+ extent: 512,
2001
+ nodeSize: 64
2002
+ };
2003
+ var SuperclusterAlgorithm = class {
2004
+ options;
2005
+ index;
2006
+ itemMap = /* @__PURE__ */ new Map();
2007
+ dirty = true;
2008
+ constructor(options) {
2009
+ this.options = {
2010
+ ...DEFAULT_OPTIONS,
2011
+ ...options
2012
+ };
2013
+ this.index = new Supercluster({
2014
+ radius: this.options.radius,
2015
+ minZoom: this.options.minZoom,
2016
+ maxZoom: this.options.maxZoom,
2017
+ extent: this.options.extent,
2018
+ nodeSize: this.options.nodeSize
2019
+ });
2020
+ }
2021
+ cluster(items, ctx) {
2022
+ if (this.dirty || this.itemMap.size !== items.length) this.load(items);
2023
+ const { south, west, north, east } = ctx.bounds;
2024
+ const rawClusters = this.index.getClusters([
2025
+ west,
2026
+ south,
2027
+ east,
2028
+ north
2029
+ ], Math.floor(ctx.zoom));
2030
+ const clusters = [];
2031
+ const singlePoints = [];
2032
+ for (const feature of rawClusters) {
2033
+ const [lng, lat] = feature.geometry.coordinates;
2034
+ const props = feature.properties;
2035
+ if ("cluster" in props && props.cluster === true) {
2036
+ const clusterProps = props;
2037
+ const clusterId = feature.id;
2038
+ const leaves = this.getLeaves(clusterId);
2039
+ let minLat = Infinity;
2040
+ let maxLat = -Infinity;
2041
+ let minLng = Infinity;
2042
+ let maxLng = -Infinity;
2043
+ for (const leaf of leaves) {
2044
+ minLat = Math.min(minLat, leaf.position.lat);
2045
+ maxLat = Math.max(maxLat, leaf.position.lat);
2046
+ minLng = Math.min(minLng, leaf.position.lng);
2047
+ maxLng = Math.max(maxLng, leaf.position.lng);
2048
+ }
2049
+ clusters.push({
2050
+ id: `sc-${String(clusterId)}`,
2051
+ position: {
2052
+ lat,
2053
+ lng
2054
+ },
2055
+ count: clusterProps.point_count,
2056
+ bounds: {
2057
+ south: minLat,
2058
+ north: maxLat,
2059
+ west: minLng,
2060
+ east: maxLng
2061
+ },
2062
+ items: leaves
2063
+ });
2064
+ } else {
2065
+ const pointProps = props;
2066
+ const item = this.itemMap.get(pointProps.itemId);
2067
+ if (item) singlePoints.push(item);
2068
+ }
2069
+ }
2070
+ return {
2071
+ clusters,
2072
+ points: singlePoints
2073
+ };
2074
+ }
2075
+ setOptions(options) {
2076
+ if (typeof options === "object" && options !== null) {
2077
+ const next = {
2078
+ ...this.options,
2079
+ ...options
2080
+ };
2081
+ this.options = next;
2082
+ this.index = new Supercluster({
2083
+ radius: next.radius,
2084
+ minZoom: next.minZoom,
2085
+ maxZoom: next.maxZoom,
2086
+ extent: next.extent,
2087
+ nodeSize: next.nodeSize
2088
+ });
2089
+ this.dirty = true;
2090
+ }
2091
+ }
2092
+ destroy() {
2093
+ this.itemMap.clear();
2094
+ }
2095
+ load(items) {
2096
+ this.itemMap.clear();
2097
+ const features = [];
2098
+ for (const item of items) {
2099
+ this.itemMap.set(item.id, item);
2100
+ features.push({
2101
+ type: "Feature",
2102
+ geometry: {
2103
+ type: "Point",
2104
+ coordinates: [item.position.lng, item.position.lat]
2105
+ },
2106
+ properties: {
2107
+ itemId: item.id,
2108
+ data: item.data,
2109
+ markerOptions: item.markerOptions
2110
+ }
2111
+ });
2112
+ }
2113
+ this.index.load(features);
2114
+ this.dirty = false;
2115
+ }
2116
+ getLeaves(clusterId) {
2117
+ if (clusterId === void 0) return [];
2118
+ const numericId = typeof clusterId === "string" ? parseInt(clusterId, 10) : clusterId;
2119
+ if (typeof numericId !== "number" || isNaN(numericId)) return [];
2120
+ const leaves = this.index.getLeaves(numericId, Infinity);
2121
+ const result = [];
2122
+ for (const leaf of leaves) {
2123
+ const props = leaf.properties;
2124
+ const item = this.itemMap.get(props.itemId);
2125
+ if (item) result.push(item);
2126
+ }
2127
+ return result;
2128
+ }
2129
+ };
2130
+
2131
+ //#endregion
2132
+ //#region src/overlays/marker-clusterer/algorithms/createAlgorithm.ts
2133
+ function isBuiltInConfig(value) {
2134
+ return "type" in value && typeof value.type === "string";
2135
+ }
2136
+ function omitUndefined(obj) {
2137
+ const result = {};
2138
+ for (const key of Object.keys(obj)) if (obj[key] !== void 0) result[key] = obj[key];
2139
+ return result;
2140
+ }
2141
+ function createAlgorithm(config) {
2142
+ switch (config.type) {
2143
+ case "grid": return new GridAlgorithm(omitUndefined({
2144
+ gridSize: config.gridSize,
2145
+ minClusterSize: config.minClusterSize,
2146
+ maxZoom: config.maxZoom
2147
+ }));
2148
+ case "radius": return new RadiusAlgorithm(omitUndefined({
2149
+ radius: config.radius,
2150
+ minClusterSize: config.minClusterSize,
2151
+ maxZoom: config.maxZoom
2152
+ }));
2153
+ case "supercluster": return new SuperclusterAlgorithm(omitUndefined({
2154
+ radius: config.radius,
2155
+ minZoom: config.minZoom,
2156
+ maxZoom: config.maxZoom,
2157
+ extent: config.extent,
2158
+ nodeSize: config.nodeSize
2159
+ }));
2160
+ }
2161
+ }
2162
+
2163
+ //#endregion
2164
+ //#region src/overlays/marker-clusterer/MarkerClusterer.tsx
2165
+ function DefaultClusterIcon({ count }) {
2166
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2167
+ style: {
2168
+ display: "flex",
2169
+ alignItems: "center",
2170
+ justifyContent: "center",
2171
+ width: 40,
2172
+ height: 40,
2173
+ borderRadius: "50%",
2174
+ backgroundColor: "rgba(0, 100, 255, 0.7)",
2175
+ color: "#fff",
2176
+ fontWeight: "bold",
2177
+ fontSize: 14,
2178
+ border: "2px solid rgba(0, 100, 255, 0.9)"
2179
+ },
2180
+ children: count
2181
+ });
2182
+ }
2183
+ function getMapBounds(map) {
2184
+ const bounds = map.getBounds();
2185
+ if (bounds instanceof naver.maps.LatLngBounds) {
2186
+ const sw = bounds.getSW();
2187
+ const ne = bounds.getNE();
2188
+ return {
2189
+ south: sw.lat(),
2190
+ west: sw.lng(),
2191
+ north: ne.lat(),
2192
+ east: ne.lng()
2193
+ };
2194
+ }
2195
+ const b = bounds;
2196
+ if ("south" in b && "north" in b && "west" in b && "east" in b) return {
2197
+ south: b.south,
2198
+ north: b.north,
2199
+ west: b.west,
2200
+ east: b.east
2201
+ };
2202
+ return {
2203
+ south: -90,
2204
+ north: 90,
2205
+ west: -180,
2206
+ east: 180
2207
+ };
2208
+ }
2209
+ function padBounds(bounds, padding) {
2210
+ const latPad = (bounds.north - bounds.south) * padding * .01;
2211
+ const lngPad = (bounds.east - bounds.west) * padding * .01;
2212
+ return {
2213
+ south: bounds.south - latPad,
2214
+ north: bounds.north + latPad,
2215
+ west: bounds.west - lngPad,
2216
+ east: bounds.east + lngPad
2217
+ };
2218
+ }
2219
+ /**
2220
+ * 다수의 `<Marker>`를 자동으로 클러스터링해주는 컴포넌트.
2221
+ *
2222
+ * ## 동작 방식
2223
+ *
2224
+ * 1. `<Marker>`는 `<MarkerClusterer>` 내부에서 직접 마커를 생성하지 않고
2225
+ * 내부 registry에 위치·데이터를 등록합니다.
2226
+ * 2. `MarkerClusterer`는 지도 이벤트(`idle`, `move`, `zoom`) 발생 시
2227
+ * 현재 줌·뷰포트 기준으로 클러스터를 재계산합니다.
2228
+ * 3. 클러스터 마커는 `createRoot`를 이용해 React 컴포넌트를 HTML 아이콘으로 렌더링합니다.
2229
+ *
2230
+ * ## 기본 사용법
2231
+ *
2232
+ * ```tsx
2233
+ * <NaverMap ...>
2234
+ * <MarkerClusterer
2235
+ * algorithm={{ type: "supercluster", radius: 60 }}
2236
+ * clusterIcon={({ count }) => <ClusterBadge count={count} />}
2237
+ * onClusterClick={({ cluster, helpers }) =>
2238
+ * helpers.zoomToCluster(cluster, { maxZoom: 16 })
2239
+ * }
2240
+ * >
2241
+ * {points.map(p => (
2242
+ * <Marker
2243
+ * key={p.id}
2244
+ * clustererItemId={p.id}
2245
+ * position={p.position}
2246
+ * item={p}
2247
+ * />
2248
+ * ))}
2249
+ * </MarkerClusterer>
2250
+ * </NaverMap>
2251
+ * ```
2252
+ *
2253
+ * ## 주의 사항
2254
+ *
2255
+ * - `<Marker>`에 반드시 `clustererItemId` prop을 지정해야 합니다.
2256
+ * - `<MarkerClusterer>`는 반드시 `<NaverMap>` 내부에 위치해야 합니다.
2257
+ * - `enabled={false}`로 설정하면 클러스터링이 해제되고 각 `<Marker>`가 개별 마커로 렌더링됩니다.
2258
+ *
2259
+ * @typeParam TData - children `<Marker>`의 `item` prop 타입
2260
+ */
2261
+ function MarkerClusterer(props) {
2262
+ const { algorithm: algorithmProp, clusterIcon, onClusterClick, behavior, clusterData, enabled = true, children } = props;
2263
+ const { map, sdkStatus } = useNaverMap();
2264
+ const registryRef = (0, react.useRef)(/* @__PURE__ */ new Map());
2265
+ const [registryVersion, setRegistryVersion] = (0, react.useState)(0);
2266
+ const registry = (0, react.useMemo)(() => ({
2267
+ enabled,
2268
+ register(item) {
2269
+ registryRef.current.set(item.id, item);
2270
+ setRegistryVersion((v) => v + 1);
2271
+ },
2272
+ unregister(id) {
2273
+ registryRef.current.delete(id);
2274
+ setRegistryVersion((v) => v + 1);
2275
+ }
2276
+ }), [enabled]);
2277
+ const algorithmRef = (0, react.useRef)(null);
2278
+ const algorithm = (0, react.useMemo)(() => {
2279
+ if (algorithmProp && !isBuiltInConfig(algorithmProp)) return algorithmProp;
2280
+ const config = algorithmProp ?? {
2281
+ type: "supercluster",
2282
+ radius: 60
2283
+ };
2284
+ if (!isBuiltInConfig(config)) return config;
2285
+ return createAlgorithm(config);
2286
+ }, [algorithmProp]);
2287
+ (0, react.useEffect)(() => {
2288
+ const prev = algorithmRef.current;
2289
+ algorithmRef.current = algorithm;
2290
+ return () => {
2291
+ if (prev && prev !== algorithm) prev.destroy?.();
2292
+ };
2293
+ }, [algorithm]);
2294
+ const pointMarkersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
2295
+ const clusterMarkersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
2296
+ const clusterIconRootsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
2297
+ const clusterIconContainersRef = (0, react.useRef)(/* @__PURE__ */ new Map());
2298
+ const helpersRef = (0, react.useRef)({
2299
+ zoomToCluster: () => {},
2300
+ fitBounds: () => {}
2301
+ });
2302
+ (0, react.useEffect)(() => {
2303
+ if (!map) return;
2304
+ helpersRef.current = {
2305
+ zoomToCluster(cluster, options) {
2306
+ if (cluster.bounds) {
2307
+ const bounds = options?.padding ? padBounds(cluster.bounds, options.padding) : cluster.bounds;
2308
+ const latLngBounds = new naver.maps.LatLngBounds(new naver.maps.LatLng(bounds.south, bounds.west), new naver.maps.LatLng(bounds.north, bounds.east));
2309
+ map.fitBounds(latLngBounds);
2310
+ if (options?.maxZoom !== void 0) {
2311
+ if (map.getZoom() > options.maxZoom) map.setZoom(options.maxZoom);
2312
+ }
2313
+ } else {
2314
+ const nextZoom = Math.min(map.getZoom() + 1, options?.maxZoom ?? 21);
2315
+ map.setCenter(new naver.maps.LatLng(cluster.position.lat, cluster.position.lng));
2316
+ map.setZoom(nextZoom);
2317
+ }
2318
+ },
2319
+ fitBounds(bounds, options) {
2320
+ const paddedBounds = options?.padding ? padBounds(bounds, options.padding) : bounds;
2321
+ const latLngBounds = new naver.maps.LatLngBounds(new naver.maps.LatLng(paddedBounds.south, paddedBounds.west), new naver.maps.LatLng(paddedBounds.north, paddedBounds.east));
2322
+ map.fitBounds(latLngBounds);
2323
+ }
2324
+ };
2325
+ }, [map]);
2326
+ const onClusterClickRef = (0, react.useRef)(onClusterClick);
2327
+ (0, react.useEffect)(() => {
2328
+ onClusterClickRef.current = onClusterClick;
2329
+ }, [onClusterClick]);
2330
+ const clusterIconRef = (0, react.useRef)(clusterIcon);
2331
+ (0, react.useEffect)(() => {
2332
+ clusterIconRef.current = clusterIcon;
2333
+ }, [clusterIcon]);
2334
+ const recompute = (0, react.useCallback)(() => {
2335
+ if (!map || sdkStatus !== "ready" || !enabled) return;
2336
+ const zoom = map.getZoom();
2337
+ const bounds = getMapBounds(map);
2338
+ const items = Array.from(registryRef.current.values());
2339
+ const { clusters, points } = algorithm.cluster(items, {
2340
+ zoom,
2341
+ bounds
2342
+ });
2343
+ const maxItems = clusterData?.maxItemsInCluster;
2344
+ const includeItems = clusterData?.includeItems ?? true;
2345
+ const processedClusters = clusters.map((c) => ({
2346
+ ...c,
2347
+ items: includeItems ? maxItems !== void 0 ? c.items?.slice(0, maxItems) : c.items : void 0
2348
+ }));
2349
+ const nextPointIds = new Set(points.map((p) => p.id));
2350
+ const prevPointMarkers = pointMarkersRef.current;
2351
+ for (const [id, marker] of prevPointMarkers) if (!nextPointIds.has(id)) {
2352
+ marker.setMap(null);
2353
+ prevPointMarkers.delete(id);
2354
+ }
2355
+ for (const point of points) {
2356
+ const existing = prevPointMarkers.get(point.id);
2357
+ if (existing) {
2358
+ const pos = new naver.maps.LatLng(point.position.lat, point.position.lng);
2359
+ existing.setPosition(pos);
2360
+ if (point.markerOptions) existing.setOptions({ ...point.markerOptions });
2361
+ } else {
2362
+ const opts = {
2363
+ position: new naver.maps.LatLng(point.position.lat, point.position.lng),
2364
+ map
2365
+ };
2366
+ if (point.markerOptions) Object.assign(opts, point.markerOptions);
2367
+ const marker = new naver.maps.Marker(opts);
2368
+ prevPointMarkers.set(point.id, marker);
2369
+ }
2370
+ }
2371
+ const nextClusterIds = new Set(processedClusters.map((c) => c.id));
2372
+ const prevClusterMarkers = clusterMarkersRef.current;
2373
+ const prevRoots = clusterIconRootsRef.current;
2374
+ const prevContainers = clusterIconContainersRef.current;
2375
+ for (const [id, marker] of prevClusterMarkers) if (!nextClusterIds.has(id)) {
2376
+ marker.setMap(null);
2377
+ prevClusterMarkers.delete(id);
2378
+ const root = prevRoots.get(id);
2379
+ if (root) {
2380
+ root.unmount();
2381
+ prevRoots.delete(id);
2382
+ }
2383
+ prevContainers.delete(id);
2384
+ }
2385
+ for (const cluster of processedClusters) {
2386
+ const existingMarker = prevClusterMarkers.get(cluster.id);
2387
+ const renderer = clusterIconRef.current;
2388
+ const iconNode = renderer ? renderer({
2389
+ cluster,
2390
+ count: cluster.count
2391
+ }) : DefaultClusterIcon({ count: cluster.count });
2392
+ if (existingMarker) {
2393
+ existingMarker.setPosition(new naver.maps.LatLng(cluster.position.lat, cluster.position.lng));
2394
+ const root = prevRoots.get(cluster.id);
2395
+ if (root) root.render(iconNode);
2396
+ } else {
2397
+ const container = document.createElement("div");
2398
+ container.style.cursor = "pointer";
2399
+ const root = (0, react_dom_client.createRoot)(container);
2400
+ root.render(iconNode);
2401
+ prevContainers.set(cluster.id, container);
2402
+ prevRoots.set(cluster.id, root);
2403
+ const marker = new naver.maps.Marker({
2404
+ position: new naver.maps.LatLng(cluster.position.lat, cluster.position.lng),
2405
+ map,
2406
+ icon: { content: container },
2407
+ clickable: true
2408
+ });
2409
+ naver.maps.Event.addListener(marker, "click", () => {
2410
+ onClusterClickRef.current?.({
2411
+ cluster,
2412
+ helpers: helpersRef.current
2413
+ });
2414
+ });
2415
+ prevClusterMarkers.set(cluster.id, marker);
2416
+ }
2417
+ }
2418
+ }, [
2419
+ map,
2420
+ sdkStatus,
2421
+ enabled,
2422
+ algorithm,
2423
+ clusterData?.includeItems,
2424
+ clusterData?.maxItemsInCluster
2425
+ ]);
2426
+ (0, react.useEffect)(() => {
2427
+ recompute();
2428
+ }, [recompute, registryVersion]);
2429
+ (0, react.useEffect)(() => {
2430
+ if (!map || sdkStatus !== "ready" || !enabled) return;
2431
+ const recomputeOn = behavior?.recomputeOn ?? "idle";
2432
+ const debounceMs = behavior?.debounceMs ?? 200;
2433
+ let timerId = null;
2434
+ const listeners = [];
2435
+ const debouncedRecompute = () => {
2436
+ if (timerId !== null) clearTimeout(timerId);
2437
+ timerId = setTimeout(() => {
2438
+ recompute();
2439
+ timerId = null;
2440
+ }, debounceMs);
2441
+ };
2442
+ if (recomputeOn === "idle") listeners.push(naver.maps.Event.addListener(map, "idle", debouncedRecompute));
2443
+ else if (recomputeOn === "move") listeners.push(naver.maps.Event.addListener(map, "bounds_changed", debouncedRecompute));
2444
+ else if (recomputeOn === "zoom") listeners.push(naver.maps.Event.addListener(map, "zoom_changed", debouncedRecompute));
2445
+ recompute();
2446
+ return () => {
2447
+ if (timerId !== null) clearTimeout(timerId);
2448
+ if (listeners.length > 0) naver.maps.Event.removeListener(listeners);
2449
+ };
2450
+ }, [
2451
+ map,
2452
+ sdkStatus,
2453
+ enabled,
2454
+ behavior?.recomputeOn,
2455
+ behavior?.debounceMs,
2456
+ recompute
2457
+ ]);
2458
+ (0, react.useEffect)(() => {
2459
+ return () => {
2460
+ for (const marker of pointMarkersRef.current.values()) marker.setMap(null);
2461
+ pointMarkersRef.current.clear();
2462
+ for (const marker of clusterMarkersRef.current.values()) marker.setMap(null);
2463
+ clusterMarkersRef.current.clear();
2464
+ for (const root of clusterIconRootsRef.current.values()) root.unmount();
2465
+ clusterIconRootsRef.current.clear();
2466
+ clusterIconContainersRef.current.clear();
2467
+ };
2468
+ }, []);
2469
+ (0, react.useEffect)(() => {
2470
+ if (enabled) return;
2471
+ for (const marker of pointMarkersRef.current.values()) marker.setMap(null);
2472
+ pointMarkersRef.current.clear();
2473
+ for (const marker of clusterMarkersRef.current.values()) marker.setMap(null);
2474
+ clusterMarkersRef.current.clear();
2475
+ for (const root of clusterIconRootsRef.current.values()) root.unmount();
2476
+ clusterIconRootsRef.current.clear();
2477
+ clusterIconContainersRef.current.clear();
2478
+ }, [enabled]);
2479
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClustererContext.Provider, {
2480
+ value: registry,
2481
+ children
2482
+ });
2483
+ }
2484
+ MarkerClusterer.displayName = "MarkerClusterer";
2485
+
1152
2486
  //#endregion
1153
2487
  //#region src/overlays/infowindow/InfoWindow.tsx
1154
2488
  function toInfoWindowOptions(props) {
@@ -2632,10 +3966,12 @@ const version = "0.0.1";
2632
3966
 
2633
3967
  //#endregion
2634
3968
  exports.Circle = Circle;
3969
+ exports.ClustererContext = ClustererContext;
2635
3970
  exports.Ellipse = Ellipse;
2636
3971
  exports.GroundOverlay = GroundOverlay;
2637
3972
  exports.InfoWindow = InfoWindow;
2638
3973
  exports.Marker = Marker;
3974
+ exports.MarkerClusterer = MarkerClusterer;
2639
3975
  exports.NaverMap = NaverMap;
2640
3976
  exports.NaverMapContext = NaverMapContext;
2641
3977
  exports.NaverMapProvider = NaverMapProvider;