OpenStreetMap Forum

The Free Wiki World Map

You are not logged in.

#1 2010-09-14 16:29:41

x10kHz
Member
Registered: 2009-10-06
Posts: 138

Данные OSM в MongoDB

Возможно лучше сделать статью в вики и проект на github.com, но пока оставлю топик тут чтобы не пропало)

Возился давно с импортом в PostgreSQL, но как-то не подружился я с ней... со стороны девелоперов нету тестового сервочка, доки в вике были старые и т.д.
Надоело, равно как и сами sql базы уже не привлекают после того как попробовал Mongo)

Не могу сказать шустрая база или нет, но очень удобная! Тесты разные... в инете идут холивары) Но масштабируется, насколько я понял, она довольно просто.
Пытаюсь сделать распределение векторных данных по уровням для векторного рендера карты с любым масштабом в реалтайме... но это лучше вынести в отдельный топик.
На данный момент есть простенький импорт/экспорт данных в MongoDB скриптами на python

Импорт 164 Гигового planet-100908.osm занял 2 суток  14 часов 4 минуты и  38 секунд
база MongoDB занимает 158.5 Гб... хотя помимо osm данных там ее вспомогательная база статистика импорта, а также парочка тестовых баз...
Иморт на Core 2 Duo под Ubuntu Server 10.04 64 бит (3-4 тысячи нодов в секунду) в почти 4 раза медленнее чем на ноуте под Windows 7 32 бит на i3 (10-13 тысяч нодов)... не знаю в чем тонкость.
Возможно питон собран криво, возможно повлиял тот факт что база располагалась на медленном 1.5 Тб винче WD. Ну и разница в производительности i3 и Core 2 Duo...
В любом случае написал заготовку импорта на С++. Буду тестить на базах поменьше... скачал 4 гиговый rus.osm
XML парсер можно не использовать т.к. файлы осма довольно просто и надеюсь быстрее "руками" разобрать.

Какое приблизительное время импорта planet.osm в postgreSQL? И на каком железе.
В базе нужно заводить всякие там индексы, настройки и т.д... я пока делал импорт тупо без всего этого...
Остальное буду добавлять по необходимости.
Пока есть некоторые функции на Java-Script, которые исполняются самим MongoDB... например, вычисление площади или длины вея.
Также есть какие-то удобные фишки MongoDB, которые я не использовал... вроде того что элемент одного объекта коллекции может ссылаться на другой объект. Деревья...
У меня самый просто случай.

pyosm.py

import xml.sax
import unicodedata
    
class Member(object):
    def __init__(self, type, ref, role=''):

        self.type = type
        self.ref = ref
        self.role = role

    def list(self):
        return {'type':self.type, 'ref':self.ref, 'role':self.role}
    
    def __repr__(self):
        return "Member(%r, %r, %r)" % (self.type, self.ref, self.role)

class Node(object):
    def __init__(self, id, lon, lat, visible, tags=None):
        self.id = id
        self.lon = lon
        self.lat = lat
        self.visible = visible

        if tags:
            self.tags = tags
        else:
            self.tags = {}

    def __repr__(self):
        return "Node(id=%r, lon=%r, lat=%r, visible=%r, tags=%r)" % (self.id, self.lon, self.lat, self.visible, self.tags)

class Way(object):
    def __init__(self, id, visible, nodes=None, tags=None):
        self.id = id
        self.visible = visible
        
        if nodes:
            self.nodes = nodes
        else:
            self.nodes = []
            
        if tags:
            self.tags = tags
        else:
            self.tags = {}
            
    def __repr__(self):
        return "Way(id=%r, visible=%r, nodes=%r, tags=%r)" % (self.id, self.visible, self.nodes, self.tags)

class Relation(object):
    def __init__(self, id, members=None, tags=None):
        self.id = id
        
        if members:
            self.members = members
        else:
            self.members = []

        if tags:
            self.tags = tags
        else:
            self.tags = {}
            
    def __repr__(self):
        return "Relation(id=%r, members=%r, tags=%r)" % (self.id, self.members, self.tags)

Файлик import.py

import time
import xml.sax
from pymongo import *
from pyosm import *

def db_clear():
    dbnodes.remove()
    dbways.remove()
    dbrelations.remove()

