Search in sources :

Example 6 with TemporalCRS

use of org.opengis.referencing.crs.TemporalCRS in project sis by apache.

the class Store method parseEnvelope.

/**
 * Parses the envelope described by the header line starting with {@code @stboundedby}.
 * The envelope returned by this method will be stored in the {@link #envelope} field.
 *
 * <p>Example:</p>
 * {@preformat text
 *   &#64;stboundedby, urn:ogc:def:crs:CRS:1.3:84, 2D, 50.23 9.23, 50.31 9.27, 2012-01-17T12:33:41Z, 2012-01-17T12:37:00Z, sec
 * }
 *
 * This method sets {@link #timeEncoding} and {@link #spatialDimensionCount} as a side-effect.
 *
 * @param  elements  the line elements. The first elements should be {@code "@stboundedby"}.
 * @return the envelope, or {@code null} if the given list does not contain enough elements.
 */
@SuppressWarnings("fallthrough")
private GeneralEnvelope parseEnvelope(final List<String> elements) throws DataStoreException, FactoryException {
    CoordinateReferenceSystem crs = null;
    int spatialDimensionCount = 2;
    boolean isDimExplicit = false;
    double[] lowerCorner = ArraysExt.EMPTY_DOUBLE;
    double[] upperCorner = ArraysExt.EMPTY_DOUBLE;
    Instant startTime = null;
    Instant endTime = null;
    Unit<Time> timeUnit = Units.SECOND;
    boolean isTimeAbsolute = false;
    int ordinal = -1;
    for (final String element : elements) {
        ordinal++;
        if (!element.isEmpty()) {
            switch(ordinal) {
                // The "@stboundedby" header.
                case 0:
                    continue;
                case 1:
                    crs = CRS.forCode(element);
                    continue;
                case 2:
                    if (element.length() == 2 && Character.toUpperCase(element.charAt(1)) == 'D') {
                        isDimExplicit = true;
                        spatialDimensionCount = element.charAt(0) - '0';
                        if (spatialDimensionCount < 1 || spatialDimensionCount > 3) {
                            throw new DataStoreReferencingException(errors().getString(Errors.Keys.IllegalCoordinateSystem_1, element));
                        }
                        continue;
                    }
                    /*
                             * According the Moving Feature specification, the [dim] element is optional.
                             * If we did not recognized the dimension, assume that we have the next element
                             * (i.e. the lower corner). Fall-through so we can process it.
                             */
                    // Fall through
                    ordinal++;
                case 3:
                    lowerCorner = CharSequences.parseDoubles(element, ORDINATE_SEPARATOR);
                    continue;
                case 4:
                    upperCorner = CharSequences.parseDoubles(element, ORDINATE_SEPARATOR);
                    continue;
                case 5:
                    startTime = Instant.parse(element);
                    continue;
                case 6:
                    endTime = Instant.parse(element);
                    continue;
                case 7:
                    switch(element.toLowerCase(Locale.US)) {
                        case "sec":
                        case "second":
                            /* Already SECOND. */
                            continue;
                        case "minute":
                            timeUnit = Units.MINUTE;
                            continue;
                        case "hour":
                            timeUnit = Units.HOUR;
                            continue;
                        case "day":
                            timeUnit = Units.DAY;
                            continue;
                        case "absolute":
                            isTimeAbsolute = true;
                            continue;
                        default:
                            throw new DataStoreReferencingException(errors().getString(Errors.Keys.UnknownUnit_1, element));
                    }
            }
            // If we reach this point, there is some remaining unknown elements. Ignore them.
            break;
        }
    }
    /*
         * Complete the CRS by adding a vertical component if needed, then a temporal component.
         * Only after the CRS has been completed we can create the envelope.
         *
         * Vertical component:
         *   Ideally, should be part of the CRS created from the authority code. But if the authority
         *   code is only for a two-dimensional CRS, we default to an arbitrary height component.
         *
         * Temporal component:
         *   Assumed never part of the authority code. We need to build the temporal component ourselves
         *   in order to set the origin to the start time.
         */
    final GeneralEnvelope envelope;
    if (crs != null) {
        int count = 0;
        final CoordinateReferenceSystem[] components = new CoordinateReferenceSystem[3];
        components[count++] = crs;
        /*
             * If the coordinates are three-dimensional but the CRS is 2D, add a vertical axis.
             * The vertical axis shall be the third one, however we do not enforce that rule
             * since Apache SIS should work correctly even if the vertical axis is elsewhere.
             */
        int dimension = crs.getCoordinateSystem().getDimension();
        if (isDimExplicit) {
            if (spatialDimensionCount > dimension) {
                components[count++] = CommonCRS.Vertical.MEAN_SEA_LEVEL.crs();
                dimension++;
            }
            if (dimension != spatialDimensionCount) {
                throw new DataStoreReferencingException(errors().getString(Errors.Keys.MismatchedDimension_3, "@stboundedby(CRS)", spatialDimensionCount, dimension));
            }
        }
        if (dimension > Short.MAX_VALUE) {
            throw new DataStoreReferencingException(errors().getString(Errors.Keys.ExcessiveNumberOfDimensions_1, dimension));
        }
        spatialDimensionCount = dimension;
        /*
             * Add a temporal axis if we have a start time (no need for end time).
             * This block presumes that the CRS does not already have a time axis.
             * If a time axis was already present, an exception will be thrown at
             * builder.createCompoundCRS(…) invocation time.
             */
        final GeodeticObjectBuilder builder = new GeodeticObjectBuilder();
        String name = crs.getName().getCode();
        if (startTime != null) {
            final TemporalCRS temporal;
            if (isTimeAbsolute) {
                temporal = TimeEncoding.DEFAULT.crs();
                timeEncoding = TimeEncoding.ABSOLUTE;
            } else {
                temporal = builder.createTemporalCRS(Date.from(startTime), timeUnit);
                timeEncoding = new TimeEncoding(temporal.getDatum(), timeUnit);
            }
            components[count++] = temporal;
            name = name + " + " + temporal.getName().getCode();
        }
        crs = builder.addName(name).createCompoundCRS(ArraysExt.resize(components, count));
        envelope = new GeneralEnvelope(crs);
    } else {
        /*
             * While illegal in principle, Apache SIS accepts missing CRS.
             * In such case, use only the number of dimensions.
             */
        int dim = spatialDimensionCount;
        // Same criterion than in above block.
        if (startTime != null)
            dim++;
        envelope = new GeneralEnvelope(dim);
    }
    /*
         * At this point we got the three- or four-dimensional spatio-temporal CRS.
         * We can now set the envelope coordinate values, including temporal values.
         */
    int dim;
    if ((dim = lowerCorner.length) != spatialDimensionCount || (dim = upperCorner.length) != spatialDimensionCount) {
        throw new DataStoreReferencingException(errors().getString(Errors.Keys.MismatchedDimension_3, "@stboundedby(BBOX)", spatialDimensionCount, dim));
    }
    for (int i = 0; i < spatialDimensionCount; i++) {
        envelope.setRange(i, lowerCorner[i], upperCorner[i]);
    }
    if (startTime != null) {
        envelope.setRange(spatialDimensionCount, timeEncoding.toCRS(startTime.toEpochMilli()), (endTime == null) ? Double.NaN : timeEncoding.toCRS(endTime.toEpochMilli()));
    }
    this.spatialDimensionCount = (short) spatialDimensionCount;
    return envelope;
}
Also used : Instant(java.time.Instant) Time(javax.measure.quantity.Time) TemporalCRS(org.opengis.referencing.crs.TemporalCRS) DataStoreReferencingException(org.apache.sis.storage.DataStoreReferencingException) GeodeticObjectBuilder(org.apache.sis.internal.referencing.GeodeticObjectBuilder) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem) GeneralEnvelope(org.apache.sis.geometry.GeneralEnvelope)

