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 }