From 93561ea1a872511a58ee717a7ddf809e043b0402 Mon Sep 17 00:00:00 2001 From: ShadelessFox Date: Tue, 19 Jan 2021 16:21:52 +0300 Subject: [PATCH] #9526 Add support for GeoPackage geometry data --- .../META-INF/MANIFEST.MF | 3 +- .../META-INF/MANIFEST.MF | 4 +- .../data/SQLiteGeometryValueHandler.java | 138 ++++++++++++++++++ .../data/SQLiteValueHandlerProvider.java | 4 + 4 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteGeometryValueHandler.java diff --git a/plugins/org.jkiss.dbeaver.ext.sqlite.ui/META-INF/MANIFEST.MF b/plugins/org.jkiss.dbeaver.ext.sqlite.ui/META-INF/MANIFEST.MF index d568316e44..49de383748 100644 --- a/plugins/org.jkiss.dbeaver.ext.sqlite.ui/META-INF/MANIFEST.MF +++ b/plugins/org.jkiss.dbeaver.ext.sqlite.ui/META-INF/MANIFEST.MF @@ -10,7 +10,8 @@ Require-Bundle: org.eclipse.ui, org.jkiss.dbeaver.ext.sqlite, org.jkiss.dbeaver.ui, org.jkiss.dbeaver.model, - org.jkiss.dbeaver.ui.editors.data + org.jkiss.dbeaver.ui.editors.data, + org.jkiss.dbeaver.data.gis.view Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-Vendor: %Bundle-Vendor diff --git a/plugins/org.jkiss.dbeaver.ext.sqlite/META-INF/MANIFEST.MF b/plugins/org.jkiss.dbeaver.ext.sqlite/META-INF/MANIFEST.MF index ba1ef5f324..9e87b2d04c 100644 --- a/plugins/org.jkiss.dbeaver.ext.sqlite/META-INF/MANIFEST.MF +++ b/plugins/org.jkiss.dbeaver.ext.sqlite/META-INF/MANIFEST.MF @@ -5,7 +5,9 @@ Bundle-SymbolicName: org.jkiss.dbeaver.ext.sqlite;singleton:=true Bundle-Version: 1.0.98.qualifier Bundle-Release-Date: 20210118 Require-Bundle: org.jkiss.dbeaver.model, - org.jkiss.dbeaver.ext.generic + org.jkiss.dbeaver.ext.generic, + org.jkiss.dbeaver.data.gis, + org.jkiss.dbeaver.model Export-Package: org.jkiss.dbeaver.ext.sqlite, org.jkiss.dbeaver.ext.sqlite.model Bundle-ActivationPolicy: lazy diff --git a/plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteGeometryValueHandler.java b/plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteGeometryValueHandler.java new file mode 100644 index 0000000000..9321df38dc --- /dev/null +++ b/plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteGeometryValueHandler.java @@ -0,0 +1,138 @@ +/* + * DBeaver - Universal Database Manager + * Copyright (C) 2010-2021 DBeaver Corp and others + * + * Licensed 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.jkiss.dbeaver.ext.sqlite.model.data; + +import org.jkiss.code.NotNull; +import org.jkiss.code.Nullable; +import org.jkiss.dbeaver.Log; +import org.jkiss.dbeaver.model.exec.DBCException; +import org.jkiss.dbeaver.model.exec.DBCSession; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCPreparedStatement; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCResultSet; +import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession; +import org.jkiss.dbeaver.model.gis.DBGeometry; +import org.jkiss.dbeaver.model.impl.jdbc.data.handlers.JDBCAbstractValueHandler; +import org.jkiss.dbeaver.model.struct.DBSTypedObject; +import org.jkiss.utils.CommonUtils; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.io.ByteArrayInStream; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKBReader; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.sql.SQLException; + +public class SQLiteGeometryValueHandler extends JDBCAbstractValueHandler { + public static final SQLiteGeometryValueHandler INSTANCE = new SQLiteGeometryValueHandler(); + + public static final String[] GEOMETRY_TYPES = new String[]{ + "GEOMETRY", + "POINT", + "LINESTRING", + "POLYGON", + "MULTIPOINT", + "MULTILINESTRING", + "MULTIPOLYGON", + "GEOMETRYCOLLECTION" + }; + + private static final Log log = Log.getLog(SQLiteGeometryValueHandler.class); + + @NotNull + @Override + public Class getValueObjectType(@NotNull DBSTypedObject attribute) { + return DBGeometry.class; + } + + @Nullable + @Override + public Object getValueFromObject(@NotNull DBCSession session, @NotNull DBSTypedObject type, @Nullable Object object, boolean copy, boolean validateValue) throws DBCException { + if (object == null) { + return new DBGeometry(); + } + + if (object instanceof byte[]) { + final byte[] bytes = (byte[]) object; + final ByteBuffer buffer = ByteBuffer.wrap(bytes); + + // http://www.geopackage.org/spec121/index.html#gpb_format + + if (buffer.get() != 'G' || buffer.get() != 'P') { + log.error("Invalid GeoPackage data"); + return object; + } + + final byte version = buffer.get(); + + if (version != 0) { + log.error("Invalid GeoPackage version: " + version); + return object; + } + + final byte flags = buffer.get(); + + if (CommonUtils.isBitSet(flags, 1)) { + buffer.order(ByteOrder.LITTLE_ENDIAN); + } + + final int srsId = buffer.getInt(); + + switch ((byte) ((flags >> 1) & 0b111)) { + case 1: + buffer.position(buffer.position() + 32); + break; + case 2: + case 3: + buffer.position(buffer.position() + 48); + break; + case 4: + buffer.position(buffer.position() + 64); + break; + } + + byte[] wkb = new byte[bytes.length - buffer.position()]; + System.arraycopy(bytes, buffer.position(), wkb, 0, wkb.length); + + try { + GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), srsId); + WKBReader wkbReader = new WKBReader(geometryFactory); + Geometry geometry = wkbReader.read(new ByteArrayInStream(wkb)); + return new DBGeometry(geometry, srsId); + } catch (IOException | ParseException e) { + throw new DBCException("Error reading GeoPackage WKB", e); + } + } + + return object; + } + + @Nullable + @Override + protected Object fetchColumnValue(DBCSession session, JDBCResultSet resultSet, DBSTypedObject type, int index) throws DBCException, SQLException { + Object object = resultSet.getObject(index); + return getValueFromObject(session, type, object, false, false); + } + + @Override + protected void bindParameter(JDBCSession session, JDBCPreparedStatement statement, DBSTypedObject paramType, int paramIndex, Object value) throws SQLException { + statement.setObject(paramIndex, value); + } +} diff --git a/plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteValueHandlerProvider.java b/plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteValueHandlerProvider.java index a6ff2314e8..40d96ca86e 100644 --- a/plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteValueHandlerProvider.java +++ b/plugins/org.jkiss.dbeaver.ext.sqlite/src/org/jkiss/dbeaver/ext/sqlite/model/data/SQLiteValueHandlerProvider.java @@ -24,6 +24,7 @@ import org.jkiss.dbeaver.model.data.DBDValueHandler; import org.jkiss.dbeaver.model.data.DBDValueHandlerProvider; import org.jkiss.dbeaver.model.impl.jdbc.data.handlers.JDBCContentValueHandler; import org.jkiss.dbeaver.model.struct.DBSTypedObject; +import org.jkiss.utils.ArrayUtils; /** * SQLiteValueHandlerProvider @@ -38,6 +39,9 @@ public class SQLiteValueHandlerProvider implements DBDValueHandlerProvider { if (dataKind == DBPDataKind.BINARY) { return JDBCContentValueHandler.INSTANCE; } + if (ArrayUtils.contains(SQLiteGeometryValueHandler.GEOMETRY_TYPES, typedObject.getTypeName())) { + return SQLiteGeometryValueHandler.INSTANCE; + } // All types must be handled by unified SQLite handler return new SQLiteValueHandler(typedObject, preferences); } -- GitLab