Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,62 @@
project(generator_tests)
set(SRC
addresses_tests.cpp
affiliation_tests.cpp
altitude_test.cpp
brands_loader_test.cpp
camera_collector_tests.cpp
cells_merger_tests.cpp
cities_boundaries_checker_tests.cpp
cities_ids_tests.cpp
city_roads_tests.cpp
cluster_finder_tests.cpp
coasts_test.cpp
collector_boundary_postcode_tests.cpp
collector_building_parts_tests.cpp
collector_routing_city_boundaries_tests.cpp
common.cpp
common.hpp
# complex_loader_tests.cpp
cross_mwm_osm_ways_collector_tests.cpp
descriptions_section_builder_tests.cpp
feature_builder_test.cpp
feature_merger_test.cpp
filter_elements_tests.cpp
gen_mwm_info_tests.cpp
# hierarchy_entry_tests.cpp
# hierarchy_tests.cpp
intermediate_data_test.cpp
maxspeeds_tests.cpp
merge_collectors_tests.cpp
metadata_parser_test.cpp
metalines_tests.cpp
mini_roundabout_tests.cpp
node_mixer_test.cpp
osm_element_helpers_tests.cpp
osm_o5m_source_test.cpp
osm_type_test.cpp
place_processor_tests.cpp
raw_generator_test.cpp
relation_tags_tests.cpp
restriction_collector_test.cpp
restriction_test.cpp
road_access_test.cpp
source_data.cpp
source_data.hpp
source_to_element_test.cpp
speed_cameras_test.cpp
srtm_parser_test.cpp
tag_admixer_test.cpp
tesselator_test.cpp
triangles_tree_coding_test.cpp
types_helper.hpp
)
omim_add_test(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME}
platform_tests_support
generator_tests_support
generator
)

View file

@ -0,0 +1,20 @@
#include "testing/testing.hpp"
#include "generator/addresses_collector.hpp"
UNIT_TEST(GenerateAddresses_AddressInfo_FormatRange)
{
generator::AddressesHolder::AddressInfo info;
info.m_house = "3000";
info.m_house2 = "3100";
TEST_EQUAL(info.FormatRange(), "3000:3100", ());
info.m_house = "72B";
info.m_house2 = "14 стр 1";
TEST_EQUAL(info.FormatRange(), "14:72", ());
info.m_house = "foo";
info.m_house2 = "bar";
TEST_EQUAL(info.FormatRange(), "", ());
}

View file

@ -0,0 +1,212 @@
#include "testing/testing.hpp"
#include "generator/affiliation.hpp"
#include "generator/feature_builder.hpp"
#include "indexer/classificator.hpp"
#include "indexer/classificator_loader.hpp"
#include "platform/platform.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "base/assert.hpp"
#include "base/file_name_utils.hpp"
#include <fstream>
#include <string>
#include <vector>
namespace
{
class AffiliationTests
{
public:
static std::string const kOne;
static std::string const kTwo;
static constexpr m2::PointD kPointInsideOne1{3.0, 3.0};
static constexpr m2::PointD kPointInsideOne2{4.0, 4.0};
static constexpr m2::PointD kPointInsideTwo1{10.0, 3.0};
static constexpr m2::PointD kPointInsideTwo2{11.0, 4.0};
static constexpr m2::PointD kPointInsideOneAndTwo1{7.0, 4.0};
static constexpr m2::PointD kPointInsideOneAndTwo2{9.0, 5.0};
static constexpr m2::PointD kPointOnBorderOne{1.0, 6.0};
static constexpr m2::PointD kPointOnBorderTwo{14.0, 6.0};
static constexpr m2::PointD kPointInsideOneBoundingBox{1.0, 9.0};
static constexpr m2::PointD kPointInsideTwoBoundingBox{14.0, 9.0};
AffiliationTests()
{
classificator::Load();
auto & platform = GetPlatform();
m_testPath = base::JoinPath(platform.WritableDir(), "AffiliationTests");
m_borderPath = base::JoinPath(m_testPath, "borders");
CHECK(Platform::MkDirRecursively(m_borderPath), (m_borderPath));
std::ofstream(base::JoinPath(m_borderPath, "One.poly")) <<
R"(One
1
5.0 0.0
0.0 5.0
5.0 10.0
10.0 5.0
5.0 0.0
END
END)";
std::ofstream(base::JoinPath(m_borderPath, "Two.poly")) <<
R"(Two
1
10.0 0.0
5.0 5.0
10.0 10.0
15.0 5.0
10.0 0.0
END
END)";
}
~AffiliationTests() { CHECK(Platform::RmDirRecursively(m_testPath), (m_testPath)); }
std::string const & GetBorderPath() const { return m_testPath; }
static feature::FeatureBuilder MakeLineFb(std::vector<m2::PointD> && geom)
{
feature::FeatureBuilder fb;
fb.AssignPoints(std::move(geom));
fb.AddType(classif().GetTypeByPath({"highway", "secondary"}));
fb.SetLinear();
return fb;
}
private:
std::string m_testPath;
std::string m_borderPath;
};
// static
std::string const AffiliationTests::kOne = "One";
std::string const AffiliationTests::kTwo = "Two";
bool Test(std::vector<std::string> && res, std::set<std::string> const & answ)
{
if (res.size() != answ.size())
return false;
std::set<std::string> r;
std::move(std::begin(res), std::end(res), std::inserter(r, std::begin(r)));
return r == answ;
}
void TestCountriesAffiliationInsideBorders(feature::AffiliationInterface const & affiliation)
{
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOne1), {AffiliationTests::kOne}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOne2), {AffiliationTests::kOne}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointOnBorderOne), {AffiliationTests::kOne}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideTwo1), {AffiliationTests::kTwo}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideTwo2), {AffiliationTests::kTwo}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOneAndTwo1),
{AffiliationTests::kOne, AffiliationTests::kTwo}),
());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOneAndTwo2),
{AffiliationTests::kOne, AffiliationTests::kTwo}),
());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointOnBorderTwo), {AffiliationTests::kTwo}), ());
TEST(Test(affiliation.GetAffiliations(
AffiliationTests::MakeLineFb({AffiliationTests::kPointInsideOne1, AffiliationTests::kPointInsideOne2})),
{AffiliationTests::kOne}),
());
TEST(Test(affiliation.GetAffiliations(
AffiliationTests::MakeLineFb({AffiliationTests::kPointInsideTwo1, AffiliationTests::kPointInsideTwo2})),
{AffiliationTests::kTwo}),
());
TEST(Test(affiliation.GetAffiliations(
AffiliationTests::MakeLineFb({AffiliationTests::kPointInsideOne1, AffiliationTests::kPointInsideTwo1})),
{AffiliationTests::kOne, AffiliationTests::kTwo}),
());
}
template <typename T>
void TestCountriesFilesAffiliation(std::string const & borderPath)
{
{
T affiliation(borderPath, false /* haveBordersForWholeWorld */);
TestCountriesAffiliationInsideBorders(affiliation);
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOneBoundingBox), {}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideTwoBoundingBox), {}), ());
}
{
T affiliation(borderPath, true /* haveBordersForWholeWorld */);
TestCountriesAffiliationInsideBorders(affiliation);
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOneBoundingBox), {AffiliationTests::kOne}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideTwoBoundingBox), {AffiliationTests::kTwo}), ());
}
}
} // namespace
UNIT_CLASS_TEST(AffiliationTests, SingleAffiliationTests)
{
std::string const kName = "Name";
feature::SingleAffiliation affiliation(kName);
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOne1), {kName}), ());
TEST(Test(affiliation.GetAffiliations(AffiliationTests::kPointInsideOneAndTwo1), {kName}), ());
TEST(Test(affiliation.GetAffiliations(
AffiliationTests::MakeLineFb({AffiliationTests::kPointInsideOne1, AffiliationTests::kPointInsideTwo1})),
{kName}),
());
TEST(affiliation.HasCountryByName(kName), ());
TEST(!affiliation.HasCountryByName("NoName"), ());
}
UNIT_CLASS_TEST(AffiliationTests, CountriesFilesAffiliationTests)
{
TestCountriesFilesAffiliation<feature::CountriesFilesAffiliation>(AffiliationTests::GetBorderPath());
}
UNIT_CLASS_TEST(AffiliationTests, CountriesFilesIndexAffiliationTests)
{
TestCountriesFilesAffiliation<feature::CountriesFilesIndexAffiliation>(AffiliationTests::GetBorderPath());
}
UNIT_TEST(Lithuania_Belarus_Border)
{
using namespace borders;
auto const bordersDir = base::JoinPath(GetPlatform().WritableDir(), BORDERS_DIR);
// https://www.openstreetmap.org/node/3951697639 should belong to both countries.
auto const point = mercator::FromLatLon({54.5443346, 25.6997363});
for (auto const country : {"Lithuania_East", "Belarus_Hrodna Region"})
{
std::vector<m2::RegionD> regions;
LoadBorders(bordersDir + country + BORDERS_EXTENSION, regions);
TEST_EQUAL(regions.size(), 1, ());
bool found = false;
for (auto const eps : {1.0E-5, 5.0E-5, 1.0E-4})
{
if (regions[0].Contains(point, CountryPolygons::ContainsCompareFn(eps)))
{
LOG(LINFO, (eps, country));
TEST_LESS_OR_EQUAL(eps, CountryPolygons::GetContainsEpsilon(), ());
found = true;
break;
}
}
TEST(found, (country));
}
}

View file

@ -0,0 +1,255 @@
#include "testing/testing.hpp"
#include "generator/altitude_generator.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include "routing/road_graph.hpp"
#include "routing/routing_helpers.hpp"
#include "indexer/altitude_loader.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/data_source.hpp"
#include "indexer/feature_processor.hpp"
#include "geometry/point2d.hpp"
#include "geometry/point_with_altitude.hpp"
#include "platform/country_file.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "platform/platform_tests_support/writable_dir_changer.hpp"
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include "base/scope_guard.hpp"
#include "defines.hpp"
#include <map>
#include <string>
#include <vector>
namespace altitude_test
{
using namespace feature;
using namespace generator;
using namespace platform;
using namespace platform::tests_support;
using namespace routing;
// These tests generate mwms with altitude sections and then check if altitudes
// in the mwms are correct. The mwms are initialized with different sets of features.
// There are several restrictions for these features:
// * all of them are linear
// * all coords of these features should be integer (it's necessary for easy implementation of
// MockAltitudeGetter)
// * if accoding to one feature a point has some altitude, the point of another feature
// with the same coords has to have the same altitude
// @TODO(bykoianko) Add ability to add to the tests not road features without altitude information.
// Directory name for creating test mwm and temporary files.
std::string const kTestDir = "altitude_generation_test";
// Temporary mwm name for testing.
std::string const kTestMwm = "test";
struct Point3D
{
Point3D(int32_t x, int32_t y, geometry::Altitude a) : m_point(x, y), m_altitude(a) {}
m2::PointI m_point;
geometry::Altitude m_altitude;
};
using TPoint3DList = std::vector<Point3D>;
TPoint3DList const kRoad1 = {{0, -1, -1}, {0, 0, 0}, {0, 1, 1}};
TPoint3DList const kRoad2 = {{0, 1, 1}, {5, 1, 1}, {10, 1, 1}};
TPoint3DList const kRoad3 = {{10, 1, 1}, {15, 6, 100}, {20, 11, 110}};
TPoint3DList const kRoad4 = {{-10, 1, -1}, {-20, 6, -100}, {-20, -11, -110}};
class MockAltitudeGetter : public AltitudeGetter
{
public:
using TMockAltitudes = std::map<m2::PointI, geometry::Altitude>;
explicit MockAltitudeGetter(std::vector<TPoint3DList> const & roads)
{
for (TPoint3DList const & geom3D : roads)
{
for (auto const & g : geom3D)
{
auto const it = m_altitudes.find(g.m_point);
if (it != m_altitudes.cend())
{
CHECK_EQUAL(it->second, g.m_altitude, ("Point", it->first, "is set with two different altitudes."));
continue;
}
m_altitudes[g.m_point] = g.m_altitude;
}
}
}
// AltitudeGetter overrides:
geometry::Altitude GetAltitude(m2::PointD const & p) override
{
m2::PointI const rounded(static_cast<int32_t>(round(p.x)), static_cast<int32_t>(round(p.y)));
auto const it = m_altitudes.find(rounded);
if (it == m_altitudes.end())
return geometry::kInvalidAltitude;
return it->second;
}
private:
TMockAltitudes m_altitudes;
};
class MockNoAltitudeGetter : public AltitudeGetter
{
public:
geometry::Altitude GetAltitude(m2::PointD const &) override { return geometry::kInvalidAltitude; }
};
std::vector<m2::PointD> ExtractPoints(TPoint3DList const & geom3D)
{
std::vector<m2::PointD> result;
for (Point3D const & p : geom3D)
result.push_back(m2::PointD(p.m_point));
return result;
}
void BuildMwmWithoutAltitudes(std::vector<TPoint3DList> const & roads, LocalCountryFile & country)
{
generator::tests_support::TestMwmBuilder builder(country, feature::DataHeader::MapType::Country);
for (TPoint3DList const & geom3D : roads)
builder.Add(generator::tests_support::TestStreet(ExtractPoints(geom3D), std::string(), std::string()));
}
void TestAltitudes(DataSource const & dataSource, MwmSet::MwmId const & mwmId, std::string const & mwmPath,
bool hasAltitudeExpected, AltitudeGetter & expectedAltitudes)
{
auto const handle = dataSource.GetMwmHandleById(mwmId);
TEST(handle.IsAlive(), ());
AltitudeLoaderCached loader(*handle.GetValue());
TEST_EQUAL(loader.HasAltitudes(), hasAltitudeExpected, ());
auto processor = [&expectedAltitudes, &loader](FeatureType & f, uint32_t const & id)
{
f.ParseGeometry(FeatureType::BEST_GEOMETRY);
size_t const pointsCount = f.GetPointsCount();
geometry::Altitudes const & altitudes = loader.GetAltitudes(id, pointsCount);
if (!routing::IsRoad(feature::TypesHolder(f)))
{
TEST(altitudes.empty(), ());
return;
}
TEST_EQUAL(altitudes.size(), pointsCount, ());
for (size_t i = 0; i < pointsCount; ++i)
{
geometry::Altitude const fromGetter = expectedAltitudes.GetAltitude(f.GetPoint(i));
geometry::Altitude const expected =
(fromGetter == geometry::kInvalidAltitude ? geometry::kDefaultAltitudeMeters : fromGetter);
TEST_EQUAL(expected, altitudes[i], ("A wrong altitude"));
}
};
feature::ForEachFeature(mwmPath, processor);
}
void TestAltitudesBuilding(std::vector<TPoint3DList> const & roads, bool hasAltitudeExpected,
AltitudeGetter & altitudeGetter)
{
classificator::Load();
Platform & platform = GetPlatform();
std::string const testDirFullPath = base::JoinPath(platform.WritableDir(), kTestDir);
// Building mwm without altitude section.
LocalCountryFile country(testDirFullPath, CountryFile(kTestMwm), 1);
ScopedDir testScopedDir(kTestDir);
ScopedFile testScopedMwm(base::JoinPath(kTestDir, kTestMwm + DATA_FILE_EXTENSION), ScopedFile::Mode::Create);
BuildMwmWithoutAltitudes(roads, country);
// Adding altitude section to mwm.
auto const mwmPath = testScopedMwm.GetFullPath();
BuildRoadAltitudes(mwmPath, altitudeGetter);
// Reading from mwm and testing altitude information.
FrozenDataSource dataSource;
auto const regResult = dataSource.RegisterMap(country);
TEST_EQUAL(regResult.second, MwmSet::RegResult::Success, ());
TestAltitudes(dataSource, regResult.first /* mwmId */, mwmPath, hasAltitudeExpected, altitudeGetter);
}
void TestBuildingAllFeaturesHaveAltitude(std::vector<TPoint3DList> const & roads, bool hasAltitudeExpected)
{
MockAltitudeGetter altitudeGetter(roads);
TestAltitudesBuilding(roads, hasAltitudeExpected, altitudeGetter);
}
void TestBuildingNoFeatureHasAltitude(std::vector<TPoint3DList> const & roads, bool hasAltitudeExpected)
{
MockNoAltitudeGetter altitudeGetter;
TestAltitudesBuilding(roads, hasAltitudeExpected, altitudeGetter);
}
UNIT_TEST(AltitudeGenerationTest_ZeroFeatures)
{
std::vector<TPoint3DList> const roads = {};
TestBuildingAllFeaturesHaveAltitude(roads, false /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_OneRoad)
{
std::vector<TPoint3DList> const roads = {kRoad1};
TestBuildingAllFeaturesHaveAltitude(roads, true /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_TwoConnectedRoads)
{
std::vector<TPoint3DList> const roads = {kRoad1, kRoad2};
TestBuildingAllFeaturesHaveAltitude(roads, true /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_TwoDisconnectedRoads)
{
std::vector<TPoint3DList> const roads = {kRoad1, kRoad3};
TestBuildingAllFeaturesHaveAltitude(roads, true /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_ThreeRoads)
{
std::vector<TPoint3DList> const roads = {kRoad1, kRoad2, kRoad3};
TestBuildingAllFeaturesHaveAltitude(roads, true /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_FourRoads)
{
std::vector<TPoint3DList> const roads = {kRoad1, kRoad2, kRoad3, kRoad4};
TestBuildingAllFeaturesHaveAltitude(roads, true /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_ZeroFeaturesWithoutAltitude)
{
std::vector<TPoint3DList> const roads = {};
TestBuildingNoFeatureHasAltitude(roads, false /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_OneRoadWithoutAltitude)
{
std::vector<TPoint3DList> const roads = {kRoad1};
TestBuildingNoFeatureHasAltitude(roads, false /* hasAltitudeExpected */);
}
UNIT_TEST(AltitudeGenerationTest_FourRoadsWithoutAltitude)
{
std::vector<TPoint3DList> const roads = {kRoad1, kRoad2, kRoad3, kRoad4};
TestBuildingNoFeatureHasAltitude(roads, false /* hasAltitudeExpected */);
}
} // namespace altitude_test

View file

@ -0,0 +1,62 @@
#include "testing/testing.hpp"
#include "generator/brands_loader.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "base/geo_object_id.hpp"
#include <string>
#include <unordered_map>
namespace brands_loader_test
{
using namespace generator;
using base::GeoObjectId;
using platform::tests_support::ScopedFile;
char const kBrandsJson[] =
"{\n"
"\"nodes\": {\n"
"\"2132500347\": 13,\n"
"\"5321137826\": 12\n"
"},\n"
"\"ways\": {\n"
"\"440527172\": 13,\n"
"\"149816366\": 12\n"
"},\n"
"\"relations\": {\n"
"\"6018309\": 13,\n"
"\"6228042\": 12\n"
"}\n"
"}";
char const kBrandTranslationsJson[] =
"{\n"
"\"12\": {\n"
"\"en\": [\"subway\"],\n"
"\"ru\": [\"\u0441\u0430\u0431\u0432\u044d\u0439\",\n"
"\"\u0441\u0430\u0431\u0432\u0435\u0439\"]\n"
"},\n"
"\"13\": {\n"
"\"en\": [\"mcdonalds\",\"mc donalds\"],\n"
"\"ru\": [\"\u043c\u0430\u043a\u0434\u043e\u043d\u0430\u043b\u044c\u0434\u0441\",\n"
"\"\u043c\u0430\u043a\u0434\u043e\u043d\u0430\u043b\u0434\u0441\"]\n"
"}\n"
"}";
UNIT_TEST(LoadBrands)
{
ScopedFile const brandsFile("brands.json", kBrandsJson);
ScopedFile const translationsFile("translations.json", kBrandTranslationsJson);
std::unordered_map<GeoObjectId, std::string> brands;
TEST(LoadBrands(brandsFile.GetFullPath(), translationsFile.GetFullPath(), brands), ());
TEST_EQUAL(brands[GeoObjectId(GeoObjectId::Type::ObsoleteOsmNode, 2132500347)], "mcdonalds", ());
TEST_EQUAL(brands[GeoObjectId(GeoObjectId::Type::ObsoleteOsmWay, 440527172)], "mcdonalds", ());
TEST_EQUAL(brands[GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 6018309)], "mcdonalds", ());
TEST_EQUAL(brands[GeoObjectId(GeoObjectId::Type::ObsoleteOsmNode, 5321137826)], "subway", ());
TEST_EQUAL(brands[GeoObjectId(GeoObjectId::Type::ObsoleteOsmWay, 149816366)], "subway", ());
TEST_EQUAL(brands[GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 6228042)], "subway", ());
}
} // namespace brands_loader_test

View file

@ -0,0 +1,249 @@
#include "testing/testing.hpp"
#include "generator/collector_camera.hpp"
#include "generator/feature_maker.hpp"
#include "generator/filter_planet.hpp"
#include "generator/generate_info.hpp"
#include "generator/intermediate_data.hpp"
#include "generator/osm2type.hpp"
#include "generator/osm_element.hpp"
#include "generator/osm_source.hpp"
#include "generator/processor_factory.hpp"
#include "generator/raw_generator.hpp"
#include "generator/translator.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/map_style.hpp"
#include "indexer/map_style_reader.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "platform/platform_tests_support/writable_dir_changer.hpp"
#include "base/macros.hpp"
#include "defines.hpp"
namespace routing_builder
{
using namespace generator;
using std::pair, std::string;
string const kSpeedCameraTag = R"(<tag k="highway" v="speed_camera"/>)";
feature::FeatureBuilder MakeFeatureBuilderWithParams(OsmElement & element)
{
feature::FeatureBuilder fb;
auto & params = fb.GetParams();
ftype::GetNameAndType(&element, params);
return fb;
}
class TranslatorForTest : public generator::Translator
{
public:
explicit TranslatorForTest(std::shared_ptr<FeatureProcessorInterface> const & processor,
std::shared_ptr<generator::cache::IntermediateData> const & cache)
: Translator(processor, cache, std::make_shared<FeatureMaker>(cache->GetCache()))
{
SetFilter(std::make_shared<FilterPlanet>());
}
// TranslatorInterface overrides:
std::shared_ptr<TranslatorInterface> Clone() const override
{
CHECK(false, ());
return {};
}
void Merge(TranslatorInterface const &) override { CHECK(false, ()); }
protected:
using Translator::Translator;
};
class TestCameraCollector
{
public:
// Directory name for creating test mwm and temporary files.
string static const kTestDir;
string static const kOsmFileName;
TestCameraCollector()
{
GetStyleReader().SetCurrentStyle(MapStyleMerged);
classificator::Load();
}
static bool Test(string const & osmSourceXML, std::set<pair<uint64_t, uint64_t>> const & trueAnswers)
{
using namespace platform::tests_support;
Platform & platform = GetPlatform();
WritableDirChanger writableDirChanger(kTestDir);
auto const & writableDir = platform.WritableDir();
ScopedDir const scopedDir(kTestDir);
auto const osmRelativePath = base::JoinPath(kTestDir, kOsmFileName);
ScopedFile const osmScopedFile(osmRelativePath, osmSourceXML);
feature::GenerateInfo genInfo;
// Generate intermediate data.
genInfo.m_cacheDir = writableDir;
genInfo.m_intermediateDir = writableDir;
genInfo.m_nodeStorageType = feature::GenerateInfo::NodeStorageType::Index;
genInfo.m_osmFileName = base::JoinPath(writableDir, osmRelativePath);
genInfo.m_osmFileType = feature::GenerateInfo::OsmSourceType::XML;
// Test save intermediate data is OK.
CHECK(GenerateIntermediateData(genInfo), ());
// Test load this data from cached file.
generator::cache::IntermediateDataObjectsCache objectsCache;
auto cache = std::make_shared<generator::cache::IntermediateData>(objectsCache, genInfo);
auto collector =
std::make_shared<CameraCollector>(genInfo.GetIntermediateFileName(CAMERAS_TO_WAYS_FILENAME), cache->GetCache());
auto processor = CreateProcessor(ProcessorType::Noop);
auto translator = std::make_shared<TranslatorForTest>(processor, cache);
translator->SetCollector(collector);
RawGenerator rawGenerator(genInfo);
rawGenerator.GenerateCustom(translator);
CHECK(rawGenerator.Execute(), ());
std::set<pair<uint64_t, uint64_t>> answers;
collector->ForEachCamera([&](auto const & camera)
{
for (auto const & w : camera.m_ways)
answers.emplace(camera.m_id, w);
});
return answers == trueAnswers;
}
};
string const TestCameraCollector::kTestDir = "camera_test";
string const TestCameraCollector::kOsmFileName = "planet" OSM_DATA_FILE_EXTENSION;
UNIT_CLASS_TEST(TestCameraCollector, test_1)
{
string const osmSourceXML =
R"(<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="1" lat="55.779384" lon="37.3699375" version="1">)" +
kSpeedCameraTag + R"(</node><node id="2" lat="55.779304" lon="37.3699375" version="1">)" + kSpeedCameraTag +
R"(</node><node id="3" lat="55.773084" lon="37.3699375" version="1">)" + kSpeedCameraTag +
R"(</node><node id="4" lat="55.773084" lon="37.3699375" version="1">
</node>
<way id="10" version="1">
<nd ref="1"/>
<nd ref="4"/>
<tag k="highway" v="unclassified"/>
</way>
<way id="20" version="1">
<nd ref="1"/>
<nd ref="2"/>
<nd ref="3"/>
<tag k="highway" v="unclassified"/>
</way>
</osm>)";
std::set<pair<uint64_t, uint64_t>> trueAnswers = {{1, 10}, {1, 20}, {2, 20}, {3, 20}};
TEST(TestCameraCollector::Test(osmSourceXML, trueAnswers), ());
}
UNIT_CLASS_TEST(TestCameraCollector, test_2)
{
string const osmSourceXML =
R"(<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="1" lat="55.779384" lon="37.3699375" version="1">)" +
kSpeedCameraTag + R"(</node><node id="2" lat="55.779304" lon="37.3699375" version="1">)" + kSpeedCameraTag +
R"(</node><node id="3" lat="55.773084" lon="37.3699375" version="1">)" + kSpeedCameraTag +
R"(</node><node id="4" lat="55.773024" lon="37.3699375" version="1">)" + kSpeedCameraTag +
R"(</node><node id="5" lat="55.773014" lon="37.3699375" version="1">)" + kSpeedCameraTag +
R"(</node>
<way id="10" version="1">
<nd ref="1"/>
<nd ref="2"/>
<tag k="highway" v="unclassified"/>
</way>
<way id="20" version="1">
<nd ref="1"/>
<nd ref="3"/>
<tag k="highway" v="unclassified"/>
</way>
<way id="30" version="1">
<nd ref="1"/>
<nd ref="3"/>
<nd ref="4"/>
<nd ref="5"/>
<tag k="highway" v="unclassified"/>
</way>
</osm>)";
std::set<pair<uint64_t, uint64_t>> trueAnswers = {{1, 10}, {2, 10}, {1, 20}, {3, 20},
{1, 30}, {3, 30}, {4, 30}, {5, 30}};
TEST(TestCameraCollector::Test(osmSourceXML, trueAnswers), ());
}
UNIT_CLASS_TEST(TestCameraCollector, test_3)
{
string const osmSourceXML =
R"(<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="1" lat="55.779384" lon="37.3699375" version="1">)" +
kSpeedCameraTag +
R"(</node><node id="2" lat="55.779384" lon="37.3699375" version="1">
</node>
<way id="10" version="1">
<nd ref="1"/>
<nd ref="2"/>
<tag k="highway" v="unclassified"/>
</way>
<way id="20" version="1">
<nd ref="1"/>
<nd ref="2"/>
<tag k="highway" v="unclassified"/>
</way>
</osm>)";
std::set<pair<uint64_t, uint64_t>> trueAnswers = {{1, 10}, {1, 20}};
TEST(TestCameraCollector::Test(osmSourceXML, trueAnswers), ());
}
UNIT_CLASS_TEST(TestCameraCollector, test_4)
{
string const osmSourceXML =
R"(<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="1" lat="55.779384" lon="37.3699375" version="1">)" +
kSpeedCameraTag +
R"(</node><way id="10" version="1">
<tag k="highway" v="unclassified"/>
</way>
<way id="20" version="1">
<tag k="highway" v="unclassified"/>
</way>
</osm>)";
std::set<pair<uint64_t, uint64_t>> trueAnswers = {};
TEST(TestCameraCollector::Test(osmSourceXML, trueAnswers), ());
}
UNIT_CLASS_TEST(TestCameraCollector, test_5)
{
string const osmSourceXML =
R"(<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="1" lat="55.779384" lon="37.3699375" version="1"></node>
<way id="10" version="1">
<nd ref="1"/>
<tag k="highway" v="unclassified"/>
</way>
</osm>)";
std::set<pair<uint64_t, uint64_t>> trueAnswers = {};
TEST(TestCameraCollector::Test(osmSourceXML, trueAnswers), ());
}
} // namespace routing_builder

View file

@ -0,0 +1,73 @@
#include "testing/testing.hpp"
#include "generator/cells_merger.hpp"
#include <vector>
namespace
{
UNIT_TEST(CellsMerger_Empty)
{
generator::cells_merger::CellsMerger merger({});
std::vector<m2::RectD> expected;
auto const result = merger.Merge();
TEST_EQUAL(result, expected, ());
}
UNIT_TEST(CellsMerger_One)
{
generator::cells_merger::CellsMerger merger({{{0.0, 0.0}, {1.0, 1.0}}});
std::vector<m2::RectD> expected{{{0.0, 0.0}, {1.0, 1.0}}};
auto const result = merger.Merge();
TEST_EQUAL(result, expected, ());
}
UNIT_TEST(CellsMerger_Two)
{
generator::cells_merger::CellsMerger merger({{{0.0, 0.0}, {1.0, 1.0}}, {{1.0, 0.0}, {2.0, 1.0}}});
std::vector<m2::RectD> expected{{{0.0, 0.0}, {1.0, 1.0}}, {{1.0, 0.0}, {2.0, 1.0}}};
auto const result = merger.Merge();
TEST_EQUAL(result, expected, ());
}
UNIT_TEST(CellsMerger_Four)
{
generator::cells_merger::CellsMerger merger(
{{{0.0, 0.0}, {1.0, 1.0}}, {{1.0, 0.0}, {2.0, 1.0}}, {{0.0, 1.0}, {1.0, 2.0}}, {{1.0, 1.0}, {2.0, 2.0}}});
std::vector<m2::RectD> expected{{{0.0, 0.0}, {2.0, 2.0}}};
auto const result = merger.Merge();
TEST_EQUAL(result, expected, ());
}
UNIT_TEST(CellsMerger_Six)
{
generator::cells_merger::CellsMerger merger({{{0.0, 0.0}, {1.0, 1.0}},
{{1.0, 0.0}, {2.0, 1.0}},
{{0.0, 1.0}, {1.0, 2.0}},
{{1.0, 1.0}, {2.0, 2.0}},
{{2.0, 0.0}, {3.0, 1.0}},
{{2.0, 1.0}, {3.0, 2.0}}});
std::vector<m2::RectD> expected{{{1.0, 0.0}, {3.0, 2.0}}, {{0.0, 0.0}, {1.0, 1.0}}, {{0.0, 1.0}, {1.0, 2.0}}};
auto const result = merger.Merge();
TEST_EQUAL(result, expected, ());
}
UNIT_TEST(CellsMerger_Eight)
{
generator::cells_merger::CellsMerger merger({{{0.0, 0.0}, {1.0, 1.0}},
{{1.0, 0.0}, {2.0, 1.0}},
{{0.0, 1.0}, {1.0, 2.0}},
{{1.0, 1.0}, {2.0, 2.0}},
{{2.0, 0.0}, {3.0, 1.0}},
{{2.0, 1.0}, {3.0, 2.0}},
{{3.0, 0.0}, {4.0, 1.0}},
{{3.0, 1.0}, {4.0, 2.0}}});
std::vector<m2::RectD> expected{{{1.0, 0.0}, {3.0, 2.0}},
{{0.0, 0.0}, {1.0, 1.0}},
{{0.0, 1.0}, {1.0, 2.0}},
{{3.0, 0.0}, {4.0, 1.0}},
{{3.0, 1.0}, {4.0, 2.0}}};
auto const result = merger.Merge();
TEST_EQUAL(result, expected, ());
}
} // namespace

View file

@ -0,0 +1,78 @@
#include "testing/testing.hpp"
#include "generator/cities_boundaries_checker.hpp"
#include "geometry/latlon.hpp"
#include "geometry/mercator.hpp"
#include "base/logging.hpp"
namespace
{
using namespace generator;
using namespace indexer;
UNIT_TEST(CitiesBoundariesChecker_Square)
{
auto const checker = CitiesBoundariesChecker({CityBoundary({{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}, {0.0, 1.0}})});
TEST(checker.InCity({0.5, 0.5}), ());
TEST(checker.InCity({0.0001, 0.0001}), ());
TEST(!checker.InCity({2.0, 2.0}), ());
TEST(!checker.InCity({20.0, 20.0}), ());
TEST(!checker.InCity({-30.0, 30.0}), ());
TEST(!checker.InCity({40.0, -40.0}), ());
}
UNIT_TEST(CitiesBoundariesChecker_NotConvexPolygon)
{
auto const checker =
CitiesBoundariesChecker({CityBoundary({{0.0, 0.0}, {1.0, -1.0}, {0.5, 0.0}, {1.0, 1.0}, {0.0, 1.0}})});
TEST(checker.InCity({0.3, 0.3}), ());
TEST(checker.InCity({0.0001, 0.0001}), ());
TEST(!checker.InCity({2.0, 2.0}), ());
TEST(!checker.InCity({20.0, 20.0}), ());
TEST(!checker.InCity({-30.0, 30.0}), ());
TEST(!checker.InCity({40.0, -40.0}), ());
}
UNIT_TEST(CitiesBoundariesChecker_IntersectedPolygons)
{
auto const checker =
CitiesBoundariesChecker({CityBoundary({{0.0, 0.0}, {1.0, -1.0}, {0.5, 0.0}, {1.0, 1.0}, {0.0, 1.0}}),
CityBoundary({{0.0, 0.0}, {1.0, -1.0}, {0.5, 0.0}, {1.0, 1.0}, {0.0, 1.0}})});
TEST(checker.InCity({0.3, 0.3}), ());
TEST(checker.InCity({0.0001, 0.0001}), ());
TEST(!checker.InCity({2.0, 2.0}), ());
TEST(!checker.InCity({20.0, 20.0}), ());
TEST(!checker.InCity({-30.0, 30.0}), ());
TEST(!checker.InCity({40.0, -40.0}), ());
}
UNIT_TEST(CitiesBoundariesChecker_SeveralPolygons)
{
auto const checker =
CitiesBoundariesChecker({CityBoundary({{0.0, 0.0}, {1.0, -1.0}, {0.5, 0.0}, {1.0, 1.0}, {0.0, 1.0}}),
CityBoundary({{10.0, 0.0}, {11.0, -1.0}, {10.5, 0.0}, {11.0, 1.0}, {10.0, 1.0}}),
CityBoundary({{0.0, 10.0}, {1.0, -11.0}, {0.5, 10.0}, {1.0, 11.0}, {0.0, 11.0}})});
TEST(checker.InCity({0.3, 0.3}), ());
TEST(checker.InCity({0.0001, 0.0001}), ());
TEST(checker.InCity({10.3, 0.3}), ());
TEST(checker.InCity({10.0001, 0.0001}), ());
TEST(checker.InCity({0.3, 10.4}), ());
TEST(checker.InCity({0.0001, 10.0001}), ());
TEST(!checker.InCity({2.0, 2.0}), ());
TEST(!checker.InCity({20.0, 20.0}), ());
TEST(!checker.InCity({-30.0, 30.0}), ());
TEST(!checker.InCity({40.0, -40.0}), ());
}
} // namespace

View file

@ -0,0 +1,95 @@
#include "testing/testing.hpp"
#include "generator/cities_ids_builder.hpp"
#include "generator/feature_builder.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_with_custom_mwms.hpp"
#include "generator/utils.hpp"
#include "indexer/classificator.hpp"
#include "indexer/data_source.hpp"
#include "indexer/feature.hpp"
#include "indexer/feature_processor.hpp"
#include "indexer/feature_to_osm.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "platform/country_defines.hpp"
#include "platform/local_country_file.hpp"
#include "geometry/point2d.hpp"
#include "base/assert.hpp"
#include "base/geo_object_id.hpp"
#include <cstddef>
#include <sstream>
#include <string>
namespace cities_ids_tests
{
using namespace generator;
using namespace generator::tests_support;
class CitiesIdsTest : public TestWithCustomMwms
{
public:
DataSource const & GetDataSource() const { return m_dataSource; }
};
UNIT_CLASS_TEST(CitiesIdsTest, BuildCitiesIds)
{
TestCity paris(m2::PointD(0, 0), "Paris", "fr", 100 /* rank */);
TestCity wien(m2::PointD(1, 1), "Wien", "de", 100 /* rank */);
TestSea marCaribe(m2::PointD(2, 2), "Mar Caribe", "es");
auto testWorldId = BuildWorld([&](TestMwmBuilder & builder)
{
builder.Add(paris);
builder.Add(wien);
builder.Add(marCaribe);
});
auto const worldMwmPath = testWorldId.GetInfo()->GetLocalFile().GetPath(MapFileType::Map);
indexer::FeatureIdToGeoObjectIdOneWay oneWayMap(GetDataSource());
TEST(oneWayMap.Load(), ());
indexer::FeatureIdToGeoObjectIdTwoWay twoWayMap(GetDataSource());
TEST(twoWayMap.Load(), ());
std::unordered_map<uint32_t, uint64_t> originalMapping;
CHECK(ParseFeatureIdToTestIdMapping(worldMwmPath, originalMapping), ());
{
size_t numFeatures = 0;
size_t numLocalities = 0;
auto const test = [&](FeatureType & ft, uint32_t index)
{
++numFeatures;
FeatureID const fid(testWorldId, index);
base::GeoObjectId gid;
FeatureID receivedFid;
bool const mustExist = ftypes::IsLocalityChecker::Instance()(ft);
TEST_EQUAL(oneWayMap.GetGeoObjectId(fid, gid), mustExist, (index, ft.GetNames()));
TEST_EQUAL(twoWayMap.GetGeoObjectId(fid, gid), mustExist, (index, ft.GetNames()));
if (!mustExist)
return;
++numLocalities;
TEST_EQUAL(twoWayMap.GetFeatureID(gid, receivedFid), mustExist, (index));
TEST_EQUAL(receivedFid, fid, ());
CHECK(originalMapping.find(index) != originalMapping.end(), ());
TEST_EQUAL(originalMapping[index], gid.GetSerialId(), ());
};
feature::ForEachFeature(worldMwmPath, test);
TEST_EQUAL(numFeatures, 3, ());
TEST_EQUAL(numLocalities, 2, ());
}
}
} // namespace cities_ids_tests

View file

@ -0,0 +1,162 @@
#include "testing/testing.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include "generator/city_roads_generator.hpp"
#include "routing/city_roads.hpp"
#include "indexer/data_source.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "platform/country_file.hpp"
#include "platform/local_country_file.hpp"
#include "platform/platform.hpp"
#include "base/assert.hpp"
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include <algorithm>
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "defines.hpp"
namespace city_roads_tests
{
using namespace coding;
using namespace platform::tests_support;
using namespace platform;
using namespace routing;
using std::string, std::vector;
// Directory name for creating test mwm and temporary files.
string const kTestDir = "city_roads_generation_test";
// Temporary mwm name for testing.
string const kTestMwm = "test";
void BuildEmptyMwm(LocalCountryFile & country)
{
generator::tests_support::TestMwmBuilder builder(country, feature::DataHeader::MapType::Country);
}
std::unique_ptr<CityRoads> LoadCityRoads(LocalCountryFile const & country)
{
FrozenDataSource dataSource;
auto const regResult = dataSource.RegisterMap(country);
TEST_EQUAL(regResult.second, MwmSet::RegResult::Success, ());
MwmSet::MwmHandle handle(dataSource.GetMwmHandleById(regResult.first));
TEST(handle.IsAlive(), ());
return routing::LoadCityRoads(handle);
}
/// \brief Builds mwm with city_roads section, read the section and compare original feature ids
/// and read ones.
/// \cityRoadFeatureIds a vector of feature ids which should be saved to city_roads
/// section and then read from it.
void TestCityRoadsBuilding(vector<uint32_t> && cityRoadFeatureIds)
{
string const writableDir = GetPlatform().WritableDir();
// Building empty mwm.
LocalCountryFile country(base::JoinPath(writableDir, kTestDir), CountryFile(kTestMwm), 0 /* version */);
ScopedDir const scopedDir(kTestDir);
string const mwmRelativePath = base::JoinPath(kTestDir, kTestMwm + DATA_FILE_EXTENSION);
ScopedFile const scopedMwm(mwmRelativePath, ScopedFile::Mode::Create);
BuildEmptyMwm(country);
// Adding city_roads section to mwm.
string const mwmFullPath = base::JoinPath(writableDir, mwmRelativePath);
vector<uint32_t> originalCityRoadFeatureIds = cityRoadFeatureIds;
routing_builder::SerializeCityRoads(mwmFullPath, std::move(cityRoadFeatureIds));
auto const cityRoads = LoadCityRoads(country);
TEST(cityRoads, ());
// Comparing loading form mwm and expected feature ids.
if (originalCityRoadFeatureIds.empty())
{
TEST(!cityRoads->HaveCityRoads(), ());
return;
}
TEST(cityRoads->HaveCityRoads(), ());
sort(originalCityRoadFeatureIds.begin(), originalCityRoadFeatureIds.end());
size_t const kMaxRoadFeatureId = originalCityRoadFeatureIds.back();
CHECK_LESS(kMaxRoadFeatureId, std::numeric_limits<uint32_t>::max(), ());
// Note. 2 is added below to test all the if-branches of CityRoads::IsCityRoad() method.
for (uint32_t fid = 0; fid < kMaxRoadFeatureId + 2; ++fid)
{
bool const isCityRoad = binary_search(originalCityRoadFeatureIds.cbegin(), originalCityRoadFeatureIds.cend(), fid);
TEST_EQUAL(cityRoads->IsCityRoad(fid), isCityRoad, (fid));
}
}
UNIT_TEST(CityRoadsGenerationTest_Empty)
{
TestCityRoadsBuilding(vector<uint32_t>({}));
}
UNIT_TEST(CityRoadsGenerationTest_FromZero)
{
TestCityRoadsBuilding(vector<uint32_t>({0, 1, 10}));
}
UNIT_TEST(CityRoadsGenerationTest_CommonCase)
{
TestCityRoadsBuilding(vector<uint32_t>({100, 203, 204, 1008, 1009}));
}
UNIT_TEST(CityRoadsGenerationTest_SortedIds1)
{
TestCityRoadsBuilding(vector<uint32_t>({1000, 1203, 11004, 11008, 11009, 11010, 11011, 11012, 11013, 11014, 11015,
11016, 11017, 11018, 11019, 11020, 11021, 11022, 11023, 11024, 11025}));
}
UNIT_TEST(CityRoadsGenerationTest_SortedIds2)
{
TestCityRoadsBuilding(vector<uint32_t>({75000, 75001, 75004, 250000, 250001, 330003, 330007}));
}
UNIT_TEST(CityRoadsGenerationTest_UnsortedIds)
{
TestCityRoadsBuilding(vector<uint32_t>({100, 1, 101, 2, 204, 1008, 1009}));
}
UNIT_TEST(CityRoadsGenerationTest_UnsortedIds2)
{
TestCityRoadsBuilding(
vector<uint32_t>({1000, 1203, 1, 11004, 11, 11009, 11010, 1011, 11012, 11013, 4, 11015,
11016, 11017, 11018, 11019, 11020, 11021, 11022, 11023, 11024, 11025, 2}));
}
UNIT_TEST(CityRoadsGenerationTest_UnsortedIds3)
{
TestCityRoadsBuilding(vector<uint32_t>(
{181998, 354195, 470394, 356217, 331537, 272789, 449031, 420305, 139273, 482371, 85866, 142591, 105206, 217360,
380898, 390284, 96547, 110547, 201338, 428964, 246086, 29141, 179975, 493052, 53822, 238723, 316810, 349592,
154010, 107966, 113307, 97285, 145351, 1153, 433747, 3176, 294890, 52537, 412384, 67264, 102626, 129329,
49219, 289549, 68559, 364318, 211462, 170032, 59900, 257505, 164691, 75922, 209439, 308889, 143329, 140010,
17175, 385681, 147374, 362296, 483109, 257221, 78957, 246540, 111001, 150408, 399397, 285220, 260539, 201792,
378034, 349308, 68275, 278584, 14869, 71593, 34209, 146363, 177111, 319287, 25550, 39549, 130341, 225177,
175089, 458144, 108978, 289265, 482825, 167725, 113023, 11551, 315969, 402715, 408055, 392033, 440099, 295901,
228495, 297924, 89638, 214496, 207133, 362012, 397374, 424077, 343967, 84297, 230518, 159067, 6210, 331991,
354649, 52253, 326650, 370671, 4187, 103637, 305287, 434759, 311923, 180429, 442122, 157044, 145067, 415419,
237154, 404737, 269198, 308605, 57594, 310628, 418737, 359989, 36231, 7505, 226473, 436781, 173066, 97001,
59617, 171771, 335309, 477484, 183747, 64957, 155749, 383375, 333286, 116341, 134386, 447463, 141022, 193133,
271221, 169748, 474166, 60912, 66253, 50231, 98297, 321309, 386693, 456121, 247835, 372693, 365330, 20209,
55571, 82275, 2165, 242495, 388715, 317264, 164407, 490188, 12846, 210451, 484847, 28868, 162385, 129045,
463485, 92956, 337331, 338627, 100319, 182452, 303265, 73616, 262562, 62935, 294606, 466803, 215791, 468825,
76934, 18187, 194429, 32913}));
}
} // namespace city_roads_tests

View file

@ -0,0 +1,153 @@
#include "testing/testing.hpp"
#include "generator/cluster_finder.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
#include "base/assert.hpp"
#include <cmath>
#include <list>
#include <string>
#include <vector>
namespace cluster_finder_tests
{
enum class Type
{
T1 = 1,
T2,
T3
};
std::string DebugPrint(Type const & t)
{
return "T" + std::to_string(static_cast<int>(t));
}
struct NamedPoint
{
NamedPoint(m2::PointD const & point, Type const & type, std::string const & name)
: m_point(point)
, m_type(type)
, m_name(name)
{}
size_t m_id = m_counter++;
m2::PointD m_point;
Type m_type;
std::string m_name;
private:
static size_t m_counter;
};
size_t NamedPoint::m_counter = 0;
m2::RectD GetLimitRect(NamedPoint const & p)
{
return m2::RectD(p.m_point, p.m_point);
}
bool operator==(NamedPoint const & left, NamedPoint const & right)
{
return left.m_id == right.m_id;
}
std::string DebugPrint(NamedPoint const & t)
{
return DebugPrint(t.m_point) + " " + DebugPrint(t.m_type) + " " + t.m_name;
}
auto const getRadiusMFunction = [](NamedPoint const & p)
{
switch (p.m_type)
{
case Type::T1: return 4000;
case Type::T2: return 8000;
case Type::T3: return 16000;
};
UNREACHABLE();
};
auto const isSameFunction = [](NamedPoint const & left, NamedPoint const & right)
{ return left.m_name == right.m_name && left.m_type == right.m_type; };
using ClusterT = std::vector<NamedPoint const *>;
void Sort(std::vector<ClusterT> & data)
{
for (auto & d : data)
std::sort(std::begin(d), std::end(d), [](NamedPoint const * l, NamedPoint const * r) { return l->m_id < r->m_id; });
std::sort(std::begin(data), std::end(data), [](ClusterT const & l, ClusterT const & r)
{
TEST(!l.empty(), ());
TEST(!r.empty(), ());
return l.front()->m_id < r.front()->m_id;
});
}
void Test(std::vector<NamedPoint> const & input, std::vector<ClusterT> & expected)
{
auto result = generator::GetClusters(input, getRadiusMFunction, isSameFunction);
Sort(result);
Sort(expected);
TEST_EQUAL(result, expected, ());
}
UNIT_TEST(ClustersFinder_Empty)
{
std::vector<ClusterT> expected;
Test({}, expected);
}
UNIT_TEST(ClustersFinder_OneElement)
{
std::vector<NamedPoint> in{NamedPoint({0.0, 0.0}, Type::T1, "name")};
std::vector<ClusterT> expected{{&in[0]}};
Test(in, expected);
}
UNIT_TEST(ClustersFinder_TwoElements)
{
std::vector<NamedPoint> in{NamedPoint({0.0, 0.0}, Type::T1, "name"), NamedPoint({0.0001, 0.0001}, Type::T1, "name")};
std::vector<ClusterT> expected{{&in[0], &in[1]}};
Test(in, expected);
}
UNIT_TEST(ClustersFinder_TwoClusters)
{
{
std::vector<NamedPoint> in{NamedPoint({0.0, 0.0}, Type::T1, "name1"),
NamedPoint({0.0001, 0.0001}, Type::T1, "name2")};
std::vector<ClusterT> expected{{&in[1]}, {&in[0]}};
Test(in, expected);
}
{
std::vector<NamedPoint> in{NamedPoint({0.0, 0.0}, Type::T1, "name"), NamedPoint({0.1, 0.1}, Type::T1, "name")};
std::vector<ClusterT> expected{{&in[0]}, {&in[1]}};
Test(in, expected);
}
}
UNIT_TEST(ClustersFinder_ThreeClusters)
{
std::vector<NamedPoint> in{
NamedPoint({0.0, 0.0}, Type::T1, "name"), NamedPoint({0.0, 0.00001}, Type::T1, "name"),
NamedPoint({0.0001, 0.0000}, Type::T1, "name"),
NamedPoint({0.0, 0.0}, Type::T2, "name"), NamedPoint({0.0, 0.001}, Type::T2, "name"),
NamedPoint({0.001, 0.0000}, Type::T2, "name"),
NamedPoint({0.0, 0.0}, Type::T1, "name21")};
std::vector<ClusterT> expected{{&in[1], &in[0], &in[2]}, {&in[3], &in[5], &in[4]}, {&in[6]}};
Test(in, expected);
}
} // namespace cluster_finder_tests

View file

@ -0,0 +1,212 @@
#include "testing/testing.hpp"
#include "generator/feature_builder.hpp"
#include "generator/feature_generator.hpp"
#include "generator/feature_helpers.hpp"
#include "coding/point_coding.hpp"
#include "geometry/cellid.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "indexer/cell_id.hpp"
#include "indexer/scales.hpp"
#include "base/logging.hpp"
#include <string>
#include <vector>
namespace coasts_test
{
using feature::FeatureBuilder;
static m2::PointU D2I(double x, double y)
{
return PointDToPointU(m2::PointD(x, y), kPointCoordBits);
}
class ProcessCoastsBase
{
public:
explicit ProcessCoastsBase(std::vector<std::string> const & vID) : m_vID(vID) {}
protected:
bool HasID(FeatureBuilder const & fb) const
{
TEST(fb.IsCoastCell(), ());
return base::IsExist(m_vID, fb.GetName());
}
private:
std::vector<std::string> const & m_vID;
};
class DoPrintCoasts : public ProcessCoastsBase
{
public:
explicit DoPrintCoasts(std::vector<std::string> const & vID) : ProcessCoastsBase(vID) {}
void operator()(FeatureBuilder const & fb, uint64_t)
{
if (HasID(fb))
{
// Check common params.
TEST(fb.IsArea(), ());
int const upperScale = scales::GetUpperScale();
TEST(fb.IsDrawableInRange(0, upperScale), ());
m2::RectD const rect = fb.GetLimitRect();
LOG(LINFO, ("ID =", fb.GetName(), "Rect =", rect, "Polygons =", fb.GetGeometry()));
// Make bound rect inflated a little.
feature::DistanceToSegmentWithRectBounds distFn(rect);
m2::RectD const boundRect = m2::Inflate(rect, distFn.GetEpsilon(), distFn.GetEpsilon());
using Points = std::vector<m2::PointD>;
using Polygons = std::list<Points>;
Polygons const & poly = fb.GetGeometry();
// Check that all simplifications are inside bound rect.
for (int level = 0; level <= upperScale; ++level)
{
TEST(fb.IsDrawableInRange(level, level), ());
for (auto const & rawPts : poly)
{
Points pts;
SimplifyPoints(distFn, level, rawPts, pts);
LOG(LINFO, ("Simplified. Level =", level, "Points =", pts));
for (auto const & p : pts)
TEST(boundRect.IsPointInside(p), (p));
}
}
}
}
};
class DoCopyCoasts : public ProcessCoastsBase
{
public:
DoCopyCoasts(std::string const & fName, std::vector<std::string> const & vID)
: ProcessCoastsBase(vID)
, m_collector(fName)
{}
void operator()(FeatureBuilder const & fb1, uint64_t)
{
if (HasID(fb1))
m_collector.Collect(fb1);
}
private:
feature::FeaturesCollector m_collector;
};
UNIT_TEST(CellID_CheckRectPoints)
{
int const level = 6;
int const count = 1 << 2 * level;
using Id = m2::CellId<19>;
using Converter = CellIdConverter<mercator::Bounds, Id>;
for (size_t i = 0; i < count; ++i)
{
Id const cell = Id::FromBitsAndLevel(i, level);
std::pair<uint32_t, uint32_t> const xy = cell.XY();
uint32_t const r = 2 * cell.Radius();
uint32_t const bound = (1 << level) * r;
double minX, minY, maxX, maxY;
Converter::GetCellBounds(cell, minX, minY, maxX, maxY);
double minX_, minY_, maxX_, maxY_;
if (xy.first > r)
{
Id neighbour = Id::FromXY(xy.first - r, xy.second, level);
Converter::GetCellBounds(neighbour, minX_, minY_, maxX_, maxY_);
TEST_ALMOST_EQUAL_ULPS(minX, maxX_, ());
TEST_ALMOST_EQUAL_ULPS(minY, minY_, ());
TEST_ALMOST_EQUAL_ULPS(maxY, maxY_, ());
TEST_EQUAL(D2I(minX, minY), D2I(maxX_, minY_), ());
TEST_EQUAL(D2I(minX, maxY), D2I(maxX_, maxY_), ());
}
if (xy.first + r < bound)
{
Id neighbour = Id::FromXY(xy.first + r, xy.second, level);
Converter::GetCellBounds(neighbour, minX_, minY_, maxX_, maxY_);
TEST_ALMOST_EQUAL_ULPS(maxX, minX_, ());
TEST_ALMOST_EQUAL_ULPS(minY, minY_, ());
TEST_ALMOST_EQUAL_ULPS(maxY, maxY_, ());
TEST_EQUAL(D2I(maxX, minY), D2I(minX_, minY_), ());
TEST_EQUAL(D2I(maxX, maxY), D2I(minX_, maxY_), ());
}
if (xy.second > r)
{
Id neighbour = Id::FromXY(xy.first, xy.second - r, level);
Converter::GetCellBounds(neighbour, minX_, minY_, maxX_, maxY_);
TEST_ALMOST_EQUAL_ULPS(minY, maxY_, ());
TEST_ALMOST_EQUAL_ULPS(minX, minX_, ());
TEST_ALMOST_EQUAL_ULPS(maxX, maxX_, ());
TEST_EQUAL(D2I(minX, minY), D2I(minX_, maxY_), ());
TEST_EQUAL(D2I(maxX, minY), D2I(maxX_, maxY_), ());
}
if (xy.second + r < bound)
{
Id neighbour = Id::FromXY(xy.first, xy.second + r, level);
Converter::GetCellBounds(neighbour, minX_, minY_, maxX_, maxY_);
TEST_ALMOST_EQUAL_ULPS(maxY, minY_, ());
TEST_ALMOST_EQUAL_ULPS(minX, minX_, ());
TEST_ALMOST_EQUAL_ULPS(maxX, maxX_, ());
TEST_EQUAL(D2I(minX, maxY), D2I(minX_, minY_), ());
TEST_EQUAL(D2I(maxX, maxY), D2I(maxX_, minY_), ());
}
}
}
/*
UNIT_TEST(WorldCoasts_CheckBounds)
{
std::vector<std::string> vID;
// bounds
vID.push_back("2222");
vID.push_back("3333");
vID.push_back("0000");
vID.push_back("1111");
// bad cells
vID.push_back("2021");
vID.push_back("2333");
vID.push_back("3313");
vID.push_back("1231");
vID.push_back("32003");
vID.push_back("21330");
vID.push_back("20110");
vID.push_back("03321");
vID.push_back("12323");
vID.push_back("1231");
vID.push_back("1311");
//DoPrintCoasts doProcess(vID);
DoCopyCoasts doProcess("/Users/alena/omim/omim/data/WorldCoasts.mwm.tmp", vID);
ForEachFeatureRawFormat("/Users/alena/omim/omim-indexer-tmp/WorldCoasts.mwm.tmp", doProcess);
}
*/
} // namespace coasts_test

View file

@ -0,0 +1,180 @@
#include "testing/testing.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "generator/collector_boundary_postcode.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/osm_element.hpp"
#include "platform/platform.hpp"
#include "coding/read_write_utils.hpp"
#include "geometry/point2d.hpp"
#include "base/scope_guard.hpp"
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace collector_boundary_postcode_tests
{
using generator::tests_support::TestWithClassificator;
using std::string, std::vector, std::unordered_map;
static string const kDumpFileName = "dump.bin";
// 0--1- 2
// | | |
// 3--4--5
// | | |
// 6--7--8
unordered_map<uint64_t, m2::PointD> const kNodes = {
{0, m2::PointD{-1.0, 1.0}}, {1, m2::PointD{0.0, 1.0}}, {2, m2::PointD{1.0, 1.0}},
{3, m2::PointD{-1.0, 0.0}}, {4, m2::PointD{0.0, 0.0}}, {5, m2::PointD{1.0, 0.0}},
{6, m2::PointD{-1.0, -1.0}}, {7, m2::PointD{0.0, -1.0}}, {8, m2::PointD{1.0, -1.0}}};
vector<uint64_t> const kPolygon1 = {4, 5, 2, 1, 4};
vector<uint64_t> const kPolygon2 = {6, 3, 4, 7, 6};
vector<uint64_t> const kPolygon3 = {8, 7, 4, 5, 8};
vector<uint64_t> const kPolygon4 = {0, 1, 4, 4, 0};
unordered_map<uint64_t, WayElement> const kWays = {{1, WayElement{1, kPolygon1}},
{2, WayElement{2, kPolygon2}},
{3, WayElement{3, kPolygon3}},
{4, WayElement{4, kPolygon4}}};
class IntermediateDataReaderTest : public generator::cache::IntermediateDataReaderInterface
{
bool GetNode(uint64_t id, double & lat, double & lon) const override
{
auto const it = kNodes.find(id);
CHECK(it != kNodes.end(), ("Unexpected node requested:", id));
lat = it->second.y;
lon = it->second.x;
return true;
}
bool GetWay(uint64_t id, WayElement & e) override
{
auto const it = kWays.find(id);
CHECK(it != kWays.end(), ("Unexpected way requested:", id));
e = it->second;
return true;
}
bool GetRelation(uint64_t /* id */, RelationElement & /* e */) override { return false; }
};
OsmElement MakePostcodeAreaRelation(uint64_t id, string postcode, uint64_t wayId)
{
auto postcodeAreaRelation = generator_tests::MakeOsmElement(
id, {{"type", "boundary"}, {"boundary", "postal_code"}, {"postal_code", postcode}},
OsmElement::EntityType::Relation);
postcodeAreaRelation.AddMember(wayId, OsmElement::EntityType::Way, "outer");
return postcodeAreaRelation;
}
auto const postcodeAreaRelation1 = MakePostcodeAreaRelation(1 /* id */, "127001" /* postcode */, 1 /* wayId */);
auto const postcodeAreaRelation2 = MakePostcodeAreaRelation(2 /* id */, "127002" /* postcode */, 2 /* wayId */);
auto const postcodeAreaRelation3 = MakePostcodeAreaRelation(3 /* id */, "127003" /* postcode */, 3 /* wayId */);
auto const postcodeAreaRelation4 = MakePostcodeAreaRelation(4 /* id */, "127004" /* postcode */, 4 /* wayId */);
unordered_map<string, vector<m2::PointD>> Read(string const & dumpFilename)
{
FileReader reader(dumpFilename);
ReaderSource<FileReader> src(reader);
unordered_map<string, vector<m2::PointD>> result;
while (src.Size() > 0)
{
string postcode;
rw::ReadNonEmpty(src, postcode);
vector<m2::PointD> geometry;
rw::ReadVectorOfPOD(src, geometry);
result.emplace(std::move(postcode), std::move(geometry));
}
return result;
}
bool CheckPostcodeExists(unordered_map<string, vector<m2::PointD>> const & data, string const & postcode,
vector<m2::PointD> const & geometry)
{
auto const it = data.find(postcode);
if (it == data.end())
return false;
if (it->second.size() != geometry.size())
return false;
for (size_t i = 0; i < geometry.size(); ++i)
if (!AlmostEqualAbs(geometry[i], it->second[i], kMwmPointAccuracy))
return false;
return true;
}
vector<m2::PointD> ConvertIdsToPoints(vector<uint64_t> const & ids)
{
vector<m2::PointD> result(ids.size());
for (size_t i = 0; i < ids.size(); ++i)
{
auto const it = kNodes.find(ids[i]);
CHECK(it != kNodes.end(), ("Unexpected node requested."));
result[i] = it->second;
}
return result;
}
void Check(string const & dumpFilename)
{
auto const data = Read(dumpFilename);
TEST(CheckPostcodeExists(data, "127001", ConvertIdsToPoints(kPolygon1)), (data));
TEST(CheckPostcodeExists(data, "127002", ConvertIdsToPoints(kPolygon2)), (data));
TEST(CheckPostcodeExists(data, "127003", ConvertIdsToPoints(kPolygon3)), (data));
TEST(CheckPostcodeExists(data, "127004", ConvertIdsToPoints(kPolygon4)), (data));
}
UNIT_CLASS_TEST(TestWithClassificator, CollectorBoundaryPostcode_1)
{
SCOPE_GUARD(rmDump, std::bind(Platform::RemoveFileIfExists, cref(kDumpFileName)));
auto cache = std::make_shared<IntermediateDataReaderTest>();
auto collector = std::make_shared<generator::BoundaryPostcodeCollector>(kDumpFileName, cache);
collector->Collect(postcodeAreaRelation1);
collector->Collect(postcodeAreaRelation2);
collector->Collect(postcodeAreaRelation3);
collector->Collect(postcodeAreaRelation4);
collector->Finish();
collector->Finalize();
Check(kDumpFileName);
}
UNIT_CLASS_TEST(TestWithClassificator, CollectorBoundaryPostcode_2)
{
SCOPE_GUARD(rmDump, std::bind(Platform::RemoveFileIfExists, cref(kDumpFileName)));
auto cache = std::make_shared<IntermediateDataReaderTest>();
auto collector1 = std::make_shared<generator::BoundaryPostcodeCollector>(kDumpFileName, cache);
auto collector2 = collector1->Clone();
collector1->Collect(postcodeAreaRelation1);
collector1->Collect(postcodeAreaRelation3);
collector2->Collect(postcodeAreaRelation2);
collector2->Collect(postcodeAreaRelation4);
collector1->Finish();
collector2->Finish();
collector1->Merge(*collector2);
collector1->Finalize();
Check(kDumpFileName);
}
} // namespace collector_boundary_postcode_tests

View file

@ -0,0 +1,230 @@
#include "testing/testing.hpp"
#include "generator/collector_building_parts.hpp"
#include "generator/feature_builder.hpp"
#include "generator/gen_mwm_info.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "generator/intermediate_data.hpp"
#include "generator/osm_element.hpp"
#include "indexer/classificator.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include <memory>
#include <unordered_map>
namespace collector_building_parts_tests
{
using namespace generator::tests_support;
class TestOSMElementCacheReader : public generator::cache::OSMElementCacheReaderInterface
{
public:
TestOSMElementCacheReader(std::unordered_map<generator::cache::Key, RelationElement> & m) : m_mapping(m) {}
// OSMElementCacheReaderInterface overrides:
bool Read(generator::cache::Key /* id */, WayElement & /* value */) override { UNREACHABLE(); }
bool Read(generator::cache::Key id, RelationElement & value) override
{
auto const it = m_mapping.find(id);
if (it == std::cend(m_mapping))
return false;
value = it->second;
return true;
}
private:
std::unordered_map<generator::cache::Key, RelationElement> & m_mapping;
};
class IntermediateDataReaderTest : public generator::cache::IntermediateDataReaderInterface
{
public:
using IdToIds = std::unordered_map<generator::cache::Key, std::vector<generator::cache::Key>>;
static generator::cache::Key const kTopRelationId1;
static generator::cache::Key const kOutlineId1;
static generator::cache::Key const kTopRelationId2;
static generator::cache::Key const kOutlineId2;
IntermediateDataReaderTest()
{
{
RelationElement topRelationElement;
topRelationElement.m_tags = {{"type", "building"}};
topRelationElement.m_relations = {
{kOutlineId1, "outline"},
{3271043, "part"},
{3271041, "part"},
};
for (auto const & p : topRelationElement.m_relations)
m_relationToRelations[p.first].emplace_back(kTopRelationId1);
topRelationElement.m_ways = {
{292789674, "part"}, {242078027, "part"}, {242078028, "part"},
{242077956, "part"}, {242077935, "part"}, {242077967, "part"},
};
for (auto const & p : topRelationElement.m_ways)
m_wayToRelations[p.first].emplace_back(kTopRelationId1);
m_IdToRelation[kTopRelationId1] = std::move(topRelationElement);
}
{
RelationElement topRelationElement;
topRelationElement.m_tags = {{"type", "building"}};
topRelationElement.m_relations = {
{kOutlineId2, "outline"},
};
for (auto const & p : topRelationElement.m_relations)
m_relationToRelations[p.first].emplace_back(kTopRelationId2);
topRelationElement.m_ways = {
{392789674, "part"}, {342078027, "part"}, {342078028, "part"},
{342077956, "part"}, {342077935, "part"}, {342077967, "part"},
};
for (auto const & p : topRelationElement.m_ways)
m_wayToRelations[p.first].emplace_back(kTopRelationId2);
m_IdToRelation[kTopRelationId2] = std::move(topRelationElement);
}
}
// IntermediateDataReaderBase overrides:
bool GetNode(generator::cache::Key, double &, double &) const override { UNREACHABLE(); }
bool GetWay(generator::cache::Key /* id */, WayElement & /* e */) override { UNREACHABLE(); }
bool GetRelation(generator::cache::Key id, RelationElement & e) override
{
auto const it = m_IdToRelation.find(id);
if (it == std::cend(m_IdToRelation))
return false;
e = it->second;
return true;
}
void ForEachRelationByWayCached(generator::cache::Key id, ForEachRelationFn & toDo) override
{
ForEachRelationById(id, toDo, m_wayToRelations);
}
void ForEachRelationByRelationCached(generator::cache::Key id, ForEachRelationFn & toDo) override
{
ForEachRelationById(id, toDo, m_relationToRelations);
}
private:
void ForEachRelationById(generator::cache::Key id, ForEachRelationFn & toDo, IdToIds const & m)
{
auto const it = m.find(id);
if (it == std::cend(m))
return;
TestOSMElementCacheReader reader(m_IdToRelation);
for (auto id : it->second)
toDo(id, reader);
}
std::unordered_map<generator::cache::Key, RelationElement> m_IdToRelation;
IdToIds m_wayToRelations;
IdToIds m_relationToRelations;
};
// static
generator::cache::Key const IntermediateDataReaderTest::kTopRelationId1 = 3271044;
// static
generator::cache::Key const IntermediateDataReaderTest::kOutlineId1 = 1360656;
// static
generator::cache::Key const IntermediateDataReaderTest::kTopRelationId2 = 4271044;
// static
generator::cache::Key const IntermediateDataReaderTest::kOutlineId2 = 2360656;
void TestCollector(std::string const & filename, feature::FeatureBuilder const & fb,
IntermediateDataReaderTest & reader, generator::cache::Key topRelationId)
{
generator::BuildingToBuildingPartsMap m(filename);
auto const & parts = m.GetBuildingPartsByOutlineId(generator::MakeCompositeId(fb));
TEST(!parts.empty(), ());
RelationElement relation;
TEST(reader.GetRelation(topRelationId, relation), ());
for (auto const & k : relation.m_ways)
{
if (k.second != "part")
continue;
auto const id = base::MakeOsmWay(k.first);
TEST(m.HasBuildingPart(id), ());
auto const it = std::find(std::cbegin(parts), std::cend(parts), id);
TEST(it != std::cend(parts), ());
}
for (auto const & k : relation.m_relations)
{
if (k.second != "part")
continue;
auto const id = base::MakeOsmRelation(k.first);
TEST(m.HasBuildingPart(id), ());
auto const it = std::find(std::cbegin(parts), std::cend(parts), id);
TEST(it != std::cend(parts), ());
}
}
UNIT_CLASS_TEST(TestWithClassificator, CollectorBuildingParts_Case1)
{
using namespace platform::tests_support;
ScopedFile file("CollectorBuildingParts", ScopedFile::Mode::DoNotCreate);
auto intermediateReader = std::make_shared<IntermediateDataReaderTest>();
feature::FeatureBuilder fb;
fb.AddOsmId(base::MakeOsmRelation(IntermediateDataReaderTest::kOutlineId1));
fb.AddType(classif().GetTypeByPath({"building"}));
fb.SetArea();
{
generator::BuildingPartsCollector collector(file.GetFullPath(), intermediateReader);
collector.CollectFeature(fb, OsmElement());
collector.Finish();
collector.Finalize();
}
TestCollector(file.GetFullPath(), fb, *intermediateReader, IntermediateDataReaderTest::kTopRelationId1);
}
UNIT_CLASS_TEST(TestWithClassificator, CollectorBuildingParts_Case2)
{
using namespace platform::tests_support;
ScopedFile file("CollectorBuildingParts", ScopedFile::Mode::DoNotCreate);
feature::FeatureBuilder fb1;
fb1.AddOsmId(base::MakeOsmRelation(IntermediateDataReaderTest::kOutlineId1));
fb1.AddType(classif().GetTypeByPath({"building"}));
fb1.SetArea();
feature::FeatureBuilder fb2;
fb2.AddOsmId(base::MakeOsmRelation(IntermediateDataReaderTest::kOutlineId2));
fb2.AddType(classif().GetTypeByPath({"building"}));
fb2.SetArea();
auto intermediateReader = std::make_shared<IntermediateDataReaderTest>();
{
auto collector1 = std::make_shared<generator::BuildingPartsCollector>(file.GetFullPath(), intermediateReader);
// We don't clone cache, because it isn't mutable.
auto collector2 = collector1->Clone(intermediateReader);
collector1->CollectFeature(fb1, OsmElement());
collector1->Finish();
collector2->CollectFeature(fb2, OsmElement());
collector2->Finish();
collector1->Merge(*collector2);
collector1->Finalize();
}
TestCollector(file.GetFullPath(), fb1, *intermediateReader, IntermediateDataReaderTest::kTopRelationId1);
TestCollector(file.GetFullPath(), fb2, *intermediateReader, IntermediateDataReaderTest::kTopRelationId2);
}
} // namespace collector_building_parts_tests

View file

@ -0,0 +1,311 @@
#include "testing/testing.hpp"
#include "generator/collector_routing_city_boundaries.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/osm2type.hpp"
#include "generator/osm_element.hpp"
#include "generator/routing_city_boundaries_processor.hpp"
#include "geometry/area_on_earth.hpp"
#include "geometry/mercator.hpp"
#include "base/geo_object_id.hpp"
#include <algorithm>
#include <memory>
#include <vector>
namespace collector_routing_city_boundaries_tests
{
using namespace generator_tests;
using namespace generator;
using namespace feature;
using BoundariesCollector = RoutingCityBoundariesCollector;
std::string const kDumpFileName = "dump.bin";
std::vector<m2::PointD> const kPolygon1 = {{1, 0}, {0, 2}, {2, 2}, {2, 0}, {0, 0}, {1, 0}};
std::vector<m2::PointD> const kPolygon2 = {{2, 0}, {0, 2}, {2, 2}, {2, 0}, {0, 0}, {2, 0}};
std::vector<m2::PointD> const kPolygon3 = {{3, 0}, {0, 2}, {2, 2}, {2, 0}, {0, 0}, {3, 0}};
std::vector<m2::PointD> const kPolygon4 = {{4, 0}, {0, 2}, {2, 2}, {2, 0}, {0, 0}, {4, 0}};
feature::FeatureBuilder MakeAreaFeatureBuilder(OsmElement element, std::vector<m2::PointD> && geometry)
{
feature::FeatureBuilder result;
auto const filterType = [](uint32_t) { return true; };
ftype::GetNameAndType(&element, result.GetParams(), filterType);
result.SetOsmId(base::MakeOsmRelation(element.m_id));
result.AddPolygon(std::move(geometry));
result.SetArea();
return result;
}
feature::FeatureBuilder MakeNodeFeatureBuilder(OsmElement element)
{
feature::FeatureBuilder result;
auto const filterType = [](uint32_t) { return true; };
ftype::GetNameAndType(&element, result.GetParams(), filterType);
result.SetOsmId(base::MakeOsmNode(element.m_id));
result.SetCenter(mercator::FromLatLon(element.m_lat, element.m_lon));
return result;
}
OsmElement MakeAreaWithPlaceNode(uint64_t id, uint64_t placeId, std::string const & role)
{
auto area = MakeOsmElement(id, {{"boundary", "administrative"}}, OsmElement::EntityType::Relation);
area.m_members.emplace_back(placeId, OsmElement::EntityType::Node, role);
return area;
}
auto const placeRelation1 = MakeOsmElement(1 /* id */, {{"place", "city"}}, OsmElement::EntityType::Relation);
auto const placeRelation2 = MakeOsmElement(2 /* id */, {{"place", "town"}}, OsmElement::EntityType::Relation);
auto const placeRelation3 = MakeOsmElement(3 /* id */, {{"place", "village"}}, OsmElement::EntityType::Relation);
auto const placeRelation4 = MakeOsmElement(4 /* id */, {{"place", "country"}}, OsmElement::EntityType::Relation);
auto const placeNode1 =
MakeOsmElement(9 /* id */, {{"place", "city"}, {"population", "200.000"}}, OsmElement::EntityType::Node);
auto const placeNode2 =
MakeOsmElement(10 /* id */, {{"place", "town"}, {"population", "10 000"}}, OsmElement::EntityType::Node);
auto const placeNode3 =
MakeOsmElement(11 /* id */, {{"place", "village"}, {"population", "1000"}}, OsmElement::EntityType::Node);
auto const placeNode4 =
MakeOsmElement(12 /* id */, {{"place", "country"}, {"population", "147000000"}}, OsmElement::EntityType::Node);
auto const relationWithLabel1 = MakeAreaWithPlaceNode(5 /* id */, 9 /* placeId */, "label" /* role */);
auto const relationWithLabel2 = MakeAreaWithPlaceNode(6 /* id */, 10 /* placeId */, "admin_centre" /* role */);
auto const relationWithLabel3 = MakeAreaWithPlaceNode(7 /* id */, 11 /* placeId */, "label" /* role */);
auto const relationWithLabel4 = MakeAreaWithPlaceNode(8 /* id */, 12 /* placeId */, "country" /* role */);
/*
void Collect(BoundariesCollector & collector, std::vector<OsmElement> const & elements,
std::vector<std::vector<m2::PointD>> geometries = {})
{
for (size_t i = 0; i < elements.size(); ++i)
{
auto const & element = elements[i];
auto featureBuilder = element.IsNode() ? MakeNodeFeatureBuilder(element)
: MakeAreaFeatureBuilder(element, std::move(geometries[i]));
if (BoundariesCollector::FilterOsmElement(element))
collector.Process(featureBuilder, element);
}
}
void Collect(std::shared_ptr<CollectorInterface> & collector,
std::vector<OsmElement> const & elements,
std::vector<std::vector<m2::PointD>> const & geometries = {})
{
auto boundariesCollector = dynamic_cast<BoundariesCollector*>(collector.get());
Collect(*boundariesCollector, elements, geometries);
}
std::vector<std::vector<m2::PointD>> ReadBoundaries(std::string const & dumpFilename)
{
FileReader reader(dumpFilename);
ReaderSource<FileReader> src(reader);
std::vector<std::vector<m2::PointD>> result;
std::vector<m2::PointD> boundary;
while (src.Size() > 0)
{
rw::ReadVectorOfPOD(src, boundary);
result.emplace_back(std::move(boundary));
}
return result;
}
bool CheckPolygonExistance(std::vector<std::vector<m2::PointD>> const & polygons,
std::vector<m2::PointD> const & polygonToFind)
{
for (auto const & polygon : polygons)
{
bool same = true;
if (polygon.size() != polygonToFind.size())
continue;
for (size_t i = 0; i < polygon.size(); ++i)
{
if (!AlmostEqualAbs(polygon[i], polygonToFind[i], 1e-6))
{
same = false;
break;
}
}
if (same)
return true;
}
return false;
}
void Check(std::string const & filename, std::string const & dumpFilename)
{
using Writer = RoutingCityBoundariesWriter;
auto const boundaries = ReadBoundaries(dumpFilename);
TEST(CheckPolygonExistance(boundaries, kPolygon1), ());
TEST(CheckPolygonExistance(boundaries, kPolygon2), ());
TEST(CheckPolygonExistance(boundaries, kPolygon3), ());
TEST(!CheckPolygonExistance(boundaries, kPolygon4), ());
auto const nodeToBoundaryFilename = Writer::GetNodeToBoundariesFilename(filename);
auto nodeToBoundary = routing_city_boundaries::LoadNodeToBoundariesData(nodeToBoundaryFilename);
TEST(nodeToBoundary.count(9), ());
TEST(nodeToBoundary.count(10), ());
TEST(nodeToBoundary.count(11), ());
TEST(!nodeToBoundary.count(12), ());
TEST(CheckPolygonExistance({nodeToBoundary[9].back().GetOuterGeometry()}, kPolygon1), ());
TEST(CheckPolygonExistance({nodeToBoundary[10].back().GetOuterGeometry()}, kPolygon2), ());
TEST(CheckPolygonExistance({nodeToBoundary[11].back().GetOuterGeometry()}, kPolygon3), ());
auto const nodeToLocalityFilename = Writer::GetNodeToLocalityDataFilename(filename);
auto nodeToLocality = routing_city_boundaries::LoadNodeToLocalityData(nodeToLocalityFilename);
TEST(nodeToLocality.count(9), ());
TEST(nodeToLocality.count(10), ());
TEST(nodeToLocality.count(11), ());
TEST(!nodeToLocality.count(12), ());
TEST_EQUAL(nodeToLocality[9].m_place, ftypes::LocalityType::City, ());
TEST_EQUAL(nodeToLocality[9].m_population, 200000, ());
TEST_EQUAL(nodeToLocality[10].m_place, ftypes::LocalityType::Town, ());
TEST_EQUAL(nodeToLocality[10].m_population, 10000, ());
TEST_EQUAL(nodeToLocality[11].m_place, ftypes::LocalityType::Village, ());
TEST_EQUAL(nodeToLocality[11].m_population, 1000, ());
}
*/
std::vector<m2::PointD> FromLatLons(std::vector<ms::LatLon> const & latlons)
{
std::vector<m2::PointD> points;
for (auto const & latlon : latlons)
points.emplace_back(mercator::FromLatLon(latlon));
return points;
}
double CalculateEarthAreaForConvexPolygon(std::vector<ms::LatLon> const & latlons)
{
CHECK(!latlons.empty(), ());
double area = 0.0;
auto const & base = latlons.front();
for (size_t i = 1; i < latlons.size() - 1; ++i)
area += ms::AreaOnEarth(base, latlons[i], latlons[i + 1]);
return area;
}
/*
UNIT_CLASS_TEST(TestWithClassificator, CollectorRoutingCityBoundaries_1)
{
auto const filename = generator_tests::GetFileName();
SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename)));
SCOPE_GUARD(rmDump, std::bind(Platform::RemoveFileIfExists, std::cref(kDumpFileName)));
std::shared_ptr<generator::cache::IntermediateDataReader> cache;
auto c1 = std::make_shared<BoundariesCollector>(filename, kDumpFileName, cache);
Collect(*c1, {placeRelation1, placeRelation2, placeRelation3, placeRelation4},
{kPolygon1, kPolygon2, kPolygon3, kPolygon4});
Collect(*c1, {relationWithLabel1, relationWithLabel2, relationWithLabel3, relationWithLabel4},
{kPolygon1, kPolygon2, kPolygon3, kPolygon4});
Collect(*c1, {placeNode1, placeNode2, placeNode3, placeNode4});
c1->Finish();
c1->Finalize();
Check(filename, kDumpFileName);
}
UNIT_CLASS_TEST(TestWithClassificator, CollectorRoutingCityBoundaries_2)
{
auto const filename = generator_tests::GetFileName();
SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename)));
SCOPE_GUARD(rmDump, std::bind(Platform::RemoveFileIfExists, std::cref(kDumpFileName)));
std::shared_ptr<generator::cache::IntermediateDataReader> cache;
auto c1 = std::make_shared<BoundariesCollector>(filename, kDumpFileName, cache);
auto c2 = c1->Clone();
Collect(*c1, {placeRelation1, placeRelation2}, {kPolygon1, kPolygon2});
Collect(c2, {placeRelation3, placeRelation4}, {kPolygon3, kPolygon4});
Collect(*c1, {relationWithLabel1, relationWithLabel2}, {kPolygon1, kPolygon2});
Collect(c2, {relationWithLabel3, relationWithLabel4}, {kPolygon3, kPolygon4});
Collect(*c1, {placeNode1, placeNode2});
Collect(c2, {placeNode3, placeNode4});
c1->Finish();
c2->Finish();
c1->Merge(*c2);
c1->Finalize();
Check(filename, kDumpFileName);
}
*/
UNIT_TEST(AreaOnEarth_Convex_Polygon_1)
{
auto const a = ms::LatLon(55.8034965, 37.696754);
auto const b = ms::LatLon(55.7997909, 37.7830427);
auto const c = ms::LatLon(55.8274225, 37.8150381);
auto const d = ms::LatLon(55.843037, 37.7401892);
auto const e = ms::LatLon(55.8452096, 37.7019668);
std::vector<ms::LatLon> const latlons = {a, b, c, d, e};
std::vector<m2::PointD> const points = FromLatLons(latlons);
double const areaTriangulated = ms::AreaOnEarth(a, b, c) + ms::AreaOnEarth(a, c, d) + ms::AreaOnEarth(a, d, e);
double const areaOnEarth = generator::AreaOnEarth(points);
TEST(AlmostEqualRel(areaTriangulated, areaOnEarth, 1e-6), (areaTriangulated, areaOnEarth));
}
UNIT_TEST(AreaOnEarth_Convex_Polygon_2)
{
std::vector<ms::LatLon> const latlons = {
{55.6348484, 36.025526}, {55.0294112, 36.8959709}, {54.9262448, 38.3719426},
{55.3561515, 39.3275397}, {55.7548279, 39.4458067}, {56.3020039, 39.3322704},
{56.5140099, 38.6368606}, {56.768935, 37.0473526}, {56.4330113, 35.6234183},
};
std::vector<m2::PointD> const points = FromLatLons(latlons);
double const areaOnEarth = generator::AreaOnEarth(points);
double const areaForConvexPolygon = CalculateEarthAreaForConvexPolygon(latlons);
TEST(AlmostEqualRel(areaForConvexPolygon, areaOnEarth, 1e-6), (areaForConvexPolygon, areaOnEarth));
}
UNIT_TEST(AreaOnEarth_Concave_Polygon)
{
auto const a = ms::LatLon(40.3429746, -7.6513617);
auto const b = ms::LatLon(40.0226711, -7.7356029);
auto const c = ms::LatLon(39.7887079, -7.0626206);
auto const d = ms::LatLon(40.1038622, -7.0394143);
auto const e = ms::LatLon(40.0759637, -6.7145263);
auto const f = ms::LatLon(40.2861884, -6.8637096);
auto const g = ms::LatLon(40.4175634, -6.5123);
auto const h = ms::LatLon(40.4352289, -6.9101221);
auto const i = ms::LatLon(40.6040786, -7.0825117);
auto const j = ms::LatLon(40.4301821, -7.2482709);
std::vector<ms::LatLon> const latlons = {a, b, c, d, e, f, g, h, i, j};
std::vector<m2::PointD> const points = FromLatLons(latlons);
double areaTriangulated = ms::AreaOnEarth(a, b, c) + ms::AreaOnEarth(a, c, d) + ms::AreaOnEarth(a, d, f) +
ms::AreaOnEarth(d, e, f) + ms::AreaOnEarth(a, f, j) + ms::AreaOnEarth(f, h, j) +
ms::AreaOnEarth(f, g, h) + ms::AreaOnEarth(h, i, j);
double const areaOnEarth = generator::AreaOnEarth(points);
TEST(AlmostEqualRel(areaTriangulated, areaOnEarth, 1e-6), ());
}
} // namespace collector_routing_city_boundaries_tests

View file

@ -0,0 +1,25 @@
#include "generator/generator_tests/common.hpp"
#include "platform/platform.hpp"
namespace generator_tests
{
OsmElement MakeOsmElement(uint64_t id, Tags const & tags, OsmElement::EntityType t)
{
OsmElement el;
el.m_id = id;
el.m_type = t;
for (auto const & t : tags)
el.AddTag(t.first, t.second);
return el;
}
std::string GetFileName(std::string const & filename)
{
auto & platform = GetPlatform();
return filename.empty() ? platform.TmpPathForFile() : platform.TmpPathForFile(filename);
}
} // namespace generator_tests

View file

@ -0,0 +1,16 @@
#pragma once
#include "generator/osm_element.hpp"
#include <string>
#include <vector>
namespace generator_tests
{
using Tags = std::vector<std::pair<std::string, std::string>>;
OsmElement MakeOsmElement(uint64_t id, Tags const & tags, OsmElement::EntityType t);
std::string GetFileName(std::string const & filename = {});
} // namespace generator_tests

View file

@ -0,0 +1,162 @@
#include "testing/testing.hpp"
#include "generator/complex_loader.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "generator/hierarchy_entry.hpp"
#include "indexer/classificator.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/complex/tree_node.hpp"
#include "coding/csv_reader.hpp"
#include "base/geo_object_id.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
namespace complex_loader_tests
{
using generator::tests_support::TestWithClassificator;
using platform::tests_support::ScopedFile;
std::string const kCsv1 =
"13835058055284963881 9223372037111861697;"
";"
"1;"
"37.5303271;"
"67.3684086;"
"amenity-university;"
"Lomonosov Moscow State Univesity;"
"Russia_Moscow\n"
"9223372036879747192 9223372036879747192;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5272372;"
"67.3775872;"
"leisure-garden;"
"Ботанический сад МГУ;"
"Russia_Moscow\n"
"9223372036938640141 9223372036938640141;"
"9223372036879747192 9223372036879747192;"
"3;"
"37.5274156;"
"67.3758813;"
"amenity-university;"
"Отдел флоры;"
"Russia_Moscow\n"
"9223372036964008573 9223372036964008573;"
"9223372036879747192 9223372036879747192;"
"3;"
"37.5279467;"
"67.3756452;"
"amenity-university;"
"Дендрарий Ботанического сада МГУ;"
"Russia_Moscow\n"
"4611686019330739245 4611686019330739245;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5357492;"
"67.3735142;"
"historic-memorial;"
"Александр Иванович Герцен;"
"Russia_Moscow\n"
"4611686019330739269 4611686019330739269;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5351269;"
"67.3741606;"
"historic-memorial;"
"Николай Гаврилович Чернышевский;"
"Russia_Moscow\n"
"4611686019330739276 4611686019330739276;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5345234;"
"67.3723206;"
"historic-memorial;"
"Николай Егорович Жуковский;"
"Russia_Moscow\n"
"13835058055283526046 9223372037165115538;"
";"
"1;"
"37.6112346;"
"67.4426053;"
"place-square;"
"Манежная площадь;"
"Russia_Moscow\n"
"4611686023709502091 4611686023709502091;"
"13835058055283526046 9223372037165115538;"
"2;"
"37.6112346;"
"67.4426053;"
"place-square;"
"Манежная площадь;"
"Russia_Moscow\n"
"4611686024983153989 4611686024983153989;"
"13835058055283526046 9223372037165115538;"
"2;"
"37.6112346;"
"67.4426053;"
"amenity-cafe;"
"ShakeUp;"
"Russia_Moscow\n";
void SortForest(tree_node::types::Ptrs<generator::HierarchyEntry> & forest)
{
std::sort(std::begin(forest), std::end(forest),
[](auto const & lhs, auto const & rhs) { return lhs->GetData().m_id < rhs->GetData().m_id; });
}
UNIT_CLASS_TEST(TestWithClassificator, Complex_IsComplex)
{
auto const filename = "test.csv";
ScopedFile sf(filename, kCsv1);
auto forest = generator::hierarchy::LoadHierachy(sf.GetFullPath());
// We need to sort forest, because LoadHierachy() returns forest, where trees aren't ordered.
SortForest(forest);
TEST_EQUAL(forest.size(), 2, ());
TEST(!generator::IsComplex(forest[0]), ());
TEST(generator::IsComplex(forest[1]), ());
}
UNIT_CLASS_TEST(TestWithClassificator, Complex_GetCountry)
{
auto const filename = "test.csv";
ScopedFile sf(filename, kCsv1);
auto forest = generator::hierarchy::LoadHierachy(sf.GetFullPath());
// We need to sort forest, because LoadHierachy() returns forest, where trees aren't ordered.
SortForest(forest);
TEST_EQUAL(forest.size(), 2, ());
TEST_EQUAL(generator::GetCountry(forest[0]), "Russia_Moscow", ());
TEST_EQUAL(generator::GetCountry(forest[1]), "Russia_Moscow", ());
}
UNIT_CLASS_TEST(TestWithClassificator, Complex_ComplexLoader)
{
auto const filename = "test.csv";
ScopedFile sf(filename, kCsv1);
generator::ComplexLoader const loader(sf.GetFullPath());
auto const forest = loader.GetForest("Russia_Moscow");
TEST_EQUAL(forest.Size(), 1, ());
forest.ForEachTree([](auto const & tree) { TEST_EQUAL(tree_node::Size(tree), 7, ()); });
}
UNIT_CLASS_TEST(TestWithClassificator, Complex_GetOrCreateComplexLoader)
{
auto const filename = "test.csv";
ScopedFile sf(filename, kCsv1);
auto const & loader = generator::GetOrCreateComplexLoader(sf.GetFullPath());
auto const forest = loader.GetForest("Russia_Moscow");
TEST_EQUAL(forest.Size(), 1, ());
forest.ForEachTree([](auto const & tree) { TEST_EQUAL(tree_node::Size(tree), 7, ()); });
}
} // namespace complex_loader_tests

View file

@ -0,0 +1,256 @@
#include "testing/testing.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/collector_collection.hpp"
#include "generator/collector_tag.hpp"
#include "generator/cross_mwm_osm_ways_collector.hpp"
#include "platform/platform.hpp"
#include "indexer/classificator.hpp"
#include "indexer/classificator_loader.hpp"
#include "geometry/mercator.hpp"
#include "base/assert.hpp"
#include "base/scope_guard.hpp"
#include <cstdint>
#include <fstream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace cross_mwm_osm_ways_collector_tests
{
using namespace generator;
using namespace generator_tests;
std::string const kTmpDirName = "cross_mwm_ways";
std::vector<std::string> const kHighwayUnclassifiedPath = {"highway", "unclassified"};
std::vector<std::pair<std::string, std::string>> const kHighwayUnclassified = {{"highway", "unclassified"}};
std::string const kOsmWayId_1 = std::to_string(base::MakeOsmWay(1).GetEncodedId());
std::string const kOsmWayId_2 = std::to_string(base::MakeOsmWay(2).GetEncodedId());
std::string const kOsmWayId_3 = std::to_string(base::MakeOsmWay(3).GetEncodedId());
class CrossMwmWayCollectorTest
{
public:
CrossMwmWayCollectorTest()
{
classificator::Load();
auto const targetDir = GetPlatform().WritableDir();
m_affiliation = std::make_shared<feature::CountriesFilesAffiliation>(targetDir, true /*haveBordersForWholeWorld*/);
auto const intermediateDir = base::JoinPath(targetDir, kTmpDirName);
if (!Platform::MkDirChecked(intermediateDir))
MYTHROW(FileSystemException, ("Can't create intermediateDir", intermediateDir));
m_intermediateDir = intermediateDir;
}
feature::CountriesFilesAffiliation const & GetCountries() { return *m_affiliation; }
~CrossMwmWayCollectorTest() { Platform::RmDirRecursively(m_intermediateDir); }
std::shared_ptr<CollectorCollection> InitCollection()
{
auto collection = std::make_shared<CollectorCollection>();
collection->Append(std::make_shared<CrossMwmOsmWaysCollector>(m_intermediateDir, m_affiliation));
return collection;
}
void Check(std::string const & countryName, std::vector<std::string> const & answers) const
{
std::ifstream stream(FormatPath(countryName));
size_t pos = 0;
std::string line;
while (std::getline(stream, line).good())
{
TEST_EQUAL(line, answers[pos], (countryName));
pos++;
}
TEST_EQUAL(pos, answers.size(), (countryName));
}
private:
std::string FormatPath(std::string const & countryName) const
{
return base::JoinPath(m_intermediateDir, CROSS_MWM_OSM_WAYS_DIR, countryName);
}
std::shared_ptr<feature::CountriesFilesAffiliation> m_affiliation;
std::string m_intermediateDir;
};
class Sample1Test : public CrossMwmWayCollectorTest
{
public:
// Cross mwm collector format is:
// osmId crossMwmSegmentsNumber [crossMwmSegmentsId forwardIsEnter]+
void Checker()
{
std::vector<std::string> answersFor_RomaniaNorth_West = {kOsmWayId_1 + " 1 1 0 ", kOsmWayId_2 + " 1 0 0 "};
std::vector<std::string> answersFor_Hungary_Northern_Great_Plain = {kOsmWayId_1 + " 1 1 1 ",
kOsmWayId_2 + " 1 0 1 "};
std::vector<std::string> answersFor_Russia_Moscow = {kOsmWayId_3 + " 1 0 1 "};
std::vector<std::string> answersFor_Russia_Moscow_Oblast_West = {kOsmWayId_3 + " 1 0 0 "};
Check("Romania_North_West", answersFor_RomaniaNorth_West);
Check("Hungary_Northern Great Plain", answersFor_Hungary_Northern_Great_Plain);
Check("Russia_Moscow", answersFor_Russia_Moscow);
Check("Russia_Moscow Oblast_West", answersFor_Russia_Moscow_Oblast_West);
}
};
feature::FeatureBuilder CreateFeatureBuilderFromOsmWay(uint64_t osmId, std::vector<ms::LatLon> const & llPoints)
{
feature::FeatureBuilder fb;
fb.AddOsmId(base::MakeOsmWay(osmId));
std::vector<m2::PointD> points;
base::Transform(llPoints, std::back_inserter(points), [](ms::LatLon const & ll) { return mercator::FromLatLon(ll); });
fb.AssignPoints(std::move(points));
fb.SetLinear();
fb.AddType(classif().GetTypeByPath(kHighwayUnclassifiedPath));
return fb;
}
void AddOsmWayByPoints(uint64_t osmId, std::vector<ms::LatLon> const & points,
std::shared_ptr<CollectorInterface> const & collection)
{
collection->CollectFeature(CreateFeatureBuilderFromOsmWay(osmId, points),
MakeOsmElement(osmId, kHighwayUnclassified, OsmElement::EntityType::Way));
}
void AppendFirstWayFromRomaniaToHungary(std::shared_ptr<CollectorInterface> const & collection)
{
{
// In "Romania_North_West", out of "Hungary_Northern Great Plain"
ms::LatLon a{47.48897, 22.22737};
// In "Romania_North_West", out of "Hungary_Northern Great Plain"
ms::LatLon b{47.52341, 22.24097};
// Out of "Romania_North_West", in "Hungary_Northern Great Plain"
ms::LatLon c{47.63462, 22.04041};
AddOsmWayByPoints(1 /* osmId */, {a, b, c} /* points */, collection);
}
}
void AppendSecondWayFromRomaniaToHungary(std::shared_ptr<CollectorInterface> const & collection)
{
{
// In "Romania_North_West", out of "Hungary_Northern Great Plain"
ms::LatLon a{47.36594, 22.16958};
// Out of "Romania_North_West", in "Hungary_Northern Great Plain"
ms::LatLon b{47.49356, 21.77018};
AddOsmWayByPoints(2 /* osmId */, {a, b} /* points */, collection);
}
}
void AppendThirdWayEndsExactlyAtRussiaMoscowBorder(std::shared_ptr<CollectorInterface> const & collection)
{
{
// At "Russia_Moscow" border
ms::LatLon a{55.50334, 36.82098};
// In "Russia_Moscow", out of "Russia_Moscow Oblast_West"
ms::LatLon b{55.50222, 36.82246};
AddOsmWayByPoints(3 /* osmId */, {a, b} /* points */, collection);
}
}
UNIT_CLASS_TEST(Sample1Test, OneCollectorTest)
{
auto collection1 = InitCollection();
AppendFirstWayFromRomaniaToHungary(collection1);
AppendSecondWayFromRomaniaToHungary(collection1);
AppendThirdWayEndsExactlyAtRussiaMoscowBorder(collection1);
collection1->Finalize();
Checker();
}
UNIT_CLASS_TEST(Sample1Test, TwoCollectorsTest)
{
auto collection1 = InitCollection();
AppendFirstWayFromRomaniaToHungary(collection1);
AppendThirdWayEndsExactlyAtRussiaMoscowBorder(collection1);
auto collection2 = collection1->Clone();
AppendSecondWayFromRomaniaToHungary(collection2);
collection1->Finish();
collection2->Finish();
collection1->Merge(*collection2);
collection1->Finalize();
Checker();
}
UNIT_CLASS_TEST(CrossMwmWayCollectorTest, Lithuania_Belarus_Kamenny_Log)
{
auto collection = InitCollection();
ms::LatLon const connected{54.5442780, 25.6996741};
auto const countries = GetCountries().GetAffiliations(mercator::FromLatLon(connected));
TEST_EQUAL(countries.size(), 1, ());
// https://www.openstreetmap.org/way/614091318 should present in Lithuania and Belarus
AddOsmWayByPoints(1,
{{54.5460103, 25.6945156},
{54.5454276, 25.6952895},
{54.5453567, 25.6953987},
{54.5453056, 25.6955672},
{54.5443252, 25.6994996},
{54.5443107, 25.6995562}, // 5 segId starts here
connected},
collection);
collection->Finalize();
Check("Lithuania_East", {kOsmWayId_1 + " 1 5 0 "});
Check("Belarus_Hrodna Region", {kOsmWayId_1 + " 1 5 1 "});
}
UNIT_CLASS_TEST(CrossMwmWayCollectorTest, Belarus_Lithuania_Kamenny_Log)
{
auto collection = InitCollection();
ms::LatLon const connected{54.5443346, 25.6997363};
auto const countries = GetCountries().GetAffiliations(mercator::FromLatLon(connected));
TEST_EQUAL(countries.size(), 2, ());
// https://www.openstreetmap.org/way/533044131
AddOsmWayByPoints(1,
{
{54.5442277, 25.7001698},
{54.5442419, 25.7001125},
connected,
},
collection);
// https://www.openstreetmap.org/way/489294139
AddOsmWayByPoints(2,
{
connected,
{54.5443587, 25.6996293},
{54.5443765, 25.6995660},
},
collection);
collection->Finalize();
Check("Belarus_Hrodna Region", {kOsmWayId_1 + " 1 1 0 ", kOsmWayId_2 + " 2 0 1 1 0 "});
Check("Lithuania_East", {kOsmWayId_1 + " 1 1 1 ", kOsmWayId_2 + " 2 0 1 1 1 "});
}
} // namespace cross_mwm_osm_ways_collector_tests

View file

@ -0,0 +1,593 @@
#include "testing/testing.hpp"
#include "generator/descriptions_section_builder.hpp"
#include "descriptions/serdes.hpp"
#include "indexer/classificator_loader.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_mwm.hpp"
#include "coding/files_container.hpp"
#include "base/assert.hpp"
#include "base/file_name_utils.hpp"
#include "base/stl_helpers.hpp"
#include <algorithm>
#include <fstream>
#include <iterator>
#include <map>
#include <numeric>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
namespace generator_tests
{
using namespace generator;
class TestDescriptionSectionBuilder
{
public:
using PageT = std::pair<std::string_view, std::string_view>;
struct WikiData
{
std::string m_url;
// A collection of pairs of languages and content.
std::vector<PageT> m_pages;
};
static std::string const kMwmFile;
static std::string const kDirPages;
static std::vector<WikiData> const kWikiData;
TestDescriptionSectionBuilder()
: m_writableDir(GetPlatform().WritableDir())
, m_wikiDir(base::JoinPath(m_writableDir, kDirPages))
{
for (auto const & m : kWikiData)
{
auto const dir = DescriptionsCollector::MakePathForWikipedia(m_wikiDir, m.m_url);
CHECK(Platform::MkDirRecursively(dir), ());
for (auto const & [lang, content] : m.m_pages)
{
auto const file = base::JoinPath(dir, std::string{lang} + ".html");
std::ofstream stream(file);
stream << content;
}
}
classificator::Load();
}
~TestDescriptionSectionBuilder() { CHECK(Platform::RmDirRecursively(m_wikiDir), ()); }
void MakeDescriptions() const
{
DescriptionsCollector collector(m_wikiDir, kMwmFile);
ForEachUrlMock(collector);
auto const & descriptions = collector.m_collection;
auto const & stat = collector.m_stat;
TEST_EQUAL(GetTestDataPages(), descriptions.GetFeaturesCount(), ());
TEST_EQUAL(GetTestDataPages(), stat.GetNumberOfPages(), ());
TEST_EQUAL(GetTestDataSize(), stat.GetTotalSize(), ());
TEST(CheckLangs(stat.GetLangStatistics()), ());
}
void MakePath() const
{
std::string trueAnswer = "/wikiDir/en.wikipedia.org/wiki/Helsinki_Olympic_Stadium";
{
std::string const wikiDir = "/wikiDir/";
std::string const wikiUrl = "http://en.wikipedia.org/wiki/Helsinki_Olympic_Stadium/";
auto const answer = DescriptionsCollector::MakePathForWikipedia(wikiDir, wikiUrl);
TEST_EQUAL(trueAnswer, answer, ());
}
{
std::string const wikiDir = "/wikiDir";
std::string const wikiUrl = "https://en.wikipedia.org/wiki/Helsinki_Olympic_Stadium";
auto const answer = DescriptionsCollector::MakePathForWikipedia(wikiDir, wikiUrl);
TEST_EQUAL(trueAnswer, answer, ());
}
}
void FindPageAndFill() const
{
{
DescriptionsCollector collector(m_wikiDir, kMwmFile);
CHECK(!kWikiData.empty(), ());
auto const & first = kWikiData.front();
descriptions::LangMeta meta;
auto const path = DescriptionsCollector::MakePathForWikipedia(m_wikiDir, first.m_url);
TEST_EQUAL(collector.FindPageAndFill(path, meta), SumPageSizes(first.m_pages), ());
TEST(CheckLangs(meta, first.m_pages), ());
}
{
DescriptionsCollector collector(m_wikiDir, kMwmFile);
StringUtf8Multilang str;
std::string const badUrl = "https://en.wikipedia.org/wiki/Not_exists";
auto const path = DescriptionsCollector::MakePathForWikipedia(m_wikiDir, badUrl);
descriptions::LangMeta meta;
TEST_EQUAL(collector.FindPageAndFill(path, meta), 0, ());
TEST(meta.empty(), ());
}
}
// The test hopes that the kWikiData.front() has a 'en' lang.
void FillStringFromFile() const
{
CHECK(!kWikiData.empty(), ());
auto const & first = kWikiData.front();
std::string const lang = "en";
auto const path = DescriptionsCollector::MakePathForWikipedia(m_wikiDir, first.m_url);
auto const fullPath = base::JoinPath(path, (lang + ".html"));
// This is a private function and should take the right path fullPath.
std::string const str = DescriptionsCollector::FillStringFromFile(fullPath);
auto const it = base::FindIf(first.m_pages, [&](auto const & p) { return p.first == lang; });
CHECK(it != std::end(first.m_pages), ());
TEST_EQUAL(str.size(), it->second.size(), ());
}
void GetFeatureDescription() const
{
DescriptionsCollector collector(m_wikiDir, kMwmFile);
CHECK(!kWikiData.empty(), ());
auto const & first = kWikiData.front();
auto const wikiUrl = first.m_url;
auto const path = DescriptionsCollector::MakePathForWikipedia(m_wikiDir, wikiUrl);
descriptions::LangMeta meta;
auto const size = collector.FindPageAndFill(path, meta);
TEST_EQUAL(size, SumPageSizes(first.m_pages), ());
CHECK_NOT_EQUAL(size, 0, ());
TEST(CheckLangs(meta, first.m_pages), ());
}
void BuildDescriptionsSection() const
{
using namespace platform;
using namespace platform::tests_support;
auto const testMwm = kMwmFile + DATA_FILE_EXTENSION;
ScopedMwm testScopedMwm(testMwm);
DescriptionsCollector collector(m_wikiDir, testScopedMwm.GetFullPath());
ForEachUrlMock(collector);
DescriptionsSectionBuilder::BuildSection(testScopedMwm.GetFullPath(), collector);
FilesContainerR container(testScopedMwm.GetFullPath());
TEST(container.IsExist(DESCRIPTIONS_FILE_TAG), ());
descriptions::Deserializer d;
auto reader = container.GetReader(DESCRIPTIONS_FILE_TAG);
for (descriptions::FeatureIndex i = 0; i < kWikiData.size(); ++i)
{
auto const & pages = kWikiData[i].m_pages;
for (auto const & p : pages)
{
auto const featureId = i;
TEST(IsSupportedLang(p.first), (p.first));
auto const langIndex = StringUtf8Multilang::GetLangIndex(p.first);
std::string const str = d.Deserialize(*reader.GetPtr(), featureId, {langIndex});
TEST_EQUAL(str, p.second, ());
}
}
}
private:
template <class ToDo>
static void ForEachUrlMock(ToDo && toDo)
{
for (size_t i = 0; i < kWikiData.size(); ++i)
toDo(kWikiData[i].m_url, static_cast<uint32_t>(i));
}
static std::map<std::string_view, size_t> GetTestDataMapLang()
{
std::map<std::string_view, size_t> langs;
for (auto const & m : kWikiData)
{
for (auto const & d : m.m_pages)
if (IsSupportedLang(d.first))
langs[d.first] += 1;
}
return langs;
}
static size_t GetTestDataPages()
{
size_t size = 0;
for (auto const & m : kWikiData)
{
auto const & pages = m.m_pages;
if (base::AnyOf(pages, [](auto const & d) { return IsSupportedLang(d.first); }))
++size;
}
return size;
}
static size_t GetTestDataSize()
{
size_t size = 0;
for (auto const & m : kWikiData)
{
for (auto const & d : m.m_pages)
if (IsSupportedLang(d.first))
size += d.second.size();
}
return size;
}
static bool IsSupportedLang(std::string_view lang)
{
return StringUtf8Multilang::GetLangIndex(lang) != StringUtf8Multilang::kUnsupportedLanguageCode;
}
static int SumPageSizes(std::vector<PageT> const & p)
{
return std::accumulate(std::begin(p), std::end(p), 0,
[](int acc, PageT const & p) { return acc + p.second.size(); });
}
static bool CheckLangs(DescriptionsCollectionBuilderStat::LangStatistics const & stat)
{
auto const langs = GetTestDataMapLang();
for (size_t code = 0; code < stat.size(); ++code)
{
if (stat[code] == 0)
continue;
auto const svLang = StringUtf8Multilang::GetLangByCode(static_cast<int8_t>(code));
if (langs.count(svLang) == 0)
return false;
if (langs.at(svLang) != stat[code])
return false;
}
return true;
}
static bool CheckLangs(descriptions::LangMeta const & meta, std::vector<PageT> const & p)
{
for (auto const & [lang, _] : meta)
{
auto const it =
base::FindIf(p, [lang = lang](auto const & p) { return StringUtf8Multilang::GetLangIndex(p.first) == lang; });
if (it == std::end(p))
return false;
}
return true;
}
std::string const m_writableDir;
std::string const m_wikiDir;
};
std::string const TestDescriptionSectionBuilder::kMwmFile = "MwmFile";
std::string const TestDescriptionSectionBuilder::kDirPages = "wiki";
UNIT_CLASS_TEST(TestDescriptionSectionBuilder, MakeDescriptions)
{
TestDescriptionSectionBuilder::MakeDescriptions();
}
UNIT_CLASS_TEST(TestDescriptionSectionBuilder, MakePath)
{
TestDescriptionSectionBuilder::MakePath();
}
UNIT_CLASS_TEST(TestDescriptionSectionBuilder, FindPageAndFill)
{
TestDescriptionSectionBuilder::FindPageAndFill();
}
UNIT_CLASS_TEST(TestDescriptionSectionBuilder, FillStringFromFile)
{
TestDescriptionSectionBuilder::FillStringFromFile();
}
UNIT_CLASS_TEST(TestDescriptionSectionBuilder, GetFeatureDescription)
{
TestDescriptionSectionBuilder::GetFeatureDescription();
}
UNIT_CLASS_TEST(TestDescriptionSectionBuilder, BuildDescriptionsSection)
{
TestDescriptionSectionBuilder::BuildDescriptionsSection();
}
// http://en.wikipedia.org/wiki/Helsinki_Olympic_Stadium/ - en, de, ru, fr
// https://en.wikipedia.org/wiki/Turku_Cathedral - en, ru
std::vector<TestDescriptionSectionBuilder::WikiData> const TestDescriptionSectionBuilder::kWikiData = {
{"http://en.wikipedia.org/wiki/Helsinki_Olympic_Stadium/",
{{"en",
R"(
<p class="mw-empty-elt">
</p>
<p>The <b>Helsinki Olympic Stadium</b> (Finnish: <i lang="fi">Helsingin Olympiastadion</i>; Swedish: <i lang="sv">Helsingfors Olympiastadion</i>), located in the Töölö district about 2.3 kilometres (1.4 mi) from the centre of the Finnish capital Helsinki, is the largest stadium in the country, nowadays mainly used for hosting sports events and big concerts. The stadium is best known for being the centre of activities in the 1952 Summer Olympics. During those games, it hosted athletics, equestrian show jumping, and the football finals.
</p><p>The stadium was also the venue for the first Bandy World Championship in 1957, the first World Athletics Championships in 1983 as well as for the 2005 World Championships in Athletics. It hosted the European Athletics Championships in 1971, 1994 and 2012.
</p><p>It is also the home stadium of the Finland national football team. The stadium is closed temporarily since March 2016 for renovation works and scheduled of reopening in 2019.
</p>
<h2>History</h2>
<p>The Olympic Stadium was designed in functionalistic style by the architects Yrjö Lindegren and Toivo Jäntti. Construction of the Olympic Stadium began in 1934 and it was completed in 1938, with the intent to host the 1940 Summer Olympics, which were moved from Tokyo to Helsinki before being cancelled due to World War II. It hosted the 1952 Summer Olympics over a decade later instead. The stadium was also to be the main venue for the cancelled 1943 Workers' Summer Olympiad.
</p><p>It was the venue for the first ever Bandy World Championship in 1957.
</p><p>The stadium was completely modernized in 19901994 and also renovated just before the 2005 World Championships in Athletics.
</p><p>In 2006 an American TV series, <i>The Amazing Race 10</i>, had one of its episodes ending at The Olympic Stadium Tower. As a task, teams had to do a face-first rappel (known as the Angel Dive) down the Helsinki Olympic Tower.
</p><p>Since March 2007, a Eurasian eagle-owl has been spotted living in and around the stadium. On June 6, 2007, during a Euro 2008 qualifying match, the owl delayed play by ten minutes after perching on a goalpost. The owl was later christened Bubi and was named as Helsinki's Resident of the Year.
</p>
<p>The 50th anniversary of the Helsinki Olympic Games hosted in the Helsinki Olympic Stadium was the main motif for one of the first Finnish euro silver commemorative coins, the 50th anniversary of the Helsinki Olympic Games commemorative coin, minted in 2002. On the reverse, a view of the Helsinki Olympic Stadium can be seen. On the right, the 500 markka commemorative coin minted in 1952 celebrating the occasion is depicted.
</p>
<h2>Features</h2>
<p>The stadium's spectator capacity was at its maximum during the 1952 Summer Olympics with over 70,000 spectator places. Nowadays the stadium has 40,600 spectator places. During concerts, depending on the size of the stage, the capacity is 45,00050,000.
</p><p>The tower of the stadium, a distinct landmark with a height of 72.71 metres (238.5 ft), a measurement of the length of the gold-medal win by Matti Järvinen in javelin throw of 1932 Summer Olympics, is open for visitors and offers impressive views over Helsinki. It is possible to see into the adjacent Telia 5G -areena.
</p><p>A Youth Hostel is located within the Stadium complex.
</p>
<h2>Future</h2>
<p>Major renovation work at the stadium started in the spring of 2016. The stadium will be closed during the construction and will reopen in 2019. During renovation all the spectator stands will be covered with canopies and the field area and the tracks will be renewed. It will also offer extended restaurant areas and more indoor sport venues.</p><p>Projected costs for the renovation is 209 million euros and it will be funded by Finnish state and the city of Helsinki.</p>
<h2>Events</h2>
<h3>Sport events</h3>
<ul><li>1952 Summer Olympics</li>
<li>1957 Bandy World Championship</li>
<li>1971 European Athletics Championships</li>
<li>1983 World Championships in Athletics</li>
<li>1994 European Athletics Championships</li>
<li>2005 World Championships in Athletics</li>
<li>UEFA Women's Euro 2009 (4 Group matches and a Final)</li>
<li>2012 European Athletics Championships</li></ul>
<h3>Concerts</h3>
<h2>References</h2>
<h2>External links</h2>
<p> Media related to Helsingin olympiastadion at Wikimedia Commons
</p>
<ul><li>1952 Summer Olympics official report. pp. 447.</li>
<li>Stadion.fi Official site</li>
<li>History of the stadium</li>
<li>Panoramic virtual tour from the stadium tower</li></ul>
)"},
{"de",
R"(
<p>Das <b>Olympiastadion Helsinki</b> (<span>finnisch</span> <span lang="fi-Latn"><i>Helsingin olympiastadion</i></span>, <span>schwedisch</span> <span lang="sv-Latn"><i>Helsingfors Olympiastadion</i></span>) ist das größte Stadion Finnlands. Das Stadionrund mit Leichtathletikanlage ist die Heimstätte der finnischen Fußballnationalmannschaft. Es liegt rund zwei Kilometer vom Stadtzentrum von Helsinki entfernt im Stadtteil Töölö. 2006 wurde es unter Denkmalschutz gestellt. Das Nationalstadion wird ab 2016 umfangreich renoviert und soll nach drei Jahren 2019 wiedereröffnet werden.</p>
<h2>Architektur</h2>
<p>Der funktionalistische Bau wurde von den Architekten Yrjö Lindegren und Toivo Jäntti entworfen. Das Stadionwahrzeichen ist der 72,71 Meter hohe Turm, dessen Höhe genau der Siegweite des finnischen Speerwurf-Olympiasiegers von 1932, Matti Järvinen, entspricht. Außerhalb von Veranstaltungen ist der Turm für Besucher als Aussichtsturm geöffnet.
</p>
<h2>Geschichte</h2>
<p>Das Olympiastadion wurde von 1934 bis 1938 erbaut mit dem Ziel der Austragung der Olympischen Sommerspiele 1940, die aber wegen des Zweiten Weltkrieges ausfielen. Von 1994 bis 1999 wurde das Stadion umgebaut, dabei wurde die Zahl der Zuschauerplätze von 70.000 auf 40.000 verringert. Es verfügte bis Ende 2015 über 39.784 Sitzplätze. Vor der Einführung des Euro als finnische Landeswährung war das Stadion auf der 10-Finnmark-Banknote zusammen mit dem Langstreckenläufer Paavo Nurmi abgebildet. Vor dem Stadion ist eine Statue des neunfachen finnischen Olympiasiegers Nurmi mit einer Gedenktafel aufgestellt. In direkter Nachbarschaft zum Olympiastadion liegt das 1915 eingeweihte Fußballstadion Töölön Pallokenttä. 2000 wurde das Gelände durch ein weiteres Fußballstadion, das Sonera Stadium mit rund 10.000 Plätzen, ergänzt.
</p><p>Zu Beginn der 2010er Jahre begannen die Planungen für eine umfassende Stadionrenovierung. Ende 2014 gab die Stadt Helsinki genauere Angaben zu den Umbauplänen. Dabei soll der historische Entwurf aus den 1930er Jahren aber erhalten bleiben. So bleibt das äußere Bild nahezu unberührt und die sichtbaren Veränderungen betreffen den Stadioninnenraum. Die Ränge bekommen eine neue Bestuhlung. Dies wird die Anzahl der Plätze auf rund 35.000 bis 36.000 verringern. Zudem sollen die Tribünen des Olympiastadions überdacht werden. Die neue Überdachung wird, wie bei dem bestehenden Dach der Haupttribüne und der Gegentribüne, von Stützpfeilern auf den Rängen getragen. Das Stadion erhält des Weiteren ein neues Besucherzentrum und die Zahl der Eingänge wird erhöht. Insgesamt werden die Räumlichkeiten der Sportstätte umfangreich renoviert. Die Bauarbeiten sollen im Frühjahr 2016 beginnen und im Frühjahr 2019 soll die Anlage wiedereröffnet werden. Während der Renovierung wird das Olympiastadion geschlossen bleiben. Die Kosten für die Umbauten nach den Plänen des Arkkitehtitoimisto K2S werden auf 209 Millionen Euro veranschlagt.</p><p>Seit dem 23. Dezember 2015 ist das Olympiastadion im Hinblick auf die Renovierung für die Öffentlichkeit geschlossen. Zuvor konnten sich Interessierte Souvenirs von den hölzernen Sitzbänke sichern. Rund 10.000 Andenkensammler kamen innerhalb einer Woche und konnten gegen Zahlung von 18 Euro ihre persönliche Erinnerung an das alte Olympiastadion abmontieren. Es war der symbolische Auftakt für die Bauarbeiten.</p><p>Im Januar 2017 wurde bekannt, dass der geplante Umbau des traditionsreichen Stadions teuerer wird als erwartet. Die Summe von 209 Mio. wurde auf 261 Mio. Euro nach oben korrigiert. Noch im Januar des Jahres soll ein Generalunternehmer für den Umbau ausgewählt werden.</p>
<h2>Veranstaltungen</h2>
<p>Die Anlage war das Hauptstadion der Olympischen Sommerspiele 1952. Nachdem das Sportstätte bereits die Leichtathletik-Weltmeisterschaften 1983 beherbergt hatte, wurden hier auch die Leichtathletik-Weltmeisterschaften 2005 ausgetragen. Im Vorfeld wurde das Dach erweitert, das jetzt die Geraden und einen kleinen Teil der Kurve überspannt. Das Stadion war einer der fünf Spielorte der Fußball-Europameisterschaft der Frauen 2009. Im Jahr 2012 war das Stadion zum dritten Mal nach 1971 und 1994 Schauplatz von Leichtathletik-Europameisterschaften. Die erste Bandy-Weltmeisterschaft 1957 wurde ebenfalls in der Sportstätte durchgeführt.
</p><p>Das Olympiastadion wird auch als Konzertarena genutzt; so waren z. B. Michael Jackson, U2, Tina Turner, The Rolling Stones, AC/DC, Dire Straits, Paul McCartney, Genesis, Metallica, Bon Jovi, Iron Maiden, Bruce Springsteen u. a. zu Gast im Stadion der Hauptstadt.</p>
<h2>Weblinks</h2>
<ul><li>stadion.fi: Website des Olympiastadions (englisch)</li>
<li>stadiumdb.com: Olympiastadion Helsinki (englisch)</li>
<li>stadionwelt.de: Bildergalerie</li></ul>
<h2>Einzelnachweise</h2>
)"},
{"ru",
R"(
<p><b>Олимпийский стадион Хельсинки</b> (фин. <span lang="fi">Helsingin olympiastadion</span>, швед. <span lang="sv">Helsingfors Olympiastadion</span>) крупнейшая спортивная арена Финляндии. Стадион расположен в районе Тёёлё, примерно в двух километрах от исторического центра Хельсинки. В летнее время, помимо спортивных мероприятий, на стадионе проводят и музыкальные концерты.
</p>
<h2>История</h2>
<p>Началом истории Олимпийского стадиона принято считать 11 декабря 1927 года, когда под эгидой городских властей Хельсинки был основан фонд, призванный собрать средства для строительства спортивной арены, способной принять Летние Олимпийские игры.
</p><p>Строительство стадиона продолжалось с 12 февраля 1934 года по 12 июня 1938 года.</p><p>Стадион готовился принять XII Олимпийские Игры 1940 года, от проведения которых отказался Токио из-за участия Японии во Второй японско-китайской войне
Однако, Игры были окончательно отменены МОК по причине начала Второй мировой войны.
</p><p>Спустя 12 лет Олимпийский стадион Хельсинки стал главной ареной XV Летних Олимпийских Игр 1952 года.
В 19901994 гг. на стадионе прошла генеральная реконструкция.
</p><p>Олимпийский стадион Хельсинки, построенный по проекту двух архитекторов: Юрьё Линдегрена (fi:Yrjö Lindegren) и Тойво Янтти (fi:Toivo Jäntti), ставший ярким образцом архитектурного стиля функционализма, отличается простотой линий и органичным сочетанием с окружающим его классическим финским пейзажем: гранитными скалами и сосновым лесом.
</p>
<h2>Размеры</h2>
<p>Башня Олимпийского стадиона имеет высоту 72 метра 71 сантиметр в честь рекорда Матти Ярвинена (fi:Matti Järvinen) в метании копья на Олимпийских Играх 1932 года.
Чаша стадиона достигает в длину 243 метра и в ширину 159 метров.
Наибольшая вместимость в 70 435 человек была во время проведения Олимпийских Игр 1952 года, и до 2002-го, за счёт мест скамеечного типа. А сейчас сокращена до 40 000 зрителей, после реконструкции и установлении 41 тысячи индивидуальных сидений.
Внутренний же вид арены напоминает древние стадионы античности.
</p>
<h2>События</h2>
<h3>1952 год. XV Олимпийские Игры</h3>
<p>Проходили с 19 июля по 3 августа 1952 года.
В Играх приняло участие 4 955 атлетов (519 женщин и 4 436 мужчин) из 69 стран мира. Разыграно 149 комплектов медалей по 17 видам спорта.
Впервые приняли участие национальные сборные СССР и ФРГ.
Олимпийское пламя зажгли Пааво Нурми и Ханнес Колехмайнен. Открыл Игры президент Финляндской республики Юхо Кусти Паасикиви. Закрывал их президент МОК Зигфрид Эдстрем, однако он забыл после торжественной речи сказать слова «Объявляю Игры XV Олимпиады закрытыми», поэтому игры формально считаются до сих пор незакрытыми.
</p>
<h3>1957 год. Чемпионат мира по хоккею с мячом</h3>
<p>Проходил с 28 февраля по 3 марта 1957 года.
</p>
<h3>1971 год. Чемпионат Европы по лёгкой атлетике</h3>
<p>Проходил с 10 по 15 августа 1971 года.
Участвовало 857 атлетов.
</p>
<h3>1983 год. Чемпионат мира по лёгкой атлетике</h3>
<p>Проходил с 7 по 14 августа 1983 года.
Участвовал 1 355 атлетов.
</p>
<h3>1994 год. Чемпионат Европы по лёгкой атлетике</h3>
<p>Проходил с 9 по 14 августа 1994 года.
</p>
<h3>2005 год. Чемпионат мира по лёгкой атлетике</h3>
<p>Проходил с 6 по 14 августа 2005 года.
Приняло участие более 1 900 атлетов.
</p>
<h3>2012 год. Чемпионат Европы по лёгкой атлетике</h3>
<p>Прошёл с 27 июня по 1 июля 2012 года.
</p><p>Зимой-летом 2005 года Олимпийский стадион подвергся модернизации: был уставлен козырёк над частью трибун и новая система освещения чаши стадиона.
</p>
<h2>Футбольная сборная Финляндии</h2>
<p>Олимпийский стадион Хельсинки является домашней ареной сборной Финляндии по футболу
</p>
<h2>Дополнительные сведения</h2>
<p>В комплексе Олимпийского стадиона находится Музей финского спорта (фин. <span lang="fi">Suomen Urheilumuseo</span>).
В северной части стадиона расположен один из самых дешевых хостелов в центре финской столицы (Stadion Hostel).
Так же организованы экскурсии на 72-метровую башню, с которой открывается прекрасная панорама города и Финского залива. (Часы работы: пн-пт 9-20, сб-вс 9-18)
</p>
<h2>Примечания</h2>
<h2>Ссылки</h2>
<ul><li>Olympiastadion</li>
<li>Suomen Urheilumuseo</li></ul>
)"},
{"fr",
R"(
<p>Le <b>stade olympique d'Helsinki</b> (en finnois et en suédois : <i>Olympiastadion</i>) situé dans le quartier de Töölö, à environ 2 <abbr class="abbr" title="kilomètre">km</abbr> du centre-ville d'Helsinki est le plus grand stade en Finlande. C'est le stade principal des Jeux olympiques d'été de 1952. Il a été construit pour y célébrer les Jeux olympiques d'été de 1940 qui ont été finalement annulés (auparavant attribués à Tokyo) en raison de la Seconde Guerre mondiale.
</p><p>Le stade a également é le siège des Championnats du monde d'athlétisme, les tout premiers en 1983 et à nouveau en 2005. Il hébergera pour la <abbr class="abbr" title="Troisième">3<sup>e</sup></abbr> fois les Championnats d'Europe d'athlétisme en 2012, après les éditions de 1971 et de 1994. C'est aussi le stade de l'Équipe de Finlande de football.
</p>
<h2>Histoire</h2>
<p>Le stage est conçu par Yrjö Lindegren et Toivo Jäntti. Ce dernier s'occupera de toutes les modifications ultérieures.
La construction du stade olympique a commencé en 1934 et elle a é achevée en 1938. Le stade a é entièrement modernisé en 1990-1994 et a également é rénové en 2005 pour les Championnats du monde d'athlétisme. Sa capacité de spectateurs était à son maximum au cours de Jeux olympiques de 1952 avec plus de 70 000 spectateurs. De nos jours, le stade a en moyenne 40 000 spectateurs.
</p><p>Il est caractérisé par une tour haute de 72 <abbr class="abbr" title="mètre">m</abbr> (dont la hauteur serait aussi celle d'un essai de lancer du javelot, un sport national).
</p>
<h2>Événements</h2>
<ul><li>Jeux olympiques d'é de 1952</li>
<li>Championnats d'Europe d'athlétisme 1971</li>
<li>Coupe d'Europe des nations d'athlétisme 1977</li>
<li>Championnats du monde d'athlétisme 1983</li>
<li>Championnats d'Europe d'athlétisme 1994</li>
<li>Championnats du monde d'athlétisme 2005</li>
<li>Championnats d'Europe d'athlétisme 2012</li></ul>
<h2>Concerts</h2>
<ul><li>The Rolling Stones, 2 septembre 1970</li>
<li>Dire Straits, 4 août 1992</li>
<li>The Rolling Stones, 6 juin 1995</li>
<li>Bon Jovi, 19 juillet 1996</li>
<li>Tina Turner, 7 août 1996</li>
<li>U2, 9 août 1997</li>
<li>Michael Jackson, 18 et 20 août 1997</li>
<li>Elton John, 25 juin 1998</li>
<li>Rolling Stones, 5 août 1998</li>
<li>Mestarit, 5 août 1999</li>
<li>AC/DC, 26 juin 2001</li>
<li>Bruce Springsteen, 16 et 17 juin 2003</li>
<li>The Rolling Stones, 16 juillet 2003</li>
<li>Metallica, 28 mai 2004</li>
<li>Paul McCartney, 17 juin 2004</li>
<li>Genesis, 11 juin 2007</li>
<li>Metallica, 15 juillet 2007</li>
<li>The Rolling Stones, <abbr class="abbr" title="Premier">1<sup>er</sup></abbr> août 2007</li>
<li>Bruce Springsteen, 11 juillet 2008</li>
<li>Iron Maiden, 18 juillet 2008</li>
<li>U2, 20 et 21 août 2010</li></ul>
<h2>Futur</h2>
<p>Le stade sera rénové en deux parties, l'une en 2016, et pour terminer en 2019. La rénovation coûtera 209 millions d'euros.
</p><p>La structure extérieure, il y aura des panneaux de bois orneront la partie haute de la façade. Pour ce qui est de la partie intérieure du toit, elle sera en bois, un toit qui couvrira toutes les tribunes d'ailleurs.
</p>
<h2>Notes</h2>
<ul><li>Le stade olympique a é, avec les coureurs Paavo Nurmi et Erik Bruun l'illustration de l'ancien billet 10 mark finnois.</li>
<li>Une série télévisée américaine, The Amazing Race, avait fait un de leurs épisodes qui se terminait à La tour du stade olympique en 2006.</li>
<li>Une auberge de jeunesse est située dans le complexe du stade.</li></ul>
<h2>Voir aussi</h2>
<h3>Liens externes</h3>
<ul><li><abbr class="abbr indicateur-langue" title="Langue : finnois">(fi)</abbr> Site officiel</li>
<li><abbr class="abbr indicateur-langue" title="Langue : finnois">(fi)</abbr> Histoire du Stade Olympique</li></ul>
<h3>Liens internes</h3>
<ul><li>Piscine olympique d'Helsinki</li></ul><p><br></p>
<ul id="bandeau-portail" class="bandeau-portail"><li><span><span></span> <span>Portail de larchitecture et de lurbanisme</span> </span></li> <li><span><span></span> <span>Portail du football</span> </span></li> <li><span><span></span> <span>Portail de lathlétisme</span> </span></li> <li><span><span></span> <span>Portail des Jeux olympiques</span> </span></li> <li><span><span></span> <span>Portail dHelsinki</span> </span></li> </ul>
)"}}},
{"https://en.wikipedia.org/wiki/Turku_Cathedral",
{{"en",
R"(
<p><b>Turku Cathedral</b> (Finnish: <i lang="fi">Turun tuomiokirkko</i>, Swedish: <i lang="sv">Åbo domkyrka</i>) is the previous catholic cathedral of Finland, today the Mother Church of the Evangelical Lutheran Church of Finland. It is the central church of the Lutheran Archdiocese of Turku and the seat of the Lutheran Archbishop of Finland, Kari Mäkinen. It is also regarded as one of the major records of Finnish architectural history.
</p><p>Considered to be the most important religious building in Finland, the cathedral has borne witness to many important events in the nation's history and has become one of the city's most recognizable symbols. The cathedral is situated in the heart of Turku next to the Old Great Square, by the river Aura. Its presence extends beyond the local precinct by having the sound of its bells chiming at noon broadcast on national radio. It is also central to Finland's annual Christmas celebrations.
</p><p>The cathedral was originally built out of wood in the late 13th century, and was dedicated as the main cathedral of Finland in 1300, the seat of the Catholic bishop of Turku. It was considerably expanded in the 14th and 15th centuries, mainly using stone as the construction material. The cathedral was badly damaged during the Great Fire of Turku in 1827, and was rebuilt to a great extent afterwards.
</p>
<h2>History</h2>
<p>As the town of Turku began to emerge in the course of the 13th century as the most important trading centre in Finland, the Bishop's see of the Diocese of Finland was transferred from its previous location at Koroinen, some distance further up on the bank of Aura river, to the middle of the town. By the end of the 13th century, a new stone church had been completed on the site of the former wooden-built parish church on Unikankare Mound, and it was consecrated in 1300 as the Cathedral Church of the Blessed Virgin Mary and Saint Henry, the first Bishop of Finland.
</p><p>At its earliest the cathedral was smaller than the present building. Its east front was where the pulpit stands now, and its roof was considerably lower than at the moment. Extensions were made to the cathedral throughout the Middle Ages. During the 14th century a new choir was added, from which the octagonal Gothic pillars in the present chancel originate. Throughout the Middle Ages, the High Altar was located opposite the easternmost pillars of the nave, until it was transferred to its present location in the apse, in what had previously been the Chapel of All Saints, in the mid-17th century.
</p><p>During the 15th century, side-chapels were added along the north and south sides of the nave, containing altars dedicated to various saints. By the end of the Middle Ages these numbered 42 in total. The roof-vaults were also raised during the latter part of the 15th century to their present height of 24 meters. Thus, by the beginning of the Modern era, the church had approximately taken on its present shape. The major later addition to the cathedral is the tower, which has been rebuilt several times, as a result of repeated fires. The worst damage was caused by the Great Fire of Turku in 1827, when most of the town was destroyed, along with the interior of both the tower and the nave and the old tower roof. The present spire of the tower, constructed after the great fire, reaches a height of 101 meters above sea level, and is visible over a considerable distance as the symbol of both the cathedral and the city of Turku itself.
</p><p>In the reformation the cathedral was taken by the Lutheran Church of Finland (Sweden). Most of the present interior also dates from the restoration carried out in the 1830s, following the Great Fire. The altarpiece, depicting the Transfiguration of Jesus, was painted in 1836 by the Swedish artist Fredrik Westin. The reredos behind the High Altar, and the pulpit in the crossing, also both date from the 1830s, and were designed by german architect Carl Ludvig Engel, known in Finland for his several other highly regarded works. The walls and roof in the chancel are decorated with frescos in the Romantic style by the court painter Robert Wilhelm Ekman, which depict events from the life of Jesus, and the two key events in the history of the Finnish Church: the baptism of the first Finnish Christians by Bishop Henry by the spring at Kupittaa, and the presentation to King Gustav Vasa by the Reformer Michael Agricola of the first Finnish translation of the New Testament.
</p><p>The Cathedral houses three organs. The current main organ of the Cathedral was built by Veikko Virtanen Oy of Espoo, Finland, in 1980 and features 81 ranks with a mechanical action.
</p>
<h2>Notable people buried in the cathedral</h2>
<ul><li>Blessed Bishop Hemming (c. 12901366), Bishop of Turku</li>
<li>Paulus Juusten (15161576), Bishop of Vyborg and later Bishop of Turku</li>
<li>Karin Månsdotter (15501612), Queen of Sweden</li>
<li>Princess Sigrid of Sweden (15661633), Swedish princess</li>
<li>Samuel Cockburn (15741621), Scottish mercenary leader</li>
<li>Torsten Stålhandske (15931644), officer in the Swedish army during the Thirty Years' War</li>
<li>Åke Henriksson Tott (15981640), Swedish soldier and politician</li></ul>
<h2>Gallery</h2>
<ul class="gallery mw-gallery-traditional"><li class="gallerybox" style="width: 155px">
<li class="gallerybox" style="width: 155px">
<li class="gallerybox" style="width: 155px">
<li class="gallerybox" style="width: 155px">
<li class="gallerybox" style="width: 155px">
<li class="gallerybox" style="width: 155px">
<li class="gallerybox" style="width: 155px">
</ul>
<h2>See also</h2>
<ul><li>Archdiocese of Turku</li>
<li>Evangelical Lutheran Church of Finland</li>
<li>Great Fire of Turku</li>
<li>Helena Escholin</li>
<li>List of tallest churches in the world</li>
<li>Turku</li></ul>
<h2>References</h2>
<h2>External links</h2>
<ul><li><span><span>Official website</span></span></li>
<li>Virtual Turku sound and images from the cathedral</li>
<li>Medieval Turku</li>
<li>Turku Cathedral photos</li>
<li>Turku church organs</li></ul>
)"},
{"ru",
R"(
<p><b>Кафедра́льный собо́р Ту́рку</b> (швед. <span lang="sv">Åbo domkyrka</span>, фин. <span lang="fi">Turun tuomiokirkko</span>) главный лютеранский храм в Финляндии. Построен во второй половине XIII века, освящён в 1300 году в честь Девы Марии и первого епископа страны святого Генриха, крестившего Финляндию.
</p>
<h2>История</h2>
<p>Собор был заложен в 1258 году и построен в северо-готическом стиле, ставшим на долгое время образцом для строительства других церквей в Финляндии. Первый каменный собор был намного меньше нынешнего. Его фасад находился на том месте, где ныне расположена кафедра. Ниже был и свод, перекрывавший пространство.
</p><p>В Средние века собор неоднократно перестраивался и расширялся. В XV веке к собору были пристроены боковые капеллы. Немного позже высота свода центрального нефа была увеличена до современных размеров (24 метра). В 1827 году собор серьёзно пострадал от пожара. 101-метровая башня собора была построена при восстановлении собора и стала символом города Турку.
</p><p>Внутреннее убранство собора выполнено в 1830-х гг. Фредриком Вестином и Карлом Энгелем. Стены и свод алтарной части украшены фресками Р. В. Экмана. В 1870-х гг. северные капеллы были украшены витражами работы Владимира Сверчкова.
</p><p>В 1980 году в соборе был установлен новый 81-регистровый орган.
</p>
<p>В капелле Святого Георгия похоронен епископ Хемминг, причисленный к лику святых. Наиболее известное надгробие собора саркофаг Катарины Монсдоттэр, жены короля Эрика XIV. В разных частях собора также покоятся Мауну II Таваст, Олави Маунунпойка, Конрад Биц, Мауну III Сяркилахти, Кнут Поссе, Павел Юстен, Исаак Ротовиус, Турстэн Столхандскэ, Окэ Тотт, Эверт Хурн.
</p><p>В южной галерее собора расположен Кафедральный музей, открытый в 1982 году после завершения реставрационных работ.
</p><p>Перед собором установлен памятник реформатору церкви Микаэлю Агриколе.
</p>
<h2>Ссылки</h2>
<ul><li class="mw-empty-elt">
<li> На Викискладе есть медиафайлы по теме Абосский собор</li></ul>
<p><br></p>
)"}}}};
} // namespace generator_tests

View file

@ -0,0 +1,389 @@
#include "testing/testing.hpp"
#include "types_helper.hpp"
#include "generator/feature_builder.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "generator/geometry_holder.hpp"
#include "indexer/data_header.hpp"
#include "indexer/feature_visibility.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "base/geo_object_id.hpp"
#include <limits>
namespace feature_builder_test
{
using namespace feature;
using namespace generator::tests_support;
using namespace tests;
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_ManyTypes)
{
FeatureBuilder fb1;
FeatureBuilderParams params;
base::StringIL arr[] = {
{"building"},
{"place", "country"},
{"place", "state"},
/// @todo Can't realize is it deprecated or we forgot to add clear styles for it.
//{ "place", "county" },
{"place", "region"},
{"place", "city"},
{"place", "town"},
};
AddTypes(params, arr);
params.FinishAddingTypes();
params.SetHouseNumberAndHouseName("75", "Best House");
params.AddName("default", "Name");
fb1.SetParams(params);
fb1.SetCenter(m2::PointD(0, 0));
TEST(fb1.RemoveInvalidTypes(), ());
TEST(fb1.IsValid(), (fb1));
FeatureBuilder::Buffer buffer;
TEST(fb1.PreSerializeAndRemoveUselessNamesForIntermediate(), ());
fb1.SerializeForIntermediate(buffer);
FeatureBuilder fb2;
fb2.DeserializeFromIntermediate(buffer);
TEST(fb2.IsValid(), (fb2));
TEST_EQUAL(fb1, fb2, ());
TEST_EQUAL(fb2.GetTypesCount(), 6, ());
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_LineTypes)
{
FeatureBuilder fb1;
FeatureBuilderParams params;
base::StringIL arr[] = {
{"railway", "rail"}, {"highway", "motorway"}, {"hwtag", "oneway"},
{"psurface", "paved_good"}, {"junction", "roundabout"},
};
AddTypes(params, arr);
params.FinishAddingTypes();
fb1.SetParams(params);
fb1.AssignPoints({{0, 0}, {1, 1}});
fb1.SetLinear();
TEST(fb1.RemoveInvalidTypes(), ());
TEST(fb1.IsValid(), (fb1));
FeatureBuilder::Buffer buffer;
TEST(fb1.PreSerializeAndRemoveUselessNamesForIntermediate(), ());
fb1.SerializeForIntermediate(buffer);
FeatureBuilder fb2;
fb2.DeserializeFromIntermediate(buffer);
TEST(fb2.IsValid(), (fb2));
TEST_EQUAL(fb1, fb2, ());
TEST_EQUAL(fb2.GetTypesCount(), 5, ());
ftypes::IsRoundAboutChecker::Instance()(fb2.GetTypes());
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_Waterfall)
{
FeatureBuilder fb1;
FeatureBuilderParams params;
base::StringIL arr[] = {{"waterway", "waterfall"}};
AddTypes(params, arr);
TEST(params.FinishAddingTypes(), ());
fb1.SetParams(params);
fb1.SetCenter(m2::PointD(1, 1));
TEST(fb1.RemoveInvalidTypes(), ());
TEST(fb1.IsValid(), (fb1));
FeatureBuilder::Buffer buffer;
TEST(fb1.PreSerializeAndRemoveUselessNamesForIntermediate(), ());
fb1.SerializeForIntermediate(buffer);
FeatureBuilder fb2;
fb2.DeserializeFromIntermediate(buffer);
TEST(fb2.IsValid(), (fb2));
TEST_EQUAL(fb1, fb2, ());
TEST_EQUAL(fb2.GetTypesCount(), 1, ());
}
UNIT_CLASS_TEST(TestWithClassificator, FBbuilder_GetMostGeneralOsmId)
{
FeatureBuilder fb;
fb.AddOsmId(base::MakeOsmNode(1));
TEST_EQUAL(fb.GetMostGenericOsmId(), base::MakeOsmNode(1), ());
fb.AddOsmId(base::MakeOsmNode(2));
fb.AddOsmId(base::MakeOsmWay(1));
TEST_EQUAL(fb.GetMostGenericOsmId(), base::MakeOsmWay(1), ());
fb.AddOsmId(base::MakeOsmNode(3));
fb.AddOsmId(base::MakeOsmWay(2));
fb.AddOsmId(base::MakeOsmRelation(1));
TEST_EQUAL(fb.GetMostGenericOsmId(), base::MakeOsmRelation(1), ());
}
UNIT_CLASS_TEST(TestWithClassificator, FVisibility_RemoveUselessTypes)
{
Classificator const & c = classif();
{
std::vector<uint32_t> types;
types.push_back(c.GetTypeByPath({"building"}));
types.push_back(c.GetTypeByPath({"amenity", "theatre"}));
TEST(RemoveUselessTypes(types, GeomType::Area), ());
TEST_EQUAL(types.size(), 2, ());
}
{
std::vector<uint32_t> types;
types.push_back(c.GetTypeByPath({"highway", "primary"}));
types.push_back(c.GetTypeByPath({"building"}));
TEST(RemoveUselessTypes(types, GeomType::Area, true /* emptyName */), ());
TEST_EQUAL(types.size(), 1, ());
TEST_EQUAL(types[0], c.GetTypeByPath({"building"}), ());
}
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_RemoveUselessNames)
{
FeatureBuilderParams params;
base::StringIL arr[] = {{"boundary", "administrative", "2"}, {"barrier", "fence"}};
AddTypes(params, arr);
params.FinishAddingTypes();
params.AddName("default", "Name");
params.AddName("ru", "Имя");
FeatureBuilder fb1;
fb1.SetParams(params);
fb1.AssignPoints({{0, 0}, {1, 1}});
fb1.SetLinear();
TEST(!fb1.GetName(0).empty(), ());
TEST(!fb1.GetName(8).empty(), ());
fb1.RemoveUselessNames();
TEST(fb1.GetName(0).empty(), ());
TEST(fb1.GetName(8).empty(), ());
TEST(fb1.IsValid(), (fb1));
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_HN)
{
FeatureBuilderParams params;
params.MakeZero();
TEST(params.AddHouseNumber("123"), ());
TEST_EQUAL(params.house.Get(), "123", ());
params.MakeZero();
TEST(params.AddHouseNumber("00123"), ());
TEST_EQUAL(params.house.Get(), "00123", ());
params.MakeZero();
TEST(params.AddHouseNumber("0"), ());
TEST_EQUAL(params.house.Get(), "0", ());
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_SerializeLocalityObjectForBuildingPoint)
{
FeatureBuilder fb;
FeatureBuilderParams params;
base::StringIL arr[] = {
{"building"},
};
AddTypes(params, arr);
params.FinishAddingTypes();
params.SetHouseNumberAndHouseName("75", "Best House");
params.AddName("default", "Name");
fb.AddOsmId(base::MakeOsmNode(1));
fb.SetParams(params);
fb.SetCenter(m2::PointD(10.1, 15.8));
TEST(fb.RemoveInvalidTypes(), ());
TEST(fb.IsValid(), (fb));
feature::DataHeader header;
header.SetGeometryCodingParams(serial::GeometryCodingParams());
header.SetScales({scales::GetUpperScale()});
feature::GeometryHolder holder(fb, header);
auto & buffer = holder.GetBuffer();
TEST(fb.PreSerializeAndRemoveUselessNamesForMwm(buffer), ());
}
UNIT_TEST(LooksLikeHouseNumber)
{
TEST(FeatureParams::LooksLikeHouseNumber("1 bis"), ());
TEST(FeatureParams::LooksLikeHouseNumber("18-20"), ());
// Brno (Czech) has a lot of fancy samples.
TEST(FeatureParams::LooksLikeHouseNumber("ev.8"), ());
TEST(FeatureParams::LooksLikeHouseNumber("D"), ());
TEST(FeatureParams::LooksLikeHouseNumber("A5"), ());
TEST(!FeatureParams::LooksLikeHouseNumber("Building 2"), ());
TEST(!FeatureParams::LooksLikeHouseNumber("Unit 3"), ());
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_HouseName)
{
FeatureBuilder fb;
FeatureBuilderParams params;
base::StringIL arr[] = {{"building"}};
AddTypes(params, arr);
params.FinishAddingTypes();
params.SetHouseNumberAndHouseName("", "St. Nicholas Lodge");
fb.SetParams(params);
fb.AssignArea({{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}, {});
fb.SetArea();
TEST(fb.RemoveInvalidTypes(), ());
TEST(fb.IsValid(), ());
TEST(fb.PreSerializeAndRemoveUselessNamesForIntermediate(), ());
TEST(fb.IsValid(), ());
TEST_EQUAL(fb.GetName(StringUtf8Multilang::kDefaultCode), "St. Nicholas Lodge", ());
TEST(fb.GetParams().house.IsEmpty(), ());
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_SerializeAccuratelyForIntermediate)
{
FeatureBuilder fb1;
FeatureBuilderParams params;
base::StringIL arr[] = {
{"railway", "rail"}, {"highway", "motorway"}, {"hwtag", "oneway"},
{"psurface", "paved_good"}, {"junction", "circular"},
};
AddTypes(params, arr);
params.FinishAddingTypes();
fb1.SetParams(params);
auto const diff = 0.33333333334567;
std::vector<m2::PointD> points;
for (size_t i = 0; i < 100; ++i)
points.push_back({i + diff, i + 1 + diff});
fb1.AssignPoints(std::move(points));
fb1.SetLinear();
TEST(fb1.RemoveInvalidTypes(), ());
TEST(fb1.IsValid(), (fb1));
FeatureBuilder::Buffer buffer;
TEST(fb1.PreSerializeAndRemoveUselessNamesForIntermediate(), ());
fb1.SerializeAccuratelyForIntermediate(buffer);
FeatureBuilder fb2;
fb2.DeserializeAccuratelyFromIntermediate(buffer);
TEST(fb2.IsValid(), (fb2));
TEST(fb1.IsExactEq(fb2), ());
TEST_EQUAL(fb2.GetTypesCount(), 5, ());
ftypes::IsRoundAboutChecker::Instance()(fb2.GetTypes());
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_RemoveUselessAltName)
{
auto const kDefault = StringUtf8Multilang::kDefaultCode;
auto const kAltName = StringUtf8Multilang::GetLangIndex("alt_name");
{
FeatureBuilderParams params;
base::StringIL arr[] = {{"shop"}};
AddTypes(params, arr);
params.FinishAddingTypes();
// We should remove alt_name which is almost equal to name.
params.AddName("default", "Перекрёсток");
params.AddName("alt_name", "Перекресток");
FeatureBuilder fb;
fb.SetParams(params);
fb.SetCenter(m2::PointD(0.0, 0.0));
TEST(!fb.GetName(kDefault).empty(), ());
TEST(!fb.GetName(kAltName).empty(), ());
fb.RemoveUselessNames();
TEST(!fb.GetName(kDefault).empty(), ());
TEST(fb.GetName(kAltName).empty(), ());
TEST(fb.IsValid(), (fb));
}
{
FeatureBuilderParams params;
base::StringIL arr[] = {{"shop"}};
AddTypes(params, arr);
params.FinishAddingTypes();
// We should not remove alt_name which differs from name.
params.AddName("default", "Государственный Универсальный Магазин");
params.AddName("alt_name", "ГУМ");
FeatureBuilder fb;
fb.SetParams(params);
fb.SetCenter(m2::PointD(0.0, 0.0));
TEST(!fb.GetName(kDefault).empty(), ());
TEST(!fb.GetName(StringUtf8Multilang::GetLangIndex("alt_name")).empty(), ());
fb.RemoveUselessNames();
TEST(!fb.GetName(kDefault).empty(), ());
TEST(!fb.GetName(kAltName).empty(), ());
TEST(fb.IsValid(), (fb));
}
}
UNIT_CLASS_TEST(TestWithClassificator, FBuilder_RemoveInconsistentTypes)
{
FeatureBuilderParams params;
base::StringIL arr[] = {
{"highway", "cycleway"}, {"hwtag", "onedir_bicycle"}, {"hwtag", "nobicycle"}, {"hwtag", "yesbicycle"}};
AddTypes(params, arr);
TEST_EQUAL(params.m_types.size(), 4, ());
TEST(params.RemoveInconsistentTypes(), ());
TEST_EQUAL(params.m_types.size(), 3, ());
TEST(!params.IsTypeExist(classif().GetTypeByPath({"hwtag", "nobicycle"})), ());
}
} // namespace feature_builder_test

View file

@ -0,0 +1,173 @@
#include "testing/testing.hpp"
#include "generator/feature_merger.hpp"
#include "indexer/classificator_loader.hpp"
#include "coding/point_coding.hpp"
#include <vector>
using namespace feature;
namespace
{
typedef m2::PointD P;
class VectorEmitter : public FeatureEmitterIFace
{
std::vector<FeatureBuilder> m_vec;
public:
virtual void operator()(FeatureBuilder const & fb) { m_vec.push_back(fb); }
size_t GetSize() const { return m_vec.size(); }
void Check(uint32_t type, size_t count) const
{
size_t test = 0;
for (size_t i = 0; i < m_vec.size(); ++i)
if (m_vec[i].HasType(type))
++test;
TEST_EQUAL(test, count, ());
}
};
} // namespace
UNIT_TEST(FeatureMerger_MultipleTypes)
{
classificator::Load();
P arrPt[] = {P(0, 0), P(1, 1), P(2, 2), P(3, 3)};
size_t const count = ARRAY_SIZE(arrPt) - 1;
FeatureBuilder arrF[count];
for (size_t i = 0; i < count; ++i)
{
arrF[i].SetLinear();
arrF[i].AssignPoints({arrPt[i], arrPt[i + 1]});
arrF[i].AddType(0);
}
arrF[0].AddType(1);
arrF[1].AddType(1);
arrF[0].AddType(2);
arrF[1].AddType(2);
arrF[1].AddType(3);
arrF[2].AddType(3);
arrF[1].AddType(4);
arrF[2].AddType(4);
FeatureMergeProcessor processor(kPointCoordBits);
for (size_t i = 0; i < count; ++i)
processor(arrF[i]);
VectorEmitter emitter;
processor.DoMerge(emitter);
TEST_EQUAL(emitter.GetSize(), 3, ());
emitter.Check(0, 1);
emitter.Check(1, 1);
emitter.Check(2, 1);
emitter.Check(3, 1);
emitter.Check(4, 1);
}
UNIT_TEST(FeatureMerger_Branches)
{
classificator::Load();
/* Try to unite next configuration
o
/\
o--o--o--o
\/
o
*/
std::vector<FeatureBuilder> vF;
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(-2, 0), P(-1, 0)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(-1, 0), P(0, 1)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(-1, 0), P(0, 0)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(-1, 0), P(0, -1)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(0, 1), P(1, 0)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(0, 0), P(1, 0)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(0, -1), P(1, 0)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(1, 0), P(2, 0)});
FeatureMergeProcessor processor(kPointCoordBits);
for (size_t i = 0; i < vF.size(); ++i)
{
vF[i].SetLinear();
vF[i].AddType(0);
processor(vF[i]);
}
VectorEmitter emitter;
processor.DoMerge(emitter);
TEST_LESS_OR_EQUAL(emitter.GetSize(), 2, ());
}
UNIT_TEST(FeatureMerger_Rounds)
{
classificator::Load();
std::vector<FeatureBuilder> vF;
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(-10, 0), P(-5, 0)});
// make first round feature
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(-4, 1), P(-3, 0), P(-4, -1), P(-5, 0), P(-4, 1)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(-3, 0), P(3, 0)});
// make second round feature
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(4, -1), P(3, 0), P(4, 1), P(5, 0), P(4, -1)});
vF.push_back(FeatureBuilder());
vF.back().AssignPoints({P(5, 0), P(10, 0)});
FeatureMergeProcessor processor(kPointCoordBits);
for (size_t i = 0; i < vF.size(); ++i)
{
vF[i].SetLinear();
vF[i].AddType(0);
processor(vF[i]);
}
VectorEmitter emitter;
processor.DoMerge(emitter);
TEST_EQUAL(emitter.GetSize(), 1, ());
}

View file

@ -0,0 +1,112 @@
#include "testing/testing.hpp"
#include "generator/filter_elements.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/osm_element.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
using namespace generator_tests;
using namespace generator;
using namespace platform::tests_support;
namespace
{
auto const kOsmElementEmpty = MakeOsmElement(0, {}, OsmElement::EntityType::Node);
auto const kOsmElementCity = MakeOsmElement(1, {{"place", "city"}, {"admin_level", "6"}}, OsmElement::EntityType::Node);
auto const kOsmElementCountry = MakeOsmElement(
2, {{"admin_level", "2"}, {"ISO3166-1:alpha2", "RU"}, {"ISO3166-1:alpha3", "RUS"}, {"ISO3166-1:numeric", "643"}},
OsmElement::EntityType::Relation);
} // namespace
UNIT_TEST(FilterData_Ids)
{
FilterData fd;
fd.AddSkippedId(kOsmElementEmpty.m_id);
TEST(fd.NeedSkipWithId(kOsmElementEmpty.m_id), ());
TEST(!fd.NeedSkipWithId(kOsmElementCity.m_id), ());
}
UNIT_TEST(FilterData_Tags)
{
FilterData fd;
FilterData::Tags tags{{"admin_level", "2"}};
fd.AddSkippedTags(tags);
TEST(fd.NeedSkipWithTags(kOsmElementCountry.Tags()), ());
TEST(!fd.NeedSkipWithTags(kOsmElementCity.Tags()), ());
}
UNIT_TEST(FilterElements_Case1)
{
ScopedFile sf("tmp.txt",
R"(
{
"node": {
"ids": [1]
},
"relation": {
"tags": [{
"ISO3166-1:alpha2": "*"
}]
}
}
)");
FilterElements fe(sf.GetFullPath());
TEST(fe.NeedSkip(kOsmElementCity), ());
TEST(!fe.NeedSkip(kOsmElementEmpty), ());
TEST(fe.NeedSkip(kOsmElementCountry), ());
}
UNIT_TEST(FilterElements_Case2)
{
ScopedFile sf("tmp.txt",
R"(
{
"node": {
"ids": [0, 1]
}
}
)");
FilterElements fe(sf.GetFullPath());
TEST(fe.NeedSkip(kOsmElementCity), ());
TEST(!fe.NeedSkip(kOsmElementCountry), ());
TEST(fe.NeedSkip(kOsmElementEmpty), ());
}
UNIT_TEST(FilterElements_Case3)
{
ScopedFile sf("tmp.txt",
R"(
{
"node": {
"tags": [{
"admin_level": "*"
}]
},
"relation": {
"tags": [{
"admin_level": "*"
}]
}
}
)");
FilterElements fe(sf.GetFullPath());
TEST(fe.NeedSkip(kOsmElementCity), ());
TEST(fe.NeedSkip(kOsmElementCountry), ());
TEST(!fe.NeedSkip(kOsmElementEmpty), ());
}
UNIT_TEST(FilterElements_Case4)
{
ScopedFile sf("tmp.txt",
R"(
{
}
)");
FilterElements fe(sf.GetFullPath());
TEST(!fe.NeedSkip(kOsmElementCity), ());
TEST(!fe.NeedSkip(kOsmElementCountry), ());
TEST(!fe.NeedSkip(kOsmElementEmpty), ());
}

View file

@ -0,0 +1,135 @@
#include "testing/testing.hpp"
#include "generator/composite_id.hpp"
#include "generator/gen_mwm_info.hpp"
#include "coding/file_writer.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
namespace
{
generator::CompositeId const kCid1(base::MakeOsmNode(0), base::MakeOsmNode(0));
generator::CompositeId const kCid2(base::MakeOsmNode(1), base::MakeOsmNode(1));
generator::CompositeId const kCid3(base::MakeOsmNode(1), base::MakeOsmNode(2));
uint32_t const kId1 = 0;
uint32_t const kId2 = 1;
uint32_t const kId3 = 2;
std::pair<generator::CompositeId, uint32_t> const kPId1(kCid1, kId1);
std::pair<generator::CompositeId, uint32_t> const kPId2(kCid2, kId2);
std::pair<generator::CompositeId, uint32_t> const kPId3(kCid3, kId3);
UNIT_TEST(OsmID2FeatureID_AddIds)
{
generator::OsmID2FeatureID mapping;
mapping.AddIds(kCid1, kId1);
mapping.AddIds(kCid2, kId2);
mapping.AddIds(kCid3, kId3);
std::vector<std::pair<generator::CompositeId, uint32_t>> const answer{kPId1, kPId2, kPId3};
size_t index = 0;
mapping.ForEach([&](auto const & pair)
{
TEST_EQUAL(pair, answer[index], ());
++index;
});
TEST_EQUAL(index, answer.size(), ());
}
UNIT_TEST(OsmID2FeatureID_GetFeatureId)
{
generator::OsmID2FeatureID mapping;
mapping.AddIds(kCid1, kId1);
mapping.AddIds(kCid2, kId2);
mapping.AddIds(kCid3, kId3);
{
std::vector<uint32_t> const answer{kId2, kId3};
TEST_EQUAL(mapping.GetFeatureIds(kCid2.m_additionalId), answer, ());
}
{
std::vector<uint32_t> const answer;
TEST_EQUAL(mapping.GetFeatureIds(base::GeoObjectId()), answer, ());
}
{
std::vector<uint32_t> const answer{
kId1,
};
TEST_EQUAL(mapping.GetFeatureIds(kCid1.m_additionalId), answer, ());
}
{
std::vector<uint32_t> const answer{
kId1,
};
TEST_EQUAL(mapping.GetFeatureIds(kCid1), answer, ());
}
{
std::vector<uint32_t> const answer{kId2};
TEST_EQUAL(mapping.GetFeatureIds(kCid2), answer, ());
}
{
std::vector<uint32_t> const answer{
kId3,
};
TEST_EQUAL(mapping.GetFeatureIds(kCid3), answer, ());
}
{
std::vector<uint32_t> const answer;
TEST_EQUAL(mapping.GetFeatureIds(generator::CompositeId(base::GeoObjectId())), answer, ());
}
}
UNIT_TEST(OsmID2FeatureID_ReadWrite)
{
using namespace platform::tests_support;
auto const filename = "test.osm2ft";
ScopedFile sf(filename, ScopedFile::Mode::DoNotCreate);
{
generator::OsmID2FeatureID mapping;
mapping.AddIds(kCid1, kId1);
mapping.AddIds(kCid2, kId2);
mapping.AddIds(kCid3, kId3);
FileWriter writer(sf.GetFullPath());
mapping.Write(writer);
}
{
generator::OsmID2FeatureID mapping;
mapping.ReadFromFile(sf.GetFullPath());
std::vector<std::pair<generator::CompositeId, uint32_t>> const answer{kPId1, kPId2, kPId3};
size_t index = 0;
mapping.ForEach([&](auto const & pair)
{
TEST_EQUAL(pair, answer[index], ());
++index;
});
TEST_EQUAL(index, answer.size(), ());
}
}
UNIT_TEST(OsmID2FeatureID_WorkingWithOldFormat)
{
using namespace platform::tests_support;
auto const filename = "test.osm2ft";
std::vector<std::pair<base::GeoObjectId, uint32_t>> answer{{kCid1.m_mainId, kId1}, {kCid2.m_mainId, kId2}};
ScopedFile sf(filename, ScopedFile::Mode::DoNotCreate);
{
FileWriter writer(sf.GetFullPath());
rw::WriteVectorOfPOD(writer, answer);
}
{
generator::OsmID2FeatureID mapping;
mapping.ReadFromFile(sf.GetFullPath());
size_t index = 0;
mapping.ForEach([&](auto const & pair)
{
TEST_EQUAL(pair.first.m_mainId, answer[index].first, ());
TEST_EQUAL(pair.second, answer[index].second, ());
++index;
});
TEST_EQUAL(index, answer.size(), ());
}
}
} // namespace

View file

@ -0,0 +1,144 @@
#include "testing/testing.hpp"
#include "generator/composite_id.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "generator/hierarchy_entry.hpp"
#include "indexer/complex/tree_node.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "base/geo_object_id.hpp"
namespace hierarchy_entry_tests
{
using generator::tests_support::TestWithClassificator;
using platform::tests_support::ScopedFile;
std::string const kCsv1 =
"13835058055284963881 9223372037111861697;"
";"
"1;"
"37.5303271;"
"67.3684086;"
"amenity-university;"
"Lomonosov Moscow State Univesity;"
"Russia_Moscow\n"
"9223372036879747192 9223372036879747192;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5272372;"
"67.3775872;"
"leisure-garden;"
"Ботанический сад МГУ;"
"Russia_Moscow\n"
"9223372036938640141 9223372036938640141;"
"9223372036879747192 9223372036879747192;"
"3;"
"37.5274156;"
"67.3758813;"
"amenity-university;"
"Отдел флоры;"
"Russia_Moscow\n"
"9223372036964008573 9223372036964008573;"
"9223372036879747192 9223372036879747192;"
"3;"
"37.5279467;"
"67.3756452;"
"amenity-university;"
"Дендрарий Ботанического сада МГУ;"
"Russia_Moscow\n"
"4611686019330739245 4611686019330739245;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5357492;"
"67.3735142;"
"historic-memorial;"
"Александр Иванович Герцен;"
"Russia_Moscow\n"
"4611686019330739269 4611686019330739269;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5351269;"
"67.3741606;"
"historic-memorial;"
"Николай Гаврилович Чернышевский;"
"Russia_Moscow\n"
"4611686019330739276 4611686019330739276;"
"13835058055284963881 9223372037111861697;"
"2;"
"37.5345234;"
"67.3723206;"
"historic-memorial;"
"Николай Егорович Жуковский;"
"Russia_Moscow";
generator::CompositeId MakeId(uint64_t f, uint64_t s)
{
return generator::CompositeId(base::GeoObjectId(f), base::GeoObjectId(s));
}
UNIT_CLASS_TEST(TestWithClassificator, Complex_HierarchyEntryCsv)
{
generator::HierarchyEntry e;
e.m_id = MakeId(4611686018725364866ull, 4611686018725364866ull);
e.m_parentId = MakeId(13835058055283414237ull, 9223372037374119493ull);
e.m_depth = 2;
e.m_center.x = 37.6262604;
e.m_center.y = 67.6098812;
e.m_type = classif().GetTypeByPath({"amenity", "restaurant"});
e.m_name = "Rybatskoye podvor'ye";
e.m_country = "Russia_Moscow";
auto const row = generator::hierarchy::HierarchyEntryToCsvRow(e);
TEST_EQUAL(row.size(), 8, ());
TEST_EQUAL(row[0], "4611686018725364866 4611686018725364866", ());
TEST_EQUAL(row[1], "13835058055283414237 9223372037374119493", ());
TEST_EQUAL(row[2], "2", ());
TEST_EQUAL(row[3], "37.6262604", ());
TEST_EQUAL(row[4], "67.6098812", ());
TEST_EQUAL(row[5], "amenity-restaurant", ());
TEST_EQUAL(row[6], "Rybatskoye podvor'ye", ());
TEST_EQUAL(row[7], "Russia_Moscow", ());
auto const res = generator::hierarchy::HierarchyEntryFromCsvRow(row);
TEST_EQUAL(e, res, ());
}
UNIT_CLASS_TEST(TestWithClassificator, Complex_LoadHierachy)
{
auto const filename = "test.csv";
ScopedFile sf(filename, kCsv1);
auto const forest = generator::hierarchy::LoadHierachy(sf.GetFullPath());
TEST_EQUAL(forest.size(), 1, ());
auto const & tree = *forest.begin();
LOG(LINFO, (tree));
TEST_EQUAL(tree_node::Size(tree), 7, ());
auto node = tree_node::FindIf(
tree, [](auto const & e) { return e.m_id == MakeId(13835058055284963881ull, 9223372037111861697ull); });
TEST(node, ());
TEST(!node->HasParent(), ());
TEST_EQUAL(node->GetChildren().size(), 4, ());
node = tree_node::FindIf(
tree, [](auto const & e) { return e.m_id == MakeId(9223372036879747192ull, 9223372036879747192ull); });
TEST(node, ());
TEST(node->HasParent(), ());
TEST_EQUAL(node->GetParent()->GetData().m_id, MakeId(13835058055284963881ull, 9223372037111861697ull), ());
TEST_EQUAL(node->GetChildren().size(), 2, ());
node = tree_node::FindIf(
tree, [](auto const & e) { return e.m_id == MakeId(9223372036938640141ull, 9223372036938640141ull); });
TEST(node, ());
TEST_EQUAL(node->GetData().m_depth, tree_node::GetDepth(node), ());
TEST_EQUAL(node->GetParent()->GetData().m_id, MakeId(9223372036879747192ull, 9223372036879747192ull), ());
TEST_EQUAL(node->GetChildren().size(), 0, ());
}
} // namespace hierarchy_entry_tests

View file

@ -0,0 +1,128 @@
#include "testing/testing.hpp"
#include "generator/feature_builder.hpp"
#include "generator/filter_complex.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "generator/hierarchy.hpp"
#include "indexer/classificator.hpp"
#include "base/stl_helpers.hpp"
#include <iterator>
#include <vector>
namespace
{
using namespace generator::tests_support;
std::vector<feature::FeatureBuilder> MakeTestSet1()
{
std::vector<feature::FeatureBuilder> fbs;
{
feature::FeatureBuilder outline;
outline.AddPolygon({{0.0, 0.0}, {0.0, 10.0}, {10.0, 10.0}, {10.0, 0.0}});
outline.AddOsmId(base::MakeOsmWay(1));
outline.AddType(classif().GetTypeByPath({"building"}));
outline.AddType(classif().GetTypeByPath({"tourism", "attraction"}));
outline.SetArea();
fbs.emplace_back(outline);
}
{
feature::FeatureBuilder buildingPart;
buildingPart.AddPolygon({{0.0, 0.0}, {0.0, 8.0}, {8.0, 8.0}, {8.0, 0.0}});
buildingPart.AddOsmId(base::MakeOsmWay(2));
buildingPart.AddType(classif().GetTypeByPath({"building:part"}));
buildingPart.SetArea();
fbs.emplace_back(buildingPart);
}
{
feature::FeatureBuilder buildingPart;
buildingPart.AddPolygon({{0.0, 0.0}, {0.0, 7.0}, {7.0, 7.0}, {7.0, 0.0}});
buildingPart.AddOsmId(base::MakeOsmWay(3));
buildingPart.AddType(classif().GetTypeByPath({"building:part"}));
buildingPart.SetArea();
fbs.emplace_back(buildingPart);
}
{
feature::FeatureBuilder buildingPart;
buildingPart.AddPolygon({{0.0, 0.0}, {0.0, 6.0}, {6.0, 6.0}, {6.0, 0.0}});
buildingPart.AddOsmId(base::MakeOsmWay(4));
buildingPart.AddType(classif().GetTypeByPath({"building:part"}));
buildingPart.SetArea();
fbs.emplace_back(buildingPart);
}
return fbs;
}
void TestDepthNodeById(generator::hierarchy::HierarchyLinker::Node::Ptr const & tree, uint64_t id, size_t depth)
{
auto osmId = base::MakeOsmWay(id);
TEST_EQUAL(tree_node::GetDepth(
tree_node::FindIf(tree, [&](auto const & data) { return data.GetCompositeId().m_mainId == osmId; })),
depth, ());
}
UNIT_CLASS_TEST(TestWithClassificator, ComplexHierarchy_FlattenBuildingParts)
{
auto trees = generator::hierarchy::BuildHierarchy(MakeTestSet1(), generator::hierarchy::GetMainType,
std::make_shared<generator::FilterComplex>());
TEST_EQUAL(trees.size(), 1, ());
TEST_EQUAL(tree_node::Size(trees[0]), 4, ());
TestDepthNodeById(trees[0], 2 /* id */, 2 /* depth */);
TestDepthNodeById(trees[0], 3 /* id */, 3 /* depth */);
TestDepthNodeById(trees[0], 4 /* id */, 4 /* depth */);
generator::hierarchy::FlattenBuildingParts(trees);
TEST_EQUAL(trees.size(), 1, ());
TEST_EQUAL(tree_node::Size(trees[0]), 4, ());
TestDepthNodeById(trees[0], 2 /* id */, 2 /* depth */);
TestDepthNodeById(trees[0], 3 /* id */, 2 /* depth */);
TestDepthNodeById(trees[0], 4 /* id */, 2 /* depth */);
}
UNIT_CLASS_TEST(TestWithClassificator, ComplexHierarchy_AddChildrenTo)
{
auto trees = generator::hierarchy::BuildHierarchy(MakeTestSet1(), generator::hierarchy::GetMainType,
std::make_shared<generator::FilterComplex>());
TEST_EQUAL(trees.size(), 1, ());
TEST_EQUAL(tree_node::Size(trees[0]), 4, ());
auto osmId = base::MakeOsmWay(3);
generator::hierarchy::AddChildrenTo(trees, [&](auto const & compositeId)
{
std::vector<generator::hierarchy::HierarchyPlace> fbs;
if (compositeId.m_mainId == osmId)
{
feature::FeatureBuilder buildingPart;
buildingPart.AddPolygon({{6.0, 6.0}, {6.0, 7.0}, {7.0, 7.0}, {7.0, 6.0}});
buildingPart.AddOsmId(base::MakeOsmWay(5));
buildingPart.AddType(classif().GetTypeByPath({"building:part"}));
buildingPart.SetArea();
fbs.emplace_back(generator::hierarchy::HierarchyPlace(buildingPart));
}
return fbs;
});
auto node = tree_node::FindIf(trees[0], [&](auto const & data) { return data.GetCompositeId().m_mainId == osmId; });
TEST(node, ());
auto const children = node->GetChildren();
TEST_EQUAL(children.size(), 2, ());
auto it = base::FindIf(
children, [](auto const & node) { return node->GetData().GetCompositeId().m_mainId == base::MakeOsmWay(4); });
CHECK(it != std::end(children), ());
CHECK((*it)->HasParent() && (*it)->GetParent()->GetData().GetCompositeId().m_mainId == osmId, ());
it = base::FindIf(children,
[](auto const & node) { return node->GetData().GetCompositeId().m_mainId == base::MakeOsmWay(5); });
CHECK(it != std::end(children), ());
CHECK((*it)->HasParent() && (*it)->GetParent()->GetData().GetCompositeId().m_mainId == osmId, ());
}
} // namespace

View file

@ -0,0 +1,109 @@
//
// intermediate_data_test.cpp
// generator_tool
//
// Created by Sergey Yershov on 20.08.15.
// Copyright (c) 2015 maps.me. All rights reserved.
//
#include "testing/testing.hpp"
#include "generator/intermediate_elements.hpp"
#include "coding/reader.hpp"
#include "coding/writer.hpp"
#include <cstdint>
#include <string>
#include <vector>
namespace intermediate_data_test
{
UNIT_TEST(Intermediate_Data_empty_way_element_save_load_test)
{
WayElement e1(1 /* fake osm id */);
using TBuffer = std::vector<uint8_t>;
TBuffer buffer;
MemWriter<TBuffer> w(buffer);
e1.Write(w);
MemReader r(buffer.data(), buffer.size());
WayElement e2(1 /* fake osm id */);
e2.Read(r);
TEST_EQUAL(e2.m_nodes.size(), 0, ());
}
UNIT_TEST(Intermediate_Data_way_element_save_load_test)
{
std::vector<uint64_t> testData = {0, 1, 2, 3, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF};
WayElement e1(1 /* fake osm id */);
e1.m_nodes = testData;
using TBuffer = std::vector<uint8_t>;
TBuffer buffer;
MemWriter<TBuffer> w(buffer);
e1.Write(w);
MemReader r(buffer.data(), buffer.size());
WayElement e2(1 /* fake osm id */);
e2.Read(r);
TEST_EQUAL(e2.m_nodes, testData, ());
}
UNIT_TEST(Intermediate_Data_relation_element_save_load_test)
{
std::vector<RelationElement::Member> testData = {{1, "inner"}, {2, "outer"}, {3, "unknown"}, {4, "inner role"}};
RelationElement e1;
e1.m_nodes = testData;
e1.m_ways = testData;
e1.m_tags.emplace("key1", "value1");
e1.m_tags.emplace("key2", "value2");
e1.m_tags.emplace("key3", "value3");
e1.m_tags.emplace("key4", "value4");
using TBuffer = std::vector<uint8_t>;
TBuffer buffer;
MemWriter<TBuffer> w(buffer);
e1.Write(w);
MemReader r(buffer.data(), buffer.size());
RelationElement e2;
e2.m_nodes.emplace_back(30, "000unknown");
e2.m_nodes.emplace_back(40, "000inner role");
e2.m_ways.emplace_back(10, "000inner");
e2.m_ways.emplace_back(20, "000outer");
e2.m_tags.emplace("key1old", "value1old");
e2.m_tags.emplace("key2old", "value2old");
e2.Read(r);
TEST_EQUAL(e2.m_nodes, testData, ());
TEST_EQUAL(e2.m_ways, testData, ());
TEST_EQUAL(e2.m_tags.size(), 4, ());
TEST_EQUAL(e2.m_tags["key1"], "value1", ());
TEST_EQUAL(e2.m_tags["key2"], "value2", ());
TEST_EQUAL(e2.m_tags["key3"], "value3", ());
TEST_EQUAL(e2.m_tags["key4"], "value4", ());
TEST_NOT_EQUAL(e2.m_tags["key1old"], "value1old", ());
TEST_NOT_EQUAL(e2.m_tags["key2old"], "value2old", ());
}
} // namespace intermediate_data_test

View file

@ -0,0 +1,442 @@
#include "testing/testing.hpp"
#include "generator/feature_builder.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include "generator/maxspeeds_builder.hpp"
#include "generator/maxspeeds_collector.hpp"
#include "generator/maxspeeds_parser.hpp"
#include "generator/osm_element.hpp"
#include "generator/restriction_generator.hpp"
#include "generator/routing_helpers.hpp"
#include "routing/maxspeeds.hpp"
#include "routing/maxspeeds_serialization.hpp"
#include "routing/routing_helpers.hpp"
#include "routing_common/maxspeed_conversion.hpp"
#include "indexer/classificator.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/data_source.hpp"
#include "indexer/feature.hpp"
#include "indexer/feature_processor.hpp"
#include "indexer/mwm_set.hpp"
#include "platform/local_country_file.hpp"
#include "platform/measurement_utils.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "geometry/point2d.hpp"
#include "base/file_name_utils.hpp"
#include "base/geo_object_id.hpp"
#include "base/scope_guard.hpp"
#include <algorithm>
#include <cstdint>
#include <map>
#include <string>
#include <vector>
namespace maxspeeds_tests
{
using namespace generator;
using namespace generator_tests;
using namespace measurement_utils;
using namespace platform::tests_support;
using namespace platform;
using namespace routing;
using namespace routing_builder;
using std::string;
using Features = std::vector<std::vector<m2::PointD>>;
// Directory name for creating test mwm and temporary files.
string const kTestDir = "maxspeeds_generation_test";
// Temporary mwm name for testing.
string const kTestMwm = "test";
// File name for keeping maxspeeds.
string const kCsv = "maxspeeds.csv";
void BuildGeometry(Features const & roads, LocalCountryFile & country)
{
generator::tests_support::TestMwmBuilder builder(country, feature::DataHeader::MapType::Country);
for (auto const & road : roads)
builder.Add(generator::tests_support::TestStreet(road, string(), string()));
}
void TestMaxspeedsSection(Features const & roads, string const & maxspeedsCsvContent,
FeatureIdToOsmId const & featureIdToOsmId)
{
classificator::Load();
string const testDirFullPath = base::JoinPath(GetPlatform().WritableDir(), kTestDir);
ScopedDir testScopedDir(kTestDir);
// Writing |maxspeedsCsvContent| to a file in |kTestDir|.
ScopedFile testScopedMaxspeedsCsv(base::JoinPath(kTestDir, kCsv), maxspeedsCsvContent);
// Writing |roads| to test mwm.
LocalCountryFile country(testDirFullPath, CountryFile(kTestMwm), 1 /* version */);
string const testMwm = kTestMwm + DATA_FILE_EXTENSION;
ScopedFile testScopedMwm(base::JoinPath(kTestDir, testMwm), ScopedFile::Mode::Create);
BuildGeometry(roads, country);
string const testMwmFullPath = base::JoinPath(testDirFullPath, testMwm);
// Create routing graph for test mwm.
auto const countryParentGetter = [](std::string const &) { return string(); };
BuildRoutingIndex(testMwmFullPath, kTestMwm, countryParentGetter);
auto routingGraph = CreateIndexGraph(testMwmFullPath, kTestMwm, countryParentGetter);
TEST(routingGraph, ());
// Creating maxspeed section in test.mwm.
BuildMaxspeedsSection(routingGraph.get(), testMwmFullPath, featureIdToOsmId, base::JoinPath(testDirFullPath, kCsv));
// Loading maxspeed section.
FrozenDataSource dataSource;
auto const regResult = dataSource.RegisterMap(country);
TEST_EQUAL(regResult.second, MwmSet::RegResult::Success, ());
auto const & mwmId = regResult.first;
auto const handle = dataSource.GetMwmHandleById(mwmId);
TEST(handle.IsAlive(), ());
auto const maxspeeds = LoadMaxspeeds(handle);
TEST(maxspeeds, ());
// Testing maxspeed section content.
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseMaxspeeds(base::JoinPath(testDirFullPath, kCsv), osmIdToMaxspeed), ());
auto processor = [&](FeatureType & f, uint32_t const & id)
{
TEST(IsRoad(feature::TypesHolder(f)), ());
// Looking for maxspeed from csv.
auto const itOsmId = featureIdToOsmId.find(id);
TEST(itOsmId != featureIdToOsmId.cend(), ());
auto const itMaxspeedCsv = osmIdToMaxspeed.find(itOsmId->second);
if (itMaxspeedCsv == osmIdToMaxspeed.cend())
return; // No maxspeed for feature |id|.
Maxspeed const maxspeedCsv = itMaxspeedCsv->second;
Maxspeed const maxspeedMwm = maxspeeds->GetMaxspeed(id);
// Comparing maxspeed from csv and maxspeed from mwm section.
TEST_EQUAL(maxspeedCsv, maxspeedMwm, ());
};
feature::ForEachFeature(testMwmFullPath, processor);
}
// Note. ParseMaxspeeds() is not tested in TestMaxspeedsSection() because it's used twice there.
// So it's important to test the function separately.
bool ParseCsv(string const & maxspeedsCsvContent, OsmIdToMaxspeed & mapping)
{
string const testDirFullPath = base::JoinPath(GetPlatform().WritableDir(), kTestDir);
ScopedDir testScopedDir(kTestDir);
ScopedFile testScopedMaxspeedsCsv(base::JoinPath(kTestDir, kCsv), maxspeedsCsvContent);
return ParseMaxspeeds(base::JoinPath(testDirFullPath, kCsv), mapping);
}
UNIT_TEST(Maxspeed_ValueToSpeed)
{
SpeedInUnits speed;
TEST(ParseMaxspeedTag("RU:rural", speed), ());
TEST_EQUAL(speed, SpeedInUnits(90, Units::Metric), ());
TEST(ParseMaxspeedTag("90", speed), ());
TEST_EQUAL(speed, SpeedInUnits(90, Units::Metric), ());
TEST(ParseMaxspeedTag("90 ", speed), ());
TEST_EQUAL(speed, SpeedInUnits(90, Units::Metric), ());
TEST(ParseMaxspeedTag("60kmh", speed), ());
TEST_EQUAL(speed, SpeedInUnits(60, Units::Metric), ());
TEST(ParseMaxspeedTag("60 kmh", speed), ());
TEST_EQUAL(speed, SpeedInUnits(60, Units::Metric), ());
TEST(ParseMaxspeedTag("60 kmh", speed), ());
TEST_EQUAL(speed, SpeedInUnits(60, Units::Metric), ());
TEST(ParseMaxspeedTag("60 kmh and some other string", speed), ());
TEST_EQUAL(speed, SpeedInUnits(60, Units::Metric), ());
TEST(ParseMaxspeedTag("75mph", speed), ());
TEST_EQUAL(speed, SpeedInUnits(75, Units::Imperial), ());
TEST(ParseMaxspeedTag("75 mph", speed), ());
TEST_EQUAL(speed, SpeedInUnits(75, Units::Imperial), ());
TEST(ParseMaxspeedTag("75 mph", speed), ());
TEST_EQUAL(speed, SpeedInUnits(75, Units::Imperial), ());
TEST(ParseMaxspeedTag("75 mph and some other string", speed), ());
TEST_EQUAL(speed, SpeedInUnits(75, Units::Imperial), ());
TEST(ParseMaxspeedTag("75mph", speed), ());
TEST_EQUAL(speed, SpeedInUnits(75, Units::Imperial), ());
TEST(!ParseMaxspeedTag("some other string", speed), ());
TEST(!ParseMaxspeedTag("60 kmph", speed), ());
TEST(!ParseMaxspeedTag("1234567890 kmh", speed), ());
TEST(!ParseMaxspeedTag("1234567890 mph", speed), ());
TEST(!ParseMaxspeedTag("1234567890", speed), ());
}
UNIT_TEST(Maxspeed_ParseEmpty)
{
string const maxspeedsCsvContent;
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
TEST(osmIdToMaxspeed.empty(), ());
}
UNIT_TEST(Maxspeed_Parse1)
{
string const maxspeedsCsvContent = R"(10,Metric,60
11,Metric,90)";
OsmIdToMaxspeed const expectedMapping = {{base::MakeOsmWay(10), {Units::Metric, 60, kInvalidSpeed}},
{base::MakeOsmWay(11), {Units::Metric, 90, kInvalidSpeed}}};
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
TEST_EQUAL(osmIdToMaxspeed, expectedMapping, ());
}
UNIT_TEST(Maxspeed_Parse2)
{
string const maxspeedsCsvContent = R"(10,Metric,60,80
11,Metric,120)";
OsmIdToMaxspeed const expectedMapping = {{base::MakeOsmWay(10), {Units::Metric, 60, 80}},
{base::MakeOsmWay(11), {Units::Metric, 120, kInvalidSpeed}}};
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
TEST_EQUAL(osmIdToMaxspeed, expectedMapping, ());
}
UNIT_TEST(Maxspeed_Parse3)
{
string const maxspeedsCsvContent = R"(184467440737095516,Imperial,60,80
184467440737095517,Metric,120)";
OsmIdToMaxspeed const expectedMapping = {{base::MakeOsmWay(184467440737095516), {Units::Imperial, 60, 80}},
{base::MakeOsmWay(184467440737095517), {Units::Metric, 120, kInvalidSpeed}}};
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
TEST_EQUAL(osmIdToMaxspeed, expectedMapping, ());
}
UNIT_TEST(Maxspeed_Parse4)
{
// Note. kNoneMaxSpeed == 65534 and kWalkMaxSpeed == 65533.
string const maxspeedsCsvContent = R"(1,Metric,200,65534
2,Metric,65533)";
OsmIdToMaxspeed const expectedMapping = {{base::MakeOsmWay(1), {Units::Metric, 200, kNoneMaxSpeed}},
{base::MakeOsmWay(2), {Units::Metric, kWalkMaxSpeed, kInvalidSpeed}}};
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
TEST_EQUAL(osmIdToMaxspeed, expectedMapping, ());
}
UNIT_TEST(Maxspeed_Parse5)
{
string const maxspeedsCsvContent = R"(2,Metric,10)";
OsmIdToMaxspeed const expectedMapping = {{base::MakeOsmWay(2), {Units::Metric, 10, kInvalidSpeed}}};
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
TEST_EQUAL(osmIdToMaxspeed, expectedMapping, ());
}
UNIT_TEST(Maxspeed_Parse6)
{
string const maxspeedsCsvContent = R"(2U,Metric,10)";
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(!ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
}
UNIT_TEST(Maxspeed_Parse7)
{
string const maxspeedsCsvContent = R"(2,Metric)";
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(!ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
}
UNIT_TEST(Maxspeed_Parse8)
{
string const maxspeedsCsvContent = R"(2,Metric,10,11m)";
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(!ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
}
UNIT_TEST(Maxspeed_ParseBig)
{
// Note. kNoneMaxSpeed == 65534.
string const maxspeedsCsvContent = R"(100,Metric,200,65534
101,Metric,60,90
102,Metric,60
103,Metric,90)";
OsmIdToMaxspeed const expectedMapping = {{base::MakeOsmWay(100), {Units::Metric, 200, kNoneMaxSpeed}},
{base::MakeOsmWay(101), {Units::Metric, 60, 90}},
{base::MakeOsmWay(102), {Units::Metric, 60, kInvalidSpeed}},
{base::MakeOsmWay(103), {Units::Metric, 90, kInvalidSpeed}}};
OsmIdToMaxspeed osmIdToMaxspeed;
TEST(ParseCsv(maxspeedsCsvContent, osmIdToMaxspeed), ());
TEST_EQUAL(osmIdToMaxspeed, expectedMapping, ());
}
UNIT_TEST(Maxspeed_SectionEmpty)
{
Features const roads;
string const maxspeedsCsvContent;
FeatureIdToOsmId const featureIdToOsmId;
TestMaxspeedsSection(roads, maxspeedsCsvContent, featureIdToOsmId);
}
UNIT_TEST(Maxspeed_Section1)
{
Features const roads = {{{0.0, 0.0}, {0.0, 1.0}, {0.0, 2.0}} /* Points of feature 0 */,
{{1.0, 0.0}, {1.0, 1.0}, {1.0, 2.0}} /* Points of feature 1 */};
string const maxspeedsCsvContent = R"(25258932,Metric,60
25258943,Metric,90)";
FeatureIdToOsmId const featureIdToOsmId = {{0 /* feature id */, base::MakeOsmWay(25258932)},
{1 /* feature id */, base::MakeOsmWay(25258943)}};
TestMaxspeedsSection(roads, maxspeedsCsvContent, featureIdToOsmId);
}
UNIT_TEST(Maxspeed_Section2)
{
Features const roads = {{{0.0, 0.0}, {0.0, 1.0}} /* Points of feature 0 */,
{{1.0, 0.0}, {1.0, 2.0}} /* Points of feature 1 */,
{{1.0, 2.0}, {1.0, 3.0}} /* Points of feature 2 */};
string const maxspeedsCsvContent = R"(25258932,Metric,60,40
32424,Metric,120)";
FeatureIdToOsmId const featureIdToOsmId = {{0 /* feature id */, base::MakeOsmWay(25258932)},
{1 /* feature id */, base::MakeOsmWay(25258943)},
{2 /* feature id */, base::MakeOsmWay(32424)}};
TestMaxspeedsSection(roads, maxspeedsCsvContent, featureIdToOsmId);
}
UNIT_TEST(Maxspeed_Section3)
{
Features const roads = {{{0.0, 0.0}, {0.0, 1.0}} /* Points of feature 0 */,
{{1.0, 0.0}, {1.0, 2.0}} /* Points of feature 1 */,
{{1.0, 2.0}, {1.0, 3.0}} /* Points of feature 2 */};
// Note. kNoneMaxSpeed == 65535 and kWalkMaxSpeed == 65534.
string const maxspeedsCsvContent = R"(25252,Metric,120,65534
258943,Metric,65533
32424,Metric,10,65533)";
FeatureIdToOsmId const featureIdToOsmId = {{0 /* feature id */, base::MakeOsmWay(25252)},
{1 /* feature id */, base::MakeOsmWay(258943)},
{2 /* feature id */, base::MakeOsmWay(32424)}};
TestMaxspeedsSection(roads, maxspeedsCsvContent, featureIdToOsmId);
}
UNIT_TEST(Maxspeed_Section4)
{
Features const roads = {{{0.0, 0.0}, {0.0, 1.0}} /* Points of feature 0 */,
{{1.0, 0.0}, {0.0, 0.0}} /* Points of feature 1 */};
string const maxspeedsCsvContent = R"(50000000000,Imperial,30
50000000001,Imperial,50)";
FeatureIdToOsmId const featureIdToOsmId = {{0 /* feature id */, base::MakeOsmWay(50000000000)},
{1 /* feature id */, base::MakeOsmWay(50000000001)}};
TestMaxspeedsSection(roads, maxspeedsCsvContent, featureIdToOsmId);
}
UNIT_TEST(Maxspeed_SectionBig)
{
Features const roads = {{{0.0, 0.0}, {0.0, 1.0}} /* Points of feature 0 */,
{{1.0, 0.0}, {1.0, 2.0}} /* Points of feature 1 */,
{{1.0, 2.0}, {1.0, 3.0}} /* Points of feature 2 */,
{{1.0, 2.0}, {1.0, 4.0}} /* Points of feature 3 */,
{{1.0, 2.0}, {2.0, 3.0}} /* Points of feature 4 */,
{{1.0, 2.0}, {2.0, 7.0}} /* Points of feature 5 */,
{{1.0, 2.0}, {7.0, 4.0}} /* Points of feature 6 */};
// Note. kNoneMaxSpeed == 65534.
string const maxspeedsCsvContent = R"(100,Imperial,100,65534
200,Imperial,50
300,Imperial,30
400,Imperial,10,20
600,)"
"Imperial,50,20\n700,Imperial,10\n";
FeatureIdToOsmId const featureIdToOsmId = {
{0 /* feature id */, base::MakeOsmWay(100)}, {1 /* feature id */, base::MakeOsmWay(200)},
{2 /* feature id */, base::MakeOsmWay(300)}, {3 /* feature id */, base::MakeOsmWay(400)},
{4 /* feature id */, base::MakeOsmWay(500)}, {5 /* feature id */, base::MakeOsmWay(600)},
{6 /* feature id */, base::MakeOsmWay(700)}};
TestMaxspeedsSection(roads, maxspeedsCsvContent, featureIdToOsmId);
}
UNIT_TEST(Maxspeed_CollectorMerge)
{
classificator::Load();
auto const filename = GetFileName();
SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename)));
auto c1 = std::make_shared<MaxspeedsCollector>(filename);
auto c2 = c1->Clone(nullptr);
c1->CollectFeature({}, MakeOsmElement(1 /* id */, {{"maxspeed", "50"}} /* tags */, OsmElement::EntityType::Way));
c2->CollectFeature({}, MakeOsmElement(2 /* id */, {{"maxspeed", "60"}} /* tags */, OsmElement::EntityType::Way));
c1->CollectFeature({}, MakeOsmElement(3 /* id */, {{"maxspeed", "70"}} /* tags */, OsmElement::EntityType::Way));
c2->CollectFeature({}, MakeOsmElement(4 /* id */, {{"maxspeed", "80"}} /* tags */, OsmElement::EntityType::Way));
c1->Finish();
c2->Finish();
c1->Merge(*c2);
c1->Finalize();
OsmIdToMaxspeed osmIdToMaxspeed;
ParseMaxspeeds(filename, osmIdToMaxspeed);
TEST_EQUAL(osmIdToMaxspeed.size(), 4, ());
TEST_EQUAL(osmIdToMaxspeed[base::MakeOsmWay(1)].GetForward(), static_cast<MaxspeedType>(50), ());
TEST_EQUAL(osmIdToMaxspeed[base::MakeOsmWay(2)].GetForward(), static_cast<MaxspeedType>(60), ());
TEST_EQUAL(osmIdToMaxspeed[base::MakeOsmWay(3)].GetForward(), static_cast<MaxspeedType>(70), ());
TEST_EQUAL(osmIdToMaxspeed[base::MakeOsmWay(4)].GetForward(), static_cast<MaxspeedType>(80), ());
}
UNIT_TEST(Maxspeed_CollectorSmoke)
{
classificator::Load();
auto const filename = GetFileName();
SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename)));
feature::FeatureBuilder builder;
auto c1 = std::make_shared<MaxspeedsCollector>(filename);
c1->CollectFeature(builder,
MakeOsmElement(1 /* id */, {{"maxspeed:forward", "50"}} /* tags */, OsmElement::EntityType::Way));
c1->CollectFeature(builder,
MakeOsmElement(2 /* id */, {{"maxspeed:backward", "50"}} /* tags */, OsmElement::EntityType::Way));
builder.SetType(classif().GetTypeByPath({"highway", "motorway_link"}));
c1->CollectFeature(builder,
MakeOsmElement(3 /* id */, {{"maxspeed:advisory", "70"}} /* tags */, OsmElement::EntityType::Way));
builder.SetType(classif().GetTypeByPath({"highway", "trunk"}));
c1->CollectFeature(builder,
MakeOsmElement(4 /* id */, {{"maxspeed:advisory", "70"}} /* tags */, OsmElement::EntityType::Way));
builder.SetType(classif().GetTypeByPath({"highway", "trunk_link"}));
c1->CollectFeature(builder, MakeOsmElement(5 /* id */, {{"maxspeed:advisory", "10"}, {"maxspeed", "20"}} /* tags */,
OsmElement::EntityType::Way));
c1->Finish();
c1->Finalize();
OsmIdToMaxspeed osmIdToMaxspeed;
ParseMaxspeeds(filename, osmIdToMaxspeed);
TEST_EQUAL(osmIdToMaxspeed.size(), 3, ());
TEST_EQUAL(osmIdToMaxspeed[base::MakeOsmWay(1)].GetForward(), static_cast<MaxspeedType>(50), ());
TEST(osmIdToMaxspeed.find(base::MakeOsmWay(2)) == osmIdToMaxspeed.end(), ());
TEST_EQUAL(osmIdToMaxspeed[base::MakeOsmWay(3)].GetForward(), static_cast<MaxspeedType>(70), ());
TEST(osmIdToMaxspeed.find(base::MakeOsmWay(4)) == osmIdToMaxspeed.end(), ());
TEST_EQUAL(osmIdToMaxspeed[base::MakeOsmWay(5)].GetForward(), static_cast<MaxspeedType>(20), ());
}
} // namespace maxspeeds_tests

View file

@ -0,0 +1,96 @@
#include "testing/testing.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/collector_collection.hpp"
#include "generator/collector_interface.hpp"
#include "generator/collector_tag.hpp"
#include "platform/platform.hpp"
#include "base/geo_object_id.hpp"
#include <fstream>
#include <memory>
#include <string>
#include <vector>
using namespace base;
using namespace generator;
using namespace generator_tests;
namespace
{
auto const kEmptyValidator = [](auto const &) { return true; };
} // namespace
UNIT_TEST(MergeCollector_MergeCase1)
{
auto const filename = GetFileName();
std::string const tagKey = "admin_level";
auto collector1 = std::make_shared<CollectorTag>(filename, tagKey, kEmptyValidator);
collector1->Collect(MakeOsmElement(1 /* id */, {{"admin_level", "1"}}, OsmElement::EntityType::Relation));
collector1->Collect(MakeOsmElement(2 /* id */, {{"admin_level", "2"}}, OsmElement::EntityType::Relation));
auto collector2 = collector1->Clone();
collector2->Collect(MakeOsmElement(3 /* id */, {{"admin_level", "3"}}, OsmElement::EntityType::Relation));
collector2->Collect(MakeOsmElement(4 /* id */, {{"admin_level", "4"}}, OsmElement::EntityType::Relation));
collector1->Finish();
collector2->Finish();
collector1->Merge(*collector2);
collector1->Finalize();
std::vector<std::string> const answers = {
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 1 /* id */).GetEncodedId()) + "\t1",
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 2 /* id */).GetEncodedId()) + "\t2",
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 3 /* id */).GetEncodedId()) + "\t3",
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 4 /* id */).GetEncodedId()) + "\t4",
};
std::ifstream stream;
stream.exceptions(std::ios::badbit);
stream.open(filename);
size_t pos = 0;
std::string line;
while (std::getline(stream, line))
TEST_EQUAL(line, answers[pos++], ());
}
UNIT_TEST(MergeCollector_MergeCase2)
{
auto const filename = GetFileName();
std::string const tagKey = "admin_level";
auto collection1 = std::make_shared<CollectorCollection>();
collection1->Append(std::make_shared<CollectorTag>(filename, tagKey, kEmptyValidator));
collection1->Collect(MakeOsmElement(1 /* id */, {{"admin_level", "1"}}, OsmElement::EntityType::Relation));
collection1->Collect(MakeOsmElement(2 /* id */, {{"admin_level", "2"}}, OsmElement::EntityType::Relation));
auto collection2 = collection1->Clone();
collection2->Collect(MakeOsmElement(3 /* id */, {{"admin_level", "3"}}, OsmElement::EntityType::Relation));
collection2->Collect(MakeOsmElement(4 /* id */, {{"admin_level", "4"}}, OsmElement::EntityType::Relation));
collection1->Finish();
collection2->Finish();
collection1->Merge(*collection2);
collection1->Finalize();
std::vector<std::string> const answers = {
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 1 /* id */).GetEncodedId()) + "\t1",
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 2 /* id */).GetEncodedId()) + "\t2",
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 3 /* id */).GetEncodedId()) + "\t3",
std::to_string(GeoObjectId(GeoObjectId::Type::ObsoleteOsmRelation, 4 /* id */).GetEncodedId()) + "\t4",
};
std::ifstream stream;
stream.exceptions(std::ios::badbit);
stream.open(filename);
size_t pos = 0;
std::string line;
while (std::getline(stream, line))
TEST_EQUAL(line, answers[pos++], ());
}

View file

@ -0,0 +1,665 @@
#include "testing/testing.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "generator/osm2meta.hpp"
#include "indexer/classificator.hpp"
#include "base/logging.hpp"
using namespace generator::tests_support;
using feature::Metadata;
UNIT_TEST(Metadata_ValidateAndFormat_stars)
{
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
// Ignore incorrect values.
p("stars", "0");
TEST(md.Empty(), ());
p("stars", "-1");
TEST(md.Empty(), ());
p("stars", "aasdasdas");
TEST(md.Empty(), ());
p("stars", "8");
TEST(md.Empty(), ());
p("stars", "10");
TEST(md.Empty(), ());
p("stars", "910");
TEST(md.Empty(), ());
p("stars", "100");
TEST(md.Empty(), ());
// Check correct values.
p("stars", "1");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "1", ());
md.Drop(Metadata::FMD_STARS);
p("stars", "2");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "2", ());
md.Drop(Metadata::FMD_STARS);
p("stars", "3");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "3", ());
md.Drop(Metadata::FMD_STARS);
p("stars", "4");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "4", ());
md.Drop(Metadata::FMD_STARS);
p("stars", "5");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "5", ());
md.Drop(Metadata::FMD_STARS);
p("stars", "6");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "6", ());
md.Drop(Metadata::FMD_STARS);
p("stars", "7");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "7", ());
md.Drop(Metadata::FMD_STARS);
// Check almost correct values.
p("stars", "4+");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "4", ());
md.Drop(Metadata::FMD_STARS);
p("stars", "5s");
TEST_EQUAL(md.Get(Metadata::FMD_STARS), "5", ());
md.Drop(Metadata::FMD_STARS);
}
UNIT_CLASS_TEST(TestWithClassificator, Metadata_ValidateAndFormat_operator)
{
uint32_t const typeAtm = classif().GetTypeByPath({"amenity", "atm"});
uint32_t const typeFuel = classif().GetTypeByPath({"amenity", "fuel"});
uint32_t const typeCarSharing = classif().GetTypeByPath({"amenity", "car_sharing"});
uint32_t const typeCarRental = classif().GetTypeByPath({"amenity", "car_rental"});
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
// Ignore tag 'operator' if feature have inappropriate type.
p("operator", "Some");
TEST(md.Empty(), ());
params.SetType(typeAtm);
p("operator", "Some1");
TEST_EQUAL(md.Get(Metadata::FMD_OPERATOR), "Some1", ());
params.SetType(typeFuel);
p("operator", "Some2");
TEST_EQUAL(md.Get(Metadata::FMD_OPERATOR), "Some2", ());
params.SetType(typeCarSharing);
params.AddType(typeCarRental);
p("operator", "Some3");
TEST_EQUAL(md.Get(Metadata::FMD_OPERATOR), "Some3", ());
}
UNIT_TEST(Metadata_ValidateAndFormat_height)
{
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
p("height", "0");
TEST(md.Empty(), ());
p("height", "0,0000");
TEST(md.Empty(), ());
p("height", "0.0");
TEST(md.Empty(), ());
p("height", "123");
TEST_EQUAL(md.Get(Metadata::FMD_HEIGHT), "123", ());
md.Drop(Metadata::FMD_HEIGHT);
p("height", "123.2");
TEST_EQUAL(md.Get(Metadata::FMD_HEIGHT), "123.2", ());
md.Drop(Metadata::FMD_HEIGHT);
p("height", "2 m");
TEST_EQUAL(md.Get(Metadata::FMD_HEIGHT), "2", ());
md.Drop(Metadata::FMD_HEIGHT);
p("height", "3-6");
TEST_EQUAL(md.Get(Metadata::FMD_HEIGHT), "6", ());
}
UNIT_TEST(Metadata_ValidateAndFormat_wikipedia)
{
char const * kWikiKey = "wikipedia";
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
#ifdef OMIM_OS_MOBILE
#define WIKIHOST "m.wikipedia.org"
#else
#define WIKIHOST "wikipedia.org"
#endif
struct Test
{
char const * source;
char const * validated;
char const * url;
};
constexpr Test tests[] = {
{"en:Bad %20Data", "en:Bad %20Data", "https://en." WIKIHOST "/wiki/Bad_%2520Data"},
{"ru:Это тест_со знаками %, ? (и скобками)", "ru:Это тест со знаками %, ? (и скобками)",
"https://ru." WIKIHOST "/wiki/Этоест_со_знаками_%25,_%3F_(и_скобками)"},
{"https://be-tarask.wikipedia.org/wiki/Вялікае_Княстваітоўскае", "be-tarask:Вялікае Княства Літоўскае",
"https://be-tarask." WIKIHOST "/wiki/Вялікае_Княстваітоўскае"},
// Final link points to https and mobile version.
{"http://en.wikipedia.org/wiki/A#id", "en:A#id", "https://en." WIKIHOST "/wiki/A#id"},
};
for (auto [source, validated, url] : tests)
{
p(kWikiKey, source);
TEST_EQUAL(md.Get(Metadata::FMD_WIKIPEDIA), validated, (source));
TEST_EQUAL(md.GetWikiURL(), url, (source));
md.Drop(Metadata::FMD_WIKIPEDIA);
}
p(kWikiKey, "invalid_input_without_language_and_colon");
TEST(md.Empty(), (md.Get(Metadata::FMD_WIKIPEDIA)));
p(kWikiKey, "https://en.wikipedia.org/wiki/");
TEST(md.Empty(), (md.Get(Metadata::FMD_WIKIPEDIA)));
p(kWikiKey, "http://wikipedia.org/wiki/Article");
TEST(md.Empty(), (md.Get(Metadata::FMD_WIKIPEDIA)));
p(kWikiKey, "http://somesite.org");
TEST(md.Empty(), (md.Get(Metadata::FMD_WIKIPEDIA)));
p(kWikiKey, "http://www.spamsitewithaslash.com/");
TEST(md.Empty(), (md.Get(Metadata::FMD_WIKIPEDIA)));
p(kWikiKey, "http://.wikipedia.org/wiki/Article");
TEST(md.Empty(), (md.Get(Metadata::FMD_WIKIPEDIA)));
// Ignore incorrect prefixes.
p(kWikiKey, "ht.tps://en.wikipedia.org/wiki/Whuh");
TEST_EQUAL(md.Get(Metadata::FMD_WIKIPEDIA), "en:Whuh", ());
md.Drop(Metadata::FMD_WIKIPEDIA);
p(kWikiKey, "http://ru.google.com/wiki/wutlol");
TEST(md.Empty(), ("Not a wikipedia site."));
#undef WIKIHOST
}
UNIT_TEST(Metadata_ValidateAndFormat_wikimedia_commons)
{
char const * kWikiKey = "wikimedia_commons";
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
p(kWikiKey, "File:Boğaz (105822801).jpeg");
TEST_EQUAL(md.Get(Metadata::FMD_WIKIMEDIA_COMMONS), "File:Boğaz (105822801).jpeg", ());
p(kWikiKey, "Category:Bosphorus");
TEST_EQUAL(md.Get(Metadata::FMD_WIKIMEDIA_COMMONS), "Category:Bosphorus", ());
md.Drop(Metadata::FMD_WIKIMEDIA_COMMONS);
p(kWikiKey, "incorrect_wikimedia_content");
TEST(md.Get(Metadata::FMD_WIKIMEDIA_COMMONS).empty(), ());
}
// Look at: https://wiki.openstreetmap.org/wiki/Key:duration for details
// about "duration" format.
UNIT_CLASS_TEST(TestWithClassificator, Metadata_ValidateAndFormat_duration)
{
FeatureBuilderParams params;
params.AddType(classif().GetTypeByPath({"route", "ferry"}));
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
auto const test = [&](std::string const & osm, std::string const & expected)
{
p("duration", osm);
if (expected.empty())
{
TEST(md.Empty(), ());
}
else
{
TEST_EQUAL(md.Get(Metadata::FMD_DURATION), expected, ());
md.Drop(Metadata::FMD_DURATION);
}
};
// "10" - 10 minutes ~ 0.16667 hours
test("10", "0.16667");
// 10:00 - 10 hours
test("10:00", "10");
test("QWE", "");
// 1:1:1 - 1 hour + 1 minute + 1 second
test("1:1:1", "1.0169");
// 10 hours and 30 minutes
test("10:30", "10.5");
test("30", "0.5");
test("60", "1");
test("120", "2");
test("35:10", "35.167");
test("35::10", "");
test("", "");
test("0", "");
test("asd", "");
test("10 minutes", "");
test("01:15 h", "");
test("08:00;07:00;06:30", "");
test("3-4 minutes", "");
test("5:00 hours", "");
test("12 min", "");
// means 20 seconds
test("PT20S", "0.0055556");
// means 7 minutes
test("PT7M", "0.11667");
// means 10 minutes and 40 seconds
test("PT10M40S", "0.17778");
test("PT50M", "0.83333");
// means 2 hours
test("PT2H", "2");
// means 7 hours and 50 minutes
test("PT7H50M", "7.8333");
test("PT60M", "1");
test("PT15M", "0.25");
// means 1000 years, but we don't support such duration.
test("PT1000Y", "");
test("PTPT", "");
// means 4 day, but we don't support such duration.
test("P4D", "");
test("PT50:20", "");
}
UNIT_CLASS_TEST(TestWithClassificator, ValidateAndFormat_facebook)
{
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
p("contact:facebook", "");
TEST(md.Empty(), ());
p("contact:facebook", "osm.us");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_FACEBOOK), "osm.us", ());
md.Drop(Metadata::FMD_CONTACT_FACEBOOK);
p("contact:facebook", "@vtbgroup");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_FACEBOOK), "vtbgroup", ());
md.Drop(Metadata::FMD_CONTACT_FACEBOOK);
p("contact:facebook", "https://www.facebook.com/pyaterochka");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_FACEBOOK), "pyaterochka", ());
md.Drop(Metadata::FMD_CONTACT_FACEBOOK);
p("contact:facebook", "facebook.de/mcdonaldsbonn/");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_FACEBOOK), "mcdonaldsbonn", ());
md.Drop(Metadata::FMD_CONTACT_FACEBOOK);
p("contact:facebook", "https://facebook.com/238702340219158/posts/284664265622965");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_FACEBOOK), "238702340219158/posts/284664265622965", ());
md.Drop(Metadata::FMD_CONTACT_FACEBOOK);
p("contact:facebook", "https://facebook.com/238702340219158/posts/284664265622965");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_FACEBOOK), "238702340219158/posts/284664265622965", ());
md.Drop(Metadata::FMD_CONTACT_FACEBOOK);
p("contact:facebook", "https://fr-fr.facebook.com/people/Paillote-Lgm/100012630853826/");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_FACEBOOK), "people/Paillote-Lgm/100012630853826", ());
md.Drop(Metadata::FMD_CONTACT_FACEBOOK);
p("contact:facebook", "https://www.sandwichparlour.com.au/");
TEST(md.Empty(), ());
}
UNIT_CLASS_TEST(TestWithClassificator, ValidateAndFormat_instagram)
{
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
p("contact:instagram", "");
TEST(md.Empty(), ());
p("contact:instagram", "instagram.com/openstreetmapus");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "openstreetmapus", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "www.instagram.com/openstreetmapus");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "openstreetmapus", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "https://instagram.com/openstreetmapus");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "openstreetmapus", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "https://en-us.instagram.com/openstreetmapus/");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "openstreetmapus", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "@open.street.map.us");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "open.street.map.us", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "_osm_");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "_osm_", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "https://www.instagram.com/explore/locations/358536820/trivium-sport-en-dance/");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "explore/locations/358536820/trivium-sport-en-dance", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "https://www.instagram.com/explore/tags/boojum/");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "explore/tags/boojum", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "https://www.instagram.com/p/BvkgKZNDbqN");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_INSTAGRAM), "p/BvkgKZNDbqN", ());
md.Drop(Metadata::FMD_CONTACT_INSTAGRAM);
p("contact:instagram", "dharampura road");
TEST(md.Empty(), ());
p("contact:instagram", "https://twitter.com/theuafpub");
TEST(md.Empty(), ());
p("contact:instagram", ".dots_not_allowed.");
TEST(md.Empty(), ());
}
UNIT_CLASS_TEST(TestWithClassificator, ValidateAndFormat_twitter)
{
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
p("contact:twitter", "");
TEST(md.Empty(), ());
p("contact:twitter", "https://twitter.com/hashtag/sotanosiete");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_TWITTER), "hashtag/sotanosiete", ());
md.Drop(Metadata::FMD_CONTACT_TWITTER);
p("contact:twitter", "twitter.com/osm_tech");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_TWITTER), "osm_tech", ());
md.Drop(Metadata::FMD_CONTACT_TWITTER);
p("contact:twitter", "http://twitter.com/osm_tech");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_TWITTER), "osm_tech", ());
md.Drop(Metadata::FMD_CONTACT_TWITTER);
p("contact:twitter", "https://twitter.com/osm_tech");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_TWITTER), "osm_tech", ());
md.Drop(Metadata::FMD_CONTACT_TWITTER);
p("contact:twitter", "osm_tech");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_TWITTER), "osm_tech", ());
md.Drop(Metadata::FMD_CONTACT_TWITTER);
p("contact:twitter", "@the_osm_tech");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_TWITTER), "the_osm_tech", ());
md.Drop(Metadata::FMD_CONTACT_TWITTER);
p("contact:twitter", "dharampura road");
TEST(md.Empty(), ());
p("contact:twitter", "http://www.facebook.com/pages/tree-house-interiors/333581653310");
TEST(md.Empty(), ());
p("contact:twitter", "dots.not.allowed");
TEST(md.Empty(), ());
p("contact:twitter", "@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
TEST(md.Empty(), ());
}
UNIT_CLASS_TEST(TestWithClassificator, ValidateAndFormat_vk)
{
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
p("contact:vk", "");
TEST(md.Empty(), ());
p("contact:vk", "vk.com/osm63ru");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_VK), "osm63ru", ());
md.Drop(Metadata::FMD_CONTACT_VK);
p("contact:vk", "www.vk.com/osm63ru");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_VK), "osm63ru", ());
md.Drop(Metadata::FMD_OPERATOR);
p("contact:vk", "http://vk.com/osm63ru");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_VK), "osm63ru", ());
md.Drop(Metadata::FMD_CONTACT_VK);
p("contact:vk", "https://vk.com/osm63ru");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_VK), "osm63ru", ());
md.Drop(Metadata::FMD_CONTACT_VK);
p("contact:vk", "https://www.vk.com/osm63ru");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_VK), "osm63ru", ());
md.Drop(Metadata::FMD_CONTACT_VK);
p("contact:vk", "osm63ru");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_VK), "osm63ru", ());
md.Drop(Metadata::FMD_CONTACT_VK);
p("contact:vk", "@osm63ru");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_VK), "osm63ru", ());
md.Drop(Metadata::FMD_CONTACT_VK);
p("contact:vk", "@_invalid_underscores_");
TEST(md.Empty(), ());
p("contact:vk", "http://www.facebook.com/pages/tree-house-interiors/333581653310");
TEST(md.Empty(), ());
p("contact:vk", "invalid-dashes");
TEST(md.Empty(), ());
p("contact:vk", "@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
TEST(md.Empty(), ());
}
UNIT_CLASS_TEST(TestWithClassificator, ValidateAndFormat_contactLine)
{
FeatureBuilderParams params;
MetadataTagProcessor p(params);
Metadata & md = params.GetMetadata();
p("contact:line", "");
TEST(md.Empty(), ());
p("contact:line", "http://line.me/ti/p/mzog4fnz24");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "mzog4fnz24", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/ti/p/xnv0g02rws");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "xnv0g02rws", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/ti/p/@dgxs9r6wad");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "dgxs9r6wad", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/ti/p/%40vne5uwke17");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "vne5uwke17", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "http://line.me/R/ti/p/bfsg1a8x9u");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "bfsg1a8x9u", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/R/ti/p/gdltt7s380");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "gdltt7s380", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/R/ti/p/@sdb2pb3lsg");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "sdb2pb3lsg", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/R/ti/p/%40b30h5mdj11");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "b30h5mdj11", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "http://line.me/R/home/public/main?id=hmczqsbav5");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "hmczqsbav5", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/R/home/public/main?id=wa1gvx91jb");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "wa1gvx91jb", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "http://line.me/R/home/public/profile?id=5qll5dyqqu");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "5qll5dyqqu", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/R/home/public/profile?id=r90ck7n1rq");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "r90ck7n1rq", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://line.me/R/home/public/profile?id=r90ck7n1rq");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "r90ck7n1rq", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://page.line.me/fom5198h");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "fom5198h", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://page.line.me/qn58n8g?web=mobile");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "qn58n8g", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "https://abc.line.me/en/some/page?id=xaladqv");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "abc.line.me/en/some/page?id=xaladqv", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "@abcd");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "abcd", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "0000");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "0000", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", ".dots.are.allowed.");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), ".dots.are.allowed.", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "@.dots.are.allowed.");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), ".dots.are.allowed.", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "-hyphen-test-");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "-hyphen-test-", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "@-hyphen-test-");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "-hyphen-test-", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "under_score");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "under_score", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "@under_score");
TEST_EQUAL(md.Get(Metadata::FMD_CONTACT_LINE), "under_score", ());
md.Drop(Metadata::FMD_CONTACT_LINE);
p("contact:line", "no");
TEST(md.Empty(), ());
p("contact:line", "yes");
TEST(md.Empty(), ());
p("contact:line", "No-upper-case");
TEST(md.Empty(), ());
p("contact:line", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
TEST(md.Empty(), ());
p("contact:line", "https://line.com/ti/p/invalid-domain");
TEST(md.Empty(), ());
}
UNIT_TEST(Metadata_ValidateAndFormat_ele)
{
FeatureBuilderParams params;
MetadataTagProcessorImpl tagProc(params);
TEST_EQUAL(tagProc.ValidateAndFormat_ele(""), "", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("not a number"), "", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("0"), "0", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("0.0"), "0", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("0.0000000"), "0", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("22.5"), "22.5", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("-100.3"), "-100.3", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("99.0000000"), "99", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("8900.000023"), "8900", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("-300.9999"), "-301", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("-300.9"), "-300.9", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("15 m"), "15", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("15.9 m"), "15.9", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("15.9m"), "15.9", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("3000 ft"), "914.4", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("3000ft"), "914.4", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("100 feet"), "30.48", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("100feet"), "30.48", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("11'"), "3.35", ());
TEST_EQUAL(tagProc.ValidateAndFormat_ele("11'4\""), "3.45", ());
}
UNIT_TEST(Metadata_ValidateAndFormat_building_levels)
{
FeatureBuilderParams params;
MetadataTagProcessorImpl tp(params);
TEST_EQUAL(tp.ValidateAndFormat_building_levels(""), "4", ());
TEST_EQUAL(tp.ValidateAndFormat_building_levels("floors"), "4", ());
TEST_EQUAL(tp.ValidateAndFormat_building_levels("between 1 and "), "", ());
TEST_EQUAL(tp.ValidateAndFormat_building_levels("0"), "0", ("OSM has many zero-level buildings."));
TEST_EQUAL(tp.ValidateAndFormat_building_levels("0.0"), "0", ());
TEST_EQUAL(tp.ValidateAndFormat_building_levels(""), "", ());
TEST_EQUAL(tp.ValidateAndFormat_building_levels("Level 1"), "", ());
TEST_EQUAL(tp.ValidateAndFormat_building_levels("2.51"), "2.5", ());
TEST_EQUAL(tp.ValidateAndFormat_building_levels("250"), "", ("Too many levels."));
}
UNIT_TEST(Metadata_ValidateAndFormat_url)
{
std::array<std::pair<char const *, char const *>, 9> constexpr kTests = {{
{"a.by", "a.by"},
{"http://test.com", "http://test.com"},
{"https://test.com", "https://test.com"},
{"test.com", "test.com"},
{"http://test.com/", "http://test.com"},
{"https://test.com/", "https://test.com"},
{"test.com/", "test.com"},
{"test.com/path", "test.com/path"},
{"test.com/path/", "test.com/path/"},
}};
FeatureBuilderParams params;
MetadataTagProcessorImpl tp(params);
for (auto const & [input, output] : kTests)
TEST_EQUAL(tp.ValidateAndFormat_url(input), output, ());
}

View file

@ -0,0 +1,216 @@
#include "testing/testing.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/metalines_builder.hpp"
#include "generator/osm2type.hpp"
#include "generator/osm_element.hpp"
#include "indexer/classificator_loader.hpp"
#include "platform/platform.hpp"
#include "coding/read_write_utils.hpp"
#include "base/scope_guard.hpp"
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace metalines_tests
{
using namespace feature;
OsmElement MakeHighway(uint64_t id, std::string const & name, std::vector<uint64_t> const & nodes,
bool isOneway = false)
{
OsmElement element;
element.m_id = id;
element.m_type = OsmElement::EntityType::Way;
element.AddTag("ref", "");
element.AddTag("name", name);
element.AddTag("highway", "primary");
if (isOneway)
element.AddTag("oneway", "yes");
element.m_nodes = nodes;
return element;
}
feature::FeatureBuilder MakeFbForTest(OsmElement element)
{
feature::FeatureBuilder result;
ftype::GetNameAndType(&element, result.GetParams());
result.SetLinear();
return result;
}
size_t MakeKey(OsmElement const & element)
{
auto const name = element.GetTag("name");
auto const ref = element.GetTag("ref");
return std::hash<std::string>{}(name + '\0' + ref);
}
LineStringMerger::InputData MakeInputData(std::vector<OsmElement> const & elements)
{
LineStringMerger::InputData inputData;
for (auto const & element : elements)
inputData.emplace(MakeKey(element), std::make_shared<LineString>(element));
return inputData;
}
bool IsEqual(LineStringMerger::LinePtr const & lineString, std::vector<int32_t> const & ways)
{
auto const & w = lineString->GetWays();
return w == ways;
}
auto const w1 = MakeHighway(1 /* id */, "w" /* name */, {1, 2, 3} /* nodes */);
auto const w2 = MakeHighway(2 /* id */, "w" /* name */, {3, 4, 5} /* nodes */);
auto const w3 = MakeHighway(3 /* id */, "w" /* name */, {5, 6, 7} /* nodes */);
auto const w4 = MakeHighway(4 /* id */, "w" /* name */, {7, 8, 9} /* nodes */);
auto const w5 = MakeHighway(5 /* id */, "w" /* name */, {9, 10, 11} /* nodes */);
auto const wo6 = MakeHighway(6 /* id */, "w" /* name */, {13, 12, 3} /* nodes */, true /* isOneway */);
auto const wo7 = MakeHighway(7 /* id */, "w" /* name */, {15, 14, 13} /* nodes */, true /* isOneway */);
auto const wo8 = MakeHighway(8 /* id */, "w" /* name */, {17, 16, 15} /* nodes */, true /* isOneway */);
auto const b1 = MakeHighway(1 /* id */, "b" /* name */, {1, 2, 3} /* nodes */);
auto const b2 = MakeHighway(2 /* id */, "b" /* name */, {3, 4, 5} /* nodes */);
UNIT_TEST(MetalinesTest_Case0)
{
auto const inputData = MakeInputData({w1});
auto outputData = LineStringMerger::Merge(inputData);
TEST_EQUAL(outputData.size(), 0 /* unique names roads count */, ());
outputData = LineStringMerger::Merge({});
TEST_EQUAL(outputData.size(), 0 /* unique names roads count */, ());
}
UNIT_TEST(MetalinesTest_Case1)
{
auto const inputData = MakeInputData({w1, w2});
auto const outputData = LineStringMerger::Merge(inputData);
auto const key = MakeKey(w1);
TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ());
TEST_EQUAL(outputData.at(key)[0]->GetWays().size(), 2 /* merged way size */, ());
TEST(IsEqual(outputData.at(key)[0], {1, 2}) /* merged way */, ());
}
UNIT_TEST(MetalinesTest_Case2)
{
auto const inputData = MakeInputData({w1, w3, w2});
auto const outputData = LineStringMerger::Merge(inputData);
auto const key = MakeKey(w1);
TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ());
TEST_EQUAL(outputData.at(key)[0]->GetWays().size(), 3 /* merged way size */, ());
TEST(IsEqual(outputData.at(key)[0], {1, 2, 3}) /* merged way */, ());
}
UNIT_TEST(MetalinesTest_Case3)
{
auto const inputData = MakeInputData({
w1,
w4,
w2,
w5,
});
auto const outputData = LineStringMerger::Merge(inputData);
auto const key = MakeKey(w1);
TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ());
TEST_EQUAL(outputData.at(key).size(), 2 /* ways count */, ());
TEST_EQUAL(outputData.at(key)[0]->GetWays().size(), 2 /* merged way size */, ());
TEST(IsEqual(outputData.at(key)[0], {1, 2}) /* merged way */, ());
TEST_EQUAL(outputData.at(key)[1]->GetWays().size(), 2 /* merged way size */, ());
TEST(IsEqual(outputData.at(key)[1], {4, 5}) /* merged way */, ());
}
UNIT_TEST(MetalinesTest_Case4)
{
auto const inputData = MakeInputData({
w1,
wo6,
});
auto const outputData = LineStringMerger::Merge(inputData);
auto const key = MakeKey(w1);
TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ());
TEST_EQUAL(outputData.at(key).size(), 1 /* ways count */, ());
TEST(IsEqual(outputData.at(key)[0], {6, -1}) /* merged way */, ());
}
UNIT_TEST(MetalinesTest_Case5)
{
auto const inputData = MakeInputData({
w1,
w2,
wo6,
});
auto const outputData = LineStringMerger::Merge(inputData);
auto const key = MakeKey(w1);
TEST_EQUAL(outputData.size(), 1 /* unique names roads count */, ());
TEST_EQUAL(outputData.at(key).size(), 1 /* ways count */, ());
TEST(IsEqual(outputData.at(key)[0], {1, 2}) /* merged way */, ());
}
UNIT_TEST(MetalinesTest_Case6)
{
auto const inputData = MakeInputData({
w1,
b1,
w2,
b2,
});
auto const outputData = LineStringMerger::Merge(inputData);
auto const keyW = MakeKey(w1);
auto const keyB = MakeKey(b1);
TEST_EQUAL(outputData.size(), 2 /* unique names roads count */, ());
TEST_EQUAL(outputData.at(keyW).size(), 1 /* ways count */, ());
TEST_EQUAL(outputData.at(keyB).size(), 1 /* ways count */, ());
}
UNIT_TEST(MetalinesTest_MetalinesBuilderMerge)
{
classificator::Load();
auto const filename = generator_tests::GetFileName();
SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename)));
auto c1 = std::make_shared<MetalinesBuilder>(filename);
auto c2 = c1->Clone();
c1->CollectFeature(MakeFbForTest(w1), w1);
c2->CollectFeature(MakeFbForTest(w2), w2);
c1->CollectFeature(MakeFbForTest(w5), w5);
c2->CollectFeature(MakeFbForTest(w4), w4);
c1->Finish();
c2->Finish();
c1->Merge(*c2);
c1->Finalize();
FileReader reader(filename);
ReaderSource<FileReader> src(reader);
std::set<std::vector<int32_t>> s;
while (src.Size() > 0)
{
std::vector<int32_t> ways;
rw::ReadVectorOfPOD(src, ways);
s.emplace(std::move(ways));
}
TEST_EQUAL(s.size(), 2, ());
TEST_EQUAL(s.count({1, 2}), 1, ());
TEST_EQUAL(s.count({4, 5}), 1, ());
}
} // namespace metalines_tests

View file

@ -0,0 +1,260 @@
#include "testing/testing.hpp"
#include "generator/mini_roundabout_transformer.hpp"
#include "generator/osm_element.hpp"
#include "coding/point_coding.hpp"
#include "geometry/latlon.hpp"
#include "geometry/mercator.hpp"
#include "geometry/point2d.hpp"
#include "base/math.hpp"
#include <algorithm>
#include <array>
#include <vector>
namespace mini_roundabout_tests
{
using namespace generator;
OsmElement MiniRoundabout(uint64_t id, double lat, double lon)
{
OsmElement miniRoundabout;
miniRoundabout.m_id = id;
miniRoundabout.m_lat = lat;
miniRoundabout.m_lon = lon;
miniRoundabout.m_type = OsmElement::EntityType::Node;
miniRoundabout.AddTag("highway", "mini_roundabout");
return miniRoundabout;
}
OsmElement Road(uint64_t id, std::vector<uint64_t> && nodes)
{
OsmElement road;
road.m_id = id;
road.m_type = OsmElement::EntityType::Way;
road.AddTag("highway", "trunk");
road.m_nodes = nodes;
return road;
}
OsmElement RoadNode(uint64_t id, double lat, double lon)
{
OsmElement node;
node.m_id = id;
node.m_lat = lat;
node.m_lon = lon;
return node;
}
void TestRunCmpPoints(std::vector<m2::PointD> const & pointsFact, std::vector<m2::PointD> const & pointsPlan, double r)
{
TEST_EQUAL(pointsFact.size(), pointsPlan.size(), ());
TEST_GREATER(pointsFact.size(), 2, ());
for (size_t i = 0; i < pointsFact.size(); ++i)
TEST(AlmostEqualAbs(pointsFact[i], pointsPlan[i], kMwmPointAccuracy), ());
}
void TestRunCmpNumbers(double val1, double val2)
{
TEST(AlmostEqualAbs(val1, val2, kMwmPointAccuracy), ());
}
UNIT_TEST(PointToPolygon_GeneralProperties)
{
m2::PointD constexpr center(0.0, 0.0);
double constexpr r = 10.0;
std::array<double, 4> const anglesDeg{0.0, -30.0, 30.0, 45.0};
for (double const & angleDeg : anglesDeg)
{
for (size_t verticesCount = 3; verticesCount < 30; ++verticesCount)
{
std::vector<m2::PointD> const circlePlain = PointToPolygon(center, r, verticesCount, angleDeg);
double const vertexLenght = DistanceOnPlain(circlePlain.front(), circlePlain.back());
for (size_t i = 0; i < circlePlain.size() - 1; ++i)
{
double const rCurrent = DistanceOnPlain(circlePlain[i], center);
TEST(AlmostEqualAbs(rCurrent, r, kMwmPointAccuracy), ());
double const vertexLengthCurrent = DistanceOnPlain(circlePlain[i], circlePlain[i + 1]);
TEST(AlmostEqualAbs(vertexLengthCurrent, vertexLenght, kMwmPointAccuracy), ());
}
}
}
}
UNIT_TEST(TrimSegment_Vertical)
{
m2::PointD const a(2.0, -1.0);
m2::PointD const b(2.0, 3.0);
double const dist = 1.0;
m2::PointD const point = GetPointAtDistFromTarget(a /* source */, b /* target */, dist);
m2::PointD const pointPlan(2.0, 2.0);
TEST(AlmostEqualAbs(point, pointPlan, kMwmPointAccuracy), ());
}
UNIT_TEST(TrimSegment_VerticalNegative)
{
m2::PointD const a(-3.0, -5.0);
m2::PointD const b(-3.0, 6.0);
double const dist = 4.0;
m2::PointD const point = GetPointAtDistFromTarget(a /* source */, b /* target */, dist);
m2::PointD const pointPlan(-3.0, 2.0);
TEST(AlmostEqualAbs(point, pointPlan, kMwmPointAccuracy), ());
}
UNIT_TEST(TrimSegment_ExceptionalCase)
{
m2::PointD const a(1.0, 2.0);
m2::PointD const b(2.0, 3.0);
double const dist = 10.0;
m2::PointD const point = GetPointAtDistFromTarget(a /* source */, b /* target */, dist);
TEST(AlmostEqualAbs(point, a, kMwmPointAccuracy), ());
}
UNIT_TEST(PointToCircle_ZeroMeridian)
{
ms::LatLon const pointOnZeroMeridian(51.0, 0.0);
m2::PointD const center = mercator::FromLatLon(pointOnZeroMeridian);
double const r = mercator::MetersToMercator(100.0);
auto const circlePlain = PointToPolygon(center, r, 6, 30.0);
std::vector<m2::PointD> const circlePlainExpected{{0.00077, 59.48054}, {0.00000, 59.48100}, {-0.00077, 59.48054},
{-0.00077, 59.47964}, {0.00000, 59.47920}, {0.00077, 59.47964}};
TestRunCmpPoints(circlePlain, circlePlainExpected, r);
}
UNIT_TEST(PointToCircle_LargeRadius)
{
ms::LatLon const pointOnZeroMeridian(74.0, 0.1);
m2::PointD const center = mercator::FromLatLon(pointOnZeroMeridian);
double const r = mercator::MetersToMercator(500000.0);
auto const circlePlain = PointToPolygon(center, r, 6, 30.0);
std::vector<m2::PointD> const circlePlainExpected{{3.99631, 114.67859}, {0.10000, 116.92812}, {-3.79631, 114.67859},
{-3.79631, 110.17951}, {0.10000, 107.92998}, {3.99631, 110.17951}};
TestRunCmpPoints(circlePlain, circlePlainExpected, r);
}
UNIT_TEST(PointToCircle_Equator)
{
ms::LatLon const pointOnZeroMeridian(0.0, 31.8);
m2::PointD const center = mercator::FromLatLon(pointOnZeroMeridian);
double const r = mercator::MetersToMercator(15.0);
auto const circlePlain = PointToPolygon(center, r, 6, 30.0);
std::vector<m2::PointD> const circlePlainExpected{
{31.80011, 0.00006}, {31.80000, 0.00013}, {31.79988, 0.00006},
{31.79988, -0.00006}, {31.80000, -0.00013}, {31.80011, -0.00006},
};
TestRunCmpPoints(circlePlain, circlePlainExpected, r);
}
UNIT_TEST(TrimSegment_Radius3)
{
m2::PointD const pointOnRoad(10.0, 10.0);
m2::PointD const pointRoundabout(15.0, 17.0);
double const r = 3.0;
m2::PointD const nextPointOnRoad =
GetPointAtDistFromTarget(pointOnRoad /* source */, pointRoundabout /* target */, r /* dist */);
double const dist = DistanceOnPlain(nextPointOnRoad, pointRoundabout);
TestRunCmpNumbers(dist, r);
}
// https://www.openstreetmap.org/node/4999694780
// Simplified example: only one road "Søren R Thornæs veg".
// This road is extended further to Way=511028249
UNIT_TEST(Manage_MiniRoundabout_1Road)
{
auto const node1 = RoadNode(1, 64.46649, 11.50000);
auto const node2 = MiniRoundabout(2, 64.46631, 11.50012);
auto const node3 = RoadNode(3, 64.46620, 11.50016);
auto const road = Road(100, {node1.m_id, node2.m_id, node3.m_id});
m2::PointD const center = mercator::FromLatLon({node2.m_lat, node2.m_lon});
m2::PointD const nearest = mercator::FromLatLon({node1.m_lat, node1.m_lon});
double const r = mercator::MetersToMercator(2.5);
auto circlePlain = PointToPolygon(center, r, 6, 30.0);
// Check for "diameters" equality.
double const diameter = r * 2.;
TEST(AlmostEqualAbs(DistanceOnPlain(circlePlain[0], circlePlain[3]), diameter, kMwmPointAccuracy), ());
TEST(AlmostEqualAbs(DistanceOnPlain(circlePlain[1], circlePlain[4]), diameter, kMwmPointAccuracy), ());
TEST(AlmostEqualAbs(DistanceOnPlain(circlePlain[2], circlePlain[5]), diameter, kMwmPointAccuracy), ());
double const edgeLen = DistanceOnPlain(circlePlain[0], circlePlain[1]);
for (size_t i = 1; i < circlePlain.size(); ++i)
TEST(AlmostEqualAbs(DistanceOnPlain(circlePlain[i - 1], circlePlain[i]), edgeLen, kMwmPointAccuracy), ());
m2::PointD const newPointOnRoad = GetPointAtDistFromTarget(nearest /* source */, center /* target */, r /* dist */);
AddPointToCircle(circlePlain, newPointOnRoad);
std::vector<m2::PointD> const circlePlainExpected{{11.50013, 85.06309}, {11.50012, 85.06310}, {11.50011, 85.06310},
{11.50010, 85.06309}, {11.50010, 85.06306}, {11.50012, 85.06305},
{11.50013, 85.06306}};
TestRunCmpPoints(circlePlain, circlePlainExpected, r);
}
// https://www.openstreetmap.org/node/1617329231
// Mini-roundabout as a part of 4 roads
// Hemel Hempstead magic roundabout
UNIT_TEST(Manage_MiniRoundabout_4Roads)
{
auto const miniRoundabout = MiniRoundabout(1, 51.7460187, -0.4738389);
auto const stationRoadNode = RoadNode(2, 51.7459314, -0.4739951);
auto const plaughRoundaboutNode = RoadNode(3, 51.7461249, -0.4738877);
auto const stationRoadLeftNode = RoadNode(4, 51.7460327, -0.4739356); // End of road
auto const plaughRoundaboutRightNode = RoadNode(5, 51.7458321, -0.4732662);
m2::PointD const center = mercator::FromLatLon({miniRoundabout.m_lat, miniRoundabout.m_lon});
double const r = mercator::MetersToMercator(2.5);
auto circlePlain = PointToPolygon(center, r, 6, 30.0);
AddPointToCircle(circlePlain, GetPointAtDistFromTarget(
mercator::FromLatLon(stationRoadNode.m_lat, stationRoadNode.m_lon) /* source */,
center /* target */, r /* dist */));
AddPointToCircle(circlePlain, GetPointAtDistFromTarget(mercator::FromLatLon(plaughRoundaboutNode.m_lat,
plaughRoundaboutNode.m_lon) /* source */,
center /* target */, r /* dist */));
AddPointToCircle(circlePlain, GetPointAtDistFromTarget(mercator::FromLatLon(stationRoadLeftNode.m_lat,
stationRoadLeftNode.m_lon) /* source */,
center /* target */, r /* dist */));
AddPointToCircle(circlePlain, GetPointAtDistFromTarget(mercator::FromLatLon(plaughRoundaboutRightNode.m_lat,
plaughRoundaboutRightNode.m_lon),
center, r));
std::vector<m2::PointD> const circlePlainExpected{
{-0.47381, 60.67520}, {-0.47383, 60.67521}, {-0.47384, 60.67521}, {-0.47385, 60.67520}, {-0.47386, 60.67520},
{-0.47385, 60.67518}, {-0.47385, 60.67518}, {-0.47383, 60.67517}, {-0.47381, 60.67518}, {-0.47381, 60.67518}};
TestRunCmpPoints(circlePlain, circlePlainExpected, r);
}
UNIT_TEST(Manage_MiniRoundabout_EqualPoints)
{
auto const miniRoundabout = MiniRoundabout(1, 10.0, 10.0);
m2::PointD const center = mercator::FromLatLon({miniRoundabout.m_lat, miniRoundabout.m_lon});
double const r = mercator::MetersToMercator(5.0);
auto circlePlain = PointToPolygon(center, r, 12, 30.0);
AddPointToCircle(circlePlain, circlePlain[11]);
AddPointToCircle(circlePlain, circlePlain[6]);
AddPointToCircle(circlePlain, circlePlain[0]);
AddPointToCircle(circlePlain, circlePlain[0]);
TEST_EQUAL(circlePlain.size(), 16, ());
}
} // namespace mini_roundabout_tests

View file

@ -0,0 +1,57 @@
#include "testing/testing.hpp"
#include "generator/node_mixer.hpp"
#include "generator/osm_element.hpp"
#include <sstream>
UNIT_TEST(NodeMixerTests)
{
std::istringstream stream1("");
generator::MixFakeNodes(stream1,
[](OsmElement & p) { TEST(false, ("Returned an object for an empty input stream.")); });
std::istringstream stream2("shop=gift\nname=Shop\n");
generator::MixFakeNodes(
stream2, [](OsmElement & p) { TEST(false, ("Returned an object for a source without coordinates.")); });
std::istringstream stream3("lat=4.0\nlon=-4.1\n");
generator::MixFakeNodes(stream3,
[](OsmElement & p) { TEST(false, ("Returned an object for a source without tags.")); });
std::istringstream stream4("lat=10.0\nlon=-4.8\nshop=gift\nname=Shop");
int count4 = 0;
generator::MixFakeNodes(stream4, [&](OsmElement & p)
{
count4++;
TEST_EQUAL(p.m_type, OsmElement::EntityType::Node, ());
TEST_EQUAL(p.m_lat, 10.0, ());
TEST_EQUAL(p.m_lon, -4.8, ());
TEST_EQUAL(p.Tags().size(), 2, ());
TEST_EQUAL(p.GetTag("name"), "Shop", ());
});
TEST_EQUAL(count4, 1, ());
std::istringstream stream5("lat=10.0\nlon=-4.8\nid=1\nname=First\n\nid=2\nlat=60\nlon=1\nname=Second\n\n\n");
int count5 = 0;
generator::MixFakeNodes(stream5, [&](OsmElement & p)
{
count5++;
TEST_EQUAL(p.m_type, OsmElement::EntityType::Node, ());
TEST_EQUAL(p.Tags().size(), 2, ());
std::string id = p.GetTag("id");
TEST(!id.empty(), ("No id tag when every object has it."));
TEST_EQUAL(p.GetTag("name"), id == "1" ? "First" : "Second", ());
});
TEST_EQUAL(count5, 2, ());
std::istringstream stream6("lat=0\nlon=-4.8\nshop=mall");
int count6 = 0;
generator::MixFakeNodes(stream6, [&](OsmElement & p)
{
count6++;
TEST_EQUAL(p.m_lat, 0.0, ());
TEST_EQUAL(p.m_lon, -4.8, ());
});
TEST_EQUAL(count6, 1, ());
}

View file

@ -0,0 +1,33 @@
#include "testing/testing.hpp"
#include "generator/osm_element_helpers.hpp"
#include "geometry/point2d.hpp"
namespace
{
using namespace generator::osm_element;
OsmElement CreateOsmElement(std::string const & populationStr)
{
OsmElement element;
element.m_tags.emplace_back("population", populationStr);
return element;
}
UNIT_TEST(ParsePopulation)
{
TEST_EQUAL(GetPopulation(CreateOsmElement("123")), 123, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("123 123")), 123123, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("12,300")), 12300, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("000")), 0, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("123.321")), 123321, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("123 000 000 (Moscow Info)")), 123000000, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("123 000 000 (123)")), 123000000, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("")), 0, ());
TEST_EQUAL(GetPopulation(CreateOsmElement(" ")), 0, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("asd")), 0, ());
TEST_EQUAL(GetPopulation(CreateOsmElement("sfa843r")), 0, ());
TEST_EQUAL(GetPopulation(OsmElement()), 0, ());
}
} // namespace

View file

@ -0,0 +1,169 @@
#include "testing/testing.hpp"
#include "generator/osm_o5m_source.hpp"
#include <iterator>
#include <set>
#include <utility>
#include <vector>
#include "source_data.hpp"
namespace osm_o5m_source_test
{
using std::begin, std::end, std::pair, std::string, std::vector;
UNIT_TEST(OSM_O5M_Source_Node_read_test)
{
string data(begin(node2_o5m_data), end(node2_o5m_data));
std::stringstream ss(data);
osm::O5MSource dataset([&ss](uint8_t * buffer, size_t size)
{ return ss.read(reinterpret_cast<char *>(buffer), size).gcount(); }, 10 /* buffer size */);
osm::O5MSource::Iterator it = dataset.begin();
osm::O5MSource::Entity const & em = *it;
TEST_EQUAL(em.id, 513709898, ());
TEST_EQUAL(em.user, string("Xmypblu"), ());
TEST_EQUAL(em.uid, 395071, ());
TEST_EQUAL(em.version, 8, ());
TEST_EQUAL(em.changeset, 12059128, ());
TEST(AlmostEqualAbs(em.lon, 38.7666704, 1e-7), ());
TEST(AlmostEqualAbs(em.lat, 55.0927062, 1e-7), ());
auto const tags = em.Tags();
auto tagIterator = tags.begin();
auto const & tag = *tagIterator;
TEST_EQUAL(tag.key, string("amenity"), ());
TEST_EQUAL(tag.value, string("cinema"), ());
++tagIterator;
TEST_EQUAL(tag.key, string("name"), ());
TEST_EQUAL(tag.value, string("КТ Горизонт"), ());
++tagIterator;
TEST(!(tagIterator != tags.end()), ());
}
UNIT_TEST(OSM_O5M_Source_Way_read_test)
{
string data(begin(way_o5m_data), end(way_o5m_data));
std::stringstream ss(data);
osm::O5MSource dataset([&ss](uint8_t * buffer, size_t size)
{ return ss.read(reinterpret_cast<char *>(buffer), size).gcount(); }, 10 /* buffer size */);
std::set<int64_t> nodes;
vector<pair<string, string>> const validTags = {{"name", "Yukon River"}, {"name:ru", "Юкон"}, {"waterway", "river"}};
for (auto const & em : dataset)
{
switch (em.type)
{
case osm::O5MSource::EntityType::Node:
{
nodes.insert(em.id);
for (auto const & tag : em.Tags())
TEST(false, ("Unexpected tag:", tag.key, tag.value));
break;
}
case osm::O5MSource::EntityType::Way:
{
size_t ndCounter = 0;
size_t tagCounter = 0;
for (auto const & nd : em.Nodes())
{
ndCounter++;
TEST(nodes.count(nd), ());
}
TEST_EQUAL(nodes.size(), ndCounter, ());
for (auto const & tag : em.Tags())
{
TEST_EQUAL(tag.key, validTags[tagCounter].first, ());
TEST_EQUAL(tag.value, validTags[tagCounter].second, ());
tagCounter++;
}
TEST_EQUAL(validTags.size(), tagCounter, ());
break;
}
default: break;
}
}
}
UNIT_TEST(OSM_O5M_Source_Relation_read_test)
{
string data(begin(relation_o5m_data), end(relation_o5m_data));
std::stringstream ss(data);
osm::O5MSource dataset([&ss](uint8_t * buffer, size_t size)
{ return ss.read(reinterpret_cast<char *>(buffer), size).gcount(); }, 10 /* buffer size */);
std::set<int64_t> nodes;
std::set<int64_t> entities;
vector<pair<string, string>> const validNodeTags = {{"name", "Whitehorse"}, {"place", "town"}};
vector<pair<string, string>> const validRelationTags = {
{"name", "Whitehorse"}, {"place", "town"}, {"type", "multipolygon"}};
using TType = osm::O5MSource::EntityType;
vector<pair<TType, string>> const relationMembers = {{TType::Way, "outer"}, {TType::Node, ""}};
for (auto const & em : dataset)
{
entities.insert(em.id);
switch (em.type)
{
case TType::Node:
{
nodes.insert(em.id);
size_t tagCounter = 0;
for (auto const & tag : em.Tags())
{
TEST_EQUAL(tag.key, validNodeTags[tagCounter].first, ());
TEST_EQUAL(tag.value, validNodeTags[tagCounter].second, ());
tagCounter++;
}
break;
}
case TType::Way:
{
size_t ndCounter = 0;
for (auto const & nd : em.Nodes())
{
ndCounter++;
TEST(nodes.count(nd), ());
}
TEST_EQUAL(nodes.size(), ndCounter, ());
for (auto const & tag : em.Tags())
TEST(false, ("Unexpected tag:", tag.key, tag.value));
break;
}
case TType::Relation:
{
size_t memberCounter = 0;
size_t tagCounter = 0;
for (auto const & member : em.Members())
{
TEST(entities.count(member.ref), ());
TEST_EQUAL(relationMembers[memberCounter].first, member.type, ("Current member:", memberCounter));
TEST_EQUAL(relationMembers[memberCounter].second, member.role, ("Current member:", memberCounter));
memberCounter++;
}
TEST_EQUAL(memberCounter, 2, ());
for (auto const & tag : em.Tags())
{
TEST_EQUAL(tag.key, validRelationTags[tagCounter].first, ());
TEST_EQUAL(tag.value, validRelationTags[tagCounter].second, ());
tagCounter++;
}
TEST_EQUAL(validRelationTags.size(), tagCounter, ());
break;
}
default: break;
}
}
}
} // namespace osm_o5m_source_test

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,308 @@
#include "testing/testing.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/intermediate_data.hpp"
#include "generator/relation_tags.hpp"
#include "indexer/classificator_loader.hpp"
// TODO: Rewrite these tests using RelationTagsEnricher with some test mock of IntermediateDataReaderInterface.
namespace relation_tags_tests
{
using namespace feature;
using namespace generator;
using namespace generator::cache;
using namespace generator_tests;
// In memory relations storage (copy-n-paste from collector_building_parts_tests.cpp).
class TestOSMElementCacheReader : public OSMElementCacheReaderInterface
{
public:
TestOSMElementCacheReader(std::unordered_map<Key, RelationElement> & m) : m_mapping(m) {}
// OSMElementCacheReaderInterface overrides:
bool Read(Key /* id */, WayElement & /* value */) override { UNREACHABLE(); }
bool Read(Key id, RelationElement & value) override
{
auto const it = m_mapping.find(id);
if (it == std::cend(m_mapping))
return false;
value = it->second;
return true;
}
private:
std::unordered_map<Key, RelationElement> & m_mapping;
};
UNIT_TEST(Process_route_with_ref)
{
/* Prepare relations data:
* Relation 1:
* - type = route
* - route = road
* - ref = E-99
* - members: [
* Way 10:
* - highway = motorway
* Way 11:
* - highway = motorway
* - ref = F-16
* ]
*/
// Create relation.
std::vector<RelationElement::Member> testMembers = {{10, ""}, {11, ""}};
RelationElement e1;
e1.m_ways = testMembers;
e1.m_tags.emplace("type", "route");
e1.m_tags.emplace("route", "road");
e1.m_tags.emplace("ref", "E-99");
std::unordered_map<Key, RelationElement> m_IdToRelation = {{1, e1}};
TestOSMElementCacheReader reader(m_IdToRelation);
// Create roads.
auto road10 = MakeOsmElement(10, {{"highway", "motorway"}}, OsmElement::EntityType::Way);
auto road11 = MakeOsmElement(11, {{"highway", "motorway"}, {"ref", "F-16"}}, OsmElement::EntityType::Way);
// Process roads tags using relation tags.
RelationTagsWay rtw;
rtw.Reset(10, &road10);
rtw(1, reader);
rtw.Reset(11, &road11);
rtw(1, reader);
// Verify roads tags.
TEST_EQUAL(road10.GetTag("ref"), "E-99", ());
TEST_EQUAL(road11.GetTag("ref"), "F-16", ());
}
UNIT_TEST(Process_route_with_ref_network)
{
/* Prepare relations data:
* Relation 1:
* - type = route
* - route = road
* - ref = SP60
* - network = IT:RA
* - members: [
* Way 10:
* - highway = motorway
* - name = Via Corleto
* Way 11:
* - highway = motorway
* - ref = SP62
* ]
*/
// Create relation.
std::vector<RelationElement::Member> testMembers = {{10, ""}, {11, ""}};
RelationElement e1;
e1.m_ways = testMembers;
e1.m_tags.emplace("type", "route");
e1.m_tags.emplace("route", "road");
e1.m_tags.emplace("ref", "SP60");
e1.m_tags.emplace("network", "IT:RA");
std::unordered_map<Key, RelationElement> m_IdToRelation = {{1, e1}};
TestOSMElementCacheReader reader(m_IdToRelation);
// Create roads.
auto road10 = MakeOsmElement(10, {{"highway", "motorway"}, {"name", "Via Corleto"}}, OsmElement::EntityType::Way);
auto road11 = MakeOsmElement(11, {{"highway", "motorway"}, {"ref", "SP62"}}, OsmElement::EntityType::Way);
// Process roads tags using relation tags.
RelationTagsWay rtw;
rtw.Reset(10, &road10);
rtw(1, reader);
rtw.Reset(11, &road11);
rtw(1, reader);
// Verify roads tags.
TEST_EQUAL(road10.GetTag("ref"), "IT:RA/SP60", ());
TEST_EQUAL(road11.GetTag("ref"), "SP62", ()); // TODO: Check refs inheritance (expected "IT:RA/SP60;SP62")
}
UNIT_TEST(Process_associatedStreet)
{
/* Prepare relations data:
* Relation 1:
* - type = associatedStreet
* - name = Main street
* - wikipedia = en:Main Street
* - place =
* - members: [
* Way 2:
* - building = yes
* - addr:housenumber = 121
* Way 3:
* - building = house
* - addr:housenumber = 123
* - addr:street = The Main Street
* ]
*/
// Create relation.
std::vector<RelationElement::Member> testMembers = {{2, "house"}, {3, "house"}, {4, "street"}};
RelationElement e1;
e1.m_ways = testMembers;
e1.m_tags.emplace("type", "associatedStreet");
e1.m_tags.emplace("name", "Main Street");
e1.m_tags.emplace("wikipedia", "en:Main Street");
std::unordered_map<Key, RelationElement> m_IdToRelation = {{1, e1}};
TestOSMElementCacheReader reader(m_IdToRelation);
// Create buildings polygons.
auto buildingWay2 =
MakeOsmElement(2, {{"building", "yes"}, {"addr:housenumber", "121"}}, OsmElement::EntityType::Way);
auto buildingWay3 = MakeOsmElement(3,
{{"shop", "convenience"},
{"addr:housenumber", "123"},
{"addr:street", "The Main Street"},
{"wikipedia", "en:Mega Theater"}},
OsmElement::EntityType::Way);
auto highway4 = MakeOsmElement(4, {{"highway", "residential"}}, OsmElement::EntityType::Way);
// Process buildings tags using relation tags.
RelationTagsWay rtw;
rtw.Reset(2, &buildingWay2);
rtw(1, reader);
rtw.Reset(3, &buildingWay3);
rtw(1, reader);
rtw.Reset(4, &highway4);
rtw(1, reader);
// Aggreagte wiki from associatedStreet only for highway.
TEST_EQUAL(buildingWay2.GetTag("addr:street"), "Main Street", ());
TEST(buildingWay2.GetTag("wikipedia").empty(), ());
TEST_EQUAL(buildingWay3.GetTag("addr:street"), "The Main Street", ());
TEST_EQUAL(buildingWay3.GetTag("wikipedia"), "en:Mega Theater", ());
TEST_EQUAL(highway4.GetTag("wikipedia"), "en:Main Street", ());
}
UNIT_TEST(RelationTags_GoodBoundary)
{
classificator::Load();
// Create relation.
std::vector<RelationElement::Member> ways = {{1, "outer"}};
std::vector<RelationElement::Member> nodes = {{2, "admin_centre"}, {3, "label"}};
auto way1 = MakeOsmElement(1, {{"boundary", "administrative"}}, OsmElement::EntityType::Way);
auto node2 =
MakeOsmElement(2, {{"place", "town"}, {"name", "Vaduz"}, {"wikidata", "Q1844"}}, OsmElement::EntityType::Node);
auto node3 = MakeOsmElement(3, {{"place", "country"}}, OsmElement::EntityType::Node);
RelationElement e1;
e1.m_ways = ways;
e1.m_nodes = nodes;
e1.m_tags.emplace("type", "boundary");
e1.m_tags.emplace("boundary", "administrative");
e1.m_tags.emplace("admin_level", "2");
e1.m_tags.emplace("name", "Liechtenstein");
e1.m_tags.emplace("name:be", "Лiхтэнштэйн");
e1.m_tags.emplace("wikidata", "Q347");
std::unordered_map<Key, RelationElement> m_IdToRelation = {{1, e1}};
TestOSMElementCacheReader reader(m_IdToRelation);
// Process ways tags using relation tags.
RelationTagsWay rtw;
rtw.Reset(1, &way1);
rtw(1, reader);
rtw.Reset(2, &node2);
rtw(1, reader);
rtw.Reset(3, &node3);
rtw(1, reader);
TEST(!way1.HasTag("name"), ());
TEST(!way1.HasTag("name:be"), ());
TEST(way1.GetTag("wikidata").empty(), ());
TEST_EQUAL(node2.GetTag("place"), "town", ());
TEST_EQUAL(node2.GetTag("name"), "Vaduz", ());
TEST(!node2.HasTag("name:be"), ());
TEST_EQUAL(node2.GetTag("wikidata"), "Q1844", ());
/// @todo Take name for places?
TEST_EQUAL(node3.GetTag("place"), "country", ());
TEST(!node3.HasTag("name"), ());
TEST(!node3.HasTag("name:be"), ());
TEST_EQUAL(node3.GetTag("wikidata"), "Q347", ());
}
UNIT_TEST(RelationTags_BadBoundary)
{
classificator::Load();
// Create relation.
std::vector<RelationElement::Member> testMembers = {{5, "outer"}, {6, "outer"}, {7, "outer"}};
/// @todo Worth to add natural=peninsula Point type.
RelationElement e1;
e1.m_ways = testMembers;
e1.m_tags.emplace("type", "boundary");
e1.m_tags.emplace("boundary", "land_area");
e1.m_tags.emplace("natural", "peninsula");
e1.m_tags.emplace("name", "Penisola italiana");
e1.m_tags.emplace("name:en", "Italian Peninsula");
e1.m_tags.emplace("wikidata", "Q145694");
std::unordered_map<Key, RelationElement> m_IdToRelation = {{1, e1}};
TestOSMElementCacheReader reader(m_IdToRelation);
// Create ways.
auto outerWay5 = MakeOsmElement(5, {{"natural", "coastline"}}, OsmElement::EntityType::Way);
auto outerWay6 = MakeOsmElement(6, {{"natural", "coastline"}, {"name", "Cala Rossa"}}, OsmElement::EntityType::Way);
auto outerWay7 = MakeOsmElement(7, {{"place", "locality"}}, OsmElement::EntityType::Way);
// Process ways tags using relation tags.
RelationTagsWay rtw;
rtw.Reset(5, &outerWay5);
rtw(1, reader);
rtw.Reset(6, &outerWay6);
rtw(1, reader);
rtw.Reset(7, &outerWay7);
rtw(1, reader);
// We don't aggregate name and wiki from type=boundary Relation if destination Way (Node) is not a place.
TEST(!outerWay5.HasTag("place"), ());
TEST(!outerWay5.HasTag("name"), ());
TEST(!outerWay5.HasTag("name:en"), ());
TEST(outerWay5.GetTag("wikidata").empty(), ());
TEST(!outerWay6.HasTag("place"), ());
TEST_EQUAL(outerWay6.GetTag("name"), "Cala Rossa", ());
TEST(!outerWay6.HasTag("name:en"), ());
TEST(outerWay6.GetTag("wikidata").empty(), ());
// Process only boundary=* valid classifier Relations.
TEST_EQUAL(outerWay7.GetTag("place"), "locality", ());
TEST(!outerWay7.HasTag("name"), ());
TEST(!outerWay7.HasTag("name:en"), ());
TEST(outerWay7.GetTag("wikidata").empty(), ());
}
} // namespace relation_tags_tests

View file

@ -0,0 +1,259 @@
#include "testing/testing.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/generator_tests_support/routing_helpers.hpp"
#include "generator/restriction_collector.hpp"
#include "routing/restrictions_serialization.hpp"
#include "traffic/traffic_cache.hpp"
#include "indexer/classificator_loader.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "base/file_name_utils.hpp"
#include "base/geo_object_id.hpp"
#include "base/scope_guard.hpp"
#include "base/stl_helpers.hpp"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace routing_builder
{
using namespace generator;
using namespace platform;
using namespace platform::tests_support;
using namespace routing;
std::string const kTestDir = "test-restrictions";
std::string const kOsmIdsToFeatureIdsName = "osm_ids_to_feature_ids" OSM2FEATURE_FILE_EXTENSION;
// 2 *
// ↗ ↘
// F5 F4
// ↗ ↘ Finish
// 1 * *<- F3 ->*-> F8 -> *-> F10 -> *
// ↖ ↑ ↗
// F6 F2 F9
// Start ↖ ↑ ↗
// 0 *-> F7 ->*-> F0 ->*-> F1 ->*
// -1 0 1 2 3 4
//
std::unique_ptr<IndexGraph> BuildTwoCubeGraph()
{
classificator::Load();
std::unique_ptr<TestGeometryLoader> loader = std::make_unique<TestGeometryLoader>();
loader->AddRoad(0 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{0.0, 0.0}, {1.0, 0.0}}));
loader->AddRoad(1 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{1.0, 0.0}, {2.0, 0.0}}));
loader->AddRoad(2 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{2.0, 0.0}, {2.0, 1.0}}));
loader->AddRoad(3 /* feature id */, false /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{1.0, 1.0}, {2.0, 1.0}}));
loader->AddRoad(4 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{0.0, 2.0}, {1.0, 1.0}}));
loader->AddRoad(5 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{-1.0, 1.0}, {0.0, 2.0}}));
loader->AddRoad(6 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{0.0, 0.0}, {-1.0, 1.0}}));
loader->AddRoad(7 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{-1.0, 0.0}, {0.0, 0.0}}));
loader->AddRoad(8 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{2.0, 1.0}, {3.0, 1.0}}));
loader->AddRoad(9 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{2.0, 0.0}, {3.0, 1.0}}));
loader->AddRoad(10 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{3.0, 1.0}, {4.0, 1.0}}));
std::vector<Joint> const joints = {
// {{/* feature id */, /* point id */}, ... }
MakeJoint({{7, 0}}), /* joint at point (-1, 0) */
MakeJoint({{0, 0}, {6, 0}, {7, 1}}), /* joint at point (0, 0) */
MakeJoint({{0, 1}, {1, 0}}), /* joint at point (1, 0) */
MakeJoint({{1, 1}, {2, 0}, {9, 0}}), /* joint at point (2, 0) */
MakeJoint({{2, 1}, {3, 1}, {8, 0}}), /* joint at point (2, 1) */
MakeJoint({{3, 0}, {4, 1}}), /* joint at point (1, 1) */
MakeJoint({{5, 1}, {4, 0}}), /* joint at point (0, 2) */
MakeJoint({{6, 1}, {5, 0}}), /* joint at point (-1, 1) */
MakeJoint({{8, 1}, {9, 1}, {10, 0}}), /* joint at point (3, 1) */
MakeJoint({{10, 1}}) /* joint at point (4, 1) */
};
traffic::TrafficCache const trafficCache;
std::shared_ptr<EdgeEstimator> estimator = CreateEstimatorForCar(trafficCache);
return BuildIndexGraph(std::move(loader), estimator, joints);
}
std::string const kosmIdsToFeatureIdsContentForTwoCubeGraph =
R"(0, 0
1, 1
2, 2
3, 3
4, 4
5, 5
6, 6
7, 7
8, 8
9, 9
10, 10)";
RelationElement MakeRelationElement(std::vector<RelationElement::Member> const & nodes,
std::vector<RelationElement::Member> const & ways,
std::map<std::string, std::string, std::less<>> const & tags)
{
RelationElement r;
r.m_nodes = nodes;
r.m_ways = ways;
r.m_tags = tags;
return r;
}
class TestRestrictionCollector
{
public:
TestRestrictionCollector() : m_scopedDir(kTestDir)
{
// Creating osm ids to feature ids mapping.
std::string const mappingRelativePath = base::JoinPath(kTestDir, kOsmIdsToFeatureIdsName);
m_scopedFile = std::make_shared<ScopedFile>(mappingRelativePath, ScopedFile::Mode::Create);
m_osmIdsToFeatureIdFullPath = m_scopedFile->GetFullPath();
ReEncodeOsmIdsToFeatureIdsMapping(kosmIdsToFeatureIdsContentForTwoCubeGraph, m_osmIdsToFeatureIdFullPath);
}
void ValidCase()
{
auto graph = BuildTwoCubeGraph();
RestrictionCollector restrictionCollector(m_osmIdsToFeatureIdFullPath, *graph);
// Adding restrictions.
TEST(restrictionCollector.AddRestriction(
{2.0, 0.0} /* coords of intersection feature with id = 1 and feature with id = 2 */,
Restriction::Type::No, /* restriction type */
{base::MakeOsmWay(1), base::MakeOsmWay(2)} /* features in format {from, (via*)?, to} */
),
());
TEST(restrictionCollector.AddRestriction({2.0, 1.0}, Restriction::Type::Only,
{base::MakeOsmWay(2), base::MakeOsmWay(3)}),
());
TEST(restrictionCollector.AddRestriction(RestrictionCollector::kNoCoords, /* no coords in case of way as via */
Restriction::Type::No,
/* from via to */
{base::MakeOsmWay(0), base::MakeOsmWay(1), base::MakeOsmWay(2)}),
());
base::SortUnique(restrictionCollector.m_restrictions);
std::vector<Restriction> expectedRestrictions = {
{Restriction::Type::No, {1, 2}},
{Restriction::Type::Only, {2, 3}},
{Restriction::Type::No, {0, 1, 2}},
};
std::sort(expectedRestrictions.begin(), expectedRestrictions.end());
TEST_EQUAL(restrictionCollector.m_restrictions, expectedRestrictions, ());
}
void InvalidCase_NoSuchFeature()
{
auto graph = BuildTwoCubeGraph();
RestrictionCollector restrictionCollector(m_osmIdsToFeatureIdFullPath, *graph);
// No such feature - 2809
TEST(!restrictionCollector.AddRestriction({2.0, 1.0}, Restriction::Type::No,
{base::MakeOsmWay(2809), base::MakeOsmWay(1)}),
());
TEST(!restrictionCollector.HasRestrictions(), ());
}
void InvalidCase_FeaturesNotIntersecting()
{
auto graph = BuildTwoCubeGraph();
RestrictionCollector restrictionCollector(m_osmIdsToFeatureIdFullPath, *graph);
// Fetures with id 1 and 2 do not intersect in {2.0, 1.0}
TEST(!restrictionCollector.AddRestriction({2.0, 1.0}, Restriction::Type::No,
{base::MakeOsmWay(1), base::MakeOsmWay(2)}),
());
// No such chain of features (1 => 2 => 4),
// because feature with id 2 and 4 do not have common joint.
TEST(!restrictionCollector.AddRestriction(RestrictionCollector::kNoCoords, Restriction::Type::No,
{base::MakeOsmWay(1), base::MakeOsmWay(2), base::MakeOsmWay(4)}),
());
TEST(!restrictionCollector.HasRestrictions(), ());
}
private:
ScopedDir m_scopedDir;
std::shared_ptr<ScopedFile> m_scopedFile;
std::string m_osmIdsToFeatureIdFullPath;
};
UNIT_CLASS_TEST(TestRestrictionCollector, ValidCase)
{
TestRestrictionCollector::ValidCase();
}
UNIT_CLASS_TEST(TestRestrictionCollector, InvalidCase_NoSuchFeature)
{
TestRestrictionCollector::InvalidCase_NoSuchFeature();
}
UNIT_CLASS_TEST(TestRestrictionCollector, InvalidCase_FeaturesNotIntersecting)
{
TestRestrictionCollector::InvalidCase_FeaturesNotIntersecting();
}
UNIT_TEST(RestrictionWriter_Merge)
{
classificator::Load();
auto const filename = generator_tests::GetFileName();
SCOPE_GUARD(_, std::bind(Platform::RemoveFileIfExists, std::cref(filename)));
auto c1 = std::make_shared<RestrictionWriter>(filename, nullptr /* cache */);
auto c2 = c1->Clone();
std::map<std::string, std::string, std::less<>> const tags = {{"type", "restriction"},
{"restriction", "no_right_turn"}};
c1->CollectRelation(
MakeRelationElement({} /* nodes */, {{1, "via"}, {11, "from"}, {21, "to"}} /* ways */, tags /* tags */));
c2->CollectRelation(
MakeRelationElement({} /* nodes */, {{2, "via"}, {12, "from"}, {22, "to"}} /* ways */, tags /* tags */));
c1->CollectRelation(
MakeRelationElement({} /* nodes */, {{3, "via"}, {13, "from"}, {23, "to"}} /* ways */, tags /* tags */));
c2->CollectRelation(
MakeRelationElement({} /* nodes */, {{4, "via"}, {14, "from"}, {24, "to"}} /* ways */, tags /* tags */));
c1->Finish();
c2->Finish();
c1->Merge(*c2);
c1->Finalize();
std::ifstream stream;
stream.exceptions(std::fstream::failbit | std::fstream::badbit);
stream.open(filename);
std::stringstream buffer;
buffer << stream.rdbuf();
std::string const correctAnswer =
"No,way,11,1,21\n"
"No,way,13,3,23\n"
"No,way,12,2,22\n"
"No,way,14,4,24\n";
TEST_EQUAL(buffer.str(), correctAnswer, ());
}
} // namespace routing_builder

View file

@ -0,0 +1,418 @@
#include "testing/testing.hpp"
#include "generator/generator_tests_support/routing_helpers.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include "generator/restriction_collector.hpp"
#include "generator/restriction_generator.hpp"
#include "traffic/traffic_cache.hpp"
#include "indexer/classificator_loader.hpp"
#include "platform/country_file.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "coding/files_container.hpp"
#include "base/assert.hpp"
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
namespace restriction_test
{
using namespace feature;
using namespace generator;
using namespace platform::tests_support;
using namespace platform;
using namespace routing;
using std::move, std::string, std::unique_ptr, std::vector;
// Directory name for creating test mwm and temporary files.
string const kTestDir = "restriction_generation_test";
// Temporary mwm name for testing.
string const kTestMwm = "test";
string const kRestrictionFileName = "restrictions_in_osm_ids.csv";
string const kOsmIdsToFeatureIdsName = "osm_ids_to_feature_ids" OSM2FEATURE_FILE_EXTENSION;
struct RestrictionUTurnForTests
{
RestrictionUTurnForTests(Restriction::Type type, uint32_t featureId, bool viaIsFirstPoint)
: m_type(type)
, m_featureId(featureId)
, m_viaIsFirstPoint(viaIsFirstPoint)
{
CHECK(m_type == Restriction::Type::NoUTurn || m_type == Restriction::Type::OnlyUTurn, ());
}
bool operator==(RestrictionUTurnForTests const & rhs) const
{
return m_type == rhs.m_type && m_featureId == rhs.m_featureId && m_viaIsFirstPoint == rhs.m_viaIsFirstPoint;
}
bool operator<(RestrictionUTurnForTests const & rhs) const
{
if (m_type != rhs.m_type)
return m_type < rhs.m_type;
if (m_featureId != rhs.m_featureId)
return m_featureId < rhs.m_featureId;
return m_viaIsFirstPoint < rhs.m_viaIsFirstPoint;
}
Restriction::Type m_type;
uint32_t m_featureId;
bool m_viaIsFirstPoint;
};
string DebugPrint(RestrictionUTurnForTests const & r)
{
std::ostringstream ss;
ss << "[" << DebugPrint(r.m_type) << "]: "
<< "feature: " << r.m_featureId << ", "
<< "isFirstPoint: " << r.m_viaIsFirstPoint;
return ss.str();
}
void BuildEmptyMwm(LocalCountryFile & country)
{
generator::tests_support::TestMwmBuilder builder(country, feature::DataHeader::MapType::Country);
}
void LoadRestrictions(string const & mwmFilePath, vector<Restriction> & restrictions,
vector<RestrictionUTurnForTests> & restrictionsUTurn)
{
FilesContainerR const cont(mwmFilePath);
if (!cont.IsExist(RESTRICTIONS_FILE_TAG))
return;
try
{
FilesContainerR::TReader const reader = cont.GetReader(RESTRICTIONS_FILE_TAG);
ReaderSource<FilesContainerR::TReader> src(reader);
RestrictionHeader header;
header.Deserialize(src);
RestrictionVec restrictionsNo;
RestrictionVec restrictionsOnly;
vector<RestrictionUTurn> restrictionsNoUTurn;
vector<RestrictionUTurn> restrictionsOnlyUTurn;
RestrictionSerializer::Deserialize(header, restrictionsNo, restrictionsOnly, restrictionsNoUTurn,
restrictionsOnlyUTurn, src);
for (auto const & r : restrictionsNo)
restrictions.emplace_back(Restriction::Type::No, vector<uint32_t>(r.begin(), r.end()));
for (auto const & r : restrictionsOnly)
restrictions.emplace_back(Restriction::Type::Only, vector<uint32_t>(r.begin(), r.end()));
for (auto const & r : restrictionsNoUTurn)
restrictionsUTurn.emplace_back(Restriction::Type::NoUTurn, r.m_featureId, r.m_viaIsFirstPoint);
for (auto const & r : restrictionsOnlyUTurn)
restrictionsUTurn.emplace_back(Restriction::Type::OnlyUTurn, r.m_featureId, r.m_viaIsFirstPoint);
}
catch (Reader::OpenException const & e)
{
TEST(false, ("Error while reading", RESTRICTIONS_FILE_TAG, "section.", e.Msg()));
}
}
/// \brief Generates a restriction section, adds it to an empty mwm,
/// loads the restriction section and test loaded restrictions.
/// \param |restrictionPath| comma separated text with restrictions in osm id terms.
/// \param |osmIdsToFeatureIdContent| comma separated text with mapping from osm ids to feature ids.
void TestRestrictionBuilding(string const & restrictionPath, string const & osmIdsToFeatureIdContent,
unique_ptr<IndexGraph> graph, vector<Restriction> & expectedNotUTurn,
vector<RestrictionUTurnForTests> & expectedUTurn)
{
Platform & platform = GetPlatform();
string const writableDir = platform.WritableDir();
string const targetDir = base::JoinPath(writableDir, kTestDir);
// Building empty mwm.
LocalCountryFile country(targetDir, CountryFile(kTestMwm), 0 /* version */);
ScopedDir const scopedDir(kTestDir);
string const mwmRelativePath = base::JoinPath(kTestDir, kTestMwm + DATA_FILE_EXTENSION);
ScopedFile const scopedMwm(mwmRelativePath, ScopedFile::Mode::Create);
BuildEmptyMwm(country);
// Creating a file with restrictions.
string const restrictionRelativePath = base::JoinPath(kTestDir, kRestrictionFileName);
ScopedFile const restrictionScopedFile(restrictionRelativePath, restrictionPath);
// Creating osm ids to feature ids mapping.
string const mappingRelativePath = base::JoinPath(kTestDir, kOsmIdsToFeatureIdsName);
ScopedFile const mappingFile(mappingRelativePath, ScopedFile::Mode::Create);
string const & osmIdsToFeatureIdFullPath = mappingFile.GetFullPath();
ReEncodeOsmIdsToFeatureIdsMapping(osmIdsToFeatureIdContent, osmIdsToFeatureIdFullPath);
string const restrictionFullPath = base::JoinPath(writableDir, restrictionRelativePath);
string const & mwmFullPath = scopedMwm.GetFullPath();
// Prepare data to collector.
auto restrictionCollector =
std::make_unique<routing_builder::RestrictionCollector>(osmIdsToFeatureIdFullPath, *graph);
TEST(restrictionCollector->Process(restrictionFullPath), ("Bad restrictions were given."));
// Adding restriction section to mwm.
SerializeRestrictions(*restrictionCollector, mwmFullPath);
// Reading from mwm section and testing restrictions.
vector<Restriction> restrictionsFromMwm;
vector<RestrictionUTurnForTests> restrictionsUTurnFromMwm;
LoadRestrictions(mwmFullPath, restrictionsFromMwm, restrictionsUTurnFromMwm);
sort(restrictionsFromMwm.begin(), restrictionsFromMwm.end());
sort(restrictionsUTurnFromMwm.begin(), restrictionsUTurnFromMwm.end());
sort(expectedNotUTurn.begin(), expectedNotUTurn.end());
sort(expectedUTurn.begin(), expectedUTurn.end());
TEST_EQUAL(restrictionsFromMwm, expectedNotUTurn, ());
TEST_EQUAL(restrictionsUTurnFromMwm, expectedUTurn, ());
}
// 2 *
// ↗ ↘
// F5 F4
// ↗ ↘ Finish
// 1 * *<- F3 ->*-> F8 -> *-> F10 -> *
// ↖ ↑ ↗
// F6 F2 F9
// Start ↘ ↑ ↗
// 0 *-> F7 ->*-> F0 ->*-> F1 ->*
// -1 0 1 2 3 4
//
std::pair<unique_ptr<IndexGraph>, string> BuildTwoCubeGraph()
{
classificator::Load();
auto loader = std::make_unique<TestGeometryLoader>();
loader->AddRoad(0 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{0.0, 0.0}, {1.0, 0.0}}));
loader->AddRoad(1 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{1.0, 0.0}, {2.0, 0.0}}));
loader->AddRoad(2 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{2.0, 0.0}, {2.0, 1.0}}));
loader->AddRoad(3 /* feature id */, false /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{1.0, 1.0}, {2.0, 1.0}}));
loader->AddRoad(4 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{0.0, 2.0}, {1.0, 1.0}}));
loader->AddRoad(5 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{-1.0, 1.0}, {0.0, 2.0}}));
loader->AddRoad(6 /* feature id */, false /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{0.0, 0.0}, {-1.0, 1.0}}));
loader->AddRoad(7 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{-1.0, 0.0}, {0.0, 0.0}}));
loader->AddRoad(8 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{2.0, 1.0}, {3.0, 1.0}}));
loader->AddRoad(9 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{2.0, 0.0}, {3.0, 1.0}}));
loader->AddRoad(10 /* feature id */, true /* one way */, 1.0 /* speed */,
RoadGeometry::Points({{3.0, 1.0}, {4.0, 1.0}}));
vector<Joint> const joints = {
// {{/* feature id */, /* point id */}, ... }
MakeJoint({{7, 0}}), /* joint at point (-1, 0) */
MakeJoint({{0, 0}, {6, 0}, {7, 1}}), /* joint at point (0, 0) */
MakeJoint({{0, 1}, {1, 0}}), /* joint at point (1, 0) */
MakeJoint({{1, 1}, {2, 0}, {9, 0}}), /* joint at point (2, 0) */
MakeJoint({{2, 1}, {3, 1}, {8, 0}}), /* joint at point (2, 1) */
MakeJoint({{3, 0}, {4, 1}}), /* joint at point (1, 1) */
MakeJoint({{5, 1}, {4, 0}}), /* joint at point (0, 2) */
MakeJoint({{6, 1}, {5, 0}}), /* joint at point (-1, 1) */
MakeJoint({{8, 1}, {9, 1}, {10, 0}}), /* joint at point (3, 1) */
MakeJoint({{10, 1}}) /* joint at point (4, 1) */
};
traffic::TrafficCache const trafficCache;
std::shared_ptr<EdgeEstimator> estimator = CreateEstimatorForCar(trafficCache);
string const osmIdsToFeatureIdsContent = R"(0, 0
1, 1
2, 2
3, 3
4, 4
5, 5
6, 6
7, 7
8, 8
9, 9
10, 10)";
return {BuildIndexGraph(std::move(loader), estimator, joints), osmIdsToFeatureIdsContent};
}
UNIT_TEST(RestrictionGenerationTest_1)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
string const restrictionPath =
/* Type ViaType ViaNodeCoords: x y from to */
R"(Only, node, 1.0, 0.0, 0, 1)";
vector<Restriction> expectedNotUTurn = {{Restriction::Type::Only, {0, 1}}};
vector<RestrictionUTurnForTests> expectedUTurn;
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
UNIT_TEST(RestrictionGenerationTest_2)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
string const restrictionPath =
/* Type ViaType from ViaWayId to */
R"(Only, way, 0, 1 2)";
vector<Restriction> expectedNotUTurn = {{Restriction::Type::Only, {0, 1, 2}}};
vector<RestrictionUTurnForTests> expectedUTurn;
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
UNIT_TEST(RestrictionGenerationTest_3)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
string const restrictionPath =
/* Type ViaType ViaNodeCoords: x y from via to */
R"(Only, node, 0.0, 0.0, 7, 6
No, way, 2, 8, 10)";
vector<Restriction> expectedNotUTurn = {{Restriction::Type::Only, {7, 6}}, {Restriction::Type::No, {2, 8, 10}}};
vector<RestrictionUTurnForTests> expectedUTurn;
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
UNIT_TEST(RestrictionGenerationTest_BadConnection_1)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
// Here features 7 and 6 don't connect at (2.0, 0.0)
string const restrictionPath =
/* Type ViaType ViaNodeCoords: x y from via to */
R"(Only, node, 2.0, 0.0, 7, 6
No, way, 2, 8, 10)";
// So we don't expect first restriction here.
vector<Restriction> expectedNotUTurn = {{Restriction::Type::No, {2, 8, 10}}};
vector<RestrictionUTurnForTests> expectedUTurn;
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
UNIT_TEST(RestrictionGenerationTest_BadConnection_2)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
// Here features 0, 1 and 3 don't have common joints (namely 1 and 3).
string const restrictionPath =
/* Type ViaType ViaNodeCoords: x y from via to */
R"(Only, node, 0.0, 0.0, 7, 6
No, way, 0, 1, 3)";
// So we don't expect second restriction here.
vector<Restriction> expectedNotUTurn = {{Restriction::Type::Only, {7, 6}}};
vector<RestrictionUTurnForTests> expectedUTurn;
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
UNIT_TEST(RestrictionGenerationTest_WithUTurn_1)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
string const restrictionPath =
/* Type ViaType ViaNodeCoords: x y from to */
R"(NoUTurn, node, 2.0, 1.0, 3, 3
OnlyUTurn, node, 0.0, 0.0, 6, 6)";
// So we don't expect second restriction here.
vector<Restriction> expectedNotUTurn;
vector<RestrictionUTurnForTests> expectedUTurn = {
{Restriction::Type::NoUTurn, 3 /* featureId */, false /* viaIsFirstPoint */},
{Restriction::Type::OnlyUTurn, 6 /* featureId */, true /* viaIsFirstPoint */}};
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
UNIT_TEST(RestrictionGenerationTest_WithUTurn_2)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
// First two are not UTurn restrictions, but still correct restriction.
// We should just convert them.
string const restrictionPath =
/* Type ViaType ViaNodeCoords: x y from via to */
R"(NoUTurn, node, 2.0, 1.0, 2, 8
NoUTurn, way, 2, 8, 10
OnlyUTurn, way, 6, 5, 4
OnlyUTurn, node, -1.0, 1.0, 6, 6)";
// So we don't expect second restriction here.
vector<Restriction> expectedNotUTurn = {
{Restriction::Type::No, {2, 8}}, {Restriction::Type::No, {2, 8, 10}}, {Restriction::Type::Only, {6, 5, 4}}};
vector<RestrictionUTurnForTests> expectedUTurn = {
{Restriction::Type::OnlyUTurn, 6 /* featureId */, false /* viaIsFirstPoint */}};
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
UNIT_TEST(RestrictionGenerationTest_WithUTurn_BadConnection_1)
{
string osmIdsToFeatureIdsContent;
unique_ptr<IndexGraph> indexGraph;
tie(indexGraph, osmIdsToFeatureIdsContent) = BuildTwoCubeGraph();
string const restrictionPath =
/* Type ViaType ViaNodeCoords: x y from via to */
R"(NoUTurn, node, 20.0, 11.0, 2, 8
OnlyUTurn, way, 6, 2, 4
OnlyUTurn, node, -10.0, 10.0, 6, 6
NoUTurn, node, -10.0, 10.0, 6, 6)";
// So we don't expect second restriction here.
vector<Restriction> expectedNotUTurn;
vector<RestrictionUTurnForTests> expectedUTurn;
TestRestrictionBuilding(restrictionPath, osmIdsToFeatureIdsContent, std::move(indexGraph), expectedNotUTurn,
expectedUTurn);
}
} // namespace restriction_test

View file

@ -0,0 +1,537 @@
#include "testing/testing.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/intermediate_data.hpp"
#include "generator/osm2type.hpp"
#include "generator/road_access_generator.hpp"
#include "generator/routing_helpers.hpp"
#include "indexer/classificator_loader.hpp"
#include "platform/platform.hpp"
#include "geometry/point2d.hpp"
#include <fstream>
#include <string>
#include <vector>
namespace road_access_test
{
using namespace generator;
using namespace generator_tests;
using namespace platform;
using namespace routing;
using namespace routing_builder;
using std::fstream, std::ifstream, std::make_pair, std::string;
/*
string const kTestDir = "road_access_generation_test";
string const kTestMwm = "test";
string const kRoadAccessFilename = "road_access_in_osm_ids.bin";
string const kOsmIdsToFeatureIdsName = "osm_ids_to_feature_ids" OSM2FEATURE_FILE_EXTENSION;
void BuildTestMwmWithRoads(LocalCountryFile & country)
{
generator::tests_support::TestMwmBuilder builder(country, feature::DataHeader::MapType::Country);
for (size_t i = 0; i < 10; ++i)
{
string const name = "road " + strings::to_string(i);
string const lang = "en";
std::vector<m2::PointD> points;
for (size_t j = 0; j < 10; ++j)
points.emplace_back(static_cast<double>(i), static_cast<double>(j));
builder.Add(generator::tests_support::TestRoad(points, name, lang));
}
}
void LoadRoadAccess(string const & mwmFilePath, VehicleType vehicleType, RoadAccess & roadAccess)
{
try
{
FilesContainerR const cont(mwmFilePath);
FilesContainerR::TReader const reader = cont.GetReader(ROAD_ACCESS_FILE_TAG);
ReaderSource<FilesContainerR::TReader> src(reader);
RoadAccessSerializer::Deserialize(src, vehicleType, roadAccess);
}
catch (Reader::Exception const & e)
{
TEST(false, ("Error while reading", ROAD_ACCESS_FILE_TAG, "section.", e.Msg()));
}
}
// todo(@m) This helper function is almost identical to the one in restriction_test.cpp.
RoadAccessByVehicleType SaveAndLoadRoadAccess(
string const & raContent, string const & mappingContent, string const & raContitionalContent = {})
{
classificator::Load();
Platform & platform = GetPlatform();
string const & writableDir = platform.WritableDir();
using namespace platform::tests_support;
// Building empty mwm.
LocalCountryFile country(base::JoinPath(writableDir, kTestDir), CountryFile(kTestMwm), 0);
ScopedDir const scopedDir(kTestDir);
ScopedFile const scopedMwm(base::JoinPath(kTestDir, kTestMwm + DATA_FILE_EXTENSION), ScopedFile::Mode::Create);
string const mwmFullPath = scopedMwm.GetFullPath();
BuildTestMwmWithRoads(country);
// Creating a file with road access.
ScopedFile const raFile(base::JoinPath(kTestDir, kRoadAccessFilename), raContent);
string const roadAccessFullPath = raFile.GetFullPath();
// Creating osm ids to feature ids mapping.
ScopedFile const mappingFile(base::JoinPath(kTestDir, kOsmIdsToFeatureIdsName), ScopedFile::Mode::Create);
string const mappingFullPath = mappingFile.GetFullPath();
ReEncodeOsmIdsToFeatureIdsMapping(mappingContent, mappingFullPath);
// Adding road access section to mwm.
auto osm2feature = CreateWay2FeatureMapper(mwmFullPath, mappingFullPath);
BuildRoadAccessInfo(mwmFullPath, roadAccessFullPath, *osm2feature);
// Reading from mwm section and testing road access.
RoadAccessByVehicleType roadAccessFromMwm, roadAccessFromFile;
for (size_t i = 0; i < static_cast<size_t>(VehicleType::Count); ++i)
{
auto const vehicleType = static_cast<VehicleType>(i);
LoadRoadAccess(mwmFullPath, vehicleType, roadAccessFromMwm[i]);
}
ReadRoadAccess(roadAccessFullPath, *osm2feature, roadAccessFromFile);
TEST_EQUAL(roadAccessFromMwm, roadAccessFromFile, ());
return roadAccessFromMwm;
}
*/
OsmElement MakeOsmElementWithNodes(uint64_t id, generator_tests::Tags const & tags, OsmElement::EntityType t,
std::vector<uint64_t> const & nodes)
{
auto r = generator_tests::MakeOsmElement(id, tags, t);
r.m_nodes = nodes;
return r;
}
class IntermediateDataTest : public cache::IntermediateDataReaderInterface
{
std::map<cache::Key, WayElement> m_map;
public:
virtual bool GetNode(cache::Key id, double & y, double & x) const { UNREACHABLE(); }
virtual bool GetWay(cache::Key id, WayElement & e)
{
auto it = m_map.find(id);
if (it != m_map.end())
{
e = it->second;
return true;
}
return false;
}
virtual bool GetRelation(cache::Key id, RelationElement & e) { UNREACHABLE(); }
void Add(OsmElement const & e) { TEST(m_map.try_emplace(e.m_id, e.m_id, e.m_nodes).second, ()); }
};
/*
UNIT_TEST(RoadAccess_Smoke)
{
string const roadAccessContent;
string const osmIdsToFeatureIdsContent;
SaveAndLoadRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
}
UNIT_TEST(RoadAccess_AccessPrivate)
{
string const roadAccessContent = R"(Car Private 0 0)";
string const osmIdsToFeatureIdsContent = R"(0, 0,)";
auto const roadAccessAllTypes =
SaveAndLoadRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
auto const & carRoadAccess = roadAccessAllTypes[static_cast<size_t>(VehicleType::Car)];
TEST_EQUAL(carRoadAccess.GetAccessWithoutConditional(0),
make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure), ());
}
UNIT_TEST(RoadAccess_Multiple_Vehicle_Types)
{
string const roadAccessContent = R"(Car Private 10 0
Car Private 20 0
Bicycle No 30 0
Car Destination 40 0)";
string const osmIdsToFeatureIdsContent = R"(10, 1,
20, 2,
30, 3,
40, 4,)";
auto const roadAccessAllTypes =
SaveAndLoadRoadAccess(roadAccessContent, osmIdsToFeatureIdsContent);
auto const & carRoadAccess = roadAccessAllTypes[static_cast<size_t>(VehicleType::Car)];
auto const & bicycleRoadAccess = roadAccessAllTypes[static_cast<size_t>(VehicleType::Bicycle)];
TEST_EQUAL(carRoadAccess.GetAccessWithoutConditional(1),
make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure), ());
TEST_EQUAL(carRoadAccess.GetAccessWithoutConditional(2),
make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure), ());
TEST_EQUAL(carRoadAccess.GetAccessWithoutConditional(3),
make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure), ());
TEST_EQUAL(carRoadAccess.GetAccessWithoutConditional(4),
make_pair(RoadAccess::Type::Destination, RoadAccess::Confidence::Sure), ());
TEST_EQUAL(bicycleRoadAccess.GetAccessWithoutConditional(3),
make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure), ());
}
*/
class TestAccessFixture
{
std::shared_ptr<IntermediateDataTest> m_cache;
std::vector<std::shared_ptr<RoadAccessCollector>> m_collectors;
std::string m_fileName;
class Way2Feature : public OsmWay2FeaturePoint
{
public:
virtual void ForEachFeature(uint64_t wayID, std::function<void(uint32_t)> const & fn) override
{
fn(base::checked_cast<uint32_t>(wayID));
}
virtual void ForEachNodeIdx(uint64_t wayID, uint32_t candidateIdx, m2::PointU pt,
std::function<void(uint32_t, uint32_t)> const & fn) override
{
auto const ll = mercator::ToLatLon(PointUToPointD(pt, kPointCoordBits, mercator::Bounds::FullRect()));
// We use nodes like id = 1 {10, 11, 12}; id = 2 {20, 21, 22} for ways.
uint32_t const node = round(ll.m_lon);
uint32_t const featureID = base::checked_cast<uint32_t>(wayID);
TEST_EQUAL(featureID, node / 10, ());
fn(featureID, node % 10);
}
} m_wya2feature;
RoadAccessByVehicleType m_roadAccess;
public:
TestAccessFixture()
: m_cache(std::make_shared<IntermediateDataTest>())
, m_fileName(generator_tests::GetFileName(ROAD_ACCESS_FILENAME))
{
classificator::Load();
}
~TestAccessFixture() { TEST(Platform::RemoveFileIfExists(m_fileName), ()); }
void CreateCollectors(size_t count = 1)
{
for (size_t i = 0; i < count; ++i)
m_collectors.push_back(std::make_shared<RoadAccessCollector>(m_fileName, m_cache));
}
void AddWay(OsmElement way, size_t idx = 0)
{
feature::FeatureBuilder builder;
ftype::GetNameAndType(&way, builder.GetParams());
builder.SetLinear();
m_cache->Add(way);
m_collectors[idx]->CollectFeature(builder, way);
}
void AddNode(OsmElement node, size_t idx = 0)
{
// Assign unique coordinates as id.
node.m_lat = node.m_lon = node.m_id;
feature::FeatureBuilder builder;
ftype::GetNameAndType(&node, builder.GetParams());
builder.SetCenter(mercator::FromLatLon(node.m_lat, node.m_lon));
m_collectors[idx]->CollectFeature(builder, node);
}
void Finish()
{
for (auto const & c : m_collectors)
c->Finish();
for (size_t i = 1; i < m_collectors.size(); ++i)
m_collectors[0]->Merge(*m_collectors[i]);
m_collectors[0]->Finalize();
ReadRoadAccess(m_fileName, m_wya2feature, m_roadAccess);
}
RoadAccess const & Get(VehicleType vehicle) const { return m_roadAccess[static_cast<uint8_t>(vehicle)]; }
};
UNIT_CLASS_TEST(TestAccessFixture, CarPermit)
{
CreateCollectors();
AddWay(MakeOsmElementWithNodes(1 /* id */, {{"highway", "motorway"}, {"access", "no"}, {"motor_vehicle", "permit"}},
OsmElement::EntityType::Way, {1, 2}));
Finish();
auto const noSure = make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure);
TEST_EQUAL(Get(VehicleType::Pedestrian).GetAccessWithoutConditional(1), noSure, ());
TEST_EQUAL(Get(VehicleType::Bicycle).GetAccessWithoutConditional(1), noSure, ());
TEST_EQUAL(Get(VehicleType::Car).GetAccessWithoutConditional(1),
make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure), ());
}
// https://www.openstreetmap.org/way/797145238#map=19/-34.61801/-58.36501
UNIT_CLASS_TEST(TestAccessFixture, HgvDesignated)
{
CreateCollectors();
AddWay(MakeOsmElementWithNodes(1 /* id */,
{{"highway", "motorway"},
{"access", "no"},
{"emergency", "yes"},
{"bus", "yes"},
{"hgv", "designated"},
{"motor_vehicle", "yes"}},
OsmElement::EntityType::Way, {1, 2}));
AddWay(MakeOsmElementWithNodes(
2 /* id */,
{{"highway", "motorway"}, {"access", "no"}, {"emergency", "yes"}, {"bus", "yes"}, {"hgv", "designated"}},
OsmElement::EntityType::Way, {2, 3}));
Finish();
auto const noSure = make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure);
TEST_EQUAL(Get(VehicleType::Pedestrian).GetAccessWithoutConditional(1), noSure, ());
TEST_EQUAL(Get(VehicleType::Bicycle).GetAccessWithoutConditional(1), noSure, ());
TEST_EQUAL(Get(VehicleType::Car).GetAccessWithoutConditional(1),
make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure), ());
TEST_EQUAL(Get(VehicleType::Car).GetAccessWithoutConditional(2),
make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure), ());
}
UNIT_CLASS_TEST(TestAccessFixture, Merge)
{
CreateCollectors(3);
AddWay(MakeOsmElementWithNodes(1 /* id */, {{"highway", "service"}} /* tags */, OsmElement::EntityType::Way,
{10, 11, 12, 13}),
0);
AddWay(MakeOsmElementWithNodes(2 /* id */, {{"highway", "service"}} /* tags */, OsmElement::EntityType::Way,
{20, 21, 22, 23}),
1);
AddWay(MakeOsmElementWithNodes(3 /* id */, {{"highway", "motorway"}} /* tags */, OsmElement::EntityType::Way,
{30, 31, 32, 33}),
2);
AddNode(MakeOsmElement(11 /* id */, {{"barrier", "lift_gate"}, {"motor_vehicle", "private"}},
OsmElement::EntityType::Node),
0);
AddNode(MakeOsmElement(22 /* id */, {{"barrier", "lift_gate"}, {"motor_vehicle", "private"}},
OsmElement::EntityType::Node),
1);
// We should ignore this barrier because it's without access tag and placed on highway-motorway.
AddNode(MakeOsmElement(32 /* id */, {{"barrier", "lift_gate"}}, OsmElement::EntityType::Node), 2);
// Ignore all motorway_junction access.
AddNode(MakeOsmElement(31 /* id */, {{"highway", "motorway_junction"}, {"access", "private"}},
OsmElement::EntityType::Node),
0);
Finish();
auto const privateSure = make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure);
auto const yesSure = make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure);
auto const & car = Get(VehicleType::Car);
TEST_EQUAL(car.GetAccessWithoutConditional({1, 1}), privateSure, ());
TEST_EQUAL(car.GetAccessWithoutConditional({2, 2}), privateSure, ());
TEST_EQUAL(car.GetAccessWithoutConditional({3, 1}), yesSure, ());
TEST_EQUAL(car.GetAccessWithoutConditional({3, 2}), yesSure, ());
}
UNIT_TEST(RoadAccessCoditional_Parse)
{
AccessConditionalTagParser parser;
using ConditionalVector = std::vector<AccessConditional>;
std::vector<std::pair<string, ConditionalVector>> const tests = {
{"no @ Mo-Su", {{RoadAccess::Type::No, "Mo-Su"}}},
{"no @ Mo-Su;", {{RoadAccess::Type::No, "Mo-Su"}}},
{"yes @ (10:00 - 20:00)", {{RoadAccess::Type::Yes, "10:00 - 20:00"}}},
{"private @ Mo-Fr 15:00-20:00", {{RoadAccess::Type::Private, "Mo-Fr 15:00-20:00"}}},
{"destination @ 10:00-20:00", {{RoadAccess::Type::Destination, "10:00-20:00"}}},
{"yes @ Mo-Fr ; Sa-Su", {{RoadAccess::Type::Yes, "Mo-Fr ; Sa-Su"}}},
{"no @ (Mo-Su) ; yes @ (Fr-Su)",
{{RoadAccess::Type::No, "Mo-Su"},
{RoadAccess::Type::Yes, "Fr-Su"}}},
{"private @ (18:00-09:00; Oct-Mar)", {{RoadAccess::Type::Private, "18:00-09:00; Oct-Mar"}}},
{"no @ (Nov-May); no @ (20:00-07:00)",
{{RoadAccess::Type::No, "Nov-May"}, {RoadAccess::Type::No, "20:00-07:00"}}},
{"no @ 22:30-05:00", {{RoadAccess::Type::No, "22:30-05:00"}}},
{"destination @ (Mo-Fr 06:00-15:00); yes @ (Mo-Fr 15:00-21:00; Sa,Su,SH,PH 09:00-21:00)",
{{RoadAccess::Type::Destination, "Mo-Fr 06:00-15:00"},
{RoadAccess::Type::Yes, "Mo-Fr 15:00-21:00; Sa,Su,SH,PH 09:00-21:00"}}},
{"no @ (Mar 15-Jul 15); private @ (Jan- Dec)",
{{RoadAccess::Type::No, "Mar 15-Jul 15"}, {RoadAccess::Type::Private, "Jan- Dec"}}},
{"no @ (06:30-08:30);destination @ (06:30-08:30 AND agricultural)",
{{RoadAccess::Type::No, "06:30-08:30"}, {RoadAccess::Type::Destination, "06:30-08:30 AND agricultural"}}},
{"no @ (Mo-Fr 00:00-08:00,20:00-24:00; Sa-Su 00:00-24:00; PH 00:00-24:00)",
{{RoadAccess::Type::No, "Mo-Fr 00:00-08:00,20:00-24:00; Sa-Su 00:00-24:00; PH 00:00-24:00"}}},
// Not valid cases
{"trash @ (Mo-Fr 00:00-10:00)", {{RoadAccess::Type::Count, "Mo-Fr 00:00-10:00"}}},
{"yes Mo-Fr", {}},
{"yes (Mo-Fr)", {}},
{"no ; Mo-Fr", {}},
{"asdsadasdasd", {}}};
std::vector<string> tags = {"motorcar:conditional", "vehicle:conditional", "motor_vehicle:conditional",
"bicycle:conditional", "foot:conditional"};
for (auto const & tag : tags)
{
for (auto const & [value, answer] : tests)
{
auto const access = parser.ParseAccessConditionalTag(tag, value);
TEST(access == answer, (value, tag));
}
}
}
UNIT_CLASS_TEST(TestAccessFixture, ExoticConditionals)
{
CreateCollectors();
AddWay(MakeOsmElementWithNodes(1 /* id */, {{"highway", "motorway"}, {"access", "no @ (wind_speed>=65)"}},
OsmElement::EntityType::Way, {10, 11, 12, 13}));
Finish();
auto const yesSure = make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure);
auto const & car = Get(VehicleType::Car);
TEST_EQUAL(car.GetAccess(1, RouteWeight()), yesSure, ());
TEST_EQUAL(car.GetAccessWithoutConditional(1), yesSure, ());
}
UNIT_CLASS_TEST(TestAccessFixture, ConditionalMerge)
{
CreateCollectors(3);
AddWay(
MakeOsmElementWithNodes(1 /* id */, {{"highway", "primary"}, {"vehicle:conditional", "no @ (Mo-Su)"}} /* tags */,
OsmElement::EntityType::Way, {10, 11, 12, 13}),
0);
AddWay(MakeOsmElementWithNodes(
2 /* id */, {{"highway", "service"}, {"vehicle:conditional", "private @ (10:00-20:00)"}} /* tags */,
OsmElement::EntityType::Way, {20, 21, 22, 23}),
1);
AddWay(MakeOsmElementWithNodes(
3 /* id */,
{{"highway", "service"}, {"vehicle:conditional", "private @ (12:00-19:00) ; no @ (Mo-Su)"}} /* tags */,
OsmElement::EntityType::Way, {30, 31, 32, 33}),
2);
Finish();
auto const & car = Get(VehicleType::Car);
// Here can be any RouteWeight start time.
TEST_EQUAL(car.GetAccess(1, RouteWeight(666)), make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure), ());
for (uint64_t id = 1; id <= 3; ++id)
TEST_GREATER(car.GetWayToAccessConditional().at(id).Size(), 0, ());
/// @todo Add more timing tests using
/// GetUnixtimeByDate(2020, Month::Apr, Weekday::Monday, 11 /* hh */, 50 /* mm */)
}
UNIT_CLASS_TEST(TestAccessFixture, WinterRoads)
{
CreateCollectors();
AddWay(MakeOsmElementWithNodes(1 /* id */, {{"highway", "primary"}, {"ice_road", "yes"}} /* tags */,
OsmElement::EntityType::Way, {10, 11, 12, 13}));
AddWay(MakeOsmElementWithNodes(2 /* id */, {{"highway", "service"}, {"winter_road", "yes"}} /* tags */,
OsmElement::EntityType::Way, {20, 21, 22, 23}));
Finish();
for (auto vehicle : {VehicleType::Pedestrian, VehicleType::Bicycle, VehicleType::Car})
{
auto const & ra = Get(vehicle);
for (uint64_t id = 1; id <= 2; ++id)
{
auto const & wac = ra.GetWayToAccessConditional();
auto const it = wac.find(id);
TEST(it != wac.end(), (id, vehicle));
TEST_GREATER(it->second.Size(), 0, ());
}
}
/// @todo Add more timing tests using
/// GetUnixtimeByDate(2020, Month::Apr, Weekday::Monday, 11 /* hh */, 50 /* mm */)
}
UNIT_CLASS_TEST(TestAccessFixture, Locked)
{
CreateCollectors();
AddWay(MakeOsmElementWithNodes(1 /* id */, {{"highway", "service"}} /* tags */, OsmElement::EntityType::Way,
{10, 11, 12, 13}));
AddWay(MakeOsmElementWithNodes(2 /* id */, {{"highway", "secondary"}} /* tags */, OsmElement::EntityType::Way,
{20, 21, 22, 23}));
AddNode(MakeOsmElement(11 /* id */, {{"barrier", "gate"}, {"locked", "yes"}}, OsmElement::EntityType::Node));
AddNode(MakeOsmElement(21 /* id */, {{"barrier", "gate"}, {"locked", "yes"}, {"access", "permissive"}},
OsmElement::EntityType::Node));
Finish();
auto const privateSure = make_pair(RoadAccess::Type::Private, RoadAccess::Confidence::Sure);
auto const yesSure = make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure);
for (VehicleType t : {VehicleType::Car, VehicleType::Bicycle, VehicleType::Pedestrian})
{
auto const & vehicle = Get(t);
TEST_EQUAL(vehicle.GetAccessWithoutConditional({1, 1}), privateSure, ());
TEST_EQUAL(vehicle.GetAccessWithoutConditional({2, 1}), yesSure, (t));
}
}
UNIT_CLASS_TEST(TestAccessFixture, CycleBarrier)
{
CreateCollectors();
AddWay(MakeOsmElementWithNodes(1, {{"highway", "track"}}, OsmElement::EntityType::Way, {10, 11, 12}));
AddNode(MakeOsmElement(10, {{"barrier", "cycle_barrier"}}, OsmElement::EntityType::Node));
AddNode(MakeOsmElement(11, {{"barrier", "cycle_barrier"}, {"bicycle", "dismount"}}, OsmElement::EntityType::Node));
AddNode(MakeOsmElement(12, {{"barrier", "cycle_barrier"}, {"bicycle", "no"}}, OsmElement::EntityType::Node));
Finish();
auto const bicycle = Get(VehicleType::Bicycle);
TEST_EQUAL(bicycle.GetAccessWithoutConditional({1, 0}),
make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure), ());
TEST_EQUAL(bicycle.GetAccessWithoutConditional({1, 1}),
make_pair(RoadAccess::Type::Yes, RoadAccess::Confidence::Sure), ());
TEST_EQUAL(bicycle.GetAccessWithoutConditional({1, 2}), make_pair(RoadAccess::Type::No, RoadAccess::Confidence::Sure),
());
}
} // namespace road_access_test

View file

@ -0,0 +1,134 @@
#include "source_data.hpp"
char const node_xml_data[] =
"<?xml version='1.0' encoding='UTF-8'?>\
<osm version='0.6' upload='true' generator='JOSM'>\
<node id='-273105' action='modify' visible='true' lat='62.18269750679' lon='-134.28965517091'>\
<tag k='name' v='Продуктовый' />\
<tag k='opening_hours' v='24/7' />\
<tag k='shop' v='convenience' />\
</node>\
</osm>";
// binary data: node.o5m
unsigned char const 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};
static_assert(sizeof(node_o5m_data) == 92, "Size check failed");
char const node2_xml_data[] =
"<?xml version='1.0' encoding='UTF-8'?>\
<osm version='0.6' generator='CGImap 0.3.3 (31790 thorn-03.openstreetmap.org)' \
copyright='OpenStreetMap and contributors' attribution='https://www.openstreetmap.org/copyright' \
license='http://opendatacommons.org/licenses/odbl/1-0/'>\
<node id='513709898' visible='true' version='8' changeset='12059128' \
timestamp='2012-06-29T18:09:47Z' user='Xmypblu' uid='395071' lat='55.0927062' lon='38.7666704'>\
<tag k='amenity' v='cinema'/>\
<tag k='name' v='КТ Горизонт'/>\
</node>\
</osm>";
// binary data: node2.o5m
unsigned char const 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};
static_assert(sizeof(node2_o5m_data) == 93, "Size check failed");
char const way_xml_data[] =
"<?xml version='1.0' encoding='UTF-8'?>\
<osm version='0.6' upload='true' generator='JOSM'>\
<node id='-273141' action='modify' visible='true' lat='60.73662144558' lon='-135.06327391353' />\
<node id='-273139' action='modify' visible='true' lat='60.73186582163' lon='-135.05498459176' />\
<node id='-273137' action='modify' visible='true' lat='60.72928677326' lon='-135.05450504422' />\
<node id='-273135' action='modify' visible='true' lat='60.72600404848' lon='-135.05101119785' />\
<node id='-273133' action='modify' visible='true' lat='60.7207107856' lon='-135.0490245009' />\
<node id='-273131' action='modify' visible='true' lat='60.71776226098' lon='-135.04587318849' />\
<node id='-273129' action='modify' visible='true' lat='60.71528261036' lon='-135.04464006624' />\
<node id='-273126' action='modify' visible='true' lat='60.71283628103' lon='-135.0465582564' />\
<node id='-273125' action='modify' visible='true' lat='60.70931726156' lon='-135.05094269106' />\
<way id='-273127' action='modify' visible='true'>\
<nd ref='-273125' />\
<nd ref='-273126' />\
<nd ref='-273129' />\
<nd ref='-273131' />\
<nd ref='-273133' />\
<nd ref='-273135' />\
<nd ref='-273137' />\
<nd ref='-273139' />\
<nd ref='-273141' />\
<tag k='name' v='Yukon River' />\
<tag k='name:ru' v='Юкон' />\
<tag k='waterway' v='river' />\
</way>\
</osm>";
// binary data: way.o5m
unsigned char const 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};
static_assert(sizeof(way_o5m_data) == 175, "Size check failed");
char const relation_xml_data[] =
"<?xml version='1.0' encoding='UTF-8'?>\
<osm version='0.6' upload='true' generator='JOSM'>\
<node id='-273196' action='modify' visible='true' lat='60.71960512058' lon='-135.0538199763'>\
<tag k='name' v='Whitehorse' />\
<tag k='place' v='town' />\
</node>\
<node id='-273174' action='modify' visible='true' lat='60.71893500205' lon='-135.02422504237' />\
<node id='-273172' action='modify' visible='true' lat='60.70097061781' lon='-134.99915155667' />\
<node id='-273170' action='modify' visible='true' lat='60.68655163161' lon='-135.03409002034' />\
<node id='-273168' action='modify' visible='true' lat='60.68836274296' lon='-135.1128728306' />\
<node id='-273166' action='modify' visible='true' lat='60.70090356772' lon='-135.1290404334' />\
<node id='-273164' action='modify' visible='true' lat='60.72181641257' lon='-135.11246178985' />\
<node id='-273162' action='modify' visible='true' lat='60.74043883115' lon='-135.08451101891' />\
<node id='-273161' action='modify' visible='true' lat='60.73997005387' lon='-135.06327391353' />\
<way id='-273163' action='modify' visible='true'>\
<nd ref='-273161' />\
<nd ref='-273162' />\
<nd ref='-273164' />\
<nd ref='-273166' />\
<nd ref='-273168' />\
<nd ref='-273170' />\
<nd ref='-273172' />\
<nd ref='-273174' />\
<nd ref='-273161' />\
</way>\
<relation id='-273177' action='modify' visible='true'>\
<member type='way' ref='-273163' role='outer' />\
<member type='node' ref='-273196' role='' />\
<tag k='name' v='Whitehorse' />\
<tag k='place' v='town' />\
<tag k='type' v='multipolygon' />\
</relation>\
</osm>";
// binary data: relation.o5m
unsigned char const 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};
static_assert(sizeof(relation_o5m_data) == 224, "Size check failed");

View file

@ -0,0 +1,10 @@
#pragma once
extern char const node_xml_data[];
extern unsigned char const node_o5m_data[92];
extern char const node2_xml_data[];
extern unsigned char const node2_o5m_data[93];
extern char const way_xml_data[];
extern unsigned char const way_o5m_data[175];
extern char const relation_xml_data[];
extern unsigned char const relation_o5m_data[224];

View file

@ -0,0 +1,61 @@
#include "testing/testing.hpp"
#include "generator/generator_tests/source_data.hpp"
#include "generator/osm_element.hpp"
#include "generator/osm_source.hpp"
#include "coding/parse_xml.hpp"
#include <cstddef>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
using namespace generator;
UNIT_TEST(Source_To_Element_create_from_xml_test)
{
std::istringstream ss(way_xml_data);
SourceReader reader(ss);
std::vector<OsmElement> elements;
ProcessOsmElementsFromXML(reader, [&elements](OsmElement && e) { elements.push_back(std::move(e)); });
TEST_EQUAL(elements.size(), 10, (elements));
}
UNIT_TEST(Source_To_Element_create_from_o5m_test)
{
std::string src(std::begin(relation_o5m_data), std::end(relation_o5m_data));
std::istringstream ss(src);
SourceReader reader(ss);
std::vector<OsmElement> elements;
ProcessOsmElementsFromO5M(reader, [&elements](OsmElement && e) { elements.push_back(std::move(e)); });
TEST_EQUAL(elements.size(), 11, (elements));
std::cout << DebugPrint(elements);
}
UNIT_TEST(Source_To_Element_check_equivalence)
{
std::istringstream ss1(relation_xml_data);
SourceReader readerXML(ss1);
std::vector<OsmElement> elementsXML;
ProcessOsmElementsFromXML(readerXML, [&elementsXML](OsmElement && e) { elementsXML.push_back(std::move(e)); });
std::string src(std::begin(relation_o5m_data), std::end(relation_o5m_data));
std::istringstream ss2(src);
SourceReader readerO5M(ss2);
std::vector<OsmElement> elementsO5M;
ProcessOsmElementsFromO5M(readerO5M, [&elementsO5M](OsmElement && e) { elementsO5M.push_back(std::move(e)); });
TEST_EQUAL(elementsXML.size(), elementsO5M.size(), ());
for (size_t i = 0; i < elementsO5M.size(); ++i)
TEST_EQUAL(elementsXML[i], elementsO5M[i], ());
}

View file

@ -0,0 +1,404 @@
#include "testing/testing.hpp"
#include "generator/camera_info_collector.hpp"
#include "generator/maxspeeds_parser.hpp"
#include "generator/generator_tests_support/routing_helpers.hpp"
#include "generator/generator_tests_support/test_generator.hpp"
#include "routing/speed_camera_ser_des.hpp"
#include "routing_common/maxspeed_conversion.hpp"
#include "platform/measurement_utils.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "geometry/point2d.hpp"
#include "base/file_name_utils.hpp"
#include "base/math.hpp"
#include "defines.hpp"
#include <algorithm>
#include <cmath>
#include <map>
#include <string>
#include <utility>
#include <vector>
namespace speed_cameras_test
{
using namespace generator;
using namespace measurement_utils;
using namespace routing;
using std::string;
// Directory name for creating test mwm and temporary files.
string const kTestDir = "speed_camera_generation_test";
// Temporary mwm name for testing.
string const kTestMwm = "test";
string const kOsmFileName = "town" OSM_DATA_FILE_EXTENSION;
double constexpr kCoefEqualityEpsilon = 1e-5;
// Pair of featureId and segmentId.
using routing::SegmentCoord;
using CameraMap = SpeedCamerasMapT;
using CameraMapItem = std::pair<SegmentCoord, RouteSegment::SpeedCamera>;
CameraMap LoadSpeedCameraFromMwm(string const & mwmFullPath)
{
FilesContainerR const cont(mwmFullPath);
CHECK(cont.IsExist(CAMERAS_INFO_FILE_TAG), ("Cannot find", CAMERAS_INFO_FILE_TAG, "section"));
try
{
FilesContainerR::TReader const reader = cont.GetReader(CAMERAS_INFO_FILE_TAG);
ReaderSource<FilesContainerR::TReader> src(reader);
CameraMap result;
DeserializeSpeedCamsFromMwm(src, result);
return result;
}
catch (Reader::OpenException const & e)
{
TEST(false, ("Error while reading", CAMERAS_INFO_FILE_TAG, "section.", e.Msg()));
return {};
}
}
std::vector<CameraMapItem> UnpackMapToVector(CameraMap const & cameraMap)
{
std::vector<CameraMapItem> result;
for (auto const & mapIter : cameraMap)
for (auto const & vectorIter : mapIter.second)
result.push_back({mapIter.first, vectorIter});
sort(result.begin(), result.end());
return result;
}
void CheckCameraMapsEquality(CameraMap const & lhs, CameraMap const & rhs, double epsilon)
{
TEST_EQUAL(lhs.size(), rhs.size(), ());
auto const & vectorL = UnpackMapToVector(lhs);
auto const & vectorR = UnpackMapToVector(rhs);
for (size_t i = 0; i < vectorL.size(); ++i)
{
// Do not check feature id because of fake nodes. Data about them placed in ./data/
// It can differ on Jenknins and local computer.
TEST_EQUAL(vectorL[i].first.GetPointId(), vectorR[i].first.GetPointId(), ());
TEST_EQUAL(vectorL[i].second.m_maxSpeedKmPH, vectorR[i].second.m_maxSpeedKmPH, ());
TEST(AlmostEqualAbs(vectorL[i].second.m_coef, vectorR[i].second.m_coef, epsilon), ());
}
}
void TestSpeedCameraSectionBuilding(string const & osmContent, CameraMap const & answer, double epsilon)
{
// ScopedFile takes relative path only?! and makes WritableDir() + kOsmFileName.
// Replace WritebleDir here with temporary path.
Platform & platform = GetPlatform();
auto const tmpDir = platform.TmpDir();
platform.SetWritableDirForTests(tmpDir);
platform::tests_support::ScopedFile const osmScopedFile(kOsmFileName, osmContent);
generator::tests_support::TestRawGenerator generator;
generator.SetupTmpFolder(base::JoinPath(tmpDir, kTestDir));
generator.BuildFB(osmScopedFile.GetFullPath(), kTestMwm);
generator.BuildFeatures(kTestMwm);
auto const & genInfo = generator.GetGenInfo();
// Step 3. Build section into mwm.
string const camerasFilename = genInfo.GetIntermediateFileName(CAMERAS_TO_WAYS_FILENAME);
if (!answer.empty())
{
// Check that intermediate file is non-empty.
TEST_NOT_EQUAL(base::FileData(camerasFilename, base::FileData::Op::READ).Size(), 0,
("SpeedCam intermediate file is empty"));
}
else
{
// Check that intermediate file is empty.
TEST_EQUAL(base::FileData(camerasFilename, base::FileData::Op::READ).Size(), 0,
("SpeedCam intermediate file is non empty"));
}
string const osmToFeatureFilename = genInfo.GetTargetFileName(kTestMwm) + OSM2FEATURE_FILE_EXTENSION;
auto const mwmFullPath = generator.GetMwmPath(kTestMwm);
BuildCamerasInfo(mwmFullPath, camerasFilename, osmToFeatureFilename);
CameraMap const cameras = LoadSpeedCameraFromMwm(mwmFullPath);
CheckCameraMapsEquality(answer, cameras, epsilon);
}
// Next unit tests check building of speed camera section in mwm.
UNIT_TEST(SpeedCameraGenerationTest_Empty)
{
string const osmContent = R"(
<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="10000000" lat="55.7793084" lon="37.3699375" version="1">
</node>
<node id="10000001" lat="55.7793085" lon="37.3699399" version="1"></node>
<way id="2000000" version="1">
<nd ref="10000000"/>
<nd ref="10000001"/>
<tag k="highway" v="primary"/>
</way>
</osm>
)";
CameraMap const answer = {};
TestSpeedCameraSectionBuilding(osmContent, answer, kCoefEqualityEpsilon);
}
UNIT_TEST(SpeedCameraGenerationTest_CameraIsOnePointOfFeature_1)
{
string const osmContent = R"(
<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="10000000" lat="55.7793084" lon="37.3699375" version="1">
<tag k="highway" v="speed_camera"/>
<tag k="maxspeed" v="100"/>
</node>
<node id="10000001" lat="55.7793085" lon="37.3699399" version="1"></node>
<way id="2000000" version="1">
<nd ref="10000000"/>
<nd ref="10000001"/>
<tag k="highway" v="primary"/>
</way>
</osm>
)";
// Geometry:
// Feature number 0: <node-camera>----<node>
// Result:
// {(0, 0), (0, 100)} - featureId - 0, segmentId - 0,
// coef - position on segment (at the beginning of segment) - 0,
// maxSpeed - 100.
CameraMap const answer = {{SegmentCoord(0, 0), std::vector<RouteSegment::SpeedCamera>{{0, 100}}}};
TestSpeedCameraSectionBuilding(osmContent, answer, kCoefEqualityEpsilon);
}
UNIT_TEST(SpeedCameraGenerationTest_CameraIsOnePointOfFeature_2)
{
string const osmContent = R"(
<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="10000001" lat="55.7793000" lon="37.3699000" version="1"></node>
<node id="10000002" lat="55.7793100" lon="37.3699100" version="1">
<tag k="highway" v="speed_camera"/>
<tag k="maxspeed" v="100"/>
</node>
<node id="10000003" lat="55.7793200" lon="37.3699200" version="1"></node>
<way id="2000000" version="1">
<nd ref="10000001"/>
<nd ref="10000002"/>
<nd ref="10000003"/>
<tag k="highway" v="primary"/>
</way>
</osm>
)";
// Geometry:
// Feature number 0: <done>----<node-camera>----<node>
// ^____segmentID = 1____^
// Result:
// {(0, 1), (0, 100)} - featureId - 0, segmentId - 1,
// coef - position on segment (at the beginning of segment) - 0,
// maxSpeed - 100.
CameraMap const answer = {{SegmentCoord(0, 1), std::vector<RouteSegment::SpeedCamera>{{0, 100}}}};
TestSpeedCameraSectionBuilding(osmContent, answer, kCoefEqualityEpsilon);
}
UNIT_TEST(SpeedCameraGenerationTest_CameraIsOnePointOfFeature_3)
{
string const osmContent = R"(
<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="10000001" lat="55.7793000" lon="37.3699000" version="1"></node>
<node id="10000002" lat="55.7793100" lon="37.3699100" version="1"></node>
<node id="10000003" lat="55.7793200" lon="37.3699200" version="1">
<tag k="highway" v="speed_camera"/>
<tag k="maxspeed" v="100"/>
</node>
<way id="2000000" version="1">
<nd ref="10000001"/>
<nd ref="10000002"/>
<nd ref="10000003"/>
<tag k="highway" v="primary"/>
</way>
</osm>
)";
// Geometry:
// Feature number 0: <node>----<node>----<node-camera>
// ^____segmentID = 1____^
// Result:
// {(0, 1), (1, 100)} - featureId - 0, segmentId - 1,
// coef - position on segment (at the end of segment) - 1,
// maxSpeed - 100.
CameraMap const answer = {{SegmentCoord(0, 1), std::vector<RouteSegment::SpeedCamera>{{1, 100}}}};
TestSpeedCameraSectionBuilding(osmContent, answer, kCoefEqualityEpsilon);
}
UNIT_TEST(SpeedCameraGenerationTest_CameraIsOnePointOfFeature_4)
{
string const osmContent = R"(
<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="10000001" lat="55.7793100" lon="37.3699100" version="1"></node>
<node id="10000002" lat="55.7793200" lon="37.3699200" version="1"></node>
<node id="10000003" lat="55.7793300" lon="37.3699300" version="1">
<tag k="highway" v="speed_camera"/>
<tag k="maxspeed" v="100"/>
</node>
<node id="10000004" lat="55.7793400" lon="37.3699400" version="1"></node>
<node id="10000005" lat="55.7793500" lon="37.3699500" version="1">
<tag k="highway" v="speed_camera"/>
<tag k="maxspeed" v="100"/>
</node>
<node id="10000006" lat="55.7793600" lon="37.3699600" version="1"></node>
<way id="2000000" version="1">
<nd ref="10000001"/>
<nd ref="10000002"/>
<nd ref="10000003"/>
<tag k="highway" v="primary"/>
</way>
<way id="2000001" version="1">
<nd ref="10000004"/>
<nd ref="10000005"/>
<nd ref="10000006"/>
<tag k="highway" v="primary"/>
</way>
</osm>
)";
// Geometry:
// Feature number 0: <node>----<node>----<node-camera>
// ^____segmentID = 1____^
//
// Feature number 1: <node>----<node-camera>----<node>
// ^____segmentID = 1____^
// Result:
// {
// (0, 1), (1, 100) - featureId - 0, segmentId - 1,
// coef - position on segment (at the end of segment) - 1,
// maxSpeed - 100.
//
// (1, 1), (0, 100) - featureId - 1, segmentId - 1,
// coef - position on segment (at the beginning of segment) - 0,
// maxSpeed - 100.
CameraMap const answer = {{SegmentCoord(0, 1), std::vector<RouteSegment::SpeedCamera>{{1, 100}}},
{SegmentCoord(1, 1), std::vector<RouteSegment::SpeedCamera>{{0, 100}}}};
TestSpeedCameraSectionBuilding(osmContent, answer, kCoefEqualityEpsilon);
}
UNIT_TEST(SpeedCameraGenerationTest_CameraIsNearFeature_1)
{
string const osmContent = R"(
<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="10000001" lat="55.7793100" lon="37.3699100" version="1"></node>
<node id="10000002" lat="55.7793200" lon="37.3699200" version="1">
<tag k="highway" v="speed_camera"/>
<tag k="maxspeed" v="100"/>
</node>
<node id="10000003" lat="55.7793300" lon="37.3699300" version="1"></node>
<way id="2000000" version="1">
<nd ref="10000001"/>
<nd ref="10000003"/>
<tag k="highway" v="primary"/>
</way>
</osm>
)";
// Geometry:
// Feature number 0: <node>--------<node>
// ^___ somewhere here camera, but it is not the point of segment.
// We add it with coef close to 0.5 because of points' coords
// (lat, lon). Coef was calculated by this program and checked by
// eye.
// Result:
// {(0, 0), (0.5, 100)} - featureId - 0, segmentId - 0,
// coef - position on segment (at the half of segment) - 0.5,
// maxSpeed - 100.
auto epsilon = mercator::DistanceOnEarth({0, 0}, {kMwmPointAccuracy, kMwmPointAccuracy}) /
mercator::DistanceOnEarth(mercator::FromLatLon(55.7793100, 37.3699100),
mercator::FromLatLon(55.7793300, 37.3699300));
epsilon = math::Clamp(epsilon, 0.0, 1.0);
CameraMap const answer = {{SegmentCoord(0, 0), std::vector<RouteSegment::SpeedCamera>{{0.5, 100}}}};
TestSpeedCameraSectionBuilding(osmContent, answer, epsilon);
}
UNIT_TEST(SpeedCameraGenerationTest_CameraIsNearFeature_2)
{
string const osmContent = R"(
<osm version="0.6" generator="osmconvert 0.8.4" timestamp="2018-07-16T02:00:00Z">
<node id="10000001" lat="55.7793100" lon="37.3699100" version="1"></node>
<node id="10000002" lat="55.7793150" lon="37.3699150" version="1">
<tag k="highway" v="speed_camera"/>
<tag k="maxspeed" v="100"/>
</node>
<node id="10000003" lat="55.7793300" lon="37.3699300" version="1"></node>
<way id="2000000" version="1">
<nd ref="10000001"/>
<nd ref="10000003"/>
<tag k="highway" v="primary"/>
</way>
</osm>
)";
// Geometry:
// Feature number 0: <node>--------<node>
// ^___ somewhere here camera, but it is not the point of segment.
// Coef was calculated by this program and checked by eye.
// Result:
// {(0, 0), (0.25, 100)} - featureId - 0, segmentId - 0,
// coef - position on segment - 0.25,
// maxSpeed - 100.
auto epsilon = mercator::DistanceOnEarth({0, 0}, {kMwmPointAccuracy, kMwmPointAccuracy}) /
mercator::DistanceOnEarth(mercator::FromLatLon(55.7793100, 37.3699100),
mercator::FromLatLon(55.7793300, 37.3699300));
epsilon = math::Clamp(epsilon, 0.0, 1.0);
CameraMap const answer = {{SegmentCoord(0, 0), std::vector<RouteSegment::SpeedCamera>{{0.25, 100}}}};
TestSpeedCameraSectionBuilding(osmContent, answer, epsilon);
}
UNIT_TEST(RoadCategoryToSpeedTest)
{
SpeedInUnits speed;
TEST(RoadCategoryToSpeed("RU:rural", speed), ());
TEST_EQUAL(speed, SpeedInUnits(90, Units::Metric), ());
TEST(speed.IsNumeric(), ());
TEST(RoadCategoryToSpeed("DE:motorway", speed), ());
TEST_EQUAL(speed, SpeedInUnits(kNoneMaxSpeed, Units::Metric), ());
TEST(!speed.IsNumeric(), ());
TEST(RoadCategoryToSpeed("UK:motorway", speed), ());
TEST_EQUAL(speed, SpeedInUnits(70, Units::Imperial), ());
TEST(speed.IsNumeric(), ());
TEST(!RoadCategoryToSpeed("UNKNOWN:unknown", speed), ());
}
} // namespace speed_cameras_test

View file

@ -0,0 +1,219 @@
#include "testing/testing.hpp"
#include "generator/srtm_parser.hpp"
#include "coding/endianness.hpp"
#include <iostream>
namespace srtm_parser_test
{
using namespace generator;
using namespace geometry;
inline std::string GetBase(ms::LatLon const & coord)
{
return SrtmTile::GetBase(coord);
}
inline SrtmTile::LatLonKey GetKey(ms::LatLon const & coord)
{
return SrtmTile::GetKey(coord);
}
UNIT_TEST(SRTM_FilenameTest)
{
auto name = GetBase({56.4566, 37.3467});
TEST_EQUAL(name, "N56E037", ());
name = GetBase({34.077433, -118.304569});
TEST_EQUAL(name, "N34W119", ());
name = GetBase({1.0, 1.0});
TEST_EQUAL(name, "N01E001", ());
name = GetBase({0.1, 0.1});
TEST_EQUAL(name, "N00E000", ());
TEST_NOT_EQUAL(GetKey({0.1, 0.1}), GetKey({1.0, 1.0}), ());
name = GetBase({-0.1, -0.1});
TEST_EQUAL(name, "S01W001", ());
TEST_NOT_EQUAL(GetKey({0.1, 0.1}), GetKey({-0.1, -0.1}), ());
name = GetBase({-0.9, -0.9});
TEST_EQUAL(name, "S01W001", ());
TEST_EQUAL(GetKey({-0.9, -0.9}), GetKey({-0.1, -0.1}), ());
name = GetBase({-1.0, -1.0});
TEST_EQUAL(name, "S01W001", ());
TEST_EQUAL(GetKey({-0.9, -0.9}), GetKey({-1.0, -1.0}), ());
name = GetBase({-1.9, -1.1});
TEST_EQUAL(name, "S02W002", ());
TEST_NOT_EQUAL(GetKey({-1.1, -1.1}), GetKey({-1.0, -1.0}), ());
name = GetBase({-35.35, -12.1});
TEST_EQUAL(name, "S36W013", ());
name = GetBase({-34.622358, -58.383654});
TEST_EQUAL(name, "S35W059", ());
}
UNIT_TEST(SRTM_TileTest)
{
SrtmTile tile;
size_t sz;
Altitude * data = tile.DataForTests(sz);
// Fill 5 last rows:
// -4 -4 -4
// -2 -2 -2
// 0 0 0
// 2 2 2
// 4 4 4
size_t row = sz - 1;
for (Altitude a = 4; a >= -4; a -= 2)
{
for (size_t i = row * sz; i < (row + 1) * sz; ++i)
data[i] = ReverseByteOrder(a);
--row;
}
double const len = 1.0 / (sz - 1);
TEST_EQUAL(tile.GetHeight({0, 0}), 4, ());
TEST_ALMOST_EQUAL_ULPS(tile.GetTriangleHeight({0, 0}), 4.0, ());
TEST_ALMOST_EQUAL_ULPS(tile.GetBilinearHeight({0, 0}), 4.0, ());
TEST_EQUAL(tile.GetHeight({len, len}), 2, ());
TEST_ALMOST_EQUAL_ULPS(tile.GetTriangleHeight({len, len}), 2.0, ());
TEST_ALMOST_EQUAL_ULPS(tile.GetBilinearHeight({len, len}), 2.0, ());
double l = len / 2;
Altitude h = tile.GetHeight({l, l});
TEST(h == 4 || h == 2, (h));
TEST_ALMOST_EQUAL_ULPS(tile.GetTriangleHeight({l, l}), 3.0, ());
TEST_ALMOST_EQUAL_ULPS(tile.GetBilinearHeight({l, l}), 3.0, ());
l = 3 * len + len / 2;
h = tile.GetHeight({l, l});
TEST(h == -4 || h == -2, (h));
TEST_ALMOST_EQUAL_ULPS(tile.GetTriangleHeight({l, l}), -3.0, ());
TEST_ALMOST_EQUAL_ULPS(tile.GetBilinearHeight({l, l}), -3.0, ());
}
/*
UNIT_TEST(SRTM_SamplesTest)
{
SrtmTileManager manager("/Users/vng/SRTM");
std::initializer_list<ms::LatLon> arr[] = {
{{ 41.899802800880578957, 12.498703841110341273}, { 41.899748897914214751, 12.498642150302543996},
{ 41.899315676124750496, 12.498172763721441925}, { 41.899207869324136766, 12.498057428732948893 }},
{{ 41.900315874986389986, 12.499267105007675127}, { 41.900234022973513959, 12.499175909900486658},
{ 41.899802800880578957, 12.498703841110341273 }},
{{ 41.899317672545265623, 12.499556783583443575}, { 41.899704976945002954, 12.498910371206022774},
{ 41.899716955394147533, 12.49888623132471821}, { 41.899730930248637151, 12.498862091443413647},
{ 41.899744905100071435, 12.49882990493497914}, { 41.899760876355117034, 12.498797718426573056},
{ 41.899772854793766896, 12.498768214127181864}, { 41.899788826041820755, 12.498736027618775779},
{ 41.899802800880578957, 12.498703841110341273 }},
{{ 41.900297907480371862, 12.497869674100513748}, { 41.900259976062137923, 12.497931364908311025},
{ 41.899866685818835776, 12.498593870539906447}, { 41.899834743357700972, 12.498647514720602203},
{ 41.899818772121129484, 12.498674336810950081}, { 41.899802800880578957, 12.498703841110341273 }},
{{ 41.899728933841061007, 12.497309092412223208}, { 41.900224041013551357, 12.497850898637267392},
{ 41.90023202658165502, 12.49786430968242712}, { 41.900224041013551357, 12.497888449563731683},
{ 41.899826757739916161, 12.498548272986312213}, { 41.899808790096251698, 12.498556319613442156},
{ 41.899802800880578957, 12.498550955195355527}, { 41.89979281885320006, 12.498545590777297321},
{ 41.899307690442057606, 12.498009148970311344}, { 41.899301701179375357, 12.497995737925151616},
{ 41.899309686862821422, 12.497976962461905259}, { 41.899716955394147533, 12.497311774621238101},
{ 41.899728933841061007, 12.497309092412223208 }},
{{ 41.899802800880578957, 12.498550955195355527}, { 41.899748897914214751, 12.498642150302543996},
{ 41.899681020039992063, 12.498754803082022136 }},
{{ 41.899912603078725226, 12.498650196929645517}, { 41.899866685818835776, 12.498593870539906447},
{ 41.899826757739916161, 12.498548272986312213 }},
{{ 41.899994455503602353, 12.498516086477906128}, { 41.899912603078725226, 12.498650196929645517},
{ 41.899912603078725226, 12.498685065647094916}, { 41.900285929140210328, 12.499090079211356397},
{ 41.90030589303923847, 12.499111536883646068}, { 41.90070516970908443, 12.498435620206862495},
{ 41.900711158840110215, 12.49842489137071766}, { 41.900715151593857399, 12.498408798116514618},
{ 41.900713155217019334, 12.498398069280369782}, { 41.90056342677709722, 12.498237136738282516},
{ 41.900327853320931126, 12.497979644670920152}, { 41.900317871375655443, 12.497976962461905259},
{ 41.900307889428788144, 12.497982326879963466}, { 41.899994455503602353, 12.498516086477906128 }},
{{ 44.759886801735603967, 34.316046940654871378 }, { 44.759500178870737841, 34.315553414192436321 },
{ 44.759132599068138347, 34.315443443622029918 }, { 44.758765016927078761, 34.315430032576841768 },
{ 44.758071746835689453, 34.315253006780551459 }, { 44.758037464032938146, 34.315255688989566352 },
{ 44.757483222565575431, 34.315306650961247215 }, { 44.756708037437867631, 34.315676795808059296 },
{ 44.756323297960172169, 34.315652655926726311 }, { 44.755963316624225001, 34.315430032576841768 },
{ 44.755833798981250027, 34.315153765046261469 }, { 44.755789991477485046, 34.314949917159594861 }},
{{ 44.759886801735603967, 34.316046940654871378 }, { 44.760006787615907342, 34.315175222718522718 },
{ 44.760048687388419353, 34.315011607967392138 }, { 44.760260090322724125, 34.314772891363304552 },
{ 44.760437211104594724, 34.314665603001913041 }, { 44.760572431981174191, 34.314383971053246114 },
{ 44.760701939002856875, 34.314300822573159166 }, { 44.761223773178279828, 34.314088928059419459 },
{ 44.761292334982400121, 34.314091610268434351 }, { 44.761376132632506142, 34.314011143997390718 },
{ 44.761667518969709079, 34.313895809008897686 }, { 44.761739889204726239, 34.31379924948365101 },
{ 44.761739889204726239, 34.313705372167419227 }, { 44.76183130410882427, 34.313568579506636524 },
{ 44.761930336758403826, 34.313549804043390168 }, { 44.761981757490261202, 34.313442515681998657 },
{ 44.762050318394869919, 34.313396918128404423 }, { 44.762176013175370315, 34.313391553710346216 },
{ 44.762316943361625476, 34.313359367201911709 }, { 44.762610229403847484, 34.313332545111563832 },
{ 44.762627369451166714, 34.313343273947708667 }, { 44.762663553978839559, 34.313313769648317475 },
{ 44.762673076219179791, 34.313278900930868076 }, { 44.762678789562635018, 34.313233303377273842 },
{ 44.762692120695184883, 34.31320379907788265 }, { 44.762720687397411723, 34.313182341405621401 },
{ 44.762734018520298207, 34.313107239552635974 }, { 44.762747349640086725, 34.313091146298432932 },
{ 44.7628063874193316, 34.313101875134577767 }, { 44.76292065391750441, 34.313123332806839016 },
{ 44.762977787081830172, 34.313056277580983533 }, { 44.763063486722373341, 34.313107239552635974 },
{ 44.763282496337311045, 34.313088464089389618 }},
{{ 44.756521381971580809, 34.332137512655094724 }, { 44.756469956380023234, 34.331619846311355104 },
{ 44.756412816780184016, 34.331308710063325407 }, { 44.756359486436011252, 34.331040489159818208 },
{ 44.75633282124546497, 34.330965387306861203 }, { 44.756294728094793811, 34.330860781154484584 },
{ 44.756264253556160781, 34.330694484194339111 }, { 44.756294728094793811, 34.330608653505208849 },
{ 44.756323297960172169, 34.330533551652223423 }, { 44.756433767973362592, 34.330359208064976428 },
{ 44.756555665673857902, 34.330109762624715586 }, { 44.756641374840555159, 34.329940783455526798 },
{ 44.756755653531747896, 34.329755711032106547 }, { 44.757010875126020721, 34.329463350247323206 },
{ 44.757180387352946127, 34.329117345281815687 }, { 44.757266095593031707, 34.32893495506743875 },
{ 44.757448939413642108, 34.328564810220626669 }, { 44.757591785745901802, 34.328301953735206098 },
{ 44.757746059388104243, 34.327797698436654628 }, { 44.757807006886359602, 34.327470468934393466 },
{ 44.75787366813893442, 34.327239798957407402 }, { 44.757986039790637278, 34.327089595251464971 },
{ 44.758026036427494887, 34.326995717935233188 }, { 44.758113648011466523, 34.326907205037088033 },
{ 44.75817078592944398, 34.326783823421465058 }, { 44.758237446762365153, 34.326400267529493249 },
{ 44.758264111073970071, 34.3263519877668557 }, { 44.758357436067782942, 34.326177644179608706 },
{ 44.758412669156371066, 34.325909423276101506 }, { 44.758505993910240761, 34.325775312824362118 },
{ 44.758526944344453113, 34.325598287028071809 }, { 44.758479329710219474, 34.325461494367289106 },
{ 44.758391718680712756, 34.325370299260100637 }, { 44.758429810449001707, 34.32524423543546277 },
{ 44.75854789477109108, 34.325085985102390396 }, { 44.758637410144764601, 34.324914323724158294 },
{ 44.758685024648755757, 34.324734615718824671 }, { 44.758707879596705936, 34.324613916312273432 },
{ 44.758766921503791991, 34.324568318758679197 }, { 44.758749780311177346, 34.32444225493404133 },
{ 44.758799299298182461, 34.324206220538968637 }, { 44.75882786792522694, 34.323988961607142301 },
{ 44.758825963350531651, 34.323667096522939346 }, { 44.758843104520543932, 34.323423015500765132 },
{ 44.758862150259034252, 34.323181616687634232 }, { 44.758915478293424428, 34.322983133219054253 },
{ 44.758944046863021526, 34.322875844857662742 }, { 44.759084984933011242, 34.322862433812474592 },
{ 44.758966901708539865, 34.32266126813487972 }, { 44.758865959405987667, 34.32256202640058973 },
{ 44.758753589465527511, 34.322331356423575244 }, { 44.75867359717138072, 34.322089957610444344 },
{ 44.758690738386590624, 34.321875380887661322 }, { 44.758690738386590624, 34.321754681481081661 },
{ 44.758698356702829813, 34.321642028701631943 }, { 44.758643123887310367, 34.321379172216211373 },
{ 44.758641219306547043, 34.321231650719283834 }, { 44.758665978851873035, 34.321027802832645648 },
{ 44.758732639113461005, 34.320837365991167189 }, { 44.75867359717138072, 34.320502089861804507 },
{ 44.75867931091034535, 34.320392119291369681 }, { 44.758808822175438991, 34.320268737675775128 },
{ 44.758793585571083895, 34.320099758506557919 }, { 44.758978329127899087, 34.319783257840441593 },
{ 44.759109744288181787, 34.319584774371861613 }, { 44.75926782299799811, 34.31950430810081798 },
{ 44.759357337256332698, 34.319305824632238 }, { 44.759387810163708821, 34.319016146056469552 },
{ 44.759481133342561066, 34.318686234345193498 }, { 44.75951351073673834, 34.318530666221164438 },
{ 44.759524938047988485, 34.318332182752584458 }, { 44.759637306488322395, 34.317903029306989993 },
{ 44.759671588341880977, 34.317436324934931235 }, { 44.759871565415501493, 34.317044722415829483 },
{ 44.759902038051663453, 34.316647755478669524 }, { 44.759915369824923914, 34.316167640061422617 },
{ 44.759886801735603967, 34.316046940654871378 }},
};
using namespace std;
for (auto const & points : arr)
{
for (auto const & p : points)
cout << manager.GetTriangleHeight(p) << ", ";
cout << endl;
}
}
*/
} // namespace srtm_parser_test

View file

@ -0,0 +1,86 @@
#include "testing/testing.hpp"
#include "generator/tag_admixer.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include <algorithm>
#include <map>
#include <set>
#include <sstream>
#include <string>
using platform::tests_support::ScopedFile;
namespace
{
void TestReplacer(std::string const & source,
std::map<TagReplacer::Tag, std::vector<TagReplacer::Tag>> const & expected)
{
auto const filename = "test.txt";
ScopedFile sf(filename, source);
TagReplacer replacer(sf.GetFullPath());
for (auto const & r : expected)
{
auto const * tags = replacer.GetTagsForTests(r.first);
TEST(tags, ());
TEST_EQUAL(*tags, r.second, ());
}
}
} // namespace
UNIT_TEST(WaysParserTests)
{
std::map<uint64_t, std::string> ways;
WaysParserHelper parser(ways);
std::istringstream stream("140247102;world_level\n86398306;another_level\n294584441;world_level");
parser.ParseStream(stream);
TEST(ways.find(140247102) != ways.end(), ());
TEST_EQUAL(ways[140247102], std::string("world_level"), ());
TEST(ways.find(86398306) != ways.end(), ());
TEST_EQUAL(ways[86398306], std::string("another_level"), ());
TEST(ways.find(294584441) != ways.end(), ());
TEST_EQUAL(ways[294584441], std::string("world_level"), ());
TEST(ways.find(140247101) == ways.end(), ());
}
UNIT_TEST(CapitalsParserTests)
{
std::set<uint64_t> capitals;
CapitalsParserHelper parser(capitals);
std::istringstream stream(
"-21.1343401;-175.201808;1082208696;t\n-16.6934156;-179.87995;242715809;f\n19.0534159;169.919199;448768937;t");
parser.ParseStream(stream);
TEST(capitals.find(1082208696) != capitals.end(), ());
TEST(capitals.find(242715809) != capitals.end(), ());
TEST(capitals.find(448768937) != capitals.end(), ());
TEST(capitals.find(140247101) == capitals.end(), ());
}
UNIT_TEST(TagsReplacer_Smoke)
{
{
std::string const source = "";
TestReplacer(source, {});
}
{
std::string const source = "aerodrome:type=international : aerodrome=international";
TestReplacer(source, {{{"aerodrome:type", "international"}, {{"aerodrome", "international"}}}});
}
{
std::string const source = " aerodrome:type = international : aerodrome = international ";
TestReplacer(source, {{{"aerodrome:type", "international"}, {{"aerodrome", "international"}}}});
}
{
std::string const source =
"natural = forest : natural = wood\n"
"# TODO\n"
"cliff=yes : natural=cliff | u\n"
"\n"
"office=travel_agent : shop=travel_agency";
TestReplacer(source, {{{"cliff", "yes"}, {{"natural", "cliff"}}},
{{"natural", "forest"}, {{"natural", "wood"}}},
{{"office", "travel_agent"}, {{"shop", "travel_agency"}}}});
}
}

View file

@ -0,0 +1,71 @@
#include "testing/testing.hpp"
#include "generator/tesselator.hpp"
#include "base/logging.hpp"
namespace tesselator_test
{
typedef m2::PointD P;
class DoDump
{
size_t & m_count;
public:
explicit DoDump(size_t & count) : m_count(count) { m_count = 0; }
void operator()(P const & p1, P const & p2, P const & p3)
{
++m_count;
LOG(LINFO, (p1, p2, p3));
}
};
size_t RunTest(std::list<std::vector<P>> const & l)
{
tesselator::TrianglesInfo info;
int const trianglesCount = tesselator::TesselateInterior(l, info);
size_t count;
info.ForEachTriangle(DoDump(count));
TEST_EQUAL(count, static_cast<size_t>(trianglesCount), ());
return count;
}
size_t RunTess(P const * arr, size_t count)
{
std::list<std::vector<P>> l;
l.emplace_back();
l.back().assign(arr, arr + count);
return RunTest(l);
}
UNIT_TEST(Tesselator_SelfISect)
{
P arr[] = {P(0, 0), P(0, 4), P(4, 4), P(1, 1), P(1, 3), P(4, 0), P(0, 0)};
TEST_EQUAL(6, RunTess(arr, ARRAY_SIZE(arr)), ());
}
UNIT_TEST(Tesselator_Odd)
{
P arr[] = {P(-100, -100), P(100, 100), P(100, -100)};
size_t const count = ARRAY_SIZE(arr);
std::list<std::vector<P>> l;
l.emplace_back();
l.back().assign(arr, arr + count);
l.emplace_back();
l.back().assign(arr, arr + count);
TEST_EQUAL(0, RunTest(l), ());
P arr1[] = {P(-100, -100), P(100, -100), P(100, 100), P(-100, 100)};
l.emplace_back();
l.back().assign(arr1, arr1 + ARRAY_SIZE(arr1));
TEST_EQUAL(2, RunTest(l), ());
}
} // namespace tesselator_test

View file

@ -0,0 +1,102 @@
#include "testing/testing.hpp"
#include "generator/tesselator.hpp"
#include "coding/geometry_coding.hpp"
#include "coding/point_coding.hpp"
#include "coding/reader.hpp"
#include "coding/writer.hpp"
#include "geometry/mercator.hpp"
namespace
{
using P = m2::PointD;
bool IsEqual(P const & p1, P const & p2)
{
return p1.EqualDxDy(p2, kMwmPointAccuracy);
}
bool FindTriangle(serial::OutPointsT const & test, P arr[])
{
size_t const count = test.size();
for (size_t i = 0; i < count; i += 3)
{
for (int base = 0; base < 3; ++base)
{
if (IsEqual(test[i], arr[base]) && IsEqual(test[i + 1], arr[(base + 1) % 3]) &&
IsEqual(test[i + 2], arr[(base + 2) % 3]))
{
return true;
}
}
}
return false;
}
void CompareTriangles(serial::OutPointsT const & test, P arrP[], int arrT[][3], size_t count)
{
TEST_EQUAL(test.size(), 3 * count, (test));
for (size_t i = 0; i < count; ++i)
{
P trg[] = {arrP[arrT[i][0]], arrP[arrT[i][1]], arrP[arrT[i][2]]};
TEST(FindTriangle(test, trg), ("Triangles:", test, " Etalon:", trg[0], trg[1], trg[2]));
}
}
void TestTrianglesCoding(P arrP[], size_t countP, int arrT[][3], size_t countT)
{
tesselator::TrianglesInfo info;
info.AssignPoints(arrP, arrP + countP);
info.Reserve(countT);
for (size_t i = 0; i < countT; ++i)
info.Add(arrT[i][0], arrT[i][1], arrT[i][2]);
serial::GeometryCodingParams cp;
serial::TrianglesChainSaver saver(cp);
tesselator::PointsInfo points;
m2::PointU (*D2U)(m2::PointD const &, uint8_t) = &PointDToPointU;
info.GetPointsInfo(saver.GetBasePoint(), saver.GetMaxPoint(),
std::bind(D2U, std::placeholders::_1, cp.GetCoordBits()), points);
info.ProcessPortions(points, saver);
std::vector<char> buffer;
MemWriter<std::vector<char>> writer(buffer);
saver.Save(writer);
TEST(!buffer.empty(), ());
MemReader reader(&buffer[0], buffer.size());
ReaderSource<MemReader> src(reader);
serial::OutPointsT triangles;
serial::LoadOuterTriangles(src, cp, triangles);
CompareTriangles(triangles, arrP, arrT, countT);
}
} // namespace
UNIT_TEST(TrianglesCoding_Smoke)
{
P arrP[] = {P(0, 0), P(0, 1), P(1, 0), P(1, 1), P(0, -1), P(-1, 0)};
int arrT[][3] = {{0, 1, 2}, {1, 3, 2}, {4, 0, 2}, {1, 0, 5}, {4, 5, 0}};
TestTrianglesCoding(arrP, ARRAY_SIZE(arrP), arrT, ARRAY_SIZE(arrT));
}
UNIT_TEST(TrianglesCoding_Rect)
{
P arrP[] = {P(-16.874999848078005, -44.999999874271452), P(-16.874999848078005, -39.374999869032763),
P(-11.249999842839316, -39.374999869032763), P(-11.249999842839316, -44.999999874271452)};
int arrT[][3] = {{2, 0, 1}, {0, 2, 3}};
TestTrianglesCoding(arrP, ARRAY_SIZE(arrP), arrT, ARRAY_SIZE(arrT));
}

View file

@ -0,0 +1,39 @@
#pragma once
#include "generator/osm_element.hpp"
#include "indexer/classificator.hpp"
#include "indexer/feature_data.hpp"
#include "base/stl_helpers.hpp"
#include <string>
#include <utility>
#include <vector>
namespace tests
{
template <size_t N>
inline void AddTypes(FeatureParams & params, base::StringIL (&arr)[N])
{
Classificator const & c = classif();
for (auto const & e : arr)
params.AddType(c.GetTypeByPath(e));
}
inline void FillXmlElement(std::vector<OsmElement::Tag> const & tags, OsmElement * p)
{
for (auto const & t : tags)
p->AddTag(t);
}
inline uint32_t GetType(std::vector<std::string> const & path)
{
return classif().GetTypeByPath(path);
}
inline uint32_t GetType(base::StringIL const & lst)
{
return classif().GetTypeByPath(lst);
}
} // namespace tests