Geotagging under Köpenhamn-Malmö semestern

Under semestern körde jag en del med Sony Ericsson W910i + GPS headset HGE-100 + Trekbuddy + Google Maps (gm2tb). Det funderade *enormt* bra som stadsnavigator, lite sämre som geocachingvekrtyg (när man var riktigt nära så var Garmin Colorado 300 mycket enklare att använda, och gjorde en trevlig insats som geotaggingh verktyg.

Här hittar ni mina geotaggade mobilfoton: Copenhagen-Malmo vacation (Geotagging).

Skall man nämna något negativt om geotagging lösningen så skulle det vara främst:

– För många menyval för att börja fotografera i Trekbuddy. Man borde kunna ha en knapp som direkt tar en till kamerans fotofunktion, och sen spara koordinater omedelbart.

– Mobiltelefonen är inte optimerad för kamera bruk. Jag fick slänga ett par stycken bilder för att jag hade fingret över en stor del av linsen. Man känner helt enkelt inte var man skall ha fingrarna, då telefonen saknar ergonomisk design anpassad flr att fotograferad. Linsen sitter helt enkelt ungefär på mitten av telefonen när man slide:at ut den (vilket man alltid har, om man kör den ihop med Trekbuddys Take snapshot funktion) vilket är ungefär där man gärna sätter handen när man skall fotografera (tummen skall ju nå till knappen på den övre delen av telefonen….)

Tisdagstema: Tro

Dagens tisdagstema är Tro.

Vad kan då vara bättre än att tro på sig själv? Jag störde mig på att geotagging var så krångligt och känsligt för klockfel, när min Trekbuddy snurrandes på min telefon faktiskt skrev ner allt man behövde veta ner i en WGPS*.GPX-fil när man fotograferade. Efter att lagt ner tid på att försöka hitta något vettigt så satte jag tron till mig själv och skapade min Trekbuddy WGPS Snapshot Parser, eller TBuddy som jag informellt kallar min skapelse.

TBuddy geotaggade bilderna hittar ni här, och de gamla felaktiga hittar ni här. Observera att positionerna skiljer sig en hel del på vissa av fotorna 🙂

Vargö

Trekbuddy WGPS Snapshot Parser: beta-release

Got my Trekbuddy WGPS Snapshot parser working yesterday and this morning I made the last changes necessary to make it beta-release-ready….

The goal of this small project was to do Geotagging based upon the information directly provided by Trekbuddy (the WGPS*.GPX file links waypoints to the specific file) rather than by matching waypoint/track times to picture file times, and do clock-wise synchronization (which I suspect is more prone to errors).

But… Wait! There is more! 🙂

I also add GPS date, GPS time as well as altitude to the pictures. It was minimal work to support that as well, and it feels nice knowing that my tool takes everything from the WGPS file and crunches it into the pictures metadata.

As many other geotagging utilities, this one also requires Exiftool to work.

Available with syntax highlighting at pastebin and a bit rougher here:

/*
 * Blaufish’s Trekbuddy WGPS-*.GPX Geotagging Snapshot Parser
 * http://blaufish.blogg.se/
 *
 * Some rights reserved;
 * http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 */

import java.io.*;
import org.xml.sax.helpers.*;
import org.xml.sax.*;

public class TBuddy extends DefaultHandler {

    String lat;
    String lon;
    String ele;
    String time;
    String link;
   
    String chars;
   
   
    @Override public void startElement(String uri, String localName, String qName, Attributes attributes) {
        if (localName.equals("wpt")) {
            lat = attributes.getValue("lat");
            lon = attributes.getValue("lon");
            ele = null;
            time = null;
            link = null;
            chars = null;
        }
        else if (localName.equals("link")) {
            link = attributes.getValue("href");
        }
    }
   
    @Override public void endElement(String uri, String localName, String name) {
        if (localName.equals("ele")) {
            ele = chars;
        }
        else if (localName.equals("time")) {
            time = chars;
        }
        else if (localName.equals("wpt")) {
            runExifTool();
        }
    }

    @Override public void characters(char[] ch, int start, int length)  {
        chars = new String(ch,start,length);
    }

    public void runExifTool() {
        if ("".equals(lat) || "".equals(lon) || "".equals(link) ) return ;
       
        StringBuilder command = new StringBuilder("exiftool");
       
        if (lat.charAt(0)==’-‘)
            command.append(" -GPSLatitudeRef=S -GPSLatitude=").append(lat.substring(1));
        else
            command.append(" -GPSLatitudeRef=N -GPSLatitude=").append(lat);
       
       
        if (lon.charAt(0)==’-‘)
            command.append(" -GPSLongitudeRef=W -GPSLongitude=").append(lon.substring(1));
        else
            command.append(" -GPSLongitudeRef=E -GPSLongitude=").append(lon);
       
        if (ele != null) {
            if (ele.charAt(0)==’-‘)
                command.append(" -GPSAltitudeRef=1 -GPSAltitude=").append(ele.substring(1));
            else
                command.append(" -GPSAltitudeRef=0 -GPSAltitude=").append(ele);               
        }
       
        if (time != null) {
            String gpsdatestamp = time.substring(0,10).replace(‘-‘, ‘:’);
            String gpstimestamp = time.substring(11,19);
            command.append(" -GPSDateStamp=").append(gpsdatestamp).append(" -GPSTimeStamp=").append(gpstimestamp);
        }
       
        command.append(" ").append(link);
       
        System.out.println(command);
        try {
            Process p = Runtime.getRuntime().exec(command.toString());
            p.waitFor();
            int exitValue = p.exitValue();
            if ( exitValue != 0) {
                InputStream[] isa = { p.getErrorStream(), p.getInputStream() };
                for( InputStream is : isa ) {
                    BufferedReader br = new BufferedReader( new InputStreamReader( is ));
                    for( String line ;  ( line = br.readLine() ) != null ; System.out.println("EXIFTOOL: "+line) );  
                }
                throw new RuntimeException("Error: exiftool returned error ("+exitValue+").");
            }
        }
        catch( Exception e ) { throw new RuntimeException(e); }   
       
    }

    public TBuddy() { }
   
    public TBuddy( String gpxFileName ) {
        try {
            XMLReader xr = XMLReaderFactory.createXMLReader();
            xr.setContentHandler( this );
            FileReader r = new FileReader(gpxFileName);
            xr.parse(new InputSource(r));
        }
        catch( Exception e ) { throw new RuntimeException(e); }           
    }
   
    public static void main(String[] args) throws Exception {
        if ( args.length == 0 ) {
            System.out.println("Blaufish’s Trekbuddy WGPS-*.GPX Geotagging Snapshot Parser\n"
                    + "http://blaufish.blogg.se\n\n"
                    + "Parses a WGPS-xxxxx.GPX file created by Trekbuddy -> Waypoint -> Record Current -> Take Snapshot,\n"
                    + "and executes the appropriate exiftool command e.g.\n\n"
                    + "exiftool -GPSLatitudeRef=N -GPSLatitude=57.622655333 -GPSLongitudeRef=E\n"
                    + "         -GPSLongitude=11.756720333 -GPSAltitudeRef=1 -GPSAltitude=19.6\n"
                    + "         -GPSDateStamp=2008:07:19 -GPSTimeStamp=08:55:00\n"
                    + "         images-2008-07-19-10-55-20/pic-1.jpg\n\n"
                    + "Some rights reserved;\n"
                    + "http://creativecommons.org/licenses/by-nc-sa/3.0/\n\n"
                    + "Usage:   java TBuddy [wgps*.gpx files…]\n"
                    + "Example: java TBuddy wgps-2008-07-19-10-55-20.gpx" );           
            return ;
        }
        for( String wgpsGpxFileName :
args ) new TBuddy( wgpsGpxFileName );
    }
}

Trekbuddy WGPS Parser… det börjar likna något! :-)

