File indexing completed on 2024-04-28 03:50:35
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2015 Constantin Mihalache <mihalache.c94@gmail.com> 0004 // 0005 0006 //self 0007 #include "OpenLocationCodeSearchRunner.h" 0008 0009 #include "GeoDataPlacemark.h" 0010 #include "GeoDataLinearRing.h" 0011 #include "GeoDataPolygon.h" 0012 #include "GeoDataStyle.h" 0013 #include "GeoDataLineStyle.h" 0014 #include "GeoDataPolyStyle.h" 0015 0016 //Qt 0017 #include <QColor> 0018 #include <QVector> 0019 0020 namespace Marble 0021 { 0022 0023 OpenLocationCodeSearchRunner::OpenLocationCodeSearchRunner( QObject *parent ): 0024 SearchRunner( parent ) 0025 { 0026 // initialize the charIndex map 0027 QString const acceptedChars = "23456789CFGHJMPQRVWX"; 0028 for( int index = 0; index < acceptedChars.size(); index++ ) { 0029 charIndex[ acceptedChars[index] ] = index; 0030 } 0031 } 0032 0033 void OpenLocationCodeSearchRunner::search( const QString &searchTerm, const GeoDataLatLonBox &preferred ) 0034 { 0035 Q_UNUSED( preferred ); 0036 0037 QVector<GeoDataPlacemark*> result; 0038 0039 if( isValidOLC( searchTerm.toUpper() ) ) { 0040 GeoDataLatLonBox boundingBox = decodeOLC( searchTerm.toUpper() ); 0041 if( !boundingBox.isEmpty() ) { 0042 GeoDataPlacemark *placemark = new GeoDataPlacemark( searchTerm ); 0043 0044 GeoDataPolygon *geometry = new GeoDataPolygon( polygonFromLatLonBox( boundingBox ) ); 0045 placemark->setGeometry( geometry ); 0046 0047 GeoDataStyle::Ptr style = GeoDataStyle::Ptr(new GeoDataStyle()); 0048 GeoDataLineStyle lineStyle; 0049 GeoDataPolyStyle polyStyle; 0050 lineStyle.setColor( QColor( Qt::GlobalColor::red ) ); 0051 lineStyle.setWidth( 2 ); 0052 polyStyle.setFill( false ); 0053 style->setLineStyle( lineStyle ); 0054 style->setPolyStyle( polyStyle ); 0055 placemark->setStyle( style ); 0056 0057 result.append( placemark ); 0058 } 0059 } 0060 0061 emit searchFinished( result ); 0062 } 0063 0064 GeoDataPolygon OpenLocationCodeSearchRunner::polygonFromLatLonBox( const GeoDataLatLonBox& boundingBox ) const 0065 { 0066 if( boundingBox.isEmpty() ) { 0067 return GeoDataPolygon(); 0068 } 0069 0070 GeoDataPolygon poly; 0071 GeoDataLinearRing outerBoundry; 0072 // north-west corner 0073 outerBoundry.append( GeoDataCoordinates( boundingBox.west(), boundingBox.north(), GeoDataCoordinates::Unit::Degree ) ); 0074 // north-east corner 0075 outerBoundry.append( GeoDataCoordinates( boundingBox.east(), boundingBox.north(), GeoDataCoordinates::Unit::Degree ) ); 0076 // south-east corner 0077 outerBoundry.append( GeoDataCoordinates( boundingBox.east(), boundingBox.south(), GeoDataCoordinates::Unit::Degree ) ); 0078 // south-west corner 0079 outerBoundry.append( GeoDataCoordinates( boundingBox.west(), boundingBox.south(), GeoDataCoordinates::Unit::Degree ) ); 0080 0081 poly.setOuterBoundary( outerBoundry ); 0082 0083 return poly; 0084 } 0085 0086 GeoDataLatLonBox OpenLocationCodeSearchRunner::decodeOLC( const QString &olc ) const 0087 { 0088 if( !isValidOLC( olc ) ) { 0089 return GeoDataLatLonBox(); 0090 } 0091 0092 // remove padding 0093 QString decoded = olc; 0094 decoded = decoded.remove( QRegExp("[0+]") ); 0095 qreal southLatitude = 0; 0096 qreal westLongitude = 0; 0097 0098 int digit = 0; 0099 qreal latitudeResolution = 400; 0100 qreal longitudeResolution = 400; 0101 0102 while( digit < decoded.size() ) { 0103 if( digit < 10 ) { 0104 latitudeResolution /= 20; 0105 longitudeResolution /= 20; 0106 southLatitude += latitudeResolution * charIndex[ decoded[digit] ]; 0107 westLongitude += longitudeResolution * charIndex[ decoded[digit+1] ]; 0108 digit += 2; 0109 } 0110 else { 0111 latitudeResolution /= 5; 0112 longitudeResolution /= 4; 0113 southLatitude += latitudeResolution *( charIndex[ decoded[digit] ] / 4 ); 0114 westLongitude += longitudeResolution *( charIndex[ decoded[digit] ] % 4 ); 0115 digit += 1; 0116 } 0117 } 0118 return GeoDataLatLonBox( southLatitude - 90 + latitudeResolution, southLatitude - 90, westLongitude - 180 + longitudeResolution, westLongitude - 180, GeoDataCoordinates::Unit::Degree ); 0119 } 0120 0121 bool OpenLocationCodeSearchRunner::isValidOLC( const QString& olc ) const 0122 { 0123 // It must have only one SEPARATOR located at an even index in 0124 // the string. 0125 QChar const separator(QLatin1Char('+')); 0126 int separatorPos = olc.indexOf(separator); 0127 if( separatorPos == -1 0128 || separatorPos != olc.lastIndexOf(separator) 0129 || separatorPos % 2 != 0 ) 0130 { 0131 return false; 0132 } 0133 int const separatorPosition = 8; 0134 // It must be a full open location code. 0135 if( separatorPos != separatorPosition ) { 0136 return false; 0137 } 0138 0139 // Test the first two characters as only some characters of the 0140 // ACCEPTED_CHARS are allowed. 0141 // 0142 // First latitude character can only take one of the first 9 values. 0143 int index0 = charIndex.value( olc[0], -1 ); 0144 if( index0 == -1 || index0 > 8 ) { 0145 return false; 0146 } 0147 // First longitude character can only take one of the first 18 values. 0148 int index1 = charIndex.value( olc[1], -1 ); 0149 if( index1 == -1 || index1 > 17) { 0150 return false; 0151 } 0152 0153 // Test the characters before the SEPARATOR. 0154 QChar const suffixPadding(QLatin1Char('0')); 0155 bool paddingBegun = false; 0156 for( int index = 0; index < separatorPos; index++ ) { 0157 if( paddingBegun ) { 0158 // Once padding has begun, there should be only padding. 0159 if( olc[index] != suffixPadding ) { 0160 return false; 0161 } 0162 continue; 0163 } 0164 if( charIndex.contains( olc[index] ) ) { 0165 continue; 0166 } 0167 if( olc[index] == suffixPadding ) { 0168 paddingBegun = true; 0169 // Padding can start only at an even index. 0170 if( index % 2 != 0 ) { 0171 return false; 0172 } 0173 continue; 0174 } 0175 return false; 0176 } 0177 0178 // Test the characters after the SEPARATOR. 0179 if( olc.size() > separatorPos +1 ) { 0180 if( paddingBegun ) { 0181 return false; 0182 } 0183 // Only one character after the SEPARATOR is not allowed. 0184 if( olc.size() == separatorPos + 2 ) { 0185 return false; 0186 } 0187 for( int index = separatorPos + 1; index < olc.size(); index++ ) { 0188 if( !charIndex.contains( olc[index] ) ) { 0189 return false; 0190 } 0191 } 0192 } 0193 0194 return true; 0195 } 0196 0197 } 0198 0199 #include "moc_OpenLocationCodeSearchRunner.cpp"