Merge pull request #3943 from mgsergio/booking-area-hotels

[booking] area hotels
This commit is contained in:
Ilya Zverev 2016-08-05 16:09:24 +04:00 committed by GitHub
commit 970abc4add
20 changed files with 646 additions and 413 deletions

View file

@ -1,10 +1,12 @@
#include "generator/booking_dataset.hpp"
#include "generator/booking_scoring.hpp"
#include "generator/feature_builder.hpp"
#include "platform/local_country_file_utils.hpp"
#include "platform/platform.hpp"
#include "indexer/classificator.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "geometry/distance_on_sphere.hpp"
@ -14,18 +16,13 @@
#include "std/fstream.hpp"
#include "std/iostream.hpp"
#include "std/sstream.hpp"
namespace generator
{
namespace
{
bool CheckForValues(string const & value)
{
auto const & tags = ftypes::IsHotelChecker::GetHotelTags();
return find(tags.begin(), tags.end(), value) != tags.end();
}
string EscapeTabs(string const & str)
{
stringstream ss;
@ -124,20 +121,19 @@ BookingDataset::BookingDataset(istream & dataSource, string const & addressRefer
LoadHotels(dataSource, addressReferencePath);
}
bool BookingDataset::BookingFilter(OsmElement const & e) const
size_t BookingDataset::GetMatchingHotelIndex(FeatureBuilder1 const & fb) const
{
return Filter(e, [&](OsmElement const & e)
{
return MatchWithBooking(e);
});
if (CanBeBooking(fb))
return MatchWithBooking(fb);
return kInvalidHotelIndex;
}
bool BookingDataset::TourismFilter(OsmElement const & e) const
bool BookingDataset::CanBeBooking(FeatureBuilder1 const & fb) const
{
return Filter(e, [&](OsmElement const & e)
{
return true;
});
if (fb.GetName(StringUtf8Multilang::kDefaultCode).empty())
return false;
return ftypes::IsHotelChecker::Instance()(fb.GetTypes());
}
BookingDataset::Hotel const & BookingDataset::GetHotel(size_t index) const
@ -152,73 +148,80 @@ BookingDataset::Hotel & BookingDataset::GetHotel(size_t index)
return m_hotels[index];
}
vector<size_t> BookingDataset::GetNearestHotels(double lat, double lon, size_t limit,
double maxDistance /* = 0.0 */) const
vector<size_t> BookingDataset::GetNearestHotels(ms::LatLon const & latLon, size_t const limit,
double const maxDistance /* = 0.0 */) const
{
namespace bgi = boost::geometry::index;
vector<size_t> indexes;
for_each(bgi::qbegin(m_rtree, bgi::nearest(TPoint(lat, lon), limit)), bgi::qend(m_rtree),
[&](TValue const & v)
for_each(bgi::qbegin(m_rtree, bgi::nearest(TPoint(latLon.lat, latLon.lon), limit)),
bgi::qend(m_rtree), [&](TValue const & v)
{
auto const & hotel = m_hotels[v.second];
double const dist = ms::DistanceOnEarth(lat, lon, hotel.lat, hotel.lon);
auto const & hotel = GetHotel(v.second);
double const dist = ms::DistanceOnEarth(latLon.lat, latLon.lon, hotel.lat, hotel.lon);
if (maxDistance != 0.0 && dist > maxDistance /* max distance in meters */)
return;
indexes.emplace_back(v.second);
});
return indexes;
}
void BookingDataset::BuildFeatures(function<void(OsmElement *)> const & fn) const
void BookingDataset::BuildHotel(size_t const hotelIndex,
function<void(FeatureBuilder1 &)> const & fn) const
{
for (auto const & hotel : m_hotels)
auto const & hotel = m_hotels[hotelIndex];
FeatureBuilder1 fb;
FeatureParams params;
fb.SetCenter(MercatorBounds::FromLatLon(hotel.lat, hotel.lon));
auto & metadata = params.GetMetadata();
metadata.Set(feature::Metadata::FMD_SPONSORED_ID, strings::to_string(hotel.id));
metadata.Set(feature::Metadata::FMD_WEBSITE, hotel.descUrl);
metadata.Set(feature::Metadata::FMD_RATING, strings::to_string(hotel.ratingUser));
metadata.Set(feature::Metadata::FMD_STARS, strings::to_string(hotel.stars));
metadata.Set(feature::Metadata::FMD_PRICE_RATE, strings::to_string(hotel.priceCategory));
// params.AddAddress(hotel.address);
// TODO(mgsergio): addr:full ???
if (!hotel.street.empty())
fb.AddStreet(hotel.street);
if (!hotel.houseNumber.empty())
fb.AddHouseNumber(hotel.houseNumber);
params.AddName(StringUtf8Multilang::GetLangByCode(StringUtf8Multilang::kDefaultCode),
hotel.name);
if (!hotel.translations.empty())
{
OsmElement e;
e.type = OsmElement::EntityType::Node;
e.id = 1;
e.lat = hotel.lat;
e.lon = hotel.lon;
e.AddTag("sponsored", "booking");
e.AddTag("name", hotel.name);
e.AddTag("ref:sponsored", strings::to_string(hotel.id));
e.AddTag("website", hotel.descUrl);
e.AddTag("rating:sponsored", strings::to_string(hotel.ratingUser));
e.AddTag("stars", strings::to_string(hotel.stars));
e.AddTag("price_rate", strings::to_string(hotel.priceCategory));
e.AddTag("addr:full", hotel.address);
if (!hotel.translations.empty())
// TODO(mgsergio): Move parsing to the hotel costruction stage.
vector<string> parts;
strings::ParseCSVRow(hotel.translations, '|', parts);
CHECK_EQUAL(parts.size() % 3, 0, ("Invalid translation string:", hotel.translations));
for (auto i = 0; i < parts.size(); i += 3)
{
vector<string> parts;
strings::ParseCSVRow(hotel.translations, '|', parts);
CHECK(parts.size() % 3 == 0, ());
for (auto i = 0; i < parts.size(); i += 3)
{
e.AddTag("name:" + parts[i], parts[i + 1]);
e.AddTag("addr:full:" + parts[i], parts[i + 2]);
}
auto const langCode = StringUtf8Multilang::GetLangIndex(parts[i]);
params.AddName(StringUtf8Multilang::GetLangByCode(langCode), parts[i + 1]);
// TODO(mgsergio): e.AddTag("addr:full:" + parts[i], parts[i + 2]);
}
}
if (!hotel.street.empty())
e.AddTag("addr:street", hotel.street);
if (!hotel.houseNumber.empty())
e.AddTag("addr:housenumber", hotel.houseNumber);
// Matching booking.com hotel types to OpenStreetMap values.
// Booking types are listed in the closed API docs.
switch (hotel.type)
{
auto const & clf = classif();
params.AddType(clf.GetTypeByPath({"sponsored", "booking"}));
// Matching booking.com hotel types to OpenStreetMap values.
// Booking types are listed in the closed API docs.
switch (hotel.type)
{
case 19:
case 205: e.AddTag("tourism", "motel"); break;
case 205: params.AddType(clf.GetTypeByPath({"tourism", "motel"})); break;
case 21:
case 206:
case 212: e.AddTag("tourism", "resort"); break;
case 212: params.AddType(clf.GetTypeByPath({"tourism", "resort"})); break;
case 3:
case 23:
@ -231,7 +234,7 @@ void BookingDataset::BuildFeatures(function<void(OsmElement *)> const & fn) cons
case 210:
case 216:
case 220:
case 223: e.AddTag("tourism", "guest_house"); break;
case 223: params.AddType(clf.GetTypeByPath({"tourism", "guest_house"})); break;
case 14:
case 204:
@ -239,29 +242,30 @@ void BookingDataset::BuildFeatures(function<void(OsmElement *)> const & fn) cons
case 218:
case 219:
case 226:
case 222: e.AddTag("tourism", "hotel"); break;
case 222: params.AddType(clf.GetTypeByPath({"tourism", "hotel"})); break;
case 211:
case 224:
case 228: e.AddTag("tourism", "chalet"); break;
case 228: params.AddType(clf.GetTypeByPath({"tourism", "chalet"})); break;
case 13:
case 225:
case 203: e.AddTag("tourism", "hostel"); break;
case 203: params.AddType(clf.GetTypeByPath({"tourism", "hostel"})); break;
case 215:
case 221:
case 227:
case 2:
case 201: e.AddTag("tourism", "apartment"); break;
case 201: params.AddType(clf.GetTypeByPath({"tourism", "apartment"})); break;
case 214: e.AddTag("tourism", "camp_site"); break;
case 214: params.AddType(clf.GetTypeByPath({"tourism", "camp_site"})); break;
default: e.AddTag("tourism", "hotel"); break;
}
fn(&e);
default: params.AddType(clf.GetTypeByPath({"tourism", "hotel"})); break;
}
fb.SetParams(params);
fn(fb);
}
void BookingDataset::LoadHotels(istream & src, string const & addressReferencePath)
@ -306,58 +310,23 @@ void BookingDataset::LoadHotels(istream & src, string const & addressReferencePa
}
}
bool BookingDataset::MatchWithBooking(OsmElement const & e) const
size_t BookingDataset::MatchWithBooking(FeatureBuilder1 const & fb) const
{
string name;
for (auto const & tag : e.Tags())
{
if (tag.key == "name")
{
name = tag.value;
break;
}
}
auto const name = fb.GetName(StringUtf8Multilang::kDefaultCode);
if (name.empty())
return false;
// Find |kMaxSelectedElements| nearest values to a point.
auto const bookingIndexes =
GetNearestHotels(e.lat, e.lon, kMaxSelectedElements, kDistanceLimitInMeters);
bool matched = false;
auto const bookingIndexes = GetNearestHotels(MercatorBounds::ToLatLon(fb.GetKeyPoint()),
kMaxSelectedElements, kDistanceLimitInMeters);
for (size_t const j : bookingIndexes)
{
if (booking_scoring::Match(GetHotel(j), e).IsMatched())
break;
if (booking_scoring::Match(GetHotel(j), fb).IsMatched())
return j;
}
return matched;
return kInvalidHotelIndex;
}
bool BookingDataset::Filter(OsmElement const & e,
function<bool(OsmElement const &)> const & fn) const
{
if (e.type != OsmElement::EntityType::Node)
return false;
if (e.Tags().empty())
return false;
bool matched = false;
for (auto const & tag : e.Tags())
{
if (tag.key == "tourism" && CheckForValues(tag.value))
{
matched = fn(e);
break;
}
}
// TODO: Need to write file with dropped osm features.
return matched;
}
} // namespace generator

View file

@ -1,7 +1,5 @@
#pragma once
#include "generator/osm_element.hpp"
#include "indexer/index.hpp"
#include "search/reverse_geocoder.hpp"
@ -12,15 +10,19 @@
#include "boost/geometry/index/rtree.hpp"
#include "std/function.hpp"
#include "std/limits.hpp"
#include "std/string.hpp"
class FeatureBuilder1;
namespace generator
{
class BookingDataset
{
public:
double static constexpr kDistanceLimitInMeters = 150;
size_t static constexpr kMaxSelectedElements = 3;
static double constexpr kDistanceLimitInMeters = 150;
static size_t constexpr kMaxSelectedElements = 3;
static size_t constexpr kInvalidHotelIndex = numeric_limits<size_t>::max();
struct Hotel
{
@ -77,33 +79,34 @@ public:
explicit BookingDataset(string const & dataPath, string const & addressReferencePath = string());
explicit BookingDataset(istream & dataSource, string const & addressReferencePath = string());
bool BookingFilter(OsmElement const & e) const;
bool TourismFilter(OsmElement const & e) const;
/// @return an index of a matched hotel or kInvalidHotelIndex on failure.
size_t GetMatchingHotelIndex(FeatureBuilder1 const & fb) const;
/// @return true if |fb| is a hotel with a name.
bool CanBeBooking(FeatureBuilder1 const & fb) const;
inline size_t Size() const { return m_hotels.size(); }
Hotel const & GetHotel(size_t index) const;
Hotel & GetHotel(size_t index);
vector<size_t> GetNearestHotels(double lat, double lon, size_t limit,
vector<size_t> GetNearestHotels(ms::LatLon const & latLon, size_t limit,
double maxDistance = 0.0) const;
bool MatchByName(string const & osmName, vector<size_t> const & bookingIndexes) const;
void BuildFeatures(function<void(OsmElement *)> const & fn) const;
void BuildHotel(size_t hotelIndex, function<void(FeatureBuilder1 &)> const & fn) const;
protected:
vector<Hotel> m_hotels;
// create the rtree using default constructor
using TPoint = boost::geometry::model::point<float, 2, boost::geometry::cs::cartesian>;
using TBox = boost::geometry::model::box<TPoint>;
using TValue = pair<TBox, size_t>;
// Create the rtree using default constructor.
boost::geometry::index::rtree<TValue, boost::geometry::index::quadratic<16>> m_rtree;
void LoadHotels(istream & path, string const & addressReferencePath);
bool MatchWithBooking(OsmElement const & e) const;
bool Filter(OsmElement const & e, function<bool(OsmElement const &)> const & fn) const;
/// @return an index of a matched hotel or numeric_limits<size_t>::max() on failure.
size_t MatchWithBooking(FeatureBuilder1 const & e) const;
};
ostream & operator<<(ostream & s, BookingDataset::Hotel const & h);
} // namespace generator

View file

@ -1,18 +1,22 @@
#include "generator/booking_dataset.hpp"
#include "generator/booking_scoring.hpp"
#include "generator/feature_builder.hpp"
#include "generator/osm_source.hpp"
#include "indexer/classificator_loader.hpp"
#include "geometry/distance_on_sphere.hpp"
#include "coding/file_name_utils.hpp"
#include "std/fstream.hpp"
#include "std/iostream.hpp"
#include "std/numeric.hpp"
#include "std/random.hpp"
#include "std/unique_ptr.hpp"
#include "3party/gflags/src/gflags/gflags.h"
DEFINE_bool(generate_classif, false, "Generate classificator.");
DEFINE_string(osm_file_name, "", "Input .o5m file");
DEFINE_string(booking_data, "", "Path to booking data in .tsv format");
DEFINE_string(sample_data, "", "Sample output path");
@ -21,18 +25,155 @@ DEFINE_uint64(seed, minstd_rand::default_seed, "Seed for random shuffle");
using namespace generator;
ostream & operator<<(ostream & s, OsmElement const & e)
namespace
{
for (auto const & tag : e.Tags())
string PrintBuilder(FeatureBuilder1 const & fb)
{
ostringstream s;
s << "Name: " << fb.GetName(StringUtf8Multilang::kDefaultCode) << '\t';
auto const params = fb.GetParams();
auto const street = params.GetStreet();
auto const house = params.house.Get();
string address = street;
if (!house.empty())
{
auto t = tag;
replace(t.key.begin(), t.key.end(), '\n', ' ');
replace(t.value.begin(), t.value.end(), '\n', ' ');
s << t.key << "=" << t.value << "\t";
if (!street.empty())
address += ", ";
address += house;
}
return s;
if (!address.empty())
s << "Address: " << address << '\t';
auto const center = MercatorBounds::ToLatLon(fb.GetKeyPoint());
s << "lat: " << center.lat << " lon: " << center.lon << '\t';
if (fb.GetGeomType() == feature::GEOM_POINT)
s << "GeomType: GEOM_POINT";
else if (fb.GetGeomType() == feature::GEOM_AREA)
s << "GeomType: GEOM_AREA";
else
CHECK(false, ());
return s.str();
}
struct Emitter : public EmitterBase
{
Emitter(feature::GenerateInfo const & info)
: m_bookingDataset(info.m_bookingDatafileName)
{
LOG_SHORT(LINFO, (m_bookingDataset.Size(), "hotels are loaded from Booking."));
LOG_SHORT(LINFO, ("OSM data:", FLAGS_osm_file_name));
}
void operator()(FeatureBuilder1 & fb) override
{
if (m_bookingDataset.CanBeBooking(fb))
m_features.emplace_back(fb);
}
void GetNames(vector<string> & names) const override
{
names.clear();
}
bool Finish() override
{
LOG_SHORT(LINFO, ("Num of tourism elements:", m_features.size()));
vector<size_t> elementIndexes(m_features.size());
iota(elementIndexes.begin(), elementIndexes.end(), 0);
shuffle(elementIndexes.begin(), elementIndexes.end(), minstd_rand(FLAGS_seed));
if (FLAGS_selection_size < elementIndexes.size())
elementIndexes.resize(FLAGS_selection_size);
stringstream outStream;
for (size_t i : elementIndexes)
{
auto const & fb = m_features[i];
auto const bookingIndexes = m_bookingDataset.GetNearestHotels(
MercatorBounds::ToLatLon(fb.GetKeyPoint()),
BookingDataset::kMaxSelectedElements,
BookingDataset::kDistanceLimitInMeters);
for (size_t const j : bookingIndexes)
{
auto const & hotel = m_bookingDataset.GetHotel(j);
auto const score = booking_scoring::Match(hotel, fb);
double const distanceMeters = ms::DistanceOnEarth(center.lat, center.lon,
hotel.lat, hotel.lon);
auto const matched = score.IsMatched();
outStream << "# ------------------------------------------" << fixed << setprecision(6)
<< endl;
outStream << (matched ? 'y' : 'n') << " \t" << i << "\t " << j
<< "\tdistance: " << distanceMeters
<< "\tdistance score: " << score.m_linearNormDistanceScore
<< "\tname score: " << score.m_nameSimilarityScore
<< "\tresult score: " << score.GetMatchingScore()
<< endl;
outStream << "# " << PrintBuilder(fb) << endl;
outStream << "# " << hotel << endl;
outStream << "# URL: https://www.openstreetmap.org/?mlat=" << hotel.lat
<< "&mlon=" << hotel.lon << "#map=18/" << hotel.lat << "/" << hotel.lon << endl;
}
if (!bookingIndexes.empty())
outStream << endl << endl;
}
if (FLAGS_sample_data.empty())
{
cout << outStream.str();
}
else
{
ofstream file(FLAGS_sample_data);
if (file.is_open())
{
file << outStream.str();
}
else
{
LOG(LERROR, ("Can't output into", FLAGS_sample_data));
return false;
}
}
return true;
}
BookingDataset m_bookingDataset;
vector<FeatureBuilder1> m_features;
};
unique_ptr<Emitter> GetEmitter(feature::GenerateInfo const & info)
{
LOG_SHORT(LINFO, ("Booking data:", FLAGS_booking_data));
return make_unique<Emitter>(info);
}
feature::GenerateInfo GetGenerateInfo()
{
feature::GenerateInfo info;
info.m_bookingDatafileName = FLAGS_booking_data;
info.m_osmFileName = FLAGS_osm_file_name;
info.SetNodeStorageType("map");
info.SetOsmFileType("o5m");
info.m_intermediateDir = my::GetDirectory(FLAGS_sample_data);
// ...
return info;
}
} // namespace
int main(int argc, char * argv[])
{
google::SetUsageMessage(
@ -40,74 +181,11 @@ int main(int argc, char * argv[])
" data and index files in several passes.");
google::ParseCommandLineFlags(&argc, &argv, true);
LOG_SHORT(LINFO, ("Booking data:", FLAGS_booking_data));
classificator::Load();
BookingDataset bookingDataset(FLAGS_booking_data);
vector<OsmElement> elements;
LOG_SHORT(LINFO, ("OSM data:", FLAGS_osm_file_name));
{
SourceReader reader =
FLAGS_osm_file_name.empty() ? SourceReader() : SourceReader(FLAGS_osm_file_name);
ProcessOsmElementsFromO5M(reader, [&](OsmElement * e)
{
if (bookingDataset.TourismFilter(*e))
elements.emplace_back(*e);
});
}
LOG_SHORT(LINFO, ("Num of tourism elements:", elements.size()));
vector<size_t> elementIndexes(elements.size());
iota(elementIndexes.begin(), elementIndexes.end(), 0);
shuffle(elementIndexes.begin(), elementIndexes.end(), minstd_rand(FLAGS_seed));
if (FLAGS_selection_size < elementIndexes.size())
elementIndexes.resize(FLAGS_selection_size);
stringstream outStream;
for (size_t i : elementIndexes)
{
OsmElement const & e = elements[i];
auto const bookingIndexes = bookingDataset.GetNearestHotels(
e.lat, e.lon, BookingDataset::kMaxSelectedElements, BookingDataset::kDistanceLimitInMeters);
for (size_t const j : bookingIndexes)
{
auto const & hotel = bookingDataset.GetHotel(j);
auto const score = booking_scoring::Match(hotel, e);
double const distanceMeters = ms::DistanceOnEarth(e.lat, e.lon, hotel.lat, hotel.lon);
bool matched = score.IsMatched();
outStream << "# ------------------------------------------" << fixed << setprecision(6)
<< endl;
outStream << (matched ? 'y' : 'n') << " \t" << i << "\t " << j
<< "\tdistance: " << distanceMeters
<< "\tdistance score: " << score.m_linearNormDistanceScore
<< "\tname score: " << score.m_nameSimilarityScore
<< "\tresult score: " << score.GetMatchingScore()
<< endl;
outStream << "# " << e << endl;
outStream << "# " << hotel << endl;
outStream << "# URL: https://www.openstreetmap.org/?mlat=" << hotel.lat
<< "&mlon=" << hotel.lon << "#map=18/" << hotel.lat << "/" << hotel.lon << endl;
}
if (!bookingIndexes.empty())
outStream << endl << endl;
}
if (FLAGS_sample_data.empty())
{
cout << outStream.str();
}
else
{
ofstream file(FLAGS_sample_data);
if (file.is_open())
file << outStream.str();
else
LOG(LERROR, ("Can't output into", FLAGS_sample_data));
}
auto info = GetGenerateInfo();
GenerateIntermediateData(info);
GenerateFeatures(info, GetEmitter);
return 0;
}

View file

@ -0,0 +1,38 @@
# Base functions tests.
TARGET = booking_quality_check
CONFIG += console warn_on
CONFIG -= app_bundle
TEMPLATE = app
ROOT_DIR = ../..
DEPENDENCIES = \
generator \
search \
routing \
indexer \
geometry \
editor \
platform \
coding \
base \
tomcrypt \
jansson \
pugixml \
stats_client \
opening_hours \
gflags \
oauthcpp \
expat \
protobuf \
include($$ROOT_DIR/common.pri)
INCLUDEPATH *= $$ROOT_DIR/3party/gflags/src
QT *= core
SOURCES += \
booking_quality_check.cpp \
HEADERS +=

View file

@ -1,6 +1,7 @@
#include "generator/booking_scoring.hpp"
#include "generator/booking_dataset.hpp"
#include "generator/feature_builder.hpp"
#include "indexer/search_delimiters.hpp"
#include "indexer/search_string_utils.hpp"
@ -20,7 +21,7 @@ namespace booking_scoring
namespace
{
// Calculated with tools/python/booking_hotels_quality.py.
double constexpr kOptimalThreshold = 0.317324;
double constexpr kOptimalThreshold = 0.304875;
template <typename T, typename U>
struct decay_equiv :
@ -125,15 +126,17 @@ bool BookingMatchScore::IsMatched() const
return GetMatchingScore() > kOptimalThreshold;
}
BookingMatchScore Match(BookingDataset::Hotel const & h, OsmElement const & e)
BookingMatchScore Match(BookingDataset::Hotel const & h, FeatureBuilder1 const & fb)
{
BookingMatchScore score;
auto const distance = ms::DistanceOnEarth(e.lat, e.lon, h.lat, h.lon);
auto const fbCenter = MercatorBounds::ToLatLon(fb.GetKeyPoint());
auto const distance = ms::DistanceOnEarth(fbCenter.lat, fbCenter.lon, h.lat, h.lon);
score.m_linearNormDistanceScore = GetLinearNormDistanceScore(distance);
// TODO(mgsergio): Check all translations and use the best one.
score.m_nameSimilarityScore = GetNameSimilarityScore(h.name, e.GetTag("name"));
score.m_nameSimilarityScore =
GetNameSimilarityScore(h.name, fb.GetName(StringUtf8Multilang::kDefaultCode));
return score;
}

View file

@ -2,7 +2,7 @@
#include "generator/booking_dataset.hpp"
struct OsmElement;
class FeatureBuilder1;
namespace generator
{
@ -17,6 +17,6 @@ struct BookingMatchScore
double m_nameSimilarityScore{};
};
BookingMatchScore Match(BookingDataset::Hotel const & h, OsmElement const & e);
BookingMatchScore Match(BookingDataset::Hotel const & h, FeatureBuilder1 const & fb);
} // namespace booking_scoring
} // namespace generator

View file

@ -469,6 +469,25 @@ osm::Id FeatureBuilder1::GetLastOsmId() const
return m_osmIds.back();
}
osm::Id FeatureBuilder1::GetMostGenericOsmId() const
{
ASSERT(!m_osmIds.empty(), ());
auto result = m_osmIds.front();
for (auto const & id : m_osmIds)
{
if (id.IsRelation())
{
result = id;
break;
}
else if (result.IsNode() && id.IsWay())
{
result = id;
}
}
return result;
}
bool FeatureBuilder1::HasOsmId(osm::Id const & id) const
{
for (auto const & cid : m_osmIds)

View file

@ -161,12 +161,15 @@ public:
inline FeatureParams const & GetParams() const { return m_params; }
/// @name For OSM debugging, store original OSM id
/// @name For OSM debugging and osm objects replacement, store original OSM id
//@{
void AddOsmId(osm::Id id);
void SetOsmId(osm::Id id);
osm::Id GetFirstOsmId() const;
osm::Id GetLastOsmId() const;
/// @returns an id of the most general element: node's one if there is no area or relation,
/// area's one if there is no relation, and relation id otherwise.
osm::Id GetMostGenericOsmId() const;
bool HasOsmId(osm::Id const & id) const;
string GetOsmIdsString() const;
//@}

View file

@ -11,7 +11,6 @@
namespace feature
{
struct GenerateInfo
{
enum class NodeStorageType
@ -27,7 +26,6 @@ struct GenerateInfo
O5M
};
// Directory for .mwm.tmp files.
string m_tmpDir;
// Directory for result .mwm files.
@ -41,7 +39,7 @@ struct GenerateInfo
NodeStorageType m_nodeStorageType;
OsmSourceType m_osmFileType;
string m_osmFileName;
string m_bookingDatafileName;
string m_bookingReferenceDir;
@ -86,18 +84,20 @@ struct GenerateInfo
{
return my::JoinFoldersToPath(m_tmpDir, fileName + ext);
}
string GetTargetFileName(string const & fileName, char const * ext = DATA_FILE_EXTENSION) const
{
return my::JoinFoldersToPath(m_targetDir, fileName + ext);
}
string GetIntermediateFileName(string const & fileName, char const * ext = DATA_FILE_EXTENSION) const
{
return my::JoinFoldersToPath(m_intermediateDir, fileName + ext);
}
string GetAddressesFileName() const
{
return ((m_genAddresses && !m_fileName.empty()) ? GetTargetFileName(m_fileName, ADDR_FILE_EXTENSION) : string());
}
};
} // namespace feature
} // namespace feature

