Read Houses and Make simple search

This commit is contained in:
Kirill Zhdanovich 2013-12-05 16:07:00 +01:00 committed by Alex Zolotarev
parent a1bb8c1cde
commit d18a824355
4 changed files with 542 additions and 30 deletions

View file

@ -1,6 +1,324 @@
#include "house_detector.hpp"
#include "../indexer/classificator.hpp"
search::HouseDetector::HouseDetector()
#include "../base/logging.hpp"
#include "../geometry/distance.hpp"
#include "../geometry/distance_on_sphere.hpp"
#include "../std/iostream.hpp"
#include "../std/set.hpp"
#include "../std/bind.hpp"
namespace search
{
int const BUILDING_PROCESS_SCALE = 15;
double const STREET_CONNECTION_LENGTH_M = 25.0;
struct StreetCreator
{
Street * street;
StreetCreator(Street * st): street(st)
{}
void operator () (CoordPointT const & point)
{
street->m_points.push_back(m2::PointD(point.first, point.second));
}
};
FeatureLoader::FeatureLoader(Index const * pIndex)
: m_pIndex(pIndex), m_pGuard(0)
{
}
FeatureLoader::~FeatureLoader()
{
Free();
}
void FeatureLoader::CreateLoader(size_t mwmID)
{
if (m_pGuard == 0 || mwmID != m_pGuard->GetID())
{
delete m_pGuard;
m_pGuard = new Index::FeaturesLoaderGuard(*m_pIndex, mwmID);
}
}
void FeatureLoader::Load(FeatureID const & id, FeatureType & f)
{
CreateLoader(id.m_mwm);
m_pGuard->GetFeature(id.m_offset, f);
}
void FeatureLoader::Free()
{
delete m_pGuard;
m_pGuard = 0;
}
template <class ToDo>
void FeatureLoader::ForEachInRect(m2::RectD const & rect, ToDo toDo)
{
m_pIndex->ForEachInRect(toDo, rect, BUILDING_PROCESS_SCALE);
}
m2::RectD Street::GetLimitRect(double offsetMeters) const
{
m2::RectD rect;
for (size_t i = 0; i < m_points.size(); ++i)
rect.Add(MercatorBounds::RectByCenterXYAndSizeInMeters(m_points[i], offsetMeters));
return rect;
}
HouseDetector::HouseDetector(Index const * pIndex)
: m_loader(pIndex), m_end2st(LessWithEpsilon(&m_epsMercator)), m_streetNum(0)
{
// default value for conversions
SetMetres2Mercator(360.0 / 40.0E06);
}
void HouseDetector::SetMetres2Mercator(double factor)
{
m_epsMercator = factor * STREET_CONNECTION_LENGTH_M;
}
void HouseDetector::FillQueue(queue<Street *> & q, Street const * street, bool isBeg)
{
pair<IterT, IterT> const & range =
m_end2st.equal_range(isBeg ? street->m_points.front() : street->m_points.back());
for (IterT it = range.first; it != range.second; ++it)
{
/// @todo create clever street names compare functions
if (it->second->m_number == -1 && street->m_name == it->second->m_name)
{
it->second->m_number = m_streetNum;
q.push(it->second);
}
}
}
void HouseDetector::Bfs(Street * st)
{
queue <Street *> q;
q.push(st);
st->m_number = m_streetNum;
while(!q.empty())
{
Street * street = q.front();
q.pop();
FillQueue(q, street, true);
FillQueue(q, street, false);
}
}
void HouseDetector::LoadStreets(vector<FeatureID> & ids)
{
sort(ids.begin(), ids.end());
for (size_t i = 0; i < ids.size(); ++i)
{
if (m_id2st.find(ids[i]) != m_id2st.end())
continue;
FeatureType f;
m_loader.Load(ids[i], f);
if (f.GetFeatureType() == feature::GEOM_LINE)
{
/// @todo Assume that default name always exist as primary compare key.
string name;
if (!f.GetName(0, name) || name.empty())
continue;
Street * st = new Street();
st->m_name = name;
f.ForEachPoint(StreetCreator(st), FeatureType::BEST_GEOMETRY);
m_id2st[ids[i]] = st;
m_end2st.insert(pair<m2::PointD, Street *> (st->m_points.front(), st));
m_end2st.insert(pair<m2::PointD, Street *> (st->m_points.back(), st));
}
else
ASSERT(false, ());
}
m_loader.Free();
}
int HouseDetector::MergeStreets()
{
for (IterT it = m_end2st.begin(); it != m_end2st.end(); ++it)
{
if (it->second->m_number == -1)
{
Street * st = it->second;
++m_streetNum;
Bfs(st);
}
}
return m_streetNum;
}
namespace
{
class HouseChecker
{
uint32_t m_types[2];
public:
HouseChecker()
{
Classificator const & c = classif();
char const * arr0[] = { "building" };
m_types[0] = c.GetTypeByPath(vector<string>(arr0, arr0 + 1));
char const * arr1[] = { "building", "address" };
m_types[1] = c.GetTypeByPath(vector<string>(arr1, arr1 + 2));
}
bool IsHouse(feature::TypesHolder const & types)
{
for (size_t i = 0; i < ARRAY_SIZE(m_types); ++i)
if (types.Has(m_types[i]))
return true;
return false;
}
};
double GetDistanceMeters(m2::PointD const & p1, m2::PointD const & p2)
{
return ms::DistanceOnEarth(MercatorBounds::YToLat(p1.y), MercatorBounds::XToLon(p1.x),
MercatorBounds::YToLat(p2.y), MercatorBounds::XToLon(p2.x));
}
class ProjectionCalcToStreet
{
Street const * m_street;
double m_distanceMeters;
typedef m2::ProjectionToSection<m2::PointD> ProjectionT;
vector<ProjectionT> m_calcs;
public:
ProjectionCalcToStreet(Street const * st, double distanceMeters)
: m_street(st), m_distanceMeters(distanceMeters)
{
}
bool GetProjection(m2::PointD const & pt, HouseProjection & proj)
{
if (m_calcs.empty())
{
for (size_t i = 1; i < m_street->m_points.size(); ++i)
{
m_calcs.push_back(ProjectionT());
m_calcs.back().SetBounds(m_street->m_points[i-1], m_street->m_points[i]);
}
}
m2::PointD resPt;
double dist = numeric_limits<double>::max();
double resDist = numeric_limits<double>::max();
for (size_t i = 0; i < m_calcs.size(); ++i)
{
m2::PointD const proj = m_calcs[i](pt);
dist = GetDistanceMeters(pt, proj);
if (dist < resDist)
{
resPt = proj;
resDist = dist;
}
}
if (resDist <= m_distanceMeters)
{
proj.m_proj = resPt;
proj.m_distance = dist;
return true;
}
else
return false;
}
};
}
template <class ProjectionCalcT>
void HouseDetector::ReadHouse(FeatureType const & f, Street * st, ProjectionCalcT & calc)
{
static HouseChecker checker;
if (checker.IsHouse(feature::TypesHolder(f)))
{
map<FeatureID, House *>::iterator const it = m_id2house.find(f.GetID());
m2::PointD const pt = (it == m_id2house.end()) ?
f.GetLimitRect(BUILDING_PROCESS_SCALE).Center() : it->second->m_point;
HouseProjection pr;
if (calc.GetProjection(pt, pr))
{
House * p;
if (it == m_id2house.end())
{
p = new House();
p->m_point = pt;
p->m_number = f.GetHouseNumber();
m_id2house[f.GetID()] = p;
}
else
{
p = it->second;
ASSERT(p != 0, ());
}
pr.m_house = p;
st->m_houses.push_back(pr);
}
}
}
void HouseDetector::ReadHouses(Street * st, double offsetMeters)
{
if (st->m_housesReaded)
return;
ProjectionCalcToStreet calcker(st, offsetMeters);
m_loader.ForEachInRect(st->GetLimitRect(offsetMeters),
bind(&HouseDetector::ReadHouse<ProjectionCalcToStreet>, this, _1, st, ref(calcker)));
st->m_housesReaded = true;
}
void HouseDetector::ReadAllHouses(double offsetMeters)
{
for (map<FeatureID, Street *>::iterator it = m_id2st.begin(); it != m_id2st.end(); ++it)
ReadHouses(it->second, offsetMeters);
}
void HouseDetector::MatchAllHouses(string const & houseNumber, vector<HouseProjection> & res)
{
/// @temporary decision to avoid duplicating houses
set<House const *> s;
for (map<FeatureID, Street *>::iterator it = m_id2st.begin(); it != m_id2st.end();++it)
{
Street const * st = it->second;
for (size_t i = 0; i < st->m_houses.size(); ++i)
{
House const * house = st->m_houses[i].m_house;
if (house->m_number == houseNumber && s.count(house) == 0)
{
res.push_back(st->m_houses[i]);
s.insert(house);
}
}
}
}
}

