mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 13:10:19 +00:00
e82f8ea565
Don't throw away data unless new data has been received. And don't store multiple copies of the same category. And most importantly, never write past the end of the array. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
209 lines
7.3 KiB
C++
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 = 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);
|
|
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);
|
|
}
|