subsurface/divesitehelpers.cpp
Dirk Hohndel 88015f402b Geo taxonomy: special case for looking up data of the displayed dive site
While we are editing a dive the displayed dive site may revert back to the
special uuid of 0. This means the user modified the existing site so the
displayed dive site is now different from the dive site referenced in the
current dive.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2015-07-15 21:28:17 -07:00

209 lines
7.3 KiB
C++

//
// infrastructure to deal with dive sites
//
#include "divesitehelpers.h"
#include "divesite.h"
#include "helpers.h"
#include "usersurvey.h"
#include "membuffer.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QUrlQuery>
#include <QEventLoop>
#include <QTimer>
struct GeoLookupInfo {
degrees_t lat;
degrees_t lon;
uint32_t uuid;
};
QVector<GeoLookupInfo> geo_lookup_data;
ReverseGeoLookupThread* ReverseGeoLookupThread::instance() {
static ReverseGeoLookupThread* self = new ReverseGeoLookupThread();
return self;
}
ReverseGeoLookupThread::ReverseGeoLookupThread(QObject *obj) : QThread(obj)
{
}
void ReverseGeoLookupThread::run() {
if (geo_lookup_data.isEmpty())
return;
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());
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
Q_FOREACH (const GeoLookupInfo& info, geo_lookup_data ) {
struct dive_site *ds = info.uuid ? get_dive_site_by_uuid(info.uuid) : &displayed_dive_site;
// 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);
timer.setSingleShot(true);
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 geonames.org: %s", qPrintable(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", qPrintable(errorObject.errorString()));
goto clear_reply;
}
QJsonObject obj = jsonDoc.object();
QVariant geoNamesObject = obj.value("geonames").toVariant();
QVariantList geoNames = geoNamesObject.toList();
if (geoNames.count() > 0) {
QVariantMap firstData = geoNames.at(0).toMap();
int ri = 0, l3 = -1, lt = -1;
if (ds->taxonomy.category == NULL) {
ds->taxonomy.category = alloc_taxonomy();
} else {
// clear out the data (except for the ocean data)
int ocean;
if ((ocean = taxonomy_index_for_category(&ds->taxonomy, TC_OCEAN)) > 0) {
ds->taxonomy.category[0] = ds->taxonomy.category[ocean];
ds->taxonomy.nr = 1;
} else {
// ocean is -1 if there is no such entry, and we didn't copy above
// if ocean is 0, so the following gets us the correct count
ds->taxonomy.nr = ocean + 1;
}
}
// get all the data - OCEAN is special, so start at COUNTRY
for (int j = TC_COUNTRY; j < TC_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;
l3 = taxonomy_index_for_category(&ds->taxonomy, TC_ADMIN_L3);
lt = taxonomy_index_for_category(&ds->taxonomy, TC_LOCALNAME);
if (l3 == -1 && lt != -1) {
// basically this means we did get a local name (what we call town), but just like most places
// we didn't get an adminName_3 - which in some regions is the actual city that town belongs to,
// then we copy the town into the city
ds->taxonomy.category[ri].value = copy_string(ds->taxonomy.category[lt].value);
ds->taxonomy.category[ri].origin = taxonomy::COPIED;
ds->taxonomy.category[ri].category = TC_ADMIN_L3;
ds->taxonomy.nr++;
}
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", qPrintable(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", qPrintable(errorObject.errorString()));
goto clear_reply;
}
QJsonObject obj = jsonDoc.object();
QVariant oceanObject = obj.value("ocean").toVariant();
QVariantMap oceanName = oceanObject.toMap();
if (oceanName["name"].isValid()) {
int idx;
if (ds->taxonomy.category == NULL)
ds->taxonomy.category = alloc_taxonomy();
idx = taxonomy_index_for_category(&ds->taxonomy, TC_OCEAN);
if (idx == -1)
idx = ds->taxonomy.nr;
if (idx < TC_NR_CATEGORIES) {
ds->taxonomy.category[idx].category = TC_OCEAN;
ds->taxonomy.category[idx].origin = taxonomy::GEOCODED;
ds->taxonomy.category[idx].value = copy_string(qPrintable(oceanName["name"].toString()));
if (idx == ds->taxonomy.nr)
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:
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;
info.lon = longitude;
info.uuid = uuid;
geo_lookup_data.append(info);
}