KOSMIndoorMap

boundarysearch.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]>
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 
15 using namespace KOSMIndoorMap;
16 
17 enum {
18  BoundingBoxMargin = 100, // margin around the final result, in meters
19  BoundingBoxMaxSize = 2000, // maximum width/height of the bounding box, in meters
20 };
21 
22 void BoundarySearch::init(OSM::Coordinate coord)
23 {
24  m_center = coord;
25 
26  m_bbox = {coord, coord};
27  m_relevantIds.clear();
28 }
29 
30 static 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 
39 /* There's a number of critieria being considered here:
40  * - a certain minimum radius around center (see BoundingBoxMargin)
41  * - an upper limit (BoundingBoxMaxSize), to avoid this growing out of control
42  * - buildings, stations or airports containing the center position
43  * -- for now with manual geometry re-assmbly from Marble vector tiles, ideally this will happen in a general step beforehand
44  * - relevant elements (e.g. platforms or terminal buildings) in the vicinity of center (TODO)
45  */
46 OSM::BoundingBox BoundarySearch::boundingBox(const OSM::DataSet& dataSet)
47 {
48  // cache tag keys for fast lookup
49  const auto buildingTag = dataSet.tagKey("building");
50  const auto railwayTag = dataSet.tagKey("railway");
51  const auto aerowayTag = dataSet.tagKey("aeroway");
52  const auto mxoidTag = dataSet.tagKey("mx:oid");
53 
54  if (m_relevantIds.empty()) { // first pass over the center tile
55  OSM::for_each(dataSet, [&](OSM::Element e) {
56  const bool isRelevant = !e.tagValue(buildingTag).isEmpty()
57  || !e.tagValue(railwayTag).isEmpty()
58  || !e.tagValue(aerowayTag).isEmpty();
59 
60  if (!isRelevant) {
61  return;
62  }
63  if (!e.boundingBox().isValid()) {
64  e.recomputeBoundingBox(dataSet);
65  }
66 
67  m_relevantIds.insert(actualId(e, mxoidTag));
68  m_bbox = OSM::unite(m_bbox, e.boundingBox());
69  }, OSM::IncludeRelations | OSM::IncludeWays);
70  }
71 
72  OSM::BoundingBox bbox = m_bbox;
73  OSM::for_each(dataSet, [&](OSM::Element e) {
74  // TODO cache the remaining tag keys here too
75  const auto railwayValue = e.tagValue(railwayTag);
76  const bool isStation = railwayValue == "station"
77  || railwayValue == "platform"
78  || e.tagValue(buildingTag) == "train_station"
79  || e.tagValue("public_transport") == "platform";
80  const bool isAirport = (e.tagValue(aerowayTag) == "aerodrome");
81  if (!isStation && !isAirport) {
82  return;
83  }
84 
85  e.recomputeBoundingBox(dataSet); // unconditionally as this obviously grows as we load more data
86 
87  if (m_relevantIds.count(actualId(e, mxoidTag))) {
88  m_bbox = OSM::unite(m_bbox, e.boundingBox());
89  bbox = OSM::unite(m_bbox, bbox);
90  } else if (OSM::intersects(e.boundingBox(), m_bbox)) {
91  bbox = OSM::unite(bbox, e.boundingBox());
92  }
93  }, OSM::IncludeRelations | OSM::IncludeWays);
94 
95  return clampBoundingBox(growBoundingBox(bbox, BoundingBoxMargin), BoundingBoxMaxSize);
96 }
97 
98 OSM::BoundingBox BoundarySearch::growBoundingBox(const OSM::BoundingBox &bbox, double meters) const
99 {
100  const auto dlon = meters / OSM::distance(m_center.latF(), 0.0, m_center.latF(), 1.0);
101  const auto dlat = meters / OSM::distance(0.0, m_center.lonF(), 1.0, m_center.lonF());
102  return OSM::BoundingBox(OSM::Coordinate(bbox.min.latF() - dlat, bbox.min.lonF() - dlon),
103  OSM::Coordinate(bbox.max.latF() + dlat, bbox.max.lonF() + dlon));
104 }
105 
106 OSM::BoundingBox BoundarySearch::clampBoundingBox(const OSM::BoundingBox &bbox, double meters) const
107 {
108  // TODO don'T do this around the bbox center, but biased towards m_center
109  const auto dlon = std::max(0.0, bbox.widthF() - (meters / OSM::distance(m_center.latF(), 0.0, m_center.latF(), 1.0))) / 2.0;
110  const auto dlat = std::max(0.0, bbox.heightF() - (meters / OSM::distance(0.0, m_center.lonF(), 1.0, m_center.lonF()))) / 2.0;
111  return OSM::BoundingBox(OSM::Coordinate(bbox.min.latF() + dlat, bbox.min.lonF() + dlon),
112  OSM::Coordinate(bbox.max.latF() - dlat, bbox.max.lonF() - dlon));
113 }
OSM-based multi-floor indoor maps for buildings.
bool isEmpty() const const
Coordinate, stored as 1e7 * degree to avoid floating point precision issues, and offset to unsigned v...
Definition: datatypes.h:37
A reference to any of OSM::Node/OSMWay/OSMRelation.
Definition: element.h:22
A key of an OSM tag.
Definition: datatypes.h:179
void recomputeBoundingBox(const DataSet &dataSet)
Recompute the bounding box of this element.
Definition: element.cpp:207
TagKey tagKey(const char *keyName) const
Look up a tag key for the given tag name, if it exists.
Definition: datatypes.cpp:27
int64_t Id
OSM element identifier.
Definition: datatypes.h:27
qlonglong toLongLong(bool *ok, int base) const const
A set of nodes, ways and relations.
Definition: datatypes.h:283
KOSM_EXPORT double distance(double lat1, double lon1, double lat2, double lon2)
Distance between two coordinates.
Definition: geomath.cpp:17
Bounding box, ie.
Definition: datatypes.h:95
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 23:04:00 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.