class run_import(object):
    def __init__(self, filename):
        self.filename = filename
        self.start = time.time()
        self.__parse()
        

    def __parse(self):
        """Parse the given XML file"""
        parser = xml.sax.make_parser()
        parser.setContentHandler(OSMXMLFileParser(self))
        parser.parse(self.filename)
        print 'Import time: ' + repr('%.2f'%(time.time() - self.start)) + ' seconds'

class OSMXMLFileParser(xml.sax.ContentHandler):
    def __init__(self, containing_obj):
        self.containing_obj = containing_obj
        self.curr_node = None
        self.curr_way = None
        self.curr_relation = None
        self.nodesCount = 0
        self.waysCount = 0
        self.relationsCount = 0

    def startElement(self, name, attrs):
        #print "Start of node " + name
        if name == 'node':
            self.curr_node = Node(id=long(attrs['id']), lon=float(attrs['lon']), lat=float(attrs['lat']), visible='true')
            
        elif name == 'way':
            #self.containing_obj.ways.append(Way())
            self.curr_way = Way(id=long(attrs['id']), visible='true')

        elif name == 'relation':
            self.curr_relation = Relation(id=long(attrs['id']))
            
        elif name == 'tag':
            #assert not self.curr_node and not self.curr_way, "curr_node (%r) and curr_way (%r) are both non-None" % (self.curr_node, self.curr_way)
            if self.curr_node:
                self.curr_node.tags[attrs['k']] = attrs['v']
            elif self.curr_way:
                self.curr_way.tags[attrs['k']] = attrs['v']
            elif self.curr_relation:
                self.curr_relation.tags[attrs['k']] = attrs['v']
                
        elif name == 'nd':
            #assert self.curr_node is None, "curr_node (%r) is non-none" % (self.curr_node)
            #assert self.curr_way is not None, "curr_way is None"
            self.curr_way.nodes.append(long(attrs['ref']))
                
        elif name == 'member':
            #assert self.curr_node is None, "curr_node (%r) is non-none" % (self.curr_node)
            #assert self.curr_way is not None, "curr_way is None"
            member = Member(type=attrs['type'], ref=long(attrs['ref']))
            if attrs.has_key('role') == True:
                member.role = attrs['role']
            self.curr_relation.members.append(member)

    def endElement(self, name):
        #print "End of node " + name
        #assert not self.curr_node and not self.curr_way, "curr_node (%r) and curr_way (%r) are both non-None" % (self.curr_node, self.curr_way)
        if name == 'node':
            dbnode = {'_id': self.curr_node.id,
                      'lon': self.curr_node.lon,
                      'lat': self.curr_node.lat,
                      'visible': self.curr_node.visible,
                      'in_ways': [],
                      'in_relations': []}
            
            if len(self.curr_node.tags) > 0:
                dbnode['tags'] = self.curr_node.tags
            dbnodes.save(dbnode)
            self.curr_node = None

        elif name == 'way':
            dbway = {'_id': self.curr_way.id,
                     'visible': self.curr_way.visible,
                      'in_relations': []}
            dbway['nodes'] = self.curr_way.nodes
            dbway['tags'] = self.curr_way.tags
            dbways.save(dbway)
            self.curr_way = None
            
        elif name == 'relation':
            dbrelation = {'_id': self.curr_relation.id,
                          'in_relations': []}
            dbrelation['members'] = []
            for member in self.curr_relation.members:
                dbrelation['members'].append(member.list())
            dbrelation['tags'] = self.curr_relation.tags
            dbrelations.save(dbrelation)
            self.curr_relation = None


if __name__ == '__main__':

    try:
        connection = Connection('localhost', 27017)
    except pymongo.errors.AutoReconnect:
        raise
    
    db = connection.planet_100908

    dbnodes = db.nodes
    dbways = db.ways
    dbrelations = db.relations

    db_clear()
    
    run_import('./../../planet-100908.osm')

    print 'In database ' + repr(dbnodes.find().count()) + ' nodes, ' + \
            repr(dbways.find().count()) + ' ways, ' + \
            repr(dbrelations.find().count()) + ' relations'

