Сервис экспорта OSM в CDR/DWG/STL — прошу обратной связи

Коллеги, привет. Делаю сервис osm2cdr.ru — экспорт данных OSM по выбранной области или административной границе в 62+ форматов: от CDR (CorelDRAW) и DWG/DXF (AutoCAD) до SVG, Shapefile, GeoJSON и STL для 3D-печати. Слои карты сохраняются редактируемыми, экспорт режется по границе места, есть каталог готовых областей (по РФ — полное дерево регионов/районов/городов).

Атрибуция ODbL вшита в каждый файл. Сервису важно мнение сообщества: чего не хватает, что сделано не по-OSM-овски? Особенно интересны замечания от тех, кто сам готовит карты для печати или ЧПУ.

Выглядит здорово. Небольшой момент - видимо поле ввода как-то размечено, предлагает автоподстановки адресов, в основном всяких доставок которыми пользовались в браузере. А после ввода нужного НП, Хром настойчиво (после каждого клика на карту) предлагает сохранить введенное как новый адрес, отказа не принимает (спрашивает снова и снова)

Отправка отзыва после генерации пишет “Не удалось отправить. Попробуйте позже.”

В логе браузера возвращает ошибку “{
“errors”: [
{
“field”: “page_url”,
“message”: “Слишком длинный текст”,
“code”: “string_too_long”
}
],
“message”: “Слишком длинный текст”
}

Сам процесс понравился, но при выборе детализации не хватает превью. Были проблемы с превью и на следующих страницах, пока несколько раз не дернул вручную не генерилось.

На сгенерированном ПДФе присутствуют артефакты, предположительно что-то пошло не так с рельефом:

Справа карта как-то обрезана (браузер vivaldi). Флаг возле leaflet может не понравиться надзорным органам (раз у вас ru домен).

Спасибо за подробные замечания — это ровно то, о чём я просил. Отвечу по пунктам.

Поле ввода. Виноват: браузер принимает его за адресное поле, поэтому подсовывает сохранённые адреса и потом навязчиво предлагает сохранить город как новый. Подсказки автозаполнения там не нужны — уберу.

Отправка отзыва. По вашему логу нашёл причину, спасибо, что заглянули в консоль: у поля со ссылкой страницы стоял лимит длины, а на странице генерации ссылка с параметрами его превышает — и отзыв отбрасывался целиком. То есть отзывы оттуда до сих пор терялись, и я об этом не знал. Уже правится: лимит поднимаю, длинную ссылку буду обрезать, а не отклонять сам отзыв.

Превью. Сейчас предпросмотр в конструкторе выключен намеренно: он рисовался упрощённо и расходился с итоговым файлом — человек ориентировался на картинку, а получал другое. Но из вашего описания видно, что отсутствие превью читается как «сломалось», и это моя недоработка в объяснении. А про «генерилось только после ручных дёрганий» — подскажите, на какой это было странице (выбор детализации или страница готовности файла)? Хочу воспроизвести.

Артефакты на PDF. Похоже на слой рельефа. Чтобы поймать точно: какой был город или область, какой стиль и был ли включён рельеф? Если сохранилась ссылка на страницу генерации — идеально. Параллельно попробую воспроизвести сам.

Обрезанная карта в Vivaldi. Проверю. Если не сложно, скажите ширину окна и масштаб страницы: подозреваю, что дело в моей вёрстке на широком окне, а не в браузере.

Флаг у подписи под картой. Это часть стандартной атрибуции картографической библиотеки — приходит вместе с ней, я его не добавлял и, признаюсь, не замечал. Спасибо, что показали со стороны: решил убрать эту подпись с карт целиком. Обязательная атрибуция данных (© OpenStreetMap contributors) остаётся — по лицензии нужна именно она.

Первые два пункта правлю сразу, по остальным вернусь в тему с результатом.

Ещё почему-то в названиях НП в приоритете name:en, хотя выбирались местные названия.

@no87xis выглядит интересно, с формальной стороны даже придираться ни к чем у не хочется.

Но выгрузка сразу во все форматы, не слишком ли амбициозно?

3D как и ожидалось весьма относительное, только экструзия полигонов.

А SVG на чем основано?

Всё-таки вот это не совсем OSM 3D.

