Skip to content

Commit 72cff86

Browse files
committed
MapillaryExportWriterThread: Write XMP data
Signed-off-by: Taylor Smock <tsmock@meta.com>
1 parent 9b63460 commit 72cff86

File tree

1 file changed

+52
-18
lines changed

1 file changed

+52
-18
lines changed

src/main/java/org/openstreetmap/josm/plugins/mapillary/io/export/MapillaryExportWriterThread.java

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,27 @@
44
import java.awt.image.BufferedImage;
55
import java.io.BufferedOutputStream;
66
import java.io.ByteArrayOutputStream;
7-
import java.io.File;
87
import java.io.IOException;
8+
import java.io.InputStream;
99
import java.io.OutputStream;
1010
import java.nio.file.Files;
11+
import java.nio.file.Path;
1112
import java.nio.file.Paths;
13+
import java.nio.file.attribute.FileTime;
1214
import java.time.ZoneOffset;
1315
import java.time.ZonedDateTime;
1416
import java.time.format.DateTimeFormatter;
1517
import java.util.concurrent.ArrayBlockingQueue;
1618

19+
import javax.annotation.Nonnull;
20+
import javax.annotation.Nullable;
1721
import javax.imageio.ImageIO;
1822

1923
import org.apache.commons.imaging.ImageReadException;
2024
import org.apache.commons.imaging.ImageWriteException;
2125
import org.apache.commons.imaging.common.RationalNumber;
2226
import org.apache.commons.imaging.formats.jpeg.exif.ExifRewriter;
27+
import org.apache.commons.imaging.formats.jpeg.xmp.JpegXmpRewriter;
2328
import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
2429
import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
2530
import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory;
@@ -73,7 +78,7 @@ public void run() {
7378
this.monitor.setCustomText("Downloaded 0/" + this.amount);
7479
BufferedImage img;
7580
INode mimg;
76-
String finalPath;
81+
Path file;
7782
for (int i = 0; i < this.amount; i++) {
7883
while (this.queue.peek() == null) {
7984
if (this.amount == this.written) {
@@ -92,9 +97,7 @@ public void run() {
9297
img = this.queue.take();
9398
mimg = this.queueImages.take();
9499
if (MapillaryImageUtils.getKey(mimg) != 0 && mimg.getUniqueId() > 0) {
95-
finalPath = Paths
96-
.get(this.path, MapillaryImageUtils.getSequenceKey(mimg), Long.toString(mimg.getUniqueId()))
97-
.toString();
100+
file = Paths.get(this.path, MapillaryImageUtils.getSequenceKey(mimg), mimg.getUniqueId() + ".jpg");
98101
} else {
99102
// Increases the progress bar.
100103
this.monitor.worked(PleaseWaitProgressMonitor.PROGRESS_BAR_MAX / this.amount);
@@ -134,24 +137,28 @@ public void run() {
134137
.format(DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss"));
135138
exifDirectory.add(ExifTagConstants.EXIF_TAG_DATE_TIME_ORIGINAL, dateTime);
136139

140+
final String xml = getXmpXml(img, mimg);
141+
137142
outputSet.setGPSInDegrees(mimg.lon(), mimg.lat());
138-
File file = new File(finalPath + ".jpg");
139-
File parentFile = file.getParentFile();
140-
if (!parentFile.exists() && !parentFile.isDirectory()) {
141-
if (!parentFile.mkdirs()) {
142-
throw new IOException("Directory could not be created: " + parentFile.getPath());
143-
}
144-
if (!parentFile.setLastModified(MapillaryImageUtils.getDate(mimg).toEpochMilli())) {
145-
throw new IOException("Could not set last modified date: " + parentFile.getPath());
146-
}
143+
Path parentFile = file.getParent();
144+
if (!Files.exists(parentFile) && !Files.isDirectory(parentFile)) {
145+
Files.createDirectories(parentFile);
146+
Files.setLastModifiedTime(parentFile,
147+
FileTime.fromMillis(MapillaryImageUtils.getDate(mimg).toEpochMilli()));
147148
}
148-
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(file.toPath()))) {
149+
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(file))) {
149150
new ExifRewriter().updateExifMetadataLossless(imageBytes, os, outputSet);
150151
}
151-
if (!file.setLastModified(MapillaryImageUtils.getDate(mimg).toEpochMilli())) {
152-
Logging.info("Unable to set last modified time for {0} to {1}", file,
153-
MapillaryImageUtils.getDate(mimg));
152+
if (xml != null) {
153+
Path tFile = Files.createTempFile("mapillary-", "-" + file.getFileName());
154+
try (InputStream is = Files.newInputStream(file);
155+
OutputStream os = new BufferedOutputStream(Files.newOutputStream(tFile))) {
156+
new JpegXmpRewriter().updateXmpXml(is, os, xml);
157+
}
158+
Files.deleteIfExists(file);
159+
Files.move(tFile, file);
154160
}
161+
Files.setLastModifiedTime(file, FileTime.fromMillis(MapillaryImageUtils.getDate(mimg).toEpochMilli()));
155162
this.written++;
156163
} catch (InterruptedException e) {
157164
if (this.written != this.amount) {
@@ -170,6 +177,33 @@ public void run() {
170177
}
171178
}
172179

180+
/**
181+
* Get the XMP information for this image
182+
*
183+
* @param bufferedImage The image data
184+
* @param imageNode The originating image node with the appropriate information
185+
* @return The XMP data to write to the image
186+
*/
187+
@Nullable
188+
private static String getXmpXml(@Nonnull BufferedImage bufferedImage, @Nonnull INode imageNode) {
189+
if (MapillaryImageUtils.IS_PANORAMIC.test(imageNode)) {
190+
// Assume equirectangular for now
191+
return "<x:xmpmeta xmlns:x='adobe:ns:meta/'>"
192+
+ "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">"
193+
+ "<rdf:Description rdf:about=\"\" xmlns:GPano=\"http://ns.google.com/photos/1.0/panorama/\">"
194+
+ "<GPano:ProjectionType>equirectangular</GPano:ProjectionType>" + "<GPano:CroppedAreaImageWidthPixels>"
195+
+ bufferedImage.getWidth() + "</GPano:CroppedAreaImageWidthPixels>" + "<GPano:FullPanoWidthPixels>"
196+
+ bufferedImage.getWidth() + "</GPano:FullPanoWidthPixels>" + "<GPano:CroppedAreaImageHeightPixels>"
197+
+ bufferedImage.getHeight() + "</GPano:CroppedAreaImageHeightPixels>" + "<GPano:FullPanoHeightPixels>"
198+
+ bufferedImage.getHeight() + "</GPano:FullPanoHeightPixels>"
199+
// We don't actually do any cropping, so these are 0.
200+
+ "<GPano:CroppedAreaLeftPixels>0</GPano:CroppedAreaLeftPixels>"
201+
+ "<GPano:CroppedAreaTopPixels>0</GPano:CroppedAreaTopPixels>"
202+
+ "</rdf:Description></rdf:RDF></x:xmpmeta>";
203+
}
204+
return null;
205+
}
206+
173207
/**
174208
* Called when the size is decreased
175209
*/

0 commit comments

Comments
 (0)