Свой редактор для ОСМ

Для svn-а есть шикарный ресурс XP-dev.com, уже около года пользуюсь. Недавно туда еще ведение проектов прикрутили. (а также - wiki и форум)

Думаю тему создавать новую не стоит, т.к., насколько я понял, кодеров тут можно пересчитать по пальцам одной руки)

Непонятки с протоколом

Пытаюсь разобраться с xml’ьными данными осма.
Из исходников JOSM’а, примеров (сохранял osm файл JOSM’мом) и самой важной странички на вики “API v0.6” нашел довольно разнообразные теги, которые могут встретится.
Вот чтобы понять как это все правильно обработать хотелось бы уточнить несколько моментов.

Не могу определиться с полным списком элементов которые могут встретиться между в xml осмофайле.

Пока бегал по страничкам вики насобирал довольно много тегов:

<?xml version='1.0' encoding='UTF-8'?>
<osm version='0.6' generator='JOSM'>
  <bounds />
  <user />
  <preferences />
  <node />
  <way />
  <nd />
  <relation />
  <member />
  <tag />
  <gpx_file />
  <changeset />
  <api />
</osm>

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

Есть ли спецификация точная для osm’о файла? В которой точно сказано какие элементы может содержать такой-то элемент и какие у этого элемента могут быть параметры и закакие значения могут принимать эти параметры?
Или в одном месте эту инфу еще не собрали, или не нашел… все как-то размазано)

Пытаюсь выяснить какие параметры и дочерние элементы может содержать каждый из них.
Например для node собрал параметры… node[ id, action, timestamp, user, uid, visible, version, lat, lon, changeset ]

Парсер xml собираюсь реализовать на libxml2. Примеры компилятся, работает…
Возможно есть какие-нибудь более подходящие либы?

http://wiki.openstreetmap.org/wiki/API_v0.6/DTD

“Заочно” мне понравился RapidXml

AkMeR, Спасибо! На эту страничку не попадал)
Ivan Komarov, Спасибо! Посмотрю что за зверь…

Как насчет VTD-XML?

Eugene, Спасибо! Посмотрел… не совсем понятно что за штучка еще, основательно не разбирался)
Но идея понравилась. И ничего за собой не тянет, кстати!
Я так понял оно умеет переваривать xml в свой формат что ли, с которым очень быстро работает…

Если кому интересно, то при компиляции исходников (из ximpleware_2.7_c.zip, в ximpleware_2.7_c_light.zip все нормально) вы получите ошибки о том что не найден “xpath1.hpp”
Эти ошибки в файлах с функцией main, какие-то утилиты, тесты, а конкретно в:

benchmark_vtdxml.c
RSSReader.c
soap.c
stats.c
update.c
vtd-xml.c

Я их вообще убрал из проекта (т.к. весь VTD собрал в либу), и все собралось нормально, впрочем можно в них поменять #include “xpath1.h” на #include “xpath.h”, тогда и они компилятся без ошибок.


UPD1: VTD вместе с моими исходниками на cpp работать не хочет, но в отдельном проекте собралось)


UPD2: RapidXML, удобно юзать, без громоздких коллбеков как в libxml2) Почитал что она довольно шустрая + упакована целиком в hpp, буду разбираться
VTD-XML не осилил) он на С… отлично работает, но в С++ проге замучался прикручивать…


UPD3 накатал парсер osm файлов на RapidXML… вроде бы полностью должен обрабатывать node, way, relation… согласно указанному AkMeR’ом API v0.6 DTD
О том что такое DTD кратко написано ТУТ, и ТУТ есть наглядные примеры. Раньше с xml особо не возился(
RapidXML действительно удобная штука, парсить довольно легко, думаю и создавать тоже не трудно)
Потестил пока только обработку node и way… ловит все атрибуты, теги, ссылки! Надеюсь скоро смогу что-нибудь нарисовать из файла, в меркатор уже пересчитывать научился… вроде рисует координаты правильно.
Сравнивал визуально с осмом)))

well, чутье меня не подвело. Грамотно сделанное с использованием лучших практик программирования и должно хорошо работать :slight_smile: Скоро тоже понадобится XML к проекту прикручивать, воспользуюсь. Спасибо за комментарий!

Заметил такую особенность! В файлах есть элемент bounds, например:

