This is an automated email from the ASF dual-hosted git repository. jsorel pushed a commit to branch feat/postgis-hexewkb in repository https://gitbox.apache.org/repos/asf/sis.git
commit e34e693f46188689f697694a9eccc56b91b01ca5 Author: jsorel <johann.so...@geomatys.com> AuthorDate: Thu Feb 17 16:06:43 2022 +0100 PostGIS: add HexEWKB geometry reader --- storage/sis-sqlstore/pom.xml | 7 +- .../apache/sis/internal/sql/postgis/HexEWKB.java | 193 +++++++++++++++++++++ 2 files changed, 199 insertions(+), 1 deletion(-) diff --git a/storage/sis-sqlstore/pom.xml b/storage/sis-sqlstore/pom.xml index 3053c18..e057e31 100644 --- a/storage/sis-sqlstore/pom.xml +++ b/storage/sis-sqlstore/pom.xml @@ -111,6 +111,11 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>net.postgis</groupId> + <artifactId>postgis-jdbc</artifactId> + <version>2.3.0</version> + </dependency> + <dependency> <groupId>org.apache.sis.core</groupId> <artifactId>sis-metadata</artifactId> <version>${project.version}</version> @@ -147,7 +152,7 @@ <dependency> <groupId>org.locationtech.jts</groupId> <artifactId>jts-core</artifactId> - <scope>test</scope> + <scope>compile</scope> </dependency> <dependency> <groupId>com.esri.geometry</groupId> diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/HexEWKB.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/HexEWKB.java new file mode 100644 index 0000000..041cf9f --- /dev/null +++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/HexEWKB.java @@ -0,0 +1,193 @@ +/* + * 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.postgis; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.CoordinateSequence; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.LinearRing; +import org.locationtech.jts.geom.MultiLineString; +import org.locationtech.jts.geom.MultiPoint; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.impl.PackedCoordinateSequence; +import org.postgis.binary.ByteGetter; +import org.postgis.binary.ValueGetter; +import static org.postgis.Geometry.*; + +/** + * PostGIS Hexa-EWKB Geometry reader/write classes. + * + * This format is the natural form returned by a query selection a geometry + * field whithout using any ST_X method. + * + * @see <a href="http://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT">4.2.1. PostGIS EWKB and EWKT</a> + * + * @author Johann Sorel (Geomatys) + * @module + */ +final class PostgisHexEWKB { + + 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; + + private final GeometryFactory gf; + + PostgisHexEWKB(final GeometryFactory gf) { + this.gf = gf; + } + + public Geometry read(final String value) { + if (value == null) { + return null; + } + + final ByteGetter.StringByteGetter bytes = new ByteGetter.StringByteGetter(value); + final ValueGetter vg; + final int endianess = bytes.get(0); + if (endianess == ValueGetter.XDR.NUMBER) { + vg = new ValueGetter.XDR(bytes); + } else if (endianess == ValueGetter.NDR.NUMBER) { + vg = new ValueGetter.NDR(bytes); + } else { + throw new IllegalArgumentException("Illegal endianess value : " + endianess); + } + + return readGeometry(vg, 0); + } + + private Geometry readGeometry(final ValueGetter data, int srid) { + byte endian = data.getByte(); // skip and test endian flag + if (endian != data.endian) { + throw new IllegalArgumentException("Endian inconsistency!"); + } + + //parse flags + final int flags = data.getInt(); + final boolean flagZ = (flags & MASK_Z) != 0; + final boolean flagM = (flags & MASK_M) != 0; + final boolean flagSRID = (flags & MASK_SRID) != 0; + final int geomType = (flags & MASK_GEOMTYPE); + final int nbDim = 2 + ((flagZ) ? 1 : 0) + ((flagM) ? 1 : 0); + + if (flagSRID) { + srid = data.getInt(); + } + + final Geometry geom; + switch (geomType) { + case POINT: geom = readPoint(data, nbDim); break; + case LINESTRING: geom = readLineString(data, nbDim); break; + case POLYGON: geom = readPolygon(data, nbDim, srid); break; + case MULTIPOINT: geom = readMultiPoint(data, srid); break; + case MULTILINESTRING: geom = readMultiLineString(data, srid); break; + case MULTIPOLYGON: geom = readMultiPolygon(data, srid); break; + case GEOMETRYCOLLECTION: geom = readCollection(data, srid); break; + default: throw new IllegalArgumentException("Unknown geometry type : "+geomType); + } + + geom.setSRID(srid); + return geom; + } + + private Point readPoint(final ValueGetter data, final int nbDim) { + switch (nbDim) { + case 2: + return gf.createPoint(new Coordinate(data.getDouble(), data.getDouble())); + case 3: + return gf.createPoint(new Coordinate(data.getDouble(), data.getDouble(), data.getDouble())); + case 4: + final CoordinateSequence cs = new PackedCoordinateSequence.Double(1, nbDim, 0); + cs.setOrdinate(0, 0, data.getDouble()); + cs.setOrdinate(0, 1, data.getDouble()); + cs.setOrdinate(0, 2, data.getDouble()); + cs.setOrdinate(0, 3, data.getDouble()); + return gf.createPoint(cs); + default: + throw new IllegalArgumentException("Invalid dimension number : " + nbDim); + } + } + + private CoordinateSequence readCS(final ValueGetter data, final int nbDim) { + final int nb = data.getInt(); + final CoordinateSequence cs = new PackedCoordinateSequence.Double(nb, nbDim, 0); + for (int index = 0; index < nb; index++) { + for (int ordinal = 0; ordinal < nbDim; ordinal++) { + cs.setOrdinate(index, ordinal, data.getDouble()); + } + } + return cs; + } + + private MultiPoint readMultiPoint(final ValueGetter data, final int srid) { + final Point[] geoms = new Point[data.getInt()]; + for (int i = 0; i < geoms.length; i++) { + geoms[i] = (Point) readGeometry(data, srid); + } + return gf.createMultiPoint(geoms); + } + + private LineString readLineString(final ValueGetter data, final int nbDim) { + return gf.createLineString(readCS(data, nbDim)); + } + + private LinearRing readLinearRing(final ValueGetter data, final int nbDim) { + return gf.createLinearRing(readCS(data, nbDim)); + } + + private Polygon readPolygon(final ValueGetter data, final int nbDim, final int srid) { + final LinearRing[] inners = new LinearRing[data.getInt() - 1]; + final LinearRing outter = readLinearRing(data, nbDim); + outter.setSRID(srid); + for (int i = 0; i < inners.length; i++) { + inners[i] = readLinearRing(data, nbDim); + inners[i].setSRID(srid); + } + return gf.createPolygon(outter, inners); + } + + private MultiLineString readMultiLineString(final ValueGetter data, final int srid) { + final LineString[] geoms = new LineString[data.getInt()]; + for (int i = 0; i < geoms.length; i++) { + geoms[i] = (LineString) readGeometry(data, srid); + } + return gf.createMultiLineString(geoms); + } + + private MultiPolygon readMultiPolygon(final ValueGetter data, final int srid) { + final Polygon[] geoms = new Polygon[data.getInt()]; + for (int i = 0; i < geoms.length; i++) { + geoms[i] = (Polygon) readGeometry(data, srid); + } + return gf.createMultiPolygon(geoms); + } + + private GeometryCollection readCollection(final ValueGetter data, final int srid) { + final Geometry[] geoms = new Geometry[data.getInt()]; + for (int i = 0; i < geoms.length; i++) { + geoms[i] = (Point) readGeometry(data, srid); + } + return gf.createGeometryCollection(geoms); + } + +}