File indexing completed on 2024-04-28 03:49:28

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2011 Dennis Nienhüser <nienhueser@kde.org>
0004 //
0005 
0006 #include "RouteSegment.h"
0007 
0008 #include "GeoDataLatLonAltBox.h"
0009 
0010 namespace Marble
0011 {
0012 
0013 RouteSegment::RouteSegment() :
0014     m_valid( false ),
0015     m_distance( 0.0 ),
0016     m_travelTime( 0 ),
0017     m_nextRouteSegment( nullptr )
0018 {
0019     // nothing to do
0020 }
0021 
0022 qreal RouteSegment::distance() const
0023 {
0024     return m_distance;
0025 }
0026 
0027 const Maneuver & RouteSegment::maneuver() const
0028 {
0029     return m_maneuver;
0030 }
0031 
0032 void RouteSegment::setManeuver( const Maneuver &maneuver )
0033 {
0034     m_maneuver = maneuver;
0035     m_valid = true;
0036 }
0037 
0038 const GeoDataLineString & RouteSegment::path() const
0039 {
0040     return m_path;
0041 }
0042 
0043 void RouteSegment::setPath( const GeoDataLineString &path )
0044 {
0045     m_path = path;
0046     m_distance = m_path.length( EARTH_RADIUS );
0047     m_bounds = m_path.latLonAltBox();
0048     m_valid = true;
0049 }
0050 
0051 int RouteSegment::travelTime() const
0052 {
0053     return m_travelTime;
0054 }
0055 
0056 void RouteSegment::setTravelTime( int seconds )
0057 {
0058     m_travelTime = seconds;
0059     m_valid = true;
0060 }
0061 
0062 GeoDataLatLonBox RouteSegment::bounds() const
0063 {
0064     return m_bounds;
0065 }
0066 
0067 const RouteSegment & RouteSegment::nextRouteSegment() const
0068 {
0069     if ( m_nextRouteSegment ) {
0070         return *m_nextRouteSegment;
0071     }
0072 
0073     static RouteSegment invalid;
0074     return invalid;
0075 }
0076 
0077 void RouteSegment::setNextRouteSegment( const RouteSegment* segment )
0078 {
0079     m_nextRouteSegment = segment;
0080     if ( segment ) {
0081         m_valid = true;
0082     }
0083 }
0084 
0085 bool RouteSegment::isValid() const
0086 {
0087     return m_valid;
0088 }
0089 
0090 qreal RouteSegment::distancePointToLine(const GeoDataCoordinates &p, const GeoDataCoordinates &a, const GeoDataCoordinates &b)
0091 {
0092     return EARTH_RADIUS * p.sphericalDistanceTo(projected(p, a, b));
0093 }
0094 
0095 GeoDataCoordinates RouteSegment::projected(const GeoDataCoordinates &p, const GeoDataCoordinates &a, const GeoDataCoordinates &b)
0096 {
0097     qreal const y0 = p.latitude();
0098     qreal const x0 = p.longitude();
0099     qreal const y1 = a.latitude();
0100     qreal const x1 = a.longitude();
0101     qreal const y2 = b.latitude();
0102     qreal const x2 = b.longitude();
0103     qreal const y01 = x0 - x1;
0104     qreal const x01 = y0 - y1;
0105     qreal const y21 = x2 - x1;
0106     qreal const x21 = y2 - y1;
0107     qreal const len = x21*x21 + y21*y21;
0108     qreal const t = (x01*x21 + y01*y21) / len;
0109     if ( t<0.0 ) {
0110         return a;
0111     } else if ( t > 1.0 ) {
0112         return b;
0113     } else {
0114         // a + t (b - a);
0115         qreal const lon = x1 + t * ( x2 - x1 );
0116         qreal const lat = y1 + t * ( y2 - y1 );
0117         return GeoDataCoordinates( lon, lat );
0118     }
0119 
0120 }
0121 
0122 qreal RouteSegment::distanceTo( const GeoDataCoordinates &point, GeoDataCoordinates &closest, GeoDataCoordinates &interpolated ) const
0123 {
0124     Q_ASSERT( !m_path.isEmpty() );
0125 
0126     if ( m_path.size() == 1 ) {
0127         closest = m_path.first();
0128         return EARTH_RADIUS * m_path.first().sphericalDistanceTo(point);
0129     }
0130 
0131     qreal minDistance = -1.0;
0132     int minIndex = 0;
0133     for ( int i=1; i<m_path.size(); ++i ) {
0134         qreal const distance = distancePointToLine( point, m_path[i-1], m_path[i] );
0135         if ( minDistance < 0.0 || distance < minDistance ) {
0136             minDistance = distance;
0137             minIndex = i;
0138         }
0139     }
0140 
0141     closest = m_path[minIndex];
0142     if ( minIndex == 0 ) {
0143         interpolated = closest;
0144     } else {
0145         interpolated = projected( point, m_path[minIndex-1], m_path[minIndex] );
0146     }
0147 
0148     return minDistance;
0149 }
0150 
0151 qreal RouteSegment::minimalDistanceTo( const GeoDataCoordinates &point ) const
0152 {
0153     if ( bounds().contains( point) ) {
0154         return 0.0;
0155     }
0156 
0157     qreal north(0.0), east(0.0), south(0.0), west(0.0);
0158     bounds().boundaries( north, south, east, west );
0159     GeoDataCoordinates const northWest( west, north );
0160     GeoDataCoordinates const northEast( east, north );
0161     GeoDataCoordinates const southhWest( west, south );
0162     GeoDataCoordinates const southEast( east, south );
0163 
0164     qreal distNorth = distancePointToLine( point, northWest, northEast );
0165     qreal distEast = distancePointToLine( point, northEast, southEast );
0166     qreal distSouth = distancePointToLine( point, southhWest, southEast );
0167     qreal distWest = distancePointToLine( point, northWest, southhWest );
0168     return qMin( qMin( distNorth, distEast ), qMin( distWest, distSouth ) );
0169 }
0170 
0171 qreal RouteSegment::projectedDirection(const GeoDataCoordinates &point) const
0172 {
0173     if (m_path.size() < 2){
0174         return 0;
0175     }
0176 
0177     qreal minDistance = -1.0;
0178     int minIndex = 0;
0179     for ( int i=1; i<m_path.size(); ++i ) {
0180         qreal const distance = distancePointToLine( point, m_path[i-1], m_path[i] );
0181         if ( minDistance < 0.0 || distance < minDistance ) {
0182             minDistance = distance;
0183             minIndex = i;
0184         }
0185     }
0186 
0187     if ( minIndex == 0 ) {
0188         return m_path[0].bearing( m_path[1], GeoDataCoordinates::Degree, GeoDataCoordinates::FinalBearing );
0189     } else {
0190         return m_path[minIndex-1].bearing( m_path[minIndex], GeoDataCoordinates::Degree, GeoDataCoordinates::FinalBearing );
0191     }
0192 }
0193 
0194 bool RouteSegment::operator ==(const RouteSegment &other) const
0195 {
0196     return  m_valid == other.m_valid &&
0197             m_distance == other.m_distance &&
0198             m_maneuver == other.m_maneuver &&
0199             m_travelTime == other.m_travelTime &&
0200             m_bounds == other.m_bounds &&
0201             m_nextRouteSegment == other.m_nextRouteSegment;
0202 }
0203 
0204 bool RouteSegment::operator !=(const RouteSegment &other) const
0205 {
0206     return !(other == *this);
0207 }
0208 
0209 }