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 }