Nu börjar koden nå brukbart alpha-stadie; den kör igenom hela geotaggingflödet! 🙂

/*
 * Blaufish’s Trekbuddy WGPS-*.GPX Geotagging Snapshot Parser
 * http://blaufish.blogg.se/
 *
 * Some rights reserved;
 * http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 */

import java.io.*;
import org.xml.sax.helpers.*;
import org.xml.sax.*;

public class TBuddy extends DefaultHandler {

    String lat;
    String lon;
    String ele;
    String time;
    String link;
    String chars;
   
    @Override public void startElement(String uri, String localName, String qName, Attributes attributes) {
        if (localName.equals("wpt")) {
            lat = attributes.getValue("lat");
            lon = attributes.getValue("lon");
            ele = null;
            time = null;
            link = null;
            chars = null;
        }
        else if (localName.equals("link")) {
            link = attributes.getValue("href");
        }
    }
   
    @Override public void endElement(String uri, String localName, String name) {
        if (localName.equals("ele")) {
            ele = chars;
        }
        else if (localName.equals("time")) {
            time = chars;
        }
        else if (localName.equals("wpt")) {
            if ("".equals(lat) || "".equals(lon) || "".equals(link) ) return ;
            StringBuilder command = new StringBuilder("exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E");
            command.append(" -GPSLatitude=").append(lat).append(" -GPSLongitude=").append(lon);
            //TODO: if (ele) command.append(" GPSAltitudeRef")
            command.append(" ").append(link);
            System.out.println(command);
            try {
                Process p = Runtime.getRuntime().exec(command.toString());
                p.waitFor();
                int exitValue = p.exitValue();
                if ( exitValue != 0) {
                    InputStream[] isa = { p.getErrorStream(), p.getInputStream() };
                    for( InputStream is : isa ) {
                        BufferedReader br = new BufferedReader( new InputStreamReader( is ));
                        for( String line ;  ( line = br.readLine() ) != null ; System.out.println(line) );  
                    }
                    throw new RuntimeException("Error: exiftool returned error ("+exitValue+").");
                }
            } catch( Exception e ) { throw new RuntimeException(e); }
        }
    }

