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