<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    Hi,<br>
    <br>
        Apologies in advance for a very long post.<br>
    <br>
        A few days ago I decided that I needed to be able to see the
    location of my geotagged images from within gwenview. I've got
    thousands of geotagged images and I randomly display them as my
    wallpaper.  Half the time I can't remember where the pictures were
    taken so I was "Opening Wallpaper Image" (Kubuntu 18.04) with
    gwenview, starting geotag, finding the file based on the truncated
    name showing in the title bar of gwenview, then, from within geotag,
    opening the browser with the location tagged.  Well, that got real
    tedious real fast so I decided to write my own little application
    that I could start from gwenview and then maybe later, look into
    incorporating it into gwenview.  The application works great and
    allows me to open Google Earth (not in the browser) with the image
    location tagged with a "pushpin".  Just for grins I also added a
    fallback to tagging the location in Google maps in the browser if
    you don't happen to have Google Earth installed.<br>
    <br>
        I looked into trying to incorporate the code into gwenview (it's
    C++ and Qt5) but building gwenview was an absolute nightmare.  Way
    too many dependencies.  At any rate, the code works as is and, if
    you add it to your "K" menu (using kmenuedit), you can start it from
    the File->Open With... in gwenview.  So, I'm still using my
    little command line app out of gwenview for the present (because it
    works ;-)  I threw the thing together in a couple of hours (I had
    already done something similar in my LiDAR processing software so it
    wasn't that difficult).  It's not elegant but I thought you guys
    might be interested.  I don't think that attaching the code as a
    file is the best idea (because some people don't like attachments)
    so here it is in it's entirety (dependencies are Qt5 and
    libexif-dev):<br>
    <br>
    <pre>#include <stdio.h></pre>
    <pre>#include <stdlib.h></pre>
    <pre>#include <errno.h></pre>
    <pre>#include <math.h></pre>
    <pre>#include <string.h></pre>
    <pre>#include <getopt.h></pre>
    <pre>#include <sys/types.h></pre>
    <pre>#include <unistd.h></pre>
    <pre>
#include <libexif/exif-data.h></pre>
    <pre>
#include <QtCore></pre>
    <pre>#include <QtWidgets></pre>
    <pre>#include <QStandardPaths></pre>
    <pre>
</pre>
    <pre>void usage (char *string)</pre>
    <pre>{</pre>
    <pre>  fprintf (stderr, "Program: %s\n", string);</pre>
    <pre>  fprintf (stderr, "Purpose: Reads EXIF data from image file and\n");</pre>
    <pre>  fprintf (stderr, "opens GoogleEarth (or browser in Google Maps)\n");</pre>
    <pre>  fprintf (stderr, "with pushpin at picture location.\n");</pre>
    <pre>  fprintf (stderr, "Usage: %s IMAGE_FILE_NAME\n\n", string);</pre>
    <pre>  fprintf (stderr, "Caveats:\n");</pre>
    <pre>  fprintf (stderr, "\tThe image file must contain GPS position data.\n\n");</pre>
    <pre>  exit (-1);</pre>
    <pre>}</pre>
    <pre>
</pre>
    <pre>
int32_t main (int32_t argc, char **argv)</pre>
    <pre>{</pre>
    <pre>  char              img_name[1024], lat[64], latref[2], lon[64], lonref[2], c;</pre>
    <pre>  extern int        optind;</pre>
    <pre>  QProcess          *googleEarthProc = NULL;</pre>
    <pre>
</pre>
    <pre>  QApplication a (argc, argv);</pre>
    <pre>
  </pre>
    <pre>  while ((c = getopt (argc, argv, "w")) != EOF)</pre>
    <pre>    {</pre>
    <pre>      switch (c)</pre>
    <pre>        {</pre>
    <pre>        case 'w':</pre>
    <pre>          // Placeholder in case you want to add a command line option</pre>
    <pre>          usage (argv[0]);</pre>
    <pre>          break;</pre>
    <pre>
        default:</pre>
    <pre>          usage (argv[0]);</pre>
    <pre>          break;</pre>
    <pre>        }</pre>
    <pre>    }</pre>
    <pre>
</pre>
    <pre>  //  Make sure we got the mandatory filename argument.</pre>
    <pre>
  if (optind >= argc) usage (argv[0]);</pre>
    <pre>
</pre>
    <pre>  strcpy (img_name, argv[optind]);</pre>
    <pre>
  </pre>
    <pre>  ExifData *exif = exif_data_new_from_file (img_name);</pre>
    <pre>
</pre>
    <pre>  ExifEntry *entry_lat = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LATITUDE);</pre>
    <pre>
  if (exif_entry_get_value (entry_lat, lat, sizeof (lat)))</pre>
    <pre>    {</pre>
    <pre>      if (QString (lat).isEmpty ()) return (-1);</pre>
    <pre>    }</pre>
    <pre>
  ExifEntry *entry_latref = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LATITUDE_REF);</pre>
    <pre>
  if (exif_entry_get_value (entry_latref, latref, sizeof (latref)))</pre>
    <pre>    {</pre>
    <pre>      if (QString (latref).isEmpty ()) return (-1);</pre>
    <pre>    }</pre>
    <pre>
  ExifEntry *entry_lon = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LONGITUDE);</pre>
    <pre>  </pre>
    <pre>  if (exif_entry_get_value (entry_lon, lon, sizeof (lon)))</pre>
    <pre>    {</pre>
    <pre>      if (QString (lon).isEmpty ()) return (-1);</pre>
    <pre>    }</pre>
    <pre>
  ExifEntry *entry_lonref = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LONGITUDE_REF);</pre>
    <pre>  </pre>
    <pre>  if (exif_entry_get_value (entry_lonref, lonref, sizeof (lonref)))</pre>
    <pre>    {</pre>
    <pre>      if (QString (lonref).isEmpty ()) return (-1);</pre>
    <pre>    }</pre>
    <pre>
  </pre>
    <pre>  QString arg, temp_folder, ge_tmp_name;</pre>
    <pre>  QStringList arguments;</pre>
    <pre>  int8_t process_id;</pre>
    <pre>  double pushpin_lat, pushpin_lon, pos[3];</pre>
    <pre>
  </pre>
    <pre>  QString ImageBaseName = QFileInfo (QString (img_name)).baseName ();</pre>
    <pre>  </pre>
    <pre>  </pre>
    <pre>  sscanf (lon, "%lf, %lf, %lf", &pos[0], &pos[1], &pos[2]);</pre>
    <pre>
  pos[2] /= 3600.0;</pre>
    <pre>  pos[1] /= 60.0;</pre>
    <pre>
  pushpin_lon = pos[0] + pos[1] + pos[2];</pre>
    <pre>  if (QString (lonref).contains ("W")) pushpin_lon = -pushpin_lon;</pre>
    <pre>
  </pre>
    <pre>  sscanf (lat, "%lf, %lf, %lf", &pos[0], &pos[1], &pos[2]);</pre>
    <pre>
  pos[2] /= 3600.0;</pre>
    <pre>  pos[1] /= 60.0;</pre>
    <pre>
  pushpin_lat = pos[0] + pos[1] + pos[2];</pre>
    <pre>  if (QString (latref).contains ("S")) pushpin_lat = -pushpin_lat;</pre>
    <pre>
  </pre>
    <pre>  exif_data_unref (exif);</pre>
    <pre>
