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 #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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Jakub Steiner + + + + + Tuomas Kuosmanen + + + + http://jimmac.musichall.cz + + + internet + tools + applications + category + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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; } diff --git a/pref.h b/pref.h index 03b28a98c..0470d0e3b 100644 --- a/pref.h +++ b/pref.h @@ -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 = "(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 += ")"; + } 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 @@ + + 5 + + + 5 + 0 - - - Location - - - Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft - - - - - - - - - + + + + + Location + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + + + Qt::RichText + + + + @@ -206,6 +219,17 @@ + + + + ... + + + + :/geocode:/geocode + + + @@ -539,8 +563,8 @@ 0 0 - 449 - 743 + 100 + 30 @@ -634,8 +658,8 @@ 0 0 - 449 - 743 + 286 + 300 @@ -975,8 +999,8 @@ 0 0 - 449 - 743 + 297 + 177 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, "category); + put_format(b, " origin='%d'", t->origin); + show_utf8(b, t->value, " value='", "'/>\n", 1); + } + } + put_format(b, "\n"); + } else { + put_format(b, "/>\n"); + } } put_format(b, "\n\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 @@ icons/process-stop.svg icons/edit-circled.svg icons/Emblem-earth.svg + icons/geocode.svg 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 + +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