Search in sources :

Example 1 with IdentifiedObject

use of org.opengis.referencing.IdentifiedObject in project sis by apache.

the class EPSGFactoryTest method testFindGeographic.

/**
 * Tests {@link EPSGFactory#newIdentifiedObjectFinder()} method with a geographic CRS.
 *
 * @throws FactoryException if an error occurred while querying the factory.
 */
@Test
@DependsOnMethod("testWGS84")
public void testFindGeographic() throws FactoryException {
    final EPSGFactory factory = TestFactorySource.factory;
    assumeNotNull(factory);
    final IdentifiedObjectFinder finder = factory.newIdentifiedObjectFinder();
    final DefaultGeographicCRS crs = (DefaultGeographicCRS) CRS.fromWKT("GEOGCS[“WGS 84”,\n" + // Use the alias instead than primary name for forcing a deeper search.
    "  DATUM[“WGS 84”,\n" + // Different name for forcing a deeper search.
    "    SPHEROID[“WGS 1984”, 6378137.0, 298.257223563]],\n" + "  PRIMEM[“Greenwich”, 0.0],\n" + "  UNIT[“degree”, 0.017453292519943295],\n" + "  AXIS[“Geodetic latitude”, NORTH],\n" + "  AXIS[“Geodetic longitude”, EAST]]");
    /*
         * First, search for a CRS with axis order that does not match the ones in the EPSG database.
         * IdentifiedObjectFinder should not accept EPSG:4326 as a match for the given CRS.
         */
    assertEquals("Full scan should be enabled by default.", IdentifiedObjectFinder.Domain.VALID_DATASET, finder.getSearchDomain());
    assertTrue("Should not find WGS84 because the axis order is not the same.", finder.find(crs.forConvention(AxesConvention.NORMALIZED)).isEmpty());
    /*
         * Ensure that the cache is empty.
         */
    finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
    assertTrue("Should not find without a full scan, because the WKT contains no identifier " + "and the CRS name is ambiguous (more than one EPSG object have this name).", finder.find(crs).isEmpty());
    /*
         * Scan the database for searching the CRS.
         */
    finder.setSearchDomain(IdentifiedObjectFinder.Domain.ALL_DATASET);
    final IdentifiedObject found = finder.findSingleton(crs);
    assertNotNull("With full scan allowed, the CRS should be found.", found);
    assertEpsgNameAndIdentifierEqual("WGS 84", 4326, found);
    /*
         * Should find the CRS without the need of a full scan, because of the cache.
         */
    finder.setSearchDomain(IdentifiedObjectFinder.Domain.DECLARATION);
    assertSame("The CRS should still in the cache.", found, finder.findSingleton(crs));
}
Also used : IdentifiedObjectFinder(org.apache.sis.referencing.factory.IdentifiedObjectFinder) IdentifiedObject(org.opengis.referencing.IdentifiedObject) DefaultGeographicCRS(org.apache.sis.referencing.crs.DefaultGeographicCRS) Test(org.junit.Test) DependsOnMethod(org.apache.sis.test.DependsOnMethod)

Example 2 with IdentifiedObject

use of org.opengis.referencing.IdentifiedObject in project sis by apache.

the class FeatureFormat method formatValue.

/**
 * Appends the given attribute value, in a truncated form if it exceed the maximal value length.
 *
 * @param  value   the value to append.
 * @param  table   where to append the value.
 * @param  length  number of characters appended before this method call in the current table cell.
 * @return number of characters appended after this method call in the current table cell, or -1 if
 *         the length exceed the maximal length (in which case the caller should break iteration).
 */
private int formatValue(final Object value, final TableAppender table, final int length) {
    String text;
    if (value instanceof InternationalString) {
        text = ((InternationalString) value).toString(displayLocale);
    } else if (value instanceof GenericName) {
        text = toString((GenericName) value);
    } else if (value instanceof AbstractIdentifiedType) {
        text = toString(((AbstractIdentifiedType) value).getName());
    } else if (value instanceof IdentifiedObject) {
        text = IdentifiedObjects.getIdentifierOrName((IdentifiedObject) value);
    } else if ((text = Geometries.toString(value)) == null) {
        text = value.toString();
    }
    final int remaining = MAXIMAL_VALUE_LENGTH - length;
    if (remaining >= text.length()) {
        table.append(text);
        return length + text.length();
    } else {
        table.append(text, 0, Math.max(0, remaining - 1)).append('…');
        return -1;
    }
}
Also used : GenericName(org.opengis.util.GenericName) InternationalString(org.opengis.util.InternationalString) InternationalString(org.opengis.util.InternationalString) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Example 3 with IdentifiedObject