Памяти скриптец есть мало, но зато работает долго...
Также есть скрипт экспорта в *.osm... довольно кривой)) Но Josm понимает и рисует правильно
Если кто работал или захочет поиграться с MongoDB, то пишите предложения, результаты тестов... может скрипты какие

Offline

#2 2010-09-14 20:39:27

Ezhick
Member
From: Moscow
Registered: 2008-10-08
Posts: 2,508
Website

Re: Данные OSM в MongoDB

ИМпорт планеты на 2.6ггц амдешном 4-ядернике с 8 гб памяти и софтрейдовой двухдисковой подсистемой занимает меньше суток в simple схему (плюс сутки на индексы) и порядка двух (+двое суток на индексы) в полноценное зеркало осм-апи.

Offline

#3 2010-09-15 00:28:59

x10kHz
Member
Registered: 2009-10-06
Posts: 138

Re: Данные OSM в MongoDB

Набросал С++ код импортирующий *.osm
Пока только ноды правда...
Но скорость!!! Ппц....  26'954.8 нодов в секунду... на дохлом-дохлом ноуте 2005 года выпуска)))
Это почти в 8-10 раз быстрее чем питоновский код на нормальном железе!
Есть что оптимизировать... хотя даже и так оочень шустро
Даж не верится, но в базе все объекты вроде на месте... с тегами, правильными параметрами и т.д...
Я счастилв!!! big_smile

Last edited by x10kHz (2010-09-15 00:29:58)

Offline

#4 2010-09-15 04:35:45

AMDmi3
Member
From: Москва
Registered: 2009-08-12
Posts: 3,315

Re: Данные OSM в MongoDB

У меня тоже очень быстрый самописный парсер OSM есть. Проблема в том, что надо еще распарсивать XML entity.

Offline

#5 2010-09-15 07:08:55

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

x10kHz: попробуй запустить на PyPy, он раза в 2-3 быстрее, чем стандартный Питон.

            if self.curr_node:
                self.curr_node.tags[attrs['k']] = attrs['v']
            elif self.curr_way:
                self.curr_way.tags[attrs['k']] = attrs['v']
            elif self.curr_relation:
                self.curr_relation.tags[attrs['k']] = attrs['v']
              

Это сжимается до

if self.curr_elt:
    self.curr_elt.tags[attrs['k']] = attrs['v']

если в других частях кода curr_node/way/etc заменить на curr_elt.

Насколько я помню, уровней вложенности в файле osm всего 1. Если больше, надо просто сделать стек из элементов, вроде такого:

#!/usr/bin/python

stack = []

def put(item):
    stack.append(item)

def pop():
    return stack.pop()

def set_property(data):
    for k, v in data.items():
        stack[-1].tags[k] = v
    
def get_id():
    return stack[-1].id

Last edited by siberiano (2010-09-15 07:18:15)

Offline

#6 2010-09-15 07:21:45

Komяpa
Member
From: Minsk
Registered: 2009-04-14
Posts: 1,321
Website

Re: Данные OSM в MongoDB

Напомню, что если питоновский скрипт оформить фнкцией (да хоть main()), а в начале (до функции) дописать вызов psyco, то все числодробильные штуки начнут исполняться со скоростью, близкой к асмовой. Жаль, что только на 32-битных системах.
import psyco
psyco.full()
Да и плодить объекты не надо лишний раз, если критично именно время исполнения.

Чистый C/C++ будет быстрее работать, но его сложнее писать smile


world processing is what we do.
[OSMF BY Team] [http://komzpa.net/] [jabber: komzpa@gmail.com] [mobile/SMS: +375257407159]

Offline

#7 2010-09-15 07:24:49

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Ещё можно SteelBank Common Lisp, он работает так же быстро, как C. Только к нему доки и такие либы под XML фиг найдёшь smile

Offline

#8 2010-09-28 18:12:34

Kaylee
Member
Registered: 2010-02-17
Posts: 66
Website

Re: Данные OSM в MongoDB

x10kHz wrote:

Набросал С++ код импортирующий *.osm

А поглядеть на этот код можно? Уж очень фантастически цифры выглядят. На Python с учетом всех оптимизаций больше 3500 объектов в секунду выжать не удалось.


OSM digest — свежие новости из мира OpenStreetMap.

Offline

#9 2011-01-31 21:32:32

x10kHz
Member
Registered: 2009-10-06
Posts: 138

Re: Данные OSM в MongoDB

Kaylee, прошу прощения за столь большую задержку!

Исходники выложил "как есть"... пока там сплошной мусор, ненужные закомменченые дебажные куски кода и прочее, но основную идею понять можно т.к. объем кода довольно маленький и даже должно работать!)) Но я сейчас не проверял... надеюсь в будущем дойдут руки привести все в порядок!

