#include "testing/testing.hpp" #include "generator/descriptions_section_builder.hpp" #include "generator/generator_tests_support/test_generator.hpp" #include "search/cities_boundaries_table.hpp" #include "search/house_to_street_table.hpp" #include "routing/index_graph_loader.hpp" #include "routing/maxspeeds.hpp" #include "indexer/classificator.hpp" #include "indexer/feature_algo.hpp" #include "indexer/ftypes_matcher.hpp" namespace raw_generator_tests { using TestRawGenerator = generator::tests_support::TestRawGenerator; uint32_t GetFeatureType(FeatureType & ft) { uint32_t res = 0; ft.ForEachType([&res](uint32_t t) { TEST_EQUAL(res, 0, ()); res = t; }); return res; } // https://github.com/organicmaps/organicmaps/issues/2035 UNIT_CLASS_TEST(TestRawGenerator, Towns) { uint32_t const cityType = classif().GetTypeByPath({"place", "city"}); uint32_t const townType = classif().GetTypeByPath({"place", "town"}); uint32_t const villageType = classif().GetTypeByPath({"place", "village"}); std::string const mwmName = "Towns"; std::string const mwmWorld = WORLD_FILE_NAME; BuildFB("./data/test_data/osm/towns.osm", mwmName, true /* makeWorld */); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { ++count; // LOG(LINFO, (fb)); TEST(!fb.HasType(cityType), ()); bool const isTown = (fb.GetName() == "El Dorado"); TEST_EQUAL(isTown, fb.HasType(townType), ()); TEST_NOT_EQUAL(isTown, fb.HasType(villageType), ()); TEST(fb.GetRank() > 0, ()); }); TEST_EQUAL(count, 4, ()); count = 0; ForEachFB(mwmWorld, [&](feature::FeatureBuilder const & fb) { ++count; TEST(!fb.HasType(villageType), ()); bool const isTown = (fb.GetName() == "El Dorado"); TEST(fb.HasType(isTown ? townType : cityType), ()); }); TEST_EQUAL(count, 1, ()); // Prepare features data source. FrozenDataSource dataSource; std::vector mwmIDs; for (auto const & name : {mwmName, mwmWorld}) { BuildFeatures(name); BuildSearch(name); auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(name))); TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); mwmIDs.push_back(std::move(res.first)); } // Load boundaries. search::CitiesBoundariesTable table(dataSource); TEST(table.Load(), ()); TEST_EQUAL(table.GetSize(), 1, ()); // Iterate for features in World. count = 0; FeaturesLoaderGuard guard(dataSource, mwmIDs[1]); size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); std::string_view const name = ft->GetName(StringUtf8Multilang::kDefaultCode); if (!name.empty()) { TEST_EQUAL(ft->GetGeomType(), feature::GeomType::Point, ()); search::CitiesBoundariesTable::Boundaries boundary; TEST(table.Get(id, boundary), ()); TEST(boundary.HasPoint(ft->GetCenter()), ()); bool const isTown = (name == "El Dorado"); TEST_EQUAL(GetFeatureType(*ft), isTown ? townType : cityType, ()); ++count; } } TEST_EQUAL(count, 1, ()); } // https://github.com/organicmaps/organicmaps/issues/2475 UNIT_CLASS_TEST(TestRawGenerator, HighwayLinks) { std::string const mwmName = "Highways"; BuildFB("./data/test_data/osm/highway_links.osm", mwmName); BuildFeatures(mwmName); BuildRouting(mwmName, "Spain"); auto const fid2osm = LoadFID2OsmID(mwmName); using namespace routing; MaxspeedType from120 = 104; // like SpeedMacro::Speed104KmPH std::unordered_map osmID2Speed = { {23011515, from120}, {23011492, from120}, {10689329, from120}, {371581901, from120}, {1017695671, from120}, {577365212, from120}, {23011612, from120}, {1017695670, from120}, {304871606, from120}, {1017695669, from120}, {577365213, from120}, {369541035, from120}, {1014336646, from120}, {466365947, from120}, {23011511, from120}}; /// @todo Actually, better to assign speed for this way too. std::unordered_set osmNoSpeed = {23691193, 1017695668}; FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName))); CHECK_EQUAL(res.second, MwmSet::RegResult::Success, ()); FeaturesLoaderGuard guard(dataSource, res.first); auto const speeds = routing::LoadMaxspeeds(guard.GetHandle()); CHECK(speeds, ()); size_t speedChecked = 0, noSpeed = 0; uint32_t const count = guard.GetNumFeatures(); for (uint32_t id = 0; id < count; ++id) { auto const iOsmID = fid2osm.find(id); if (iOsmID == fid2osm.end()) continue; auto const osmID = iOsmID->second.GetSerialId(); auto const iSpeed = osmID2Speed.find(osmID); if (iSpeed != osmID2Speed.end()) { ++speedChecked; auto const speed = speeds->GetMaxspeed(id); TEST(speed.IsValid(), ()); TEST_EQUAL(speed.GetForward(), iSpeed->second, ()); } auto const iNoSpeed = osmNoSpeed.find(osmID); if (iNoSpeed != osmNoSpeed.end()) { ++noSpeed; TEST(!speeds->GetMaxspeed(id).IsValid(), ()); } } TEST_EQUAL(speedChecked, osmID2Speed.size(), ()); TEST_EQUAL(noSpeed, osmNoSpeed.size(), ()); } UNIT_CLASS_TEST(TestRawGenerator, Building3D) { auto const & buildingChecker = ftypes::IsBuildingChecker::Instance(); auto const & buildingPartChecker = ftypes::IsBuildingPartChecker::Instance(); auto const & buildingHasPartsChecker = ftypes::IsBuildingHasPartsChecker::Instance(); std::string const mwmName = "Building3D"; BuildFB("./data/test_data/osm/building3D.osm", mwmName); size_t buildings = 0, buildingParts = 0, buildingHasParts = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { auto const & types = fb.GetTypes(); if (buildingChecker(types)) ++buildings; if (buildingPartChecker(types)) ++buildingParts; if (buildingHasPartsChecker(types)) ++buildingHasParts; }); TEST_EQUAL(buildings, 1, ()); TEST_GREATER(buildingParts, 0, ()); TEST_EQUAL(buildingHasParts, 1, ()); } // https://www.openstreetmap.org/relation/13430355 UNIT_CLASS_TEST(TestRawGenerator, BuildingRelation) { auto const & buildingChecker = ftypes::IsBuildingChecker::Instance(); auto const & buildingPartChecker = ftypes::IsBuildingPartChecker::Instance(); auto const & buildingHasPartsChecker = ftypes::IsBuildingHasPartsChecker::Instance(); std::string const mwmName = "Building"; BuildFB("./data/test_data/osm/building_relation.osm", mwmName); { size_t buildings = 0, buildingParts = 0, buildingHasParts = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { auto const & types = fb.GetTypes(); if (buildingChecker(types)) ++buildings; if (buildingPartChecker(types)) ++buildingParts; if (buildingHasPartsChecker(types)) ++buildingHasParts; }); /// @todo Should be 1, 3, 1 when will implement one FB with multiple polygons. TEST_EQUAL(buildings, 2, ()); TEST_EQUAL(buildingParts, 3, ()); TEST_EQUAL(buildingHasParts, 2, ()); } BuildFeatures(mwmName); size_t features = 0; double buildings = 0, buildingParts = 0; ForEachFeature(mwmName, [&](std::unique_ptr ft) { if (ft->GetGeomType() != feature::GeomType::Area) return; feature::TypesHolder types(*ft); if (buildingChecker(types)) buildings += feature::CalcArea(*ft); else if (buildingPartChecker(types)) buildingParts += feature::CalcArea(*ft); ++features; }); TEST_EQUAL(features, 5, ()); TEST_ALMOST_EQUAL_ABS(buildings, buildingParts, 1.0E-4, ()); } UNIT_CLASS_TEST(TestRawGenerator, AreaHighway) { std::string const mwmName = "AreaHighway"; BuildFB("./data/test_data/osm/highway_area.osm", mwmName); uint32_t const waterType = classif().GetTypeByPath({"natural", "water", "tunnel"}); uint32_t const pedestrianType = classif().GetTypeByPath({"highway", "pedestrian", "area"}); size_t waters = 0, pedestrians = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.HasType(waterType)) ++waters; if (fb.HasType(pedestrianType)) ++pedestrians; }); TEST_EQUAL(waters, 2, ()); TEST_EQUAL(pedestrians, 4, ()); } // place=region doesn't have drawing rules, but we keep it in // GetNondrawableStandaloneIndexScale for the search. UNIT_CLASS_TEST(TestRawGenerator, Place_Region) { uint32_t const regionType = classif().GetTypeByPath({"place", "region"}); std::string const mwmName = "Region"; std::string const worldMwmName = WORLD_FILE_NAME; BuildFB("./data/test_data/osm/place_region.osm", mwmName, true /* makeWorld */); size_t worldRegions = 0, countryRegions = 0; ForEachFB(worldMwmName, [&](feature::FeatureBuilder const & fb) { if (fb.HasType(regionType)) { TEST(!fb.GetName().empty(), ()); ++worldRegions; } }); TEST_EQUAL(worldRegions, 1, ()); worldRegions = 0; // Prepare features data source. for (auto const & name : {mwmName, worldMwmName}) { BuildFeatures(name); BuildSearch(name); ForEachFeature(name, [&](std::unique_ptr ft) { if (feature::TypesHolder(*ft).Has(regionType)) { TEST_EQUAL(ft->GetGeomType(), feature::GeomType::Point, ()); TEST(!ft->GetName(StringUtf8Multilang::kDefaultCode).empty(), ()); if (name == worldMwmName) ++worldRegions; else ++countryRegions; } }); } TEST_EQUAL(worldRegions, 1, ()); TEST_EQUAL(countryRegions, 0, ()); } UNIT_CLASS_TEST(TestRawGenerator, MiniRoundabout) { uint32_t const roadType = classif().GetTypeByPath({"highway", "secondary"}); std::string const mwmName = "MiniRoundabout"; BuildFB("./data/test_data/osm/mini_roundabout.osm", mwmName, false /* makeWorld */); size_t roadsCount = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.HasType(roadType)) ++roadsCount; }); // Splitted on 3 parts + 4 created roundabouts. TEST_EQUAL(roadsCount, 4 + 3, ()); // Prepare features data source. BuildFeatures(mwmName); BuildRouting(mwmName, "United Kingdom"); FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName))); TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); std::vector roads, rounds; FeaturesLoaderGuard guard(dataSource, res.first); size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); if (feature::TypesHolder(*ft).Has(roadType)) { TEST_EQUAL(ft->GetGeomType(), feature::GeomType::Line, ()); ft->ParseGeometry(FeatureType::BEST_GEOMETRY); size_t const ptsCount = ft->GetPointsCount(); TEST_GREATER(ptsCount, 1, ()); auto const firstPt = ft->GetPoint(0); auto const lastPt = ft->GetPoint(ptsCount - 1); LOG(LINFO, ("==", id, firstPt, lastPt)); if ((lastPt.x - firstPt.x) > 0.2) roads.push_back(id); if (fabs(lastPt.x - firstPt.x) < 0.1) rounds.push_back(id); } } TEST_EQUAL(roads, std::vector({1, 3, 5}), ()); TEST_EQUAL(rounds, std::vector({0, 2, 4, 6}), ()); using namespace routing; RoadAccess access; ReadRoadAccessFromMwm(*(guard.GetHandle().GetValue()), VehicleType::Car, access); LOG(LINFO, (access)); SpeedCamerasMapT camerasMap; ReadSpeedCamsFromMwm(*(guard.GetHandle().GetValue()), camerasMap); LOG(LINFO, (camerasMap)); } namespace { std::string_view GetPostcode(FeatureType & ft) { return ft.GetMetadata(feature::Metadata::FMD_POSTCODE); } } // namespace UNIT_CLASS_TEST(TestRawGenerator, Postcode_Relations) { std::string const mwmName = "Postcodes"; BuildFB("./data/test_data/osm/postcode_relations.osm", mwmName, false /* makeWorld */); BuildFeatures(mwmName); size_t count = 0; ForEachFeature(mwmName, [&count](std::unique_ptr ft) { auto const name = ft->GetName(StringUtf8Multilang::kDefaultCode); if (name == "Boulevard Malesherbes") { TEST_EQUAL(GetPostcode(*ft), "75017", ()); ++count; } else if (name == "Facebook France") { TEST_EQUAL(GetPostcode(*ft), "75002", ()); ++count; } }); TEST_EQUAL(count, 2, ()); } UNIT_CLASS_TEST(TestRawGenerator, Building_Address) { std::string const mwmName = "Address"; BuildFB("./data/test_data/osm/building_address.osm", mwmName, false /* makeWorld */); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (ftypes::IsBuildingChecker::Instance()(fb.GetTypes())) { auto const & params = fb.GetParams(); TEST_EQUAL(params.GetStreet(), "Airport Boulevard", ()); TEST_EQUAL(params.GetPostcode(), "819666", ()); ++count; } }); TEST_EQUAL(count, 1, ()); BuildFeatures(mwmName); BuildSearch(mwmName); FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName))); CHECK_EQUAL(res.second, MwmSet::RegResult::Success, ()); FeaturesLoaderGuard guard(dataSource, res.first); count = 0; size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); if (ftypes::IsBuildingChecker::Instance()(*ft)) { TEST_EQUAL(ft->GetHouseNumber(), "78", ()); TEST_EQUAL(GetPostcode(*ft), "819666", ()); ++count; auto value = guard.GetHandle().GetValue(); if (!value->m_house2street) value->m_house2street = search::LoadHouseToStreetTable(*value); auto res = value->m_house2street->Get(id); TEST(res, ()); auto street = guard.GetFeatureByIndex(res->m_streetId); TEST_EQUAL(street->GetName(StringUtf8Multilang::kDefaultCode), "Airport Boulevard", ()); } } TEST_EQUAL(count, 1, ()); } // https://github.com/organicmaps/organicmaps/issues/4974 UNIT_TEST(Relation_Wiki) { std::string const mwmName = "Relation"; std::string const arrFiles[] = { "./data/test_data/osm/village_relation.osm", "./data/test_data/osm/novenkoe_village.osm", "./data/test_data/osm/nikolaevka_village.osm", }; std::string const arrWiki[] = { "fr:Charmois-l'Orgueilleux", "ru:Новенькое (Локтевский район)", "ru:Николаевка (Локтевский район)", }; for (size_t i = 0; i < std::size(arrFiles); ++i) { TestRawGenerator generator; uint32_t const villageType = classif().GetTypeByPath({"place", "village"}); generator.BuildFB(arrFiles[i], mwmName); size_t count = 0; generator.ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { switch (fb.GetGeomType()) { case feature::GeomType::Point: { TEST(fb.HasType(villageType), ()); ++count; TEST_EQUAL(fb.GetMetadata().Get(feature::Metadata::FMD_WIKIPEDIA), arrWiki[i], ()); break; } case feature::GeomType::Line: { TEST(fb.GetMetadata().Get(feature::Metadata::FMD_WIKIPEDIA).empty(), ()); break; } default: TEST(false, ()); break; } }); TEST_EQUAL(count, 1, ()); } } UNIT_CLASS_TEST(TestRawGenerator, AssociatedStreet_Wiki) { uint32_t const roadType = classif().GetTypeByPath({"highway", "residential"}); std::string const mwmName = "Street"; BuildFB("./data/test_data/osm/associated_street.osm", mwmName, false /* makeWorld */); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.HasType(roadType)) { TEST_EQUAL(fb.GetMetadata().Get(feature::Metadata::FMD_WIKIPEDIA), "uk:Вулиця Боричів Тік", ()); ++count; } }); TEST_EQUAL(count, 5, ()); BuildFeatures(mwmName); generator::WikidataHelper wikidata(GetMwmPath(mwmName), GetGenInfo().GetIntermediateFileName(kWikidataFilename)); count = 0; ForEachFeature(mwmName, [&](std::unique_ptr ft) { if (feature::TypesHolder(*ft).Has(roadType)) { ++count; auto const data = wikidata.GetWikidataId(ft->GetID().m_index); TEST(data, ()); TEST_EQUAL(*data, "Q4471511", ()); } }); TEST_EQUAL(count, 5, ()); } UNIT_TEST(Place_CityRelations) { std::string const mwmName = "Cities"; std::string const worldMwmName = WORLD_FILE_NAME; std::string const arrFiles[] = { // 1 Relation with many polygons + 1 Node. "./data/test_data/osm/gorlovka_city.osm", // 2 Relations + 1 Node "./data/test_data/osm/tver_city.osm", // 1 Relation + 1 Node with _different_ names. "./data/test_data/osm/reykjavik_city.osm", "./data/test_data/osm/berlin_city.osm", // Relation boundary is place=suburb, but border_type=city "./data/test_data/osm/riviera_beach_city.osm", "./data/test_data/osm/hotchkiss_town.osm", "./data/test_data/osm/voronezh_city.osm", "./data/test_data/osm/minsk_city.osm", // 1 boundary-only Relation + 1 Node "./data/test_data/osm/kadikoy_town.osm", // 2 Relations + 1 Node "./data/test_data/osm/stolbtcy_town.osm", // 1 Way + 1 Relation + 1 Node "./data/test_data/osm/dmitrov_town.osm", "./data/test_data/osm/lesnoy_town.osm", "./data/test_data/osm/pushkino_city.osm", "./data/test_data/osm/korday_town.osm", "./data/test_data/osm/bad_neustadt_town.osm", /// @todo We don't store villages in World now, but for the future! // 1 Relation + 1 Node (not linked with each other) //"./data/test_data/osm/palm_beach_village.osm", }; ms::LatLon arrNotInBoundary[] = { {48.2071448, 37.9729054}, // gorlovka {56.9118261, 36.2258988}, // tver {64.0469397, -21.9772409}, // reykjavik {52.4013879, 13.0601531}, // berlin {26.7481191, -80.0836532}, // riviera beach {38.7981690, -107.7347750}, // hotchkiss {51.7505379, 39.5894547}, // voronezh {53.9170050, 27.8576710}, // minsk {41.0150982, 29.0213844}, // kadikoy {53.5086454, 26.6979711}, // stolbtcy {56.3752679, 37.3288391}, // dmitrov {54.0026933, 27.6356912}, // lesnoy {56.0807652, 37.9277319}, // pushkino {43.2347760, 74.7573240}, // korday {50.4006992, 10.2020744}, // bad_neustadt //{26.6757006, -80.0547346}, // palm beach }; size_t constexpr kManyBoundriesUpperIndex = 8; static_assert(std::size(arrFiles) == std::size(arrNotInBoundary)); for (size_t i = 0; i < std::size(arrFiles); ++i) { TestRawGenerator generator; generator.BuildFB(arrFiles[i], mwmName, true /* makeWorld */); auto const & checker = ftypes::IsCityTownOrVillageChecker::Instance(); // Check that we have only 1 city without duplicates. size_t count = 0; generator.ForEachFB(worldMwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Point) { ++count; TEST(checker(fb.GetTypes()), ()); TEST(fb.GetRank() > 0, ()); } }); TEST_EQUAL(count, 1, ()); count = 0; generator.ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (checker(fb.GetTypes())) { ++count; TEST(fb.GetRank() > 0, ()); } }); TEST_EQUAL(count, 1, ()); // Build boundaries table. generator.BuildFeatures(worldMwmName); generator.BuildSearch(worldMwmName); // Check that we have valid boundary in World. FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(generator.GetMwmPath(worldMwmName))); TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); search::CitiesBoundariesTable table(dataSource); TEST(table.Load(), ()); TEST_EQUAL(table.GetSize(), 1, ()); FeaturesLoaderGuard guard(dataSource, res.first); bool foundCity = false; size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); if (checker(*ft)) { TEST_EQUAL(ft->GetGeomType(), feature::GeomType::Point, ()); foundCity = true; search::CitiesBoundariesTable::Boundaries boundary; TEST(table.Get(ft->GetID(), boundary), ()); TEST(boundary.HasPoint(ft->GetCenter()), ()); TEST(!boundary.HasPoint(mercator::FromLatLon(arrNotInBoundary[i])), (i)); if (i < kManyBoundriesUpperIndex) TEST_GREATER(boundary.GetCount(), 1, (i)); else TEST_EQUAL(boundary.GetCount(), 1, (i)); } } TEST(foundCity, ()); } } UNIT_TEST(Place_CityRelations_IncludePoint) { std::string const mwmName = "Cities"; std::string const worldMwmName = WORLD_FILE_NAME; std::string const arrFiles[] = { "./data/test_data/osm/valentin_alsina_town.osm", }; ms::LatLon arrInBoundary[] = { {-34.6699107, -58.4302163}, // valentin_alsina }; for (size_t i = 0; i < std::size(arrFiles); ++i) { TestRawGenerator generator; generator.BuildFB(arrFiles[i], mwmName, true /* makeWorld */); auto const & checker = ftypes::IsCityTownOrVillageChecker::Instance(); // Check that we have only 1 city without duplicates. size_t count = 0; generator.ForEachFB(worldMwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Point) { ++count; TEST(checker(fb.GetTypes()), ()); TEST(fb.GetRank() > 0, ()); } }); TEST_EQUAL(count, 1, ()); // Build boundaries table. generator.BuildFeatures(worldMwmName); generator.BuildSearch(worldMwmName); // Check that we have valid boundary in World. FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(generator.GetMwmPath(worldMwmName))); TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); search::CitiesBoundariesTable table(dataSource); TEST(table.Load(), ()); TEST_EQUAL(table.GetSize(), 1, ()); FeaturesLoaderGuard guard(dataSource, res.first); bool foundCity = false; size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); if (checker(*ft)) { TEST_EQUAL(ft->GetGeomType(), feature::GeomType::Point, ()); foundCity = true; search::CitiesBoundariesTable::Boundaries boundary; TEST(table.Get(ft->GetID(), boundary), ()); TEST(boundary.HasPoint(ft->GetCenter()), ()); TEST(boundary.HasPoint(mercator::FromLatLon(arrInBoundary[i])), (i)); } } TEST(foundCity, ()); } } UNIT_CLASS_TEST(TestRawGenerator, Place_NoCityBoundaries) { std::string const mwmName = "Cities"; std::string const worldMwmName = WORLD_FILE_NAME; struct TestDataSample { std::string m_osmInput; size_t m_inWorld = 0; size_t m_inCountry = 0; }; TestDataSample const arrInput[] = { // Check that we have only 2 cities without duplicates (Pargas, Қордай). // Boundaries are removed because of "very big". {"./data/test_data/osm/no_boundary_towns.osm", 2, 2}, // 3 villages in country and 0 in World. {"./data/test_data/osm/us_villages_like_towns.osm", 0, 3}, }; for (size_t i = 0; i < std::size(arrInput); ++i) { TestRawGenerator generator; generator.BuildFB(arrInput[i].m_osmInput, mwmName, true /* makeWorld */); auto const & checker = ftypes::IsCityTownOrVillageChecker::Instance(); size_t count = 0; generator.ForEachFB(worldMwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Point) { ++count; TEST_GREATER(fb.GetRank(), 10, (i)); TEST(checker(fb.GetTypes()), (i)); } }); TEST_EQUAL(count, arrInput[i].m_inWorld, (i)); count = 0; generator.ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Point && checker(fb.GetTypes())) { ++count; TEST_GREATER(fb.GetRank(), 10, (i)); } }); TEST_EQUAL(count, arrInput[i].m_inCountry, (i)); // Build boundaries table. generator.BuildFeatures(worldMwmName); generator.BuildSearch(worldMwmName); // Check that we have NO boundaries in World. FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(generator.GetMwmPath(worldMwmName))); TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); search::CitiesBoundariesTable table(dataSource); TEST(table.Load(), ()); TEST_EQUAL(table.GetSize(), 0, (i)); } } UNIT_CLASS_TEST(TestRawGenerator, Place_2Villages) { std::string const mwmName = "Villages"; BuildFB("./data/test_data/osm/tarachevo_villages.osm", mwmName, false /* makeWorld */); auto const & checker = ftypes::IsCityTownOrVillageChecker::Instance(); // Check that we have 2 villages (Тарачево). size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Point) { ++count; TEST(checker(fb.GetTypes()), ()); TEST_EQUAL(fb.GetName(), "Тарачево", ()); } }); TEST_EQUAL(count, 2, ()); } UNIT_CLASS_TEST(TestRawGenerator, Relation_Fence) { std::string const mwmName = "Fences"; BuildFB("./data/test_data/osm/fence_relation.osm", mwmName); uint32_t const fenceType = classif().GetTypeByPath({"barrier", "fence"}); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Line) { ++count; TEST(fb.HasType(fenceType), ()); } }); TEST_EQUAL(count, 2, ()); } // https://www.openstreetmap.org/changeset/133837637 UNIT_CLASS_TEST(TestRawGenerator, Shuttle_Route) { std::string const mwmName = "Shuttle"; BuildFB("./data/test_data/osm/shuttle_route.osm", mwmName); uint32_t const railType = classif().GetTypeByPath({"railway", "rail"}); uint32_t const shuttleType = classif().GetTypeByPath({"route", "shuttle_train"}); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Line && fb.HasType(railType, 2)) { ++count; TEST(fb.HasType(shuttleType), (fb.GetMostGenericOsmId())); } }); TEST_GREATER(count, 30, ()); } // https://github.com/organicmaps/organicmaps/issues/4924 UNIT_TEST(MiniRoundabout_Connectivity) { std::string const mwmName = "MiniRoundabout"; std::string const arrFiles[] = { "./data/test_data/osm/mini_roundabout_1.osm", "./data/test_data/osm/mini_roundabout_2.osm", "./data/test_data/osm/mini_roundabout_3.osm", }; for (auto const & fileName : arrFiles) { TestRawGenerator generator; uint32_t const roundaboutType = classif().GetTypeByPath({"junction", "roundabout"}); uint32_t const tertiaryType = classif().GetTypeByPath({"highway", "tertiary"}); uint32_t const residentialType = classif().GetTypeByPath({"highway", "residential"}); generator.BuildFB(fileName, mwmName, false /* makeWorld */); generator.BuildFeatures(mwmName); FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(generator.GetMwmPath(mwmName))); TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); FeaturesLoaderGuard guard(dataSource, res.first); FeatureType::PointsBufferT roundabout; auto const IsPointInRoundabout = [&roundabout](m2::PointD const & pt) { for (auto const & p : roundabout) if (AlmostEqualAbs(p, pt, kMwmPointAccuracy)) return true; return false; }; size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); feature::TypesHolder types(*ft); if (types.Has(roundaboutType)) { TEST_EQUAL(ft->GetGeomType(), feature::GeomType::Line, ()); TEST(roundabout.empty(), ()); roundabout = ft->GetPoints(FeatureType::BEST_GEOMETRY); } } TEST(!roundabout.empty(), ()); size_t count = 0; for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); feature::TypesHolder types(*ft); if (types.Has(tertiaryType) || types.Has(residentialType)) { TEST_EQUAL(ft->GetGeomType(), feature::GeomType::Line, ()); auto const & pts = ft->GetPoints(FeatureType::BEST_GEOMETRY); TEST(IsPointInRoundabout(pts.front()) || IsPointInRoundabout(pts.back()), ()); ++count; } } TEST_GREATER(count, 1, ()); } } UNIT_CLASS_TEST(TestRawGenerator, Addr_Interpolation) { std::string const mwmName = "Address"; BuildFB("./data/test_data/osm/addr_interpol.osm", mwmName); uint32_t const addrType = classif().GetTypeByPath({"addr:interpolation", "even"}); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.GetGeomType() == feature::GeomType::Line && fb.HasType(addrType)) { ++count; auto const & params = fb.GetParams(); TEST_EQUAL(params.ref, "3602:3800", ()); TEST_EQUAL(params.GetStreet(), "Juncal", ()); } }); TEST_EQUAL(count, 1, ()); BuildFeatures(mwmName); BuildSearch(mwmName); FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName))); CHECK_EQUAL(res.second, MwmSet::RegResult::Success, ()); FeaturesLoaderGuard guard(dataSource, res.first); count = 0; size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); if (ftypes::IsAddressInterpolChecker::Instance()(*ft)) { ++count; auto value = guard.GetHandle().GetValue(); if (!value->m_house2street) value->m_house2street = search::LoadHouseToStreetTable(*value); auto res = value->m_house2street->Get(id); TEST(res, ()); auto street = guard.GetFeatureByIndex(res->m_streetId); TEST_EQUAL(street->GetName(StringUtf8Multilang::kDefaultCode), "Juncal", ()); } } TEST_EQUAL(count, 1, ()); } // https://github.com/organicmaps/organicmaps/issues/4994 UNIT_CLASS_TEST(TestRawGenerator, NamedAddress) { std::string const mwmName = "Address"; BuildFB("./data/test_data/osm/named_address.osm", mwmName); uint32_t const addrType = classif().GetTypeByPath({"building", "address"}); size_t withName = 0, withNumber = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { TEST_EQUAL(fb.GetGeomType(), feature::GeomType::Point, ()); TEST(fb.HasType(addrType), ()); auto const & params = fb.GetParams(); TEST(params.house.IsEmpty() != fb.GetName().empty(), ()); if (params.house.IsEmpty()) ++withName; else ++withNumber; TEST(params.GetStreet().empty(), ()); TEST_EQUAL(params.GetPostcode(), "LV-5695", ()); }); TEST_EQUAL(withName, 3, ()); TEST_EQUAL(withNumber, 0, ()); } // https://github.com/organicmaps/organicmaps/issues/5096 UNIT_CLASS_TEST(TestRawGenerator, Place_State) { uint32_t const stateType = classif().GetTypeByPath({"place", "state"}); std::string const mwmName = "State"; std::string const worldMwmName = WORLD_FILE_NAME; BuildFB("./data/test_data/osm/place_state.osm", mwmName, true /* makeWorld */); size_t states = 0; ForEachFB(worldMwmName, [&](feature::FeatureBuilder const & fb) { if (fb.HasType(stateType)) { TEST(!fb.GetName().empty(), ()); ++states; } }); TEST_EQUAL(states, 1, ()); states = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.HasType(stateType)) { TEST(!fb.GetName().empty(), ()); ++states; } }); TEST_EQUAL(states, 1, ()); } UNIT_CLASS_TEST(TestRawGenerator, CycleBarrier) { uint32_t const barrierType = classif().GetTypeByPath({"barrier", "cycle_barrier"}); std::string const mwmName = "CycleBarrier"; BuildFB("./data/test_data/osm/cycle_barrier.osm", mwmName, false /* makeWorld */); size_t barriersCount = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { if (fb.HasType(barrierType)) ++barriersCount; }); TEST_EQUAL(barriersCount, 1, ()); // Prepare features data source. BuildFeatures(mwmName); BuildRouting(mwmName, "Switzerland"); FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName))); TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); FeaturesLoaderGuard guard(dataSource, res.first); using namespace routing; RoadAccess carAcc, bicycleAcc; ReadRoadAccessFromMwm(*(guard.GetHandle().GetValue()), VehicleType::Car, carAcc); LOG(LINFO, ("Car access:", carAcc)); ReadRoadAccessFromMwm(*(guard.GetHandle().GetValue()), VehicleType::Bicycle, bicycleAcc); LOG(LINFO, ("Bicycle access:", bicycleAcc)); TEST_EQUAL(carAcc.GetPointToAccess().size(), 1, ()); TEST_EQUAL(carAcc, bicycleAcc, ()); } // https://github.com/organicmaps/organicmaps/issues/9029 UNIT_CLASS_TEST(TestRawGenerator, Addr_Street_Place) { std::string const mwmName = "Address"; struct TestData { std::string m_file; size_t m_addrCount; bool m_checkStreet, m_checkPlace; }; TestData const arrFiles[] = { {"./data/test_data/osm/addr_street_place.osm", 1, true, true}, {"./data/test_data/osm/addr_street_very_far.osm", 2, true, false}, {"./data/test_data/osm/zelenograd.osm", 1, false, true}, {"./data/test_data/osm/addr_area_street.osm", 1, true, false}, }; for (auto const & data : arrFiles) { TestRawGenerator generator; generator.BuildFB(data.m_file, mwmName); generator.BuildFeatures(mwmName); generator.BuildSearch(mwmName); FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(generator.GetMwmPath(mwmName))); CHECK_EQUAL(res.second, MwmSet::RegResult::Success, ()); FeaturesLoaderGuard guard(dataSource, res.first); auto value = guard.GetHandle().GetValue(); auto const house2street = search::LoadHouseToStreetTable(*value); auto const house2place = search::LoadHouseToPlaceTable(*value); size_t const numFeatures = guard.GetNumFeatures(); size_t count = 0; for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); if (ftypes::IsBuildingChecker::Instance()(*ft)) { TEST(!ft->GetHouseNumber().empty(), ()); ++count; if (data.m_checkStreet) TEST(house2street->Get(ft->GetID().m_index), ()); if (data.m_checkPlace) TEST(house2place->Get(ft->GetID().m_index), ()); } } TEST_EQUAL(count, data.m_addrCount, ()); } } UNIT_CLASS_TEST(TestRawGenerator, Area_Relation_Bad) { std::string const mwmName = "AreaRel"; BuildFB("./data/test_data/osm/area_relation_bad.osm", mwmName); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { TEST_EQUAL(fb.GetGeomType(), feature::GeomType::Area, ()); TEST(!fb.GetOuterGeometry().empty(), ()); auto const r = fb.GetLimitRect(); TEST(!r.IsEmptyInterior(), (r)); ++count; }); TEST_EQUAL(count, 7, ()); BuildFeatures(mwmName); FrozenDataSource dataSource; auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName))); CHECK_EQUAL(res.second, MwmSet::RegResult::Success, ()); FeaturesLoaderGuard guard(dataSource, res.first); size_t const numFeatures = guard.GetNumFeatures(); for (size_t id = 0; id < numFeatures; ++id) { auto ft = guard.GetFeatureByIndex(id); auto const r = ft->GetLimitRect(scales::GetUpperScale()); TEST(!r.IsEmptyInterior(), (r)); } } UNIT_CLASS_TEST(TestRawGenerator, Railway_Station) { std::string const mwmName = "Railway"; auto const & cl = classif(); BuildFB("./data/test_data/osm/railway_station.osm", mwmName); size_t count = 0; ForEachFB(mwmName, [&](feature::FeatureBuilder const & fb) { TEST_EQUAL(fb.GetGeomType(), feature::GeomType::Area, ()); TEST(!fb.GetOuterGeometry().empty(), ()); TEST(fb.HasType(cl.GetTypeByPath({"railway", "station", "subway", "lille"})), (fb)); ++count; }); TEST_EQUAL(count, 1, ()); } } // namespace raw_generator_tests