Skip to content
Browse files

Initial checkin for Github upload.

  • Loading branch information...
0 parents commit 8765ff1c77576f829dd897ee30b9e6f6f6e63dfe @freefoote committed Aug 14, 2012
Showing with 9,580 additions and 0 deletions.
  1. +340 −0 COPYING
  2. +14 −0 INSTALL
  3. +51 −0 Makefile
  4. +29 −0 Makefile.mingw32
  5. +59 −0 README.md
  6. +59 −0 README.md~
  7. +32 −0 README.mingw32
  8. +114 −0 RELEASES
  9. +322 −0 correlate.c
  10. +81 −0 correlate.h
  11. +140 −0 doc/command.html
  12. +113 −0 doc/concepts.html
  13. BIN doc/corr.png
  14. +399 −0 doc/gpscorrelate-manpage.xml.in
  15. +62 −0 doc/gui.html
  16. +35 −0 doc/index.html
  17. +784 −0 exif-gps.cpp
  18. +44 −0 exif-gps.h
  19. +3,419 −0 gpscorrelate-gui.svg
  20. +11 −0 gpscorrelate.desktop
  21. +928 −0 gpscorrelate2.glade
  22. +38 −0 gpsstructure.h
  23. +278 −0 gpx-read.c
  24. +29 −0 gpx-read.h
  25. +1,315 −0 gui.c
  26. +42 −0 gui.h
  27. +678 −0 main-command.c
  28. +54 −0 main-gui.c
  29. +77 −0 unixtime.c
  30. +33 −0 unixtime.h