Example 7 with TemporalCRS

use of org.opengis.referencing.crs.TemporalCRS in project sis by apache.

the class CoordinateFormat method initialize.

/**
 * Computes the value of transient fields from the given CRS.
 */
private void initialize(final CoordinateReferenceSystem crs) {
    types = null;
    formats = null;
    units = null;
    toFormatUnit = null;
    unitSymbols = null;
    epochs = null;
    negate = 0;
    lastCRS = crs;
    if (crs == null) {
        return;
    }
    /*
         * If no CRS were specified, we will format everything as numbers. Working with null CRS
         * is sometime useful because null CRS are allowed in DirectPosition according ISO 19107.
         * Otherwise (if a CRS is given), infer the format subclasses from the axes.
         */
    final CoordinateSystem cs = crs.getCoordinateSystem();
    final int dimension = cs.getDimension();
    final byte[] types = new byte[dimension];
    final Format[] formats = new Format[dimension];
    for (int i = 0; i < dimension; i++) {
        final CoordinateSystemAxis axis = cs.getAxis(i);
        final Unit<?> unit = axis.getUnit();
        /*
             * Formatter for angular units. Target unit is DEGREE_ANGLE.
             * Type is LONGITUDE, LATITUDE or ANGLE depending on axis direction.
             */
        if (Units.isAngular(unit)) {
            byte type = ANGLE;
            final AxisDirection dir = axis.getDirection();
            if (AxisDirection.NORTH.equals(dir)) {
                type = LATITUDE;
            } else if (AxisDirection.EAST.equals(dir)) {
                type = LONGITUDE;
            } else if (AxisDirection.SOUTH.equals(dir)) {
                type = LATITUDE;
                negate(i);
            } else if (AxisDirection.WEST.equals(dir)) {
                type = LONGITUDE;
                negate(i);
            }
            types[i] = type;
            formats[i] = getFormat(Angle.class);
            setConverter(dimension, i, unit.asType(javax.measure.quantity.Angle.class).getConverterTo(Units.DEGREE));
            continue;
        }
        /*
             * Formatter for temporal units. Target unit is MILLISECONDS.
             * Type is DATE.
             */
        if (Units.isTemporal(unit)) {
            final CoordinateReferenceSystem t = CRS.getComponentAt(crs, i, i + 1);
            if (t instanceof TemporalCRS) {
                if (epochs == null) {
                    epochs = new long[dimension];
                }
                types[i] = DATE;
                formats[i] = getFormat(Date.class);
                epochs[i] = ((TemporalCRS) t).getDatum().getOrigin().getTime();
                setConverter(dimension, i, unit.asType(Time.class).getConverterTo(Units.MILLISECOND));
                if (AxisDirection.PAST.equals(axis.getDirection())) {
                    negate(i);
                }
                continue;
            }
            types[i] = TIME;
        // Fallthrough: formatted as number.
        }
        /*
             * Formatter for all other units. Do NOT set types[i] since it may have been set
             * to a non-zero value by previous case. If not, the default value (zero) is the
             * one we want.
             */
        formats[i] = getFormat(Number.class);
        if (unit != null) {
            if (units == null) {
                units = new Unit<?>[dimension];
            }
            units[i] = unit;
            final String symbol = getFormat(Unit.class).format(unit);
            if (!symbol.isEmpty()) {
                if (unitSymbols == null) {
                    unitSymbols = new String[dimension];
                }
                unitSymbols[i] = symbol;
            }
        }
    }
    // Assign only on success.
    this.types = types;
    this.formats = formats;
}
Also used : CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) Unit(javax.measure.Unit) Date(java.util.Date) TemporalCRS(org.opengis.referencing.crs.TemporalCRS) Format(java.text.Format) SimpleDateFormat(java.text.SimpleDateFormat) NumberFormat(java.text.NumberFormat) DateFormat(java.text.DateFormat) AngleFormat(org.apache.sis.measure.AngleFormat) CompoundFormat(org.apache.sis.io.CompoundFormat) DecimalFormat(java.text.DecimalFormat) Angle(org.apache.sis.measure.Angle) AxisDirection(org.opengis.referencing.cs.AxisDirection) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Example 8 with TemporalCRS