http://github.com/chertov/OpenStreetMap-MongoDB

Простите за этот позор, надеюсь хоть кому-нибудь пригодится big_smile

Offline

#10 2011-03-09 15:58:47

wildMan
Member
From: Minsk
Registered: 2008-03-05
Posts: 509

Re: Данные OSM в MongoDB

если кому интересно то в базе

mongo -uosm -posm wildman.bn.by/osm

лежит дамп беларуси. в ближайшее время сделаю ежедневное обновление. пока что за 7-е число.


--
OSMF BY Team

Offline

#11 2011-03-09 19:09:45

andriano
Member
Registered: 2009-06-15
Posts: 1,667

Re: Данные OSM в MongoDB

Kaylee wrote:
x10kHz wrote:

Набросал С++ код импортирующий *.osm

А поглядеть на этот код можно? Уж очень фантастически цифры выглядят. На Python с учетом всех оптимизаций больше 3500 объектов в секунду выжать не удалось.

Совершенно обычная скорость.
Парсер моего конвертера обрабатывает planet.osm примерно 2 ч 40 м на обычном двухъядернике 2.6 ГГц 2006 г. без всяких RAID'ов.
Скорость обработки узлов составляет 139962.8 узлов/сек.
Собственно, такое указание скорости не совсем корректно. Лучше посчитать по-другому:
исходный файл - 204 Гб + результирующие файлы суммарным объемом около 49 Гбайт, то скорость обмена с диском составляет 26.3 Мб/сек.
Другими словами, скорость обработки определяется не столько производительностью процессора, сколько скоростью работы жесткого диска.

PS. Собственно, парсер работает хоть и на двухъядернике, но в 1 поток. Учитывая, что узким местом является производительность диска, я не стал заботиться о распараллеливании обработки на 2 ядра.
В том и прелесть компилируемых языков, что, если не делать откровенных глупостей, любой алгоритм обработки данных сложности O(n) упирается именно в производительность жесткого диска, а не процессора.

Last edited by andriano (2011-03-09 19:16:42)

Offline

#12 2011-05-19 16:29:09

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Кто заливал osm в mongo, какие объёмы базы получаются? У меня файл Новосибирской области (12 мегов bz2, или 120 распакованный) занял 1.1G. :-O

У меня получился скрипт, обрабатывающий 2.2К точек в секунду.

Что интересно, парсер .osm.bz2 > .json.bz2 на pypy работает медленнее, чем на стандартном питоне 2.6.

Last edited by siberiano (2011-05-19 16:34:40)

Offline

#13 2011-05-20 17:57:07

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Поскольку для js есть xml-парсеры, попробую переписать питоновский скрипт на js, чтобы работал прямо в mongodb, получится компактнее и быстрее.

Offline

#14 2011-05-24 05:31:13

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Как скомпилировать и запустить файл .cpp? Интересно попробовать, насколько будет быстро работать. Ещё вопрос, можно ли ему направить через stdin поток из распаковщика bz2?

Индексы пробовал, работают нормально, ускоряют очень хорошо. Можно сделать индекс по любому тегу:
(через pymongo)

db.way.create_index('tags.building:levels')

(если теги находятся в отдельном объекте: way = {_id: ..., {tags: {building: 'yes', building:levels: 9}}})

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

Запросы лучше делать пореже и доставать данные сразу одним. Например, если нужно по одиночке обработать точки из линии, лучше запросить их сразу все:
db.node.find({'id': {'$in': [id1, id2, ...]}})
чем по одной.

Запрос update поддерживает только либо установку конкретного значения, либо увеличение или уменьшение на число, поэтому если нужно записать сложные вычисления, запросы придётся делать по одному.

