Reduce NewName Instructructions / Name Changes

With @karenzshea's name / ref split (ref. #2857) in master we want to
make use of it and reduce `NewName` instructions when ever possible.
This is a first step towards #2744 by using the already existing name
change heuristic from the extractor now in post-processing as well.

Limitations: at the moment we don't have the `SuffixTable` in
post-processing; this would require us serializing and subsequently
deserializing the table, passing it through from the profiles to the
API.
This commit is contained in:
Daniel J. Hofmann
2016-09-12 15:24:15 +02:00
committed by Moritz Kobitzsch
parent 7ffe832ff8
commit 60010dd998
10 changed files with 412 additions and 193 deletions
+127
View File
@@ -12,8 +12,13 @@
#include "util/simple_logger.hpp"
#include <algorithm>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
namespace osrm
{
namespace util
@@ -131,6 +136,128 @@ inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruc
return instruction.type == extractor::guidance::TurnType::StayOnRoundabout;
}
// Name Change Logic
// Used both during Extraction as well as during Post-Processing
inline std::pair<std::string, std::string> getPrefixAndSuffix(const std::string &data)
{
const auto suffix_pos = data.find_last_of(' ');
if (suffix_pos == std::string::npos)
return {};
const auto prefix_pos = data.find_first_of(' ');
auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1));
boost::to_lower(result.first);
boost::to_lower(result.second);
return result;
}
// Note: there is an overload without suffix checking below.
// (that's the reason we template the suffix table here)
template <typename SuffixTable>
inline bool requiresNameAnnounced(const std::string &from_name,
const std::string &from_ref,
const std::string &to_name,
const std::string &to_ref,
const SuffixTable &suffix_table)
{
// first is empty and the second is not
if ((from_name.empty() && from_ref.empty()) && !(to_name.empty() && to_ref.empty()))
return true;
// FIXME, handle in profile to begin with?
// Input for this function should be a struct separating streetname, suffix (e.g. road,
// boulevard, North, West ...), and a list of references
// check similarity of names
const auto names_are_empty = from_name.empty() && to_name.empty();
const auto name_is_contained =
boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name);
const auto checkForPrefixOrSuffixChange = [](
const std::string &first, const std::string &second, const SuffixTable &suffix_table) {
const auto first_prefix_and_suffixes = getPrefixAndSuffix(first);
const auto second_prefix_and_suffixes = getPrefixAndSuffix(second);
// reverse strings, get suffices and reverse them to get prefixes
const auto checkTable = [&](const std::string &str) {
return str.empty() || suffix_table.isSuffix(str);
};
const auto getOffset = [](const std::string &str) -> std::size_t {
if (str.empty())
return 0;
else
return str.length() + 1;
};
const bool is_prefix_change = [&]() -> bool {
if (!checkTable(first_prefix_and_suffixes.first))
return false;
if (!checkTable(second_prefix_and_suffixes.first))
return false;
return !first.compare(getOffset(first_prefix_and_suffixes.first),
std::string::npos,
second,
getOffset(second_prefix_and_suffixes.first),
std::string::npos);
}();
const bool is_suffix_change = [&]() -> bool {
if (!checkTable(first_prefix_and_suffixes.second))
return false;
if (!checkTable(second_prefix_and_suffixes.second))
return false;
return !first.compare(0,
first.length() - getOffset(first_prefix_and_suffixes.second),
second,
0,
second.length() - getOffset(second_prefix_and_suffixes.second));
}();
return is_prefix_change || is_suffix_change;
};
const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table);
const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change;
const auto name_is_removed = !from_name.empty() && to_name.empty();
// references are contained in one another
const auto refs_are_empty = from_ref.empty() && to_ref.empty();
const auto ref_is_contained =
from_ref.empty() || to_ref.empty() ||
(from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos);
const auto ref_is_removed = !from_ref.empty() && to_ref.empty();
const auto obvious_change =
(names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) ||
(names_are_equal && refs_are_empty) || (ref_is_contained && name_is_removed) ||
(names_are_equal && ref_is_removed) || is_suffix_change;
const auto needs_announce =
// " (Ref)" -> "Name "
(from_name.empty() && !from_ref.empty() && !to_name.empty() && to_ref.empty());
return !obvious_change || needs_announce;
}
// Overload without suffix checking
inline bool requiresNameAnnounced(const std::string &from_name,
const std::string &from_ref,
const std::string &to_name,
const std::string &to_ref)
{
// Dummy since we need to provide a SuffixTable but do not have the data for it.
// (Guidance Post-Processing does not keep the suffix table around at the moment)
struct NopSuffixTable final
{
NopSuffixTable(){}
bool isSuffix(const std::string &) const { return false; }
} static const table;
return requiresNameAnnounced(from_name, from_ref, to_name, to_ref, table);
}
} // namespace guidance
} // namespace util
} // namespace osrm
+6
View File
@@ -23,7 +23,13 @@ class NameTable
public:
NameTable(const std::string &filename);
// This class provides a limited view over all the string data we serialize out.
// The following functions are a subset of what is available.
// See the data facades for they provide full access to this serialized string data.
// (at time of writing this: get{Name,Ref,Pronunciation,Destinations}ForID(name_id);)
std::string GetNameForID(const unsigned name_id) const;
std::string GetRefForID(const unsigned name_id) const;
};
} // namespace util
} // namespace osrm