7#include "platformfinder_p.h"
9#include <QRegularExpression>
13PlatformFinder::PlatformFinder()
15 m_collator.setLocale(
QLocale());
16 m_collator.setNumericMode(
true);
17 m_collator.setIgnorePunctuation(
true);
21PlatformFinder::~PlatformFinder() =
default;
26 if (!name.isEmpty()) {
31 for (
const char *n : {
"platform",
"voie",
"gleis" }) {
40std::vector<Platform> PlatformFinder::find(
const MapData &data)
45 for (
auto it = m_data.levelMap().begin(); it != m_data.levelMap().end(); ++it) {
46 for (
const auto &e : (*it).second) {
47 if (!e.hasTags() || e.tagValue(m_tagKeys.disused) ==
"yes") {
52 const auto platformRef = e.tagValue(m_tagKeys.platform_colon_ref);
53 if (!platformRef.isEmpty() && e.tagValue(
"pole") ==
"landmark_sign") {
55 for (
const auto &name : names) {
57 p.setLevel(levelForPlatform((*it).first, e));
61 section.setPosition(e);
62 p.setSections({section});
63 m_floatingSections.push_back(std::move(p));
66 if (e.tagValue(m_tagKeys.railway) ==
"platform_marker") {
68 p.setLevel(levelForPlatform((*it).first, e));
71 section.setPosition(e);
72 p.setSections({section});
73 m_floatingSections.push_back(std::move(p));
76 if (e.type() == OSM::Type::Node) {
79 const auto railway = e.tagValue(m_tagKeys.railway);
80 if (railway ==
"platform") {
83 const auto ifopts = e.tagValue(
"ref:IFOPT").split(
';');
84 for (
auto i = 0; i < names.size(); ++i) {
87 platform.setName(names[i]);
88 platform.setLevel(levelForPlatform((*it).first, e));
89 platform.setMode(modeForElement(e));
90 platform.setSections(sectionsForPath(e.outerPath(m_data.dataSet()), names[i]));
91 if (ifopts.size() == names.size()) {
96 m_platformAreas.push_back(std::move(platform));
99 else if (railway ==
"platform_edge" && e.type() == OSM::Type::Way) {
103 platform.setLevel(levelForPlatform((*it).first, e));
104 platform.setSections(sectionsForPath(e.outerPath(m_data.dataSet()), platform.name()));
106 addPlatform(std::move(platform));
108 else if (!railway.isEmpty() && e.type() == OSM::Type::Way && railway !=
"disused") {
109 OSM::for_each_node(m_data.dataSet(), *e.way(), [&](
const auto &node) {
110 if (!OSM::contains(m_data.boundingBox(), node.coordinate)) {
113 if (
OSM::tagValue(node, m_tagKeys.railway) ==
"buffer_stop") {
117 const auto pt =
OSM::tagValue(node, m_tagKeys.public_transport);
118 if (pt ==
"stop_point" || pt ==
"stop_position") {
121 platform.setTrack({e});
122 platform.setLevel(levelForPlatform((*it).first, e));
125 platform.setIfopt(
QString::fromUtf8(platform.stopPoint().tagValue(
"ref:IFOPT")));
126 if (platform.mode() == Platform::Unknown) {
127 platform.setMode(modeForElement(e));
129 platform.setSections(sectionsForPath(e.outerPath(m_data.dataSet()), platform.name()));
131 addPlatform(std::move(platform));
138 OSM::for_each(m_data.dataSet(), [
this](
OSM::Element e) {
139 const auto route = e.tagValue(m_tagKeys.route);
140 if (route.isEmpty() || route ==
"tracks") {
144 }, OSM::IncludeRelations);
146 mergePlatformAreas();
147 for (
auto &p : m_floatingSections) {
148 addPlatform(std::move(p));
150 m_floatingSections.clear();
153 return std::move(m_platforms);
156void PlatformFinder::resolveTagKeys()
158 m_tagKeys.level = m_data.dataSet().tagKey(
"level");
159 m_tagKeys.platform_ref = m_data.dataSet().tagKey(
"platform_ref");
160 m_tagKeys.platform_colon_ref = m_data.dataSet().tagKey(
"platform:ref");
161 m_tagKeys.public_transport = m_data.dataSet().tagKey(
"public_transport");
162 m_tagKeys.railway = m_data.dataSet().tagKey(
"railway");
163 m_tagKeys.railway_platform_section = m_data.dataSet().tagKey(
"railway:platform:section");
164 m_tagKeys.route = m_data.dataSet().tagKey(
"route");
165 m_tagKeys.disused = m_data.dataSet().tagKey(
"disused");
171 case OSM::Type::Null:
173 case OSM::Type::Node:
174 scanRoute(*e.node(), route);
177 OSM::for_each_node(m_data.dataSet(), *e.way(), [
this, route](
const OSM::Node &node) {
178 scanRoute(node, route);
181 case OSM::Type::Relation:
182 OSM::for_each_member(m_data.dataSet(), *e.relation(), [
this, route](
OSM::Element e) {
191 const auto pt =
OSM::tagValue(node, m_tagKeys.public_transport);
196 for (
auto &p : m_platforms) {
197 if (p.stopPoint().id() == node.id) {
199 for (
const auto &lineName : l) {
200 if (lineName.isEmpty()) {
203 auto lines = p.takeLines();
204 const auto it = std::lower_bound(lines.begin(), lines.end(), lineName, m_collator);
205 if (it == lines.end() || (*it) != lineName) {
206 lines.insert(it, lineName);
208 p.setLines(std::move(lines));
215std::vector<PlatformSection> PlatformFinder::sectionsForPath(
const std::vector<const OSM::Node*> &path,
const QString &platformName)
const
217 std::vector<PlatformSection> sections;
224 const auto n = (*it);
225 const auto platformRef =
OSM::tagValue(*n, m_tagKeys.platform_ref);
226 if (!platformRef.isEmpty() && platformRef != platformName.
toUtf8()) {
229 const auto pt =
OSM::tagValue(*n, m_tagKeys.public_transport);
230 if (pt ==
"platform_section_sign") {
233 sec.setName(
QString::fromUtf8(sec.
position().tagValue(
"platform_section_sign_value",
"local_ref",
"ref",
"platform_section_sign")));
234 sections.push_back(std::move(sec));
237 const auto railway_platform_section =
OSM::tagValue(*n, m_tagKeys.railway_platform_section);
238 if (!railway_platform_section.isEmpty()) {
242 sections.push_back(std::move(sec));
252}
static constexpr const mode_map[] = {
253 {
"rail", Platform::Rail },
254 {
"light_rail", Platform::Rail },
255 {
"subway", Platform::Subway },
256 {
"tram", Platform::Tram },
257 {
"monorail", Platform::Tram },
258 {
"bus", Platform::Bus },
263 const auto railway = elem.tagValue(m_tagKeys.railway);
264 for (
const auto &mode : mode_map) {
265 const auto modeTag = elem.tagValue(mode.name);
266 if (railway == mode.name || (!modeTag.isEmpty() && modeTag !=
"no")) {
272 return Platform::Rail;
277 if (ml.numericLevel() != 0) {
278 return qRound(ml.numericLevel() / 10.0) * 10;
280 return e.tagValue(m_tagKeys.level).
isEmpty() ? std::numeric_limits<int>::min() : 0;
283void PlatformFinder::addPlatform(
Platform &&platform)
292 m_platforms.push_back(std::move(platform));
295void PlatformFinder::mergePlatformAreas()
299 std::size_t prevCount = 0;
301 while (prevCount != m_platformAreas.size() && !m_platformAreas.empty()) {
302 prevCount = m_platformAreas.size();
303 for (
auto it = m_platformAreas.begin(); it != m_platformAreas.end();) {
312 it = m_platformAreas.erase(it);
318 if (prevCount == m_platformAreas.size()) {
319 m_platforms.push_back(m_platformAreas.back());
320 m_platformAreas.erase(std::prev(m_platformAreas.end()));
325void PlatformFinder::finalizeResult()
327 if (m_platforms.empty()) {
334 for (
auto it = m_platforms.begin(); it != std::prev(m_platforms.end()) && it != m_platforms.end(); ++it) {
335 for (
auto it2 = std::next(it); it2 != m_platforms.end();) {
338 it2 = m_platforms.erase(it2);
346 m_platforms.erase(std::remove_if(m_platforms.begin(), m_platforms.end(), [](
const auto &p) {
347 return !p.isValid() || p.mode() == Platform::Bus;
348 }), m_platforms.end());
351 for (
auto &p : m_platforms) {
352 auto sections = p.takeSections();
353 sections.erase(std::remove_if(sections.begin(), sections.end(), [](
const auto &s) { return !s.isValid(); }), sections.end());
354 std::sort(sections.begin(), sections.end(), [
this](
const auto &lhs,
const auto &rhs) {
355 return m_collator.compare(lhs.name(), rhs.name()) < 0;
357 p.setSections(std::move(sections));
361 std::sort(m_platforms.begin(), m_platforms.end(), [
this](
const auto &lhs,
const auto &rhs) {
362 if (lhs.mode() == rhs.mode()) {
363 if (lhs.name() == rhs.name()) {
364 return lhs.stopPoint().id() < rhs.stopPoint().id();
366 return m_collator.compare(lhs.name(), rhs.name()) < 0;
368 return lhs.mode() < rhs.mode();
Raw OSM map data, separated by levels.
A reference to any of OSM::Node/OSM::Way/OSM::Relation.
QString path(const QString &relativePath)
OSM-based multi-floor indoor maps for buildings.
QByteArray tagValue(const Elem &elem, TagKey key)
Returns the tag value for key of elem.
bool isEmpty() const const
QString fromUtf8(QByteArrayView str)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QByteArray toUtf8() const const