File indexing completed on 2024-04-28 03:50:34
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 "MarbleDebug.h" 0009 #include "MarbleLocale.h" 0010 #include "GeoDataPlacemark.h" 0011 #include "GeoDataExtendedData.h" 0012 #include "GeoDataData.h" 0013 #include "HttpDownloadManager.h" 0014 #include "osm/OsmPlacemarkData.h" 0015 0016 #include <QUrl> 0017 #include <QTimer> 0018 #include <QNetworkReply> 0019 #include <QDomDocument> 0020 0021 namespace Marble 0022 { 0023 0024 OsmNominatimRunner::OsmNominatimRunner( QObject *parent ) : 0025 ReverseGeocodingRunner( parent ), 0026 m_manager(this) 0027 { 0028 connect(&m_manager, SIGNAL(finished(QNetworkReply*)), 0029 this, SLOT(handleResult(QNetworkReply*))); 0030 } 0031 0032 OsmNominatimRunner::~OsmNominatimRunner() 0033 { 0034 // nothing to do 0035 } 0036 0037 0038 void OsmNominatimRunner::returnNoReverseGeocodingResult() 0039 { 0040 emit reverseGeocodingFinished( m_coordinates, GeoDataPlacemark() ); 0041 } 0042 0043 void OsmNominatimRunner::reverseGeocoding( const GeoDataCoordinates &coordinates ) 0044 { 0045 m_coordinates = coordinates; 0046 QString base = "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 = "&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("User-Agent", HttpDownloadManager::userAgent("Browser", "OsmNominatimRunner") ); 0055 0056 QEventLoop eventLoop; 0057 0058 QTimer timer; 0059 timer.setSingleShot( true ); 0060 timer.setInterval( 15000 ); 0061 0062 connect( &timer, SIGNAL(timeout()), 0063 &eventLoop, SLOT(quit())); 0064 connect( this, SIGNAL(reverseGeocodingFinished(GeoDataCoordinates,GeoDataPlacemark)), 0065 &eventLoop, SLOT(quit()) ); 0066 0067 // @todo FIXME Must currently be done in the main thread, see bug 257376 0068 QTimer::singleShot( 0, this, SLOT(startReverseGeocoding()) ); 0069 timer.start(); 0070 0071 eventLoop.exec(); 0072 } 0073 0074 void OsmNominatimRunner::startReverseGeocoding() 0075 { 0076 QNetworkReply *reply = m_manager.get( m_request ); 0077 connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), 0078 this, SLOT(returnNoReverseGeocodingResult())); 0079 } 0080 0081 void OsmNominatimRunner::handleResult( QNetworkReply* reply ) 0082 { 0083 if ( !reply->bytesAvailable() ) { 0084 returnNoReverseGeocodingResult(); 0085 return; 0086 } 0087 0088 QDomDocument xml; 0089 if ( !xml.setContent( reply->readAll() ) ) { 0090 mDebug() << "Cannot parse osm nominatim result " << xml.toString(); 0091 returnNoReverseGeocodingResult(); 0092 return; 0093 } 0094 0095 QDomElement root = xml.documentElement(); 0096 QDomNodeList places = root.elementsByTagName(QStringLiteral("result")); 0097 if ( places.size() == 1 ) { 0098 QString address = places.item( 0 ).toElement().text(); 0099 GeoDataPlacemark placemark; 0100 placemark.setVisualCategory(GeoDataPlacemark::Coordinate); 0101 placemark.setAddress( address ); 0102 placemark.setCoordinate( m_coordinates ); 0103 0104 QDomNode details = root.firstChildElement(QStringLiteral("addressparts")); 0105 extractChildren( details, placemark ); 0106 0107 emit reverseGeocodingFinished( m_coordinates, placemark ); 0108 } else { 0109 returnNoReverseGeocodingResult(); 0110 } 0111 } 0112 0113 void OsmNominatimRunner::extractChildren(const QDomNode &node, GeoDataPlacemark &placemark) 0114 { 0115 QMap<QString, QString> tagTranslator; 0116 tagTranslator["house_number"] = "addr:housenumber"; 0117 tagTranslator["road"] = "addr:street"; 0118 tagTranslator["suburb"] = "addr:suburb"; 0119 tagTranslator["city"] = "addr:city"; 0120 tagTranslator["state_district"] = "addr:district"; 0121 tagTranslator["state"] = "addr:state"; 0122 tagTranslator["postcode"] = "addr:postcode"; 0123 tagTranslator["country_code"] = "addr:country"; // correct mapping 0124 // @todo Find a proper mapping for those 0125 //tagTranslator["village"] = ""; 0126 //tagTranslator["town"] = ""; 0127 0128 GeoDataExtendedData data; 0129 OsmPlacemarkData osmData; 0130 QDomNodeList nodes = node.childNodes(); 0131 for (int i=0, n=nodes.length(); i<n; ++i) { 0132 QDomNode child = nodes.item(i); 0133 data.addValue( GeoDataData( child.nodeName(), child.toElement().text() ) ); 0134 0135 if (tagTranslator.contains(child.nodeName())) { 0136 QString const key = tagTranslator[child.nodeName()]; 0137 osmData.addTag(key, child.toElement().text()); 0138 } 0139 } 0140 placemark.setExtendedData(data); 0141 placemark.setOsmData(osmData); 0142 } 0143 0144 } // namespace Marble 0145 0146 #include "moc_OsmNominatimReverseGeocodingRunner.cpp"