use of org.opengis.referencing.IdentifiedObject in project sis by apache.

the class Formatter method append.

/**
 * Appends the given {@code FormattableObject}.
 * This method performs the following steps:
 *
 * <ul>
 *   <li>Invoke <code>object.{@linkplain FormattableObject#formatTo(Formatter) formatTo}(this)</code>.</li>
 *   <li>Prepend the keyword returned by the above method call (e.g. {@code "GEOCS"}).</li>
 *   <li>If the given object is an instance of {@link IdentifiedObject}, then append complementary information:</li>
 * </ul>
 *
 * <blockquote><table class="sis">
 *   <caption>Complementary WKT elements</caption>
 *   <tr><th>WKT 2 element</th><th>WKT 1 element</th><th>For types</th></tr>
 *   <tr><td>{@code Anchor[…]}</td>        <td></td> <td>{@link Datum}</td></tr>
 *   <tr><td>{@code Scope[…]}</td>         <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
 *   <tr><td>{@code Area[…]}</td>          <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
 *   <tr><td>{@code BBox[…]}</td>          <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
 *   <tr><td>{@code VerticalExtent[…]}</td><td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
 *   <tr><td>{@code TimeExtent[…]}</td>    <td></td> <td>{@link ReferenceSystem}, {@link Datum}, {@link CoordinateOperation}</td></tr>
 *   <tr><td>{@code Id[…]}</td><td>{@code Authority[…]}</td><td>{@link IdentifiedObject}</td></tr>
 *   <tr><td>{@code Remarks[…]}</td>       <td></td> <td>{@link ReferenceSystem}, {@link CoordinateOperation}</td></tr>
 * </table></blockquote>
 *
 * @param  object  the formattable object to append to the WKT, or {@code null} if none.
 */
public void append(final FormattableObject object) {
    if (object == null) {
        return;
    }
    /*
         * Safety check: ensure that we do not have circular dependencies (e.g. a ProjectedCRS contains
         * a Conversion which may contain the ProjectedCRS as its target CRS). Without this protection,
         * a circular dependency would cause an OutOfMemoryError.
         */
    final int stackDepth = enclosingElements.size();
    for (int i = stackDepth; --i >= 0; ) {
        if (enclosingElements.get(i) == object) {
            throw new IllegalStateException(Errors.getResources(locale).getString(Errors.Keys.CircularReference));
        }
    }
    enclosingElements.add(object);
    if (hasContextualUnit < 0) {
        // Test if leftmost bit is set to 1.
        throw new IllegalStateException(Errors.getResources(locale).getString(Errors.Keys.TreeDepthExceedsMaximum));
    }
    hasContextualUnit <<= 1;
    /*
         * Add a new line if it was requested, open the bracket and increase indentation in case the
         * element to format contains other FormattableObject elements.
         */
    appendSeparator();
    int base = buffer.length();
    elementStart = buffer.appendCodePoint(symbols.getOpeningBracket(0)).length();
    indent(+1);
    /*
         * Formats the inner part, then prepend the WKT keyword.
         * The result looks like the following:
         *
         *         <previous text>,
         *           PROJCS["NAD27 / Idaho Central",
         *             GEOGCS[...etc...],
         *             ...etc...
         */
    IdentifiedObject info = (object instanceof IdentifiedObject) ? (IdentifiedObject) object : null;
    String keyword = object.formatTo(this);
    if (keyword == null) {
        if (info != null) {
            setInvalidWKT(info, null);
        } else {
            setInvalidWKT(object.getClass(), null);
        }
        keyword = getName(object.getClass());
    } else if (toUpperCase != 0) {
        final Locale locale = symbols.getLocale();
        keyword = (toUpperCase >= 0) ? keyword.toUpperCase(locale) : keyword.toLowerCase(locale);
    }
    if (highlightError && colors != null) {
        final String color = colors.getAnsiSequence(ElementKind.ERROR);
        if (color != null) {
            buffer.insert(base, color + BACKGROUND_DEFAULT);
            base += color.length();
        }
    }
    highlightError = false;
    buffer.insert(base, keyword);
    /*
         * When formatting geometry coordinates, we may need to shift all numbers by the width
         * of the keyword inserted above in order to keep numbers properly aligned. Exemple:
         *
         *     BOX[ 4.000 -10.000
         *         50.000   2.000]
         */
    if (keywordSpaceAt != null) {
        final int length = keyword.length();
        final CharSequence additionalMargin = CharSequences.spaces(keyword.codePointCount(0, length));
        final int n = keywordSpaceAt.size();
        for (int i = 0; i < n; ) {
            int p = keywordSpaceAt.getInt(i);
            // Take in account spaces added previously.
            p += (++i * length);
            buffer.insert(p, additionalMargin);
        }
        keywordSpaceAt.clear();
    }
    /*
         * Format the SCOPE["…"], AREA["…"] and other elements. Some of those information
         * are available only for Datum, CoordinateOperation and ReferenceSystem objects.
         */
    if (info == null && convention.majorVersion() != 1 && object instanceof GeneralParameterValue) {
        info = ((GeneralParameterValue) object).getDescriptor();
    }
    if (info != null) {
        appendComplement(info, (stackDepth >= 1) ? enclosingElements.get(stackDepth - 1) : null, (stackDepth >= 2) ? enclosingElements.get(stackDepth - 2) : null);
    }
    /*
         * Close the bracket, then update the queue of enclosed elements by removing this element.
         */
    buffer.appendCodePoint(symbols.getClosingBracket(0));
    indent(-1);
    enclosingElements.remove(stackDepth);
    hasContextualUnit >>>= 1;
}
Also used : Locale(java.util.Locale) GeneralParameterValue(org.opengis.parameter.GeneralParameterValue) InternationalString(org.opengis.util.InternationalString) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Example 4 with IdentifiedObject