use of org.opengis.referencing.crs.TemporalCRS in project sis by apache.

the class SubTypes method castOrCopy.

/**
 * Returns a SIS implementation for the given coordinate reference system.
 *
 * @see AbstractCRS#castOrCopy(CoordinateReferenceSystem)
 */
static AbstractCRS castOrCopy(final CoordinateReferenceSystem object) {
    if (object instanceof DerivedCRS) {
        return DefaultDerivedCRS.castOrCopy((DerivedCRS) object);
    }
    if (object instanceof ProjectedCRS) {
        return DefaultProjectedCRS.castOrCopy((ProjectedCRS) object);
    }
    if (object instanceof GeodeticCRS) {
        if (object instanceof GeographicCRS) {
            return DefaultGeographicCRS.castOrCopy((GeographicCRS) object);
        }
        if (object instanceof GeocentricCRS) {
            return DefaultGeocentricCRS.castOrCopy((GeocentricCRS) object);
        }
        /*
             * The GeographicCRS and GeocentricCRS types are not part of ISO 19111.
             * ISO uses a single type, GeodeticCRS, for both of them and infer the
             * geographic or geocentric type from the coordinate system. We do this
             * check here for instantiating the most appropriate SIS type, but only
             * if we need to create a new object anyway (see below for rational).
             */
        if (object instanceof DefaultGeodeticCRS) {
            /*
                 * Result of XML unmarshalling — keep as-is. We avoid creating a new object because it
                 * would break object identities specified in GML document by the xlink:href attribute.
                 * However we may revisit this policy in the future. See SC_CRS.setElement(AbstractCRS).
                 */
            return (DefaultGeodeticCRS) object;
        }
        final Map<String, ?> properties = IdentifiedObjects.getProperties(object);
        final GeodeticDatum datum = ((GeodeticCRS) object).getDatum();
        final CoordinateSystem cs = object.getCoordinateSystem();
        if (cs instanceof EllipsoidalCS) {
            return new DefaultGeographicCRS(properties, datum, (EllipsoidalCS) cs);
        }
        if (cs instanceof SphericalCS) {
            return new DefaultGeocentricCRS(properties, datum, (SphericalCS) cs);
        }
        if (cs instanceof CartesianCS) {
            return new DefaultGeocentricCRS(properties, datum, (CartesianCS) cs);
        }
    }
    if (object instanceof VerticalCRS) {
        return DefaultVerticalCRS.castOrCopy((VerticalCRS) object);
    }
    if (object instanceof TemporalCRS) {
        return DefaultTemporalCRS.castOrCopy((TemporalCRS) object);
    }
    if (object instanceof EngineeringCRS) {
        return DefaultEngineeringCRS.castOrCopy((EngineeringCRS) object);
    }
    if (object instanceof ImageCRS) {
        return DefaultImageCRS.castOrCopy((ImageCRS) object);
    }
    if (object instanceof CompoundCRS) {
        return DefaultCompoundCRS.castOrCopy((CompoundCRS) object);
    }
    /*
         * Intentionally check for AbstractCRS after the interfaces because user may have defined his own
         * subclass implementing the interface. If we were checking for AbstractCRS before the interfaces,
         * the returned instance could have been a user subclass without the JAXB annotations required
         * for XML marshalling.
         */
    if (object == null || object instanceof AbstractCRS) {
        return (AbstractCRS) object;
    }
    return new AbstractCRS(object);
}
Also used : CartesianCS(org.opengis.referencing.cs.CartesianCS) EngineeringCRS(org.opengis.referencing.crs.EngineeringCRS) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) DerivedCRS(org.opengis.referencing.crs.DerivedCRS) CompoundCRS(org.opengis.referencing.crs.CompoundCRS) GeodeticDatum(org.opengis.referencing.datum.GeodeticDatum) GeodeticCRS(org.opengis.referencing.crs.GeodeticCRS) SphericalCS(org.opengis.referencing.cs.SphericalCS) TemporalCRS(org.opengis.referencing.crs.TemporalCRS) ProjectedCRS(org.opengis.referencing.crs.ProjectedCRS) ImageCRS(org.opengis.referencing.crs.ImageCRS) VerticalCRS(org.opengis.referencing.crs.VerticalCRS) EllipsoidalCS(org.opengis.referencing.cs.EllipsoidalCS) GeographicCRS(org.opengis.referencing.crs.GeographicCRS) GeocentricCRS(org.opengis.referencing.crs.GeocentricCRS)

