forked from organicmaps/organicmaps
[search] Get street name and house number from point.
This commit is contained in:
parent
21370ee3d9
commit
2fe8fee598
8 changed files with 208 additions and 41 deletions
|
@ -101,13 +101,13 @@ public:
|
|||
|
||||
m2::PointD GetCenter(FeatureType const & f, int scale)
|
||||
{
|
||||
feature::EGeomType const type = f.GetFeatureType();
|
||||
EGeomType const type = f.GetFeatureType();
|
||||
switch (type)
|
||||
{
|
||||
case feature::GEOM_POINT:
|
||||
case GEOM_POINT:
|
||||
return f.GetCenter();
|
||||
|
||||
case feature::GEOM_LINE:
|
||||
case GEOM_LINE:
|
||||
{
|
||||
CalculateLineCenter doCalc;
|
||||
f.ForEachPointRef(doCalc, scale);
|
||||
|
@ -116,7 +116,7 @@ m2::PointD GetCenter(FeatureType const & f, int scale)
|
|||
|
||||
default:
|
||||
{
|
||||
ASSERT_EQUAL ( type, feature::GEOM_AREA, () );
|
||||
ASSERT_EQUAL(type, GEOM_AREA, ());
|
||||
CalculatePointOnSurface doCalc(f.GetLimitRect(scale));
|
||||
f.ForEachTriangleRef(doCalc, scale);
|
||||
return doCalc.GetCenter();
|
||||
|
@ -124,6 +124,50 @@ m2::PointD GetCenter(FeatureType const & f, int scale)
|
|||
}
|
||||
}
|
||||
|
||||
m2::PointD GetCenter(FeatureType const & f) { return GetCenter(f, FeatureType::BEST_GEOMETRY); }
|
||||
m2::PointD GetCenter(FeatureType const & f)
|
||||
{
|
||||
return GetCenter(f, FeatureType::BEST_GEOMETRY);
|
||||
}
|
||||
|
||||
double GetMinDistance(FeatureType const & ft, m2::PointD const & pt, int scale)
|
||||
{
|
||||
double res = numeric_limits<double>::max();
|
||||
auto distanceFn = [&] (m2::PointD const & p)
|
||||
{
|
||||
double const d = MercatorBounds::DistanceOnEarth(p, pt);
|
||||
if (d < res)
|
||||
res = d;
|
||||
};
|
||||
|
||||
/// @todo Need to check projection here?
|
||||
EGeomType const type = ft.GetFeatureType();
|
||||
switch (type)
|
||||
{
|
||||
case GEOM_POINT:
|
||||
distanceFn(ft.GetCenter());
|
||||
break;
|
||||
|
||||
case GEOM_LINE:
|
||||
ft.ForEachPoint(distanceFn, scale);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT_EQUAL(type, GEOM_AREA, ());
|
||||
ft.ForEachTriangle([&] (m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
|
||||
{
|
||||
distanceFn(p1);
|
||||
distanceFn(p2);
|
||||
distanceFn(p3);
|
||||
}, scale);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
double GetMinDistance(FeatureType const & ft, m2::PointD const & pt)
|
||||
{
|
||||
return GetMinDistance(ft, pt, FeatureType::BEST_GEOMETRY);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,4 +10,7 @@ namespace feature
|
|||
m2::PointD GetCenter(FeatureType const & f, int scale);
|
||||
m2::PointD GetCenter(FeatureType const & f);
|
||||
|
||||
double GetMinDistance(FeatureType const & ft, m2::PointD const & pt, int scale);
|
||||
double GetMinDistance(FeatureType const & ft, m2::PointD const & pt);
|
||||
|
||||
}
|
||||
|
|
42
map/map_tests/address_tests.cpp
Normal file
42
map/map_tests/address_tests.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "search/reverse_geocoder.hpp"
|
||||
#include "search/search_string_utils.hpp"
|
||||
|
||||
#include "indexer/index.hpp"
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void TestAddress(search::ReverseGeocoder & coder,
|
||||
ms::LatLon const & ll, string const & stName, string const & hName)
|
||||
{
|
||||
search::ReverseGeocoder::Street street;
|
||||
search::ReverseGeocoder::Building building;
|
||||
coder.GetNearbyAddress(MercatorBounds::FromLatLon(ll), building, street);
|
||||
|
||||
string key;
|
||||
search::GetStreetNameAsKey(street.m_name, key);
|
||||
|
||||
TEST_EQUAL(stName, key, ());
|
||||
TEST_EQUAL(hName, building.m_name, ());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(ReverseGeocoder_Smoke)
|
||||
{
|
||||
platform::LocalCountryFile file = platform::LocalCountryFile::MakeForTesting("minsk-pass");
|
||||
|
||||
Index index;
|
||||
TEST_EQUAL(index.RegisterMap(file).second, MwmSet::RegResult::Success, ());
|
||||
|
||||
search::ReverseGeocoder coder(index);
|
||||
|
||||
TestAddress(coder, {53.89815, 27.54265}, "мясникова", "32");
|
||||
TestAddress(coder, {53.89953, 27.54189}, "немига", "42");
|
||||
TestAddress(coder, {53.89666, 27.54904}, "советская", "19");
|
||||
TestAddress(coder, {53.89724, 27.54983}, "независимости", "11");
|
||||
TestAddress(coder, {53.89745, 27.55835}, "карламаркса", "18А");
|
||||
}
|
|
@ -36,6 +36,7 @@ SOURCES += \
|
|||
gps_track_test.cpp \
|
||||
kmz_unarchive_test.cpp \
|
||||
mwm_url_tests.cpp \
|
||||
address_tests.cpp \
|
||||
|
||||
!linux* {
|
||||
SOURCES += working_time_tests.cpp \
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "reverse_geocoder.hpp"
|
||||
#include "search_string_utils.hpp"
|
||||
|
||||
#include "search/v2/house_to_street_table.hpp"
|
||||
|
||||
#include "indexer/feature.hpp"
|
||||
#include "indexer/feature_algo.hpp"
|
||||
#include "indexer/ftypes_matcher.hpp"
|
||||
|
@ -13,34 +15,27 @@ namespace search
|
|||
{
|
||||
namespace
|
||||
{
|
||||
size_t constexpr kMaxStreetIndex = 16;
|
||||
size_t constexpr kSimilarityThresholdPercent = 10;
|
||||
|
||||
/// @todo Need to check projection here?
|
||||
double CalculateMinDistance(FeatureType const & ft, m2::PointD const & pt)
|
||||
{
|
||||
ASSERT_EQUAL(ft.GetFeatureType(), feature::GEOM_LINE, ());
|
||||
|
||||
double res = numeric_limits<double>::max();
|
||||
ft.ForEachPoint([&] (m2::PointD const & p)
|
||||
{
|
||||
double const d = MercatorBounds::DistanceOnEarth(p, pt);
|
||||
if (d < res)
|
||||
res = d;
|
||||
}, FeatureType::BEST_GEOMETRY);
|
||||
|
||||
return res;
|
||||
}
|
||||
int const kQueryScale = scales::GetUpperScale();
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
double const ReverseGeocoder::kLookupRadiusM = 500.0;
|
||||
|
||||
// static
|
||||
m2::RectD ReverseGeocoder::GetLookupRect(m2::PointD const & center)
|
||||
{
|
||||
return MercatorBounds::RectByCenterXYAndSizeInMeters(center, kLookupRadiusM);
|
||||
}
|
||||
|
||||
void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, vector<Street> & streets)
|
||||
{
|
||||
m2::PointD const & center = feature::GetCenter(addrFt);
|
||||
m2::RectD const rect = MercatorBounds::RectByCenterXYAndSizeInMeters(
|
||||
center, kLookupRadiusM);
|
||||
GetNearbyStreets(feature::GetCenter(addrFt), streets);
|
||||
}
|
||||
|
||||
void ReverseGeocoder::GetNearbyStreets(m2::PointD const & center, vector<Street> & streets)
|
||||
{
|
||||
m2::RectD const rect = GetLookupRect(center);
|
||||
|
||||
auto const addStreet = [&](FeatureType const & ft)
|
||||
{
|
||||
|
@ -56,25 +51,49 @@ void ReverseGeocoder::GetNearbyStreets(FeatureType const & addrFt, vector<Street
|
|||
return;
|
||||
|
||||
ASSERT(!name.empty(), ());
|
||||
streets.push_back({ft.GetID(), CalculateMinDistance(ft, center), name});
|
||||
streets.push_back({ft.GetID(), feature::GetMinDistance(ft, center), name});
|
||||
};
|
||||
|
||||
m_index.ForEachInRect(addStreet, rect, scales::GetUpperScale());
|
||||
m_index.ForEachInRect(addStreet, rect, kQueryScale);
|
||||
sort(streets.begin(), streets.end(), my::CompareBy(&Street::m_distanceMeters));
|
||||
}
|
||||
|
||||
void ReverseGeocoder::GetNearbyBuildings(m2::PointD const & center, vector<Building> & buildings)
|
||||
{
|
||||
// Seems like a copy-paste here of the GetNearbyStreets function.
|
||||
// Trying to factor out common logic will cause many variables logic.
|
||||
|
||||
m2::RectD const rect = GetLookupRect(center);
|
||||
|
||||
auto const addBld = [&](FeatureType const & ft)
|
||||
{
|
||||
if (!ftypes::IsBuildingChecker::Instance()(ft))
|
||||
return;
|
||||
|
||||
// Skip empty house nubers.
|
||||
string const number = ft.GetHouseNumber();
|
||||
if (number.empty())
|
||||
return;
|
||||
|
||||
buildings.push_back({ft.GetID(), feature::GetMinDistance(ft, center),
|
||||
number, feature::GetCenter(ft)});
|
||||
};
|
||||
|
||||
m_index.ForEachInRect(addBld, rect, kQueryScale);
|
||||
sort(buildings.begin(), buildings.end(), my::CompareBy(&Building::m_distanceMeters));
|
||||
}
|
||||
|
||||
// static
|
||||
size_t ReverseGeocoder::GetMatchedStreetIndex(string const & keyName,
|
||||
vector<Street> const & streets)
|
||||
{
|
||||
strings::UniString const expected = strings::MakeUniString(keyName);
|
||||
|
||||
// Do limit possible return values.
|
||||
size_t const count = min(streets.size(), kMaxStreetIndex);
|
||||
|
||||
// Find the exact match or the best match in kSimilarityTresholdPercent limit.
|
||||
size_t const count = streets.size();
|
||||
size_t result = count;
|
||||
size_t minPercent = kSimilarityThresholdPercent + 1;
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
string key;
|
||||
|
@ -98,6 +117,39 @@ size_t ReverseGeocoder::GetMatchedStreetIndex(string const & keyName,
|
|||
}
|
||||
}
|
||||
|
||||
return (result < count ? result : streets.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
void ReverseGeocoder::GetNearbyAddress(m2::PointD const & center,
|
||||
Building & building, Street & street)
|
||||
{
|
||||
vector<Building> buildings;
|
||||
GetNearbyBuildings(center, buildings);
|
||||
|
||||
vector<Street> streets;
|
||||
unique_ptr<search::v2::HouseToStreetTable> table;
|
||||
MwmSet::MwmId mwmId;
|
||||
|
||||
for (auto const & b : buildings)
|
||||
{
|
||||
if (!table || mwmId != b.m_id.m_mwmId)
|
||||
{
|
||||
auto handle = m_index.GetMwmHandleById(b.m_id.m_mwmId);
|
||||
auto value = handle.GetValue<MwmValue>();
|
||||
if (value)
|
||||
table = search::v2::HouseToStreetTable::Load(*value);
|
||||
}
|
||||
|
||||
GetNearbyStreets(b.m_center, streets);
|
||||
|
||||
uint32_t const ind = table->Get(b.m_id.m_index);
|
||||
if (ind < streets.size())
|
||||
{
|
||||
building = b;
|
||||
street = streets[ind];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace search
|
||||
|
|
|
@ -17,25 +17,48 @@ class ReverseGeocoder
|
|||
{
|
||||
Index & m_index;
|
||||
|
||||
public:
|
||||
static double const kLookupRadiusM;
|
||||
|
||||
ReverseGeocoder(Index & index) : m_index(index) {}
|
||||
|
||||
struct Street
|
||||
struct Object
|
||||
{
|
||||
FeatureID m_id;
|
||||
double m_distanceMeters;
|
||||
string m_name;
|
||||
|
||||
Object() = default;
|
||||
Object(FeatureID const & id, double dist, string const & name)
|
||||
: m_id(id), m_distanceMeters(dist), m_name(name)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static double const kLookupRadiusM;
|
||||
static m2::RectD GetLookupRect(m2::PointD const & center);
|
||||
|
||||
explicit ReverseGeocoder(Index & index) : m_index(index) {}
|
||||
|
||||
using Street = Object;
|
||||
|
||||
struct Building : public Object
|
||||
{
|
||||
m2::PointD m_center;
|
||||
|
||||
Building() = default;
|
||||
Building(FeatureID const & id, double dist, string const & hn, m2::PointD const & center)
|
||||
: Object(id, dist, hn), m_center(center)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void GetNearbyStreets(FeatureType const & addrFt, vector<Street> & streets);
|
||||
|
||||
static size_t GetMatchedStreetIndex(string const & keyName, vector<Street> const & streets);
|
||||
|
||||
void GetNearbyAddress(m2::PointD const & center,
|
||||
Building & building, Street & street);
|
||||
|
||||
private:
|
||||
template <class TCompare>
|
||||
void GetNearbyStreets(FeatureType const & ft, TCompare comp, vector<Street> & streets);
|
||||
void GetNearbyStreets(m2::PointD const & center, vector<Street> & streets);
|
||||
void GetNearbyBuildings(m2::PointD const & center, vector<Building> & buildings);
|
||||
};
|
||||
|
||||
} // namespace search
|
||||
|
|
|
@ -53,9 +53,9 @@ unique_ptr<HouseToStreetTable> HouseToStreetTable::Load(MwmValue & value)
|
|||
if (format == MwmTraits::HouseToStreetTableFormat::Fixed3BitsDDVector)
|
||||
result.reset(new Fixed3BitsTable(value));
|
||||
}
|
||||
catch (Reader::OpenException & e)
|
||||
catch (Reader::OpenException const & ex)
|
||||
{
|
||||
LOG(LWARNING, (e.Msg()));
|
||||
LOG(LWARNING, (ex.Msg()));
|
||||
}
|
||||
|
||||
if (!result)
|
||||
|
|
|
@ -14,6 +14,8 @@ class HouseToStreetTable
|
|||
public:
|
||||
virtual ~HouseToStreetTable() = default;
|
||||
|
||||
/// @todo Actually, value may be nullptr in the very common case .
|
||||
/// It's better to construct a table from MwmHandle.
|
||||
static unique_ptr<HouseToStreetTable> Load(MwmValue & value);
|
||||
|
||||
// Returns an index number of a correct street corresponding to a
|
||||
|
|
Loading…
Add table
Reference in a new issue