This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sis.git
commit 00c9d831d07cbe1f6e91ced45e7eb42ea626e509 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Jan 7 19:59:31 2022 +0100 Add a test for TIFF `GEO_METADATA` tag. The test using temporal extent forces us to add a minimalist "sis-temporal" implementation. --- .../apache/sis/test/integration/MetadataTest.java | 22 +-- .../org/apache/sis/test/integration/Metadata.xml | 4 +- .../sis/internal/temporal/DefaultInstant.java | 66 +++++++++ .../sis/internal/temporal/DefaultPeriod.java | 72 ++++++++++ .../internal/temporal/DefaultPeriodDuration.java | 88 ++++++++++++ .../internal/temporal/DefaultTemporalFactory.java | 150 +++++++++++++++++++++ .../apache/sis/internal/temporal/Primitive.java | 75 +++++++++++ .../apache/sis/internal/temporal/package-info.java | 27 ++++ .../sis/internal/util/TemporalUtilities.java | 6 +- .../sis/util/collection/TreeTableFormat.java | 2 +- .../sis/storage/landsat/MetadataReaderTest.java | 4 +- storage/sis-geotiff/pom.xml | 5 + .../apache/sis/storage/geotiff/XMLMetadata.java | 3 +- .../sis/storage/geotiff/XMLMetadataTest.java | 57 +++++++- .../sis/storage/netcdf/MetadataReaderTest.java | 4 +- .../sis/internal/storage/MetadataBuilder.java | 2 +- 16 files changed, 556 insertions(+), 31 deletions(-) diff --git a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java index f90ecb0..4656208 100644 --- a/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java +++ b/core/sis-referencing/src/test/java/org/apache/sis/test/integration/MetadataTest.java @@ -86,7 +86,7 @@ import static org.apache.sis.test.Assert.*; * * @author Guilhem Legal (Geomatys) * @author Martin Desruisseaux (Geomatys) - * @version 1.0 + * @version 1.2 * * @see org.apache.sis.metadata.iso.DefaultMetadataTest * @@ -114,26 +114,10 @@ public final strictfp class MetadataTest extends TestCase { */ @After public void assertNoUnexpectedLog() { - loggings.skipNextLogIfContains("sis-temporal"); loggings.assertNoUnexpectedLog(); } /** - * Sets the temporal extent. The current implementation does nothing, because {@code sis-metadata} does not have - * any dependency to {@code sis-temporal}. However a future version or an other module may implement this method. - * - * @param extent the extent to set. - * @param startTime the start time in the {@code "yyyy-MM-dd"} format. - * @param endTime the end time in the {@code "yyyy-MM-dd"} format. - */ - protected void setTemporalBounds(final DefaultTemporalExtent extent, final String startTime, final String endTime) { - /* - * Note: if this MetadataTest class is made final and this method removed, - * then testUnmarshalling() can be simplified. - */ - } - - /** * Programmatically creates the metadata to marshal, or to compare against the unmarshalled metadata. * * @return the hard-coded representation of {@value #XML_FILE} content. @@ -272,7 +256,7 @@ public final strictfp class MetadataTest extends TestCase { nameAndIdentifier("D28", "Depth below D28", "CRS for testing purpose"), datum, cs); final DefaultTemporalExtent temporal = new DefaultTemporalExtent(); - setTemporalBounds(temporal, "1990-06-05", "1990-07-02"); + temporal.setBounds(TestUtilities.date("1990-06-05 00:00:00"), TestUtilities.date("1990-07-02 00:00:00")); identification.setExtents(singleton(new DefaultExtent( null, new DefaultGeographicBoundingBox(1.1666, 1.1667, 36.4, 36.6), @@ -432,7 +416,7 @@ public final strictfp class MetadataTest extends TestCase { private static void replace(final StringBuffer buffer, final String toSearch, final String replaceBy) { final int i = buffer.indexOf(toSearch); assertTrue("String to replace not found.", i >= 0); - buffer.replace(i, i+toSearch.length(), replaceBy); + buffer.replace(i, i + toSearch.length(), replaceBy); } /** diff --git a/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml b/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml index c4c29fb..f6bbea0 100644 --- a/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml +++ b/core/sis-referencing/src/test/resources/org/apache/sis/test/integration/Metadata.xml @@ -390,8 +390,8 @@ <gmd:EX_TemporalExtent> <gmd:extent> <gml:TimePeriod gml:id="extent"> - <gml:beginPosition>1990-06-05</gml:beginPosition> - <gml:endPosition>1990-07-02</gml:endPosition> + <gml:beginPosition>1990-06-05T00:00:00Z</gml:beginPosition> + <gml:endPosition>1990-07-02T00:00:00Z</gml:endPosition> </gml:TimePeriod> </gmd:extent> </gmd:EX_TemporalExtent> diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultInstant.java b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultInstant.java new file mode 100644 index 0000000..b312280 --- /dev/null +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultInstant.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.temporal; + +import java.util.Date; +import org.opengis.temporal.Instant; +import org.opengis.temporal.TemporalPosition; + + +/** + * Default implementation of GeoAPI instant. This is a temporary class; + * GeoAPI temporal interfaces are expected to change a lot in a future revision. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +final class DefaultInstant extends Primitive implements Instant { + /** The date in milliseconds since epoch. */ + private final long millis; + + /** Creates a new instant for the given date. */ + DefaultInstant(final Date time) { + millis = time.getTime(); + } + + /** Returns the date used for describing temporal position. */ + @Override public Date getDate() { + return new Date(millis); + } + + /** Association to a temporal reference system. */ + @Override public TemporalPosition getTemporalPosition() { + throw DefaultTemporalFactory.unsupported(); + } + + /** String representation in ISO format. */ + @Override public String toString() { + return java.time.Instant.ofEpochMilli(millis).toString(); + } + + /** Hash code value of the time position. */ + @Override public int hashCode() { + return Long.hashCode(millis) ^ 57; + } + + /** Compares with given object for equality. */ + @Override public boolean equals(final Object obj) { + return (obj instanceof DefaultInstant) && ((DefaultInstant) obj).millis == millis; + } +} diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriod.java b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriod.java new file mode 100644 index 0000000..353d644 --- /dev/null +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriod.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.temporal; + +import java.util.Objects; +import org.opengis.temporal.Instant; +import org.opengis.temporal.Period; + + +/** + * Default implementation of GeoAPI period. This is a temporary class; + * GeoAPI temporal interfaces are expected to change a lot in a future revision. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +final class DefaultPeriod extends Primitive implements Period { + /** Bounds making this period. */ + private final Instant beginning, ending; + + /** Creates a new period with the given bounds. */ + DefaultPeriod(final Instant beginning, final Instant ending) { + this.beginning = beginning; + this.ending = ending; + } + + /** The beginning instant at which this period starts. */ + @Override public Instant getBeginning() { + return beginning; + } + + /** The ending instant at which this period ends. */ + @Override public Instant getEnding() { + return ending; + } + + /** String representation. */ + @Override public String toString() { + return "[" + beginning + " … " + ending + ']'; + } + + /** Hash code value of the time position. */ + @Override public int hashCode() { + return Objects.hash(beginning, ending); + } + + /** Compares with given object for equality. */ + @Override public boolean equals(final Object obj) { + if (obj instanceof DefaultPeriod) { + DefaultPeriod other = (DefaultPeriod) obj; + return Objects.equals(other.beginning, beginning) && + Objects.equals(other.ending, ending); + } + return false; + } +} diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriodDuration.java b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriodDuration.java new file mode 100644 index 0000000..052a12c --- /dev/null +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultPeriodDuration.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.temporal; + +import java.util.Objects; +import org.opengis.temporal.PeriodDuration; +import org.opengis.util.InternationalString; + + +/** + * Default implementation of GeoAPI period duration. This is a temporary class; + * GeoAPI temporal interfaces are expected to change a lot in a future revision. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +final class DefaultPeriodDuration implements PeriodDuration { + /** Components of this period duration. */ + private final InternationalString years, months, week, days, hours, minutes, seconds; + + /** + * Creates a new duration. + */ + DefaultPeriodDuration( + final InternationalString years, final InternationalString months, + final InternationalString week, final InternationalString days, + final InternationalString hours, final InternationalString minutes, final InternationalString seconds) + { + this.years = years; + this.months = months; + this.week = week; + this.days = days; + this.hours = hours; + this.minutes = minutes; + this.seconds = seconds; + } + + + @Override public InternationalString getDesignator() {return null;} + @Override public InternationalString getYears() {return years;} + @Override public InternationalString getMonths() {return months;} + @Override public InternationalString getDays() {return days;} + @Override public InternationalString getTimeIndicator() {return null;} + @Override public InternationalString getHours() {return hours;} + @Override public InternationalString getMinutes() {return minutes;} + @Override public InternationalString getSeconds() {return seconds;} + + /** String representation. */ + @Override public String toString() { + return "PeriodDuration[" + years + '-' + months + '-' + days + ' ' + hours + ':' + minutes + ':' + seconds + ']'; + } + + /** Hash code value of the time position. */ + @Override public int hashCode() { + return Objects.hash(years, months, week, days, hours, minutes, seconds); + } + + /** Compares with given object for equality. */ + @Override public boolean equals(final Object obj) { + if (obj instanceof DefaultPeriodDuration) { + DefaultPeriodDuration other = (DefaultPeriodDuration) obj; + return Objects.equals(other.years, years) && + Objects.equals(other.months, months) && + Objects.equals(other.week, week) && + Objects.equals(other.days, days) && + Objects.equals(other.hours, hours) && + Objects.equals(other.minutes, minutes) && + Objects.equals(other.seconds, seconds); + } + return false; + } +} diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java new file mode 100644 index 0000000..d040aea --- /dev/null +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/DefaultTemporalFactory.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.temporal; + +import java.util.Date; +import java.util.Collection; +import javax.measure.Unit; +import javax.measure.quantity.Time; +import org.opengis.temporal.*; +import org.opengis.metadata.Identifier; +import org.opengis.metadata.extent.Extent; +import org.opengis.util.InternationalString; +import org.apache.sis.util.resources.Errors; + + +/** + * Default implementation of temporal object factory. This is a temporary class; + * GeoAPI temporal interfaces are expected to change a lot in a future revision. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +public final class DefaultTemporalFactory implements TemporalFactory { + /** The unique instance of this factory. */ + public static final TemporalFactory INSTANCE = new DefaultTemporalFactory(); + + /** Creates the singleton instance. */ + private DefaultTemporalFactory() { + } + + /** Creates an {@link Instant} for the given date. */ + @Override public Instant createInstant(Date date) { + return new DefaultInstant(date); + } + + /** Creates a period for the two given instants. */ + @Override public Period createPeriod(Instant begin, Instant end) { + return new DefaultPeriod(begin, end); + } + + /** Creates a period duration. */ + @Override public PeriodDuration createPeriodDuration( + InternationalString years, InternationalString months, + InternationalString week, InternationalString days, + InternationalString hours, InternationalString minutes, InternationalString seconds) + { + return new DefaultPeriodDuration(years, months, week, days, hours, minutes, seconds); + } + + /** Returns the exception to be thrown by all unsupported methods. */ + static UnsupportedOperationException unsupported() { + return new UnsupportedOperationException(Errors.format(Errors.Keys.MissingRequiredModule_1, "sis-temporal")); + } + + /** Unsupported. */ + @Override public Calendar createCalendar(Identifier name, Extent domainOfValidity) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public Calendar createCalendar(Identifier name, Extent domainOfValidity, Collection<CalendarEra> referenceFrame, Clock timeBasis) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public CalendarDate createCalendarDate(TemporalReferenceSystem frame, IndeterminateValue indeterminatePosition, InternationalString calendarEraName, int[] calendarDate) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public CalendarEra createCalendarEra(InternationalString name, InternationalString referenceEvent, CalendarDate referenceDate, JulianDate julianReference, Period epochOfUse) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public Clock createClock(Identifier name, Extent domainOfValidity, InternationalString referenceEvent, ClockTime referenceTime, ClockTime utcReference) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public ClockTime createClockTime(TemporalReferenceSystem frame, IndeterminateValue indeterminatePosition, Number[] clockTime) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public DateAndTime createDateAndTime(TemporalReferenceSystem frame, IndeterminateValue indeterminatePosition, InternationalString calendarEraName, int[] calendarDate, Number[] clockTime) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public IntervalLength createIntervalLenght(Unit unit, int radix, int factor, int value) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public JulianDate createJulianDate(TemporalReferenceSystem frame, IndeterminateValue indeterminatePosition, Number coordinateValue) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public OrdinalEra createOrdinalEra(InternationalString name, Date beginning, Date end, Collection<OrdinalEra> member) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public OrdinalPosition createOrdinalPosition(TemporalReferenceSystem frame, IndeterminateValue indeterminatePosition, OrdinalEra ordinalPosition) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public OrdinalReferenceSystem createOrdinalReferenceSystem(Identifier name, Extent domainOfValidity, Collection<OrdinalEra> ordinalEraSequence) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public TemporalCoordinate createTemporalCoordinate(TemporalReferenceSystem frame, IndeterminateValue indeterminatePosition, Number coordinateValue) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public TemporalCoordinateSystem createTemporalCoordinateSystem(Identifier name, Extent domainOfValidity, Date origin, Unit<Time> interval) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public TemporalPosition createTemporalPosition(TemporalReferenceSystem frame, IndeterminateValue indeterminatePosition) { + throw unsupported(); + } + + /** Unsupported. */ + @Override public TemporalReferenceSystem createTemporalReferenceSystem(Identifier name, Extent domainOfValidity) { + throw unsupported(); + } +} diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/Primitive.java b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/Primitive.java new file mode 100644 index 0000000..412c704 --- /dev/null +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/Primitive.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.temporal; + +import org.opengis.metadata.Identifier; +import org.opengis.temporal.Duration; +import org.opengis.temporal.RelativePosition; +import org.opengis.temporal.TemporalGeometricPrimitive; +import org.opengis.temporal.TemporalPrimitive; + + +/** + * Base implementation of GeoAPI temporal primitives. This is a temporary class; + * GeoAPI temporal interfaces are expected to change a lot in a future revision. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +class Primitive implements TemporalGeometricPrimitive, Identifier { + /** + * For sub-class constructors. + */ + Primitive() { + } + + /** + * The primary name by which this object is identified. + * This field is inherited from ISO 19111 {@code IdentifiedObject} and is in principle mandatory. + */ + @Override + public final Identifier getName() { + return this; + } + + /** + * Returns the string representation as the code for this object. This is not a correct identifier code, + * but we use that as a trick for forcing {@link org.apache.sis.util.collection.TreeTableFormat} to show + * the temporal value, because the formatter handles {@link org.opengis.referencing.IdentifiedObject} in + * a special way. + */ + @Override public final String getCode() { + return toString(); + } + + /** position of this primitive relative to another primitive. */ + @Override public final RelativePosition relativePosition(TemporalPrimitive other) { + throw DefaultTemporalFactory.unsupported(); + } + + /** Absolute value of the difference between temporal positions. */ + @Override public final Duration distance(TemporalGeometricPrimitive other) { + throw DefaultTemporalFactory.unsupported(); + } + + /** Duration of this temporal geometric primitive. */ + @Override public final Duration length() { + return null; // Do not throw an exception here; this is invoked by reflection. + } +} diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java new file mode 100644 index 0000000..b5039c3 --- /dev/null +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/temporal/package-info.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Place-holder for a future "sis-temporal" module. This is a temporary package; + * the temporal GeoAPI interfaces are expected to change a lot in a future revision. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +package org.apache.sis.internal.temporal; diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java b/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java index 46b554d..c4f697d 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java +++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/TemporalUtilities.java @@ -22,8 +22,8 @@ import org.opengis.temporal.Period; import org.opengis.temporal.TemporalFactory; import org.opengis.temporal.TemporalPrimitive; import org.apache.sis.util.Static; -import org.apache.sis.util.resources.Errors; import org.apache.sis.internal.system.DefaultFactories; +import org.apache.sis.internal.temporal.DefaultTemporalFactory; /** @@ -32,7 +32,7 @@ import org.apache.sis.internal.system.DefaultFactories; * * @author Martin Desruisseaux (Geomatys) * @author Guilhem Legal (Geomatys) - * @version 1.0 + * @version 1.2 * @since 0.3 * @module */ @@ -63,7 +63,7 @@ public final class TemporalUtilities extends Static { if (factory != null) { return factory; } - throw new UnsupportedOperationException(Errors.format(Errors.Keys.MissingRequiredModule_1, "sis-temporal")); + return DefaultTemporalFactory.INSTANCE; } /** diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java b/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java index fbddfdb..24dbef0 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java +++ b/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java @@ -898,7 +898,7 @@ public class TreeTableFormat extends TabularFormat<TreeTable> { /** * Creates a new format to use for parsing and formatting values of the given type. - * This method is invoked by the first time that a format is needed for the given type. + * This method is invoked the first time that a format is needed for the given type. * Subclasses can override this method if they want to configure the way dates, numbers * or other objects are formatted. * See {@linkplain org.apache.sis.io.CompoundFormat#createFormat(Class) parent class documentation} diff --git a/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java b/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java index 32df297..fe050d3 100644 --- a/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java +++ b/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java @@ -29,6 +29,7 @@ import org.opengis.metadata.content.CoverageContentType; import org.opengis.metadata.content.TransferFunctionType; import org.opengis.metadata.identification.Progress; import org.opengis.metadata.identification.TopicCategory; +import org.opengis.metadata.extent.TemporalExtent; import org.opengis.metadata.maintenance.ScopeCode; import org.opengis.metadata.spatial.DimensionNameType; import org.opengis.util.FactoryException; @@ -46,7 +47,7 @@ import static org.apache.sis.test.TestUtilities.date; * * @author Thi Phuong Hao Nguyen (VNSC) * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.8 * @module */ @@ -85,6 +86,7 @@ public class MetadataReaderTest extends TestCase { final ContentVerifier verifier = new ContentVerifier(); verifier.addPropertyToIgnore(Metadata.class, "metadataStandard"); // Because hard-coded in SIS. verifier.addPropertyToIgnore(Metadata.class, "referenceSystemInfo"); // Very verbose and depends on EPSG connection. + verifier.addPropertyToIgnore(TemporalExtent.class, "extent"); // Because currently time-zone sensitive. verifier.addMetadataToVerify(actual); verifier.addExpectedValues( "defaultLocale+otherLocale[0]", "en", diff --git a/storage/sis-geotiff/pom.xml b/storage/sis-geotiff/pom.xml index 5cb9a4f..9453689 100644 --- a/storage/sis-geotiff/pom.xml +++ b/storage/sis-geotiff/pom.xml @@ -132,6 +132,11 @@ <type>test-jar</type> <scope>test</scope> </dependency> + <dependency> + <groupId>org.glassfish.jaxb</groupId> + <artifactId>jaxb-runtime</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java index 299163f..5c94f25 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/XMLMetadata.java @@ -356,7 +356,8 @@ final class XMLMetadata implements Filter { * The `mergeMetadata` method applies heuristic rules for adding components. */ metadata.mergeMetadata(XML.unmarshal(new StAXSource(reader), - Collections.singletonMap(XML.WARNING_FILTER, this)), listeners.getLocale()); + Collections.singletonMap(XML.WARNING_FILTER, this)), + (listeners != null) ? listeners.getLocale() : null); } reader.close(); // No need to close the underlying input stream. } diff --git a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java index cb21eba..8d86a63 100644 --- a/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java +++ b/storage/sis-geotiff/src/test/java/org/apache/sis/storage/geotiff/XMLMetadataTest.java @@ -18,8 +18,10 @@ package org.apache.sis.storage.geotiff; import org.apache.sis.metadata.iso.DefaultMetadata; import org.apache.sis.internal.storage.MetadataBuilder; +import org.apache.sis.internal.xml.LegacyNamespaces; import org.apache.sis.util.collection.DefaultTreeTable; import org.apache.sis.util.collection.TableColumn; +import org.apache.sis.xml.Namespaces; import org.apache.sis.test.TestCase; import org.junit.Test; @@ -36,7 +38,7 @@ import static org.apache.sis.test.Assert.*; */ public final strictfp class XMLMetadataTest extends TestCase { /** - * A GDAL metadata. + * A GDAL metadata. The format is specific to the GDAL project. */ private static final String GDAL_METADATA = "<GDALMetadata>\n" + @@ -48,6 +50,34 @@ public final strictfp class XMLMetadataTest extends TestCase { "</GDALMetadata>\n"; /** + * A DGIWG metadata in ISO 19115 format. + */ + private static final String GEO_METADATA = + "<gmd:MD_Metadata\n" + + " xmlns:gmd = \"" + LegacyNamespaces.GMD + "\"\n" + + " xmlns:gml = \"" + Namespaces.GML + "\"\n>" + + " <gmd:identificationInfo>\n" + + " <gmd:MD_DataIdentification>\n" + + " <gmd:extent>\n" + + " <gmd:EX_Extent>\n" + + " <gmd:temporalElement>\n" + + " <gmd:EX_TemporalExtent>\n" + + " <gmd:extent>\n" + + " <gml:TimePeriod>\n" + + " <gml:description>Acquisition period</gml:description>\n" + + " <gml:beginPosition>2018-02-28T03:04:00</gml:beginPosition>\n" + + " <gml:endPosition>2018-02-28T04:48:00</gml:endPosition>\n" + + " </gml:TimePeriod>\n" + + " </gmd:extent>\n" + + " </gmd:EX_TemporalExtent>\n" + + " </gmd:temporalElement>\n" + + " </gmd:EX_Extent>\n" + + " </gmd:extent>\n" + + " </gmd:MD_DataIdentification>\n" + + " </gmd:identificationInfo>\n" + + "</gmd:MD_Metadata>\n"; + + /** * Tests parsing of GDAL metadata and formatting as a tree table. * THe XML document is like below: * @@ -100,7 +130,30 @@ public final strictfp class XMLMetadataTest extends TestCase { assertMultilinesEquals( "Metadata\n" + " └─Identification info\n" + - " └─Citation………………… My image\n", + " ├─Citation……………………………… My image\n" + + " └─Extent\n" + + " └─Temporal element\n" + + " └─Extent……………… [2018-02-28T04:48:00Z … 2018-02-28T03:04:00Z]\n", + metadata.toString()); + } + + /** + * Tests parsing DGIWG metadata and conversion to ISO 19115 metadata. + * + * @throws Exception if an error occurred during XML parsing. + */ + @Test + public void testGeoMetadata() throws Exception { + XMLMetadata xml = new XMLMetadata(GEO_METADATA, false); + MetadataBuilder builder = new MetadataBuilder(); + xml.appendTo(builder); + DefaultMetadata metadata = builder.build(false); + assertMultilinesEquals( + "Metadata\n" + + " └─Identification info\n" + + " └─Extent\n" + + " └─Temporal element\n" + + " └─Extent……………… [2018-02-28T02:04:00Z … 2018-02-28T03:48:00Z]\n", metadata.toString()); } } diff --git a/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java b/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java index 333f091..339ae0d 100644 --- a/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java +++ b/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java @@ -20,6 +20,7 @@ import java.io.IOException; import org.opengis.metadata.Metadata; import org.opengis.metadata.citation.Role; import org.opengis.metadata.citation.DateType; +import org.opengis.metadata.extent.TemporalExtent; import org.opengis.metadata.identification.KeywordType; import org.opengis.metadata.content.TransferFunctionType; import org.opengis.metadata.spatial.SpatialRepresentationType; @@ -44,7 +45,7 @@ import static org.apache.sis.test.TestUtilities.date; * for reading netCDF attributes. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.3 * @module */ @@ -104,6 +105,7 @@ public final strictfp class MetadataReaderTest extends TestCase { final ContentVerifier verifier = new ContentVerifier(); verifier.addPropertyToIgnore(Metadata.class, "metadataStandard"); verifier.addPropertyToIgnore(Metadata.class, "referenceSystemInfo"); + verifier.addPropertyToIgnore(TemporalExtent.class, "extent"); verifier.addMetadataToVerify(actual); verifier.addExpectedValues( // Hard-coded diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java index 5e498bf..a10e3e3 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java @@ -3209,7 +3209,7 @@ parse: for (int i = 0; i < length;) { * but some types of metadata components are accepted as well. * * @param source the source metadata to merge. Will never be modified. - * @param locale the locale to use for error message in exceptions. + * @param locale the locale to use for error message in exceptions, or {@code null} for the default locale. * @return {@code true} if the given source has been merged, * or {@code false} if its type is not managed by this builder. * @throws RuntimeException if the merge failed (may be {@link IllegalArgumentException},