#!/usr/bin/perl -w # Multi output jpeg tagger and georeferencer # by Marc MERLIN / v1.0 2008/07/14 # many tweaks to support per file/directory GPS mappings / v1.1 2008/12/15 # This script does require a little editting to suit your particular setup, # starting with the EDITME block below. # # This script has 3 functions depending on what name it has (use symlinks): # pictprocess (or addsize) # -> reads an HTML file, and edits IMG SRC urls to add picture sizes and # links to a georeferenced map that you created with # http://www.gpsvisualizer.com/map # and for which you modified the output to add some PHP that receives the # url encoded point and adds a GV_Marker entry for it. # addsize does the same, but only touches picture links that do not already # include gps info # gen_gmaps_gv -> pictprocess # -> This takes a bunch of jpegs and generates javascript code suitable for # pasting as georeferenced photo markers in a map created with gpsvizualiser # gen_rig_gps_mapping -> pictprocess # -> This call generates a .rig_mapping file with pointers from filenames # to text to display and georeferenced link to add to a picture in Rig # http://rig.powerpulsar.com/ | http://sourceforge.net/projects/rig-thumbnail/ # with the georeference patch # # # This script can also be used as a library like so # require "/usr/local/bin/pictprocess"; # my $title="test"; # print printsub("","/blogimg/thumb1024_349_Day2_Natureworld.jpg", "\"$title\"", /Pix/Outings/Misc/ # Library should only contain the top level RIG /Pix or /PrivPix since rig-cache # gets inserted in there later # Had: /var/local/www/Pix/albums/Flying/Flights|20101015_PAO_WLW_PAO # Had: /home/merlin/public_html/perso/halloween/Pix/albums|20101029_Google #warn "Had: $library|$album\n"; $library =~ s#.*/(perso/[^/]*/Pix)/albums#$1# or $library =~ s#.*Pix/albums#Pix#; ($lib_dir = $library) =~ s#(.*Pix)/?##; # Got: Pix/Flying/Flights|Flying/Flights|Pix # Got: perso/halloween/Pix||perso/halloween/Pix #warn "Got: $library|$lib_dir|$1\n"; $library = "/$1"; # /var/local/www/Pix/albums/Flying/Flights/20101015_PAO_WLW_PAO -> /Pix + Flying/Flights + 20101015_PAO_WLW_PAO warn("$pwd -> $library + $lib_dir + $album\n"); $default_albumname = "Gmap"; } } guess_gmap_cfg(); # on my setup, each photo album gets its own JS google map php file so that it # shows the gps track, but if you just want to put the photo on a map, you # could share the same php file sub gpscgi() { $_ = ($default_albumname or $albumname or $album); return "/perso/gps/gmap/$_.php" }; my $gpscgi = gpscgi(); # MUST HAVE trailing / sub photodir() { return "$library/$lib_dir/$album/" }; my $photodir = photodir(); # Where thumbnails are located in case we run in georeferenced link list # generation mode sub tndir() { "$library/rig-cache/$lib_dir/$album/prev1024_" }; my $tndir = tndir(); my $skip_gps = 0; ### STATIC GLOBALS -- END ### sub gen_gmaps_rig_gps_mapping($$); sub html_pict_process(); # mode #2 is to generate photo waypoints in javascript (née livescript) # and the output file is if ($0 =~ /gen_gmaps_gv$/) { # In gv JS generation mode, where the JS code is output $gv_output_file = "gvoutput"; gen_gmaps_rig_gps_mapping("gv", $gv_output_file); exit; } # mode #3 generates a text file that maps picture names to URLs. Those # files are used by RIG to generate links from the picture album to elsif ($0 =~ /gen_rig_gps_mapping$/) { $rig_mapping_file = ".rig_mapping"; gen_gmaps_rig_gps_mapping("rig", $rig_mapping_file); exit; } elsif ($0 =~ /addsize$/ or $0 =~ /pictprocess$/) { if ($0 =~ /addsize$/) { # if we only add size, we skip pictures that have GPS info $skip_gps = 1; } $backup=0; if (@ARGV and $ARGV[0] eq "-b") { shift @ARGV; $backup=1; } html_pict_process(); exit; } # sub size_and_gps { my ($fullfile) = @_; my ($resline, $posline); my ($sizex, $sizey, $lat, $lon) = (0, 0, "", ""); #print STDERR "Checking '$fullfile'\n"; open(JHEAD, '-|', 'jhead', $fullfile) or die "$!"; my @lines = ; close(JHEAD); #print STDERR "Return from jhead $fullfile is ".join("", @lines); #print STDERR join("",grep(/^GPS L/, @lines)); $resline = join("",grep(/^Resolution/, @lines)) or die "Can't read resolution for $fullfile"; $resline =~ m#^Resolution\s+:\s+(\d+) x (\d+)# or die "Can't match resolution for $fullfile"; #print STDERR "got resline $resline\n"; ($sizex, $sizey) = ($1, $2); # Spec. Instr. : Lat 17.7650480, Lon -88.6527690 - Bearing: 0 - Altitude: 27m if (@_ = grep(/^Spec. ?Instr./, @lines)) { $posline = $_[0]; $posline =~ m#Spec. ?Instr.\s+:\s+Lat (\S+), Lon (\S+)# or die "Can't match gps data for $fullfile / $posline"; ($lat, $lon) = ($1, $2); } # GPS Latitude : N 17d 45m 54.173s # GPS Longitude: W 88d 39m 9.9684s elsif ($posline = join("",grep(/^GPS L.*: [NSEW]/, @lines))) { $posline =~ m#Latitude : (N|S) +(\d+)d\s+(\S+)m\s+(\S+)s# or die "Can't match latitude gps data for $fullfile / $posline"; my ($lats, $lat1, $lat2, $lat3) = ($1, $2, $3, $4); $posline =~ m#Longitude: (W|E) +(\d+)d\s+(\S+)m\s+(\S+)s# or die "Can't match longitude gps data for $fullfile / $posline"; my ($lons, $lon1, $lon2, $lon3) = ($1, $2, $3, $4); $lat = $lat1 + $lat2/60 + $lat3/3600; $lat *= -1 if ($lats eq "S"); $lon = $lon1 + $lon2/60 + $lon3/3600; $lon *= -1 if ($lons eq "W"); } #print "Size: $sizex/$sizey/$lat/$lon\n"; return ($sizex, $sizey, $lat, $lon); } sub printsub { # basedir is where the html file was parsed from for relative URLs # filename is /blogimg/foo.jpg # before is "\"$title\""$closehref"; } $after =~ s/\s+HEIGHT=\d+//i; $after =~ s/\s+WIDTH=\d+//i; # filename can have a path, if it's full, it's based off the HTDOCS # root if it's not a full path, it's relative to $basedir which is # where the html file was read from. $basedir = $HTDOCS if ($filename =~ m#^/#); # Local hack: $basedir = $HTDOCS2 if ($filename =~ m#^/(Pix|PrivPix|gifs)/#); #print "resulting basedir is $basedir\n"; $fullfile = "$basedir$filename"; if (not -f "$fullfile") { warn "PICTPROCESS: FILE '$fullfile' NOT FOUND, CAN'T SCAN FOR SIZE\n"; #system("stat $fullfile"); return "$before$filename$after>"; } # back from the system days #$fullfile =~ s#'#\\'#g; #$fullfile =~ s#"#\\"#g; ($sizex, $sizey, $lat, $lon) = size_and_gps($fullfile); $return = "$before\"$filename\"$after WIDTH=$sizex HEIGHT=$sizey>"; if ($lat) { my $photo = $filename; my $photourl; my $tnurl = uri_escape($filename); # Reset value that could be Gmap.php so that we can set it if # guess_gmap_cfg is called $default_albumname = ""; ($gmapdir = $fullfile) =~ s|(.*?)[^/]+$|$1| if (not $gmapdir); #warn "gmapdir is $gmapdir\n"; # get basefile, can be used for per filename pattern matching in $CFG ($_ = $fullfile) =~ s|.*?([^/]+)$|$1|; $album = "CHANGEME"; if (-f "$gmapdir/$CFG") { eval `cat "$gmapdir/$CFG"` } else { #warn("\n\nCalling guess map\n\n"); guess_gmap_cfg(); } #warn "read config $gmapdir/$CFG $album $library/$lib_dir/$album\n"; # horrible hack that forces perl to pull the variables here so that # they then show up in the function calls below, otherwise you get # Variable "$lib_dir" is not available at (eval 1) line 10. my $z; ($z, $z, $z, $z) = ($library, $lib_dir, $album, $albumname); #warn "gpscgi is: ".gpscgi()."\n"; #warn photodir()."\n"; if ($album eq "CHANGEME") { warn "Cannot find gmap match for filename $_ in $gmapdir/$CFG, skipping georef link"; return $return; } my $photodir = uri_escape(photodir()); my $gpscgi = gpscgi(); # remove leading dir in filename and jpeg extension for a lame picture # name generation $photo =~ s#.*/##; $photo =~ s/thumb\d+_//; $photo =~ s/.jpg//; $photourl = "$photodir$photo.html"; #warn "Now have $photo | $photourl\n"; $return = "".$return.""; } #print "$return\n"; return $return; } sub gen_gmaps_rig_gps_mapping($$) { my ($type, $outfile) = @_; die "$0 list of jpegs to scan\n" unless (@ARGV); open(OUT, ">$outfile") or die "Can't open $outfile: $!"; foreach my $file (@ARGV) { my ($lat, $lon); die "$0 is meant to be run in the directory pictures are in, as in $0 *.jpg\n" if ($file =~ m#/#); ($_, $_, $lat, $lon) = size_and_gps($file); if ($lat) { my $photo = $file; $photo =~ s/.jpg//; my $photourl = "$photodir$photo.html"; my $tnurl = "${tndir}$photo.jpg"; if ($type eq "gv") { # print OUT <\n"; } print "Wrote $photo / $lat / $lon\n"; } else { print "Missing GPS data for $file\n"; } } close(OUT); if ($type eq "gv") { print "All gv data written to $outfile, merge those waypoints to your JS html file\n"; } else { print "All gps mapping data written to $outfile, Rig should now pick this up\n"; } } sub html_pict_process() { die "$0 list of html pages to scan/edit\n" unless (@ARGV); foreach my $file (@ARGV) { my @filedata; my $filedata; my $oldfiledata; # basedir will be used for computing the path to pictures with # relative pathnames if we're working on an html file not in cwd(). my $basedir = "./"; my $outfile; $file =~ s#'#\\'#g; $file =~ s#"#\\"#g; open (HTML, "$file") or die "Can't open $file: $!"; @filedata = ; close (HTML); print "Working on $file\n"; $filedata = join("", @filedata); $oldfiledata = $filedata; if ($file =~ m#/#) { # note that $basedir is based off the path of the html file # we're reading, not the directory the included image is in. ($basedir = $file) =~ s#(.+/)[^/]*#$1#; } #print "Basedir is $basedir\n"; if ($filedata =~ m#gps/gmap# and $skip_gps) { warn "Not touching $file in addsize mode because it contains gps data\n"; next; } # All the IMG/HREF urls gets replaced in the html file that # was slurped entirely in filedata. # support IMG SRC=/foo and IMG SRC="/foo" # both regexes have to run since they have to support both kinds of # urls over an entire file in one pass. Unquoted comes first so that # we can quote links $filedata =~ s#(]+?gps/gmap[^>]+?>)?(]+)([^>]*?)>()?#printsub($basedir,$3,$2,$4,$1,$5)#eisg; $filedata =~ s#(]+?gps/gmap[^>]+?>)?(]*?)>()?#printsub($basedir,$3,$2,$4,$1,$5)#eisg; if ($oldfiledata ne $filedata) { $outfile = "$file.new.$$"; print "Changing $file\n"; open (HTML, ">$outfile") or die "Can't open $outfile: $!"; print HTML $filedata; close (HTML); if ($backup) { rename ($file,"$file.old.$$") or die "Can't rename $file in $file.old.$$: $!"; } else { unlink ($file) or die "Can't unlink $file: $!"; } rename ($outfile,$file) or die "Can't rename $outfile in $file: $!"; } } } 1;