Если lon и lat (именно в таком порядке) собрать в массив или в объект, можно сделать по ним пространственный индекс. Но единственная команда, которая доступна - поиск точек в определённых границах либо по расстоянию до них. Для подсчёта расстояний между парами точек и длин путей такая команда слишком медленная.

Offline

#15 2011-05-25 16:54:14

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

После замеров скорости работы скриптов на питоновском профайлере cProfile оказалось, что самая затратная операция - count(). Вызвать .count у запроса - в 4 раза дороже, чем .next().

Оказалось, что вызовы count занимают в моём скрипте больше половины времени, 160 секунд из 290.

Переделал скрипт. Было:

for node in db.nodes.find(...):
    bad_neighbors = db.nodes.find({'_id': {'$in': node['neighbors']}})
    while bad_neighbors.count() > 0:
        for neighbor in bad_neighbors:
            ...
        bad_neighbors.rewind()

Заменил на счётчик в виде переменной, который запускается в цикле.

for node in db.nodes.find(...):
    bad_neighbors = db.nodes.find({'_id': {'$in': node['neighbors']}})
    bad_neighbors_count = 0
    while True:
        for neighbor in bad_neighbors:
            bad_neighbors_count += 1
            ...
        if bad_neighbors_count == 0:
            break
        bad_neighbors.rewind()

Суммарное время сократилось до 134 секунд, из них 107 - на cursor.next, в которых один и тот же объект достаётся минимум дважды. Есть куда оптимизировать.

Last edited by siberiano (2011-05-25 17:00:31)

Offline

#16 2011-05-25 18:30:06

Komяpa
Member
From: Minsk
Registered: 2009-04-14
Posts: 1,321
Website

Re: Данные OSM в MongoDB

siberiano, стесняюсь спросить, с монгой никогда не работал - а  while bad_neighbors: не работает, или не подходит по контексту?


