001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.plugins.streetside.io.export; 003 004import java.awt.image.BufferedImage; 005import java.io.BufferedOutputStream; 006import java.io.ByteArrayOutputStream; 007import java.io.FileOutputStream; 008import java.io.IOException; 009import java.io.OutputStream; 010import java.util.concurrent.ArrayBlockingQueue; 011 012import javax.imageio.ImageIO; 013 014import org.apache.commons.imaging.ImageReadException; 015import org.apache.commons.imaging.ImageWriteException; 016import org.apache.commons.imaging.Imaging; 017import org.apache.commons.imaging.common.ImageMetadata; 018import org.apache.commons.imaging.common.RationalNumber; 019import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; 020import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter; 021import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; 022import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants; 023import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants; 024import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory; 025import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet; 026import org.apache.log4j.Logger; 027import org.openstreetmap.josm.gui.progress.ProgressMonitor; 028import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; 029import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage; 030import org.openstreetmap.josm.plugins.streetside.StreetsideImage; 031import org.openstreetmap.josm.plugins.streetside.StreetsideImportedImage; 032 033/** 034 * Writes the images from the queue in the file system. 035 * 036 * @author nokutu 037 * @see StreetsideExportManager 038 */ 039public class StreetsideExportWriterThread extends Thread { 040 041 final static Logger logger = Logger.getLogger(StreetsideExportWriterThread.class); 042 043 private final String path; 044 private final ArrayBlockingQueue<BufferedImage> queue; 045 private final ArrayBlockingQueue<StreetsideAbstractImage> queueImages; 046 private final int amount; 047 private final ProgressMonitor monitor; 048 049 /** 050 * Main constructor. 051 * 052 * @param path 053 * Path to write the pictures. 054 * @param queue 055 * Queue of {@link StreetsideAbstractImage} objects. 056 * @param queueImages 057 * Queue of {@link BufferedImage} objects. 058 * @param amount 059 * Amount of images that are going to be exported. 060 * @param monitor 061 * Progress monitor. 062 */ 063 public StreetsideExportWriterThread(String path, 064 ArrayBlockingQueue<BufferedImage> queue, 065 ArrayBlockingQueue<StreetsideAbstractImage> queueImages, int amount, 066 ProgressMonitor monitor) { 067 this.path = path; 068 this.queue = queue; 069 this.queueImages = queueImages; 070 this.amount = amount; 071 this.monitor = monitor; 072 } 073 074 @Override 075 public void run() { 076 monitor.setCustomText("Downloaded 0/" + amount); 077 BufferedImage img; 078 StreetsideAbstractImage mimg; 079 String finalPath = ""; 080 for (int i = 0; i < amount; i++) { 081 try { 082 img = queue.take(); 083 mimg = queueImages.take(); 084 if (path == null && mimg instanceof StreetsideImportedImage) { 085 String path = ((StreetsideImportedImage) mimg).getFile().getPath(); 086 finalPath = path.substring(0, path.lastIndexOf('.')); 087 } else if (mimg instanceof StreetsideImage) { 088 finalPath = path + '/' + ((StreetsideImage) mimg).getId(); 089 } else if (mimg instanceof StreetsideImportedImage) { 090 finalPath = path + '/' + ((StreetsideImportedImage) mimg).getFile().getName(); 091 } 092 093 // Transforms the image into a byte array. 094 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 095 ImageIO.write(img, "jpg", outputStream); 096 byte[] imageBytes = outputStream.toByteArray(); 097 098 // Write EXIF tags 099 TiffOutputSet outputSet = null; 100 TiffOutputDirectory exifDirectory; 101 TiffOutputDirectory gpsDirectory; 102 // If the image is imported, loads the rest of the EXIF data. 103 if (mimg instanceof StreetsideImportedImage) { 104 final ImageMetadata metadata = Imaging 105 .getMetadata(((StreetsideImportedImage) mimg).getFile()); 106 final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata; 107 if (null != jpegMetadata) { 108 final TiffImageMetadata exif = jpegMetadata.getExif(); 109 if (null != exif) { 110 outputSet = exif.getOutputSet(); 111 } 112 } 113 } 114 if (null == outputSet) { 115 outputSet = new TiffOutputSet(); 116 } 117 exifDirectory = outputSet.getOrCreateExifDirectory(); 118 gpsDirectory = outputSet.getOrCreateGPSDirectory(); 119 120 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF); 121 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF, 122 GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF_VALUE_TRUE_NORTH); 123 124 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION); 125 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION, 126 RationalNumber.valueOf(mimg.getMovingHe())); 127 128 exifDirectory.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL); 129 if (mimg instanceof StreetsideImportedImage) { 130 exifDirectory.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, 131 mimg.getDate("yyyy/MM/dd HH:mm:ss")); 132 } else if (mimg instanceof StreetsideImage) { 133 exifDirectory.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, 134 mimg.getDate("yyyy/MM/dd HH/mm/ss")); 135 } 136 outputSet.setGPSInDegrees(mimg.getMovingLatLon().lon(), mimg.getMovingLatLon().lat()); 137 OutputStream os = new BufferedOutputStream(new FileOutputStream(finalPath + ".jpg")); 138 new ExifRewriter().updateExifMetadataLossless(imageBytes, os, outputSet); 139 140 os.close(); 141 } catch (InterruptedException e) { 142 logger.info("Streetside export cancelled"); 143 return; 144 } catch (IOException | ImageReadException | ImageWriteException e) { 145 logger.error(e); 146 } 147 148 // Increases the progress bar. 149 monitor.worked(PleaseWaitProgressMonitor.PROGRESS_BAR_MAX / amount); 150 monitor.setCustomText("Downloaded " + (i + 1) + "/" + amount); 151 } 152 } 153}