mahal_map 2.0.0 → 2.0.1

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 CHANGED
@@ -12,7 +12,7 @@ npm install mahal_map maplibre-gl @grammaps/maps3d-web
12
12
 
13
13
  Обе зависимости — `peerDependencies`, в бандл `mahal_map` они не входят. Библиотека их не импортирует: MapLibre и `Maps3D` приходят снаружи, аргументами `create()` либо через `window`.
14
14
 
15
- - `maplibre-gl` (3–6) — обязателен всегда.
15
+ - `maplibre-gl` (`^6.10.0`) — обязателен всегда.
16
16
  - `@grammaps/maps3d-web` (>=0.5.0) — даёт стили, тайлы, объём, пробки, рельеф, планы этажей, клик по объектам. Помечен `optional`: без него карта поднимется на запасном векторном стиле, но 3D и слоёв платформы на ней не будет.
17
17
 
18
18
  MapLibre можно не класть в свою сборку вовсе — платформа отдаёт согласованную версию вместе с веб-воркером:
@@ -21,6 +21,49 @@ MapLibre можно не класть в свою сборку вовсе — п
21
21
  const maplibregl = await Maps3D.maplibre({ base: "https://navi.gram.tj" });
22
22
  ```
23
23
 
24
+ ## Веб-воркер MapLibre
25
+
26
+ MapLibre разбирает векторные тайлы в веб-воркере и ищет его файл рядом со своим модулем. Сборщик складывает библиотеку в общий бандл, рядом файла не оказывается, и вместо скрипта сервер отдаёт `index.html`.
27
+
28
+ Симптом обманчивый: **карта показывает пустой фон, ошибок про карту нет**. В консоли лежит только `Uncaught SyntaxError: Unexpected token '<'` — ни слова ни про MapLibre, ни про воркер, ни про тайлы.
29
+
30
+ `mahal_map` это распознаёт сам. Если через 8 с после создания карты не разобран ни один векторный тайл, в консоль уходит предупреждение с причиной и обоими решениями:
31
+
32
+ ```
33
+ [MahalMap SDK] Карта пуста: ни один векторный тайл не разобран — похоже, не запустился веб-воркер MapLibre.
34
+ ```
35
+
36
+ Проверка молчит на растровых стилях (им воркер не нужен), в фоновой вкладке (там MapLibre приостановлен) и когда фичи отрисованы. Отключается через `workerCheck: false`, своя задержка — `workerCheck: 12000`.
37
+
38
+ ### Решение 1 — взять библиотеку у платформы
39
+
40
+ Версия согласована с Maps3D, веб-воркер приезжает вместе с ней, настраивать нечего:
41
+
42
+ ```ts
43
+ const maplibregl = await Maps3D.maplibre({ base: "https://navi.gram.tj" });
44
+
45
+ const map = MahalMap.create({ container: "map" }, maplibregl, Maps3D);
46
+ ```
47
+
48
+ ### Решение 2 — оставить свою сборку
49
+
50
+ Тогда адрес воркера нужно свести с реальным файлом — и в разработке, и в собранной версии:
51
+
52
+ ```ts
53
+ // vite.config.ts — чтобы адрес воркера совпал с файлом во время разработки
54
+ export default defineConfig({ optimizeDeps: { exclude: ["maplibre-gl"] } });
55
+ ```
56
+
57
+ ```ts
58
+ // в приложении — чтобы адрес совпал и в собранной версии
59
+ import * as maplibregl from "maplibre-gl";
60
+ import "maplibre-gl/dist/maplibre-gl.css";
61
+
62
+ maplibregl.setWorkerUrl("/maplibre-gl-worker.mjs"); // файл скопирован в public/ из maplibre-gl/dist
63
+ ```
64
+
65
+ > `import * as maplibregl` — не случайность: в MapLibre 6 default-экспорта нет, `import maplibregl from "maplibre-gl"` даёт `undefined`.
66
+
24
67
  ## Миграция с 1.x на 2.0
25
68
 
26
69
  Собственных URL стилей у `mahal_map` больше нет — их целиком отдаёт `@grammaps/maps3d-web`.
@@ -58,7 +101,7 @@ const maplibregl = await Maps3D.maplibre({ base: "https://navi.gram.tj" });
58
101
  ## Быстрый старт через NPM
59
102
 
60
103
  ```ts
61
- import maplibregl from "maplibre-gl";
104
+ import * as maplibregl from "maplibre-gl";
62
105
  import "maplibre-gl/dist/maplibre-gl.css";
63
106
  import { Maps3D } from "@grammaps/maps3d-web";
64
107
  import { MahalMap, keyUtils } from "mahal_map";