world processing is what we do.
[OSMF BY Team] [http://komzpa.net/] [jabber: komzpa@gmail.com] [mobile/SMS: +375257407159]

Offline

#17 2011-05-25 18:31:25

Miroff
Member
Registered: 2010-01-26
Posts: 147

Re: Данные OSM в MongoDB

Я написал плагин к Osmosis. После некоторого размышления я изменил структуру базы, убрав оттуда обратные ссылки (node -> way, way -> relation, relation -> relation). Слишком большой оверхед при импорте получался.

Пока получается такая статистика:
Импорт Новосибирской области (с gis-lab.info) занимает 84 секунды из которых 10 уходит на создание нужных индексов. Машина -- одноядерный Athlon 3200.

> db.stats()
{
    "db" : "osm",
    "collections" : 6,
    "objects" : 967076,
    "avgObjSize" : 62.81341693930984,
    "dataSize" : 60745348,
    "storageSize" : 109661952,
    "numExtents" : 21,
    "indexes" : 6,
    "indexSize" : 67698688,
    "fileSize" : 469762048,
    "ok" : 1
}

Места действительно жрет как слон веники. Как это можно оптимизировать, не представляю.

Offline

#18 2011-05-25 18:48:03

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Komяpa wrote:

siberiano, стесняюсь спросить, с монгой никогда не работал - а  while bad_neighbors: не работает, или не подходит по контексту?

Я пошагово удаляю соседей, которые не на перекрёстках и на ходу меняю список соседей. Поэтому когда ближайших обработал, надо запросить ещё раз, и так пока по всем направлениям не дойду до перекрёстков.

А my_cursor всегда переводится в True, даже если в нём пусто:
In [21]: x = db.routes.find({'_id': -1})
In [22]: 1 if x else 0
Out[22]: 1

Offline

#19 2011-05-25 19:16:56

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Miroff wrote:

Я написал плагин к Osmosis. После некоторого размышления я изменил структуру базы, убрав оттуда обратные ссылки (node -> way, way -> relation, relation -> relation). Слишком большой оверхед при импорте получался.

Пока получается такая статистика:
Импорт Новосибирской области (с gis-lab.info) занимает 84 секунды из которых 10 уходит на создание нужных индексов. Машина -- одноядерный Athlon 3200.

Места действительно жрет как слон веники. Как это можно оптимизировать, не представляю.

Он оставляет место под расширения документов, чтобы если начнёшь писать в них новые свойства, не пришлось раскидывать кластерами по файлу.

Если ты что-то удалил из базы, чтобы сжать, нужно $ mongod --repair --dbpath=...

А какие индексы делаешь? Я для того чтобы сделать дорожный граф, создал единственный индекс - tags.highway.

Сейчас после небольшой работы с монго я пришёл к такой схеме:

1. исходную базу делать с минимумом информации, и в объекты ничего лишнего не писать, а лучше даже удалять лишнее сразу при импорте.

2. никаких рабочих вычислений на этой базе делать нельзя. Для предметной области нужно делать отдельную БД, и туда экспортировать объекты уже в том виде, в котором нужно.

3. Лучше держать немного больших документов, чем много мелких. Будет оптимальнее использоваться диск (больше реальных данных, меньше доля "заделов"). То есть нужно всё денормализовывать, чтобы не делать "join"ов. Чем меньше раз запрашиваешь курсор, тем лучше. Напротив, атомарная запись или запись одного документа в mongo очень быстрая, ею можно пользоваться часто.

4. потоковая конвертация с mongo - тормозная. Если есть возможность, лучше всё сразу считать в память, потом формировать объекты, какие нужно, и записывать. У меня размер коллекции в базе - 87 МБ, в памяти Питона те же объекты занимают 9 МБ.

Last edited by siberiano (2011-05-25 19:19:37)

Offline

#20 2011-05-27 09:15:21

Miroff
Member
Registered: 2010-01-26
Posts: 147

Re: Данные OSM в MongoDB

siberiano wrote:

А какие индексы делаешь? Я для того чтобы сделать дорожный граф, создал единственный индекс - tags.highway.

Сейчас после небольшой работы с монго я пришёл к такой схеме:

1. исходную базу делать с минимумом информации, и в объекты ничего лишнего не писать, а лучше даже удалять лишнее сразу при импорте.

2. никаких рабочих вычислений на этой базе делать нельзя. Для предметной области нужно делать отдельную БД, и туда экспортировать объекты уже в том виде, в котором нужно.

3. Лучше держать немного больших документов, чем много мелких. Будет оптимальнее использоваться диск (больше реальных данных, меньше доля "заделов"). То есть нужно всё денормализовывать, чтобы не делать "join"ов. Чем меньше раз запрашиваешь курсор, тем лучше. Напротив, атомарная запись или запись одного документа в mongo очень быстрая, ею можно пользоваться часто.

4. потоковая конвертация с mongo - тормозная. Если есть возможность, лучше всё сразу считать в память, потом формировать объекты, какие нужно, и записывать. У меня размер коллекции в базе - 87 МБ, в памяти Питона те же объекты занимают 9 МБ.

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

Nodes:
{ 
    "_id" : NumberLong(32521222), 
    "lat" : 54.7724042, 
    "lon" : 83.1009863, 
    "tags": {"amenity": "parking"} 
}

Ways:
{ 
    "_id" : NumberLong(17237995), 
    "nodes" : [NumberLong(178727045), NumberLong(269537100)], 
    "tags" : { "highway" : "primary"}, 
}

Relations:
{ 
    "_id" : NumberLong(129298), 
    "members" : [{
        "ref" : NumberLong(23363997),
        "type" : "way",
        "role" : "to"
    }, {
        "ref" : NumberLong(35133693),
        "type" : "way",
        "role" : "from"
    }, {
        "ref" : NumberLong(248503088),
        "type" : "node",
        "role" : "via"
    }], 
    "tags" : { "restriction" : "no_left_turn", "type" : "restriction" } 
}

Индексы на полях ways.nodes и (relations.members.type, relations.members.ref). Без индексов не получится отслеживать изменения геометрии при изменении объекта. Что отсюда можно выкинуть, не представляю.

Основная проблема у меня в том, что для того чтобы построить геометрию, скажем, для вея, нужны координаты его точек, а это довольно тяжелая выборка. Выгрузить все ноды в память тоже не вариант. Для РФ они занимают 1Гб только в бинарном виде.

Offline

#21 2011-05-27 10:06:54

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Кстати, а ты можешь расшарить в битторренте базу России? (mongodubp => bz2) Именно эти три коллекции.

Вот пример документа у меня в базе. При работе с ними никаких дополнительных запросов не нужно. Теги путей я уберу скоро, они не нужны. Из файла области таких документов получается ~36 000.

{
    "_id" : 130763674,
    "changeset" : 6422529,
    "id" : 130763674,
    "lat" : 55.0410469,
    "lon" : 82.3909092,
    "timestamp" : "2010-11-21T13:30:55Z",
    "uid" : 183878,
    "user" : "whalerich",
    "version" : "9",
    "routes" : [
        {
            "way" : [
                {
                    "ref" : "M-51",
                    "highway" : "trunk"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk"
                }
            ],
            "via" : [
                130763195,
                659857287
            ],
            "id" : 210244290,
            "len" : 721.3430402945971
        },
        {
            "way" : [
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                },
                {
                    "ref" : "M-51",
                    "highway" : "trunk",
                    "oneway" : "yes"
                }
            ],
            "via" : [
                997755222,
                130763193,
                997754917,
                130763192,
                997754958,
                410845442,
                410845443
            ],
            "id" : 210244284,
            "len" : 913.9816212646475
        },
        {
            "way" : [
                {
                    "name" : "Северный обход Новосибирска",
                    "highway" : "trunk"
                },
                {
                    "name" : "Северный обход Новосибирска",
                    "highway" : "trunk"
                }
            ],
            "via" : [
                707377151
            ],
            "id" : 389013040,
            "len" : 335.20540758147973
        }
    ],
    "neighbors" : [
        210244290,
        210244284,
        389013040
    ]
}

