Search in sources :

Example 1 with GeodeticObjectBuilder

use of org.apache.sis.internal.referencing.GeodeticObjectBuilder in project sis by apache.

the class DefaultProjectedCRSTest method testWKT1_WithExplicitAxisLength.

/**
 * Tests WKT 1 formatting of a pseudo-projection with explicit {@code "semi-major"} and {@code "semi-minor"}
 * parameter values. This was a way to define the Google pseudo-projection using standard projection method
 * name before EPSG introduced the <cite>"Popular Visualisation Pseudo Mercator"</cite> projection method.
 * The approach tested in this method is now deprecated at least for the Google projection (while it may
 * still be useful for other projections), but we still test it for compatibility reasons.
 *
 * @throws FactoryException if the CRS creation failed.
 */
@Test
@DependsOnMethod("testWKT1")
public void testWKT1_WithExplicitAxisLength() throws FactoryException {
    final ProjectedCRS crs = new GeodeticObjectBuilder().setConversionMethod("Mercator (variant A)").setConversionName("Popular Visualisation Pseudo-Mercator").setParameter("semi-major", 6378137, Units.METRE).setParameter("semi-minor", 6378137, Units.METRE).addName("WGS 84 / Pseudo-Mercator").createProjectedCRS(HardCodedCRS.WGS84, HardCodedCS.PROJECTED);
    assertWktEquals(Convention.WKT1, "PROJCS[“WGS 84 / Pseudo-Mercator”,\n" + "  GEOGCS[“WGS 84”,\n" + "    DATUM[“World Geodetic System 1984”,\n" + "      SPHEROID[“WGS84”, 6378137.0, 298.257223563]],\n" + "      PRIMEM[“Greenwich”, 0.0],\n" + "    UNIT[“degree”, 0.017453292519943295],\n" + "    AXIS[“Longitude”, EAST],\n" + "    AXIS[“Latitude”, NORTH]],\n" + "  PROJECTION[“Mercator_1SP”, AUTHORITY[“EPSG”, “9804”]],\n" + // Non-standard: appears because its value is different than the ellipsoid value.
    "  PARAMETER[“semi_minor”, 6378137.0],\n" + "  PARAMETER[“latitude_of_origin”, 0.0],\n" + "  PARAMETER[“central_meridian”, 0.0],\n" + "  PARAMETER[“scale_factor”, 1.0],\n" + "  PARAMETER[“false_easting”, 0.0],\n" + "  PARAMETER[“false_northing”, 0.0],\n" + "  UNIT[“metre”, 1],\n" + "  AXIS[“Easting”, EAST],\n" + "  AXIS[“Northing”, NORTH]]", crs);
    loggings.assertNextLogContains("semi_minor", "WGS84");
    loggings.assertNoUnexpectedLog();
}
Also used : ProjectedCRS(org.opengis.referencing.crs.ProjectedCRS) GeodeticObjectBuilder(org.apache.sis.internal.referencing.GeodeticObjectBuilder) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 2 with GeodeticObjectBuilder

use of org.apache.sis.internal.referencing.GeodeticObjectBuilder in project sis by apache.

the class CommonAuthorityFactory method createAuto.

/**
 * Creates a projected CRS from parameters in the {@code AUTO(2)} namespace.
 *
 * @param  code        the user-specified code, used only for error reporting.
 * @param  projection  the projection code (e.g. 42001).
 * @param  isLegacy    {@code true} if the code was found in {@code "AUTO"} or {@code "AUTO1"} namespace.
 * @param  factor      the multiplication factor for the unit of measurement.
 * @param  longitude   a longitude in the desired projection zone.
 * @param  latitude    a latitude in the desired projection zone.
 * @return the projected CRS for the given projection and parameters.
 */
