Added SrtmTileManager::GetTriangleHeight.

Signed-off-by: Viktor Govako <viktor.govako@gmail.com>
This commit is contained in:
Viktor Govako
2023-07-31 21:25:52 -03:00
committed by zyphlar
parent 81c06f1ae7
commit fca2a64a80
10 changed files with 531 additions and 155 deletions

View File

@@ -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 <algorithm>
#include <type_traits>
#include <utility>
#include <vector>
#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<FeatureAltitude>;
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<FeatureAltitude> 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<Writer> 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<uint32_t>(w->Pos() - startOffset);
@@ -191,8 +174,7 @@ void BuildRoadAltitudes(std::string const & mwmPath, AltitudeGetter & altitudeGe
{
// Altitude info serialization to memory.
MemWriter<std::vector<uint8_t>> 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<uint32_t>(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."));
}

View File

@@ -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)
{

View File

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

View File

@@ -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 <cstddef>
#include <cstdint>
#include <limits>
#include <iostream>
#include <mutex>
#include <vector>
#include <gflags/gflags.h>
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 <class FnT> void ForEachMWM(SafeTileManager & manager, FnT && fn)
{
std::vector<platform::LocalCountryFile> localFiles;
FindAllLocalMapsAndCleanup(std::numeric_limits<int64_t>::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<std::thread> 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<geometry::Altitude>(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;

View File

@@ -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<uint8_t> & 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<uint8_t> & 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<int>(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<size_t>(std::round(kArcSecondsInDegree * lt));
auto const col = static_cast<size_t>(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<size_t>(std::round(ll.m_lat)), static_cast<size_t>(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<int> const p1(static_cast<int>(std::round(ll.m_lon)), static_cast<int>(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<geometry::Altitude>(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<geometry::Altitude *>(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<int32_t>(tileCenter.m_lat), static_cast<int32_t>(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

View File

@@ -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<geometry::Altitude const *>(m_data.data());
};
}
inline size_t Size() const { return m_data.size() / sizeof(geometry::Altitude); }
void Invalidate();
std::string m_data;
std::vector<uint8_t> 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<int32_t, int32_t>;
static LatLonKey GetKey(ms::LatLon const & coord);
@@ -68,7 +89,8 @@ private:
}
};
std::unordered_map<LatLonKey, SrtmTile, Hash> m_tiles;
using MapT = std::unordered_map<LatLonKey, SrtmTile, Hash>;
MapT m_tiles;
DISALLOW_COPY(SrtmTileManager);
};

View File

@@ -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 <class TSink>
void Serialize(geometry::Altitude minAltitude, TSink & sink) const

View File

@@ -7,20 +7,25 @@
#include <memory>
#include <string>
#include <utility>
namespace feature
{
template <class ToDo>
void ForEachFeature(ModelReaderPtr const & reader, ToDo && toDo)
void ForEachFeature(FilesContainerR const & cont, ToDo && toDo)
{
FeaturesVectorTest features((FilesContainerR(reader)));
features.GetVector().ForEach(std::forward<ToDo>(toDo));
FeaturesVectorTest features(cont);
features.GetVector().ForEach(toDo);
}
template <class ToDo>
void ForEachFeature(ModelReaderPtr reader, ToDo && toDo)
{
ForEachFeature(FilesContainerR(reader), toDo);
}
template <class ToDo>
void ForEachFeature(std::string const & fPath, ToDo && toDo)
{
ForEachFeature(std::make_unique<FileReader>(fPath), std::forward<ToDo>(toDo));
ForEachFeature(std::make_unique<FileReader>(fPath), toDo);
}
} // namespace feature

View File

@@ -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<AltitudeLoaderCached> 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<MwmSet::MwmId, MwmSet::RegResult> const res = dataSource.RegisterMap(country);
TEST_EQUAL(res.second, MwmSet::RegResult::Success, ());
auto const handle = dataSource.GetMwmHandleById(res.first);
TEST(handle.IsAlive(), ());
pair<MwmSet::MwmId, MwmSet::RegResult> 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<AltitudeLoaderCached>(*handle.GetValue());
m_altitudes = make_unique<AltitudeLoaderCached>(*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

View File

@@ -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<Altitude> & altProvider,
Contours<Altitude> & 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)
{