| 
									
										
										
										
											2017-04-27 20:24:53 +02:00
										 |  |  | // SPDX-License-Identifier: GPL-2.0
 | 
					
						
							| 
									
										
										
										
											2015-02-23 09:09:48 -08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // infrastructure to deal with dive sites
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2015-05-10 12:44:35 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "divesitehelpers.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-23 09:09:48 -08:00
										 |  |  | #include "divesite.h"
 | 
					
						
							|  |  |  | #include "helpers.h"
 | 
					
						
							|  |  |  | #include "membuffer.h"
 | 
					
						
							| 
									
										
										
										
											2017-07-04 15:48:43 +02:00
										 |  |  | #include <QDebug>
 | 
					
						
							| 
									
										
										
										
											2015-02-23 09:09:48 -08:00
										 |  |  | #include <QJsonDocument>
 | 
					
						
							|  |  |  | #include <QJsonArray>
 | 
					
						
							|  |  |  | #include <QJsonObject>
 | 
					
						
							|  |  |  | #include <QNetworkReply>
 | 
					
						
							|  |  |  | #include <QNetworkRequest>
 | 
					
						
							|  |  |  | #include <QNetworkAccessManager>
 | 
					
						
							|  |  |  | #include <QUrlQuery>
 | 
					
						
							|  |  |  | #include <QEventLoop>
 | 
					
						
							| 
									
										
										
										
											2015-06-01 15:47:25 -03:00
										 |  |  | #include <QTimer>
 | 
					
						
							| 
									
										
										
										
											2015-02-23 09:09:48 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-04 07:49:56 -07:00
										 |  |  | ReverseGeoLookupThread* ReverseGeoLookupThread::instance() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-05-16 00:27:08 +03:00
										 |  |  | 	static ReverseGeoLookupThread* self = new ReverseGeoLookupThread(); | 
					
						
							| 
									
										
										
										
											2015-05-10 12:44:35 -03:00
										 |  |  | 	return self; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-16 00:27:08 +03:00
										 |  |  | ReverseGeoLookupThread::ReverseGeoLookupThread(QObject *obj) : QThread(obj) | 
					
						
							| 
									
										
										
										
											2015-02-23 09:09:48 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-05-10 12:44:35 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-04 07:49:56 -07:00
										 |  |  | void ReverseGeoLookupThread::run() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2015-02-23 09:09:48 -08:00
										 |  |  | 	QNetworkRequest request; | 
					
						
							|  |  |  | 	QNetworkAccessManager *rgl = new QNetworkAccessManager(); | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 	QEventLoop loop; | 
					
						
							|  |  |  | 	QString mapquestURL("http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&accept-language=%1&lat=%2&lon=%3"); | 
					
						
							| 
									
										
										
										
											2017-10-16 11:59:17 +02:00
										 |  |  | 	QString geonamesURL("http://api.geonames.org/findNearbyPlaceNameJSON?lang=%1&lat=%2&lng=%3&radius=50&username=dirkhh"); | 
					
						
							|  |  |  | 	QString geonamesOceanURL("http://api.geonames.org/oceanJSON?lang=%1&lat=%2&lng=%3&radius=50&username=dirkhh"); | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 	QString divelogsURL("https://www.divelogs.de/mapsearch_divespotnames.php?lat=%1&lng=%2&radius=50"); | 
					
						
							|  |  |  | 	QTimer timer; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-23 09:09:48 -08:00
										 |  |  | 	request.setRawHeader("Accept", "text/json"); | 
					
						
							|  |  |  | 	request.setRawHeader("User-Agent", getUserAgent().toUtf8()); | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 	connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 	struct dive_site *ds = &displayed_dive_site; | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 	// first check the findNearbyPlaces API from geonames - that should give us country, state, city
 | 
					
						
							| 
									
										
										
										
											2017-10-16 11:59:17 +02:00
										 |  |  | 	request.setUrl(geonamesURL.arg(uiLanguage(NULL).section(QRegExp("[-_ ]"), 0, 0)).arg(ds->latitude.udeg / 1000000.0).arg(ds->longitude.udeg / 1000000.0)); | 
					
						
							| 
									
										
										
										
											2015-06-01 15:47:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 	QNetworkReply *reply = rgl->get(request); | 
					
						
							|  |  |  | 	timer.setSingleShot(true); | 
					
						
							|  |  |  | 	connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); | 
					
						
							|  |  |  | 	timer.start(5000);   // 5 secs. timeout
 | 
					
						
							|  |  |  | 	loop.exec(); | 
					
						
							| 
									
										
										
										
											2015-06-01 15:47:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-04 07:49:56 -07:00
										 |  |  | 	if (timer.isActive()) { | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 		timer.stop(); | 
					
						
							| 
									
										
										
										
											2017-10-04 07:49:56 -07:00
										 |  |  | 		if (reply->error() > 0) { | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 			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; | 
					
						
							| 
									
										
										
										
											2015-07-13 15:18:52 -07:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 					// 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; | 
					
						
							| 
									
										
										
										
											2015-07-13 15:18:52 -07:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			// 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_origin::GEOCODED; | 
					
						
							| 
									
										
										
										
											2017-10-04 07:49:56 -07:00
										 |  |  | 					free((void *)ds->taxonomy.category[ri].value); | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 					ds->taxonomy.category[ri].value = copy_string(qPrintable(firstData[taxonomy_api_names[j]].toString())); | 
					
						
							|  |  |  | 					ri++; | 
					
						
							| 
									
										
										
										
											2015-07-10 09:51:50 -07:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 			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_origin::GEOCOPIED; | 
					
						
							|  |  |  | 				ds->taxonomy.category[ri].category = TC_ADMIN_L3; | 
					
						
							|  |  |  | 				ds->taxonomy.nr++; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			mark_divelist_changed(true); | 
					
						
							| 
									
										
										
										
											2015-06-01 15:47:25 -03:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 			report_error("geonames.org did not provide reverse lookup information"); | 
					
						
							|  |  |  | 			qDebug() << "no reverse geo lookup; geonames returned\n" << fullReply; | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 	} 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
 | 
					
						
							| 
									
										
										
										
											2017-10-16 11:59:17 +02:00
										 |  |  | 	request.setUrl(geonamesOceanURL.arg(uiLanguage(NULL).section(QRegExp("[-_ ]"), 0, 0)).arg(ds->latitude.udeg / 1000000.0).arg(ds->longitude.udeg / 1000000.0)); | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 	reply = rgl->get(request); | 
					
						
							|  |  |  | 	connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); | 
					
						
							|  |  |  | 	timer.start(5000);   // 5 secs. timeout
 | 
					
						
							|  |  |  | 	loop.exec(); | 
					
						
							| 
									
										
										
										
											2017-10-04 07:49:56 -07:00
										 |  |  | 	if (timer.isActive()) { | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 		timer.stop(); | 
					
						
							| 
									
										
										
										
											2017-10-04 07:49:56 -07:00
										 |  |  | 		if (reply->error() > 0) { | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 			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_origin::GEOCODED; | 
					
						
							|  |  |  | 				ds->taxonomy.category[idx].value = copy_string(qPrintable(oceanName["name"].toString())); | 
					
						
							|  |  |  | 				if (idx == ds->taxonomy.nr) | 
					
						
							|  |  |  | 					ds->taxonomy.nr++; | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 			mark_divelist_changed(true); | 
					
						
							| 
									
										
										
										
											2015-05-10 12:44:35 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 	} else { | 
					
						
							|  |  |  | 		report_error("timeout accessing geonames.org"); | 
					
						
							|  |  |  | 		disconnect(reply, SIGNAL(finished()), &loop, SLOT(quit())); | 
					
						
							|  |  |  | 		reply->abort(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-10 12:44:35 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | clear_reply: | 
					
						
							| 
									
										
										
										
											2017-10-02 23:20:08 -07:00
										 |  |  | 	reply->deleteLater(); | 
					
						
							| 
									
										
										
										
											2015-07-01 12:36:21 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-02 23:16:14 -07:00
										 |  |  | 	rgl->deleteLater(); | 
					
						
							| 
									
										
										
										
											2015-05-10 12:44:35 -03:00
										 |  |  | } |