Skip to content

Commit

Permalink
Add postgis plugin with support for JTS data types
Browse files Browse the repository at this point in the history
  • Loading branch information
bchapuis committed Jul 22, 2022
1 parent ef3b9eb commit 7299111
Show file tree
Hide file tree
Showing 4 changed files with 403 additions and 6 deletions.
43 changes: 37 additions & 6 deletions internal/build/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<artifactId>basepom-oss</artifactId>
<groupId>org.basepom</groupId>
<version>46</version>
<relativePath />
<relativePath/>
</parent>

<groupId>org.jdbi.internal</groupId>
Expand Down Expand Up @@ -58,6 +59,7 @@
<dep.jdbi-policy.version>3.31.1-SNAPSHOT</dep.jdbi-policy.version>
<dep.jetbrainsAnnotations.version>21.0.1</dep.jetbrainsAnnotations.version>
<dep.joda-time.version>2.9.9</dep.joda-time.version>
<dep.jts.version>1.19.0</dep.jts.version>
<dep.junit5.version>5.8.2</dep.junit5.version>
<dep.kotlin.version>1.7.10</dep.kotlin.version>
<dep.mockito.version>4.2.0</dep.mockito.version>
Expand All @@ -69,6 +71,7 @@
<dep.plugin.sortpom.version>2.8.0</dep.plugin.sortpom.version>
<dep.slf4j.version>1.7.32</dep.slf4j.version>
<dep.spring.version>5.3.20</dep.spring.version>
<dep.testcontainers.version>1.16.0</dep.testcontainers.version>
<dep.vavr.version>0.9.3</dep.vavr.version>
<jdbi.check.fail-detekt>${jdbi.check.fail-kotlin}</jdbi.check.fail-detekt>
<jdbi.check.fail-kotlin>${basepom.check.fail-extended}</jdbi.check.fail-kotlin>
Expand Down Expand Up @@ -226,6 +229,12 @@
</exclusions>
</dependency>

<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>${dep.jts.version}</version>
</dependency>

