Задача «Чтение карты (Фабричный метод)»

Задание

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

Описание

Картографическая информация хранится в формате аналогичном OpenStreetMap и опирается на 3 базовых класса:

Node — узел (точка) на карте

  • id — уникальный целочисленный идентификатор узла
  • lattitude — широта узла
  • longitude — долгота узла
  • tags — набор тегов в виде key-value строковых значений, где key — строка и value — строка

Way — путь (линия) на карте

  • id — уникальный целочисленный идентификатор линии
  • nodes — направленный список узлов
  • tags — набор тегов в виде key-value строковых значений, где key — строка и value — строка

Relation — отношение (группа узлов и линий)

  • id — уникальный целочисленный идентификатор отношения
  • members — список узлов и линий, каждый элемент состоит из
    • type — тип элемента в списке (узел или путь)
    • id — идентификатор элемента (узла или пути)
    • role — роль элемента, строка с описанием
  • tags — набор тегов в виде key-value строковых значений, где key — строка и value — строка

Данные хранятся в файле в простом текстовом TSV (tab-separated values) формате в кодировке UTF-8. Каждая строка содержит информацию об одном элементе.

Примеры записи:

узел

n676312584  54.8419441  83.1128523  #historic="memorial"    #int_name="M. A. Lavrentyev"    #name="М. А. Лаврентьев"    #name:en="M. A. Lavrentyev" #name:ru="М. А. Лаврентьев"

// n{id}    {lat}   {lon}   #{key1}="{value1}"  ...

путь

w23328568   n252613842  n252613843  n910721898  n252613876  #addr:postcode="630090" #cladr:code="54000001000071400" #cladr:name="Академика Лаврентьева" #cladr:suffix="Проспект"    #highway="secondary"    #lanes="4"  #lit="yes"  #name="проспект Академика Лаврентьева"  #name:en="Lavrentyev Ave"   #surface="asphalt"


// w{id}    n{id1}  ... n{idN}  #{key1}="{value1}"  ...

отношение

r366519 w46676881="outer"   n46676881="outer"   w124965071="outer"  n124965071="outer"  w88178918="outer"   n88178918="outer"   w88178923="outer"   n88178923="outer"   w310614929="outer"  n310614929="outer"  w46677631="outer"   n46677631="outer"   #admin_level="9"    #boundary="administrative"  #name="Советский район"   #type="boundary"

// r{id}    w{id1}="{role1}"    n{id2}="{role2}"    ... w{N}="{roleN}"  #{key1}="{value1}"  ...

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

В данных содержатся названия на разных языках, поэтому в задаче необходимо использовать wstring, wofstream, wifstream.

Пример работы с файлами в кодировке UTF-8:

#include <locale>   // locale
#include <string>   // wstring
#include <codecvt>  // codecvt_utf8_utf16, codecvt_utf8
#include <fstream>  // wifstream, wofstream

template <size_t bits, std::codecvt_mode mode>
struct codecvt_utf8_wchar_impl;

// реализация для Windows: wchar_t - 16-бит, UTF-16
template <std::codecvt_mode mode>
struct codecvt_utf8_wchar_impl<16, mode> {
    using type = std::codecvt_utf8_utf16<wchar_t, 0x10FFFF, mode>;
};

// реализация для Linux/Unix-подобных OS: wchar_t - 32-бит, UCS-32
template <std::codecvt_mode mode>
struct codecvt_utf8_wchar_impl<32, mode> {
    using type = std::codecvt_utf8<wchar_t, 0x10FFFF, mode>;
};

// реализация для текущей OS
template <std::codecvt_mode mode>
using codecvt_utf8_wchar = typename codecvt_utf8_wchar_impl<sizeof(wchar_t) * 8, mode>::type;

void main(void) {
    std::wifstream wifs("w-RU-NVS-1.uft8.txt", std::ios::binary);
    wifs.imbue(std::locale(wifs.getloc(), new codecvt_utf8_wchar<std::consume_header>));

    std::wofstream wofs("w-test.txt", std::ios::binary);
    wofs.imbue(std::locale(wofs.getloc(), new codecvt_utf8_wchar<std::generate_header>));

    while ( wifs ) {
        std::wstring wstr;
        std::getline(wifs, wstr);
        wofs << L"test € тест" << std::endl;
        wofs << wstr << std::endl;
    }
}

Архив с примерами карт ~ 65 MB.