Example 9 with TemporalCRS

use of org.opengis.referencing.crs.TemporalCRS in project sis by apache.

the class GeodeticObjectBuilder method createTemporalCRS.

/**
 * Creates a temporal CRS from the given origin and temporal unit. For this method, the CRS name is optional:
 * if no {@code addName(…)} method has been invoked, then a default name will be used.
 *
 * @param  origin  the epoch in milliseconds since January 1st, 1970 at midnight UTC.
 * @param  unit    the unit of measurement.
 * @return a temporal CRS using the given origin and units.
 * @throws FactoryException if an error occurred while building the temporal CRS.
 */
public TemporalCRS createTemporalCRS(final Date origin, final Unit<Time> unit) throws FactoryException {
    /*
         * Try to use one of the pre-defined datum and coordinate system if possible.
         * This not only saves a little bit of memory, but also provides better names.
         */
    TimeCS cs = null;
    TemporalDatum datum = null;
    for (final CommonCRS.Temporal c : CommonCRS.Temporal.values()) {
        if (datum == null) {
            final TemporalDatum candidate = c.datum();
            if (origin.equals(candidate.getOrigin())) {
                datum = candidate;
            }
        }
        if (cs == null) {
            final TemporalCRS crs = c.crs();
            final TimeCS candidate = crs.getCoordinateSystem();
            if (unit.equals(candidate.getAxis(0).getUnit())) {
                if (datum == candidate && properties.isEmpty()) {
                    return crs;
                }
                cs = candidate;
            }
        }
    }
    /*
         * Create the datum and coordinate system before the CRS if we were not able to use a pre-defined object.
         * In the datum case, we will use the same metadata than the CRS (domain of validity, scope, etc.) except
         * the identifier and the remark.
         */
    onCreate(false);
    try {
        if (cs == null) {
            final CSFactory csFactory = getCSFactory();
            // To be used as a template, except for units.
            cs = CommonCRS.Temporal.JAVA.crs().getCoordinateSystem();
            cs = csFactory.createTimeCS(name(cs), csFactory.createCoordinateSystemAxis(name(cs.getAxis(0)), "t", AxisDirection.FUTURE, unit));
        }
        if (properties.get(TemporalCRS.NAME_KEY) == null) {
            properties.putAll(name(cs));
        }
        if (datum == null) {
            final Object remarks = properties.remove(TemporalCRS.REMARKS_KEY);
            final Object identifier = properties.remove(TemporalCRS.IDENTIFIERS_KEY);
            datum = getDatumFactory().createTemporalDatum(properties, origin);
            properties.put(TemporalCRS.IDENTIFIERS_KEY, identifier);
            properties.put(TemporalCRS.REMARKS_KEY, remarks);
            // Share the Identifier instance.
            properties.put(TemporalCRS.NAME_KEY, datum.getName());
        }
        return getCRSFactory().createTemporalCRS(properties, datum, cs);
    } finally {
        onCreate(true);
    }
}
Also used : TemporalDatum(org.opengis.referencing.datum.TemporalDatum) TemporalCRS(org.opengis.referencing.crs.TemporalCRS) CSFactory(org.opengis.referencing.cs.CSFactory) IdentifiedObject(org.opengis.referencing.IdentifiedObject) CommonCRS(org.apache.sis.referencing.CommonCRS) TimeCS(org.opengis.referencing.cs.TimeCS)