<dependency>
<groupId>com.opentable.components</groupId>
<artifactId>otj-pg-embedded</artifactId>
Expand Down Expand Up @@ -430,6 +439,27 @@
<type>pom</type>
<scope>import</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${dep.testcontainers.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${dep.testcontainers.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>${dep.testcontainers.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down Expand Up @@ -638,7 +668,8 @@
<configuration>
<configLocation>policy/checkstyle.xml</configLocation>
<suppressionsLocation>policy/checkstyle-suppress.xml</suppressionsLocation>
<sourceDirectories>${project.build.sourceDirectory},${project.build.testSourceDirectory}</sourceDirectories>
<sourceDirectories>${project.build.sourceDirectory},${project.build.testSourceDirectory}
</sourceDirectories>
<skip>${basepom.check.skip-extended}</skip>
</configuration>
</plugin>
Expand Down Expand Up @@ -736,7 +767,7 @@
<configuration>
<rules>
<!-- Ensure consistency -->
<dependencyConvergence />
<dependencyConvergence/>
<requireProperty>
<property>moduleName</property>
<message>This module must set a moduleName.</message>
Expand Down Expand Up @@ -791,7 +822,7 @@
</goals>
</pluginExecutionFilter>
<action>
<ignore />
<ignore/>
</action>
</pluginExecution>
<pluginExecution>
Expand All @@ -804,7 +835,7 @@
</goals>
</pluginExecutionFilter>
<action>
<ignore />
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
Expand Down
24 changes: 24 additions & 0 deletions postgres/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
Expand Down
218 changes: 218 additions & 0 deletions postgres/src/main/java/org/jdbi/v3/postgres/PostgisPlugin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/*
* Copyright (C) 2020 The Baremaps Authors
*
* 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.jdbi.v3.postgres;

import static org.locationtech.jts.io.WKBConstants.wkbNDR;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.argument.AbstractArgumentFactory;
import org.jdbi.v3.core.argument.Argument;
import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.mapper.ColumnMapper;
import org.jdbi.v3.core.spi.JdbiPlugin;
import org.jdbi.v3.core.statement.StatementContext;
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.io.ParseException;
import org.locationtech.jts.io.WKBReader;
import org.locationtech.jts.io.WKBWriter;

/**
* Postgis plugin. Adds support for binding and mapping the following data types:
*
* <ul>
* <li>{@link org.locationtech.jts.geom.Point}</li>
* <li>{@link org.locationtech.jts.geom.LineString}</li>
* <li>{@link org.locationtech.jts.geom.LinearRing}</li>
* <li>{@link org.locationtech.jts.geom.Polygon}</li>
* <li>{@link org.locationtech.jts.geom.MultiPoint}</li>
* <li>{@link org.locationtech.jts.geom.MultiLineString}</li>
* <li>{@link org.locationtech.jts.geom.MultiPolygon}</li>
* <li>{@link org.locationtech.jts.geom.GeometryCollection}</li>
* <li>{@link org.locationtech.jts.geom.Geometry}</li>
*/
public class PostgisPlugin extends JdbiPlugin.Singleton {

@Override
public void customizeJdbi(Jdbi jdbi) {
// Register argument factories
jdbi.registerArgument(new PointArgumentFactory());
jdbi.registerArgument(new LineStringArgumentFactory());
jdbi.registerArgument(new LinearRingArgumentFactory());
jdbi.registerArgument(new PolygonArgumentFactory());
jdbi.registerArgument(new MultiPointArgumentFactory());
jdbi.registerArgument(new MultiLineStringArgumentFactory());
jdbi.registerArgument(new MultiPolygonArgumentFactory());
jdbi.registerArgument(new GeometryCollectionArgumentFactory());
jdbi.registerArgument(new GeometryArgumentFactory());

// Register column mappers
jdbi.registerColumnMapper(new PointColumnMapper());
jdbi.registerColumnMapper(new LineStringColumnMapper());
jdbi.registerColumnMapper(new LinearRingColumnMapper());
jdbi.registerColumnMapper(new PolygonColumnMapper());
jdbi.registerColumnMapper(new MultiPointColumnMapper());
jdbi.registerColumnMapper(new MultiLineStringColumnMapper());
jdbi.registerColumnMapper(new MultiPolygonColumnMapper());
jdbi.registerColumnMapper(new GeometryCollectionColumnMapper());
jdbi.registerColumnMapper(new GeometryColumnMapper());
}

/**
* Serializes a geometry in the WKB format.
*
* @param geometry
* @return
*/
public static byte[] serialize(Geometry geometry) {
if (geometry == null) {
return null;
}
WKBWriter writer = new WKBWriter(2, wkbNDR, true);
return writer.write(geometry);
}

/**
* Deserializes a geometry in the WKB format.
*
* @param wkb
* @return
*/
public static Geometry deserialize(byte[] wkb) {
if (wkb == null) {
return null;
}
try {
WKBReader reader = new WKBReader(new GeometryFactory());
return reader.read(wkb);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}

abstract static class BaseArgumentFactory<T extends Geometry> extends AbstractArgumentFactory<T> {

public BaseArgumentFactory() {
super(Types.OTHER);
}

@Override
public Argument build(T value, ConfigRegistry config) {
return (position, statement, ctx) -> statement.setBytes(position, serialize(value));
}
}

abstract static class BaseColumnMapper<T extends Geometry> implements ColumnMapper<T> {

@Override
public T map(ResultSet r, int columnNumber, StatementContext ctx) throws SQLException {
byte[] bytes = hexStringToByteArray(r.getString(columnNumber));
return (T) deserialize(bytes);
}

private static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] =
(byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
}
return data;
}
}

static final class GeometryArgumentFactory extends BaseArgumentFactory<Geometry> {

}

static final class GeometryCollectionArgumentFactory extends BaseArgumentFactory<GeometryCollection> {

}

static final class GeometryCollectionColumnMapper extends BaseColumnMapper<GeometryCollection> {

}

static final class GeometryColumnMapper extends BaseColumnMapper<Geometry> {

}

static final class LinearRingArgumentFactory extends BaseArgumentFactory<LinearRing> {

}

static final class LinearRingColumnMapper extends BaseColumnMapper<LinearRing> {

}

static final class LineStringArgumentFactory extends BaseArgumentFactory<LineString> {

}

static final class LineStringColumnMapper extends BaseColumnMapper<LineString> {

}

static final class MultiLineStringArgumentFactory extends BaseArgumentFactory<MultiLineString> {

}

static final class MultiLineStringColumnMapper extends BaseColumnMapper<MultiLineString> {

}

static final class MultiPointArgumentFactory extends BaseArgumentFactory<MultiPoint> {

}

static final class MultiPointColumnMapper extends BaseColumnMapper<MultiPoint> {

}

static final class MultiPolygonArgumentFactory extends BaseArgumentFactory<MultiPolygon> {

}

static final class MultiPolygonColumnMapper extends BaseColumnMapper<MultiPolygon> {

}

static final class PointArgumentFactory extends BaseArgumentFactory<Point> {

}

static final class PointColumnMapper extends BaseColumnMapper<Point> {

}

static final class PolygonArgumentFactory extends BaseArgumentFactory<Polygon> {

}

static final class PolygonColumnMapper extends BaseColumnMapper<Polygon> {

}
}

0 comments on commit 7299111

Please sign in to comment.