File indexing completed on 2024-11-17 04:47:25
0001 /* 0002 SPDX-FileCopyrightText: 2020 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "element.h" 0008 0009 using namespace OSM; 0010 0011 Coordinate Element::center() const 0012 { 0013 switch (type()) { 0014 case Type::Null: 0015 return {}; 0016 case Type::Node: 0017 return node()->coordinate; 0018 case Type::Way: 0019 return way()->bbox.center(); 0020 case Type::Relation: 0021 return relation()->bbox.center(); 0022 } 0023 0024 return {}; 0025 } 0026 0027 BoundingBox Element::boundingBox() const 0028 { 0029 switch (type()) { 0030 case Type::Null: 0031 return {}; 0032 case Type::Node: 0033 return BoundingBox(node()->coordinate, node()->coordinate); 0034 case Type::Way: 0035 return way()->bbox; 0036 case Type::Relation: 0037 return relation()->bbox; 0038 } 0039 0040 return {}; 0041 } 0042 0043 QString Element::tagValue(const QLatin1StringView &key) const { 0044 switch (type()) { 0045 case Type::Null: 0046 return {}; 0047 case Type::Node: 0048 return OSM::tagValue(*node(), key); 0049 case Type::Way: 0050 return OSM::tagValue(*way(), key); 0051 case Type::Relation: 0052 return OSM::tagValue(*relation(), key); 0053 } 0054 0055 return {}; 0056 } 0057 0058 QString OSM::Element::tagValue(const char *key) const 0059 { 0060 return tagValue(QLatin1StringView(key)); 0061 } 0062 0063 QString Element::url() const 0064 { 0065 switch (type()) { 0066 case Type::Null: 0067 return {}; 0068 case Type::Node: 0069 return node()->url(); 0070 case Type::Way: 0071 return way()->url(); 0072 case Type::Relation: 0073 return relation()->url(); 0074 } 0075 0076 return {}; 0077 } 0078 0079 template <typename Iter> 0080 static void appendNodesFromWay(const DataSet &dataSet, std::vector<const Node*> &nodes, const Iter& nodeBegin, const Iter &nodeEnd) 0081 { 0082 nodes.reserve(nodes.size() + std::distance(nodeBegin, nodeEnd)); 0083 for (auto it = nodeBegin; it != nodeEnd; ++it) { 0084 const auto nodeIt = std::lower_bound(dataSet.nodes.begin(), dataSet.nodes.end(), (*it)); 0085 if (nodeIt == dataSet.nodes.end() || (*nodeIt).id != (*it)) { 0086 continue; 0087 } 0088 nodes.push_back(&(*nodeIt)); 0089 } 0090 } 0091 0092 static OSM::Id appendNextPath(const DataSet &dataSet, std::vector<const Node*> &nodes, OSM::Id startNode, std::vector<const Way*> &ways) 0093 { 0094 if (ways.empty()) { 0095 return {}; 0096 } 0097 0098 for (auto it = std::next(ways.begin()); it != ways.end(); ++it) { 0099 assert(!(*it)->nodes.empty()); // ensured in the caller 0100 if ((*it)->nodes.front() == startNode) { 0101 appendNodesFromWay(dataSet, nodes, (*it)->nodes.begin(), (*it)->nodes.end()); 0102 const auto lastNodeId = (*it)->nodes.back(); 0103 ways.erase(it); 0104 return lastNodeId; 0105 } 0106 // path segments can also be backwards 0107 if ((*it)->nodes.back() == startNode) { 0108 appendNodesFromWay(dataSet, nodes, (*it)->nodes.rbegin(), (*it)->nodes.rend()); 0109 const auto lastNodeId = (*it)->nodes.front(); 0110 ways.erase(it); 0111 return lastNodeId; 0112 } 0113 } 0114 0115 return {}; 0116 } 0117 0118 std::vector<const Node*> Element::outerPath(const DataSet &dataSet) const 0119 { 0120 switch (type()) { 0121 case Type::Null: 0122 return {}; 0123 case Type::Node: 0124 return {node()}; 0125 case Type::Way: 0126 { 0127 std::vector<const Node*> nodes; 0128 appendNodesFromWay(dataSet, nodes, way()->nodes.begin(), way()->nodes.end()); 0129 return nodes; 0130 } 0131 case Type::Relation: 0132 { 0133 if (tagValue("type") != QLatin1StringView("multipolygon")) { 0134 return {}; 0135 } 0136 0137 // collect the relevant ways 0138 std::vector<const Way*> ways; 0139 for (const auto &member : relation()->members) { 0140 if (member.role != QLatin1StringView("outer")) { 0141 continue; 0142 } 0143 const auto it = std::lower_bound(dataSet.ways.begin(), dataSet.ways.end(), member.id); 0144 if (it != dataSet.ways.end() && (*it).id == member.id && !(*it).nodes.empty()) { 0145 ways.push_back(&(*it)); 0146 } 0147 } 0148 0149 // stitch them together (there is no well-defined order) 0150 std::vector<const Node*> nodes; 0151 for (auto it = ways.begin(); it != ways.end();) { 0152 assert(!(*it)->nodes.empty()); // ensured above 0153 0154 appendNodesFromWay(dataSet, nodes, (*it)->nodes.begin(), (*it)->nodes.end()); 0155 const auto startNode = (*it)->nodes.front(); 0156 auto lastNode = (*it)->nodes.back(); 0157 0158 do { 0159 lastNode = appendNextPath(dataSet, nodes, lastNode, ways); 0160 } while (lastNode && lastNode != startNode); 0161 0162 it = ways.erase(it); 0163 } 0164 0165 return nodes; 0166 } 0167 } 0168 0169 return {}; 0170 } 0171 0172 void Element::recomputeBoundingBox(const DataSet &dataSet) 0173 { 0174 switch (type()) { 0175 case Type::Null: 0176 case Type::Node: 0177 break; 0178 case Type::Way: 0179 way()->bbox = std::accumulate(way()->nodes.begin(), way()->nodes.end(), OSM::BoundingBox(), [&dataSet](auto bbox, auto nodeId) { 0180 const auto nodeIt = std::lower_bound(dataSet.nodes.begin(), dataSet.nodes.end(), nodeId); 0181 if (nodeIt == dataSet.nodes.end() || (*nodeIt).id != nodeId) { 0182 return bbox; 0183 } 0184 return OSM::unite(bbox, {(*nodeIt).coordinate, (*nodeIt).coordinate}); 0185 }); 0186 break; 0187 case Type::Relation: 0188 relation()->bbox = {}; 0189 for_each_member(dataSet, *relation(), [this, &dataSet](auto mem) { 0190 mem.recomputeBoundingBox(dataSet); 0191 relation()->bbox = OSM::unite(relation()->bbox, mem.boundingBox()); 0192 }); 0193 break; 0194 } 0195 }