mirror of
https://github.com/subsurface/subsurface.git
synced 2024-12-01 06:30:26 +00:00
bda482a30a
This renames the local name for the location as town and adds the concept of a city as the level 3 admin category. In some regions (e.g. at times in Italy) the local hamlet name is shown as toponymName but the name of the actual city is given as adminName3. With this change "city" will always reflect our best guess: adminName3 if it exists, otherwise the toponymName. Whereas "town" is always the toponymName. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
194 lines
6.7 KiB
C++
194 lines
6.7 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();
|
|
// 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++;
|
|
}
|
|
}
|
|
for (int j = ri - 1; j >= 0; j--) {
|
|
if (ds->taxonomy.category[j].category == TC_ADMIN_L3)
|
|
l3 = j;
|
|
else if (ds->taxonomy.category[j].category == TC_LOCALNAME)
|
|
lt = j;
|
|
}
|
|
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;
|
|
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", 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()) {
|
|
if (ds->taxonomy.category == NULL)
|
|
ds->taxonomy.category = alloc_taxonomy();
|
|
ds->taxonomy.category[ds->taxonomy.nr].category = TC_OCEAN;
|
|
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:
|
|
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);
|
|
}
|