diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | divesite.c | 15 | ||||
-rw-r--r-- | divesite.h | 2 | ||||
-rw-r--r-- | divesitehelpers.cpp | 120 | ||||
-rw-r--r-- | divesitehelpers.h | 1 | ||||
-rw-r--r-- | icons/geocode.svg | 1010 | ||||
-rw-r--r-- | load-git.c | 18 | ||||
-rw-r--r-- | parse-xml.c | 24 | ||||
-rw-r--r-- | pref.h | 5 | ||||
-rw-r--r-- | qt-models/divelocationmodel.cpp | 4 | ||||
-rw-r--r-- | qt-ui/maintab.cpp | 28 | ||||
-rw-r--r-- | qt-ui/maintab.h | 1 | ||||
-rw-r--r-- | qt-ui/maintab.ui | 66 | ||||
-rw-r--r-- | qt-ui/preferences.cpp | 25 | ||||
-rw-r--r-- | save-git.c | 8 | ||||
-rw-r--r-- | save-xml.c | 15 | ||||
-rw-r--r-- | subsurface.qrc | 1 | ||||
-rw-r--r-- | subsurfacestartup.c | 4 | ||||
-rw-r--r-- | taxonomy.c | 36 | ||||
-rw-r--r-- | taxonomy.h | 39 |
20 files changed, 1358 insertions, 65 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 58b61aea4..534e8e2b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -306,6 +306,7 @@ set(SUBSURFACE_CORE_LIB_SRCS configuredivecomputer.cpp configuredivecomputerthreads.cpp divesitehelpers.cpp + taxonomy.c checkcloudconnection.cpp windowtitleupdate.cpp divelogexportlogic.cpp diff --git a/divesite.c b/divesite.c index 41d96de5d..cd8ee8a7d 100644 --- a/divesite.c +++ b/divesite.c @@ -169,6 +169,19 @@ void copy_dive_site(struct dive_site *orig, struct dive_site *copy) copy->notes = copy_string(orig->notes); copy->description = copy_string(orig->description); copy->uuid = orig->uuid; + copy->taxonomy.nr = orig->taxonomy.nr; + if (orig->taxonomy.category == NULL) { + free(copy->taxonomy.category); + copy->taxonomy.category = NULL; + } else { + if (copy->taxonomy.category == NULL) + copy->taxonomy.category = alloc_taxonomy(); + for (int i = 0; i < NR_CATEGORIES; i++) { + free((void *)copy->taxonomy.category[i].value); + copy->taxonomy.category[i] = orig->taxonomy.category[i]; + copy->taxonomy.category[i].value = copy_string(orig->taxonomy.category[i].value); + } + } } void clear_dive_site(struct dive_site *ds) @@ -182,4 +195,6 @@ void clear_dive_site(struct dive_site *ds) ds->latitude.udeg = 0; ds->longitude.udeg = 0; ds->uuid = 0; + ds->taxonomy.nr = 0; + free_taxonomy(ds->taxonomy.category); } diff --git a/divesite.h b/divesite.h index 71f3c0de7..306272e96 100644 --- a/divesite.h +++ b/divesite.h @@ -2,6 +2,7 @@ #define DIVESITE_H #include "units.h" +#include "taxonomy.h" #include <stdlib.h> #ifdef __cplusplus @@ -17,6 +18,7 @@ struct dive_site degrees_t latitude, longitude; char *description; char *notes; + struct taxonomy_data taxonomy; }; struct dive_site_table { diff --git a/divesitehelpers.cpp b/divesitehelpers.cpp index 62b48ba94..d635969da 100644 --- a/divesitehelpers.cpp +++ b/divesitehelpers.cpp @@ -41,54 +41,134 @@ 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; + 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: diff --git a/icons/geocode.svg b/icons/geocode.svg new file mode 100644 index 000000000..517e8788f --- /dev/null +++ b/icons/geocode.svg @@ -0,0 +1,1010 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="48px" + height="48px" + id="svg3440" + sodipodi:version="0.32" + inkscape:version="0.91 r13725" + sodipodi:docname="geocode.svg" + version="1.1"> + <defs + id="defs3"> + <linearGradient + id="linearGradient4263" + osb:paint="solid"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop4265" /> + </linearGradient> + <linearGradient + id="linearGradient6527"> + <stop + style="stop-color:#346604;stop-opacity:1;" + offset="0" + id="stop6529" /> + <stop + style="stop-color:#4e9a06;stop-opacity:1;" + offset="1" + id="stop6531" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient6001"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop6003" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop6005" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4825"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4827" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop4829" /> + </linearGradient> + <linearGradient + id="linearGradient4126"> + <stop + style="stop-color:#ffffff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop4128" /> + <stop + style="stop-color:#ffffff;stop-opacity:0.16494845;" + offset="1.0000000" + id="stop4130" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient4114"> + <stop + style="stop-color:#000000;stop-opacity:1;" + offset="0" + id="stop4116" /> + <stop + style="stop-color:#000000;stop-opacity:0;" + offset="1" + id="stop4118" /> + </linearGradient> + <linearGradient + id="linearGradient3962"> + <stop + style="stop-color:#d3e9ff;stop-opacity:1.0000000;" + offset="0.0000000" + id="stop3964" /> + <stop + style="stop-color:#d3e9ff;stop-opacity:1.0000000;" + offset="0.15517241" + id="stop4134" /> + <stop + style="stop-color:#4074ae;stop-opacity:1.0000000;" + offset="0.75000000" + id="stop4346" /> + <stop + style="stop-color:#36486c;stop-opacity:1.0000000;" + offset="1.0000000" + id="stop3966" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3962" + id="radialGradient3968" + gradientTransform="matrix(1.0056311,0,0,1.0056304,-1.7078855,0.3818364)" + cx="18.247644" + cy="15.716079" + fx="18.247644" + fy="15.716079" + r="29.993349" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4114" + id="radialGradient4120" + gradientTransform="scale(1.643990,0.608276)" + cx="15.115514" + cy="63.965388" + fx="15.115514" + fy="63.965388" + r="12.289036" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4126" + id="radialGradient4132" + gradientTransform="matrix(1.0056311,0,0,1.0056304,-1.7078855,0.3818364)" + cx="15.601279" + cy="12.142302" + fx="15.601279" + fy="12.142302" + r="43.526714" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4825" + id="radialGradient5983" + gradientUnits="userSpaceOnUse" + cx="12.071323" + cy="12.493138" + fx="12.071323" + fy="12.493138" + r="6.7175145" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4825" + id="radialGradient5985" + gradientUnits="userSpaceOnUse" + cx="12.071323" + cy="12.493138" + fx="12.071323" + fy="12.493138" + r="6.7175145" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4825" + id="radialGradient5987" + gradientUnits="userSpaceOnUse" + cx="12.071323" + cy="12.493138" + fx="12.071323" + fy="12.493138" + r="6.7175145" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4825" + id="radialGradient5989" + gradientUnits="userSpaceOnUse" + cx="12.071323" + cy="12.493138" + fx="12.071323" + fy="12.493138" + r="6.7175145" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6001" + id="linearGradient6007" + x1="-25.176178" + y1="30.057165" + x2="-22.252472" + y2="21.041553" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6001" + id="linearGradient6011" + gradientUnits="userSpaceOnUse" + x1="-25.176178" + y1="30.057165" + x2="-22.113543" + y2="22.661524" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6001" + id="linearGradient6015" + gradientUnits="userSpaceOnUse" + x1="-22.822565" + y1="28.337734" + x2="-22.113543" + y2="22.661524" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient6001" + id="linearGradient6019" + gradientUnits="userSpaceOnUse" + x1="-21.658581" + y1="15.649428" + x2="-21.962101" + y2="21.336346" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6533" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient4126-4" + id="radialGradient3976" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.5,0,20)" + cx="23.857143" + cy="40" + fx="23.857143" + fy="40" + r="17.142857" /> + <linearGradient + id="linearGradient4126-4" + inkscape:collect="always"> + <stop + id="stop4128-3" + offset="0" + style="stop-color:#000000;stop-opacity:1;" /> + <stop + id="stop4130-7" + offset="1" + style="stop-color:#000000;stop-opacity:0;" /> + </linearGradient> + <radialGradient + r="17.142857" + fy="40" + fx="23.857143" + cy="40" + cx="23.857143" + gradientTransform="matrix(1,0,0,0.5,0,20)" + gradientUnits="userSpaceOnUse" + id="radialGradient6386" + xlink:href="#linearGradient4126-4" + inkscape:collect="always" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6405" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6407" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6409" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6411" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6413" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6415" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6417" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6419" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6421" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6423" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6425" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6427" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6429" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6431" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6433" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6435" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6437" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6439" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6441" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6443" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6445" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6447" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6449" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6451" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6453" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6455" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6457" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6459" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6461" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6463" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6465" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6467" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6469" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6471" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6473" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6475" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6477" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6479" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6481" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient6527" + id="radialGradient6483" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.95295618,-0.02665615,0.02617771,0.93591558,0.66318174,2.1792646)" + cx="37.17944" + cy="31.09733" + fx="37.17944" + fy="31.09733" + r="19.094028" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="0.17254902" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:zoom="0.70710678" + inkscape:cx="-8.1678924" + inkscape:cy="22.159373" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="1680" + inkscape:window-height="979" + inkscape:window-x="-4" + inkscape:window-y="0" + inkscape:showpageshadow="false" + inkscape:window-maximized="1"> + <inkscape:grid + type="xygrid" + id="grid6369" /> + </sodipodi:namedview> + <metadata + id="metadata4"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + <dc:creator> + <cc:Agent> + <dc:title>Jakub Steiner</dc:title> + </cc:Agent> + </dc:creator> + <dc:contributor> + <cc:Agent> + <dc:title>Tuomas Kuosmanen</dc:title> + </cc:Agent> + </dc:contributor> + <cc:license + rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" /> + <dc:source>http://jimmac.musichall.cz</dc:source> + <dc:subject> + <rdf:Bag> + <rdf:li>internet</rdf:li> + <rdf:li>tools</rdf:li> + <rdf:li>applications</rdf:li> + <rdf:li>category</rdf:li> + </rdf:Bag> + </dc:subject> + </cc:Work> + <cc:License + rdf:about="http://creativecommons.org/licenses/by-sa/2.0/"> + <cc:permits + rdf:resource="http://web.resource.org/cc/Reproduction" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/Distribution" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/Notice" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/Attribution" /> + <cc:permits + rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> + <cc:requires + rdf:resource="http://web.resource.org/cc/ShareAlike" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <g + id="g4279" + transform="matrix(0.84505189,0,0,0.87570125,6.6573985,5.9718062)"> + <ellipse + style="color:#000000;display:block;overflow:visible;visibility:visible;opacity:0.6;fill:url(#radialGradient6386);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none" + id="path6548" + transform="matrix(1.070555,0,0,0.525,-1.5403842,22.5)" + inkscape:r_cx="true" + inkscape:r_cy="true" + cx="23.857143" + cy="40" + rx="17.142857" + ry="8.5714283" /> + <path + inkscape:connector-curvature="0" + id="path3214" + d="m 42.499998,23.999308 c 0,10.769899 -8.731089,19.50069 -19.499752,19.50069 -10.76965,0 -19.5002462,-8.73089 -19.5002462,-19.50069 C 3.4999998,13.229902 12.230596,4.5 23.000246,4.5 c 10.768663,0 19.499752,8.729902 19.499752,19.499308 l 0,0 z" + style="fill:url(#radialGradient3968);fill-opacity:1;fill-rule:nonzero;stroke:#39396c;stroke-width:0.99999994;stroke-miterlimit:4;stroke-opacity:1" /> + <g + transform="matrix(0.98791378,0,0,0.98789128,-1.5861235,0.6160591)" + style="fill:url(#radialGradient6533);fill-opacity:1;fill-rule:nonzero;stroke:none" + id="g4136"> + <g + style="fill:url(#radialGradient6409);fill-opacity:1" + id="g4138"> + <g + style="fill:url(#radialGradient6407);fill-opacity:1" + id="g4142"> + <path + sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6405);fill-opacity:1" + id="path4144" + d="M 43.879656,20.648284 43.5264,21.3316 c -0.334,-0.3936 -0.709,-0.7246 -1.0898,-1.0703 l -0.8359,0.123 -0.7637,-0.8633 0,1.0684 0.6543,0.4951 0.4355,0.4932 0.582,-0.6582 c 0.1465,0.2744 0.291,0.5488 0.4365,0.8232 l 0,0.8223 -0.6553,0.7402 -1.1992,0.8232 -0.9082,0.9063 -0.582,-0.6602 0.291,-0.7402 -0.5811,-0.6582 -0.9814,-2.0977 -0.8359,-0.9453 -0.2188,0.2461 0.3281,1.1934 0.6172,0.6992 c 0.3525,1.0176 0.7012,1.9902 1.1641,2.9629 0.7178,0 1.3945,-0.0762 2.1074,-0.166 l 0,0.5762 -0.8721,2.1392 -0.7998,0.9043 -0.6543,1.4004 c 0,0.7676 0,1.5352 0,2.3027 l 0.2188,0.9063 -0.3633,0.4102 -0.8008,0.4941 -0.8359,0.6992 0.6914,0.7813 -0.9453,0.8242 0.1816,0.5332 -1.418,1.6055 -0.9443,0 -0.7998,0.4941 -0.5098,0 0,-0.6582 -0.2168,-1.3184 c -0.2813,-0.8262 -0.5742,-1.6465 -0.8721,-2.4668 0,-0.6055 0.0361,-1.2051 0.0723,-1.8105 l 0.3643,-0.8223 -0.5098,-0.9883 0.0371,-1.3574 -0.6914,-0.7813 0.3457,-1.1309 -0.5625,-0.6382 -0.9824,0 -0.3271,-0.3701 -0.9814,0.6177 -0.3994,-0.4536 -0.9092,0.7817 c -0.6172,-0.6997 -1.2354,-1.3989 -1.8535,-2.0981 l -0.7266,-1.7285 0.6543,-0.9863 -0.3633,-0.4111 0.7988,-1.8936 c 0.6563,-0.8164 1.3418,-1.5996 2.0352,-2.3857 l 1.2363,-0.3291 1.3809,-0.1641 0.9453,0.2471 1.3447,1.3564 0.4727,-0.5342 0.6533,-0.082 1.2363,0.4111 0.9453,0 0.6543,-0.5762 0.291,-0.4111 -0.6553,-0.4111 -1.0908,-0.082 c -0.3027,-0.4199 -0.584,-0.8613 -0.9434,-1.2344 l -0.3643,0.1641 -0.1455,1.0703 -0.6543,-0.7402 -0.1445,-0.8242 -0.7266,-0.5742 -0.292,0 0.7275,0.8223 -0.291,0.7402 -0.5811,0.1641 0.3633,-0.7402 -0.6553,-0.3281 -0.5801,-0.6582 -1.0918,0.2461 -0.1445,0.3281 -0.6543,0.4121 -0.3633,0.9053 -0.9082,0.4521 -0.4004,-0.4521 -0.4355,0 0,-1.4814 0.9453,-0.4941 0.7266,0 -0.1465,-0.5752 -0.5801,-0.5762 0.9805,-0.2061 0.5449,-0.6162 0.4355,-0.7412 0.8008,0 -0.2188,-0.5752 0.5098,-0.3291 0,0.6582 1.0898,0.2461 1.0898,-0.9043 0.0732,-0.4121 0.9443,-0.6577 c -0.3418,0.0425 -0.6836,0.0737 -1.0176,0.1646 l 0,-0.7411 0.3633,-0.8228 -0.3633,0 -0.7984,0.7402 -0.2188,0.4116 0.2188,0.5767 -0.3643,0.9863 -0.5811,-0.3291 -0.5078,-0.5752 -0.8008,0.5752 -0.291,-1.3159 1.3809,-0.9048 0,-0.4941 0.873,-0.5757 1.3809,-0.3296 0.9453,0.3296 1.7441,0.3291 -0.4355,0.4932 -0.9453,0 0.9453,0.9873 0.7266,-0.8223 0.157078,-0.282273 c 3.129549,2.868759 5.87898,6.664564 6.592678,11.694257 z" /> + </g> + </g> + <g + style="fill:url(#radialGradient6415);fill-opacity:1" + id="g4146"> + <g + style="fill:url(#radialGradient6413);fill-opacity:1" + id="g4150"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6411);fill-opacity:1" + id="path4152" + d="m 26.0703,9.2363 -0.0732,0.4932 0.5098,0.3291 0.8711,-0.5757 -0.4355,-0.4937 -0.582,0.3296 -0.29,-0.0825" /> + </g> + </g> + <g + style="fill:url(#radialGradient6421);fill-opacity:1" + id="g4154"> + <g + style="fill:url(#radialGradient6419);fill-opacity:1" + id="g4158"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6417);fill-opacity:1" + id="path4160" + d="m 26.8701,5.8633 -1.8906,-0.7407 -2.1797,0.2466 -2.6904,0.7402 -0.5088,0.4941 1.6719,1.1514 0,0.6582 -0.6543,0.6582 0.873,1.729 0.5801,-0.3301 0.7285,-1.1514 c 1.123,-0.3472 2.1299,-0.7407 3.1973,-1.2344 l 0.873,-2.2212" /> + </g> + </g> + <g + style="fill:url(#radialGradient6427);fill-opacity:1" + id="g4162"> + <g + style="fill:url(#radialGradient6425);fill-opacity:1" + id="g4166"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6423);fill-opacity:1" + id="path4168" + d="m 28.833,12.7749 -0.291,-0.7412 -0.5098,0.165 0.1465,0.9043 0.6543,-0.3281" /> + </g> + </g> + <g + style="fill:url(#radialGradient6433);fill-opacity:1" + id="g4170"> + <g + style="fill:url(#radialGradient6431);fill-opacity:1" + id="g4174"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6429);fill-opacity:1" + id="path4176" + d="m 29.123,12.6089 -0.1455,0.9883 0.7998,-0.165 0.5811,-0.5752 -0.5088,-0.4941 C 29.6787,11.9078 29.4824,11.483 29.2685,11.0465 l -0.4355,0 0,0.4932 0.29,0.3291 0,0.7402" /> + </g> + </g> + <g + style="fill:url(#radialGradient6439);fill-opacity:1" + id="g4178"> + <g + style="fill:url(#radialGradient6437);fill-opacity:1" + id="g4182"> + <path + sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6435);fill-opacity:1" + id="path4184" + d="m 18.3652,28.2422 -0.582,-1.1523 -1.0903,-0.2466 -0.5815,-1.5625 -1.4536,0.1641 -1.2354,-0.9043 -1.3091,1.1514 0,0.1816 c -0.396,-0.1143 -0.8828,-0.1299 -1.2354,-0.3467 l -0.291,-0.8223 0,-0.9053 -0.8721,0.082 c 0.0728,-0.5762 0.145,-1.1514 0.2183,-1.7275 l -0.5093,0 -0.5083,0.6582 -0.5093,0.2461 -0.7271,-0.4102 -0.0728,-0.9053 0.1455,-0.9873 1.0908,-0.8223 0.8721,0 0.145,-0.4941 1.0903,0.2461 0.7998,0.9883 0.1455,-1.6465 1.3813,-1.1514 0.5088,-1.2344 1.0176,-0.4111 0.5815,-0.8223 1.3081,-0.248 0.6548,-0.9863 c -0.6543,0 -1.3086,0 -1.9629,0 l 1.2358,-0.5762 0.8716,0 1.2363,-0.4121 0.1455,-0.4922 -0.4365,-0.4121 -0.5088,-0.165 0.1455,-0.4932 -0.3633,-0.7402 -0.8726,0.3281 0.1455,-0.6577 -1.0176,-0.5762 -0.7993,1.3979 0.0723,0.4941 -0.7993,0.3301 -0.5093,1.0693 -0.2178,-0.9873 -1.3813,-0.5762 -0.2183,-0.7402 1.8174,-1.0703 0.7998,-0.7402 0.0728,-0.9048 -0.436,-0.2471 -0.5815,-0.0825 -0.3633,0.9053 c 0,0 -0.703332,0.055478 -0.859632,0.094078 C 9.9867169,11.059151 6.5013568,14.983265 5.6916,22.2885 c 0.0371,0.1738 0.6792,1.1816 0.6792,1.1816 l 1.5264,0.9043 1.5264,0.4121 0.6548,0.8232 1.0171,0.7402 0.5815,-0.082 0.436,0.1963 0,0.1328 -0.5811,1.563 -0.4365,0.6582 0.1455,0.3301 -0.3633,1.2324 1.3086,2.3867 1.3081,1.1523 0.582,0.8223 -0.0732,1.7285 0.4365,0.9863 -0.4365,1.8926 c 0,0 -0.0342,-0.0117 0.0215,0.1777 0.0562,0.1895 2.3291,1.4512 2.4736,1.3438 0.144,-0.1094 0.2671,-0.2051 0.2671,-0.2051 l -0.145,-0.4102 0.5811,-0.5762 0.2183,-0.5762 0.9453,-0.3301 0.7266,-1.8105 -0.2178,-0.4922 0.5078,-0.7402 1.0908,-0.248 0.582,-1.3164 -0.1455,-1.6445 0.8721,-1.2344 0.1455,-1.2344 C 20.7331,29.4607 19.5495,28.8513 18.365,28.242" /> + </g> + </g> + <g + style="fill:url(#radialGradient6445);fill-opacity:1" + id="g4186"> + <g + style="fill:url(#radialGradient6443);fill-opacity:1" + id="g4190"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6441);fill-opacity:1" + id="path4192" + d="m 16.7656,9.5649 0.7266,0.4937 0.582,0 0,-0.5757 -0.7266,-0.3291 -0.582,0.4111" /> + </g> + </g> + <g + style="fill:url(#radialGradient6451);fill-opacity:1" + id="g4194"> + <g + style="fill:url(#radialGradient6449);fill-opacity:1" + id="g4198"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6447);fill-opacity:1" + id="path4200" + d="m 14.876,8.9072 -0.3638,0.9048 0.7271,0 0.3638,-0.8228 C 15.9166,8.7675 16.2286,8.5444 16.5479,8.331 l 0.7271,0.2471 c 0.4844,0.3291 0.9688,0.6582 1.4536,0.9868 L 19.4561,8.9072 18.6558,8.5781 18.292,7.8374 16.9111,7.6728 16.8383,7.2612 16.184,7.4262 15.8936,8.002 15.5298,7.2613 l -0.145,0.3291 0.0728,0.8228 -0.5816,0.494" /> + </g> + </g> + <g + style="fill:url(#radialGradient6461);fill-opacity:1" + id="g4202"> + <g + id="g4204" + style="opacity:0.75;fill:url(#radialGradient6455);fill-opacity:1"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6453);fill-opacity:1" + d="" + id="path4206" /> + </g> + <g + style="fill:url(#radialGradient6459);fill-opacity:1" + id="g4208"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6457);fill-opacity:1" + d="" + id="path4210" /> + </g> + </g> + <g + style="fill:url(#radialGradient6471);fill-opacity:1" + id="g4212"> + <g + id="g4214" + style="opacity:0.75;fill:url(#radialGradient6465);fill-opacity:1"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6463);fill-opacity:1" + d="" + id="path4216" /> + </g> + <g + style="fill:url(#radialGradient6469);fill-opacity:1" + id="g4218"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6467);fill-opacity:1" + d="" + id="path4220" /> + </g> + </g> + <g + style="fill:url(#radialGradient6477);fill-opacity:1" + id="g4222"> + <g + style="fill:url(#radialGradient6475);fill-opacity:1" + id="g4226"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6473);fill-opacity:1" + id="path4228" + d="M 17.4922,6.8496 17.856,6.521 18.5831,6.3564 c 0.498,-0.2422 0.998,-0.4053 1.5264,-0.5762 l -0.29,-0.4937 -0.9385,0.1348 -0.4434,0.4419 -0.731,0.106 -0.6499,0.3052 -0.3159,0.1528 -0.1929,0.2583 0.9443,0.1641" /> + </g> + </g> + <g + style="fill:url(#radialGradient6483);fill-opacity:1" + id="g4230"> + <g + style="fill:url(#radialGradient6481);fill-opacity:1" + id="g4234"> + <path + inkscape:connector-curvature="0" + style="fill:url(#radialGradient6479);fill-opacity:1" + id="path4236" + d="m 18.7285,14.6665 0.4365,-0.6582 -0.6548,-0.4932 0.2183,1.1514" /> + </g> + </g> + </g> + <path + inkscape:connector-curvature="0" + id="path4122" + d="m 41.509682,23.999343 c 0,10.222944 -8.287677,18.51034 -18.509448,18.51034 -10.222709,0 -18.5099181,-8.287489 -18.5099181,-18.51034 0,-10.222477 8.2872091,-18.5090269 18.5099181,-18.5090269 10.221771,0 18.509448,8.2865499 18.509448,18.5090269 l 0,0 z" + style="opacity:0.39560439;fill:none;stroke:url(#radialGradient4132);stroke-width:0.99999994;stroke-miterlimit:4;stroke-opacity:1" /> + </g> + <g + id="g4273" + transform="matrix(1.4956472,0,0,1.3787502,153.00985,-30.324792)"> + <circle + r="5" + cy="28" + cx="-80" + id="path3457" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3459" + d="m -84.122312,30.680358 c 3.979207,6.02077 3.979207,5.734066 3.979207,5.734066" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.9553436px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path3461" + d="m -80.03025,36.522258 c 4.407011,-6.153274 4.407011,-6.153274 4.407011,-6.153274" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.04144919;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <path + inkscape:connector-curvature="0" + id="path4271" + d="m -80,25 c 0,6 0,6 0,6 l 0,-3 -3,0 6,0" + style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/load-git.c b/load-git.c index 0610cd0d8..7cfad325f 100644 --- a/load-git.c +++ b/load-git.c @@ -300,6 +300,22 @@ static void parse_site_gps(char *line, struct membuffer *str, void *_ds) ds->longitude = parse_degrees(line, &line); } +static void parse_site_geo(char *line, struct membuffer *str, void *_ds) +{ + fprintf(stderr, "line |%s| str |%s|\n", line, mb_cstring(str)); + struct dive_site *ds = _ds; + if (ds->taxonomy.category == NULL) + ds->taxonomy.category = alloc_taxonomy(); + int nr = ds->taxonomy.nr; + if (nr < NR_CATEGORIES) { + struct taxonomy *t = &ds->taxonomy.category[nr]; + t->value = strdup(mb_cstring(str)); + sscanf(line, "cat %d origin %d \"", &t->category, &t->origin); + fprintf(stderr, "found category %d origin %d value |%s|\n", t->category, t->origin, t->value); + ds->taxonomy.nr++; + } +} + /* Parse key=val parts of samples and cylinders etc */ static char *parse_keyvalue_entry(void (*fn)(void *, const char *, const char *), void *fndata, char *line) { @@ -906,7 +922,7 @@ static void dive_parser(char *line, struct membuffer *str, void *_dive) struct keyword_action site_action[] = { #undef D #define D(x) { #x, parse_site_ ## x } - D(description), D(gps), D(name), D(notes) + D(description), D(geo), D(gps), D(name), D(notes) }; static void site_parser(char *line, struct membuffer *str, void *_ds) diff --git a/parse-xml.c b/parse-xml.c index 4b6901f7f..879e31fd2 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -1431,6 +1431,8 @@ static void try_to_fill_dive_site(struct dive_site **ds_p, const char *name, cha start_match("divesite", name, buf); struct dive_site *ds = *ds_p; + if (ds->taxonomy.category == NULL) + ds->taxonomy.category = alloc_taxonomy(); if (MATCH("uuid", hex_value, &ds->uuid)) return; @@ -1442,6 +1444,15 @@ static void try_to_fill_dive_site(struct dive_site **ds_p, const char *name, cha return; if (MATCH("gps", gps_location, ds)) return; + if (MATCH("cat.geo", get_index, (int *)&ds->taxonomy.category[ds->taxonomy.nr].category)) + return; + if (MATCH("origin.geo", get_index, (int *)&ds->taxonomy.category[ds->taxonomy.nr].origin)) + return; + if (MATCH("value.geo", utf8_string, &ds->taxonomy.category[ds->taxonomy.nr].value)) { + if (ds->taxonomy.nr < NR_CATEGORIES) + ds->taxonomy.nr++; + return; + } nonmatch("divesite", name, buf); } @@ -1517,14 +1528,17 @@ static void dive_site_end(void) if (!cur_dive_site) return; if (cur_dive_site->uuid) { - uint32_t tmp = create_dive_site_with_gps(cur_dive_site->name, cur_dive_site->latitude, cur_dive_site->longitude); - struct dive_site *ds = get_dive_site_by_uuid(tmp); - ds->uuid = cur_dive_site->uuid; - ds->notes = cur_dive_site->notes; - ds->description = cur_dive_site->description; + struct dive_site *ds = alloc_dive_site(); + if (cur_dive_site->taxonomy.nr == 0) { + free(cur_dive_site->taxonomy.category); + cur_dive_site->taxonomy.category = NULL; + } + copy_dive_site(cur_dive_site, ds); + if (verbose > 3) printf("completed dive site uuid %x8 name {%s}\n", ds->uuid, ds->name); } + free_taxonomy(cur_dive_site->taxonomy.category); free(cur_dive_site); cur_dive_site = NULL; } @@ -6,6 +6,7 @@ extern "C" { #endif #include "units.h" +#include "taxonomy.h" /* can't use 'bool' for the boolean values - different size in C and C++ */ typedef struct @@ -28,9 +29,7 @@ typedef struct { bool enable_geocoding; bool parse_dive_without_gps; bool tag_existing_dives; - char *first_item; - char *second_item; - char *third_item; + enum taxonomy_category category[3]; } geocoding_prefs_t; struct preferences { diff --git a/qt-models/divelocationmodel.cpp b/qt-models/divelocationmodel.cpp index 53f518941..3d3c77616 100644 --- a/qt-models/divelocationmodel.cpp +++ b/qt-models/divelocationmodel.cpp @@ -116,6 +116,8 @@ GeoReferencingOptionsModel *GeoReferencingOptionsModel::instance() { GeoReferencingOptionsModel::GeoReferencingOptionsModel(QObject *parent) : QStringListModel(parent) { QStringList list; - list << "Country" << "State" << "District" << "Town" << "Suburb" << "Body of Water" << "Site Name"; + int i; + for (i = 0; i < NR_CATEGORIES; i++) + list << taxonomy_category_names[i]; setStringList(list); } diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 144a43094..591f665d5 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -77,6 +77,7 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), ui.location->setCompleter(completer); connect(ui.addDiveSite, SIGNAL(clicked()), this, SLOT(showDiveSiteSimpleEdit())); + connect(ui.geocodeButton, SIGNAL(clicked()), this, SLOT(reverseGeocode())); QAction *action = new QAction(tr("Apply changes"), this); connect(action, SIGNAL(triggered(bool)), this, SLOT(acceptChanges())); @@ -506,9 +507,26 @@ void MainTab::updateDiveInfo(bool clear) if (!clear) { struct dive_site *ds = get_dive_site_by_uuid(displayed_dive.dive_site_uuid); + ui.geocodeButton->setVisible(ds && dive_site_has_gps_location(ds)); if (ds) { + // construct the location tags + QString locationTag; + if (ds->taxonomy.nr) { + locationTag = "<small><small>(tags: "; + QString connector = ""; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < NR_CATEGORIES; j++) { + if (ds->taxonomy.category[j].category == prefs.geocoding.category[i]) { + locationTag += connector + QString(ds->taxonomy.category[j].value); + connector = " / "; + break; + } + } + } + locationTag += ")</small></small>"; + } ui.location->setText(ds->name); - ui.locationTags->setText(ds->description); // TODO: This should be three tags following davide's explanation. + ui.locationTags->setText(locationTag); if (displayed_dive.dive_site_uuid) copy_dive_site(get_dive_site_by_uuid(displayed_dive.dive_site_uuid), &displayed_dive_site); } else { @@ -546,6 +564,7 @@ void MainTab::updateDiveInfo(bool clear) ui.watertemp->setVisible(false); // rename the remaining fields and fill data from selected trip ui.LocationLabel->setText(tr("Trip location")); + ui.locationTags->clear(); ui.location->setText(currentTrip->location); ui.NotesLabel->setText(tr("Trip notes")); ui.notes->setText(currentTrip->notes); @@ -1538,3 +1557,10 @@ void MainTab::showAndTriggerEditSelective(struct dive_components what) weightModel->changed = true; } } + +void MainTab::reverseGeocode() +{ + ReverseGeoLookupThread *geoLookup = ReverseGeoLookupThread::instance(); + geoLookup->lookup(&displayed_dive_site); + MainWindow::instance()->information()->updateDiveInfo(); +} diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h index c96acb28a..306aee66e 100644 --- a/qt-ui/maintab.h +++ b/qt-ui/maintab.h @@ -98,6 +98,7 @@ slots: void disableGeoLookupEdition(); void setCurrentLocationIndex(); void showDiveSiteSimpleEdit(); + void reverseGeocode(); private: Ui::MainTab ui; WeightModel *weightModel; diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui index 7ac703076..3318bf788 100644 --- a/qt-ui/maintab.ui +++ b/qt-ui/maintab.ui @@ -167,25 +167,38 @@ </item> <item> <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>5</number> + </property> + <property name="rightMargin"> + <number>5</number> + </property> <property name="spacing"> <number>0</number> </property> <item> - <widget class="QLabel" name="LocationLabel"> - <property name="text"> - <string>Location</string> - </property> - <property name="alignment"> - <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="locationTags"> - <property name="text"> - <string/> - </property> - </widget> + <layout class="QHBoxLayout" name="LocationLayout" stretch="0,1"> + <item> + <widget class="QLabel" name="LocationLabel"> + <property name="text"> + <string>Location</string> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="locationTags"> + <property name="text"> + <string/> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + </widget> + </item> + </layout> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> @@ -207,6 +220,17 @@ </widget> </item> <item> + <widget class="QToolButton" name="geocodeButton"> + <property name="text"> + <string>...</string> + </property> + <property name="icon"> + <iconset resource="../subsurface.qrc"> + <normaloff>:/geocode</normaloff>:/geocode</iconset> + </property> + </widget> + </item> + <item> <widget class="QtWaitingSpinner" name="waitingSpinner" native="true"/> </item> </layout> @@ -539,8 +563,8 @@ <rect> <x>0</x> <y>0</y> - <width>449</width> - <height>743</height> + <width>100</width> + <height>30</height> </rect> </property> <layout class="QGridLayout" name="equipmentTabScrollAreaLayout"> @@ -634,8 +658,8 @@ <rect> <x>0</x> <y>0</y> - <width>449</width> - <height>743</height> + <width>286</width> + <height>300</height> </rect> </property> <layout class="QGridLayout" name="diveInfoScrollAreaLayout"> @@ -975,8 +999,8 @@ <rect> <x>0</x> <y>0</y> - <width>449</width> - <height>743</height> + <width>297</width> + <height>177</height> </rect> </property> <layout class="QHBoxLayout" name="horizontalLayout"> diff --git a/qt-ui/preferences.cpp b/qt-ui/preferences.cpp index c93460f9a..25638c991 100644 --- a/qt-ui/preferences.cpp +++ b/qt-ui/preferences.cpp @@ -241,9 +241,9 @@ void PreferencesDialog::setUiFromPrefs() ui.enable_geocoding->setChecked( prefs.geocoding.enable_geocoding ); ui.parse_without_gps->setChecked(prefs.geocoding.parse_dive_without_gps); ui.tag_existing_dives->setChecked(prefs.geocoding.tag_existing_dives); - ui.first_item->setCurrentText(prefs.geocoding.first_item); - ui.second_item->setCurrentText(prefs.geocoding.second_item); - ui.third_item->setCurrentText(prefs.geocoding.third_item); + ui.first_item->setCurrentIndex(prefs.geocoding.category[0]); + ui.second_item->setCurrentIndex(prefs.geocoding.category[1]); + ui.third_item->setCurrentIndex(prefs.geocoding.category[2]); } void PreferencesDialog::restorePrefs() @@ -288,6 +288,13 @@ void PreferencesDialog::rememberPrefs() else \ prefs.field = default_prefs.field +#define GET_ENUM(name, type, field) \ + v = s.value(QString(name)); \ + if (v.isValid()) \ + prefs.field = (enum type)v.toInt(); \ + else \ + prefs.field = default_prefs.field + #define GET_INT_DEF(name, field, defval) \ v = s.value(QString(name)); \ if (v.isValid()) \ @@ -455,9 +462,9 @@ void PreferencesDialog::syncSettings() s.setValue("enable_geocoding", ui.enable_geocoding->isChecked()); s.setValue("parse_dives_without_gps", ui.parse_without_gps->isChecked()); s.setValue("tag_existing_dives", ui.tag_existing_dives->isChecked()); - s.setValue("first_item", ui.first_item->currentText()); - s.setValue("second_item", ui.second_item->currentText()); - s.setValue("third_item", ui.third_item->currentText()); + s.setValue("cat0", ui.first_item->currentIndex()); + s.setValue("cat1", ui.second_item->currentIndex()); + s.setValue("cat2", ui.third_item->currentIndex()); s.endGroup(); loadSettings(); @@ -603,9 +610,9 @@ void PreferencesDialog::loadSettings() GET_BOOL("enable_geocoding", geocoding.enable_geocoding); GET_BOOL("parse_dives_without_gps", geocoding.parse_dive_without_gps); GET_BOOL("tag_existing_dives", geocoding.tag_existing_dives); - GET_TXT("first_item", geocoding.first_item); - GET_TXT("second_item", geocoding.second_item); - GET_TXT("third_item", geocoding.third_item); + GET_ENUM("cat0", taxonomy_category, geocoding.category[0]); + GET_ENUM("cat1", taxonomy_category, geocoding.category[1]); + GET_ENUM("cat2", taxonomy_category, geocoding.category[2]); s.endGroup(); } diff --git a/save-git.c b/save-git.c index a18ef8f84..ff82ca841 100644 --- a/save-git.c +++ b/save-git.c @@ -897,6 +897,14 @@ static void save_divesites(git_repository *repo, struct dir *tree) show_utf8(&b, "description ", ds->description, "\n"); show_utf8(&b, "notes ", ds->notes, "\n"); show_gps(&b, ds->latitude, ds->longitude); + if (prefs.geocoding.enable_geocoding) + for (int j = 0; j < ds->taxonomy.nr; j++) { + struct taxonomy *t = &ds->taxonomy.category[j]; + if (t->category != NONE) { + put_format(&b, "geo cat %d origin %d ", t->category, t->origin); + show_utf8(&b, "", t->value, "\n" ); + } + } blob_insert(repo, subdir, &b, mb_cstring(&site_file_name)); } } diff --git a/save-xml.c b/save-xml.c index 988ede1bf..a81d4258d 100644 --- a/save-xml.c +++ b/save-xml.c @@ -539,7 +539,20 @@ void save_dives_buffer(struct membuffer *b, const bool select_only) } show_utf8(b, ds->description, " description='", "'", 1); show_utf8(b, ds->notes, " notes='", "'", 1); - put_format(b, "/>\n"); + if (prefs.geocoding.enable_geocoding && ds->taxonomy.nr) { + put_format(b, ">\n"); + for (int j = 0; j < ds->taxonomy.nr; j++) { + struct taxonomy *t = &ds->taxonomy.category[j]; + if (t->category != NONE) { + put_format(b, "<geo cat='%d'", t->category); + put_format(b, " origin='%d'", t->origin); + show_utf8(b, t->value, " value='", "'/>\n", 1); + } + } + put_format(b, "</site>\n"); + } else { + put_format(b, "/>\n"); + } } put_format(b, "</divesites>\n<dives>\n"); for (trip = dive_trip_list; trip != NULL; trip = trip->next) diff --git a/subsurface.qrc b/subsurface.qrc index a28c86ad6..349d9d7b2 100644 --- a/subsurface.qrc +++ b/subsurface.qrc @@ -78,5 +78,6 @@ <file alias="filter-close">icons/process-stop.svg</file> <file alias="edit">icons/edit-circled.svg</file> <file alias="satellite">icons/Emblem-earth.svg</file> + <file alias="geocode">icons/geocode.svg</file> </qresource> </RCC> diff --git a/subsurfacestartup.c b/subsurfacestartup.c index f5b0e7b4b..3005e7e04 100644 --- a/subsurfacestartup.c +++ b/subsurfacestartup.c @@ -74,9 +74,7 @@ struct preferences default_prefs = { .enable_geocoding = false, .parse_dive_without_gps = false, .tag_existing_dives = false, - .first_item = NULL, - .second_item = NULL, - .third_item = NULL + .category = { 0 } } }; diff --git a/taxonomy.c b/taxonomy.c new file mode 100644 index 000000000..2c101962a --- /dev/null +++ b/taxonomy.c @@ -0,0 +1,36 @@ +#include "taxonomy.h" +#include "gettext.h" +#include <stdlib.h> + +char *taxonomy_category_names[NR_CATEGORIES] = { + QT_TRANSLATE_NOOP("getTextFromC", "None"), + QT_TRANSLATE_NOOP("getTextFromC", "Ocean"), + QT_TRANSLATE_NOOP("getTextFromC", "Country"), + QT_TRANSLATE_NOOP("getTextFromC", "State"), + QT_TRANSLATE_NOOP("getTextFromC", "County"), + QT_TRANSLATE_NOOP("getTextFromC", "City") +}; + +// these are the names for geoname.org +char *taxonomy_api_names[NR_CATEGORIES] = { + "none", + "name", + "countryName", + "adminName1", + "adminName2", + "toponymName" +}; + +struct taxonomy *alloc_taxonomy() +{ + return calloc(NR_CATEGORIES, sizeof(struct taxonomy)); +} + +void free_taxonomy(struct taxonomy *t) +{ + if (t) { + for (int i = 0; i < NR_CATEGORIES; i++) + free((void *)t[i].value); + free(t); + } +} diff --git a/taxonomy.h b/taxonomy.h new file mode 100644 index 000000000..fef6364e2 --- /dev/null +++ b/taxonomy.h @@ -0,0 +1,39 @@ +#ifndef TAXONOMY_H +#define TAXONOMY_H + +#ifdef __cplusplus +extern "C" { +#endif + +enum taxonomy_category { + NONE, + OCEAN, + COUNTRY, + ADMIN_L1, + ADMIN_L2, + LOCALNAME, + NR_CATEGORIES +}; + +extern char *taxonomy_category_names[NR_CATEGORIES]; +extern char *taxonomy_api_names[NR_CATEGORIES]; + +struct taxonomy { + int category; /* the category for this tag: ocean, country, admin_l1, admin_l2, localname, etc */ + const char *value; /* the value returned, parsed, or manually entered for that category */ + enum { GEOCODED, PARSED, MANUAL } origin; +}; + +/* the data block contains 3 taxonomy structures - unused ones have a tag value of NONE */ +struct taxonomy_data { + int nr; + struct taxonomy *category; +}; + +struct taxonomy *alloc_taxonomy(); +void free_taxonomy(struct taxonomy *t); + +#ifdef __cplusplus +} +#endif +#endif // TAXONOMY_H |