Changeset 19845 in osm for applications/editors/josm


Ignore:
Timestamp:
2010-02-03T11:10:46+01:00 (15 years ago)
Author:
bastik
Message:

'josm plugin photo_geotagging: added mtime handling and GPSTimeStamp writing'

Location:
applications/editors/josm/plugins/photo_geotagging
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/photo_geotagging/build.xml

    r19672 r19845  
    2525
    2626        <!-- enter the SVN commit message -->
    27         <property name="commit.message" value="josm plugin photo_geotagging: update" />
     27        <property name="commit.message" value="josm plugin photo_geotagging: added mtime handling and GPSTimeStamp writing" />
    2828        <!-- enter the *lowest* JOSM version this plugin is currently compatible with -->
    29         <property name="plugin.main.version" value="2904" />
     29        <property name="plugin.main.version" value="2931" />
    3030
    3131
  • applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/ExifGPSTagger.java

    r19671 r19845  
    1 // This is from a file of the sanselan project that is supposed to show, how the library can be used:
    2 // https://svn.apache.org/repos/asf/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/sampleUsage/WriteExifMetadataExample.java
     1// Wrapper class for sanselan library
    32package org.openstreetmap.josm.plugins.photo_geotagging;
    43
     
    65
    76import java.io.BufferedOutputStream;
    8 import java.io.IOException;
    97import java.io.File;
    108import java.io.FileOutputStream;
     9import java.io.IOException;
    1110import java.io.OutputStream;
     11import java.util.Calendar;
     12import java.util.GregorianCalendar;
     13import java.util.TimeZone;
     14import java.text.DecimalFormat;
    1215
    1316import org.apache.sanselan.ImageReadException;
     
    1821import org.apache.sanselan.formats.jpeg.exifRewrite.ExifRewriter;
    1922import org.apache.sanselan.formats.tiff.TiffImageMetadata;
     23import org.apache.sanselan.formats.tiff.constants.TagInfo;
     24import org.apache.sanselan.formats.tiff.constants.TiffConstants;
     25import org.apache.sanselan.formats.tiff.fieldtypes.FieldType;
     26import org.apache.sanselan.formats.tiff.write.TiffOutputDirectory;
     27import org.apache.sanselan.formats.tiff.write.TiffOutputField;
    2028import org.apache.sanselan.formats.tiff.write.TiffOutputSet;
    2129
     
    2432     * Set the GPS values in JPEG EXIF metadata.
    2533     * This is taken from one of the examples of the sanselan project.
    26      *
    27      * @param jpegImageFile
    28      *            A source image file.
    29      * @param dst
    30      *            The output file.
    31      * @throws IOException
    32      * @throws ImageReadException
    33      * @throws ImageWriteException
     34     *
     35     * @param jpegImageFile A source image file.
     36     * @param dst The output file.
     37     * @param lat latitude
     38     * @param lon longitude
     39     * @param gpsTime time in milliseconds
    3440     */
    35     public static void setExifGPSTag(File jpegImageFile, File dst, double lat, double lon) throws IOException {
     41    public static void setExifGPSTag(File jpegImageFile, File dst, double lat, double lon, long gpsTime) throws IOException {
    3642        try {
    37             setExifGPSTagWorker(jpegImageFile, dst, lat, lon);
     43            setExifGPSTagWorker(jpegImageFile, dst, lat, lon, gpsTime);
    3844        } catch (ImageReadException ire) {
    3945            throw new IOException(tr("Read error!"));
     
    4147            throw new IOException(tr("Write error!"));
    4248        }
    43     }       
    44    
    45     public static void setExifGPSTagWorker(File jpegImageFile, File dst, double lat, double lon) throws IOException,
     49    }
     50
     51    public static void setExifGPSTagWorker(File jpegImageFile, File dst, double lat, double lon, long gpsTime) throws IOException,
    4652            ImageReadException, ImageWriteException
    4753    {
    4854        OutputStream os = null;
    49         try
    50         {
     55        try {
    5156            TiffOutputSet outputSet = null;
    5257
    53             // note that metadata might be null if no metadata is found.
    5458            IImageMetadata metadata = Sanselan.getMetadata(jpegImageFile);
    5559            JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
    56             if (null != jpegMetadata)
    57             {
    58                 // note that exif might be null if no Exif metadata is found.
     60            if (null != jpegMetadata) {
    5961                TiffImageMetadata exif = jpegMetadata.getExif();
    6062
    61                 if (null != exif)
    62                 {
    63                     // TiffImageMetadata class is immutable (read-only).
    64                     // TiffOutputSet class represents the Exif data to write.
    65                     //
    66                     // Usually, we want to update existing Exif metadata by
    67                     // changing
    68                     // the values of a few fields, or adding a field.
    69                     // In these cases, it is easiest to use getOutputSet() to
    70                     // start with a "copy" of the fields read from the image.
     63                if (null != exif) {
    7164                    outputSet = exif.getOutputSet();
    7265                }
    7366            }
    7467
    75             // if file does not contain any exif metadata, we create an empty
    76             // set of exif metadata. Otherwise, we keep all of the other
    77             // existing tags.
    78             if (null == outputSet)
     68            if (null == outputSet) {
    7969                outputSet = new TiffOutputSet();
     70            }
    8071
    81             {
    82                 outputSet.setGPSInDegrees(lon, lat);
    83             }
     72            Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
     73
     74            calendar.setTimeInMillis(gpsTime);
     75
     76            final int year =   calendar.get(Calendar.YEAR);
     77            final int month =  calendar.get(Calendar.MONTH);
     78            final int day =    calendar.get(Calendar.DAY_OF_MONTH);
     79            final int hour =   calendar.get(Calendar.HOUR_OF_DAY);
     80            final int minute = calendar.get(Calendar.MINUTE);
     81            final int second = calendar.get(Calendar.SECOND);
     82
     83            DecimalFormat yearFormatter = new DecimalFormat("0000");
     84            DecimalFormat monthFormatter = new DecimalFormat("00");
     85            DecimalFormat dayFormatter = new DecimalFormat("00");
     86
     87            final String yearStr = yearFormatter.format(year);
     88            final String monthStr = monthFormatter.format(month);
     89            final String dayStr = dayFormatter.format(day);
     90            final String dateStamp = yearStr+":"+monthStr+":"+dayStr;
     91            //System.err.println("date: "+dateStamp+"  h/m/s: "+hour+"/"+minute+"/"+second);
     92
     93            Double[] timeStamp = {new Double(hour), new Double(minute), new Double(second)};
     94            TiffOutputField gpsTimeStamp = TiffOutputField.create(
     95                    TiffConstants.GPS_TAG_GPS_TIME_STAMP,
     96                    outputSet.byteOrder, timeStamp);
     97            TiffOutputDirectory exifDirectory = outputSet.getOrCreateGPSDirectory();
     98            // make sure to remove old value if present (this method will
     99            // not fail if the tag does not exist).
     100            exifDirectory.removeField(TiffConstants.GPS_TAG_GPS_TIME_STAMP);
     101            exifDirectory.add(gpsTimeStamp);
     102
     103            TiffOutputField gpsDateStamp = SanselanFixes.create(
     104                    TiffConstants.GPS_TAG_GPS_DATE_STAMP,
     105                    outputSet.byteOrder, dateStamp);
     106            // make sure to remove old value if present (this method will
     107            // not fail if the tag does not exist).
     108            exifDirectory.removeField(TiffConstants.GPS_TAG_GPS_DATE_STAMP);
     109            exifDirectory.add(gpsDateStamp);
     110
     111            SanselanFixes.setGPSInDegrees(outputSet, lon, lat);
    84112
    85113            os = new FileOutputStream(dst);
     
    91119            os.close();
    92120            os = null;
    93         } finally
    94         {
    95             if (os != null)
    96                 try
    97                 {
     121        } finally {
     122            if (os != null) {
     123                try {
    98124                    os.close();
    99                 } catch (IOException e)
    100                 {
    101 
    102                 }
     125                } catch (IOException e) {}
     126            }
    103127        }
    104128    }
  • applications/editors/josm/plugins/photo_geotagging/src/org/openstreetmap/josm/plugins/photo_geotagging/GeotaggingPlugin.java

    r19671 r19845  
    55
    66import java.awt.Component;
    7 import java.awt.Dimension;
    8 import java.awt.GridBagLayout;
    9 import java.awt.Rectangle;
    10 import java.awt.event.ActionEvent;
    11 import java.awt.event.ActionListener;
    127
    13 import java.io.IOException;
    14 import java.io.File;
     8import javax.swing.JMenuItem;
    159
    16 import java.util.ArrayList;
    17 import java.util.List;
    18 
    19 import javax.swing.AbstractListModel;
    20 import javax.swing.JCheckBox;
    21 import javax.swing.JLabel;
    22 import javax.swing.JList;
    23 import javax.swing.JMenuItem;
    24 import javax.swing.JOptionPane;
    25 import javax.swing.JPanel;
    26 import javax.swing.JScrollPane;
    27 import javax.swing.UIManager;
    28 
    29 import java.text.DecimalFormat;
    30 
    31 import org.openstreetmap.josm.Main;
    32 import org.openstreetmap.josm.gui.ExtendedDialog;
    33 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
    3410import org.openstreetmap.josm.gui.layer.Layer;
    3511import org.openstreetmap.josm.gui.layer.geoimage.GeoImageLayer;
    3612import org.openstreetmap.josm.gui.layer.geoimage.ImageEntry;
    37 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    3813import org.openstreetmap.josm.plugins.Plugin;
    3914import org.openstreetmap.josm.plugins.PluginInformation;
    40 import org.openstreetmap.josm.tools.GBC;
    4115import org.openstreetmap.josm.tools.ImageProvider;
    4216
     
    4620 * It extends the core geoimage feature of JOSM by adding a new entry
    4721 * to the right click menu of any image layer.
    48  * 
     22 *
    4923 * The real work (writing lat/lon values to file) is done by the pure Java
    5024 * sanselan library.
    5125 */
    5226public class GeotaggingPlugin extends Plugin {
    53     final static boolean debug = false;
    54     final static String KEEP_BACKUP = "plugins.photo_geotagging.keep_backup";
    55    
    5627    public GeotaggingPlugin(PluginInformation info) {
    5728        super(info);
    5829        GeoImageLayer.registerMenuAddition(new GeotaggingMenuAddition());
    5930    }
    60    
     31
     32    /**
     33     * Adds a menu entry to the right click menu of each geoimage layer.
     34     */
    6135    class GeotaggingMenuAddition implements GeoImageLayer.LayerMenuAddition {
    6236        public Component getComponent(Layer layer) {
    6337            JMenuItem geotaggingItem = new JMenuItem(tr("Write coordinates to image header"), ImageProvider.get("geotagging"));;
    64             geotaggingItem.addActionListener(new GeotagImages((GeoImageLayer) layer));
     38            geotaggingItem.addActionListener(new GeotaggingAction((GeoImageLayer) layer));
     39            geotaggingItem.setEnabled(enabled((GeoImageLayer) layer));
    6540            return geotaggingItem;
    6641        }
    67     }
    68    
    69     class GeotagImages implements ActionListener {
    70         final private GeoImageLayer layer;
    71         public GeotagImages(GeoImageLayer layer) {
    72             this.layer = layer;
    73         }
    74        
    75         public void actionPerformed(ActionEvent arg0) {
    76             final List<ImageEntry> images = new ArrayList<ImageEntry>();
     42
     43        /**
     44         * Check if there is any suitable image.
     45         */
     46        private boolean enabled(GeoImageLayer layer) {
    7747            for (ImageEntry e : layer.getImages()) {
    78                 if (e.getPos() != null) {
    79                     images.add(e);
    80                 }
     48                if (e.getPos() != null && e.getGpsTime() != null)
     49                    return true;
    8150            }
    82 
    83             final JPanel cont = new JPanel(new GridBagLayout());
    84             cont.add(new JLabel(tr("Write position information into the exif header of the following files:")), GBC.eol());
    85            
    86             FileList files = new FileList();
    87             files.setVisibleRowCount(Math.min(files.getModel().getSize(), 10));
    88             final List<String> strs = new ArrayList<String>();
    89             DecimalFormat cDdFormatter = new DecimalFormat("###0.000000");
    90 
    91             for (ImageEntry e : images) {
    92                 strs.add(e.getFile().getAbsolutePath()+" ("+cDdFormatter.format(e.getPos().lat())+","+cDdFormatter.format(e.getPos().lon())+")");
    93             }
    94             files.getFileListModel().setFiles(strs);
    95             JScrollPane scroll = new JScrollPane(files);
    96             scroll.setPreferredSize(new Dimension(300, 250));
    97             cont.add(scroll, GBC.eol().fill(GBC.BOTH));
    98            
    99             final JCheckBox backups = new JCheckBox(tr("keep backup files"), Main.pref.getBoolean(KEEP_BACKUP, true));
    100             cont.add(backups, GBC.eol());
    101            
    102             int result = new ExtendedDialog(
    103                     Main.parent,
    104                     tr("Photo Geotagging Plugin"),
    105                     new String[] {tr("OK"), tr("Cancel")})
    106                 .setButtonIcons(new String[] {"ok.png", "cancel.png"})
    107                 .setContent(cont)
    108                 .setCancelButton(2)
    109                 .setDefaultButton(1)
    110                 .showDialog()
    111                 .getValue();
    112 
    113             if (result != 1)
    114                 return;
    115            
    116             final boolean keep_backup = backups.isSelected();
    117             Main.pref.put(KEEP_BACKUP, keep_backup);
    118 
    119             Main.worker.execute(new GeoTaggingRunnable(images, keep_backup));
     51            return false;
    12052        }
    12153    }
    122 
    123     class GeoTaggingRunnable extends PleaseWaitRunnable {
    124         private boolean cancelled = false;
    125         final private boolean keep_backup;
    126         final List<ImageEntry> images;
    127         private Boolean override_backup = null;
    128 
    129         private File fileFrom;
    130         private File fileTo;
    131         private File fileDelete;
    132        
    133         public GeoTaggingRunnable(List<ImageEntry> images, boolean keep_backup) {
    134             super(tr("Photo Geotagging Plugin"));
    135             this.images = images;
    136             this.keep_backup = keep_backup;
    137         }
    138         @Override
    139         protected void realRun() {
    140             progressMonitor.subTask(tr("Writing position information to image files..."));
    141             progressMonitor.setTicksCount(images.size());
    142        
    143            
    144             for (int i=0; i<images.size(); ++i) {
    145                 if (cancelled) return;
    146 
    147                 ImageEntry e = images.get(i);
    148                 if (debug) {
    149                     System.err.print("i:"+i+" "+e.getFile().getName()+" ");
    150                 }
    151                
    152                 fileFrom = null;
    153                 fileTo = null;
    154                 fileDelete = null;
    155                    
    156                 try {
    157                     chooseFiles(e.getFile());
    158                     if (cancelled) return;
    159                     ExifGPSTagger.setExifGPSTag(fileFrom, fileTo, e.getPos().lat(), e.getPos().lon());
    160                     cleanupFiles();
    161                 } catch (IOException ioe) {
    162                     ioe.printStackTrace();
    163                     JOptionPane.showMessageDialog(Main.parent, tr("Error: ")+ioe.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
    164                     return;
    165                 }
    166                 progressMonitor.worked(1);
    167                 if (debug) {
    168                 System.err.println("");
    169                 }
    170             }
    171         }
    172 
    173         private void chooseFiles(File file) throws IOException {
    174             if (debug) {                   
    175             System.err.println("f: "+file.getAbsolutePath());
    176             }
    177 
    178             if (!keep_backup) {
    179                 chooseFilesNoBackup(file);
    180                 return;
    181             }
    182 
    183             File fileBackup = new File(file.getParentFile(),file.getName()+"_");
    184             if (fileBackup.exists()) {
    185                 if (debug) {
    186                     System.err.println("FILE EXISTS");
    187                 }
    188 
    189                 confirm_override();
    190                 if (cancelled)
    191                     return;
    192                
    193                 if (override_backup) {
    194                     if (!fileBackup.delete())
    195                         throw new IOException(tr("File could not be deleted!"));
    196                 } else {
    197                     chooseFilesNoBackup(file);
    198                     return;
    199                 }
    200             }
    201             if (!file.renameTo(fileBackup))
    202                 throw new IOException(tr("Could not rename file!"));
    203                
    204             fileFrom = fileBackup;
    205             fileTo = file;
    206             fileDelete = null;
    207         }
    208 
    209         private void chooseFilesNoBackup(File file) throws IOException {
    210             File fileTmp;
    211             fileTmp = File.createTempFile("img", ".jpg", file.getParentFile());
    212             if (debug) {
    213                 System.err.println("TMP: "+fileTmp.getAbsolutePath());
    214             }
    215             if (! file.renameTo(fileTmp))
    216                 throw new IOException(tr("Could not rename file!"));
    217                
    218             fileFrom = fileTmp;
    219             fileTo = file;
    220             fileDelete = fileTmp;
    221         }       
    222 
    223         private void confirm_override() {
    224             if (override_backup == null) {
    225                 JLabel l = new JLabel(tr("<html><h3>There are old backup files in the image directory!</h3>"));
    226                 l.setIcon(UIManager.getIcon("OptionPane.warningIcon"));
    227                 int override = new ExtendedDialog(
    228                         Main.parent,
    229                         tr("Override old backup files?"),
    230                         new String[] {tr("Cancel"), tr("Keep old backups and continue"), tr("Override")})
    231                     .setButtonIcons(new String[] {"cancel.png", "ok.png", "dialogs/delete.png"})
    232                     .setContent(l)
    233                     .setCancelButton(1)
    234                     .setDefaultButton(2)
    235                     .showDialog()
    236                     .getValue();
    237                 if (override == 2) {
    238                     override_backup = false;
    239                 } else if (override == 3) {
    240                     override_backup = true;
    241                 } else {
    242                     cancelled = true;
    243                     return;
    244                 }
    245             }
    246         }
    247 
    248         private void cleanupFiles() throws IOException {
    249             if (fileDelete != null) {
    250                 if (!fileDelete.delete())
    251                     throw new IOException(tr("Could not delete temporary file!"));
    252             }
    253         }
    254        
    255         @Override
    256         protected void finish() {
    257         }
    258        
    259         @Override
    260         protected void cancel() {
    261             cancelled = true;
    262         }
    263     }
    264    
    265     static class FileList extends JList {
    266         public FileList() {
    267             super(new FileListModel());
    268         }
    269 
    270         public FileListModel getFileListModel() {
    271             return (FileListModel)getModel();
    272         }
    273     }
    274 
    275     static class FileListModel extends AbstractListModel{
    276         private List<String> files;
    277 
    278         public FileListModel() {
    279             files = new ArrayList<String>();
    280         }
    281 
    282         public FileListModel(List<String> files) {
    283             setFiles(files);
    284         }
    285 
    286         public void setFiles(List<String> files) {
    287             if (files == null) {
    288                 this.files = new ArrayList<String>();
    289             } else {
    290                 this.files = files;
    291             }
    292             fireContentsChanged(this,0,getSize());
    293         }
    294 
    295         public Object getElementAt(int index) {
    296             if (files == null) return null;
    297             return files.get(index);
    298         }
    299 
    300         public int getSize() {
    301             if (files == null) return 0;
    302             return files.size();
    303         }
    304     }   
    30554}
Note: See TracChangeset for help on using the changeset viewer.