Additional parameters from ZoneDetect
authorColin Clark <colin.clark@cclark.uk>
Tue, 21 Aug 2018 16:02:20 +0000 (17:02 +0100)
committerColin Clark <colin.clark@cclark.uk>
Tue, 21 Aug 2018 16:02:20 +0000 (17:02 +0100)
Additional parameters derived from GPS data:
formatted.countryname - ISO 3166 country name
formatted.countrycode - ISO 3166 two-letter country code

doc/docbook/GuideReferenceTags.xml
doc/docbook/GuideSidebarsInfo.xml
src/exif-common.c
web/help/GuideReferenceXmpExif.html
web/help/GuideSidebarsInfo.html

index 96758a1..91a8a54 100644 (file)
               </para>\r
             </entry>\r
           </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.countryname</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.GPSInfo.GPSLatitude\r
+                <para />\r
+                Exif.GPSInfo.GPSLatitudeRef\r
+                <para />\r
+                Exif.GPSInfo.GPSLongitude\r
+                <para />\r
+                Exif.GPSInfo.GPSLongitudeRef\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>ISO 3166 country name indicated by lat/long</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.countrycode</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.GPSInfo.GPSLatitude\r
+                <para />\r
+                Exif.GPSInfo.GPSLatitudeRef\r
+                <para />\r
+                Exif.GPSInfo.GPSLongitude\r
+                <para />\r
+                Exif.GPSInfo.GPSLongitudeRef\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>ISO 3166 two-letter abbreviated country name indicated by lat/long</para>\r
+            </entry>\r
+          </row>\r
           <row>\r
             <entry>\r
               <para>formatted.star_rating</para>\r
index 4a19e5b..79d59ba 100644 (file)
               </footnote>\r
             </entry>\r
           </row>\r
+          <row>\r
+            <entry>formatted.countryname</entry>\r
+            <entry>ISO 3166 country name indicated by GPS lat/long values</entry>\r
+          </row>\r
+          <row>\r
+            <entry>formatted.countrycode</entry>\r
+            <entry>ISO 3166 two-letter abbreviated country name indicated by GPS lat/long values</entry>\r
+          </row>\r
           <row>\r
             <entry>file.size</entry>\r
             <entry>file size in bytes</entry>\r
index c060dd6..ba6c06d 100644 (file)
@@ -612,25 +612,21 @@ static gchar *exif_build_formatted_GPSAltitude(ExifData *exif)
 }
 
 /**
- * @brief Extracts timezone from a ZoneDetect search structure
- * @param results ZoneDetect search structure
- * @returns Timezone in the form "Europe/London"
+ * @brief Extracts timezone data from a ZoneDetect search structure
+ * @param[in] results ZoneDetect search structure
+ * @param[out] timezone in the form "Europe/London"
+ * @param[out] countryname in the form "United Kingdom"
+ * @param[out] countryalpha2 in the form "GB"
  * 
  * Refer to https://github.com/BertoldVdb/ZoneDetect
  * for structure details
  */
-static gchar *zd_tz(ZoneDetectResult* results)
+static void zd_tz(ZoneDetectResult *results, gchar **timezone, gchar **countryname, gchar **countryalpha2)
 {
-       gchar *timezone = NULL;
        gchar *timezone_pre = NULL;
        gchar *timezone_id = NULL;
        unsigned int index = 0;
 
-    if (!results)
-               {
-               return NULL;
-               }
-
        while(results[index].lookupResult != ZD_LOOKUP_END)
                {
                if(results[index].data)
@@ -645,29 +641,36 @@ static gchar *zd_tz(ZoneDetectResult* results)
                                        {
                                        timezone_id = g_strdup(results[index].data[i]);
                                        }
+                               if (g_strstr_len(results[index].fieldNames[i], -1, "CountryName"))
+                                       {
+                                       *countryname = g_strdup(results[index].data[i]);
+                                       }
+                               if (g_strstr_len(results[index].fieldNames[i], -1, "CountryAlpha2"))
+                                       {
+                                       *countryalpha2 = g_strdup(results[index].data[i]);
+                                       }
                                }
                        }
                index++;
                }
 