@@ -87,28 +130,30 @@ const map = MahalMap.create(
87
130
 
88
131
  ## Быстрый старт через Browser SDK
89
132
 
90
- Сначала подключите MapLibre, затем `mahal_map.sdk.js`. Для browser SDK параметр `apikey` обязателен: без него карта не инициализируется.
133
+ Для browser SDK параметр `apikey` обязателен: без него карта не инициализируется.
134
+
135
+ **MapLibre 6 поставляется только как ESM** — классической сборки `dist/maplibre-gl.js` для `<script src>` в ней больше нет. Поэтому библиотеку карты подключают модульным скриптом и передают в `create()` вторым аргументом:
91
136
 
92
137
  ```html
93
138
  <link
94
139
  rel="stylesheet"
95
- href="https://unpkg.com/maplibre-gl@5.3.0/dist/maplibre-gl.css"
140
+ href="https://cdn.jsdelivr.net/npm/maplibre-gl@6.10.0/dist/maplibre-gl.css"
96
141
  />
97
- <script src="https://unpkg.com/maplibre-gl@5.3.0/dist/maplibre-gl.js"></script>
98
142
  <script src="https://cp.mahal.tj/sdk/mahal_map.sdk.js?apikey=YOUR_MAP_API_KEY"></script>
99
- ```
100
-
101
- После этого глобальный объект `MahalMap` доступен в `window`:
102
143
 
103
- ```html
104
144
  <div id="map" style="width: 100%; height: 500px"></div>
105
145
 
106
- <script>
107
- const map = MahalMap.create({
108
- container: "map",
109
- center: [69.624024, 40.279687],
110
- zoom: 12,
111
- });
146
+ <script type="module">
147
+ import * as maplibregl from "https://cdn.jsdelivr.net/npm/maplibre-gl@6.10.0/+esm";
148
+
149
+ const map = MahalMap.create(
150
+ {
151
+ container: "map",
152
+ center: [69.624024, 40.279687],
153
+ zoom: 12,
154
+ },
155
+ maplibregl,
156
+ );
112
157
  </script>
113
158
  ```
114
159
 
@@ -131,7 +176,7 @@ const map = MahalMap.create(
131
176
  <script src="https://cp.mahal.tj/sdk/mahal_map.sdk.js?apikey=YOUR_MAP_API_KEY&lang=ru"></script>
132
177
  ```
133
178
 
134
- Если `lang` не передан или передан `lang=tj`, SDK добавляет только `token`.
179
+ `lang` влияет на поиск и роутинг, но не на стиль: подписи на карте приходят из самого стиля платформы.
135
180
 
136
181
  ## Параметры создания карты
137
182
 
@@ -156,6 +201,7 @@ interface IMahalMapOptions {
156
201
  family?: "default" | "navigator" | "mobile";
157
202
  preset?: Maps3DThemeName | string;
158
203
  antialias?: boolean;
204
+ workerCheck?: boolean | number;
159
205
  maps3d?: Omit<IMaps3DLayerOptions, "apiKey" | "base">;
160
206
  }
161
207
  ```
@@ -175,6 +221,7 @@ interface IMahalMapOptions {
175
221
  | `family` | `"default" \| "navigator" \| "mobile"` | Семейство стилей. `theme` выбирает внутри него: `default` → `light`/`dark`, `navigator` → `navigator-light`/`navigator-dark`, `mobile` → `mobile-*`. |
176
222
  | `preset` | `Maps3DThemeName \| string` | Явное имя темы платформы или полный URL стиля вместо пары `family` + `theme`. Задан — стиль зафиксирован. |
177
223
  | `antialias` | `boolean` | Сглаживание сцены. По умолчанию `true` при включённом 3D: без него тонкая геометрия (перила, мачты, ряды сидений) на отдалении рассыпается в рябь. |
224
+ | `workerCheck` | `boolean \| number` | Диагностика неработающего веб-воркера MapLibre через 8 с после создания карты. `false` отключает, число задаёт свою задержку в мс. См. [Веб-воркер MapLibre](#веб-воркер-maplibre). |
178
225
  | `maps3d` | `object` | Опции Maps3D: `buildings`, `traffic`, `indoor`, `closures`, `places`, `minZoom`, `lodBias`, `memoryBudget`, `maskReplaced`, `typeReplacements`. |
179
226
 
180
227
  ### Стили и темы
@@ -234,38 +281,133 @@ map.setStyle("light"); // → navigator-light
234
281
 
235
282
  ### Реестр слоёв
236
283
 
237
- Единая дверь ко всем слоям платформы. Состояние слоя три независимых поля: `wanted` (чего хочет приложение), `available` (что позволяют стиль и данные), `active` (что нарисовано сейчас).
284
+ Единая дверь ко всем слоям платформы. Идентификаторы и параметры типизированы: опечатка в имени слоя или в значении параметра ошибка компиляции, а не тихий `false` в рантайме.
238
285
 
239
286
  ```ts
240
287
  map.setLayer("terrain", true, { mode: "on" });
241
288
  map.setLayer("traffic", true);
242
289
  map.setLayer("indoor", true, { level: 2 });
290
+ map.setLayer("buildings", true, { detail: "facade" });
243
291
 
292
+ map.getLayerState("terrain"); // снимок одного слоя или null
293
+ map.getLayers(); // снимок всех — по нему рисуется панель слоёв
294
+ ```
295
+
296
+ | Слой | Параметры | По умолчанию |
297
+ | ------------------ | ------------------------------------------ | ------------ |
298
+ | `buildings` | `detail: footprint\|volume\|roofs\|facade` | включён |
299
+ | `objects` | — | включён |
300
+ | `traffic` | — | выключен |
301
+ | `trafficRaster` | — | выключен |
302
+ | `parking` | `highlight`, `paid`, `free`, `unknown` | включён |
303
+ | `fuel`, `charging` | — | включены |
304
+ | `closures` | — | выключен |
305
+ | `indoor` | `level` | выключен |
306
+ | `terrain` | `mode: auto\|on\|off` | `auto` |
307
+
308
+ Стартовое состояние можно задать сразу при создании карты — тогда первый кадр уже правильный, без моргания:
309
+
310
+ ```ts
311
+ MahalMap.create(
312
+ { container: "map", maps3d: { traffic: true, closures: true } },
313
+ maplibregl,
314
+ Maps3D,
315
+ );
316
+ ```
317
+
318
+ Полный список — в разделе [Опции `maps3d`](#опции-maps3d-расширенные).
319
+
320
+ #### `wanted`, `available`, `active` — три разных вопроса
321
+
322
+ Поля независимы, и это главная ловушка реестра:
323
+
324
+ | Поле | Вопрос |
325
+ | ----------- | --------------------------------------- |
326
+ | `wanted` | чего хочет приложение |
327
+ | `available` | что позволяют стиль и данные |
328
+ | `active` | что нарисовано прямо сейчас |
329
+
330
+ Слой может быть включён и при этом не нарисован — и это не ошибка:
331
+
332
+ ```ts
333
+ map.setLayer("terrain", true); // wanted: true
334
+ const state = map.getLayerState("terrain");
335
+
336
+ state?.available; // false — в стиле нет ключа maps3d:terrain
337
+ state?.active; // false — либо режим auto, а зум слишком близкий
338
+ ```
339
+
340
+ Поэтому галочку в интерфейсе рисуют по `wanted`, а пометку «сейчас не видно» — по `active`. Проверять сразу после `setLayer()` бесполезно: тайлы ещё едут. Правильный способ — подписка.
341
+
342
+ #### Панель слоёв: подписка и отписка
343
+
344
+ `onLayers()` возвращает функцию отписки. Звать её обязательно — иначе колбэк переживёт компонент и будет дёргать размонтированное состояние:
345
+
346
+ ```ts
244
347
  const unsubscribe = map.onLayers((state) => {
245
348
  console.log(state.id, state.wanted, state.available, state.active);
246
349
  });
247
350
 
248
- map.getLayerState("terrain"); // снимок одного слоя или null
249
- map.getLayers(); // снимок всех — по нему рисуется панель слоёв
351
+ // при размонтировании компонента
352
+ unsubscribe();
353
+ ```
354
+
355
+ `map.destroy()` снимает слой Maps3D целиком, так что после него подписка всё равно мертва — но до него отписываться нужно самим.
356
+
357
+ #### Парковки, заправки, зарядки
358
+
359
+ `Maps3D` рисует их сам, забирая эти классы у POI-слоёв стиля. Поэтому выключение слоя убирает объекты с карты полностью, а не оставляет значок стиля.
360
+
361
+ Подтип парковки берётся из атрибута `fee`:
362
+
363
+ | Параметр | Условие |
364
+ | ----------- | --------------- |
365
+ | `paid` | `fee=yes` |
366
+ | `free` | `fee=no` |
367
+ | `unknown` | атрибута нет |
368
+
369
+ ```ts
370
+ // только платные, с подсветкой
371
+ map.setLayer("parking", true, { paid: true, free: false, unknown: false, highlight: true });
250
372
  ```
251
373
 
252
- | Слой | Параметры | По умолчанию |
253
- | ------------------ | ---------------------------------------- | ------------ |
254
- | `buildings` | `detail: footprint\|volume\|roofs\|facade` | включён |
255
- | `objects` | — | включён |
256
- | `traffic` | — | выключен |
257
- | `trafficRaster` | — | выключен |
258
- | `parking` | `highlight`, `paid`, `free`, `unknown` | включён |
259
- | `fuel`, `charging` | — | включены |
260
- | `closures` | — | выключен |
261
- | `indoor` | `level` | выключен |
262
- | `terrain` | `mode: auto\|on\|off` | `auto` |
374
+ #### Рельеф
263
375
 
264
- Слой может быть включён и при этом не нарисован: рельеф в режиме `auto` появляется на обзорных зумах, перекрытия требуют своего тайлсета, планы этажей данных по зданию. Рельеф в режиме `on` заметно дороже по трафику и времени кадра.
376
+ `auto` показывает рельеф на обзорных зумах, `on`всегда. Режим `on` заметно дороже по трафику и времени кадра, чем `auto`.
265
377
 
266
- Парковки, заправки и зарядки `Maps3D` рисует сам, забирая эти классы у POI-слоёв стиля, — поэтому выключение слоя убирает объекты с карты полностью, а не оставляет значок стиля.
378
+ ```ts
379
+ map.setLayer("terrain", true, { mode: "auto" });
380
+ ```
381
+
382
+ #### Планы этажей
383
+
384
+ Слой включается реестром, но одного этого мало — без переключения этажа он бесполезен:
385
+
386
+ ```ts
387
+ map.setLayer("indoor", true);
388
+
389
+ const levels = map.getIndoorLevels(); // этажи в текущем виде карты, [] — данных нет
390
+ map.setIndoorLevel(1); // нумерация OSM: 0 — первый наземный
391
+ map.refreshIndoor(); // перечитать после правки картографом
392
+ ```
393
+
394
+ После поиска показать найденный объект вместе с его этажом:
395
+
396
+ ```ts
397
+ // level приходит у объектов внутри зданий; без него откроется первый этаж
398
+ // и метка окажется в чужом зале
399
+ map.showPlace(68.787, 38.573, place.level, 18);
400
+ ```
401
+
402
+ #### Слой, которого нет в списке
267
403
 
268
- `setLayer()` возвращает `false`, если такого слоя в подключённой сборке Maps3D нет (или Maps3D не передан вовсе). После полной смены стиля волю клиента возвращает `map.refreshLayers()` — при `setStyle()` библиотека вызывает его сама.
404
+ `setLayer()` принимает только известные идентификаторы. Если в новой сборке Maps3D появится слой, которого ещё нет в типах, он доступен напрямую через слой, там `id` остаётся строкой:
405
+
406
+ ```ts
407
+ map.getMaps3DLayer()?.setLayer?.("новый-слой", true);
408
+ ```
409
+
410
+ `setLayer()` возвращает `false`, если такого слоя в подключённой сборке Maps3D нет или Maps3D не передан вовсе. После полной смены стиля волю клиента возвращает `map.refreshLayers()` — при `setStyle()` библиотека вызывает его сама.
269
411
 
270
412
  ### 3D-здания
271
413
 
@@ -367,15 +509,16 @@ layer?.buildings?.setWindowStyle(4);
367
509
 
368
510
  ### Подключение и выключение 3D-слоя: полный пример (Vue 3)
369
511
 
370
- Кнопка-переключатель «3D ⇄ контуры», тонкая настройка окон и корректная очистка при размонтировании. Слой `Maps3D` поднимает и цепляет сама библиотека — вручную `new Maps3D(...)`, `transformRequest` и `attach()` писать не нужно.
512
+ Кнопка-переключатель «3D ⇄ контуры», панель слоёв на живом состоянии реестра и корректная очистка при размонтировании. Слой `Maps3D` поднимает и цепляет сама библиотека — вручную `Maps3D.enhance(...)`, `transformRequest` и `attach()` писать не нужно.
371
513
 
372
514
  ```vue
373
515
  <script setup lang="ts">
374
516
  import { computed, onBeforeUnmount, onMounted, ref, shallowRef } from "vue";
375
- import maplibregl from "maplibre-gl";
517
+ import * as maplibregl from "maplibre-gl";
376
518
  import "maplibre-gl/dist/maplibre-gl.css";
377
519
  import { Maps3D } from "@grammaps/maps3d-web";
378
520
  import { MahalMap, keyUtils } from "mahal_map";
521
+ import type { IMaps3DLayerState, Maps3DLayerId } from "mahal_map";
379
522
 
380
523
  const API_KEY = "YOUR_MAP_API_KEY";
381
524
 
@@ -383,6 +526,15 @@ const mahalMap = shallowRef<MahalMap | null>(null);
383
526
  const is3dEnabled = ref(true);
384
527
  const isLayerReady = ref(false);
385
528
 
529
+ // Панель слоёв рисуется по живому снимку реестра
530
+ const layers = ref<IMaps3DLayerState[]>([]);
531
+ let unsubscribeLayers: (() => void) | null = null;
532
+
533
+ function toggleLayer(id: Maps3DLayerId, on: boolean) {
534
+ // Опечатка в id или в параметрах не скомпилируется
535
+ mahalMap.value?.setLayer(id, on);
536
+ }
537
+
386
538
  const buildingModeText = computed(() =>
387
539
  is3dEnabled.value ? "3D включено" : "Контуры",
388
540
  );
@@ -433,11 +585,21 @@ onMounted(async () => {
433
585
  buildings.setWindowGlow?.(0.22);
434
586
  buildings.setEdgeRadius(1.2);
435
587
 
588
+ // Слои: стартовый снимок плюс подписка на изменения.
589
+ // Состояние приходит асинхронно — сразу после setLayer() проверять бесполезно.
590
+ layers.value = map.getLayers();
591
+ unsubscribeLayers = map.onLayers(() => {
592
+ layers.value = map.getLayers();
593
+ });
594
+
436
595
  isLayerReady.value = true;
437
596
  });
438
597
 
439
598
  onBeforeUnmount(() => {
440
599
  isLayerReady.value = false;
600
+ // Отписку снимаем сами: иначе колбэк переживёт компонент.
601
+ unsubscribeLayers?.();
602
+ unsubscribeLayers = null;
441
603
  // destroy() сам снимает слой Maps3D и удаляет карту MapLibre.
442
604
  mahalMap.value?.destroy();
443
605
  mahalMap.value = null;
@@ -458,6 +620,24 @@ onBeforeUnmount(() => {
458
620
  >
459
621
  {{ buildingToggleText }}
460
622
  </button>
623
+
624
+ <ul class="layers">
625
+ <li v-for="layer in layers" :key="layer.id">
626
+ <label>
627
+ <!-- галочка по wanted: это воля приложения -->
628
+ <input
629
+ type="checkbox"
630
+ :checked="layer.wanted"
631
+ :disabled="!layer.available"
632
+ @change="toggleLayer(layer.id as Maps3DLayerId, !layer.wanted)"
633
+ />
634
+ {{ layer.id }}
635
+ </label>
636
+ <!-- слой включён, но не нарисован — это норма, а не ошибка -->
637
+ <small v-if="layer.wanted && !layer.active">сейчас не видно</small>
638
+ <small v-else-if="!layer.available">нет в стиле</small>
639
+ </li>
640
+ </ul>
461
641
  </section>
462
642
  </main>
463
643
  </template>
@@ -492,15 +672,18 @@ onBeforeUnmount(() => {
492
672
 
493
673
  #### То же самое без сборщика (browser SDK)
494
674
 
675
+ Библиотеку карты проще взять у платформы: версия согласована с Maps3D, веб-воркер приезжает вместе с ней, про ESM-сборку думать не нужно.
676
+
495
677
  ```html
496
- <script src="https://unpkg.com/maplibre-gl@5.3.0/dist/maplibre-gl.js"></script>
497
678
  <script src="https://cdn.jsdelivr.net/npm/@grammaps/maps3d-web/dist/maps3d.global.js"></script>
498
679
  <script src="https://cp.mahal.tj/sdk/mahal_map.sdk.js?apikey=YOUR_MAP_API_KEY"></script>
499
680
 
500
681
  <div id="map" style="width: 100%; height: 500px"></div>
501
682
  <button id="toggle3d" type="button">Выключить 3D</button>
502
683
 
503
- <script>
684
+ <script type="module">
685
+ const maplibregl = await Maps3D.maplibre({ base: "https://navi.gram.tj" });
686
+
504
687
  // Maps3D берётся из window.Maps3D — третий аргумент передавать не нужно.
505
688
  const map = MahalMap.create({
506
689
  container: "map",
@@ -637,7 +820,7 @@ try {
637
820
  </template>
638
821
 
639
822
  <script setup lang="ts">
640
- import maplibregl from "maplibre-gl";
823
+ import * as maplibregl from "maplibre-gl";
641
824
  import "maplibre-gl/dist/maplibre-gl.css";
642
825
  import type { MahalMap as MahalMapInstance } from "mahal_map";
643
826
  import { onBeforeUnmount, onMounted, ref } from "vue";
@@ -884,6 +1067,12 @@ MahalMap.getLayers(map);
884
1067
  MahalMap.onLayers(map, (state) => console.log(state.id, state.active));
885
1068
  MahalMap.refreshLayers(map);
886
1069
 
1070
+ // Планы этажей
1071
+ MahalMap.getIndoorLevels(map);
1072
+ MahalMap.setIndoorLevel(map, 1);
1073
+ MahalMap.refreshIndoor(map);
1074
+ MahalMap.showPlace(map, 68.787, 38.573, 2, 18);
1075
+
887
1076
  // Выделение зданий и клики
888
1077
  MahalMap.onBuildingClick(map, (building) => console.log(building?.id));
889
1078
  MahalMap.selectBuilding(map, 123456789);
@@ -939,6 +1128,10 @@ MahalMap.setZoom(map, 14);
939
1128
  | `getLayers(instance)` | Снимок всех слоёв. |
940
1129
  | `onLayers(instance, callback)` | Подписка на изменения слоёв; возвращает отписку. |
941
1130
  | `refreshLayers(instance)` | Пере-применить волю клиента ко всем слоям. |
1131
+ | `getIndoorLevels(instance)` | Этажи, найденные в текущем виде карты. |
1132
+ | `setIndoorLevel(instance, level)` | Переключить этаж (0 — первый наземный). |
1133
+ | `refreshIndoor(instance)` | Перечитать планы этажей. |
1134
+ | `showPlace(instance, lon, lat, level?, zoom?)` | Показать объект и открыть его этаж. |
942
1135
  | `onBuildingClick(instance, callback)` | Клик по зданию. |
943
1136
  | `selectBuilding(instance, id, style?)` | Выделить здание по `osm_id`. |
944
1137
  | `clearSelection(instance)` | Снять выделение. |
package/dist/index.d.mts CHANGED
@@ -26,17 +26,64 @@ interface IAdditionalParamType {
26
26
  * более старая сборка на странице не ломала типы у потребителя.
27
27
  */
28
28
  type Maps3DThemeName = "light" | "dark" | "navigator-light" | "navigator-dark" | "mobile-light" | "mobile-dark";
29
- /** Идентификатор слоя в реестре: buildings, objects, traffic, terrain, indoor, closures, parking, fuel, charging. */
30
- type Maps3DLayerId = string;
31
29
  /** Уровень детализации зданий. */
32
30
  type Maps3DBuildingDetail = "footprint" | "volume" | "roofs" | "facade";
31
+ /** Режим рельефа: `auto` показывает его на обзорных зумах, `on` — всегда (дороже по трафику и кадру). */
32
+ type Maps3DTerrainMode = "auto" | "on" | "off";
33
+ /**
34
+ * Параметры каждого слоя реестра. Служит и справочником, и источником типов:
35
+ * ключи дают список id, значения — допустимые `params` в setLayer().
36
+ *
37
+ * У самой Maps3D id слоя типизирован как `string`, поэтому опечатка там проходит
38
+ * компиляцию и проваливается в рантайме (setLayer вернёт false). Здесь список
39
+ * фиксирован по таблице слоёв 0.7.3 — промах ловится на сборке.
40
+ */
41
+ interface IMaps3DLayerParams {
42
+ /** Объёмные здания. По умолчанию включён. */
43
+ buildings: {
44
+ detail?: Maps3DBuildingDetail;
45
+ };
46
+ /** Расставленные 3D-объекты. По умолчанию включён. */
47
+ objects: Record<string, never>;
48
+ /** Пробки векторным слоем. По умолчанию выключен. */
49
+ traffic: Record<string, never>;
50
+ /** Пробки картинкой с сервера, без клика по дороге. По умолчанию выключен. */
51
+ trafficRaster: Record<string, never>;
52
+ /**
53
+ * Парковки. По умолчанию включён. Подтип берётся из атрибута `fee`:
54
+ * `paid` — `fee=yes`, `free` — `fee=no`, `unknown` — атрибута нет.
55
+ */
56
+ parking: {
57
+ highlight?: boolean;
58
+ paid?: boolean;
59
+ free?: boolean;
60
+ unknown?: boolean;
61
+ };
62
+ /** Заправки. По умолчанию включён. */
63
+ fuel: Record<string, never>;
64
+ /** Зарядки. По умолчанию включён. */
65
+ charging: Record<string, never>;
66
+ /** Перекрытия дорог. По умолчанию выключен, требует своего тайлсета. */
67
+ closures: Record<string, never>;
68
+ /** Планы этажей. По умолчанию выключен, требует данных по зданию. */
69
+ indoor: {
70
+ level?: number;
71
+ };
72
+ /** Рельеф. По умолчанию `auto`. */
73
+ terrain: {
74
+ mode?: Maps3DTerrainMode;
75
+ };
76
+ }
77
+ /** Идентификатор слоя в реестре Maps3D. */
78
+ type Maps3DLayerId = keyof IMaps3DLayerParams;
33
79
  /**
34
80
  * Снимок состояния слоя. Три поля независимы: слой может быть включён клиентом (`wanted`),
35
81
  * но не нарисован — рельеф в режиме auto ждёт обзорного зума, перекрытия ждут своего
36
82
  * тайлсета, планы этажей — данных по зданию.
37
83
  */
38
84
  interface IMaps3DLayerState {
39
- id: Maps3DLayerId;
85
+ /** Не сужен до Maps3DLayerId: сборка Maps3D может отдать слой, которого нет в нашем списке. */
86
+ id: string;
40
87
  /** Чего хочет клиент: последний setLayer либо умолчание стиля. */
41
88
  wanted: boolean;
42
89
  /** Что позволяет стиль: объявлен ли слой и есть ли под него данные. */
@@ -150,8 +197,8 @@ interface IMaps3DLayer {
150
197
  readonly ready?: Promise<void>;
151
198
  destroy?(): void;
152
199
  remove?(): void;
153
- setLayer?(id: Maps3DLayerId, on: boolean, params?: Record<string, unknown>): boolean;
154
- layer?(id: Maps3DLayerId): IMaps3DLayerState | null;
200
+ setLayer?(id: string, on: boolean, params?: Record<string, unknown>): boolean;
201
+ layer?(id: string): IMaps3DLayerState | null;
155
202
  layers?(): IMaps3DLayerState[];
156
203
  onLayers?(callback: (state: IMaps3DLayerState) => void): () => void;
157
204
  refreshLayers?(): void;
@@ -255,6 +302,12 @@ interface IMahalMapOptions {
255
302
  antialias?: boolean;
256
303
  /** Опции Maps3D: buildings, traffic, indoor, closures, terrain, minZoom, lodBias, memoryBudget, ... */
257
304
  maps3d?: Omit<IMaps3DLayerOptions, "apiKey" | "base">;
305
+ /**
306
+ * Диагностика неработающего веб-воркера MapLibre: если через 8 с после создания карты
307
+ * не разобран ни один векторный тайл, в консоль уходит предупреждение с причиной и
308
+ * решением. `false` отключает проверку, число задаёт свою задержку в мс.
309
+ */
310
+ workerCheck?: boolean | number;
258
311
  /**
259
312
  * @deprecated Игнорируется с 2.0.0. Движок один — платформа MahalMaps через
260
313
  * @grammaps/maps3d-web. Без переданного Maps3D карта поднимается на запасном стиле.
@@ -382,6 +435,8 @@ declare class MahalMap {
382
435
  private static instances;
383
436
  private static defaultLanguage;
384
437
  private static disposedMaps3DLayers;
438
+ /** Через сколько мс после создания карты проверяем, ожил ли веб-воркер MapLibre. */
439
+ private static readonly WORKER_CHECK_DELAY;
385
440
  private isReady;
386
441
  private readyCallbacks;
387
442
  private map;
@@ -400,6 +455,7 @@ declare class MahalMap {
400
455
  private maps3dLayer?;
401
456
  private maps3dReady?;
402
457
  private buildingsEnabled;
458
+ private workerCheckTimer?;
403
459
  private constructor();
404
460
  private static getInstanceKey;
405
461
  private static normalizeLanguage;
@@ -453,11 +509,15 @@ declare class MahalMap {
453
509
  static getMaps3DLayer(instance: MahalMap): IMaps3DLayer | undefined;
454
510
  static whenMaps3DReady(instance: MahalMap): Promise<IMaps3DLayer | undefined>;
455
511
  static toggle3DBuildings(instance: MahalMap, enabled: boolean): void;
456
- static setLayer(instance: MahalMap, id: Maps3DLayerId, on: boolean, params?: Record<string, unknown>): boolean;
512
+ static setLayer<Id extends Maps3DLayerId>(instance: MahalMap, id: Id, on: boolean, params?: IMaps3DLayerParams[Id]): boolean;
457
513
  static getLayerState(instance: MahalMap, id: Maps3DLayerId): IMaps3DLayerState | null;
458
514
  static getLayers(instance: MahalMap): IMaps3DLayerState[];
459
515
  static onLayers(instance: MahalMap, callback: (state: IMaps3DLayerState) => void): () => void;
460
516
  static refreshLayers(instance: MahalMap): void;
517
+ static getIndoorLevels(instance: MahalMap): number[];
518
+ static setIndoorLevel(instance: MahalMap, level: number): void;
519
+ static refreshIndoor(instance: MahalMap): void;
520
+ static showPlace(instance: MahalMap, lon: number, lat: number, level?: number | null, zoom?: number): void;
461
521
  static onBuildingClick(instance: MahalMap, callback: (info: IMaps3DBuildingClickInfo | null) => void): void;
462
522
  static selectBuilding(instance: MahalMap, id: number | string | null, style?: IMaps3DSelectionStyle): boolean;
463
523
  static clearSelection(instance: MahalMap): void;
@@ -500,8 +560,15 @@ declare class MahalMap {
500
560
  * Резолвится в undefined, если слой выключен, Maps3D не передан или подключение упало.
501
561
  */
502
562
  whenMaps3DReady(): Promise<IMaps3DLayer | undefined>;
503
- /** Включить/выключить слой. `false` — такого слоя в подключённой сборке Maps3D нет. */
504
- setLayer(id: Maps3DLayerId, on: boolean, params?: Record<string, unknown>): boolean;
563
+ /**
564
+ * Включить/выключить слой. `params` типизирован под конкретный слой: `terrain` ждёт
565
+ * `{ mode }`, `buildings` — `{ detail }`, `indoor` — `{ level }`, у остальных параметров нет.
566
+ *
567
+ * Возвращает `false`, если такого слоя в подключённой сборке Maps3D нет или Maps3D не передан.
568
+ * Слой, которого ещё нет в нашем списке id, доступен напрямую:
569
+ * `map.getMaps3DLayer()?.setLayer?.("новый", true)`.
570
+ */
571
+ setLayer<Id extends Maps3DLayerId>(id: Id, on: boolean, params?: IMaps3DLayerParams[Id]): boolean;
505
572
  /** Состояние слоя: wanted (воля клиента), available (позволяет стиль), active (нарисовано). */
506
573
  getLayerState(id: Maps3DLayerId): IMaps3DLayerState | null;
507
574
  /** Снимок всех слоёв — по нему приложение рисует свою панель. */
@@ -510,6 +577,18 @@ declare class MahalMap {
510
577
  onLayers(callback: (state: IMaps3DLayerState) => void): () => void;
511
578
  /** Пере-применить волю клиента ко всем слоям — после полной смены стиля. */
512
579
  refreshLayers(): void;
580
+ /** Этажи, найденные в текущем виде карты. Пусто — данных по зданию нет. */
581
+ getIndoorLevels(): number[];
582
+ /** Переключить этаж. Нумерация OSM: 0 — первый наземный. */
583
+ setIndoorLevel(level: number): void;
584
+ /** Перечитать планы этажей — после правки картографом. */
585
+ refreshIndoor(): void;
586
+ /**
587
+ * Показать найденный объект и открыть его этаж. Ровно то, что нужно после поиска:
588
+ * у объектов внутри зданий приходит `level` — без него карта откроет первый этаж
589
+ * и метка окажется в чужом зале.
590
+ */
591
+ showPlace(lon: number, lat: number, level?: number | null, zoom?: number): void;
513
592
  /** Клик по зданию: { id, height, props } либо null. Повторный вызов заменяет обработчик. */
514
593
  onBuildingClick(callback: (info: IMaps3DBuildingClickInfo | null) => void): void;
515
594
  /** Выделить здание по osm_id. `false` — здания сейчас нет в загруженных данных. */
@@ -539,6 +618,23 @@ declare class MahalMap {
539
618
  * вызов teardown попал бы на уже освобождённый объект.
540
619
  */
541
620
  private static teardownMaps3DLayer;
621
+ /**
622
+ * Диагностика неработающего веб-воркера MapLibre.
623
+ *
624
+ * Симптом злой: тайлы приходят, но разобрать их некому — карта показывает пустой фон,
625
+ * а в консоли лежит только `Uncaught SyntaxError: Unexpected token '<'`, где нет ни слова
626
+ * ни про MapLibre, ни про воркер. Причина почти всегда одна: сборщик не перенёс файл
627
+ * воркера рядом со сборкой, и вместо скрипта сервер отдаёт index.html.
628
+ *
629
+ * Проверку вешаем на таймер от создания карты, а НЕ на событие "load": при мёртвом
630
+ * воркере стиль не дозагружается и "load" не приходит вовсе.
631
+ *
632
+ * Смотрим на результат, а не на события: у Maps3D свой сторож (`gn`) слушает
633
+ * `data{sourceDataType:"content"}`, но это событие приходит при регистрации источника,
634
+ * за ~30 мс и без единого тайла — тревога снимается раньше, чем что-то могло сломаться.
635
+ */
636
+ private watchWorkerHealth;
637
+ private reportBrokenWorker;
542
638
  /**
543
639
  * Приводит объём зданий к желаемому состоянию. Реестр слоёв (0.7.x) — основная дверь:
544
640
  * он же прячет и возвращает штатные здания стиля. setBuildingsEnabled — путь для
@@ -821,4 +917,4 @@ declare namespace index {
821
917
  export { index_checkCoordinates as checkCoordinates, index_debounce as debounce, index_geojsonPolyline as geojsonPolyline, index_geometryPolyline as geometryPolyline, index_trimValue as trimValue };
822
918
  }
823
919
 
824
- export { CheckJSApi, type IAdditionalParamType, type ICheckJSApiParam, type ICheckJSApiResponse, type IMahalMapOptions, type IMapMarker, type IMaps3DBuildingClickInfo, type IMaps3DBuildingsController, type IMaps3DBuildingsOptions, type IMaps3DClosuresOptions, type IMaps3DDiagnostics, type IMaps3DIndoorOptions, type IMaps3DLayer, type IMaps3DLayerOptions, type IMaps3DLayerState, type IMaps3DMapOptionsInput, type IMaps3DMapOptionsResult, type IMaps3DObjectsLightOptions, type IMaps3DPlacesOptions, type IMaps3DRoadHit, type IMaps3DSelectionStyle, type IMaps3DTrafficOptions, type IMaps3DTransformRequestOptions, type IResponse, type IRoute, type ISearchByLocationParam, type ISearchParam, type ISearchResponse, MahalMap, MahalMapDefaultMarker, type MahalMapDefaultMarkerProps, type MapEngine, type MapLanguage, type MapStyleFamily, type Maps3DBuildingDetail, type Maps3DCtor, type Maps3DLayerId, type Maps3DThemeName, type Maps3DTransformRequest, type MeasureIcons, type MeasureLabels, type MeasureMode, type MeasurePoint, type MeasureShape, type MeasureState, type MeasureStyleOptions, MeasureTool, type MeasureToolOptions, Router, Search, SearchByLocation, SearchPoi, type Theme, index$2 as keyUtils, index$1 as measureUtils, index as utils };
920
+ export { CheckJSApi, type IAdditionalParamType, type ICheckJSApiParam, type ICheckJSApiResponse, type IMahalMapOptions, type IMapMarker, type IMaps3DBuildingClickInfo, type IMaps3DBuildingsController, type IMaps3DBuildingsOptions, type IMaps3DClosuresOptions, type IMaps3DDiagnostics, type IMaps3DIndoorOptions, type IMaps3DLayer, type IMaps3DLayerOptions, type IMaps3DLayerParams, type IMaps3DLayerState, type IMaps3DMapOptionsInput, type IMaps3DMapOptionsResult, type IMaps3DObjectsLightOptions, type IMaps3DPlacesOptions, type IMaps3DRoadHit, type IMaps3DSelectionStyle, type IMaps3DTrafficOptions, type IMaps3DTransformRequestOptions, type IResponse, type IRoute, type ISearchByLocationParam, type ISearchParam, type ISearchResponse, MahalMap, MahalMapDefaultMarker, type MahalMapDefaultMarkerProps, type MapEngine, type MapLanguage, type MapStyleFamily, type Maps3DBuildingDetail, type Maps3DCtor, type Maps3DLayerId, type Maps3DTerrainMode, type Maps3DThemeName, type Maps3DTransformRequest, type MeasureIcons, type MeasureLabels, type MeasureMode, type MeasurePoint, type MeasureShape, type MeasureState, type MeasureStyleOptions, MeasureTool, type MeasureToolOptions, Router, Search, SearchByLocation, SearchPoi, type Theme, index$2 as keyUtils, index$1 as measureUtils, index as utils };