use of org.opengis.referencing.IdentifiedObject in project sis by apache.

the class Formatter method appendComplement.

/**
 * Appends the optional complementary attributes common to many {@link IdentifiedObject} subtypes.
 * Those attributes are {@code ANCHOR}, {@code SCOPE}, {@code AREA}, {@code BBOX}, {@code VERTICALEXTENT},
 * {@code TIMEEXTENT}, {@code ID} (previously known as {@code AUTHORITY}) and {@code REMARKS},
 * and have a special treatment: they are written by {@link #append(FormattableObject)}
 * after the {@code formatTo(Formatter)} method returned.
 *
 * <p>The {@code ID[<name>,<code>,…]} element is normally written only for the root element
 * (unless the convention is {@code INTERNAL}), but there is various exceptions to this rule.
 * If formatted, the {@code ID} element will be by default on the same line than the enclosing
 * element (e.g. {@code SPHEROID["Clarke 1866", …, ID["EPSG", 7008]]}). Other example:</p>
 *
 * {@preformat text
 *   PROJCS["NAD27 / Idaho Central",
 *     GEOGCS[...etc...],
 *     ...etc...
 *     ID["EPSG", 26769]]
 * }
 *
 * For non-internal conventions, all elements other than {@code ID[…]} are formatted
 * only for {@link CoordinateOperation} and root {@link ReferenceSystem} instances,
 * with an exception for remarks of {@code ReferenceSystem} embedded inside {@code CoordinateOperation}.
 * Those restrictions are our interpretation of the following ISO 19162 requirement:
 *
 * <blockquote>(…snip…) {@code <scope extent identifier remark>} is a collection of four optional attributes
 * which may be applied to a coordinate reference system, a coordinate operation or a boundCRS. (…snip…)
 * Identifier (…snip…) may also be utilised for components of these objects although this is not recommended
 * except for coordinate operation methods (including map projections) and parameters. (…snip…)
 * A {@code <remark>} can be included within the descriptions of source and target CRS embedded within
 * a coordinate transformation as well as within the coordinate transformation itself.</blockquote>
 */
