From c05b668e38ee489c032d3310ef0f6bc671d3e19b Mon Sep 17 00:00:00 2001 From: Viktor Govako Date: Mon, 31 Jul 2023 21:25:52 -0300 Subject: [PATCH] Added SrtmTileManager::GetTriangleHeight. Signed-off-by: Viktor Govako --- generator/altitude_generator.cpp | 68 ++---- .../feature_segments_checker.cpp | 2 +- .../generator_tests/srtm_parser_test.cpp | 95 +++++++- .../srtm_coverage_checker.cpp | 213 +++++++++++++++--- generator/srtm_parser.cpp | 130 +++++++---- generator/srtm_parser.hpp | 38 +++- indexer/feature_altitude.hpp | 2 +- indexer/feature_processor.hpp | 15 +- .../get_altitude_test.cpp | 91 ++++++-- topography_generator/generator.cpp | 32 +-- 10 files changed, 531 insertions(+), 155 deletions(-) diff --git a/generator/altitude_generator.cpp b/generator/altitude_generator.cpp index 27b5bd05f..1d84fec49 100644 --- a/generator/altitude_generator.cpp +++ b/generator/altitude_generator.cpp @@ -9,7 +9,6 @@ #include "indexer/feature_processor.hpp" #include "coding/files_container.hpp" -#include "coding/internal/file_data.hpp" #include "coding/read_write_utils.hpp" #include "coding/reader.hpp" #include "coding/succinct_mapper.hpp" @@ -27,30 +26,26 @@ #include "defines.hpp" -#include -#include -#include -#include - #include "3party/succinct/elias_fano.hpp" #include "3party/succinct/mapper.hpp" #include "3party/succinct/rs_bit_vector.hpp" +namespace routing +{ using namespace feature; +using namespace geometry; namespace { -using namespace routing; - class SrtmGetter : public AltitudeGetter { public: explicit SrtmGetter(std::string const & srtmDir) : m_srtmManager(srtmDir) {} // AltitudeGetter overrides: - geometry::Altitude GetAltitude(m2::PointD const & p) override + Altitude GetAltitude(m2::PointD const & p) override { - return m_srtmManager.GetHeight(mercator::ToLatLon(p)); + return m_srtmManager.GetTriangleHeight(mercator::ToLatLon(p)); } private: @@ -63,31 +58,20 @@ public: struct FeatureAltitude { FeatureAltitude() : m_featureId(0) {} - FeatureAltitude(uint32_t featureId, Altitudes const & altitudes) - : m_featureId(featureId), m_altitudes(altitudes) + FeatureAltitude(uint32_t featureId, geometry::Altitudes && altitudes) + : m_featureId(featureId), m_altitudes(std::move(altitudes)) { } uint32_t m_featureId; - Altitudes m_altitudes; + feature::Altitudes m_altitudes; }; - using TFeatureAltitudes = std::vector; - explicit Processor(AltitudeGetter & altitudeGetter) - : m_altitudeGetter(altitudeGetter), m_minAltitude(geometry::kInvalidAltitude) + : m_minAltitude(geometry::kInvalidAltitude), m_altitudeGetter(altitudeGetter) { } - TFeatureAltitudes const & GetFeatureAltitudes() const { return m_featureAltitudes; } - - succinct::bit_vector_builder & GetAltitudeAvailabilityBuilder() - { - return m_altitudeAvailabilityBuilder; - } - - geometry::Altitude GetMinAltitude() const { return m_minAltitude; } - void operator()(FeatureType & f, uint32_t const & id) { if (id != m_altitudeAvailabilityBuilder.size()) @@ -109,10 +93,10 @@ public: return; geometry::Altitudes altitudes; - geometry::Altitude minFeatureAltitude = geometry::kInvalidAltitude; + Altitude minFeatureAltitude = geometry::kInvalidAltitude; for (size_t i = 0; i < pointsCount; ++i) { - geometry::Altitude const a = m_altitudeGetter.GetAltitude(f.GetPoint(i)); + Altitude const a = m_altitudeGetter.GetAltitude(f.GetPoint(i)); if (a == geometry::kInvalidAltitude) { // One invalid point invalidates the whole feature. @@ -128,7 +112,7 @@ public: } hasAltitude = true; - m_featureAltitudes.emplace_back(id, Altitudes(std::move(altitudes))); + m_featureAltitudes.emplace_back(id, std::move(altitudes)); if (m_minAltitude == geometry::kInvalidAltitude) m_minAltitude = minFeatureAltitude; @@ -140,20 +124,20 @@ public: bool IsFeatureAltitudesSorted() { - return std::is_sorted(m_featureAltitudes.begin(), m_featureAltitudes.end(), - base::LessBy(&Processor::FeatureAltitude::m_featureId)); + return base::IsSortedAndUnique(m_featureAltitudes.begin(), m_featureAltitudes.end(), + base::LessBy(&Processor::FeatureAltitude::m_featureId)); } +public: + std::vector m_featureAltitudes; + succinct::bit_vector_builder m_altitudeAvailabilityBuilder; + Altitude m_minAltitude; + private: AltitudeGetter & m_altitudeGetter; - TFeatureAltitudes m_featureAltitudes; - succinct::bit_vector_builder m_altitudeAvailabilityBuilder; - geometry::Altitude m_minAltitude; }; } // namespace -namespace routing -{ void BuildRoadAltitudes(std::string const & mwmPath, AltitudeGetter & altitudeGetter) { try @@ -174,15 +158,14 @@ void BuildRoadAltitudes(std::string const & mwmPath, AltitudeGetter & altitudeGe auto w = cont.GetWriter(ALTITUDES_FILE_TAG); AltitudeHeader header; - header.m_minAltitude = processor.GetMinAltitude(); + header.m_minAltitude = processor.m_minAltitude; auto const startOffset = w->Pos(); header.Serialize(*w); { // Altitude availability serialization. coding::FreezeVisitor visitor(*w); - succinct::bit_vector_builder & builder = processor.GetAltitudeAvailabilityBuilder(); - succinct::rs_bit_vector(&builder).map(visitor); + succinct::rs_bit_vector(&processor.m_altitudeAvailabilityBuilder).map(visitor); } header.m_featureTableOffset = base::checked_cast(w->Pos() - startOffset); @@ -191,8 +174,7 @@ void BuildRoadAltitudes(std::string const & mwmPath, AltitudeGetter & altitudeGe { // Altitude info serialization to memory. MemWriter> writer(deltas); - Processor::TFeatureAltitudes const & featureAltitudes = processor.GetFeatureAltitudes(); - for (auto const & a : featureAltitudes) + for (auto const & a : processor.m_featureAltitudes) { offsets.push_back(base::checked_cast(writer.Pos())); a.m_altitudes.Serialize(header.m_minAltitude, writer); @@ -200,8 +182,7 @@ void BuildRoadAltitudes(std::string const & mwmPath, AltitudeGetter & altitudeGe } { // Altitude offsets serialization. - CHECK(std::is_sorted(offsets.begin(), offsets.end()), ()); - CHECK(adjacent_find(offsets.begin(), offsets.end()) == offsets.end(), ()); + CHECK(base::IsSortedAndUnique(offsets.begin(), offsets.end()), ()); succinct::elias_fano::elias_fano_builder builder(offsets.back(), offsets.size()); for (uint32_t offset : offsets) @@ -221,9 +202,10 @@ void BuildRoadAltitudes(std::string const & mwmPath, AltitudeGetter & altitudeGe w->Seek(startOffset); header.Serialize(w); w->Seek(endOffset); + LOG(LINFO, (ALTITUDES_FILE_TAG, "section is ready. The size is", header.m_endOffset)); if (processor.HasAltitudeInfo()) - LOG(LINFO, ("Min altitude is", processor.GetMinAltitude())); + LOG(LINFO, ("Min altitude is", processor.m_minAltitude)); else LOG(LINFO, ("Min altitude isn't defined.")); } diff --git a/generator/feature_segments_checker/feature_segments_checker.cpp b/generator/feature_segments_checker/feature_segments_checker.cpp index ab6ca3f17..807cddd25 100644 --- a/generator/feature_segments_checker/feature_segments_checker.cpp +++ b/generator/feature_segments_checker/feature_segments_checker.cpp @@ -188,7 +188,7 @@ public: for (uint32_t i = 0; i < numPoints; ++i) { // Feature segment altitude. - geometry::Altitude altitude = m_srtmManager.GetHeight(mercator::ToLatLon(f.GetPoint(i))); + geometry::Altitude altitude = m_srtmManager.GetTriangleHeight(mercator::ToLatLon(f.GetPoint(i))); pointAltitudes[i] = altitude == geometry::kInvalidAltitude ? 0 : altitude; if (i == 0) { diff --git a/generator/generator_tests/srtm_parser_test.cpp b/generator/generator_tests/srtm_parser_test.cpp index fa8b7acc0..e26083634 100644 --- a/generator/generator_tests/srtm_parser_test.cpp +++ b/generator/generator_tests/srtm_parser_test.cpp @@ -2,13 +2,19 @@ #include "generator/srtm_parser.hpp" -using namespace generator; +#include -namespace +namespace srtm_parser_test { -inline std::string GetBase(ms::LatLon const & coord) { return SrtmTile::GetBase(coord); } +using namespace generator; +using namespace geometry; -UNIT_TEST(FilenameTests) +inline std::string GetBase(ms::LatLon const & coord) +{ + return SrtmTile::GetBase(coord); +} + +UNIT_TEST(SRTM_FilenameTest) { auto name = GetBase({56.4566, 37.3467}); TEST_EQUAL(name, "N56E037", ()); @@ -19,10 +25,89 @@ UNIT_TEST(FilenameTests) name = GetBase({0.1, 0.1}); TEST_EQUAL(name, "N00E000", ()); + name = GetBase({-0.9, -0.9}); + TEST_EQUAL(name, "S01W001", ()); + + name = GetBase({-1.0, -1.0}); + TEST_EQUAL(name, "S01W001", ()); + + name = GetBase({-1.9, -1.1}); + TEST_EQUAL(name, "S02W002", ()); + name = GetBase({-35.35, -12.1}); TEST_EQUAL(name, "S36W013", ()); name = GetBase({-34.622358, -58.383654}); TEST_EQUAL(name, "S35W059", ()); } -} // namespace + +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_EQUAL(tile.GetTriangleHeight({0, 0}), 4, ()); + TEST_EQUAL(tile.GetHeight({len, len}), 2, ()); + TEST_EQUAL(tile.GetTriangleHeight({len, len}), 2, ()); + + // Key difference here: GetHeight snaps on the nearest node, while GetTriangleHeight calculates + // from the nearest nodes' triangle. + double l = len / 2; + Altitude h = tile.GetHeight({l, l}); + TEST(h == 4 || h == 2, (h)); + TEST_EQUAL(tile.GetTriangleHeight({l, l}), 3, ()); + + l = 3 * len + len / 2; + h = tile.GetHeight({l, l}); + TEST(h == -4 || h == -2, (h)); + TEST_EQUAL(tile.GetTriangleHeight({l, l}), -3, ()); +} + +/* +UNIT_TEST(SRTM_SamplesTest) +{ + SrtmTileManager manager("/Users/vng/SRTM"); + + std::initializer_list 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 diff --git a/generator/srtm_coverage_checker/srtm_coverage_checker.cpp b/generator/srtm_coverage_checker/srtm_coverage_checker.cpp index 12d182ab9..024197fc4 100644 --- a/generator/srtm_coverage_checker/srtm_coverage_checker.cpp +++ b/generator/srtm_coverage_checker/srtm_coverage_checker.cpp @@ -2,72 +2,116 @@ #include "routing/routing_helpers.hpp" +#include "indexer/classificator.hpp" #include "indexer/classificator_loader.hpp" #include "indexer/feature.hpp" -#include "indexer/feature_altitude.hpp" #include "indexer/feature_data.hpp" #include "indexer/feature_processor.hpp" +#include "geometry/distance_on_sphere.hpp" #include "geometry/mercator.hpp" #include "geometry/point_with_altitude.hpp" -#include "platform/country_file.hpp" #include "platform/local_country_file.hpp" #include "platform/local_country_file_utils.hpp" #include "platform/platform.hpp" #include "base/logging.hpp" -#include -#include -#include +#include +#include #include #include DEFINE_string(srtm_path, "", "Path to directory with SRTM files"); DEFINE_string(mwm_path, "", "Path to mwm files (writable dir)"); +DEFINE_bool(check_dist, false, "Check feature sections distance"); -int main(int argc, char * argv[]) +class SafeTileManager { - gflags::SetUsageMessage("SRTM coverage checker."); - gflags::ParseCommandLineFlags(&argc, &argv, true); + generator::SrtmTileManager m_manager; + std::mutex m_mutex; - Platform & platform = GetPlatform(); - if (!FLAGS_mwm_path.empty()) - platform.SetWritableDirForTests(FLAGS_mwm_path); + uint32_t m_ferry; - if (FLAGS_srtm_path.empty()) +public: + explicit SafeTileManager(std::string const & dir) : m_manager(dir) { - LOG(LERROR, ("SRTM files directory is not specified.")); - return -1; + m_ferry = classif().GetTypeByPath({"route", "ferry"}); + CHECK(m_ferry != Classificator::INVALID_TYPE, ()); } - LOG(LINFO, ("writable dir =", platform.WritableDir())); - LOG(LINFO, ("srtm dir =", FLAGS_srtm_path)); + bool IsAltitudeRoad(FeatureType & ft) const + { + feature::TypesHolder types(ft); + return (routing::IsRoad(types) && !types.Has(m_ferry)); + } + geometry::Altitude GetHeight(ms::LatLon const & coord) + { + std::lock_guard guard(m_mutex); + return m_manager.GetTriangleHeight(coord); + } + + void Purge() + { + std::lock_guard guard(m_mutex); + m_manager.Purge(); + } +}; + +template void ForEachMWM(SafeTileManager & manager, FnT && fn) +{ std::vector localFiles; FindAllLocalMapsAndCleanup(std::numeric_limits::max() /* latestVersion */, localFiles); - generator::SrtmTileManager manager(FLAGS_srtm_path); - classificator::Load(); + // Better use ComputationalThreadPool, but we want to call SafeTileManager::Purge after each batch. + size_t constexpr kThreadsCount = 24; + std::vector pool; + size_t workers = 0; for (auto & file : localFiles) { + // Skip worlds. + if (file.GetDirectory().empty() || file.GetCountryName().starts_with("World")) + continue; + file.SyncWithDisk(); if (!file.OnDisk(MapFileType::Map)) { - LOG(LINFO, ("Warning! Routing file not found for:", file.GetCountryName())); + LOG_SHORT(LWARNING, ("Map file not found for:", file.GetCountryName())); continue; } - auto const path = file.GetPath(MapFileType::Map); - LOG(LINFO, ("Mwm", path, "is being processed.")); + LOG_SHORT(LINFO, ("Processing", file.GetCountryName())); + pool.emplace_back([&fn, &file]() { fn(file); }); + + if (++workers == kThreadsCount) + { + for (auto & t : pool) + t.join(); + pool.clear(); + + manager.Purge(); + workers = 0; + } + } + + for (auto & t : pool) + t.join(); +} + +void CheckCoverage(SafeTileManager & manager) +{ + ForEachMWM(manager, [&](platform::LocalCountryFile const & file) + { size_t all = 0; size_t good = 0; - feature::ForEachFeature(path, [&](FeatureType & ft, uint32_t fid) { - if (!routing::IsRoad(feature::TypesHolder(ft))) + feature::ForEachFeature(file.GetPath(MapFileType::Map), [&](FeatureType & ft, uint32_t) + { + if (!manager.IsAltitudeRoad(ft)) return; ft.ParseGeometry(FeatureType::BEST_GEOMETRY); @@ -82,9 +126,126 @@ int main(int argc, char * argv[]) }); auto const bad = all - good; - auto const percent = all == 0 ? 0.0 : bad * 100.0 / all; - LOG(LINFO, (percent > 10.0 ? "Huge" : "Low", "error rate in:", file.GetCountryName(), - "good:", good, "bad:", bad, "all:", all, "%:", percent)); + auto const percent = (all == 0) ? 0.0 : bad * 100.0 / all; + LOG_SHORT(LINFO, (percent > 10.0 ? "Huge" : "Low", "error rate in:", file.GetCountryName(), + "good:", good, "bad:", bad, "all:", all, "%:", percent)); + }); +} + +void CheckDistance(SafeTileManager & manager) +{ + ForEachMWM(manager, [&](platform::LocalCountryFile const & file) + { + size_t all = 0; + size_t added = 0; + size_t invalid = 0; + feature::ForEachFeature(file.GetPath(MapFileType::Map), [&](FeatureType & ft, uint32_t) + { + if (!manager.IsAltitudeRoad(ft)) + return; + + ft.ParseGeometry(FeatureType::BEST_GEOMETRY); + all += ft.GetPointsCount(); + + for (size_t i = 1; i < ft.GetPointsCount(); ++i) + { + auto const ll1 = mercator::ToLatLon(ft.GetPoint(i-1)); + auto const alt1 = manager.GetHeight(ll1); + auto const ll2 = mercator::ToLatLon(ft.GetPoint(i)); + auto const alt2 = manager.GetHeight(ll2); + + if (alt1 == geometry::kInvalidAltitude || alt2 == geometry::kInvalidAltitude) + { + ++invalid; + continue; + } + + // Divide by 1 second sections. + size_t const sections = std::round(ms::DistanceOnSphere(ll1.m_lat, ll1.m_lon, ll2.m_lat, ll2.m_lon) * 3600); + if (sections < 2) + continue; + + for (size_t j = 1; j < sections; ++j) + { + double const a = j / double(sections); + ms::LatLon const ll(ll2.m_lat * a + ll1.m_lat * (1 - a), ll2.m_lon * a + ll1.m_lon * (1 - a)); + + // Get diff between approx altitude and real one. + auto const alt = manager.GetHeight(ll); + if (alt == geometry::kInvalidAltitude) + { + LOG_SHORT(LWARNING, ("Invalid altitude for the middle point:", ll)); + ++added; + } + else + { + auto const approxAlt = static_cast(std::round(alt2 * a + alt1 * (1 - a))); + if (abs(alt - approxAlt) >= std::max(1, abs(alt)/10)) // 10% + ++added; + } + } + } + }); + + auto const percent = added * 100.0 / all; + std::string prefix = "Low"; + if (percent >= 1) + prefix = "Huge"; + else if (added >= 1000) + prefix = "Medium"; + + LOG_SHORT(LINFO, (prefix, file.GetCountryName(), "all:", all, "invalid:", invalid, "added:", added, "%:", percent)); + }); +} + +int main(int argc, char * argv[]) +{ + gflags::SetUsageMessage("SRTM coverage checker."); + gflags::ParseCommandLineFlags(&argc, &argv, true); + + if (FLAGS_srtm_path.empty()) + { + LOG_SHORT(LERROR, ("SRTM files directory is not specified.")); + return -1; + } + + classificator::Load(); + + if (!FLAGS_mwm_path.empty()) + { + SafeTileManager manager(FLAGS_srtm_path); + + Platform & platform = GetPlatform(); + platform.SetWritableDirForTests(FLAGS_mwm_path); + + if (FLAGS_check_dist) + CheckDistance(manager); + else + CheckCoverage(manager); + } + else + { + generator::SrtmTileManager manager(FLAGS_srtm_path); + + using namespace std; + cout << "Enter lat lon. Or Ctrl + C to exit." << endl; + + while (true) + { + double lat, lon; + cin >> lat >> lon; + if (!cin) + { + cout << "Invalid lat lon." << endl; + cin.clear(); + cin.ignore(10000, '\n'); + } + else + { + cout << "H = " << manager.GetHeight({lat, lon}) << "; TrgH = " << manager.GetTriangleHeight({lat, lon}); + cout << endl; + } + } } return 0; diff --git a/generator/srtm_parser.cpp b/generator/srtm_parser.cpp index fffa1b286..6b19981dc 100644 --- a/generator/srtm_parser.cpp +++ b/generator/srtm_parser.cpp @@ -21,10 +21,16 @@ size_t constexpr kSrtmTileSize = (kArcSecondsInDegree + 1) * (kArcSecondsInDegre struct UnzipMemDelegate : public ZipFileReader::Delegate { - explicit UnzipMemDelegate(std::string & buffer) : m_buffer(buffer), m_completed(false) {} + explicit UnzipMemDelegate(std::vector & buffer) : m_buffer(buffer), m_completed(false) + { + m_buffer.reserve(kSrtmTileSize); + } // ZipFileReader::Delegate overrides: - void OnBlockUnzipped(size_t size, char const * data) override { m_buffer.append(data, size); } + void OnBlockUnzipped(size_t size, char const * data) override + { + m_buffer.insert(m_buffer.end(), data, data + size); + } void OnStarted() override { @@ -34,7 +40,7 @@ struct UnzipMemDelegate : public ZipFileReader::Delegate void OnCompleted() override { m_completed = true; } - std::string & m_buffer; + std::vector & m_buffer; bool m_completed; }; @@ -45,11 +51,6 @@ std::string GetSrtmContFileName(std::string const & dir, std::string const & bas } // namespace // SrtmTile ---------------------------------------------------------------------------------------- -SrtmTile::SrtmTile() -{ - Invalidate(); -} - SrtmTile::SrtmTile(SrtmTile && rhs) : m_data(std::move(rhs.m_data)), m_valid(rhs.m_valid) { rhs.Invalidate(); @@ -90,7 +91,7 @@ void SrtmTile::Init(std::string const & dir, ms::LatLon const & coord) } else { - FileReader(base::JoinPath(dir, file)).ReadAsString(m_data); + m_data = base::ReadFile(base::JoinPath(dir, file)); } if (m_data.size() != kSrtmTileSize) @@ -103,11 +104,9 @@ void SrtmTile::Init(std::string const & dir, ms::LatLon const & coord) m_valid = true; } -geometry::Altitude SrtmTile::GetHeight(ms::LatLon const & coord) const +// static +ms::LatLon SrtmTile::GetCoordInSeconds(ms::LatLon const & coord) { - if (!IsValid()) - return geometry::kInvalidAltitude; - double ln = coord.m_lon - static_cast(coord.m_lon); if (ln < 0) ln += 1; @@ -116,15 +115,75 @@ geometry::Altitude SrtmTile::GetHeight(ms::LatLon const & coord) const lt += 1; lt = 1 - lt; // from North to South - auto const row = static_cast(std::round(kArcSecondsInDegree * lt)); - auto const col = static_cast(std::round(kArcSecondsInDegree * ln)); + return { kArcSecondsInDegree * lt, kArcSecondsInDegree * ln }; +} +geometry::Altitude SrtmTile::GetHeight(ms::LatLon const & coord) const +{ + if (!IsValid()) + return geometry::kInvalidAltitude; + + auto const ll = GetCoordInSeconds(coord); + + return GetHeightRC(static_cast(std::round(ll.m_lat)), static_cast(std::round(ll.m_lon))); +} + +geometry::Altitude SrtmTile::GetHeightRC(size_t row, size_t col) const +{ size_t const ix = row * (kArcSecondsInDegree + 1) + col; - - CHECK_LESS(ix, Size(), (coord)); + CHECK_LESS(ix, Size(), (row, col)); return ReverseByteOrder(Data()[ix]); } +geometry::Altitude SrtmTile::GetTriangleHeight(ms::LatLon const & coord) const +{ + if (!IsValid()) + return geometry::kInvalidAltitude; + + auto const ll = GetCoordInSeconds(coord); + + m2::Point const p1(static_cast(std::round(ll.m_lon)), static_cast(std::round(ll.m_lat))); + + auto p2 = p1; + if (p2.x > ll.m_lon) + { + if (p2.x > 0) + --p2.x; + } + else if (p2.x < ll.m_lon) + { + if (p2.x < kArcSecondsInDegree) + ++p2.x; + } + + auto p3 = p1; + if (p3.y > ll.m_lat) + { + if (p3.y > 0) + --p3.y; + } + else if (p3.y < ll.m_lat) + { + if (p3.y < kArcSecondsInDegree) + ++p3.y; + } + + // Approximate height from triangle p1, p2, p3. + // p1.y == p2.y; p1.x == p3.x + // https://stackoverflow.com/questions/36090269/finding-height-of-point-on-height-map-triangles + int const det = (p2.y - p3.y) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.y - p3.y); + if (det == 0) + return GetHeightRC(p1.y, p1.x); + + double const a1 = ((p2.y - p3.y) * (ll.m_lon - p3.x) + (p3.x - p2.x) * (ll.m_lat - p3.y)) / det; + double const a2 = ((p3.y - p1.y) * (ll.m_lon - p3.x) + (p1.x - p3.x) * (ll.m_lat - p3.y)) / det; + double const a3 = 1 - a1 - a2; + + return static_cast(std::round(a1 * GetHeightRC(p1.y, p1.x) + + a2 * GetHeightRC(p2.y, p2.x) + + a3 * GetHeightRC(p3.y, p3.x))); +} + // static std::string SrtmTile::GetPath(std::string const & dir, std::string const & base) { @@ -168,6 +227,14 @@ std::string SrtmTile::GetBase(ms::LatLon const & coord) return ss.str(); } +geometry::Altitude * SrtmTile::DataForTests(size_t & sz) +{ + m_valid = true; + sz = kArcSecondsInDegree + 1; + m_data.resize(kSrtmTileSize, 0); + return reinterpret_cast(m_data.data()); +} + void SrtmTile::Invalidate() { m_data.clear(); @@ -176,31 +243,22 @@ void SrtmTile::Invalidate() } // SrtmTileManager --------------------------------------------------------------------------------- -SrtmTileManager::SrtmTileManager(std::string const & dir) : m_dir(dir) {} -geometry::Altitude SrtmTileManager::GetHeight(ms::LatLon const & coord) +SrtmTile const & SrtmTileManager::GetTile(ms::LatLon const & coord) { - auto const key = GetKey(coord); - - auto it = m_tiles.find(key); - if (it == m_tiles.end()) + auto res = m_tiles.emplace(GetKey(coord), SrtmTile()); + if (res.second) { - SrtmTile tile; try { - tile.Init(m_dir, coord); + res.first->second.Init(m_dir, coord); } catch (RootException const & e) { std::string const base = SrtmTile::GetBase(coord); LOG(LINFO, ("Can't init SRTM tile:", base, "reason:", e.Msg())); } - - // It's OK to store even invalid tiles and return invalid height - // for them later. - it = m_tiles.emplace(key, std::move(tile)).first; } - - return it->second.GetHeight(coord); + return res.first->second; } // static @@ -210,13 +268,9 @@ SrtmTileManager::LatLonKey SrtmTileManager::GetKey(ms::LatLon const & coord) return {static_cast(tileCenter.m_lat), static_cast(tileCenter.m_lon)}; } -SrtmTile const & SrtmTileManager::GetTile(ms::LatLon const & coord) +void SrtmTileManager::Purge() { - // Touch the tile to force its loading. - GetHeight(coord); - auto const key = GetKey(coord); - auto const it = m_tiles.find(key); - CHECK(it != m_tiles.end(), (coord)); - return it->second; + MapT().swap(m_tiles); } + } // namespace generator diff --git a/generator/srtm_parser.hpp b/generator/srtm_parser.hpp index f6cc95710..0f82f95f9 100644 --- a/generator/srtm_parser.hpp +++ b/generator/srtm_parser.hpp @@ -17,29 +17,40 @@ namespace generator class SrtmTile { public: - SrtmTile(); + SrtmTile() : m_valid(false) {} SrtmTile(SrtmTile && rhs); void Init(std::string const & dir, ms::LatLon const & coord); inline bool IsValid() const { return m_valid; } - // Returns height in meters at |coord| or kInvalidAltitude. + /// @return Height in meters at |coord| or kInvalidAltitude. + /// @{ + /// Nearest serialized height. geometry::Altitude GetHeight(ms::LatLon const & coord) const; + /// Height from underlying triangle (better than GetHeight). + geometry::Altitude GetTriangleHeight(ms::LatLon const & coord) const; + /// @} static std::string GetBase(ms::LatLon const & coord); static ms::LatLon GetCenter(ms::LatLon const & coord); static std::string GetPath(std::string const & dir, std::string const & base); + /// Used in unit tests only to prepare mock tile. + geometry::Altitude * DataForTests(size_t & sz); + private: + static ms::LatLon GetCoordInSeconds(ms::LatLon const & coord); + geometry::Altitude GetHeightRC(size_t row, size_t col) const; + inline geometry::Altitude const * Data() const { return reinterpret_cast(m_data.data()); - }; + } inline size_t Size() const { return m_data.size() / sizeof(geometry::Altitude); } void Invalidate(); - std::string m_data; + std::vector m_data; bool m_valid; DISALLOW_COPY(SrtmTile); @@ -48,12 +59,22 @@ private: class SrtmTileManager { public: - explicit SrtmTileManager(std::string const & dir); - - geometry::Altitude GetHeight(ms::LatLon const & coord); + explicit SrtmTileManager(std::string const & dir) : m_dir(dir) {} SrtmTile const & GetTile(ms::LatLon const & coord); + geometry::Altitude GetHeight(ms::LatLon const & coord) + { + return GetTile(coord).GetHeight(coord); + } + + geometry::Altitude GetTriangleHeight(ms::LatLon const & coord) + { + return GetTile(coord).GetTriangleHeight(coord); + } + + void Purge(); + private: using LatLonKey = std::pair; static LatLonKey GetKey(ms::LatLon const & coord); @@ -68,7 +89,8 @@ private: } }; - std::unordered_map m_tiles; + using MapT = std::unordered_map; + MapT m_tiles; DISALLOW_COPY(SrtmTileManager); }; diff --git a/indexer/feature_altitude.hpp b/indexer/feature_altitude.hpp index e5a3f28f0..12af12dce 100644 --- a/indexer/feature_altitude.hpp +++ b/indexer/feature_altitude.hpp @@ -77,7 +77,7 @@ class Altitudes public: Altitudes() = default; - explicit Altitudes(geometry::Altitudes const & altitudes) : m_altitudes(altitudes) {} + explicit Altitudes(geometry::Altitudes && altitudes) : m_altitudes(std::move(altitudes)) {} template void Serialize(geometry::Altitude minAltitude, TSink & sink) const diff --git a/indexer/feature_processor.hpp b/indexer/feature_processor.hpp index 913505a84..1db0bcf3e 100644 --- a/indexer/feature_processor.hpp +++ b/indexer/feature_processor.hpp @@ -7,20 +7,25 @@ #include #include -#include namespace feature { template -void ForEachFeature(ModelReaderPtr const & reader, ToDo && toDo) +void ForEachFeature(FilesContainerR const & cont, ToDo && toDo) { - FeaturesVectorTest features((FilesContainerR(reader))); - features.GetVector().ForEach(std::forward(toDo)); + FeaturesVectorTest features(cont); + features.GetVector().ForEach(toDo); +} + +template +void ForEachFeature(ModelReaderPtr reader, ToDo && toDo) +{ + ForEachFeature(FilesContainerR(reader), toDo); } template void ForEachFeature(std::string const & fPath, ToDo && toDo) { - ForEachFeature(std::make_unique(fPath), std::forward(toDo)); + ForEachFeature(std::make_unique(fPath), toDo); } } // namespace feature diff --git a/routing/routing_integration_tests/get_altitude_test.cpp b/routing/routing_integration_tests/get_altitude_test.cpp index 8030bafd8..0df73c1f8 100644 --- a/routing/routing_integration_tests/get_altitude_test.cpp +++ b/routing/routing_integration_tests/get_altitude_test.cpp @@ -6,6 +6,7 @@ #include "indexer/classificator.hpp" #include "indexer/classificator_loader.hpp" #include "indexer/data_source.hpp" +#include "indexer/feature_algo.hpp" #include "indexer/feature_altitude.hpp" #include "indexer/feature_data.hpp" #include "indexer/feature_processor.hpp" @@ -27,27 +28,42 @@ namespace get_altitude_tests { using namespace feature; +using namespace geometry; using namespace platform; using namespace std; -void TestAltitudeOfAllMwmFeatures(string const & countryId, - geometry::Altitude const altitudeLowerBoundMeters, - geometry::Altitude const altitudeUpperBoundMeters) +class FeaturesGuard { - FrozenDataSource dataSource; +public: + FrozenDataSource m_dataSource; + MwmSet::MwmHandle m_handle; + unique_ptr m_altitudes; - LocalCountryFile const country = integration::GetLocalCountryFileByCountryId(CountryFile(countryId)); - TEST_NOT_EQUAL(country, LocalCountryFile(), ()); - TEST(country.HasFiles(), (country)); + explicit FeaturesGuard(string const & countryId) + { + LocalCountryFile const country = integration::GetLocalCountryFileByCountryId(CountryFile(countryId)); + TEST_NOT_EQUAL(country, LocalCountryFile(), ()); + TEST(country.HasFiles(), (country)); - pair const res = dataSource.RegisterMap(country); - TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); - auto const handle = dataSource.GetMwmHandleById(res.first); - TEST(handle.IsAlive(), ()); + pair const res = m_dataSource.RegisterMap(country); + TEST_EQUAL(res.second, MwmSet::RegResult::Success, ()); + m_handle = m_dataSource.GetMwmHandleById(res.first); + TEST(m_handle.IsAlive(), ()); + TEST(GetValue(), ()); - auto altitudeLoader = make_unique(*handle.GetValue()); + m_altitudes = make_unique(*GetValue()); + } - ForEachFeature(country.GetPath(MapFileType::Map), [&](FeatureType & f, uint32_t const & id) + MwmValue const * GetValue() { return m_handle.GetValue(); } +}; + +void TestAltitudeOfAllMwmFeatures(string const & countryId, + Altitude const altitudeLowerBoundMeters, + Altitude const altitudeUpperBoundMeters) +{ + FeaturesGuard features(countryId); + + ForEachFeature(features.GetValue()->m_cont, [&](FeatureType & f, uint32_t const & id) { if (!routing::IsRoad(TypesHolder(f))) return; @@ -57,7 +73,7 @@ void TestAltitudeOfAllMwmFeatures(string const & countryId, if (pointsCount == 0) return; - geometry::Altitudes const & altitudes = altitudeLoader->GetAltitudes(id, pointsCount); + auto const & altitudes = features.m_altitudes->GetAltitudes(id, pointsCount); TEST(!altitudes.empty(), ("Empty altitude vector. MWM:", countryId, ", feature id:", id, ", altitudes:", altitudes)); @@ -69,7 +85,7 @@ void TestAltitudeOfAllMwmFeatures(string const & countryId, }); } -UNIT_TEST(AllMwmFeaturesGetAltitudeTest) +UNIT_TEST(GetAltitude_AllMwmFeaturesTest) { classificator::Load(); @@ -80,4 +96,49 @@ UNIT_TEST(AllMwmFeaturesGetAltitudeTest) TestAltitudeOfAllMwmFeatures("Netherlands_North Holland_Amsterdam", -25 /* altitudeLowerBoundMeters */, 50 /* altitudeUpperBoundMeters */); } + +/* +void PrintGeometryAndAltitude(std::string const & countryID, ms::LatLon const & ll, double distM) +{ + FeaturesGuard features(countryID); + auto const point = mercator::FromLatLon(ll); + m2::RectD const rect = mercator::RectByCenterXYAndSizeInMeters(point, distM); + + features.m_dataSource.ForEachInRect([&](FeatureType & ft) + { + if (!routing::IsRoad(TypesHolder(ft))) + return; + + ft.ParseGeometry(FeatureType::BEST_GEOMETRY); + size_t const pointsCount = ft.GetPointsCount(); + if (pointsCount == 0) + return; + + if (GetMinDistanceMeters(ft, point) > distM) + return; + + stringstream geomSS; + geomSS.precision(20); + for (size_t i = 0; i < pointsCount; ++i) + { + auto const ll = mercator::ToLatLon(ft.GetPoint(i)); + geomSS << "{ " << ll.m_lat << ", " << ll.m_lon << " }, "; + } + LOG(LINFO, (geomSS.str())); + + auto const & altitudes = features.m_altitudes->GetAltitudes(ft.GetID().m_index, pointsCount); + LOG(LINFO, (ft.GetName(StringUtf8Multilang::kDefaultCode), altitudes)); + + }, rect, scales::GetUpperScale()); +} + +UNIT_TEST(GetAltitude_SamplesTest) +{ + classificator::Load(); + + PrintGeometryAndAltitude("Italy_Lazio", {41.8998667, 12.4985937}, 15.0); + PrintGeometryAndAltitude("Crimea", { 44.7598876, 34.3160482 }, 5.0); +} +*/ + } // namespace get_altitude_tests diff --git a/topography_generator/generator.cpp b/topography_generator/generator.cpp index 6ceaf1e84..0f59fb3cb 100644 --- a/topography_generator/generator.cpp +++ b/topography_generator/generator.cpp @@ -102,30 +102,35 @@ public: } private: - Altitude GetValueImpl(ms::LatLon const & pos) + Altitude GetValueImpl(ms::LatLon pos) { if (m_preferredTile != nullptr) { + using mercator::kPointEqualityEps; + // Each SRTM tile overlaps the top row in the bottom tile and the right row in the left tile. // Try to prevent loading a new tile if the position can be found in the loaded one. auto const latDist = pos.m_lat - m_leftBottomOfPreferredTile.m_lat; auto const lonDist = pos.m_lon - m_leftBottomOfPreferredTile.m_lon; - if (latDist > -mercator::kPointEqualityEps && latDist < 1.0 + mercator::kPointEqualityEps && lonDist > -mercator::kPointEqualityEps && lonDist < 1.0 + mercator::kPointEqualityEps) + if (latDist > -kPointEqualityEps && latDist < 1.0 + kPointEqualityEps && + lonDist > -kPointEqualityEps && lonDist < 1.0 + kPointEqualityEps) { - ms::LatLon innerPos = pos; if (latDist < 0.0) - innerPos.m_lat += mercator::kPointEqualityEps; + pos.m_lat += kPointEqualityEps; else if (latDist >= 1.0) - innerPos.m_lat -= mercator::kPointEqualityEps; + pos.m_lat -= kPointEqualityEps; if (lonDist < 0.0) - innerPos.m_lon += mercator::kPointEqualityEps; + pos.m_lon += kPointEqualityEps; else if (lonDist >= 1.0) - innerPos.m_lon -= mercator::kPointEqualityEps; - return m_preferredTile->GetHeight(innerPos); + pos.m_lon -= kPointEqualityEps; + + /// @todo Can't call GetTriangleHeight here and below because it breaks + /// ContoursBuilder::AddSegment level constraint. Should investigate deeper. + return m_preferredTile->GetHeight(pos); } } - return m_srtmManager.GetHeight(pos); + return m_srtmManager.GetTile(pos).GetHeight(pos); } Altitude GetMedianValue(ms::LatLon const & pos) @@ -179,6 +184,8 @@ public: , m_bottomLat(bottomLat) {} + /// @todo Should we use the same approach as in SrtmTile::GetTriangleHeight? + /// This function is used in ASTER fiter only. Altitude GetValue(ms::LatLon const & pos) override { double ln = pos.m_lon - m_leftLon; @@ -374,11 +381,10 @@ private: ValuesProvider & altProvider, Contours & contours) { - auto const avoidSeam = lat == kAsterTilesLatTop || (lat == kAsterTilesLatBottom - 1); - if (avoidSeam) + // Avoid seam between SRTM and ASTER. + if ((lat == kAsterTilesLatTop) || (lat == kAsterTilesLatBottom - 1)) { - m_srtmProvider.SetPrefferedTile(ms::LatLon(lat == kAsterTilesLatTop ? lat - 0.5 : lat + 0.5, - lon)); + m_srtmProvider.SetPrefferedTile(ms::LatLon(lat == kAsterTilesLatTop ? lat - 0.5 : lat + 0.5, lon)); SeamlessAltitudeProvider seamlessAltProvider(m_srtmProvider, altProvider, [](ms::LatLon const & pos) {