Linfiniti Geo Blog

GIS for Open Source People

Browsing Posts published on June 17, 2009

I recently acquired a Blackberry Bold. The device has a built in GPS and Camera and supports ‘GeoTagging’ images taken with the camera. Its pretty easy to get at the positional information under Ubuntu Linux. First I installed the exiftags package:

sudo apt-get install exiftags

To simply see a list of file names and the location each image was taken at, you can do something like:

for FILE in *; do echo $FILE >> geo.txt; exiftags $FILE | grep "Long\|Lat" >> geo.txt; done; cat geo.txt

Wanting a little more control over the process I decided to write a little python app to pull out the position info. To do that I used the excellent python-pyexiv2 package:

sudo apt-get install python-pyexiv2

From there I could write a script to snag the info I wanted. The script is quite well commented below so just read the source to understand what it does.

NoteThis article was updated 17 June 2009, 10pm GMT+2.

#!/usr/bin/python
#
# Read the lat / lon from a geotagged image
#
#         (c)Tim Sutton 2009
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# Import the pyexiv2 library for reading exif tags from images
import pyexiv2
# Now load the regular expressions library
import re # regexp
# Needed for traversing the dirs
import os

myOutput = file('/tmp/photos.txt',"wt")
myOutput.write("Filename,Lon,Lat\n")

# shamelessly hard coded for now...
mInputDir = '/home/timlinux/Photos/2009/'

myCount = 0
if not os.path.isdir(mInputDir):
    print "Input dir does note exist...quitting"
    sys.exit(0);

for myRoot, myDirs, myFiles in os.walk(mInputDir):
  for myFile in myFiles:
    myPath = myRoot + "/" + myFile

    # This is the image I will be getting the geotagging info from
    myImage = None 

    try:
      myImage = pyexiv2.Image(myPath)
    except:
      continue

    # Use exiv2 to get the metadata from the image
    myImage.readMetadata()

    # For debugging you can do:
    #print exifKeys()
    #myImage.exifKeys()

    # Get the position info as reported by exiv2

    myLonDirection = None
    myLonDegrees = None
    myLonMinutes = None
    myLatDirection = None
    myLatDegrees = None
    myLatMinutes = None
    try:
      # Will be either 'E' or 'W'
      myLonDirection = myImage['Exif.GPSInfo.GPSLongitudeRef']
      # Will return a rational number like : '27/1'
      myLonDegrees = myImage['Exif.GPSInfo.GPSLongitude'][0]
      # Will return a rational number like : '53295/1000'
      myLonMinutes = myImage['Exif.GPSInfo.GPSLongitude'][1]
      # Will be either 'N' or 'S'
      myLatDirection = myImage['Exif.GPSInfo.GPSLatitudeRef']
      # Will return a rational number like : '27/1'
      myLatDegrees = myImage['Exif.GPSInfo.GPSLatitude'][0]
      # Will return a rational number like : '56101/1000'
      myLatMinutes = myImage['Exif.GPSInfo.GPSLatitude'][1]
    except:
      continue

    # Get the degree and minute values

    myRegexp = re.compile( '^[0-9]*' )
    myLonDegreesFloat = float(myRegexp.search( str(myLonDegrees) ).group())
    myLatDegreesFloat = float(myRegexp.search( str(myLatDegrees) ).group())
    myLonMinutesFloat = float(myRegexp.search( str(myLonMinutes) ).group())
    myLatMinutesFloat = float(myRegexp.search( str(myLatMinutes) ).group())

    # Divide the values by the divisor 

    myRegexp = re.compile( '[0-9]*$' )
    myLon = myLonDegreesFloat / float(myRegexp.search( str(myLonDegrees) ).group())
    myLat = myLatDegreesFloat / float(myRegexp.search( str(myLatDegrees) ).group())
    myLonMin = myLonMinutesFloat / float(myRegexp.search( str(myLonMinutes) ).group())
    myLatMin = myLatMinutesFloat / float(myRegexp.search( str(myLatMinutes) ).group())

    # We now have degrees and decimal minutes, so convert to decimal degrees...

    myLon = myLon + (myLonMin / 60)
    myLat = myLat + (myLatMin / 60)

    # Use a negative sign as needed

    if myLonDirection == 'W': myLon = 0 - myLon
    if myLatDirection == 'S': myLat = 0 - myLat

    # Lets see what we got

    myOutput.write( myPath + "," + str(myLon) + "," + str(myLat) + "\n" )

Now you can save the script (I called it showlonlat.py) and then run it to get the Lon/Lat out in decimal degrees…e.g.

chmod +x showlatlong.py
./showlonlat.py

The file generated by his script (hard coded to /tmp/photos.txt) can now be opened with QGIS using the delimited text plugin. From there its a simple matter to create an action in the layer properties dialog so that the image for each point is shown when you click on it. Here is what the end result is like:

