You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2021/12/25 03:18:58 UTC
[sis] branch geoapi-4.0 updated: Complete the implementation of `GeometryGetter` with the use of `InfoStatements` for fetching the CRS from a SRID code.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new 4ce25c9 Complete the implementation of `GeometryGetter` with the use of `InfoStatements` for fetching the CRS from a SRID code.
4ce25c9 is described below
commit 4ce25c9f9211c0df8fa6fbf1d6e32ddd2bbb5160
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Dec 25 04:17:44 2021 +0100
Complete the implementation of `GeometryGetter` with the use of `InfoStatements` for fetching the CRS from a SRID code.
---
.../apache/sis/internal/sql/feature/Analyzer.java | 8 +--
.../apache/sis/internal/sql/feature/Column.java | 6 +-
.../apache/sis/internal/sql/feature/Database.java | 76 +++++++++++++++-------
.../sis/internal/sql/feature/FeatureAdapter.java | 5 +-
.../sis/internal/sql/feature/FeatureIterator.java | 26 ++++++--
.../sis/internal/sql/feature/GeometryGetter.java | 51 ++++-----------
.../sis/internal/sql/feature/ValueGetter.java | 51 +++++++++------
.../apache/sis/internal/sql/postgis/Postgres.java | 10 +--
.../internal/sql/feature/GeometryGetterTest.java | 51 ++++++++++++---
.../sis/internal/sql/feature/ResultSetMock.java | 68 +++++++++++++++++++
.../sis/internal/sql/postgis/PostgresTest.java | 71 ++++++++++++++++++--
.../sis/internal/sql/postgis/SpatialFeatures.sql | 14 ++--
12 files changed, 312 insertions(+), 125 deletions(-)
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java
index 11ae100..2a288ea 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java
@@ -125,12 +125,10 @@ final class Analyzer {
* @param database information about the spatial database.
* @param connection an existing connection to the database, used only for the lifetime of this {@code Analyzer}.
* @param metadata value of {@code connection.getMetaData()} (provided because already known by caller).
- * @param isSpatial whether the database contains "GEOMETRY_COLUMNS" and "SPATIAL_REF_SYS" tables.
* @param customizer user-specified modification to the features, or {@code null} if none.
*/
Analyzer(final Database<?> database, final Connection connection, final DatabaseMetaData metadata,
- final boolean isSpatial, final SchemaModifier customizer)
- throws SQLException
+ final SchemaModifier customizer) throws SQLException
{
this.database = database;
this.tables = new HashMap<>();
@@ -140,7 +138,7 @@ final class Analyzer {
this.metadata = metadata;
this.escape = metadata.getSearchStringEscape();
this.nameFactory = DefaultFactories.forBuildin(NameFactory.class);
- spatialInformation = isSpatial ? database.createInfoStatements(connection) : null;
+ spatialInformation = database.isSpatial() ? database.createInfoStatements(connection) : null;
}
/**
@@ -269,7 +267,7 @@ final class Analyzer {
/**
* Initializes the value getter on the given column.
- * This method shall be invoked only after geometry columns have been identifier.
+ * This method shall be invoked only after geometry columns have been identified.
*/
final ValueGetter<?> setValueGetter(final Column column) {
ValueGetter<?> getter = database.getMapping(column);
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Column.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Column.java
index f6a558a..cd28d13 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Column.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Column.java
@@ -39,6 +39,10 @@ import org.apache.sis.util.Localized;
* possibly completed with information about a geometry column.
* The aim is to describe all information about a column that is needed for mapping to feature model.
*
+ * <h2>Multi-threading</h2>
+ * {@code Column} instances shall be kept unmodified after all fields have been initialized.
+ * The same instances may be read concurrently by many threads.
+ *
* @author Alexis Manin (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @version 1.1
@@ -217,7 +221,7 @@ public final class Column {
if (isNullable) {
attribute.setMinimumOccurs(0);
}
- valueGetter.getCRS().ifPresent(attribute::setCRS);
+ valueGetter.getDefaultCRS().ifPresent(attribute::setCRS);
return attribute;
}
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java
index 8bd9f8b..7da6ed9 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Database.java
@@ -17,8 +17,10 @@
package org.apache.sis.internal.sql.feature;
import java.util.Set;
+import java.util.Map;
import java.util.List;
import java.util.HashSet;
+import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.WeakHashMap;
import java.util.ArrayList;
@@ -75,7 +77,7 @@ import org.apache.sis.util.Debug;
* <ul>
* <li>{@link #getMapping(Column)} for adding column types to recognize.</li>
* <li>{@link #createInfoStatements(Connection)} for more info about spatial information.</li>
- * <li>{@link #addIgnoredTables(Set)} for specifying more tables to ignore.</li>
+ * <li>{@link #addIgnoredTables(Map)} for specifying more tables to ignore.</li>
* </ul>
*
* <h2>Multi-threading</h2>
@@ -135,9 +137,18 @@ public class Database<G> extends Syntax {
private Table[] tables;
/**
+ * Whether the database contains "GEOMETRY_COLUMNS" and/or "SPATIAL_REF_SYS" tables.
+ * May also be set to {@code true} if some database-specific tables are found such as
+ * {@code "geography_columns"} and {@code "raster_columns"} in PostGIS.
+ * This field is initialized by {@link #analyze analyze(…)} and shall not be modified after that point.
+ *
+ * @see #isSpatial()
+ */
+ private boolean isSpatial;
+
+ /**
* {@code true} if this database contains at least one geometry column.
- * This field is initialized by {@link #analyze(SQLStore, Connection, ResourceDefinition...)}
- * and shall not be modified after that point.
+ * This field is initialized by {@link #analyze analyze(…)} and shall not be modified after that point.
*
* @see #hasGeometry()
*/
@@ -303,23 +314,23 @@ public class Database<G> extends Syntax {
tableCRS = tableCRS .toLowerCase(Locale.US).intern();
tableGeom = tableGeom.toLowerCase(Locale.US).intern();
}
- final Set<String> ignoredTables = new HashSet<>(8);
- ignoredTables.add(tableCRS);
- ignoredTables.add(tableGeom);
+ final Map<String,Boolean> ignoredTables = new HashMap<>(8);
+ ignoredTables.put(tableCRS, Boolean.TRUE);
+ ignoredTables.put(tableGeom, Boolean.TRUE);
addIgnoredTables(ignoredTables);
- final boolean isSpatial = hasTable(metadata, tableTypes, ignoredTables);
+ isSpatial = hasTable(metadata, tableTypes, ignoredTables);
/*
* Collect the names of all tables specified by user, ignoring the tables
* used for database internal working (for example by PostGIS).
*/
- final Analyzer analyzer = new Analyzer(this, connection, metadata, isSpatial, customizer);
+ final Analyzer analyzer = new Analyzer(this, connection, metadata, customizer);
final Set<TableReference> declared = new LinkedHashSet<>();
for (final GenericName tableName : tableNames) {
final String[] names = TableReference.splitName(tableName);
try (ResultSet reflect = metadata.getTables(names[2], names[1], names[0], tableTypes)) {
while (reflect.next()) {
final String table = analyzer.getUniqueString(reflect, Reflection.TABLE_NAME);
- if (ignoredTables.contains(table)) {
+ if (ignoredTables.containsKey(table)) {
continue;
}
declared.add(new TableReference(
@@ -376,8 +387,8 @@ public class Database<G> extends Syntax {
}
/**
- * Returns {@code true} if the database contains all specified tables. This method updates
- * {@link #schemaOfSpatialTables} and {@link #catalogOfSpatialTables} for the tables found.
+ * Returns {@code true} if the database contains at least one specified tables associated to {@code Boolean.TRUE}.
+ * This method updates {@link #schemaOfSpatialTables} and {@link #catalogOfSpatialTables} for the tables found.
* If many occurrences of the same table are found, this method searches for a common pair
* of catalog and schema names. All tables should be in the same (catalog, schema) pair.
* If this is not the case, no (catalog,schema) will be used and the search for the tables
@@ -388,23 +399,26 @@ public class Database<G> extends Syntax {
* @param tables name of the table to search.
* @return whether the given table has been found.
*/
- private boolean hasTable(final DatabaseMetaData metadata, final String[] tableTypes, final Set<String> tables)
+ private boolean hasTable(final DatabaseMetaData metadata, final String[] tableTypes, final Map<String,Boolean> tables)
throws SQLException
{
// `SimpleImmutableEntry` used as a way to store a (catalog,schema) pair of strings.
final FrequencySortedSet<SimpleImmutableEntry<String,String>> schemas = new FrequencySortedSet<>(true);
int count = 0;
- for (final String name : tables) {
- boolean found = false;
- try (ResultSet reflect = metadata.getTables(null, null, name, tableTypes)) {
- while (reflect.next()) {
- found = true;
- schemas.add(new SimpleImmutableEntry<>(
- reflect.getString(Reflection.TABLE_CAT),
- reflect.getString(Reflection.TABLE_SCHEM)));
+ for (final Map.Entry<String,Boolean> entry : tables.entrySet()) {
+ if (entry.getValue()) {
+ String name = entry.getKey();
+ boolean found = false;
+ try (ResultSet reflect = metadata.getTables(null, null, name, tableTypes)) {
+ while (reflect.next()) {
+ found = true;
+ schemas.add(new SimpleImmutableEntry<>(
+ reflect.getString(Reflection.TABLE_CAT),
+ reflect.getString(Reflection.TABLE_SCHEM)));
+ }
}
+ if (found) count++;
}
- if (found) count++;
}
if (count == 0) {
return false;
@@ -455,6 +469,16 @@ public class Database<G> extends Syntax {
}
/**
+ * Returns {@code true} if this database is a spatial database.
+ * Tables such as "SPATIAL_REF_SYS" are used as sentinel values.
+ *
+ * @return whether this database is a spatial database.
+ */
+ public final boolean isSpatial() {
+ return isSpatial;
+ }
+
+ /**
* Returns {@code true} if this database contains at least one geometry column.
*
* @return whether at least one geometry column has been found.
@@ -540,7 +564,6 @@ public class Database<G> extends Syntax {
final GeometryType type = columnDefinition.getGeometryType();
final Class<? extends G> geometryClass = geomLibrary.getGeometryClass(type).asSubclass(geomLibrary.rootClass);
return new GeometryGetter<>(geomLibrary, geometryClass, columnDefinition.getGeometryCRS(), getBinaryEncoding(columnDefinition));
- // TODO: need to invoke GeometryGetter.setSridResolver(statements(…)) somewhere.
}
/**
@@ -555,13 +578,16 @@ public class Database<G> extends Syntax {
}
/**
- * Adds to the given set a list of tables to ignore when searching for feature tables.
- * The given set already contains the {@code "SPATIAL_REF_SYS"} and {@code "GEOMETRY_COLUMNS"}
+ * Adds to the given map a list of tables to ignore when searching for feature tables.
+ * The given map already contains the {@code "SPATIAL_REF_SYS"} and {@code "GEOMETRY_COLUMNS"}
* entries when this method is invoked. The default implementation adds nothing.
*
+ * <p>Values tells whether the table can be used as a sentinel value for determining
+ * that this database {@linkplain #isSpatial is a spatial database}.</p>
+ *
* @param ignoredTables where to add names of tables to ignore.
*/
- protected void addIgnoredTables(final Set<String> ignoredTables) {
+ protected void addIgnoredTables(final Map<String,Boolean> ignoredTables) {
}
/**
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureAdapter.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureAdapter.java
index ac85330..fc3a6f2 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureAdapter.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureAdapter.java
@@ -318,15 +318,16 @@ final class FeatureAdapter {
* Creates a feature with attribute values initialized to values fetched from the given result set.
* This method does not follow associations.
*
+ * @param stmts prepared statements for fetching CRS from SRID, or {@code null} if none.
* @param result the result set from which to get attribute values.
* @return the feature with attribute values initialized.
* @throws Exception if an error occurred while reading the database or converting values.
*/
- final Feature createFeature(final ResultSet result) throws Exception {
+ final Feature createFeature(final InfoStatements stmts, final ResultSet result) throws Exception {
final Feature feature = featureType.newInstance();
for (int i=0; i<attributes.length; i++) {
final Column column = attributes[i];
- final Object value = column.valueGetter.getValue(result, i+1);
+ final Object value = column.valueGetter.getValue(stmts, result, i+1);
if (value != null) {
feature.setPropertyValue(column.propertyName, value);
}
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureIterator.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureIterator.java
index fba5727..844821a 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureIterator.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/FeatureIterator.java
@@ -43,9 +43,13 @@ import org.opengis.filter.SortBy;
* Each {@code FeatureIterator} iterator is created for one specific SQL query
* and can be used for only one iteration.
*
+ * <h2>Parallelism</h2>
+ * Current implementation of {@code FeatureIterator} does not support parallelism.
+ * This iterator is not thread-safe and the {@link #trySplit()} method always returns {@code null}.
+ *
* @author Martin Desruisseaux (Geomatys)
* @author Alexis Manin (Geomatys)
- * @version 1.1
+ * @version 1.2
* @since 1.0
* @module
*/
@@ -83,6 +87,13 @@ final class FeatureIterator implements Spliterator<Feature>, AutoCloseable {
private final long estimatedSize;
/**
+ * A cache of statements for fetching spatial information such as geometry columns or SRID.
+ * This is non-null only if the {@linkplain Database#isSpatial() database is spatial}.
+ * The same instance is shared by all dependencies of this {@code FeatureIterator}.
+ */
+ private final InfoStatements spatialInformation;
+
+ /**
* The feature sets referenced through foreigner keys, or an empty array if none.
* This includes the associations inferred from both the imported and exported keys.
* The first {@link FeatureAdapter#importCount} iterators are for imported keys,
@@ -110,6 +121,7 @@ final class FeatureIterator implements Spliterator<Feature>, AutoCloseable {
throws SQLException, InternalDataStoreException
{
adapter = table.adapter(connection);
+ spatialInformation = table.database.isSpatial() ? table.database.createInfoStatements(connection) : null;
String sql = adapter.sql;
if (distinct || filter != null || sort != null || offset > 0 || count > 0) {
final SQLBuilder builder = new SQLBuilder(table.database).append(sql);
@@ -153,7 +165,10 @@ final class FeatureIterator implements Spliterator<Feature>, AutoCloseable {
* @param offset number of rows to skip in underlying SQL query, or ≤ 0 for none.
* @param count maximum number of rows to return, or ≤ 0 for no limit.
*/
- private FeatureIterator(final FeatureAdapter adapter, final Connection connection) throws SQLException {
+ private FeatureIterator(final FeatureAdapter adapter, final Connection connection,
+ final InfoStatements spatialInformation) throws SQLException
+ {
+ this.spatialInformation = spatialInformation;
this.adapter = adapter;
statement = connection.prepareStatement(adapter.sql);
dependencies = new FeatureIterator[adapter.dependencies.length];
@@ -166,7 +181,7 @@ final class FeatureIterator implements Spliterator<Feature>, AutoCloseable {
private FeatureIterator dependency(final int i) throws SQLException {
FeatureIterator dependency = dependencies[i];
if (dependency == null) {
- dependency = new FeatureIterator(adapter.dependencies[i], result.getStatement().getConnection());
+ dependency = new FeatureIterator(adapter.dependencies[i], result.getStatement().getConnection(), spatialInformation);
dependencies[i] = dependency;
}
return dependency;
@@ -236,7 +251,7 @@ final class FeatureIterator implements Spliterator<Feature>, AutoCloseable {
*/
private boolean fetch(final Consumer<? super Feature> action, final boolean all) throws Exception {
while (result.next()) {
- final Feature feature = adapter.createFeature(result);
+ final Feature feature = adapter.createFeature(spatialInformation, result);
for (int i=0; i < dependencies.length; i++) {
WeakValueHashMap<?,Object> instances = null;
Object key = null, value = null;
@@ -311,6 +326,9 @@ final class FeatureIterator implements Spliterator<Feature>, AutoCloseable {
*/
@Override
public void close() throws SQLException {
+ if (spatialInformation != null) {
+ spatialInformation.close();
+ }
/*
* Only one of `statement` and `result` should be non-null. The connection should be closed by
* the `FeatureIterator` instance having a non-null `result` because it is the main one created
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/GeometryGetter.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/GeometryGetter.java
index 1151690..5d6f2c6 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/GeometryGetter.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/GeometryGetter.java
@@ -41,6 +41,9 @@ import org.apache.sis.internal.feature.Geometries;
* <li><a href="http://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT">PostGIS extended format</a></li>
* </ul>
*
+ * <h2>Multi-threading</h2>
+ * {@code GeometryGetter} instances shall be thread-safe.
+ *
* @author Johann Sorel (Geomatys)
* @author Alexis Manin (Geomatys)
* @author Martin Desruisseaux (Geomatys)
@@ -59,14 +62,7 @@ final class GeometryGetter<G, V extends G> extends ValueGetter<V> {
private final Geometries<G> geometryFactory;
/**
- * The mapper to use for resolving a Spatial Reference Identifier (SRID) integer
- * as Coordinate Reference System (CRS) object.
- * This is {@code null} if there is no mapping to apply.
- */
- private InfoStatements fromSridToCRS;
-
- /**
- * The Coordinate Reference System if {@link #fromSridToCRS} can not map the SRID.
+ * The Coordinate Reference System if {@link InfoStatements} can not map the SRID.
* This is {@code null} if there is no default.
*/
private final CoordinateReferenceSystem defaultCRS;
@@ -96,21 +92,11 @@ final class GeometryGetter<G, V extends G> extends ValueGetter<V> {
}
/**
- * Sets the mapper to use for resolving a Spatial Reference Identifier (SRID) integer
- * as Coordinate Reference System (CRS) object.
- *
- * @param fromSridToCRS mapper for resolving SRID integers as CRS objects, or {@code null} if none.
- */
- final void setSridResolver(final InfoStatements fromSridToCRS) {
- this.fromSridToCRS = fromSridToCRS;
- }
-
- /**
* Returns the default coordinate reference system for this column.
* The default CRS is declared in the {@code "GEOMETRY_COLUMNS"} table.
*/
@Override
- public Optional<CoordinateReferenceSystem> getCRS() {
+ public Optional<CoordinateReferenceSystem> getDefaultCRS() {
return Optional.ofNullable(defaultCRS);
}
@@ -119,6 +105,7 @@ final class GeometryGetter<G, V extends G> extends ValueGetter<V> {
* The given result set must have its cursor position on the line to read.
* This method does not modify the cursor position.
*
+ * @param stmts prepared statements for fetching CRS from SRID, or {@code null} if none.
* @param source the result set from which to get the value.
* @param columnIndex index of the column in which to get the value.
* @return geometry value in the given column. May be {@code null}.
@@ -128,32 +115,20 @@ final class GeometryGetter<G, V extends G> extends ValueGetter<V> {
* find a way to ensure that SQL queries use {@code ST_AsBinary} function.
*/
@Override
- public V getValue(final ResultSet source, final int columnIndex) throws Exception {
+ public V getValue(final InfoStatements stmts, final ResultSet source, final int columnIndex) throws Exception {
final byte[] wkb = encoding.getBytes(source, columnIndex);
if (wkb == null) return null;
- final GeometryWrapper<G> geom = read(wkb);
- return valueType.cast(geom.implementation());
- }
-
- /**
- * Parses a WKB stored in the given byte array.
- *
- * @param wkb the array containing the WKB to decode. Should neither be null nor empty.
- * @return geometry parsed from the given array of bytes. Never null, never empty.
- * @throws Exception if the WKB can not be parsed. The exception type depends on the geometry implementation.
- */
- final GeometryWrapper<G> read(final byte[] wkb) throws Exception {
- final GeometryWrapper<G> wrapper = geometryFactory.parseWKB(ByteBuffer.wrap(wkb));
+ final GeometryWrapper<G> geom = geometryFactory.parseWKB(ByteBuffer.wrap(wkb));
CoordinateReferenceSystem crs = defaultCRS;
- if (fromSridToCRS != null) {
- final OptionalInt srid = wrapper.getSRID();
+ if (stmts != null) {
+ final OptionalInt srid = geom.getSRID();
if (srid.isPresent()) {
- crs = fromSridToCRS.fetchCRS(srid.getAsInt());
+ crs = stmts.fetchCRS(srid.getAsInt());
}
}
if (crs != null) {
- wrapper.setCoordinateReferenceSystem(crs);
+ geom.setCoordinateReferenceSystem(crs);
}
- return wrapper;
+ return valueType.cast(geom.implementation());
}
}
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java
index 4920b95..ad0adfc 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/ValueGetter.java
@@ -38,9 +38,12 @@ import org.apache.sis.util.ArgumentChecks;
* The {@code ValueGetter} getter method will typically delegate to the most appropriate {@link ResultSet} getter
* method for the column type, but may also perform some conversions such as parsing geometry Well-Known Binary (WKB).
*
- * <p>The {@link #getValue(ResultSet, int)} method is invoked with the result set cursor placed on the row of interest.
- * The index of the column to read must be specified. It allows to reuse the same {@code ValueGetter} instance for an
- * arbitrary amount of columns.</p>
+ * <p>The {@link #getValue(InfoStatements, ResultSet, int)} method is invoked with the result set cursor placed on the
+ * row of interest. The index of the column to read must be specified. It allows to reuse the same {@code ValueGetter}
+ * instance for an arbitrary amount of columns.</p>
+ *
+ * <h2>Multi-threading</h2>
+ * {@code ValueGetter} instances shall be thread-safe.
*
* @param <T> type of values in the column.
*
@@ -73,12 +76,18 @@ public abstract class ValueGetter<T> {
* The given result set must have its cursor position on the line to read.
* This method does not modify the cursor position.
*
+ * <div class="note"><b>Note:</b>
+ * The {@code stmts} is the same reference for all features created by a new {@link FeatureIterator} instance,
+ * including its dependencies. But the {@code source} will vary depending on whether we are iterating over the
+ * main feature or one of its dependencies.</div>
+ *
+ * @param stmts prepared statements for fetching CRS from SRID, or {@code null} if none.
* @param source the result set from which to get the value.
* @param columnIndex index of the column in which to get the value.
* @return value in the given column. May be {@code null}.
* @throws Exception if an error occurred. May be an SQL error, a WKB parsing error, <i>etc.</i>
*/
- public abstract T getValue(ResultSet source, int columnIndex) throws Exception;
+ public abstract T getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws Exception;
/**
* Returns the default coordinate reference system for this column.
@@ -90,7 +99,7 @@ public abstract class ValueGetter<T> {
*
* @return the default coordinate reference system for values in this column.
*/
- public Optional<CoordinateReferenceSystem> getCRS() {
+ public Optional<CoordinateReferenceSystem> getDefaultCRS() {
return Optional.empty();
}
@@ -104,7 +113,7 @@ public abstract class ValueGetter<T> {
private AsObject() {super(Object.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Object getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Object getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
return source.getObject(columnIndex);
}
}
@@ -119,7 +128,7 @@ public abstract class ValueGetter<T> {
private AsString() {super(String.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public String getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public String getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
return source.getString(columnIndex);
}
}
@@ -141,7 +150,7 @@ public abstract class ValueGetter<T> {
}
/** Fetches the value from the specified column in the given result set. */
- @Override public byte[] getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public byte[] getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
return encoding.getBytes(source, columnIndex);
}
}
@@ -157,7 +166,7 @@ public abstract class ValueGetter<T> {
private AsByte() {super(Byte.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Byte getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Byte getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
byte value = source.getByte(columnIndex);
return source.wasNull() ? null : value;
}
@@ -174,7 +183,7 @@ public abstract class ValueGetter<T> {
private AsShort() {super(Short.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Short getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Short getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
short value = source.getShort(columnIndex);
return source.wasNull() ? null : value;
}
@@ -191,7 +200,7 @@ public abstract class ValueGetter<T> {
private AsInteger() {super(Integer.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Integer getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Integer getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
int value = source.getInt(columnIndex);
return source.wasNull() ? null : value;
}
@@ -208,7 +217,7 @@ public abstract class ValueGetter<T> {
private AsLong() {super(Long.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Long getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Long getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
long value = source.getLong(columnIndex);
return source.wasNull() ? null : value;
}
@@ -225,7 +234,7 @@ public abstract class ValueGetter<T> {
private AsFloat() {super(Float.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Float getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Float getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
float value = source.getFloat(columnIndex);
return source.wasNull() ? null : value;
}
@@ -242,7 +251,7 @@ public abstract class ValueGetter<T> {
private AsDouble() {super(Double.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Double getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Double getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
double value = source.getDouble(columnIndex);
return source.wasNull() ? null : value;
}
@@ -258,7 +267,7 @@ public abstract class ValueGetter<T> {
private AsBigDecimal() {super(BigDecimal.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public BigDecimal getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public BigDecimal getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
return source.getBigDecimal(columnIndex);
}
}
@@ -274,7 +283,7 @@ public abstract class ValueGetter<T> {
private AsBoolean() {super(Boolean.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Boolean getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Boolean getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
boolean value = source.getBoolean(columnIndex);
return source.wasNull() ? null : value;
}
@@ -292,7 +301,7 @@ public abstract class ValueGetter<T> {
private AsDate() {super(Date.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Date getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Date getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
return source.getDate(columnIndex);
}
}
@@ -310,7 +319,7 @@ public abstract class ValueGetter<T> {
private AsLocalTime() {super(LocalTime.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public LocalTime getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public LocalTime getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
final Time time = source.getTime(columnIndex);
return (time != null) ? time.toLocalTime() : null;
}
@@ -329,7 +338,7 @@ public abstract class ValueGetter<T> {
private AsInstant() {super(Instant.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public Instant getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public Instant getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
final Timestamp time = source.getTimestamp(columnIndex);
return (time != null) ? time.toInstant() : null;
}
@@ -348,7 +357,7 @@ public abstract class ValueGetter<T> {
private AsOffsetDateTime() {super(OffsetDateTime.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public OffsetDateTime getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public OffsetDateTime getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
final Timestamp time = source.getTimestamp(columnIndex);
if (time == null) return null;
final int offsetMinute = time.getTimezoneOffset();
@@ -369,7 +378,7 @@ public abstract class ValueGetter<T> {
private AsOffsetTime() {super(OffsetTime.class);}
/** Fetches the value from the specified column in the given result set. */
- @Override public OffsetTime getValue(ResultSet source, int columnIndex) throws SQLException {
+ @Override public OffsetTime getValue(InfoStatements stmts, ResultSet source, int columnIndex) throws SQLException {
final Time time = source.getTime(columnIndex);
if (time == null) return null;
final int offsetMinute = time.getTimezoneOffset();
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/Postgres.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/Postgres.java
index cb36120..86f7f77 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/Postgres.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/Postgres.java
@@ -16,7 +16,7 @@
*/
package org.apache.sis.internal.sql.postgis;
-import java.util.Set;
+import java.util.Map;
import java.sql.Types;
import java.sql.Connection;
import java.sql.Statement;
@@ -150,10 +150,10 @@ public final class Postgres<G> extends Database<G> {
* @param ignoredTables where to add names of tables to ignore.
*/
@Override
- protected void addIgnoredTables(final Set<String> ignoredTables) {
- ignoredTables.add("geography_columns"); // Postgis 1+
- ignoredTables.add("raster_columns"); // Postgis 2
- ignoredTables.add("raster_overviews");
+ protected void addIgnoredTables(final Map<String,Boolean> ignoredTables) {
+ ignoredTables.put("geography_columns", Boolean.TRUE); // Postgis 1+
+ ignoredTables.put("raster_columns", Boolean.TRUE); // Postgis 2
+ ignoredTables.put("raster_overviews", Boolean.FALSE);
}
/**
diff --git a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/GeometryGetterTest.java b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/GeometryGetterTest.java
index e57e0c5..4e197d6 100644
--- a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/GeometryGetterTest.java
+++ b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/GeometryGetterTest.java
@@ -20,10 +20,14 @@ import java.nio.ByteBuffer;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.internal.feature.Geometries;
import org.apache.sis.internal.feature.GeometryWrapper;
-import org.apache.sis.referencing.crs.HardCodedCRS;
+import org.apache.sis.internal.feature.GeometryWithCRS;
+import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.crs.HardCodedCRS;
import org.apache.sis.setup.GeometryLibrary;
import org.apache.sis.test.TestCase;
import org.junit.Test;
@@ -103,9 +107,18 @@ public strictfp final class GeometryGetterTest extends TestCase {
// Read the point.
point.rewind();
- final GeometryWrapper<?> read = createReader(library, BinaryEncoding.RAW).read(point.array());
- assertSame(HardCodedCRS.WGS84, read.getCoordinateReferenceSystem());
- assertEquals(GF.createPoint(42.2, 43.3), read.implementation());
+ final ResultSet r = ResultSetMock.create(point.array());
+ final Object geometry = createReader(library, BinaryEncoding.RAW).getValue(null, r, 1);
+ assertEquals(GF.createPoint(42.2, 43.3), geometry);
+ final GeometryWrapper<?> wrapper = Geometries.implementation(library).castOrWrap(geometry);
+ /*
+ * If the wrapper is an instance of `GeometryWithCRS`, then the CRS is stored
+ * with the wrapper instead of the geometry implementation. In such case, the
+ * CRS is lost on `GeometryWrapper.implementation()` and can not be tested.
+ */
+ if (!(wrapper instanceof GeometryWithCRS)) {
+ assertSame(HardCodedCRS.WGS84, wrapper.getCoordinateReferenceSystem());
+ }
}
/**
@@ -115,25 +128,43 @@ public strictfp final class GeometryGetterTest extends TestCase {
* another test class} having a connection to a database.
*
* @param connection connection to the database.
- * @param spatialRefSys helper method for fetching CRS from SRID codes.
+ * @param fromSridToCRS the resolver of Spatial Reference Identifier (SRID) to CRS, or {@code null}.
* @param encoding the way binary data are encoded (raw or hexadecimal).
* @throws Exception if an error occurred while querying the database or parsing the WKT or WKB.
*/
- public void testFromDatabase(final Connection connection, final InfoStatements spatialRefSys,
+ public void testFromDatabase(final Connection connection, final InfoStatements fromSridToCRS,
final BinaryEncoding encoding) throws Exception
{
final GeometryGetter<?,?> reader = createReader(GeometryLibrary.JTS, encoding);
- reader.setSridResolver(spatialRefSys);
try (Statement stmt = connection.createStatement();
- ResultSet results = stmt.executeQuery("SELECT \"WKT\",\"WKB\" FROM features.\"Geometries\""))
+ ResultSet results = stmt.executeQuery("SELECT \"WKT\",\"WKB\",\"SRID\" FROM features.\"Geometries\""))
{
while (results.next()) {
final String wkt = results.getString(1);
- final Geometry geometry = (Geometry) reader.getValue(results, 2);
+ final Geometry geometry = (Geometry) reader.getValue(fromSridToCRS, results, 2);
final GeometryWrapper<?> expected = GF.parseWKT(wkt);
assertEquals("WKT and WKB parsings gave different results.", expected.implementation(), geometry);
- assertSame("SRID", CommonCRS.WGS84.normalizedGeographic(), GF.castOrWrap(geometry).getCoordinateReferenceSystem());
+ assertSame("SRID", getExpectedCRS(results.getInt(3)),
+ GF.castOrWrap(geometry).getCoordinateReferenceSystem());
}
}
}
+
+ /**
+ * Returns the expected CRS for the given SRID. Note that a SRID is not necessary an EPSG code.
+ * This method accepts only the SRID used in the {@code "SpatialFeatures"} test schema.
+ * The mapping from SRID to CRS is hard-coded to the same mapping than the spatial database
+ * used for the test. Other databases may have different mapping.
+ *
+ * @param srid the SRID for which to get the CRS.
+ * @return the CRS for the given SRID.
+ * @throws FactoryException if an error occurred while fetching the CRS.
+ */
+ public static CoordinateReferenceSystem getExpectedCRS(final int srid) throws FactoryException {
+ switch (srid) {
+ case 3395: return CRS.forCode("EPSG:3395");
+ case 4326: return CommonCRS.WGS84.normalizedGeographic();
+ default: throw new AssertionError(srid);
+ }
+ }
}
diff --git a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/ResultSetMock.java b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/ResultSetMock.java
new file mode 100644
index 0000000..746a4f1
--- /dev/null
+++ b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/ResultSetMock.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.feature;
+
+import java.sql.ResultSet;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationHandler;
+
+
+/**
+ * An implementation of {@link ResultSet} where the {@code getBytes(1)} method returns a
+ * predefined array of bytes. Because of the large amount of methods in {@code ResultSet},
+ * this class implements that interface using reflection.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.2
+ * @since 1.2
+ * @module
+ */
+final class ResultSetMock implements InvocationHandler {
+ /**
+ * The bytes to return on invocation on {@link ResultSet#getBytes(int)}.
+ */
+ private final byte[] wkb;
+
+ /**
+ * Creates a new handler for a {@link ResultSet} mock.
+ */
+ private ResultSetMock(final byte[] wkb) {
+ this.wkb = wkb;
+ }
+
+ /**
+ * Creates a mock where {@link ResultSet#getBytes(int)} returns the given array of bytes.
+ */
+ static ResultSet create(final byte[] wkb) {
+ return (ResultSet) Proxy.newProxyInstance(ResultSetMock.class.getClassLoader(),
+ new Class<?>[] {ResultSet.class}, new ResultSetMock(wkb));
+ }
+
+ /**
+ * Implementation of all {@link ResultSet} methods. Only {@code ResultSet.getBytes(1)}
+ * is currently supported. All other method invocations cause an {@link AssertionError}
+ * to be thrown.
+ */
+ @Override
+ public Object invoke(final Object proxy, final Method method, final Object[] args) {
+ if (method.getName().equals("getBytes") && args.length == 1 && Integer.valueOf(1).equals(args[0])) {
+ return wkb;
+ }
+ throw new AssertionError(method);
+ }
+}
diff --git a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java
index 8eed2b7..7b67836 100644
--- a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java
+++ b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/PostgresTest.java
@@ -23,9 +23,11 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.lang.reflect.Method;
-import org.opengis.referencing.crs.ProjectedCRS;
+import java.util.stream.Stream;
+import org.opengis.util.FactoryException;
import org.apache.sis.setup.OptionKey;
import org.apache.sis.setup.GeometryLibrary;
+import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.sql.SQLStore;
import org.apache.sis.storage.sql.SQLStoreProvider;
import org.apache.sis.storage.sql.ResourceDefinition;
@@ -34,6 +36,8 @@ import org.apache.sis.storage.sql.SQLStoreTest;
import org.apache.sis.internal.storage.io.ChannelDataInput;
import org.apache.sis.internal.sql.feature.BinaryEncoding;
import org.apache.sis.internal.sql.feature.GeometryGetterTest;
+import org.apache.sis.internal.feature.jts.JTS;
+import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.crs.HardCodedCRS;
import org.apache.sis.test.sql.TestDatabase;
import org.apache.sis.test.DependsOn;
@@ -41,7 +45,14 @@ import org.apache.sis.test.TestCase;
import org.apache.sis.util.Version;
import org.junit.Test;
-import static org.opengis.test.Assert.*;
+// Branch-dependent imports
+import org.opengis.feature.Feature;
+
+// Optional dependencies
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Geometry;
+
+import static org.junit.Assert.*;
/**
@@ -92,6 +103,7 @@ public final strictfp class PostgresTest extends TestCase {
testInfoStatements(info);
testGeometryGetter(info, connection);
testRasterReader(TestRaster.USHORT, info, connection);
+ testFeatures(store);
}
}
}
@@ -102,9 +114,9 @@ public final strictfp class PostgresTest extends TestCase {
*
* @throws Exception if an error occurred while testing the database.
*/
- private void testInfoStatements(final ExtendedInfo info) throws Exception {
+ private static void testInfoStatements(final ExtendedInfo info) throws Exception {
assertEquals("findSRID", 4326, info.findSRID(HardCodedCRS.WGS84));
- assertInstanceOf("fetchCRS", ProjectedCRS.class, info.fetchCRS(3395));
+ assertSame("fetchCRS", CRS.forCode("EPSG:3395"), info.fetchCRS(3395));
}
/**
@@ -113,15 +125,17 @@ public final strictfp class PostgresTest extends TestCase {
*
* @throws Exception if an error occurred while testing the database.
*/
- private void testGeometryGetter(final ExtendedInfo info, final Connection connection) throws Exception {
+ private static void testGeometryGetter(final ExtendedInfo info, final Connection connection) throws Exception {
final GeometryGetterTest test = new GeometryGetterTest();
test.testFromDatabase(connection, info, BinaryEncoding.HEXADECIMAL);
}
/**
- * Tests {@link RasterReader}.
+ * Tests {@link RasterReader} in the context of a database.
*/
- private void testRasterReader(final TestRaster test, final ExtendedInfo info, final Connection connection) throws Exception {
+ private static void testRasterReader(final TestRaster test, final ExtendedInfo info, final Connection connection)
+ throws Exception
+ {
final BinaryEncoding encoding = BinaryEncoding.HEXADECIMAL;
final RasterReader reader = new RasterReader(info);
try (PreparedStatement stmt = connection.prepareStatement("SELECT image FROM features.\"SpatialData\" WHERE filename=?")) {
@@ -134,4 +148,47 @@ public final strictfp class PostgresTest extends TestCase {
assertFalse(r.next());
}
}
+
+ /**
+ * Tests the construction of feature instances.
+ */
+ private static void testFeatures(final SQLStore store) throws DataStoreException {
+ try (Stream<Feature> features = store.findResource("SpatialData").features(false)) {
+ features.forEach(PostgresTest::validate);
+ }
+ }
+
+ /**
+ * Invoked for each feature instances for performing some checks on the feature.
+ * This method performs only a superficial verification of geometries.
+ */
+ private static void validate(final Feature feature) {
+ final String filename = feature.getPropertyValue("filename").toString();
+ final Geometry geometry = (Geometry) feature.getPropertyValue("geometry");
+ final int geomSRID;
+ switch (filename) {
+ case "raster-ushort.wkb": {
+ assertNull(geometry);
+ return;
+ }
+ case "point-prj": {
+ final Point p = (Point) geometry;
+ assertEquals(2, p.getX(), STRICT);
+ assertEquals(3, p.getY(), STRICT);
+ geomSRID = 3395;
+ break;
+ }
+ case "polygon-prj": geomSRID = 3395; break;
+ case "linestring":
+ case "polygon":
+ case "multi-linestring":
+ case "multi-polygon": geomSRID = 4326; break;
+ default: throw new AssertionError(filename);
+ }
+ try {
+ assertEquals(GeometryGetterTest.getExpectedCRS(geomSRID), JTS.getCoordinateReferenceSystem(geometry));
+ } catch (FactoryException e) {
+ throw new AssertionError(e);
+ }
+ }
}
diff --git a/storage/sis-sqlstore/src/test/resources/org/apache/sis/internal/sql/postgis/SpatialFeatures.sql b/storage/sis-sqlstore/src/test/resources/org/apache/sis/internal/sql/postgis/SpatialFeatures.sql
index 7302488..2dec522 100644
--- a/storage/sis-sqlstore/src/test/resources/org/apache/sis/internal/sql/postgis/SpatialFeatures.sql
+++ b/storage/sis-sqlstore/src/test/resources/org/apache/sis/internal/sql/postgis/SpatialFeatures.sql
@@ -32,16 +32,16 @@ INSERT INTO features."SpatialData" ("filename", "image") VALUES
-- Geometries with arbitrary coordinate values.
--
INSERT INTO features."SpatialData" ("filename", "geometry") VALUES
- ('hexa-wkb.csv:1', ST_GeomFromText('POINT(0 0)', 4326)),
- ('hexa-wkb.csv:2', ST_GeomFromText('LINESTRING(-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932)', 4326)),
- ('hexa-wkb.csv:3', ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))', 4326)),
- ('hexa-wkb.csv:4', ST_GeomFromText('POLYGON'
+ ('point-prj', ST_GeomFromText('POINT(2 3)', 3395)),
+ ('linestring', ST_GeomFromText('LINESTRING(-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932)', 4326)),
+ ('polygon-prj', ST_GeomFromText('POLYGON((0 0,0 1,1 1,1 0,0 0))', 3395)),
+ ('polygon', ST_GeomFromText('POLYGON'
|| '((-71.1776585052917 42.3902909739571,-71.1776820268866 42.3903701743239,-71.1776063012595 42.3903825660754,'
|| '-71.1775826583081 42.3903033653531,-71.1776585052917 42.3902909739571))', 4326)),
- ('hexa-wkb.csv:5', ST_GeomFromText('MULTILINESTRING'
+ ('multi-linestring', ST_GeomFromText('MULTILINESTRING'
|| '((-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932),'
|| '(-71.1031627617667 42.3152960829043,-71.102923838298 42.3149156848307))', 4326)),
- ('hexa-wkb.csv:6', ST_GeomFromText('MULTIPOLYGON('
+ ('multi-polygon', ST_GeomFromText('MULTIPOLYGON('
|| '((-71.1031880899493 42.3152774590236,-71.1031627617667 42.3152960829043,-71.102923838298 42.3149156848307,'
|| '-71.1023097974109 42.3151969047397,-71.1019285062273 42.3147384934248,-71.102505233663 42.3144722937587,'
|| '-71.1027748747100 42.3141658254797,-71.1031139451630 42.3142739188902,-71.103248764160 42.3140248998700,'
@@ -65,6 +65,6 @@ INSERT INTO features."SpatialData" ("filename", "geometry") VALUES
-- Used for parsing the same geometry in two ways and comparing the results.
--
CREATE VIEW features."Geometries" AS
- SELECT ST_AsText(geometry) AS "WKT", geometry AS "WKB"
+ SELECT ST_AsText(geometry) AS "WKT", geometry AS "WKB", ST_SRID(geometry) AS "SRID"
FROM features."SpatialData"
WHERE geometry IS NOT NULL;