diff --git a/pom.xml b/pom.xml index 32518e9..3ebe0fb 100644 --- a/pom.xml +++ b/pom.xml @@ -21,48 +21,84 @@ de.deepamehta deepamehta-geomaps 4.4-SNAPSHOT + provided org.neo4j neo4j-kernel 1.8.1 + provided + + + org.neo4j + neo4j-graph-collections + 0.4-neo4j-1.8.2 + provided org.neo4j neo4j-spatial 0.9.1-neo4j-1.8.2 + provided + - org.neo4j - neo4j-graph-collections - 0.4-neo4j-1.8.2 + org.geotools + gt-api + 8.0 + runtime org.geotools - gt-metadata + gt-opengis 8.0 + runtime org.geotools - gt-opengis + gt-metadata 8.0 + runtime org.geotools gt-referencing 8.0 + runtime + com.vividsolutions jts 1.12 + provided org.jscience jscience 4.3.1 + runtime + + + + com.tinkerpop.gremlin + gremlin-java + 1.5 + provided + + + com.tinkerpop.gremlin + gremlin-groovy + 1.5 + provided + + + com.tinkerpop + pipes + 1.0 + provided @@ -93,9 +129,10 @@ org.neo4j.graphdb, org.neo4j.graphdb.traversal, org.neo4j.kernel, !org.neo4j.*, - !com.tinkerpop.*, + !com.tinkerpop.blueprints.*, !org.geotools.*, !org.json.simple.*, !au.com.objectix.jgridshift, + !groovy.lang, !org.codehaus.groovy.*, !jline, !org.apache.commons.collections, !org.apache.commons.pool.*, !org.apache.commons.logging.*, !org.apache.commons.lang, !org.apache.log4j, @@ -103,8 +140,9 @@ neo4j-spatial, neo4j-graph-collections, - gt-metadata, gt-opengis, gt-referencing, - jts, jscience + gt-api, gt-opengis, gt-metadata, gt-referencing, + jts, jscience, + gremlin-java, gremlin-groovy, pipes diff --git a/src/main/java/de/deepamehta/plugins/geospatial/GeospatialPlugin.java b/src/main/java/de/deepamehta/plugins/geospatial/GeospatialPlugin.java index f2182e2..5f44776 100644 --- a/src/main/java/de/deepamehta/plugins/geospatial/GeospatialPlugin.java +++ b/src/main/java/de/deepamehta/plugins/geospatial/GeospatialPlugin.java @@ -1,5 +1,6 @@ package de.deepamehta.plugins.geospatial; +import de.deepamehta.plugins.geospatial.service.GeospatialService; import de.deepamehta.plugins.geomaps.model.GeoCoordinate; import de.deepamehta.core.CompositeValue; @@ -11,26 +12,48 @@ import de.deepamehta.core.service.event.PostCreateTopicListener; import de.deepamehta.core.service.event.PostUpdateTopicListener; +import org.neo4j.graphdb.Node; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.collections.rtree.NullListener; import org.neo4j.gis.spatial.SimplePointLayer; +import org.neo4j.gis.spatial.SpatialDatabaseRecord; import org.neo4j.gis.spatial.SpatialDatabaseService; +import org.neo4j.gis.spatial.pipes.GeoPipeFlow; +import org.neo4j.gis.spatial.pipes.GeoPipeline; -// ### import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.Point; // ### import com.vividsolutions.jts.geom.Geometry; // ### import com.vividsolutions.jts.geom.GeometryFactory; -// ### import com.vividsolutions.jts.geom.Point; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.Produces; +import javax.ws.rs.Consumes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.logging.Logger; -public class GeospatialPlugin extends PluginActivator implements PostCreateTopicListener, PostUpdateTopicListener { +@Path("/geospatial") +@Consumes("application/json") +@Produces("application/json") +public class GeospatialPlugin extends PluginActivator implements GeospatialService, PostCreateTopicListener, + PostUpdateTopicListener { // ------------------------------------------------------------------------------------------------------- Constants private static final String DEFAULT_LAYER_NAME = "dm4.geospatial.layer"; + private static final String PROP_GEO_COORD_ID = "geo_coord_id"; + // ---------------------------------------------------------------------------------------------- Instance Variables SimplePointLayer layer; @@ -41,6 +64,43 @@ public class GeospatialPlugin extends PluginActivator implements PostCreateTopic + // **************************************** + // *** GeospatialService Implementation *** + // **************************************** + + + + @GET + @Path("/{geo_coord}/distance/{distance}") + @Override + public List getTopicsWithinDistance(@PathParam("geo_coord") GeoCoordinate geoCoord, + @PathParam("distance") double maxDistanceInKm) { + try { + // query geospatial index + Coordinate point = new Coordinate(geoCoord.lon, geoCoord.lat); + GeoPipeline spatialRecords = GeoPipeline.startNearestNeighborLatLonSearch(layer, point, maxDistanceInKm); + /* ### .sort("OrthodromicDistance") */ + // + // build result + List geoCoords = new ArrayList(); + for (GeoPipeFlow spatialRecord : spatialRecords) { + // Note: long distance = spatialRecord.getProperty("OrthodromicDistance") + long geoCoordId = (Long) spatialRecord.getRecord().getGeomNode().getProperty(PROP_GEO_COORD_ID, -1L); + if (geoCoordId == -1) { + Point p = (Point) spatialRecord.getGeometry(); + throw new RuntimeException("A spatial database record misses the \"" + PROP_GEO_COORD_ID + + "\" property (lon=" + p.getX() + ", lat=" + p.getY() + ")"); + } + geoCoords.add(dms.getTopic(geoCoordId, true)); // fetchComposite=true + } + return geoCoords; + } catch (Exception e) { + throw new RuntimeException("Quering the geospatial index failed", e); + } + } + + + // **************************** // *** Hook Implementations *** // **************************** @@ -52,7 +112,7 @@ public void init() { GraphDatabaseService neo4j = (GraphDatabaseService) dms.getDatabaseVendorObject(); SpatialDatabaseService spatialDB = new SpatialDatabaseService(neo4j); // - //spatialDB.deleteLayer(DEFAULT_LAYER_NAME, null); + // spatialDB.deleteLayer(DEFAULT_LAYER_NAME, new NullListener()); // if (spatialDB.containsLayer(DEFAULT_LAYER_NAME)) { logger.info("########## Default layer already exists (\"" + DEFAULT_LAYER_NAME + "\")"); @@ -91,7 +151,8 @@ private void indexIfGeoCoordinate(Topic topic) { GeoCoordinate geoCoord = geoCoordinate(topic); logger.info("########## Indexing Geo Coordinate " + topic.getId() + " (long=" + geoCoord.lon + ", lat=" + geoCoord.lat + ")"); - layer.add(geoCoord.lon, geoCoord.lat); + SpatialDatabaseRecord record = layer.add(geoCoord.lon, geoCoord.lat); + record.setProperty(PROP_GEO_COORD_ID, topic.getId()); } } @@ -103,4 +164,13 @@ private GeoCoordinate geoCoordinate(Topic geoCoordTopic) { comp.getDouble("dm4.geomaps.latitude") ); } + + // ### not used + private Map nodeProperties(Node node) { + Map props = new HashMap(); + for (String key : node.getPropertyKeys()) { + props.put(key, node.getProperty(key)); + } + return props; + } } diff --git a/src/main/java/de/deepamehta/plugins/geospatial/service/GeospatialService.java b/src/main/java/de/deepamehta/plugins/geospatial/service/GeospatialService.java new file mode 100644 index 0000000..0e4cd37 --- /dev/null +++ b/src/main/java/de/deepamehta/plugins/geospatial/service/GeospatialService.java @@ -0,0 +1,15 @@ +package de.deepamehta.plugins.geospatial.service; + +import de.deepamehta.plugins.geomaps.model.GeoCoordinate; + +import de.deepamehta.core.Topic; +import de.deepamehta.core.service.PluginService; + +import java.util.List; + + + +public interface GeospatialService extends PluginService { + + List getTopicsWithinDistance(GeoCoordinate geoCoord, double maxDistanceInKm); +}