@SuppressWarnings("null")
private ProjectedCRS createAuto(final String code, final int projection, final boolean isLegacy, final double factor, final double longitude, final double latitude) throws FactoryException {
    Boolean isUTM = null;
    String method = null;
    String param = null;
    switch(projection) {
        /*
             * 42001: Universal Transverse Mercator   —   central meridian must be in the center of a UTM zone.
             * 42002: Transverse Mercator             —   like 42001 except that central meridian can be anywhere.
             * 42003: WGS 84 / Auto Orthographic      —   defined by "Central_Meridian" and "Latitude_of_Origin".
             * 42004: WGS 84 / Auto Equirectangular   —   defined by "Central_Meridian" and "Standard_Parallel_1".
             * 42005: WGS 84 / Auto Mollweide         —   defined by "Central_Meridian" only.
             */
        case 42001:
            isUTM = true;
            break;
        case 42002:
            isUTM = (latitude == 0) && (Zoner.UTM.centralMeridian(Zoner.UTM.zone(0, longitude)) == longitude);
            break;
        case 42003:
            method = "Orthographic";
            param = Constants.LATITUDE_OF_ORIGIN;
            break;
        case 42004:
            method = "Equirectangular";
            param = Constants.STANDARD_PARALLEL_1;
            break;
        case 42005:
            method = "Mollweide";
            break;
        default:
            throw noSuchAuthorityCode(String.valueOf(projection), code, null);
    }
    /*
         * For the (Universal) Transverse Mercator case (AUTO:42001 and 42002), we delegate to the CommonCRS
         * enumeration if possible because CommonCRS will itself delegate to the EPSG factory if possible.
         * The Math.signum(latitude) instruction is for preventing "AUTO:42001" to handle the UTM special cases
         * (Norway and Svalbard) or to switch on the Universal Polar Stereographic projection for high latitudes,
         * because the WMS specification does not said that we should.
         */
    final CommonCRS datum = CommonCRS.WGS84;
    // To be set, directly or indirectly, to WGS84.geographic().
    final GeographicCRS baseCRS;
    // Temporary UTM projection, for extracting other properties.
    final ProjectedCRS crs;
    // Coordinate system with (E,N) axes in metres.
    CartesianCS cs;
    try {
        if (isUTM != null && isUTM) {
            crs = datum.universal(Math.signum(latitude), longitude);
            if (factor == (isLegacy ? Constants.EPSG_METRE : 1)) {
                return crs;
            }
            baseCRS = crs.getBaseCRS();
            cs = crs.getCoordinateSystem();
        } else {
            cs = projectedCS;
            if (cs == null) {
                crs = datum.universal(Math.signum(latitude), longitude);
                projectedCS = cs = crs.getCoordinateSystem();
                baseCRS = crs.getBaseCRS();
            } else {
                crs = null;
                baseCRS = datum.geographic();
            }
        }
        /*
             * At this point we got a coordinate system with axes in metres.
             * If the user asked for another unit of measurement, change the axes now.
             */
        Unit<Length> unit;
        if (isLegacy) {
            unit = createUnitFromEPSG(factor).asType(Length.class);
        } else {
            unit = Units.METRE;
            if (factor != 1)
                unit = unit.multiply(factor);
        }
        if (!Units.METRE.equals(unit)) {
            cs = (CartesianCS) CoordinateSystems.replaceLinearUnit(cs, unit);
        }
        /*
             * Set the projection name, operation method and parameters. The parameters for the Transverse Mercator
             * projection are a little bit more tedious to set, so we use a convenience method for that.
             */
        final GeodeticObjectBuilder builder = new GeodeticObjectBuilder();
        if (isUTM != null) {
            if (isUTM && crs != null) {
                builder.addName(crs.getName());
            }
            // else default to the conversion name, which is "Transverse Mercator".
            builder.setTransverseMercator(isUTM ? Zoner.UTM : Zoner.ANY, latitude, longitude);
        } else {
            builder.setConversionMethod(method).addName(PROJECTION_NAMES[projection - FIRST_PROJECTION_CODE]).setParameter(Constants.CENTRAL_MERIDIAN, longitude, Units.DEGREE);
            if (param != null) {
                builder.setParameter(param, latitude, Units.DEGREE);
            }
        }
        return builder.createProjectedCRS(baseCRS, cs);
    } catch (IllegalArgumentException e) {
        throw noSuchAuthorityCode(String.valueOf(projection), code, e);
    }
}
Also used : CartesianCS(org.opengis.referencing.cs.CartesianCS) ProjectedCRS(org.opengis.referencing.crs.ProjectedCRS) Length(javax.measure.quantity.Length) GeodeticObjectBuilder(org.apache.sis.internal.referencing.GeodeticObjectBuilder) InternationalString(org.opengis.util.InternationalString) SimpleInternationalString(org.apache.sis.util.iso.SimpleInternationalString) CommonCRS(org.apache.sis.referencing.CommonCRS) GeographicCRS(org.opengis.referencing.crs.GeographicCRS)

