mahal_map 2.2.3 → 2.4.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/README.md +93 -20
- package/dist/index.d.mts +135 -31
- package/dist/index.d.ts +135 -31
- package/dist/index.js +1 -1
- package/dist/index.mjs +8 -8
- package/dist/mahal_map.sdk.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install mahal_map maplibre-gl @grammaps/maps3d-web
|
|
|
13
13
|
Обе зависимости — `peerDependencies`, в бандл `mahal_map` они не входят. Библиотека их не импортирует: MapLibre и `Maps3D` приходят снаружи, аргументами `create()` либо через `window`.
|
|
14
14
|
|
|
15
15
|
- `maplibre-gl` (`^6.10.0`) — обязателен всегда.
|
|
16
|
-
- `@grammaps/maps3d-web` (>=0.
|
|
16
|
+
- `@grammaps/maps3d-web` (>=0.11.0 — в ней отписки у подписок, `selectedBuildingKind()`, этажи в состоянии слоя и починенный клик по зданию сверху; на сборках постарше карта работает через запасные пути, но отписка и `selectedBuildingKind` не гарантированы) — даёт стили, тайлы, объём, пробки, рельеф, планы этажей, клик по объектам. Помечен `optional`: без него карта поднимется на запасном векторном стиле, но 3D и слоёв платформы на ней не будет.
|
|
17
17
|
|
|
18
18
|
MapLibre можно не класть в свою сборку вовсе — платформа отдаёт согласованную версию вместе с веб-воркером:
|
|
19
19
|
|
|
@@ -51,6 +51,8 @@ const map = MahalMap.create({ container: "map" }, maplibregl, Maps3D);
|
|
|
51
51
|
|
|
52
52
|
```ts
|
|
53
53
|
// vite.config.ts — чтобы адрес воркера совпал с файлом во время разработки
|
|
54
|
+
import { defineConfig } from "vite";
|
|
55
|
+
|
|
54
56
|
export default defineConfig({ optimizeDeps: { exclude: ["maplibre-gl"] } });
|
|
55
57
|
```
|
|
56
58
|
|
|
@@ -427,6 +429,15 @@ map.setIndoorLevel(1); // нумерация OSM: 0 — первый назем
|
|
|
427
429
|
map.refreshIndoor(); // перечитать после правки картографом
|
|
428
430
|
```
|
|
429
431
|
|
|
432
|
+
С Maps3D 0.11.0 список этажей приходит в состоянии слоя — опрашивать `getIndoorLevels()` на `idle`/`moveend` не нужно:
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
map.onLayers((state) => {
|
|
436
|
+
if (state.id !== "indoor") return;
|
|
437
|
+
renderLevelPicker(state.params.levels ?? [], state.params.level);
|
|
438
|
+
});
|
|
439
|
+
```
|
|
440
|
+
|
|
430
441
|
После поиска показать найденный объект вместе с его этажом:
|
|
431
442
|
|
|
432
443
|
```ts
|
|
@@ -492,13 +503,13 @@ await maps3d.ready; // enhance() ждёт нативную карту MapLibre,
|
|
|
492
503
|
```ts
|
|
493
504
|
map.setSelectionStyle({ color: "#e23b2f", opacity: 0.55, durationMs: 300 });
|
|
494
505
|
|
|
495
|
-
map.onBuildingClick((building) => {
|
|
506
|
+
const stopBuildingClick = map.onBuildingClick((building) => {
|
|
496
507
|
if (!building) return;
|
|
497
|
-
// SDK уже подсветил
|
|
498
|
-
console.log(building.props?.osm_id, building.height);
|
|
508
|
+
// SDK уже подсветил его. index >= 0 — объёмный дом сцены, -1 — дом из слоя стиля
|
|
509
|
+
console.log(building.props?.osm_id, building.height, building.index);
|
|
499
510
|
});
|
|
500
511
|
|
|
501
|
-
map.onRoadClick((road) => {
|
|
512
|
+
const stopRoadClick = map.onRoadClick((road) => {
|
|
502
513
|
if (!road) return;
|
|
503
514
|
console.log(road.nameRu ?? road.name, road.class);
|
|
504
515
|
});
|
|
@@ -506,8 +517,13 @@ map.onRoadClick((road) => {
|
|
|
506
517
|
// Выделить здание по osm_id — например, после поиска
|
|
507
518
|
map.selectBuilding(123456789); // false, если здания нет в загруженных данных
|
|
508
519
|
map.selectedBuilding(); // текущий id или null
|
|
520
|
+
map.selectedBuildingKind(); // 'scene' — объём, 'flat' — слой стиля, null — выделения нет
|
|
509
521
|
map.clearSelection();
|
|
510
522
|
|
|
523
|
+
// При размонтировании
|
|
524
|
+
stopBuildingClick();
|
|
525
|
+
stopRoadClick();
|
|
526
|
+
|
|
511
527
|
// Дорога под точкой холста, допуск по умолчанию 12 px
|
|
512
528
|
map.roadAt({ x: 320, y: 240 });
|
|
513
529
|
map.roadsNamed; // есть ли в текущем стиле названия дорог
|
|
@@ -524,6 +540,22 @@ map.setSelectionStyle({ color: "#e23b2f" });
|
|
|
524
540
|
map.toggle3DBuildings(true); // подписка и стиль выделения уже применены к новому слою
|
|
525
541
|
```
|
|
526
542
|
|
|
543
|
+
#### Отписка
|
|
544
|
+
|
|
545
|
+
`onBuildingClick`, `onBuildingHover`, `onRoadClick`, `onParkingHover` и `onParkingClick` возвращают функцию отписки и принимают `null`. Повторный вызов заменяет обработчик. Отписка снимает **только свою** подписку: если обработчик уже заменён следующим вызовом, она ничего не делает — отложенный вызов прежнего владельца (например, cleanup размонтированного компонента) не погасит обработчик нового. Работает и для подписки, сделанной до подъёма слоя: снятая до подъёма, она на слой не попадёт.
|
|
546
|
+
|
|
547
|
+
```ts
|
|
548
|
+
useEffect(() => map.onBuildingClick(openCard), [map]); // отписка уходит в cleanup
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
#### Порог зума объёма
|
|
552
|
+
|
|
553
|
+
`getBuildingsMinZoom()` — зум, с которого поднимается объём: `maps3d.minZoom`, который `mahal_map` передал в Maps3D, либо умолчание Maps3D (`16`). В отличие от `rendered` в состоянии слоя, ответ не зависит от текущего зума.
|
|
554
|
+
|
|
555
|
+
```ts
|
|
556
|
+
const willBe3D = targetZoom >= map.getBuildingsMinZoom();
|
|
557
|
+
```
|
|
558
|
+
|
|
527
559
|
### Наведение на здание
|
|
528
560
|
|
|
529
561
|
Требует `@grammaps/maps3d-web` **0.8.0** и старше. Подсветку и курсор рисует сам SDK — приложение задаёт облик и получает уведомления.
|
|
@@ -539,11 +571,11 @@ map.onBuildingHover((building) => {
|
|
|
539
571
|
|
|
540
572
|
Колбэк приходит **только в момент смены** дома под курсором, а не на каждое движение мыши.
|
|
541
573
|
|
|
542
|
-
Слежение включается самой подпиской: пока `onBuildingHover` не вызван, SDK курсор не трогает и сцену не разбирает. `onBuildingHover(null)`
|
|
574
|
+
Слежение включается самой подпиской: пока `onBuildingHover` не вызван, SDK курсор не трогает и сцену не разбирает. `onBuildingHover(null)` или возвращённая отписка снимает подписку и гасит подсветку.
|
|
543
575
|
|
|
544
576
|
| Метод | Описание |
|
|
545
577
|
| --- | --- |
|
|
546
|
-
| `onBuildingHover(cb \| null)` | Подписка на наведение; `null` отписывает и гасит подсветку. |
|
|
578
|
+
| `onBuildingHover(cb \| null)` | Подписка на наведение; возвращает отписку, `null` отписывает и гасит подсветку. |
|
|
547
579
|
| `setHoverStyle(s)` | Облик: `color`, `opacity`, `glow`, `durationMs`, `easing`, `cursor`. `cursor: null` — не трогать курсор. |
|
|
548
580
|
| `setHoverEnabled(on)` | Вкл/выкл наведение, не трогая подписку. |
|
|
549
581
|
| `hoverEnabled` | Включено ли наведение сейчас. |
|
|
@@ -571,11 +603,13 @@ row.addEventListener("mouseleave", () => map.hoverBuilding(null));
|
|
|
571
603
|
map.setHoverEnabled(false); // подписка сохраняется
|
|
572
604
|
```
|
|
573
605
|
|
|
574
|
-
Цену ограничивает сам SDK: разбор не чаще раза в кадр, пропускается во время движения
|
|
606
|
+
Цену ограничивает сам SDK: разбор не чаще раза в кадр, пропускается во время движения камеры — и от жестов, и от `jumpTo`/`easeTo` из приложения (с 0.11.0) — и не трогает сцену, пока курсор на том же доме. С 0.11.0 наведение читает пиксель из GPU асинхронно и главный поток не держит; гасить наведение на время движения карты снаружи больше не нужно.
|
|
575
607
|
|
|
576
608
|
### Парковки: наведение, клик, справка
|
|
577
609
|
|
|
578
610
|
> Нужен `@grammaps/maps3d-web` **0.10.0+**. На более старой сборке методы ничего не делают и возвращают пустые значения (`false`, `null`).
|
|
611
|
+
>
|
|
612
|
+
> Подписки `onParkingHover`/`onParkingClick` можно делать до подъёма слоя — они запоминаются и переигрываются, как у зданий. Обе возвращают отписку.
|
|
579
613
|
|
|
580
614
|
Устроено как у зданий: приложение задаёт облик и получает уведомления, подсветку и курсор рисует SDK.
|
|
581
615
|
|
|
@@ -604,13 +638,13 @@ map.getMap().on("contextmenu", (e) => console.log(map.parkingAt(e.point, 4)));
|
|
|
604
638
|
|
|
605
639
|
| Метод | Описание |
|
|
606
640
|
| ---------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
607
|
-
| `onParkingHover(callback \| null)` | Участок под курсором либо `null`. Зовётся **только при смене участка**. `null` — отписаться и погасить. |
|
|
641
|
+
| `onParkingHover(callback \| null)` | Участок под курсором либо `null`. Зовётся **только при смене участка**. Возвращает отписку; `null` — отписаться и погасить. |
|
|
608
642
|
| `setParkingHoverStyle(style)` | Облик подсветки: `color`, `opacity`, `outlineColor`, `outlineWidth`, `cursor`. |
|
|
609
643
|
| `setParkingHoverEnabled(enabled)` | Выключить/включить наведение, не теряя подписку. |
|
|
610
644
|
| `parkingHoverEnabled` | Геттер: включено ли наведение. |
|
|
611
645
|
| `hoverParking(id \| null)` | Подсветить участок программно по `osm_id`; `null` — погасить. `false` — участка нет в данных. |
|
|
612
646
|
| `hoveredParking()` | `osm_id` подсвеченного участка или `null`. |
|
|
613
|
-
| `onParkingClick(callback \| null)` | Клик по участку: справка либо `null`. `null` вместо колбэка — отписаться.
|
|
647
|
+
| `onParkingClick(callback \| null)` | Клик по участку: справка либо `null`. Возвращает отписку; `null` вместо колбэка — отписаться. |
|
|
614
648
|
| `parkingAt(point, tolPx?)` | Справка по точке холста. Допуск по умолчанию `0` — строго внутрь участка. |
|
|
615
649
|
|
|
616
650
|
Облик подсветки (`IMaps3DParkingHoverStyle`):
|
|
@@ -664,6 +698,8 @@ MahalMap.toggle3DBuildings(map, false);
|
|
|
664
698
|
|
|
665
699
|
Для переключения пользуйтесь именно `toggle3DBuildings()`: он поднимет слой, если его ещё не было (`enable3D: false` при создании), а на сборках Maps3D без реестра слоёв откатится на `setBuildingsEnabled()`.
|
|
666
700
|
|
|
701
|
+
Поднять слой можно, только если `Maps3D` был при `create()` — тогда карта стоит на стиле платформы. Карта, созданная без него, работает на запасном стиле без источников платформы и без ключа в запросах: `toggle3DBuildings(true)` на ней ничего не делает и пишет предупреждение. Подключить `Maps3D` позже — значит пересоздать карту.
|
|
702
|
+
|
|
667
703
|
```ts
|
|
668
704
|
map.toggle3DBuildings(false); // совместимо со всеми сборками
|
|
669
705
|
```
|
|
@@ -841,7 +877,7 @@ onBeforeUnmount(() => {
|
|
|
841
877
|
| `antialias: true` не забыть | ставится сам при включённом 3D |
|
|
842
878
|
| `Maps3D.enhance(map, opts); await maps3d.ready` | `enable3D: true` + `maps3d: {...}`, `await map.whenMaps3DReady()` |
|
|
843
879
|
| `map.setStyle(Maps3D.styleUrl("dark", base))` | `map.setStyle("dark")` — внутри выбранного семейства |
|
|
844
|
-
| `maps3d.refreshLayers()` после смены стиля | вызывается сам на `
|
|
880
|
+
| `maps3d.refreshLayers()` после смены стиля | вызывается сам на `styledata`, когда новый стиль загружен |
|
|
845
881
|
| `maps3d.destroy(); map.remove()` | `map.destroy()` |
|
|
846
882
|
| ключ руками в каждый вызов | один `keyUtils.saveKey()` на всё |
|
|
847
883
|
|
|
@@ -945,7 +981,7 @@ const map = MahalMap.create(
|
|
|
945
981
|
);
|
|
946
982
|
```
|
|
947
983
|
|
|
948
|
-
В NPM-версии второй аргумент `maplibreObject` рекомендуется передавать явно.
|
|
984
|
+
В NPM-версии второй аргумент `maplibreObject` рекомендуется передавать явно. Без него SDK ищет `window.maplibregl` — он есть, только если MapLibre подключён скриптом (`<script src=".../maplibre-gl.js">`). ESM-импорт глобальную переменную не создаёт: тогда передайте библиотеку вторым аргументом (`MahalMap.create({ container: "map" }, maplibregl)`) либо назначьте `window.maplibregl = maplibregl` до вызова `create()`.
|
|
949
985
|
|
|
950
986
|
### `MahalMap.createAsync(options, maplibreObject?, maps3dCtor?)`
|
|
951
987
|
|
|
@@ -994,7 +1030,7 @@ try {
|
|
|
994
1030
|
</div>
|
|
995
1031
|
<template #fallback>
|
|
996
1032
|
<div class="flex h-[360px] items-center justify-center">
|
|
997
|
-
|
|
1033
|
+
Загрузка карты…
|
|
998
1034
|
</div>
|
|
999
1035
|
</template>
|
|
1000
1036
|
</ClientOnly>
|
|
@@ -1019,10 +1055,17 @@ onMounted(async () => {
|
|
|
1019
1055
|
|
|
1020
1056
|
keyUtils.saveKey(import.meta.env.VITE_MAHAL_API_KEY_TILE);
|
|
1021
1057
|
|
|
1058
|
+
// Без элемента SDK взял бы контейнер по умолчанию — id "map", то есть чужой элемент.
|
|
1059
|
+
const container = mapElement.value;
|
|
1060
|
+
|
|
1061
|
+
if (!container || disposed) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1022
1065
|
try {
|
|
1023
1066
|
const instance = await MahalMap.createAsync(
|
|
1024
1067
|
{
|
|
1025
|
-
container
|
|
1068
|
+
container,
|
|
1026
1069
|
center: DUSHANBE_CENTER,
|
|
1027
1070
|
zoom: 11,
|
|
1028
1071
|
},
|
|
@@ -1258,6 +1301,8 @@ MahalMap.showPlace(map, 68.787, 38.573, 2, 18);
|
|
|
1258
1301
|
MahalMap.onBuildingClick(map, (building) => console.log(building?.id));
|
|
1259
1302
|
MahalMap.selectBuilding(map, 123456789);
|
|
1260
1303
|
MahalMap.selectedBuilding(map);
|
|
1304
|
+
MahalMap.selectedBuildingKind(map);
|
|
1305
|
+
MahalMap.getBuildingsMinZoom(map);
|
|
1261
1306
|
MahalMap.setSelectionStyle(map, { color: "#e23b2f" });
|
|
1262
1307
|
MahalMap.clearSelection(map);
|
|
1263
1308
|
MahalMap.onRoadClick(map, (road) => console.log(road?.name));
|
|
@@ -1313,7 +1358,7 @@ MahalMap.setZoom(map, 14);
|
|
|
1313
1358
|
| `getMap(instance)` | Возвращает нативный MapLibre Map. |
|
|
1314
1359
|
| `getCamera(instance)` | Возвращает CameraController. |
|
|
1315
1360
|
| `setStyle(instance, theme)` | Переключает тему стандартного стиля. |
|
|
1316
|
-
| `setLanguage(instance, lang)` |
|
|
1361
|
+
| `setLanguage(instance, lang)` | Запоминает язык для поиска и роутинга; стиль и подписи не меняет. |
|
|
1317
1362
|
| `setCenter(instance, center)` | Меняет центр карты. |
|
|
1318
1363
|
| `setZoom(instance, zoom)` | Меняет zoom карты. |
|
|
1319
1364
|
| `addMarker(instance, marker)` | Добавляет маркер. |
|
|
@@ -1325,16 +1370,18 @@ MahalMap.setZoom(map, 14);
|
|
|
1325
1370
|
| `getLayers(instance)` | Снимок всех слоёв. |
|
|
1326
1371
|
| `onLayers(instance, callback)` | Подписка на изменения слоёв; возвращает отписку. |
|
|
1327
1372
|
| `refreshLayers(instance)` | Пере-применить волю клиента ко всем слоям. |
|
|
1328
|
-
| `getIndoorLevels(instance)` |
|
|
1373
|
+
| `getIndoorLevels(instance)` | Этажи в текущем виде карты (с 0.11.0 — из `params.levels` слоя). |
|
|
1329
1374
|
| `setIndoorLevel(instance, level)` | Переключить этаж (0 — первый наземный). |
|
|
1330
1375
|
| `refreshIndoor(instance)` | Перечитать планы этажей. |
|
|
1331
1376
|
| `showPlace(instance, lon, lat, level?, zoom?)` | Показать объект и открыть его этаж. |
|
|
1332
|
-
| `onBuildingClick(instance, callback)`
|
|
1377
|
+
| `onBuildingClick(instance, callback \| null)` | Клик по зданию; возвращает отписку. |
|
|
1333
1378
|
| `selectBuilding(instance, id, style?)` | Выделить здание по `osm_id`. |
|
|
1334
1379
|
| `clearSelection(instance)` | Снять выделение. |
|
|
1335
1380
|
| `selectedBuilding(instance)` | Идентификатор выделенного здания или `null`. |
|
|
1381
|
+
| `selectedBuildingKind(instance)` | `'scene'` / `'flat'` / `null` — чем нарисовано выделение. |
|
|
1382
|
+
| `getBuildingsMinZoom(instance)` | Зум появления объёма, переданный в Maps3D (по умолчанию `16`). |
|
|
1336
1383
|
| `setSelectionStyle(instance, style)` | Облик выделения. |
|
|
1337
|
-
| `onRoadClick(instance, callback)`
|
|
1384
|
+
| `onRoadClick(instance, callback \| null)` | Клик по дороге; возвращает отписку. |
|
|
1338
1385
|
| `roadAt(instance, point, tolPx?)` | Дорога под точкой холста. |
|
|
1339
1386
|
| `onParkingHover(instance, callback \| null)` | Парковка под курсором (только при смене участка). |
|
|
1340
1387
|
| `setParkingHoverStyle(instance, style)` | Облик подсветки парковки. |
|
|
@@ -1649,6 +1696,8 @@ interface MeasureShape {
|
|
|
1649
1696
|
import {
|
|
1650
1697
|
Search,
|
|
1651
1698
|
SearchPoi,
|
|
1699
|
+
createSearch,
|
|
1700
|
+
createSearchPoi,
|
|
1652
1701
|
SearchByLocation,
|
|
1653
1702
|
CheckJSApi,
|
|
1654
1703
|
Router,
|
|
@@ -1657,7 +1706,7 @@ import {
|
|
|
1657
1706
|
|
|
1658
1707
|
### `Search(text, token, additionalParam?)`
|
|
1659
1708
|
|
|
1660
|
-
Поиск адресов (геокодер).
|
|
1709
|
+
Поиск адресов (геокодер). Каждый вызов — отдельный запрос, без дебаунса: статический метод зовут разные поля ввода, и общий дебаунс отменял бы запрос одного поля вызовом из другого. Для ввода по символу — `createSearch()`.
|
|
1661
1710
|
|
|
1662
1711
|
```ts
|
|
1663
1712
|
const results = await Search("Рудаки 33", token, {
|
|
@@ -1679,7 +1728,7 @@ const results = await Search("Рудаки 33", token, {
|
|
|
1679
1728
|
|
|
1680
1729
|
### `SearchPoi(text, token, additionalParam?)`
|
|
1681
1730
|
|
|
1682
|
-
Поиск POI (организации, объекты). Сигнатура
|
|
1731
|
+
Поиск POI (организации, объекты). Сигнатура та же, что у `Search`, и тоже без дебаунса; для ввода по символу — `createSearchPoi()`.
|
|
1683
1732
|
|
|
1684
1733
|
```ts
|
|
1685
1734
|
const places = await SearchPoi("кафе", token, {
|
|
@@ -1691,6 +1740,30 @@ const places = await SearchPoi("кафе", token, {
|
|
|
1691
1740
|
|
|
1692
1741
|
Возвращает `ISearchResponse[]`.
|
|
1693
1742
|
|
|
1743
|
+
### `createSearch(delay?)` / `createSearchPoi(delay?)`
|
|
1744
|
+
|
|
1745
|
+
Поиск с дебаунсом для **одного** поля ввода. Возвращают функцию с сигнатурой `Search`/`SearchPoi`; пауза по умолчанию — 500 мс. Создавайте экземпляр на поле (компонент) — очередь у каждого своя, и чужие поля его запросы не отменяют.
|
|
1746
|
+
|
|
1747
|
+
Вызов, перебитый следующим вызовом **этого же** экземпляра до ухода запроса, отклоняется с `DebounceCancelledError`. Это не сбой: ответ получит последний вызов, а отменённый пропускайте молча.
|
|
1748
|
+
|
|
1749
|
+
```ts
|
|
1750
|
+
import { createSearch } from "mahal_map";
|
|
1751
|
+
|
|
1752
|
+
const searchAddress = createSearch(); // одна на поле ввода
|
|
1753
|
+
|
|
1754
|
+
input.addEventListener("input", async () => {
|
|
1755
|
+
try {
|
|
1756
|
+
suggestions = await searchAddress(input.value, token);
|
|
1757
|
+
} catch (error) {
|
|
1758
|
+
// Перебит следующим вводом в этом же поле
|
|
1759
|
+
if ((error as Error).name === "DebounceCancelledError") return;
|
|
1760
|
+
showError(error);
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
```
|
|
1764
|
+
|
|
1765
|
+
Сверяйте по `name`, а не через `instanceof`: на странице может оказаться две сборки SDK. Класс тоже экспортируется — `import { DebounceCancelledError } from "mahal_map"`.
|
|
1766
|
+
|
|
1694
1767
|
### `SearchByLocation(params)`
|
|
1695
1768
|
|
|
1696
1769
|
Обратный геокодинг: адреса и POI по координатам. Без дебаунса.
|