<osm version='0.6' generator='JOSM'>
<bounds minlat='55.7545543020341' minlon='37.5684356689453' maxlat='55.783427185411' maxlon='37.6307487487793' origin='OpenStreetMap server' />
<node id='297532810' timestamp='2009-05-29T20:12:44Z' uid='82861' user='azomazo' visible='true' version='38' lat='55.5810759' lon='37.5962018' />

но в описании API v0.6 DTD его нет…
Как правильно-то? Это его JOSM “неофициально” дописывает?
Да и uid, кстати, тоже нет!

Ivan Komarov, парсер осмофайлов прикручивать будете?
Я, думаю, допишу скоро… пока еще не отлажен толком, все типы храню как QString( но ноды и веи уже собираются правильно!
Сейчас релейшены тестить буду. Да и просто как тутор по работе с либой могу выложить) Из стандартного мануала не все понятно было.

Нет - свое, девичье. Проект еще не завели где-нибудь?
Add: кстати, о птичках: у qt-а же свои есть средства по работе с XML. их не смотрели?

У JOSM`а - немного другой формат файла.
По поводу уид - http://wiki.openstreetmap.org/wiki/API_v0.6#Reliably_identifying_users - взамен user.

Ivan Komarov, проект пока еще не завел…
Думаю, побольше нужно написать, привести код в человеческий вид, а то стыдно показывать))

С Qt и начал, честно говоря… но что-то мы с его QDomDocument сразу не подружились… не то что с рапидХМЛ!
У меня при работой с QDomDocument вылетала прога так и не понял почему, вроде исключения старался поймать…
В общем не понравилось сразу… да и не слышал чтобы Qt’ешный парсер производительностью блистал)

AkMeR, Спасибо! Я думал это дело от АПИ 0.5 например осталось, просто в ДЖОСМе не обновили…

Нарисовал осмофайлик кое-как… в JOSM’е по кускам загрузил центр Москвы, osm файл весит 20 Мб
Никаких тормозов не наблюдается, хотя тут и тормозить нечему… все в одном большом VBO.

Render

Сперва просто линиями обвел, поскольку никакой триангуляции пока нет, сверху отмечены вершины темным.
Прикрутил примитивную двигалку, зумилку… Летает))

Проблема, как я и ожидал в работе с памятью… нужно очень быстро получать координаты точек по id’шникам.
Я сперва тупо перебором по массиву точек искал, чтобы по-быстрее посмотреть что получится)

Точек много… просто нодов в файле ~94’000. В полигонах вершин 114’000 т.к. некоторые ноды используются повторно.
По id’шникам координаты искать “влоб” не получится никак)

Сейчас заюзал Boost.unordered_map… пока очень шустро работает, но цифры не мерял, не знаю сколько оно памяти есть)
Кстати, 20 метровый файл RapidXML грузит секунд 10 наверно…

Так, к слову. Удалось на своей HTC-шке запустить OpenGL-ES -based приложение.

И сколько при этом памяти потребляет? И на каком железе? Правда, 20-мегабайтные файлы для редактирования вряд ли кто-то загружать будет, а то 10 секунд - это уже неудобно.

Программа предполагается кроссплатформенной?

И еще, в качестве пожелания и для обсуждения. Мне давно уже укажется, что хороший редактор для OSM должен иметь в себе препроцессор (встроенный или серверный). Дело в том, что сейчас порог входа в проект для новичков достаточно высок - необходимо обладать знаниями о раличных тегах и их сочетаниях, читать форум и вики для понимания существующих договоренностей в сообществе и так далее (знать английский, например). JOSM, Merkaartor и Potlatch уже предоставили возможность использования готовых пресетов для множества объектов, но и это не идеальное решение. Возможно, ситуацию изменит Mapzen, но его пока мало кто видел, да и не все готовы работать с онлайновым редактором.

Решением этой проблемы я вижу создание набора правил, обрабатывающих те или иные ситуации. По этим правилам программа автоматически заменяет “пользовательское” представление на принятое в проекте. Например, рисует человек линию, в списке типов отмечает - “улица” (не непонятный и общий highway, а просто “улица”). Дальше - какая улица? Он выбирает - “дворовый проезд”. И программа сама проставляет все необходимые теги, и пользователю совершенно не обязательно знать, что в базе это будет сохранено как набор highway=residential + living_street=yes.
То же самое, например, с релейшенами. Это сложное и не очень понятное понятие. Зачем забивать пользователю голову, если он может просто выделить куски улицы, нажать кнопки, скажем, “Транспорт” → “Автобус” и так обозначить маршрут? Или границу.
Или та же проблема типов дорог. Пусть программа смотрит - если дорога нарисована внутри границ города, то предлагать один список понятных типов, если за пределами - другой (федеральная трасса, дорога между городами и т.д.).
Еще на форуме обсуждалось наличие чисто локальных реалий - например, как обозначать аул или гаражи. Тут решение может быть таким же - программа автоматически ставит принятые сообществом “похожие” теги плюс свой, дополнительный. Тогда рендеры и другие редакторы увидят стандартное представление, а пользователи нашего редактора - именно свой “аул”. И будут уверены в том, что сохранилось именно так, а он не стал каким-нибудь hamlet’ом.
Еще есть просто сложные объекты - строения на островах внутри озера, берега рек всякие. Такие вещи надо знать как рисовать - вот это должно быть с таким тегом, чтобы рисовалось красиво, а вот это - таким релейшеном. Почему бы не переложить применение этого сакрального знания на программу?

Таких правил может быть очень много. При этом они позволят унифицировать подходы к рисованию внутри сообщества. А если что-то поменяется будет принят пропозал или просто большинство решит по-другому, то все правки можно будет автоматически исправить. А новые уже будут делаться с учетом изменений, поскольку редактор каждый раз будет обновлять с сервера список правил.

Я считаю, что главное - дать возможность людям рисовать, максимально просто для них. А чтобы не переделывать потом, лучше сразу предоставить удобный для всех инструмент. Все помнят, что для работы с GPSMapEdit’ом нужно было проходить специальные курсы. С имеющимися редакторами OSM стало проще, но давайте сделаем еще один шаг к удобству!

Удваиваю и подписываюсь.

Ivan Komarov, а на этом HTC что стоит? Андройд или вин мобиайл? Жалко под андройд Qt нету, сам андройд это, конечно, круто, но не нравится мне их явская идеология)
Eugene, я обычно все приложения пишу на кроссплатформенных либах, winAPI практически никогда не использую.
Пока на стандартном С++, помимо Qt и либ nVidia для OpenGL (там обычный glut, glew и их Cg шейдеры) использую boost, RapidXML. RapidXML весь в исходниках и ничего за собой не тянет, думаю, будет работать на всем что имеет компилятор С++ со стандартной либой.

На счет 10 - секунд это я погорячился! Сейчас поставил мерялку на основе виндовской GetTickCount()… загрузка 20 мб xml файла ~6’700 ms, генерация ~16’000 way’ев из ~95’000 нодов ~620 мс.
Памяти сколько ест точно не знаю… если посмотреть в диспетчере задач, то при отображении 20Мб файла процесс занимает 105Мб. Если открыть очень маленький osm файл, то 18 Мб.
Железо: проц Q6600, 2Gb оперативы, видюшка 8800GT 512Мб

Пока особо не экономил… данные дублируются, например у нодов широта, долгота хранятся одновременно и в QString и в double.
Можно оптимизировать. При загрузке xml есть что сокращать… слишком дотошно отслеживаю соблюдение стандарта, может даже слишком много всяких проверок.

Про удобство редактирования идея очень правильная! Я, например, релейшены не осилил…
только один использую для полигонов с дырками… даже разбираться лень было как посмотрел что это такое. Порог входа для новичков действительно очень высок!
Тема очень важная! Если получится основу редактора написать обязательно нужно реализовывать что-то такое, иначе новый редактор просто не нужен)
На мой взгляд это должно быть удобно как чисто для редактирования (выравнивание, объединение, всякие умные залипания и т.д) так и для расстановки тегов… нужно это автоматизировать. Ну и это должно быть еще быстро и красиво)

Очень хорошо было бы прикрутить скриптовый язык. Я уже пытался нечто подобное сделать в своих проектах.
Как например в modo, да и во многих CAD’ах, рисовалках.
Сталкивался с проблемами связи Python и С++, которую так и не решил как мне бы хотелось.
Если конкретно, то хотелось бы иметь возможность вызывать методы экземпляров классов С++ созданных и работающих в программе из скрипта.
Возможно кто уже с таким сталкивался?
Вроде как такое умеет Kross… либа KDE. Но пока результата нет.
Думаю, со временем решаемо.

6.1 но рендер оказался при ближайшем рассмотрении софтверный :frowning: Но работает, что уже хорошо

та же фигня :slight_smile:

А встроенное, средствами QT, скриптование чем не угодило? В 4.6 они вовсе JS деклалировали как бэкэнд скриптовый. Но сам не щупал.

Не работал с ним, да и не хочется… он вроде как на ява-скрипт смахивает.
Не слышал чтобы Qt script где-нибудь использовали. Мне кажется лучше уж питон взять, либ для него много, туторов всяких… проблема только в связке.
Вон даже мапник под питон заточили)

UPD: Занялся вопросом прикручивания питона к C++! Давно меня эта тема интересует, да и на работе нужно бы использовать…
Раньше не получалось создать pyd из С++ исходников… ну никак, а сейчас получилось! Причем на удивление быстро и гладко!)
Использовал SWIG… можно работать с С++ классами из питона!!!

Теперь могу вызвать С++ функции, создавать экземпляры С++ классов в питоне, понимает примитивные типы, а также некоторые std, например vector, string.
Думаю теперь когда питон знает что такое нод, вей и т.д. нужно вызвать скрипт из С++ кода (это позволяет Python API) и как-нибудь передать ему указатель на класс содержащий координаты нодов, например.
Надеюсь тогда можно будет создавать алгоритмы обработки тегов или рисования, создание интерфейса и т.д без перекомпиляции приложения!
Отлаженные алгоритмы можно переносить в C++ и “увековечить” в проге, если они будут часто выполняться или требовать высокой производительности.
Правда как передать указатель на уже созданный С++ объект питону я еще не разобрался, даже не уверен возможно ли это, но надеюсь выкрутится можно…

swig может помочь, если хочется быстро что-то сварганить. При этом он не лишён косяков. Лучше делать без него - это не так уж и сложно. Я в своё время конструировал древовидные питоновские структуры прямо в C-модуле.

Вот, кстати, интересная новость: Nokia анонсировала официальный порт Qt для платформы Maemo 5
там даже отображение OSM’овских карт показали

UPD: Заюзал Boost.Python!
Получилось написать пример, который заработал!!!

Статический и динамический миры соединены!!!
Даже не верится что оно работает)))

Вот если кому интересно, надеюсь пригодится:

#include <stdlib.h>
#include <iostream>

#define BOOST_PYTHON_STATIC_LIB
#include <boost/python.hpp>
using namespace boost::python;

class CppClass {
public:
    CppClass()
    {
        t = 0;
    }
    int getNum() {
        return t;
    }
    void inc()
    {
        t++;
    }
private: 
    int t;
};

int main( int argc, char ** argv ) 
{
    setlocale(LC_ALL, "Russian");
    try 
    {
        Py_Initialize();

        object main_module((handle<>(borrowed(PyImport_AddModule("__main__")))));

        object main_namespace = main_module.attr("__dict__");
        main_namespace["CppClass"] = class_<CppClass>("CppClass")
                                                        .def("getNum",&CppClass::getNum)
                                                        .def("inc",&CppClass::inc);

        CppClass *cpp = new CppClass();

        main_namespace["cpp"] = ptr(cpp);

        PyRun_String( "print \"Python: \", cpp.getNum()\n", 
            Py_file_input, main_namespace.ptr(), main_namespace.ptr());

        std::cout << "C++ inc()" << std::endl;
        cpp->inc();

        PyRun_String( "print \"Python: \", cpp.getNum()\n"
            "cpp.inc()\n"
            "print \"Python inc()\" \n"
            "print \"Python: \", cpp.getNum()\n",
            Py_file_input, main_namespace.ptr(), main_namespace.ptr());

        std::cout << "C++: " << cpp->getNum() << std::endl;
        std::cout << "C++ inc()" << std::endl;
        cpp->inc();

        PyRun_String( "print \"Python: \", cpp.getNum()\n",
            Py_file_input, main_namespace.ptr(), main_namespace.ptr() );

        Py_Finalize();
    } 
    catch( error_already_set ) 
    {
        PyErr_Print();
    }

    system("pause");
    return EXIT_SUCCESS;
}

На выходе получаем:

Python:  0
C++ inc()
Python:  1
Python inc()
Python:  2
C++: 2
C++ inc()
Python:  3
Для продолжения нажмите любую клавишу . . .

PS:* кажется я эту тему уже, можно сказать, в блог превратил)))*