-       timezone = g_strconcat(timezone_pre, timezone_id, NULL);
+       *timezone = g_strconcat(timezone_pre, timezone_id, NULL);
        g_free(timezone_pre);
        g_free(timezone_id);
-       return timezone;
 }
 
 /**
- * @brief Creates local time from GPS lat/long
- * @param exif 
- * @returns Localised time and date
- * 
- * GPS lat/long is translated to timezone using ZoneDetect.
- * GPS UTC is converted to Unix time stamp (seconds since 1970).
- * The TZ environment variable is set to the relevant timezone
- * and the Unix timestamp converted to local time using locale.
- * If the conversion fails, unformatted UTC is returned.
+ * @brief Gets timezone data from an exif structure
+ * @param[in] exif
+ * @returns TRUE if timezone data found
+ * @param[out] exif_date_time exif date/time in the form 2018:11:30:17:05:04
+ * @param[out] timezone in the form "Europe/London"
+ * @param[out] countryname in the form "United Kingdom"
+ * @param[out] countryalpha2 in the form "GB"
+ *
+ *
  */
-static gchar *exif_build_formatted_localtime(ExifData *exif)
+static gboolean exif_build_tz_data(ExifData *exif, gchar **exif_date_time, gchar **timezone, gchar **countryname, gchar **countryalpha2)
 {
        gfloat latitude;
        gfloat longitude;
@@ -677,24 +680,14 @@ static gchar *exif_build_formatted_localtime(ExifData *exif)
        gchar *text_longitude_ref;
        gchar *text_date;
        gchar *text_time;
-       gchar *text_date_time = NULL;
-       gchar buf[128];
-       gchar *tmp;
-       gint buflen;
-       GError *error = NULL;
        gchar *lat_deg;
        gchar *lat_min;
        gchar *lon_deg;
        gchar *lon_min;
-       gchar *time_zone;
-       gchar *time_zone_org;
-       struct tm *tm_local;
-       struct tm tm_utc;
-       time_t stamp;
        gchar *zd_path;
-       gchar *zone_selected;
        ZoneDetect *cd;
        ZoneDetectResult *results;
+       gboolean ret = FALSE;
 
        text_latitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLatitude");
        text_longitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLongitude");
@@ -706,7 +699,7 @@ static gchar *exif_build_formatted_localtime(ExifData *exif)
        if (text_latitude && text_longitude && text_latitude_ref &&
                                                text_longitude_ref && text_date && text_time)
                {
-               text_date_time = g_strconcat(text_date, ":", text_time, NULL);
+               *exif_date_time = g_strconcat(text_date, ":", text_time, NULL);
 
                lat_deg = strtok(text_latitude, "deg'");
                lat_min = strtok(NULL, "deg'");
@@ -730,42 +723,11 @@ static gchar *exif_build_formatted_localtime(ExifData *exif)
                        if (cd)
                                {
                                results = ZDLookup(cd, latitude, longitude, NULL);
-                               zone_selected = zd_tz(results);
-                               time_zone = g_strconcat("TZ=", zone_selected, NULL);
-                               time_zone_org = g_strconcat("TZ=", getenv("TZ"), NULL);
-                               putenv("TZ=UTC");
-                               g_free(zone_selected);
-
-                               memset(&tm_utc, 0, sizeof(tm_utc));
-                               if (text_date_time && strptime(text_date_time, "%Y:%m:%d:%H:%M:%S", &tm_utc))
+                               if (results)
                                        {
-                                       stamp = mktime(&tm_utc);        // Convert the struct to a Unix timestamp
-                                       putenv(time_zone);      // Switch to destination time zone
-
-                                       tm_local = localtime(&stamp);
-
-                                       /* Convert to localtime using locale */
-                                       buflen = strftime(buf, sizeof(buf), "%x %X", tm_local);
-                                       if (buflen > 0)
-                                               {
-                                               tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
-                                               if (error)
-                                                       {
-                                                       log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
-                                                       g_error_free(error);
-                                                       }
-                                               else
-                                                       {
-                                                       g_free(text_date_time);
-                                                       text_date_time = g_strdup(tmp);
-                                                       }
-                                               }
-                                               g_free(tmp);
+                                       zd_tz(results, timezone, countryname, countryalpha2);
+                                       ret = TRUE;
                                        }
-                               putenv(time_zone_org);
-
-                               g_free(time_zone);
-                               g_free(time_zone_org);
                                }
                        else
                                {
@@ -776,96 +738,149 @@ static gchar *exif_build_formatted_localtime(ExifData *exif)
                g_free(zd_path);
                }
 
-       g_free(text_latitude);
-       g_free(text_longitude);
-       g_free(text_latitude_ref);
-       g_free(text_longitude_ref);
-       g_free(text_date);
-       g_free(text_time);
-
-       return text_date_time;
+       return ret;
 }
 
 /**
- * @brief Gets timezone from GPS lat/long
- * @param exif 
- * @returns Timezone string in the form "Europe/London"
- * 
- * 
+ * @brief Creates local time from GPS lat/long
+ * @param[in] exif
+ * @returns Localised time and date
+ *
+ * GPS lat/long is translated to timezone using ZoneDetect.
+ * GPS UTC is converted to Unix time stamp (seconds since 1970).
+ * The TZ environment variable is set to the relevant timezone
+ * and the Unix timestamp converted to local time using locale.
+ * If the conversion fails, unformatted UTC is returned.
  */
-static gchar *exif_build_formatted_timezone(ExifData *exif)
+static gchar *exif_build_formatted_localtime(ExifData *exif)
 {
-       gfloat latitude;
-       gfloat longitude;
-       gchar *text_latitude;
-       gchar *text_longitude;
-       gchar *text_latitude_ref;
-       gchar *text_longitude_ref;
-       gchar *lat_deg;
-       gchar *lat_min;
-       gchar *lon_deg;
-       gchar *lon_min;
-       gchar *time_zone = NULL;
-       gchar *zd_path;
-       ZoneDetect *cd;
-       ZoneDetectResult *results;
-
-       text_latitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLatitude");
-       text_longitude = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLongitude");
-       text_latitude_ref = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLatitudeRef");
-       text_longitude_ref = exif_get_data_as_text(exif, "Exif.GPSInfo.GPSLongitudeRef");
+       gchar buf[128];
+       gchar *tmp;
+       gint buflen;
+       GError *error = NULL;
+       gchar *time_zone_image;
+       gchar *time_zone_org;
+       struct tm *tm_local;
+       struct tm tm_utc;
+       time_t stamp;
+       gchar *exif_date_time = NULL;
+       gchar *timezone = NULL;
+       gchar *countryname = NULL;
+       gchar *countryalpha2 = NULL;
 
-       if ((text_latitude && g_strrstr(text_latitude, "deg")) &&
-               (text_longitude && g_strrstr(text_longitude, "deg")) &&
-               (
-                       (text_latitude_ref && g_strrstr(text_latitude_ref, "N")) ||
-                       (text_latitude_ref && g_strrstr(text_latitude_ref, "S"))
-               ) &&
-               (
-                       (text_longitude_ref && g_strrstr(text_longitude_ref, "E")) ||
-                       (text_longitude_ref && g_strrstr(text_longitude_ref, "W"))
-               )
-               )
+       if (exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2))
                {
-               lat_deg = strtok(text_latitude, "deg'");
-               lat_min = strtok(NULL, "deg'");
-               latitude = atof(lat_deg) + atof(lat_min) / 60;
-               if (g_strcmp0(text_latitude_ref, "South") == 0)
-                       {
-                       latitude = -latitude;
-                       }
-               lon_deg = strtok(text_longitude, "deg'");
-               lon_min = strtok(NULL, "deg'");
-               longitude = atof(lon_deg) + atof(lon_min) / 60;
-               if (g_strcmp0(text_longitude_ref, "West") == 0)
-                       {
-                       longitude = -longitude;
-                       }
-               zd_path = g_build_filename(GQ_BIN_DIR, TIMEZONE_DATABASE, NULL);
-               if (g_file_test(zd_path, G_FILE_TEST_EXISTS))
+               time_zone_image = g_strconcat("TZ=", timezone, NULL);
+               time_zone_org = g_strconcat("TZ=", getenv("TZ"), NULL);
+               putenv("TZ=UTC");
+
+               memset(&tm_utc, 0, sizeof(tm_utc));
+               if (exif_date_time && strptime(exif_date_time, "%Y:%m:%d:%H:%M:%S", &tm_utc))
                        {
-                       cd = ZDOpenDatabase(zd_path);
-                       if (cd)
-                               {
-                               results = ZDLookup(cd, latitude, longitude, NULL);
-                               time_zone = zd_tz(results);
-                               ZDFreeResults(results);
-                               }
-                       else
+                       stamp = mktime(&tm_utc);        // Convert the struct to a Unix timestamp
+                       putenv(time_zone_image);        // Switch to destination time zone
+
+                       tm_local = localtime(&stamp);
+
+                       /* Convert to localtime using locale */
+                       buflen = strftime(buf, sizeof(buf), "%x %X", tm_local);
+                       if (buflen > 0)
                                {
-                               log_printf("Error: Init of timezone database %s failed\n", zd_path);
+                               tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error);
+                               if (error)
+                                       {
+                                       log_printf("Error converting locale strftime to UTF-8: %s\n", error->message);
+                                       g_error_free(error);
+                                       }
+                               else
+                                       {
+                                       g_free(exif_date_time);
+                                       exif_date_time = g_strdup(tmp);
+                                       }
                                }
-                       ZDCloseDatabase(cd);
+                               g_free(tmp);
                        }
-               g_free(zd_path);
+               putenv(time_zone_org);
+
+               g_free(time_zone_image);
+               g_free(time_zone_org);
                }
 