View file

@ -122,6 +122,23 @@ UNIT_TEST(FBuilder_Waterfall)
TEST_EQUAL(fb2.GetTypesCount(), 1, ());
}
UNIT_TEST(FBbuilder_GetMostGeneralOsmId)
{
FeatureBuilder1 fb;
fb.AddOsmId(osm::Id::Node(1));
TEST_EQUAL(fb.GetMostGenericOsmId(), osm::Id::Node(1), ());
fb.AddOsmId(osm::Id::Node(2));
fb.AddOsmId(osm::Id::Way(1));
TEST_EQUAL(fb.GetMostGenericOsmId(), osm::Id::Way(1), ());
fb.AddOsmId(osm::Id::Node(3));
fb.AddOsmId(osm::Id::Way(2));
fb.AddOsmId(osm::Id::Relation(1));
TEST_EQUAL(fb.GetMostGenericOsmId(), osm::Id::Relation(1), ());
}
UNIT_TEST(FVisibility_RemoveNoDrawableTypes)
{
classificator::Load();

View file

@ -223,7 +223,7 @@ namespace ftype
CachedTypes()
{
Classificator const & c = classif();
for (auto const & e : (StringIL[]) { {"entrance"}, {"highway"} })
m_types.push_back(c.GetTypeByPath(e));
@ -648,6 +648,7 @@ namespace ftype
{ "addr:housename", "*", [&params](string & k, string & v) { params.AddHouseName(v); k.clear(); v.clear(); }},
{ "addr:street", "*", [&params](string & k, string & v) { params.AddStreet(v); k.clear(); v.clear(); }},
//{ "addr:streetnumber", "*", [&params](string & k, string & v) { params.AddStreet(v); k.clear(); v.clear(); }},
// This line was first introduced by vng and was never used uncommented.
//{ "addr:full", "*", [&params](string & k, string & v) { params.AddAddress(v); k.clear(); v.clear(); }},
// addr:postcode must be passed to the metadata processor.

View file

@ -38,11 +38,21 @@ uint64_t Id::OsmId() const
return m_encodedId & RESET;
}
bool Id::IsNode() const
{
return ((m_encodedId & NODE) == NODE);
}
bool Id::IsWay() const
{
return ((m_encodedId & WAY) == WAY);
}
bool Id::IsRelation() const
{
return ((m_encodedId & RELATION) == RELATION);
}
string Id::Type() const
{
if ((m_encodedId & RELATION) == RELATION)

View file

@ -21,7 +21,9 @@ public:
static Id Relation(uint64_t osmId);
uint64_t OsmId() const;
bool IsNode() const;
bool IsWay() const;
bool IsRelation() const;
/// For debug output
string Type() const;

View file

@ -18,6 +18,9 @@
#include "platform/platform.hpp"
#include "geometry/mercator.hpp"
#include "geometry/tree4d.hpp"
#include "base/stl_helpers.hpp"
#include "coding/parse_xml.hpp"
@ -183,7 +186,82 @@ public:
}
};
class MainFeaturesEmitter
/// Used to make a "good" node for a highway graph with OSRM for low zooms.
class Place
{
public:
Place(FeatureBuilder1 const & ft, uint32_t type) : m_ft(ft), m_pt(ft.GetKeyPoint()), m_type(type)
{
using namespace ftypes;
switch (IsLocalityChecker::Instance().GetType(m_type))
{
case COUNTRY: m_thresholdM = 300000.0; break;
case STATE: m_thresholdM = 100000.0; break;
case CITY: m_thresholdM = 30000.0; break;
case TOWN: m_thresholdM = 20000.0; break;
case VILLAGE: m_thresholdM = 10000.0; break;
default: m_thresholdM = 10000.0; break;
}
}
FeatureBuilder1 const & GetFeature() const { return m_ft; }
m2::RectD GetLimitRect() const
{
return MercatorBounds::RectByCenterXYAndSizeInMeters(m_pt, m_thresholdM);
}
bool IsEqual(Place const & r) const
{
return (AreTypesEqual(m_type, r.m_type) &&
m_ft.GetName() == r.m_ft.GetName() &&
(IsPoint() || r.IsPoint()) &&
MercatorBounds::DistanceOnEarth(m_pt, r.m_pt) < m_thresholdM);
}
/// Check whether we need to replace place @r with place @this.
bool IsBetterThan(Place const & r) const
{
// Check ranks.
uint8_t const r1 = m_ft.GetRank();
uint8_t const r2 = r.m_ft.GetRank();
if (r1 != r2)
return r1 > r2;
// Check types length.
// ("place-city-capital-2" is better than "place-city").
uint8_t const l1 = ftype::GetLevel(m_type);
uint8_t const l2 = ftype::GetLevel(r.m_type);
if (l1 != l2)
return l1 > l2;
// Assume that area places has better priority than point places at the very end ...
/// @todo It was usefull when place=XXX type has any area fill style.
/// Need to review priority logic here (leave the native osm label).
return !IsPoint();
}
private:
bool IsPoint() const { return (m_ft.GetGeomType() == feature::GEOM_POINT); }
static bool AreTypesEqual(uint32_t t1, uint32_t t2)
{
// Use 2-arity places comparison for filtering.
// ("place-city-capital-2" is equal to "place-city")
ftype::TruncValue(t1, 2);
ftype::TruncValue(t2, 2);
return (t1 == t2);
}
FeatureBuilder1 m_ft;
m2::PointD m_pt;
uint32_t m_type;
double m_thresholdM;
};
class MainFeaturesEmitter : public EmitterBase
{
using TWorldGenerator = WorldMapGenerator<feature::FeaturesCollector>;
using TCountriesGenerator = CountryMapGenerator<feature::Polygonizer<feature::FeaturesCollector>>;
@ -193,9 +271,18 @@ class MainFeaturesEmitter
unique_ptr<CoastlineFeaturesGenerator> m_coasts;
unique_ptr<feature::FeaturesCollector> m_coastsHolder;
string const m_skippedElementsPath;
ostringstream m_skippedElements;
string m_srcCoastsFile;
bool m_failOnCoasts;
generator::BookingDataset m_bookingDataset;
/// Used to prepare a list of cities to serve as a list of nodes
/// for building a highway graph with OSRM for low zooms.
m4::Tree<Place> m_places;
enum TypeIndex
{
NATURAL_COASTLINE,
@ -211,7 +298,9 @@ class MainFeaturesEmitter
public:
MainFeaturesEmitter(feature::GenerateInfo const & info)
: m_failOnCoasts(info.m_failOnCoasts)
: m_skippedElementsPath(info.GetIntermediateFileName("skipped_elements", ".lst"))
, m_failOnCoasts(info.m_failOnCoasts)
, m_bookingDataset(info.m_bookingDatafileName, info.m_bookingReferenceDir)
{
Classificator const & c = classif();
@ -248,47 +337,69 @@ public:
m_world.reset(new TWorldGenerator(info));
}
void operator()(FeatureBuilder1 fb)
void operator()(FeatureBuilder1 & fb) override
{
uint32_t const coastType = Type(NATURAL_COASTLINE);
bool const hasCoast = fb.HasType(coastType);
static uint32_t const placeType = classif().GetTypeByPath({"place"});
uint32_t const type = fb.GetParams().FindType(placeType, 1);
if (m_coasts)
auto hotelIndex = generator::BookingDataset::kInvalidHotelIndex;
if (type != ftype::GetEmptyValue() && !fb.GetName().empty())
{
if (hasCoast)
m_places.ReplaceEqualInRect(
Place(fb, type),
[](Place const & p1, Place const & p2) { return p1.IsEqual(p2); },
[](Place const & p1, Place const & p2) { return p1.IsBetterThan(p2); });
}
else if ((hotelIndex = m_bookingDataset.GetMatchingHotelIndex(fb)) !=
generator::BookingDataset::kInvalidHotelIndex)
{
m_skippedElements << DebugPrint(fb.GetMostGenericOsmId()) << endl;
// Turn a hotel into a simple building.
if (fb.GetGeomType() == feature::GEOM_AREA)
{
CHECK(fb.GetGeomType() != feature::GEOM_POINT, ());
// leave only coastline type
fb.SetType(coastType);
(*m_coasts)(fb);
// Remove all information about a hotel.
auto params = fb.GetParams();
params.ClearName();
auto & meta = params.GetMetadata();
meta.Drop(feature::Metadata::EType::FMD_STARS);
meta.Drop(feature::Metadata::EType::FMD_WEBSITE);
meta.Drop(feature::Metadata::EType::FMD_PHONE_NUMBER);
auto const & c = classif();
auto const tourism = c.GetTypeByPath({"tourism"});
my::EraseIf(params.m_Types, [&c, tourism](uint32_t type)
{
ftype::TruncValue(type, 1);
return type == tourism;
});
fb.SetParams(params);
Emit(fb);
}
return;
}
if (hasCoast)
else
{
fb.PopExactType(Type(NATURAL_LAND));
fb.PopExactType(coastType);
Emit(fb);
}
else if ((fb.HasType(Type(PLACE_ISLAND)) || fb.HasType(Type(PLACE_ISLET))) &&
fb.GetGeomType() == feature::GEOM_AREA)
{
fb.AddType(Type(NATURAL_LAND));
}
if (!fb.RemoveInvalidTypes())
return;
if (m_world)
(*m_world)(fb);
if (m_countries)
(*m_countries)(fb);
}
/// @return false if coasts are not merged and FLAG_fail_on_coasts is set
bool Finish()
bool Finish() override
{
DumpSkippedElements();
// Emit all booking objecs to the map.
for (size_t hotelIndex = 0; hotelIndex < m_bookingDataset.Size(); ++hotelIndex)
m_bookingDataset.BuildHotel(hotelIndex, [this](FeatureBuilder1 & fb) { Emit(fb); });
m_places.ForEach([this](Place const & p)
{
// m_places are no longer used after this point.
Emit(const_cast<FeatureBuilder1 &>(p.GetFeature()));
});
if (m_world)
m_world->DoMerge();
@ -340,16 +451,82 @@ public:
return true;
}
inline void GetNames(vector<string> & names) const
void GetNames(vector<string> & names) const override
{
if (m_countries)
names = m_countries->Parent().Names();
else
names.clear();
}
private:
void Emit(FeatureBuilder1 & fb)
{
uint32_t const coastType = Type(NATURAL_COASTLINE);
bool const hasCoast = fb.HasType(coastType);
if (m_coasts)
{
if (hasCoast)
{
CHECK(fb.GetGeomType() != feature::GEOM_POINT, ());
// leave only coastline type
fb.SetType(coastType);
(*m_coasts)(fb);
}
return;
}
if (hasCoast)
{
fb.PopExactType(Type(NATURAL_LAND));
fb.PopExactType(coastType);
}
else if ((fb.HasType(Type(PLACE_ISLAND)) || fb.HasType(Type(PLACE_ISLET))) &&
fb.GetGeomType() == feature::GEOM_AREA)
{
fb.AddType(Type(NATURAL_LAND));
}
if (!fb.RemoveInvalidTypes())
return;
if (m_world)
(*m_world)(fb);
if (m_countries)
(*m_countries)(fb);
}
void DumpSkippedElements()
{
auto const skippedElements = m_skippedElements.str();
if (skippedElements.empty())
{
LOG(LINFO, ("No osm object was skipped."));
return;
}
ofstream file(m_skippedElementsPath, ios_base::app);
if (file.is_open())
{
file << m_skippedElements.str();
LOG(LINFO, ("Saving skipped elements to", m_skippedElementsPath, "done."));
}
else
{
LOG(LERROR, ("Can't output into", m_skippedElementsPath));
}
}
};
} // anonymous namespace
unique_ptr<EmitterBase> MakeMainFeatureEmitter(feature::GenerateInfo const & info)
{
LOG(LINFO, ("Processing booking data from", info.m_bookingDatafileName, "done."));
return make_unique<MainFeaturesEmitter>(info);
}
template <typename TElement, typename TCache>
void AddElementToCache(TCache & cache, TElement const & em)
@ -496,7 +673,7 @@ void ProcessOsmElementsFromO5M(SourceReader & stream, function<void(OsmElement *
///////////////////////////////////////////////////////////////////////////////////////////////////
template <class TNodesHolder>
bool GenerateFeaturesImpl(feature::GenerateInfo & info)
bool GenerateFeaturesImpl(feature::GenerateInfo & info, EmitterBase & emitter)
{
try
{
@ -506,9 +683,9 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info)
TDataCache cache(nodes, info);
cache.LoadIndex();
MainFeaturesEmitter bucketer(info);
OsmToFeatureTranslator<MainFeaturesEmitter, TDataCache> parser(
bucketer, cache, info.m_makeCoasts ? classif().GetCoastType() : 0,
// TODO(mgsergio): Get rid of EmitterBase template parameter.
OsmToFeatureTranslator<EmitterBase, TDataCache> parser(
emitter, cache, info.m_makeCoasts ? classif().GetCoastType() : 0,
info.GetAddressesFileName());
TagAdmixer tagAdmixer(info.GetIntermediateFileName("ways", ".csv"),
@ -516,25 +693,13 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info)
TagReplacer tagReplacer(GetPlatform().ResourcesDir() + REPLACED_TAGS_FILE);
OsmTagMixer osmTagMixer(GetPlatform().ResourcesDir() + MIXED_TAGS_FILE);
// If info.m_bookingDatafileName is empty then no data will be loaded.
generator::BookingDataset bookingDataset(info.m_bookingDatafileName,
info.m_bookingReferenceDir);
stringstream skippedElements;
// Here we can add new tags to element!!!
// Here we can add new tags to the elements!
auto const fn = [&](OsmElement * e)
{
tagReplacer(e);
tagAdmixer(e);
osmTagMixer(e);
if (bookingDataset.BookingFilter(*e))
{
skippedElements << e->id << endl;
return;
}
parser.EmitElement(e);
};
@ -551,30 +716,11 @@ bool GenerateFeaturesImpl(feature::GenerateInfo & info)
LOG(LINFO, ("Processing", info.m_osmFileName, "done."));
if (!info.m_bookingDatafileName.empty())
{
bookingDataset.BuildFeatures([&](OsmElement * e) { parser.EmitElement(e); });
LOG(LINFO, ("Processing booking data from", info.m_bookingDatafileName, "done."));
string skippedElementsPath = info.GetIntermediateFileName("skipped_elements", ".lst");
ofstream file(skippedElementsPath);
if (file.is_open())
{
file << skippedElements.str();
LOG(LINFO, ("Saving skipped elements to", skippedElementsPath, "done."));
}
else
{
LOG(LERROR, ("Can't output into", skippedElementsPath));
}
}
parser.Finish();
// Stop if coasts are not merged and FLAG_fail_on_coasts is set
if (!bucketer.Finish())
if (!emitter.Finish())
return false;
bucketer.GetNames(info.m_bucketNames);
emitter.GetNames(info.m_bucketNames);
}
catch (Reader::Exception const & ex)
{
@ -619,16 +765,17 @@ bool GenerateIntermediateDataImpl(feature::GenerateInfo & info)
return true;
}
bool GenerateFeatures(feature::GenerateInfo & info)
bool GenerateFeatures(feature::GenerateInfo & info, EmitterFactory factory)
{
auto emitter = factory(info);
switch (info.m_nodeStorageType)
{
case feature::GenerateInfo::NodeStorageType::File:
return GenerateFeaturesImpl<cache::RawFilePointStorage<cache::EMode::Read>>(info);
return GenerateFeaturesImpl<cache::RawFilePointStorage<cache::EMode::Read>>(info, *emitter);
case feature::GenerateInfo::NodeStorageType::Index:
return GenerateFeaturesImpl<cache::MapFilePointStorage<cache::EMode::Read>>(info);
return GenerateFeaturesImpl<cache::MapFilePointStorage<cache::EMode::Read>>(info, *emitter);
case feature::GenerateInfo::NodeStorageType::Memory:
return GenerateFeaturesImpl<cache::RawMemPointStorage<cache::EMode::Read>>(info);
return GenerateFeaturesImpl<cache::RawMemPointStorage<cache::EMode::Read>>(info, *emitter);
}
return false;
}

View file

@ -30,11 +30,31 @@ public:
uint64_t Read(char * buffer, uint64_t bufferSize);
};
class FeatureBuilder1;
// Emitter is used in OsmElemen to FeatureBuilder translation process.
class EmitterBase
{
public:
virtual ~EmitterBase() = default;
bool GenerateFeatures(feature::GenerateInfo & info);
/// This method is used by OsmTranslator to pass |fb| to Emitter for further processing.
virtual void operator()(FeatureBuilder1 & fb) = 0;
/// Finish is used in GenerateFeatureImpl to make whatever work is needed after
/// all OmsElements are processed.
virtual bool Finish() { return true; }
/// Sets buckets (mwm names).
// TODO(syershov): Make this topic clear.
virtual void GetNames(vector<string> & names) const = 0;
};
unique_ptr<EmitterBase> MakeMainFeatureEmitter(feature::GenerateInfo const & info);
using EmitterFactory = function<unique_ptr<EmitterBase>(feature::GenerateInfo const &)>;
bool GenerateFeatures(feature::GenerateInfo & info,
EmitterFactory factory = MakeMainFeatureEmitter);
bool GenerateIntermediateData(feature::GenerateInfo & info);
void ProcessOsmElementsFromO5M(SourceReader & stream, function<void(OsmElement *)> processor);
void ProcessOsmElementsFromXML(SourceReader & stream, function<void(OsmElement *)> processor);

View file

@ -9,8 +9,6 @@
#include "indexer/feature_visibility.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "geometry/tree4d.hpp"
#include "coding/file_writer.hpp"
#include "base/cache.hpp"
@ -24,77 +22,6 @@
namespace
{
class Place
{
FeatureBuilder1 m_ft;
m2::PointD m_pt;
uint32_t m_type;
double m_thresholdM;
bool IsPoint() const { return (m_ft.GetGeomType() == feature::GEOM_POINT); }
static bool IsEqualTypes(uint32_t t1, uint32_t t2)
{
// Use 2-arity places comparison for filtering.
// ("place-city-capital-2" is equal to "place-city")
ftype::TruncValue(t1, 2);
ftype::TruncValue(t2, 2);
return (t1 == t2);
}
public:
Place(FeatureBuilder1 const & ft, uint32_t type) : m_ft(ft), m_pt(ft.GetKeyPoint()), m_type(type)
{
using namespace ftypes;
switch (IsLocalityChecker::Instance().GetType(m_type))
{
case COUNTRY: m_thresholdM = 300000.0; break;
case STATE: m_thresholdM = 100000.0; break;
case CITY: m_thresholdM = 30000.0; break;
case TOWN: m_thresholdM = 20000.0; break;
case VILLAGE: m_thresholdM = 10000.0; break;
default: m_thresholdM = 10000.0; break;
}
}
FeatureBuilder1 const & GetFeature() const { return m_ft; }
m2::RectD GetLimitRect() const
{
return MercatorBounds::RectByCenterXYAndSizeInMeters(m_pt, m_thresholdM);
}
bool IsEqual(Place const & r) const
{
return (IsEqualTypes(m_type, r.m_type) &&
m_ft.GetName() == r.m_ft.GetName() &&
(IsPoint() || r.IsPoint()) &&
MercatorBounds::DistanceOnEarth(m_pt, r.m_pt) < m_thresholdM);
}
/// Check whether we need to replace place @r with place @this.
bool IsBetterThan(Place const & r) const
{
// Check ranks.
uint8_t const r1 = m_ft.GetRank();
uint8_t const r2 = r.m_ft.GetRank();
if (r1 != r2)
return (r2 < r1);
// Check types length.
// ("place-city-capital-2" is better than "place-city").
uint8_t const l1 = ftype::GetLevel(m_type);
uint8_t const l2 = ftype::GetLevel(r.m_type);
if (l1 != l2)
return (l2 < l1);
// Assume that area places has better priority than point places at the very end ...
/// @todo It was usefull when place=XXX type has any area fill style.
/// Need to review priority logic here (leave the native osm label).
return !IsPoint();
}
};
/// Generated features should include parent relation tags to make
/// full types matching and storing any additional info.
class RelationTagsBase
@ -243,7 +170,6 @@ protected:
}
}
};
} // namespace
/// @param TEmitter Feature accumulating policy
@ -255,7 +181,7 @@ class OsmToFeatureTranslator
TCache & m_holder;
uint32_t m_coastType;
unique_ptr<FileWriter> m_addrWriter;
m4::Tree<Place> m_places;
RelationTagsNode m_nodeRelations;
RelationTagsWay m_wayRelations;
@ -334,32 +260,25 @@ class OsmToFeatureTranslator
return params.IsValid();
}
void EmitFeatureBase(FeatureBuilder1 & ft, FeatureParams const & params)
void EmitFeatureBase(FeatureBuilder1 & ft, FeatureParams const & params) const
{
ft.SetParams(params);
if (ft.PreSerialize())
{
string addr;
if (m_addrWriter && ftypes::IsBuildingChecker::Instance()(params.m_Types) && ft.FormatFullAddress(addr))
m_addrWriter->Write(addr.c_str(), addr.size());
static uint32_t const placeType = classif().GetTypeByPath({"place"});
uint32_t const type = params.FindType(placeType, 1);
if (type != ftype::GetEmptyValue() && !ft.GetName().empty())
if (m_addrWriter && ftypes::IsBuildingChecker::Instance()(params.m_Types) &&
ft.FormatFullAddress(addr))
{
m_places.ReplaceEqualInRect(Place(ft, type),
[](Place const & p1, Place const & p2) { return p1.IsEqual(p2); },
[](Place const & p1, Place const & p2) { return p1.IsBetterThan(p2); });
m_addrWriter->Write(addr.c_str(), addr.size());
}
else
m_emitter(ft);
m_emitter(ft);
}
}
/// @param[in] params Pass by value because it can be modified.
//@{
void EmitPoint(m2::PointD const & pt, FeatureParams params, osm::Id id)
void EmitPoint(m2::PointD const & pt, FeatureParams params, osm::Id id) const
{
if (feature::RemoveNoDrawableTypes(params.m_Types, feature::GEOM_POINT))
{
@ -370,7 +289,7 @@ class OsmToFeatureTranslator
}
}
void EmitLine(FeatureBuilder1 & ft, FeatureParams params, bool isCoastLine)
void EmitLine(FeatureBuilder1 & ft, FeatureParams params, bool isCoastLine) const
{
if (isCoastLine || feature::RemoveNoDrawableTypes(params.m_Types, feature::GEOM_LINE))
{
@ -380,7 +299,7 @@ class OsmToFeatureTranslator
}
template <class MakeFnT>
void EmitArea(FeatureBuilder1 & ft, FeatureParams params, MakeFnT makeFn)
void EmitArea(FeatureBuilder1 & ft, FeatureParams params, MakeFnT makeFn) const
{
using namespace feature;
@ -564,19 +483,11 @@ public:
}
public:
OsmToFeatureTranslator(TEmitter & emitter, TCache & holder,
uint32_t coastType, string const & addrFilePath)
OsmToFeatureTranslator(TEmitter & emitter, TCache & holder, uint32_t coastType,
string const & addrFilePath = {})
: m_emitter(emitter), m_holder(holder), m_coastType(coastType)
{
if (!addrFilePath.empty())
m_addrWriter.reset(new FileWriter(addrFilePath));
}
void Finish()
{
m_places.ForEach([this] (Place const & p)
{
m_emitter(p.GetFeature());
});
}
};

