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