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 }