@SuppressWarnings("null")
private void appendComplement(final IdentifiedObject object, final FormattableObject parent, final FormattableObject gp) {
    isComplement = true;
    // Whether to format ID[…] elements.
    final boolean showIDs;
    // Whether we shall limit to a single ID[…] element.
    final boolean filterID;
    // Whether to format any element other than ID[…] and Remarks[…].
    final boolean showOthers;
    // Whether to format Remarks[…].
    final boolean showRemarks;
    if (convention == Convention.INTERNAL) {
        showIDs = true;
        filterID = false;
        showOthers = true;
        showRemarks = true;
    } else {
        /*
             * Except for the special cases of OperationMethod and Parameters, ISO 19162 recommends to format the
             * ID only for the root element.  But Apache SIS adds an other exception to this rule by handling the
             * components of CompoundCRS as if they were root elements. The reason is that users often create their
             * own CompoundCRS from standard components, for example by adding a time axis to some standard CRS like
             * "WGS84". The resulting CompoundCRS usually have no identifier. Then the users often need to extract a
             * particular component of a CompoundCRS, most often the horizontal part, and will need its identifier
             * for example in a Web Map Service (WMS). Those ID are lost if we do not format them here.
             */
        if (parent == null || parent instanceof CompoundCRS) {
            showIDs = true;
        } else if (gp instanceof CoordinateOperation && !(parent instanceof IdentifiedObject)) {
            // "SourceCRS[…]" and "TargetCRS[…]" sub-elements in CoordinateOperation.
            showIDs = true;
        } else if (convention == Convention.WKT2_SIMPLIFIED) {
            showIDs = false;
        } else {
            showIDs = (object instanceof OperationMethod) || (object instanceof GeneralParameterDescriptor);
        }
        if (convention.majorVersion() == 1) {
            filterID = true;
            showOthers = false;
            showRemarks = false;
        } else {
            filterID = (parent != null);
            if (object instanceof CoordinateOperation) {
                showOthers = !(parent instanceof ConcatenatedOperation);
                showRemarks = showOthers;
            } else if (object instanceof ReferenceSystem) {
                showOthers = (parent == null);
                showRemarks = (parent == null) || (gp instanceof CoordinateOperation);
            } else {
                // Mandated by ISO 19162.
                showOthers = false;
                showRemarks = false;
            }
        }
    }
    if (showOthers) {
        appendForSubtypes(object);
    }
    if (showIDs) {
        Collection<ReferenceIdentifier> identifiers = object.getIdentifiers();
        if (identifiers != null) {
            // Paranoiac check
            if (filterID) {
                for (final ReferenceIdentifier id : identifiers) {
                    if (Citations.identifierMatches(authority, id.getAuthority())) {
                        identifiers = Collections.singleton(id);
                        break;
                    }
                }
            }
            for (ReferenceIdentifier id : identifiers) {
                if (!(id instanceof FormattableObject)) {
                    id = ImmutableIdentifier.castOrCopy(id);
                }
                append((FormattableObject) id);
                if (filterID)
                    break;
            }
        }
    }
    if (showRemarks) {
        appendOnNewLine(WKTKeywords.Remark, object.getRemarks(), ElementKind.REMARKS);
    }
    isComplement = false;
}
Also used : ReferenceIdentifier(org.opengis.referencing.ReferenceIdentifier) CompoundCRS(org.opengis.referencing.crs.CompoundCRS) GeneralParameterDescriptor(org.opengis.parameter.GeneralParameterDescriptor) CoordinateOperation(org.opengis.referencing.operation.CoordinateOperation) ConcatenatedOperation(org.opengis.referencing.operation.ConcatenatedOperation) IdentifiedObject(org.opengis.referencing.IdentifiedObject) ReferenceSystem(org.opengis.referencing.ReferenceSystem) OperationMethod(org.opengis.referencing.operation.OperationMethod)

Example 5 with IdentifiedObject

use of org.opengis.referencing.IdentifiedObject in project sis by apache.

the class EPSGDataAccess method createObject.

/**
 * Returns an arbitrary object from a code. The default implementation delegates to more specific methods,
 * for example {@link #createCoordinateReferenceSystem(String)}, {@link #createDatum(String)}, <i>etc.</i>
 * until a successful one is found.
 *
 * <p><strong>Note that this method may be ambiguous</strong> since the same EPSG code can be used for different
 * kind of objects. This method throws an exception if it detects an ambiguity on a <em>best-effort</em> basis.
 * It is recommended to invoke the most specific {@code createFoo(String)} method when the desired type is known,
 * both for performance reason and for avoiding ambiguity.</p>
 *
 * @param  code  value allocated by EPSG.
 * @return the object for the given code.
 * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
 * @throws FactoryException if the object creation failed for some other reason.
 *
 * @see #createCoordinateReferenceSystem(String)
 * @see #createDatum(String)
 * @see #createCoordinateSystem(String)
 */
