File indexing completed on 2025-01-05 03:59:28
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org> 0004 // 0005 0006 #include "OsmNominatimReverseGeocodingRunner.h" 0007 0008 #include "MarbleLocale.h" 0009 #include "GeoDataPlacemark.h" 0010 #include "GeoDataExtendedData.h" 0011 #include "GeoDataData.h" 0012 #include "HttpDownloadManager.h" 0013 #include "OsmPlacemarkData.h" 0014 0015 #include <QUrl> 0016 #include <QTimer> 0017 #include <QNetworkReply> 0018 #include <QDomDocument> 0019 0020 #include "digikam_debug.h" 0021 0022 namespace Marble 0023 { 0024 0025 OsmNominatimRunner::OsmNominatimRunner(QObject* parent) 0026 : ReverseGeocodingRunner(parent), 0027 m_manager (this) 0028 { 0029 connect(&m_manager, SIGNAL(finished(QNetworkReply*)), 0030 this, SLOT(handleResult(QNetworkReply*))); 0031 } 0032 0033 OsmNominatimRunner::~OsmNominatimRunner() 0034 { 0035 // nothing to do 0036 } 0037 0038 void OsmNominatimRunner::returnNoReverseGeocodingResult() 0039 { 0040 Q_EMIT reverseGeocodingFinished( m_coordinates, GeoDataPlacemark() ); 0041 } 0042 0043 void OsmNominatimRunner::reverseGeocoding( const GeoDataCoordinates &coordinates ) 0044 { 0045 m_coordinates = coordinates; 0046 QString base = QString::fromUtf8("https://nominatim.openstreetmap.org/reverse?format=xml&addressdetails=1"); 0047 // @todo: Alternative URI with addressdetails=1 could be used for shorter placemark name 0048 QString query = QString::fromUtf8("&lon=%1&lat=%2&accept-language=%3"); 0049 double lon = coordinates.longitude( GeoDataCoordinates::Degree ); 0050 double lat = coordinates.latitude( GeoDataCoordinates::Degree ); 0051 QString url = QString( base + query ).arg( lon ).arg( lat ).arg( MarbleLocale::languageCode() ); 0052 0053 m_request.setUrl(QUrl(url)); 0054 m_request.setRawHeader(QByteArray("User-Agent"), 0055 HttpDownloadManager::userAgent(QLatin1String("Browser"), 0056 QLatin1String("OsmNominatimRunner")) ); 0057 0058 QEventLoop eventLoop; 0059 0060 QTimer timer; 0061 timer.setSingleShot( true ); 0062 timer.setInterval( 15000 ); 0063 0064 connect( &timer, SIGNAL(timeout()), 0065 &eventLoop, SLOT(quit())); 0066 0067 connect( this, SIGNAL(reverseGeocodingFinished(GeoDataCoordinates,GeoDataPlacemark)), 0068 &eventLoop, SLOT(quit()) ); 0069 0070 // @todo FIXME Must currently be done in the main thread, see bug 257376 0071 QTimer::singleShot( 0, this, SLOT(startReverseGeocoding()) ); 0072 timer.start(); 0073 0074 eventLoop.exec(); 0075 } 0076 0077 void OsmNominatimRunner::startReverseGeocoding() 0078 { 0079 QNetworkReply *reply = m_manager.get( m_request ); 0080 0081 connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), 0082 this, SLOT(returnNoReverseGeocodingResult())); 0083 } 0084 0085 void OsmNominatimRunner::handleResult( QNetworkReply* reply ) 0086 { 0087 if ( !reply->bytesAvailable() ) 0088 { 0089 returnNoReverseGeocodingResult(); 0090 return; 0091 } 0092 0093 QDomDocument xml; 0094 0095 if ( !xml.setContent( reply->readAll() ) ) 0096 { 0097 qCDebug(DIGIKAM_MARBLE_LOG) << "Cannot parse osm nominatim result " << xml.toString(); 0098 returnNoReverseGeocodingResult(); 0099 return; 0100 } 0101 0102 QDomElement root = xml.documentElement(); 0103 QDomNodeList places = root.elementsByTagName(QStringLiteral("result")); 0104 0105 if ( places.size() == 1 ) 0106 { 0107 QString address = places.item( 0 ).toElement().text(); 0108 GeoDataPlacemark placemark; 0109 placemark.setVisualCategory(GeoDataPlacemark::Coordinate); 0110 placemark.setAddress( address ); 0111 placemark.setCoordinate( m_coordinates ); 0112 0113 QDomNode details = root.firstChildElement(QStringLiteral("addressparts")); 0114 extractChildren( details, placemark ); 0115 0116 Q_EMIT reverseGeocodingFinished( m_coordinates, placemark ); 0117 } 0118 else 0119 { 0120 returnNoReverseGeocodingResult(); 0121 } 0122 } 0123 0124 void OsmNominatimRunner::extractChildren(const QDomNode &node, GeoDataPlacemark &placemark) 0125 { 0126 QMap<QString, QString> tagTranslator; 0127 tagTranslator[QLatin1String("house_number")] = QLatin1String("addr:housenumber"); 0128 tagTranslator[QLatin1String("road")] = QLatin1String("addr:street"); 0129 tagTranslator[QLatin1String("suburb")] = QLatin1String("addr:suburb"); 0130 tagTranslator[QLatin1String("city")] = QLatin1String("addr:city"); 0131 tagTranslator[QLatin1String("state_district")] = QLatin1String("addr:district"); 0132 tagTranslator[QLatin1String("state")] = QLatin1String("addr:state"); 0133 tagTranslator[QLatin1String("postcode")] = QLatin1String("addr:postcode"); 0134 tagTranslator[QLatin1String("country_code")] = QLatin1String("addr:country"); // correct mapping 0135 // @todo Find a proper mapping for those 0136 //tagTranslator["village"] = ""; 0137 //tagTranslator["town"] = ""; 0138 0139 GeoDataExtendedData data; 0140 OsmPlacemarkData osmData; 0141 QDomNodeList nodes = node.childNodes(); 0142 0143 for (int i = 0 , n = nodes.length() ; i < n ; ++i) 0144 { 0145 QDomNode child = nodes.item(i); 0146 data.addValue( GeoDataData( child.nodeName(), child.toElement().text() ) ); 0147 0148 if (tagTranslator.contains(child.nodeName())) 0149 { 0150 QString const key = tagTranslator[child.nodeName()]; 0151 osmData.addTag(key, child.toElement().text()); 0152 } 0153 } 0154 0155 placemark.setExtendedData(data); 0156 placemark.setOsmData(osmData); 0157 } 0158 0159 } // namespace Marble 0160 0161 #include "moc_OsmNominatimReverseGeocodingRunner.cpp"