</pre>
    <pre>  //  Check for google-earth or googleearth or google-earth-pro.</pre>
    <pre>
  QString command;</pre>
    <pre>  command = QStandardPaths::findExecutable ("google-earth-pro");</pre>
    <pre>  if (command.isEmpty ())</pre>
    <pre>    {</pre>
    <pre>      command = QStandardPaths::findExecutable ("google-earth");</pre>
    <pre>      if (command.isEmpty ())</pre>
    <pre>        {</pre>
    <pre>          command = QStandardPaths::findExecutable ("googleearth");</pre>
    <pre>          if (command.isEmpty ())</pre>
    <pre>            {</pre>
    <pre>              //  If we couldn't find Google Earth open Google maps in browser with sat background and marker...</pre>
    <pre>  </pre>
    <pre>              QString browser_string = QString (<a class="moz-txt-link-rfc2396E" href="http://www.google.com/maps/place/%1,%2/@%1,%2,17z/data=!3m1!1e3">"http://www.google.com/maps/place/%1,%2/@%1,%2,17z/data=!3m1!1e3"</a>).arg (pushpin_lat, 0, 'f', 11).arg (pushpin_lon, 0, 'f', 11);</pre>
    <pre>              QDesktopServices::openUrl (QUrl (browser_string, QUrl::TolerantMode));</pre>
    <pre>
              return (0);</pre>
    <pre>            }</pre>
    <pre>        }</pre>
    <pre>    }</pre>
    <pre>  </pre>
    <pre>    </pre>
    <pre>  arguments.clear ();</pre>
    <pre>
  </pre>
    <pre>  temp_folder = QStandardPaths::writableLocation (QStandardPaths::TempLocation);</pre>
    <pre>      </pre>
    <pre>      </pre>
    <pre>  process_id = getpid ();</pre>
    <pre>
  ge_tmp_name = temp_folder + QString ("/gePic_GE_%1_tmp.kml").arg (process_id);</pre>
    <pre>
  QFile file (ge_tmp_name);</pre>
    <pre>  if (!file.open (QIODevice::WriteOnly | QIODevice::Text)) return (-1);</pre>
    <pre>      </pre>
    <pre>
  QTextStream ts (&file);</pre>
    <pre>
