Locating geotagged images with Google Earth

Jan Depner jan at pfmabe.software
Sun May 24 21:13:58 BST 2020


Hi,

     Apologies in advance for a very long post.

     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.

     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):

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <math.h>

#include <string.h>

#include <getopt.h>

#include <sys/types.h>

#include <unistd.h>

#include <libexif/exif-data.h>

#include <QtCore>

#include <QtWidgets>

#include <QStandardPaths>

void usage (char *string)

{

   fprintf (stderr, "Program: %s\n", string);

   fprintf (stderr, "Purpose: Reads EXIF data from image file and\n");

   fprintf (stderr, "opens GoogleEarth (or browser in Google Maps)\n");

   fprintf (stderr, "with pushpin at picture location.\n");

   fprintf (stderr, "Usage: %s IMAGE_FILE_NAME\n\n", string);

   fprintf (stderr, "Caveats:\n");

   fprintf (stderr, "\tThe image file must contain GPS position data.\n\n");

   exit (-1);

}

int32_t main (int32_t argc, char **argv)

{

   char              img_name[1024], lat[64], latref[2], lon[64], lonref[2], c;

   extern int        optind;

   QProcess          *googleEarthProc = NULL;

   QApplication a (argc, argv);

   

   while ((c = getopt (argc, argv, "w")) != EOF)

     {

       switch (c)

         {

         case 'w':

           // Placeholder in case you want to add a command line option

           usage (argv[0]);

           break;

         default:

           usage (argv[0]);

           break;

         }

     }

   //  Make sure we got the mandatory filename argument.

   if (optind >= argc) usage (argv[0]);

   strcpy (img_name, argv[optind]);

   

   ExifData *exif = exif_data_new_from_file (img_name);

   ExifEntry *entry_lat = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LATITUDE);

   if (exif_entry_get_value (entry_lat, lat, sizeof (lat)))

     {

       if (QString (lat).isEmpty ()) return (-1);

     }

   ExifEntry *entry_latref = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LATITUDE_REF);

   if (exif_entry_get_value (entry_latref, latref, sizeof (latref)))

     {

       if (QString (latref).isEmpty ()) return (-1);

     }

   ExifEntry *entry_lon = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LONGITUDE);

   

   if (exif_entry_get_value (entry_lon, lon, sizeof (lon)))

     {

       if (QString (lon).isEmpty ()) return (-1);

     }

   ExifEntry *entry_lonref = exif_data_get_entry (exif, (ExifTag) EXIF_TAG_GPS_LONGITUDE_REF);

   

   if (exif_entry_get_value (entry_lonref, lonref, sizeof (lonref)))

     {

       if (QString (lonref).isEmpty ()) return (-1);

     }

   

   QString arg, temp_folder, ge_tmp_name;

   QStringList arguments;

   int8_t process_id;

   double pushpin_lat, pushpin_lon, pos[3];

   

   QString ImageBaseName = QFileInfo (QString (img_name)).baseName ();

   

   

   sscanf (lon, "%lf, %lf, %lf", &pos[0], &pos[1], &pos[2]);

   pos[2] /= 3600.0;

   pos[1] /= 60.0;

   pushpin_lon = pos[0] + pos[1] + pos[2];

   if (QString (lonref).contains ("W")) pushpin_lon = -pushpin_lon;

   

   sscanf (lat, "%lf, %lf, %lf", &pos[0], &pos[1], &pos[2]);

   pos[2] /= 3600.0;

   pos[1] /= 60.0;

   pushpin_lat = pos[0] + pos[1] + pos[2];

   if (QString (latref).contains ("S")) pushpin_lat = -pushpin_lat;

   

   exif_data_unref (exif);

   //  Check for google-earth or googleearth or google-earth-pro.

   QString command;

   command = QStandardPaths::findExecutable ("google-earth-pro");

   if (command.isEmpty ())

     {

       command = QStandardPaths::findExecutable ("google-earth");

       if (command.isEmpty ())

         {

           command = QStandardPaths::findExecutable ("googleearth");

           if (command.isEmpty ())

             {

               //  If we couldn't find Google Earth open Google maps in browser with sat background and marker...

   

               QString browser_string = QString ("http://www.google.com/maps/place/%1,%2/@%1,%2,17z/data=!3m1!1e3").arg (pushpin_lat, 0, 'f', 11).arg (pushpin_lon, 0, 'f', 11);

               QDesktopServices::openUrl (QUrl (browser_string, QUrl::TolerantMode));

               return (0);

             }

         }

     }

   

     

   arguments.clear ();

   

   temp_folder = QStandardPaths::writableLocation (QStandardPaths::TempLocation);

       

       

   process_id = getpid ();

   ge_tmp_name = temp_folder + QString ("/gePic_GE_%1_tmp.kml").arg (process_id);

   QFile file (ge_tmp_name);

   if (!file.open (QIODevice::WriteOnly | QIODevice::Text)) return (-1);

       

   QTextStream ts (&file);

   ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";

   ts << "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n";

   ts << "  <Document>\n";

   ts << "    <Placemark>\n";

   ts << QString ("      <name>%1</name>\n").arg (ImageBaseName);

   ts << "      <Point>\n";

   ts << "        <altitudeMode>relativeToGround</altitudeMode>\n";

   ts << "        <coordinates>\n";

   ts << QString ("          %1,%2\n").arg (pushpin_lon, 0, 'f', 11).arg (pushpin_lat, 0, 'f', 11);

   ts << "        </coordinates>\n";

   ts << "      </Point>\n";

   ts << "    </Placemark>\n";

   ts << "  </Document>\n";

   ts << "</kml>\n";

       

   file.close ();

   arguments << ge_tmp_name;

   googleEarthProc = new QProcess (0);

   googleEarthProc->start (command, arguments);

   googleEarthProc->waitForFinished ();

   file.remove ();

   

   return (0);

}



I wrote a silly little script to build a Makefile and make the app:

#!/bin/bash

# Building the .pro file using qmake

rm -f xyPic.pro Makefile

qmake -project -o xyPic.tmp

cat >xyPic.pro <<EOF

RC_FILE = xyPic.rc

LIBS += -lexif

QT += widgets

CONFIG += console

EOF

cat xyPic.tmp >>xyPic.pro

rm xyPic.tmp

# Building the Makefile file using qmake

qmake -o Makefile

make


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.

     Again, sorry for the long post.  If there is a dev out there that 
is interested in adding this to gwenview, go for it.

V/r,
Jan Depner



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/gwenview-devel/attachments/20200524/deedf8b8/attachment.htm>


More information about the Gwenview-devel mailing list