Skip to content

Commit

Permalink
Add support for native point array properties
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy2003 committed May 30, 2024
1 parent 95f88c4 commit 848eb29
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 44 deletions.
10 changes: 10 additions & 0 deletions src/main/java/org/neo4j/gis/spatial/AbstractGeometryEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import org.apache.commons.lang3.ArrayUtils;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.neo4j.gis.spatial.rtree.Envelope;
import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.Node;
Expand All @@ -30,6 +31,15 @@ public abstract class AbstractGeometryEncoder implements GeometryEncoder, Consta

protected String bboxProperty = PROP_BBOX;

private GeometryFactory geometryFactory;

protected GeometryFactory getGeometryFactory() {
if (geometryFactory == null) {
geometryFactory = new GeometryFactory();
}
return geometryFactory;
}

// Public methods

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.locationtech.jts.geom.Polygon;
import org.neo4j.gis.spatial.encoders.Configurable;
import org.neo4j.gis.spatial.encoders.NativePointEncoder;
import org.neo4j.gis.spatial.encoders.NativePointsEncoder;
import org.neo4j.gis.spatial.encoders.SimplePointEncoder;
import org.neo4j.gis.spatial.index.IndexManager;
import org.neo4j.gis.spatial.index.LayerGeohashPointIndex;
Expand Down Expand Up @@ -526,6 +527,8 @@ String getSignature() {
"longitude:latitude"));
addRegisteredLayerType(new RegisteredLayerType("NativePoint", NativePointEncoder.class,
SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "location"));
addRegisteredLayerType(new RegisteredLayerType("NativePoints", NativePointsEncoder.class,
EditableLayerImpl.class, DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "geometry"));
addRegisteredLayerType(new RegisteredLayerType("NativeGeohash", NativePointEncoder.class,
SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerGeohashPointIndex.class, "location"));
addRegisteredLayerType(new RegisteredLayerType("NativeZOrder", NativePointEncoder.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.neo4j.gis.spatial.AbstractGeometryEncoder;
import org.neo4j.gis.spatial.SpatialDatabaseService;
Expand All @@ -36,22 +35,14 @@
public class NativePointEncoder extends AbstractGeometryEncoder implements Configurable {

private static final String DEFAULT_GEOM = "location";
private static GeometryFactory geometryFactory;
private String locationProperty = DEFAULT_GEOM;
private Neo4jCRS crs = Neo4jCRS.findCRS("WGS-84");

protected static GeometryFactory getGeometryFactory() {
if (geometryFactory == null) {
geometryFactory = new GeometryFactory();
}
return geometryFactory;
}

@Override
protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
int gtype = SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass());
if (gtype == GTYPE_POINT) {
container.setProperty("gtype", gtype);
container.setProperty(PROP_TYPE, gtype);
Neo4jPoint neo4jPoint = new Neo4jPoint((Point) geometry, crs);
container.setProperty(locationProperty, neo4jPoint);
} else {
Expand Down
106 changes: 106 additions & 0 deletions src/main/java/org/neo4j/gis/spatial/encoders/NativePointsEncoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j Spatial.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.gis.spatial.encoders;

import java.util.Arrays;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.neo4j.gis.spatial.AbstractGeometryEncoder;
import org.neo4j.gis.spatial.SpatialDatabaseService;
import org.neo4j.gis.spatial.encoders.neo4j.Neo4jCRS;
import org.neo4j.gis.spatial.encoders.neo4j.Neo4jPoint;
import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.Transaction;

/**
* Simple encoder that stores line strings as an array of points.
*/
public class NativePointsEncoder extends AbstractGeometryEncoder implements Configurable {

private String property = "geometry";
private Neo4jCRS crs = Neo4jCRS.findCRS("WGS-84");

@Override
protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
var gtype = SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass());
if (geometry instanceof LineString lineString) {
var neo4jPoints = Arrays.stream(lineString.getCoordinates())
.map(coordinate -> new Neo4jPoint(coordinate, crs))
.toArray(org.neo4j.graphdb.spatial.Point[]::new);
container.setProperty(PROP_TYPE, gtype);
container.setProperty(property, neo4jPoints);
} else {
throw new IllegalArgumentException(
"Can only store point-arrays as linestring: " + SpatialDatabaseService.convertGeometryTypeToName(
gtype));
}

}

@Override
public Geometry decodeGeometry(Entity container) {
var points = ((org.neo4j.graphdb.spatial.Point[]) container.getProperty(property));
var factory = getGeometryFactory();
var coordinates = Arrays.stream(points)
.map(point -> {
if (point.getCRS().getCode() != crs.getCode()) {
throw new IllegalStateException(
"Trying to decode geometry with wrong CRS: layer configured to crs=" + crs
+ ", but geometry has crs=" + point.getCRS().getCode());
}
double[] coordinate = point.getCoordinate().getCoordinate();
if (crs.dimensions() == 3) {
return new Coordinate(coordinate[0], coordinate[1], coordinate[2]);
} else {
return new Coordinate(coordinate[0], coordinate[1]);
}
})
.toArray(Coordinate[]::new);
return factory.createLineString(coordinates);
}

@Override
public String getConfiguration() {
return property + ":" + bboxProperty + ": " + crs.getCode();
}

@Override
public void setConfiguration(String configuration) {
if (configuration != null && !configuration.trim().isEmpty()) {
String[] fields = configuration.split(":");
if (fields.length > 0) {
property = fields[0];
}
if (fields.length > 1) {
bboxProperty = fields[1];
}
if (fields.length > 2) {
crs = Neo4jCRS.findCRS(fields[2]);
}
}
}

@Override
public String getSignature() {
return "NativePointEncoder(geometry='" + property + "', bbox='" + bboxProperty + "', crs=" + crs.getCode()
+ ")";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.CoordinateList;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.neo4j.gis.spatial.AbstractGeometryEncoder;
import org.neo4j.gis.spatial.SpatialDatabaseException;
import org.neo4j.graphdb.Direction;
Expand All @@ -41,19 +40,10 @@
// TODO: Consider generalizing this code and making a general linked list geometry store available in the library
public class SimpleGraphEncoder extends AbstractGeometryEncoder {

private GeometryFactory geometryFactory;

protected enum SimpleRelationshipTypes implements RelationshipType {
FIRST, NEXT
}

private GeometryFactory getGeometryFactory() {
if (geometryFactory == null) {
geometryFactory = new GeometryFactory();
}
return geometryFactory;
}

private static Node testIsNode(Entity container) {
if (!(container instanceof Node)) {
throw new SpatialDatabaseException("Cannot decode non-node geometry: " + container);
Expand All @@ -64,7 +54,7 @@ private static Node testIsNode(Entity container) {
@Override
protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
Node node = testIsNode(container);
node.setProperty("gtype", GTYPE_LINESTRING);
node.setProperty(PROP_TYPE, GTYPE_LINESTRING);
Node prev = null;
for (Coordinate coord : geometry.getCoordinates()) {
Node point = tx.createNode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.neo4j.gis.spatial.AbstractGeometryEncoder;
import org.neo4j.gis.spatial.SpatialDatabaseService;
import org.neo4j.graphdb.Entity;
Expand All @@ -34,21 +33,13 @@ public class SimplePointEncoder extends AbstractGeometryEncoder implements Confi

public static final String DEFAULT_X = "longitude";
public static final String DEFAULT_Y = "latitude";
protected GeometryFactory geometryFactory;
protected String xProperty = DEFAULT_X;
protected String yProperty = DEFAULT_Y;

protected GeometryFactory getGeometryFactory() {
if (geometryFactory == null) {
geometryFactory = new GeometryFactory();
}
return geometryFactory;
}

@Override
protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
container.setProperty(
"gtype",
PROP_TYPE,
SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass()));
Coordinate[] coords = geometry.getCoordinates();
container.setProperty(xProperty, coords[0].x);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.neo4j.gis.spatial.AbstractGeometryEncoder;
import org.neo4j.gis.spatial.SpatialDatabaseService;
import org.neo4j.graphdb.Entity;
Expand All @@ -35,18 +34,9 @@
// TODO: Consider switching from Float to Double according to Davide Savazzi
public class SimplePropertyEncoder extends AbstractGeometryEncoder {

protected GeometryFactory geometryFactory;

protected GeometryFactory getGeometryFactory() {
if (geometryFactory == null) {
geometryFactory = new GeometryFactory();
}
return geometryFactory;
}

@Override
protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
container.setProperty("gtype", SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass()));
container.setProperty(PROP_TYPE, SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass()));
Coordinate[] coords = geometry.getCoordinates();
float[] data = new float[coords.length * 2];
for (int i = 0; i < coords.length; i++) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ protected void addNodeGeometry(WrappedNode node, int gtype, Envelope bbox, int v
gtype = vertices > 1 ? GTYPE_MULTIPOINT : GTYPE_POINT;
}
Node geomNode = tx.createNode();
geomNode.setProperty("gtype", gtype);
geomNode.setProperty(PROP_TYPE, gtype);
geomNode.setProperty("vertices", vertices);
geomNode.setProperty(PROP_BBOX,
new double[]{bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox.getMaxY()});
Expand Down Expand Up @@ -1207,7 +1207,7 @@ protected void updateGeometryMetaDataFromMember(WrappedNode member, GeometryMeta
try (var relationships = member.getRelationships(OSMRelation.GEOM)) {
for (Relationship rel : relationships) {
nodeProps = getNodeProperties(WrappedNode.fromNode(rel.getEndNode()));
metaGeom.checkSupportedGeometry((Integer) nodeProps.get("gtype"));
metaGeom.checkSupportedGeometry((Integer) nodeProps.get(PROP_TYPE));
metaGeom.expandToIncludeBBox(nodeProps);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,16 @@ public void find_no_geometries_using_closest_on_empty_layer() {
testCallCount(db, "CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)", null, 0);
}

@Test
public void testNativePoints() {
execute("CREATE (node:Foo { points: [point({latitude: 5.0, longitude: 4.0}), point({latitude: 6.0, longitude: 5.0})]})");
execute("CALL spatial.addLayer('line','NativePoints','points') YIELD node" +
" MATCH (n:Foo)" +
" WITH collect(n) AS nodes" +
" CALL spatial.addNodes('line', nodes) YIELD count RETURN count");
testCallCount(db, "CALL spatial.closest('line',{lon:5.1, lat:4.1}, 1.0)", null, 1);
}

/*
@Test
Expand Down

0 comments on commit 848eb29

Please sign in to comment.