-       g_free(text_latitude);
-       g_free(text_longitude);
-       g_free(text_latitude_ref);
-       g_free(text_longitude_ref);
+       g_free(timezone);
+       g_free(countryname);
+       g_free(countryalpha2);
+
+       return exif_date_time;
+}
+
+/**
+ * @brief Gets timezone from GPS lat/long
+ * @param[in] exif
+ * @returns Timezone string in the form "Europe/London"
+ *
+ *
+ */
+static gchar *exif_build_formatted_timezone(ExifData *exif)
+{
+       gchar *time_zone = NULL;
+       gchar *exif_date_time = NULL;
+       gchar *timezone = NULL;
+       gchar *countryname = NULL;
+       gchar *countryalpha2 = NULL;
+
+       exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2);
+
+       g_free(exif_date_time);
+       g_free(countryname);
+       g_free(countryalpha2);
+
+       return timezone;
+}
+
+/**
+ * @brief Gets countryname from GPS lat/long
+ * @param[in] exif
+ * @returns Countryname string
+ *
+ *
+ */
+static gchar *exif_build_formatted_countryname(ExifData *exif)
+{
+       gchar *exif_date_time = NULL;
+       gchar *timezone = NULL;
+       gchar *countryname = NULL;
+       gchar *countryalpha2 = NULL;
+
+       exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2);
+
+       g_free(exif_date_time);
+       g_free(timezone);
+       g_free(countryalpha2);
+
+       return countryname;
+}
+
+/**
+ * @brief Gets two-letter country code from GPS lat/long
+ * @param[in] exif
+ * @returns Countryalpha2 string
+ *
+ *
+ */
+static gchar *exif_build_formatted_countrycode(ExifData *exif)
+{
+       gchar *exif_date_time = NULL;
+       gchar *timezone = NULL;
+       gchar *countryname = NULL;
+       gchar *countryalpha2 = NULL;
+
+       exif_build_tz_data(exif, &exif_date_time, &timezone, &countryname, &countryalpha2);
+
+       g_free(exif_date_time);
+       g_free(timezone);
+       g_free(countryname);
 
-       return time_zone;
+       return countryalpha2;
 }
 
 static gchar *exif_build_formatted_star_rating(ExifData *exif)