</pre>
    <pre>  ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";</pre>
    <pre>  ts << "<kml xmlns=\<a class="moz-txt-link-rfc2396E" href="http://www.opengis.net/kml/2.2\">"http://www.opengis.net/kml/2.2\"</a>>\n";</pre>
    <pre>  ts << "  <Document>\n";</pre>
    <pre>  ts << "    <Placemark>\n";</pre>
    <pre>  ts << QString ("      <name>%1</name>\n").arg (ImageBaseName);</pre>
    <pre>  ts << "      <Point>\n";</pre>
    <pre>  ts << "        <altitudeMode>relativeToGround</altitudeMode>\n";</pre>
    <pre>  ts << "        <coordinates>\n";</pre>
    <pre>  ts << QString ("          %1,%2\n").arg (pushpin_lon, 0, 'f', 11).arg (pushpin_lat, 0, 'f', 11);</pre>
    <pre>  ts << "        </coordinates>\n";</pre>
    <pre>  ts << "      </Point>\n";</pre>
    <pre>  ts << "    </Placemark>\n";</pre>
    <pre>  ts << "  </Document>\n";</pre>
    <pre>  ts << "</kml>\n";</pre>
    <pre>
      </pre>
    <pre>  file.close ();</pre>
    <pre>
  arguments << ge_tmp_name;</pre>
    <pre>
  googleEarthProc = new QProcess (0);</pre>
    <pre>
  googleEarthProc->start (command, arguments);</pre>
    <pre>
  googleEarthProc->waitForFinished ();</pre>
    <pre>
  file.remove ();</pre>
    <pre>  </pre>
    <pre>  return (0);</pre>
    <pre>}</pre>
    <br>
    <br>
    I wrote a silly little script to build a Makefile and make the app:<br>
    <br>
    <pre>#!/bin/bash</pre>
    <pre>
</pre>
    <pre># Building the .pro file using qmake</pre>
    <pre>
rm -f xyPic.pro Makefile</pre>
    <pre>
qmake -project -o xyPic.tmp</pre>
    <pre>cat >xyPic.pro <<EOF</pre>
    <pre>RC_FILE = xyPic.rc</pre>
    <pre>LIBS += -lexif</pre>
    <pre>QT += widgets</pre>
    <pre>CONFIG += console</pre>
    <pre>EOF</pre>
    <pre>
</pre>
    <pre>cat xyPic.tmp >>xyPic.pro</pre>
    <pre>rm xyPic.tmp</pre>
    <pre>
</pre>
    <pre># Building the Makefile file using qmake</pre>
    <pre>
qmake -o Makefile</pre>
    <pre>
</pre>
    <pre>make</pre>
    <pre>
</pre>
    <br>
    All of the above is in the public domain (as is all of my
    software).  My LiDAR (and sonar) processing code does something a
    bit more interesting.  It builds a link file and makes Google Earth
    monitor it for changes every "N" seconds to update the viewed area
    and change the pushpin location but that would probably be overkill
    for an image viewer.  This program doesn't have any real error
    handling, if you don't have GPS in the image it just doesn't come
    up.<br>
    <br>
        Again, sorry for the long post.  If there is a dev out there
    that is interested in adding this to gwenview, go for it.<br>
    <br>
    V/r,<br>
    Jan Depner<br>
    <br>
    <br>
    <br>
  </body>
</html>