Last edited by siberiano (2011-05-27 10:09:00)

Offline

#22 2011-12-03 23:18:25

x10kHz
Member
Registered: 2009-10-06
Posts: 138

Re: Данные OSM в MongoDB

Дело мое движется время от времени...
Хочу описать некоторый алгоритм, чтобы стало себе понятнее)

Для быстрого отображения или редактирования карты хотелось бы иметь возможность быстро определить какие веи и "свободные" ноды входят целиком, "задевают" этот тайл или накрывают его.
(Если тайл лежит целиком внутри вея, то этот вей задает его цвет полностью и этот признак тоже хотелось бы иметь в базе.)

Алгоритм, который собирает эту инфу я и накодил. Наверняка есть баги какие-нибудь и пока нет поддержки дырок, но ее ввести достаточно легко вроде бы.
Работает эта штука следующим образом.
Алгоритм пробегает по всем веям в базе и делает с ними следующее:
Сперва находится тайл максимального уровня, который содержит в себе целиком данный вей.
Для этого берем любую точку вея и смотрю в каком тайле максимального уровня (например 15 или 17) она лежит. Смотрим лежат ли все остальные точки вея (ну или bbox вея) в этом же тайле. Если нет, то берем предка этого тайла и делаем аналогичную проверку для него. Когда мы получили тайл в котором лежат все точки вея, то этот тай будет для нас скажем "корневым"... тайлом с которого мы запустим итерационный процесс.

После этого составляется список тайлов для проверки. Пока в него добавляется только корневой тайл.
Проверка заключается в том что ищется пересечение тайла с веем... и тайлу ставится признак того полностью внутри ли он, полностью снаружи или на него попадает граница вея.
Если тайл полностью снаружи, то мы благополучно о нем забываем и выкидываем из списка.
Если тайл полностью внутри вея, то мы добавляем в базе этому тайлу признак того что вей такой-то накрывает тайл полностью, после чего тоже удаляем из списка.
Если тайл попал на границу вея, то мы оставляем его в списке, а в базу ничего не добавляем. (Хотя возможно и нужно добавить инфу о том что в базе будут его потомки. Чтобы потом было удобнее по всему дереву пробегать.)
После проверки всех тайлов и добавления информации в базу мы делим все оставшиеся в списке тайлы на 4 новых, уровень которых выше соответственно на единицу. Список конечно чистим от старых тайлов прошлого уровня. После чего запускаем тест снова. И так продолжается до какого-то максимального уровня...
Когда дошли до этого уровня, то сохраняем в базу инфу для каждого тайла. О том что он содержит вей такой-то...

