sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ama...@apache.org
Subject [sis] 41/45: WIP(SQLStore): Add PostGIS support.
Date Tue, 12 Nov 2019 16:45:08 GMT
This is an automated email from the ASF dual-hosted git repository.

amanin pushed a commit to branch refactor/sql-store
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 08b8773706a86070f5fa082dcf3365fbd099469b
Author: Alexis Manin <amanin@apache.org>
AuthorDate: Wed Nov 6 16:45:44 2019 +0100

    WIP(SQLStore): Add PostGIS support.
---
 .../sis/internal/sql/feature/EWKBReader.java       | 151 +++++++++++++++++++++
 .../internal/sql/feature/GeometryIdentifier.java   |  97 +++++++++++++
 2 files changed, 248 insertions(+)

diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/EWKBReader.java
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/EWKBReader.java
new file mode 100644
index 0000000..1681034
--- /dev/null
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/EWKBReader.java
@@ -0,0 +1,151 @@
+package org.apache.sis.internal.sql.feature;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+import org.apache.sis.internal.feature.Geometries;
+import org.apache.sis.math.Vector;
+import org.apache.sis.setup.GeometryLibrary;
+
+/**
+ * PostGIS Hexa-EWKB Geometry reader/write classes.
+ * http://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT
+ *
+ * This format is the natural form returned by a query selection a geometry field
+ * whithout using any ST_X method.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+class EWKBReader {
+
+    private static final int MASK_Z         = 0x80000000;
+    private static final int MASK_M         = 0x40000000;
+    private static final int MASK_SRID      = 0x20000000;
+    private static final int MASK_GEOMTYPE  = 0x1FFFFFFF;
+
+    // Copied from PostGIS JDBC source code to avoid dependency for too little information.
+    private static final int POINT = 1;
+    private static final int LINESTRING = 2;
+    private static final int POLYGON = 3;
+    private static final int MULTIPOINT = 4;
+    private static final int MULTILINESTRING = 5;
+    private static final int MULTIPOLYGON = 6;
+    private static final int GEOMETRYCOLLECTION = 7;
+
+    private final Geometries factory;
+
+    EWKBReader() {
+        this(null);
+    }
+
+    EWKBReader(GeometryLibrary library) {
+        this.factory = Geometries.implementation(library);
+    }
+
+    Object read(final byte[] eWkb) {
+        return new Reader(ByteBuffer.wrap(eWkb)).read();
+    }
+
+    private class Reader {
+        final ByteBuffer buffer;
+        final int geomType;
+        final int dimension;
+        final int srid;
+
+        private Reader(ByteBuffer buffer) {
+            final byte endianess = buffer.get();
+            if (isBigEndian(endianess)) {
+                this.buffer = buffer.order(ByteOrder.BIG_ENDIAN);
+            } else this.buffer = buffer;
+            final int     flags     = buffer.getInt();
+            final boolean flagZ     = (flags & MASK_Z)    != 0;
+            final boolean flagM     = (flags & MASK_M)    != 0;
+            final boolean flagSRID  = (flags & MASK_SRID) != 0;
+                          geomType  = (flags & MASK_GEOMTYPE);
+                          dimension = 2 + ((flagZ)?1:0) + ((flagM)?1:0);
+                          srid      = flagSRID ? buffer.getInt() : 0;
+        }
+
+        Object read() {
+            final Object geom = decodeGeometry();
+            if (srid > 0) {
+                // TODO: set CRS
+            }
+            return geom;
+        }
+
+        Object decodeGeometry() {
+            switch (geomType) {
+                case POINT:              return readPoint();
+                case LINESTRING:         return readLineString();
+                case POLYGON:            return readPolygon();
+                case MULTIPOINT:         return readMultiPoint();
+                case MULTILINESTRING:    return readMultiLineString();
+                case MULTIPOLYGON:       return readMultiPolygon();
+                case GEOMETRYCOLLECTION: return readCollection();
+            }
+
+            throw new UnsupportedOperationException("Unsupported geometry type: "+geomType);
+        }
+
+        private Object readMultiLineString() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Object readMultiPolygon() {
+            throw new UnsupportedOperationException();
+        }
+
+        private Object readCollection() {
+            throw new UnsupportedOperationException();
+        }
+
+        final Object readPoint() {
+            final double[] ordinates = readCoordinateSequence(1);
+            // TODO: we lose information here ! We need to evolve Geometry API.
+            return factory.createPoint(ordinates[0], ordinates[1]);
+        }
+
+        final Object readLineString() {
+            final double[] ordinates = readCoordinateSequence();
+            return factory.createPolyline(dimension, Vector.create(ordinates));
+        }
+
+        private Object readPolygon() {
+            final int nbRings=buffer.getInt();
+            final Vector outerShell = Vector.create(readCoordinateSequence());
+
+            final double[] nans = new double[dimension];
+            Arrays.fill(nans, Double.NaN);
+            final Vector separator = Vector.create(nans);
+            final Vector[] allShells = new Vector[nbRings + nbRings -1]; // include ring
separators
+            allShells[0] = outerShell;
+            for (int i = 1 ; i < nbRings ;) {
+                allShells[i++] = separator;
+                allShells[i++] = Vector.create(readCoordinateSequence());
+            }
+
+            return factory.toPolygon(factory.createPolyline(dimension, outerShell));
+        }
+
+        private double[] readMultiPoint() {
+            throw new UnsupportedOperationException();
+        }
+
+        private double[] readCoordinateSequence() {
+            return readCoordinateSequence(buffer.getInt());
+        }
+
+        private double[] readCoordinateSequence(int nbPts) {
+            final double[] brutPoint = new double[dimension*nbPts];
+            for (int i = 0 ; i < brutPoint.length ; i++) brutPoint[i] = buffer.getDouble();
+            return brutPoint;
+        }
+
+    }
+
+    private static boolean isBigEndian(byte endianess) {
+        return endianess == 0; // org.postgis.binary.ValueGetter.XDR.NUMBER
+    }
+}
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/GeometryIdentifier.java
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/GeometryIdentifier.java
new file mode 100644
index 0000000..cd0cdc0
--- /dev/null
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/GeometryIdentifier.java
@@ -0,0 +1,97 @@
+package org.apache.sis.internal.sql.feature;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.sis.internal.metadata.sql.SQLBuilder;
+
+import static org.apache.sis.util.ArgumentChecks.ensureNonEmpty;
+
+/**
+ * Not THREAD-SAFE !
+ */
+class GeometryIdentifier implements AutoCloseable {
+
+    private final Connection c;
+    final PreparedStatement identifySchemaQuery;
+    final PreparedStatement columnQuery;
+
+    /**
+     * Prefetch an SQL builder, so subsequent calls will use a copy constructor to avoid
re-analyzing database metadata.
+     */
+    private final SQLBuilder template;
+
+    public GeometryIdentifier(Connection c, boolean quoteSchema) throws SQLException {
+        this.c = c;
+        template = new SQLBuilder(c.getMetaData(), quoteSchema);
+        identifySchemaQuery = c.prepareStatement("SELECT DISTINCT(f_table_schema) FROM GEOMETRY_COLUMNS
WHERE f_table_name = ?");
+        columnQuery = c.prepareStatement("SELECT f_geometry_column, coord_dimension, srid
FROM GEOMETRY_COLUMNS WHERE f_schema_name = ? AND table = ?");
+    }
+
+    Set<GeometryColumn> fetchGeometricColumns(String schema, final String table) throws
SQLException {
+        final SQLBuilder builder = new SQLBuilder(template);
+        ensureNonEmpty("Table name", table);
+        if (schema == null || (schema = schema.trim()).isEmpty()) {
+            // To avoid ambiguity, we have to restrict search to a single schema
+            identifySchemaQuery.setString(1, table);
+            try (ResultSet result = identifySchemaQuery.executeQuery()) {
+                if (!result.next()) return Collections.EMPTY_SET;
+                schema = result.getString(1);
+                if (result.next()) throw new IllegalArgumentException("Multiple tables match
given name. Please specify schema to remove all ambiguities");
+            }
+        }
+
+        columnQuery.setString(1, schema);
+        columnQuery.setString(2, table);
+        try (ResultSet result = columnQuery.executeQuery()) {
+            final HashSet<GeometryColumn> cols = new HashSet<>();
+            while (result.next()) {
+                cols.add(new GeometryColumn(result.getString(1), result.getInt(2), result.getInt(3)));
+            }
+            return cols;
+        }
+    }
+
+    @Override
+    public void close() throws SQLException {
+        try (SQLCloseable c1 = columnQuery::close; SQLCloseable c2 = identifySchemaQuery::close;)
{}
+    }
+
+    private interface SQLCloseable extends AutoCloseable {
+        @Override
+        void close() throws SQLException;
+    }
+
+    static final class GeometryColumn {
+        final String name;
+        final int dimension;
+        final int srid;
+
+        GeometryColumn(String name, int dimension, int srid) {
+            this.name = name;
+            this.dimension = dimension;
+            this.srid = srid;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            GeometryColumn that = (GeometryColumn) o;
+            return dimension == that.dimension &&
+                    srid == that.srid &&
+                    name.equals(that.name);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(name);
+        }
+    }
+}


Mime
View raw message