* /branches/gpst.gpxfix/
[gpstools.git] / gpst-pic
blob10f873f9d008698907d9a2fcdcbaf9758e639bba
1 #!/usr/bin/perl -w
3 #=======================================================================
4 # $Id$
5 # Extract EXIF data from pictures for use with COPY in Postgres
7 # Character set: UTF-8
8 # ©opyleft 2008– Øyvind A. Holm <sunny@sunbase.org>
9 # License: GNU General Public License version 2 or later, see end of
10 # file for legal stuff.
11 #=======================================================================
13 BEGIN {
14 our @version_array;
17 use strict;
18 use Getopt::Long;
20 BEGIN {
21 push(@INC, "$ENV{'HOME'}/bin/src/gpstools");
22 our @version_array;
25 use GPST;
26 use GPSTxml;
28 $| = 1;
30 our $Debug = 0;
31 our $NA = '\N';
32 our %Std = (
34 'output-format' => 'pgtab',
35 'timezone' => '',
38 our %Opt = (
40 'author' => '',
41 'debug' => 0,
42 'description' => '',
43 'help' => 0,
44 'output-format' => $Std{'output-format'},
45 'strip-whitespace' => 0,
46 'timezone' => $Std{'timezone'},
47 'verbose' => 0,
48 'version' => 0,
52 our $progname = $0;
53 $progname =~ s/^.*\/(.*?)$/$1/;
55 my $rcs_id = '$Id$';
56 my $id_date = $rcs_id;
57 $id_date =~ s/^.*?\d+ (\d\d\d\d-.*?\d\d:\d\d:\d\d\S+).*/$1/;
59 push(@main::version_array, $rcs_id);
61 Getopt::Long::Configure("bundling");
62 GetOptions(
64 "author|a=s" => \$Opt{'author'},
65 "debug" => \$Opt{'debug'},
66 "description|d=s" => \$Opt{'description'},
67 "help|h" => \$Opt{'help'},
68 "output-format|o=s" => \$Opt{'output-format'},
69 "strip-whitespace|w" => \$Opt{'strip-whitespace'},
70 "timezone|T=s" => \$Opt{'timezone'},
71 "verbose|v+" => \$Opt{'verbose'},
72 "version" => \$Opt{'version'},
74 ) || die("$progname: Option error. Use -h for help.\n");
76 $Opt{'debug'} && ($Debug = 1);
77 $Opt{'help'} && usage(0);
78 if ($Opt{'version'}) {
79 print_version();
80 exit(0);
83 $GPST::Spc = $Opt{'strip-whitespace'} ? "" : " ";
84 my $Spc = $GPST::Spc; # FIXME
85 my $tz_str = "";
86 if (length($Opt{'timezone'})) {
87 if ($Opt{'timezone'} =~ /^[\+\-][0-2][0-9]{3}$/) {
88 $tz_str = $Opt{'timezone'};
89 } elsif ($Opt{'timezone'} =~ /^z$/i) {
90 $tz_str = $Opt{'timezone'};
91 } elsif ($Opt{'timezone'} =~ /^[a-z]+$/i) {
92 $tz_str = " $Opt{'timezone'}";
93 } else {
94 die("$progname: $Opt{'timezone'}: Invalid time zone\n");
98 if ($Opt{'output-format'} eq "xml") {
99 print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<gpstpic>\n");
102 if ($#ARGV < 0) {
103 while (<>) {
104 chomp();
105 print_entry($_);
107 } else {
108 for my $fname (@ARGV) {
109 print_entry($fname);
113 if ($Opt{'output-format'} eq "xml") {
114 print("</gpstpic>\n");
117 sub print_entry {
118 # {{{
119 my $filename = shift;
120 my $Retval = 0;
121 my ($date, $coor) =
122 ( '', '');
123 my @Dates = ();
124 local *PicFP;
125 D("filename = '$filename'");
126 if (open(PicFP, "exifprobe -L \"$filename\" |")) { # FIXME: Quick & Dirty™
127 while (<PicFP>) {
128 if (/DateTime/) {
129 s/^.*'(.*?)'.*$/$1/;
130 chomp($date = $_);
131 $date =~ s/^(\d\d\d\d)(.)(\d\d)(.)(\d\d)(.)(\d\d:\d\d:\d\d)(.*)/$1-$3-${5}T$7$8/;
132 D("date = '$date'");
133 push(@Dates, $date);
136 close(PicFP);
137 @Dates = reverse sort @Dates;
138 $date = $Dates[0];
139 defined($date) || ($date = '');
140 D("final date = '$date'");
141 if ($date =~ /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d$/) {
142 $filename =~ s/^.*\/(.*?)$/$1/;
143 my $Output = "";
144 if ($Opt{'output-format'} eq "xml") {
145 if (length("$filename$date")) {
146 $Output = join("",
147 "$Spc$Spc<img>\n",
148 length($filename)
149 ? sprintf("$Spc$Spc$Spc$Spc<filename>%s</filename>\n",
150 txt_to_xml($filename))
151 : "",
152 length($date)
153 ? sprintf("$Spc$Spc$Spc$Spc<date>%s</date>\n",
154 txt_to_xml($date))
155 : "",
156 "$Spc$Spc</img>\n",
159 } elsif ($Opt{'output-format'} eq "pgtab") {
160 # Version information {{{
161 # Without version field (same as version 1):
162 # date \t
163 # "(lat,lon)"-coordinates \t
164 # description \t
165 # filename \t
166 # author \t
167 # Version 1:
168 # "1" \t
169 # date \t
170 # "(lat,lon)"-coordinates \t
171 # description \t
172 # filename \t
173 # author \n
174 # }}}
175 $Output = pgtab_entry(
176 1, # Version number
177 $date,
178 $coor,
179 $Opt{'description'},
180 $filename,
181 $Opt{'author'}
183 } else {
184 die("$progname: $Opt{'output-format'}: Unknown output format\n");
186 print($Output);
187 $Opt{'verbose'} && print(STDERR $Output);
188 } else {
189 if (length($date)) {
190 warn("$filename: $date: Invalid date format");
191 $Retval = 2;
194 } else {
195 warn("$filename: Cannot open exifprobe(1) pipe: $!");
196 $Retval = 1;
198 return($Retval);
199 # }}}
200 } # print_entry()
202 sub pgtab_entry {
203 # {{{
204 my ($Version, $Date, $Coor, $Descr, $Filename, $Author) = @_;
205 defined($Date) || ($Date = $NA);
206 defined($Coor) || ($Coor = $NA);
207 defined($Descr) || ($Descr = $NA);
208 defined($Filename) || ($Filename = $NA);
209 defined($Author) || ($Author = $NA);
210 my $Retval = "";
211 if ($Version == 1) {
212 $Retval = join("\t",
213 1, # Version number
214 postgresql_copy_safe($Date) . $tz_str,
215 length($Coor)
216 ? postgresql_copy_safe($Coor)
217 : $NA,
218 length($Opt{'description'})
219 ? postgresql_copy_safe($Opt{'description'})
220 : $NA,
221 length($Filename)
222 ? postgresql_copy_safe($Filename)
223 : $NA,
224 length($Opt{'author'})
225 ? postgresql_copy_safe($Opt{'author'})
226 : $NA
227 ) . "\n";
229 return($Retval);
230 # }}}
231 } # pgtab_entry()
233 sub print_version {
234 # Print program version {{{
235 for (@main::version_array) {
236 print("$_\n");
238 # }}}
239 } # print_version()
241 sub usage {
242 # Send the help message to stdout {{{
243 my $Retval = shift;
245 if ($Opt{'verbose'}) {
246 print("\n");
247 print_version();
249 print(<<END);
251 Usage: $progname [options] [file [files [...]]]
253 Extract EXIF info from pictures for use with PostgreSQL's COPY command.
254 If no filenames are specified on the command line, file names are read
255 from stdin.
257 Options:
259 -a, --author x
260 Specify author of picture.
261 -d, --description x
262 Specify description for picture.
263 -h, --help
264 Show this help.
265 -v, --verbose
266 Increase level of verbosity. Can be repeated.
267 -o x, --output-format x
268 Create output of type x:
270 pgtab
271 Default: "$Std{'output-format'}".
272 -T X, --timezone X
273 Prepend X as timezone to the date. Valid formats:
274 UTC offset
275 A '+' or '-' followed by a four-digit number (HHMM) which
276 indicates the offset relative to UTC. Examples:
277 +0000
278 -1600
279 +0630
280 Time zone abbreviation. Examples:
284 -w, --strip-whitespace
285 Strip all unnecessary whitespace.
286 --version
287 Print version information.
288 --debug
289 Print debugging messages.
292 exit($Retval);
293 # }}}
294 } # usage()
296 sub msg {
297 # Print a status message to stderr based on verbosity level {{{
298 my ($verbose_level, $Txt) = @_;
300 if ($Opt{'verbose'} >= $verbose_level) {
301 print(STDERR "$progname: $Txt\n");
303 # }}}
304 } # msg()
306 sub D {
307 # Print a debugging message {{{
308 $Debug || return;
309 my @call_info = caller;
310 chomp(my $Txt = shift);
311 my $File = $call_info[1];
312 $File =~ s#\\#/#g;
313 $File =~ s#^.*/(.*?)$#$1#;
314 print(STDERR "$File:$call_info[2] $$ $Txt\n");
315 return("");
316 # }}}
317 } # D()
319 __END__
321 # Plain Old Documentation (POD) {{{
323 =pod
325 =head1 NAME
329 =head1 REVISION
331 $Id$
333 =head1 SYNOPSIS
335 [options] [file [files [...]]]
337 =head1 DESCRIPTION
341 =head1 OPTIONS
343 =over 4
345 =item B<-h>, B<--help>
347 Print a brief help summary.
349 =item B<-v>, B<--verbose>
351 Increase level of verbosity. Can be repeated.
353 =item B<--version>
355 Print version information.
357 =item B<--debug>
359 Print debugging messages.
361 =back
363 =head1 BUGS
367 =head1 AUTHOR
369 Made by Øyvind A. Holm S<E<lt>sunny@sunbase.orgE<gt>>.
371 =head1 COPYRIGHT
373 Copyleft © Øyvind A. Holm E<lt>sunny@sunbase.orgE<gt>
374 This is free software; see the file F<COPYING> for legalese stuff.
376 =head1 LICENCE
378 This program is free software; you can redistribute it and/or modify it
379 under the terms of the GNU General Public License as published by the
380 Free Software Foundation; either version 2 of the License, or (at your
381 option) any later version.
383 This program is distributed in the hope that it will be useful, but
384 WITHOUT ANY WARRANTY; without even the implied warranty of
385 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
386 See the GNU General Public License for more details.
388 You should have received a copy of the GNU General Public License along
389 with this program; if not, write to the Free Software Foundation, Inc.,
390 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
392 =head1 SEE ALSO
394 =cut
396 # }}}
398 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :
399 # End of file $Id$