План: офлайн-данные мира — свой пакет региона и подложка поверхности из OSM
30.09.2026. Статус: отложено (решение пользователя 30.09.2026: план сохраняем, реализацию откладываем). Замер размеров сделан (этап 0–1).
Замер вектора OSM — docs/plan/osm_vector_pack.md (сделан, 30.09.2026).
Идея-альтернатива (01.10.2026): список мест полётов из OSM и закачка полных данных по выбранному
месту вместо пакетов регионов — docs/plan/on_demand_location.md.
Цель
- Без интернета. Всё нужное для полёта (рельеф, земной покров, OSM) заранее перепаковано в свой компактный формат и лежит рядом с игрой пакетами регионов.
- Подробнее поверхность. Подложку 0,5–2 м/пикс (дороги, поля, лес, вода, скалы, посёлки) игра рисует сама на GPU из вектора OSM плитками вокруг пилота. Сейчас подложка — WorldCover 10 м, а OSM только в воде и только у встроенных локаций.
- Любая точка пакета — как встроенная локация. Сейчас при старте с карты нет ЛЭП, дорог и рек OSM. С пакетом они есть везде, где есть данные OSM.
Решения пользователя (не обсуждаются)
- Источник OSM —
.osm.pbf(Geofabrik), не OsmAnd OBF. - Берём только нужное, перепаковываем в свой компактный формат, кладём рядом с игрой.
- Растр подложки генерится из вектора, не хранится.
- Без домов: дома ставятся процедурно там, где есть посёлок. Без заборов.
- Ключевые объекты для пилота — ЛЭП; также аэродромы и прочее по оценке (см. «Состав пакета»).
- Замер размеров — на небольшой гористой европейской стране (Словения).
- Сеть убрать совсем, когда пакеты заработают: загрузка Terrarium/WorldCover по точке и
кеш
user://terrain_cacheудаляются, старт возможен только внутри пакетов. - Регион — пока только Алтай (и то под вопросом, см. вопрос в конце).
- Встроенные локации — из пакетов, Overpass-путь (
fetch_osm.py,data/osm) удалить. - Карта выбора старта — из того же вектора (реки, дороги, посёлки, старты); пункт TODO про martin закрыт этим планом.
- Рельеф — 30 м как основа. Из OSM в рельеф берём только обрывы, ровные озёра и врезанные русла. Подробный рельеф (лидарные модели 1–5 м, z13) не делаем: физика на другой сетке может не сойтись.
Как сейчас (по коду)
- Рельеф. Встроенные локации:
data/terrain/<id>/(detail.f32.br,far.f32.br), 8–11 МБ на локацию. Произвольная точка: тайлы Terrarium z12/z9 по сети, кешuser://terrain_cache(terrarium_loader.gd,configs/world.json → runtime_terrain). - Покров. Карта классов
SurfaceLayer(surface_layer.gd): лес, трава, поле, кусты, грунт, вода, застройка, снег. По ней красится земля, ставятся деревья, считаются источники термиков (surface.thermal.class_strength). Встроенные — PNG из WorldCover. Произвольная точка — WorldCover COG по HTTP range (worldcover_loader.gd). - Маска «деталь 10 м» (только встроенные): R — доля леса (WorldCover), G — доля воды,
растеризуется из OSM (
tools/terrain/osm_water.py). - OSM. Только офлайн через Overpass (
tools/osm/fetch_osm.py→data/osm/<id>.json, 1–2,8 МБ): дороги, дома, ЛЭП, реки, озёра, посёлки, поля, заборы. ЧитаетOsmData(scripts/world_objects/osm_data.gd), дальшеroad_mesher,building_placer,power_line_planner,obstacle_index. - Карта выбора старта — своя отмывка из Terrarium (
map_picker.gd). Пункт TODO «Векторная карта для выбора старта (рендер через martin)».
Состав пакета
| Слой | Теги OSM / источник | Для чего в игре |
|---|---|---|
| Рельеф | Terrarium z12 (≈24–30 м) и z9 для дали, или Copernicus GLO-30 | высоты, физика склона и потока |
| Покров (фон) | ESA WorldCover (классы, 10–25 м) | классы там, где OSM пусто |
| Дороги | highway=* (с track/path — по замеру), railway=rail/narrow_gauge | подложка, ориентиры, посадка |
| Вода | waterway=river/stream/canal/ditch/drain, natural=water, water=*, natural=wetland/glacier | подложка, класс «вода» |
| Землепользование | landuse=forest/meadow/farmland/orchard/vineyard/grass/residential/industrial/quarry/allotments, natural=wood/scrub/heath/grassland/bare_rock/scree/shingle/sand, natural=cliff (линия) | подложка, классы для термиков |
| Посёлки | place=* (точки и полигоны) + landuse=residential | процедурные дома, подписи на карте |
| Объекты для пилота | power=line/minor_line/tower/pole, aerialway=*, aeroway=aerodrome/airstrip/runway/helipad, sport=free_flying (старты и посадки), power=generator + generator:source=wind, man_made=mast/tower/communications_tower/chimney, natural=peak/saddle | столкновения, места посадки, готовые старты, ориентиры |
Не берём: building=*, barrier=*, адреса, POI, служебные поля (авторы, версии).
Физика и границы модели
- Подложка меняет физику, а не только вид. Класс поверхности задаёт силу источника термика. Внутри полигона OSM класс берётся из OSM (поле среди леса, деревня, осыпь); вне полигонов — из WorldCover. Сетка классов для термиков — 10–25 м, агрегируется из того же вектора.
- Рельеф: сетка ≈24–30 м (z13 не даёт новой информации там, где исходник 30 м). Из OSM в
высоты вносятся три поправки: уступ вдоль
natural=cliff, ровная поверхность озера по уровню воды (вода не «висит» на склоне), неглубоко врезанное русло реки. Насыпей и выемок нет. Поправки не должны ломать физику потока: они мельче шага сетки или сглажены до него. - Полнота OSM разная: в глуши подложка = WorldCover, объектов нет. Дата данных — дата выгрузки PBF. Это граница модели, не ошибка.
- Высоты препятствий: из
height/ele, если есть; иначе типовые (как сейчас для ЛЭП вpower_line_planner.gd: решётчатая опора по напряжению, иначе столб).
Устройство
- Сборка (офлайн, один раз на регион) —
tools/pack/build_pack.py: PBF + рельеф + WorldCover → один файл пакета: заголовок, индекс ячеек (например 0,25°×0,25°), в ячейке — рельеф (int16, сжатие), классы, вектор по слоям (целые координаты в сетке ячейки ~0,5–1 м, дельты + varint, упрощение ~1 м), brotli. Формат — по итогам замера. - Игра: читатель пакета (
FileAccess+ смещения по индексу) отдаётHeightLayer,SurfaceLayer,OsmDataтем же потребителям, что сейчас. Сетевой загрузки нет (terrarium_loader.gd,worldcover_loader.gdпо сети — удалить). - Карта выбора старта рисуется из пакета: отмывка рельефа + вода, дороги, посёлки, ЛЭП,
старты
free_flying; выбрать точку можно только внутри пакетов. - Подложка на GPU: плитки (например 2×2 км при 1 м) вокруг пилота рисуются из вектора
(
SubViewportилиRenderingDevice), вблизи мельче, вдали грубее; шейдер рельефа берёт из них класс и маски вместо «детали 10 м». Треугуляция полигонов — на CPU, в фоне. - Где лежат пакеты: папка рядом с исполняемым файлом (и/или
user://packs). Раздача — отдельными файлами на itch. Атрибуция: © OpenStreetMap contributors (ODbL), ESA WorldCover (CC BY 4.0), рельеф — по источнику.
Размеры (замер на Словении, osm_vector_pack.md)
Словения (20,4 тыс. км², самая плотная разметка OSM): ≈ 80 МБ — вектор OSM 63,2 МБ (81 % из него — землепользование), рельеф 30 м (int16, шаг 1 м, предсказатель, brotli) 13,6 МБ, покров 25 м 2,4 МБ, дальний рельеф 0,5 МБ. Рельеф и покров — пересчёт с пробных квадратов.
Оценки по замеру: Республика Алтай ≈ 90 МБ (OSM в 23 раза реже Словении, основной объём — растры), Швейцария ≈ 150 МБ, Австрия ≈ 230–330 МБ, Россия ≈ 14 ГБ, мир ≈ 110–125 ГБ. Растеризация 10×10 км при 1 м на CPU (skia, один поток) — ≈ 3–4,5 с.
Задачи
| # | Задача | Исполнитель | Приёмка | Дни |
|---|---|---|---|---|
| 0 | Замер вектора OSM на Словении (идёт) | dp-researcher | таблица по слоям, байт/км², время сборки; растр 10×10 км @1 м из пакета и время; пересчёт на Альпы/Россию/мир → osm_vector_pack.md | 0,5 |
| 1 | Замер рельефа и покрова в нашем формате на Словении (int16 + сжатие; классы) | dp-researcher | МБ на страну и на км², время; итоговая таблица «пакет Словении целиком» | 0,5 |
| 2 | Формат пакета и контракты: раскладка файла, координаты (lat/lon → ячейка), слои, классы, версия; docs/offline_data_contracts.md + контрактный тест | dp-coordinator | контракт принят; тест формы данных | 0,5 |
| 3 | Сборщик tools/pack/build_pack.py | dp-engineer | собираются Словения и Республика Алтай; повторная сборка даёт тот же файл; размеры в пределах замера | 1,5 |
| 4 | Чтение пакета в игре: рельеф, покров, OsmData из пакета; сетевую загрузку и кеш удалить | dp-engineer | с отключённой сетью старт в любой точке пакета работает; в коде нет HTTP-загрузки рельефа и покрова | 1,5–2 |
| 4а | Поправки рельефа из OSM при сборке: обрывы, ровные озёра, русла | dp-engineer | озёра ровные (разброс высот воды < 0,5 м); скриншоты обрыва, озера, русла | 1 |
| 5 | Подложка на GPU плитками вокруг пилота; шейдер рельефа берёт из неё класс и маски | dp-engineer | плитка 2×2 км @1 м рисуется без рывка кадра (замер); скриншоты до/после на одном месте | 2–3 |
| 6 | Классы для термиков из OSM + WorldCover | dp-engineer | тест: поле внутри леса даёт класс «поле», осыпь — «грунт»; термики стоят на местах полигонов | 1 |
| 7 | Объекты для пилота из пакета: ЛЭП и канатки (столкновения через obstacle_index), аэродромы и старты free_flying (места посадки, точки старта на карте), ветряки и мачты | dp-engineer | в Словении есть ЛЭП/канатки с столкновением; старты видны на карте выбора | 1,5–2 |
| 8 | Процедурные дома по place/landuse=residential через building_placer.gd | dp-engineer | посёлки застроены, дома не на дорогах и не в воде | 1 |
| 9 | Встроенные локации — из пакетов; убрать Overpass-путь (fetch_osm.py, data/osm) | dp-mechanic / dp-engineer | 4 локации летаются как раньше, data/osm удалён | 0,5–1 |
| 9а | Карта выбора старта из вектора пакета; пункт TODO про martin закрыть | dp-engineer | на карте видны реки, дороги, посёлки, ЛЭП, старты; точка вне пакетов не выбирается | 1,5–2 |
| 10 | Раздача: пакеты на itch отдельными файлами, где игра их ищет, атрибуция | dp-writer + главная сессия | инструкция и строка в ASSETS.md | 0,5 |
Сейчас в работе только волна A — анализ размера; остальное не начинаем. Шлюз после A: делать ли вообще (раздавать пакет порядка 1 ГБ, возможно, бессмысленно) и какие регионы.
Волны: A — 0, 1 (замеры) → шлюз. B — 2 → 3, 4, 4а. C — 5, 6, 7, 9а (параллельно, после 4). D — 8, 9, 10. Итого ~14–16 дней.
Риски
- Рельеф большого региона — гигабайты (Россия целиком, вероятно, не нужна: только регионы полётов).
- Склейка линий и полигонов на границах ячеек (реки, ЛЭП, большие лесные полигоны).
- Большие полигоны OSM (лес на сотни км²) — дорогая треугуляция; резать по ячейкам при сборке.
- Рывки кадра при дорисовке плиток — треугуляция в фоне, рисование малыми порциями.
- Полнота OSM в глуши — подложка там не лучше WorldCover.
Вопросы пользователю
- Регионы при одном Алтае. Встроенные локации — две на Алтае (Онгудай, «Алтай» у Горно-Алтайска) и две на Южном Урале (Аскарово, Аушкуль, Башкортостан). Если сеть убрана, а пакет только алтайский, уральским нужен свой пакет. Варианты: (а) пакет «Республика Алтай» + маленький пакет Южного Урала вокруг Аскарово и Аушкуля; (б) пакеты только квадратами вокруг встроенных локаций (старт с карты — лишь внутри них); (в) убрать уральские локации. Решить после замера (шлюз волны A).
Почему не OsmAnd OBF (исследование 30.09.2026)
Первая версия этого документа разбирала OsmAnd OBF (эксперимент — tools/research/obf_region/,
свой читатель на Python). Итог:
- ToS OsmAnd (п. 9.2) запрещает автоматическое скачивание, кеширование и раздачу их файлов без письменного согласия.
- Формат нестандартный (свой тип провода protobuf, версии без обещаний совместимости), живых библиотек вне OsmAnd нет.
- Рельефа и сплошного покрова нужного качества нет (изолинии или GeoTIFF ~75 м).
- Всё полезное в OBF — данные OSM, которые законно и проще взять из
.osm.pbf.
MBTiles/PMTiles и martin для полёта хуже своего пакета (объекты порезаны по тайлам, в стандартных схемах нет ЛЭП); для карты выбора старта не нужны — рисуем из своего вектора.
Источники
- Geofabrik: https://download.geofabrik.de/ ; ODbL: https://opendatacommons.org/licenses/odbl/1-0/
- osmium / pyosmium: https://osmcode.org/
- ESA WorldCover: https://esa-worldcover.org/ ; Copernicus DEM GLO-30: https://spacedata.copernicus.eu/collections/copernicus-digital-elevation-model
- AWS Terrain Tiles: https://registry.opendata.aws/terrain-tiles/
- OsmAnd Terms of Use: https://osmand.net/help-online/terms-of-use/ ; OBF: https://osmand.net/docs/technical/osmand-file-formats/osmand-obf/