340 COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
14 INSTALL
@@ -0,0 +1,14 @@
+Installation of GPSCorrelate.
+
+- Make sure you have the appropriate development libraries.
+ You will need: libxml2, libgtk2.0, libexiv2.
+ To create the manpage you need:
+ - xsltproc from http://xmlsoft.org/XSLT/
+ - manpages/docbook.xsl properly installed from http://docbook.sourceforge.net/projects/xsl/
+ or internet access (xsltproc will download it automatically if it is not installed)
+- Compile the program:
+ make
+- Install the program:
+ make install
+
+... and that should be it.
51 Makefile
@@ -0,0 +1,51 @@
+# Makefile for gpscorrelate
+# Written by Daniel Foote.
+
+
+COBJS = main-command.o unixtime.o gpx-read.o correlate.o exif-gps.o
+GOBJS = main-gui.o gui.o unixtime.o gpx-read.o correlate.o exif-gps.o
+CFLAGS = -Wall
+override CFLAGS += $(shell pkg-config --cflags libxml-2.0 gtk+-2.0) -I/usr/include/exiv2
+OFLAGS = -Wall
+override OFLAGS += $(shell pkg-config --libs libxml-2.0 gtk+-2.0) -lm -lexiv2
+prefix = /usr/local
+bindir = $(prefix)/bin
+datadir = $(prefix)/share
+mandir = $(datadir)/man
+docdir = $(datadir)/doc/gpscorrelate
+applicationsdir = $(datadir)/applications
+
+all: gpscorrelate gpscorrelate-gui gpscorrelate.1
+
+gpscorrelate: $(COBJS)
+ g++ $(OFLAGS) -o $@ $(COBJS)
+
+gpscorrelate-gui: $(GOBJS)
+ g++ $(OFLAGS) -o $@ $(GOBJS)
+
+.c.o:
+ gcc $(CFLAGS) -c -o $*.o $<
+
+.cpp.o:
+ g++ $(CFLAGS) -c -o $*.o $<
+
+clean:
+ rm -f *.o gpscorrelate{,.exe} gpscorrelate-gui{,.exe}
+
+install: all
+ install -d $(DESTDIR)$(bindir)
+ install gpscorrelate gpscorrelate-gui $(DESTDIR)$(bindir)
+ install -d $(DESTDIR)$(mandir)/man1
+ install -m 0644 gpscorrelate.1 $(DESTDIR)$(mandir)/man1
+ install -d $(DESTDIR)$(docdir)
+ install -m 0644 doc/*.html doc/*.png $(DESTDIR)$(docdir)
+
+install-desktop-file:
+ desktop-file-install --vendor="" --dir="$(DESTDIR)$(applicationsdir)" gpscorrelate.desktop
+ install -p -m0644 -D gpscorrelate-gui.svg $(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/gpscorrelate-gui.svg
+
+doc/gpscorrelate-manpage.xml: doc/gpscorrelate-manpage.xml.in
+ sed 's,@DOCDIR@,$(docdir),' $< > $@
+
+gpscorrelate.1: doc/gpscorrelate-manpage.xml
+ xsltproc http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
29 Makefile.mingw32
@@ -0,0 +1,29 @@
+# Makefile for gpscorrelate
+# Written by Daniel Foote.
+
+CC = i486-mingw32-gcc
+CXX = i486-mingw32-g++
+COBJS = main-command.o unixtime.o gpx-read.o correlate.o exif-gps.o
+GOBJS = main-gui.o gui.o unixtime.o gpx-read.o correlate.o exif-gps.o
+CFLAGS = -mms-bitfields -Wall $(shell pkg-config --cflags libxml-2.0 gtk+-2.0 exiv2)
+OFLAGS = -Wall $(shell pkg-config --libs exiv2 libxml-2.0 gtk+-2.0) -lm -liconv -lexpat
+
+
+all: gpscorrelate.exe gpscorrelate-gui.exe
+
+gpscorrelate.exe: $(COBJS)
+ $(CXX) -o $@ $(COBJS) $(OFLAGS)
+
+gpscorrelate-gui.exe: $(GOBJS)
+ $(CXX) -o $@ $(GOBJS) $(OFLAGS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $*.o $<
+
+.cpp.o:
+ $(CXX) $(CFLAGS) -c -o $*.o $<
+
+clean:
+ rm -f *.o gpscorrelate{,.exe} gpscorrelate-gui{,.exe}
+
+
59 README.md
@@ -0,0 +1,59 @@
+# GPSCorrelate
+
+Daniel Foote, 2005.
+
+## Status
+
+Abandoned. I no longer has the time to work on this and hasn't used it in many years. It's being uploaded to github so others can fork it and make their own modifications - as I get emails every few months asking for new features.
+
+## What is it?
+
+Digital cameras are cool. So is GPS. And, EXIF tags are really cool too.
+
+What happens when you merge the three? You end up with a set of photos taken with a digital camera that are "stamped" with the location at which they were taken.
+
+The EXIF standard defines a number of tags that are for use with GPS.
+
+A variety of programs exist around the place to match GPS data with digital camera photos, but most of them are Windows or MacOS only. Which doesn't really suit me that much. Also, each one takes the GPS data in a different format.
+
+So I wrote my own. A little bit of C, a little bit of C++, a shade of GTK+, and you end up with... what I have here. I wrote both a command line and GUI version of the program.
+
+## Things you should know:
+
+* The program takes GPS data in GPX format. This is an XML format. I recommend using GPSBabel - it can convert from lots of formats to GPX, as well as download from several brands of popular GPS receivers.
+* The program can "interpolate" between points (linearly) to get better results. (That is, for GPS logs that are not one sample per second, like those I get off my Garmin eTrex GPS)
+* The resolution is down to one second. But that should be good enough for most things. (This is a limit of the EXIF tags format, as well as GPX)
+* For best results, you should synchronise your camera to the GPS time before you start taking photos. Note: digital cameras clocks drift quickly - even over a short period of time (say, a week).
+
+## Installation
+
+To build, you will need:
+
+* The Exiv2 library (C++ EXIF tag handling): http://www.exiv2.org/
+* libxml
+* GTK+ (if compiling the GUI).
+
+You can build the command line version and the GUI together simply with
+"make". There is presently no "make install".
+
+## Release History
+
+* v1.6.1: 13 February 2010: Added a desktop icon submitted by Till Maas, and also a patch to fix build issues on Fedora. Thanks again Till!
+* v1.6.0: 5 April 2009: Added another patch that I forgot to include in 1.5.9. Thanks again Eugeniy.
+* v1.5.9: 4 April 2009: Incorporated patches from the new Debian maintainer: Fixes crash on empty tags, Fixes writing of negative altitudes, Fixes display of negative altitudes, Fixes invalid use of Exiv2 toRational(). Thanks Eugeniy for organising all these fixes; you did all the work - I just applied the patches you supplied.
+* 1 November 2008: The 'Till Maas' release. Added gpscorrelate.desktop contributed by Till. Added patches for the Makefile by Till, to improve the installation. Added manpage, originally from Debian, but converted to XML by Till. Added patches for the Makefile by Till, to configure and install the manpages. Added patches by Till to remove compilation warnings. Thanks for your work!
+* 21 September 2008: Fixed a reading of negative elevation bug (they were always written correctly), Added an install target to the makefile as supplied in a patch by Till Maas, updated the GUI to remember the last open photo and GPX directories. Also added patches to allow compilation for Win32 and makefiles to match, contributed by Julo Castillo.
+* 21 November 2007: Re-release of 1.5.6, including patch from Marc Horowitz to fix negative timezone adjustments.
+* 19 November 2007: Re-release of 1.5.6, updating the version number to be correct.
+* 1 October 2007: Version 1.5.6: Incorporated patch from Marc Horowitz that allows gpscorrelate to correctly calculate negative timezone adjustments. Previously, the minutes were not subtracted from the timezone adjustment.
+* 20 August 2007: Version 1.5.5: Made altitude data in GPX files optional. This should have been the case since the beginning, but it seems it was not.
+* 22 June 2007: Version 1.5.4: Added Photo Offset time, as a fine adjustment between photo time and GPS time. Read the docs to understand it. GUI now has extra settings, and a "Strip GPS tags" button. GUI Now remembers settings on exit, into ~/.gpscorrelaterc. These are reloaded next time the GUI is started.
+* 20 June 2007: Version 1.5.3: GPS coordinates, including altitude, are not written as Rational values instead of Signed Rational values, this now meets the EXIF specifications. Default format for writing coordinates is now DD MM SS.SS. The old behaviour can be forced with the --degmins parameter. If altitude is negative, the correct sea level reference value is now written.
+* 6th June 2007: Version 1.5.2: Fixed bug where program would die with uncaught exception if input files were not JPEGs at all. Now the exception is caught. Fixed very silly bug where timestamps were incorrectly calculated: in struct tm, I didn't realise that tm_mon was 0-based, and didn't decrement it. This caused failures on dates spanning months with different numbers of days. Because the timestamps inside EXIF data and the timestamps from GPX data were converted the same way, the matching still worked. The date part is written as GPSDateStamp, which is wrong, and thus a --fix-datestamp option is provided. Turns out GPS Timestamp wasn't correct either. This time was out by the local timezone. This did not affect matches. --fix-timestamps will fix this as well. Added a --version option.
+* 3rd April 2007: Version 1.5.1: Included patch from Marc Horowitz (an MIT one) to correctly remove all GPS tags when using the "remove GPS tags" feature. It seems my original code missed two. The patch instead iterates over the tags and removes anything starting with "Exif.GPSInfo". Thanks!
+* 24 Feb 2007: Version 1.5: Fixed very silly bug where it would segfault on certain GPX files. Turns out those GPX files don't have time data on the trackpoints, and this is due to that track coming from certain parts of the GPS memory (where the timestamps get stripped to save space on the GPS device itself). This is something gpscorrelate should have handled.
+* 28 May 2006: Version 1.4: Added option to preserve mtime on input photos. Patch submitted by Russell Steicke. (http://adelie.cx/). Also added patch to make GPX read correctly in non-C locales - would interpret "." as thousands seperator in some locales.
+* 25 April 2006: Version 1.3: It would appear that the Exiv2 API changed somewhat. And gpscorrelate didn't work. Reported to me by a friendly chap. Now fixed to work correctly with the latest Exiv2 v0.9.1.
+* Not Released: Version 1.2: Added --machine/-o option. This outputs the tags from the passed files in a machine-readable CSV output.
+* 1 March 2005: Version 1.1: by default it now does not interpolate between GPX track segments (it ignored them before). You can still interpolate between track segments if you want. GPX track segments generally indicate where data was lost, or the unit was turned off - you don't really want matches when this is the case.
+* 24 Feburary 2005: Version 1.0: Initial working release.
59 README.md~
@@ -0,0 +1,59 @@
+# GPSCorrelate
+
+Daniel Foote, 2005.
+
+## Status
+
+Abandoned. I no longer has the time to work on this and hasn't used it in many years. It's being uploaded to github so others can fork it and make their own modifications - as I get emails every few months asking for new features.
+
+## What is it?
+
+Digital cameras are cool. So is GPS. And, EXIF tags are really cool too.
+
+What happens when you merge the three? You end up with a set of photos taken with a digital camera that are "stamped" with the location at which they were taken.
+
+The EXIF standard defines a number of tags that are for use with GPS.
+
+A variety of programs exist around the place to match GPS data with digital camera photos, but most of them are Windows or MacOS only. Which doesn't really suit me that much. Also, each one takes the GPS data in a different format.
+
+So I wrote my own. A little bit of C, a little bit of C++, a shade of GTK+, and you end up with... what I have here. I wrote both a command line and GUI version of the program.
+
+## Things you should know:
+
+* The program takes GPS data in GPX format. This is an XML format. I recommend using GPSBabel - it can convert from lots of formats to GPX, as well as download from several brands of popular GPS receivers.
+* The program can "interpolate" between points (linearly) to get better results. (That is, for GPS logs that are not one sample per second, like those I get off my Garmin eTrex GPS)
+* The resolution is down to one second. But that should be good enough for most things. (This is a limit of the EXIF tags format, as well as GPX)
+* For best results, you should synchronise your camera to the GPS time before you start taking photos. Note: digital cameras clocks drift quickly - even over a short period of time (say, a week).
+
+## Installation
+
+To build, you will need:
+
+* The Exiv2 library (C++ EXIF tag handling): http://www.exiv2.org/
+* libxml
+* GTK+ (if compiling the GUI).
+
+You can build the command line version and the GUI together simply with
+"make". There is presently no "make install".
+
+## Release History
+
+* v1.6.1: 13 February 2010: Added a desktop icon submitted by Till Maas, and also a patch to fix build issues on Fedora. Thanks again Till!
+* v1.6.0: 5 April 2009: Added another patch that I forgot to include in 1.5.9. Thanks again Eugeniy.
+* v1.5.9: 4 April 2009: Incorporated patches from the new Debian maintainer: Fixes crash on empty tags, Fixes writing of negative altitudes, Fixes display of negative altitudes, Fixes invalid use of Exiv2 toRational(). Thanks Eugeniy for organising all these fixes; you did all the work - I just applied the patches you supplied.
+* 1 November 2008: The 'Till Maas' release. Added gpscorrelate.desktop contributed by Till. Added patches for the Makefile by Till, to improve the installation. Added manpage, originally from Debian, but converted to XML by Till. Added patches for the Makefile by Till, to configure and install the manpages. Added patches by Till to remove compilation warnings. Thanks for your work!
+* 21 September 2008: Fixed a reading of negative elevation bug (they were always written correctly), Added an install target to the makefile as supplied in a patch by Till Maas, updated the GUI to remember the last open photo and GPX directories. Also added patches to allow compilation for Win32 and makefiles to match, contributed by Julo Castillo.
+* 21 November 2007: Re-release of 1.5.6, including patch from Marc Horowitz to fix negative timezone adjustments.
+* 19 November 2007: Re-release of 1.5.6, updating the version number to be correct.
+* 1 October 2007: Version 1.5.6: Incorporated patch from Marc Horowitz that allows gpscorrelate to correctly calculate negative timezone adjustments. Previously, the minutes were not subtracted from the timezone adjustment.
+* 20 August 2007: Version 1.5.5: Made altitude data in GPX files optional. This should have been the case since the beginning, but it seems it was not.
+* 22 June 2007: Version 1.5.4: Added Photo Offset time, as a fine adjustment between photo time and GPS time. Read the docs to understand it. GUI now has extra settings, and a "Strip GPS tags" button. GUI Now remembers settings on exit, into ~/.gpscorrelaterc. These are reloaded next time the GUI is started.
+* 20 June 2007: Version 1.5.3: GPS coordinates, including altitude, are not written as Rational values instead of Signed Rational values, this now meets the EXIF specifications. Default format for writing coordinates is now DD MM SS.SS. The old behaviour can be forced with the --degmins parameter. If altitude is negative, the correct sea level reference value is now written.
+* 6th June 2007: Version 1.5.2: Fixed bug where program would die with uncaught exception if input files were not JPEGs at all. Now the exception is caught. Fixed very silly bug where timestamps were incorrectly calculated: in struct tm, I didn't realise that tm_mon was 0-based, and didn't decrement it. This caused failures on dates spanning months with different numbers of days. Because the timestamps inside EXIF data and the timestamps from GPX data were converted the same way, the matching still worked. The date part is written as GPSDateStamp, which is wrong, and thus a --fix-datestamp option is provided. Turns out GPS Timestamp wasn't correct either. This time was out by the local timezone. This did not affect matches. --fix-timestamps will fix this as well. Added a --version option.
+* 3rd April 2007: Version 1.5.1: Included patch from Marc Horowitz (an MIT one) to correctly remove all GPS tags when using the "remove GPS tags" feature. It seems my original code missed two. The patch instead iterates over the tags and removes anything starting with "Exif.GPSInfo". Thanks!
+* 24 Feb 2007: Version 1.5: Fixed very silly bug where it would segfault on certain GPX files. Turns out those GPX files don't have time data on the trackpoints, and this is due to that track coming from certain parts of the GPS memory (where the timestamps get stripped to save space on the GPS device itself). This is something gpscorrelate should have handled.
+* 28 May 2006: Version 1.4: Added option to preserve mtime on input photos. Patch submitted by Russell Steicke. (http://adelie.cx/). Also added patch to make GPX read correctly in non-C locales - would interpret "." as thousands seperator in some locales.
+* 25 April 2006: Version 1.3: It would appear that the Exiv2 API changed somewhat. And gpscorrelate didn't work. Reported to me by a friendly chap. Now fixed to work correctly with the latest Exiv2 v0.9.1.
+Not Released: Version 1.2: Added --machine/-o option. This outputs the tags from the passed files in a machine-readable CSV output.
+* 1 March 2005: Version 1.1: by default it now does not interpolate between GPX track segments (it ignored them before). You can still interpolate between track segments if you want. GPX track segments generally indicate where data was lost, or the unit was turned off - you don't really want matches when this is the case.
+* 24 Feburary 2005: Version 1.0: Initial working release.
32 README.mingw32
@@ -0,0 +1,32 @@
+To compile the windows version you need a mingw32 cross compiler
+toolchain (binutils, gcc, g++, etc). See this page for more pointers
+
+ http://www.ecn.wfu.edu/~cottrell/cross-gtk/
+
+To compile gpscorrelate for win32 you need cross-compiled versions of:
+ libiconv
+ gettext
+ cairo
+ atk
+ libpng
+ libxml2
+ pango
+ pixman
+ zlib
+ expat
+ fontconfig
+ freetype
+ gettext
+ glib2
+ gtk2
+ exiv2
+
+
+Once you have all the dependencies cross-compiled, export
+PKG_CONFIG_PATH to the database of your toolchain environment
+(somehting like /usr/i486-mingw32/lib/pkgconfig/)
+
+Finally run
+ # make -f Makefile.mingw32
+
+(Makefile.mingw32 and README.mingw32 written and contributed by Julio Castilo. Thanks!)
114 RELEASES
@@ -0,0 +1,114 @@
+Release History:
+
+v1.0: 24 Feb 2005
+ Initial release.
+
+v1.1: 1 Mar 2005
+ Instead of ignoring track segments, we record them now,
+ and by default don't interpolate between them. This can
+ be disabled, ie, match between track segments.
+
+v1.2: (Not released until 1.3)
+ Added --machine/-o option. This outputs the tags from
+ the passed files in a machine-readable CSV output.
+
+v1.3: 25 April 2006
+ It would appear that the Exiv2 API changed somewhat.
+ And gpscorrelate didn't work. Reported to me by a friendly chap.
+ Now fixed to work correctly with the latest Exiv2 v0.9.1.
+
+v1.4: 28 May 2006
+ Added option to preserve mtime on input photos. Patch submitted
+ by Russell Steicke. (http://adelie.cx/).
+ Also added patch to make GPX read correctly in non-C locales -
+ would interpret "." as thousands seperator in some locales.
+
+v1.5: 24 Feb 2007
+ Fixed very silly bug where it would segfault on certain GPX files.
+ Turns out those GPX files don't have time data on the trackpoints,
+ and this is due to that track coming from certain parts of the GPS
+ memory (where the timestamps get stripped to save space on the GPS
+ device itself). This is something gpscorrelate should have handled.
+
+v1.5.1: 3rd April 2007
+ Included patch from Marc Horowitz (an MIT one) to correctly remove
+ all GPS tags when using the "remove GPS tags" feature. It seems my
+ original code missed two. The patch instead iterates over the tags
+ and removes anything starting with "Exif.GPSInfo". Thanks!
+
+v1.5.2: 6 June 2007
+ - Fixed bug where program would die with uncaught exception if input
+ files were not JPEGs at all. Now the exception is caught.
+ - Fixed very silly bug where timestamps were incorrectly calculated:
+ in struct tm, I didn't realise that tm_mon was 0-based, and didn't
+ decrement it. This caused failures on dates spanning months with
+ different numbers of days. Because the timestamps inside EXIF data
+ and the timestamps from GPX data were converted the same way, the
+ matching still worked. The date part is written as GPSDateStamp,
+ which is wrong, and thus a --fix-datestamp option is provided.
+ - Turns out GPS Timestamp wasn't correct either. This time was out
+ by the local timezone. This did not affect matches. --fix-timestamps
+ will fix this as well.
+ - Added a --version option.
+
+v1.5.3: 20 June 2007
+ - GPS coordinates, including altitude, are not written as Rational
+ values instead of Signed Rational values, this now meets the EXIF
+ specifications.
+ - Default format for writing coordinates is now DD MM SS.SS. The old
+ behaviour can be forced with the --degmins parameter.
+ - If altitude is negative, the correct sea level reference value is
+ now written.
+
+v1.5.4: 22 June 2007
+ - Added Photo Offset time, as a fine adjustment between photo time and
+ GPS time. Read the docs to understand it.
+ - GUI now has extra settings, and a "Strip GPS tags" button.
+ - GUI Now remembers settings on exit, into ~/.gpscorrelaterc. These are
+ reloaded next time the GUI is started.
+
+v1.5.5: 20 August 2007
+ - Made altitude data in GPX files optional. This should have been the case
+ since the beginning, but it seems it was not.
+
+v1.5.6: 1 October 2007
+ - Incorporated patch from Marc Horowitz that allows gpscorrelate to correctly
+ calculate negative timezone adjustments. Previously, the minutes were not
+ subtracted from the timezone adjustment.
+
+v1.5.7: 21 September 2008
+ - Fixed a bug where altitude data was not read correctly if the value was
+ negative - instead it would read positive. It was always written correctly,
+ though. Thanks to Andrzej Novak for pointing this one out.
+ - Added an 'install' target to the makefile provided by Till Maas. Thanks!
+ - Update the GUI to remember the last directory for Photos and GPX data when
+ using the file chooser dialog. This also persists across program invocations.
+ This was suggested by Till Maas.
+ - Added Makefile.mingw32 contributed by Julio Castillo, which allows cross
+ compliation on Win32. This also included some cross-platform patches for the
+ code, which is greatly appreciated.
+
+v1.5.8: 1 November 2008
+ The 'Till Maas' release.
+ - Added gpscorrelate.desktop contributed by Till.
+ - Added patches for the Makefile by Till, to improve the installation.
+ - Added manpage, originally from Debian, but converted to XML by Till.
+ - Added patches for the Makefile by Till, to configure and install the manpages.
+ - Added patches by Till to remove compilation warnings.
+ - Thanks for your work!
+
+v1.5.9: 4 April 2009
+ Incorporated patches from the new Debian maintainer:
+ - Fixes crash on empty tags
+ - Fixes writing of negative altitudes.
+ - Fixes display of negative altitudes.
+ - Fixes invalid use of Exiv2 toRational().
+ Thanks Eugeniy for organising all these fixes; you did all the work - I just
+ applied the patches you supplied.
+
+v1.6.0: 5 April 2009
+ Added another patch that I forgot to include in 1.5.9. Thanks again Eugeniy.
+
+v1.6.1: 13 February 2010
+ - Added desktop icon created by Till Maas.
+ - Added a patch to fix future build issues on Fedora.
322 correlate.c
@@ -0,0 +1,322 @@
+/* correlate.c
+ * Written by Daniel Foote.
+ * Started Feb 2005.
+ *
+ * The functions in this file match the timestamps on
+ * the photos to the GPS data, and then, if a match
+ * is found, writes the GPS data into the EXIF data
+ * in the photo. For future reference... */
+
+/* Copyright 2005 Daniel Foote.
+ *
+ * This file is part of gpscorrelate.
+ *
+ * gpscorrelate 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.
+ *
+ * gpscorrelate is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gpscorrelate; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "gpsstructure.h"
+#include "exif-gps.h"
+#include "correlate.h"
+#include "unixtime.h"
+
+/* Internal functions used to make it work. */
+void Round(struct GPSPoint* First, struct GPSPoint* Result,
+ struct CorrelateOptions* Options, time_t PhotoTime);
+void Interpolate(struct GPSPoint* First, struct GPSPoint* Result,
+ struct CorrelateOptions* Options, time_t PhotoTime);
+
+/* This function returns a GPSPoint with the point selected for the
+ * file. This allows us to do funky stuff like not actually write
+ * the files - ie, just correlate and keep into memory... */
+
+struct GPSPoint* CorrelatePhoto(char* Filename,
+ struct CorrelateOptions* Options)
+{
+ /* Read out the timestamp from the EXIF data. */
+ char* TimeTemp; int IncludesGPS = 0;
+ TimeTemp = ReadExifDate(Filename, &IncludesGPS);
+ if (!TimeTemp)
+ {
+ /* Error reading the time from the file. Abort. */
+ /* If this was a read error, then a seperate message
+ * will appear on the console. Otherwise, we were
+ * returned here due to the lack of exif tags. */
+ Options->Result = CORR_NOEXIFINPUT;
+ return 0;
+ }
+ if (IncludesGPS)
+ {
+ /* Already have GPS data in the file!
+ * So we can't do this again... */
+ Options->Result = CORR_GPSDATAEXISTS;
+ return 0;
+ }
+
+ /* Now convert the time into Unixtime. */
+ time_t PhotoTime =
+ ConvertToUnixTime(TimeTemp, EXIF_DATE_FORMAT,
+ Options->TimeZoneHours, Options->TimeZoneMins);
+
+ /* Add the PhotoOffset time. This is to make the Photo time match
+ * the GPS time - ie, it is (GPS - Photo). */
+ PhotoTime += Options->PhotoOffset;
+
+ /* Free the memory for the time string - it won't otherwise
+ * be freed for us. */
+ free(TimeTemp);
+
+ /* Check to see if MinTime and MaxTime are filled in.
+ * If not, fill them in. */
+ if (Options->MinTime == 0 && Options->MaxTime == 0)
+ {
+ /* Alright, fill them in!
+ * Requires us to go through the list and keeping
+ * the biggest and smallest. The list should,
+ * however, be sorted. But we do it this way anyway. */
+ struct GPSPoint* Fill = NULL;
+ Options->MinTime = Options->Points->Time;
+ for (Fill = Options->Points; Fill; Fill = Fill->Next)
+ {
+ /* Ignore trackseg markers... */
+ if (Fill->Lat == 1000 && Fill->Long == 1000)
+ continue;
+ /* Check the Min time */
+ if (Fill->Time < Options->MinTime)
+ Options->MinTime = Fill->Time;
+ /* Check the Max time */
+ if (Fill->Time > Options->MaxTime)
+ Options->MaxTime = Fill->Time;
+ }
+ }
+
+ /* Check that the photo is within the times that
+ * our tracks are for. Can't really match it if
+ * we were not logging when it was taken. */
+ /* Note: photos taken between logging sessions of the
+ * same file will still make it inside of this. In
+ * some cases, it won't matter, but if it does, then
+ * keep this in mind!! */
+ if ((PhotoTime < Options->MinTime) ||
+ (PhotoTime > Options->MaxTime))
+ {
+ /* Outside the range. Abort. */
+ Options->Result = CORR_NOMATCH;
+ return 0;
+ }
+
+ /* Time to run through the list, and see if our PhotoTime
+ * is in between two points. Alternately, it might be
+ * exactly on a point... even better... */
+ struct GPSPoint* Search;
+ struct GPSPoint* Actual = malloc(sizeof(struct GPSPoint));
+
+ Options->Result = CORR_NOMATCH; /* For convenience later */
+
+ for (Search = Options->Points; Search; Search = Search->Next)
+ {
+ /* Sanity check: we need to peek at the next point.
+ * Make sure we can. */
+ if (Search->Next == NULL) break;
+ /* Sanity check: does this point have the same
+ * timestamp as the next? If so, skip onward. */
+ if (Search->Time == Search->Next->Time) continue;
+ /* Sanity check: does this point have a later
+ * timestamp than the next point? If so, skip. */
+ if (Search->Time > Search->Next->Time) continue;
+
+ if (Options->DoBetweenTrkSeg)
+ {
+ /* Righto, we are interpolating between segments.
+ * So simply do nothing! Simple! */
+ } else {
+ /* Don't check between track segments.
+ * If the end of segment marker is set, then simply
+ * "jump" over this point. */
+ if (Search->EndOfSegment)
+ {
+ continue;
+ }
+ }
+
+ /* Sanity check / track segment fix: is the photo time before
+ * the current point? If so, we've gone past it. Hrm. */
+ if (Search->Time > PhotoTime)
+ {
+ Options->Result = CORR_NOMATCH;
+ break;
+ }
+
+ /* Sort of sanity check: is this photo inside our
+ * "feather" time? If not, abort. */
+ if (Options->FeatherTime)
+ {
+ /* Is the point between these two? */
+ if ((PhotoTime > Search->Time) &&
+ (PhotoTime < Search->Next->Time))
+ {
+ /* It is. Now is it too far
+ * from these two? */
+ if (((Search->Time + Options->FeatherTime) < PhotoTime) &&
+ ((Search->Next->Time - Options->FeatherTime) > PhotoTime))
+ {
+ /* We are inside the feather
+ * time between two points.
+ * Abort. */
+ Options->Result = CORR_TOOFAR;
+ free(Actual);
+ return NULL;
+ }
+ }
+ } /* endif (Options->Feather) */
+
+ /* First test: is it exactly this point? */
+ if (PhotoTime == Search->Time)
+ {
+ /* This is the point, exactly.
+ * Copy out the data and return that. */
+ Actual->Lat = Search->Lat;
+ Actual->Long = Search->Long;
+ Actual->Elev = Search->Elev;
+ Actual->Time = Search->Time;
+
+ Options->Result = CORR_OK;
+ break;
+ }
+
+ /* Second test: is it between this and the
+ * next point? */
+ if ((PhotoTime > Search->Time) &&
+ (PhotoTime < Search->Next->Time))
+ {
+ /* It is between these points.
+ * Unless told otherwise, we interpolate.
+ * If not interpolating, we round to nearest.
+ * If points are eqidistant, we round down. */
+ if (Options->NoInterpolate)
+ {
+ /* No interpolation. Round. */
+ Round(Search, Actual,
+ Options, PhotoTime);
+ Options->Result = CORR_ROUND;
+ break;
+ } else {
+ /* Interpolate away! */
+ Interpolate(Search, Actual,
+ Options, PhotoTime);
+ Options->Result = CORR_INTERPOLATED;
+ break;
+ }
+ }
+ } /* End for() loop to search. */
+
+ /* Did we actually match it at all? */
+ if (Options->Result == CORR_NOMATCH)
+ {
+ /* Nope, no match at all. */
+ /* Return with nothing. */
+ free(Actual);
+ return NULL;
+ }
+
+ /* Write the data back into the Exif info. If we're allowed. */
+ if (Options->NoWriteExif)
+ {
+ /* Don't write exif tags. Just return. */
+ return Actual;
+ } else {
+ /* Do write the exif tags. And then return. */
+ if (WriteGPSData(Filename, Actual, Options->Datum, Options->NoChangeMtime, Options->DegMinSecs))
+ {
+ /* All ok. Good! Return. */
+ return Actual;
+ } else {
+ /* Not good. Return point, but note failure. */
+ Options->Result = CORR_EXIFWRITEFAIL;
+ return Actual;
+ }
+ }
+
+ /* Looks like nothing matched. Free the prepared memory,
+ * and return nothing. */
+ free(Actual);
+ return NULL;
+};
+
+void Round(struct GPSPoint* First, struct GPSPoint* Result,
+ struct CorrelateOptions* Options, time_t PhotoTime)
+{
+ /* Round the point between the two points - ie, it will end
+ * up being one or the other point. */
+ struct GPSPoint* CopyFrom = NULL;
+
+ /* Determine the difference between the two points.
+ * We're using the scale function used by interpolate.
+ * This gives us a good view of where we are... */
+ double Scale = (double)First->Next->Time - (double)First->Time;
+ Scale = ((double)PhotoTime - (double)First->Time) / Scale;
+
+ /* Compare our scale. */
+ if (Scale <= 0.5)
+ {
+ /* Closer to the first point. */
+ CopyFrom = First;
+ } else {
+ /* Closer to the second point. */
+ CopyFrom = First->Next;
+ }
+
+ /* Copy the numbers over... */
+ Result->Lat = CopyFrom->Lat;
+ Result->Long = CopyFrom->Long;
+ Result->Elev = CopyFrom->Elev;
+ Result->Time = CopyFrom->Time;
+
+ /* Done! */
+
+}
+
+void Interpolate(struct GPSPoint* First, struct GPSPoint* Result,
+ struct CorrelateOptions* Options, time_t PhotoTime)
+{
+ /* Interpolate between the two points. The first point
+ * is First, the other First->Next. Results into Result. */
+
+ /* Calculate the "scale": a decimal giving the relative distance
+ * in time between the two points. Ie, a number between 0 and 1 -
+ * 0 is the first point, 1 is the next point, and 0.5 would be
+ * half way. */
+ double Scale = (double)First->Next->Time - (double)First->Time;
+ Scale = ((double)PhotoTime - (double)First->Time) / Scale;
+
+ /* Now calculate the Lattitude. */
+ Result->Lat = First->Lat + ((First->Next->Lat - First->Lat) * Scale);
+
+ /* And the longitude. */
+ Result->Long = First->Long + ((First->Next->Long - First->Long) * Scale);
+
+ /* And the elevation. If elevation wasn't set, it should be zero.
+ * Which works quite fine for us. */
+ Result->Elev = First->Elev + ((First->Next->Elev - First->Elev) * Scale);
+
+ /* The time is not interpolated, but matches photo. */
+ Result->Time = PhotoTime;
+
+ /* And that should have fixed us... */
+
+}
81 correlate.h
@@ -0,0 +1,81 @@
+/* correlate.h
+ * Written by Daniel Foote.
+ * Started Feb 2005.
+ *
+ * This file contains the options structure and prototypes for
+ * functions in correlate.c.
+ */
+
+/* Copyright 2005 Daniel Foote.
+ *
+ * This file is part of gpscorrelate.
+ *
+ * gpscorrelate 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.
+ *
+ * gpscorrelate is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gpscorrelate; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* A structure of options to pass to the correlate function.
+ * Not really sure if this is needed, but... */
+struct CorrelateOptions {
+ int NoWriteExif;
+ int NoInterpolate;
+ int NoChangeMtime;
+ int TimeZoneHours; /* To add to photos to make them UTC. */
+ int TimeZoneMins;
+ int FeatherTime;
+ char* Datum; /* Datum of the data; when writing. */
+ int DoBetweenTrkSeg; /* Match between track segments. */
+ int DegMinSecs; /* Write out data as DD MM SS.SS (more accurate than in the past) */
+
+ int Result;
+
+ time_t MinTime; /* Calculated on first pass. Used to */
+ time_t MaxTime; /* determine when to throw photos out. */
+
+ int PhotoOffset; /* Offset applied to Photo time. This is ADDED to PHOTO TIME
+ to make it match GPS time. In seconds.
+ This is (GPS - Photo) */
+
+ struct GPSPoint* Points; /* Points to use... */
+};
+
+/* Return codes in order:
+ * _OK - all ok. Correlated exactly.
+ * _INTERPOLATED - all ok, interpolated point.
+ * _ROUND - point rounded to nearest.
+ * _NOMATCH - could not find a match - photo timestamp outside GPS data
+ * (This could be due to timezone of photos not set/set wrong).
+ * Returns NULL for Point.
+ * _TOOFAR - point outside "feather" time. Too far from any point.
+ * Returns NULL for Point.
+ * _EXIFWRITEFAIL - unable to write EXIF tags.
+ * _NOEXIFINPUT - The source file contained no EXIF tags, or not the one we wanted. Hmm.
+ * Returns NULL for Point.
+ * _GPSDATAEXISTS - There is already GPS data in the photo... you probably don't want
+ * to fiddle with it.
+ * Returns NULL for Point.
+ */
+#define CORR_OK 1
+#define CORR_INTERPOLATED 2
+#define CORR_ROUND 3
+#define CORR_NOMATCH 4
+#define CORR_TOOFAR 5
+#define CORR_EXIFWRITEFAIL 6
+#define CORR_NOEXIFINPUT 7
+#define CORR_GPSDATAEXISTS 8
+
+
+struct GPSPoint* CorrelatePhoto(char* Filename,
+ struct CorrelateOptions* Options);
+
140 doc/command.html
@@ -0,0 +1,140 @@
+<html><head><title>GPS Correlate Documentation: Command line version</title></head>
+
+<body>
+
+<h1 align="center">GPS Correlate Documentation: Command line version</h1>
+
+<hr />
+
+<h2>Command line options</h2>
+
+<p>Basic usage:</p>
+
+<p><pre>
+gpscorrelate [OPTIONS] -g gpsdata.gpx photo1.jpg photo2.jpg ...
+</pre></p>
+
+<p>The options are:</p>
+
+<table border="0" width="100%">
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--gps or -g gps_data.gpx</b>
+</td><td>
+Specify the file to read the GPS data from
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--timeadd or -z +/-XX:[XX]</b>
+</td><td>
+Specifies the timezone of the photos. For example, in Perth, Western Australia, this is +8:00. This can be specified as either "8", "+8", "8:00", or "+8:00".
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--no-interpolation or -i</b>
+</td><td>
+Disable interpolation between the points. Instead of interpolating, the program will "round" to the nearest point. If the photo is exactly half way between the two points, it will round down to the earliest point.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--verbose or -v</b>
+</td><td>
+Show the final fixes on the screen.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--no-write or -n</b>
+</td><td>
+Don't write the GPS EXIF tags back to the file. Useful with -v, to do a trial run.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--datum or -d "datum"</b>
+</td><td>
+Specifies the "datum" to write into the GPS EXIF tags. By default, it is WGS-84. However, GPX is not supposed to store anything but WGS-84, so use if you must.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--max-dist or -m time</b>
+</td><td>
+Specifies the maximum distance around a point that a photo will be matched. In seconds.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--show or -s</b>
+</td><td>
+Just show the GPS data embedded into the EXIF tags of the photos specified on the command line, and then quit.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--machine or -o</b>
+</td><td>
+Just show the GPS data embedded into the EXIF tags of the photos specified on the command line, and then quit. This option varies from --show in that it will output a machine readable format: CSV. In this case the fields are:<br />
+"filename.jpg","2005:04:23 19:31:00",Latitude,Longitude,Elevation<br />
+Where the first value is the filename, as passed, the second is the timestamp, and the last three are floating point values, with a leading plus or minus.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--remove or -r</b>
+</td><td>
+Remove GPS EXIF tags from the specified files, and then quit. Note that this only removes the GPS tags that the program could add, it does not delete all possible GPS EXIF tags. All other tags are left alone.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--ignore-tracksegs or -t</b>
+</td><td>
+Ignores tracksegments in the original GPX file and interpolates between them. Generally track segments show multiple sessions of GPS logging: between them is generally when the GPS was not logging.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--fix-datestamps or -f</b>
+</td><td>
+Prior to 1.5.2 was two bugs that wrote the wrong value for the GPSDateStamp and GPSTimeStamp tag. This option will check each supplied filename for the problem and correct it. Use with --no-write to prevent writing these changes (useful for checking for the issue). This option also implies --no-mtime. You will also need to use --timeadd to specify the difference between localtime and UTC time for the supplied photos.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--degmins or -p</b>
+</td><td>
+Prior to 1.5.3, Longitude and Lattitude values were written as DD MM.MM. After 1.5.3, the default is to write them as DD MM SS.SS, which is more accurate. To force the old behaviour, use this flag.
+</td></tr>
+
+<tr>
+<td valign="top" nowrap="nowrap">
+<b>--photooffset or -O seconds</b>
+</td><td>
+This parameter specifies the Photo offset, that is a number of seconds added to the photo timestamp. It is calculated with GPS - Photo time. See the <a href="concepts.html">GPS Correlate concepts</a> for more details on exactly how to use this.
+</td></tr>
+
+</table>
+
+<p>Examples of usage:</p>
+
+<p>In a directory full of photos:</p>
+
+<pre>gpscorrelate -g Test.gpx -z +8 *.jpg</pre>
+
+<p>Removing or showing GPS tags:</p>
+
+<pre>gpscorrelate --show *.jpg
+gpscorrelate --remove *.jpg</pre>
+
+<p>And thats about it... there is not too much more to say about this program that is not already said in the <a href="concepts.html">GPS Correlate concepts</a> documentation.</p>
+
+<hr />
+
+<a href="index.html">Return to the contents</a>
+
+</body>
+</html>
113 doc/concepts.html
@@ -0,0 +1,113 @@
+<html><head><title>GPS Correlate Documentation: Concepts</title></head>
+
+<body>
+
+<h1 align="center">GPS Correlate Documentation: Concepts</h1>
+
+<hr />
+
+<h2>Step 1: Taking photos and recording GPS data.</h2>
+
+<p>Before you can correlate any photos with GPS data, you first have to have some photos and some GPS data. This generally involves walking/driving/going somewhere, taking photos, and taking a GPS (or GPS like device) with you.</p>
+
+<p>Most cheap GPS devices have a "tracklog" facility that record where you have been. The resolution of these tracks is widely variable - my <a href="http://www.garmin.com/products/etrex/">Garmin eTrex</a> GPS records up to 1600 data points, at around 20 metres between each point. This works out to travelling about 500 kilometres (by car) or 50 kilometres (on foot) before you run out of memory. (These numbers vary very much depending on many factors.)</p>
+
+<p>Other GPS devices will vary in their capacity. If you are using a GPS device made only for a computer, or have attached your GPS device to a computer, you may wish to log the data on the computer. In this case, you will probably end up logging the one second fixes, which results in a fair bit of data (nothing excessive), but very accurate results.</p>
+
+<p>As to taking the photos? Not too many digital cameras add GPS data to the photos themselves - although they do exist. If you have one, then you don't need this program!</p>
+
+<p>Besides that, take photos as you normally would. The correlation program matches photos to GPS data by their timestamp, so it would be a good idea to synchronise the cameras time to the time from the GPS before you start taking photos. (Not the other way around: the GPS gets its time from the satelites, which means that it is probably correct... and most GPSs will not allow you to set their time, anyway).</p>
+
+<p>The photos will have to be tagged with "EXIF tags", which describe metadata about the photo, including the date and time. These tags are embedded in the photos themselves. That is pretty much standard for digital cameras nowadays, however, some very very very old digital cameras (like the ancient floppy-disk Sony Mavica that a family member owns) don't store EXIF tags. These will not work with this program. There are probably ways around this, though - but they are outside what I am talking about here. (Give me half a second, and I'd start talking about writing a script to grab... no, better stop there...)</p>
+
+<p>When you are finished the photo shoot, download the photos as normal and get ready. Mind that any permanent rotations or editing do not remove the EXIF tags.</p>
+
+<h3>What you need to know:</h3>
+
+<ul>
+<li><b>Syncronise your cameras time from the GPS time.</b> Don't ask, just do it. And do it just before your photo shoot.</li>
+<li><b>Use a GPS that has a tracklog capability.</b> Most do - although the quality/resolution varies.</li>
+<li><b>Take your GPS with you, and keep it close to the camera.</b> GPS is accurate down to several meters, so use your discretion.</li>
+</ul>
+
+<hr />
+
+<h2>Step 2: Getting the GPS data ready.</h2>
+
+<p>The GPS correlate program accepts GPS data in the GPX format: an open XML format. The format is quite arbitrary, and, naturally, everyone has their own GPS file format. So, in that way, this program is no exception.</p>
+
+<p>Exactly how you will get the GPS data from a discrete device is very much device dependant. Many devices have serial cables or USB cables to download the data (although the USB cables are often glorified USB-to-serial convertors). What format it will turn out as is also the big question. I personally recommend GPSBabel, which can convert between many formats (thus translating from whatever strange one your downloading method/program produces) and can also download from Garmin and Magellen GPS devices directly (so you can download straight to GPX and retain lots of useful metadata, like track segments).</p>
+
+<p>If you logged the GPS data with a computer... well, it could be any format, depending on how it is logged. One trick would be to log the NMEA sentences as they came from the GPS directly into a file. Once the logging is done, GPSBabel can convert these NMEA sentences directly into GPX format. Usually these are one second fixes, so this would yeild very good results - however, lugging around a laptop to log the data might not be convenient. A PDA might be convenient, though...</p>
+
+<p>The other thing to know about GPX is that is stores data quite cleverly, seperating tracks into "track segments". This can be explained quite simply with an example. When I drive through a tunnel with my GPS, it has no reception in the tunnel. My GPS records this lack of signal on the map by not showing a track between the points where I was in the tunnel. GPSBabel correctly reads this and enters this in the GPX file by seperating the segments with "track segments". Prior to version 1.1, this program ignored track segments and just hauled out points and considered them to be a single track. So if a photo was taken in between the time where there was no GPS data, you would not get a correct answer (depending on the situation). Now, the program will not match a photo between track segments. You can, however, disable this functionality, and still match between track segments.</p>
+
+<h3>What you need to know:</h3>
+
+<ul>
+<li><b>The result of downloading the GPS data could be any format.</b> Convert it with GPSBabel to GPX format, or better yet use GPSBabel to download the data in the first place.</li>
+<li><b>Absolute best results would be logging NMEA sentences to file, and then GPX'ing it with GPSBabel.</b> This will give you lots of one second fixes. Very accurate.</li>
+<li><b>The GPX format is good for archiving tracks.</b> Due to its XML nature, it should be readable for a while to come yet.</li>
+<li><b>The GPX file is split up into "track segments".</b> Generally these define when you were or were not logging GPS data. This program does not interpolate between track segments, by default.</li>
+</ul>
+
+<hr />
+
+<h2>Step 3: Getting ready to correlate.</h2>
+
+<p>To correlate, you need photos and GPS data. I assume the last two steps would have resulted you with a series of JPEG files with EXIF tags, and a GPX file with a "tracklog" of where you have been when you took the photos.</p>
+
+<p>The last thing you need to know is the timezone that the camera is set to. GPS data is always in UTC, so when we correlate the photos, we need to know how much to add or subtract to the photos time to make the photos time UTC. Just "know" this value, there are places to add this in in the programs.</p>
+
+<p>(This also assumes that the camera is set for local time where you are taking the photos. It is really arbitrary, so long as you know the difference between your cameras time and UTC. If you really wanted, just set the camera to UTC and be done with it... although thats not so friendly when you try to use the EXIF data for other things. Up to you.)</p>
+
+<p>As an example, here in Perth, Western Australia, the timezone is +8:00. So that is the value that I use when correlating the photos.</p>
+
+<p>Since 1.5.4, there is an option to add an offset to the photo time to make it match up with GPS data. An example case is where you take a photo of your GPS showing the current time. You can use this to calculate the difference between the GPSs time and the photos time.</p>
+
+<p>Example calculations:
+<pre>GPS Time in Photo: 10:10:10
+Timestamp on Photo: 10:09:30
+GPS - Photo: +40
+
+GPS Time in Photo: 10:10:10
+Timestamp on Photo: 10:11:30
+GPS - Photo: -80</pre></p>
+
+<p>The value determined in the above examples becomes the "Photo offset" time, in seconds, which is added to the photos timestamp before the correlation is performed.</p>
+
+<h3>What you need to know:</h3>
+
+<ul>
+<li><b>The timezone the camera was set to when taking photos.</b> In hours and minutes, for example: +8:00.</li>
+<li><b>As of 1.5.4, you can fine tune the offset between the GPS time and the Photos time.</b> Use the photo offset function to set this.</li>
+</ul>
+
+<hr />
+
+<h2>Step 4: Correlation.</h2>
+
+<p>Time to correlate! Exactly how this is performed depends on whether or not you use the <a href="gui.html">GUI version</a> (prettier) or the <a href="command.html">command line version</a> (scriptable).</p>
+
+<p>Now there are a few options to consider when deciding how to allow the program to determine exactly the location at which the photo was taken. The program works down to the second. If you have one second GPS fixes available, then the photos will be matched exactly with a point having exactly the same timestamp. As this is the accuracy of the camera, and the accuracy of the GPS - you can not get more accurate than this with the given equipment.</p>
+
+<p>When there is less than one second data available, there are a few options available to make the results more accurate. By default, if the program finds photos between points, it will linearly interpolate between the points to determine the correct location of the photo.</p>
+
+<p>You may not wish to allow the interpolation, for whatever reason. If you disable interpolation, then the program will instead "round" to the nearest point. That means that the photo will be set to have the location that is the recorded closest fix in time. (Rounding down if you manage to exactly center it.)</p>
+
+<p>The final major setting has many names, of which I can not decide the best one for. Sometimes I call this "feather time", and other times I call this "max distance". In any event, this setting, specified in seconds, defines the furthest time from any recorded GPS point that a photo will be matched. For example, if two GPS data points are 15 minutes apart, and you took a photo in the middle, you may not wish to match this, as the camera might well have been somewhere else. In this case, set the "feather time" to the maximum time from a recorded point that you want a photo matched. In the above example, you might set it to 60 seconds, if you know how often your GPS is likely to take another data point.</p>
+
+<h3>What you need to know:</h3>
+
+<ul>
+<li><b>You can use the <a href="gui.html">GUI version</a> if that suits you better.</b></li>
+<li><b>Or there is always the dependable <a href="command.html">command line</a> version.</b></li>
+<li><b>By default, the program linearly interpolates.</b> If disabled, it rounds to the nearest point.</li>
+<li><b>"Feather time" or "max distance", in seconds</b> is the maximum time from a data point that a photo will be matched.</li>
+</ul>
+
+<hr />
+
+<a href="index.html">Return to the contents</a>
+
+</body></html>
BIN doc/corr.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
399 doc/gpscorrelate-manpage.xml.in
@@ -0,0 +1,399 @@
+<?xml version='1.0' encoding='ISO-8859-1'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+
+ <!ENTITY dhfirstname "<firstname>Stefano</firstname>">
+ <!ENTITY dhsurname "<surname>Zacchiroli</surname>">
+ <!ENTITY dhdate "<date>30 Oct 2008</date>">
+ <!ENTITY dhsection "<manvolnum>1</manvolnum>">
+ <!ENTITY dhemail "<email>zack@debian.org</email>">
+ <!ENTITY dhusername "Stefano Zacchiroli">
+ <!ENTITY dhucpackage "<refentrytitle>GPSCORRELATE</refentrytitle>">
+ <!ENTITY dhpackage "gpscorrelate">
+
+ <!ENTITY debian "<productname>Debian</productname>">
+ <!ENTITY gnu "<acronym>GNU</acronym>">
+ <!ENTITY gpl "&gnu; <acronym>GPL</acronym>">
+]>
+
+<refentry>
+ <refentryinfo>
+ <address>
+ &dhemail;
+ </address>
+ <author>
+ &dhfirstname;
+ &dhsurname;, Till Maas
+ </author>
+ <copyright>
+ <year>2006, 2008</year>
+ <holder>&dhusername; &dhemail;, Till Maas</holder>
+ </copyright>
+ &dhdate;
+ </refentryinfo>
+ <refmeta>
+ &dhucpackage;
+
+ &dhsection;
+ </refmeta>
+ <refnamediv>
+ <refname>&dhpackage;</refname>
+ <refpurpose>correlates digital images with GPS data filling EXIF fields</refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <group>
+ <arg choice="plain">-z</arg>
+ <arg choice="plain">
+ --timeadd +/-<replaceable>HH</replaceable>[:<replaceable>MM</replaceable>]
+ </arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-O</arg>
+ <arg choice="plain">
+ --photooffset <replaceable>seconds</replaceable>
+ </arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-i</arg>
+ <arg choice="plain">--no-interpolation</arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-v</arg>
+ <arg choice="plain">--verbose</arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-d</arg>
+ <arg choice="plain">--datum <replaceable>datum</replaceable>
+ </arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-n</arg>
+ <arg choice="plain">--no-write</arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-m</arg>
+ <arg choice="plain">--max-dist <replaceable>time</replaceable>
+ </arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-t</arg>
+ <arg choice="plain">--ignore-tracksegs</arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-M</arg>
+ <arg choice="plain">--no-mtime</arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-f</arg>
+ <arg choice="plain">--fix-timestamps</arg>
+ </group>
+
+ <group>
+ <arg choice="plain">-p</arg>
+ <arg choice="plain">--degmins</arg>
+ </group>
+
+
+ <arg choice="plain">
+ -g <replaceable>file.gpx</replaceable>
+ </arg>
+ <arg rep="repeat" choice="plain">
+ <replaceable>image.jpg</replaceable>
+ </arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <group choice="plain">
+ <arg choice="plain">-s</arg>
+ <arg choice="plain">--show</arg>
+ <arg choice="plain">-o</arg>
+ <arg choice="plain">--machine</arg>
+ </group>
+ <arg rep="repeat" choice="plain"><replaceable>image.jpg</replaceable></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <group>
+ <arg choice="plain">-M</arg>
+ <arg choice="plain">--no-mtime</arg>
+ </group>
+
+ <group choice="req">
+ <arg choice="plain">-r</arg>
+ <arg choice="plain">--remove</arg>
+ </group>
+ <arg rep="repeat" choice="plain"><replaceable>image.jpg</replaceable></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>&dhpackage;</command>
+ <group choice="plain">
+ <arg choice="plain">-V</arg>
+ <arg choice="plain">--version</arg>
+ <arg choice="plain">-h</arg>
+ <arg choice="plain">--help</arg>
+ </group>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>&dhpackage;-gui</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>This manual page documents briefly the <command>&dhpackage;</command>
+ and <command>&dhpackage;-gui</command> commands.</para>
+
+ <para>There is an extended documenation available in HTML format; see below.</para>
+
+ <para><command>&dhpackage;</command> is a program that acts on digital images
+ in JPEG format filling EXIF (Exchangeable Image File Format) fields
+ related to GPS (Global Positioning System) information. Source for the
+ GPS data is a record of GPS information encoded in GPX (GPS Exchange
+ Format) Format. The act of filling those fields is referred to as
+ <emphasis>correlation</emphasis>.</para>
+
+ <para> If GPS data are available at the precise moment the image was taken
+ (with a 1-second granularity) the GPS data are stored unmodified in EXIF
+ fields. If they are not linear interpolation of GPS data available at
+ moments before and after the image was taken can be used. </para>
+
+ <para><command>&dhpackage;</command> is a command line tool implementing
+ correlation whereas <command>&dhpackage;-gui</command> is the
+ corresponding GTK+ graphical user interface. </para>
+
+ </refsect1>
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <para>These programs follow the usual &gnu; command line syntax, with long
+ options starting with two dashes (`-'). A summary of options is included
+ below. For a complete description, see the HTML documentation. </para>
+
+ <variablelist>
+ <varlistentry>
+ <term>
+ <option>-g</option>,
+ <option>--gps</option>
+ <replaceable>file.gpx</replaceable>
+ </term>
+ <listitem>
+ <para>correlate using the specified GPX file with GPS data</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-s</option>,
+ <option>--show</option>
+ </term>
+ <listitem>
+ <para>only show the GPS data of the given images</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-o</option>,
+ <option>--machine</option>
+ </term>
+ <listitem>
+ <para>only show the GPS data of the given images in a machine
+ readable output, if there is any</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-r</option>,
+ <option>--remove</option>
+ </term>
+ <listitem>
+ <para>only remove GPS EXIF data from the given images</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-z</option>,
+ <option>--timeadd</option>
+ <userinput>+/-</userinput><replaceable>XX</replaceable>[<userinput>:</userinput><replaceable>XX</replaceable>]
+ </term>
+ <listitem>
+ <para>time to add to GPS data to make it match the timestamps of the
+ images. GPS data is in UTC; images are not likely to be in UTC.
+ Enter the timezone used when taking the images: eg,
+ <userinput>+8</userinput> for Perth </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-O</option>,
+ <option>--photooffset</option>
+ <replaceable>seconds</replaceable>
+ </term>
+ <listitem>
+ <para>time in seconds to add to the photo timestamp to make it match
+ the GPS timestamp. To determine the amount of seconds needed, just
+ create a picture of your GPS device showing the current time and
+ compare it with the timestamp of your photo file. </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-i</option>,
+ <option>--no-interpolation</option>
+ </term>
+ <listitem>
+ <para>disable interpolation between points. Interpolation is linear,
+ points are rounded if disabled </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-v</option>,
+ <option>--verbose</option>
+ </term>
+ <listitem>
+ <para>show which GPS data has been selected for each image</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-d</option>,
+ <option>--datum</option> <replaceable>datum</replaceable>
+ </term>
+ <listitem>
+ <para>specify measurement datum. If not set, WGS-84 used</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-n</option>,
+ <option>--no-write</option>
+ </term>
+ <listitem>
+ <para>do not write the exif data. Useful with
+ <userinput>--verbose</userinput></para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-m</option>,
+ <option>--max-dist</option> <replaceable>time</replaceable>
+ </term>
+ <listitem>
+ <para>max time outside points that image will be matched. Time is in
+ seconds </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-t</option>,
+ <option>--ignore-tracksegs</option>
+ </term>
+ <listitem>
+ <para>Interpolate between track segments, too</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-M</option>,
+ <option>--no-mtime</option>
+ </term>
+ <listitem>
+ <para>Do not change mtime of modified files</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-f</option>,
+ <option>--fix-timestamps</option>
+ </term>
+ <listitem>
+ <para>Fix broken GPS datestamps written with versions &lt; 1.5.2</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-p</option>,
+ <option>--degmins</option>
+ </term>
+ <listitem>
+ <para>Write location as DD MM.MM as was default before &lt; 1.5.3</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-h</option>,
+ <option>--help</option>
+ </term>
+ <listitem>
+ <para>Only show summary of options</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-V</option>,
+ <option>--version</option>
+ </term>
+ <listitem>
+ <para>Only print the version</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </refsect1>
+ <refsect1>
+ <title>SEE ALSO</title>
+
+ <para>gpsd (1), gpsbabel (1), gpxlogger (1), cgpxlogger (1).</para>
+
+ <para>The documentation of &dhpackage; and &dhpackage;-gui in HTML format
+ are available on the filesystem at <filename
+ class="directory">@DOCDIR@</filename>.
+ </para>
+
+ </refsect1>
+ <refsect1>
+ <title>AUTHOR</title>
+
+ <para>This manual page was initially written by &dhusername; &dhemail; for
+ the &debian; system. It was extended by Till Maas
+ <email>opensource@till.name</email>. Permission is granted to copy,
+ distribute and/or modify this document under the terms of the &gnu;
+ General Public License, Version 2 any later version published by the Free
+ Software Foundation. </para>
+
+ <para> On Debian systems, the complete text of the GNU General Public
+ License can be found in /usr/share/common-licenses/GPL. </para>
+
+ </refsect1>
+</refentry>
+
62 doc/gui.html
@@ -0,0 +1,62 @@
+<html><head><title>GPS Correlate Documentation: GUI version</title></head>
+
+<body>
+
+<h1 align="center">GPS Correlate Documentation: GUI version</h1>
+
+<hr />
+
+<h2>The Correlation Window</h2>
+
+<img src="corr.png" alt="The Correlation Window, 89,363 bytes" width="880" height="530">
+
+<h2>How to use the Correlation Window</h2>
+
+<h3>Step 1: Add Photos</h3>
+
+<p>Clicking the "Add Photos..." button will allow you to select photos to add to the list. You can select multiple photos in one go from the open dialog that appears. Once you have chosen the photos that you want, click "Open" to select the photos. They will be scanned for EXIF tags and added to the list. If they already have GPS EXIF tags, the data from those tags will be shown in the window. Otherwise, you will just see the timestamp from the photo, and the status of the photo.</p>
+
+<p>Selecting a photo or multiple photos from the list on the right and clicking "Remove Photos" will remove these from the list.</p>
+
+<h3>Step 2: GPS Data</h3>
+
+<p>Clicking the "Choose..." button will bring up a dialog asking the GPX file to read GPS data from. Once you have selected the file, it will be loaded into memory. While the loading is proceeding, you will see a dialog asking you to wait while the data is loaded. Once loaded, the name of the file that the data is coming from will be shown above the button.</p>
+
+<h3>Step 3: Set options</h3>
+
+<p>The <b>"Interpolate"</b> checkbox defines whether or not to interpolate between GPS points. By default, this is checked. Clearing this box makes the algorithm round points.</p>
+
+<p>The <b>"Don't write"</b> checkbox, if checked, will prevent the GPS EXIF tags being written back to the photo. However, the point that is matched will be shown in the list, which allows you to correlate and check the results first.</p>
+
+<p>The <b>"Don't change mtime"</b> checkbox, if checked, will prevent changes to the JPEG files from updating the files mtime.</p>
+
+<p>The <b>"Between Segments"</b> checkbox, if checked, will ignore track segments, and match photos between track segments. Usually a track segment differentiates between multiple GPS data logging sessions, so interpolating between track segments could well be interpolating when there was no GPS data.</p>
+
+<p>The <b>"Write DD MM SS.SS"</b> checkbox, if un-checked, will force the pre 1.5.3 behaviour of writing Longitude and Lattitude as DD MM.MM values. The default is now to write values in DD MM SS.SS.</p>
+
+<p>The <b>"Max gap time"</b> box specifies the maximum distance from a point that a photo will be matched. In seconds.</p>
+
+<p>The <b>"Time Zone"</b> box specifies the time zone that the photos were taken in, so that the times of the photos can be adjusted to match the GPS data.</p>
+
+<p>The <b>"Photo Offset"</b> box specifies the number of seconds to add to the photos time to match the GPS data. See the <a href="concepts.html">GPS Correlate Concepts</a> documentation to understand this value.</p>
+
+<p>The <b>"GPS Datum"</b> box specifies the Datum of the source GPS data, which is written into the GPS EXIF tags. By default this is "WGS-84", but really should not be changed, as the GPX format is only supposed to store WGS-84 data. However, you can change this if you wish.</p>
+
+<h3>Step 4: Correlate!</h3>
+
+<p>The "Correlate Photos" button will start the correlation proceedure on the selected photos with the loaded GPS data. If no photos are selected or no GPS data is loaded, then an error will be generated. Otherwise, the photos will be correlated and you will see their status change in the list as the process goes on. As photos are matched, their locations are also shown in the photo list.</p>
+
+<h3>Other Tools</h3>
+
+<p>The <b>"Strip GPS tags"</b> button will remove GPS tags from the selected photos.</p>
+
+<h3>Saving Settings</h3>
+
+<p>As of 1.5.4, when you exit the GUI program, it will save your settings to ~/.gpscorrelaterc. These will be reloaded next time you start gpscorrelate. The command line version does not make use of this file.</p>
+
+<hr />
+
+<a href="index.html">Return to the contents</a>
+
+</body></html>
+
35 doc/index.html
@@ -0,0 +1,35 @@
+<html><head><title>GPS Correlate Documentation</title></head>
+
+<body>
+
+<h1 align="center">GPS Correlate Documentation</h1>
+
+<p align="center">Written by Daniel Foote.</p>
+
+<h2>Contents</h2>
+
+<ul>
+<li><a href="concepts.html">GPS Photo Correlation Concepts</a></li>
+<li><a href="command.html">Usage of the command line client</a></li>
+<li><a href="gui.html">Usage of the GUI version</a></li>
+</ul>
+
+<h2>Important Note for users before 1.5.2</h2>
+
+<p>Prior to version 1.5.2, there was a bug in gpscorrelate that caused gpscorrelate to incorrectly parse dates. The result is that the internal date format was one month out (ie, December was considered as the following January, and January was considered as Feburary).</p>
+
+<p>This did not affect the matching of photos, as the GPX data and the EXIF data were passed through the same buggy conversion.</p>
+
+<p>When writing out GPS data, gpscorrelate wrote a GPSTimeStamp and a GPSDateStamp tag, which had the UTC GPS Time and Date of the match. The DateStamp and TimeStamp tags were written incorrectly (from two distinctly seperate bugs), and was around one month ahead of the real time. The original date and time of the photo were not modified.</p>
+
+<p>Version 1.5.2 introduces a new command line option, --fix-datestamps. Running gpscorrelate with this option and then a series of JPEG files that were tagged will detect and correct the problem. This will modify the GPSDateStamp and GPSTimeStamp tag to be correct, but only if this is required. You can run gpscorrelate with the -n (no write) option just to see which files are affected without making any changes. When correcting files, --no-mtime (don't change the mtime of the file) is implied.</p>
+
+<p>When using --fix-datestamps, you will also need to use --timeadd to specify the difference between the photo time and UTC.</p>
+
+<h2>More information</h2>
+
+<p>For more information, later versions, or comments/questions, please visit <a href="http://freefoote.dview.net">http://freefoote.dview.net</a>.</p>
+
+</body>
+</html>
+
784 exif-gps.cpp
@@ -0,0 +1,784 @@
+/* exif-gps.cpp
+ * Written by Daniel Foote.
+ * Started Feb 2005.
+ *
+ * This file contains routines for reading dates
+ * from exif data, and writing GPS data into the
+ * appropriate photos.
+ *
+ * Uses the libexiv2 library.
+ * From http://home.arcor.de/ahuggel/exiv2/
+ */
+
+/* Copyright 2005 Daniel Foote.
+ *
+ * This file is part of gpscorrelate.
+ *
+ * gpscorrelate 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.
+ *
+ * gpscorrelate is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gpscorrelate; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <iostream>
+#include <iomanip>
+#include <cstring>
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+
+#include "exiv2/image.hpp"
+#include "exiv2/exif.hpp"
+
+#include "gpsstructure.h"
+#include "exif-gps.h"
+
+/* Debug
+int main(int argc, char* argv[])
+{
+
+ printf("Starting with write...\n");
+
+ struct GPSPoint Foo;
+
+ Foo.Lat = -41.1234567;
+ Foo.Long = 115.12345;
+ Foo.Elev = 25.12345;
+ Foo.Time = 123456;
+
+ WriteGPSData(argv[1], &Foo, "WGS-84", 0);
+
+ printf("Done write, now reading...\n");
+
+ int GPS = 0;
+ char* Ret = ReadExifDate(argv[1], &GPS);
+ if (Ret)
+ {
+ printf("Date: %s.\n", Ret);
+ } else {
+ printf("Failed!\n");
+ }
+
+ if (GPS)
+ {
+ printf("Includes GPS data!\n");
+ } else {
+ printf("No GPS data!\n");
+ }
+
+};
+*/
+
+char* ReadExifDate(char* File, int* IncludesGPS)
+{
+ // Open and read the file.
+ Exiv2::ExifData ExifRead;
+ Exiv2::Image::AutoPtr Image;
+
+ try {
+ Image = Exiv2::ImageFactory::open(File);
+ } catch (Exiv2::Error e) {
+ fprintf(stderr, "Failed to open file %s.\n", File);
+ return 0;
+ }
+ Image->readMetadata();
+ if (Image.get() == NULL)
+ {
+ //fprintf(stderr, "%s\n",
+ // Exiv2::ExifData::strError(Result,
+ // File).c_str());
+ fprintf(stderr, "Failed to read file %s.\n", File);
+ return 0;
+ }
+
+ ExifRead = Image->exifData();
+
+ // Read the tag out.
+ Exiv2::Exifdatum& Tag = ExifRead["Exif.Photo.DateTimeOriginal"];
+
+ // Check that the tag is not blank.
+ std::string Value = Tag.toString();
+
+ if (Value.length() == 0)
+ {
+ // No date/time stamp.
+ // Not good.
+ // Just return - above us will handle it.
+ return 0;
+ }
+
+ // Copy the tag and return that.
+ char* Copy = (char*)malloc((sizeof(char) * Value.length()) + 1);
+ strcpy(Copy, Value.c_str());
+
+ // Check if we have GPS tags.
+ Exiv2::Exifdatum& GPSData = ExifRead["Exif.GPSInfo.GPSVersionID"];
+
+ Value = GPSData.toString();
+
+ if (Value.length() == 0)
+ {
+ // No GPS data.
+ *IncludesGPS = 0;
+ } else {
+ // Seems to include GPS data...
+ *IncludesGPS = 1;
+ }
+
+ // Now return, passing a pointer to the date string.
+ return Copy; // Its up to the caller to free this.
+};
+
+char* ReadExifData(char* File, double* Lat, double* Long, double* Elev, int* IncludesGPS)
+{
+ // This function varies in that it reads
+ // much more data than the last, specifically
+ // for display purposes. For the GUI version.
+ // Open and read the file.
+ Exiv2::ExifData ExifRead;
+ Exiv2::Image::AutoPtr Image;
+
+ try {
+ Image = Exiv2::ImageFactory::open(File);
+ } catch (Exiv2::Error e) {
+ fprintf(stderr, "Failed to open file %s.\n", File);
+ return 0;
+ }
+ Image->readMetadata();
+ if (Image.get() == NULL)
+ {
+ //fprintf(stderr, "%s\n",
+ // Exiv2::ExifData::strError(Result,
+ // File).c_str());
+ fprintf(stderr, "Unable to open file %s.\n", File);
+ return 0;
+ }
+
+ ExifRead = Image->exifData();
+
+ // Read the tag out.
+ Exiv2::Exifdatum& Tag = ExifRead["Exif.Photo.DateTimeOriginal"];
+
+ // Check that the tag is not blank.
+ std::string Value = Tag.toString();
+
+ if (Value.length() == 0)
+ {
+ // No date/time stamp.
+ // Not good.
+ // Just return - above us will handle it.
+ return 0;
+ }
+
+ // Copy the tag and return that.
+ char* Copy = (char*)malloc((sizeof(char) * Value.length()) + 1);
+ strcpy(Copy, Value.c_str());
+
+ // Check if we have GPS tags.
+ Exiv2::Exifdatum GPSData = ExifRead["Exif.GPSInfo.GPSVersionID"];
+
+ Value = GPSData.toString();
+
+ if (Value.length() == 0)
+ {
+ // No GPS data.
+ // Just return.
+ *IncludesGPS = 0;
+ } else {
+ // Seems to include GPS data...
+ *IncludesGPS = 1;
+ // Read it out and send it up!
+ // What we are trying to do here is convert the
+ // three rationals:
+ // dd/v mm/v ss/v
+ // To a decimal
+ // dd.dddddd...
+ // dd/v is easy: result = dd/v.
+ // mm/v is harder:
+ // mm
+ // -- / 60 = result.
+ // v
+ // ss/v is sorta easy.
+ // ss
+ // -- / 3600 = result
+ // v
+ // Each part is added to the final number.
+ Exiv2::Rational RatNum;
+
+ GPSData = ExifRead["Exif.GPSInfo.GPSLatitude"];
+ if (GPSData.count() < 3)
+ *Lat = nan("invalid");
+ else {
+ RatNum = GPSData.toRational(0);
+ *Lat = (double)RatNum.first / (double)RatNum.second;
+ RatNum = GPSData.toRational(1);
+ *Lat = *Lat + (((double)RatNum.first / (double)RatNum.second) / 60);
+ RatNum = GPSData.toRational(2);
+ *Lat = *Lat + (((double)RatNum.first / (double)RatNum.second) / 3600);
+
+ GPSData = ExifRead["Exif.GPSInfo.GPSLatitudeRef"];
+ if (strcmp(GPSData.toString().c_str(), "S") == 0)
+ {
+ // Negate the value - Western Hemisphere.
+ *Lat = -*Lat;
+ }
+ }
+
+ GPSData = ExifRead["Exif.GPSInfo.GPSLongitude"];
+ if (GPSData.count() < 3)
+ *Long = nan("invalid");
+ else {
+ RatNum = GPSData.toRational(0);
+ *Long = (double)RatNum.first / (double)RatNum.second;
+ RatNum = GPSData.toRational(1);
+ *Long = *Long + (((double)RatNum.first / (double)RatNum.second) / 60);
+ RatNum = GPSData.toRational(2);
+ *Long = *Long + (((double)RatNum.first / (double)RatNum.second) / 3600);
+
+ GPSData = ExifRead["Exif.GPSInfo.GPSLongitudeRef"];
+ if (strcmp(GPSData.toString().c_str(), "W") == 0)
+ {
+ // Negate the value - Western Hemisphere.
+ *Long = -*Long;
+ }
+ }
+
+ // Finally, read elevation out. This one is simple.
+ GPSData = ExifRead["Exif.GPSInfo.GPSAltitude"];
+ if (GPSData.count() < 1)
+ *Elev = nan("invalid");
+ else {
+ RatNum = GPSData.toRational(0);
+ *Elev = (double)RatNum.first / (double)RatNum.second;
+ }
+
+ // Is the altitude below sea level? If so, negate the value.
+ GPSData = ExifRead["Exif.GPSInfo.GPSAltitudeRef"];
+ if (GPSData.count() >= 1 && GPSData.toLong() == 1)
+ {
+ // Negate the elevation.
+ *Elev = -*Elev;
+ }
+ }
+
+
+ // Now return, passing a pointer to the date string.
+ return Copy; // Its up to the caller to free this.
+};
+
+// This function is for the --fix-datestamp option.
+// DateStamp and TimeStamp should be 12-char strings.
+char* ReadGPSTimestamp(char* File, char* DateStamp, char* TimeStamp, int* IncludesGPS)
+{
+ // This function varies in that it reads
+ // much more data than the last, specifically
+ // for display purposes. For the GUI version.
+ // Open and read the file.
+ Exiv2::ExifData ExifRead;
+ Exiv2::Image::AutoPtr Image;
+
+ try {
+ Image = Exiv2::ImageFactory::open(File);
+ } catch (Exiv2::Error e) {
+ fprintf(stderr, "Failed to open file %s.\n", File);
+ return 0;
+ }
+ Image->readMetadata();
+ if (Image.get() == NULL)
+ {
+ //fprintf(stderr, "%s\n",
+ // Exiv2::ExifData::strError(Result,
+ // File).c_str());
+ fprintf(stderr, "Unable to open file %s.\n", File);
+ return 0;
+ }
+
+ ExifRead = Image->exifData();
+
+ // Read the tag out.
+ Exiv2::Exifdatum& Tag = ExifRead["Exif.Photo.DateTimeOriginal"];
+
+ // Check that the tag is not blank.
+ std::string Value = Tag.toString();
+
+ if (Value.length() == 0)
+ {
+ // No date/time stamp.
+ // Not good.
+ // Just return - above us will handle it.
+ return 0;
+ }
+
+ // Copy the tag and return that.
+ char* Copy = (char*)malloc((sizeof(char) * Value.length()) + 1);
+ strcpy(Copy, Value.c_str());
+
+ // Check if we have GPS tags.
+ Exiv2::Exifdatum& GPSData = ExifRead["Exif.GPSInfo.GPSVersionID"];
+
+ Value = GPSData.toString();
+
+ if (Value.length() == 0)
+ {
+ // No GPS data.
+ // Just return.
+ *IncludesGPS = 0;
+ } else {
+ // Seems to include GPS data...
+ *IncludesGPS = 1;
+
+ Exiv2::Rational RatNum1;
+ Exiv2::Rational RatNum2;
+ Exiv2::Rational RatNum3;
+
+ // Read out the Time and Date stamp, for correction.
+ GPSData = ExifRead["Exif.GPSInfo.GPSTimeStamp"];
+ if (GPSData.count() < 3) {
+ *IncludesGPS = 0;
+ return Copy;
+ }
+ RatNum1 = GPSData.toRational(0);
+ RatNum2 = GPSData.toRational(1);
+ RatNum3 = GPSData.toRational(2);
+ snprintf(TimeStamp, 12, "%02d:%02d:%02d",
+ RatNum1.first, RatNum2.first, RatNum3.first);
+
+ GPSData = ExifRead["Exif.GPSInfo.GPSDateStamp"];
+ if (GPSData.count() < 3) {
+ *IncludesGPS = 0;
+ return Copy;
+ }
+ RatNum1 = GPSData.toRational(0);
+ RatNum2 = GPSData.toRational(1);
+ RatNum3 = GPSData.toRational(2);
+ snprintf(DateStamp, 12, "%04d:%02d:%02d",
+ RatNum1.first, RatNum2.first, RatNum3.first);
+ }
+
+ return Copy;
+};
+
+void ConvertToRational(double Number,
+ long int* Numerator, long int* Denominator,
+ int Rounding)
+{
+ // This function converts the given decimal number
+ // to a rational (fractional) number.
+ //
+ // Examples in comments use Number as 25.12345, Rounding as 4.
+
+ // Split up the number.
+ double Whole = trunc(Number);
+ double Fractional = Number - Whole;
+
+ // Calculate the "number" used for rounding.
+ // This is 10^Digits - ie, 4 places gives us 10000.
+ double Rounder = pow(10, Rounding);
+
+ // Round the fractional part, and leave the number
+ // as greater than 1.
+ // To do this we: (for example)
+ // 0.12345 * 10000 = 1234.5
+ // floor(1234.5) = 1234 - now bigger than 1 - ready...
+ Fractional = trunc(Fractional * Rounder);
+
+ // Convert the whole thing to a fraction.
+ // Fraction is:
+ // (25 * 10000) + 1234 251234
+ // ------------------- = ------ = 25.1234
+ // 10000 10000
+ double NumTemp = (Whole * Rounder) + Fractional;
+ double DenTemp = Rounder;
+
+ // Now we should reduce until we can reduce no more.
+
+ // Try simple reduction...
+ // if Num
+ // ----- = integer out then....
+ // Den
+ if (trunc(NumTemp / DenTemp) == (NumTemp / DenTemp))
+ {
+ // Divide both by Denominator.
+ NumTemp /= DenTemp;
+ DenTemp /= DenTemp;
+ }
+
+ // And, if that fails, brute force it.
+ while (1)
+ {
+ // Jump out if we can't integer divide one.
+ if ((NumTemp / 2) != trunc(NumTemp / 2)) break;
+ if ((DenTemp / 2) != trunc(DenTemp / 2)) break;
+ // Otherwise, divide away.
+ NumTemp /= 2;
+ DenTemp /= 2;
+ }
+
+ // Copy out the numbers.
+ *Numerator = (int)NumTemp;
+ *Denominator = (int)DenTemp;
+
+ // And finished...
+
+}
+
+int WriteGPSData(char* File, struct GPSPoint* Point, char* Datum, int NoChangeMtime, int DegMinSecs)
+{
+ // Write the GPS data to the file...
+
+ struct stat statbuf;
+ struct stat statbuf2;
+ struct utimbuf utb;
+ if (NoChangeMtime)
+ stat(File, &statbuf);
+ Exiv2::Image::AutoPtr Image;
+
+ try {
+ Image = Exiv2::ImageFactory::open(File);
+ } catch (Exiv2::Error e) {
+ fprintf(stderr, "Failed to open file %s.\n", File);
+ return 0;
+ }
+ Image->readMetadata();
+ if (Image.get() == NULL)
+ {
+ // It failed if we got here.
+ //fprintf(stderr, "%s\n",
+ // Exiv2::ExifData::strError(Result, File).c_str());
+ fprintf(stderr, "Failed to open file %s.\n", File);
+ return 0;
+ }
+
+ Exiv2::ExifData &ExifToWrite = Image->exifData();
+
+ char ScratchBuf[100];
+ long int Nom, Denom;
+ long int Deg, Min, Sec;
+ double FracPart;
+
+ // Do all the easy constant ones first.
+ // GPSVersionID tag: standard says is should be four bytes: 02 00 00 00
+ // (and, must be present).
+ Exiv2::Value::AutoPtr Value = Exiv2::Value::create(Exiv2::unsignedByte);
+ Value->read("2 0 0 0");
+ ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSVersionID"), Value.get());
+ // Datum: the datum of the measured data. If not given, we insert WGS-84.
+ ExifToWrite["Exif.GPSInfo.GPSMapDatum"] = Datum;
+
+ // Now start adding data.
+ // ALTITUDE.
+ // Altitude reference: byte "00" meaning "sea level".
+ // Or "01" if the altitude value is negative.
+ Value = Exiv2::Value::create(Exiv2::unsignedByte);
+ if (Point->Elev > 0)
+ {
+ Value->read("0");
+ } else {
+ Value->read("1");
+ }
+ ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitudeRef"), Value.get());
+ // And the actual altitude.
+ Value = Exiv2::Value::create(Exiv2::unsignedRational);
+ ConvertToRational(fabs(Point->Elev), &Nom, &Denom, 4);
+ snprintf(ScratchBuf, 100, "%ld/%ld", Nom, Denom);
+
+ /* printf("Altitude: %f -> %s\n", Point->Elev, ScratchBuf); */
+ Value->read(ScratchBuf);
+ ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSAltitude"), Value.get());
+
+ // LATTITUDE
+ // Latitude reference: "N" or "S".
+ if (Point->Lat < 0)
+ {
+ // Less than Zero: ie, minus: means
+ // Southern hemisphere. Where I live.
+ ExifToWrite["Exif.GPSInfo.GPSLatitudeRef"] = "S";
+ } else {
+ // More than Zero: ie, plus: means
+ // Northern hemisphere.
+ ExifToWrite["Exif.GPSInfo.GPSLatitudeRef"] = "N";
+ }
+ // Now the actual lattitude itself.
+ // The original comment read:
+ // This is done as three rationals.
+ // I choose to do it as:
+ // dd/1 - degrees.
+ // mmmm/100 - minutes
+ // 0/1 - seconds
+ // Exif standard says you can do it with minutes
+ // as mm/1 and then seconds as ss/1, but its
+ // (slightly) more accurate to do it as
+ // mmmm/100 than to split it.
+ // We also absolute the value (with fabs())
+ // as the sign is encoded in LatRef.
+ // Further note: original code did not translate between
+ // dd.dddddd to dd mm.mm - that's why we now multiply
+ // by 6000 - x60 to get minutes, x100 to get to mmmm/100.
+ //
+ // Rereading the EXIF standard, it's quite ok to do DD MM SS.SS
+ // Which is much more accurate. This is the new default, unless otherwise
+ // set.
+ Value = Exiv2::Value::create(Exiv2::unsignedRational);
+
+ if (DegMinSecs)
+ {
+ Deg = (int)floor(fabs(Point->Lat)); // Slice off after decimal.
+ Min = (int)floor((fabs(Point->Lat) - floor(fabs(Point->Lat))) * 60); // Now grab just the minutes.
+ FracPart = ((fabs(Point->Lat) - floor(fabs(Point->Lat))) * 60) - (double)Min; // Grab the fractional minute.
+ Sec = (int)floor(FracPart * 6000); // Convert to seconds.
+
+ /* printf("New style lattitude: %f -> %ld/%ld/ %ld/100\n", Point->Lat, Deg, Min, Sec); */
+
+ snprintf(ScratchBuf, 100, "%ld/1 %ld/1 %ld/100", Deg, Min, Sec);
+ } else {
+ Deg = (int)floor(fabs(Point->Lat)); // Slice off after decimal.
+ Min = (int)floor((fabs(Point->Lat) - floor(fabs(Point->Lat))) * 6000);
+ snprintf(ScratchBuf, 100, "%ld/1 %ld/100 0/1", Deg, Min);
+ }
+ Value->read(ScratchBuf);
+ ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSLatitude"), Value.get());
+
+ // LONGITUDE
+ // Longitude reference: "E" or "W".
+ if (Point->Long < 0)
+ {
+ // Less than Zero: ie, minus: means
+ // Western hemisphere.
+ ExifToWrite["Exif.GPSInfo.GPSLongitudeRef"] = "W";
+ } else {
+ // More than Zero: ie, plus: means
+ // Eastern hemisphere. Where I live.
+ ExifToWrite["Exif.GPSInfo.GPSLongitudeRef"] = "E";
+ }
+ // Now the actual longitude itself.
+ // This is done as three rationals.
+ // I choose to do it as:
+ // dd/1 - degrees.
+ // mmmm/100 - minutes
+ // 0/1 - seconds
+ // Exif standard says you can do it with minutes
+ // as mm/1 and then seconds as ss/1, but its
+ // (slightly) more accurate to do it as
+ // mmmm/100 than to split it.
+ // We also absolute the value (with fabs())
+ // as the sign is encoded in LongRef.
+ // Further note: original code did not translate between
+ // dd.dddddd to dd mm.mm - that's why we now multiply
+ // by 6000 - x60 to get minutes, x100 to get to mmmm/100.
+ Value = Exiv2::Value::create(Exiv2::unsignedRational);
+
+ if (DegMinSecs)
+ {
+ Deg = (int)floor(fabs(Point->Long)); // Slice off after decimal.
+ Min = (int)floor((fabs(Point->Long) - floor(fabs(Point->Long))) * 60); // Now grab just the minutes.
+ FracPart = ((fabs(Point->Long) - floor(fabs(Point->Long))) * 60) - (double)Min; // Grab the fractional minute.
+ Sec = (int)floor(FracPart * 6000); // Convert to seconds.
+
+ /* printf("New style longitude: %f -> %ld/%ld/ %ld/100\n", Point->Long, Deg, Min, Sec); */
+
+ snprintf(ScratchBuf, 100, "%ld/1 %ld/1 %ld/100", Deg, Min, Sec);
+ } else {
+ Deg = (int)floor(fabs(Point->Long)); // Slice off after decimal.
+ Min = (int)floor((fabs(Point->Long) - floor(fabs(Point->Long))) * 6000);
+ snprintf(ScratchBuf, 100, "%ld/1 %ld/100 0/1", Deg, Min);
+ }
+ Value->read(ScratchBuf);
+ ExifToWrite.add(Exiv2::ExifKey("Exif.GPSInfo.GPSLongitude"), Value.get());
+
+ // The timestamp.
+ // Make up the timestamp...
+ // The timestamp is taken as the UTC time of the photo.
+ // If interpolation occured, then this time is