StreetsideExportWriterThread.java
// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.streetside.io.export;
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import javax.imageio.ImageIO;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.imaging.common.ImageMetadata;
import org.apache.commons.imaging.common.RationalNumber;
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter;
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
import org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage;
import org.openstreetmap.josm.plugins.streetside.StreetsideImage;
import org.openstreetmap.josm.plugins.streetside.StreetsideImportedImage;
import org.openstreetmap.josm.tools.Logging;
/**
* Writes the images from the queue in the file system.
*
* @author nokutu
* @see StreetsideExportManager
*/
public class StreetsideExportWriterThread extends Thread {
private final String path;
private final ArrayBlockingQueue<BufferedImage> queue;
private final ArrayBlockingQueue<StreetsideAbstractImage> queueImages;
private final int amount;
private final ProgressMonitor monitor;
/**
* Main constructor.
*
* @param path
* Path to write the pictures.
* @param queue
* Queue of {@link StreetsideAbstractImage} objects.
* @param queueImages
* Queue of {@link BufferedImage} objects.
* @param amount
* Amount of images that are going to be exported.
* @param monitor
* Progress monitor.
*/
public StreetsideExportWriterThread(String path,
ArrayBlockingQueue<BufferedImage> queue,
ArrayBlockingQueue<StreetsideAbstractImage> queueImages, int amount,
ProgressMonitor monitor) {
this.path = path;
this.queue = queue;
this.queueImages = queueImages;
this.amount = amount;
this.monitor = monitor;
}
@Override
public void run() {
monitor.setCustomText("Downloaded 0/" + amount);
BufferedImage img;
StreetsideAbstractImage mimg;
String finalPath = "";
for (int i = 0; i < amount; i++) {
try {
img = queue.take();
mimg = queueImages.take();
if (path == null && mimg instanceof StreetsideImportedImage) {
String path = ((StreetsideImportedImage) mimg).getFile().getPath();
finalPath = path.substring(0, path.lastIndexOf('.'));
} else if (mimg instanceof StreetsideImage) {
finalPath = path + '/' + ((StreetsideImage) mimg).getId();
} else if (mimg instanceof StreetsideImportedImage) {
finalPath = path + '/' + ((StreetsideImportedImage) mimg).getFile().getName();
}
// Transforms the image into a byte array.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", outputStream);
byte[] imageBytes = outputStream.toByteArray();
// Write EXIF tags
TiffOutputSet outputSet = null;
TiffOutputDirectory exifDirectory;
TiffOutputDirectory gpsDirectory;
// If the image is imported, loads the rest of the EXIF data.
if (mimg instanceof StreetsideImportedImage) {
final ImageMetadata metadata = Imaging
.getMetadata(((StreetsideImportedImage) mimg).getFile());
final JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
if (null != jpegMetadata) {
final TiffImageMetadata exif = jpegMetadata.getExif();
if (null != exif) {
outputSet = exif.getOutputSet();
}
}
}
if (null == outputSet) {
outputSet = new TiffOutputSet();
}
exifDirectory = outputSet.getOrCreateExifDirectory();
gpsDirectory = outputSet.getOrCreateGPSDirectory();
gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF);
gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF,
GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION_REF_VALUE_TRUE_NORTH);
gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION);
gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_IMG_DIRECTION,
RationalNumber.valueOf(mimg.getMovingHe()));
exifDirectory.removeField(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
if (mimg instanceof StreetsideImportedImage) {
exifDirectory.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL,
mimg.getDate("yyyy/MM/dd HH:mm:ss"));
} else if (mimg instanceof StreetsideImage) {
exifDirectory.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL,
mimg.getDate("yyyy/MM/dd HH/mm/ss"));
}
outputSet.setGPSInDegrees(mimg.getMovingLatLon().lon(), mimg.getMovingLatLon().lat());
OutputStream os = new BufferedOutputStream(new FileOutputStream(finalPath + ".jpg"));
new ExifRewriter().updateExifMetadataLossless(imageBytes, os, outputSet);
os.close();
} catch (InterruptedException e) {
Logging.info("Streetside export cancelled");
return;
} catch (IOException | ImageReadException | ImageWriteException e) {
Logging.error(e);
}
// Increases the progress bar.
monitor.worked(PleaseWaitProgressMonitor.PROGRESS_BAR_MAX / amount);
monitor.setCustomText("Downloaded " + (i + 1) + "/" + amount);
}
}
}