This is an automated email from the ASF dual-hosted git repository.
tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git
The following commit(s) were added to refs/heads/main by this push:
new d7a6d413e8 [TIKA-4781] Map EXIF GPS altitude to the existing geo:alt
property (#2941)
d7a6d413e8 is described below
commit d7a6d413e812f2b2a6222fa2245eaa82a45e8a52
Author: Dominik Schmidt <[email protected]>
AuthorDate: Fri Jul 10 20:45:52 2026 +0200
[TIKA-4781] Map EXIF GPS altitude to the existing geo:alt property (#2941)
The GeotagHandler mapped latitude, longitude and the GPS timestamp from
the EXIF GpsDirectory, but not the altitude, although tika-core defines
Geographic.ALTITUDE and metadata-extractor exposes the value in the same
directory. Consumers only got the raw description string
("GPS:GPS Altitude" = "227.4 metres").
Set geo:alt as a signed value honoring GPSAltitudeRef (1 = below sea
level = negative), formatted with the same DecimalFormat as latitude
and longitude. Analogous to TIKA-4425, which added the normalized GPS
timestamp from the same directory; see TIKA-2861 for the corresponding
ISO 6709 altitude handling in MP4/QuickTime.
The existing IMG_1034.heic fixture already carries an altitude, so
HeifParserTest now asserts it end to end; handler-level tests cover the
above and below sea level cases.
---
.../tika/parser/image/ImageMetadataExtractor.java | 15 ++++++++++++--
.../apache/tika/parser/image/HeifParserTest.java | 1 +
.../parser/image/ImageMetadataExtractorTest.java | 24 ++++++++++++++++++++++
3 files changed, 38 insertions(+), 2 deletions(-)
diff --git
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
index 30140ec677..900c4d3f55 100644
---
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
+++
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/main/java/org/apache/tika/parser/image/ImageMetadataExtractor.java
@@ -684,10 +684,10 @@ public class ImageMetadataExtractor {
}
public void handle(Directory directory, Metadata metadata) throws
MetadataException {
+ DecimalFormat geoDecimalFormat = new
DecimalFormat(GEO_DECIMAL_FORMAT_STRING,
+ new DecimalFormatSymbols(Locale.ENGLISH));
GeoLocation geoLocation = ((GpsDirectory)
directory).getGeoLocation();
if (geoLocation != null) {
- DecimalFormat geoDecimalFormat = new
DecimalFormat(GEO_DECIMAL_FORMAT_STRING,
- new DecimalFormatSymbols(Locale.ENGLISH));
metadata.set(TikaCoreProperties.LATITUDE,
geoDecimalFormat.format(geoLocation.getLatitude()));
metadata.set(TikaCoreProperties.LONGITUDE,
@@ -697,6 +697,17 @@ public class ImageMetadataExtractor {
if (gpsDate != null) {
metadata.set(Geographic.TIMESTAMP, gpsDate);
}
+ Rational altitude = ((GpsDirectory)
directory).getRational(GpsDirectory.TAG_ALTITUDE);
+ if (altitude != null) {
+ double metres = altitude.doubleValue();
+ //GPSAltitudeRef 1 means below sea level
+ Integer altitudeRef =
+ ((GpsDirectory)
directory).getInteger(GpsDirectory.TAG_ALTITUDE_REF);
+ if (altitudeRef != null && altitudeRef == 1) {
+ metres = -metres;
+ }
+ metadata.set(TikaCoreProperties.ALTITUDE,
geoDecimalFormat.format(metres));
+ }
}
}
diff --git
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/HeifParserTest.java
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/HeifParserTest.java
index 2f9a80960d..107f408620 100644
---
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/HeifParserTest.java
+++
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/HeifParserTest.java
@@ -51,6 +51,7 @@ public class HeifParserTest extends TikaTest {
assertEquals("image/heic", metadata.get(Metadata.CONTENT_TYPE));
assertEquals("23.177917", metadata.get(Metadata.LATITUDE));
assertEquals("113.394317", metadata.get(Metadata.LONGITUDE));
+ assertEquals("42.810337", metadata.get(Geographic.ALTITUDE));
assertEquals("2018-02-05T07:11:43Z",
metadata.get(Geographic.TIMESTAMP));
}
diff --git
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/ImageMetadataExtractorTest.java
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/ImageMetadataExtractorTest.java
index 267659c005..4db12e8b71 100644
---
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/ImageMetadataExtractorTest.java
+++
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-image-module/src/test/java/org/apache/tika/parser/image/ImageMetadataExtractorTest.java
@@ -28,11 +28,13 @@ import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
+import com.drew.lang.Rational;
import com.drew.metadata.Directory;
import com.drew.metadata.MetadataException;
import com.drew.metadata.Tag;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.ExifSubIFDDirectory;
+import com.drew.metadata.exif.GpsDirectory;
import com.drew.metadata.jpeg.JpegCommentDirectory;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
@@ -85,6 +87,28 @@ public class ImageMetadataExtractorTest {
"Should be ISO date without time zone");
}
+ @Test
+ public void testGeotagHandlerAltitude() throws MetadataException {
+ GpsDirectory gps = Mockito.mock(GpsDirectory.class);
+
Mockito.when(gps.getRational(GpsDirectory.TAG_ALTITUDE)).thenReturn(new
Rational(2274, 10));
+
Mockito.when(gps.getInteger(GpsDirectory.TAG_ALTITUDE_REF)).thenReturn(0);
+ Metadata metadata = new Metadata();
+
+ new ImageMetadataExtractor.GeotagHandler().handle(gps, metadata);
+ assertEquals("227.4", metadata.get(TikaCoreProperties.ALTITUDE));
+ }
+
+ @Test
+ public void testGeotagHandlerAltitudeBelowSeaLevel() throws
MetadataException {
+ GpsDirectory gps = Mockito.mock(GpsDirectory.class);
+
Mockito.when(gps.getRational(GpsDirectory.TAG_ALTITUDE)).thenReturn(new
Rational(2274, 10));
+
Mockito.when(gps.getInteger(GpsDirectory.TAG_ALTITUDE_REF)).thenReturn(1);
+ Metadata metadata = new Metadata();
+
+ new ImageMetadataExtractor.GeotagHandler().handle(gps, metadata);
+ assertEquals("-227.4", metadata.get(TikaCoreProperties.ALTITUDE));
+ }
+
@Test
public void testExifHandlerParseDateFallback() throws MetadataException {
ExifIFD0Directory exif = Mockito.mock(ExifIFD0Directory.class);