KOSMIndoorMap

boundarysearch.cpp
1/*
2 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "boundarysearch_p.h"
8
9#include <osm/datatypes.h>
10#include <osm/element.h>
11#include <osm/geomath.h>
12
13#include <QDebug>
14
15using namespace KOSMIndoorMap;
16
17enum {
18 BoundingBoxMargin = 100, // margin around the final result, in meters
19 BoundingBoxMaxSize = 2000, // maximum width/height of the bounding box, in meters
20};
21
22void BoundarySearch::init(OSM::Coordinate coord)
23{
24 m_center = coord;
25
26 m_bbox = {coord, coord};
27 m_relevantIds.clear();
28}
29
30static OSM::Id actualId(OSM::Element e, OSM::TagKey mxoidTag)
31{
32 const auto mxoid = e.tagValue(mxoidTag);
33 if (!mxoid.isEmpty()) {
34 return mxoid.toLongLong();
35 }
36 return e.id();
37}
38
39static bool isPolygon(OSM::Element e)
40{
41 return (e.type() == OSM::Type::Way && e.way()->isClosed())
42 || (e.type() == OSM::Type::Relation && e.tagValue("type") == "multipolygon");
43}
44
45void BoundarySearch::resolveTagKeys(const OSM::DataSet& dataSet)
46{
47 // cache tag keys for fast lookup
48 m_tag.mxoid = dataSet.tagKey("mx:oid");
49 m_tag.building = dataSet.tagKey("building");
50 m_tag.railway = dataSet.tagKey("railway");
51 m_tag.aeroway = dataSet.tagKey("aeroway");
52 m_tag.public_transport = dataSet.tagKey("public_transport");
53 m_tag.amenity = dataSet.tagKey("amenity");
54 m_tag.tourism = dataSet.tagKey("tourism");
55 m_tag.leisure = dataSet.tagKey("leisure");
56 m_tag.shop = dataSet.tagKey("shop");
57}
58
59bool BoundarySearch::isRelevantPolygon(const OSM::Element e) const
60{
61 if (!isPolygon(e) || !OSM::contains(e.boundingBox(), m_center)) {
62 return false;
63 }
64
65 return !e.tagValue(m_tag.amenity).isEmpty()
66 || !e.tagValue(m_tag.tourism).isEmpty()
67 || !e.tagValue(m_tag.leisure).isEmpty()
68 || !e.tagValue(m_tag.shop).isEmpty();
69}
70
71/* There's a number of critieria being considered here:
72 * - a certain minimum radius around center (see BoundingBoxMargin)
73 * - an upper limit (BoundingBoxMaxSize), to avoid this growing out of control
74 * - buildings, stations or airports containing the center position
75 * -- for now with manual geometry re-assmbly from Marble vector tiles, ideally this will happen in a general step beforehand
76 * - relevant elements (e.g. platforms or terminal buildings) in the vicinity of center (TODO)
77 */
78OSM::BoundingBox BoundarySearch::boundingBox(const OSM::DataSet& dataSet)
79{
80 resolveTagKeys(dataSet);
81
82 if (m_relevantIds.empty()) { // first pass over the center tile
83 OSM::for_each(dataSet, [this, &dataSet](OSM::Element e) {
84 if (!e.boundingBox().isValid()) {
85 e.recomputeBoundingBox(dataSet);
86 }
87
88 const bool isRelevant = !e.tagValue(m_tag.building).isEmpty()
89 || !e.tagValue(m_tag.railway).isEmpty()
90 || !e.tagValue(m_tag.aeroway).isEmpty()
91 || isRelevantPolygon(e);
92
93 if (!isRelevant) {
94 return;
95 }
96
97 m_relevantIds.insert(actualId(e, m_tag.mxoid));
98 m_bbox = OSM::unite(m_bbox, e.boundingBox());
99 }, OSM::IncludeRelations | OSM::IncludeWays);
100 }
101
102 OSM::BoundingBox bbox = m_bbox;
103 OSM::for_each(dataSet, [&](OSM::Element e) {
104 const auto railwayValue = e.tagValue(m_tag.railway);
105 const bool isStation = railwayValue == "station"
106 || railwayValue == "platform"
107 || e.tagValue(m_tag.building) == "train_station"
108 || e.tagValue(m_tag.public_transport) == "platform";
109 const bool isAirport = (e.tagValue(m_tag.aeroway) == "aerodrome");
110 const bool isRelevant = isRelevantPolygon(e);
111 if (!isStation && !isAirport && !isRelevant) {
112 return;
113 }
114
115 e.recomputeBoundingBox(dataSet); // unconditionally as this obviously grows as we load more data
116
117 if (m_relevantIds.count(actualId(e, m_tag.mxoid))) {
118 m_bbox = OSM::unite(m_bbox, e.boundingBox());
119 bbox = OSM::unite(m_bbox, bbox);
120 } else if (!isRelevant && OSM::intersects(e.boundingBox(), m_bbox)) {
121 bbox = OSM::unite(bbox, e.boundingBox());
122 }
123 }, OSM::IncludeRelations | OSM::IncludeWays);
124
125 return clampBoundingBox(growBoundingBox(bbox, BoundingBoxMargin), BoundingBoxMaxSize);
126}
127
128OSM::BoundingBox BoundarySearch::growBoundingBox(const OSM::BoundingBox &bbox, double meters) const
129{
130 const auto dlon = meters / OSM::distance(m_center.latF(), 0.0, m_center.latF(), 1.0);
131 const auto dlat = meters / OSM::distance(0.0, m_center.lonF(), 1.0, m_center.lonF());
132 return OSM::BoundingBox(OSM::Coordinate(bbox.min.latF() - dlat, bbox.min.lonF() - dlon),
133 OSM::Coordinate(bbox.max.latF() + dlat, bbox.max.lonF() + dlon));
134}
135
136OSM::BoundingBox BoundarySearch::clampBoundingBox(const OSM::BoundingBox &bbox, double meters) const
137{
138 // TODO don'T do this around the bbox center, but biased towards m_center
139 const auto dlon = std::max(0.0, bbox.widthF() - (meters / OSM::distance(m_center.latF(), 0.0, m_center.latF(), 1.0))) / 2.0;
140 const auto dlat = std::max(0.0, bbox.heightF() - (meters / OSM::distance(0.0, m_center.lonF(), 1.0, m_center.lonF()))) / 2.0;
141 return OSM::BoundingBox(OSM::Coordinate(bbox.min.latF() + dlat, bbox.min.lonF() + dlon),
142 OSM::Coordinate(bbox.max.latF() - dlat, bbox.max.lonF() - dlon));
143}
Bounding box, ie.
Definition datatypes.h:95
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition datatypes.h:37
A set of nodes, ways and relations.
Definition datatypes.h:340
TagKey tagKey(const char *keyName) const
Look up a tag key for the given tag name, if it exists.
Definition datatypes.cpp:27
A reference to any of OSM::Node/OSM::Way/OSM::Relation.
Definition element.h:24
void recomputeBoundingBox(const DataSet &dataSet)
Recompute the bounding box of this element.
Definition element.cpp:207
A key of an OSM tag.
Definition datatypes.h:179
OSM-based multi-floor indoor maps for buildings.
KOSM_EXPORT double distance(double lat1, double lon1, double lat2, double lon2)
Distance between two coordinates.
Definition geomath.cpp:16
int64_t Id
OSM element identifier.
Definition datatypes.h:30
bool isEmpty() const const
qlonglong toLongLong(bool *ok, int base) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:03 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.