View file

@ -36,34 +36,34 @@ void ReadCommon(unique_ptr<Reader> classificator,
c.ReadTypesMapping(s);
}
}
}
} // namespace
namespace classificator
{
void Load()
void Load()
{
LOG(LDEBUG, ("Reading of classificator started"));
Platform & p = GetPlatform();
MapStyle const originMapStyle = GetStyleReader().GetCurrentStyle();
for (size_t i = 0; i < MapStyleCount; ++i)
{
LOG(LDEBUG, ("Reading of classificator started"));
Platform & p = GetPlatform();
MapStyle const originMapStyle = GetStyleReader().GetCurrentStyle();
for (size_t i = 0; i < MapStyleCount; ++i)
MapStyle const mapStyle = static_cast<MapStyle>(i);
// Read the merged style only if it was requested.
if (mapStyle != MapStyleMerged || originMapStyle == MapStyleMerged)
{
MapStyle const mapStyle = static_cast<MapStyle>(i);
// Read the merged style only if it was requested.
if (mapStyle != MapStyleMerged || originMapStyle == MapStyleMerged)
{
GetStyleReader().SetCurrentStyle(mapStyle);
ReadCommon(p.GetReader("classificator.txt"),
p.GetReader("types.txt"));
GetStyleReader().SetCurrentStyle(mapStyle);
ReadCommon(p.GetReader("classificator.txt"),
p.GetReader("types.txt"));
drule::LoadRules();
}
drule::LoadRules();
}
GetStyleReader().SetCurrentStyle(originMapStyle);
LOG(LDEBUG, ("Reading of classificator finished"));
}
GetStyleReader().SetCurrentStyle(originMapStyle);
LOG(LDEBUG, ("Reading of classificator finished"));
}
} // namespace classificator

View file

@ -239,6 +239,11 @@ bool IsDummyName(string const & s)
// FeatureParams implementation
/////////////////////////////////////////////////////////////////////////////////////////
void FeatureParams::ClearName()
{
name.Clear();
}
bool FeatureParams::AddName(string const & lang, string const & s)
{
if (IsDummyName(s))

View file

@ -222,6 +222,8 @@ public:
FeatureParams() : m_geomType(0xFF), m_reverseGeometry(false) {}
void ClearName();
bool AddName(string const & lang, string const & s);
bool AddHouseName(string const & s);
bool AddHouseNumber(string houseNumber);

View file

@ -41,6 +41,11 @@ SUBDIRS = 3party base coding geometry editor indexer routing search
CONFIG(gtool):!CONFIG(no-tests) {
SUBDIRS *= map
# Booking quality check
booking_quality_check.subdir = generator/booking_quality_check
booking_quality_check.depends = $$SUBDIRS
SUBDIRS *= booking_quality_check
routing_integration_tests.subdir = routing/routing_integration_tests
routing_integration_tests.depends = $$SUBDIRS
routing_consistency_tests.subdir = routing/routing_consistency_tests