Вот это OSM 3D

Вернулся с результатами — всё из вашего списка разобрано, спасибо ещё раз за такую предметную обратную связь.

Поле ввода. Починил: поле теперь явно размечено как поисковое, а не адресное. Подстановки старых адресов и навязчивое «сохранить адрес» после кликов по карте должны исчезнуть. Если у вас всё ещё всплывает — напишите, проверю глубже: у этой браузерной эвристики есть запасной триггер, который лечится только сменой текста-подсказки в поле (заготовка уже есть).

Отзыв со страницы генерации. Исправлено ещё по горячим следам: длинная ссылка страницы больше не топит отзыв, теперь она просто обрезается.

Превью. Двумя частями. Превью карточек и страниц готовых карт теперь дорисовывается само — раньше, если картинка ещё не была готова к первому открытию, страница навсегда оставалась без неё, пока не перезагрузишь (это и были ваши «ручные дёрганья», честный баг). А в самом конструкторе превью нет намеренно, и теперь это доведено до конца: старое рисовалось упрощённо и расходилось с итоговым файлом — смотришь на одно, получаешь другое. Я убрал его совсем, вместе со всеми кнопками и окошками: итоговая карта всегда рендерится заново в полном качестве, а состав слоёв виден в самих шагах.

Артефакты на PDF. Воспроизвёл на трёх областях — вы угадали, это рельеф: подложка с тенями рельефа растягивалась до печатного размера грубо, без сглаживания, отсюда квадратные пятна. Исправил — теперь растяжка сглаженная. Повторите, пожалуйста, свой заказ; если пятна остались — напишите город и настройки, догоню.

Обрезка карты справа. Вы правы, и браузер ни при чём — это моя вёрстка: карта была на 18 пикселей шире своей рамки, и правый край срезался на любом экране. Починил, теперь края симметричны.

Значок у подписи внизу карты. Убрал подпись целиком — и с сайта, и из скачиваемых интерактивных карт. Обязательная пометка об источнике данных («© OpenStreetMap contributors») осталась, как и положено.

1 Like

Экспорт в .osm странноватый, во многих местах в тегах NULL:

Вероятно это дефолтное значение, если тега нет, но это уже какой-то странный OSM XML получается. Плюс двоеточия в ключах преобразуются в нижние подчёркивания, что уже не «Tags carry over as key-value pairs losslessly»

@TrickyFoxy, сначала по вашему сообщению выше. Увидел, спасибо за конкретику со скриншотом. NULL в значениях тегов и двоеточия, ставшие подчёркиваниями, проверю на боевом сервисе и отвечу отдельным постом с замерами, а не на глазок. Вторая половина претензии выглядит справедливой сразу: если ключи переименовываются, слово «losslessly» на нашей странице своё обещание не держит — это надо либо чинить, либо убирать со страницы.

Дальше — то, с чем я изначально шёл в тему.

Небольшой апдейт по 3D — и заодно ответ на замечание из этой темы, что у нас
«просто экструзия полигонов». Замечание было по делу, поэтому отвечаю не
словами, а тем, что удалось померить.

Что там на самом деле кроме экструзии. Основа — да, экструзия контура по
высоте из height / building:levels. Но поверх неё читаются building:part
(S3DB): части здания собираются в отдельные тела, а min_height и
building:min_level дают ярусам их нижнюю отметку — башня со стилобатом
получается ярусами, а не одним параллелепипедом. Крыши строятся по roof:shape
для gabled, hipped, half-hipped, gambrel, skillion, pyramidal, dome, round, onion
и mansard; всё остальное честно остаётся плоским. На bbox Кремля это 2 592 крыши
из 2 595 размеченных — не строятся many (2 штуки) и saltbox (1). Это
по-прежнему не полноценное моделирование, но и не только выдавливание.

Чтобы не быть голословным — один и тот же bbox Кремля
(37.606,55.747,37.625,55.757), один и тот же ракурс, слева наш экспорт времён
того замечания, справа сегодняшний:

:down_arrow: КАРТИНКА 1 сюда :down_arrow:

И то же самое крупным планом — шатры башен, купола и луковицы, стена объёмом,
деревья, а заодно фонари, скамьи и ограды, которых в исходной претензии не было
вовсе:

:down_arrow: КАРТИНКА 2 сюда :down_arrow:

Сцена целиком: 5 687 объёмных тел, 167 091 грань, 890 деревьев, 4 500 объектов
подложки (дороги, тротуары, трава, вода). Цвета — из building:colour и
roof:colour, 68 разных значений на этот bbox.

Что чинили сейчас. У 3D-выгрузки была неприятная особенность: соседние
здания в OSM часто делят общую стену, и их контуры имеют общее ребро с
буквально совпадающими координатами. После склейки сцены такое ребро получает
четыре грани вместо двух, модель перестаёт быть watertight, и слайсер ругается —
для печати это брак.

Механизм разведения совпадающих границ у нас был с начала августа, но при
разборе выяснилось, что он ломался в трёх местах сразу.

Во-первых, он вызывался ровно из одного места и видел только тела
building:part — обычные контуры зданий не разводил вовсе. Теперь контуры и
части попадают в один граф соприкосновений.

Во-вторых, механизм целиком отключался, если тел в сцене становилось больше
20 000 — то есть ровно на тех заказах, где плотная застройка и общие стены
встречаются чаще всего. Потолок был выставлен «на глаз», поэтому мы его не
угадывали заново, а померили: пик памяти на 250 000 тел — 453 МБ, время
построения графа — 7,3 с. Потолок поднят до этой цифры по замеру, а не по
ощущению.

В-третьих, величина развода была задана в метрах исходника — доли миллиметра,
константой. Звучит безобидно, пока не вспомнишь, что координаты в файле лежат
во float32, и шаг сетки float32 растёт вместе с размером сцены. На заказе
2 × 2 км константа в 0,5 мм — это восемь шагов сетки, запас есть. На заказе
10 км — уже один шаг, то есть развода фактически нет: вершины садятся обратно
друг на друга при первом же округлении. Теперь база и шаг считаются от размера
кадра заказа (в шагах сетки float32), а прежние миллиметры остались полом. На
городских заказах до ~2 км не изменилось ни байта — проверено побайтово.

Чем это подтверждено. Пять городских сцен, одни и те же bbox, формат STL,
заказы через боевой сервис до и после правки. Считались рёбра с числом граней,
отличным от двух, при побитовой склейке вершин по float32 — ровно то, что видит
читатель файла:

сцена было стало
Московский Кремль 0 0
Казань, центр 0 0
Суздаль 0 0
Прага, Мала Страна 1 0
Гейдельберг, старый город 9 0

Число треугольников при этом не изменилось нигде — разъезжаются только вершины,
которые раньше совпадали.

Отдельно — самый крупный заказ, который мы прогнали: Гейдельберг целиком,
10 × 5,5 км, 20 988 зданий. Именно на нём было видно обе поломки сразу, и
именно он давал 344 дефектных ребра. После правки — 0, при том же числе
треугольников (374 596) и без роста времени заказа (34,6 с до, 32,8 с после,
тот же класс воркера). Это, насколько мы смогли проверить, потолок не сцены, а
нашего терпения: больше пока не заказывали.

Где это НЕ работает — честно. Два известных ограничения:

  1. Вырожденная геометрия в самих данных (самопересечения, нулевые площади)
    остаётся законным остатком — её мы не «чиним» молча.
  2. Таблица выше — про STL: печатная модель, побитовая склейка. В OBJ-выгрузке
    той же сцены Кремля остаются 27 незакрытых тел из 5 687 (0,47 %) — это
    16 малых форм (ленты оград и стен на изломах) и 11 крышных надстроек по
    24–30 граней. Другой класс дефекта, к общим стенам отношения не имеет;
    месяц назад их было 50 из 4 424.

О чём прошу. Если кто-то гоняет наши STL/OBJ/glTF через слайсер или
MeshLab — интересны сцены, где watertight всё ещё ломается. Особенно города с
плотной исторической застройкой и с активно размеченным S3DB: именно там
вылезают случаи, которых нет в наших пяти сценах. Bbox и формат — этого хватит,
чтобы воспроизвести.


1 Like

@TrickyFoxy, как обещал — отдельным постом с замерами. Оба пункта подтвердились, оба почини́ли, всё уже на бою.

