[geocoder] Add borders class to support region borders in reverse serverside geocoder

This commit is contained in:
tatiana-kondakova 2018-04-12 19:04:35 +03:00 committed by mpimenov
parent 4a3b58ed33
commit 50d45e1f91
7 changed files with 307 additions and 0 deletions

View file

@ -51,6 +51,7 @@
#define LOCALITY_DATA_FILE_TAG "locdata"
#define LOCALITY_INDEX_FILE_TAG "locidx"
#define BORDERS_FILE_TAG "borders"
#define READY_FILE_EXTENSION ".ready"
#define RESUME_FILE_EXTENSION ".resume"

View file

@ -6,6 +6,8 @@ set(
SRC
altitude_loader.cpp
altitude_loader.hpp
borders.cpp
borders.hpp
categories_holder.cpp
categories_holder.hpp
categories_holder_loader.cpp

109
indexer/borders.cpp Normal file
View file

@ -0,0 +1,109 @@
#include "indexer/borders.hpp"
#include "coding/file_container.hpp"
#include "coding/geometry_coding.hpp"
#include "coding/var_record_reader.hpp"
#include "base/logging.hpp"
#include "base/macros.hpp"
#include "defines.hpp"
using namespace std;
namespace
{
template <class Reader>
class BordersVector
{
public:
BordersVector(Reader const & reader) : m_recordReader(reader, 256 /* expectedRecordSize */) {}
template <class ToDo>
void ForEach(ToDo && toDo) const
{
m_recordReader.ForEachRecord([&](uint32_t pos, char const * data, uint32_t /*size*/) {
ArrayByteSource src(data);
serial::GeometryCodingParams cp = {};
uint64_t id;
ReadPrimitiveFromSource(src, id);
size_t size;
ReadPrimitiveFromSource(src, size);
size_t outerSize;
ReadPrimitiveFromSource(src, outerSize);
vector<m2::PointD> outer(outerSize);
// todo:(@t.yan) consider delta coding techniques.
for (auto & point : outer)
point = serial::LoadPoint(src, cp);
vector<vector<m2::PointD>> inners(size);
for (auto & inner : inners)
{
size_t innerSize;
ReadPrimitiveFromSource(src, innerSize);
inner = vector<m2::PointD>(innerSize);
for (auto & point : inner)
point = serial::LoadPoint(src, cp);
}
toDo(id, outer, inners);
});
}
private:
friend class BordersVectorReader;
VarRecordReader<FilesContainerR::TReader, &VarRecordSizeReaderVarint> m_recordReader;
DISALLOW_COPY(BordersVector);
};
class BordersVectorReader
{
public:
explicit BordersVectorReader(string const & filePath)
: m_cont(filePath), m_vector(m_cont.GetReader(BORDERS_FILE_TAG))
{
}
BordersVector<ModelReaderPtr> const & GetVector() const { return m_vector; }
private:
FilesContainerR m_cont;
BordersVector<ModelReaderPtr> m_vector;
DISALLOW_COPY(BordersVectorReader);
};
} // namespace
namespace indexer
{
bool Borders::Border::IsPointInside(m2::PointD const & point) const
{
if (!m_outer.Contains(point))
return false;
for (auto const & inner : m_inners)
{
if (inner.Contains(point))
return false;
}
return true;
}
bool Borders::Deserialize(string const & filename)
{
try
{
BordersVectorReader reader(filename);
auto const & records = reader.GetVector();
DeserializeFromVec(records);
}
catch (Reader::Exception const & e)
{
LOG(LERROR, ("Error while reading file:", e.Msg()));
return false;
}
return true;
}
} // namespace indexer

62
indexer/borders.hpp Normal file
View file

@ -0,0 +1,62 @@
#pragma once
#include "geometry/point2d.hpp"
#include "geometry/region2d.hpp"
#include <cstdint>
#include <map>
#include <string>
#include <utility>
#include <vector>
namespace indexer
{
// Stores region borders for countries, states, cities, city districts in reverse geocoder.
// Used to check exact object region after getting regions short list from regions geo index.
// Each region may have several outer borders. It may be islands or separated parts like
// Kaliningrad region which is part of Russia or Alaska which is part of US.
// Each outer border may have several inner borders e.g. Vatican and San Marino are
// located inside Italy but are not parts of it.
class Borders
{
public:
bool IsPointInside(uint64_t id, m2::PointD const & point) const
{
auto const range = m_borders.equal_range(id);
for (auto it = range.first; it != range.second; ++it)
{
if (it->second.IsPointInside(point))
return true;
}
return false;
}
bool Deserialize(std::string const & filename);
template <typename BordersVec>
void DeserializeFromVec(BordersVec const & vec)
{
vec.ForEach([this](uint64_t id, std::vector<m2::PointD> const & outer,
std::vector<std::vector<m2::PointD>> const & inners) {
auto it = m_borders.insert(std::make_pair(id, Border()));
it->second.m_outer = m2::RegionD(outer);
for (auto const & inner : inners)
it->second.m_inners.push_back(m2::RegionD(inner));
});
}
private:
struct Border
{
Border() = default;
bool IsPointInside(m2::PointD const & point) const;
m2::RegionD m_outer;
std::vector<m2::RegionD> m_inners;
};
std::multimap<uint64_t, Border> m_borders;
};
} // namespace indexer

View file

@ -2,6 +2,7 @@ project(indexer_tests)
set(
SRC
borders_test.cpp
bounds.hpp
categories_test.cpp
cell_coverer_test.cpp

View file

@ -0,0 +1,120 @@
#include "testing/testing.hpp"
#include "indexer/borders.hpp"
#include "geometry/point2d.hpp"
#include <cstdint>
#include <vector>
using namespace indexer;
using namespace std;
namespace
{
struct BordersVector
{
struct Border
{
uint64_t m_id;
vector<m2::PointD> m_outer;
vector<vector<m2::PointD>> m_inners;
};
template <typename ToDo>
void ForEach(ToDo && toDo) const
{
for (auto const & border : m_borders)
toDo(border.m_id, border.m_outer, border.m_inners);
}
vector<Border> m_borders;
};
UNIT_TEST(BordersTest)
{
{
BordersVector vec;
vec.m_borders.resize(1);
vec.m_borders[0].m_id = 0;
vec.m_borders[0].m_outer = {m2::PointD{0, 0}, m2::PointD{1, 0}, m2::PointD{1, 1}, m2::PointD{0, 1}};
indexer::Borders borders;
borders.DeserializeFromVec(vec);
TEST(borders.IsPointInside(0, m2::PointD{0.5, 0.5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{-0.5, 0.5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{-0.5, -0.5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{-0.5, -0.5}), ());
}
{
BordersVector vec;
vec.m_borders.resize(2);
vec.m_borders[0].m_id = 0;
vec.m_borders[0].m_outer = {m2::PointD{0, 0}, m2::PointD{1, 0}, m2::PointD{1, 1}, m2::PointD{0, 1}};
vec.m_borders[1].m_id = 0;
vec.m_borders[1].m_outer = {m2::PointD{2, 2}, m2::PointD{3, 2}, m2::PointD{3, 3}, m2::PointD{2, 3}};
indexer::Borders borders;
borders.DeserializeFromVec(vec);
TEST(borders.IsPointInside(0, m2::PointD{0.5, 0.5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{0.5, 2.5}), ());
TEST(borders.IsPointInside(0, m2::PointD{2.5, 2.5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{2.5, 0.5}), ());
}
{
BordersVector vec;
vec.m_borders.resize(2);
vec.m_borders[0].m_id = 0;
vec.m_borders[0].m_outer = {m2::PointD{0, 0}, m2::PointD{1, 0}, m2::PointD{1, 1}, m2::PointD{0, 1}};
vec.m_borders[1].m_id = 1;
vec.m_borders[1].m_outer = {m2::PointD{2, 2}, m2::PointD{3, 2}, m2::PointD{3, 3}, m2::PointD{2, 3}};
indexer::Borders borders;
borders.DeserializeFromVec(vec);
TEST(borders.IsPointInside(0, m2::PointD{0.5, 0.5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{2.5, 2.5}), ());
TEST(borders.IsPointInside(1, m2::PointD{2.5, 2.5}), ());
TEST(!borders.IsPointInside(1, m2::PointD{0.5, 0.5}), ());
}
{
BordersVector vec;
vec.m_borders.resize(1);
vec.m_borders[0].m_id = 0;
vec.m_borders[0].m_outer = {m2::PointD{0, 0}, m2::PointD{10, 0}, m2::PointD{10, 10}, m2::PointD{0, 10}};
vec.m_borders[0].m_inners = {{m2::PointD{2, 2}, m2::PointD{8, 2}, m2::PointD{8, 8}, m2::PointD{2, 8}}};
indexer::Borders borders;
borders.DeserializeFromVec(vec);
TEST(borders.IsPointInside(0, m2::PointD{1, 1}), ());
TEST(borders.IsPointInside(0, m2::PointD{9, 9}), ());
TEST(!borders.IsPointInside(0, m2::PointD{3, 7}), ());
TEST(!borders.IsPointInside(0, m2::PointD{5, 5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{6, 6}), ());
TEST(!borders.IsPointInside(0, m2::PointD{7, 3}), ());
TEST(!borders.IsPointInside(0, m2::PointD{7, 7}), ());
}
{
BordersVector vec;
vec.m_borders.resize(1);
vec.m_borders[0].m_id = 0;
vec.m_borders[0].m_outer = {m2::PointD{0, 0}, m2::PointD{10, 0}, m2::PointD{10, 10}, m2::PointD{0, 10}};
vec.m_borders[0].m_inners = {{m2::PointD{2, 2}, m2::PointD{5, 2}, m2::PointD{5, 5}, m2::PointD{2, 5}},
{m2::PointD{5, 5}, m2::PointD{8, 5}, m2::PointD{8, 8}, m2::PointD{5, 8}}};
indexer::Borders borders;
borders.DeserializeFromVec(vec);
TEST(borders.IsPointInside(0, m2::PointD{1, 1}), ());
TEST(borders.IsPointInside(0, m2::PointD{9, 9}), ());
TEST(borders.IsPointInside(0, m2::PointD{3, 7}), ());
TEST(borders.IsPointInside(0, m2::PointD{7, 3}), ());
TEST(!borders.IsPointInside(0, m2::PointD{5, 5}), ());
TEST(!borders.IsPointInside(0, m2::PointD{6, 6}), ());
TEST(!borders.IsPointInside(0, m2::PointD{7, 7}), ());
}
}
} // namespace

View file

@ -76,6 +76,9 @@
40009064201F5CB000963E18 /* locality_index_builder.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4000905F201F5CAF00963E18 /* locality_index_builder.hpp */; };
40009065201F5CB000963E18 /* locality_object.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 40009060201F5CB000963E18 /* locality_object.hpp */; };
40009066201F5CB000963E18 /* locality_index_builder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 40009061201F5CB000963E18 /* locality_index_builder.cpp */; };
406B37F4207FBABB000F3648 /* borders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 406B37F2207FBABA000F3648 /* borders.cpp */; };
406B37F5207FBABB000F3648 /* borders.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 406B37F3207FBABA000F3648 /* borders.hpp */; };
406B37F7207FBAC7000F3648 /* borders_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 406B37F6207FBAC7000F3648 /* borders_test.cpp */; };
4095DEB22020AC0000C591A3 /* locality_index.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4095DEB12020AC0000C591A3 /* locality_index.hpp */; };
4099F6491FC7142A002A7B05 /* fake_feature_ids.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4099F6471FC71429002A7B05 /* fake_feature_ids.cpp */; };
4099F64A1FC7142A002A7B05 /* fake_feature_ids.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4099F6481FC7142A002A7B05 /* fake_feature_ids.hpp */; };
@ -305,6 +308,9 @@
4000905F201F5CAF00963E18 /* locality_index_builder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = locality_index_builder.hpp; sourceTree = "<group>"; };
40009060201F5CB000963E18 /* locality_object.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = locality_object.hpp; sourceTree = "<group>"; };
40009061201F5CB000963E18 /* locality_index_builder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = locality_index_builder.cpp; sourceTree = "<group>"; };
406B37F2207FBABA000F3648 /* borders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = borders.cpp; sourceTree = "<group>"; };
406B37F3207FBABA000F3648 /* borders.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = borders.hpp; sourceTree = "<group>"; };
406B37F6207FBAC7000F3648 /* borders_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = borders_test.cpp; sourceTree = "<group>"; };
4095DEB12020AC0000C591A3 /* locality_index.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = locality_index.hpp; sourceTree = "<group>"; };
4099F6471FC71429002A7B05 /* fake_feature_ids.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fake_feature_ids.cpp; sourceTree = "<group>"; };
4099F6481FC7142A002A7B05 /* fake_feature_ids.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = fake_feature_ids.hpp; sourceTree = "<group>"; };
@ -596,6 +602,7 @@
670C60F81AB0657700C38A8C /* indexer_tests */ = {
isa = PBXGroup;
children = (
406B37F6207FBAC7000F3648 /* borders_test.cpp */,
39F376C1207D324E0058E8E0 /* scale_index_reading_tests.cpp */,
39F376BE207D32410058E8E0 /* cities_boundaries_serdes_tests.cpp */,
40C3C090205BF9F400CED188 /* bounds.hpp */,
@ -666,6 +673,8 @@
6753409C1A3F53CB00A0A8C3 /* indexer */ = {
isa = PBXGroup;
children = (
406B37F2207FBABA000F3648 /* borders.cpp */,
406B37F3207FBABA000F3648 /* borders.hpp */,
4095DEB12020AC0000C591A3 /* locality_index.hpp */,
4000905D201F5CAF00963E18 /* cell_value_pair.hpp */,
40009061201F5CB000963E18 /* locality_index_builder.cpp */,
@ -845,6 +854,7 @@
347F337F1C454242009758CC /* trie_builder.hpp in Headers */,
34664CF71D49FEC1003D7096 /* centers_table.hpp in Headers */,
6753414C1A3F540F00A0A8C3 /* tree_structure.hpp in Headers */,
406B37F5207FBABB000F3648 /* borders.hpp in Headers */,
F6DF5F311CD0FD9A00A87154 /* categories_index.hpp in Headers */,
347F337D1C454242009758CC /* succinct_trie_builder.hpp in Headers */,
675341381A3F540F00A0A8C3 /* mwm_set.hpp in Headers */,
@ -1085,6 +1095,7 @@
6753411A1A3F540F00A0A8C3 /* feature_impl.cpp in Sources */,
56C74C1C1C749E4700B71B9F /* categories_holder_loader.cpp in Sources */,
3D74EF241F8F559D0081202C /* ugc_types_test.cpp in Sources */,
406B37F4207FBABB000F3648 /* borders.cpp in Sources */,
40A1C30A202B321000F71672 /* locality_index_test.cpp in Sources */,
6753410D1A3F540F00A0A8C3 /* drawing_rules.cpp in Sources */,
675341301A3F540F00A0A8C3 /* index.cpp in Sources */,
@ -1135,6 +1146,7 @@
6758AED11BB4413000C26E27 /* drules_selector_parser.cpp in Sources */,
E906DE3B1CF44934004C4F5E /* postcodes_matcher.cpp in Sources */,
3D489BF31D4F87740052AA38 /* osm_editor_test.cpp in Sources */,
406B37F7207FBAC7000F3648 /* borders_test.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};