co-maps/generator/generator_tests/raw_generator_test.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1232 lines
36 KiB
C++
Raw Permalink Normal View History

2025-11-22 13:58:55 +01:00
#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<MwmSet::MwmId> 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<uint64_t, uint16_t> 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<uint64_t> 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<FeatureType> 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<FeatureType> 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<uint32_t> 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<uint32_t>({1, 3, 5}), ());
TEST_EQUAL(rounds, std::vector<uint32_t>({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<FeatureType> 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<FeatureType> 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