Blackberry Images visualised in QGIS
(click the image for a larger view)

In a future article I will do an integration of this script into a QGIS python plugin so that it scans a whole directory structure and builds a spatial lite or shape file with the geometries for all the images, as well as their full path names.

Where I live there is no broadband available and I rely on a Vodacom 3G modem to connect to the internet. I wanted to share the connection with other computers and guests visiting our house. Here is what I did:

Setup the gateway machine

I installed a small wireless router that also includes a 4 port hub. I ensured that any computer connecting to it will get its address via dhcp, and that my ‘gateway computer’ always gets the same IP address. The gateway computer is the one where the 3G modem is plugged in to and that is also connected to the household network. My gateway computer has a wireless card for its connection to the household network, so I obtained the mac address for the wireless adapter like this:

sudo ifconfig

And got the ‘HWaddr’ from the listing that followed:

wlan0     Link encap:Ethernet  HWaddr 00:14:c1:32:ed:ec
          inet addr:192.168.1.105  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::214:c1ff:fe32:edec/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:551 errors:0 dropped:0 overruns:0 frame:0
          TX packets:858 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:147611 (147.6 KB)  TX bytes:117464 (117.4 KB)

I then used the DHCP Server option in my wireless router to specify that the mac address 00:14:c1:32:ed:ec always gets assigned the IP Address 192.168.1.253. This address is outside of the normal address range given to DHCP clients. We use a fixed address so that we can specify this as the gateway machine for other clients on the network. After making these changes on the router, I stopped and started networking using the gnome network manager applet and rechecked my ifconfig:

wlan0     Link encap:Ethernet  HWaddr 00:14:c1:32:ed:ec
          inet addr:192.168.1.253  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: fe80::214:c1ff:fe32:edec/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:579 errors:0 dropped:0 overruns:0 frame:0
          TX packets:914 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:153373 (153.3 KB)  TX bytes:127111 (127.1 KB)

Setup routing on your gateway machine

The next thing to do is right click on the network manager applet and choose edit connections. Now choose the wireless tab (or wired if you gateway machine connects to your household router using a cable connection). Next click on the network to select it and then click edit. In the dialog that appears, go to the ‘IPv4 Settings’ tab, and click on the ‘Routes’ button. In the dialog that appears there, tick the option marked ‘Use this connection only for resources on its network’. Now press OK, Apply, Close in succession to close the network manager dialogs. Finally right click the network manager icon and disable networking, then right click again and re-enable it. What have we just done? We have told Linux not to try to use the internal wireless/wired network for internet traffic on the gateway machine. We can verify this by looking at the routing table. First make sure your 3G connection is up too using the network manager. Now do:

route -n

Your output should look something like this (note that you actual network addresses may vary):

[~] route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
10.64.64.64     0.0.0.0         255.255.255.255 UH    0      0        0 ppp0
192.168.1.0     0.0.0.0         255.255.255.0   U     2      0        0 wlan0
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 wlan0
0.0.0.0         10.64.64.64     0.0.0.0         UG    0      0        0 ppp0

The significant thing here is that the 0.0.0.0 destination is set to use your ppp0 interface (the 3G modem) and traffic on the 192.168.1.0 network is set to use the internal network (in my case wlan0 – this may be eth0 if you are using a wired connection).

Connection sharing

Next we are going to install firestarter, which is a user friendly firewall gui.

sudo apt-get install firestarter

The firestarter installation should go quickly, after which you can launch the program using System -> Administration -> Firestarter. In the wizard that appears, make the following choices:

Internet connected device: Dialup device (ppp0)
Tick the item marked : Start the firewall on dial-out
Tick the item marked: IP address is assigned via DHCP

Press next

Tick the item marked: Enable internet connection sharing
Choose an appropriate local area internet device: I chose ‘Wireless device (wlan0)’

Press next

Tick ‘Start firewall now’

Press finish.

Test that your internet is still working after doing this by opening a browser and surfing to a page (e.g. a random google search).

If all that is working, you are ready to go on and configure your clients!

Configuring clients

We are going to statically assign IPv4 settings for each client. If your wireless router supports setting, dns, default gateway etc for the dhcp server it is much simpler to do it there and let all your clients just use DHCP. However mine doesnt so I need to be carefull to assign each device a unique IP address. Here are the settings I use for clients:

Static Address
IP Address: 192.168.1.2xx (replace XX with a unique number for each client
Subnet Mask: 255.255.255.0
Primary DNS: 208.67.222.222
Secondary DNS: 208.67.208.208
Default Gateway: 192.168.1.253 (this is our gateway machines local network address)

All you need to do then is enable networking on your client and try to connect to the internet. If all went well you should be googling away before you can say ‘boo to a ghost’.