Example 3 with GeodeticObjectBuilder

use of org.apache.sis.internal.referencing.GeodeticObjectBuilder 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 4 with GeodeticObjectBuilder

use of org.apache.sis.internal.referencing.GeodeticObjectBuilder in project sis by apache.

the class DefaultProjectedCRSTest method testWKT2_ForEquirectangular.

/**
 * Tests formatting of “Equidistant Cylindrical (Spherical)” projected CRS. This one is a special case
 * because it is simplified to an affine transform. The referencing module should be able to find the
 * original projection parameters.
 *
 * @throws FactoryException if the CRS creation failed.
 */
@Test
@DependsOnMethod("testWKT2_Simplified")
public void testWKT2_ForEquirectangular() throws FactoryException {
    final ProjectedCRS crs = new GeodeticObjectBuilder().setConversionMethod("Equirectangular").setConversionName("Equidistant Cylindrical (Spherical)").setParameter("False easting", 1000, Units.METRE).setParameter("False northing", 2000, Units.METRE).addName("Equidistant Cylindrical (Spherical)").createProjectedCRS(HardCodedCRS.WGS84, HardCodedCS.PROJECTED);
    assertWktEquals(Convention.WKT2_SIMPLIFIED, "ProjectedCRS[“Equidistant Cylindrical (Spherical)”,\n" + "  BaseGeodCRS[“WGS 84”,\n" + "    Datum[“World Geodetic System 1984”,\n" + "      Ellipsoid[“WGS84”, 6378137.0, 298.257223563]],\n" + "    Unit[“degree”, 0.017453292519943295]],\n" + "  Conversion[“Equidistant Cylindrical (Spherical)”,\n" + "    Method[“Equidistant Cylindrical (Spherical)”],\n" + "    Parameter[“Latitude of 1st standard parallel”, 0.0],\n" + "    Parameter[“Longitude of natural origin”, 0.0],\n" + "    Parameter[“False easting”, 1000.0],\n" + "    Parameter[“False northing”, 2000.0]],\n" + "  CS[Cartesian, 2],\n" + "    Axis[“Easting (E)”, east],\n" + "    Axis[“Northing (N)”, north],\n" + "    Unit[“metre”, 1]]", crs);
}
Also used : ProjectedCRS(org.opengis.referencing.crs.ProjectedCRS) GeodeticObjectBuilder(org.apache.sis.internal.referencing.GeodeticObjectBuilder) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Aggregations

GeodeticObjectBuilder (org.apache.sis.internal.referencing.GeodeticObjectBuilder)4 ProjectedCRS (org.opengis.referencing.crs.ProjectedCRS)3 DependsOnMethod (org.apache.sis.test.DependsOnMethod)2 Test (org.junit.Test)2 Instant (java.time.Instant)1 Length (javax.measure.quantity.Length)1 Time (javax.measure.quantity.Time)1 GeneralEnvelope (org.apache.sis.geometry.GeneralEnvelope)1 CommonCRS (org.apache.sis.referencing.CommonCRS)1 DataStoreReferencingException (org.apache.sis.storage.DataStoreReferencingException)1 SimpleInternationalString (org.apache.sis.util.iso.SimpleInternationalString)1 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)1 GeographicCRS (org.opengis.referencing.crs.GeographicCRS)1 TemporalCRS (org.opengis.referencing.crs.TemporalCRS)1 CartesianCS (org.opengis.referencing.cs.CartesianCS)1 InternationalString (org.opengis.util.InternationalString)1