@@ -898,6 +913,8 @@ ExifFormattedText ExifFormattedList[] = {
        EXIF_FORMATTED_TAG(GPSAltitude,         N_("GPS altitude")),
        EXIF_FORMATTED_TAG(localtime,           N_("Local time")),
        EXIF_FORMATTED_TAG(timezone,            N_("Time zone")),
+       EXIF_FORMATTED_TAG(countryname,         N_("Country name")),
+       EXIF_FORMATTED_TAG(countrycode,         N_("Country code")),
        EXIF_FORMATTED_TAG(star_rating,         N_("Star rating")),
        {"file.size",                           N_("File size"),        NULL},
        {"file.date",                           N_("File date"),        NULL},
index 5eeb985..f4830cd 100644 (file)
@@ -756,6 +756,44 @@ dd.answer div.label { float: left; }
             </td>
 </tr>
 <tr>
+<td class="td-colsep td-rowsep">
+              <span class="para">formatted.countryname</span>
+            </td>
+<td class="td-colsep td-rowsep">
+              <span class="para">
+                Exif.GPSInfo.GPSLatitude
+                <p class="para block block-first"></p>
+                Exif.GPSInfo.GPSLatitudeRef
+                <p class="para block"></p>
+                Exif.GPSInfo.GPSLongitude
+                <p class="para block"></p>
+                Exif.GPSInfo.GPSLongitudeRef
+              </span>
+            </td>
+<td class="td-rowsep">
+              <span class="para">ISO 3166 country name indicated by lat/long</span>
+            </td>
+</tr>
+<tr class="tr-shade">
+<td class="td-colsep td-rowsep">
+              <span class="para">formatted.countrycode</span>
+            </td>
+<td class="td-colsep td-rowsep">
+              <span class="para">
+                Exif.GPSInfo.GPSLatitude
+                <p class="para block block-first"></p>
+                Exif.GPSInfo.GPSLatitudeRef
+                <p class="para block"></p>
+                Exif.GPSInfo.GPSLongitude
+                <p class="para block"></p>
+                Exif.GPSInfo.GPSLongitudeRef
+              </span>
+            </td>
+<td class="td-rowsep">
+              <span class="para">ISO 3166 two-letter abbreviated country name indicated by lat/long</span>
+            </td>
+</tr>
+<tr>
 <td class="td-colsep">
               <span class="para">formatted.star_rating</span>
             </td>
index e31b30e..66e562b 100644 (file)
@@ -727,6 +727,14 @@ dd.answer div.label { float: left; }
             </td>
 </tr>
 <tr class="tr-shade">
+<td class="td-colsep td-rowsep">formatted.countryname</td>
+<td class="td-rowsep">ISO 3166 country name indicated by GPS lat/long values</td>
+</tr>
+<tr>
+<td class="td-colsep td-rowsep">formatted.countrycode</td>
+<td class="td-rowsep">ISO 3166 two-letter abbreviated country name indicated by GPS lat/long values</td>
+</tr>
+<tr class="tr-shade">
 <td class="td-colsep td-rowsep">file.size</td>
 <td class="td-rowsep">file size in bytes</td>
 </tr>