Return-Path: X-Original-To: apmail-sis-commits-archive@www.apache.org Delivered-To: apmail-sis-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id C8C36189D0 for ; Tue, 15 Dec 2015 22:57:15 +0000 (UTC) Received: (qmail 88169 invoked by uid 500); 15 Dec 2015 22:57:15 -0000 Delivered-To: apmail-sis-commits-archive@sis.apache.org Received: (qmail 88141 invoked by uid 500); 15 Dec 2015 22:57:15 -0000 Mailing-List: contact commits-help@sis.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sis-dev@sis.apache.org Delivered-To: mailing list commits@sis.apache.org Received: (qmail 88129 invoked by uid 99); 15 Dec 2015 22:57:15 -0000 Received: from Unknown (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 15 Dec 2015 22:57:15 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 283CDC0CEF for ; Tue, 15 Dec 2015 22:57:15 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.246 X-Spam-Level: * X-Spam-Status: No, score=1.246 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-0.554] autolearn=disabled Received: from mx1-us-west.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id Lmq1DRnT3T0F for ; Tue, 15 Dec 2015 22:57:09 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-us-west.apache.org (ASF Mail Server at mx1-us-west.apache.org) with ESMTP id C59AC26AC9 for ; Tue, 15 Dec 2015 22:57:08 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 013C2E0044 for ; Tue, 15 Dec 2015 22:57:07 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 882613A0702 for ; Tue, 15 Dec 2015 22:57:07 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1720269 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/referencing/factory/ sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/ sis-utility/src/main/java/org/apache/sis/util/resources/ Date: Tue, 15 Dec 2015 22:57:07 -0000 To: commits@sis.apache.org From: desruisseaux@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20151215225707.882613A0702@svn01-us-west.apache.org> Author: desruisseaux Date: Tue Dec 15 22:57:07 2015 New Revision: 1720269 URL: http://svn.apache.org/viewvc?rev=1720269&view=rev Log: First real geodetic methods in EPSGFactory: createEllipsoid and createPrimeMeridian. Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java [UTF-8] Tue Dec 15 22:57:07 2015 @@ -133,8 +133,8 @@ public abstract class GeodeticAuthorityF * Returns an arbitrary object from a code. The returned object will typically be an instance of {@link Datum}, * {@link CoordinateSystem}, {@link CoordinateReferenceSystem} or {@link CoordinateOperation}. * This method may be used when the type of the object to create is unknown. - * But it is recommended to invoke the most specific {@code createFoo(String)} method when the type is known, - * both for performance reason and for avoiding ambiguity. + * But 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. * *
Note for subclasses
* In default {@code GeodeticAuthorityFactory} implementation, all {@code createFoo(String)} methods ultimately Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Tue Dec 15 22:57:07 2015 @@ -44,6 +44,8 @@ import java.net.URISyntaxException; import javax.measure.unit.Unit; import javax.measure.unit.SI; import javax.measure.unit.NonSI; +import javax.measure.quantity.Angle; +import javax.measure.quantity.Length; import javax.measure.converter.ConversionException; import org.opengis.util.NameSpace; @@ -60,6 +62,7 @@ import org.opengis.referencing.Identifie import org.opengis.metadata.citation.Citation; import org.opengis.metadata.citation.OnLineFunction; import org.opengis.metadata.quality.PositionalAccuracy; +import org.opengis.metadata.extent.Extent; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.apache.sis.internal.referencing.DeprecatedCode; import org.apache.sis.internal.system.Loggers; @@ -67,6 +70,8 @@ import org.apache.sis.internal.util.Cons import org.apache.sis.metadata.iso.ImmutableIdentifier; import org.apache.sis.metadata.iso.citation.DefaultCitation; import org.apache.sis.metadata.iso.citation.DefaultOnlineResource; +import org.apache.sis.metadata.iso.extent.DefaultExtent; +import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; import org.apache.sis.referencing.NamedIdentifier; import org.apache.sis.referencing.AbstractIdentifiedObject; import org.apache.sis.referencing.datum.BursaWolfParameters; @@ -309,6 +314,21 @@ public abstract class EPSGFactory extend protected final Connection connection; /** + * The factory to use for creating {@link CoordinateReferenceSystem} instances from the properties read in the database. + */ + protected final CRSFactory crsFactory; + + /** + * The factory to use for creating {@link CoordinateSystem} instances from the properties read in the database. + */ + protected final CSFactory csFactory; + + /** + * The factory to use for creating {@link Datum} instances from the properties read in the database. + */ + protected final DatumFactory datumFactory; + + /** * The locale for producing error messages. This is usually the default locale. * * @see #getLocale() @@ -319,15 +339,29 @@ public abstract class EPSGFactory extend * Creates a factory using the given connection. The connection will be {@linkplain Connection#close() closed} * when this factory will be {@linkplain #dispose() disposed}. * - * @param connection The connection to the underlying EPSG database. - * @param nameFactory The factory to use for creating authority codes as {@link GenericName} instances. - */ - public EPSGFactory(final Connection connection, final NameFactory nameFactory) { + * @param connection The connection to the underlying EPSG database. + * @param crsFactory The factory to use for creating {@link CoordinateReferenceSystem} instances. + * @param csFactory The factory to use for creating {@link CoordinateSystem} instances. + * @param datumFactory The factory to use for creating {@link Datum} instances. + * @param nameFactory The factory to use for creating authority codes as {@link GenericName} instances. + */ + public EPSGFactory(final Connection connection, + final CRSFactory crsFactory, + final CSFactory csFactory, + final DatumFactory datumFactory, + final NameFactory nameFactory) + { super(nameFactory); - ArgumentChecks.ensureNonNull("connection", connection); - this.connection = connection; - this.namespace = nameFactory.createNameSpace(nameFactory.createLocalName(null, Constants.EPSG), null); - this.locale = Locale.getDefault(Locale.Category.DISPLAY); + ArgumentChecks.ensureNonNull("connection", connection); + ArgumentChecks.ensureNonNull("crsFactory", crsFactory); + ArgumentChecks.ensureNonNull("csFactory", csFactory); + ArgumentChecks.ensureNonNull("datumFactory", datumFactory); + this.connection = connection; + this.crsFactory = crsFactory; + this.csFactory = csFactory; + this.datumFactory = datumFactory; + this.namespace = nameFactory.createNameSpace(nameFactory.createLocalName(null, Constants.EPSG), null); + this.locale = Locale.getDefault(Locale.Category.DISPLAY); } /** @@ -958,6 +992,431 @@ addURIs: for (int i=0; ; i++) { return properties; } + /** + * Returns the name, aliases and domain of validity for the {@link IdentifiedObject} to construct. + * + * @param table The table on which a query has been executed. + * @param name The name for the {@link IndentifiedObject} to construct. + * @param code The EPSG code of the object to construct. + * @param domainCode The code for the domain of validity, or {@code null} if none. + * @param scope The scope, or {@code null} if none. + * @param remarks Remarks, or {@code null} if none. + * @param deprecated {@code true} if the object to create is deprecated. + * @return The name together with a set of properties. + */ + private Map createProperties(final String table, final String name, final String code, + String domainCode, String scope, String remarks, final boolean deprecated) throws SQLException, FactoryException + { + final Map properties = createProperties(table, name, code, remarks, deprecated); + if (domainCode != null && !(domainCode = domainCode.trim()).isEmpty()) { + final Extent extent = buffered.createExtent(domainCode); + properties.put(Datum.DOMAIN_OF_VALIDITY_KEY, extent); + } + if (scope != null && !(scope = scope.trim()).isEmpty()) { + properties.put(Datum.SCOPE_KEY, scope); + } + return properties; + } + + /** + * Returns an arbitrary object from a code. The default implementation delegates to more specific methods, + * for example {@link #createCoordinateReferenceSystem(String)}, {@link #createDatum(String)}, etc. + * until a successful one is found. + * + *

Note that this method may be ambiguous since the same EPSG code can be used for different + * kind of objects. This method may throw an exception if it detects an ambiguity, but this is not guaranteed. + * 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.

+ * + * @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 String KEY = "IdentifiedObject"; + PreparedStatement stmt = statements.get(KEY); // Null allowed. + StringBuilder query = null; // Will be created only if the last statement does not suit. + /* + * Iterates through all tables listed in TableInfo.EPSG, starting with the table used during the last call to + * this method. This approach assumes that two consecutive calls will often return the same type of object. + * If the object type changed, then this method will have to discard the old prepared statement and prepare + * a new one, which may be a costly operation. Only the last successful prepared statement is cached, + * in order to keep the amount of statements low. Unsuccessful statements are immediately disposed. + */ + final String epsg = trimAuthority(code); + final boolean isPrimaryKey = isPrimaryKey(epsg); + final int tupleToSkip = isPrimaryKey ? lastObjectType : -1; + int index = -1; + for (int i = -1; i < TableInfo.EPSG.length; i++) { + if (i == tupleToSkip) { + // Avoid to test the same table twice. Avoid also a NullPointerException + // if 'stmt' is null, since 'lastObjectType' should be -1 in this case. + continue; + } + try { + if (i >= 0) { + final TableInfo table = TableInfo.EPSG[i]; + final String column = isPrimaryKey ? table.codeColumn : table.nameColumn; + if (column == null) { + continue; + } + if (query == null) { + query = new StringBuilder("SELECT "); + } + query.setLength(7); // 7 is the length of "SELECT " in the above line. + query.append(table.codeColumn).append(" FROM ").append(table.table) + .append(" WHERE ").append(column).append(" = ?"); + if (isPrimaryKey) { + assert !statements.containsKey(KEY) : table; + stmt = prepareStatement(KEY, query.toString()); + } else { + // Do not cache the statement for names. + stmt = connection.prepareStatement(adaptSQL(query.toString())); + } + } + /* + * Checks if at least one record is found for the code. If the code is the primary key, then we + * will stop at the first table found since the EPSG database contains few duplicate identifiers + * (actually it still have some, but we assume that the risk of collision is low). + * If the code is a name, then we need to search in all tables since duplicate names exist. + */ + final ResultSet result; + if (isPrimaryKey) { + result = executeQuery(stmt, epsg); + } else { + stmt.setString(1, epsg); + result = stmt.executeQuery(); + } + final boolean present; + try { + present = result.next(); + } finally { + result.close(); + } + if (present) { + if (index >= 0) { + throw new FactoryDataException(error().getString(Errors.Keys.DuplicatedIdentifier_1, code)); + } + index = (i < 0) ? lastObjectType : i; + if (isPrimaryKey) { + // Do not scan other tables, since EPSG code are more likely to be unique. + // Note that names are more at risk to be duplicated, so we do not stop for names. + break; + } + } + if (isPrimaryKey) { + if (statements.remove(KEY) == null) { + throw new AssertionError(code); // Should never happen. + } + } + stmt.close(); + } 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 (isPrimaryKey) { + lastObjectType = index; + } + if (index >= 0) { + switch (index) { + 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); + case 9: break; // Can not cast Unit to IdentifiedObject + default: throw new AssertionError(index); // Should not happen + } + } + throw noSuchAuthorityCode(IdentifiedObject.class, code); + } + + /** + * Creates a geometric figure that can be used to describe the approximate shape of the earth. + * In mathematical terms, it is a surface formed by the rotation of an ellipse about its minor axis. + * + * @param code Value allocated by EPSG. + * @return The ellipsoid 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 #createGeodeticDatum(String) + * @see #createEllipsoidalCS(String) + * @see org.apache.sis.referencing.datum.DefaultEllipsoid + */ + @Override + public synchronized Ellipsoid createEllipsoid(final String code) + throws NoSuchAuthorityCodeException, FactoryException + { + ArgumentChecks.ensureNonNull("code", code); + Ellipsoid returnValue = null; + try { + final String primaryKey = toPrimaryKey(Ellipsoid.class, code, + "[Ellipsoid]", "ELLIPSOID_CODE", "ELLIPSOID_NAME"); + final PreparedStatement stmt = prepareStatement("[Ellipsoid]", + "SELECT ELLIPSOID_CODE," + + " ELLIPSOID_NAME," + + " SEMI_MAJOR_AXIS," + + " INV_FLATTENING," + + " SEMI_MINOR_AXIS," + + " UOM_CODE," + + " REMARKS," + + " DEPRECATED" + + " FROM [Ellipsoid]" + + " WHERE ELLIPSOID_CODE = ?"); + try (ResultSet result = executeQuery(stmt, primaryKey)) { + while (result.next()) { + /* + * One of 'semiMinorAxis' and 'inverseFlattening' values can be NULL in the database. + * Consequently, we don't use 'getString(ResultSet, int)' for those parameters because + * we do not want to thrown an exception if a NULL value is found. + */ + final String epsg = getString(result, 1, code); + final String name = getString(result, 2, code); + final double semiMajorAxis = getDouble(result, 3, code); + final double inverseFlattening = result.getDouble( 4); + final double semiMinorAxis = result.getDouble( 5); + final String unitCode = getString(result, 6, code); + final String remarks = result.getString( 7); + final boolean deprecated = result.getInt ( 8) != 0; + final Unit unit = buffered.createUnit(unitCode).asType(Length.class); + final Map properties = createProperties("[Ellipsoid]", name, epsg, remarks, deprecated); + final Ellipsoid ellipsoid; + if (inverseFlattening == 0) { + if (semiMinorAxis == 0) { + // Both are null, which is not allowed. + final String column = result.getMetaData().getColumnName(3); + throw new FactoryDataException(error().getString(Errors.Keys.NullValueInTable_3, code, column)); + } else { + // We only have semiMinorAxis defined. It is OK + ellipsoid = datumFactory.createEllipsoid(properties, semiMajorAxis, semiMinorAxis, unit); + } + } else { + if (semiMinorAxis != 0) { + // Both 'inverseFlattening' and 'semiMinorAxis' are defined. + // Log a warning and create the ellipsoid using the inverse flattening. + final LogRecord record = Messages.getResources(locale).getLogRecord(Level.WARNING, + Messages.Keys.AmbiguousEllipsoid_1, Constants.EPSG + DefaultNameSpace.DEFAULT_SEPARATOR + code); + record.setLoggerName(Loggers.CRS_FACTORY); + Logging.log(EPSGFactory.class, "createEllipsoid", record); + } + ellipsoid = datumFactory.createFlattenedSphere(properties, semiMajorAxis, inverseFlattening, unit); + } + returnValue = ensureSingleton(ellipsoid, returnValue, code); + } + } + } catch (SQLException exception) { + throw databaseFailure(Ellipsoid.class, code, exception); + } + if (returnValue == null) { + throw noSuchAuthorityCode(Ellipsoid.class, code); + } + return returnValue; + } + + /** + * Creates a prime meridian defining the origin from which longitude values are determined. + * + * @param code Value allocated by EPSG. + * @return The prime meridian 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 #createGeodeticDatum(String) + * @see org.apache.sis.referencing.datum.DefaultPrimeMeridian + */ + @Override + public synchronized PrimeMeridian createPrimeMeridian(final String code) + throws NoSuchAuthorityCodeException, FactoryException + { + ArgumentChecks.ensureNonNull("code", code); + PrimeMeridian returnValue = null; + try { + final String primaryKey = toPrimaryKey(PrimeMeridian.class, code, + "[Prime Meridian]", "PRIME_MERIDIAN_CODE", "PRIME_MERIDIAN_NAME"); + final PreparedStatement stmt = prepareStatement("[Prime Meridian]", + "SELECT PRIME_MERIDIAN_CODE," + + " PRIME_MERIDIAN_NAME," + + " GREENWICH_LONGITUDE," + + " UOM_CODE," + + " REMARKS," + + " DEPRECATED" + + " FROM [Prime Meridian]" + + " WHERE PRIME_MERIDIAN_CODE = ?"); + try (ResultSet result = executeQuery(stmt, primaryKey)) { + while (result.next()) { + final String epsg = getString(result, 1, code); + final String name = getString(result, 2, code); + final double longitude = getDouble(result, 3, code); + final String unitCode = getString(result, 4, code); + final String remarks = result.getString( 5); + final boolean deprecated = result.getInt ( 6) != 0; + final Unit unit = buffered.createUnit(unitCode).asType(Angle.class); + Map properties = createProperties("[Prime Meridian]", name, epsg, remarks, deprecated); + PrimeMeridian primeMeridian = datumFactory.createPrimeMeridian(properties, longitude, unit); + returnValue = ensureSingleton(primeMeridian, returnValue, code); + } + } + } catch (SQLException exception) { + throw databaseFailure(PrimeMeridian.class, code, exception); + } + if (returnValue == null) { + throw noSuchAuthorityCode(PrimeMeridian.class, code); + } + return returnValue; + } + + /** + * Creates information about spatial, vertical, and temporal extent (usually a domain of validity) from a code. + * + * @param code Value allocated by EPSG. + * @return The extent 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 org.apache.sis.metadata.iso.extent.DefaultExtent + */ + @Override + public synchronized Extent createExtent(final String code) + throws NoSuchAuthorityCodeException, FactoryException + { + ArgumentChecks.ensureNonNull("code", code); + Extent returnValue = null; + try { + final String primaryKey = toPrimaryKey(Extent.class, code, + "[Area]", "AREA_CODE", "AREA_NAME"); + final PreparedStatement stmt = prepareStatement("[Area]", + "SELECT AREA_OF_USE," + + " AREA_SOUTH_BOUND_LAT," + + " AREA_NORTH_BOUND_LAT," + + " AREA_WEST_BOUND_LON," + + " AREA_EAST_BOUND_LON" + + " FROM [Area]" + + " WHERE AREA_CODE = ?"); + try (ResultSet result = executeQuery(stmt, primaryKey)) { + while (result.next()) { + String description = result.getString(1); + if (description != null && (description = description.trim()).isEmpty()) { + description = null; + } + DefaultGeographicBoundingBox bbox = null; + double ymin = result.getDouble(2); if (result.wasNull()) ymin = Double.NaN; + double ymax = result.getDouble(3); if (result.wasNull()) ymax = Double.NaN; + double xmin = result.getDouble(4); if (result.wasNull()) xmin = Double.NaN; + double xmax = result.getDouble(5); if (result.wasNull()) xmax = Double.NaN; + if (!Double.isNaN(ymin) || !Double.isNaN(ymax) || !Double.isNaN(xmin) || !Double.isNaN(xmax)) { + /* + * Fix an error found in EPSG:3790 New Zealand - South Island - Mount Pleasant mc + * for older database (this error is fixed in EPSG database 8.2). + * + * Do NOT apply anything similar for the x axis, because xmin > xmax is not error: + * it describes a bounding box spanning the anti-meridian (±180° of longitude). + */ + if (ymin > ymax) { + final double t = ymin; + ymin = ymax; + ymax = t; + } + bbox = new DefaultGeographicBoundingBox(xmin, xmax, ymin, ymax); + } + if (description != null || bbox != null) { + DefaultExtent extent = new DefaultExtent(description, bbox, null, null); + extent.freeze(); + returnValue = ensureSingleton(extent, returnValue, code); + } + } + } + } catch (SQLException exception) { + throw databaseFailure(Extent.class, code, exception); + } + if (returnValue == null) { + throw noSuchAuthorityCode(Extent.class, code); + } + return returnValue; + } + + /** + * Creates an unit of measurement from a code. + * + * @param code Value allocated by EPSG. + * @return The unit of measurement 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. + */ + @Override + public synchronized Unit createUnit(final String code) throws NoSuchAuthorityCodeException, FactoryException { + ArgumentChecks.ensureNonNull("code", code); + Unit returnValue = null; + try { + final String primaryKey = toPrimaryKey(Unit.class, code, + "[Unit of Measure]", "UOM_CODE", "UNIT_OF_MEAS_NAME"); + final PreparedStatement stmt; + stmt = prepareStatement("[Unit of Measure]", + "SELECT UOM_CODE," + + " FACTOR_B," + + " FACTOR_C," + + " TARGET_UOM_CODE," + + " UNIT_OF_MEAS_NAME" + + " FROM [Unit of Measure]" + + " WHERE UOM_CODE = ?"); + try (ResultSet result = executeQuery(stmt, primaryKey)) { + while (result.next()) { + final int source = getInt(result, 1, code); + final double b = result.getDouble(2); + final double c = result.getDouble(3); + final int target = getInt(result, 4, code); + if (source == target) { + /* + * The unit is a base unit. Verify its consistency: + * conversion from 'source' to itself shall be the identity function. + */ + final boolean pb = (b != 1); + if (pb || c != 1) { + throw new FactoryDataException(error().getString(Errors.Keys.InconsistentAttribute_2, + pb ? "FACTOR_B" : "FACTOR_C", pb ? b : c)); + } + } + Unit unit = Units.valueOfEPSG(source); // Check in our list of hard-coded unit codes. + if (unit == null) { + final Unit base = Units.valueOfEPSG(target); + if (base != null && b != 0 && c != 0) { // May be 0 if the conversion is non-linear. + unit = Units.multiply(base, b/c); + } else try { + unit = Units.valueOf(result.getString(5)); // Try parsing the unit symbol as a fallback. + } catch (IllegalArgumentException e) { + throw new FactoryDataException(error().getString(Errors.Keys.UnknownUnit_1, code), e); + } + } + returnValue = ensureSingleton(unit, returnValue, code); + } + } + } catch (SQLException exception) { + throw databaseFailure(Unit.class, code, exception); + } + if (returnValue == null) { + throw noSuchAuthorityCode(Unit.class, code); + } + return returnValue; + } + final boolean isProjection(final int code) throws NoSuchIdentifierException, SQLException { return false; } @@ -1048,6 +1507,20 @@ addURIs: for (int i=0; ; i++) { } /** + * Constructs an exception for recursive calls. + */ + private FactoryException recursiveCall(final Class type, final String code) { + return new FactoryException(error().getString(Errors.Keys.RecursiveCreateCallForCode_2, type, code)); + } + + /** + * Constructs an exception for a database failure. + */ + private FactoryException databaseFailure(Class type, String code, SQLException cause) { + return new FactoryException(error().getString(Errors.Keys.DatabaseError_2, type, code), cause); + } + + /** * Minor shortcut for fetching the error resources. */ private Errors error() { Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java [UTF-8] Tue Dec 15 22:57:07 2015 @@ -191,6 +191,11 @@ public final class Errors extends Indexe public static final short ColinearAxisDirections_2 = 14; /** + * Database error while creating a ‘{0}’ object for code “{1}”. + */ + public static final short DatabaseError_2 = 209; + + /** * Thread “{0}” is dead. */ public static final short DeadThread_1 = 15; @@ -861,6 +866,11 @@ public final class Errors extends Indexe public static final short RecordAlreadyDefined_2 = 161; /** + * Recursive call while creating an object of type ‘{0}’ for code “{1}”. + */ + public static final short RecursiveCreateCallForCode_2 = 210; + + /** * Recursive call while creating an object for the “{0}” key. */ public static final short RecursiveCreateCallForKey_1 = 99; @@ -1011,6 +1021,11 @@ public final class Errors extends Indexe public static final short UnknownType_1 = 118; /** + * Unit “{0}” is not recognized. + */ + public static final short UnknownUnit_1 = 211; + + /** * This affine transform is unmodifiable. */ public static final short UnmodifiableAffineTransform = 119; Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] (original) +++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties [ISO-8859-1] Tue Dec 15 22:57:07 2015 @@ -49,6 +49,7 @@ CircularReference = Circ ClassNotFinal_1 = Class \u2018{0}\u2019 is not final. CloneNotSupported_1 = Can not clone an object of type \u2018{0}\u2019. ColinearAxisDirections_2 = Axis directions {0} and {1} are colinear. +DatabaseError_2 = Database error while creating a \u2018{0}\u2019 object for code \u201c{1}\u201d. DeadThread_1 = Thread \u201c{0}\u201d is dead. DirectoryNotExpected_1 = The \u201c{0}\u201d file points to a directory instead of a regular file. DisposedFactory = The factory has been disposed. @@ -184,6 +185,7 @@ ParameterNotFound_2 = No p PropertyNotFound_2 = No property named \u201c{1}\u201d has been found in \u201c{0}\u201d. RecordAlreadyDefined_2 = Record \u201c{1}\u201d is already defined in schema \u201c{0}\u201d. RecursiveCreateCallForKey_1 = Recursive call while creating an object for the \u201c{0}\u201d key. +RecursiveCreateCallForCode_2 = Recursive call while creating an object of type \u2018{0}\u2019 for code \u201c{1}\u201d. RequireDecimalSeparator = A decimal separator is required. SingularMatrix = Matrix is singular. StalledThread_1 = Thread \u201c{0}\u201d seems stalled. @@ -213,6 +215,7 @@ UnknownKeyword_1 = Keyw UnknownOption_1 = Option \u201c{0}\u201d is not recognized. UnknownType_1 = Type \u2018{0}\u2019 is unknown in this context. UnknownTypeForProperty_1 = Type of the \u201c{0}\u201d property is unknown. +UnknownUnit_1 = Unit \u201c{0}\u201d is not recognized. UnmodifiableAffineTransform = This affine transform is unmodifiable. UnmodifiableCellValue_2 = The cell at column \u201c{1}\u201d of row \u201c{0}\u201d is unmodifiable. UnmodifiableGeometry = This geometry is not modifiable. Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] (original) +++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties [ISO-8859-1] Tue Dec 15 22:57:07 2015 @@ -46,6 +46,7 @@ CircularReference = R\u0 ClassNotFinal_1 = La classe \u2018{0}\u2019 n\u2019est pas finale. CloneNotSupported_1 = Un objet de type \u2018{0}\u2019 ne peut pas \u00eatre clon\u00e9. ColinearAxisDirections_2 = Les directions d\u2019axes {0} et {1} sont colin\u00e9aires. +DatabaseError_2 = Erreur de base de donn\u00e9es lors de la cr\u00e9ation d\u2019un objet \u2018{0}\u2019 pour le code \u00ab\u202f{1}\u202f\u00bb. DeadThread_1 = La t\u00e2che \u00ab\u202f{0}\u202f\u00bb est morte. DirectoryNotExpected_1 = Le fichier \u00ab\u202f{0}\u202f\u00bb d\u00e9signe un r\u00e9pertoire plut\u00f4t qu\u2019un fichier r\u00e9gulier. DisposedFactory = La fabrique a \u00e9t\u00e9 dispos\u00e9e. @@ -180,6 +181,7 @@ ParameterNotFound_2 = Aucu PropertyNotFound_2 = Aucune propri\u00e9t\u00e9 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb n\u2019a \u00e9t\u00e9 trouv\u00e9e dans \u00ab\u202f{0}\u202f\u00bb. RecordAlreadyDefined_2 = L\u2019enregistrement \u00ab\u202f{1}\u202f\u00bb est d\u00e9j\u00e0 d\u00e9finit dans le sch\u00e9ma \u00ab\u202f{0}\u202f\u00bb. RecursiveCreateCallForKey_1 = Appel r\u00e9cursif lors de la cr\u00e9ation d\u2019un objet pour la cl\u00e9 \u00ab\u202f{0}\u202f\u00bb. +RecursiveCreateCallForCode_2 = Appels r\u00e9cursifs lors de la cr\u00e9ation d\u2019un objet de type \u2018{0}\u2019 pour le code \u00ab\u202f{1}\u202f\u00bb. RequireDecimalSeparator = Un s\u00e9parateur d\u00e9cimal est requis. SingularMatrix = La matrice est singuli\u00e8re. StalledThread_1 = La t\u00e2che \u00ab\u202f{0}\u202f\u00bb semble bloqu\u00e9e. @@ -209,6 +211,7 @@ UnknownKeyword_1 = Le m UnknownOption_1 = L\u2019option \u00ab\u202f{0}\u202f\u00bb n\u2019est pas reconnue. UnknownType_1 = Le type \u2018{0}\u2019 n\u2019est pas reconnu dans ce contexte. UnknownTypeForProperty_1 = Le type de la propri\u00e9t\u00e9 \u00ab\u202f{0}\u202f\u00bb est inconnu. +UnknownUnit_1 = Les unit\u00e9s \u00ab\u202f{0}\u202f\u00bb ne sont pas reconnues. UnmodifiableAffineTransform = Cette transformation affine n\u2019est pas modifiable. UnmodifiableCellValue_2 = La cellule \u00e0 la colonne \u00ab\u202f{1}\u202f\u00bb de la ligne \u00ab\u202f{0}\u202f\u00bb n\u2019est pas modifiable. UnmodifiableGeometry = Cette g\u00e9om\u00e9trie n\u2019est pas modifiable. Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] (original) +++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.java [UTF-8] Tue Dec 15 22:57:07 2015 @@ -61,6 +61,12 @@ public final class Messages extends Inde public static final short AlreadyRegistered_2 = 0; /** + * Ambiguity between inverse flattening and semi minor axis length for “{0}”. Using inverse + * flattening. + */ + public static final short AmbiguousEllipsoid_1 = 30; + + /** * Changed the container capacity from {0} to {1} elements. */ public static final short ChangedContainerCapacity_2 = 1; Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] (original) +++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages.properties [ISO-8859-1] Tue Dec 15 22:57:07 2015 @@ -15,6 +15,7 @@ # limitations under the License. # AlreadyRegistered_2 = {0} \u201c{1}\u201d is already registered. The second instance will be ignored. +AmbiguousEllipsoid_1 = Ambiguity between inverse flattening and semi minor axis length for \u201c{0}\u201d. Using inverse flattening. ChangedContainerCapacity_2 = Changed the container capacity from {0} to {1} elements. ConformanceMeansDatumShift = This result indicates if a datum shift method has been applied. ConstantProjParameterValue_1 = This parameter is shown for completeness, but should never have a value different than {0} for this projection. Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties?rev=1720269&r1=1720268&r2=1720269&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] (original) +++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/resources/Messages_fr.properties [ISO-8859-1] Tue Dec 15 22:57:07 2015 @@ -22,6 +22,7 @@ # U+00A0 NO-BREAK SPACE before : # AlreadyRegistered_2 = Le {0} \u00ab\u202f{1}\u202f\u00bb est d\u00e9j\u00e0 inscrit dans le registre. La seconde instance sera ignor\u00e9e. +AmbiguousEllipsoid_1 = Ambigu\u00eft\u00e9 entre l\u2019aplatissement et la longueur du semi-axe mineur pour \u00ab\u202f{0}\u202f\u00bb. Utilise l\u2019aplatissement. ChangedContainerCapacity_2 = Changement de la capacit\u00e9 du conteneur de {0} vers {1} \u00e9l\u00e9ments. ConformanceMeansDatumShift = Ce r\u00e9sultat indique si un changement de r\u00e9f\u00e9rentiel a \u00e9t\u00e9 appliqu\u00e9. ConstantProjParameterValue_1 = Ce param\u00e8tre est montr\u00e9 pour \u00eatre plus complet, mais sa valeur ne devrait jamais \u00eatre diff\u00e9rente de {0} pour cette projection.