Example 10 with TemporalCRS

use of org.opengis.referencing.crs.TemporalCRS in project sis by apache.

the class CommonCRSTest method testTemporal.

/**
 * Verifies the epoch values of temporal enumeration compared to the Julian epoch.
 *
 * @see <a href="http://en.wikipedia.org/wiki/Julian_day">Wikipedia: Julian day</a>
 */
@Test
public void testTemporal() {
    final double julianEpoch = CommonCRS.Temporal.JULIAN.datum().getOrigin().getTime() / DAY_LENGTH;
    assertTrue(julianEpoch < 0);
    for (final CommonCRS.Temporal e : CommonCRS.Temporal.values()) {
        final String epoch;
        final double days;
        switch(e) {
            // Fall through
            case JAVA:
            case UNIX:
                epoch = "1970-01-01 00:00:00";
                days = 2440587.5;
                break;
            case TRUNCATED_JULIAN:
                epoch = "1968-05-24 00:00:00";
                days = 2440000.5;
                break;
            case DUBLIN_JULIAN:
                epoch = "1899-12-31 12:00:00";
                days = 2415020.0;
                break;
            case MODIFIED_JULIAN:
                epoch = "1858-11-17 00:00:00";
                days = 2400000.5;
                break;
            case JULIAN:
                epoch = "4713-01-01 12:00:00";
                days = 0;
                break;
            default:
                throw new AssertionError(e);
        }
        final String name = e.name();
        final TemporalDatum datum = e.datum();
        final TemporalCRS crs = e.crs();
        final Date origin = datum.getOrigin();
        Validators.validate(crs);
        // Datum before CRS creation.
        assertSame(name, datum, e.datum());
        // Datum after CRS creation.
        assertSame(name, crs.getDatum(), e.datum());
        assertEquals(name, epoch, format(origin));
        assertEquals(name, days, origin.getTime() / DAY_LENGTH - julianEpoch, 0);
    }
}
Also used : TemporalDatum(org.opengis.referencing.datum.TemporalDatum) TemporalCRS(org.opengis.referencing.crs.TemporalCRS) Date(java.util.Date) Test(org.junit.Test)

