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 "geomath.h" 0008 0009 #include <QLineF> 0010 0011 #include <cmath> 0012 #include <limits> 0013 0014 using namespace OSM; 0015 0016 // see https://en.wikipedia.org/wiki/Haversine_formula 0017 uint32_t OSM::distance(double lat1, double lon1, double lat2, double lon2) 0018 { 0019 const auto degToRad = M_PI / 180.0; 0020 const auto earthRadius = 6371000.0; // in meters 0021 0022 const auto d_lat = (lat1 - lat2) * degToRad; 0023 const auto d_lon = (lon1 - lon2) * degToRad; 0024 0025 const auto a = pow(sin(d_lat / 2.0), 2) + cos(lat1 * degToRad) * cos(lat2 * degToRad) * pow(sin(d_lon / 2.0), 2); 0026 return 2.0 * earthRadius * atan2(sqrt(a), sqrt(1.0 - a));} 0027 0028 uint32_t OSM::distance(Coordinate coord1, Coordinate coord2) 0029 { 0030 return distance(coord1.latF(), coord1.lonF(), coord2.latF(), coord2.lonF()); 0031 } 0032 0033 uint32_t OSM::distance(const std::vector<const OSM::Node*> &path, OSM::Coordinate coord) 0034 { 0035 if (path.empty()) { 0036 return std::numeric_limits<uint32_t>::max(); 0037 } 0038 0039 if (path.size() == 1) { 0040 return distance(path[0]->coordinate, coord); 0041 } 0042 0043 uint32_t dist = std::numeric_limits<uint32_t>::max(); 0044 OSM::Id firstNode = 0; 0045 for (auto it = path.begin(); it != std::prev(path.end()); ++it) { 0046 const auto nextIt = std::next(it); 0047 if (firstNode == 0) { // starting a new loop 0048 firstNode = (*it)->id; 0049 } 0050 if ((*nextIt)->id == firstNode) { // just closed a loop, so this is not a line on the path 0051 firstNode = 0; 0052 continue; 0053 } 0054 0055 // compute distance between line segment and coord 0056 const QLineF lineSegment(QPointF((*it)->coordinate.latF(), (*it)->coordinate.lonF()), QPointF((*nextIt)->coordinate.latF(), (*nextIt)->coordinate.lonF())); 0057 QLineF n = lineSegment.normalVector(); 0058 n.translate(coord.latF() - n.p1().x(), coord.lonF() - n.p1().y()); 0059 QPointF p; 0060 const auto intersect = lineSegment.intersects(n, &p); 0061 if (intersect == QLineF::BoundedIntersection) { 0062 dist = std::min(dist, distance(p.x(), p.y(), coord.latF(), coord.lonF())); 0063 } else { 0064 dist = std::min(dist, std::min(distance((*it)->coordinate, coord), distance((*nextIt)->coordinate, coord))); 0065 } 0066 } 0067 return dist; 0068 }