source: josm/trunk/src/org/openstreetmap/josm/data/gpx/GpxImageEntry.java@ 19320

Last change on this file since 19320 was 19320, checked in by stoecker, 7 days ago

see #24104 - drop deprecated elements which aren't used anywhere

  • Property svn:eol-style set to native
File size: 19.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.gpx;
3
4import java.awt.Dimension;
5import java.awt.image.BufferedImage;
6import java.io.File;
7import java.io.IOException;
8import java.io.InputStream;
9import java.net.URI;
10import java.nio.file.Files;
11import java.time.Instant;
12import java.util.Date;
13import java.util.List;
14import java.util.Objects;
15
16import javax.imageio.IIOParam;
17
18import org.openstreetmap.josm.data.IQuadBucketType;
19import org.openstreetmap.josm.data.coor.CachedLatLon;
20import org.openstreetmap.josm.data.coor.ILatLon;
21import org.openstreetmap.josm.data.coor.LatLon;
22import org.openstreetmap.josm.data.imagery.street_level.Projections;
23import org.openstreetmap.josm.data.osm.BBox;
24import org.openstreetmap.josm.gui.layer.geoimage.ImageMetadata;
25
26/**
27 * Stores info about each image
28 * @since 14205 (extracted from gui.layer.geoimage.ImageEntry)
29 */
30public class GpxImageEntry implements Comparable<GpxImageEntry>, IQuadBucketType, ImageMetadata {
31 private File file;
32 private Integer exifOrientation;
33 private LatLon exifCoor;
34 private Double exifImgDir;
35 private Instant exifTime;
36 private Projections cameraProjection = Projections.UNKNOWN;
37 /**
38 * Flag isNewGpsData indicates that the GPS data of the image is new or has changed.
39 * GPS data includes the position, speed, elevation, time (e.g. as extracted from the GPS track).
40 * The flag can used to decide for which image file the EXIF GPS data is (re-)written.
41 */
42 private boolean isNewGpsData;
43 /** Temporary source of GPS time if not correlated with GPX track. */
44 private Instant exifGpsTime;
45
46 private String iptcCaption;
47 private String iptcHeadline;
48 private List<String> iptcKeywords;
49 private String iptcObjectName;
50
51 /**
52 * The following values are computed from the correlation with the gpx track
53 * or extracted from the image EXIF data.
54 */
55 private CachedLatLon pos;
56 /** Speed in kilometer per hour */
57 private Double speed;
58 /** Elevation (altitude) in meters */
59 private Double elevation;
60 /** The time after correlation with a gpx track */
61 private Instant gpsTime;
62
63 private int width;
64 private int height;
65
66 /**
67 * When the correlation dialog is open, we like to show the image position
68 * for the current time offset on the map in real time.
69 * On the other hand, when the user aborts this operation, the old values
70 * should be restored. We have a temporary copy, that overrides
71 * the normal values if it is not null. (This may be not the most elegant
72 * solution for this, but it works.)
73 */
74 private GpxImageEntry tmp;
75
76 /**
77 * Constructs a new {@code GpxImageEntry}.
78 */
79 public GpxImageEntry() {}
80
81 /**
82 * Constructs a new {@code GpxImageEntry} from an existing instance.
83 * @param other existing instance
84 * @since 14624
85 */
86 public GpxImageEntry(GpxImageEntry other) {
87 file = other.file;
88 exifOrientation = other.exifOrientation;
89 exifCoor = other.exifCoor;
90 exifImgDir = other.exifImgDir;
91 exifTime = other.exifTime;
92 isNewGpsData = other.isNewGpsData;
93 exifGpsTime = other.exifGpsTime;
94 pos = other.pos;
95 speed = other.speed;
96 elevation = other.elevation;
97 gpsTime = other.gpsTime;
98 width = other.width;
99 height = other.height;
100 tmp = other.tmp;
101 }
102
103 /**
104 * Constructs a new {@code GpxImageEntry}.
105 * @param file Path to image file on disk
106 */
107 public GpxImageEntry(File file) {
108 setFile(file);
109 }
110
111 @Override
112 public URI getImageURI() {
113 return this.getFile().toURI();
114 }
115
116 /**
117 * Returns width of the image this GpxImageEntry represents.
118 * @return width of the image this GpxImageEntry represents
119 * @since 13220
120 */
121 @Override
122 public int getWidth() {
123 return width;
124 }
125
126 /**
127 * Returns height of the image this GpxImageEntry represents.
128 * @return height of the image this GpxImageEntry represents
129 * @since 13220
130 */
131 @Override
132 public int getHeight() {
133 return height;
134 }
135
136 /**
137 * Returns the position value. The position value from the temporary copy
138 * is returned if that copy exists.
139 * @return the position value
140 */
141 @Override
142 public CachedLatLon getPos() {
143 if (tmp != null)
144 return tmp.pos;
145 return pos;
146 }
147
148 /**
149 * Returns the speed value. The speed value from the temporary copy is
150 * returned if that copy exists.
151 * @return the speed value
152 */
153 @Override
154 public Double getSpeed() {
155 if (tmp != null)
156 return tmp.speed;
157 return speed;
158 }
159
160 /**
161 * Returns the elevation value. The elevation value from the temporary
162 * copy is returned if that copy exists.
163 * @return the elevation value
164 */
165 @Override
166 public Double getElevation() {
167 if (tmp != null)
168 return tmp.elevation;
169 return elevation;
170 }
171
172 /**
173 * Returns the GPS time value. The GPS time value from the temporary copy
174 * is returned if that copy exists.
175 * @return the GPS time value
176 */
177 @Override
178 public Instant getGpsInstant() {
179 return tmp != null ? tmp.gpsTime : gpsTime;
180 }
181
182 /**
183 * Convenient way to determine if this entry has a GPS time, without the cost of building a defensive copy.
184 * @return {@code true} if this entry has a GPS time
185 * @since 6450
186 */
187 @Override
188 public boolean hasGpsTime() {
189 return (tmp != null && tmp.gpsTime != null) || gpsTime != null;
190 }
191
192 /**
193 * Returns associated file.
194 * @return associated file
195 */
196 public File getFile() {
197 return file;
198 }
199
200 /**
201 * Returns a display name for this entry
202 * @return a display name for this entry
203 */
204 @Override
205 public String getDisplayName() {
206 return file == null ? "" : file.getName();
207 }
208
209 /**
210 * Returns EXIF orientation
211 * @return EXIF orientation
212 */
213 @Override
214 public Integer getExifOrientation() {
215 return exifOrientation != null ? exifOrientation : 1;
216 }
217
218 /**
219 * Returns EXIF time
220 * @return EXIF time
221 * @since 17715
222 */
223 @Override
224 public Instant getExifInstant() {
225 return exifTime;
226 }
227
228 /**
229 * Convenient way to determine if this entry has a EXIF time, without the cost of building a defensive copy.
230 * @return {@code true} if this entry has a EXIF time
231 * @since 6450
232 */
233 @Override
234 public boolean hasExifTime() {
235 return exifTime != null;
236 }
237
238 /**
239 * Returns the EXIF GPS time.
240 * @return the EXIF GPS time
241 * @since 17715
242 */
243 @Override
244 public Instant getExifGpsInstant() {
245 return exifGpsTime;
246 }
247
248 /**
249 * Convenient way to determine if this entry has a EXIF GPS time, without the cost of building a defensive copy.
250 * @return {@code true} if this entry has a EXIF GPS time
251 * @since 6450
252 */
253 @Override
254 public boolean hasExifGpsTime() {
255 return exifGpsTime != null;
256 }
257
258 private static Date getDefensiveDate(Instant date) {
259 if (date == null)
260 return null;
261 return Date.from(date);
262 }
263
264 @Override
265 public LatLon getExifCoor() {
266 return exifCoor;
267 }
268
269 @Override
270 public Double getExifImgDir() {
271 if (tmp != null)
272 return tmp.exifImgDir;
273 return exifImgDir;
274 }
275
276 @Override
277 public Instant getLastModified() {
278 return Instant.ofEpochMilli(this.getFile().lastModified());
279 }
280
281 /**
282 * Sets the width of this GpxImageEntry.
283 * @param width set the width of this GpxImageEntry
284 * @since 13220
285 */
286 @Override
287 public void setWidth(int width) {
288 this.width = width;
289 }
290
291 /**
292 * Sets the height of this GpxImageEntry.
293 * @param height set the height of this GpxImageEntry
294 * @since 13220
295 */
296 @Override
297 public void setHeight(int height) {
298 this.height = height;
299 }
300
301 /**
302 * Sets the position.
303 * @param pos cached position
304 */
305 public void setPos(CachedLatLon pos) {
306 this.pos = pos;
307 }
308
309 /**
310 * Sets the position.
311 * @param pos position (will be cached)
312 */
313 public void setPos(LatLon pos) {
314 setPos(pos != null ? new CachedLatLon(pos) : null);
315 }
316
317 @Override
318 public void setPos(ILatLon pos) {
319 if (pos instanceof CachedLatLon) {
320 this.setPos((CachedLatLon) pos);
321 } else if (pos instanceof LatLon) {
322 this.setPos((LatLon) pos);
323 } else if (pos != null) {
324 this.setPos(new LatLon(pos));
325 } else {
326 this.setPos(null);
327 }
328 }
329
330 /**
331 * Sets the speed.
332 * @param speed speed
333 */
334 @Override
335 public void setSpeed(Double speed) {
336 this.speed = speed;
337 }
338
339 /**
340 * Sets the elevation.
341 * @param elevation elevation
342 */
343 @Override
344 public void setElevation(Double elevation) {
345 this.elevation = elevation;
346 }
347
348 /**
349 * Sets associated file.
350 * @param file associated file
351 */
352 public void setFile(File file) {
353 this.file = file;
354 }
355
356 /**
357 * Sets EXIF orientation.
358 * @param exifOrientation EXIF orientation
359 */
360 @Override
361 public void setExifOrientation(Integer exifOrientation) {
362 this.exifOrientation = exifOrientation;
363 }
364
365 /**
366 * Sets EXIF time.
367 * @param exifTime EXIF time
368 * @since 17715
369 */
370 @Override
371 public void setExifTime(Instant exifTime) {
372 this.exifTime = exifTime;
373 }
374
375 /**
376 * Sets the EXIF GPS time.
377 * @param exifGpsTime the EXIF GPS time
378 * @since 17715
379 */
380 @Override
381 public void setExifGpsTime(Instant exifGpsTime) {
382 this.exifGpsTime = exifGpsTime;
383 }
384
385 /**
386 * Sets the GPS time.
387 * @param gpsTime the GPS time
388 * @since 17715
389 */
390 @Override
391 public void setGpsTime(Instant gpsTime) {
392 this.gpsTime = gpsTime;
393 }
394
395 public void setExifCoor(LatLon exifCoor) {
396 this.exifCoor = exifCoor;
397 }
398
399 @Override
400 public void setExifCoor(ILatLon exifCoor) {
401 if (exifCoor instanceof LatLon) {
402 this.exifCoor = (LatLon) exifCoor;
403 } else if (exifCoor != null) {
404 this.exifCoor = new LatLon(exifCoor);
405 } else {
406 this.exifCoor = null;
407 }
408 }
409
410 @Override
411 public void setExifImgDir(Double exifDir) {
412 this.exifImgDir = exifDir;
413 }
414
415 /**
416 * Sets the IPTC caption.
417 * @param iptcCaption the IPTC caption
418 * @since 15219
419 */
420 @Override
421 public void setIptcCaption(String iptcCaption) {
422 this.iptcCaption = iptcCaption;
423 }
424
425 /**
426 * Sets the IPTC headline.
427 * @param iptcHeadline the IPTC headline
428 * @since 15219
429 */
430 @Override
431 public void setIptcHeadline(String iptcHeadline) {
432 this.iptcHeadline = iptcHeadline;
433 }
434
435 /**
436 * Sets the IPTC keywords.
437 * @param iptcKeywords the IPTC keywords
438 * @since 15219
439 */
440 @Override
441 public void setIptcKeywords(List<String> iptcKeywords) {
442 this.iptcKeywords = iptcKeywords;
443 }
444
445 /**
446 * Sets the IPTC object name.
447 * @param iptcObjectName the IPTC object name
448 * @since 15219
449 */
450 @Override
451 public void setIptcObjectName(String iptcObjectName) {
452 this.iptcObjectName = iptcObjectName;
453 }
454
455 /**
456 * Returns the IPTC caption.
457 * @return the IPTC caption
458 * @since 15219
459 */
460 @Override
461 public String getIptcCaption() {
462 return iptcCaption;
463 }
464
465 /**
466 * Returns the IPTC headline.
467 * @return the IPTC headline
468 * @since 15219
469 */
470 @Override
471 public String getIptcHeadline() {
472 return iptcHeadline;
473 }
474
475 /**
476 * Returns the IPTC keywords.
477 * @return the IPTC keywords
478 * @since 15219
479 */
480 @Override
481 public List<String> getIptcKeywords() {
482 return iptcKeywords;
483 }
484
485 /**
486 * Returns the IPTC object name.
487 * @return the IPTC object name
488 * @since 15219
489 */
490 @Override
491 public String getIptcObjectName() {
492 return iptcObjectName;
493 }
494
495 @Override
496 public int compareTo(GpxImageEntry image) {
497 if (exifTime != null && image.exifTime != null)
498 return exifTime.compareTo(image.exifTime);
499 else if (exifTime == null && image.exifTime == null)
500 return 0;
501 else if (exifTime == null)
502 return -1;
503 else
504 return 1;
505 }
506
507 @Override
508 public int hashCode() {
509 return Objects.hash(height, width, isNewGpsData,
510 elevation, exifCoor, exifGpsTime, exifImgDir, exifOrientation, exifTime,
511 iptcCaption, iptcHeadline, iptcKeywords, iptcObjectName,
512 file, gpsTime, pos, speed, tmp, cameraProjection);
513 }
514
515 @Override
516 public boolean equals(Object obj) {
517 if (this == obj)
518 return true;
519 if (obj == null || getClass() != obj.getClass())
520 return false;
521 GpxImageEntry other = (GpxImageEntry) obj;
522 return height == other.height
523 && width == other.width
524 && isNewGpsData == other.isNewGpsData
525 && Objects.equals(elevation, other.elevation)
526 && Objects.equals(exifCoor, other.exifCoor)
527 && Objects.equals(exifGpsTime, other.exifGpsTime)
528 && Objects.equals(exifImgDir, other.exifImgDir)
529 && Objects.equals(exifOrientation, other.exifOrientation)
530 && Objects.equals(exifTime, other.exifTime)
531 && Objects.equals(iptcCaption, other.iptcCaption)
532 && Objects.equals(iptcHeadline, other.iptcHeadline)
533 && Objects.equals(iptcKeywords, other.iptcKeywords)
534 && Objects.equals(iptcObjectName, other.iptcObjectName)
535 && Objects.equals(file, other.file)
536 && Objects.equals(gpsTime, other.gpsTime)
537 && Objects.equals(pos, other.pos)
538 && Objects.equals(speed, other.speed)
539 && Objects.equals(tmp, other.tmp)
540 && cameraProjection == other.cameraProjection;
541 }
542
543 /**
544 * Make a fresh copy and save it in the temporary variable. Use
545 * {@link #applyTmp()} or {@link #discardTmp()} if the temporary variable
546 * is not needed anymore.
547 * @return the fresh copy.
548 */
549 public GpxImageEntry createTmp() {
550 tmp = new GpxImageEntry(this);
551 tmp.tmp = null;
552 return tmp;
553 }
554
555 /**
556 * Get temporary variable that is used for real time parameter
557 * adjustments. The temporary variable is created if it does not exist
558 * yet. Use {@link #applyTmp()} or {@link #discardTmp()} if the temporary
559 * variable is not needed anymore.
560 * @return temporary variable
561 */
562 public GpxImageEntry getTmp() {
563 if (tmp == null) {
564 createTmp();
565 }
566 return tmp;
567 }
568
569 /**
570 * Copy the values from the temporary variable to the main instance. The
571 * temporary variable is deleted.
572 * @see #discardTmp()
573 */
574 public void applyTmp() {
575 if (tmp != null) {
576 pos = tmp.pos;
577 speed = tmp.speed;
578 elevation = tmp.elevation;
579 gpsTime = tmp.gpsTime;
580 exifImgDir = tmp.exifImgDir;
581 isNewGpsData = isNewGpsData || tmp.isNewGpsData;
582 tmp = null;
583 }
584 tmpUpdated();
585 }
586
587 /**
588 * Delete the temporary variable. Temporary modifications are lost.
589 * @see #applyTmp()
590 */
591 public void discardTmp() {
592 tmp = null;
593 tmpUpdated();
594 }
595
596 /**
597 * If it has been tagged i.e. matched to a gpx track or retrieved lat/lon from exif
598 * @return {@code true} if it has been tagged
599 */
600 public boolean isTagged() {
601 return pos != null;
602 }
603
604 /**
605 * String representation. (only partial info)
606 */
607 @Override
608 public String toString() {
609 return file.getName()+": "+
610 "pos = "+pos+" | "+
611 "exifCoor = "+exifCoor+" | "+
612 (tmp == null ? " tmp==null" :
613 " [tmp] pos = "+tmp.pos);
614 }
615
616 /**
617 * Indicates that the image has new GPS data.
618 * That flag is set by new GPS data providers. It is used e.g. by the photo_geotagging plugin
619 * to decide for which image file the EXIF GPS data needs to be (re-)written.
620 * @since 6392
621 */
622 public void flagNewGpsData() {
623 isNewGpsData = true;
624 }
625
626 /**
627 * Indicate that the temporary copy has been updated. Mostly used to prevent UI issues.
628 * By default, this is a no-op. Override when needed in subclasses.
629 * @since 17579
630 */
631 protected void tmpUpdated() {
632 // No-op by default
633 }
634
635 @Override
636 public BBox getBBox() {
637 // new BBox(LatLon) is null safe.
638 // Use `getPos` instead of `getExifCoor` since the image may be correlated against a GPX track
639 return new BBox(this.getPos());
640 }
641
642 /**
643 * Remove the flag that indicates new GPS data.
644 * The flag is cleared by a new GPS data consumer.
645 */
646 public void unflagNewGpsData() {
647 isNewGpsData = false;
648 }
649
650 /**
651 * Queries whether the GPS data changed. The flag value from the temporary
652 * copy is returned if that copy exists.
653 * @return {@code true} if GPS data changed, {@code false} otherwise
654 * @since 6392
655 */
656 public boolean hasNewGpsData() {
657 if (tmp != null)
658 return tmp.isNewGpsData;
659 return isNewGpsData;
660 }
661
662 /**
663 * Extract GPS metadata from image EXIF. Has no effect if the image file is not set
664 *
665 * If successful, fills in the LatLon, speed, elevation, image direction, and other attributes
666 * @since 9270
667 */
668 @Override
669 public void extractExif() {
670 ImageMetadata.super.extractExif();
671 }
672
673 @Override
674 public InputStream getInputStream() throws IOException {
675 return Files.newInputStream(this.getFile().toPath());
676 }
677
678 /**
679 * Reads the image represented by this entry in the given target dimension.
680 * @param target the desired dimension used for {@linkplain IIOParam#setSourceSubsampling subsampling} or {@code null}
681 * @return the read image, or {@code null}
682 * @throws IOException if any I/O error occurs
683 * @since 18246
684 */
685 public BufferedImage read(Dimension target) throws IOException {
686 throw new UnsupportedOperationException("read not implemented for " + this.getClass().getSimpleName());
687 }
688
689 /**
690 * Get the projection type for this entry
691 * @return The projection type
692 * @since 18246
693 */
694 @Override
695 public Projections getProjectionType() {
696 return this.cameraProjection;
697 }
698
699 @Override
700 public void setProjectionType(Projections newProjection) {
701 this.cameraProjection = newProjection;
702 }
703
704 /**
705 * Returns a {@link WayPoint} representation of this GPX image entry.
706 * @return a {@code WayPoint} representation of this GPX image entry (containing position, instant and elevation)
707 * @since 18065
708 */
709 public WayPoint asWayPoint() {
710 CachedLatLon position = getPos();
711 WayPoint wpt = null;
712 if (position != null) {
713 wpt = new WayPoint(position);
714 wpt.setInstant(exifTime);
715 Double ele = getElevation();
716 if (ele != null) {
717 wpt.put(GpxConstants.PT_ELE, ele.toString());
718 }
719 }
720 return wpt;
721 }
722}
Note: See TracBrowser for help on using the repository browser.