osmgl 0.18.1 → 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,10 +921,39 @@ 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;
906
- constructor(ctx: Context, name: string, vertexSource: string, fragmentSource: string, defines?: Record<string, string | number | boolean>);
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;
938
+ constructor(ctx: Context, name: string, vertexSource: string, fragmentSource: string, defines?: Record<string, string | number | boolean>,
939
+ /**
940
+ * Места атрибутов, которые надо повторить, — у ВАРИАНТА программы (см. surfaceShader): VAO
941
+ * собран по местам базовой программы и обязан подойти каждому варианту. Общий список мест
942
+ * (ATTRIB_SLOTS) тут не годится: атрибутов больше, чем гарантированных шестнадцати мест.
943
+ */
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;
907
957
  private cacheLocations;
908
958
  use(): void;
909
959
  attribLocation(name: string): number;
@@ -1057,6 +1107,8 @@ interface LayerBase {
1057
1107
  overlay?: boolean | 'roads' | 'volume';
1058
1108
  /** Выключить слой, не удаляя из стиля. */
1059
1109
  visible?: boolean;
1110
+ /** Свободные данные приложения у слоя (`metadata` слоя MapLibre). Движок их не читает. */
1111
+ metadata?: Record<string, unknown>;
1060
1112
  /**
1061
1113
  * Радиус скругления углов в МЕТРАХ (заливки и объём).
1062
1114
  *
@@ -1069,6 +1121,25 @@ interface LayerBase {
1069
1121
  * из соседних тайлов сошлись бы со ступенькой.
1070
1122
  */
1071
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';
1072
1143
  /**
1073
1144
  * Обрезать заливку по квадрату тайла.
1074
1145
  *
@@ -1127,6 +1198,8 @@ interface RasterLayerSpec {
1127
1198
  minzoom?: number;
1128
1199
  maxzoom?: number;
1129
1200
  visible?: boolean;
1201
+ /** Свободные данные приложения у слоя (`metadata` слоя MapLibre). Движок их не читает. */
1202
+ metadata?: Record<string, unknown>;
1130
1203
  /**
1131
1204
  * Рисовать ПОВЕРХ векторной карты, а не под ней.
1132
1205
  *
@@ -1142,7 +1215,8 @@ interface RasterLayerSpec {
1142
1215
  interface FillLayerSpec extends LayerBase {
1143
1216
  type: 'fill';
1144
1217
  paint: MaterialPaint & {
1145
- color: Interpolated<string>;
1218
+ /** Цвет заливки; `{ from, property }` — «как у такого-то слоя» (плоские здания берут `roofColor`). */
1219
+ color: Interpolated<string> | ColorFrom;
1146
1220
  opacity?: Interpolated<number>;
1147
1221
  };
1148
1222
  }
@@ -1248,6 +1322,23 @@ interface LineLayerSpec extends LayerBase {
1248
1322
  */
1249
1323
  widthExtra?: Interpolated<number>;
1250
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;
1251
1342
  /**
1252
1343
  * ШИРИНА В МЕТРАХ вместо пикселей — для РАЗМЕТКИ.
1253
1344
  *
@@ -1308,7 +1399,14 @@ interface LineLayerSpec extends LayerBase {
1308
1399
  interface FillExtrusionLayerSpec extends LayerBase {
1309
1400
  type: 'fill-extrusion';
1310
1401
  paint: {
1311
- 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;
1312
1410
  roofColor?: Interpolated<string>;
1313
1411
  opacity?: Interpolated<number>;
1314
1412
  /** Затенение у основания, 0..1. */
@@ -1351,6 +1449,41 @@ interface FillExtrusionLayerSpec extends LayerBase {
1351
1449
  * Цвет при этом остаётся за темой: ручка только добавляет блик и градиент поверх него.
1352
1450
  */
1353
1451
  ice?: Interpolated<number>;
1452
+ /**
1453
+ * ОТРАЖЕНИЕ ДОМОВ В СТЕКЛЯННОЙ ЗЕМЛЕ, 0..1 — сила зеркала. Ноль — отражения нет.
1454
+ *
1455
+ * Работает только с z18 (`REFLECTION_MIN_ZOOM` в `layers/renderer`) и без рельефа: это второй
1456
+ * проход по всем домам кадра, и на общем плане он удвоил бы самую дорогую геометрию.
1457
+ */
1458
+ reflection?: Interpolated<number>;
1459
+ /**
1460
+ * БЛЕСК СТЕКЛЯННОЙ ЗЕМЛИ, 0..1: отражение неба по Френелю — чем дальше и чем более скользящий
1461
+ * взгляд, тем сильнее земля берёт цвет горизонта. Работает на любом зуме (это один квад), в
1462
+ * отличие от отражения домов.
1463
+ */
1464
+ reflectionGloss?: Interpolated<number>;
1465
+ /**
1466
+ * ДОМ КАК СТЕКЛО (по образцу Baidu) — только вместе с `ice`.
1467
+ *
1468
+ * `glassTint` — цвет стекла у земли (у Baidu бирюзовый), `glassTintAmount` — насколько (0..1),
1469
+ * `glassTintHeight` — на какой высоте, метры, цвет растворяется: низ цветной, верх светлый, как
1470
+ * у Baidu. Кровля светлее всего — оттенка на ней одна десятая. `glassRim` — светлая
1471
+ * кромка по Френелю: грань, повёрнутая к взгляду ребром, светится, как край стекла.
1472
+ */
1473
+ glassTint?: Interpolated<string>;
1474
+ glassTintAmount?: Interpolated<number>;
1475
+ glassTintHeight?: Interpolated<number>;
1476
+ glassRim?: Interpolated<number>;
1477
+ /**
1478
+ * Насколько верх дома ярче низа, 0..1 — в СВОЁМ цвете, без подмеса белого (0.1 — на 10 %).
1479
+ * Кровля — наравне с верхом стены. Только с `glassTintAmount`.
1480
+ */
1481
+ glassTopLight?: Interpolated<number>;
1482
+ /**
1483
+ * Градиент по плоской крыше, 0..1: ближний к камере край — в тон стекла, дальний — светлый.
1484
+ * Чтобы крыши стеклянного квартала не сливались в одно поле. Только с `glassTintAmount`.
1485
+ */
1486
+ glassRoofGradient?: Interpolated<number>;
1354
1487
  /**
1355
1488
  * Доля светящихся окон, 0..1 — для ночных тем. Какие именно окна горят,
1356
1489
  * решает хеш ячейки, поэтому при движении камеры свет не «бегает».
@@ -1380,6 +1513,11 @@ interface FillExtrusionLayerSpec extends LayerBase {
1380
1513
  roofVariation?: Interpolated<number>;
1381
1514
  /** Множитель высоты (например, 0 — «сложить» здания). */
1382
1515
  heightScale?: Interpolated<number>;
1516
+ /**
1517
+ * Градиент стены по высоте — `fill-extrusion-vertical-gradient` MapLibre. Работает только со
1518
+ * светом MapLibre (`lighting: 'maplibre'`); не задан — включён, как и у MapLibre.
1519
+ */
1520
+ verticalGradient?: boolean;
1383
1521
  };
1384
1522
  }
1385
1523
  /**
@@ -1390,6 +1528,8 @@ interface FillExtrusionLayerSpec extends LayerBase {
1390
1528
  * иконки силуэтные (одноцветные): раскрасить пиктограмму в несколько цветов
1391
1529
  * таким способом нельзя.
1392
1530
  */