    @Override public void characters(char[] ch, int start, int length)  {
        chars = new String(ch,start,length);
    }

    public static void main(String[] args) throws Exception {
        String gpxFileName = "wgps-2008-07-19-10-55-20.gpx"; //args[0];
        XMLReader xr = XMLReaderFactory.createXMLReader();
        xr.setContentHandler( new TBuddy() );
        FileReader r = new FileReader(gpxFileName);
        xr.parse(new InputSource(r));   
    }
}

Kör man den så får man följande resultat:

exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.622655333 -GPSLongitude=11.756720333 images-2008-07-19-10-55-20/pic-1.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.622602999 -GPSLongitude=11.756684000 images-2008-07-19-10-55-20/pic-2.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.621614499 -GPSLongitude=11.755567166 images-2008-07-19-10-55-20/pic-3.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.619316333 -GPSLongitude=11.752745333 images-2008-07-19-10-55-20/pic-4.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.618741666 -GPSLongitude=11.753624333 images-2008-07-19-10-55-20/pic-5.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.618713833 -GPSLongitude=11.753612999 images-2008-07-19-10-55-20/pic-6.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.619045333 -GPSLongitude=11.753285166 images-2008-07-19-10-55-20/pic-7.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.618418499 -GPSLongitude=11.752591000 images-2008-07-19-10-55-20/pic-8.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.620901666 -GPSLongitude=11.745262166 images-2008-07-19-10-55-20/pic-9.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.620182499 -GPSLongitude=11.740988333 images-2008-07-19-10-55-20/pic-10.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.620427999 -GPSLongitude=11.741272000 images-2008-07-19-10-55-20/pic-11.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.620418833 -GPSLongitude=11.741281499 images-2008-07-19-10-55-20/pic-12.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.617661166 -GPSLongitude=11.750696500 images-2008-07-19-10-55-20/pic-13.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.617662500 -GPSLongitude=11.750697333 images-2008-07-19-10-55-20/pic-14.jpg
exiftool -GPSLatitudeRef=N -GPSLongitudeRef=E -GPSLatitude=57.617656166 -GPSLongitude=11.750698999 images-2008-07-19-10-55-20/pic-15.jpg

Kollar man sen om filerna är korrekt geotaggade, så yes… det är dom 🙂

exiftool.exe -n images-2008-07-19-10-55-20\pic-15.jpg
ExifTool Version Number         : 7.13
File Name                       : pic-15.jpg
Directory                       : images-2008-07-19-10-55-20
File Size                       : 52091
File Modification Date/Time     : 2008:07:21 22:45:11
File Type                       : JPEG
MIME Type                       : image/jpeg
JFIF Version                    : 1 1
Exif Byte Order                 : MM
X Resolution                    : 1
Y Resolution                    : 1
Resolution Unit                 : 1
Y Cb Cr Positioning             : 1
GPS Version ID                  : 2 2 0 0
GPS Latitude Ref                : N
GPS Longitude Ref               : E
Image Width                     : 480
Image Height                    : 640
Encoding Process                : 0
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : 2 1
GPS Latitude                    : 57.6176561659989
GPS Longitude                   : 11.7506989989989
GPS Position                    : 57.6176561659989 11.7506989989989
Image Size                      : 480×640

Cool, inte sant? I mån av tid/Intresse så skall jag fixa så att den geotaggar in GPS Datum / Tid in i filerna också.

Just nu känner jag mig ganska nöjd, lagom att lägga sig att sova 🙂