Skip to content

Commit c1bb41c

Browse files
committed
OrthdromicDistance for non-points and version 0.25.7
* Changed some procedures to functions * Support returning Points in 3.4 * Support returning non-Points as maps * Orthdromic distance for non-points looks for nearest points distance * Fix for bug reported by community (withinDistance)
1 parent 9e7fb3b commit c1bb41c

File tree

11 files changed

+250
-83
lines changed

11 files changed

+250
-83
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ The Neo4j Spatial Plugin is available for inclusion in the server version of Neo
228228
* [v0.25.4 for Neo4j 3.2.8](https://github.com/neo4j-contrib/m2/blob/master/releases/org/neo4j/neo4j-spatial/0.25.4-neo4j-3.2.8/neo4j-spatial-0.25.4-neo4j-3.2.8-server-plugin.jar?raw=true)
229229
* [v0.25.5 for Neo4j 3.3.5](https://github.com/neo4j-contrib/m2/blob/master/releases/org/neo4j/neo4j-spatial/0.25.5-neo4j-3.3.5/neo4j-spatial-0.25.5-neo4j-3.3.5-server-plugin.jar?raw=true)
230230
* [v0.25.6 for Neo4j 3.4.5](https://github.com/neo4j-contrib/m2/blob/master/releases/org/neo4j/neo4j-spatial/0.25.6-neo4j-3.4.5/neo4j-spatial-0.25.6-neo4j-3.4.5-server-plugin.jar?raw=true)
231+
* [v0.25.7 for Neo4j 3.4.9](https://github.com/neo4j-contrib/m2/blob/master/releases/org/neo4j/neo4j-spatial/0.25.7-neo4j-3.4.9/neo4j-spatial-0.25.7-neo4j-3.4.9-server-plugin.jar?raw=true)
231232

232233
For versions up to 0.15-neo4j-2.3.4:
233234

@@ -343,7 +344,7 @@ Add the following repositories and dependency to your project's pom.xml:
343344
<dependency>
344345
<groupId>org.neo4j</groupId>
345346
<artifactId>neo4j-spatial</artifactId>
346-
<version>0.25.6-neo4j-3.4.5</version>
347+
<version>0.25.7-neo4j-3.4.9</version>
347348
</dependency>
348349
~~~
349350

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<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/maven-v4_0_0.xsd">
33
<properties>
4-
<neo4j.version>3.4.5</neo4j.version>
4+
<neo4j.version>3.4.9</neo4j.version>
55
<neo4j.java.version>1.8</neo4j.java.version>
66
<maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
77
<maven-gpg-plugin.version>1.4</maven-gpg-plugin.version>
@@ -21,7 +21,7 @@
2121
<modelVersion>4.0.0</modelVersion>
2222
<artifactId>neo4j-spatial</artifactId>
2323
<groupId>org.neo4j</groupId>
24-
<version>0.25.6-neo4j-3.4.5</version>
24+
<version>0.25.7-neo4j-3.4.9</version>
2525
<name>Neo4j - Spatial Components</name>
2626
<description>Spatial utilities and components for Neo4j</description>
2727
<url>http://components.neo4j.org/${project.artifactId}/${project.version}</url>

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.neo4j.gis.spatial.pipes.GeoPipeline;
2626

2727
import com.vividsolutions.jts.geom.Coordinate;
28+
import org.neo4j.gis.spatial.pipes.processing.OrthodromicDistance;
2829

2930
public class SimplePointLayer extends EditableLayerImpl {
3031

@@ -53,18 +54,18 @@ public Integer getGeometryType() {
5354
public List<GeoPipeFlow> findClosestPointsTo(Coordinate coordinate, double d) {
5455
return GeoPipeline
5556
.startNearestNeighborLatLonSearch(this, coordinate, d)
56-
.sort("OrthodromicDistance").toList();
57+
.sort(OrthodromicDistance.DISTANCE).toList();
5758
}
5859

5960
public List<GeoPipeFlow> findClosestPointsTo(Coordinate coordinate, int numberOfItemsToFind) {
6061
return GeoPipeline
6162
.startNearestNeighborLatLonSearch(this, coordinate, 2 * numberOfItemsToFind)
62-
.sort("OrthodromicDistance").next(numberOfItemsToFind);
63+
.sort(OrthodromicDistance.DISTANCE).next(numberOfItemsToFind);
6364
}
6465

6566
public List<GeoPipeFlow> findClosestPointsTo(Coordinate coordinate) {
6667
return GeoPipeline
6768
.startNearestNeighborLatLonSearch(this, coordinate, 2 * LIMIT_RESULTS)
68-
.sort("OrthodromicDistance").next(LIMIT_RESULTS);
69+
.sort(OrthodromicDistance.DISTANCE).next(LIMIT_RESULTS);
6970
}
7071
}

src/main/java/org/neo4j/gis/spatial/pipes/GeoPipeline.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ public static GeoPipeline startNearestNeighborLatLonSearch(Layer layer, Coordina
364364
.calculateOrthodromicDistance(point);
365365

366366
if (layer.getGeometryType() != null && layer.getGeometryType() == Constants.GTYPE_POINT) {
367-
pipeline = pipeline.propertyFilter("OrthodromicDistance", maxDistanceInKm, FilterPipe.Filter.LESS_THAN_EQUAL);
367+
pipeline = pipeline.propertyFilter(OrthodromicDistance.DISTANCE, maxDistanceInKm, FilterPipe.Filter.LESS_THAN_EQUAL);
368368
}
369369

370370
return pipeline;

src/main/java/org/neo4j/gis/spatial/pipes/processing/OrthodromicDistance.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
*/
2020
package org.neo4j.gis.spatial.pipes.processing;
2121

22+
import com.vividsolutions.jts.geom.Geometry;
23+
import com.vividsolutions.jts.geom.Point;
24+
import com.vividsolutions.jts.operation.distance.DistanceOp;
2225
import org.neo4j.gis.spatial.pipes.AbstractGeoPipe;
2326
import org.neo4j.gis.spatial.pipes.GeoPipeFlow;
2427

@@ -29,49 +32,58 @@
2932
/**
3033
* Calculates distance between the given geometry and item geometry for each item in the pipeline.
3134
* This pipe assume Layer contains geometries with Latitude / Longitude coordinates in degrees.
32-
*
35+
*
3336
* Algorithm reference: http://www.movable-type.co.uk/scripts/latlong-db.html
3437
*/
3538
public class OrthodromicDistance extends AbstractGeoPipe {
3639

3740
private Coordinate reference;
38-
public static final double earthRadiusInKm = 6371;
39-
41+
public static final double earthRadiusInKm = 6371;
42+
public static final String DISTANCE = "OrthodromicDistance";
43+
4044
public OrthodromicDistance(Coordinate reference) {
41-
this(reference, "OrthodromicDistance");
45+
this(reference, OrthodromicDistance.DISTANCE);
4246
}
43-
47+
4448
/**
4549
* @param resultPropertyName property name to use for geometry output
46-
*/
50+
*/
4751
public OrthodromicDistance(Coordinate reference, String resultPropertyName) {
4852
super(resultPropertyName);
4953
this.reference = reference;
5054
}
51-
52-
@Override
53-
protected GeoPipeFlow process(GeoPipeFlow flow) {
54-
// TODO check Geometry is a point? use Centroid?
55-
Coordinate point = flow.getGeometry().getCoordinate();
5655

57-
// d = acos(sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(lon2 - lon1)) * R
58-
double distanceInKm = calculateDistance(reference, point);
59-
56+
@Override
57+
protected GeoPipeFlow process(GeoPipeFlow flow) {
58+
double distanceInKm = calculateDistanceToGeometry(reference, flow.getGeometry());
6059
setProperty(flow, distanceInKm);
6160
return flow;
6261
}
6362

63+
public static double calculateDistanceToGeometry(Coordinate reference, Geometry geometry) {
64+
if (geometry instanceof Point) {
65+
Point point = (Point) geometry;
66+
return calculateDistance(reference, point.getCoordinate());
67+
} else {
68+
Geometry referencePoint = geometry.getFactory().createPoint(reference);
69+
DistanceOp ops = new DistanceOp(referencePoint, geometry);
70+
Coordinate[] nearest = ops.nearestPoints();
71+
assert nearest.length == 2;
72+
return calculateDistance(nearest[0], nearest[1]);
73+
}
74+
}
75+
6476
public static Envelope suggestSearchWindow(Coordinate reference, double maxDistanceInKm) {
6577
double lat = reference.y;
6678
double lon = reference.x;
67-
79+
6880
// first-cut bounding box (in degrees)
6981
double maxLat = lat + Math.toDegrees(maxDistanceInKm / earthRadiusInKm);
7082
double minLat = lat - Math.toDegrees(maxDistanceInKm / earthRadiusInKm);
7183
// compensate for degrees longitude getting smaller with increasing latitude
7284
double maxLon = lon + Math.toDegrees(maxDistanceInKm / earthRadiusInKm / Math.cos(Math.toRadians(lat)));
7385
double minLon = lon - Math.toDegrees(maxDistanceInKm / earthRadiusInKm / Math.cos(Math.toRadians(lat)));
74-
return new Envelope(minLon, maxLon, minLat, maxLat);
86+
return new Envelope(minLon, maxLon, minLat, maxLat);
7587
}
7688

7789
public static double calculateDistance(Coordinate reference, Coordinate point) {

src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.neo4j.gis.spatial.osm.OSMImporter;
3838
import org.neo4j.gis.spatial.pipes.GeoPipeFlow;
3939
import org.neo4j.gis.spatial.pipes.GeoPipeline;
40+
import org.neo4j.gis.spatial.pipes.processing.OrthodromicDistance;
4041
import org.neo4j.gis.spatial.rtree.ProgressLoggingListener;
4142
import org.neo4j.graphdb.GraphDatabaseService;
4243
import org.neo4j.graphdb.Node;
@@ -49,6 +50,7 @@
4950
import org.neo4j.procedure.*;
5051

5152
import static org.neo4j.gis.spatial.SpatialDatabaseService.RTREE_INDEX_NAME;
53+
import static org.neo4j.helpers.collection.MapUtil.map;
5254
import static org.neo4j.procedure.Mode.*;
5355

5456
import org.opengis.referencing.ReferenceIdentifier;
@@ -70,7 +72,6 @@
7072

7173
public class SpatialProcedures {
7274

73-
public static final String DISTANCE = "OrthodromicDistance";
7475
@Context
7576
public GraphDatabaseService db;
7677

@@ -125,7 +126,12 @@ public static class GeometryResult {
125126
public final Object geometry;
126127

127128
public GeometryResult(org.neo4j.graphdb.spatial.Geometry geometry) {
128-
this.geometry = geometry;
129+
// Unfortunately Neo4j 3.4 only copes with Points, other types need to be converted to a public type
130+
if(geometry instanceof org.neo4j.graphdb.spatial.Point) {
131+
this.geometry = geometry;
132+
}else{
133+
this.geometry = toMap(geometry);
134+
}
129135
}
130136
}
131137

@@ -534,31 +540,48 @@ public Stream<NodeDistanceResult> findGeometriesWithinDistance(
534540
Layer layer = getLayerOrThrow(name);
535541
return GeoPipeline
536542
.startNearestNeighborLatLonSearch(layer, toCoordinate(coordinate), distanceInKm)
537-
.sort(DISTANCE)
543+
.sort(OrthodromicDistance.DISTANCE)
538544
.stream().map(r -> {
539-
double distance = r.hasProperty(DISTANCE) ? ((Number) r.getProperty(DISTANCE)).doubleValue() : -1;
545+
double distance = r.hasProperty(OrthodromicDistance.DISTANCE) ? ((Number) r.getProperty(OrthodromicDistance.DISTANCE)).doubleValue() : -1;
540546
return new NodeDistanceResult(r.getGeomNode(), distance);
541547
});
542548
}
543549

544-
@Procedure("spatial.decodeGeometry")
550+
@UserFunction("spatial.decodeGeometry")
545551
@Description("Returns a geometry of a layer node as the Neo4j geometry type, to be passed to other procedures or returned to a client")
546-
public Stream<GeometryResult> decodeGeometry(
552+
public Object decodeGeometry(
547553
@Name("layerName") String name,
548554
@Name("node") Node node) {
549555

550556
Layer layer = getLayerOrThrow(name);
551-
return Stream.of(layer.getGeometryEncoder().decodeGeometry(node)).map(geom -> new GeometryResult(toNeo4jGeometry(layer, geom)));
557+
GeometryResult result = new GeometryResult(toNeo4jGeometry(layer, layer.getGeometryEncoder().decodeGeometry(node)));
558+
return result.geometry;
559+
}
560+
561+
@UserFunction("spatial.asMap")
562+
@Description("Returns a Map object representing the Geometry, to be passed to other procedures or returned to a client")
563+
public Object asMap(@Name("object") Object geometry) {
564+
return toGeometryMap(geometry);
565+
}
566+
567+
@UserFunction("spatial.asGeometry")
568+
@Description("Returns a geometry object as the Neo4j geometry type, to be passed to other functions or procedures or returned to a client")
569+
public Object asGeometry(
570+
@Name("geometry") Object geometry) {
571+
572+
return toNeo4jGeometry(null, geometry);
552573
}
553574

575+
@Deprecated
554576
@Procedure("spatial.asGeometry")
555577
@Description("Returns a geometry object as the Neo4j geometry type, to be passed to other procedures or returned to a client")
556-
public Stream<GeometryResult> asGeometry(
578+
public Stream<GeometryResult> asGeometryProc(
557579
@Name("geometry") Object geometry) {
558580

559581
return Stream.of(geometry).map(geom -> new GeometryResult(toNeo4jGeometry(null, geom)));
560582
}
561583

584+
@Deprecated
562585
@Procedure(value = "spatial.asExternalGeometry", deprecatedBy = "spatial.asGeometry")
563586
@Description("Returns a geometry object as an external geometry type to be returned to a client")
564587
// This only existed temporarily because the other method, asGeometry, returned the wrong type due to a bug in Neo4j 3.0
@@ -647,23 +670,23 @@ public Neo4jPoint(double x, double y, CRS crs) {
647670
}
648671
}
649672

650-
private org.neo4j.graphdb.spatial.Coordinate toNeo4jCoordinate(Coordinate coordinate) {
673+
private static org.neo4j.graphdb.spatial.Coordinate toNeo4jCoordinate(Coordinate coordinate) {
651674
if (coordinate.z == Coordinate.NULL_ORDINATE) {
652675
return new org.neo4j.graphdb.spatial.Coordinate(coordinate.x, coordinate.y);
653676
} else {
654677
return new org.neo4j.graphdb.spatial.Coordinate(coordinate.x, coordinate.y, coordinate.z);
655678
}
656679
}
657680

658-
private List<org.neo4j.graphdb.spatial.Coordinate> toNeo4jCoordinates(Coordinate[] coordinates) {
681+
private static List<org.neo4j.graphdb.spatial.Coordinate> toNeo4jCoordinates(Coordinate[] coordinates) {
659682
ArrayList<org.neo4j.graphdb.spatial.Coordinate> converted = new ArrayList<>();
660683
for (Coordinate coordinate : coordinates) {
661684
converted.add(toNeo4jCoordinate(coordinate));
662685
}
663686
return converted;
664687
}
665688

666-
private org.neo4j.graphdb.spatial.Geometry toNeo4jGeometry(Layer layer, Object value) {
689+
private static org.neo4j.graphdb.spatial.Geometry toNeo4jGeometry(Layer layer, Object value) {
667690
if (value instanceof org.neo4j.graphdb.spatial.Geometry) {
668691
return (org.neo4j.graphdb.spatial.Geometry) value;
669692
}
@@ -703,9 +726,71 @@ private org.neo4j.graphdb.spatial.Geometry toNeo4jGeometry(Layer layer, Object v
703726
throw new RuntimeException("Can't convert " + value + " to a geometry");
704727
}
705728

729+
private static Object toPublic(Object obj) {
730+
if (obj instanceof Map) {
731+
return toPublic((Map) obj);
732+
} else if (obj instanceof PropertyContainer) {
733+
return toPublic(((PropertyContainer) obj).getProperties());
734+
} else if (obj instanceof Geometry) {
735+
return toMap((Geometry) obj);
736+
} else {
737+
return obj;
738+
}
739+
}
740+
741+
private static Map<String, Object> toGeometryMap(Object geometry) {
742+
return toMap(toNeo4jGeometry(null, geometry));
743+
}
744+
745+
private static Map<String, Object> toMap(Geometry geometry) {
746+
return toMap(toNeo4jGeometry(null, geometry));
747+
}
748+
749+
private static double[] toCoordinateArrayFromDoubles(List<Double> coords) {
750+
double[] coordinates = new double[coords.size()];
751+
for (int i = 0; i < coordinates.length; i++) {
752+
coordinates[i] = coords.get(i);
753+
}
754+
return coordinates;
755+
}
756+
757+
private static double[][] toCoordinateArrayFromCoordinates(List<org.neo4j.graphdb.spatial.Coordinate> coords) {
758+
List<double[]> coordinates = new ArrayList<>(coords.size());
759+
for (org.neo4j.graphdb.spatial.Coordinate coord : coords) {
760+
coordinates.add(toCoordinateArrayFromDoubles(coord.getCoordinate()));
761+
}
762+
return toCoordinateArray(coordinates);
763+
}
764+
765+
private static double[][] toCoordinateArray(List<double[]> coords) {
766+
double[][] coordinates = new double[coords.size()][];
767+
for (int i = 0; i < coordinates.length; i++) {
768+
coordinates[i] = coords.get(i);
769+
}
770+
return coordinates;
771+
}
772+
773+
private static Map<String, Object> toMap(org.neo4j.graphdb.spatial.Geometry geometry) {
774+
if (geometry instanceof org.neo4j.graphdb.spatial.Point) {
775+
org.neo4j.graphdb.spatial.Point point = (org.neo4j.graphdb.spatial.Point) geometry;
776+
return map("type", geometry.getGeometryType(), "coordinate", toCoordinateArrayFromDoubles(point.getCoordinate().getCoordinate()));
777+
} else {
778+
return map("type", geometry.getGeometryType(), "coordinates", toCoordinateArrayFromCoordinates(geometry.getCoordinates()));
779+
}
780+
}
781+
782+
private static Map<String, Object> toPublic(Map incoming) {
783+
Map<String, Object> map = new HashMap<>(incoming.size());
784+
for (Object key : incoming.keySet()) {
785+
map.put(key.toString(), toPublic(incoming.get(key)));
786+
}
787+
return map;
788+
}
789+
706790
private static CRS findCRS(String crs) {
707791
switch (crs) {
708-
case "WGS-84":
792+
case "WGS-84": // name in Neo4j CRS table
793+
case "WGS84(DD)": // name in geotools crs library
709794
return makeCRS(4326, "WGS-84", "http://spatialreference.org/ref/epsg/4326/");
710795
case "Cartesian":
711796
return makeCRS(7203, "cartesian", "http://spatialreference.org/ref/sr-org/7203/");
@@ -749,19 +834,19 @@ private Coordinate toCoordinate(Object value) {
749834
throw new RuntimeException("Can't convert " + value + " to a coordinate");
750835
}
751836

752-
private Coordinate toCoordinate(org.neo4j.graphdb.spatial.Coordinate point) {
837+
private static Coordinate toCoordinate(org.neo4j.graphdb.spatial.Coordinate point) {
753838
List<Double> coordinate = point.getCoordinate();
754839
return new Coordinate(coordinate.get(0), coordinate.get(1));
755840
}
756841

757-
private Coordinate toCoordinate(Map map) {
842+
private static Coordinate toCoordinate(Map map) {
758843
if (map == null) return null;
759844
Coordinate coord = toCoordinate(map, "longitude", "latitude");
760845
if (coord == null) return toCoordinate(map, "lon", "lat");
761846
return coord;
762847
}
763848

764-
private Coordinate toCoordinate(Map map, String xName, String yName) {
849+
private static Coordinate toCoordinate(Map map, String xName, String yName) {
765850
if (map.containsKey(xName) && map.containsKey(yName))
766851
return new Coordinate(((Number) map.get(xName)).doubleValue(), ((Number) map.get(yName)).doubleValue());
767852
return null;

src/main/java/org/neo4j/gis/spatial/server/plugin/SpatialPlugin.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.vividsolutions.jts.io.WKTReader;
2525
import org.neo4j.gis.spatial.*;
2626
import org.neo4j.gis.spatial.pipes.GeoPipeline;
27+
import org.neo4j.gis.spatial.pipes.processing.OrthodromicDistance;
2728
import org.neo4j.graphdb.GraphDatabaseService;
2829
import org.neo4j.graphdb.Node;
2930
import org.neo4j.graphdb.Transaction;
@@ -279,7 +280,7 @@ public Iterable<Node> findGeometriesWithinDistance(
279280

280281
List<Node> result = GeoPipeline
281282
.startNearestNeighborLatLonSearch(layer, new Coordinate(pointX, pointY), distanceInKm)
282-
.sort("OrthodromicDistance").toNodeList();
283+
.sort(OrthodromicDistance.DISTANCE).toNodeList();
283284
tx.success();
284285
return result;
285286
}

0 commit comments

Comments
 (0)