Проиллюстрирую работу алгоритма на простом примере:

На данный момент найден "корневой тайл":
92560283.png

Теперь его поделили и проверили его потомков...
только два тайла задевают за вей, их и оставили.
43623414.png

делим дальше, ситуация аналогичная...
68739611.png

64775145.png

72237786.png

95209022.png

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

Но есть пример интереснее:

Вей №8105524

Найденный "корневой" тайл:
37548439.png

Отбрасываем внешние тайлы...
67695058.png

41411298.png

27491846.png

На этом шаге находятся 5 зеленых тайлов, которые полностью "накрыты" веем... т.е. их цвет и цвет всех их потомков будет определятся стилем этого вея.
Поэтому мы сохраняем об этом инфу в базе для каждого из этих 5-ти тайлов и выкидываем их из списка просмотра...
52642594.png

Опять появились зеленые тайлы и мы сохраняем о них инфу, но этот шаг последний т.к. достигнут максимальный уровень и поэтому сохраняем данные и для всех остальных оставшихся в списке тайлах.
45269785.png

Думаю наверняка где-то это уже реализовано, но пока свой велосипед удобнее)
Думаю если держать такую информацию о тайлах в каком-то, ориентированном на быстрое чтение, виде, то можно сделать быстрый поиск необходимых веев и нодов для рендера карты.
Статистику не собирал, но много тайлов максимального уровня будет там где есть мелкие веи, населенные пункты и т.д. для лесов, морей, рек такой информации должно быть мало...
Рендер может определить цвет каких тайлов ему нужен исходя из то куда направлена камера.
Затем посмотреть есть ли эти тайлы в базе, вычислить их цвет исходя из информации о их предках, если их нет... или найти потомков и составить "представление" для данного тайла.
Получив id всех необходимых объектов для отрисовки выкидываем дубликаты и подгружаем того что нет в кэше... рисуем. На мой взгляд вроде бы идея имеет право на жизнь)
Правда тут у меня возникают некоторые вопросы, которые я еще не продумал) Например как лучше добавить разную геометрию для разных уровней... или отрезать вообще часть "глобальных" уровней сделав пререндер и т.д. Как удобнее и оптимальнее бегать по дереву тайлов и быстро использовать имеющуюся инфу, но надеюсь я на правильном пути.

Описывал по памяти... возможно где-то как-то алгоритм неправильно воспроизвел, но основная идея думаю понятна.
Если есть какие-то идеи по поводу оптимизации этого алгоритма или может что-нибудь в принципе другое для шустрого поиска того что нужно рисовать здесь и сейчас, заметите какие-то косяки, не стыковки и прочее - буду рад услышать!

Last edited by x10kHz (2012-06-26 05:24:32)

Offline

#23 2011-12-05 17:23:20

siberiano
Moderator
From: Novosibirsk
Registered: 2010-02-25
Posts: 1,003
Website

Re: Данные OSM в MongoDB

Интересно. Кстати, попробовал на pypy 1.7, экспорт из .osm.bz2 в .json.bz2 получился чуть-чуть быстрее.

Offline

#24 2012-05-30 16:45:56

x10kHz
Member
Registered: 2009-10-06
Posts: 138

Re: Данные OSM в MongoDB

Товарищи, посмотрите пожалуйста отношение №934119... там в участниках несколько outer-веев, причем они не замкнуты!
Корректно ли вообще такое отношение?
Я так понимаю в мультиполигоне должен быть только один outer и несколько inner'ов могут быть... причем и те и другие должны быть закрытыми веями.
Также все эти веи должны быть с непересекающимися контурами и несамопересекающимися.

Если есть несколько outer-веев, то такой мультиполигон может быть разбит на несколько отношений... и тут как бы отношение уже несет другой смысл, оно описывает теги для отдельных outer'ов... не знаю правильно ли так делать... отношение multipolygon должно указывать где дырки у конкретного вея

Offline

#25 2012-05-30 17:35:03

esaulenka
Member
From: Москва
Registered: 2010-08-21
Posts: 1,194
Website

Re: Данные OSM в MongoDB

x10kHz wrote:

Корректно ли вообще такое отношение?

Вот тут всё написано.

Offline

Board footer

Powered by FluxBB