diff --git a/generator/altitude_generator.cpp b/generator/altitude_generator.cpp index 3548d135d3..596a461626 100644 --- a/generator/altitude_generator.cpp +++ b/generator/altitude_generator.cpp @@ -15,6 +15,8 @@ #include "coding/reader.hpp" #include "coding/varint.hpp" +#include "geometry/latlon.hpp" + #include "coding/internal/file_data.hpp" #include "base/assert.hpp" @@ -46,9 +48,10 @@ class SrtmGetter : public IAltitudeGetter public: SrtmGetter(string const & srtmPath) : m_srtmManager(srtmPath) {} - feature::TAltitude GetAltitude(ms::LatLon const & coord) override + // IAltitudeGetter overrides: + feature::TAltitude GetAltitude(m2::PointD const & p) override { - return m_srtmManager.GetHeight(coord); + return m_srtmManager.GetHeight(MercatorBounds::ToLatLon(p)); } private: @@ -96,7 +99,7 @@ public: TAltitude minFeatureAltitude = kInvalidAltitude; for (size_t i = 0; i < pointsCount; ++i) { - TAltitude const a = m_altitudeGetter.GetAltitude(MercatorBounds::ToLatLon(f.GetPoint(i))); + TAltitude const a = m_altitudeGetter.GetAltitude(f.GetPoint(i)); if (a == kInvalidAltitude) { valid = false; @@ -182,8 +185,7 @@ void SerializeHeader(TAltitudeSectionVersion version, TAltitude minAltitude, namespace routing { -void BuildRoadAltitudes(IAltitudeGetter & altitudeGetter, string const & baseDir, - string const & countryName) +void BuildRoadAltitudes(string const & baseDir, string const & countryName, IAltitudeGetter & altitudeGetter) { try { @@ -268,6 +270,6 @@ void BuildRoadAltitudes(string const & srtmPath, string const & baseDir, string { LOG(LINFO, ("srtmPath =", srtmPath, "baseDir =", baseDir, "countryName =", countryName)); SrtmGetter srtmGetter(srtmPath); - BuildRoadAltitudes(srtmGetter, baseDir, countryName); + BuildRoadAltitudes(baseDir, countryName, srtmGetter); } } // namespace routing diff --git a/generator/altitude_generator.hpp b/generator/altitude_generator.hpp index 5f8e76a377..78cc1c903e 100644 --- a/generator/altitude_generator.hpp +++ b/generator/altitude_generator.hpp @@ -1,6 +1,6 @@ #pragma once -#include "geometry/latlon.hpp" +#include "geometry/point2d.hpp" #include "indexer/feature_altitude.hpp" @@ -11,11 +11,11 @@ namespace routing class IAltitudeGetter { public: - virtual feature::TAltitude GetAltitude(ms::LatLon const & coord) = 0; + virtual feature::TAltitude GetAltitude(m2::PointD const & p) = 0; }; -void BuildRoadAltitudes(IAltitudeGetter const & altitudeGetter, string const & baseDir, - string const & countryName); +void BuildRoadAltitudes(string const & baseDir, string const & countryName, + IAltitudeGetter & altitudeGetter); void BuildRoadAltitudes(string const & srtmPath, string const & baseDir, string const & countryName); } // namespace routing diff --git a/generator/generator_tests/altitude_test.cpp b/generator/generator_tests/altitude_test.cpp new file mode 100644 index 0000000000..094d65780a --- /dev/null +++ b/generator/generator_tests/altitude_test.cpp @@ -0,0 +1,232 @@ +#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/index.hpp" +#include "indexer/feature_processor.hpp" + +#include "geometry/point2d.hpp" + +#include "coding/file_name_utils.hpp" + +#include "platform/country_file.hpp" +#include "platform/platform.hpp" + +#include "platform/platform_tests_support/writable_dir_changer.hpp" + +#include "base/logging.hpp" +#include "base/scope_guard.hpp" + +#include "std/string.hpp" + +using namespace feature; +using namespace generator; +using namespace platform; +using namespace routing; +using namespace tests_support; + +namespace +{ +// These tests generate mwms with altitude sections and then check if altitudes +// in the mwms are correct. The mwms are inited with different sets of features defined below. +// 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. +string const kTestDir = "altitude_generation_test"; +// Temporary mwm name for testing. +string const kTestMwm = "test"; + +struct Rounded3DPoint +{ + Rounded3DPoint(int32_t x, int32_t y, TAltitude a) : point2D(x, y), altitude(a) {} + + m2::PointI point2D; + TAltitude altitude; +}; + +using TRounded3DGeom = vector; + +TRounded3DGeom const kRoad1 = {{0, -1, -1}, {0, 0, 0}, {0, 1, 1}}; +TRounded3DGeom const kRoad2 = {{0, 1, 1}, {5, 1, 1}, {10, 1, 1}}; +TRounded3DGeom const kRoad3 = {{10, 1, 1}, {15, 6, 100}, {20, 11, 110}}; +TRounded3DGeom const kRoad4 = {{-10, 1, -1}, {-20, 6, -100}, {-20, -11, -110}}; + +class MockAltitudeGetter : public IAltitudeGetter +{ +public: + using TMockAltitudes = map; + + MockAltitudeGetter(TMockAltitudes && altitudes) : m_altitudes(altitudes) {} + + // IAltitudeGetter overrides: + TAltitude GetAltitude(m2::PointD const & p) override + { + m2::PointI const rounded(static_cast(round(p.x)), static_cast(round(p.y))); + auto const it = m_altitudes.find(rounded); + if (it == m_altitudes.end()) + return kInvalidAltitude; + + return it->second; + } + +private: + TMockAltitudes const & m_altitudes; +}; + +template +vector ConvertTo2DGeom(TRounded3DGeom const & geom3D) +{ + vector dv; + dv.clear(); + for (Rounded3DPoint const & p : geom3D) + dv.push_back(T(p.point2D)); + return dv; +} + +TAltitudes ConvertToAltitudes(TRounded3DGeom const & geom3D) +{ + TAltitudes altitudes; + altitudes.clear(); + for (Rounded3DPoint const & p : geom3D) + altitudes.push_back(p.altitude); + return altitudes; +} + +void FillAltitudes(vector const & roadFeatures, + MockAltitudeGetter::TMockAltitudes & altitudes) +{ + for (TRounded3DGeom const & geom3D : roadFeatures) + { + vector const featureGeom = ConvertTo2DGeom(geom3D); + TAltitudes const featureAlt = ConvertToAltitudes(geom3D); + CHECK_EQUAL(featureGeom.size(), featureAlt.size(), ()); + for (size_t i = 0; i < featureGeom.size(); ++i) + { + auto it = altitudes.find(featureGeom[i]); + if (it != altitudes.end()) + { + CHECK_EQUAL(it->second, featureAlt[i], ("Point", it->first, "is set with two different altitudes.")); + continue; + } + altitudes[featureGeom[i]] = featureAlt[i]; + } + } +} + +void BuildMwmWithoutAltitude(vector const & roadFeatures, LocalCountryFile & country) +{ + TestMwmBuilder builder(country, feature::DataHeader::country); + + for (TRounded3DGeom const & geom3D : roadFeatures) + builder.Add(TestStreet(ConvertTo2DGeom(geom3D), string(), string())); +} + +void ReadAndTestAltitudeInfo(MwmValue const * mwmValue, string const & mwmPath, MockAltitudeGetter & altitudeGetter) +{ + AltitudeLoader const loader(mwmValue); + + auto processor = [&altitudeGetter, &loader](FeatureType const & f, uint32_t const & id) + { + f.ParseGeometry(FeatureType::BEST_GEOMETRY); + size_t const pointsCount = f.GetPointsCount(); + TAltitudes const altitudes = loader.GetAltitude(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) + TEST_EQUAL(altitudeGetter.GetAltitude(f.GetPoint(i)), altitudes[i], ("A wrong altitude")); + }; + feature::ForEachFromDat(mwmPath, processor); +} + +void TestAltitudeSection(vector const & roadFeatures) +{ + classificator::Load(); + Platform & platform = GetPlatform(); + string const testDirFullPath = my::JoinFoldersToPath(platform.WritableDir(), kTestDir); + + MY_SCOPE_GUARD(removeTmpDir, [&] () + { + GetPlatform().RmDirRecursively(testDirFullPath); + }); + + // Building mwm without altitude section. + platform.MkDir(testDirFullPath); + LocalCountryFile country(testDirFullPath, CountryFile(kTestMwm), 1); + BuildMwmWithoutAltitude(roadFeatures, country); + + // Creating MockAltitudeGetter. + MockAltitudeGetter::TMockAltitudes altitudes; + FillAltitudes(roadFeatures, altitudes); + MockAltitudeGetter altitudeGetter(move(altitudes)); + + // Adding altitude section to mwm. + BuildRoadAltitudes(testDirFullPath, kTestMwm, altitudeGetter); + + // Reading from mwm and testing altitue information. + Index index; + pair regResult = index.RegisterMap(country); + TEST_EQUAL(regResult.second, MwmSet::RegResult::Success, ()); + + MwmSet::MwmHandle mwmHandle = index.GetMwmHandleById(regResult.first); + CHECK(mwmHandle.IsAlive(), ()); + + string const mwmPath = my::JoinFoldersToPath(testDirFullPath, kTestMwm + DATA_FILE_EXTENSION); + ReadAndTestAltitudeInfo(mwmHandle.GetValue(), mwmPath, altitudeGetter); +} +} // namespace + +UNIT_TEST(ZeroFeatures_AltitudeGenerationTest) +{ + vector const roadFeatures = {}; + TestAltitudeSection(roadFeatures); +} + +UNIT_TEST(OneRoad_AltitudeGenerationTest) +{ + vector const roadFeatures = {kRoad1}; + TestAltitudeSection(roadFeatures); +} + +UNIT_TEST(TwoConnectedRoads_AltitudeGenerationTest) +{ + vector const roadFeatures = {kRoad1, kRoad2}; + TestAltitudeSection(roadFeatures); +} + +UNIT_TEST(TwoDisconnectedRoads_AltitudeGenerationTest) +{ + vector const roadFeatures = {kRoad1, kRoad3}; + TestAltitudeSection(roadFeatures); +} + +UNIT_TEST(ThreeRoads_AltitudeGenerationTest) +{ + vector const roadFeatures = {kRoad1, kRoad2, kRoad3}; + TestAltitudeSection(roadFeatures); +} + +UNIT_TEST(FourRoads_AltitudeGenerationTest) +{ + vector const roadFeatures = {kRoad1, kRoad2, kRoad3, kRoad4}; + TestAltitudeSection(roadFeatures); +} diff --git a/generator/generator_tests/generator_tests.pro b/generator/generator_tests/generator_tests.pro index 11683b6b58..b782763808 100644 --- a/generator/generator_tests/generator_tests.pro +++ b/generator/generator_tests/generator_tests.pro @@ -4,7 +4,7 @@ CONFIG -= app_bundle TEMPLATE = app ROOT_DIR = ../.. -DEPENDENCIES = generator drape_frontend routing search storage indexer drape map platform editor geometry \ +DEPENDENCIES = generator_tests_support generator drape_frontend routing search storage indexer drape map platform editor geometry \ coding base freetype expat fribidi tomcrypt jansson protobuf osrm stats_client \ minizip succinct pugixml tess2 gflags oauthcpp @@ -19,6 +19,7 @@ HEADERS += \ SOURCES += \ ../../testing/testingmain.cpp \ + altitude_test.cpp \ check_mwms.cpp \ coasts_test.cpp \ feature_builder_test.cpp \