Мерил боевым заказом, чтобы не гадать по коду: центр Москвы, bbox 37.605,55.755,37.625,55.765 (~1,4 км²), 10 437 объектов. Тот же bbox тем же заказом до и после правки.

Про NULL. Вы предположили верно — это именно «значение по умолчанию, когда тега нет», только не задуманное, а случайное. QGIS отдаёт пустой атрибут не как None, а как QVariant-NULL: он проходит проверку «значение не пустое», а при переводе в строку даёт литерал NULL. Проверка в экспортёре была написана как «не None и не пустая строка» — QVariant-NULL пролезал в обе дырки.

Масштаб оказался хуже, чем выглядит на скриншоте:

тегов в файле из них NULL объектов с таким тегом
было 110 879 55 942 (50,5%) 10 179 из 10 437 (97,5%)
стало 54 937 0 0

То есть половина тегов файла была выдумана, и NULL хоть раз встречался у 199 ключей из 214. Обратите внимание на арифметику: 54 937 = 110 879 − 55 942 ровно. Снялись именно фиктивные теги, ни одного настоящего при этом не потеряно — это я проверял отдельно, потому что «почистить лишнее» легко превращается в «выкинуть нужное».

Про двоеточия. Тоже правда, и причина неприятная. Наш загрузчик сознательно переименовывает несколько колонок с двоеточием в имена с подчёркиванием — это нужно правилам стилей QGIS, которые адресуют поля по имени. Внутреннее имя утекло в формат, где ключ тега — это и есть семантика OSM. Задело ровно 8 ключей: addr:street, addr:housenumber, addr:city, addr:postcode, building:levels, roof:shape, roof:height, tower:type — 3 875 тегов, из них 2 350 с реальными данными. Ваш пример building:levels — 711 тегов, 573 с этажностью.

Сейчас в файле на том же bbox: addr:housenumber 669, addr:street 663, building:levels 573, roof:shape 383, addr:city 37, addr:postcode 20, tower:type 11, roof:height 5. Вариантов с подчёркиванием не осталось ни одного.

Про «losslessly». Претензия справедлива, спорить не о чем: если ключи переименовываются, обещание не держится. Формулировку со страницы формата убрал и заменил на проверяемую — «ключи сохраняются в исходном виде, двоеточие включительно». Слово «без потерь» не вернул сознательно: выгрузка отфильтрована по выбранным слоям, это не побитовая копия куска OSM, и называть её так было неправильно с самого начала — независимо от бага.

Что нашлось попутно. Раз уж полез мерить — после снятия NULL в файле осталось 54 937 тегов, и 26 770 из них (49%) оказались нашими собственными именами полей, выданными за теги OSM: osm_id и osm_type по 10 437, road_class 4 504 (он ещё и дублирует highway значением), poi_category 1 097, green_category 295. В JOSM они неотличимы от настоящих тегов, а таких ключей в OSM нет. Убрал их в собственное пространство имён osm2cdr: — туда, где уже жил osm2cdr:layer. Выкидывать совсем не стал: osm_id — единственная связь строки файла с исходным объектом, она людям нужна.

Одна оговорка про кэш. Готовые области из каталога отдаются из предсобранного кэша, а ключ этого кэша про версию кода ничего не знал — то есть правка сама по себе до уже собранных файлов не доехала бы. Пересобрал: 1 337 лежавших там .osm и Overpass-JSON помечены устаревшими и перестроятся при следующем обращении. Если качали .osm по каталожной области раньше — берите заново.

Что осталось как было и меняться не будет: отрицательные идентификаторы у узлов и путей. Это маркер «данные не из живой базы»; файл рассчитан на просмотр в JOSM, импорт и конвертацию, а не на заливку обратно в OSM.

Спасибо за конкретику и за скриншот — по нему было сразу видно, куда смотреть. Если возьмёте свежую выгрузку и что-то в тегах опять покажется странным, пишите: как видите, дефект оказался ровно вдвое больше видимой его части, так что такие сообщения окупаются.

1 Like

Это конечно большой шаг вперед! Стало намного лучше. Даже деревья прорезались.
Но некоторые билдинг-парты теряются.

И еще кажется, что при экспорте (по крайней мере в ) obj/gltf ось Z должна смотреть вверх.