@Override
public synchronized IdentifiedObject createObject(final String code) throws NoSuchAuthorityCodeException, FactoryException {
    ArgumentChecks.ensureNonNull("code", code);
    final boolean isPrimaryKey = isPrimaryKey(code);
    final StringBuilder query = new StringBuilder("SELECT ");
    final int queryStart = query.length();
    int found = -1;
    try {
        final int pk = isPrimaryKey ? toPrimaryKeys(null, null, null, code)[0] : 0;
        for (int i = 0; i < TableInfo.EPSG.length; i++) {
            final TableInfo table = TableInfo.EPSG[i];
            final String column = isPrimaryKey ? table.codeColumn : table.nameColumn;
            if (column == null) {
                continue;
            }
            query.setLength(queryStart);
            query.append(table.codeColumn);
            if (!isPrimaryKey) {
                // Only for filterFalsePositive(…).
                query.append(", ").append(column);
            }
            query.append(" FROM ").append(table.table).append(" WHERE ").append(column).append(isPrimaryKey ? " = ?" : " LIKE ?");
            try (PreparedStatement stmt = connection.prepareStatement(translator.apply(query.toString()))) {
                /*
                     * Check if at least one record is found for the code or the name.
                     * Ensure that there is not two values for the same code or name.
                     */
                if (isPrimaryKey) {
                    stmt.setInt(1, pk);
                } else {
                    stmt.setString(1, toLikePattern(code));
                }
                Integer present = null;
                try (ResultSet result = stmt.executeQuery()) {
                    while (result.next()) {
                        if (isPrimaryKey || SQLUtilities.filterFalsePositive(code, result.getString(2))) {
                            present = ensureSingleton(getOptionalInteger(result, 1), present, code);
                        }
                    }
                }
                if (present != null) {
                    if (found >= 0) {
                        throw new FactoryDataException(error().getString(Errors.Keys.DuplicatedIdentifier_1, code));
                    }
                    found = i;
                }
            }
        }
    } catch (SQLException exception) {
        throw databaseFailure(IdentifiedObject.class, code, exception);
    }
    /*
         * If a record has been found in one table, then delegates to the appropriate method.
         */
    if (found >= 0) {
        switch(found) {
            case 0:
                return createCoordinateReferenceSystem(code);
            case 1:
                return createCoordinateSystem(code);
            case 2:
                return createCoordinateSystemAxis(code);
            case 3:
                return createDatum(code);
            case 4:
                return createEllipsoid(code);
            case 5:
                return createPrimeMeridian(code);
            case 6:
                return createCoordinateOperation(code);
            case 7:
                return createOperationMethod(code);
            case 8:
                return createParameterDescriptor(code);
            // Can not cast Unit to IdentifiedObject
            case 9:
                break;
            // Should not happen
            default:
                throw new AssertionError(found);
        }
    }
    throw noSuchAuthorityCode(IdentifiedObject.class, code);
}
Also used : FactoryDataException(org.apache.sis.referencing.factory.FactoryDataException) SQLException(java.sql.SQLException) ResultSet(java.sql.ResultSet) PreparedStatement(java.sql.PreparedStatement) InternationalString(org.opengis.util.InternationalString) SimpleInternationalString(org.apache.sis.util.iso.SimpleInternationalString) AbstractIdentifiedObject(org.apache.sis.referencing.AbstractIdentifiedObject) IdentifiedObject(org.opengis.referencing.IdentifiedObject)

Aggregations

IdentifiedObject (org.opengis.referencing.IdentifiedObject)30 AbstractIdentifiedObject (org.apache.sis.referencing.AbstractIdentifiedObject)15 InternationalString (org.opengis.util.InternationalString)10 FactoryException (org.opengis.util.FactoryException)8 IllegalArgumentException (com.sun.star.lang.IllegalArgumentException)5 IdentifiedObjectFinder (org.apache.sis.referencing.factory.IdentifiedObjectFinder)5 DataStoreException (org.apache.sis.storage.DataStoreException)5 Identifier (org.opengis.metadata.Identifier)5 ReferenceIdentifier (org.opengis.referencing.ReferenceIdentifier)5 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)5 HashMap (java.util.HashMap)4 Cache (org.apache.sis.util.collection.Cache)3 Test (org.junit.Test)3 GenericName (org.opengis.util.GenericName)3 NameToIdentifier (org.apache.sis.internal.metadata.NameToIdentifier)2 FormattableObject (org.apache.sis.io.wkt.FormattableObject)2 NamedIdentifier (org.apache.sis.referencing.NamedIdentifier)2 DependsOnMethod (org.apache.sis.test.DependsOnMethod)2 SimpleInternationalString (org.apache.sis.util.iso.SimpleInternationalString)2 Extent (org.opengis.metadata.extent.Extent)2