View file

@ -1,70 +1,118 @@
#pragma once
#include "../indexer/feature_decl.hpp"
#include "../indexer/index.hpp"
#include "../geometry/point2d.hpp"
#include "../std/string.hpp"
#include "../std/queue.hpp"
namespace search
{
const double s_epsilon = 10.0 * 360.0 / 40E6;
class FeatureLoader
{
Index const * m_pIndex;
Index::FeaturesLoaderGuard * m_pGuard;
void CreateLoader(size_t mwmID);
public:
FeatureLoader(Index const * pIndex);
~FeatureLoader();
void Load(FeatureID const & id, FeatureType & f);
void Free();
template <class ToDo> void ForEachInRect(m2::RectD const & rect, ToDo toDo);
};
class House
struct House
{
string m_number;
m2::PointD m_point;
public:
};
struct HouseProjection
{
House const * m_house;
m2::PointD m_proj;
double m_distance;
static bool LessDistance(HouseProjection const & r1, HouseProjection const & r2)
{
return r1.m_distance < r2.m_distance;
}
};
// many features combines to street
class Street
struct Street
{
string m_name;
vector<m2::PointD> m_points;
vector<House> m_houses;
vector<HouseProjection> m_houses;
bool m_housesReaded;
int m_number;
Street() : m_housesReaded(false), m_number(-1) {}
/// Get limit rect for street with ortho offset to the left and right.
m2::RectD GetLimitRect(double offsetMeters) const;
};
class HouseDetector
{
FeatureLoader m_loader;
// id -> street
//map<FeatureID, Street *> m_id2st;
map<FeatureID, Street *> m_id2st;
map<FeatureID, House *> m_id2house;
public:
struct LessWithEpsilon
class LessWithEpsilon
{
//static double s_epsilon;
double * m_eps;
public:
LessWithEpsilon(double * eps) : m_eps(eps) {}
bool operator() (m2::PointD const & p1, m2::PointD const & p2) const
{
if (p1.x + s_epsilon < p2.x)
if (p1.x + *m_eps < p2.x)
return true;
else if (p2.x + s_epsilon < p1.x)
else if (p2.x + *m_eps < p1.x)
return false;
else
{
return (p1.y + s_epsilon < p2.y);
}
return (p1.y + *m_eps < p2.y);
}
};
private:
// start, end point -> street
double m_epsMercator;
typedef multimap<m2::PointD, Street *, LessWithEpsilon>::iterator IterT;
multimap<m2::PointD, Street *, LessWithEpsilon> m_end2st;
Street * GetNext(Street const *);
Street * GetPrev(Street const *);
int m_streetNum;
void FillQueue(queue<Street *> & q, Street const * street, bool isBeg);
void Bfs(Street * st);
template <class ProjectionCalcT>
void ReadHouse(FeatureType const & f, Street * st, ProjectionCalcT & calc);
void SetMetres2Mercator(double factor);
public:
HouseDetector();
HouseDetector(Index const * pIndex);
void LoadStreets(vector<FeatureID> & ids);
/// @return number of different joined streets.
int MergeStreets();
void ReadHouses(Street * st, double offsetMeters);
void ReadAllHouses(double offsetMeters);
void MatchAllHouses(string const & houseNumber, vector<HouseProjection> & res);
};
}

