diff --git a/generator/generator_tests/osm_o5m_source_test.cpp b/generator/generator_tests/osm_o5m_source_test.cpp new file mode 100644 index 0000000000..ab9458a0dc --- /dev/null +++ b/generator/generator_tests/osm_o5m_source_test.cpp @@ -0,0 +1,342 @@ +// +// osm_o5m_source_test.cpp +// generator_tool +// +// Created by Sergey Yershov on 14.04.15. +// + +#include "testing/testing.hpp" +#include "generator/osm_o5m_source.hpp" + +#include "std/set.hpp" + + +/* + + + + + + + + + + + */ + +/* begin binary data: node.o5m */ +uint8_t node_o5m_data[] = /* 92 */ +{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x51,0xA1,0xAB,0x21,0x00,0xCD + ,0xE6,0xD7,0x80,0x0A,0xBE,0xCE,0x82,0xD1,0x04,0x00,0x6E,0x61,0x6D,0x65,0x00 + ,0xD0,0x9F,0xD1,0x80,0xD0,0xBE,0xD0,0xB4,0xD1,0x83,0xD0,0xBA,0xD1,0x82,0xD0 + ,0xBE,0xD0,0xB2,0xD1,0x8B,0xD0,0xB9,0x00,0x00,0x6F,0x70,0x65,0x6E,0x69,0x6E + ,0x67,0x5F,0x68,0x6F,0x75,0x72,0x73,0x00,0x32,0x34,0x2F,0x37,0x00,0x00,0x73 + ,0x68,0x6F,0x70,0x00,0x63,0x6F,0x6E,0x76,0x65,0x6E,0x69,0x65,0x6E,0x63,0x65 + ,0x00,0xFE}; +/* end binary data. size = 92 bytes */ + + +/* + + + + + + + + + */ + +/* begin binary data: node2.o5m */ +uint8_t node2_o5m_data[] = /* 93 */ +{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x52,0x94,0xDD,0xF4,0xE9,0x03 + ,0x08,0xD6,0xBD,0xEF,0xFE,0x09,0xF0,0x87,0xC0,0x0B,0x00,0xBF,0x8E,0x18,0x00 + ,0x58,0x6D,0x79,0x70,0x62,0x6C,0x75,0x00,0xA0,0xCC,0xDA,0xF1,0x02,0xAC,0xEB + ,0xB3,0x8D,0x04,0x00,0x61,0x6D,0x65,0x6E,0x69,0x74,0x79,0x00,0x63,0x69,0x6E + ,0x65,0x6D,0x61,0x00,0x00,0x6E,0x61,0x6D,0x65,0x00,0xD0,0x9A,0xD0,0xA2,0x20 + ,0xD0,0x93,0xD0,0xBE,0xD1,0x80,0xD0,0xB8,0xD0,0xB7,0xD0,0xBE,0xD0,0xBD,0xD1 + ,0x82,0x00,0xFE}; +/* end binary data. size = 93 bytes */ + + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ + +/* begin binary data: way.o5m */ +uint8_t way_o5m_data[] = /* 175 */ +{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x0E,0xE9,0xAB,0x21,0x00,0xC5 + ,0x94,0x88,0x88,0x0A,0x8C,0xB1,0x9D,0xC3,0x04,0x10,0x08,0x04,0x00,0x9C,0x8F + ,0x0A,0x87,0xE7,0x05,0x10,0x07,0x04,0x00,0xF6,0x4A,0xFD,0x92,0x03,0x10,0x08 + ,0x04,0x00,0xF6,0xA1,0x04,0xF5,0x80,0x04,0x10,0x08,0x04,0x00,0xB4,0xB6,0x02 + ,0x89,0xBB,0x06,0x10,0x08,0x04,0x00,0xB4,0xEC,0x03,0xD9,0xCC,0x03,0x10,0x08 + ,0x04,0x00,0xD6,0xC0,0x01,0xB7,0x83,0x03,0x10,0x08,0x06,0x00,0xDB,0xAB,0x02 + ,0x9F,0xFE,0x02,0x10,0x08,0x02,0x00,0x87,0xAD,0x05,0xEB,0xA5,0x04,0xFF,0x11 + ,0x44,0xCD,0xAB,0x21,0x00,0x0B,0xC9,0xAB,0x21,0x01,0x05,0x03,0x03,0x03,0x03 + ,0x03,0x03,0x00,0x6E,0x61,0x6D,0x65,0x00,0x59,0x75,0x6B,0x6F,0x6E,0x20,0x52 + ,0x69,0x76,0x65,0x72,0x00,0x00,0x6E,0x61,0x6D,0x65,0x3A,0x72,0x75,0x00,0xD0 + ,0xAE,0xD0,0xBA,0xD0,0xBE,0xD0,0xBD,0x00,0x00,0x77,0x61,0x74,0x65,0x72,0x77 + ,0x61,0x79,0x00,0x72,0x69,0x76,0x65,0x72,0x00,0xFE}; +/* end binary data. size = 175 bytes */ + + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ + +/* begin binary data: relation.o5m*/ +uint8_t relation_o5m_data[] = /* 224 */ +{0xFF,0xE0,0x04,0x6F,0x35,0x6D,0x32,0xFF,0x10,0x2B,0xD7,0xAC,0x21,0x00,0xAD + ,0xCF,0xFC,0x87,0x0A,0xA6,0xCE,0x88,0xC3,0x04,0x00,0x6E,0x61,0x6D,0x65,0x00 + ,0x57,0x68,0x69,0x74,0x65,0x68,0x6F,0x72,0x73,0x65,0x00,0x00,0x70,0x6C,0x61 + ,0x63,0x65,0x00,0x74,0x6F,0x77,0x6E,0x00,0x10,0x07,0x2C,0x00,0x9A,0x90,0x24 + ,0xD9,0x68,0x10,0x08,0x04,0x00,0xDE,0xCD,0x1E,0xF7,0xF6,0x15,0x10,0x08,0x04 + ,0x00,0x91,0xD3,0x2A,0xFB,0xCC,0x11,0x10,0x08,0x04,0x00,0xE7,0x95,0x60,0xFE + ,0x9A,0x02,0x10,0x08,0x04,0x00,0x97,0xDE,0x13,0xC0,0xA7,0x0F,0x10,0x08,0x04 + ,0x00,0xB6,0x9E,0x14,0xD2,0xC3,0x19,0x10,0x08,0x04,0x00,0xA6,0x8F,0x22,0xE0 + ,0xDD,0x16,0x10,0x07,0x02,0x00,0xA6,0xF6,0x19,0x9F,0x49,0xFF,0x11,0x10,0x95 + ,0xAC,0x21,0x00,0x0B,0x91,0xAC,0x21,0x01,0x03,0x03,0x03,0x03,0x03,0x03,0x1A + ,0xFF,0x12,0x46,0xB1,0xAC,0x21,0x00,0x11,0x95,0xAC,0x21,0x00,0x31,0x6F,0x75 + ,0x74,0x65,0x72,0x00,0xD7,0xAC,0x21,0x00,0x30,0x00,0x00,0x6E,0x61,0x6D,0x65 + ,0x00,0x57,0x68,0x69,0x74,0x65,0x68,0x6F,0x72,0x73,0x65,0x00,0x00,0x70,0x6C + ,0x61,0x63,0x65,0x00,0x74,0x6F,0x77,0x6E,0x00,0x00,0x74,0x79,0x70,0x65,0x00 + ,0x6D,0x75,0x6C,0x74,0x69,0x70,0x6F,0x6C,0x79,0x67,0x6F,0x6E,0x00,0xFE}; +/* end binary data. size = 224 bytes */ + + +UNIT_TEST(OSM_O5M_Source_Node_read_test) +{ + std::string data(std::begin(node2_o5m_data), std::end(node2_o5m_data)); + std::stringstream ss(data); + + osm::O5MSourceReader dataset([&ss](uint8_t * buffer, size_t size) + { + return ss.read((char *)buffer, size).gcount(); + }); + + osm::O5MSourceReader::iterator it = dataset.begin(); + osm::O5MSourceReader::Entity const & em = *it; + + CHECK_EQUAL(em.id, 513709898, ()); + CHECK_EQUAL(em.user, string("Xmypblu"), ()); + CHECK_EQUAL(em.uid, 395071, ()); + CHECK_EQUAL(em.version, 8, ()); + CHECK_EQUAL(em.changeset, 12059128, ()); + CHECK_EQUAL(em.lon, 387666704, ()); + CHECK_EQUAL(em.lat, 550927062, ()); + + auto const & tags = em.tags(); + auto tag_iterator = tags.begin(); + auto const & tag = *tag_iterator; + CHECK_EQUAL(tag.key, string("amenity"), ()); + CHECK_EQUAL(tag.value, string("cinema"), ()); + tag_iterator++; + CHECK_EQUAL(tag.key, string("name"), ()); + CHECK_EQUAL(tag.value, string("КТ Горизонт"), ()); + tag_iterator++; + CHECK(!(tag_iterator != tags.end()), ()); +} + +UNIT_TEST(OSM_O5M_Source_Way_read_test) +{ + std::string data(std::begin(way_o5m_data), std::end(way_o5m_data)); + std::stringstream ss(data); + + osm::O5MSourceReader dataset([&ss](uint8_t * buffer, size_t size) + { + return ss.read((char *)buffer, size).gcount(); + }); + + set nodes; + + vector> valid_tags = { + {"name", "Yukon River"}, + {"name:ru", "Юкон"}, + {"waterway", "river"} + }; + + for (auto const & em : dataset) + { + switch (em.type) { + case osm::O5MSourceReader::O5M_CMD_NODE: + { + nodes.insert(em.id); + for (auto const & tag : em.tags()) + { + CHECK(false, ("Unexpected tag:", tag.key , tag.value)); + } + break; + } + case osm::O5MSourceReader::O5M_CMD_WAY: + { + size_t nd_count = 0; + size_t tag_count = 0; + for (auto const &nd : em.nodes()) + { + nd_count++; + CHECK(nodes.count(nd), ()); + } + CHECK_EQUAL(nodes.size(), nd_count, ()); + for (auto const & tag : em.tags()) + { + CHECK_EQUAL(tag.key, valid_tags[tag_count].first, ()); + CHECK_EQUAL(tag.value, valid_tags[tag_count].second, ()); + tag_count++; + } + CHECK_EQUAL(valid_tags.size(), tag_count, ()); + break; + } + default: + break; + } + } +} + +UNIT_TEST(OSM_O5M_Source_Relation_read_test) +{ + std::string data(std::begin(relation_o5m_data), std::end(relation_o5m_data)); + std::stringstream ss(data); + + osm::O5MSourceReader dataset([&ss](uint8_t * buffer, size_t size) + { + return ss.read((char *)buffer, size).gcount(); + }); + + set nodes; + set entities; + + vector> node_valid_tags = { + {"name", "Whitehorse"}, + {"place", "town"} + }; + + vector> relation_valid_tags = { + {"name", "Whitehorse"}, + {"place", "town"}, + {"type", "multipolygon"} + }; + + vector> relation_members = { + {osm::O5MSourceReader::O5M_CMD_WAY, "outer"}, + {osm::O5MSourceReader::O5M_CMD_NODE, ""} + }; + + for (auto const & em : dataset) + { + entities.insert(em.id); + + switch (em.type) { + case osm::O5MSourceReader::O5M_CMD_NODE: + { + nodes.insert(em.id); + size_t tag_count = 0; + for (auto const & tag : em.tags()) + { + CHECK_EQUAL(tag.key, node_valid_tags[tag_count].first, ()); + CHECK_EQUAL(tag.value, node_valid_tags[tag_count].second, ()); + tag_count++; + } + break; + } + case osm::O5MSourceReader::O5M_CMD_WAY: + { + size_t nd_count = 0; + for (auto const &nd : em.nodes()) + { + nd_count++; + CHECK(nodes.count(nd), ()); + } + CHECK_EQUAL(nodes.size(), nd_count, ()); + for (auto const & tag : em.tags()) + { + CHECK(false, ("Unexpected tag:", tag.key , tag.value)); + } + break; + } + case osm::O5MSourceReader::O5M_CMD_REL: + { + size_t member_count = 0; + size_t tag_count = 0; + for (auto const &member : em.members()) + { + CHECK(entities.count(member.ref), ()); + CHECK_EQUAL(relation_members[member_count].first, member.type, ("Current member:", member_count)); + CHECK_EQUAL(relation_members[member_count].second, member.role, ("Current member:", member_count)); + member_count++; + } + CHECK_EQUAL(member_count, 2, ()); + for (auto const & tag : em.tags()) + { + CHECK_EQUAL(tag.key, relation_valid_tags[tag_count].first, ()); + CHECK_EQUAL(tag.value, relation_valid_tags[tag_count].second, ()); + tag_count++; + } + CHECK_EQUAL(relation_valid_tags.size(), tag_count, ()); + break; + } + default: + break; + } + } +} diff --git a/generator/osm_o5m_source.hpp b/generator/osm_o5m_source.hpp new file mode 100644 index 0000000000..cd636440d3 --- /dev/null +++ b/generator/osm_o5m_source.hpp @@ -0,0 +1,567 @@ +// +// osm_o5m_source.hpp +// generator +// +// Created by Sergey Yershov on 14.04.15. +// + +#pragma once + +#include "std/iostream.hpp" +#include "std/sstream.hpp" +#include "std/vector.hpp" +#include "std/iomanip.hpp" + +namespace +{ + template + class StreamBuffer + { + using BufferT = std::vector; + + ReaderT const & m_reader; + BufferT m_buffer; + size_t const m_buffer_base_size; + size_t m_byte_counter; + + BufferT::value_type const * m_position; + BufferT::value_type const * m_start; + BufferT::value_type const * m_end; + + public: + StreamBuffer(ReaderT const &reader, size_t buffer_size) + : m_reader(reader) + , m_buffer(buffer_size) + , m_buffer_base_size(buffer_size) + , m_byte_counter(0) + { + m_position = m_start = &m_buffer.front(); + m_end = (&m_buffer.back())+1; + read_buffer(); + } + + size_t byte_counter() + { + size_t b = m_byte_counter; + m_byte_counter = 0; + return b; + } + + inline BufferT::value_type get() + { + if(m_position == m_end) + read_buffer(); + ++m_byte_counter; + return *(m_position++); + } + + inline BufferT::value_type peek() + { + if(m_position == m_end) + read_buffer(); + return *m_position; + } + + void skip(size_t size = 1) + { + if(m_position + size >= m_end) + { + size -= (m_end - m_position); + for (read_buffer(); size > m_buffer.size(); size -= m_buffer.size()) + read_buffer(); + } + m_position += size; + m_byte_counter += size; + } + + void read(BufferT::value_type * dest, size_t size) { + if(m_position + size >= m_end) + { + size_t remainder = (m_end - m_position); + memcpy(dest, m_position, remainder); + size -= remainder; + dest += remainder; + for (read_buffer(); size > m_buffer.size(); size -= m_buffer.size()) + { + memcpy(dest, m_buffer.data(), m_buffer.size()); + dest += m_buffer.size(); + read_buffer(); + } + } + memcpy(dest, m_position, size); + m_position += size; + } + + private: + + void read_buffer() + { + size_t readed_bytes = 0; + + if (m_buffer.size() != m_buffer_base_size) + m_buffer.resize(m_buffer_base_size); + + readed_bytes = m_reader(m_buffer.data(), m_buffer.size()); + + if (readed_bytes != m_buffer.size()) + { + m_buffer.resize(readed_bytes); + m_start = &m_buffer.front(); + m_end = (&m_buffer.back())+1; + } + m_position = m_start; + } + }; +} // anonymouse namespace + +namespace osm +{ + + template + class O5MSource + { + template + using TReadFunc = std::function; + + struct StringTableRec + { + char ksize; + char vsize; + unsigned short int align; + char k[254]; + char v[254]; + }; + + struct KeyValue + { + char const * key = nullptr; + char const * value = nullptr; + + KeyValue() = default; + KeyValue(char const *k, char const *v) : key(k), value(v) {} + }; + + struct Member + { + int64_t ref = 0; + uint8_t type = 0; + char const * role = nullptr; + }; + + std::vector m_string_table; + std::vector m_string_buffer; + size_t m_string_current_index; + StreamBuffer m_buffer; + size_t m_remainder; + int64_t m_delta_state[3]; + int64_t m_id = 0; + int32_t m_lon = 0; + int32_t m_lat = 0; + uint64_t m_timestamp = 0; + uint64_t m_changeset = 0; + int64_t m_refarea_size = 0; + + public: + enum ECommand + { + O5M_CMD_END=0xfe, + O5M_CMD_NODE=0x10, + O5M_CMD_WAY=0x11, + O5M_CMD_REL=0x12, + O5M_CMD_BBOX=0xdb, + O5M_CMD_TSTAMP=0xdc, + O5M_CMD_HEADER=0xe0, + O5M_CMD_SYNC=0xee, + O5M_CMD_JUMP=0xef, + O5M_CMD_RESET=0xff + }; + + template + class SubElements + { + O5MSource * m_reader; + TFunc m_func; + public: + + class iterator + { + O5MSource * m_reader; + TValue m_val; + TFunc m_func; + public: + + + iterator() : m_reader(nullptr) {} + explicit iterator(O5MSource *reader, TFunc const & func) + : m_reader(reader) + , m_func(func) + { + next_val(); + } + + bool operator != (iterator const &iter) {return m_reader != iter.m_reader;} + + iterator & operator++() { next_val(); return *this; } + iterator operator++(int) { next_val(); return *this; } + + void next_val() { m_reader = m_reader ? m_func(&m_val) : nullptr; } + + TValue const & operator*() const { return m_val; } + }; + + SubElements(O5MSource * reader, TFunc const & func) : m_reader(reader), m_func(func) {} + + void skip() { while(m_reader ? m_func(nullptr) : nullptr); } + + iterator const begin() const { return iterator(m_reader, m_func); } + iterator const end() const { return iterator(); } + }; + + struct Entity + { + uint8_t type = 0; + int64_t id = 0; + uint64_t version = 0; + int32_t lon = 0; + int32_t lat = 0; + int64_t timestamp = 0; + int64_t changeset = 0; + uint64_t uid = 0; + char const * user = nullptr; + + using RefsT = SubElements>; + using NodesT = SubElements>; + using TagsT = SubElements>; + + RefsT members() const + { + return RefsT((type == O5M_CMD_REL) ? m_reader : nullptr, [this](Member *val) + { + return (m_reader) ? m_reader->ReadMember(val) : nullptr; + }); + } + + NodesT nodes() const + { + return NodesT((type == O5M_CMD_WAY) ? m_reader : nullptr, [this](int64_t *val) + { + return (m_reader) ? m_reader->ReadNd(val) : nullptr; + }); + } + + TagsT tags() const + { + members().skip(); + nodes().skip(); + bool const validType = (type == O5M_CMD_NODE || type == O5M_CMD_WAY || type == O5M_CMD_REL); + return TagsT( validType ? m_reader : nullptr, [this](KeyValue *val) + { + return (m_reader) ? m_reader->ReadStringPair(val) : nullptr; + }); + } + + void SkipRemainder() + { + if (!(type == O5M_CMD_NODE || type == O5M_CMD_WAY || type == O5M_CMD_REL)) + return; + if (type == O5M_CMD_WAY) + while(m_reader ? m_reader->ReadNd(nullptr) : nullptr); + if (type == O5M_CMD_REL) + while(m_reader ? m_reader->ReadMember(nullptr) : nullptr); + + while(m_reader ? m_reader->ReadStringPair(nullptr) : nullptr); + } + + Entity() : m_reader(nullptr) {} + Entity(O5MSource * reader) : m_reader(reader) {} + protected: + O5MSource * m_reader; + }; + + uint64_t ReadUInt() + { + uint8_t b; + uint8_t i = 0; + uint64_t ret = 0LL; + + do + { + b = m_buffer.get(); + ret |= (uint64_t)(b & 0x7f) << (i++ * 7); + } while ( b & 0x80 ); + size_t rb = m_buffer.byte_counter(); + m_remainder -= rb; + m_refarea_size -= rb; + return ret; + } + + int64_t ReadInt() + { + uint64_t ret = ReadUInt(); + return (ret & 1) ? (-int64_t(ret >> 1) - 1) : int64_t(ret >> 1); + } + + O5MSource * ReadMember(Member * ref) + { + if (m_refarea_size <= 0) + return nullptr; + int64_t delta = ReadInt(); + KeyValue kv; + if(!ReadStringPair(&kv, true /*single string*/)) + return nullptr; + m_delta_state[kv.key[0] - '0'] += delta; + if (ref) + { + ref->ref = m_delta_state[kv.key[0] - '0']; + ref->type = kv.key[0] - ' '; + ref->role = &kv.key[1]; + } + return this; + } + + O5MSource * ReadNd(int64_t * nd) + { + if (m_refarea_size <= 0) + return nullptr; + (nd ? *nd : m_delta_state[0]) = (m_delta_state[0] += ReadInt()); + return this; + } + + O5MSource * ReadStringPair(KeyValue * kv, bool single = false) + { + if (m_remainder == 0) + return nullptr; + + uint64_t key = ReadUInt(); + if (key) + { + // lookup table + if (kv) + *kv = KeyValue(m_string_table[m_string_current_index-key].k, m_string_table[m_string_current_index-key].v); + return this; + } + + char *pBuf = m_string_buffer.data(); + size_t sizes[2] = {0,0}; + for (size_t i=0; i<(single?1:2); i++) + { + do + { + *pBuf = m_buffer.get(); + sizes[i]++; + } while ( *(pBuf++) ); + } + size_t rb = m_buffer.byte_counter(); + m_remainder -= rb; + m_refarea_size -= rb; + + if (sizes[0]+sizes[1] < 500) + { + memcpy(m_string_table[m_string_current_index].k, m_string_buffer.data(), sizes[0]); + memcpy(m_string_table[m_string_current_index].v, m_string_buffer.data()+sizes[0], sizes[1]); + size_t key = m_string_current_index++; + if (kv) + *kv = KeyValue(m_string_table[key].k, m_string_table[key].v); + return this; + } + + if (kv) + *kv = KeyValue(m_string_buffer.data(), m_string_buffer.data() + sizes[0]); + return this; + } + + void ReadIdAndVersion(Entity * e) + { + e->id = (m_id += ReadInt()); + + if ((e->version = ReadUInt()) == 0) + { + e->timestamp = 0; + e->changeset = 0; + e->uid = 0; + e->user = nullptr; + return; + } + + e->timestamp = (m_timestamp += ReadInt()); + if (m_timestamp) + { + e->changeset = (m_changeset += ReadInt()); + KeyValue kv; + ReadStringPair(&kv); + uint8_t i = 0; + do e->uid |= (uint64_t)((*kv.key) & 0x7f) << (i++ * 7); + while ( *(kv.key++) & 0x80 ); + e->user = kv.value; + } + } + + void ReadLonLat(Entity * e) + { + e->lon = (m_lon += ReadInt()); + e->lat = (m_lat += ReadInt()); + } + + + O5MSource * ReadEntity(Entity * entity) + { + + entity->SkipRemainder(); + + entity->type = m_buffer.get(); + + if (O5M_CMD_END == entity->type) + return nullptr; + + if (O5M_CMD_RESET == entity->type) + { + Reset(); + return ReadEntity(entity); + } + + entity->uid = 0; + entity->user = nullptr; + m_refarea_size = 0; + + m_remainder = ReadUInt(); /* entity size */ + + switch (entity->type) { + case O5M_CMD_NODE: { + ReadIdAndVersion(entity); + ReadLonLat(entity); + } break; + case O5M_CMD_WAY: { + ReadIdAndVersion(entity); + m_refarea_size = ReadUInt(); + } break; + case O5M_CMD_REL: { + ReadIdAndVersion(entity); + m_refarea_size = ReadUInt(); + } break; + case O5M_CMD_BBOX: { + } break; + case O5M_CMD_TSTAMP: { + ReadUInt(); + } break; + case O5M_CMD_HEADER: { + } break; + case O5M_CMD_SYNC: { + } break; + case O5M_CMD_JUMP: { + } break; + + default: + break; + } + return this; + } + + void InitStringTable() + { + m_string_current_index = 0; + m_string_buffer.resize(1024); + m_string_table.resize(15000); + } + + void Reset() + { + m_delta_state[0] = 0; + m_delta_state[1] = 0; + m_delta_state[2] = 0; + m_id = 0; + m_lon = 0; + m_lat = 0; + m_timestamp = 0; + m_changeset = 0; + m_remainder = 0; + } + + public: + + static char const * cmd_to_text(uint8_t cmd) + { + switch (cmd) { + case O5M_CMD_END: return "O5M_CMD_END"; break; + case O5M_CMD_NODE: return "O5M_CMD_NODE"; break; + case O5M_CMD_WAY: return "O5M_CMD_WAY"; break; + case O5M_CMD_REL: return "O5M_CMD_REL"; break; + case O5M_CMD_BBOX: return "O5M_CMD_BBOX"; break; + case O5M_CMD_TSTAMP: return "O5M_CMD_TSTAMP"; break; + case O5M_CMD_HEADER: return "O5M_CMD_HEADER"; break; + case O5M_CMD_SYNC: return "O5M_CMD_SYNC"; break; + case O5M_CMD_JUMP: return "O5M_CMD_JUMP"; break; + case O5M_CMD_RESET: return "O5M_CMD_RESET"; break; + default: return "Unknown command"; + } + } + + class iterator + { + O5MSource * m_reader; + Entity m_entity; + + public: + iterator() : m_reader(nullptr), m_entity(nullptr) {} + iterator(O5MSource *reader) : m_reader(reader), m_entity(reader) + { + m_reader->Reset(); + next_val(); + } + + bool operator != (iterator const &iter) {return m_reader != iter.m_reader;} + iterator & operator++() { next_val(); return *this; } + iterator operator++(int) { next_val(); return *this; } + + void next_val() { m_reader = m_reader->ReadEntity(&m_entity) ? m_reader : nullptr; } + + Entity const & operator*() const { return m_entity; } + }; + + iterator const begin() {return iterator(this);} + iterator const end() {return iterator();} + + O5MSource(ReaderT const & reader) : m_buffer(reader, 10000 /* buffer size */) + { + if (m_buffer.get() != O5M_CMD_RESET) + { + throw std::runtime_error("Incorrect o5m start"); + } + CheckHeader(); + InitStringTable(); + } + + bool CheckHeader() + { + size_t len = 4; + if (m_buffer.get() == O5M_CMD_HEADER && ReadUInt() == len) + { + std::string sign(len,' '); + m_buffer.read((uint8_t *)sign.data(), len); + if (sign == "o5m2" || sign == "o5c2") + return true; + } + return false; + } + + friend std::ostream & operator << (std::ostream & s, O5MSource::Entity const & em) + { + s << cmd_to_text(em.type) << " ID: " << em.id; + if (em.version) + { + std::time_t timestamp = em.timestamp; + std::tm stm = *std::gmtime(×tamp); + s << " Version: " << em.version << " timestamp: " << std::put_time(&stm, "%FT%TZ"); + s << " changeset: " << em.changeset << " uid: " << em.uid << " user: " << em.user; + } + if (em.type == O5M_CMD_NODE) + { + s << std::endl << " lon: " << em.lon << " lat: " << em.lat; + } + return s; + } + }; + + using TReadFunc = std::function; + typedef O5MSource O5MSourceReader; + +} // namespace osm diff --git a/generator/osm_source.cpp b/generator/osm_source.cpp index 2df5d11608..a10d7ebfd1 100644 --- a/generator/osm_source.cpp +++ b/generator/osm_source.cpp @@ -22,48 +22,11 @@ #include "coding/parse_xml.hpp" #include "3party/o5mreader/o5mreader.h" +#include "generator/source_reader.hpp" #define DECODE_O5M_COORD(coord) (static_cast(coord) / 1E+7) -namespace -{ - class SourceReader - { - string const &m_filename; - FILE * m_file; - - public: - SourceReader(string const & filename) : m_filename(filename) - { - if (m_filename.empty()) - { - LOG(LINFO, ("Read OSM data from stdin...")); - m_file = freopen(NULL, "rb", stdin); - } - else - { - LOG(LINFO, ("Read OSM data from", filename)); - m_file = fopen(filename.c_str(), "rb"); - } - } - - ~SourceReader() - { - if (!m_filename.empty()) - fclose(m_file); - } - - inline FILE * Handle() { return m_file; } - - uint64_t Read(char * buffer, uint64_t bufferSize) - { - return fread(buffer, sizeof(char), bufferSize, m_file); - } - }; -} - - namespace feature { diff --git a/generator/source_reader.hpp b/generator/source_reader.hpp new file mode 100644 index 0000000000..fb208b3a8b --- /dev/null +++ b/generator/source_reader.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "std/string.hpp" +#include "std/cstdio.hpp" + +namespace +{ + class SourceReader + { + string const &m_filename; + FILE * m_file; + + public: + SourceReader(string const & filename) : m_filename(filename) + { + if (m_filename.empty()) + { + LOG(LINFO, ("Read OSM data from stdin...")); + m_file = freopen(NULL, "rb", stdin); + } + else + { + LOG(LINFO, ("Read OSM data from", filename)); + m_file = fopen(filename.c_str(), "rb"); + } + } + + ~SourceReader() + { + if (!m_filename.empty()) + fclose(m_file); + } + + inline FILE * Handle() { return m_file; } + + uint64_t Read(char * buffer, uint64_t bufferSize) + { + return fread(buffer, sizeof(char), bufferSize, m_file); + } + }; +} +