Потому что выглядит странно ( и в блендере так же).

Может так надо для 3д-принтера, не знаю :slight_smile:

@Zkir, спасибо — оба замечания в точку, оба подтвердились, оба починены. Извините за паузу: ответ задержался на неделю, и это моя вина.

Про ось Z в glTF/GLB. Вы правы, это была прямая наша ошибка. Спецификация glTF 2.0 требует «вверх» = +Y, а файл обязан либо лежать в этих осях, либо нести поворот в узле. У нас не было ни того, ни другого: на боевом файле размах был 1 046 м и 1 732 м в плане против 15,1 м по Z, и корневой узел шёл вообще без матрицы. Отсюда и город набок.

Починено: сцена разворачивается на −90° вокруг X, и поворот кладётся в узлы графа сцены, а не в геометрию. Проверил на том же боевом файле — высота уехала с Z на Y (15,10 м теперь по Y), матрица появилась у всех 21 узла, а буферы вершин остались побайтово прежними. Заодно нашёл и поправил собственную документацию: две наши страницы советовали доворачивать модель на 90° при импорте — после исправления этот совет сам стал вредным.

OBJ и STL не трогал. В OBJ осей нет вовсе, наш выбор Z-вверх записан в README.txt внутри архива; в STL Z-вверх — де-факто стандарт слайсеров, и ваша догадка про принтер верна ровно для него. Вопрос из прошлого поста оставляю открытым: разворачивать ли OBJ под дефолт Blender или оставить как есть для тех, кто тянет наши файлы в CAD.

Про 3D Tiles. Там тот же класс дефекта, и даже двойной: сама плитка писалась «на боку», а tileset.json не нёс transform вовсе — набор вставал в геоцентр, а не на своё место. Тоже починено и тоже уже на сайте: поворот в +Y лежит в узле сцены каждой плитки, а корень tileset.json несёт transform, и набор встаёт на заказанную рамку. Проверил живым заказом на ту же рамку Лавры: поворот стоит у всех 6 плиток из 6, а начало координат набора ложится ровно в центр рамки — 56,3090° с. ш., 38,1295° в. д.

Про потерянные building:part. Дефект наш, и вы нашли его на очень удачном объекте. Причина: наш дедуп снимает здания, размеченные в OSM дважды, и опознавал «одно и то же здание» по побайтовому совпадению геометрии. А часть здания сплошь и рядом нарисована по ТОМУ ЖЕ кольцу, что и контур — геометрия совпадает побайтово, части и контур попадали в одну группу «дублей», и выигрывал контур: у него есть имя и тип здания, а у части их нет. Часть исчезала молча.

С вашей Водяной башней получалось особенно обидно: купольную часть 0–23 м съедал дедуп, а шатёр 23–28 м оставался — то есть в модели висел шатёр без башни под ним.

Замер до и после, кусок вокруг Лавры (38.124,56.306 – 38.135,56.312): в слой приходили все 132 части, а до сцены доезжали 125; теперь доезжают 132. На Кремле, где частей на порядок больше, — было 3 359, стало 3 441. Ваша 18067316 теперь в сцене со своей высотой 23 м и куполом, и шатёр стоит на башне.

Заодно проверил, что дедуп не сломался в другую сторону: лишних совпадающих тел не появилось, время рендера не изменилось. Два признака теперь считаются одним телом только если совпадают и геометрия, и назначение (контур либо часть), а у частей — ещё и отметки низа с верхом: иначе два яруса башни на одном кольце схлопывались бы в один.

Обе правки уже на сайте. Перед этим ответом проверил живым заказом на ту же рамку Лавры: в файле у каждого узла сцены стоит поворот в +Y, а у вашей башни рядом с центроидом есть вершины от 0 до 23 м (купол) и шатёр 23–28 м над ними. Кэш готовых файлов после выката сброшен: новый заказ на ту же рамку рендерится заново, а не берётся из старого.

Если найдёте ещё места, где парты пропадают, — киньте ссылками. Чем больше разных случаев, тем меньше шансов починить один и решить, что закрыл все.

1 Like

Из любопытства, вам ответы ИИ генерирует, или у вас такой стиль выработался? Не то чтобы по факту что-то было не так, но прям очень похоже.

1 Like