View file

@ -1,9 +1,22 @@
#include "../../testing/testing.hpp"
#include "../../base/logging.hpp"
#include "../../platform/platform.hpp"
#include "../../indexer/scales.hpp"
#include "../../indexer/index.hpp"
#include "../../indexer/classificator_loader.hpp"
#include "../house_detector.hpp"
#include "../../std/iostream.hpp"
UNIT_TEST(LESS_WITH_EPSILON)
{
search::HouseDetector::LessWithEpsilon compare;
double q = 3.0 * 360.0 / 40.0E06;
search::HouseDetector::LessWithEpsilon compare(&q);
{
m2::PointD a(1, 1);
m2::PointD b(2, 2);
@ -24,38 +37,168 @@ UNIT_TEST(LESS_WITH_EPSILON)
}
{
m2::PointD a(1, 1);
m2::PointD b(1 + search::s_epsilon, 1);
m2::PointD b(1 + q, 1);
TEST(!compare(a, b), ());
TEST(!compare(b, a), ());
}
{
m2::PointD a(1, 1);
m2::PointD b(1 + search::s_epsilon, 1 - search::s_epsilon);
m2::PointD b(1 + q, 1 - q);
TEST(!compare(a, b), ());
TEST(!compare(b, a), ());
}
{
m2::PointD a(1, 1 - search::s_epsilon);
m2::PointD b(1 + search::s_epsilon, 1);
m2::PointD a(1, 1 - q);
m2::PointD b(1 + q, 1);
TEST(!compare(a, b), ());
TEST(!compare(b, a), ());
}
{
m2::PointD a(1 - search::s_epsilon, 1 - search::s_epsilon);
m2::PointD a(1 - q, 1 - q);
m2::PointD b(1, 1);
TEST(!compare(a, b), ());
TEST(!compare(b, a), ());
}
{
m2::PointD a(1, 1);
m2::PointD b(1 + search::s_epsilon, 1 + search::s_epsilon);
m2::PointD b(1 + q, 1 + q);
TEST(!compare(a, b), ());
TEST(!compare(b, a), ());
}
{
m2::PointD a(1, 1);
m2::PointD b(1 + 2 * search::s_epsilon, 1 + search::s_epsilon);
m2::PointD b(1 + 2 * q, 1 + q);
TEST(compare(a, b), ());
TEST(!compare(b, a), ());
}
}
struct Process
{
vector <FeatureID> vect;
vector <string> streetNames;
void operator() (FeatureType const & f)
{
if (f.GetFeatureType() == feature::GEOM_LINE)
{
string name;
if (f.GetName(0, name))
{
for (size_t i = 0; i < streetNames.size(); ++i)
if (name == streetNames[i])
{
vect.push_back(f.GetID());
break;
}
}
}
}
};
UNIT_TEST(STREET_MERGE_TEST)
{
classificator::Load();
Index index;
m2::RectD rect;
index.Add("minsk-pass.mwm", rect);
{
search::HouseDetector houser(&index);
Process toDo;
toDo.streetNames.push_back("Московская улица");
index.ForEachInScale(toDo, scales::GetUpperScale());
houser.LoadStreets(toDo.vect);
TEST_EQUAL(houser.MergeStreets(), 1, ());
}
{
search::HouseDetector houser(&index);
Process toDo;
toDo.streetNames.push_back("проспект Независимости");
toDo.streetNames.push_back("Московская улица");
index.ForEachInScale(toDo, scales::GetUpperScale());
houser.LoadStreets(toDo.vect);
TEST_EQUAL(houser.MergeStreets(), 2, ());
}
{
search::HouseDetector houser(&index);
Process toDo;
toDo.streetNames.push_back("проспект Независимости");
toDo.streetNames.push_back("Московская улица");
toDo.streetNames.push_back("Вишнёвый переулок");
toDo.streetNames.push_back("Студенческий переулок");
toDo.streetNames.push_back("Полоцкий переулок");
index.ForEachInScale(toDo, scales::GetUpperScale());
houser.LoadStreets(toDo.vect);
TEST_EQUAL(houser.MergeStreets(), 5, ());
}
{
search::HouseDetector houser(&index);
Process toDo;
toDo.streetNames.push_back("проспект Независимости");
toDo.streetNames.push_back("Московская улица");
toDo.streetNames.push_back("улица Кирова");
toDo.streetNames.push_back("улица Городской Вал");
index.ForEachInScale(toDo, scales::GetUpperScale());
houser.LoadStreets(toDo.vect);
TEST_EQUAL(houser.MergeStreets(), 4, ());
}
}
namespace
{
void GetCanditates(Index & index, vector<string> const & v, string const & houseName,
vector<search::HouseProjection> & res, double offset)
{
search::HouseDetector houser(&index);
Process toDo;
toDo.streetNames = v;
index.ForEachInScale(toDo, scales::GetUpperScale());
houser.LoadStreets(toDo.vect);
houser.ReadAllHouses(offset);
houser.MatchAllHouses(houseName, res);
sort(res.begin(), res.end(), &search::HouseProjection::LessDistance);
}
}
UNIT_TEST(SEARCH_HOUSE_NUMBER_SMOKE_TEST)
{
classificator::Load();
Index index;
m2::RectD rect;
index.Add("minsk-pass.mwm", rect);
{
vector <string> streetName(1, "Московская улица");
vector <search::HouseProjection> res;
string houseName = "7";
GetCanditates(index, streetName, houseName, res, 100);
TEST_EQUAL(res.size(), 1, ());
TEST_ALMOST_EQUAL(res[0].m_house->m_point, m2::PointD(27.539850827603416406, 64.222406776416349317), ());
TEST_EQUAL(res[0].m_house->m_number, houseName, ());
}
{
vector <string> streetName(1, "проспект Независимости");
vector <search::HouseProjection> res;
string houseName = "10";
GetCanditates(index, streetName, houseName, res, 40);
TEST_EQUAL(res.size(), 1, ());
TEST_ALMOST_EQUAL(res[0].m_house->m_point, m2::PointD(27.551358845467561309, 64.234708728154814139), ());
TEST_EQUAL(res[0].m_house->m_number, houseName, ());
}
{
vector <string> streetName(1, "улица Ленина");
vector <search::HouseProjection> res;
string houseName = "9";
GetCanditates(index, streetName, houseName, res, 50);
TEST_EQUAL(res.size(), 1, ());
TEST_ALMOST_EQUAL(res[0].m_house->m_point, m2::PointD(27.560341563525355468, 64.240918042070561), ());
TEST_EQUAL(res[0].m_house->m_number, houseName, ());
}
}

View file

@ -6,7 +6,8 @@ CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../..
DEPENDENCIES = search platform indexer geometry coding base
DEPENDENCIES = search platform indexer geometry coding base \
protobuf
include($$ROOT_DIR/common.pri)
@ -17,6 +18,8 @@ win32 {
win32-g++: LIBS += -lpthread
}
macx-*: LIBS *= "-framework Foundation" "-framework IOKit"
SOURCES += \
../../testing/testingmain.cpp \
#keyword_matcher_test.cpp \