diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2015-07-01 12:36:21 -0700 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2015-07-02 06:50:49 -0700 |
commit | b42bae2ce8b665c45020f53ef8b494dffc086d38 (patch) | |
tree | 742c51c27e315a29250a77538b4f2ecb25b4afb2 | |
parent | 3055f4ac2212fef67a6e191538fe615f48be7042 (diff) | |
download | subsurface-b42bae2ce8b665c45020f53ef8b494dffc086d38.tar.gz |
Geo taxonomy: download the taxonomy data from geonames.org
There are a number of web servies we could use. All have their drawbacks.
This one is free with free data. It's daily limits are reasonably high.
For many coordinates I tested the results were good, for others at least
not terrible.
We can always consider supporting multiple such services. But for now this
seems like a reasonable choice.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r-- | divesitehelpers.cpp | 121 | ||||
-rw-r--r-- | divesitehelpers.h | 1 |
2 files changed, 102 insertions, 20 deletions
diff --git a/divesitehelpers.cpp b/divesitehelpers.cpp index 62b48ba94..4cc40f203 100644 --- a/divesitehelpers.cpp +++ b/divesitehelpers.cpp @@ -41,54 +41,135 @@ void ReverseGeoLookupThread::run() { QNetworkRequest request; QNetworkAccessManager *rgl = new QNetworkAccessManager(); + QEventLoop loop; + QString mapquestURL("http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&accept-language=%1&lat=%2&lon=%3"); + QString geonamesURL("http://api.geonames.org/findNearbyPlaceNameJSON?language=%1&lat=%2&lng=%3&radius=50&username=dirkhh"); + QString geonamesOceanURL("http://api.geonames.org/oceanJSON?language=%1&lat=%2&lng=%3&radius=50&username=dirkhh"); + QString divelogsURL("https://www.divelogs.de/mapsearch_divespotnames.php?lat=%1&lng=%2&radius=50"); + QTimer timer; + request.setRawHeader("Accept", "text/json"); request.setRawHeader("User-Agent", getUserAgent().toUtf8()); - QEventLoop loop; - QString apiCall("http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&accept-language=%1&lat=%2&lon=%3"); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + Q_FOREACH (const GeoLookupInfo& info, geo_lookup_data ) { - request.setUrl(apiCall.arg(uiLanguage(NULL)).arg(info.lat.udeg / 1000000.0).arg(info.lon.udeg / 1000000.0)); + struct dive_site *ds = get_dive_site_by_uuid(info.uuid); + + // first check the findNearbyPlaces API from geonames - that should give us country, state, city + request.setUrl(geonamesURL.arg(uiLanguage(NULL)).arg(info.lat.udeg / 1000000.0).arg(info.lon.udeg / 1000000.0)); QNetworkReply *reply = rgl->get(request); - QTimer timer; timer.setSingleShot(true); - - QEventLoop loop; - connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); - timer.start(500); // 30 secs. timeout + timer.start(5000); // 5 secs. timeout loop.exec(); if(timer.isActive()) { timer.stop(); - if(reply->error() > 0) + if(reply->error() > 0) { + report_error("got error accessing geonames.org: %s", reply->errorString()); goto clear_reply; - + } int v = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (v < 200 || v >= 300) goto clear_reply; - + QByteArray fullReply = reply->readAll(); QJsonParseError errorObject; - QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &errorObject); - if (errorObject.error != QJsonParseError::NoError) + QJsonDocument jsonDoc = QJsonDocument::fromJson(fullReply, &errorObject); + if (errorObject.error != QJsonParseError::NoError) { + report_error("error parsing geonames.org response: %s", errorObject.errorString()); goto clear_reply; - + } QJsonObject obj = jsonDoc.object(); - QJsonObject address = obj.value("address").toObject(); - - struct dive_site *ds = get_dive_site_by_uuid(info.uuid); - ds->notes = add_to_string(ds->notes, "countrytag: %s", address.value("country").toString().toUtf8().data()); - + QVariant geoNamesObject = obj.value("geonames").toVariant(); + QVariantList geoNames = geoNamesObject.toList(); + if (geoNames.count() > 0) { + QVariantMap firstData = geoNames.at(0).toMap(); + int ri = 0; + if (ds->taxonomy.category == NULL) + ds->taxonomy.category = alloc_taxonomy(); + // get all the data - OCEAN is special, so start at COUNTRY + for (int j = COUNTRY; j < NR_CATEGORIES; j++) { + if (firstData[taxonomy_api_names[j]].isValid()) { + ds->taxonomy.category[ri].category = j; + ds->taxonomy.category[ri].origin = taxonomy::GEOCODED; + free((void*)ds->taxonomy.category[ri].value); + ds->taxonomy.category[ri].value = copy_string(qPrintable(firstData[taxonomy_api_names[j]].toString())); + ri++; + } + } + ds->taxonomy.nr = ri; + mark_divelist_changed(true); + } else { + report_error("geonames.org did not provide reverse lookup information"); + qDebug() << "no reverse geo lookup; geonames returned\n" << fullReply; + } } else { + report_error("timeout accessing geonames.org"); + disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + reply->abort(); + } + // next check the oceans API to figure out the body of water + request.setUrl(geonamesOceanURL.arg(uiLanguage(NULL)).arg(info.lat.udeg / 1000000.0).arg(info.lon.udeg / 1000000.0)); + reply = rgl->get(request); + connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); + timer.start(5000); // 5 secs. timeout + loop.exec(); + if(timer.isActive()) { + timer.stop(); + if(reply->error() > 0) { + report_error("got error accessing oceans API of geonames.org: %s", reply->errorString()); + goto clear_reply; + } + int v = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (v < 200 || v >= 300) + goto clear_reply; + QByteArray fullReply = reply->readAll(); + QJsonParseError errorObject; + QJsonDocument jsonDoc = QJsonDocument::fromJson(fullReply, &errorObject); + if (errorObject.error != QJsonParseError::NoError) { + report_error("error parsing geonames.org response: %s", errorObject.errorString()); + goto clear_reply; + } + QJsonObject obj = jsonDoc.object(); + QVariant oceanObject = obj.value("ocean").toVariant(); + QVariantMap oceanName = oceanObject.toMap(); + if (oceanName["name"].isValid()) { + if (ds->taxonomy.category == NULL) + ds->taxonomy.category = alloc_taxonomy(); + ds->taxonomy.category[ds->taxonomy.nr].category = OCEAN; + qDebug() << "set category of slot" << ds->taxonomy.nr << "to OCEAN(1)"; + ds->taxonomy.category[ds->taxonomy.nr].origin = taxonomy::GEOCODED; + ds->taxonomy.category[ds->taxonomy.nr].value = copy_string(qPrintable(oceanName["name"].toString())); + ds->taxonomy.nr++; + mark_divelist_changed(true); + } + } else { + report_error("timeout accessing geonames.org"); disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit())); reply->abort(); } - clear_reply: +clear_reply: reply->deleteLater(); } rgl->deleteLater(); } +void ReverseGeoLookupThread::lookup(dive_site *ds) +{ + if (!ds) + return; + GeoLookupInfo info; + info.lat = ds->latitude; + info.lon = ds->longitude; + info.uuid = ds->uuid; + + geo_lookup_data.clear(); + geo_lookup_data.append(info); + run(); +} + extern "C" void add_geo_information_for_lookup(degrees_t latitude, degrees_t longitude, uint32_t uuid) { GeoLookupInfo info; info.lat = latitude; diff --git a/divesitehelpers.h b/divesitehelpers.h index dad60be36..a08069bc0 100644 --- a/divesitehelpers.h +++ b/divesitehelpers.h @@ -8,6 +8,7 @@ class ReverseGeoLookupThread : public QThread { Q_OBJECT public: static ReverseGeoLookupThread *instance(); + void lookup(struct dive_site *ds); void run() Q_DECL_OVERRIDE; private: |