1531
+ /** Якорь подписи или значка — какая точка бокса стоит на объекте (`text-anchor` у MapLibre). */
1532
+ type SymbolAnchor = 'center' | 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
1393
1533
  interface SymbolLayerSpec extends LayerBase {
1394
1534
  type: 'symbol';
1395
1535
  layout?: {
@@ -1406,6 +1546,16 @@ interface SymbolLayerSpec extends LayerBase {
1406
1546
  * занимает до половины строки. Так подписывают улицы Яндекс и 2ГИС — см. `text/abbreviate`.
1407
1547
  */
1408
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;
1409
1559
  /**
1410
1560
  * Разрядка букв в долях кегля — как `text-letter-spacing` у Mapbox.
1411
1561
  *
@@ -1442,6 +1592,15 @@ interface SymbolLayerSpec extends LayerBase {
1442
1592
  * приклеенным сбоку.
1443
1593
  */
1444
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';
1445
1604
  /**
1446
1605
  * Шаг повторения подписи вдоль линии, экранные пиксели (`textPlacement:
1447
1606
  * 'line'`). По умолчанию 250 — как `symbol-spacing` у Mapbox.
@@ -1450,11 +1609,63 @@ interface SymbolLayerSpec extends LayerBase {
1450
1609
  * раз, находится только в одном месте, и на соседнем экране та же улица
1451
1610
  * снова безымянная. Ноль — ставить ровно один раз.
1452
1611
  */
1453
- symbolSpacing?: number;
1612
+ symbolSpacing?: Interpolated<number>;
1454
1613
  /** Смещение подписи от точки привязки, CSS-пиксели. */
1455
1614
  offset?: [number, number];
1456
1615
  /** Максимум символов в строке до переноса. */
1457
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;
1458
1669
  /** Не участвовать в коллизиях (подпись всегда видна). */
1459
1670
  allowOverlap?: boolean;
1460
1671
  /**
@@ -1523,7 +1734,7 @@ interface SymbolLayerSpec extends LayerBase {
1523
1734
  * Значок доворачивается на поворот камеры, поэтому стрелка у входа смотрит
1524
1735
  * внутрь дома при любом развороте карты.
1525
1736
  */
1526
- iconRotate?: string;
1737
+ iconRotate?: string | number | unknown[];
1527
1738
  /**
1528
1739
  * Показывать слой ТОЛЬКО у выбранного здания (`highlight`).
1529
1740
  *
@@ -1593,6 +1804,10 @@ interface SymbolLayerSpec extends LayerBase {
1593
1804
  haloColor?: Interpolated<string> | ColorFrom;
1594
1805
  /** Толщина обводки в CSS-пикселях. Обводка обязательна поверх пёстрой карты. */
1595
1806
  haloWidth?: Interpolated<number>;
1807
+ /** Размытие обводки текста в пикселях — `text-halo-blur` MapLibre. */
1808
+ haloBlur?: Interpolated<number>;
1809
+ /** Размытие обводки значка — `icon-halo-blur`. */
1810
+ iconHaloBlur?: Interpolated<number>;
1596
1811
  /**
1597
1812
  * Свой цвет значка — иначе он красится цветом подписи (одна программа на
1598
1813
  * текст и иконки). Нужен плашкам: щит номера трассы это светлая заливка с
@@ -1851,6 +2066,13 @@ interface LightSpec {
1851
2066
  fillAltitude: number;
1852
2067
  fillColor: string;
1853
2068
  fillIntensity: number;
2069
+ /**
2070
+ * Сила теней, 0..1 (по умолчанию 1). Ноль — теней нет ВОВСЕ: проход карты теней не запускается.
2071
+ *
2072
+ * Заведено ради стеклянных тем по образцу Baidu: там город светится, и серое пятно у основания
2073
+ * каждого дома читается грязью. Дробное значение ослабляет тень на земле.
2074
+ */
2075
+ shadowStrength?: number;
1854
2076
  }
1855
2077
  /**
1856
2078
  * Небо и дымка.
@@ -2027,6 +2249,8 @@ interface StyleSpec {
2027
2249
  */
2028
2250
  levelHeight?: number;
2029
2251
  light?: Partial<LightSpec>;
2252
+ /** Свет объёма MapLibre — корневой `light` стиля MapLibre (см. `MapLibreLightSpec`). */
2253
+ mapLibreLight?: Partial<MapLibreLightSpec>;
2030
2254
  /** Небо и дымка. */
2031
2255
  sky?: Partial<SkySpec>;
2032
2256
  /** Дополнительные источники тайлов помимо `base` (его задаёт карта опцией `tiles`). */
@@ -2040,8 +2264,52 @@ interface StyleSpec {
2040
2264
  * Менеджер моделей читает его отсюда после каждого `setStyle` и сам решает, что перезагрузить.
2041
2265
  */
2042
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
+ }[];
2043
2294
  layers: StyleLayerSpec[];
2044
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;
2045
2313
  /**
2046
2314
  * Свет по умолчанию. Азимуты источников намеренно НЕ диагональные (120/300,
2047
2315
  * а не 135/315): на диагонали пары стен «восток/юг» и «запад/север» получают
@@ -2110,6 +2378,12 @@ interface LayerPlan {
2110
2378
  /** Скругление углов в метрах — геометрия, поэтому едет в воркер. */
2111
2379
  cornerRadius?: number;
2112
2380
  cornerSegments?: number;
2381
+ /** Линии: данные стыков в вершине (`lineCap`/`lineJoin` заданы) — см. `generators/line`. */
2382
+ joinData?: boolean;
2383
+ /** Только плоские крыши (`fill-extrusion` из стиля MapLibre). */
2384
+ flatRoofs?: boolean;
2385
+ /** Плоские нормали стен на каждое ребро, без сглаживания изломов (свет MapLibre). */
2386
+ flatNormals?: boolean;
2113
2387
  /** Резать заливку по тайлу — для полупрозрачных слоёв. */
2114
2388
  clipToTile?: boolean;
2115
2389
  /** Рельеф (тип `surface`): уровень и форма края, тоже геометрия. */
@@ -2150,14 +2424,23 @@ interface LayerPlan {
2150
2424
  /** Покраска на фиче: выражения, которые воркер посчитает и положит в вершину. */
2151
2425
  dataPaint?: DataPaintSpec;
2152
2426
  /** Только для symbol: шаблоны подставляются уже в воркере, чтобы не гонять свойства. */
2153
- textField?: string | string[];
2154
- iconImage?: string | string[];
2155
- /** Свойство фичи с поворотом значка (стрелка входа смотрит внутрь дома). */
2156
- 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[];
2157
2434
  /** `line` — подпись изгибается по дороге: воркер тогда кладёт и геометрию линии. */
2158
2435
  textPlacement?: 'point' | 'line';
2159
2436
  /** Сокращать родовое слово в названии («улица» → «ул.») — см. `text/abbreviate`. */
2160
2437
  abbreviate?: boolean;
2438
+ /** Вдоль линии — ВСЕ части составной линии, а не самая длинная (стиль MapLibre). */
2439
+ allLineParts?: boolean;
2440
+ /** Не склеивать куски линий с одной подписью — `line-center` у MapLibre (склеивает только `line`). */
2441
+ noLineMerge?: boolean;
2442
+ /** Регистр подписи (`text-transform`) — меняется в воркере вместе с сокращением. */
2443
+ textTransform?: 'none' | 'uppercase' | 'lowercase';
2161
2444
  }
2162
2445
  /** Источник по умолчанию: основная карта. */
2163
2446
  declare const BASE_SOURCE = "base";
@@ -2372,6 +2655,30 @@ interface BuildingsOptions {
2372
2655
  metersPerUnit?: number;
2373
2656
  /** Покраска на фиче: цвет и прозрачность считаются здесь и ложатся в вершины. */
2374
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';
2375
2682
  }
2376
2683
  declare function generateBuildings(layer: VectorTileLayer, extent: number, filter?: Filter, options?: BuildingsOptions,
2377
2684
  /**
@@ -2401,6 +2708,145 @@ interface BuildingSeam {
2401
2708
  holeCuts: boolean[][];
2402
2709
  }
2403
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
+
2404
2850
  /**
2405
2851
  * Сбор подписей из тайла.
2406
2852
  *
@@ -2449,16 +2895,28 @@ interface SymbolPlan {
2449
2895
  filter?: Filter;
2450
2896
  /** `line` — подпись изгибается по дороге; иначе стоит точкой у якоря. */
2451
2897
  textPlacement?: 'point' | 'line';
2452
- /** Шаблон подписи или список запасных: первый непустой побеждает. */
2453
- textField?: string | string[];
2454
- /** Шаблон имени иконки или список запасных. */
2455
- iconImage?: string | string[];
2456
- /** Свойство фичи с поворотом значка в градусах. */
2457
- iconRotate?: string;
2898
+ /** Шаблон подписи, список запасных (первый непустой побеждает) или выражение MapLibre. */
2899
+ textField?: string | unknown[];
2900
+ /** Шаблон имени иконки, список запасных или выражение. */
2901
+ iconImage?: string | unknown[];
2902
+ /** Свойство фичи с поворотом значка в градусах, число или выражение. */
2903
+ iconRotate?: string | number | unknown[];
2904
+ /** Зум тайла: его спрашивают `['zoom']` в выражениях и фильтры. */
2905
+ zoom?: number;
2458
2906
  /** Сокращать родовое слово в названии («улица» → «ул.») — см. `text/abbreviate`. */
2459
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';
2460
2914
  }
2461
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;
2462
2920
  /**
2463
2921
  * Подстановка `{prop}` в шаблон. Список шаблонов — это фолбэки: первый,
2464
2922
  * который раскрылся целиком, побеждает. Так `["{name:ru}", "{name}"]` даёт
@@ -2566,6 +3024,11 @@ interface LoadRequest {
2566
3024
  };
2567
3025
  /** Что строить из этого тайла — план из стиля. */
2568
3026
  plan: LayerPlan[];
3027
+ /**
3028
+ * Общее состояние стиля (`setGlobalStateProperty`) на момент запроса: у воркера свой экземпляр
3029
+ * модуля, и без этого фильтры и значения по фиче видели бы пустое состояние.
3030
+ */
3031
+ globalState?: Record<string, unknown>;
2569
3032
  }
2570
3033
  interface LoadedResponse {
2571
3034
  type: 'loaded';
@@ -2676,29 +3139,271 @@ declare class WorkerPool {
2676
3139
  destroy(): void;
2677
3140
  }
2678
3141
 
2679
- 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
+
2680
3152
  /**
2681
- * Значение стиля на конкретном зуме.
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.
2682
3345
  *
2683
- * Между остановками интерполируем ЛИНЕЙНО по зуму, а не экспоненциально: для
2684
- * ширин дорог и прозрачностей так предсказуемее, а «экспоненту» при желании
2685
- * задают частыми остановками.
3346
+ * Подпись: слой, поставленный импортёром MapLibre (`placementMode: maplibre`), остаётся слоем
3347
+ * MapLibre и получает явные значения там, где умолчания v1 и MapLibre расходятся; любой другой — это
3348
+ * своя система подписей (`osmgl-symbol-layout: block`), и его поля переносятся одним
3349
+ * переименованием: умолчания у этой системы свои, и пустое поле значит ровно то же, что в v1.
2686
3350
  */
2687
- declare function evaluateNumber(v: Interpolated<number> | undefined, zoom: number, fallback: number): number;
2688
- type RGBA = [number, number, number, number];
2689
- declare function evaluateColor(v: Interpolated<string> | undefined, zoom: number, fallback: RGBA): RGBA;
3351
+ declare function layerV1toV2(layer: StyleLayerSpec, style: StyleSpec, report?: ConvertReport): LayerSpecV2;
2690
3352
  /**
2691
- * Проверка фильтра. Отсутствующее свойство никогда не равно значению
2692
- * (кроме '!=' и '!in', где отсутствие считается несовпадением, — так же
2693
- * ведёт себя MapLibre).
3353
+ * СТИЛЬ v1 → v2. Чистая функция: та же внутренняя модель получается обратно загрузчиком
3354
+ * (`importMapLibreStyle`) — это и есть доказательство «без потерь» (`tests/style-v2.test.mjs`).
2694
3355
  */
2695
- declare function matchesFilter(filter: Filter | undefined, props: PropBag,
2696
- /** Остальное окружение фичи: номер, тип геометрии, состояние. Нужно выражениям. */
2697
- ctx?: {
2698
- id?: number | string | null;
2699
- geometryType?: string;
2700
- featureState?: Record<string, unknown>;
2701
- }): 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
+ };
2702
3407
 
2703
3408
  /**
2704
3409
  * Тема — набор правок поверх базового стиля, а не его копия.
@@ -2717,6 +3422,11 @@ interface ThemeSpec {
2717
3422
  sky?: Partial<SkySpec>;
2718
3423
  /** Правки paint-свойств по id слоя. Неизвестные id игнорируются. */
2719
3424
  paint?: Record<string, Record<string, unknown>>;
3425
+ /**
3426
+ * Правки layout-свойств по id слоя — то же, что `paint`, но для раскладки (кегль, выравнивание
3427
+ * подписи). Нужны теме, которой подписи дорог положены на асфальт (`textPitchAlignment: 'map'`).
3428
+ */
3429
+ layout?: Record<string, Record<string, unknown>>;
2720
3430
  /** Слои, скрытые в этой теме. */
2721
3431
  hidden?: string[];
2722
3432
  /**
@@ -2730,12 +3440,26 @@ interface ThemeSpec {
2730
3440
  * пришлось бы держать синхронным в двух местах.
2731
3441
  */
2732
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>>;
2733
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;
2734
3458
  /**
2735
3459
  * Накладывает тему на базовый стиль. Базовый не мутируется — возвращается
2736
3460
  * новый объект, иначе переключение туда-обратно накапливало бы правки.
2737
3461
  */
2738
- declare function applyTheme(base: StyleSpec, theme: ThemeSpec): StyleSpec;
3462
+ declare function applyTheme(base: StyleSpec, input: ThemeInput): StyleSpec;
2739
3463
 
2740
3464
  /**
2741
3465
  * Разобранный стиль. Держит порядок слоёв (он же порядок отрисовки: художника
@@ -2744,6 +3468,8 @@ declare function applyTheme(base: StyleSpec, theme: ThemeSpec): StyleSpec;
2744
3468
  declare class Style {
2745
3469
  readonly spec: StyleSpec;
2746
3470
  readonly light: LightSpec;
3471
+ /** Свет объёма MapLibre — для слоёв с `lighting: 'maplibre'` (см. `MapLibreLightSpec`). */
3472
+ readonly mapLibreLight: MapLibreLightSpec;
2747
3473
  readonly sky: SkySpec;
2748
3474
  readonly layers: StyleLayerSpec[];
2749
3475
  /**
@@ -2775,6 +3501,11 @@ declare class Style {
2775
3501
  * и должен оставаться пригодным, когда пользователь отъедет или приблизится.
2776
3502
  */
2777
3503
  workerPlan(source?: string): LayerPlan[];
3504
+ /**
3505
+ * План воркера для ОДНОГО слоя. Отдельно — ради правок на лету (`setPaintProperty` и соседи):
3506
+ * по нему видно, задела ли правка геометрию, и пересобирать тайлы нужно только тогда.
3507
+ */
3508
+ layerPlan(l: Exclude<StyleLayerSpec, RasterLayerSpec>): LayerPlan;
2778
3509
  /** Источники, на которые ссылается хоть один слой. */
2779
3510
  usedSources(): string[];
2780
3511
  /**
@@ -2839,9 +3570,22 @@ declare const RELIEF_STYLE: StyleSpec;
2839
3570
  * читается шумом; вода осталась водой, её блики и есть смысл материала.
2840
3571
  */
2841
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;
2842
3586
  declare const THEMES: Record<string, ThemeSpec>;
2843
3587
  /** Стиль с наложенной темой. Имя неизвестной темы приводит к базовому стилю. */
2844
- declare function styleWithTheme(theme: string | ThemeSpec, base?: StyleSpec): StyleSpec;
3588
+ declare function styleWithTheme(theme: string | ThemeInput, base?: StyleSpec): StyleSpec;
2845
3589
 
2846
3590
  interface CoverOptions {
2847
3591
  minzoom: number;
@@ -3134,7 +3878,16 @@ declare class TileSource extends Evented<TileSourceEvents> {
3134
3878
  maxTiles?: number;
3135
3879
  }): void;
3136
3880
  /** Новый план (сменился стиль): кеш геометрии устарел целиком. */
3137
- 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;
3138
3891
  reload(): void;
3139
3892
  destroy(): void;
3140
3893
  }
