osmgl 0.19.0 → 0.20.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/osmgl.d.cts CHANGED
@@ -400,6 +400,8 @@ declare class Transform {
400
400
  */
401
401
  get projZOffset(): number;
402
402
  get projMatrix(): Mat4;
403
+ /** Мировые пиксели → экран в однородных координатах (x, y, z, w; делить на w). */
404
+ get pixelMatrix(): Mat4;
403
405
  get viewMatrix(): Mat4;
404
406
  /** Центр карты в мировых пикселях. */
405
407
  get centerPointPx(): {
@@ -681,6 +683,18 @@ declare class GestureManager {
681
683
  constructor(canvas: HTMLCanvasElement, tr: Transform, onChange: (reason: 'pan' | 'zoom' | 'rotate' | 'pitch') => void, onInteractionEnd: () => void, options?: Partial<GestureOptions>);
682
684
  setOptions(options: Partial<GestureOptions>): void;
683
685
  private bind;
686
+ /**
687
+ * ЖЕСТЫ С ЭЛЕМЕНТОВ ПОВЕРХ КАНВЫ (маркеры приложения), как у MapLibre (02.10.2026).
688
+ *
689
+ * Маркер — HTML-элемент со своими событиями, и жест, начатый на нём, или колесо над ним до канвы
690
+ * не доходили: карта останавливалась. У MapLibre обработчики висят на контейнере, и события с
691
+ * маркера всплывают к ним. Здесь — те же обработчики на слое маркеров. Щелчок по маркеру без
692
+ * движения остаётся щелчком: канва захватывает указатель только когда он сдвинулся больше 3 px,
693
+ * иначе событие `click` ушло бы с маркера на канву.
694
+ */
695
+ forwardFrom(el: HTMLElement, accepts?: (target: EventTarget | null) => boolean): void;
696
+ /** Указатели, начавшие жест на маркере: канва захватит их, как только они сдвинутся. */
697
+ private uncaptured;
684
698
  private point;
685
699
  private onContextMenu;
686
700
  private onPointerDown;
@@ -832,6 +846,11 @@ interface Capabilities {
832
846
  maxAnisotropy: number;
833
847
  timerQuery: boolean;
834
848
  rendererName: string;
849
+ /**
850
+ * Есть `KHR_parallel_shader_compile`: о готовности программы можно спросить, не дожидаясь её
851
+ * (см. `Shader.ready`). Без расширения такой вопрос сам ждёт конца компиляции.
852
+ */
853
+ parallelCompile: boolean;
835
854
  }
836
855
  type DepthFunc = 'less' | 'lequal' | 'always' | 'greater';
837
856
  type BlendMode = 'none' | 'alpha' | 'premultiplied' | 'add' | 'multiply';
@@ -882,6 +901,8 @@ declare class Context {
882
901
  * закрываются дырки в утопленных поверхностях — острова).
883
902
  */
884
903
  setColorMask(on: boolean): void;
904
+ /** Текущая функция глубины — чтобы проход, сменивший её на время, вернул прежнюю. */
905
+ get depthFunc(): DepthFunc;
885
906
  setDepthFunc(f: DepthFunc): void;
886
907
  setBlend(mode: BlendMode): void;
887
908
  setCull(mode: CullMode): void;
@@ -900,9 +921,20 @@ declare class Context {
900
921
  declare class Shader {
901
922
  private ctx;
902
923
  readonly name: string;
903
- readonly program: WebGLProgram;
924
+ private readonly _program;
904
925
  private uniforms;
905
926
  private attribs;
927
+ /**
928
+ * Сборка ещё не проверена: шейдеры лежат здесь до первого обращения к программе.
929
+ *
930
+ * ★ ПРОВЕРКА ОТЛОЖЕНА НАМЕРЕННО (03.10.2026). Конструктор раньше сразу спрашивал
931
+ * `COMPILE_STATUS`/`LINK_STATUS`, а этот вопрос ЖДЁТ конца компиляции в главном потоке. Программ
932
+ * при создании карты два десятка, и они собирались строго по очереди: 24 ожидания на 0.8–0.9 с
933
+ * одной задачей (замер: холодный профиль Chromium, D3D11). Теперь все программы сначала
934
+ * отправляются компилятору, и он собирает их в своих потоках одновременно, а ждём мы только ту, что
935
+ * понадобилась первой (`finish`), — к тому моменту остальные уже в работе или готовы.
936
+ */
937
+ private pending;
906
938
  constructor(ctx: Context, name: string, vertexSource: string, fragmentSource: string, defines?: Record<string, string | number | boolean>,
907
939
  /**
908
940
  * Места атрибутов, которые надо повторить, — у ВАРИАНТА программы (см. surfaceShader): VAO
@@ -910,6 +942,18 @@ declare class Shader {
910
942
  * (ATTRIB_SLOTS) тут не годится: атрибутов больше, чем гарантированных шестнадцати мест.
911
943
  */
912
944
  bind?: ReadonlyMap<string, number>);
945
+ /** Программа GL. Первое обращение дожидается сборки (см. `pending`). */
946
+ get program(): WebGLProgram;
947
+ /** Сборка не удалась: каждое следующее обращение бросает ту же ошибку, а не рисует удалённой программой. */
948
+ private failure;
949
+ /**
950
+ * Собрана ли программа — НЕ дожидаясь её, если драйвер это умеет (`KHR_parallel_shader_compile`).
951
+ * Без расширения честного неблокирующего ответа нет, и программа считается готовой: тогда первое
952
+ * обращение просто подождёт, как и раньше.
953
+ */
954
+ get ready(): boolean;
955
+ /** Дождаться сборки, проверить её и разобрать места юниформов и атрибутов. */
956
+ private finish;
913
957
  private cacheLocations;
914
958
  use(): void;
915
959
  attribLocation(name: string): number;
@@ -1063,6 +1107,8 @@ interface LayerBase {
1063
1107
  overlay?: boolean | 'roads' | 'volume';
1064
1108
  /** Выключить слой, не удаляя из стиля. */
1065
1109
  visible?: boolean;
1110
+ /** Свободные данные приложения у слоя (`metadata` слоя MapLibre). Движок их не читает. */
1111
+ metadata?: Record<string, unknown>;
1066
1112
  /**
1067
1113
  * Радиус скругления углов в МЕТРАХ (заливки и объём).
1068
1114
  *
@@ -1075,6 +1121,25 @@ interface LayerBase {
1075
1121
  * из соседних тайлов сошлись бы со ступенькой.
1076
1122
  */
1077
1123
  cornerRadius?: number;
1124
+ /**
1125
+ * Крыши только плоские — как у `fill-extrusion` MapLibre. По умолчанию невысокий дом простой
1126
+ * формы получает скатную крышу (по тегу `roof_shape` или по размеру); стилю, пришедшему из
1127
+ * MapLibre, это чужой облик. Геометрия — поэтому поле рядом с фильтром, а не в `paint`.
1128
+ */
1129
+ flatRoofs?: boolean;
1130
+ /**
1131
+ * СВЕТ ОБЪЁМА ПО МОДЕЛИ MapLibre (`'maplibre'`) — у `fill-extrusion`, пришедшего из стиля MapLibre.
1132
+ *
1133
+ * Цвет вершины считается ровно формулой `fill_extrusion.vertex.glsl`: яркость цвета, подсветка
1134
+ * 0.03, один направленный источник из `StyleSpec.mapLibreLight`, градиент стены по высоте. Наш
1135
+ * свет, затенение у основания, тени, дымка, стекло и разброс тона такой слой не получает вовсе —
1136
+ * у MapLibre их нет, и «похожий» облик был бы другим цветом каждой грани.
1137
+ *
1138
+ * Поле не только покраска, но и ГЕОМЕТРИЯ: нормали стен у такого слоя плоские на каждое ребро, как
1139
+ * у MapLibre (`fill_extrusion_bucket`), без нашего сглаживания на пологих изломах. Поэтому оно
1140
+ * лежит рядом с фильтром и едет в план воркера.
1141
+ */
1142
+ lighting?: 'maplibre';
1078
1143
  /**
1079
1144
  * Обрезать заливку по квадрату тайла.
1080
1145
  *
@@ -1133,6 +1198,8 @@ interface RasterLayerSpec {
1133
1198
  minzoom?: number;
1134
1199
  maxzoom?: number;
1135
1200
  visible?: boolean;
1201
+ /** Свободные данные приложения у слоя (`metadata` слоя MapLibre). Движок их не читает. */
1202
+ metadata?: Record<string, unknown>;
1136
1203
  /**
1137
1204
  * Рисовать ПОВЕРХ векторной карты, а не под ней.
1138
1205
  *
@@ -1148,7 +1215,8 @@ interface RasterLayerSpec {
1148
1215
  interface FillLayerSpec extends LayerBase {
1149
1216
  type: 'fill';
1150
1217
  paint: MaterialPaint & {
1151
- color: Interpolated<string>;
1218
+ /** Цвет заливки; `{ from, property }` — «как у такого-то слоя» (плоские здания берут `roofColor`). */
1219
+ color: Interpolated<string> | ColorFrom;
1152
1220
  opacity?: Interpolated<number>;
1153
1221
  };
1154
1222
  }
@@ -1254,6 +1322,23 @@ interface LineLayerSpec extends LayerBase {
1254
1322
  */
1255
1323
  widthExtra?: Interpolated<number>;
1256
1324
  dashArray?: [number, number];
1325
+ /**
1326
+ * Пунктир В ШИРИНАХ ЛИНИИ — как `line-dasharray` у MapLibre: [штрих, пробел] множителями
1327
+ * текущей ширины. Толще линия — длиннее штрих, поэтому пунктир на любом зуме одинаков по виду.
1328
+ */
1329
+ dashWidths?: [number, number];
1330
+ /**
1331
+ * Зазор посередине линии в пикселях — `line-gap-width` у MapLibre. Ненулевой превращает линию
1332
+ * в «трубу»: две полосы шириной `width` по краям зазора. Так рисуются обводки мостов и тоннелей.
1333
+ */
1334
+ gapWidth?: Interpolated<number>;
1335
+ /**
1336
+ * Концы и стыки линии — `line-cap`, `line-join`, `line-miter-limit` MapLibre. Без них — прежняя
1337
+ * капсула (скругление везде). Заданные включают свой вариант программы и данные стыков в вершине.
1338
+ */
1339
+ lineCap?: 'butt' | 'round' | 'square';
1340
+ lineJoin?: 'bevel' | 'round' | 'miter';
1341
+ miterLimit?: number;
1257
1342
  /**
1258
1343
  * ШИРИНА В МЕТРАХ вместо пикселей — для РАЗМЕТКИ.
1259
1344
  *
@@ -1314,7 +1399,14 @@ interface LineLayerSpec extends LayerBase {
1314
1399
  interface FillExtrusionLayerSpec extends LayerBase {
1315
1400
  type: 'fill-extrusion';
1316
1401
  paint: {
1317
- color: Interpolated<string>;
1402
+ /** Цвет стен; `{ from }` — «как у такого-то слоя» (так красятся этажи под цвет дома). */
1403
+ color: Interpolated<string> | ColorFrom;
1404
+ /**
1405
+ * Высота и низ стены в метрах — как `fill-extrusion-height/base` у MapLibre: число или
1406
+ * выражение по фиче. Не заданы — берутся из свойств тайла (`render_height`, `height`, этажи).
1407
+ */
1408
+ height?: unknown;
1409
+ base?: unknown;
1318
1410
  roofColor?: Interpolated<string>;
1319
1411
  opacity?: Interpolated<number>;
1320
1412
  /** Затенение у основания, 0..1. */
@@ -1421,6 +1513,11 @@ interface FillExtrusionLayerSpec extends LayerBase {
1421
1513
  roofVariation?: Interpolated<number>;
1422
1514
  /** Множитель высоты (например, 0 — «сложить» здания). */
1423
1515
  heightScale?: Interpolated<number>;
1516
+ /**
1517
+ * Градиент стены по высоте — `fill-extrusion-vertical-gradient` MapLibre. Работает только со
1518
+ * светом MapLibre (`lighting: 'maplibre'`); не задан — включён, как и у MapLibre.
1519
+ */
1520
+ verticalGradient?: boolean;
1424
1521
  };
1425
1522
  }
1426
1523
  /**
@@ -1431,6 +1528,8 @@ interface FillExtrusionLayerSpec extends LayerBase {
1431
1528
  * иконки силуэтные (одноцветные): раскрасить пиктограмму в несколько цветов
1432
1529
  * таким способом нельзя.
1433
1530
  */
1531
+ /** Якорь подписи или значка — какая точка бокса стоит на объекте (`text-anchor` у MapLibre). */
1532
+ type SymbolAnchor = 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
1434
1533
  interface SymbolLayerSpec extends LayerBase {
1435
1534
  type: 'symbol';
1436
1535
  layout?: {
@@ -1447,6 +1546,16 @@ interface SymbolLayerSpec extends LayerBase {
1447
1546
  * занимает до половины строки. Так подписывают улицы Яндекс и 2ГИС — см. `text/abbreviate`.
1448
1547
  */
1449
1548
  abbreviate?: boolean;
1549
+ /**
1550
+ * Регистр подписи — как `text-transform` у MapLibre. Меняется в воркере вместе с сокращением:
1551
+ * под этот текст раскладка и ищет место.
1552
+ */
1553
+ textTransform?: 'none' | 'uppercase' | 'lowercase';
1554
+ /**
1555
+ * Гарнитура подписи — имя набора глифов на сервере шрифтов, как `text-font` у MapLibre
1556
+ * (`Noto Sans Bold`). Не задана — общий шрифт карты (`Noto Sans Regular`).
1557
+ */
1558
+ textFont?: string;
1450
1559
  /**
1451
1560
  * Разрядка букв в долях кегля — как `text-letter-spacing` у Mapbox.
1452
1561
  *
@@ -1483,6 +1592,15 @@ interface SymbolLayerSpec extends LayerBase {
1483
1592
  * приклеенным сбоку.
1484
1593
  */
1485
1594
  textPlacement?: 'point' | 'line';
1595
+ /**
1596
+ * Как подпись вдоль линии стоит относительно наклона карты.
1597
+ *
1598
+ * `viewport` (по умолчанию) — буквы обращены к экрану и при наклоне не сжимаются: читается
1599
+ * лучше всего. `map` — буквы ЛЕЖАТ НА ДОРОГЕ и в наклоне сжимаются в перспективе вместе с ней,
1600
+ * как `text-pitch-alignment: map` у MapLibre (у `symbol-placement: line` это его умолчание).
1601
+ * Только для `textPlacement: 'line'`.
1602
+ */
1603
+ textPitchAlignment?: 'viewport' | 'map';
1486
1604
  /**
1487
1605
  * Шаг повторения подписи вдоль линии, экранные пиксели (`textPlacement:
1488
1606
  * 'line'`). По умолчанию 250 — как `symbol-spacing` у Mapbox.
@@ -1491,11 +1609,63 @@ interface SymbolLayerSpec extends LayerBase {
1491
1609
  * раз, находится только в одном месте, и на соседнем экране та же улица
1492
1610
  * снова безымянная. Ноль — ставить ровно один раз.
1493
1611
  */
1494
- symbolSpacing?: number;
1612
+ symbolSpacing?: Interpolated<number>;
1495
1613
  /** Смещение подписи от точки привязки, CSS-пиксели. */
1496
1614
  offset?: [number, number];
1497
1615
  /** Максимум символов в строке до переноса. */
1498
1616
  maxLineChars?: number;
1617
+ /**
1618
+ * РАСКЛАДКА ПО ЯКОРЯМ, КАК У MapLibre. Заданный `textAnchor` или `iconAnchor` включает её вместо
1619
+ * нашей блочной (`iconPlacement`): значок и подпись ставятся НЕЗАВИСИМО — каждый своим якорем к
1620
+ * точке объекта, подпись ещё и со смещением `textOffset` в em (кеглях). Так устроены все стили
1621
+ * MapLibre: значок POI стоит центром на объекте, подпись — верхним краем под ним.
1622
+ */
1623
+ textAnchor?: SymbolAnchor;
1624
+ /** Смещение подписи от точки в em (× `textSize`), `text-offset` у MapLibre. */
1625
+ textOffset?: [number, number];
1626
+ iconAnchor?: SymbolAnchor;
1627
+ /**
1628
+ * Размер значка МНОЖИТЕЛЕМ его натурального размера — `icon-size` у MapLibre. Натуральный —
1629
+ * размер картинки спрайта в CSS-пикселях (пиксели / `pixelRatio`). Задан — `iconSize` не читается.
1630
+ */
1631
+ iconScale?: Interpolated<number>;
1632
+ /** Перенос строк по ширине в em — `text-max-width` у MapLibre (по умолчанию у них 10). */
1633
+ textMaxWidth?: Interpolated<number>;
1634
+ /**
1635
+ * Вдоль линии (`textPlacement: 'line'`) подпись ставится НЕ изгибом по дороге, а горизонтально
1636
+ * в узлах сетки — как щит трассы у MapLibre (`text-rotation-alignment: viewport`).
1637
+ */
1638
+ lineUpright?: boolean;
1639
+ /** Значок вдоль линии повёрнут по дороге (`icon-rotation-alignment: map`) — стрелка односторонней. */
1640
+ iconAlongLine?: boolean;
1641
+ /**
1642
+ * Значок ЛЕЖИТ на карте (`icon-pitch-alignment: map`): при наклоне сплющивается вместе с землёй.
1643
+ * Пока — у значков вдоль линии без подписи (стрелки направления).
1644
+ */
1645
+ iconPitchAlignment?: 'map' | 'viewport';
1646
+ /**
1647
+ * Места вдоль линии (`textPlacement: 'line'`, значки и подписи «стоя») — по алгоритму MapLibre:
1648
+ * в координатах тайла, с переувеличением и срезом по тайлу (`text/maplibre-anchors.ts`). Без поля —
1649
+ * наша сетка по земле. Ставит импортёр стилей MapLibre.
1650
+ */
1651
+ lineAnchors?: 'maplibre';
1652
+ /**
1653
+ * Раскладка коллизий КАК У MapLibre: порядок — верхний слой первым, тайлы по их правилу, внутри
1654
+ * тайла по порядку символов, без надбавки «уже стоявшим»; подпись вдоль линии занимает место
1655
+ * цепочкой кругов. Ставит импортёр стилей MapLibre.
1656
+ */
1657
+ placementMode?: 'maplibre';
1658
+ /** Одно место в середине линии (`symbol-placement: line-center`); только с `lineAnchors`. */
1659
+ lineCenter?: boolean;
1660
+ /** Наибольший излом под подписью вдоль линии, градусы (`text-max-angle`, по умолчанию 45). */
1661
+ textMaxAngle?: Interpolated<number>;
1662
+ /**
1663
+ * Что делать с незнакомым именем значка: `dot` (по умолчанию) — нейтральная точка, чтобы объект
1664
+ * не пропал; `skip` — не рисовать значок вовсе, как MapLibre (подпись при этом остаётся).
1665
+ */
1666
+ iconMissing?: 'dot' | 'skip';
1667
+ /** Значок — из встроенного набора движка, даже при `iconMissing: 'skip'` (круги слоя `circle`). */
1668
+ iconBuiltin?: boolean;
1499
1669
  /** Не участвовать в коллизиях (подпись всегда видна). */
1500
1670
  allowOverlap?: boolean;
1501
1671
  /**
@@ -1564,7 +1734,7 @@ interface SymbolLayerSpec extends LayerBase {
1564
1734
  * Значок доворачивается на поворот камеры, поэтому стрелка у входа смотрит
1565
1735
  * внутрь дома при любом развороте карты.
1566
1736
  */
1567
- iconRotate?: string;
1737
+ iconRotate?: string | number | unknown[];
1568
1738
  /**
1569
1739
  * Показывать слой ТОЛЬКО у выбранного здания (`highlight`).
1570
1740
  *
@@ -1634,6 +1804,10 @@ interface SymbolLayerSpec extends LayerBase {
1634
1804
  haloColor?: Interpolated<string> | ColorFrom;
1635
1805
  /** Толщина обводки в CSS-пикселях. Обводка обязательна поверх пёстрой карты. */
1636
1806
  haloWidth?: Interpolated<number>;
1807
+ /** Размытие обводки текста в пикселях — `text-halo-blur` MapLibre. */
1808
+ haloBlur?: Interpolated<number>;
1809
+ /** Размытие обводки значка — `icon-halo-blur`. */
1810
+ iconHaloBlur?: Interpolated<number>;
1637
1811
  /**
1638
1812
  * Свой цвет значка — иначе он красится цветом подписи (одна программа на
1639
1813
  * текст и иконки). Нужен плашкам: щит номера трассы это светлая заливка с
@@ -2075,6 +2249,8 @@ interface StyleSpec {
2075
2249
  */
2076
2250
  levelHeight?: number;
2077
2251
  light?: Partial<LightSpec>;
2252
+ /** Свет объёма MapLibre — корневой `light` стиля MapLibre (см. `MapLibreLightSpec`). */
2253
+ mapLibreLight?: Partial<MapLibreLightSpec>;
2078
2254
  /** Небо и дымка. */
2079
2255
  sky?: Partial<SkySpec>;
2080
2256
  /** Дополнительные источники тайлов помимо `base` (его задаёт карта опцией `tiles`). */
@@ -2088,8 +2264,52 @@ interface StyleSpec {
2088
2264
  * Менеджер моделей читает его отсюда после каждого `setStyle` и сам решает, что перезагрузить.
2089
2265
  */
2090
2266
  model3dKey?: string;
2267
+ /**
2268
+ * Свободные данные приложения — как `metadata` у MapLibre. Движок их не читает; хранит редактор
2269
+ * стилей (своё состояние) и продукт (`maps3d:*` — облик SDK). Объявлено, чтобы проверка стиля не
2270
+ * считала поле опечаткой.
2271
+ */
2272
+ metadata?: Record<string, unknown>;
2273
+ /**
2274
+ * ШРИФТЫ СТИЛЯ — шаблон адреса SDF-глифов `…/{fontstack}/{range}.pbf`, ровно как `glyphs` у MapLibre.
2275
+ *
2276
+ * Нет поля — шрифты карты (опция `fonts`, `setGlyphs`). Есть — стиль ставит свои на время, пока он
2277
+ * стоит, а стиль без них возвращает карте её собственные. Так стиль, сохранённый редактором или
2278
+ * переведённый из MapLibre, не теряет шрифтов, с которыми его рисовали.
2279
+ */
2280
+ glyphs?: string;
2281
+ /**
2282
+ * СПРАЙТ СТИЛЯ — адрес без расширения (`…/sprite`, к нему дописываются `.json`/`.png`, а на
2283
+ * плотном экране `@2x`) или список `{ id, url }` — форма та же, что у `sprite` MapLibre, чтобы
2284
+ * редактор и импортёр переносили её как есть. Значки дополнительного спрайта зовутся `id:имя`;
2285
+ * `id: 'default'` — основной, без приставки.
2286
+ *
2287
+ * Нет поля — значки карты (встроенные, `setSprite`/`addSprite`) не трогаются. Есть — правило то же,
2288
+ * что у `glyphs`: на время стиля его спрайт, после — снова карты.
2289
+ */
2290
+ sprite?: string | {
2291
+ id: string;
2292
+ url: string;
2293
+ }[];
2091
2294
  layers: StyleLayerSpec[];
2092
2295
  }
2296
+ /**
2297
+ * СВЕТ ОБЪЁМА MapLibre — корневой `light` стиля MapLibre, как его читает `fill_extrusion_program`.
2298
+ *
2299
+ * Свой свет (`LightSpec`) им не заменить: у MapLibre другая модель — ОДИН источник, яркость по
2300
+ * формуле от светлоты самого цвета и нижняя граница в оттенок света. Светит он только слоям с
2301
+ * `lighting: 'maplibre'`; наши темы его не видят.
2302
+ */
2303
+ interface MapLibreLightSpec {
2304
+ /** `viewport` — источник привязан к экрану и поворачивается вместе с курсом камеры; `map` — к карте. */
2305
+ anchor: 'map' | 'viewport';
2306
+ /** `[r, азимут, полярный угол]` в градусах, как в спецификации: азимут 0 — верх (север у `map`). */
2307
+ position: [number, number, number];
2308
+ color: Interpolated<string>;
2309
+ intensity: Interpolated<number>;
2310
+ }
2311
+ /** Умолчания спецификации MapLibre (`light` в `v8.json`). */
2312
+ declare const DEFAULT_MAPLIBRE_LIGHT: MapLibreLightSpec;
2093
2313
  /**
2094
2314
  * Свет по умолчанию. Азимуты источников намеренно НЕ диагональные (120/300,
2095
2315
  * а не 135/315): на диагонали пары стен «восток/юг» и «запад/север» получают
@@ -2158,6 +2378,12 @@ interface LayerPlan {
2158
2378
  /** Скругление углов в метрах — геометрия, поэтому едет в воркер. */
2159
2379
  cornerRadius?: number;
2160
2380
  cornerSegments?: number;
2381
+ /** Линии: данные стыков в вершине (`lineCap`/`lineJoin` заданы) — см. `generators/line`. */
2382
+ joinData?: boolean;
2383
+ /** Только плоские крыши (`fill-extrusion` из стиля MapLibre). */
2384
+ flatRoofs?: boolean;
2385
+ /** Плоские нормали стен на каждое ребро, без сглаживания изломов (свет MapLibre). */
2386
+ flatNormals?: boolean;
2161
2387
  /** Резать заливку по тайлу — для полупрозрачных слоёв. */
2162
2388
  clipToTile?: boolean;
2163
2389
  /** Рельеф (тип `surface`): уровень и форма края, тоже геометрия. */
@@ -2198,14 +2424,23 @@ interface LayerPlan {
2198
2424
  /** Покраска на фиче: выражения, которые воркер посчитает и положит в вершину. */
2199
2425
  dataPaint?: DataPaintSpec;
2200
2426
  /** Только для symbol: шаблоны подставляются уже в воркере, чтобы не гонять свойства. */
2201
- textField?: string | string[];
2202
- iconImage?: string | string[];
2203
- /** Свойство фичи с поворотом значка (стрелка входа смотрит внутрь дома). */
2204
- iconRotate?: string;
2427
+ textField?: string | unknown[];
2428
+ iconImage?: string | unknown[];
2429
+ /** Только для fill-extrusion: высота и низ стены выражением (метры), считаются на фиче. */
2430
+ extrusionHeight?: unknown;
2431
+ extrusionBase?: unknown;
2432
+ /** Свойство фичи с поворотом значка (стрелка входа смотрит внутрь дома) или выражение. */
2433
+ iconRotate?: string | number | unknown[];
2205
2434
  /** `line` — подпись изгибается по дороге: воркер тогда кладёт и геометрию линии. */
2206
2435
  textPlacement?: 'point' | 'line';
2207
2436
  /** Сокращать родовое слово в названии («улица» → «ул.») — см. `text/abbreviate`. */
2208
2437
  abbreviate?: boolean;
2438
+ /** Вдоль линии — ВСЕ части составной линии, а не самая длинная (стиль MapLibre). */
2439
+ allLineParts?: boolean;
2440
+ /** Не склеивать куски линий с одной подписью — `line-center` у MapLibre (склеивает только `line`). */
2441
+ noLineMerge?: boolean;
2442
+ /** Регистр подписи (`text-transform`) — меняется в воркере вместе с сокращением. */
2443
+ textTransform?: 'none' | 'uppercase' | 'lowercase';
2209
2444
  }
2210
2445
  /** Источник по умолчанию: основная карта. */
2211
2446
  declare const BASE_SOURCE = "base";
@@ -2420,6 +2655,30 @@ interface BuildingsOptions {
2420
2655
  metersPerUnit?: number;
2421
2656
  /** Покраска на фиче: цвет и прозрачность считаются здесь и ложатся в вершины. */
2422
2657
  paint?: FeaturePaint;
2658
+ /** Только плоские крыши (`fill-extrusion` MapLibre). */
2659
+ flatRoofs?: boolean;
2660
+ /**
2661
+ * Нормаль стены — нормаль СВОЕГО ребра, без сглаживания на пологих изломах: так их строит
2662
+ * `fill_extrusion_bucket` MapLibre, и свет MapLibre (`lighting: 'maplibre'`) ждёт ровно их.
2663
+ */
2664
+ flatNormals?: boolean;
2665
+ /** Высота и низ стены выражением MapLibre (метры); без них — свойства тайла. */
2666
+ height?: unknown;
2667
+ base?: unknown;
2668
+ /** Зум тайла — для `['zoom']` в этих выражениях и в фильтре. */
2669
+ zoom?: number;
2670
+ /**
2671
+ * Как резать дом по тайлу (02.10.2026).
2672
+ *
2673
+ * `'square'` (по умолчанию) — по квадрату тайла с нахлёстом `bleedRing`: так режется наш облик, у
2674
+ * которого куски потом сшиваются.
2675
+ *
2676
+ * `'buffer'` — как MapLibre: БЛИЖНИЕ стороны (0) не режутся, кусок за ними — исходная геометрия
2677
+ * буфера; ДАЛЬНИЕ (extent) режутся по пределу упаковки, внутрь. Нужно `fill-extrusion` из стиля
2678
+ * MapLibre: сшивки у него нет, и стена, пересекающая стык, должна остаться ОДНОЙ прямой.
2679
+ * Подробно — у `addBuilding`. Скругление углов с этим режимом не сочетается и его отключает.
2680
+ */
2681
+ tileClip?: 'square' | 'buffer';
2423
2682
  }
2424
2683
  declare function generateBuildings(layer: VectorTileLayer, extent: number, filter?: Filter, options?: BuildingsOptions,
2425
2684
  /**
@@ -2449,6 +2708,145 @@ interface BuildingSeam {
2449
2708
  holeCuts: boolean[][];
2450
2709
  }
2451
2710
 
2711
+ /**
2712
+ * ВЫРАЖЕНИЯ СТИЛЯ MapLibre.
2713
+ *
2714
+ * Стиль, написанный под MapLibre, почти целиком состоит из них: ширина дороги — `interpolate` по
2715
+ * зуму, цвет — `match` по классу, видимость — `case`. Без интерпретатора такой стиль у нас не
2716
+ * работает вовсе, а переписывать его на наши упрощённые формы (`stops`) для каждого приложения —
2717
+ * это ровно та работа, которой перенос и должен избавить.
2718
+ *
2719
+ * ★ ИНТЕРПРЕТАТОР, А НЕ КОМПИЛЯТОР.
2720
+ *
2721
+ * MapLibre компилирует выражение в замыкания один раз и потом зовёт их. Мы считаем прямо по
2722
+ * массиву, но КЕШИРУЕМ разбор (`compile`) — на зумовых свойствах это вызов на слой за кадр, и
2723
+ * разница между подходами там неизмерима. На фильтрах, где вызовов десятки тысяч на тайл, кеш
2724
+ * решает: разобранное выражение переиспользуется для каждой фичи.
2725
+ *
2726
+ * ★ ЧТО УМЕЕТ, А ЧТО НЕТ.
2727
+ *
2728
+ * Умеет всё, что встречается в стилях: данные (`get`, `has`, `id`, `geometry-type`,
2729
+ * `feature-state`, `global-state`), решения (`case`, `match`, `coalesce`, логика, сравнения),
2730
+ * шкалы (`interpolate` линейный, экспоненциальный и по кривой Безье, `step`), арифметику,
2731
+ * строки, цвета, переменные (`let`/`var`), массивы (`at`, `length`, `slice`, `in`).
2732
+ *
2733
+ * Не умеет: `format` (богатый текст с разными шрифтами в одной подписи — у нас подпись
2734
+ * однородная), `image` в значении «встроить картинку в текст», `distance`, `within` для линий и
2735
+ * полигонов (точка проверяется, остальное — нет), `resolved-locale`. Всё перечисленное отмечено и
2736
+ * в `porting/maplibre-api.md`.
2737
+ */
2738
+ /** Значение выражения: то, чем оперирует язык. */
2739
+ type ExprValue = number | string | boolean | null | ExprValue[] | RGBAColor;
2740
+ /** Цвет как значение языка: четыре канала 0..1, как их отдаёт `parseColor`. */
2741
+ interface RGBAColor {
2742
+ r: number;
2743
+ g: number;
2744
+ b: number;
2745
+ a: number;
2746
+ }
2747
+ /** Всё, что выражение может спросить об окружении. */
2748
+ interface ExprContext {
2749
+ zoom?: number;
2750
+ /** Свойства фичи (для фильтров и data-driven значений). */
2751
+ properties?: Record<string, unknown>;
2752
+ /** Номер фичи: `['id']`. */
2753
+ id?: number | string | null;
2754
+ /** Тип геометрии: `Point`, `LineString`, `Polygon`. */
2755
+ geometryType?: string;
2756
+ /** Состояние фичи (`map.setFeatureState`). */
2757
+ featureState?: Record<string, unknown>;
2758
+ /** Общее состояние стиля (`map.setGlobalStateProperty`). */
2759
+ globalState?: Record<string, unknown>;
2760
+ /** Точка фичи в координатах карты — нужна `within` для точечных фич. */
2761
+ point?: [number, number];
2762
+ }
2763
+ /**
2764
+ * ОБЩЕЕ СОСТОЯНИЕ СТИЛЯ (`map.setGlobalStateProperty`).
2765
+ *
2766
+ * Лежит модульной переменной, а не в карте: значения стиля считаются в десятках мест, и тащить
2767
+ * через все них ещё один аргумент ради редкого случая значит переписать половину сигнатур. Карта
2768
+ * на странице одна — то же допущение, что и у MapLibre, где состояние висит на стиле.
2769
+ *
2770
+ * ★ У ВОРКЕРА СВОЙ ЭКЗЕМПЛЯР МОДУЛЯ. Раньше состояние туда не доходило вовсе, и фильтры, значения по
2771
+ * фиче и подписи (всё, что считается в воркере) видели пустое состояние. Теперь оно едет с каждым
2772
+ * запросом тайла (`LoadRequest.globalState`) и ставится `replaceGlobalState` перед разбором.
2773
+ */
2774
+ declare const GLOBAL_STATE: Record<string, unknown>;
2775
+ /** Заменить состояние целиком — воркеру перед разбором тайла. */
2776
+ declare function replaceGlobalState(state: Record<string, unknown>): void;
2777
+ /** Похоже ли значение на выражение: массив, первый элемент которого — имя операции. */
2778
+ declare function isExpression(value: unknown): value is ExprValue[];
2779
+ /**
2780
+ * Посчитать выражение.
2781
+ *
2782
+ * Разбор кешируется на самом массиве: стиль живёт долго, а значения из него берут каждый кадр.
2783
+ */
2784
+ declare function evaluateExpression(expr: unknown, ctx: ExprContext): ExprValue;
2785
+ /** Число из выражения; `fallback`, если получилось не число. */
2786
+ declare function expressionNumber(expr: unknown, ctx: ExprContext, fallback: number): number;
2787
+ /** Цвет из выражения в наш формат `[r, g, b, a]`. */
2788
+ declare function expressionColor(expr: unknown, ctx: ExprContext): [number, number, number, number] | null;
2789
+ /**
2790
+ * ОПЕРАЦИИ, КОТОРЫЕ ПОНИМАЕТ `compile`. Ровно список его `case`, ни одной больше.
2791
+ *
2792
+ * Нужен проверке стиля (`style/validate.ts`): сам `compile` о незнакомой операции молчит до
2793
+ * вычисления (см. его ветку `default`), а редактор обязан сказать о ней сразу, на вводе. Что список
2794
+ * не разошёлся с `switch`, сверяет тест (`tests/style-reference.test.mjs`) — по исходнику.
2795
+ */
2796
+ declare const EXPRESSION_OPERATORS: ReadonlySet<string>;
2797
+ /** Замечание к выражению: где (путь внутри него) и что не так. */
2798
+ interface ExpressionIssue {
2799
+ /** Путь от корня выражения: `[2][1]` — второй аргумент первого аргумента. Пусто — само выражение. */
2800
+ path: string;
2801
+ message: string;
2802
+ }
2803
+ /**
2804
+ * ЧТО В ВЫРАЖЕНИИ НЕ ТАК — статически, без вычисления.
2805
+ *
2806
+ * Идёт по тем же правилам разбора, что и `compile`: где у операции стоит не выражение, а ИМЯ или
2807
+ * ЗНАЧЕНИЕ (вид кривой у `interpolate`, метки у `match`, имена у `let`, содержимое `literal`), туда
2808
+ * не заходит — иначе `['linear']` и `['a', 'b']`-метка выглядели бы незнакомыми операциями.
2809
+ *
2810
+ * Проверяет то, на чём движок молча выдал бы пустое значение: незнакомую операцию, остановки шкалы
2811
+ * не числом или не по возрастанию, нечётные пары, имя переменной не строкой. Типы аргументов не
2812
+ * выводит: интерпретатор приводит их сам (`to-number` и т.п.), и ругань на каждое приведение была бы
2813
+ * шумом.
2814
+ */
2815
+ declare function expressionIssues(expr: unknown, path?: string): ExpressionIssue[];
2816
+
2817
+ type PropBag = Record<string, string | number | boolean | null>;
2818
+ /** Окружение фичи для фильтра: номер, тип геометрии, зум тайла, состояние. */
2819
+ interface FilterContext {
2820
+ id?: number | string | null;
2821
+ geometryType?: string;
2822
+ zoom?: number;
2823
+ featureState?: Record<string, unknown>;
2824
+ }
2825
+ /** Окружение фичи тайла — то, что фильтр знает о ней помимо свойств. */
2826
+ declare function featureContext(feature: {
2827
+ id?: number;
2828
+ type: number;
2829
+ }, zoom?: number): FilterContext;
2830
+ /**
2831
+ * Значение стиля на конкретном зуме.
2832
+ *
2833
+ * Между остановками интерполируем ЛИНЕЙНО по зуму, а не экспоненциально: для
2834
+ * ширин дорог и прозрачностей так предсказуемее, а «экспоненту» при желании
2835
+ * задают частыми остановками.
2836
+ */
2837
+ declare function evaluateNumber(v: Interpolated<number> | undefined, zoom: number, fallback: number): number;
2838
+
2839
+ type RGBA = [number, number, number, number];
2840
+ declare function evaluateColor(v: Interpolated<string> | undefined, zoom: number, fallback: RGBA): RGBA;
2841
+ /**
2842
+ * Проверка фильтра. Отсутствующее свойство никогда не равно значению
2843
+ * (кроме '!=' и '!in', где отсутствие считается несовпадением, — так же
2844
+ * ведёт себя MapLibre).
2845
+ */
2846
+ declare function matchesFilter(filter: Filter | undefined, props: PropBag,
2847
+ /** Остальное окружение фичи: номер, тип геометрии, состояние. Нужно выражениям. */
2848
+ ctx?: FilterContext): boolean;
2849
+
2452
2850
  /**
2453
2851
  * Сбор подписей из тайла.
2454
2852
  *
@@ -2497,16 +2895,28 @@ interface SymbolPlan {
2497
2895
  filter?: Filter;
2498
2896
  /** `line` — подпись изгибается по дороге; иначе стоит точкой у якоря. */
2499
2897
  textPlacement?: 'point' | 'line';
2500
- /** Шаблон подписи или список запасных: первый непустой побеждает. */
2501
- textField?: string | string[];
2502
- /** Шаблон имени иконки или список запасных. */
2503
- iconImage?: string | string[];
2504
- /** Свойство фичи с поворотом значка в градусах. */
2505
- iconRotate?: string;
2898
+ /** Шаблон подписи, список запасных (первый непустой побеждает) или выражение MapLibre. */
2899
+ textField?: string | unknown[];
2900
+ /** Шаблон имени иконки, список запасных или выражение. */
2901
+ iconImage?: string | unknown[];
2902
+ /** Свойство фичи с поворотом значка в градусах, число или выражение. */
2903
+ iconRotate?: string | number | unknown[];
2904
+ /** Зум тайла: его спрашивают `['zoom']` в выражениях и фильтры. */
2905
+ zoom?: number;
2506
2906
  /** Сокращать родовое слово в названии («улица» → «ул.») — см. `text/abbreviate`. */
2507
2907
  abbreviate?: boolean;
2908
+ /** Вдоль линии — все части составной линии, как MapLibre (у нас по умолчанию — самая длинная). */
2909
+ allLineParts?: boolean;
2910
+ /** Не склеивать куски линий (MapLibre склеивает только при `symbol-placement: line`). */
2911
+ noLineMerge?: boolean;
2912
+ /** Регистр подписи, как `text-transform` у MapLibre. */
2913
+ textTransform?: 'none' | 'uppercase' | 'lowercase';
2508
2914
  }
2509
2915
  declare function generateSymbols(layer: VectorTileLayer, plan: SymbolPlan): SymbolBucketData | null;
2916
+ /** Подпись или имя значка у фичи: шаблон, список запасных или выражение. */
2917
+ declare function resolveSymbolField(field: string | unknown[], props: Record<string, PropValue>, ctx: FilterContext): string;
2918
+ /** Поворот значка в градусах: имя свойства (наш формат), число или выражение (MapLibre). */
2919
+ declare function iconRotateOf(rotate: string | number | unknown[] | undefined, props: Record<string, PropValue>, ctx: FilterContext): number;
2510
2920
  /**
2511
2921
  * Подстановка `{prop}` в шаблон. Список шаблонов — это фолбэки: первый,
2512
2922
  * который раскрылся целиком, побеждает. Так `["{name:ru}", "{name}"]` даёт
@@ -2614,6 +3024,11 @@ interface LoadRequest {
2614
3024
  };
2615
3025
  /** Что строить из этого тайла — план из стиля. */
2616
3026
  plan: LayerPlan[];
3027
+ /**
3028
+ * Общее состояние стиля (`setGlobalStateProperty`) на момент запроса: у воркера свой экземпляр
3029
+ * модуля, и без этого фильтры и значения по фиче видели бы пустое состояние.
3030
+ */
3031
+ globalState?: Record<string, unknown>;
2617
3032
  }
2618
3033
  interface LoadedResponse {
2619
3034
  type: 'loaded';
@@ -2724,29 +3139,271 @@ declare class WorkerPool {
2724
3139
  destroy(): void;
2725
3140
  }
2726
3141
 
2727
- type PropBag = Record<string, string | number | boolean | null>;
3142
+ type MapLibreSupportStatus = '✅' | '🟡' | '❌';
3143
+ interface MapLibreSupport {
3144
+ status: MapLibreSupportStatus;
3145
+ /** Чем это свойство у osmgl (или «—»). */
3146
+ osmgl: string;
3147
+ /** Что не так или что сказать о нём. */
3148
+ note: string;
3149
+ }
3150
+ declare const MAPLIBRE_SUPPORT: Record<string, MapLibreSupport>;
3151
+
2728
3152
  /**
2729
- * Значение стиля на конкретном зуме.
3153
+ * СПРАВОЧНИК СТИЛЯ osmgl — машиночитаемый, для редактора стилей и для проверки (`validate.ts`).
3154
+ *
3155
+ * Один источник правды о том, какие поля есть у стиля, что они значат, чем заданы по умолчанию и
3156
+ * как движок их читает. По нему строит формы редактор в админке (как Maputnik строит свои по
3157
+ * `v8.json` MapLibre), по нему же проверяется стиль на каждом нажатии клавиши. В сборке он ложится
3158
+ * ещё и файлом `dist/style-reference.json` — для тех, кто не тянет движок целиком.
3159
+ *
3160
+ * ★ СПРАВОЧНИК НЕ ДОЛЖЕН РАЗОЙТИСЬ СО `spec.ts`. Тест `tests/style-reference.test.mjs` разбирает
3161
+ * `spec.ts` компилятором TypeScript и сверяет поля в обе стороны: новое поле в спеке без строки
3162
+ * здесь (и наоборот) роняет `npm test`. Значения по умолчанию взяты из кода, который их применяет
3163
+ * (`layers/renderer.ts`, `layers/symbols.ts`, `layers/ground-arrows.ts`, `worker/process.ts`,
3164
+ * `generators/*`), а не из комментариев; где умолчание выводится из другого поля, это сказано в
3165
+ * тексте, а `default` не задан.
3166
+ *
3167
+ * Что значат признаки:
3168
+ * `zoom` — значение может меняться по зуму: остановки `{stops}` или выражение с `['zoom']`;
3169
+ * `data` — значение может зависеть от ФИЧИ (`['get', …]`) и движок его на фиче ДЕЙСТВИТЕЛЬНО
3170
+ * считает. Таких полей мало: цвет, прозрачность и ширина у fill/line/fill-extrusion
3171
+ * (воркер кладёт их в вершины, `generators/data-paint.ts`), высота и низ дома, шаблоны
3172
+ * подписи и значка, поворот значка. Остальное считается раз на слой, и `['get']` в нём
3173
+ * даёт запасное значение;
3174
+ * `geometry` — правка пересобирает тайлы (поле едет в план воркера, `Style.workerPlan`). Цвет,
3175
+ * прозрачность и ширина, заданные ПО ФИЧЕ, тоже пересобирают — это отмечено
3176
+ * `geometryIfData`.
3177
+ */
3178
+
3179
+ /** Вид значения: по нему редактор выбирает поле ввода. */
3180
+ type PropType = 'number' | 'integer' | 'boolean' | 'string' | 'color' | 'enum' | 'array' | 'object' | 'filter' | 'expression' | 'colorFrom' | 'formatted' | 'resolvedImage' | 'padding' | 'sources' | 'light' | 'sky' | 'terrain' | 'sprite';
3181
+ interface PropRef {
3182
+ type: PropType;
3183
+ /** Что это, по-русски, одно-два предложения; единицы. */
3184
+ doc: string;
3185
+ /** Значение, которое движок берёт, когда поле не задано. */
3186
+ default?: unknown;
3187
+ minimum?: number;
3188
+ maximum?: number;
3189
+ step?: number;
3190
+ /** Единицы: `px`, `m`, `em`, `deg`, `ms`, `×width`… */
3191
+ units?: string;
3192
+ /** Допустимые значения перечисления → что каждое значит. */
3193
+ values?: Record<string, string>;
3194
+ /** Элемент массива. */
3195
+ items?: PropRef;
3196
+ /** Точная длина массива. */
3197
+ length?: number;
3198
+ /** Поля объекта. */
3199
+ properties?: Record<string, PropRef>;
3200
+ /** Значения словаря с произвольными ключами (`groundBy`, `sources`). */
3201
+ additionalProperties?: PropRef;
3202
+ required?: boolean;
3203
+ /** Может меняться по зуму. */
3204
+ zoom: boolean;
3205
+ /** Может зависеть от фичи — и движок действительно считает его на фиче. */
3206
+ data: boolean;
3207
+ /** Правка пересобирает тайлы (поле в плане воркера). */
3208
+ geometry?: boolean;
3209
+ /** Пересобирает тайлы, когда значение зависит от фичи (цвет/прозрачность/ширина по `get`). */
3210
+ geometryIfData?: boolean;
3211
+ /** Строка на месте числа — ИМЯ СВОЙСТВА фичи (`iconRotate: 'rotation'`). */
3212
+ propertyName?: boolean;
3213
+ /** К каким типам слоёв относится общее поле (нет — ко всем, у кого оно есть). */
3214
+ appliesTo?: string[];
3215
+ /** Раздел интерфейса (`groups`). */
3216
+ group?: string;
3217
+ since?: string;
3218
+ deprecated?: string;
3219
+ experimental?: boolean;
3220
+ /** Свойство MapLibre, которое импортёр переводит в это поле. */
3221
+ mapLibre?: string;
3222
+ /**
3223
+ * ИМЯ В ФОРМАТЕ v2 (`porting/unified-style-format.md`, §3): `раздел.имя`, где раздел у слоя —
3224
+ * `layer` (рядом с `id`), `layout` или `paint`; у корня — `root`, `sky`, `light`, `osmgl-light`,
3225
+ * `terrain`, `osmgl-tint`, `source`. Свойство MapLibre — его именем, своё — с приставкой `osmgl-`.
3226
+ * Одна таблица на всё: по ней работают загрузчик v2, конвертер v1 → v2, API карты на именах v2,
3227
+ * проверка стиля и редактор. Заполняется в конце файла (`V2_NAMES`), у КАЖДОЙ записи — тест.
3228
+ */
3229
+ v2?: string;
3230
+ /** Цвет-ссылка `{ from }` в v2 лежит отдельным свойством рядом с цветом: здесь — каким. */
3231
+ v2From?: string;
3232
+ /** Поле v1 общее на два свойства v2 (`allowOverlap` → `text-allow-overlap` и `icon-allow-overlap`): второе. */
3233
+ v2Also?: string[];
3234
+ /** Как это свойство MapLibre рисует osmgl: ✅ / 🟡 / ❌ и примечание (§2 того же документа). */
3235
+ support?: MapLibreSupport;
3236
+ }
3237
+ interface LayerTypeRef {
3238
+ doc: string;
3239
+ /** Какие поля `layerCommon` у слоя этого типа есть. Нет списка — все. */
3240
+ common?: string[];
3241
+ /** Свои поля верхнего уровня (рядом с `id`, вне `paint`/`layout`). */
3242
+ fields: Record<string, PropRef>;
3243
+ paint: Record<string, PropRef>;
3244
+ layout: Record<string, PropRef>;
3245
+ }
3246
+ type LayerTypeName = 'fill' | 'line' | 'fill-extrusion' | 'surface' | 'symbol' | 'raster';
3247
+ interface StyleReference {
3248
+ version: 1;
3249
+ root: Record<string, PropRef>;
3250
+ source: Record<string, PropRef>;
3251
+ layerCommon: Record<string, PropRef>;
3252
+ layers: Record<LayerTypeName, LayerTypeRef>;
3253
+ groups: Record<string, string>;
3254
+ }
3255
+ interface ExpressionOperatorRef {
3256
+ doc: string;
3257
+ /** Аргументы, как их пишут: `['get', имя, объект?]`. */
3258
+ args: string;
3259
+ /** Тип результата. */
3260
+ returns: string;
3261
+ /** Раздел: данные, решения, шкалы, математика… */
3262
+ group: string;
3263
+ }
3264
+ declare const STYLE_REFERENCE: StyleReference;
3265
+ /**
3266
+ * СВОИ ФЛАГИ v2 БЕЗ ПОЛЯ v1 — переключатели поведения (§1.2): без поля значение — как у MapLibre,
3267
+ * своё поведение движка включается явно. Конвертер v1 → v2 дописывает их во встроенные стили, чтобы
3268
+ * те рисовались как прежде.
3269
+ */
3270
+ declare const STYLE_REFERENCE_V2_FLAGS: Record<string, Record<string, PropRef>>;
3271
+ /**
3272
+ * Операции выражений, которые понимает движок (`style/expression.ts`). Сверяется тестом со
3273
+ * списком `case` в `compile` — ни одной лишней и ни одной пропущенной.
3274
+ */
3275
+ declare const STYLE_REFERENCE_EXPRESSIONS: Record<string, ExpressionOperatorRef>;
3276
+ /** Разделы выражений → названия. */
3277
+ declare const STYLE_REFERENCE_EXPRESSION_GROUPS: Record<string, string>;
3278
+
3279
+ /**
3280
+ * ФОРМАТ СТИЛЯ v2 = MapLibre v8 + `osmgl-*` (`porting/unified-style-format.md`).
3281
+ *
3282
+ * Внешний формат стиля osmgl — синтаксис MapLibre как есть: те же типы слоёв, имена свойств, поля
3283
+ * корня и источников, выражения и старые функции. Свои свойства лежат в тех же `paint`/`layout` с
3284
+ * приставкой `osmgl-` (геометрия — в `layout`, покраска — в `paint`), свой тип слоя —
3285
+ * `osmgl-surface`. Признак расширений — `metadata["osmgl:version"]: 2`; без него файл — «чистый
3286
+ * MapLibre», он же валидный v2.
3287
+ *
3288
+ * ★ ВНУТРЕННЯЯ МОДЕЛЬ ДВИЖКА ОСТАЛАСЬ v1 (`StyleSpec`). Переименовывать поля на трёх платформах
3289
+ * ради имён смысла нет: облик от этого не меняется, а порты читают поля строками по всему рендеру.
3290
+ * Поэтому v2 — формат обмена: загрузчик (`importMapLibreStyle`, расширенный чтением `osmgl-*`)
3291
+ * переводит его во внутреннюю модель, а конвертер здесь — обратно. Таблица имён ОДНА — поле `v2` у
3292
+ * записей `STYLE_REFERENCE`; этот модуль только собирает из неё словари и знает про особые случаи,
3293
+ * где перевод — не переименование (пунктир, капсула, якоря подписи, ссылка на цвет).
3294
+ *
3295
+ * ★ БЕЗ ПОЛЯ — УМОЛЧАНИЕ MapLibre. Иначе стиль MapLibre «без правок» рисовался бы не как в MapLibre.
3296
+ * Своё поведение движка включается явными флагами (§1.2): `osmgl-line-shape: capsule`,
3297
+ * `osmgl-lighting: osmgl`, `osmgl-roofs: auto`, `osmgl-height-from-data`, `osmgl-symbol-layout:
3298
+ * block` (своя система подписей целиком — с её умолчаниями), `osmgl-line-anchors`,
3299
+ * `osmgl-text-halo-tracking`. Конвертер v1 → v2 дописывает их во встроенные стили, и те рисуются
3300
+ * пиксель в пиксель как прежде — это проверяет `tests/style-v2.test.mjs`.
3301
+ */
3302
+ /** Ключ признака v2 в `metadata` корня. */
3303
+ declare const OSMGL_VERSION_KEY = "osmgl:version";
3304
+ /** Значение признака: версия расширений osmgl поверх MapLibre v8. */
3305
+ declare const OSMGL_FORMAT_VERSION = 2;
3306
+ /** Стиль v2 (он же MapLibre v8) — в той форме, в какой его пишет конвертер. */
3307
+ interface StyleSpecV2 {
3308
+ version: 8;
3309
+ name?: string;
3310
+ metadata?: Record<string, unknown>;
3311
+ sources: Record<string, Record<string, unknown>>;
3312
+ layers: LayerSpecV2[];
3313
+ [key: string]: unknown;
3314
+ }
3315
+ interface LayerSpecV2 {
3316
+ id: string;
3317
+ type: string;
3318
+ source?: string;
3319
+ 'source-layer'?: string;
3320
+ minzoom?: number;
3321
+ maxzoom?: number;
3322
+ filter?: unknown;
3323
+ metadata?: unknown;
3324
+ layout?: Record<string, unknown>;
3325
+ paint?: Record<string, unknown>;
3326
+ }
3327
+ /** Тема v2: та же накладка, ключи — имена v2 (`paint[id]["line-color"]`, `osmgl-light`). */
3328
+ interface ThemeSpecV2 {
3329
+ name: string;
3330
+ 'osmgl-light'?: Record<string, unknown>;
3331
+ sky?: Record<string, unknown>;
3332
+ paint?: Record<string, Record<string, unknown>>;
3333
+ layout?: Record<string, Record<string, unknown>>;
3334
+ 'osmgl-model3d-key'?: string;
3335
+ [key: string]: unknown;
3336
+ }
3337
+ /** Стиль с признаком v2 (`metadata["osmgl:version"]`). Чистый MapLibre — тоже v2, но без своих полей. */
3338
+ declare function isOsmglV2(value: unknown): boolean;
3339
+ type V1Where = 'field' | 'paint' | 'layout';
3340
+ type V2Where = 'layer' | 'layout' | 'paint';
3341
+ /** Что конвертер не смог перенести: `слой: поле`. */
3342
+ type ConvertReport = string[];
3343
+ /**
3344
+ * СЛОЙ v1 → v2.
2730
3345
  *
2731
- * Между остановками интерполируем ЛИНЕЙНО по зуму, а не экспоненциально: для
2732
- * ширин дорог и прозрачностей так предсказуемее, а «экспоненту» при желании
2733
- * задают частыми остановками.
3346
+ * Подпись: слой, поставленный импортёром MapLibre (`placementMode: maplibre`), остаётся слоем
3347
+ * MapLibre и получает явные значения там, где умолчания v1 и MapLibre расходятся; любой другой — это
3348
+ * своя система подписей (`osmgl-symbol-layout: block`), и его поля переносятся одним
3349
+ * переименованием: умолчания у этой системы свои, и пустое поле значит ровно то же, что в v1.
2734
3350
  */
2735
- declare function evaluateNumber(v: Interpolated<number> | undefined, zoom: number, fallback: number): number;
2736
- type RGBA = [number, number, number, number];
2737
- declare function evaluateColor(v: Interpolated<string> | undefined, zoom: number, fallback: RGBA): RGBA;
3351
+ declare function layerV1toV2(layer: StyleLayerSpec, style: StyleSpec, report?: ConvertReport): LayerSpecV2;
2738
3352
  /**
2739
- * Проверка фильтра. Отсутствующее свойство никогда не равно значению
2740
- * (кроме '!=' и '!in', где отсутствие считается несовпадением, — так же
2741
- * ведёт себя MapLibre).
3353
+ * СТИЛЬ v1 → v2. Чистая функция: та же внутренняя модель получается обратно загрузчиком
3354
+ * (`importMapLibreStyle`) — это и есть доказательство «без потерь» (`tests/style-v2.test.mjs`).
2742
3355
  */
2743
- declare function matchesFilter(filter: Filter | undefined, props: PropBag,
2744
- /** Остальное окружение фичи: номер, тип геометрии, состояние. Нужно выражениям. */
2745
- ctx?: {
2746
- id?: number | string | null;
2747
- geometryType?: string;
2748
- featureState?: Record<string, unknown>;
2749
- }): boolean;
3356
+ declare function styleV1toV2(style: StyleSpec, report?: ConvertReport): StyleSpecV2;
3357
+ /**
3358
+ * ОДНО СВОЙСТВО v2 → поля v1. Возвращает список правок (`where`, `key`, `value`), `undefined` у
3359
+ * значения — снять поле. `null` — имя незнакомо. Общее для загрузчика, тем и API карты
3360
+ * (`setPaintProperty('road', 'line-color', …)`).
3361
+ */
3362
+ declare function propertyV2toV1(type: string, where: V2Where, name: string, value: unknown): {
3363
+ where: V1Where;
3364
+ key: string;
3365
+ value: unknown;
3366
+ }[] | null;
3367
+ /** Внутренняя накладка: то же, что `ThemeSpec`, плюс поля слоя (их v1-темы не правили). */
3368
+ interface ThemeInternal {
3369
+ name: string;
3370
+ background?: string;
3371
+ light?: Partial<LightSpec>;
3372
+ sky?: Partial<SkySpec>;
3373
+ paint?: Record<string, Record<string, unknown>>;
3374
+ layout?: Record<string, Record<string, unknown>>;
3375
+ fields?: Record<string, Record<string, unknown>>;
3376
+ hidden?: string[];
3377
+ model3dKey?: string;
3378
+ }
3379
+ /**
3380
+ * ТЕМА v1 → v2 ПО БАЗОВОМУ СТИЛЮ: ключ `color` значит `fill-color`, `line-color` или `text-color`
3381
+ * смотря по типу слоя, а тип знает только база. Id, которого в базе нет, переводится по имени поля
3382
+ * (если оно однозначно) и попадает в отчёт.
3383
+ */
3384
+ declare function themeV1toV2(theme: ThemeInternal, base: StyleSpec, report?: ConvertReport): ThemeSpecV2;
3385
+ /**
3386
+ * ТЕМА (v2 или v1) → ВНУТРЕННЯЯ НАКЛАДКА. Ключи разбираются по одному: имя v2 переводится по
3387
+ * таблице, имя v1 принимается как синоним (с одним предупреждением), незнакомое остаётся как есть —
3388
+ * так тема прошлого выпуска, написанная приложением, продолжает работать.
3389
+ */
3390
+ declare function themeToInternal(theme: Record<string, unknown>, base?: StyleSpec): ThemeInternal;
3391
+ /**
3392
+ * СТИЛЬ v2 → ЧИСТЫЙ MapLibre v8: «Скачать для MapLibre» (§1.4, вопрос 2 §6).
3393
+ *
3394
+ * MapLibre файл со своими полями не откроет — его валидатор отвергает незнакомые свойства и типы
3395
+ * слоёв. Поэтому `osmgl-*` срезаются, а то, у чего в MapLibre есть ближайший аналог, переводится
3396
+ * ПРИБЛИЖЁННО (и попадает в отчёт `report`): поверхность — заливкой её цвета, ссылка на цвет — уже
3397
+ * разрешённым цветом, список шаблонов подписи — `coalesce`, своя блочная раскладка подписи — её
3398
+ * умолчаниями, записанными явно. Основе без адреса (`base`, «тайлы даёт карта») адрес можно дать
3399
+ * опцией `tiles`.
3400
+ */
3401
+ declare function toMapLibreStyle(v2: StyleSpecV2, opts?: {
3402
+ tiles?: string | string[];
3403
+ }, report?: ConvertReport): Record<string, unknown> & {
3404
+ version: 8;
3405
+ layers: Record<string, unknown>[];
3406
+ };
2750
3407
 
2751
3408
  /**
2752
3409
  * Тема — набор правок поверх базового стиля, а не его копия.
@@ -2765,6 +3422,11 @@ interface ThemeSpec {
2765
3422
  sky?: Partial<SkySpec>;
2766
3423
  /** Правки paint-свойств по id слоя. Неизвестные id игнорируются. */
2767
3424
  paint?: Record<string, Record<string, unknown>>;
3425
+ /**
3426
+ * Правки layout-свойств по id слоя — то же, что `paint`, но для раскладки (кегль, выравнивание
3427
+ * подписи). Нужны теме, которой подписи дорог положены на асфальт (`textPitchAlignment: 'map'`).
3428
+ */
3429
+ layout?: Record<string, Record<string, unknown>>;
2768
3430
  /** Слои, скрытые в этой теме. */
2769
3431
  hidden?: string[];
2770
3432
  /**
@@ -2778,12 +3440,26 @@ interface ThemeSpec {
2778
3440
  * пришлось бы держать синхронным в двух местах.
2779
3441
  */
2780
3442
  model3dKey?: string;
3443
+ /**
3444
+ * Правки полей слоя рядом с фильтром (`cornerRadius`, `visible: true`…) по id — их приносит тема v2
3445
+ * (`layout["osmgl-corner-radius"]`, `visibility: visible`). Темы v1 полей слоя не правили.
3446
+ */
3447
+ fields?: Record<string, Record<string, unknown>>;
2781
3448
  }
3449
+ /**
3450
+ * ТЕМА НА ВХОДЕ — v2 или v1.
3451
+ *
3452
+ * Внешний формат темы — v2: те же накладки, ключи — имена v2 (`paint[id]["line-color"]`,
3453
+ * `osmgl-light`, `paint.background["background-color"]`, `layout[id].visibility: none`). Тема v1
3454
+ * (`color`, `light`, `background`, `hidden`) принимается ещё два выпуска — с одним предупреждением
3455
+ * на имя. Внутри движка тема всегда в именах v1: `ThemeSpec`.
3456
+ */
3457
+ type ThemeInput = ThemeSpec | ThemeSpecV2;
2782
3458
  /**
2783
3459
  * Накладывает тему на базовый стиль. Базовый не мутируется — возвращается
2784
3460
  * новый объект, иначе переключение туда-обратно накапливало бы правки.
2785
3461
  */
2786
- declare function applyTheme(base: StyleSpec, theme: ThemeSpec): StyleSpec;
3462
+ declare function applyTheme(base: StyleSpec, input: ThemeInput): StyleSpec;
2787
3463
 
2788
3464
  /**
2789
3465
  * Разобранный стиль. Держит порядок слоёв (он же порядок отрисовки: художника
@@ -2792,6 +3468,8 @@ declare function applyTheme(base: StyleSpec, theme: ThemeSpec): StyleSpec;
2792
3468
  declare class Style {
2793
3469
  readonly spec: StyleSpec;
2794
3470
  readonly light: LightSpec;
3471
+ /** Свет объёма MapLibre — для слоёв с `lighting: 'maplibre'` (см. `MapLibreLightSpec`). */
3472
+ readonly mapLibreLight: MapLibreLightSpec;
2795
3473
  readonly sky: SkySpec;
2796
3474
  readonly layers: StyleLayerSpec[];
2797
3475
  /**
@@ -2823,6 +3501,11 @@ declare class Style {
2823
3501
  * и должен оставаться пригодным, когда пользователь отъедет или приблизится.
2824
3502
  */
2825
3503
  workerPlan(source?: string): LayerPlan[];
3504
+ /**
3505
+ * План воркера для ОДНОГО слоя. Отдельно — ради правок на лету (`setPaintProperty` и соседи):
3506
+ * по нему видно, задела ли правка геометрию, и пересобирать тайлы нужно только тогда.
3507
+ */
3508
+ layerPlan(l: Exclude<StyleLayerSpec, RasterLayerSpec>): LayerPlan;
2826
3509
  /** Источники, на которые ссылается хоть один слой. */
2827
3510
  usedSources(): string[];
2828
3511
  /**
@@ -2887,9 +3570,22 @@ declare const RELIEF_STYLE: StyleSpec;
2887
3570
  * читается шумом; вода осталась водой, её блики и есть смысл материала.
2888
3571
  */
2889
3572
  declare const CITY_STYLE: StyleSpec;
3573
+ /**
3574
+ * ВСТРОЕННЫЕ СТИЛИ osmgl ПО ИМЕНИ — для `map.setStyle('osm-city')`, как `mlight-city` у стилей MapLibre.
3575
+ *
3576
+ * Имя — имя файла в `styles/` без расширения: тем же именем стиль публикуется и по ссылке
3577
+ * (`/styles/{slug}.json`), и строка, годная для `setStyle`, не зависит от того, лежит стиль в пакете
3578
+ * или на сервере. Встроенный за JSON в сеть не ходит.
3579
+ */
3580
+ declare const OSMGL_STYLES: Record<string, {
3581
+ name: string;
3582
+ style: StyleSpec;
3583
+ }>;
3584
+ /** Встроенный стиль osmgl по имени; не встроенное имя (в том числе ссылка) — `null`. */
3585
+ declare function builtinOsmglStyle(name: string): StyleSpec | null;
2890
3586
  declare const THEMES: Record<string, ThemeSpec>;
2891
3587
  /** Стиль с наложенной темой. Имя неизвестной темы приводит к базовому стилю. */
2892
- declare function styleWithTheme(theme: string | ThemeSpec, base?: StyleSpec): StyleSpec;
3588
+ declare function styleWithTheme(theme: string | ThemeInput, base?: StyleSpec): StyleSpec;
2893
3589
 
2894
3590
  interface CoverOptions {
2895
3591
  minzoom: number;
@@ -3182,7 +3878,16 @@ declare class TileSource extends Evented<TileSourceEvents> {
3182
3878
  maxTiles?: number;
3183
3879
  }): void;
3184
3880
  /** Новый план (сменился стиль): кеш геометрии устарел целиком. */
3185
- setPlan(plan: LayerPlan[]): void;
3881
+ setPlan(plan: LayerPlan[], key?: string): void;
3882
+ /**
3883
+ * Слепок плана, которым воркер сейчас режет тайлы, — для сравнения при смене стиля.
3884
+ *
3885
+ * Сравнивать новый стиль надо с тем, ЧТО У ВОРКЕРА, и делать это дёшево: раньше на каждый
3886
+ * `setStyle` сериализовались планы и прежнего стиля, и нового. Слепок текущего считается один раз
3887
+ * на план (лениво, при первом вопросе) и приходит готовым вместе с новым (`setPlan(plan, key)`).
3888
+ */
3889
+ get planKey(): string;
3890
+ private _planKey;
3186
3891
  reload(): void;
3187
3892
  destroy(): void;
3188
3893
  }
@@ -3438,6 +4143,8 @@ declare class GlyphSource {
3438
4143
  private glyphs;
3439
4144
  private loaded;
3440
4145
  private inflight;
4146
+ /** Номер набора глифов: растёт на каждый догруженный диапазон и смену адреса (см. `shapeTextCached`). */
4147
+ version: number;
3441
4148
  constructor(
3442
4149
  /** Шаблон вида /tileserver/fonts/{fontstack}/{range}.pbf */
3443
4150
  urlTemplate: string, fontstack?: string);
@@ -3505,6 +4212,11 @@ interface Box {
3505
4212
  y1: number;
3506
4213
  x2: number;
3507
4214
  y2: number;
4215
+ /**
4216
+ * Радиус, если это КРУГ (центр — середина бокса): так у MapLibre место занимает подпись вдоль
4217
+ * линии — цепочкой кругов (`placeCollisionCircles`). Без поля — прямоугольник.
4218
+ */
4219
+ r?: number;
3508
4220
  /**
3509
4221
  * Чьё это место.
3510
4222
  *
@@ -3551,6 +4263,43 @@ interface Candidate {
3551
4263
  }[];
3552
4264
  /** Те же вершины линии в меркаторе (x, y парами) — по ним сетка мест отмеряется ПО ЗЕМЛЕ. */
3553
4265
  lineWorld?: number[];
4266
+ /**
4267
+ * Линия целиком в координатах СВОЕГО ТАЙЛА — для мест по алгоритму MapLibre
4268
+ * (`lineAnchors: 'maplibre'`): он считает их в тайле, с переувеличением и срезом по квадрату.
4269
+ */
4270
+ tileLine?: TileLine;
4271
+ /**
4272
+ * Порядок раскладки MapLibre (`placementMode: 'maplibre'`): номер тайла по их сортировке
4273
+ * (`getRenderableIds(true)`) × 1e6 + номер символа в тайле. Приоритет такого кандидата — только
4274
+ * полоса слоя, ранг фичи не участвует.
4275
+ */
4276
+ mlOrder?: number;
4277
+ /** Зум данных тайла кандидата — для единиц боксов MapLibre (см. `placeAnchored`). */
4278
+ tileZ?: number;
4279
+ }
4280
+ interface TileLine {
4281
+ /** x, y парами, единицы тайла (`extent`). */
4282
+ pts: number[];
4283
+ extent: number;
4284
+ /** Зум данных тайла. */
4285
+ z: number;
4286
+ /** Тайл в меркаторе: левый верхний угол и сторона. */
4287
+ minX: number;
4288
+ minY: number;
4289
+ span: number;
4290
+ /** Ключ тайла — для «повтора той же подписи ближе полушага» в пределах тайла. */
4291
+ key: string;
4292
+ /** Номер линии в бакете и сам бакет: места MapLibre считаются сразу на весь бакет (см. `bucketAnchors`). */
4293
+ index: number;
4294
+ bucket: SymbolBucketView;
4295
+ }
4296
+ /** То, что нужно от бакета подписей для мест MapLibre: линии, их границы, тексты и значки. */
4297
+ interface SymbolBucketView {
4298
+ lines: Int16Array;
4299
+ starts: Uint32Array;
4300
+ texts: string[];
4301
+ icons: string[];
4302
+ extent: number;
3554
4303
  }
3555
4304
  /**
3556
4305
  * Размещённая подпись: то, что реально видно на экране в этом кадре.
@@ -3569,7 +4318,27 @@ interface PlacedSymbol {
3569
4318
  box: Box;
3570
4319
  /** Позиция слоя в стиле: верхняя подпись выигрывает попадание. */
3571
4320
  order: number;
4321
+ /** Ключ подписи — тот, под которым она стоит (после сверки с прошлой раскладкой, см. `stableKey`). */
4322
+ key?: string;
3572
4323
  }
4324
+ /**
4325
+ * ★ МЕСТО ВДОЛЬ ЛИНИИ В СТИЛЕ MapLibre — СО СВОИМ РЕШЕНИЕМ И СВОЕЙ ПРОЗРАЧНОСТЬЮ (03.10.2026).
4326
+ *
4327
+ * У MapLibre каждое место линии (стрелка направления, щит) — отдельный символ со своим
4328
+ * `crossTileID`: решение «стоит / не стоит» принимает расстановка раз в `fadeDuration`, а между
4329
+ * расстановками каждое место держит своё решение и плавно идёт к нему (`JointOpacityState`).
4330
+ *
4331
+ * У нас решение было на ЛИНИЮ целиком: между расстановками решённая линия рисовала ВСЕ свои места
4332
+ * без проверки — и те, которым расстановка отказала (стрелка под названием улицы). На каждой
4333
+ * расстановке такая стрелка пропадала на кадр и тут же возвращалась: мигание с периодом 300 мс
4334
+ * (замер на зуме 20→17, наклон 46°: 56 возвратов у нас против 18 у MapLibre, серии «+17 −1 +17 −1»).
4335
+ * Теперь решение — на МЕСТО:
4336
+ * - на расстановке (`following = false`) место проверяется; встало — `show`, не встало, а прежде
4337
+ * стояло или ещё гаснет — `ghost` (гаснет без занятия места), иначе `none`;
4338
+ * - между расстановками — только прежнее решение: решённое `show` (без проверки), гаснущее `ghost`,
4339
+ * остальное `none` — новое место ждёт следующую расстановку, как у MapLibre.
4340
+ */
4341
+ declare function mapLibreSpot(following: boolean, decided: boolean, fits: boolean, wasShown: boolean, fadingOut: boolean): 'show' | 'ghost' | 'none';
3573
4342
  declare class SymbolRenderer {
3574
4343
  private shader;
3575
4344
  private locations;
@@ -3586,6 +4355,17 @@ declare class SymbolRenderer {
3586
4355
  private pendingIcons;
3587
4356
  /** Сменить адрес шрифтов: `map.setGlyphs`. Разложенные подписи пересоберутся сами. */
3588
4357
  setGlyphsUrl(url: string): void;
4358
+ /**
4359
+ * ГАРНИТУРЫ СЛОЁВ (`textFont`, 02.10.2026). Свой набор глифов на каждую — как `text-font` у
4360
+ * MapLibre: жирная подпись дороги берёт `Noto Sans Bold` с того же сервера шрифтов. Основной
4361
+ * шрифт карты — `glyphs`; в атласе глифы других гарнитур лежат под своим префиксом.
4362
+ */
4363
+ private fonts;
4364
+ private fontUrl;
4365
+ private pendingFonts;
4366
+ private glyphsFor;
4367
+ /** Ключ глифа в атласе: у основной гарнитуры — прежний, у остальных — с её именем. */
4368
+ private glyphKey;
3589
4369
  /**
3590
4370
  * Кого звать, когда стиль просит картинку, которой нет (`map.setMissingStyleImageResolver`).
3591
4371
  *
@@ -3613,6 +4393,7 @@ declare class SymbolRenderer {
3613
4393
  * Ставит рендерер; на обычной карте возвращает ноль, и проекция идёт как раньше.
3614
4394
  */
3615
4395
  elevationAt: ((mercX: number, mercY: number) => number) | null;
4396
+ private readonly mainFont;
3616
4397
  constructor(fontUrl: string, fontstack?: string);
3617
4398
  init(ctx: Context): void;
3618
4399
  /**
@@ -3668,13 +4449,66 @@ declare class SymbolRenderer {
3668
4449
  text: string;
3669
4450
  mercX: number;
3670
4451
  mercY: number;
4452
+ key?: string;
3671
4453
  }): string;
4454
+ /**
4455
+ * Вставшие в прошлой раскладке по «слой|текст»: тройки (mercX, mercY, ключ) — см. `stableKey`.
4456
+ */
4457
+ private placedByName;
4458
+ /** Решённые подписи, которые в этом кадре не легли: не рисуются, но и не проигрывают. */
4459
+ private heldNow;
4460
+ /** Вставшие в этом кадре экземпляры подписей вдоль линии (ключ — слой, текст, точка земли). */
4461
+ private placedInstances;
4462
+ /**
4463
+ * ТОЧНЫЕ места вставших экземпляров этого кадра (меркатор) — для сверки с MapLibre на странице
4464
+ * примера. Ключи `placedInstances` для неё не годятся: точка в них квантована шагом
4465
+ * `INSTANCE_QUANT` (≈4 м), а это на z20 уже 27 px — больше допуска сверки (12 px), и сверка
4466
+ * насчитывала «пропавшие» стрелки там, где они стояли (z19.98: 4 из 16 при фактических 16 из 16).
4467
+ */
4468
+ private placedMarks;
4469
+ /** Экземпляры, уже разобранные в этом кадре: улица приезжает несколькими кусками. */
4470
+ private drawnInstances;
4471
+ /** Призраки этой раскладки: их ключ тоже сверяется — вернувшаяся подпись обязана его узнать. */
4472
+ private ghostsNow;
4473
+ /**
4474
+ * КЛЮЧ — ПО ДОПУСКУ, А НЕ ПО КЛЕТКЕ ОКРУГЛЕНИЯ (02.10.2026, как `CrossTileSymbolIndex` у MapLibre).
4475
+ *
4476
+ * Округление режет пространство на клетки, и объект у границы клетки получал в тайлах соседних
4477
+ * уровней РАЗНЫЕ ключи: при зуме подпись пропадала и тут же проявлялась с нуля — моргала (замер
4478
+ * на одном отдалении: 6 таких). Если в прошлой раскладке стояла подпись того же слоя и текста в
4479
+ * пределах `KEY_TOLERANCE`, берём её ключ.
4480
+ */
4481
+ /**
4482
+ * `nameKey` — «слой|текст», `ownKey` — собственный ключ места (`keyOf`). Обе строки приходят готовыми
4483
+ * из `anchorStatics`: собирать их заново на каждый кадр и каждую подпись стоило больше самой сверки.
4484
+ */
4485
+ private stableKey;
4486
+ /**
4487
+ * НЕИЗМЕННОЕ У МЕСТА ПОДПИСИ — НА БАКЕТ, А НЕ НА КАДР (03.10.2026).
4488
+ *
4489
+ * Сбор кандидатов шёл каждый кадр и каждый раз заново строил строки ключей (шаблон из слоя, текста
4490
+ * и координат) и копировал линию тайла. Хуже самой сборки — то, что СВЕЖАЯ строка в `Set.has`
4491
+ * хешируется заново по всей длине: на стиле MapLibre в 315 слоёв проверки ключей в цикле раскладки
4492
+ * были самой горячей строкой кадра (замер: `decidedKeys.has`, `placedKeys.has` — больше, чем вся
4493
+ * расстановка). Готовая строка хеш помнит. Записи живут с бакетом (`WeakMap`) и заполняются
4494
+ * лениво — по тем местам, что дошли до раскладки.
4495
+ */
4496
+ private anchorStaticsCache;
4497
+ private anchorStatics;
3672
4498
  setHover(key: string | null): boolean;
3673
4499
  setHidden(key: string | null): boolean;
3674
4500
  /**
3675
4501
  * Подпись под точкой экрана. Идём СВЕРХУ ВНИЗ по порядку стиля: под курсором
3676
4502
  * оказывается то, что человек видит верхним, а не то, что нарисовано первым.
3677
4503
  */
4504
+ /**
4505
+ * ВСЕ подписи под точкой, сверху вниз — для редактора стилей («какие слои здесь нарисованы»).
4506
+ * Одна подпись (`querySymbol`) нужна клику; редактору нужны и те, что легли под ней.
4507
+ */
4508
+ querySymbols(point: {
4509
+ x: number;
4510
+ y: number;
4511
+ }, tolerance?: number): PlacedSymbol[];
3678
4512
  querySymbol(point: {
3679
4513
  x: number;
3680
4514
  y: number;
@@ -3715,16 +4549,6 @@ declare class SymbolRenderer {
3715
4549
  /** Дымка этого кадра и камера — по ним считается затухание подписи вдаль. */
3716
4550
  private fog;
3717
4551
  private tr;
3718
- /**
3719
- * ГДЕ ПОДПИСЬ СТОЯЛА — переведённое в расстояние вдоль СЕГОДНЯШНЕЙ линии.
3720
- *
3721
- * Место запоминается точкой на земле, а не расстоянием от начала линии. Расстояние не годится:
3722
- * при наклоне камера срезает у ломаной начало (точку за собой спроецировать нельзя), и с каждым
3723
- * сдвигом карты отсчёт ведётся от другой вершины — сетка мест уезжает вдоль улицы, а с ней и
3724
- * название. Точка на асфальте не зависит ни от камеры, ни от обрезки: её достаточно спроецировать
3725
- * заново и померить, на каком она расстоянии сейчас.
3726
- */
3727
- private rememberedAt;
3728
4552
  /**
3729
4553
  * Запомнить места этого кадра точками на земле.
3730
4554
  *
@@ -3733,7 +4557,25 @@ declare class SymbolRenderer {
3733
4557
  * предыдущих: на следующем кадре они оказывались без памяти и выбирали место заново. Это и было
3734
4558
  * остаточное «ползание» на части улиц.
3735
4559
  */
3736
- private rememberAt;
4560
+ /** Вид сверху на той же камере (наклон 0) — по нему раскладывается подпись на асфальте. */
4561
+ private flatCam;
4562
+ private flatCamFor;
4563
+ private flatCamKey;
4564
+ private flatCamera;
4565
+ /**
4566
+ * Последний квад буквы — с плоского вида на экран. Пикселей на тексель у каждого угла свой
4567
+ * (ближний край буквы крупнее дальнего): его берёт шейдер для ширины сглаживания.
4568
+ * `false` — угол ушёл за камеру, квад снят.
4569
+ */
4570
+ private toGround;
4571
+ /**
4572
+ * Запомненные экземпляры подписи на этой линии: где начинается строка и точка земли её середины.
4573
+ *
4574
+ * Точка земли — середина строки при первой постановке; она же ключ экземпляра. Переписывать её
4575
+ * тем, что нарисовалось, нельзя: перевод «экран → земля → экран» каждый раз даёт доли пикселя, и
4576
+ * подпись медленно, но безостановочно ползла бы по улице.
4577
+ */
4578
+ private rememberedMarks;
3737
4579
  /**
3738
4580
  * Попадает ли бокс в область раскладки — кадр, расширенный на `LAYOUT_MARGIN`.
3739
4581
  *
@@ -3760,6 +4602,45 @@ declare class SymbolRenderer {
3760
4602
  private shownAt;
3761
4603
  /** Есть ли в кадре подпись, которая ещё проявляется — по ней решается, нужен ли следующий кадр. */
3762
4604
  private fading;
4605
+ /**
4606
+ * Длительность проявления и угасания этого кадра: у своих тем `FADE_MS`, у стиля MapLibre — их
4607
+ * `ML_FADE_MS`. Решается по стилю в начале раскладки (`render`).
4608
+ */
4609
+ private fadeMs;
4610
+ /**
4611
+ * ★ ЗАТУХАНИЕ УШЕДШИХ ПОДПИСЕЙ — КАК У MAPLIBRE (02.10.2026).
4612
+ *
4613
+ * Подпись, проигравшая место (сосед важнее, карту сдвинули), раньше пропадала в тот же кадр, а
4614
+ * появлялась плавно — на глаз это читалось как мигание. MapLibre держит прозрачность на каждую
4615
+ * подпись и гасит невставшую за то же время, за которое проявляет (`symbolFadeChange`). Здесь —
4616
+ * «призрак»: стоявшая в прошлой раскладке подпись, которой места не нашлось, рисуется ещё
4617
+ * `FADE_MS` по своему последнему месту БЕЗ проверки и без занятия места — соседям она не мешает.
4618
+ * Запись: когда начала гаснуть и с какой прозрачности (гаснуть могла и недопроявившаяся).
4619
+ */
4620
+ private hiddenAt;
4621
+ /** Прозрачность призрака, которого сейчас раскладывают; `-1` — обычная раскладка. */
4622
+ private ghostOpacity;
4623
+ /**
4624
+ * ★ РЕШЕНИЕ «КТО ВИДЕН» — НЕ ЧАЩЕ РАЗА В 300 МС, КАК У MAPLIBRE (02.10.2026).
4625
+ *
4626
+ * Веб раскладывал подписи заново в КАЖДОМ кадре зума, и решение «помещается ли» перещёлкивалось
4627
+ * туда-обратно на долях пикселя: за один зум 14–26 подписей «встала — пропала — встала», каждый раз
4628
+ * с проявлением с нуля. Это и было мигание. MapLibre расставляет раз в 300 мс (чаще при отдалении,
4629
+ * `zoomAdjustment`), а между расстановками двигает уже выбранные. Здесь так же: пока камера едет,
4630
+ * между полными расстановками раскладываются ТОЛЬКО решённые подписи, без проверки столкновений, —
4631
+ * место у них и так своё.
4632
+ */
4633
+ private decidedKeys;
4634
+ private decidedAt;
4635
+ private decidedZoom;
4636
+ /** Камера прошлой раскладки: изменилась — значит, едем. */
4637
+ private lastCamera;
4638
+ /** Раскладывается решённая подпись между расстановками: место не проверяется. */
4639
+ private following;
4640
+ /** Текущая прозрачность проявления — без побочных эффектов, в отличие от `fadeOf`. */
4641
+ private fadeInOf;
4642
+ /** Текущая прозрачность гаснущей подписи; `0` — погасла. */
4643
+ private fadeOutOf;
3763
4644
  /**
3764
4645
  * ГОТОВЫЙ КАДР ПОДПИСЕЙ — ПОВТОРЯЕМ, ЕСЛИ НИЧЕГО НЕ МЕНЯЛОСЬ (30.09.2026).
3765
4646
  *
@@ -3780,6 +4661,20 @@ declare class SymbolRenderer {
3780
4661
  private styleIds;
3781
4662
  private nextStyleId;
3782
4663
  private idOf;
4664
+ /**
4665
+ * ★ ПРАВКА ПОКРАСКИ НЕ ПЕРЕСОБИРАЕТ РАСКЛАДКУ (03.10.2026).
4666
+ *
4667
+ * В подписи кадра стоял номер ОБЪЕКТА стиля, а редактор на каждую правку ставит новый стиль: смена
4668
+ * одного цвета заливки стоила полной расстановки подписей — 100–250 мс при CPU ×4 на стиле MapLibre в
4669
+ * 315 слоёв. Теперь в подпись идёт только то, от чего раскладка зависит: слои подписей с их
4670
+ * фильтрами, зумами и `layout`, из покраски — обводка (от неё считается разрядка букв,
4671
+ * `letterSpacingFor`) и наличие тени значка, плюс дымка (за ней подписи не ставятся). Цвета и
4672
+ * прозрачности читаются при отрисовке (`drawRanges`) у слоёв НОВОГО стиля — см. `render`.
4673
+ */
4674
+ private layoutStamps;
4675
+ private layoutStamp;
4676
+ /** Стиль, по слоям которого собраны `lastRanges`: покраска берётся у его слоёв. */
4677
+ private rangesStyle;
3783
4678
  private frameSignature;
3784
4679
  /** Прозрачность подписи: только что появилась — проявляется, стоит давно — единица. */
3785
4680
  private fadeOf;
@@ -3805,11 +4700,137 @@ declare class SymbolRenderer {
3805
4700
  * повезло оказаться раньше в списке после сдвига камеры на пиксель.
3806
4701
  */
3807
4702
  order(candidates: Candidate[]): void;
4703
+ /**
4704
+ * Стиль сменился, а раскладка та же (см. `layoutStamp`): диапазоны переводятся на слои НОВОГО стиля
4705
+ * — по ним `drawRanges` берёт покраску, — и ссылки на чужой цвет тоже.
4706
+ */
4707
+ private adoptStyle;
3808
4708
  /** Запомнить, что встало, — на этом держится липкость следующего кадра. */
3809
4709
  private rememberPlaced;
3810
4710
  render(ctx: Context, tr: Transform, sources: Map<string, TileSource>, style: Style): void;
3811
4711
  private collect;
3812
4712
  private place;
4713
+ /**
4714
+ * РАСКЛАДКА ПО ЯКОРЯМ (MapLibre): значок и подпись — каждый своим якорем к точке объекта.
4715
+ *
4716
+ * Отличие от блочной раскладки не косметическое. У нас значок с подписью — один блок, и на объекте
4717
+ * стоит СЕРЕДИНА блока; у MapLibre на объекте стоит то, что назвал якорь: у POI — центр значка, а
4718
+ * подпись висит под ним на `text-offset`. Стиль MapLibre, разложенный блоком, съезжает вверх на
4719
+ * полподписи, и подписи соседей сталкиваются не там, где у MapLibre.
4720
+ *
4721
+ * Место в сетке коллизий — общий охват значка и подписи: раздельные боксы у MapLibre нужны только
4722
+ * для `*-optional`, которого у нас пока нет.
4723
+ */
4724
+ private placeAnchored;
4725
+ /**
4726
+ * Размер значка в раскладке по якорям: у картинки спрайта — натуральный × `iconScale`; у
4727
+ * встроенного силуэта натурального нет — тогда `iconSize` в пикселях (16 × `iconScale`).
4728
+ */
4729
+ private anchoredIconSize;
4730
+ /** Перспектива у точки экрана — как `getPerspectiveRatio` MapLibre: 0.5 + 0.5 · до центра / до точки. */
4731
+ private perspectiveAt;
4732
+ /**
4733
+ * Отметить вставшее МЕСТО символа (для сверки с MapLibre и учёта экземпляров): ключ тот же, что у
4734
+ * подписей вдоль линии, — слой, текст, точка в меркаторе с шагом `INSTANCE_QUANT`.
4735
+ */
4736
+ private notePlaced;
4737
+ /** Ключ места символа: слой, текст и точка земли (квантованная `INSTANCE_QUANT`). */
4738
+ private instanceKey;
4739
+ /**
4740
+ * ★ КЛЮЧ МЕСТА ЛИНИИ В СТИЛЕ MapLibre — ПО ДОПУСКУ, КАК `CrossTileSymbolIndex` (03.10.2026).
4741
+ *
4742
+ * Места вдоль линии считаются в тайле с переувеличением по ЦЕЛОМУ зуму кадра (`bucketAnchors`), и
4743
+ * на каждом переходе через целый зум точная точка стрелки сдвигается на доли метра — ключ по
4744
+ * точной точке менялся, решение «стоит» терялось, и стрелка пропадала до следующей расстановки
4745
+ * (замер на 18→21: у всех стрелок разом серия «+16 −4»). MapLibre такие места сводит в один
4746
+ * символ: ключ по сетке в 2 пикселя тайла ДАННЫХ (`roundingFactor`), допуск одна клетка, и одно
4747
+ * прежнее место не достаётся двум новым (`zoomCrossTileIDs`). Здесь так же: ищем ближайшее место
4748
+ * того же слоя и текста из прошлого кадра в пределах `2⁻⁸ / 2^z` меркатора (2 px тайла z).
4749
+ */
4750
+ private mlSpotKey;
4751
+ /**
4752
+ * Места линий стиля MapLibre прошлого и этого кадра — «слой|текст» → клетка `ML_SPOT_CELL` (номером) →
4753
+ * места (см. `mlSpotKey`). Двухуровневые, а не строкой «слой|текст|x|y»: поиск смотрит девять клеток,
4754
+ * и девять свежих строк на каждое место каждой линии каждый кадр были заметной частью раскладки.
4755
+ */
4756
+ private mlSpotsPrev;
4757
+ private mlSpotsNow;
4758
+ /** Ключи мест, уже отданные в этом кадре: одно прежнее место — одному новому. */
4759
+ private mlSpotsUsed;
4760
+ /**
4761
+ * ★ ПРОПАВШЕЕ МЕСТО ГАСНЕТ, А НЕ ИСЧЕЗАЕТ (03.10.2026).
4762
+ *
4763
+ * На переходе через целый зум места линии пересчитываются с новым переувеличением, и часть прежних
4764
+ * мест перестаёт существовать вовсе: новых точек рядом нет. Такая стрелка исчезала в тот же кадр, а
4765
+ * её замена ждала расстановки (до 300 мс) и проявлялась с нуля — провал, который на экране и есть
4766
+ * мигание (замер на 16→20, наклон 46°: у 8 стрелок серия «+12 −27 +…» на переходе через z17).
4767
+ * MapLibre держит старый тайл до загрузки нового и ещё `fadeDuration` после (`holdingForFade`), так
4768
+ * что пропавший символ у них гаснет на своём месте. Здесь так же: стоявшее в прошлом кадре место,
4769
+ * которого в этом кадре нет ни в одной линии, гаснет по запомненной точке без занятия места.
4770
+ */
4771
+ private mlShownNow;
4772
+ private mlShownPrev;
4773
+ private mlOrphans;
4774
+ /** Прозрачность осиротевшего места: только угасание; `null` — погасло. */
4775
+ private mapLibreOrphanAlpha;
4776
+ /**
4777
+ * Осиротевшие места этого кадра: стояли (или гасли сиротами) в прошлом, а в этом их не посетила ни
4778
+ * одна линия. Собираются по кандидату, чтобы раскладка шла тем же путём, что и у живых мест.
4779
+ */
4780
+ private collectOrphans;
4781
+ /**
4782
+ * Решение и прозрачность места линии в стиле MapLibre (см. `mapLibreSpot`). `fits` — прошло ли
4783
+ * место проверку (спрашивается только на расстановке). Возвращает прозрачность или `null`, если
4784
+ * место не рисуется. Гаснущее место получает запись в `hiddenAt`, вставшее — возраст в `shownAt`.
4785
+ */
4786
+ private mapLibreSpotAlpha;
4787
+ /** Вид бакета для мест MapLibre — один объект на бакет, чтобы по нему держать кеш мест. */
4788
+ private bucketViews;
4789
+ private bucketView;
4790
+ /** Места MapLibre по бакету: слой·целый зум·версия картинок → места каждой линии (единицы ML_EXTENT). */
4791
+ private mlAnchorCache;
4792
+ /**
4793
+ * МЕСТА ВДОЛЬ ЛИНИЙ — СРАЗУ НА ВЕСЬ БАКЕТ ТАЙЛА, в его порядке, как `performSymbolLayout` MapLibre.
4794
+ *
4795
+ * Иначе нельзя из-за отсева повторов (`anchorIsTooClose`: тот же текст в тайле ближе полушага шага
4796
+ * второй раз не ставится). Он идёт по ВСЕМ линиям бакета по порядку — и линия, ушедшая за камеру,
4797
+ * тоже глушит свои повторы у соседей. Считая места по кандидатам в кадре, мы теряли эти линии, и
4798
+ * у соседних вставали лишние щиты. От камеры места не зависят, поэтому и кешируются до смены
4799
+ * целого зума; кеш не ставится, пока не приехали глифы и картинки — по недостающим ширины не те.
4800
+ */
4801
+ private bucketAnchors;
4802
+ /**
4803
+ * МЕСТА ВДОЛЬ ЛИНИИ ПО АЛГОРИТМУ MapLibre (`text/maplibre-anchors.ts`), в меркаторе.
4804
+ *
4805
+ * Переувеличение — по ЦЕЛОМУ зуму кадра против зума данных тайла, как у тайлов MapLibre с
4806
+ * `overscaledZ`: между целыми зумами места не переезжают, на целом — пересчитываются. Угол — в
4807
+ * координатах тайла (вдоль звена); экранный угол раскладка берёт сама, проецируя точку чуть дальше.
4808
+ */
4809
+ private mapLibreAnchors;
4810
+ /** Места MapLibre для изогнутой строки — как начала строки на экранной линии `screen`. */
4811
+ private mapLibreTextAts;
4812
+ /** Якоря MapLibre (меркатор, парами) к последнему `mapLibreTextAts` — по ним ключи экземпляров. */
4813
+ private mlTextMarks;
4814
+ /**
4815
+ * ЗНАЧКИ, ЛЕЖАЩИЕ НА ДОРОГЕ (`icon-pitch-alignment: map`) — стрелки направления у MapLibre.
4816
+ *
4817
+ * Значок стоящий и значок лежащий отличаются не поворотом, а ПЛОСКОСТЬЮ: лежащий сплющивается
4818
+ * наклоном камеры, как сам асфальт, и дальние стрелки мельче ближних. Раскладка поэтому идёт в
4819
+ * плоском виде сверху (`flatCamera`) — там значок обычный прямоугольник, повёрнутый по звену, — а
4820
+ * его углы переносятся на экран через землю (`toGround`), тем же приёмом, что буквы названий улиц
4821
+ * «по карте». Бокс коллизий — охват перенесённых углов: место значок занимает там, где виден.
4822
+ */
4823
+ private placeIconsOnGround;
4824
+ /**
4825
+ * ЗНАЧКИ И ПОДПИСИ «СТОЯ» ВДОЛЬ ЛИНИИ — как `symbol-placement: line` у MapLibre для стрелок
4826
+ * одностороннего движения и щитов трасс.
4827
+ *
4828
+ * Узлы — та же сетка мест на земле, что у названий улиц (`groundGrid`): через `symbolSpacing`
4829
+ * пикселей целого зума, поэтому при панораме стрелки едут вместе с асфальтом, а не скользят по нему.
4830
+ * В каждом узле ставится обычная раскладка по якорям; значок при `iconAlongLine` повёрнут по
4831
+ * звену (плюс свой `iconRotate`), подпись всегда горизонтальна.
4832
+ */
4833
+ private placePointsOnLine;
3813
4834
  /**
3814
4835
  * Подпись вдоль линии: по квадру на глиф, каждый со своим поворотом.
3815
4836
  *
@@ -3853,6 +4874,26 @@ declare class SymbolRenderer {
3853
4874
  private requestMissing;
3854
4875
  destroy(ctx: Context): void;
3855
4876
  }
4877
+ /**
4878
+ * Линия объекта из тайла в экранные координаты.
4879
+ *
4880
+ * Обрывается на первой точке за камерой: дальше ломаная не имеет смысла, а
4881
+ * оставшегося куска подписи может хватить. Пустой результат — объект не виден.
4882
+ */
4883
+ declare function projectLine(tr: Transform, lines: Int16Array, starts: Uint32Array, index: number, originX: number, originY: number, scale: number,
4884
+ /** Высота земли под точкой, метры — на рельефе название улицы идёт по склону вместе с дорогой. */
4885
+ elevationAt?: ((mercX: number, mercY: number) => number) | null,
4886
+ /** Сюда — те же вершины в меркаторе, парами: по ним сетка мест отмеряется по земле. */
4887
+ world?: number[],
4888
+ /**
4889
+ * Стиль MapLibre: пропускать НАЧАЛО линии за камерой, а не бросать линию целиком (на наклоне улица
4890
+ * часто начинается за спиной, а видна дальше — у MapLibre её места стоят, у нас линия выпадала),
4891
+ * и звено, пересекающее плоскость камеры, РЕЗАТЬ по ближней плоскости, а не терять с вершиной.
4892
+ */
4893
+ skipLeading?: boolean): {
4894
+ x: number;
4895
+ y: number;
4896
+ }[] | undefined;
3856
4897
  /**
3857
4898
  * Куда ставить подпись у слоя со стрелкой на земле.
3858
4899
  *
@@ -4081,8 +5122,23 @@ declare class IndoorMask {
4081
5122
  * Берётся из бакета оболочки (`class = level`) — это и есть след здания на
4082
5123
  * уровне. Пересчитывается при смене этажа или набора тайлов, а не каждый кадр:
4083
5124
  * разбор бакета стоит заметно дороже отрисовки.
5125
+ *
5126
+ * ★ ПЕРЕСЧЁТ — ИЗ КЕША, А НЕ ЗАНОВО (03.10.2026). Набор тайлов меняется на каждом шаге панорамы, и
5127
+ * каждая смена разбирала заново ВСЁ: контуры планов всех тайлов, рамки всех зданий всех тайлов
5128
+ * (перебор треугольников) и урезанные индексы каждого тайла с заливкой в видеопамять. На стиле osmgl в
5129
+ * 304 слоя это была треть работы кадра при панораме — до 0.6 с длинной задачей при CPU ×4 (замер
5130
+ * редактора стилей: `collectHidden` + `prepare` + `cutHidden`). Теперь контур тайла плана и центры
5131
+ * домов тайла считаются один раз на меш (`footprintOf`, `centersOf`), проверка «внутри плана» идёт
5132
+ * по сетке треугольников контура, а урезанные индексы пересобираются, только если сменился набор
5133
+ * скрытых домов ЭТОГО тайла (`indicesFor`).
4084
5134
  */
4085
5135
  sync(sources: Map<string, TileSource>, style: Style, zoom: number): void;
5136
+ /** Сетки контуров планов текущей маски — по ним идёт проверка «внутри» (см. `inside`). */
5137
+ private grids;
5138
+ /** Номер состава `hidden`: растёт на каждое изменение (см. `indicesFor`). */
5139
+ private hiddenVersion;
5140
+ /** Лежит ли точка в контуре плана: то же, что `insideFootprint(parts, x, y, 0)`, но по сеткам. */
5141
+ private inside;
4086
5142
  /**
4087
5143
  * Отметить дома этого тайла, попавшие под план.
4088
5144
  *
@@ -4091,13 +5147,7 @@ declare class IndoorMask {
4091
5147
  */
4092
5148
  private collectHidden;
4093
5149
  destroy(): void;
4094
- private free;
4095
- /**
4096
- * Индексы здания без тех фич, что стоят под открытым этажом.
4097
- *
4098
- * Считается ОДИН раз на тайл и на маску: перебор треугольников с проверкой
4099
- * принадлежности — работа для загрузки, а не для кадра.
4100
- */
5150
+ private free;
4101
5151
  /**
4102
5152
  * Отметить дома под планом по ВСЕМ видимым тайлам сразу.
4103
5153
  *
@@ -4105,8 +5155,15 @@ declare class IndoorMask {
4105
5155
  * просмотренном тайле, в этом кадре останется наполовину видимым.
4106
5156
  */
4107
5157
  prepare(source: TileSource, layerId: string): void;
4108
- /** Геометрия зданий тайла без тех, что стоят под открытым этажом. */
4109
- indicesFor(ctx: Context, tile: Tile, layerId: string, id: TileID, vbo: VertexBuffer, locations: Map<string, number>): {
5158
+ /**
5159
+ * Геометрия зданий тайла без тех, что стоят под открытым этажом.
5160
+ *
5161
+ * Считается ОДИН раз на тайл и на состав скрытого В ЭТОМ ТАЙЛЕ: перебор треугольников и заливка
5162
+ * индексов — работа для загрузки, а не для кадра. Подпись — номера скрытых домов тайла.
5163
+ */
5164
+ indicesFor(ctx: Context, tile: Tile, layerId: string, id: TileID, vbo: VertexBuffer, locations: Map<string, number>,
5165
+ /** Цвет из данных: без него урезанный дом рисовался бы чёрным (см. `cutHidden`). */
5166
+ dataBuffer?: VertexBuffer | null): {
4110
5167
  vao: VertexArray;
4111
5168
  count: number;
4112
5169
  } | null;
@@ -5537,6 +6594,11 @@ declare class LayerRenderer {
5537
6594
  private materialTime;
5538
6595
  /** Хоть один материал в кадре анимируется — значит нужен следующий кадр. */
5539
6596
  private materialAnimating;
6597
+ /**
6598
+ * Следующий кадр нужен ТОЛЬКО ради материала (бегущей воды): карта может рисовать его реже
6599
+ * (`MATERIAL_IDLE_MS` у `Map`) — так же, как натив.
6600
+ */
6601
+ onlyMaterialsAnimating: boolean;
5540
6602
  /**
5541
6603
  * Материал ТЕКУЩЕГО слоя анимирован (бегущая вода) — но кадр просим, только если слой что-то
5542
6604
  * нарисовал.
@@ -5614,6 +6676,12 @@ declare class LayerRenderer {
5614
6676
  featureId: number;
5615
6677
  color: string;
5616
6678
  opacity: number;
6679
+ /**
6680
+ * Куски БЕЗЫМЯННОГО дома: ключ тайла → номер куска в нём (см. `anonymousParts`). Задан —
6681
+ * подсвечиваются только эти тайлы и каждый своим номером: номер такого дома свой в каждом
6682
+ * тайле, и «по номеру во всех тайлах» покрасил бы у соседа чужой дом.
6683
+ */
6684
+ parts?: ReadonlyMap<string, number>;
5617
6685
  } | null;
5618
6686
  /** Куски подсветки по тайлам: `null` — в этом тайле такой фичи нет. */
5619
6687
  private highlightGpu;
@@ -5750,8 +6818,9 @@ declare class LayerRenderer {
5750
6818
  /**
5751
6819
  * Какие фичи ЕСТЬ в бакете. Живёт до перезагрузки тайла и переживает смену набора замен.
5752
6820
  *
5753
- * Ограничен по размеру грубо, сбросом целиком: записи вытесненных тайлов сами по себе не уходят,
5754
- * а точный учёт стоил бы больше, чем пересчёт пары сотен списков раз в долгую сессию.
6821
+ * Держится на самом буфере номеров фич (`WeakMap`): уходит вместе с тайлом, и ограничивать его
6822
+ * размер сбросом целиком не нужно. Строится по отрезкам фич (`featureRuns`), а не по номеру на
6823
+ * каждый треугольник: у тайла домов их сотни тысяч, отрезков — тысячи.
5755
6824
  */
5756
6825
  private bucketIdCache;
5757
6826
  /** Разобранные поверхности без заменённых фигур. Сбрасывается вместе с `hiddenCut`. */
@@ -5812,6 +6881,27 @@ declare class LayerRenderer {
5812
6881
  /** Места атрибутов у шейдера пачки: и вершинные, и на экземпляр. */
5813
6882
  private modelInstLocs;
5814
6883
  init(ctx: Context): void;
6884
+ /** Программы из `init`, о готовности которых ещё не спрашивали; пусто — `finishInit` пройден. */
6885
+ private initShaders;
6886
+ private initCtx;
6887
+ /**
6888
+ * Программы карты ещё собираются: кадр рисовать нечем.
6889
+ *
6890
+ * ★ КАРТА НЕ ЖДЁТ КОМПИЛЯТОР (03.10.2026). Создание карты было одной задачей главного потока на
6891
+ * 0.8–0.9 с: два десятка программ собирались по очереди, и каждую ждали. Теперь `init` только
6892
+ * отправляет их компилятору, а кадр, пока он работает (`KHR_parallel_shader_compile`), спрашивает
6893
+ * готовность БЕЗ ожидания и пропускает отрисовку — тайлы тем временем уже грузятся. Без расширения
6894
+ * ответ «готово» приходит сразу, и первый кадр подождёт сборку, как раньше.
6895
+ */
6896
+ get programsPending(): boolean;
6897
+ /**
6898
+ * ВОПРОСЫ К ПРОГРАММАМ — ТОЛЬКО ПОСЛЕ ТОГО, КАК ЗАКАЗАНЫ ВСЕ.
6899
+ *
6900
+ * Номер атрибута, юниформ — любой вопрос к программе ждёт конца её сборки (см. `Shader.pending`).
6901
+ * Стоял такой вопрос между созданием программ — и следующие уходили компилятору только после того,
6902
+ * как была собрана эта: потоки компилятора простаивали, а главный поток ждал по очереди.
6903
+ */
6904
+ private finishInit;
5815
6905
  /** true, если что-то ещё анимируется и нужен следующий кадр. */
5816
6906
  render(ctx: Context, tr: Transform, sources: Map<string, TileSource>, style: Style, now: number, rasterSources?: Map<string, RasterSource>): boolean;
5817
6907
  /**
@@ -5860,6 +6950,24 @@ declare class LayerRenderer {
5860
6950
  * нужно знать, какому куску мира она принадлежит, — отсюда `u_tile_merc`.
5861
6951
  * На плоской карте всё наоборот: матрица своя у каждого тайла.
5862
6952
  */
6953
+ /**
6954
+ * СЛОЙ, УПАВШИЙ ПРИ ОТРИСОВКЕ, ПРОПУСКАЕТСЯ, А НЕ ГАСИТ КАДР.
6955
+ *
6956
+ * Исключение из одного слоя раньше выходило из `render` целиком: кадр обрывался на полпути, и
6957
+ * так каждый следующий — карта становилась пустой из-за одного поля одного слоя (поймано в
6958
+ * редакторе стилей: строка в прозрачности заливки). Значения не того типа теперь отсекает разбор
6959
+ * стиля (`style/sanitize.ts`), а это — вторая линия: любая другая беда в слое стоит только этого
6960
+ * слоя. Во всех проходах, перебирающих слои, тело цикла обёрнуто в `try`; в V8 он бесплатен, пока
6961
+ * ничего не бросают, — покадровый путь не дорожает (замер — README, «Справочник стиля и проверка»).
6962
+ *
6963
+ * Сказано ОДИН раз на слой и причину: слой бросает каждый кадр, а консоль — не журнал кадров.
6964
+ * Пропуск не запоминается: правка, починившая слой, видна сразу, без смены стиля.
6965
+ */
6966
+ private readonly reported;
6967
+ /** Обработчик падений слоёв (карта превращает их в событие `error`). */
6968
+ onLayerError: ((what: string, error: unknown) => void) | null;
6969
+ private reportFailure;
6970
+ private layerFailed;
5863
6971
  /**
5864
6972
  * ПЛОСКИЙ ПРОХОД: земля в порядке стиля, без теста глубины.
5865
6973
  *
@@ -6088,11 +7196,21 @@ declare class LayerRenderer {
6088
7196
  * переключение теней соберёт свой набор один раз. Фасад зависит от зума — перебираем зумы слоя.
6089
7197
  */
6090
7198
  private warmVariants;
7199
+ /** Варианты программ, нужные стилю из `warmVariants`: по ним кадр решает, можно ли его рисовать. */
7200
+ private styleShaders;
7201
+ /** Стиль последнего кадра, нарисованного целиком, — его показываем, пока собираются программы нового. */
7202
+ private drawnStyle;
7203
+ /** С какого кадра ждём программы нового стиля; -1 — не ждём. */
7204
+ private styleWaitSince;
7205
+ /** Хоть один вариант стиля ещё собирается — спрашивается без ожидания (см. `Shader.ready`). */
7206
+ private variantsPending;
6091
7207
  /** Рисунок вдоль линии: 0 сплошная, 1 штрих, 2 шевроны, 3 стрелки, 4 только стрелки, 5 стрелки-штрих. */
6092
7208
  private linePatternOf;
6093
7209
  /** Программа линий под этот слой и кадр — тот же приём, что у поверхности (см. surfaceShader). */
6094
7210
  private lineShader;
6095
7211
  private applyMaterial;
7212
+ /** Метров на аппаратный пиксель у самого близкого к камере края земли — на кадр (см. `applyMaterial`). */
7213
+ private finestMeters;
6096
7214
  private drawFill;
6097
7215
  private drawLine;
6098
7216
  /**
@@ -6203,6 +7321,15 @@ declare class LayerRenderer {
6203
7321
  */
6204
7322
  private growScale;
6205
7323
  private applyLight;
7324
+ /**
7325
+ * СВЕТ MapLibre — юниформы ровно как у `fillExtrusionUniformValues` (`fill_extrusion_program.ts`).
7326
+ *
7327
+ * Положение `[r, азимут, полярный]` переводится `sphericalToCartesian` (азимут +90°, длина r НЕ
7328
+ * нормируется — у MapLibre вектор длиной 1.15, и крыша получает dot = 0.996, а не 0.866). При
7329
+ * `anchor: viewport` вектор поворачивается курсом камеры (`mat3.fromRotation`), и свет остаётся
7330
+ * на месте относительно экрана. Оси те же, что у тайла: x на восток, y на юг.
7331
+ */
7332
+ private applyMapLibreLight;
6206
7333
  /** Буферы GPU создаются лениво и живут в Tile.buckets — источник их и уничтожит. */
6207
7334
  /**
6208
7335
  * Собрана ли поверхность этого слоя как ПЛОСКАЯ заливка.
@@ -6390,6 +7517,124 @@ declare class Objects3DManager {
6390
7517
  clear(drop: (m: Model) => void): void;
6391
7518
  }
6392
7519
 
7520
+ /**
7521
+ * ИМПОРТ СТИЛЯ MapLibre v8 В osmgl.
7522
+ *
7523
+ * Стиль MapLibre подключается как есть (`map.setStyle(url)`), а не переписывается заранее руками:
7524
+ * такие стили приходят по ссылке во время работы и меняются без нас. Разбор соответствия всех
7525
+ * полей и план — `porting/maplibre-style-import.md`.
7526
+ *
7527
+ * Импортёр — ЧИСТЫЙ ПЕРЕВОД: переименование полей, перевод единиц, старые функции → выражения,
7528
+ * старые фильтры → наша форма. Он ничего не «приближает»: если движок чего-то не умеет, свойство
7529
+ * попадает в отчёт (`unsupported`), а не заменяется похожим. Пробел движка закрывается в движке.
7530
+ *
7531
+ * Сеть здесь не нужна: TileJSON-источники разворачивает `loadMapLibreStyle`, а сюда приходит
7532
+ * уже готовый стиль. Так перевод проверяется тестами без сервера.
7533
+ */
7534
+ /** Стиль MapLibre — в том объёме, в каком его читает импортёр. */
7535
+ interface MapLibreStyle {
7536
+ version: 8;
7537
+ name?: string;
7538
+ metadata?: unknown;
7539
+ center?: [number, number];
7540
+ zoom?: number;
7541
+ bearing?: number;
7542
+ pitch?: number;
7543
+ sources: Record<string, MapLibreSource>;
7544
+ sprite?: string | {
7545
+ id: string;
7546
+ url: string;
7547
+ }[];
7548
+ glyphs?: string;
7549
+ layers: MapLibreLayer[];
7550
+ [key: string]: unknown;
7551
+ }
7552
+ interface MapLibreSource {
7553
+ type: string;
7554
+ url?: string;
7555
+ tiles?: string[];
7556
+ minzoom?: number;
7557
+ maxzoom?: number;
7558
+ scheme?: 'xyz' | 'tms';
7559
+ tileSize?: number;
7560
+ encoding?: 'mapbox' | 'terrarium';
7561
+ data?: unknown;
7562
+ coordinates?: unknown;
7563
+ [key: string]: unknown;
7564
+ }
7565
+ interface MapLibreLayer {
7566
+ id: string;
7567
+ type: string;
7568
+ source?: string;
7569
+ 'source-layer'?: string;
7570
+ minzoom?: number;
7571
+ maxzoom?: number;
7572
+ filter?: unknown;
7573
+ metadata?: unknown;
7574
+ layout?: Record<string, unknown>;
7575
+ paint?: Record<string, unknown>;
7576
+ }
7577
+ interface MapLibreImport {
7578
+ style: StyleSpec;
7579
+ /** Шаблон глифов (`{fontstack}`/`{range}`), если стиль его задал. */
7580
+ glyphs?: string;
7581
+ /** Спрайты: основной — с пустым `id`, дополнительные — со своим (имена значков `id:имя`). */
7582
+ sprites: {
7583
+ id: string;
7584
+ url: string;
7585
+ }[];
7586
+ /** Камера стиля — применяется только при первой загрузке, как у MapLibre. */
7587
+ camera: {
7588
+ center?: [number, number];
7589
+ zoom?: number;
7590
+ bearing?: number;
7591
+ pitch?: number;
7592
+ roll?: number;
7593
+ /** Высота центра над уровнем моря, м (`centerAltitude`). */
7594
+ centerAltitude?: number;
7595
+ };
7596
+ /** Проекция стиля (`projection.type`): у движка есть меркатор и глобус. */
7597
+ projection?: 'mercator' | 'globe';
7598
+ /** Умолчания общего состояния (`state`): имя → значение `default`. */
7599
+ state?: Record<string, unknown>;
7600
+ /** Стиль с признаком v2 (`metadata["osmgl:version"]: 2`) — свой, а не чужой стиль MapLibre. */
7601
+ osmgl: boolean;
7602
+ /** Что не перенесено: «тип.свойство» → число слоёв. Пусто — стиль перенесён целиком. */
7603
+ unsupported: Record<string, number>;
7604
+ }
7605
+ declare function isMapLibreStyle(value: unknown): value is MapLibreStyle;
7606
+ /**
7607
+ * Перевести стиль MapLibre в стиль osmgl.
7608
+ *
7609
+ * У источников `url` уже должен быть шаблоном тайлов (`.../{z}/{x}/{y}.pbf`): TileJSON
7610
+ * разворачивает `loadMapLibreStyle`. Источник с `tiles[]` годится как есть.
7611
+ */
7612
+ declare function importMapLibreStyle(ml: MapLibreStyle): MapLibreImport;
7613
+ /**
7614
+ * Значение свойства: старая функция (`{stops, base, type, property}`) → выражение, остальное как есть.
7615
+ *
7616
+ * Правила — `function/convert.ts` из maplibre-style-spec. Вид функции по умолчанию: у числа и
7617
+ * цвета — `exponential` (интерполяция), у всего прочего — `interval` (ступенька).
7618
+ */
7619
+ declare function convertValue(value: unknown): unknown;
7620
+ /**
7621
+ * Старый фильтр остаётся старым — `matchesFilter` его понимает, включая `$type` и `$id`. Переводим
7622
+ * только `none`, которого у нас нет, и проходим вглубь `all`/`any`. Фильтр-выражение — как есть.
7623
+ */
7624
+ declare function convertFilter(filter: unknown): Filter;
7625
+ /**
7626
+ * Загрузить стиль MapLibre по ссылке (или взять готовый объект) и перевести его.
7627
+ *
7628
+ * Разворачивает TileJSON-источники (`url` без `{z}`) в шаблон тайлов и зумы, относительные адреса
7629
+ * приводит к абсолютным по адресу стиля — как это делает MapLibre.
7630
+ *
7631
+ * Строка — это ссылка ЛИБО имя встроенного стиля (`mlight-city`, `mnight-city`, см.
7632
+ * `style/maplibre-styles.ts`). Встроенный стиль за JSON в сеть не ходит, а относительные адреса в
7633
+ * нём разрешаются по адресу, с которого снят снимок, — как если бы его скачали. У готового объекта
7634
+ * адреса нет, и тогда его можно передать третьим аргументом (`baseUrl`).
7635
+ */
7636
+ declare function loadMapLibreStyle(input: string | MapLibreStyle, fetcher?: (url: string) => Promise<unknown>, baseUrl?: string | null): Promise<MapLibreImport>;
7637
+
6393
7638
  /**
6394
7639
  * ЧТО НАРИСОВАНО ПОД ТОЧКОЙ — с теми же именами полей, что у MapLibre.
6395
7640
  *
@@ -6536,6 +7781,20 @@ declare function pickFill(tr: Transform, source: TileSource, layerIds: string[],
6536
7781
  },
6537
7782
  /** Высота земли, метры: план этажа на рельефе лежит на склоне, как и всё остальное. */
6538
7783
  groundAtMerc?: (mx: number, my: number) => number): BuildingHit | null;
7784
+ /**
7785
+ * КУСКИ БЕЗЫМЯННОГО ДОМА ПО ТАЙЛАМ: ключ тайла → номер куска в нём (02.10.2026).
7786
+ *
7787
+ * Номер дома без `osm_id` свой в каждом тайле (−1, −2, … по порядку фич), и подсветка «по номеру во
7788
+ * всех тайлах сразу», которой выделяется обычный дом, покрасила бы в соседнем тайле чужой дом, а
7789
+ * свою половину оставила бы серой. Сшить номера заранее нечем: воркер видит один тайл.
7790
+ *
7791
+ * Связываем по ГЕОМЕТРИИ, в момент выбора. Кусок, разрезанный границей, кладёт вершины крыши на
7792
+ * линию разреза — по одной на каждый конец каждого разреза, — и у продолжения в соседнем тайле они
7793
+ * стоят там же (с поправкой на нахлёст `bleedRing`, до единицы вдоль стены). Продолжением считаем
7794
+ * безымянный кусок соседа, у которого совпали ДВЕ такие точки и больше: у дома, лишь касающегося
7795
+ * нашего углом, общая точка одна. Обход идёт дальше от найденных кусков — дом бывает и на три тайла.
7796
+ */
7797
+ declare function anonymousParts(source: TileSource, layerId: string, tile: TileID, featureId: number): Map<string, number>;
6539
7798
  /**
6540
7799
  * Треугольники одной фичи — из них строится подсветка.
6541
7800
  *
@@ -6869,6 +8128,8 @@ interface LinePlacement {
6869
8128
  path: ScreenPoint[];
6870
8129
  /** Где строка начинается — расстояние от начала куска линии, пиксели. */
6871
8130
  at: number;
8131
+ /** Множитель кегля этого экземпляра (стиль MapLibre на наклоне, см. `scalesAt`); нет — 1. */
8132
+ scale?: number;
6872
8133
  }
6873
8134
  /** Настройки раскладки; всё необязательное — по умолчанию как у Mapbox. */
6874
8135
  interface LinePlacementOptions {
@@ -6882,6 +8143,8 @@ interface LinePlacementOptions {
6882
8143
  fontSize?: number;
6883
8144
  /** Предельная сумма изломов в окне, градусы. */
6884
8145
  maxAngle?: number;
8146
+ /** Не раздвигать буквы на поворотах (MapLibre этого не делает: буквы стоят ровно по смещениям). */
8147
+ noCurveGap?: boolean;
6885
8148
  /**
6886
8149
  * Годится ли конкретная раскладка — коллизии, поля экрана, что угодно вызывающего.
6887
8150
  *
@@ -7148,6 +8411,11 @@ declare function sdfFromAlpha(alpha: Uint8Array | Uint8ClampedArray, width: numb
7148
8411
  */
7149
8412
  interface GlyphLookup {
7150
8413
  get(code: number): Glyph | undefined;
8414
+ /**
8415
+ * Меняется, когда меняется набор глифов (догрузился диапазон, сменился адрес). По нему
8416
+ * `shapeTextCached` понимает, что прежняя раскладка устарела. Нет поля — кеша нет.
8417
+ */
8418
+ readonly version?: number;
7151
8419
  }
7152
8420
  /** Один разложенный символ: где стоит и какой глиф рисовать. */
7153
8421
  interface ShapedGlyph {
@@ -7162,6 +8430,7 @@ interface Shaped {
7162
8430
  width: number;
7163
8431
  height: number;
7164
8432
  }
8433
+ declare function shapeTextCached(text: string, source: GlyphLookup, maxLineChars?: number, letterSpacing?: number, wrapWidth?: number): Shaped | null;
7165
8434
  /**
7166
8435
  * Раскладка строки в позиции глифов.
7167
8436
  *
@@ -7173,7 +8442,12 @@ interface Shaped {
7173
8442
  /**
7174
8443
  * @param letterSpacing разрядка между буквами в единицах кегля 24 — см. `letterSpacingFor`
7175
8444
  */
7176
- declare function shapeText(text: string, source: GlyphLookup, maxLineChars?: number, letterSpacing?: number): Shaped | null;
8445
+ declare function shapeText(text: string, source: GlyphLookup, maxLineChars?: number, letterSpacing?: number,
8446
+ /**
8447
+ * Перенос ПО ШИРИНЕ вместо числа знаков — `text-max-width` у MapLibre, в единицах кегля 24
8448
+ * (em × 24). Ноль/не задано — прежний перенос по знакам.
8449
+ */
8450
+ wrapWidth?: number): Shaped | null;
7177
8451
 
7178
8452
  /**
7179
8453
  * Сокращение родового слова в названии улицы: «улица Истаравшан» → «ул. Истаравшан».
@@ -7340,6 +8614,35 @@ declare function footprintOf(picked: {
7340
8614
  indices: Uint32Array;
7341
8615
  }, extent: number, id: TileID): Float64Array;
7342
8616
 
8617
+ /**
8618
+ * ИНДЕКСЫ МЕША БЕЗ ЧАСТИ ФИЧ — копированием ОТРЕЗКОВ, а не по треугольнику.
8619
+ *
8620
+ * Резка скрытых домов (замена 3D-моделью, сшитые по границе тайла, план этажа) шла по каждому
8621
+ * треугольнику: проверка по множеству и `push` в массив чисел. В тайле плотного центра у слоя домов
8622
+ * около 400 тысяч треугольников, и сборка швов при панораме пересобирает такой тайл на каждый новый
8623
+ * сшитый дом, причём для каждого объёмного слоя отдельно: 3–12 мс на резку при CPU ×1, треть работы
8624
+ * кадра при ×4 (замер: стиль osmgl в 304 слоя, `cutHidden`).
8625
+ *
8626
+ * Треугольники фичи в буфере лежат подряд (генератор пишет фичу целиком), поэтому меш один раз
8627
+ * раскладывается на отрезки «фича → треугольники [от, до)», и резка проверяет по множеству фичу, а не
8628
+ * треугольник, а копирует индексы целыми отрезками. Порядок треугольников прежний — ответ совпадает с
8629
+ * перебором один в один, и если фича почему-то лежит в нескольких местах, это просто несколько отрезков.
8630
+ */
8631
+ interface FeatureRuns {
8632
+ /** Фича отрезка. */
8633
+ fids: Float64Array;
8634
+ /** Первый треугольник отрезка; конец — начало следующего (у последнего — `triangles`). */
8635
+ starts: Uint32Array;
8636
+ triangles: number;
8637
+ }
8638
+ /** Отрезки фич меша; считаются один раз на буфер номеров фич (он живёт, пока жив меш). */
8639
+ declare function featureRuns(featureIds: ArrayBuffer, indexCount: number): FeatureRuns;
8640
+ /**
8641
+ * Индексы без треугольников фич из `hidden`. `null` — выбрасывать нечего (все фичи остаются).
8642
+ * Совпадает с перебором `for t: if (!hidden.has(ids[t])) kept.push(...)`.
8643
+ */
8644
+ declare function keptIndices(indices: Uint32Array, featureIds: ArrayBuffer, hidden: ReadonlySet<number>): Uint32Array | null;
8645
+
7343
8646
  declare class GroundArrows {
7344
8647
  private vbo;
7345
8648
  private ibo;
@@ -7561,6 +8864,12 @@ interface LineOptions {
7561
8864
  * целый и один на бакет, а подход к эстакаде набирает высоту непрерывно.
7562
8865
  */
7563
8866
  liftAt?: (properties: Record<string, unknown>) => LiftField | null;
8867
+ /**
8868
+ * ФОРМА КОНЦОВ И СТЫКОВ (line-cap / line-join MapLibre): в вершину вместо ширины из данных и
8869
+ * подъёма идут направление предыдущего звена и признаки «начало/конец линии» — по ним шейдер
8870
+ * (`LINE_SHAPE`) режет торцы и строит острые стыки. Ширины из данных и подъёма у таких слоёв нет.
8871
+ */
8872
+ joinData?: boolean;
7564
8873
  }
7565
8874
  declare function generateLine(layer: VectorTileLayer, filter: Filter | undefined, maxSegment?: number,
7566
8875
  /** Уровни поверхностей тайла: по ним дорога ложится на то, по чему идёт. Нет — лежит на нуле. */
@@ -8738,6 +10047,35 @@ interface PierOptions {
8738
10047
  */
8739
10048
  declare function bridgePiers(axis: Ring | number[], profile: BridgeProfile, opts: PierOptions): Pier[];
8740
10049
 
10050
+ /**
10051
+ * СТИЛЬ ПО ССЫЛКЕ — ЛЮБОГО ИЗ ДВУХ ФОРМАТОВ.
10052
+ *
10053
+ * Редактор стилей публикует по адресу `/styles/{slug}.json` и стили MapLibre (v8), и наши (v1), и
10054
+ * обёртки вокруг JSON нет намеренно: файл годится для прямой передачи в `map.setStyle(url)`.
10055
+ * Формат определяется по полю `version` самого JSON — ровно так же его определяет сервер
10056
+ * (`docs/map-v2/STYLE-EDITOR.md`, §2), поэтому разногласия «сервер принял, карта не поняла» нет.
10057
+ *
10058
+ * Строка — это ссылка ЛИБО имя встроенного стиля: `mlight-city`/`mnight-city` (MapLibre) или
10059
+ * `osm-3d`/`osm-city`/… (наши, `OSMGL_STYLES`). Встроенный за JSON в сеть не ходит.
10060
+ */
10061
+ type LoadedStyle = {
10062
+ format: 'maplibre';
10063
+ imported: MapLibreImport;
10064
+ } | {
10065
+ format: 'osmgl';
10066
+ style: StyleSpec;
10067
+ url: string | null;
10068
+ /** Перевод стиля v2: его проекция, состояние и камера (у стиля v1 их нет). */
10069
+ imported?: MapLibreImport;
10070
+ };
10071
+ declare function loadStyle(input: string | StyleSpec | MapLibreStyle, fetcher?: (url: string) => Promise<unknown>): Promise<LoadedStyle>;
10072
+ /**
10073
+ * Относительные адреса источников — по адресу стиля, как у MapLibre: стиль, лежащий рядом со
10074
+ * своими тайлами, пишет `/tiles/{z}/{x}/{y}.pbf`, и подключённый с чужой страницы обязан ходить
10075
+ * туда же, а не на сервер этой страницы. Адрес данных `geojson` строкой — туда же.
10076
+ */
10077
+ declare function resolveStyleUrls(style: StyleSpec, styleUrl: string): StyleSpec;
10078
+
8741
10079
  /**
8742
10080
  * Положение камеры в адресе страницы.
8743
10081
  *
@@ -8995,69 +10333,51 @@ declare class LogoControl implements IControl {
8995
10333
  }
8996
10334
 
8997
10335
  /**
8998
- * ВЫРАЖЕНИЯ СТИЛЯ MapLibre.
8999
- *
9000
- * Стиль, написанный под MapLibre, почти целиком состоит из них: ширина дороги — `interpolate` по
9001
- * зуму, цвет — `match` по классу, видимость — `case`. Без интерпретатора такой стиль у нас не
9002
- * работает вовсе, а переписывать его на наши упрощённые формы (`stops`) для каждого приложения —
9003
- * это ровно та работа, которой перенос и должен избавить.
9004
- *
9005
- * ★ ИНТЕРПРЕТАТОР, А НЕ КОМПИЛЯТОР.
9006
- *
9007
- * MapLibre компилирует выражение в замыкания один раз и потом зовёт их. Мы считаем прямо по
9008
- * массиву, но КЕШИРУЕМ разбор (`compile`) — на зумовых свойствах это вызов на слой за кадр, и
9009
- * разница между подходами там неизмерима. На фильтрах, где вызовов десятки тысяч на тайл, кеш
9010
- * решает: разобранное выражение переиспользуется для каждой фичи.
10336
+ * ПРОВЕРКА СТИЛЯ osmgl — по справочнику (`reference.ts`), без карты и без сети.
9011
10337
  *
9012
- * ★ ЧТО УМЕЕТ, А ЧТО НЕТ.
9013
- *
9014
- * Умеет всё, что встречается в стилях: данные (`get`, `has`, `id`, `geometry-type`,
9015
- * `feature-state`, `global-state`), решения (`case`, `match`, `coalesce`, логика, сравнения),
9016
- * шкалы (`interpolate` линейный, экспоненциальный и по кривой Безье, `step`), арифметику,
9017
- * строки, цвета, переменные (`let`/`var`), массивы (`at`, `length`, `slice`, `in`).
10338
+ * Делит находки на две кучи, и граница между ними — «что увидит человек»:
10339
+ * ОШИБКА — движок нарисует не то или не нарисует вовсе: незнакомый тип слоя, цвет, который не
10340
+ * разобрался (это фуксин на карте), выражение с незнакомой операцией (пустое значение),
10341
+ * ссылка на несуществующий слой или источник, число вне допустимого;
10342
+ * ПРЕДУПРЕЖДЕНИЕ — движок тихо обойдётся: незнакомое поле (опечатка или поле MapLibre, которого у
10343
+ * нас нет — оно просто не читается), значение по фиче там, где движок считает раз на
10344
+ * слой (возьмётся запасное), поле общего вида у слоя, которому оно ни к чему.
9018
10345
  *
9019
- * Не умеет: `format` (богатый текст с разными шрифтами в одной подписи — у нас подпись
9020
- * однородная), `image` в значении «встроить картинку в текст», `distance`, `within` для линий и
9021
- * полигонов (точка проверяется, остальное — нет), `resolved-locale`. Всё перечисленное отмечено и
9022
- * в `porting/maplibre-api.md`.
10346
+ * Работает на каждом нажатии клавиши в редакторе, поэтому без вычисления выражений и без копий:
10347
+ * один проход по дереву стиля. Замер — в `tests/style-validate.test.mjs` (300 слоёв).
9023
10348
  */
9024
- /** Значение выражения: то, чем оперирует язык. */
9025
- type ExprValue = number | string | boolean | null | ExprValue[] | RGBAColor;
9026
- /** Цвет как значение языка: четыре канала 0..1, как их отдаёт `parseColor`. */
9027
- interface RGBAColor {
9028
- r: number;
9029
- g: number;
9030
- b: number;
9031
- a: number;
10349
+ interface StyleIssue {
10350
+ /** Путь к значению: `layers[3].paint.color`, `sources.indoor.type`, `sky.fogRange[1]`. */
10351
+ path: string;
10352
+ /** Идентификатор слоя, если находка внутри слоя. */
10353
+ layer?: string;
10354
+ message: string;
9032
10355
  }
9033
- /** Всё, что выражение может спросить об окружении. */
9034
- interface ExprContext {
9035
- zoom?: number;
9036
- /** Свойства фичи (для фильтров и data-driven значений). */
9037
- properties?: Record<string, unknown>;
9038
- /** Номер фичи: `['id']`. */
9039
- id?: number | string | null;
9040
- /** Тип геометрии: `Point`, `LineString`, `Polygon`. */
9041
- geometryType?: string;
9042
- /** Состояние фичи (`map.setFeatureState`). */
9043
- featureState?: Record<string, unknown>;
9044
- /** Общее состояние стиля (`map.setGlobalStateProperty`). */
9045
- globalState?: Record<string, unknown>;
9046
- /** Точка фичи в координатах карты — нужна `within` для точечных фич. */
9047
- point?: [number, number];
10356
+ interface StyleValidation {
10357
+ errors: StyleIssue[];
10358
+ warnings: StyleIssue[];
9048
10359
  }
9049
- /** Похоже ли значение на выражение: массив, первый элемент которого — имя операции. */
9050
- declare function isExpression(value: unknown): value is ExprValue[];
9051
10360
  /**
9052
- * Посчитать выражение.
10361
+ * Проверить стиль osmgl.
9053
10362
  *
9054
- * Разбор кешируется на самом массиве: стиль живёт долго, а значения из него берут каждый кадр.
10363
+ * Принимает что угодно — то, что пришло из редактора, бывает не стилем вовсе. Стиль MapLibre (v8)
10364
+ * не проверяет: для него есть официальный валидатор, а здесь сказано об этом одной ошибкой.
9055
10365
  */
9056
- declare function evaluateExpression(expr: unknown, ctx: ExprContext): ExprValue;
9057
- /** Число из выражения; `fallback`, если получилось не число. */
9058
- declare function expressionNumber(expr: unknown, ctx: ExprContext, fallback: number): number;
9059
- /** Цвет из выражения в наш формат `[r, g, b, a]`. */
9060
- declare function expressionColor(expr: unknown, ctx: ExprContext): [number, number, number, number] | null;
10366
+ declare function validateStyle(spec: unknown, ref?: StyleReference): StyleValidation;
10367
+ /**
10368
+ * Проверить ТЕМУ относительно стиля: что её правки попадают в существующие слои и в поля, которые у
10369
+ * этих слоёв есть. Тема о незнакомом id молчит (`applyTheme` пишет в консоль и пропускает), и
10370
+ * перекрашенный «в никуда» слой иначе не найти.
10371
+ */
10372
+ declare function validateTheme(theme: unknown, style: unknown, ref?: StyleReference): StyleValidation;
10373
+
10374
+ /**
10375
+ * То же, что `parseColor`, но неразобранное — `null`, а не фуксин.
10376
+ *
10377
+ * Нужно проверке стиля (`style/validate.ts`): по фуксину не отличить опечатку от честного
10378
+ * `magenta`, а редактор обязан сказать про опечатку сразу, а не после того, как дорога порозовеет.
10379
+ */
10380
+ declare function tryParseColor(input: string): [number, number, number, number] | null;
9061
10381
 
9062
10382
  interface RibbonOptions {
9063
10383
  /** Полуширина полотна в единицах тайла. */
@@ -9305,6 +10625,50 @@ declare function generateSurface(layer: VectorTileLayer, extent: number, filter:
9305
10625
  * векторных тайлах нет — так записано и в их спецификации.
9306
10626
  */
9307
10627
  declare const cutMargin: (extent: number) => number;
10628
+ /**
10629
+ * ОБРЕЗКА ЗДАНИЯ ПО ТАЙЛУ — на выходе НАСТОЯЩИЕ многоугольники, сколько бы их ни вышло (02.10.2026).
10630
+ *
10631
+ * Покольцевая обрезка (`clipPolygonToTile`) режет внешний контур и дыры порознь, и в двух случаях
10632
+ * выдаёт не многоугольник, а вырожденную фигуру:
10633
+ * • ДЫРА, ЗАДЕТАЯ РАЗРЕЗОМ (двор, открытый к границе тайла), остаётся дырой, чья сторона лежит на
10634
+ * том же разрезе, что и сторона внешнего контура. Дальше `bleedRing` отодвигает разрез наружу у
10635
+ * обоих колец по их собственным рёбрам, и дыра вылезает за внешний контур. earcut на таком входе
10636
+ * строит мусор: на карте двор накрыт огромным косым треугольником, вокруг — щепки. Замер на доме
10637
+ * во дворе Худжанда (14/11360/6185): площадь крыши 869 единиц против 714 настоящих;
10638
+ * • ВОГНУТЫЙ КОНТУР, ДВАЖДЫ ПЕРЕСЁКШИЙ ГРАНИЦУ (П-образный корпус, ушедший перемычкой в соседний
10639
+ * тайл), склеивается перемычкой нулевой ширины вдоль разреза. После того же сдвига перемычка
10640
+ * получает площадь и ложится крышей поперёк двора.
10641
+ *
10642
+ * Такие случаи режутся булевым пересечением с квадратом (`clipShapeToSquare`): двор, вскрытый
10643
+ * разрезом, становится вырезом в контуре, разделённые части — отдельными фигурами. Это та же
10644
+ * геометрия, что у MapLibre: его тайл приходит обрезанным тайлсервером — тоже честной операцией над
10645
+ * многоугольником, а не покольцево.
10646
+ *
10647
+ * Остальным (подавляющему большинству) хватает покольцевой обрезки, и они идут прежним путём —
10648
+ * без булевой операции и с тем же результатом, что и раньше.
10649
+ */
10650
+ declare function clipPolygonParts(poly: {
10651
+ outer: Ring;
10652
+ holes: Ring[];
10653
+ }, extent: number, margin?: number): {
10654
+ outer: Ring;
10655
+ holes: Ring[];
10656
+ }[];
10657
+ /**
10658
+ * ВЕРШИНЫ РАЗРЕЗА — НА СЕТКУ УПАКОВКИ, И ТОЛЬКО ВНУТРЬ ДОМА (02.10.2026).
10659
+ *
10660
+ * Точка, где разрез пересёк стену, по второй координате лежит где придётся (1200.9412), а вершина
10661
+ * пишется на сетку `1 / scale` единицы. Округление «к ближайшему» сдвигает её с линии стены в
10662
+ * любую сторону — и у соседнего тайла в другую: половинки одной прямой стены на стыке расходятся
10663
+ * ступенькой (на z22 одна восьмая единицы — это два пикселя).
10664
+ *
10665
+ * Здесь вершина сдвигается ВДОЛЬ РАЗРЕЗА к соседней вершине того же разреза — и только до ближайшего
10666
+ * узла сетки. Угол многоугольника при вершине на разрезе не больше развёрнутого (весь кусок лежит по
10667
+ * одну сторону прямой разреза), поэтому такой сдвиг уводит ребро стены внутрь дома, а не наружу:
10668
+ * кусок остаётся ВНУТРИ настоящего контура. Наружу его линию тогда задаёт сосед, у которого эта часть
10669
+ * дома лежит в буфере целиком, из исходных вершин (см. `generateBuildings`, `tileClip: 'buffer'`).
10670
+ */
10671
+ declare function snapCutsInward(ring: Ring, lo: number, hi: number, scale: number): Ring;
9308
10672
 
9309
10673
  /**
9310
10674
  * Объединение полигонов слоя в один контур — до построения меша.
@@ -9390,6 +10754,8 @@ interface PolygonPiece {
9390
10754
  * Длина — на одну меньше числа точек замкнутого кольца, то есть по числу рёбер.
9391
10755
  */
9392
10756
  cut: boolean[];
10757
+ /** Ложные рёбра дыр, по дыре на массив. Не заданы — концы стен у дыр не выправляются. */
10758
+ holeCuts?: boolean[][];
9393
10759
  }
9394
10760
  /**
9395
10761
  * Собрать полигон из кусков.
@@ -9401,6 +10767,107 @@ interface PolygonPiece {
9401
10767
  */
9402
10768
  declare function stitchPieces(pieces: PolygonPiece[], minWidth?: number): Shape[] | null;
9403
10769
 
10770
+ /**
10771
+ * МЕСТА СИМВОЛОВ ВДОЛЬ ЛИНИИ — КАК У MapLibre, один в один.
10772
+ *
10773
+ * Перенос `symbol/get_anchors.ts`, `clip_line.ts` и `check_max_angle.ts` из maplibre-gl-js (BSD-3).
10774
+ * Нужен стилям MapLibre: стрелки направления и щиты трасс у них стоят там, где их ставит ЭТОТ
10775
+ * алгоритм, и любая своя сетка мест (наша `groundGrid`) даёт другую картину — стрелок вдвое меньше,
10776
+ * щиты не на тех перекрёстках.
10777
+ *
10778
+ * Чем он отличается от нашей сетки:
10779
+ * - места считаются в координатах ТАЙЛА, с переувеличением (`overscaling` — во сколько раз зум
10780
+ * кадра больше зума данных тайла, по целому зуму), а не по экрану;
10781
+ * - линия режется по квадрату тайла, место за ним отдаётся соседнему тайлу;
10782
+ * - первое место — на смещении `(половина подписи + 2 em) · масштаб · переувеличение % шаг`, у линии,
10783
+ * пришедшей из соседнего тайла, — `шаг/2 · переувеличение % шаг`; не нашлось ни одного — одно
10784
+ * посередине линии;
10785
+ * - подпись (не значок) не ставится на изломе круче `text-max-angle` в окне 3/5 кегля.
10786
+ *
10787
+ * Координаты тайла — в единицах MapLibre (`EXTENT` = 8192): от них зависят округления и смещения.
10788
+ */
10789
+ /** Размер тайла в единицах MapLibre. */
10790
+ declare const ML_EXTENT = 8192;
10791
+ interface Pt {
10792
+ x: number;
10793
+ y: number;
10794
+ }
10795
+ interface LineAnchor {
10796
+ x: number;
10797
+ y: number;
10798
+ /** Номер звена линии, на котором стоит место. */
10799
+ segment: number;
10800
+ /** Угол звена, радианы (как `b.angleTo(a)` у MapLibre). */
10801
+ angle: number;
10802
+ }
10803
+ /** Разрезать линию по прямоугольнику — `clipLine` MapLibre (с округлением точек среза). */
10804
+ declare function clipLine(line: Pt[], x1: number, y1: number, x2: number, y2: number): Pt[][];
10805
+ /**
10806
+ * Одно место в середине линии — `getCenterAnchor` MapLibre (`symbol-placement: line-center`).
10807
+ * Линия берётся ЦЕЛИКОМ, без среза по тайлу; излом под подписью проверяется так же.
10808
+ */
10809
+ declare function centerAnchor(line: Pt[], maxAngle: number, hasText: boolean, shapedLabelLength: number, boxScale: number): LineAnchor | null;
10810
+ /**
10811
+ * Места вдоль одной линии тайла — `getAnchors` MapLibre.
10812
+ *
10813
+ * @param line точки в единицах `ML_EXTENT`, уже разрезанные по тайлу (`clipLine`)
10814
+ * @param spacing шаг в единицах тайла (`symbol-spacing · tilePixelRatio`)
10815
+ * @param shapedLabelLength длина подписи: max(ширина текста в единицах кегля 24, ширина значка в px)
10816
+ * @param boxScale `tilePixelRatio · textMaxSize / 24`
10817
+ * @param hasText есть ли текст — у значка без текста угол излома не проверяется
10818
+ * @param maxAngle `text-max-angle`, радианы
10819
+ */
10820
+ declare function lineAnchors(line: Pt[], spacing: number, maxAngle: number, hasText: boolean, shapedLabelLength: number, boxScale: number, overscaling: number, extent?: number): LineAnchor[];
10821
+
10822
+ /**
10823
+ * Адрес, с которого снят снимок. Относительные ссылки внутри встроенного стиля разрешаются по нему
10824
+ * — так же, как если бы стиль скачали по ссылке `MAPLIBRE_STYLE_BASE + имя + '.json'`. В нынешних
10825
+ * снимках все адреса абсолютные, но правка стиля на сервере вправе это изменить.
10826
+ */
10827
+ declare const MAPLIBRE_STYLE_BASE = "https://navi.gram.tj/styles/";
10828
+ /** Светлый «город» — снимок `https://navi.gram.tj/styles/mlight-city.json`. */
10829
+ declare const MLIGHT_CITY_STYLE: MapLibreStyle;
10830
+ /** Ночной «город» — снимок `https://navi.gram.tj/styles/mnight-city.json`. */
10831
+ declare const MNIGHT_CITY_STYLE: MapLibreStyle;
10832
+ /**
10833
+ * Имя → встроенный стиль MapLibre и показываемое название.
10834
+ *
10835
+ * Название своё, а не поле `name` стиля: у обоих снимков там «MapTiler 3D» (наследство шаблона
10836
+ * Maputnik), и в меню два одинаковых пункта ничего бы не сказали.
10837
+ */
10838
+ declare const MAPLIBRE_STYLES: Record<string, {
10839
+ name: string;
10840
+ style: MapLibreStyle;
10841
+ }>;
10842
+ /**
10843
+ * Встроенный стиль по имени — с адресом, по которому разрешаются его относительные ссылки.
10844
+ * Не встроенное имя (в том числе любая ссылка) — `null`: такую строку грузят по сети.
10845
+ */
10846
+ declare function builtinMapLibreStyle(name: string): {
10847
+ style: MapLibreStyle;
10848
+ url: string;
10849
+ } | null;
10850
+
10851
+ /**
10852
+ * СБОРКА ГЕОМЕТРИИ ТАЙЛА — отдельно от его загрузки.
10853
+ *
10854
+ * Отделено ради источника `geojson`: данные там приходят не тайлами с сервера, а целым набором от
10855
+ * приложения, и режет их на тайлы сам движок (`source/geojson-source.ts`). Загрузка у них разная,
10856
+ * а сборка обязана быть одна и та же — иначе слой из GeoJSON рисовался бы иначе, чем такой же слой
10857
+ * из тайлов.
10858
+ *
10859
+ * `tile` — что угодно с полем `layers`: настоящий разобранный MVT или набор, собранный из GeoJSON.
10860
+ */
10861
+ declare function buildTile(tile: {
10862
+ layers: Record<string, VectorTileLayer>;
10863
+ }, req: Pick<LoadRequest, 'zoom' | 'metersPerTile' | 'sourceMaxZoom' | 'plan' | 'tile' | 'globalState'>): {
10864
+ buckets: Record<string, BucketMesh>;
10865
+ seams: BuildingSeam[];
10866
+ splits: Record<string, (string | number)[]>;
10867
+ levels: number[];
10868
+ failed: string[];
10869
+ };
10870
+
9404
10871
  /**
9405
10872
  * Цель рендера с одной только глубиной — карта теней.
9406
10873
  *
@@ -9599,10 +11066,19 @@ interface MapOptions {
9599
11066
  * они на порядки дольше.
9600
11067
  */
9601
11068
  sources?: Record<string, SourceSpec>;
9602
- /** Стиль: объект спецификации. По умолчанию — встроенный `styles/osm-3d.json`. */
9603
- style?: StyleSpec;
9604
- /** Тема оформления: `day` | `night` | `mono` | `pale` либо своя накладка. */
9605
- theme?: string | ThemeSpec;
11069
+ /**
11070
+ * Стиль: объект спецификации. По умолчанию — встроенный `styles/osm-3d.json`.
11071
+ *
11072
+ * Годится и стиль MapLibre v8 — объектом или ссылкой: он переводится импортёром
11073
+ * (`style/maplibre-import.ts`) и подключается вместе со своими источниками, шрифтами и спрайтом.
11074
+ * Ссылка может вести и на НАШ стиль (`"version": 1`) — формат решает поле `version` самого JSON
11075
+ * (так публикует стили редактор: `/styles/{slug}.json`). Встроенные — по имени, без загрузки JSON:
11076
+ * стили MapLibre `'mlight-city'`, `'mnight-city'` (`Map.mapLibreStyleList`) и наши `'osm-3d'`,
11077
+ * `'osm-city'`, … (`Map.styleList`).
11078
+ */
11079
+ style?: StyleSpec | MapLibreStyle | StyleSpecV2 | string;
11080
+ /** Тема оформления: `day` | `night` | `mono` | `pale` либо своя накладка (v2; v1 — синоним на время переезда). */
11081
+ theme?: string | ThemeInput;
9606
11082
  /** Шаблон адреса SDF-глифов (формат Mapbox, его отдаёт наш tileserver-gl). */
9607
11083
  fonts?: string;
9608
11084
  /** Начертание из набора шрифтов тайлсервера. */
@@ -9673,6 +11149,16 @@ interface MapOptions {
9673
11149
  highlightColor?: string;
9674
11150
  }
9675
11151
  /** Подпись или значок карты под курсором. */
11152
+ /** Слой стиля под точкой экрана (`layersAt`): подпись ИЛИ фича геометрии. */
11153
+ interface StyleLayerHit {
11154
+ /** Идентификатор слоя стиля. */
11155
+ layer: string;
11156
+ type: StyleLayerSpec['type'];
11157
+ /** Фича заливки, линии, объёма или поверхности — с её свойствами из тайла. */
11158
+ feature?: QueriedFeature;
11159
+ /** Подпись или значок, как их разместила раскладка этого кадра. */
11160
+ symbol?: SymbolHit;
11161
+ }
9676
11162
  interface SymbolHit {
9677
11163
  /** Слой стиля: по нему понятно, что это — POI, дом, остановка. */
9678
11164
  layerId: string;
@@ -9810,6 +11296,39 @@ declare class Map$1 extends Evented<MapEvents> {
9810
11296
  /** Стиль разобран и первый кадр показан. Имя `loaded` занято методом MapLibre. */
9811
11297
  private ready;
9812
11298
  private destroyed;
11299
+ /** Стиль по ссылке или стиль MapLibre из опций: подключается, когда у карты уже есть источники. */
11300
+ private pendingMapLibre;
11301
+ /** Проекция и общее состояние своего стиля v2, переданного объектом в опциях: ставятся после разбора камеры. */
11302
+ private pendingStyleExtras;
11303
+ /** Камера стиля применена: у MapLibre она берётся только при первой загрузке. */
11304
+ private styleCameraApplied;
11305
+ /**
11306
+ * Номер последнего запроса ВНЕШНЕГО стиля (по ссылке или MapLibre): ответ на устаревший запрос
11307
+ * отбрасывается. Общий у обоих форматов: две быстрые смены стиля по ссылке не должны закончиться
11308
+ * первой, какого бы формата ни был каждый.
11309
+ */
11310
+ private mapLibreSeq;
11311
+ /** Источники, поставленные стилем osmgl по ссылке, — чтобы не пересоздавать неизменившиеся. */
11312
+ private urlStyleSources;
11313
+ /** Источники, поставленные стилем MapLibre, — чтобы не пересоздавать неизменившиеся. */
11314
+ private mapLibreSources;
11315
+ /** Итог последнего импорта стиля MapLibre (что не перенесено, камера стиля). */
11316
+ private lastImport;
11317
+ /**
11318
+ * Основа карты ДО первого стиля MapLibre: импорт её подменяет, и возврат к своему стилю обязан
11319
+ * вернуть и её, иначе наш стиль остался бы на чужих тайлах. Шрифты и спрайт живут отдельно — их
11320
+ * приносит любой стиль, не только MapLibre (см. `syncStyleAssets`).
11321
+ */
11322
+ private beforeMapLibre;
11323
+ /**
11324
+ * Шрифты и спрайт КАРТЫ, отложенные на время стиля со своими (`glyphs`/`sprite` стиля); `null` —
11325
+ * стоят собственные карты. Запоминаются при первом таком стиле и возвращаются первым стилем без них.
11326
+ */
11327
+ private mapAssets;
11328
+ /** Что поставил стиль в последний раз: смена темы и правка слоя не должны перекачивать спрайт. */
11329
+ private styleAssetsKey;
11330
+ /** Номер последней смены спрайта стилем: догрузка дополнительных спрайтов устаревшей смены бросается. */
11331
+ private styleAssetsSeq;
9813
11332
  private background;
9814
11333
  private maxPixelRatio;
9815
11334
  /** Шаг прилипания зума у кнопок (`zoomIn`/`zoomOut`). Ноль — не липнет. */
@@ -10173,8 +11692,78 @@ declare class Map$1 extends Evented<MapEvents> {
10173
11692
  * фильтры, источники). Смена одних цветов — а это ровно случай переключения
10174
11693
  * темы — обходится сменой юниформов: тайлы не перекачиваются и не
10175
11694
  * перемалываются заново, картинка меняется в том же кадре.
11695
+ *
11696
+ * Строка — ССЫЛКА на стиль любого из двух форматов (решает поле `version` JSON: 8 — MapLibre,
11697
+ * 1 — наш) либо имя встроенного: `'mlight-city'`, `'mnight-city'` (MapLibre) или `'osm-3d'`,
11698
+ * `'osm-city'`, … (наши). Встроенный лежит в пакете, и за его JSON карта в сеть не ходит.
11699
+ *
11700
+ * Стиль по ссылке (и встроенный по имени) становится ОСНОВОЙ для тем — как `setBaseStyle`, — и
11701
+ * приносит свои источники. Объект спецификации ставится «на один раз», как и прежде.
11702
+ *
11703
+ * `keepEdits: false` — для редактора стилей: спецификация и есть вся правда, и правки, сделанные
11704
+ * на лету (`setPaintProperty`, `addLayer`, `setFilter`, …), сбрасываются, а не накладываются
11705
+ * поверх неё заново. По умолчанию они переживают смену стиля — ради тем (см. `layerOverrides`).
11706
+ */
11707
+ setStyle(spec: StyleSpec | MapLibreStyle | StyleSpecV2 | string, opts?: {
11708
+ keepEdits?: boolean;
11709
+ }): this;
11710
+ /** Внутренняя модель (v1) — в карту: план воркера, правки на лету, шрифты, рельеф. */
11711
+ private installSpec;
11712
+ /**
11713
+ * СТИЛЬ MapLibre: перевод, затем источники, шрифты и спрайт самого стиля.
11714
+ *
11715
+ * Порядок важен. Сначала стиль — у уже подключённых источников меняется план; потом источники —
11716
+ * новые сразу заводятся под новый план, а неизменившиеся не трогаются (пересоздание сбросило бы
11717
+ * загруженные тайлы). Ответ на устаревший запрос отбрасывается: две быстрые смены стиля не
11718
+ * должны закончиться первым из них.
11719
+ */
11720
+ private applyExternalStyle;
11721
+ /**
11722
+ * ТО, ЧТО СТИЛЬ v8/v2 ГОВОРИТ О КАРТЕ, А НЕ О СЛОЯХ: проекция, умолчания общего состояния и
11723
+ * камера. Камера — только при первой загрузке и только там, где приложение не задало своё в
11724
+ * опциях (так у MapLibre); состояние — только ключи, которых приложение ещё не ставило.
11725
+ */
11726
+ private applyStyleExtras;
11727
+ /**
11728
+ * НАШ СТИЛЬ ПО ССЫЛКЕ (или встроенный по имени): основа для тем плюс его собственные источники.
11729
+ *
11730
+ * Основа — потому что ссылку ставят ВМЕСТО стиля карты, а не поверх: следующий `setTheme` должен
11731
+ * лечь на неё, а не вернуть стиль из опций. Источники стиля по ссылке заводятся, изменившиеся —
11732
+ * пересоздаются, неизменившиеся не трогаются (пересоздание сбросило бы загруженные тайлы).
11733
+ * Источник, принесённый прошлым стилем по ссылке и пропавший из нового, снимается: иначе он
11734
+ * продолжал бы качать тайлы, которые никто не рисует. Встроенный стиль заводит только недостающие
11735
+ * источники — как `setBaseStyle`: они те же, что из опций карты.
11736
+ */
11737
+ private installOsmglStyle;
11738
+ /** СТИЛЬ MapLibre, уже переведённый (см. `applyExternalStyle`): стиль (с его шрифтами и спрайтом) и источники. */
11739
+ private installMapLibreStyle;
11740
+ /** Итог последнего перевода стиля MapLibre: что не перенесено и камера стиля. */
11741
+ getMapLibreImport(): MapLibreImport | null;
11742
+ /** Идёт ли сейчас установка переведённого стиля (тогда это не возврат к своему). */
11743
+ private applyingMapLibre;
11744
+ /**
11745
+ * ВОЗВРАТ СО СТИЛЯ MapLibre НА СВОЙ: источники, шрифты и спрайт — как были до импорта.
11746
+ *
11747
+ * Источник, который поставил импорт, заменяется тем, что объявил новый стиль; нет его там — для
11748
+ * основы берётся адрес из опций карты, остальные снимаются. Запрос импорта, ещё летящий по сети,
11749
+ * отменяется: иначе он пришёл бы позже и вернул чужой стиль поверх своего.
11750
+ */
11751
+ private leaveMapLibre;
11752
+ /**
11753
+ * ШРИФТЫ И СПРАЙТ СТИЛЯ: свои у стиля — ставятся, нет их — у карты собственные.
11754
+ *
11755
+ * Одно правило на все пути: наш стиль объектом, по ссылке, основа для тем и перевод MapLibre (он
11756
+ * кладёт `glyphs`/`sprite` в сам стиль). Свои карты (опция `fonts`, встроенные значки, `setSprite`)
11757
+ * запоминаются при первом стиле со своими и возвращаются первым стилем без них — так стиль,
11758
+ * сохранённый редактором, рисуется теми значками, с которыми его делали, а переход на наш стиль не
11759
+ * оставляет карту с чужими. Поля по отдельности: стиль только со спрайтом берёт шрифты карты.
11760
+ *
11761
+ * Повторная установка того же (смена темы, правка слоя — это тоже `setStyle`) ничего не делает:
11762
+ * спрайт перекачивался бы на каждое движение ползунка в редакторе.
10176
11763
  */
10177
- setStyle(spec: StyleSpec): this;
11764
+ private syncStyleAssets;
11765
+ /** Поставить шрифты и набор спрайтов, не трогая то, что уже стоит как надо. */
11766
+ private applyAssets;
10178
11767
  /**
10179
11768
  * Добавить слой. `beforeId` — перед каким слоем встать; без него слой ложится поверх всех.
10180
11769
  *
@@ -10201,8 +11790,17 @@ declare class Map$1 extends Evented<MapEvents> {
10201
11790
  * `setPaintProperty`, который меняет только юниформы.
10202
11791
  */
10203
11792
  setFilter(id: string, filter: Filter | null): this;
10204
- /** Спецификация стиля целиком — копией, чтобы правки снаружи не меняли живой стиль. */
10205
- getStyle(): StyleSpec;
11793
+ /**
11794
+ * Спецификация стиля целиком — В ФОРМАТЕ v2 (MapLibre v8 + `osmgl-*`) и копией, чтобы правки
11795
+ * снаружи не меняли живой стиль. Её можно отдать обратно в `setStyle` и получить ту же карту, а
11796
+ * срезанную `toMapLibreStyle` — открыть в MapLibre.
11797
+ */
11798
+ getStyle(): StyleSpecV2;
11799
+ /**
11800
+ * Внутренняя модель движка (формат v1) — для отладки и старого кода; формат обмена — `getStyle()`.
11801
+ * Слои — ЖИВЫЕ, с правками на лету (`setPaintProperty`, `setLayerVisible`), как у `getStyle` MapLibre.
11802
+ */
11803
+ getStyleV1(): StyleSpec;
10206
11804
  /**
10207
11805
  * Стиль разобран.
10208
11806
  *
@@ -10242,6 +11840,22 @@ declare class Map$1 extends Evented<MapEvents> {
10242
11840
  * и правки надо уметь применить заново — в том же порядке, в каком их сделали.
10243
11841
  */
10244
11842
  private applyLayerEdits;
11843
+ /**
11844
+ * КАКИЕ СЛОИ СТИЛЯ НАРИСОВАЛИ ТО, ЧТО ПОД ТОЧКОЙ ЭКРАНА — для редактора стилей («инспектор»:
11845
+ * щёлкнул по карте — увидел список слоёв и выбрал нужный).
11846
+ *
11847
+ * Сверху вниз: сначала подписи и значки (они над всем), потом геометрия в обратном порядке
11848
+ * стиля. Подпись берётся у того, кто её разместил (`querySymbol` — экранная раскладка этого
11849
+ * кадра), геометрия — из нарисованных мешей (`queryRenderedFeatures`). Слои, погашенные зумом или
11850
+ * `visible: false`, в ответ не попадают: их на экране нет, хотя меш в тайле и лежит.
11851
+ *
11852
+ * Слой может прийти несколько раз — по разу на каждую фичу под точкой (две пересекающиеся
11853
+ * дороги одного слоя). Подложку (`raster`) и `background` не возвращает: под любой точкой они
11854
+ * есть всегда.
11855
+ */
11856
+ layersAt(point: ScreenPoint$1, opts?: {
11857
+ tolerance?: number;
11858
+ }): StyleLayerHit[];
10245
11859
  /**
10246
11860
  * ЧТО НАРИСОВАНО В ЭТОЙ ТОЧКЕ (или в этом прямоугольнике) ЭКРАНА.
10247
11861
  *
@@ -10437,12 +12051,19 @@ declare class Map$1 extends Evented<MapEvents> {
10437
12051
  * выбранный маршрут». Значение живёт у стиля, а не у слоя, — поэтому и имя общее.
10438
12052
  */
10439
12053
  setGlobalStateProperty(key: string, value: unknown): this;
12054
+ /**
12055
+ * СОСТОЯНИЕ ДОЛЖНО ДОЙТИ ДО ВОРКЕРА. Фильтры, значения по фиче и подписи считаются там, у
12056
+ * воркера свой экземпляр модуля, и состояние уходит к нему с каждым запросом тайла
12057
+ * (`LoadRequest.globalState`). Тайлы, уже разобранные по старому состоянию, пересобираются — но
12058
+ * только у источников, чей план вообще смотрит на `global-state`.
12059
+ */
12060
+ private replanForGlobalState;
10440
12061
  getGlobalState(): Record<string, unknown>;
10441
12062
  /** Повторять ли мир по горизонтали при отъезде. */
10442
12063
  getRenderWorldCopies(): boolean;
10443
12064
  setRenderWorldCopies(render: boolean): this;
10444
12065
  /** Переключить тему оформления поверх текущей структуры слоёв. */
10445
- setTheme(theme: string | ThemeSpec): this;
12066
+ setTheme(theme: string | ThemeInput): this;
10446
12067
  /**
10447
12068
  * СМЕНИТЬ ОСНОВУ, на которую ложатся темы.
10448
12069
  *
@@ -10451,7 +12072,7 @@ declare class Map$1 extends Evented<MapEvents> {
10451
12072
  * рельефная) нужно заменить саму основу — этим и занимается этот метод. Тема применяется сразу,
10452
12073
  * чтобы палитра не слетала на время переключения.
10453
12074
  */
10454
- setBaseStyle(style: StyleSpec, theme?: string | ThemeSpec): this;
12075
+ setBaseStyle(input: StyleSpec | StyleSpecV2, theme?: string | ThemeInput): this;
10455
12076
  /** Имена доступных тем. */
10456
12077
  static get themes(): string[];
10457
12078
  /**
@@ -10465,16 +12086,61 @@ declare class Map$1 extends Evented<MapEvents> {
10465
12086
  id: string;
10466
12087
  name: string;
10467
12088
  }[];
12089
+ /**
12090
+ * Встроенные стили MapLibre — для того же выпадающего списка: имя для `setStyle` и название.
12091
+ *
12092
+ * Отдельно от тем: тема — накладка на НАШ стиль и меняет только цвета, а это другой стиль целиком
12093
+ * (свои тайлы, шрифты и спрайт), и `setTheme` с него возвращает карту на нашу основу.
12094
+ */
12095
+ static get mapLibreStyleList(): {
12096
+ id: string;
12097
+ name: string;
12098
+ }[];
12099
+ /** Встроенные стили osmgl — имя для `setStyle` (`'osm-city'`) и название. */
12100
+ static get styleList(): {
12101
+ id: string;
12102
+ name: string;
12103
+ }[];
10468
12104
  /**
10469
12105
  * Правка layout-свойств слоя на лету: размер кегля, зазор до значка, шаг
10470
12106
  * разрежения, расположение иконки.
10471
12107
  *
10472
12108
  * Отдельно от `setPaintProperty` не для симметрии: paint — это юниформы, а
10473
12109
  * layout влияет на РАЗМЕЩЕНИЕ, и его правка пересобирает раскладку подписей
10474
- * (но не геометрию). Исключение — `textField`, `iconImage` и `splitBy`: они
10475
- * читаются в воркере, поэтому тянут за собой новый план и перекачку разбора.
12110
+ * (но не геометрию). Исключение — то, что читает воркер (`textField`, `iconImage`,
12111
+ * `iconRotate`, `textPlacement`, `abbreviate`, `textTransform`, …): такая правка тянет
12112
+ * за собой новый план и пересборку тайлов, но только источника этого слоя.
10476
12113
  */
10477
12114
  setLayoutProperty(layerId: string, prop: string, value: unknown): this;
12115
+ /**
12116
+ * ПРАВКА СВОЙСТВА СЛОЯ ПО ИМЕНИ v2 — общее у `setPaintProperty` и `setLayoutProperty`.
12117
+ *
12118
+ * Имя v2 (`line-color`, `osmgl-corner-radius`, `visibility`) переводится по той же таблице, что и
12119
+ * стиль целиком, — в поле внутренней модели, где бы оно ни лежало: `line-cap` у нас в `paint`,
12120
+ * `osmgl-corner-radius` — рядом с фильтром. Имя v1 (`color`, `textSize`) принимается как синоним
12121
+ * с одним предупреждением: два выпуска приложения переезжают на новые имена. Незнакомое имя
12122
+ * кладётся как есть, тоже с предупреждением, — так было и раньше.
12123
+ */
12124
+ private setLayerProperty;
12125
+ /** Имя свойства (v2 или v1) → правки полей внутренней модели. */
12126
+ private v1Ops;
12127
+ /**
12128
+ * Значение свойства слоя по имени v2 (`line-color`, `osmgl-corner-radius`) — как его отдал бы
12129
+ * `getStyle()`. `undefined` — не задано (у MapLibre — умолчание спецификации).
12130
+ */
12131
+ getPaintProperty(layerId: string, prop: string): unknown;
12132
+ getLayoutProperty(layerId: string, prop: string): unknown;
12133
+ private layerV2;
12134
+ /** Слепок плана воркера для слоя: по нему видно, задела ли правка геометрию. */
12135
+ private planKeyOf;
12136
+ /**
12137
+ * ПЕРЕСОБРАТЬ ТАЙЛЫ, ТОЛЬКО ЕСЛИ ПРАВКА ДОШЛА ДО ВОРКЕРА — и только у источника этого слоя.
12138
+ *
12139
+ * Раньше решение принималось по имени свойства (три имени у раскладки, «значение по фиче» у
12140
+ * покраски), и правка, о которой список не знал, — `lineCap`, `textPlacement`, `height` дома —
12141
+ * молча не доезжала до геометрии. Сравнение планов знает всё, что знает воркер.
12142
+ */
12143
+ private replanIfChanged;
10478
12144
  /**
10479
12145
  * Правка paint-свойств слоя на лету. Геометрия НЕ пересобирается — меняются
10480
12146
  * только юниформы, поэтому цвет и ширину можно крутить хоть каждый кадр.
@@ -10772,7 +12438,15 @@ declare class Map$1 extends Evented<MapEvents> {
10772
12438
  data: Float32Array;
10773
12439
  format: string;
10774
12440
  } | null;
12441
+ /** Кадр упал вне слоёв (тени, отражение, небо): сказать один раз на причину, цикл не рвать. */
12442
+ private frameFailures;
12443
+ private reportFrameFailure;
10775
12444
  private renderFrame;
12445
+ /** Будильник следующего кадра ряби (см. конец `renderFrame`). */
12446
+ private materialTimer;
12447
+ /** Следующий кадр заказан паузой ряби, а не потоком кадров (см. `renderFrame`). */
12448
+ private pacedWake;
12449
+ private scheduleMaterialFrame;
10776
12450
  /** Поставить будильник на ближайший повтор; уже стоящий на более ранний срок не трогается. */
10777
12451
  private scheduleRetryWake;
10778
12452
  private stopFrameLoop;
@@ -10780,4 +12454,4 @@ declare class Map$1 extends Evented<MapEvents> {
10780
12454
  destroy(): void;
10781
12455
  }
10782
12456
 
10783
- export { type AnimationOptions, type ArrowSize, AssetLoader, type AssetLoaderOptions, AttributionControl, type AxisChain, BASE_SOURCE, BUILDING_HEIGHT_SCALE, BUILDING_POS_SCALE, BUILDING_VERTEX_STRIDE, BoxZoomHandler, type BridgeProfile, type BuildingHit, type BuildingsMesh, CITY_STYLE, type CameraOptions, type CameraUpdate, Circle, type CircleOptions, type ControlPosition, CooperativeGesturesHandler, type CoverOptions, type CustomLayer, DATA_PAINT_STRIDE, DEFAULT_ICONS, DEFAULT_LIGHT, DEFAULT_SHADOWS, DEFAULT_SKY, DEFAULT_STYLE, type DataPaintSpec, type DemEncoding, DemSource, type DemSourceOptions, DemTile, DemTileEntry, DepthTarget, type DeviceHints, EARTH_CIRCUMFERENCE, type ExprContext, type ExprValue, FILL_VERTEX_STRIDE, FeaturePaint, type Filter, type FitBoundsOptions, type FitViewport, type FogPlanes, FullscreenControl, GLOBE_MAX_PITCH, GLOBE_ZOOM_MAX, GLOBE_ZOOM_MIN, GLYPH_BORDER, GLYPH_SIZE, GROUND_SCALE, type GeoJSONData, type GeoJSONFeature, type GeoJSONFeatureCollection, type GeoJSONGeometry, GeoJSONSource, GeolocateControl, GeomType, GestureHandler, type GestureOptions, GlobeControl, type Glyph, type GlyphMetric, GlyphSource, type GpuClass, GroundArrows, type IControl, IconSource, ImageManager, IndoorMask, type Interpolated, type Junctions, LEVEL_LIMIT, LINE_VERTEX_STRIDE, type LaneArrow, type LaneGuide, type LaneGuideOptions, type LayerPlan, LayerRenderer, type LiftField, type LightSpec, type LinePlacement, type LngLat, LngLatBounds, type LngLatLike, LogoControl, MAX_GRADE, MAX_LATITUDE, MESH_BOTTOM, MESH_ISLAND, MESH_KERB_SIDE, MESH_KERB_TOP, MESH_SIDE, MESH_TOP, MIN_PIER_HEIGHT_METERS, MODEL_VERTEX_STRIDE, Map$1 as Map, type MapEvents, MapObject, type MapObjectOptions, type MapOptions, Marker, type MarkerOptions, type Marking, type MarkingKind, type MarkingOptions, type MarkingRing, type MediaCoordinates, type MediaKind, MediaSource, Model, ModelLayer, type ModelMesh, type ModelOptions, type ModelPrimitive, NavigationControl, ObjectManager, type ObjectPaint, type ObjectPart, PIER_SPACING_METERS, type Padding, type Pier, type PierOptions, type PlacedGlyph, type PlacedSymbol, Polygon, type PolygonOptions, type PolygonPiece, Polyline, type PolylineOptions, type PolylineSegment, Popup, type PopupOptions, type Projection, type ProtocolHandler, type QualityChange, QualityGovernor, type QualityName, type QualityProfile, type QueriedFeature, type QueryGeometry, RELIEF_STYLE, ROAD_SIGNS, RUNTIME_FILL_STRIDE, RUNTIME_LINE_STRIDE, type RampOptions, RasterSource, type RasterSourceOptions, RasterTile, type RenderedQueryOptions, type RoadAxis, type RoadChain, type RoadEdges, type RoadGraph, type RoadGraphOptions, type RoadHeightOptions, type RoadHeights, type RoadMesh, type RoadMeshOptions, type RoadNode, type RoadRay, type RoadSourceEdges, type RoadSurface, type RoadSurfaceOptions, type RoundOptions, type RuntimeMesh, SDF_EDGE, SDF_PX, SIGN_SIZE, SOFT_STYLE, SPLIT_SEP, SURFACE_CUT_BLEED, SURFACE_LEVEL_SCALE, SURFACE_VERTEX_STRIDE, ScaleControl, type ScreenPoint$1 as ScreenPoint, SdfAtlas, type ShadowSettings, type SkySpec, Style, type StyleImage, type StyleImageData, type StyleImageOptions, type StyleLayerSpec, type StyleSpec, type SymbolHit, SymbolRenderer, TAPER_METERS, TERRAIN_STYLE, THEMES, TILE_SIZE, type TaperedWidth, TerrainControl, type ThemeSpec, type TileAddress, TileID, Transform, type TransformConstrain, type TransformRequest, VectorTile, VectorTileFeature, VectorTileLayer, abbreviateStreet, addGround, altitudeFromMercatorZ, applyTheme, arrowLabelOffset, arrowVertices, bridgePiers, bridgeProfile, buildRoadGraph, buildRuntimeFill, buildRuntimeLine, cameraForBounds, chainAxes, circumferenceAtLatitude, clampLat, clampPixelRatio, classifyRenderer, clipLineToRect, coordinateDigits, coveringTiles, createGround, cutMargin, Map$1 as default, densifyLine, deviceHints, distToSegment, evaluateColor, evaluateExpression, evaluateNumber, expressionColor, expressionNumber, extractFeature, featureDrivenPaint, featureLevel, fillFootprint, fogPlanes, footprintOf, formatHash, generateBuildings, generateFill, generateLine, generateSurface, generateSymbols, globeBasis, globeFlatMix, globeRadius, globeToLocal, groundAt, iconRotation, insideFootprint, isExpression, junctionIndex, laneArrow, laneGuides, latFromMercatorY, liftFields, lngFromMercatorX, lngLatToUnit, makeFeaturePaint, matchesFilter, mercatorMetersPerTile, mercatorX, mercatorY, mercatorZFromAltitude, metersPerPixel, metersPerTile, needsDataPaint, nextQualityDown, parseGlb, parseGlbAsync, parseGlyphs, parseHash, parseLift, patchUV, pickBuilding, pickFill, pitchIntent, pixelsPerMeter, placeAlongLine, placeMarking, pointInRing, qualityFor, qualityProfile, queryRendered, querySource, raySphere, refineLine, relaxAngle, relaxCurvature, resolveTemplate, ribbonFromEdges, ribbonRing, ribbonShape, ringEdges, roadEdges, roadHeights, roadMarkings, roadMesh, roadSurfaces, roundLine, roundRing, roundRings, sdfFromAlpha, shapeText, snapRoadEnds, speedSign, splitBucketKey, splitByGround, splitPolygons, squareClosed, stitchPieces, stopLine, styleWithTheme, taperedWidth, tileOriginMeters, tileUrl, toLngLat, unitToLngLat, unpackVector, waffle, worldSize, yieldTargetsOf, zebra };
12457
+ export { type AnimationOptions, type ArrowSize, AssetLoader, type AssetLoaderOptions, AttributionControl, type AxisChain, BASE_SOURCE, BUILDING_HEIGHT_SCALE, BUILDING_POS_SCALE, BUILDING_VERTEX_STRIDE, BoxZoomHandler, type BridgeProfile, type BuildingHit, type BuildingsMesh, CITY_STYLE, type CameraOptions, type CameraUpdate, Circle, type CircleOptions, type ControlPosition, CooperativeGesturesHandler, type CoverOptions, type CustomLayer, DATA_PAINT_STRIDE, DEFAULT_ICONS, DEFAULT_LIGHT, DEFAULT_MAPLIBRE_LIGHT, DEFAULT_SHADOWS, DEFAULT_SKY, DEFAULT_STYLE, type DataPaintSpec, type DemEncoding, DemSource, type DemSourceOptions, DemTile, DemTileEntry, DepthTarget, type DeviceHints, EARTH_CIRCUMFERENCE, EXPRESSION_OPERATORS, type ExprContext, type ExprValue, type ExpressionIssue, type ExpressionOperatorRef, FILL_VERTEX_STRIDE, FeaturePaint, type Filter, type FitBoundsOptions, type FitViewport, type FogPlanes, FullscreenControl, GLOBAL_STATE, GLOBE_MAX_PITCH, GLOBE_ZOOM_MAX, GLOBE_ZOOM_MIN, GLYPH_BORDER, GLYPH_SIZE, GROUND_SCALE, type GeoJSONData, type GeoJSONFeature, type GeoJSONFeatureCollection, type GeoJSONGeometry, GeoJSONSource, GeolocateControl, GeomType, GestureHandler, type GestureOptions, GlobeControl, type Glyph, type GlyphMetric, GlyphSource, type GpuClass, GroundArrows, type IControl, IconSource, ImageManager, IndoorMask, type Interpolated, type Junctions, LEVEL_LIMIT, LINE_VERTEX_STRIDE, type LaneArrow, type LaneGuide, type LaneGuideOptions, type LayerPlan, LayerRenderer, type LayerSpecV2, type LayerTypeName, type LayerTypeRef, type LiftField, type LightSpec, type LinePlacement, type LngLat, LngLatBounds, type LngLatLike, type LoadedStyle, LogoControl, MAPLIBRE_STYLES, MAPLIBRE_STYLE_BASE, MAPLIBRE_SUPPORT, MAX_GRADE, MAX_LATITUDE, MESH_BOTTOM, MESH_ISLAND, MESH_KERB_SIDE, MESH_KERB_TOP, MESH_SIDE, MESH_TOP, MIN_PIER_HEIGHT_METERS, MLIGHT_CITY_STYLE, ML_EXTENT, MNIGHT_CITY_STYLE, MODEL_VERTEX_STRIDE, Map$1 as Map, type MapEvents, type MapLibreImport, type MapLibreLayer, type MapLibreLightSpec, type MapLibreSource, type MapLibreStyle, type MapLibreSupport, type MapLibreSupportStatus, MapObject, type MapObjectOptions, type MapOptions, Marker, type MarkerOptions, type Marking, type MarkingKind, type MarkingOptions, type MarkingRing, type MediaCoordinates, type MediaKind, MediaSource, Model, ModelLayer, type ModelMesh, type ModelOptions, type ModelPrimitive, NavigationControl, OSMGL_FORMAT_VERSION, OSMGL_STYLES, OSMGL_VERSION_KEY, ObjectManager, type ObjectPaint, type ObjectPart, PIER_SPACING_METERS, type Padding, type Pier, type PierOptions, type PlacedGlyph, type PlacedSymbol, Polygon, type PolygonOptions, type PolygonPiece, Polyline, type PolylineOptions, type PolylineSegment, Popup, type PopupOptions, type Projection, type PropRef, type PropType, type ProtocolHandler, type QualityChange, QualityGovernor, type QualityName, type QualityProfile, type QueriedFeature, type QueryGeometry, RELIEF_STYLE, ROAD_SIGNS, RUNTIME_FILL_STRIDE, RUNTIME_LINE_STRIDE, type RampOptions, RasterSource, type RasterSourceOptions, RasterTile, type RenderedQueryOptions, type RoadAxis, type RoadChain, type RoadEdges, type RoadGraph, type RoadGraphOptions, type RoadHeightOptions, type RoadHeights, type RoadMesh, type RoadMeshOptions, type RoadNode, type RoadRay, type RoadSourceEdges, type RoadSurface, type RoadSurfaceOptions, type RoundOptions, type RuntimeMesh, SDF_EDGE, SDF_PX, SIGN_SIZE, SOFT_STYLE, SPLIT_SEP, STYLE_REFERENCE, STYLE_REFERENCE_EXPRESSIONS, STYLE_REFERENCE_EXPRESSION_GROUPS, STYLE_REFERENCE_V2_FLAGS, SURFACE_CUT_BLEED, SURFACE_LEVEL_SCALE, SURFACE_VERTEX_STRIDE, ScaleControl, type ScreenPoint$1 as ScreenPoint, SdfAtlas, type ShadowSettings, type SkySpec, Style, type StyleImage, type StyleImageData, type StyleImageOptions, type StyleIssue, type StyleLayerHit, type StyleLayerSpec, type StyleReference, type StyleSpec, type StyleSpecV2, type StyleValidation, type SymbolHit, SymbolRenderer, TAPER_METERS, TERRAIN_STYLE, THEMES, TILE_SIZE, type TaperedWidth, TerrainControl, type ThemeInput, type ThemeSpec, type ThemeSpecV2, type TileAddress, TileID, Transform, type TransformConstrain, type TransformRequest, VectorTile, VectorTileFeature, VectorTileLayer, abbreviateStreet, addGround, altitudeFromMercatorZ, anonymousParts, applyTheme, arrowLabelOffset, arrowVertices, bridgePiers, bridgeProfile, buildRoadGraph, buildRuntimeFill, buildRuntimeLine, buildTile, builtinMapLibreStyle, builtinOsmglStyle, cameraForBounds, chainAxes, circumferenceAtLatitude, clampLat, clampPixelRatio, classifyRenderer, clipLineToRect, clipPolygonParts, convertFilter as convertMapLibreFilter, convertValue as convertMapLibreValue, coordinateDigits, coveringTiles, createGround, cutMargin, Map$1 as default, densifyLine, deviceHints, distToSegment, evaluateColor, evaluateExpression, evaluateNumber, expressionColor, expressionIssues, expressionNumber, extractFeature, featureContext, featureDrivenPaint, featureLevel, featureRuns, fillFootprint, fogPlanes, footprintOf, formatHash, generateBuildings, generateFill, generateLine, generateSurface, generateSymbols, globeBasis, globeFlatMix, globeRadius, globeToLocal, groundAt, iconRotateOf, iconRotation, importMapLibreStyle, insideFootprint, isExpression, isMapLibreStyle, isOsmglV2, junctionIndex, keptIndices, laneArrow, laneGuides, latFromMercatorY, layerV1toV2, liftFields, lngFromMercatorX, lngLatToUnit, loadMapLibreStyle, loadStyle, makeFeaturePaint, centerAnchor as mapLibreCenterAnchor, clipLine as mapLibreClipLine, lineAnchors as mapLibreLineAnchors, mapLibreSpot, matchesFilter, mercatorMetersPerTile, mercatorX, mercatorY, mercatorZFromAltitude, metersPerPixel, metersPerTile, needsDataPaint, nextQualityDown, parseGlb, parseGlbAsync, parseGlyphs, parseHash, parseLift, patchUV, pickBuilding, pickFill, pitchIntent, pixelsPerMeter, placeAlongLine, placeMarking, pointInRing, projectLine as projectSymbolLine, propertyV2toV1, qualityFor, qualityProfile, queryRendered, querySource, raySphere, refineLine, relaxAngle, relaxCurvature, replaceGlobalState, resolveStyleUrls, resolveSymbolField, resolveTemplate, ribbonFromEdges, ribbonRing, ribbonShape, ringEdges, roadEdges, roadHeights, roadMarkings, roadMesh, roadSurfaces, roundLine, roundRing, roundRings, sdfFromAlpha, shapeText, shapeTextCached, snapCutsInward, snapRoadEnds, speedSign, splitBucketKey, splitByGround, splitPolygons, squareClosed, stitchPieces, stopLine, styleV1toV2, styleWithTheme, taperedWidth, themeToInternal, themeV1toV2, tileOriginMeters, tileUrl, toLngLat, toMapLibreStyle, tryParseColor, unitToLngLat, unpackVector, validateStyle, validateTheme, waffle, worldSize, yieldTargetsOf, zebra };