Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--defines.hpp1
-rw-r--r--generator/CMakeLists.txt2
-rw-r--r--generator/booking_quality_check/booking_quality_check.cpp5
-rw-r--r--generator/cities_boundaries_builder.cpp106
-rw-r--r--generator/cities_boundaries_builder.hpp18
-rw-r--r--generator/generate_info.hpp9
-rw-r--r--generator/generator.pro2
-rw-r--r--generator/generator_tool/generator_tool.cpp20
-rw-r--r--generator/osm_id.hpp7
-rw-r--r--generator/osm_source.cpp52
-rw-r--r--generator/osm_source.hpp4
-rw-r--r--generator/osm_translator.hpp18
-rw-r--r--generator/routing_helpers.cpp26
-rw-r--r--generator/utils.hpp30
-rw-r--r--geometry/diamond_box.hpp2
-rw-r--r--indexer/cities_boundaries_serdes.hpp182
-rw-r--r--indexer/indexer_tests/cities_boundaries_serdes_tests.cpp66
17 files changed, 462 insertions, 88 deletions
diff --git a/defines.hpp b/defines.hpp
index 99a4d69207..a982e36870 100644
--- a/defines.hpp
+++ b/defines.hpp
@@ -24,6 +24,7 @@
#define INDEX_FILE_TAG "idx"
#define SEARCH_INDEX_FILE_TAG "sdx"
#define SEARCH_ADDRESS_FILE_TAG "addr"
+#define CITIES_BOUNDARIES_FILE_TAG "cities_boundaries"
#define HEADER_FILE_TAG "header"
#define VERSION_FILE_TAG "version"
#define METADATA_FILE_TAG "meta"
diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt
index 5815608c6b..184b125201 100644
--- a/generator/CMakeLists.txt
+++ b/generator/CMakeLists.txt
@@ -20,6 +20,8 @@ set(SRC
centers_table_builder.hpp
check_model.cpp
check_model.hpp
+ cities_boundaries_builder.cpp
+ cities_boundaries_builder.hpp
coastlines_generator.cpp
coastlines_generator.hpp
dumper.cpp
diff --git a/generator/booking_quality_check/booking_quality_check.cpp b/generator/booking_quality_check/booking_quality_check.cpp
index c12cefcb5b..1621179460 100644
--- a/generator/booking_quality_check/booking_quality_check.cpp
+++ b/generator/booking_quality_check/booking_quality_check.cpp
@@ -117,6 +117,11 @@ public:
m_features.emplace(fb.GetMostGenericOsmId(), fb);
}
+ void EmitCityBoundary(FeatureBuilder1 const & /* fb */,
+ FeatureParams const & /* params */) override
+ {
+ }
+
void GetNames(vector<string> & names) const override
{
names.clear();
diff --git a/generator/cities_boundaries_builder.cpp b/generator/cities_boundaries_builder.cpp
new file mode 100644
index 0000000000..c59cf3cb46
--- /dev/null
+++ b/generator/cities_boundaries_builder.cpp
@@ -0,0 +1,106 @@
+#include "generator/cities_boundaries_builder.hpp"
+
+#include "generator/utils.hpp"
+
+#include "search/categories_cache.hpp"
+#include "search/cbv.hpp"
+#include "search/mwm_context.hpp"
+
+#include "indexer/cities_boundaries_serdes.hpp"
+#include "indexer/city_boundary.hpp"
+#include "indexer/classificator.hpp"
+#include "indexer/index.hpp"
+#include "indexer/mwm_set.hpp"
+
+#include "platform/local_country_file.hpp"
+
+#include "coding/compressed_bit_vector.hpp"
+
+#include "base/assert.hpp"
+#include "base/checked_cast.hpp"
+
+#include <map>
+#include <vector>
+
+#include "defines.hpp"
+
+using namespace indexer;
+using namespace search;
+using namespace std;
+
+namespace generator
+{
+namespace
+{
+bool ParseFeatureIdToOsmIdMapping(string const & path, map<uint32_t, vector<osm::Id>> & mapping)
+{
+ return ForEachOsmId2FeatureId(path, [&](osm::Id const & osmId, uint32_t const featureId) {
+ mapping[featureId].push_back(osmId);
+ });
+}
+
+struct LocalitiesSource
+{
+ LocalitiesSource()
+ {
+ auto & c = classif();
+ m_city = c.GetTypeByPath({"place", "city"});
+ m_town = c.GetTypeByPath({"place", "town"});
+ }
+
+ template <typename Fn>
+ void ForEachType(Fn && fn) const
+ {
+ fn(m_city);
+ fn(m_town);
+ }
+
+ uint32_t m_city = 0;
+ uint32_t m_town = 0;
+};
+
+CBV GetLocalities(string const & dataPath)
+{
+ Index index;
+ auto const result = index.Register(platform::LocalCountryFile::MakeTemporary(dataPath));
+ CHECK_EQUAL(result.second, MwmSet::RegResult::Success, ("Can't register", dataPath));
+
+ search::MwmContext context(index.GetMwmHandleById(result.first));
+ return search::CategoriesCache(LocalitiesSource{}, my::Cancellable{}).Get(context);
+}
+} // namespace
+
+bool BuildCitiesBoundaries(string const & dataPath, string const & osmToFeaturePath,
+ OsmIdToBoundariesTable & table)
+{
+ auto const localities = GetLocalities(dataPath);
+
+ map<uint32_t, vector<osm::Id>> mapping;
+ if (!ParseFeatureIdToOsmIdMapping(dataPath + OSM2FEATURE_FILE_EXTENSION, mapping))
+ return false;
+
+ vector<vector<CityBoundary>> all;
+
+ localities.ForEach([&](uint64_t fid) {
+ vector<CityBoundary> bs;
+
+ auto it = mapping.find(base::asserted_cast<uint32_t>(fid));
+ if (it != mapping.end())
+ {
+ for (auto const & osmId : it->second)
+ {
+ auto const & b = table.Get(osmId);
+ bs.insert(bs.end(), b.begin(), b.end());
+ }
+ }
+
+ all.emplace_back(move(bs));
+ });
+
+ FilesContainerW container(dataPath, FileWriter::OP_WRITE_EXISTING);
+ FileWriter sink = container.GetWriter(CITIES_BOUNDARIES_FILE_TAG);
+ indexer::CitiesBoundariesSerDes::Serialize(sink, all);
+
+ return true;
+}
+} // namespace generator
diff --git a/generator/cities_boundaries_builder.hpp b/generator/cities_boundaries_builder.hpp
new file mode 100644
index 0000000000..581d2faaca
--- /dev/null
+++ b/generator/cities_boundaries_builder.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "generator/osm_id.hpp"
+
+#include "indexer/city_boundary.hpp"
+
+#include "base/clustering_map.hpp"
+
+#include <string>
+
+namespace generator
+{
+using OsmIdToBoundariesTable =
+ base::ClusteringMap<osm::Id, indexer::CityBoundary, osm::HashId>;
+
+bool BuildCitiesBoundaries(std::string const & dataPath, std::string const & osmToFeaturePath,
+ OsmIdToBoundariesTable & table);
+} // namespace generator
diff --git a/generator/generate_info.hpp b/generator/generate_info.hpp
index 0de215dee9..ed53c53e37 100644
--- a/generator/generate_info.hpp
+++ b/generator/generate_info.hpp
@@ -1,11 +1,14 @@
#pragma once
-#include "defines.hpp"
+#include "generator/cities_boundaries_builder.hpp"
+
+#include "coding/file_name_utils.hpp"
#include "base/logging.hpp"
-#include "coding/file_name_utils.hpp"
+#include "defines.hpp"
+#include <memory>
#include <string>
#include <vector>
@@ -46,6 +49,8 @@ struct GenerateInfo
std::string m_opentableReferenceDir;
std::string m_viatorDatafileName;
+ std::shared_ptr<generator::OsmIdToBoundariesTable> m_boundariesTable;
+
uint32_t m_versionDate = 0;
std::vector<std::string> m_bucketNames;
diff --git a/generator/generator.pro b/generator/generator.pro
index d115e083e6..7aa16a251a 100644
--- a/generator/generator.pro
+++ b/generator/generator.pro
@@ -22,6 +22,7 @@ SOURCES += \
borders_loader.cpp \
centers_table_builder.cpp \
check_model.cpp \
+ cities_boundaries_builder.cpp \
coastlines_generator.cpp \
dumper.cpp \
feature_builder.cpp \
@@ -65,6 +66,7 @@ HEADERS += \
borders_loader.hpp \
centers_table_builder.hpp \
check_model.hpp \
+ cities_boundaries_builder.hpp \
coastlines_generator.hpp \
dumper.hpp \
feature_builder.hpp \
diff --git a/generator/generator_tool/generator_tool.cpp b/generator/generator_tool/generator_tool.cpp
index b672da6415..70a3bbc81d 100644
--- a/generator/generator_tool/generator_tool.cpp
+++ b/generator/generator_tool/generator_tool.cpp
@@ -3,6 +3,7 @@
#include "generator/borders_loader.hpp"
#include "generator/centers_table_builder.hpp"
#include "generator/check_model.hpp"
+#include "generator/cities_boundaries_builder.hpp"
#include "generator/dumper.hpp"
#include "generator/feature_generator.hpp"
#include "generator/feature_sorter.hpp"
@@ -72,6 +73,7 @@ DEFINE_bool(generate_geometry, false,
"3rd pass - split and simplify geometry and triangles for features.");
DEFINE_bool(generate_index, false, "4rd pass - generate index.");
DEFINE_bool(generate_search_index, false, "5th pass - generate search index.");
+DEFINE_bool(generate_cities_boundaries, false, "Generate section with cities boundaries");
DEFINE_bool(generate_world, false, "Generate separate world file.");
DEFINE_bool(split_by_polygons, false,
"Use countries borders to split planet by regions and countries.");
@@ -153,6 +155,9 @@ int main(int argc, char ** argv)
genInfo.m_opentableReferenceDir = FLAGS_opentable_reference_path;
genInfo.m_viatorDatafileName = FLAGS_viator_data;
+ if (FLAGS_generate_cities_boundaries)
+ genInfo.m_boundariesTable = make_shared<generator::OsmIdToBoundariesTable>();
+
genInfo.m_versionDate = static_cast<uint32_t>(FLAGS_planet_version);
if (!FLAGS_node_storage.empty())
@@ -175,8 +180,8 @@ int main(int argc, char ** argv)
// Load classificator only when necessary.
if (FLAGS_make_coasts || FLAGS_generate_features || FLAGS_generate_geometry ||
- FLAGS_generate_index || FLAGS_generate_search_index || FLAGS_calc_statistics ||
- FLAGS_type_statistics || FLAGS_dump_types || FLAGS_dump_prefixes ||
+ FLAGS_generate_index || FLAGS_generate_search_index || FLAGS_generate_cities_boundaries ||
+ FLAGS_calc_statistics || FLAGS_type_statistics || FLAGS_dump_types || FLAGS_dump_prefixes ||
FLAGS_dump_feature_names != "" || FLAGS_check_mwm || FLAGS_srtm_path != "" ||
FLAGS_make_routing_index || FLAGS_make_cross_mwm || FLAGS_generate_traffic_keys ||
FLAGS_transit_path != "")
@@ -282,6 +287,17 @@ int main(int argc, char ** argv)
LOG(LCRITICAL, ("Error generating centers table."));
}
+ if (FLAGS_generate_cities_boundaries)
+ {
+ LOG(LINFO, ("Generating cities boundaries for", datFile));
+ CHECK(genInfo.m_boundariesTable, ());
+ if (!generator::BuildCitiesBoundaries(datFile, osmToFeatureFilename,
+ *genInfo.m_boundariesTable))
+ {
+ LOG(LCRITICAL, ("Error generating cities boundaries."));
+ }
+ }
+
if (!FLAGS_srtm_path.empty())
routing::BuildRoadAltitudes(datFile, FLAGS_srtm_path);
diff --git a/generator/osm_id.hpp b/generator/osm_id.hpp
index f67e7bf29e..703cd18560 100644
--- a/generator/osm_id.hpp
+++ b/generator/osm_id.hpp
@@ -1,9 +1,9 @@
#pragma once
#include <cstdint>
+#include <functional>
#include <string>
-
namespace osm
{
@@ -34,5 +34,10 @@ public:
bool operator==(uint64_t other) const { return OsmId() == other; }
};
+struct HashId : private std::hash<uint64_t>
+{
+ size_t operator()(Id const & id) const { return std::hash<uint64_t>::operator()(id.OsmId()); }
+};
+
std::string DebugPrint(osm::Id const & id);
} // namespace osm
diff --git a/generator/osm_source.cpp b/generator/osm_source.cpp
index 90c67357c8..0b6406a60b 100644
--- a/generator/osm_source.cpp
+++ b/generator/osm_source.cpp
@@ -1,3 +1,4 @@
+#include "generator/cities_boundaries_builder.hpp"
#include "generator/coastlines_generator.hpp"
#include "generator/feature_generator.hpp"
#include "generator/intermediate_data.hpp"
@@ -16,6 +17,7 @@
#include "generator/opentable_dataset.hpp"
#include "generator/viator_dataset.hpp"
+#include "indexer/city_boundary.hpp"
#include "indexer/classificator.hpp"
#include "platform/platform.hpp"
@@ -28,6 +30,8 @@
#include "coding/file_name_utils.hpp"
#include "coding/parse_xml.hpp"
+#include <memory>
+
#include "defines.hpp"
using namespace std;
@@ -244,7 +248,7 @@ public:
// Assume that area places has better priority than point places at the very end ...
/// @todo It was usefull when place=XXX type has any area fill style.
/// Need to review priority logic here (leave the native osm label).
- return !IsPoint();
+ return !IsPoint() && r.IsPoint();
}
private:
@@ -285,6 +289,7 @@ class MainFeaturesEmitter : public EmitterBase
generator::BookingDataset m_bookingDataset;
generator::OpentableDataset m_opentableDataset;
generator::ViatorDataset m_viatorDataset;
+ shared_ptr<generator::OsmIdToBoundariesTable> m_boundariesTable;
/// Used to prepare a list of cities to serve as a list of nodes
/// for building a highway graph with OSRM for low zooms.
@@ -301,7 +306,25 @@ class MainFeaturesEmitter : public EmitterBase
};
uint32_t m_types[TYPES_COUNT];
- inline uint32_t Type(TypeIndex i) const { return m_types[i]; }
+ uint32_t Type(TypeIndex i) const { return m_types[i]; }
+
+ uint32_t GetPlaceType(FeatureParams const & params) const
+ {
+ static uint32_t const placeType = classif().GetTypeByPath({"place"});
+ return params.FindType(placeType, 1);
+ }
+
+ void UnionEqualPlacesIds(Place const & place)
+ {
+ if (!m_boundariesTable)
+ return;
+
+ auto const id = place.GetFeature().GetLastOsmId();
+ m_places.ForEachInRect(place.GetLimitRect(), [&](Place const & p) {
+ if (p.IsEqual(place))
+ m_boundariesTable->Union(p.GetFeature().GetLastOsmId(), id);
+ });
+ }
public:
MainFeaturesEmitter(feature::GenerateInfo const & info)
@@ -310,6 +333,7 @@ public:
, m_bookingDataset(info.m_bookingDatafileName, info.m_bookingReferenceDir)
, m_opentableDataset(info.m_opentableDatafileName, info.m_opentableReferenceDir)
, m_viatorDataset(info.m_viatorDatafileName)
+ , m_boundariesTable(info.m_boundariesTable)
{
Classificator const & c = classif();
@@ -348,8 +372,7 @@ public:
void operator()(FeatureBuilder1 & fb) override
{
- static uint32_t const placeType = classif().GetTypeByPath({"place"});
- uint32_t const type = fb.GetParams().FindType(placeType, 1);
+ uint32_t const type = GetPlaceType(fb.GetParams());
// TODO(mgserigio): Would it be better to have objects that store callback
// and can be piped: action-if-cond1 | action-if-cond-2 | ... ?
@@ -366,9 +389,10 @@ public:
});
}
+ Place const place(fb, type);
+ UnionEqualPlacesIds(place);
m_places.ReplaceEqualInRect(
- Place(fb, type),
- [](Place const & p1, Place const & p2) { return p1.IsEqual(p2); },
+ place, [](Place const & p1, Place const & p2) { return p1.IsEqual(p2); },
[](Place const & p1, Place const & p2) { return p1.IsBetterThan(p2); });
return;
}
@@ -400,6 +424,22 @@ public:
Emit(fb);
}
+ void EmitCityBoundary(FeatureBuilder1 const & fb, FeatureParams const & params) override
+ {
+ if (!m_boundariesTable)
+ return;
+
+ auto const type = GetPlaceType(params);
+ if (type == ftype::GetEmptyValue())
+ return;
+
+ auto const id = fb.GetLastOsmId();
+ m_boundariesTable->Append(id, indexer::CityBoundary(fb.GetOuterGeometry()));
+
+ Place const place(fb, type);
+ UnionEqualPlacesIds(place);
+ }
+
/// @return false if coasts are not merged and FLAG_fail_on_coasts is set
bool Finish() override
{
diff --git a/generator/osm_source.hpp b/generator/osm_source.hpp
index 6c530971ec..5c59180c20 100644
--- a/generator/osm_source.hpp
+++ b/generator/osm_source.hpp
@@ -34,6 +34,7 @@ public:
};
class FeatureBuilder1;
+class FeatureParams;
// Emitter is used in OsmElemen to FeatureBuilder translation process.
class EmitterBase
@@ -43,6 +44,9 @@ public:
/// This method is used by OsmTranslator to pass |fb| to Emitter for further processing.
virtual void operator()(FeatureBuilder1 & fb) = 0;
+
+ virtual void EmitCityBoundary(FeatureBuilder1 const & fb, FeatureParams const & params) = 0;
+
/// Finish is used in GenerateFeatureImpl to make whatever work is needed after
/// all OmsElements are processed.
virtual bool Finish() { return true; }
diff --git a/generator/osm_translator.hpp b/generator/osm_translator.hpp
index b6cc1a342c..5872fb1e84 100644
--- a/generator/osm_translator.hpp
+++ b/generator/osm_translator.hpp
@@ -324,6 +324,15 @@ class OsmToFeatureTranslator
return true;
}
+ bool IsCityBoundary(FeatureParams const & params)
+ {
+ feature::TypesHolder types;
+ for (auto const type : params.m_Types)
+ types.Add(type);
+ auto const type = ftypes::IsLocalityChecker::Instance().GetType(types);
+ return type == ftypes::CITY || type == ftypes::TOWN;
+ }
+
void EmitFeatureBase(FeatureBuilder1 & ft, FeatureParams const & params) const
{
ft.SetParams(params);
@@ -363,7 +372,7 @@ class OsmToFeatureTranslator
}
template <class MakeFnT>
- void EmitArea(FeatureBuilder1 & ft, FeatureParams params, MakeFnT makeFn) const
+ void EmitArea(FeatureBuilder1 & ft, FeatureParams params, MakeFnT makeFn)
{
using namespace feature;
@@ -374,6 +383,13 @@ class OsmToFeatureTranslator
// Key point here is that IsDrawableLike and RemoveNoDrawableTypes
// work a bit different for GEOM_AREA.
+ if (IsCityBoundary(params))
+ {
+ auto fb = ft;
+ makeFn(fb);
+ m_emitter.EmitCityBoundary(fb, params);
+ }
+
if (IsDrawableLike(params.m_Types, GEOM_AREA))
{
// Make the area feature if it has unique area styles.
diff --git a/generator/routing_helpers.cpp b/generator/routing_helpers.cpp
index 0d5376079a..cb30c859de 100644
--- a/generator/routing_helpers.cpp
+++ b/generator/routing_helpers.cpp
@@ -1,6 +1,6 @@
#include "generator/routing_helpers.hpp"
-#include "generator/gen_mwm_info.hpp"
+#include "generator/utils.hpp"
#include "coding/file_reader.hpp"
#include "coding/reader.hpp"
@@ -15,25 +15,11 @@ namespace
template <class ToDo>
bool ForEachRoadFromFile(string const & filename, ToDo && toDo)
{
- gen::OsmID2FeatureID osmIdsToFeatureIds;
- try
- {
- FileReader reader(filename);
- ReaderSource<FileReader> src(reader);
- osmIdsToFeatureIds.Read(src);
- }
- catch (FileReader::Exception const & e)
- {
- LOG(LERROR, ("Exception while reading file:", filename, ". Msg:", e.Msg()));
- return false;
- }
-
- osmIdsToFeatureIds.ForEach([&](gen::OsmID2FeatureID::ValueT const & p) {
- if (p.first.IsWay())
- toDo(p.second /* feature id */, p.first /* osm id */);
- });
-
- return true;
+ return generator::ForEachOsmId2FeatureId(filename,
+ [&](osm::Id const & osmId, uint32_t const featureId) {
+ if (osmId.IsWay())
+ toDo(featureId, osmId);
+ });
}
} // namespace
diff --git a/generator/utils.hpp b/generator/utils.hpp
index f00dba6e5c..db026718e7 100644
--- a/generator/utils.hpp
+++ b/generator/utils.hpp
@@ -1,8 +1,38 @@
#pragma once
+#include "generator/gen_mwm_info.hpp"
+
#include "indexer/index.hpp"
+#include "coding/file_reader.hpp"
+#include "coding/reader.hpp"
+
+#include "base/logging.hpp"
+
namespace generator
{
void LoadIndex(Index & index);
+
+template <typename ToDo>
+bool ForEachOsmId2FeatureId(string const & path, ToDo && toDo)
+{
+ gen::OsmID2FeatureID mapping;
+ try
+ {
+ FileReader reader(path);
+ NonOwningReaderSource source(reader);
+ mapping.Read(source);
+ }
+ catch (FileReader::Exception const & e)
+ {
+ LOG(LERROR, ("Exception while reading file:", path, ", message:", e.Msg()));
+ return false;
+ }
+
+ mapping.ForEach([&](gen::OsmID2FeatureID::ValueT const & p) {
+ toDo(p.first /* osm id */, p.second /* feature id */);
+ });
+
+ return true;
+}
} // namespace generator
diff --git a/geometry/diamond_box.hpp b/geometry/diamond_box.hpp
index 341249c591..29aa5b8fcf 100644
--- a/geometry/diamond_box.hpp
+++ b/geometry/diamond_box.hpp
@@ -33,7 +33,7 @@ public:
bool operator==(DiamondBox const & rhs) const { return m_box == rhs.m_box; }
- DECLARE_VISITOR(visitor(Points(), "points"))
+ DECLARE_VISITOR(visitor(m_box, "box"))
DECLARE_DEBUG_PRINT(DiamondBox)
private:
diff --git a/indexer/cities_boundaries_serdes.hpp b/indexer/cities_boundaries_serdes.hpp
index 913802ddad..c19e46ac1e 100644
--- a/indexer/cities_boundaries_serdes.hpp
+++ b/indexer/cities_boundaries_serdes.hpp
@@ -1,30 +1,37 @@
#pragma once
+#include "indexer/city_boundary.hpp"
#include "indexer/coding_params.hpp"
#include "indexer/geometry_coding.hpp"
#include "coding/bit_streams.hpp"
#include "coding/elias_coder.hpp"
#include "coding/point_to_integer.hpp"
+#include "coding/reader.hpp"
#include "coding/varint.hpp"
+#include "coding/write_to_sink.hpp"
#include "geometry/bounding_box.hpp"
#include "geometry/calipers_box.hpp"
#include "geometry/diamond_box.hpp"
+#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "base/assert.hpp"
#include "base/logging.hpp"
+#include "base/macros.hpp"
+#include "base/visitor.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
+#include <limits>
#include <vector>
namespace indexer
{
template <typename Sink>
-class CityBoundaryEncoder
+class CitiesBoundariesEncoder
{
public:
struct Visitor
@@ -39,7 +46,7 @@ public:
void operator()(m2::PointU const & p)
{
- WriteVarUint(m_sink, EncodeDelta(p, m_last));
+ WriteVarUint(m_sink, ::EncodeDelta(p, m_last));
m_last = p;
}
@@ -54,43 +61,50 @@ public:
void operator()(m2::CalipersBox const & cbox)
{
- double const kEps = 1e-5;
-
auto ps = cbox.Points();
- auto us = ToU(ps);
CHECK(!ps.empty(), ());
CHECK_LESS_OR_EQUAL(ps.size(), 4, ());
CHECK(ps.size() != 3, ());
- while (ps.size() != 4)
+ if (ps.size() == 1)
+ {
+ auto const p0 = ps[0];
+ while (ps.size() != 4)
+ ps.push_back(p0);
+ }
+ else if (ps.size() == 2)
{
- auto const lp = ps.back();
- ps.push_back(lp);
+ auto const p0 = ps[0];
+ auto const p1 = ps[1];
- auto const lu = us.back();
- us.push_back(lu);
+ ps.push_back(p1);
+ ps.push_back(p0);
}
ASSERT_EQUAL(ps.size(), 4, ());
- ASSERT_EQUAL(us.size(), 4, ());
- size_t pivot = ps.size();
+ size_t bestCurr = ps.size();
+ double bestLength = -1;
for (size_t curr = 0; curr < ps.size(); ++curr)
{
size_t const next = (curr + 1) % ps.size();
- if (ps[next].x >= ps[curr].x - kEps && ps[next].y >= ps[curr].y - kEps)
+
+ auto const length = ps[curr].Length(ps[next]);
+ if (length > bestLength)
{
- pivot = curr;
- break;
+ bestCurr = curr;
+ bestLength = length;
}
}
- CHECK(pivot != ps.size(), ());
- std::rotate(us.begin(), us.begin() + pivot, us.end());
+ CHECK(bestCurr != ps.size(), ());
+ std::rotate(ps.begin(), ps.begin() + bestCurr, ps.end());
+
+ auto const us = ToU(ps);
(*this)(us[0]);
- EncodePositiveDelta(us[0], us[1]);
+ EncodeDelta(us[0], us[1]);
uint64_t const width = us[3].Length(us[0]);
WriteVarUint(m_sink, width);
@@ -131,6 +145,14 @@ public:
return us;
}
+ void EncodeDelta(m2::PointU const & curr, m2::PointU const & next)
+ {
+ auto const dx = static_cast<int32_t>(next.x) - static_cast<int32_t>(curr.x);
+ auto const dy = static_cast<int32_t>(next.y) - static_cast<int32_t>(curr.y);
+ WriteVarInt(m_sink, dx);
+ WriteVarInt(m_sink, dy);
+ }
+
void EncodePositiveDelta(m2::PointU const & curr, m2::PointU const & next)
{
ASSERT_GREATER_OR_EQUAL(next.x, curr.x, ());
@@ -149,7 +171,7 @@ public:
m2::PointU m_last;
};
- CityBoundaryEncoder(Sink & sink, serial::CodingParams const & params)
+ CitiesBoundariesEncoder(Sink & sink, serial::CodingParams const & params)
: m_sink(sink), m_visitor(sink, params)
{
}
@@ -162,8 +184,11 @@ public:
BitWriter<Sink> writer(m_sink);
for (auto const & bs : boundaries)
{
- CHECK(!bs.empty(), ());
- coding::GammaCoder::Encode(writer, bs.size());
+ CHECK_LESS(bs.size(), std::numeric_limits<uint64_t>::max(), ());
+ auto const success =
+ coding::GammaCoder::Encode(writer, static_cast<uint64_t>(bs.size()) + 1);
+ ASSERT(success, ());
+ UNUSED_VALUE(success);
}
}
@@ -176,12 +201,11 @@ public:
private:
Sink & m_sink;
- serial::CodingParams m_params;
Visitor m_visitor;
};
template <typename Source>
-class CityBoundaryDecoder
+class CitiesBoundariesDecoderV0
{
public:
struct Visitor
@@ -201,7 +225,7 @@ public:
void operator()(m2::PointU & p)
{
- p = DecodeDelta(ReadVarUint<uint64_t>(m_source), m_last);
+ p = ::DecodeDelta(ReadVarUint<uint64_t>(m_source), m_last);
m_last = p;
}
@@ -223,7 +247,7 @@ public:
std::vector<m2::PointU> points(4);
points[0] = pivot;
- points[1] = pivot + DecodePositiveDelta();
+ points[1] = DecodeDelta(pivot);
auto const width = ReadVarUint<uint64_t>(m_source);
@@ -273,6 +297,13 @@ public:
return ps;
}
+ m2::PointU DecodeDelta(m2::PointU const & base)
+ {
+ auto const dx = ReadVarInt<int32_t>(m_source);
+ auto const dy = ReadVarInt<int32_t>(m_source);
+ return m2::PointU(base.x + dx, base.y + dy);
+ }
+
m2::PointU DecodePositiveDelta()
{
auto const dx = ReadVarUint<uint32_t>(m_source);
@@ -285,7 +316,7 @@ public:
m2::PointU m_last;
};
- CityBoundaryDecoder(Source & source, serial::CodingParams const & params)
+ CitiesBoundariesDecoderV0(Source & source, serial::CodingParams const & params)
: m_source(source), m_visitor(source, params)
{
}
@@ -300,7 +331,8 @@ public:
for (auto & bs : boundaries)
{
auto const size = coding::GammaCoder::Decode(reader);
- bs.resize(size);
+ ASSERT_GREATER_OR_EQUAL(size, 1, ());
+ bs.resize(size - 1);
}
}
@@ -315,4 +347,100 @@ private:
Source & m_source;
Visitor m_visitor;
};
+
+struct CitiesBoundariesSerDes
+{
+ template <typename Sink>
+ struct WriteToSinkVisitor
+ {
+ WriteToSinkVisitor(Sink & sink) : m_sink(sink) {}
+
+ template <typename T>
+ typename enable_if<is_integral<T>::value || is_enum<T>::value, void>::type operator()(
+ T const & t, char const * /* name */ = nullptr)
+ {
+ WriteToSink(m_sink, t);
+ }
+
+ template <typename T>
+ typename enable_if<!is_integral<T>::value && !is_enum<T>::value, void>::type operator()(
+ T const & t, char const * /* name */ = nullptr)
+ {
+ t.Visit(*this);
+ }
+
+ Sink & m_sink;
+ };
+
+ template <typename Source>
+ struct ReadFromSourceVisitor
+ {
+ ReadFromSourceVisitor(Source & source) : m_source(source) {}
+
+ template <typename T>
+ typename enable_if<is_integral<T>::value || is_enum<T>::value, void>::type operator()(
+ T & t, char const * /* name */ = nullptr)
+ {
+ t = ReadPrimitiveFromSource<T>(m_source);
+ }
+
+ template <typename T>
+ typename enable_if<!is_integral<T>::value && !is_enum<T>::value, void>::type operator()(
+ T & t, char const * /* name */ = nullptr)
+ {
+ t.Visit(*this);
+ }
+
+ Source & m_source;
+ };
+
+ static uint8_t constexpr kLatestVersion = 0;
+
+ struct HeaderV0
+ {
+ static uint8_t const kDefaultCoordBits = 19;
+
+ HeaderV0() {}
+
+ DECLARE_VISITOR(visitor(m_coordBits, "coordBits"))
+
+ uint8_t m_coordBits = kDefaultCoordBits;
+ };
+
+ template <typename Sink>
+ static void Serialize(Sink & sink, std::vector<std::vector<CityBoundary>> const & boundaries)
+ {
+ uint8_t const version = kLatestVersion;
+
+ WriteToSinkVisitor<Sink> visitor(sink);
+ visitor(version);
+
+ HeaderV0 const header;
+ visitor(header);
+
+ serial::CodingParams const params(header.m_coordBits,
+ m2::PointD(MercatorBounds::minX, MercatorBounds::minY));
+ CitiesBoundariesEncoder<Sink> encoder(sink, params);
+ encoder(boundaries);
+ }
+
+ template <typename Source>
+ static void Deserialize(Source & source, std::vector<std::vector<CityBoundary>> & boundaries)
+ {
+ ReadFromSourceVisitor<Source> visitor(source);
+
+ uint8_t version;
+ visitor(version);
+
+ CHECK_EQUAL(version, 0, ());
+
+ HeaderV0 header;
+ visitor(header);
+
+ serial::CodingParams const params(header.m_coordBits,
+ m2::PointD(MercatorBounds::minX, MercatorBounds::minY));
+ CitiesBoundariesDecoderV0<Source> decoder(source, params);
+ decoder(boundaries);
+ }
+};
} // namespace indexer
diff --git a/indexer/indexer_tests/cities_boundaries_serdes_tests.cpp b/indexer/indexer_tests/cities_boundaries_serdes_tests.cpp
index 7bf99d2a00..534044be3d 100644
--- a/indexer/indexer_tests/cities_boundaries_serdes_tests.cpp
+++ b/indexer/indexer_tests/cities_boundaries_serdes_tests.cpp
@@ -23,78 +23,88 @@ namespace
using Boundary = vector<CityBoundary>;
using Boundaries = vector<Boundary>;
-void TestEqual(BoundingBox const & lhs, BoundingBox const & rhs, double eps)
+static_assert(CitiesBoundariesSerDes::kLatestVersion == 0, "");
+static_assert(CitiesBoundariesSerDes::HeaderV0::kDefaultCoordBits == 19, "");
+
+// Precision of mercator coords encoded with 19 bits.
+double const kEps = 1e-3;
+
+void TestEqual(vector<PointD> const & lhs, vector<PointD> const & rhs)
+{
+ TEST_EQUAL(lhs.size(), rhs.size(), (lhs, rhs));
+ for (size_t i = 0; i < lhs.size(); ++i)
+ TEST(AlmostEqualAbs(lhs[i], rhs[i], kEps), (lhs, rhs));
+}
+
+void TestEqual(BoundingBox const & lhs, BoundingBox const & rhs)
{
- TEST(AlmostEqualAbs(lhs.Min(), rhs.Min(), eps), (lhs, rhs));
- TEST(AlmostEqualAbs(lhs.Max(), rhs.Max(), eps), (lhs, rhs));
+ TEST(AlmostEqualAbs(lhs.Min(), rhs.Min(), kEps), (lhs, rhs));
+ TEST(AlmostEqualAbs(lhs.Max(), rhs.Max(), kEps), (lhs, rhs));
}
-void TestEqual(CalipersBox const & lhs, CalipersBox const & rhs, double eps) {}
+void TestEqual(CalipersBox const & lhs, CalipersBox const & rhs)
+{
+ TestEqual(lhs.Points(), rhs.Points());
+}
-void TestEqual(DiamondBox const & lhs, DiamondBox const & rhs, double eps)
+void TestEqual(DiamondBox const & lhs, DiamondBox const & rhs)
{
auto const lps = lhs.Points();
auto const rps = rhs.Points();
TEST_EQUAL(lps.size(), 4, (lhs));
TEST_EQUAL(rps.size(), 4, (rhs));
- for (size_t i = 0; i < 4; ++i)
- TEST(AlmostEqualAbs(lps[i], rps[i], eps), (lhs, rhs));
+ TestEqual(lps, rps);
}
-void TestEqual(CityBoundary const & lhs, CityBoundary const & rhs, double eps)
+void TestEqual(CityBoundary const & lhs, CityBoundary const & rhs)
{
- TestEqual(lhs.m_bbox, rhs.m_bbox, eps);
- TestEqual(lhs.m_cbox, rhs.m_cbox, eps);
- TestEqual(lhs.m_dbox, rhs.m_dbox, eps);
+ TestEqual(lhs.m_bbox, rhs.m_bbox);
+ TestEqual(lhs.m_cbox, rhs.m_cbox);
+ TestEqual(lhs.m_dbox, rhs.m_dbox);
}
-void TestEqual(Boundary const & lhs, Boundary const & rhs, double eps)
+void TestEqual(Boundary const & lhs, Boundary const & rhs)
{
TEST_EQUAL(lhs.size(), rhs.size(), (lhs, rhs));
for (size_t i = 0; i < lhs.size(); ++i)
- TestEqual(lhs[i], rhs[i], eps);
+ TestEqual(lhs[i], rhs[i]);
}
-void TestEqual(Boundaries const & lhs, Boundaries const & rhs, double eps)
+void TestEqual(Boundaries const & lhs, Boundaries const & rhs)
{
TEST_EQUAL(lhs.size(), rhs.size(), (lhs, rhs));
for (size_t i = 0; i < lhs.size(); ++i)
- TestEqual(lhs[i], rhs[i], eps);
+ TestEqual(lhs[i], rhs[i]);
}
-Boundaries EncodeDecode(Boundaries const & boundaries, CodingParams const & params)
+Boundaries EncodeDecode(Boundaries const & boundaries)
{
vector<uint8_t> buffer;
{
MemWriter<decltype(buffer)> sink(buffer);
- CityBoundaryEncoder<decltype(sink)> encoder(sink, params);
- encoder(boundaries);
+ CitiesBoundariesSerDes::Serialize(sink, boundaries);
}
{
Boundaries boundaries;
MemReader reader(buffer.data(), buffer.size());
NonOwningReaderSource source(reader);
- CityBoundaryDecoder<decltype(source)> decoder(source, params);
- decoder(boundaries);
+ CitiesBoundariesSerDes::Deserialize(source, boundaries);
return boundaries;
}
}
-void TestEncodeDecode(Boundaries const & expected, CodingParams const & params, double eps)
+void TestEncodeDecode(Boundaries const & expected)
{
- Boundaries const actual = EncodeDecode(expected, params);
- TestEqual(expected, actual, eps);
+ Boundaries const actual = EncodeDecode(expected);
+ TestEqual(expected, actual);
}
UNIT_TEST(CitiesBoundariesSerDes_Smoke)
{
- CodingParams const params(19 /* coordBits */, PointD(MercatorBounds::minX, MercatorBounds::minY));
- double const kEps = 1e-3;
-
{
Boundaries const expected;
- TestEncodeDecode(expected, params, kEps);
+ TestEncodeDecode(expected);
}
{
@@ -107,7 +117,7 @@ UNIT_TEST(CitiesBoundariesSerDes_Smoke)
vector<PointD>{{PointD(1.000, 1.000), PointD(1.002, 1.000), PointD(1.002, 1.003)}});
Boundaries const expected = {{boundary0, boundary1}};
- TestEncodeDecode(expected, params, kEps);
+ TestEncodeDecode(expected);
}
}
} // namespace