Aggregations

TemporalCRS (org.opengis.referencing.crs.TemporalCRS)11 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)7 VerticalCRS (org.opengis.referencing.crs.VerticalCRS)5 Test (org.junit.Test)4 DefaultTemporalCRS (org.apache.sis.referencing.crs.DefaultTemporalCRS)3 GeographicCRS (org.opengis.referencing.crs.GeographicCRS)3 TransformException (org.opengis.referencing.operation.TransformException)3 Date (java.util.Date)2 DefaultGeographicBoundingBox (org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox)2 DefaultVerticalExtent (org.apache.sis.metadata.iso.extent.DefaultVerticalExtent)2 DependsOnMethod (org.apache.sis.test.DependsOnMethod)2 ProjectedCRS (org.opengis.referencing.crs.ProjectedCRS)2 SingleCRS (org.opengis.referencing.crs.SingleCRS)2 CoordinateSystem (org.opengis.referencing.cs.CoordinateSystem)2 TemporalDatum (org.opengis.referencing.datum.TemporalDatum)2 DateFormat (java.text.DateFormat)1 DecimalFormat (java.text.DecimalFormat)1 Format (java.text.Format)1 NumberFormat (java.text.NumberFormat)1 SimpleDateFormat (java.text.SimpleDateFormat)1