Skip to content

Commit ccc291d

Browse files
authored
add function to convert neo4j native point(s) to WKT format (#409)
1 parent a1cc0e5 commit ccc291d

File tree

5 files changed

+64
-4
lines changed

5 files changed

+64
-4
lines changed

src/main/java/org/neo4j/gis/spatial/Constants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,6 @@ public interface Constants {
6969
int GTYPE_MULTILINESTRING = 5;
7070
int GTYPE_MULTIPOLYGON = 6;
7171

72+
int SRID_COORDINATES_2D = 4326;
73+
int SRID_COORDINATES_3D = 4979;
7274
}

src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jCRS.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
*/
2020
package org.neo4j.gis.spatial.encoders.neo4j;
2121

22+
import org.neo4j.gis.spatial.Constants;
2223
import org.neo4j.values.storable.CoordinateReferenceSystem;
2324

2425
public class Neo4jCRS implements org.neo4j.graphdb.spatial.CRS {
@@ -51,7 +52,7 @@ public int dimensions() {
5152
public static Neo4jCRS findCRS(String crs) {
5253
return switch (crs) { // name in Neo4j CRS table
5354
case "WGS-84", "WGS84(DD)" -> // name in geotools crs library
54-
makeCRS(4326);
55+
makeCRS(Constants.SRID_COORDINATES_2D);
5556
case "Cartesian" -> makeCRS(7203);
5657
default -> throw new IllegalArgumentException("Cypher type system does not support CRS: " + crs);
5758
};

src/main/java/org/neo4j/gis/spatial/functions/SpatialFunctions.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,22 @@
2020

2121
package org.neo4j.gis.spatial.functions;
2222

23+
import static org.neo4j.gis.spatial.Constants.SRID_COORDINATES_2D;
24+
import static org.neo4j.gis.spatial.Constants.SRID_COORDINATES_3D;
25+
26+
import java.util.Arrays;
27+
import java.util.Collection;
28+
import org.locationtech.jts.geom.Coordinate;
2329
import org.locationtech.jts.geom.Geometry;
2430
import org.locationtech.jts.io.ParseException;
2531
import org.locationtech.jts.io.WKTReader;
32+
import org.locationtech.jts.io.WKTWriter;
2633
import org.neo4j.gis.spatial.Layer;
2734
import org.neo4j.gis.spatial.procedures.SpatialProcedures.GeometryResult;
2835
import org.neo4j.gis.spatial.utilities.GeoJsonUtils;
2936
import org.neo4j.gis.spatial.utilities.SpatialApiBase;
3037
import org.neo4j.graphdb.Node;
38+
import org.neo4j.graphdb.spatial.Point;
3139
import org.neo4j.procedure.Description;
3240
import org.neo4j.procedure.Name;
3341
import org.neo4j.procedure.UserFunction;
@@ -58,7 +66,6 @@ public Object asGeometry(@Name("geometry") Object geometry) {
5866
return toNeo4jGeometry(null, geometry);
5967
}
6068

61-
6269
@UserFunction("spatial.wktToGeoJson")
6370
@Description("Converts a WKT to GeoJson structure")
6471
public Object wktToGeoJson(@Name("wkt") String wkt) throws ParseException {
@@ -69,4 +76,38 @@ public Object wktToGeoJson(@Name("wkt") String wkt) throws ParseException {
6976
Geometry geometry = wktReader.read(wkt);
7077
return GeoJsonUtils.toGeoJsonStructure(geometry);
7178
}
79+
80+
@UserFunction("spatial.neo4jGeometryToWkt")
81+
@Description("Converts a point or point array to WKT")
82+
public String nativeToWkt(@Name("data") Object object) {
83+
if (object instanceof Point point) {
84+
var coordinate = convertToCoordinate(point);
85+
return WKTWriter.toPoint(coordinate);
86+
}
87+
if (object instanceof Point[] points) {
88+
var coordinates = Arrays.stream(points).map(SpatialFunctions::convertToCoordinate)
89+
.toArray(Coordinate[]::new);
90+
return WKTWriter.toLineString(coordinates);
91+
}
92+
if (object instanceof Collection<?> points) {
93+
var coordinates = points.stream()
94+
.filter(Point.class::isInstance)
95+
.map(Point.class::cast)
96+
.map(SpatialFunctions::convertToCoordinate)
97+
.toArray(Coordinate[]::new);
98+
return WKTWriter.toLineString(coordinates);
99+
}
100+
throw new IllegalArgumentException("Unsupported type: " + object.getClass());
101+
}
102+
103+
private static Coordinate convertToCoordinate(Point point) {
104+
double[] coordinate = point.getCoordinate().getCoordinate();
105+
if (point.getCRS().getCode() == SRID_COORDINATES_3D) {
106+
return new Coordinate(coordinate[0], coordinate[1], coordinate[2]);
107+
} else if (point.getCRS().getCode() == SRID_COORDINATES_2D) {
108+
return new Coordinate(coordinate[0], coordinate[1]);
109+
} else {
110+
throw new IllegalArgumentException("Unsupported CRS: " + point.getCRS().getCode());
111+
}
112+
}
72113
}

src/main/java/org/neo4j/gis/spatial/utilities/GeotoolsAdapter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
2828
import org.geotools.referencing.CRS;
2929
import org.geotools.referencing.ReferencingFactoryFinder;
30+
import org.neo4j.gis.spatial.Constants;
3031
import org.neo4j.gis.spatial.SpatialDatabaseException;
3132

3233
/**
@@ -54,8 +55,8 @@ public static CoordinateReferenceSystem getCRS(String crsText) {
5455

5556
public static Integer getEPSGCode(CoordinateReferenceSystem crs) {
5657
try {
57-
// TODO: upgrade geotools to avoid Java11 failures on CRS.lookupEpsgCode
58-
return (crs == WGS84) ? Integer.valueOf(4326) : (crs == GENERIC_2D) ? null : CRS.lookupEpsgCode(crs, true);
58+
return (crs == WGS84) ? Integer.valueOf(Constants.SRID_COORDINATES_2D)
59+
: (crs == GENERIC_2D) ? null : CRS.lookupEpsgCode(crs, true);
5960
} catch (FactoryException e) {
6061
System.err.println("Failed to lookup CRS: " + e.getMessage());
6162
return null;

src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,4 +311,19 @@ public void testGeometryCollection() {
311311
)));
312312
}
313313
}
314+
315+
@Test
316+
public void testPointToWkt() {
317+
Object wkt = executeObject("return spatial.neo4jGeometryToWkt(point({longitude: 1, latitude: 2})) as wkt",
318+
"wkt");
319+
assertThat(wkt, equalTo("POINT ( 1 2 )"));
320+
}
321+
322+
@Test
323+
public void testPointArrayToWkt() {
324+
Object wkt = executeObject(
325+
"return spatial.neo4jGeometryToWkt([point({longitude: 1, latitude: 2}), point({longitude: 3, latitude: 4}) ]) as wkt",
326+
"wkt");
327+
assertThat(wkt, equalTo("LINESTRING (1 2, 3 4)"));
328+
}
314329
}

0 commit comments

Comments
 (0)