@@ -3212,6 +3965,16 @@ declare class SeamAssembler {
3212
3965
  * камеры от того набора кусков, что есть прямо сейчас, — и здание «дышало» бы: то целое, то
3213
3966
  * половина. Пока хоть один кусок на месте, дом остаётся тем же самым.
3214
3967
  */
3968
+ /**
3969
+ * Дома, которые НЕ СШИЛИСЬ, с подписью набора кусков, на котором это случилось (01.10.2026).
3970
+ *
3971
+ * Не сшивается дом, у которого часть кусков не загружена: остатки не смыкаются. Без этой памяти
3972
+ * такой дом считался «устаревшим» на каждом заходе и снова отдавался объединению; когда таких
3973
+ * домов больше бюджета, заход не заканчивался НИКОГДА (`pending`), и карта в покое рисовала кадр
3974
+ * за кадром, проигрывая одни и те же объединения (замер на эмуляторе: ~28 кадров в секунду,
3975
+ * поток GL занят наполовину). Повторяем только при смене набора кусков.
3976
+ */
3977
+ private failed;
3215
3978
  private cache;
3216
3979
  /**
3217
3980
  * Пересобрать то, что изменилось.
@@ -3380,6 +4143,8 @@ declare class GlyphSource {
3380
4143
  private glyphs;
3381
4144
  private loaded;
3382
4145
  private inflight;
4146
+ /** Номер набора глифов: растёт на каждый догруженный диапазон и смену адреса (см. `shapeTextCached`). */
4147
+ version: number;
3383
4148
  constructor(
3384
4149
  /** Шаблон вида /tileserver/fonts/{fontstack}/{range}.pbf */
3385
4150
  urlTemplate: string, fontstack?: string);
@@ -3447,6 +4212,11 @@ interface Box {
3447
4212
  y1: number;
3448
4213
  x2: number;
3449
4214
  y2: number;
4215
+ /**
4216
+ * Радиус, если это КРУГ (центр — середина бокса): так у MapLibre место занимает подпись вдоль
4217
+ * линии — цепочкой кругов (`placeCollisionCircles`). Без поля — прямоугольник.
4218
+ */
4219
+ r?: number;
3450
4220
  /**
3451
4221
  * Чьё это место.
3452
4222
  *
@@ -3491,6 +4261,45 @@ interface Candidate {
3491
4261
  x: number;
3492
4262
  y: number;
3493
4263
  }[];
4264
+ /** Те же вершины линии в меркаторе (x, y парами) — по ним сетка мест отмеряется ПО ЗЕМЛЕ. */
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;
3494
4303
  }
3495
4304
  /**
3496
4305
  * Размещённая подпись: то, что реально видно на экране в этом кадре.
@@ -3509,7 +4318,27 @@ interface PlacedSymbol {
3509
4318
  box: Box;
3510
4319
  /** Позиция слоя в стиле: верхняя подпись выигрывает попадание. */
3511
4320
  order: number;
4321
+ /** Ключ подписи — тот, под которым она стоит (после сверки с прошлой раскладкой, см. `stableKey`). */
4322
+ key?: string;
3512
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';
3513
4342
  declare class SymbolRenderer {
3514
4343
  private shader;
3515
4344
  private locations;
@@ -3526,6 +4355,17 @@ declare class SymbolRenderer {
3526
4355
  private pendingIcons;
3527
4356
  /** Сменить адрес шрифтов: `map.setGlyphs`. Разложенные подписи пересоберутся сами. */
3528
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;
3529
4369
  /**
3530
4370
  * Кого звать, когда стиль просит картинку, которой нет (`map.setMissingStyleImageResolver`).
3531
4371
  *
@@ -3553,6 +4393,7 @@ declare class SymbolRenderer {
3553
4393
  * Ставит рендерер; на обычной карте возвращает ноль, и проекция идёт как раньше.
3554
4394
  */
3555
4395
  elevationAt: ((mercX: number, mercY: number) => number) | null;
4396
+ private readonly mainFont;
3556
4397
  constructor(fontUrl: string, fontstack?: string);
3557
4398
  init(ctx: Context): void;
3558
4399
  /**
@@ -3608,13 +4449,66 @@ declare class SymbolRenderer {
3608
4449
  text: string;
3609
4450
  mercX: number;
3610
4451
  mercY: number;
4452
+ key?: string;
3611
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;
3612
4498
  setHover(key: string | null): boolean;
3613
4499
  setHidden(key: string | null): boolean;
3614
4500
  /**
3615
4501
  * Подпись под точкой экрана. Идём СВЕРХУ ВНИЗ по порядку стиля: под курсором
3616
4502
  * оказывается то, что человек видит верхним, а не то, что нарисовано первым.
3617
4503
  */
4504
+ /**
4505
+ * ВСЕ подписи под точкой, сверху вниз — для редактора стилей («какие слои здесь нарисованы»).
4506
+ * Одна подпись (`querySymbol`) нужна клику; редактору нужны и те, что легли под ней.
4507
+ */
4508
+ querySymbols(point: {
4509
+ x: number;
4510
+ y: number;
4511
+ }, tolerance?: number): PlacedSymbol[];
3618
4512
  querySymbol(point: {
3619
4513
  x: number;
3620
4514
  y: number;
@@ -3655,16 +4549,6 @@ declare class SymbolRenderer {
3655
4549
  /** Дымка этого кадра и камера — по ним считается затухание подписи вдаль. */
3656
4550
  private fog;
3657
4551
  private tr;
3658
- /**
3659
- * ГДЕ ПОДПИСЬ СТОЯЛА — переведённое в расстояние вдоль СЕГОДНЯШНЕЙ линии.
3660
- *
3661
- * Место запоминается точкой на земле, а не расстоянием от начала линии. Расстояние не годится:
3662
- * при наклоне камера срезает у ломаной начало (точку за собой спроецировать нельзя), и с каждым
3663
- * сдвигом карты отсчёт ведётся от другой вершины — сетка мест уезжает вдоль улицы, а с ней и
3664
- * название. Точка на асфальте не зависит ни от камеры, ни от обрезки: её достаточно спроецировать
3665
- * заново и померить, на каком она расстоянии сейчас.
3666
- */
3667
- private rememberedAt;
3668
4552
  /**
3669
4553
  * Запомнить места этого кадра точками на земле.
3670
4554
  *
@@ -3673,7 +4557,25 @@ declare class SymbolRenderer {
3673
4557
  * предыдущих: на следующем кадре они оказывались без памяти и выбирали место заново. Это и было
3674
4558
  * остаточное «ползание» на части улиц.
3675
4559
  */
3676
- 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;
3677
4579
  /**
3678
4580
  * Попадает ли бокс в область раскладки — кадр, расширенный на `LAYOUT_MARGIN`.
3679
4581
  *
@@ -3700,6 +4602,45 @@ declare class SymbolRenderer {
3700
4602
  private shownAt;
3701
4603
  /** Есть ли в кадре подпись, которая ещё проявляется — по ней решается, нужен ли следующий кадр. */
3702
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;
3703
4644
  /**
3704
4645
  * ГОТОВЫЙ КАДР ПОДПИСЕЙ — ПОВТОРЯЕМ, ЕСЛИ НИЧЕГО НЕ МЕНЯЛОСЬ (30.09.2026).
3705
4646
  *
@@ -3720,6 +4661,20 @@ declare class SymbolRenderer {
3720
4661
  private styleIds;
3721
4662
  private nextStyleId;
3722
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;
3723
4678
  private frameSignature;
3724
4679
  /** Прозрачность подписи: только что появилась — проявляется, стоит давно — единица. */
3725
4680
  private fadeOf;
@@ -3745,11 +4700,137 @@ declare class SymbolRenderer {
3745
4700
  * повезло оказаться раньше в списке после сдвига камеры на пиксель.
3746
4701
  */
3747
4702
  order(candidates: Candidate[]): void;
4703
+ /**
4704
+ * Стиль сменился, а раскладка та же (см. `layoutStamp`): диапазоны переводятся на слои НОВОГО стиля
4705
+ * — по ним `drawRanges` берёт покраску, — и ссылки на чужой цвет тоже.
4706
+ */
4707
+ private adoptStyle;
3748
4708
  /** Запомнить, что встало, — на этом держится липкость следующего кадра. */
3749
4709
  private rememberPlaced;
3750
4710
  render(ctx: Context, tr: Transform, sources: Map<string, TileSource>, style: Style): void;
3751
4711
  private collect;
3752
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;
3753
4834
  /**
3754
4835
  * Подпись вдоль линии: по квадру на глиф, каждый со своим поворотом.
3755
4836
  *
@@ -3793,6 +4874,26 @@ declare class SymbolRenderer {
3793
4874
  private requestMissing;
3794
4875
  destroy(ctx: Context): void;
3795
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;
3796
4897
  /**
3797
4898
  * Куда ставить подпись у слоя со стрелкой на земле.
3798
4899
  *
@@ -4021,8 +5122,23 @@ declare class IndoorMask {
4021
5122
  * Берётся из бакета оболочки (`class = level`) — это и есть след здания на
4022
5123
  * уровне. Пересчитывается при смене этажа или набора тайлов, а не каждый кадр:
4023
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`).
4024
5134
  */
4025
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;
4026
5142
  /**
4027
5143
  * Отметить дома этого тайла, попавшие под план.
4028
5144
  *
@@ -4032,12 +5148,6 @@ declare class IndoorMask {
4032
5148
  private collectHidden;
4033
5149
  destroy(): void;
4034
5150
  private free;
4035
- /**
4036
- * Индексы здания без тех фич, что стоят под открытым этажом.
4037
- *
4038
- * Считается ОДИН раз на тайл и на маску: перебор треугольников с проверкой
4039
- * принадлежности — работа для загрузки, а не для кадра.
4040
- */
4041
5151
  /**
4042
5152
  * Отметить дома под планом по ВСЕМ видимым тайлам сразу.
4043
5153
  *
@@ -4045,8 +5155,15 @@ declare class IndoorMask {
4045
5155
  * просмотренном тайле, в этом кадре останется наполовину видимым.
4046
5156
  */
4047
5157
  prepare(source: TileSource, layerId: string): void;
4048
- /** Геометрия зданий тайла без тех, что стоят под открытым этажом. */
4049
- 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): {
4050
5167
  vao: VertexArray;
4051
5168
  count: number;
4052
5169
  } | null;
@@ -5400,6 +6517,19 @@ declare const DEFAULT_SHADOWS: ShadowSettings;
5400
6517
  declare class LayerRenderer {
5401
6518
  private fill;
5402
6519
  private surface;
6520
+ /**
6521
+ * ВАРИАНТЫ ПРОГРАММЫ ПОВЕРХНОСТИ: без кода теней и под один материал (см. NO_SHADOWS и MATERIAL в
6522
+ * шейдере). Собираются по первой надобности и живут до конца — их не больше десятка.
6523
+ */
6524
+ private surfaceVariants;
6525
+ /** Варианты программы зданий: без теней, без фасада, без стекла (см. buildingShader). */
6526
+ private buildingVariants;
6527
+ /** Варианты программы линий: без теней, под один материал и один рисунок (см. lineShader). */
6528
+ private lineVariants;
6529
+ /** Для чего варианты уже собраны (см. warmVariants): стиль, тени, детализация. */
6530
+ private warmedStyle;
6531
+ private warmedShadows;
6532
+ private warmedDetail;
5403
6533
  private surfaceLocs;
5404
6534
  private line;
5405
6535
  private building;
@@ -5443,6 +6573,17 @@ declare class LayerRenderer {
5443
6573
  /** Настройки теней; правит карта через `setShadows`. */
5444
6574
  readonly shadows: ShadowSettings;
5445
6575
  private shadowTarget;
6576
+ /** Отражение домов в стеклянной земле (см. `renderReflection`). */
6577
+ private reflectionTarget;
6578
+ private reflectionShader;
6579
+ private reflectionQuad;
6580
+ private reflectionBuffers;
6581
+ /** Сила отражения в ЭТОМ кадре; ноль — прохода нет. */
6582
+ private reflectionStrength;
6583
+ /** Блеск стеклянной земли в этом кадре (см. `reflectionGloss`). */
6584
+ private reflectionGloss;
6585
+ /** Идёт проход отражения: шейдер домов переворачивает их по высоте. */
6586
+ private mirror;
5446
6587
  /** Пустышка под sampler2DShadow, когда теней нет, — см. createShadowPlaceholder. */
5447
6588
  private shadowPlaceholder;
5448
6589
  /** Тени посчитаны в этом кадре и их можно накладывать. */
@@ -5453,6 +6594,11 @@ declare class LayerRenderer {
5453
6594
  private materialTime;
5454
6595
  /** Хоть один материал в кадре анимируется — значит нужен следующий кадр. */
5455
6596
  private materialAnimating;
6597
+ /**
6598
+ * Следующий кадр нужен ТОЛЬКО ради материала (бегущей воды): карта может рисовать его реже
6599
+ * (`MATERIAL_IDLE_MS` у `Map`) — так же, как натив.
6600
+ */
6601
+ onlyMaterialsAnimating: boolean;
5456
6602
  /**
5457
6603
  * Материал ТЕКУЩЕГО слоя анимирован (бегущая вода) — но кадр просим, только если слой что-то
5458
6604
  * нарисовал.
@@ -5530,6 +6676,12 @@ declare class LayerRenderer {
5530
6676
  featureId: number;
5531
6677
  color: string;
5532
6678
  opacity: number;
6679
+ /**
6680
+ * Куски БЕЗЫМЯННОГО дома: ключ тайла → номер куска в нём (см. `anonymousParts`). Задан —
6681
+ * подсвечиваются только эти тайлы и каждый своим номером: номер такого дома свой в каждом
6682
+ * тайле, и «по номеру во всех тайлах» покрасил бы у соседа чужой дом.
6683
+ */
6684
+ parts?: ReadonlyMap<string, number>;
5533
6685
  } | null;
5534
6686
  /** Куски подсветки по тайлам: `null` — в этом тайле такой фичи нет. */
5535
6687
  private highlightGpu;
@@ -5641,7 +6793,10 @@ declare class LayerRenderer {
5641
6793
  hiddenVenueLayer: string | null;
5642
6794
  /** Урезанные индексы по тайлам; ключ — тайл, отпечаток — состав набора. */
5643
6795
  private hiddenCut;
6796
+ /** Отпечаток общего набора скрытого (заменённые + оболочки). `null` — ещё не считали. */
5644
6797
  private hiddenStamp;
6798
+ /** Отпечаток раскладки швов по тайлам. */
6799
+ private hiddenTileStamps;
5645
6800
  /**
5646
6801
  * Отпечаток наборов скрытого — ОДИН раз за кадр, а не на каждый тайк.
5647
6802
  *
@@ -5663,8 +6818,9 @@ declare class LayerRenderer {
5663
6818
  /**
5664
6819
  * Какие фичи ЕСТЬ в бакете. Живёт до перезагрузки тайла и переживает смену набора замен.
5665
6820
  *
5666
- * Ограничен по размеру грубо, сбросом целиком: записи вытесненных тайлов сами по себе не уходят,
5667
- * а точный учёт стоил бы больше, чем пересчёт пары сотен списков раз в долгую сессию.
6821
+ * Держится на самом буфере номеров фич (`WeakMap`): уходит вместе с тайлом, и ограничивать его
6822
+ * размер сбросом целиком не нужно. Строится по отрезкам фич (`featureRuns`), а не по номеру на
6823
+ * каждый треугольник: у тайла домов их сотни тысяч, отрезков — тысячи.
5668
6824
  */
5669
6825
  private bucketIdCache;
5670
6826
  /** Разобранные поверхности без заменённых фигур. Сбрасывается вместе с `hiddenCut`. */
@@ -5725,6 +6881,27 @@ declare class LayerRenderer {
5725
6881
  /** Места атрибутов у шейдера пачки: и вершинные, и на экземпляр. */
5726
6882
  private modelInstLocs;
5727
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;
5728
6905
  /** true, если что-то ещё анимируется и нужен следующий кадр. */
5729
6906
  render(ctx: Context, tr: Transform, sources: Map<string, TileSource>, style: Style, now: number, rasterSources?: Map<string, RasterSource>): boolean;
5730
6907
  /**
@@ -5773,6 +6950,24 @@ declare class LayerRenderer {
5773
6950
  * нужно знать, какому куску мира она принадлежит, — отсюда `u_tile_merc`.
5774
6951
  * На плоской карте всё наоборот: матрица своя у каждого тайла.
5775
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;
5776
6971
  /**
5777
6972
  * ПЛОСКИЙ ПРОХОД: земля в порядке стиля, без теста глубины.
5778
6973
  *
@@ -5977,7 +7172,45 @@ declare class LayerRenderer {
5977
7172
  * рисует землю. Программе без соответствующих юниформов установка молча проходит мимо.
5978
7173
  */
5979
7174
  private applyTileScale;
7175
+ /** Номер материала слоя; ноль — без материала (в том числе при нулевой детализации). */
7176
+ private materialIdOf;
7177
+ /**
7178
+ * Программа поверхности под этот слой и кадр. Тени выключены — берётся вариант без их кода,
7179
+ * материал — только свой. Так программа меньше, и мобильный GPU держит больше пикселей в работе:
7180
+ * на Adreno 610 полотно подешевело с 32 до 6 мс за кадр (01.10.2026).
7181
+ */
7182
+ private surfaceShader;
7183
+ /**
7184
+ * Программа зданий под этот слой и кадр — тот же приём, что у поверхности: выключенное (тени,
7185
+ * фасад при нулевой детализации, стекло вне ледяной темы) в программу не попадает. Цена здания
7186
+ * на мобильном GPU определяется размером программы, а не тем, какие ветки выполнились.
7187
+ */
7188
+ private buildingShader;
7189
+ /**
7190
+ * СОБРАТЬ ВАРИАНТЫ ПРОГРАММ ЗАРАНЕЕ — при смене стиля, а не при первой встрече в кадре.
7191
+ *
7192
+ * Сборка программы на мобильном GPU стоит десятки миллисекунд (замер на Adreno 610: кадр 187 мс
7193
+ * без единой заливки — это сборка). Встреча «по пути» приходится ровно на проезд по городу, когда
7194
+ * в кадр въезжает слой нового вида, — и выглядит рывком. Поэтому все варианты стиля собираются
7195
+ * разом: один долгий кадр при загрузке вместо рывков на ходу. Тени — по ожидаемому состоянию;
7196
+ * переключение теней соберёт свой набор один раз. Фасад зависит от зума — перебираем зумы слоя.
7197
+ */
7198
+ private warmVariants;
7199
+ /** Варианты программ, нужные стилю из `warmVariants`: по ним кадр решает, можно ли его рисовать. */
7200
+ private styleShaders;
7201
+ /** Стиль последнего кадра, нарисованного целиком, — его показываем, пока собираются программы нового. */
7202
+ private drawnStyle;
7203
+ /** С какого кадра ждём программы нового стиля; -1 — не ждём. */
7204
+ private styleWaitSince;
7205
+ /** Хоть один вариант стиля ещё собирается — спрашивается без ожидания (см. `Shader.ready`). */
7206
+ private variantsPending;
7207
+ /** Рисунок вдоль линии: 0 сплошная, 1 штрих, 2 шевроны, 3 стрелки, 4 только стрелки, 5 стрелки-штрих. */
7208
+ private linePatternOf;
7209
+ /** Программа линий под этот слой и кадр — тот же приём, что у поверхности (см. surfaceShader). */
7210
+ private lineShader;
5980
7211
  private applyMaterial;
7212
+ /** Метров на аппаратный пиксель у самого близкого к камере края земли — на кадр (см. `applyMaterial`). */
7213
+ private finestMeters;
5981
7214
  private drawFill;
5982
7215
  private drawLine;
5983
7216
  /**
@@ -6039,6 +7272,28 @@ declare class LayerRenderer {
6039
7272
  * тот проход считает всё лежащим на нуле.
6040
7273
  */
6041
7274
  private drawSurface;
7275
+ /**
7276
+ * Сила отражения в кадре: наибольшая из `reflection` видимых слоёв домов, с плавным входом на
7277
+ * `REFLECTION_MIN_ZOOM`. На рельефе и глобусе отражения нет: земля там не плоскость, и зеркало в
7278
+ * плоскости z = 0 показало бы дома не там, где они отражаются.
7279
+ */
7280
+ private reflectionStrengthOf;
7281
+ /** Блеск стеклянной земли: наибольший `reflectionGloss` видимых слоёв домов, на любом зуме. */
7282
+ private reflectionGlossOf;
7283
+ /**
7284
+ * ОТРАЖЕНИЕ ДОМОВ — В СВОЙ БУФЕР, ДО ОСНОВНОГО КАДРА.
7285
+ *
7286
+ * Приём зеркала в плоскости (`Reflector` three.js): для плоскости земли отражённая камера
7287
+ * равносильна тем же домам, перевёрнутым по высоте, — их и рисует обычный проход зданий с
7288
+ * `u_mirror = −1`. Намотка треугольников при этом зеркальная, поэтому отсекаются ПЕРЕДНИЕ грани.
7289
+ * Тени в отражении не нужны: его и так едва видно. Буфер — в полразмера кадра.
7290
+ */
7291
+ private renderReflection;
7292
+ /**
7293
+ * СТЕКЛЯННАЯ ЗЕМЛЯ — один квад во весь экран: размытое отражение домов (с z18) и блеск неба по
7294
+ * Френелю (на любом зуме). Выше горизонта не рисуется: там небо, а не стекло.
7295
+ */
7296
+ private drawReflection;
6042
7297
  private drawBuildings;
6043
7298
  /** Подсветить один сшитый дом: геометрия берётся из его же меша. */
6044
7299
  private drawSeamHighlight;
@@ -6066,6 +7321,15 @@ declare class LayerRenderer {
6066
7321
  */
6067
7322
  private growScale;
6068
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;
6069
7333
  /** Буферы GPU создаются лениво и живут в Tile.buckets — источник их и уничтожит. */
6070
7334
  /**
6071
7335
  * Собрана ли поверхность этого слоя как ПЛОСКАЯ заливка.
@@ -6253,6 +7517,124 @@ declare class Objects3DManager {
6253
7517
  clear(drop: (m: Model) => void): void;
6254
7518
  }
6255
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
+
6256
7638
  /**
6257
7639
  * ЧТО НАРИСОВАНО ПОД ТОЧКОЙ — с теми же именами полей, что у MapLibre.
6258
7640
  *
@@ -6399,6 +7781,20 @@ declare function pickFill(tr: Transform, source: TileSource, layerIds: string[],
6399
7781
  },
6400
7782
  /** Высота земли, метры: план этажа на рельефе лежит на склоне, как и всё остальное. */
6401
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>;
6402
7798
  /**
6403
7799
  * Треугольники одной фичи — из них строится подсветка.
6404
7800
  *
@@ -6732,6 +8128,8 @@ interface LinePlacement {
6732
8128
  path: ScreenPoint[];
6733
8129
  /** Где строка начинается — расстояние от начала куска линии, пиксели. */
6734
8130
  at: number;
8131
+ /** Множитель кегля этого экземпляра (стиль MapLibre на наклоне, см. `scalesAt`); нет — 1. */
8132
+ scale?: number;
6735
8133
  }
6736
8134
  /** Настройки раскладки; всё необязательное — по умолчанию как у Mapbox. */
6737
8135
  interface LinePlacementOptions {
@@ -6745,6 +8143,8 @@ interface LinePlacementOptions {
6745
8143
  fontSize?: number;
6746
8144
  /** Предельная сумма изломов в окне, градусы. */
6747
8145
  maxAngle?: number;
8146
+ /** Не раздвигать буквы на поворотах (MapLibre этого не делает: буквы стоят ровно по смещениям). */
8147
+ noCurveGap?: boolean;
6748
8148
  /**
6749
8149
  * Годится ли конкретная раскладка — коллизии, поля экрана, что угодно вызывающего.
6750
8150
  *
@@ -7011,6 +8411,11 @@ declare function sdfFromAlpha(alpha: Uint8Array | Uint8ClampedArray, width: numb
7011
8411
  */
7012
8412
  interface GlyphLookup {
7013
8413
  get(code: number): Glyph | undefined;
8414
+ /**
8415
+ * Меняется, когда меняется набор глифов (догрузился диапазон, сменился адрес). По нему
8416
+ * `shapeTextCached` понимает, что прежняя раскладка устарела. Нет поля — кеша нет.
8417
+ */
8418
+ readonly version?: number;
7014
8419
  }
7015
8420
  /** Один разложенный символ: где стоит и какой глиф рисовать. */
7016
8421
  interface ShapedGlyph {
@@ -7025,6 +8430,7 @@ interface Shaped {
7025
8430
  width: number;
7026
8431
  height: number;
7027
8432
  }
8433
+ declare function shapeTextCached(text: string, source: GlyphLookup, maxLineChars?: number, letterSpacing?: number, wrapWidth?: number): Shaped | null;
7028
8434
  /**
7029
8435
  * Раскладка строки в позиции глифов.
7030
8436
  *
@@ -7036,7 +8442,12 @@ interface Shaped {
7036
8442
  /**
7037
8443
  * @param letterSpacing разрядка между буквами в единицах кегля 24 — см. `letterSpacingFor`
7038
8444
  */
7039
- 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;
7040
8451
 
7041
8452
  /**
7042
8453
  * Сокращение родового слова в названии улицы: «улица Истаравшан» → «ул. Истаравшан».
@@ -7203,6 +8614,35 @@ declare function footprintOf(picked: {
7203
8614
  indices: Uint32Array;
7204
8615
  }, extent: number, id: TileID): Float64Array;
7205
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
+
7206
8646
  declare class GroundArrows {
7207
8647
  private vbo;
7208
8648
  private ibo;
@@ -7424,6 +8864,12 @@ interface LineOptions {
7424
8864
  * целый и один на бакет, а подход к эстакаде набирает высоту непрерывно.
7425
8865
  */
7426
8866
  liftAt?: (properties: Record<string, unknown>) => LiftField | null;
8867
+ /**
8868
+ * ФОРМА КОНЦОВ И СТЫКОВ (line-cap / line-join MapLibre): в вершину вместо ширины из данных и
8869
+ * подъёма идут направление предыдущего звена и признаки «начало/конец линии» — по ним шейдер
8870
+ * (`LINE_SHAPE`) режет торцы и строит острые стыки. Ширины из данных и подъёма у таких слоёв нет.
8871
+ */
8872
+ joinData?: boolean;
7427
8873
  }
7428
8874
  declare function generateLine(layer: VectorTileLayer, filter: Filter | undefined, maxSegment?: number,
7429
8875
  /** Уровни поверхностей тайла: по ним дорога ложится на то, по чему идёт. Нет — лежит на нуле. */
@@ -8601,6 +10047,35 @@ interface PierOptions {
8601
10047
  */
8602
10048
  declare function bridgePiers(axis: Ring | number[], profile: BridgeProfile, opts: PierOptions): Pier[];
8603
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
+
8604
10079
  /**
8605
10080
  * Положение камеры в адресе страницы.
8606
10081
  *
@@ -8858,69 +10333,51 @@ declare class LogoControl implements IControl {
8858
10333
  }
8859
10334
 
8860
10335
  /**
8861
- * ВЫРАЖЕНИЯ СТИЛЯ MapLibre.
8862
- *
8863
- * Стиль, написанный под MapLibre, почти целиком состоит из них: ширина дороги — `interpolate` по
8864
- * зуму, цвет — `match` по классу, видимость — `case`. Без интерпретатора такой стиль у нас не
8865
- * работает вовсе, а переписывать его на наши упрощённые формы (`stops`) для каждого приложения —
8866
- * это ровно та работа, которой перенос и должен избавить.
8867
- *
8868
- * ★ ИНТЕРПРЕТАТОР, А НЕ КОМПИЛЯТОР.
8869
- *
8870
- * MapLibre компилирует выражение в замыкания один раз и потом зовёт их. Мы считаем прямо по
8871
- * массиву, но КЕШИРУЕМ разбор (`compile`) — на зумовых свойствах это вызов на слой за кадр, и
8872
- * разница между подходами там неизмерима. На фильтрах, где вызовов десятки тысяч на тайл, кеш
8873
- * решает: разобранное выражение переиспользуется для каждой фичи.
10336
+ * ПРОВЕРКА СТИЛЯ osmgl — по справочнику (`reference.ts`), без карты и без сети.
8874
10337
  *
8875
- * ★ ЧТО УМЕЕТ, А ЧТО НЕТ.
8876
- *
8877
- * Умеет всё, что встречается в стилях: данные (`get`, `has`, `id`, `geometry-type`,
8878
- * `feature-state`, `global-state`), решения (`case`, `match`, `coalesce`, логика, сравнения),
8879
- * шкалы (`interpolate` линейный, экспоненциальный и по кривой Безье, `step`), арифметику,
8880
- * строки, цвета, переменные (`let`/`var`), массивы (`at`, `length`, `slice`, `in`).
10338
+ * Делит находки на две кучи, и граница между ними — «что увидит человек»:
10339
+ * ОШИБКА — движок нарисует не то или не нарисует вовсе: незнакомый тип слоя, цвет, который не
10340
+ * разобрался (это фуксин на карте), выражение с незнакомой операцией (пустое значение),
10341
+ * ссылка на несуществующий слой или источник, число вне допустимого;
10342
+ * ПРЕДУПРЕЖДЕНИЕ — движок тихо обойдётся: незнакомое поле (опечатка или поле MapLibre, которого у
10343
+ * нас нет — оно просто не читается), значение по фиче там, где движок считает раз на
10344
+ * слой (возьмётся запасное), поле общего вида у слоя, которому оно ни к чему.
8881
10345
  *
8882
- * Не умеет: `format` (богатый текст с разными шрифтами в одной подписи — у нас подпись
8883
- * однородная), `image` в значении «встроить картинку в текст», `distance`, `within` для линий и
8884
- * полигонов (точка проверяется, остальное — нет), `resolved-locale`. Всё перечисленное отмечено и
8885
- * в `porting/maplibre-api.md`.
10346
+ * Работает на каждом нажатии клавиши в редакторе, поэтому без вычисления выражений и без копий:
10347
+ * один проход по дереву стиля. Замер — в `tests/style-validate.test.mjs` (300 слоёв).
8886
10348
  */
8887
- /** Значение выражения: то, чем оперирует язык. */
8888
- type ExprValue = number | string | boolean | null | ExprValue[] | RGBAColor;
8889
- /** Цвет как значение языка: четыре канала 0..1, как их отдаёт `parseColor`. */
8890
- interface RGBAColor {
8891
- r: number;
8892
- g: number;
8893
- b: number;
8894
- 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;
8895
10355
  }
8896
- /** Всё, что выражение может спросить об окружении. */
8897
- interface ExprContext {
8898
- zoom?: number;
8899
- /** Свойства фичи (для фильтров и data-driven значений). */
8900
- properties?: Record<string, unknown>;
8901
- /** Номер фичи: `['id']`. */
8902
- id?: number | string | null;
8903
- /** Тип геометрии: `Point`, `LineString`, `Polygon`. */
8904
- geometryType?: string;
8905
- /** Состояние фичи (`map.setFeatureState`). */
8906
- featureState?: Record<string, unknown>;
8907
- /** Общее состояние стиля (`map.setGlobalStateProperty`). */
8908
- globalState?: Record<string, unknown>;
8909
- /** Точка фичи в координатах карты — нужна `within` для точечных фич. */
8910
- point?: [number, number];
10356
+ interface StyleValidation {
10357
+ errors: StyleIssue[];
10358
+ warnings: StyleIssue[];
8911
10359
  }
8912
- /** Похоже ли значение на выражение: массив, первый элемент которого — имя операции. */
8913
- declare function isExpression(value: unknown): value is ExprValue[];
8914
10360
  /**
8915
- * Посчитать выражение.
10361
+ * Проверить стиль osmgl.
8916
10362
  *
8917
- * Разбор кешируется на самом массиве: стиль живёт долго, а значения из него берут каждый кадр.
10363
+ * Принимает что угодно — то, что пришло из редактора, бывает не стилем вовсе. Стиль MapLibre (v8)
10364
+ * не проверяет: для него есть официальный валидатор, а здесь сказано об этом одной ошибкой.
8918
10365
  */
8919
- declare function evaluateExpression(expr: unknown, ctx: ExprContext): ExprValue;
8920
- /** Число из выражения; `fallback`, если получилось не число. */
8921
- declare function expressionNumber(expr: unknown, ctx: ExprContext, fallback: number): number;
8922
- /** Цвет из выражения в наш формат `[r, g, b, a]`. */
8923
- 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;
8924
10381
 
8925
10382
  interface RibbonOptions {
8926
10383
  /** Полуширина полотна в единицах тайла. */
@@ -9168,6 +10625,50 @@ declare function generateSurface(layer: VectorTileLayer, extent: number, filter:
9168
10625
  * векторных тайлах нет — так записано и в их спецификации.
9169
10626
  */
9170
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;
9171
10672
 
9172
10673
  /**
9173
10674
  * Объединение полигонов слоя в один контур — до построения меша.
@@ -9253,6 +10754,8 @@ interface PolygonPiece {
9253
10754
  * Длина — на одну меньше числа точек замкнутого кольца, то есть по числу рёбер.
9254
10755
  */
9255
10756
  cut: boolean[];
10757
+ /** Ложные рёбра дыр, по дыре на массив. Не заданы — концы стен у дыр не выправляются. */
10758
+ holeCuts?: boolean[][];
9256
10759
  }
9257
10760
  /**
9258
10761
  * Собрать полигон из кусков.
@@ -9264,6 +10767,107 @@ interface PolygonPiece {
9264
10767
  */
9265
10768
  declare function stitchPieces(pieces: PolygonPiece[], minWidth?: number): Shape[] | null;
9266
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
+
9267
10871
  /**
9268
10872
  * Цель рендера с одной только глубиной — карта теней.
9269
10873
  *
@@ -9462,10 +11066,19 @@ interface MapOptions {
9462
11066
  * они на порядки дольше.
9463
11067
  */
9464
11068
  sources?: Record<string, SourceSpec>;
9465
- /** Стиль: объект спецификации. По умолчанию — встроенный `styles/osm-3d.json`. */
9466
- style?: StyleSpec;
9467
- /** Тема оформления: `day` | `night` | `mono` | `pale` либо своя накладка. */
9468
- 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;
9469
11082
  /** Шаблон адреса SDF-глифов (формат Mapbox, его отдаёт наш tileserver-gl). */
9470
11083
  fonts?: string;
9471
11084
  /** Начертание из набора шрифтов тайлсервера. */
@@ -9536,6 +11149,16 @@ interface MapOptions {
9536
11149
  highlightColor?: string;
9537
11150
  }
9538
11151
  /** Подпись или значок карты под курсором. */
11152
+ /** Слой стиля под точкой экрана (`layersAt`): подпись ИЛИ фича геометрии. */
11153
+ interface StyleLayerHit {
11154
+ /** Идентификатор слоя стиля. */
11155
+ layer: string;
11156
+ type: StyleLayerSpec['type'];
11157
+ /** Фича заливки, линии, объёма или поверхности — с её свойствами из тайла. */
11158
+ feature?: QueriedFeature;
11159
+ /** Подпись или значок, как их разместила раскладка этого кадра. */
11160
+ symbol?: SymbolHit;
11161
+ }
9539
11162
  interface SymbolHit {
9540
11163
  /** Слой стиля: по нему понятно, что это — POI, дом, остановка. */
9541
11164
  layerId: string;
@@ -9673,6 +11296,39 @@ declare class Map$1 extends Evented<MapEvents> {
9673
11296
  /** Стиль разобран и первый кадр показан. Имя `loaded` занято методом MapLibre. */
9674
11297
  private ready;
9675
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;
9676
11332
  private background;
9677
11333
  private maxPixelRatio;
9678
11334
  /** Шаг прилипания зума у кнопок (`zoomIn`/`zoomOut`). Ноль — не липнет. */
@@ -10036,8 +11692,78 @@ declare class Map$1 extends Evented<MapEvents> {
10036
11692
  * фильтры, источники). Смена одних цветов — а это ровно случай переключения
10037
11693
  * темы — обходится сменой юниформов: тайлы не перекачиваются и не
10038
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
+ * спрайт перекачивался бы на каждое движение ползунка в редакторе.
10039
11763
  */
10040
- setStyle(spec: StyleSpec): this;
11764
+ private syncStyleAssets;
11765
+ /** Поставить шрифты и набор спрайтов, не трогая то, что уже стоит как надо. */
11766
+ private applyAssets;
10041
11767
  /**
10042
11768
  * Добавить слой. `beforeId` — перед каким слоем встать; без него слой ложится поверх всех.
10043
11769
  *
@@ -10064,8 +11790,17 @@ declare class Map$1 extends Evented<MapEvents> {
10064
11790
  * `setPaintProperty`, который меняет только юниформы.
10065
11791
  */
10066
11792
  setFilter(id: string, filter: Filter | null): this;
10067
- /** Спецификация стиля целиком — копией, чтобы правки снаружи не меняли живой стиль. */
10068
- 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;
10069
11804
  /**
10070
11805
  * Стиль разобран.
10071
11806
  *
@@ -10105,6 +11840,22 @@ declare class Map$1 extends Evented<MapEvents> {
10105
11840
  * и правки надо уметь применить заново — в том же порядке, в каком их сделали.
10106
11841
  */
10107
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[];
10108
11859
  /**
10109
11860
  * ЧТО НАРИСОВАНО В ЭТОЙ ТОЧКЕ (или в этом прямоугольнике) ЭКРАНА.
10110
11861
  *
@@ -10300,12 +12051,19 @@ declare class Map$1 extends Evented<MapEvents> {
10300
12051
  * выбранный маршрут». Значение живёт у стиля, а не у слоя, — поэтому и имя общее.
10301
12052
  */
10302
12053
  setGlobalStateProperty(key: string, value: unknown): this;
12054
+ /**
12055
+ * СОСТОЯНИЕ ДОЛЖНО ДОЙТИ ДО ВОРКЕРА. Фильтры, значения по фиче и подписи считаются там, у
12056
+ * воркера свой экземпляр модуля, и состояние уходит к нему с каждым запросом тайла
12057
+ * (`LoadRequest.globalState`). Тайлы, уже разобранные по старому состоянию, пересобираются — но
12058
+ * только у источников, чей план вообще смотрит на `global-state`.
12059
+ */
12060
+ private replanForGlobalState;
10303
12061
  getGlobalState(): Record<string, unknown>;
10304
12062
  /** Повторять ли мир по горизонтали при отъезде. */
10305
12063
  getRenderWorldCopies(): boolean;
10306
12064
  setRenderWorldCopies(render: boolean): this;
10307
12065
  /** Переключить тему оформления поверх текущей структуры слоёв. */
10308
- setTheme(theme: string | ThemeSpec): this;
12066
+ setTheme(theme: string | ThemeInput): this;
10309
12067
  /**
10310
12068
  * СМЕНИТЬ ОСНОВУ, на которую ложатся темы.
10311
12069
  *
@@ -10314,7 +12072,7 @@ declare class Map$1 extends Evented<MapEvents> {
10314
12072
  * рельефная) нужно заменить саму основу — этим и занимается этот метод. Тема применяется сразу,
10315
12073
  * чтобы палитра не слетала на время переключения.
10316
12074
  */
10317
- setBaseStyle(style: StyleSpec, theme?: string | ThemeSpec): this;
12075
+ setBaseStyle(input: StyleSpec | StyleSpecV2, theme?: string | ThemeInput): this;
10318
12076
  /** Имена доступных тем. */
10319
12077
  static get themes(): string[];
10320
12078
  /**
@@ -10328,16 +12086,61 @@ declare class Map$1 extends Evented<MapEvents> {
10328
12086
  id: string;
10329
12087
  name: string;
10330
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
+ }[];
10331
12104
  /**
10332
12105
  * Правка layout-свойств слоя на лету: размер кегля, зазор до значка, шаг
10333
12106
  * разрежения, расположение иконки.
10334
12107
  *
10335
12108
  * Отдельно от `setPaintProperty` не для симметрии: paint — это юниформы, а
10336
12109
  * layout влияет на РАЗМЕЩЕНИЕ, и его правка пересобирает раскладку подписей
10337
- * (но не геометрию). Исключение — `textField`, `iconImage` и `splitBy`: они
10338
- * читаются в воркере, поэтому тянут за собой новый план и перекачку разбора.
12110
+ * (но не геометрию). Исключение — то, что читает воркер (`textField`, `iconImage`,
12111
+ * `iconRotate`, `textPlacement`, `abbreviate`, `textTransform`, …): такая правка тянет
12112
+ * за собой новый план и пересборку тайлов, но только источника этого слоя.
10339
12113
  */
10340
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;
10341
12144
  /**
10342
12145
  * Правка paint-свойств слоя на лету. Геометрия НЕ пересобирается — меняются
10343
12146
  * только юниформы, поэтому цвет и ширину можно крутить хоть каждый кадр.
@@ -10635,7 +12438,15 @@ declare class Map$1 extends Evented<MapEvents> {
10635
12438
  data: Float32Array;
10636
12439
  format: string;
10637
12440
  } | null;
12441
+ /** Кадр упал вне слоёв (тени, отражение, небо): сказать один раз на причину, цикл не рвать. */
12442
+ private frameFailures;
12443
+ private reportFrameFailure;
10638
12444
  private renderFrame;
12445
+ /** Будильник следующего кадра ряби (см. конец `renderFrame`). */
12446
+ private materialTimer;
12447
+ /** Следующий кадр заказан паузой ряби, а не потоком кадров (см. `renderFrame`). */
12448
+ private pacedWake;
12449
+ private scheduleMaterialFrame;
10639
12450
  /** Поставить будильник на ближайший повтор; уже стоящий на более ранний срок не трогается. */
10640
12451
  private scheduleRetryWake;
10641
12452
  private stopFrameLoop;
@@ -10643,4 +12454,4 @@ declare class Map$1 extends Evented<MapEvents> {
10643
12454
  destroy(): void;
10644
12455
  }
10645
12456
 
10646
- 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 };