diff --git a/coding/file_container.cpp b/coding/file_container.cpp index 32eb9d623b..623b83cf5d 100644 --- a/coding/file_container.cpp +++ b/coding/file_container.cpp @@ -105,7 +105,7 @@ void FilesContainerW::Open(FileWriter::Op op) } default: - ASSERT ( false, ("Unsupperted options") ); + ASSERT ( false, ("Unsupported options") ); break; } diff --git a/data/packed_polygons.bin b/data/packed_polygons.bin new file mode 100644 index 0000000000..1795a7dc77 Binary files /dev/null and b/data/packed_polygons.bin differ diff --git a/generator/borders_loader.cpp b/generator/borders_loader.cpp index a79cbd8796..d2ecb45806 100644 --- a/generator/borders_loader.cpp +++ b/generator/borders_loader.cpp @@ -1,70 +1,171 @@ #include "borders_loader.hpp" #include "borders_generator.hpp" +#include "../storage/country_polygon.hpp" + +#include "../indexer/geometry_serialization.hpp" +#include "../indexer/scales.hpp" + +#include "../geometry/simplification.hpp" +#include "../geometry/distance.hpp" + +#include "../coding/file_container.hpp" +#include "../coding/read_write_utils.hpp" + #include "../base/logging.hpp" #include "../base/string_utils.hpp" #include "../std/fstream.hpp" #include "../std/vector.hpp" +#include "../std/bind.hpp" + #define BORDERS_DIR "borders/" #define BORDERS_EXTENSION ".borders" #define POLYGONS_FILE "polygons.lst" + namespace borders { class PolygonLoader { - string m_baseDir; - CountryPolygons & m_polygons; - m2::RectD & m_rect; + CountryPolygons m_polygons; + m2::RectD m_rect; + + string const & m_baseDir; + CountriesContainerT & m_countries; public: - PolygonLoader(string const & basePolygonsDir, CountryPolygons & polygons, m2::RectD & rect) - : m_baseDir(basePolygonsDir), m_polygons(polygons), m_rect(rect) - { - } + PolygonLoader(string const & baseDir, CountriesContainerT & countries) + : m_baseDir(baseDir), m_countries(countries) {} - void operator()(string const & name) + void operator() (string const & name) { if (m_polygons.m_name.empty()) m_polygons.m_name = name; - vector coutryBorders; - if (osm::LoadBorders(m_baseDir + BORDERS_DIR + name + BORDERS_EXTENSION, coutryBorders)) + vector borders; + if (osm::LoadBorders(m_baseDir + BORDERS_DIR + name + BORDERS_EXTENSION, borders)) { - for (size_t i = 0; i < coutryBorders.size(); ++i) + for (size_t i = 0; i < borders.size(); ++i) { - m2::RectD const rect(coutryBorders[i].GetRect()); + m2::RectD const rect(borders[i].GetRect()); m_rect.Add(rect); - m_polygons.m_regions.Add(coutryBorders[i], rect); + m_polygons.m_regions.Add(borders[i], rect); } } } + + void Finish() + { + if (!m_polygons.IsEmpty()) + { + ASSERT_NOT_EQUAL ( m_rect, m2::RectD::GetEmptyRect(), () ); + m_countries.Add(m_polygons, m_rect); + } + + m_polygons.Clear(); + m_rect.MakeEmpty(); + } }; - bool LoadCountriesList(string const & baseDir, CountriesContainerT & countries) + template + void ForEachCountry(string const & baseDir, ToDo & toDo) { - countries.Clear(); ifstream stream((baseDir + POLYGONS_FILE).c_str()); string line; - LOG(LINFO, ("Loading countries.")); + while (stream.good()) { std::getline(stream, line); if (line.empty()) continue; - CountryPolygons country; - m2::RectD rect; - - PolygonLoader loader(baseDir, country, rect); - strings::Tokenize(line, "|", loader); - if (!country.m_regions.IsEmpty()) - countries.Add(country, rect); + strings::Tokenize(line, "|", bind(ref(toDo), _1)); + toDo.Finish(); } + } + + bool LoadCountriesList(string const & baseDir, CountriesContainerT & countries) + { + countries.Clear(); + + LOG(LINFO, ("Loading countries.")); + + PolygonLoader loader(baseDir, countries); + ForEachCountry(baseDir, loader); + LOG(LINFO, ("Countries loaded:", countries.GetSize())); + return !countries.IsEmpty(); } + class PackedBordersGenerator + { + FilesContainerW m_writer; + string const & m_baseDir; + + vector m_polys; + + public: + PackedBordersGenerator(string const & baseDir) + : m_writer(baseDir + PACKED_POLYGONS_FILE), m_baseDir(baseDir) + { + } + + void operator() (string const & name) + { + vector borders; + if (osm::LoadBorders(m_baseDir + BORDERS_DIR + name + BORDERS_EXTENSION, borders)) + { + // use index in vector as tag + FileWriter w = m_writer.GetWriter(strings::to_string(m_polys.size())); + serial::CodingParams cp; + + uint32_t const count = static_cast(borders.size()); + + // calc rect + m2::RectD rect; + for (uint32_t i = 0; i < count; ++i) + rect.Add(borders[i].GetRect()); + + // store polygon info + m_polys.push_back(storage::CountryDef(name, rect)); + + // write polygons as paths + WriteVarUint(w, count); + for (uint32_t i = 0; i < count; ++i) + { + typedef vector VectorT; + typedef mn::DistanceToLineSquare DistanceT; + + VectorT const & in = borders[i].Data(); + VectorT out; + + /// @todo Choose scale level for simplification. + double const eps = my::sq(scales::GetEpsilonForSimplify(10)); + DistanceT dist; + SimplifyNearOptimal(20, in.begin(), in.end(), eps, dist, + AccumulateSkipSmallTrg(dist, out, eps)); + + serial::SaveOuterPath(out, cp, w); + } + } + } + + void Finish() {} + + void WritePolygonsInfo() + { + FileWriter w = m_writer.GetWriter(PACKED_POLYGONS_INFO_TAG); + rw::Write(w, m_polys); + } + }; + + void GeneratePackedBorders(string const & baseDir) + { + PackedBordersGenerator generator(baseDir); + ForEachCountry(baseDir, generator); + generator.WritePolygonsInfo(); + } } diff --git a/generator/borders_loader.hpp b/generator/borders_loader.hpp index e27554b996..d581171540 100644 --- a/generator/borders_loader.hpp +++ b/generator/borders_loader.hpp @@ -14,6 +14,14 @@ namespace borders { CountryPolygons(string const & name = "") : m_name(name), m_index(-1) {} + bool IsEmpty() const { return m_regions.IsEmpty(); } + void Clear() + { + m_regions.Clear(); + m_name.clear(); + m_index = -1; + } + RegionsContainerT m_regions; string m_name; mutable int m_index; @@ -22,4 +30,6 @@ namespace borders typedef m4::Tree CountriesContainerT; bool LoadCountriesList(string const & baseDir, CountriesContainerT & countries); + + void GeneratePackedBorders(string const & baseDir); } diff --git a/generator/generator_tool/generator_tool.cpp b/generator/generator_tool/generator_tool.cpp index 99e5c60984..e6f9b35e01 100644 --- a/generator/generator_tool/generator_tool.cpp +++ b/generator/generator_tool/generator_tool.cpp @@ -3,6 +3,7 @@ #include "../feature_sorter.hpp" #include "../update_generator.hpp" #include "../borders_generator.hpp" +#include "../borders_loader.hpp" #include "../classif_routine.hpp" #include "../dumper.hpp" #include "../statistics.hpp" @@ -56,6 +57,7 @@ DEFINE_string(generate_borders, "", DEFINE_bool(dump_types, false, "If defined, prints all types combinations and their total count"); DEFINE_bool(dump_prefixes, false, "If defined, prints statistics on feature name prefixes"); DEFINE_bool(unpack_mwm, false, "Unpack each section of mwm into a separate file with name filePath.sectionName."); +DEFINE_bool(generate_packed_borders, false, "Generate packed file with country polygons"); string AddSlashIfNeeded(string const & str) { @@ -237,9 +239,10 @@ int main(int argc, char ** argv) feature::DumpPrefixes(path + FLAGS_output + ".mwm"); if (FLAGS_unpack_mwm) - { UnpackMwm(path + FLAGS_output + ".mwm"); - } + + if (FLAGS_generate_packed_borders) + borders::GeneratePackedBorders(path); return 0; } diff --git a/geometry/region2d.hpp b/geometry/region2d.hpp index a6cb66855c..1024e8dca8 100644 --- a/geometry/region2d.hpp +++ b/geometry/region2d.hpp @@ -127,6 +127,8 @@ namespace m2 std::swap(m_rect, rhs.m_rect); } + ContainerT Data() const { return m_points; } + public: /// Taken from Computational Geometry in C and modified template diff --git a/storage/country_info.cpp b/storage/country_info.cpp new file mode 100644 index 0000000000..a73d3b3968 --- /dev/null +++ b/storage/country_info.cpp @@ -0,0 +1,63 @@ +#include "country_info.hpp" + +#include "../indexer/geometry_serialization.hpp" + +#include "../geometry/region2d.hpp" + +#include "../coding/read_write_utils.hpp" + +#include "../base/string_utils.hpp" + + +namespace storage +{ + CountryInfoGetter::CountryInfoGetter(ModelReaderPtr reader) + : m_reader(reader) + { + ReaderSource src(m_reader.GetReader(PACKED_POLYGONS_INFO_TAG)); + rw::Read(src, m_countries); + } + + template + void CountryInfoGetter::ForEachCountry(m2::PointD const & pt, ToDo & toDo) + { + for (size_t i = 0; i < m_countries.size(); ++i) + if (m_countries[i].m_rect.IsPointInside(pt)) + if (!toDo(i)) + return; + } + + bool CountryInfoGetter::GetByPoint::operator() (size_t id) + { + ReaderSource src(m_info.m_reader.GetReader(strings::to_string(id))); + + uint32_t const count = ReadVarUint(src); + for (size_t i = 0; i < count; ++i) + { + vector points; + serial::LoadOuterPath(src, serial::CodingParams(), points); + + m2::RegionD rgn(points.begin(), points.end()); + if (rgn.Contains(m_pt)) + { + m_res = id; + return false; + } + } + + return true; + } + + string CountryInfoGetter::GetRegionName(m2::PointD const & pt) + { + GetByPoint doGet(*this, pt); + ForEachCountry(pt, doGet); + + if (doGet.m_res != -1) + { + return m_countries[doGet.m_res].m_name; + } + + return string(); + } +} diff --git a/storage/country_info.hpp b/storage/country_info.hpp new file mode 100644 index 0000000000..60151aa566 --- /dev/null +++ b/storage/country_info.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "country_polygon.hpp" + +#include "../coding/file_container.hpp" + + +namespace storage +{ + class CountryInfoGetter + { + FilesContainerR m_reader; + vector m_countries; + + template + void ForEachCountry(m2::PointD const & pt, ToDo & toDo); + + class GetByPoint + { + CountryInfoGetter const & m_info; + m2::PointD const & m_pt; + + public: + size_t m_res; + + GetByPoint(CountryInfoGetter & info, m2::PointD const & pt) + : m_info(info), m_pt(pt), m_res(-1) {} + bool operator() (size_t id); + }; + + public: + CountryInfoGetter(ModelReaderPtr reader); + + string GetRegionName(m2::PointD const & pt); + }; +} diff --git a/storage/country_polygon.hpp b/storage/country_polygon.hpp new file mode 100644 index 0000000000..813f31c242 --- /dev/null +++ b/storage/country_polygon.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "../indexer/point_to_int64.hpp" +#include "../indexer/coding_params.hpp" + +#include "../geometry/rect2d.hpp" + +#include "../coding/read_write_utils.hpp" + + +#define PACKED_POLYGONS_FILE "packed_polygons.bin" +#define PACKED_POLYGONS_INFO_TAG "info" + +namespace storage +{ + struct CountryDef + { + string m_name; + m2::RectD m_rect; + + CountryDef() {} + CountryDef(string const & name, m2::RectD const & r) + : m_name(name), m_rect(r) + { + } + }; + + template void Read(TSource & src, CountryDef & p) + { + rw::Read(src, p.m_name); + + pair r; + r.first = ReadVarInt(src); + r.second = ReadVarInt(src); + p.m_rect = Int64ToRect(r, serial::CodingParams().GetCoordBits()); + } + + template void Write(TSink & sink, CountryDef const & p) + { + rw::Write(sink, p.m_name); + + pair const r = RectToInt64(p.m_rect, serial::CodingParams().GetCoordBits()); + WriteVarInt(sink, r.first); + WriteVarInt(sink, r.second); + } +} diff --git a/storage/storage.pro b/storage/storage.pro index e6d9bc07d8..3d86ad1246 100644 --- a/storage/storage.pro +++ b/storage/storage.pro @@ -16,7 +16,10 @@ HEADERS += \ country.hpp \ simple_tree.hpp \ storage.hpp \ + country_polygon.hpp \ + country_info.hpp \ SOURCES += \ country.cpp \ storage.cpp \ + country_info.cpp \ diff --git a/storage/storage_tests/country_info_test.cpp b/storage/storage_tests/country_info_test.cpp new file mode 100644 index 0000000000..8bd10dc497 --- /dev/null +++ b/storage/storage_tests/country_info_test.cpp @@ -0,0 +1,18 @@ +#include "../../testing/testing.hpp" + +#include "../country_info.hpp" + +#include "../../indexer/mercator.hpp" + +#include "../../platform/platform.hpp" + + +UNIT_TEST(CountryInfo_GetByPoint_Smoke) +{ + storage::CountryInfoGetter getter(GetPlatform().GetReader(PACKED_POLYGONS_FILE)); + + // Minsk + TEST_EQUAL(getter.GetRegionName( + m2::PointD(MercatorBounds::LonToX(27.5618818), + MercatorBounds::LatToY(53.9022651))), "Belarus", ()); +} diff --git a/storage/storage_tests/storage_tests.pro b/storage/storage_tests/storage_tests.pro index 11426321b3..a489096f65 100644 --- a/storage/storage_tests/storage_tests.pro +++ b/storage/storage_tests/storage_tests.pro @@ -6,7 +6,7 @@ CONFIG -= app_bundle TEMPLATE = app ROOT_DIR = ../.. -DEPENDENCIES = storage platform coding base jansson tomcrypt +DEPENDENCIES = storage indexer platform coding base jansson tomcrypt include($$ROOT_DIR/common.pri) @@ -23,3 +23,4 @@ SOURCES += \ ../../testing/testingmain.cpp \ country_test.cpp \ simple_tree_test.cpp \ + country_info_test.cpp \