diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..639f1bec --- /dev/null +++ b/docs/index.html @@ -0,0 +1,2160 @@ + + + + + + + +Neo4j Spatial v5.20.1-SNAPSHOT + + + + + +
+
+

Introduction

+
+
+

Neo4j Spatial is a library of utilities for Neo4j that facilitate the enabling of spatial operations on data. +In particular, you can add spatial indexes to already located data, and perform spatial operations on the data like searching for data within specified regions or within a specified distance of a point of interest. +In addition, classes are provided to expose the data to GeoTools and thereby to GeoTools-enabled applications like GeoServer and uDig.

+
+
+
+one street +
+
+
+

The key features include:

+
+
+
    +
  • +

    Utilities for importing from ESRI Shapefile as well as Open Street Map files

    +
  • +
  • +

    Support for all the common geometry types: Point, LineString, Polygon, etc.

    +
  • +
  • +

    An RTree index for fast searches on geometries

    +
  • +
  • +

    Support for topology operations during the search (contains, within, intersects, covers, disjoint, etc.)

    +
  • +
  • +

    The possibility to enable spatial operations on any graph of data, regardless of the way the spatial data is stored, as long as an adapter is provided to map from the graph to the geometries.

    +
  • +
  • +

    Ability to split a single layer or dataset into multiple sub-layers or views with pre-configured filters

    +
  • +
+
+
+

Get Started

+
+

The easiest way to get started with neo4j-spatial is to grab the server-plugin-*.jar from the latest release, copy it to your $NEO4J_HOME/plugins and restart your Neo4j server.

+
+
+

From there you can use all the Neo4j Spatial Procedures in your Cypher queries to add Nodes to the spatial index and perform a number of spatial point, distance and intersection queries.

+
+
+
Simple Example
+
+
CALL spatial.addPointLayer('geom');
+CALL spatial.layers();
+
+CREATE (n:Node {latitude:60.1,longitude:15.2})
+WITH n
+CALL spatial.addNode('geom',n) YIELD node
+RETURN node;
+
+CALL spatial.bbox('geom',{lon:15.0,lat:60.0}, {lon:15.3, lat:61.0});
+
+
+
+

A good example blog post for how to use them with one of the neo4j drivers, is Spatial Procedures in Legis Graph by William Lyon (Part 1).

+
+ +
+

A general introduction to Neo4j Spatial and a simple example on how to use the Spatial Java APIs can be found in this blog post by Craig Taverner.

+
+
+
+
+
+

Index and Querying

+
+
+

The current index is an RTree index, but it has been developed in an extensible way allowing for other indices to be added if necessary.

+
+
+

Loading and Indexing

+
+

It is possible to load data into the database and add it to the index during the load. +It is also possible to add existing spatial data to the index later. These are two very different scenarios, and in fact can lead to different graph structures, so we will explain them each in turn.

+
+
+

To load data directly into the index, the simplest approach is to start by creating a Layer that suites your data. +There are many possible choices built into Neo4j-Spatial, but two common ones +would be:

+
+
+
    +
  • +

    SimplePointLayer - an editable layer that allows you to add only Points to the database. +This is a good choice if you only have point data and are interested primarily in proximity searches. +This layer includes utility methods specifically for that case.

    +
  • +
  • +

    EditableLayer(Impl) - this is the default editable layer implementation and can handle any type of simple geometry. +This includes Point, LineString and Polygon, as well as Multi-Point, Multi-LineString and Multi-Polygon. +Since it is a generic implementation and cannot know about the topology of your data model, it stores each geometry separately in a single property of a single node. +The storage format is WKB, or 'Well Known Binary', which is a binary format specific to geographic geometries, and also used by the popular open source spatial database PostGIS.

    +
  • +
+
+
+
+
+
+

Layers and GeometryEncoders

+
+
+

The primary type that defines a collection of geometries is the Layer. +A layer contains an index for querying. +In addition, a Layer can be an EditableLayer if it is possible to add and modify geometries in the layer. +The next most important interface is the GeometryEncoder.

+
+
+

The DefaultLayer is the standard layer, making use of the WKBGeometryEncoder for storing all geometry types as byte[] properties of one node per geometry instance.

+
+
+

The OSMLayer is a special layer supporting Open Street Map and storing the OSM model as a single fully connected graph. +The set of Geometries provided by this layer includes Points, LineStrings and Polygons, and as such cannot be exported to Shapefile format, since that format only allows a single Geometry per layer. +However, OMSLayer extends DynamicLayer, which allow it to provide any number of sub-layers, each with a specific geometry type and in addition based on an OSM tag filter. +For example, you can have a layer providing all cycle paths as LineStrings, or a layer providing all lakes as Polygons. +Underneath these are all still backed by the same fully connected graph, but exposed dynamically as apparently separate geometry layers.

+
+
+
+
+

Neo4j Spatial Procedures

+
+
+

Neo4j Spatial is also packaged as a ZIP file that can be unzipped into the Neo4j Server $NEO4J_HOME/plugins directory. +After restarting the server, you should be able to use the following procedure calls from the Cypher query language.

+
+ + +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Table 1. Available Spatial Procedures
namedescriptionsignature

"spatial.addLayer"

"Adds a new layer with the given type (see spatial.getAllLayerTypes) and configuration, returns the layer root node"

"spatial.addLayer(name :: STRING?, type :: STRING?, encoderConfig :: STRING?) :: (node :: NODE?)"

"spatial.addLayerWithEncoder"

"Adds a new layer with the given encoder class and configuration, returns the layer root node"

"spatial.addLayerWithEncoder(name :: STRING?, encoder :: STRING?, encoderConfig :: STRING?) :: (node :: NODE?)"

"spatial.addNode"

"Adds the given node to the layer, returns the geometry-node"

"spatial.addNode(layerName :: STRING?, node :: NODE?) :: (node :: NODE?)"

"spatial.addNodes"

"Adds the given nodes list to the layer, returns the count"

"spatial.addNodes(layerName :: STRING?, nodes :: LIST? OF NODE?) :: (count :: INTEGER?)"

"spatial.addPointLayer"

"Adds a new simple point layer, returns the layer root node"

"spatial.addPointLayer(name :: STRING?) :: (node :: NODE?)"

"spatial.addPointLayerWithConfig"

"Adds a new simple point layer with the given configuration, returns the layer root node"

"spatial.addPointLayerWithConfig(name :: STRING?, encoderConfig :: STRING?) :: (node :: NODE?)"

"spatial.addPointLayerXY"

"Adds a new simple point layer with the given properties for x and y coordinates, returns the layer root node"

"spatial.addPointLayerXY(name :: STRING?, xProperty :: STRING?, yProperty :: STRING?) :: (node :: NODE?)"

"spatial.addWKT"

"Adds the given WKT string to the layer, returns the created geometry node"

"spatial.addWKT(layerName :: STRING?, geometry :: STRING?) :: (node :: NODE?)"

"spatial.addWKTLayer"

"Adds a new WKT layer with the given node property to hold the WKT string, returns the layer root node"

"spatial.addWKTLayer(name :: STRING?, nodePropertyName :: STRING?) :: (node :: NODE?)"

"spatial.addWKTs"

"Adds the given WKT string list to the layer, returns the created geometry nodes"

"spatial.addWKTs(layerName :: STRING?, geometry :: LIST? OF STRING?) :: (node :: NODE?)"

"spatial.asExternalGeometry"

"Returns a geometry object as an external geometry type to be returned to a client"

"spatial.asExternalGeometry(geometry :: ANY?) :: (geometry :: ANY?)"

"spatial.asGeometry"

"Returns a geometry object as an internal cypher geometry type, to be passed to other procedures but not returned to a client"

"spatial.asGeometry(geometry :: ANY?) :: (geometry :: ANY?)"

"spatial.bbox"

"Finds all geometry nodes in the given layer within the lower left and upper right coordinates of a box"

"spatial.bbox(layerName :: STRING?, min :: ANY?, max :: ANY?) :: (node :: NODE?)"

"spatial.closest"

"Finds all geometry nodes in the layer within the distance to the given coordinate"

"spatial.closest(layerName :: STRING?, coordinate :: ANY?, distanceInKm :: FLOAT?) :: (node :: NODE?)"

"spatial.decodeGeometry"

"Returns a geometry of a layer node as internal cypher geometry type, to be passed to other procedures but not returned to a client"

"spatial.decodeGeometry(layerName :: STRING?, node :: NODE?) :: (geometry :: ANY?)"

"spatial.getFeatureAttributes"

"Returns feature attributes of the given layer"

"spatial.getFeatureAttributes(name :: STRING?) :: (name :: STRING?)"

"spatial.importOSM"

"Imports the provided osm-file from URI to a layer of the same name, returns the count of data added"

"spatial.importOSM(uri :: STRING?) :: (count :: INTEGER?)"

"spatial.importOSMToLayer"

"Imports the provided osm-file from URI to a layer, returns the count of data added"

"spatial.importOSMToLayer(layerName :: STRING?, uri :: STRING?) :: (count :: INTEGER?)"

"spatial.importShapefile"

"Imports the provided shape-file from URI to a layer of the same name, returns the count of data added"

"spatial.importShapefile(uri :: STRING?) :: (count :: INTEGER?)"

"spatial.importShapefileToLayer"

"Imports the provided shape-file from URI to the given layer, returns the count of data added"

"spatial.importShapefileToLayer(layerName :: STRING?, uri :: STRING?) :: (count :: INTEGER?)"

"spatial.intersects"

"Returns all geometry nodes that intersect the given geometry (shape, polygon) in the layer"

"spatial.intersects(layerName :: STRING?, geometry :: ANY?) :: (node :: NODE?)"

"spatial.layer"

"Returns the layer root node for the given layer name"

"spatial.layer(name :: STRING?) :: (node :: NODE?)"

"spatial.layerTypes"

"Returns the different registered layer types"

"spatial.layerTypes() :: (name :: STRING?, signature :: STRING?)"

"spatial.layers"

"Returns name, and details for all layers"

"spatial.layers() :: (name :: STRING?, signature :: STRING?)"

"spatial.procedures"

"Lists all spatial procedures with name and signature"

"spatial.procedures() :: (name :: STRING?, signature :: STRING?)"

"spatial.removeLayer"

"Removes the given layer"

"spatial.removeLayer(name :: STRING?) :: VOID"

"spatial.setFeatureAttributes"

"Sets the feature attributes of the given layer"

"spatial.setFeatureAttributes(name :: STRING?, attributeNames :: LIST? OF STRING?) :: (node :: NODE?)"

"spatial.updateFromWKT"

"Internal procedure, updates the geometry node with the given id with a new WKT string"

"spatial.updateFromWKT(layerName :: STRING?, geometry :: STRING?, geometryNodeId :: INTEGER?) :: (node :: NODE?)"

"spatial.withinDistance"

"Returns all geometry nodes and their ordered distance in the layer within the distance to the given coordinate"

"spatial.withinDistance(layerName :: STRING?, coordinate :: ANY?, distanceInKm :: FLOAT?) :: (node :: NODE?, distance :: FLOAT?)"

+
+
+
+

Querying

+
+
+

JTS Queries

+
+

Neo4j-Spatial contains the 'Java Topology Suite', a library of geometries and geometry operations. In fact, whenever we use the term 'Geometry' we are refering to the JTS class Geometry. Likewise, the subclasses of Geometry: Point, LineString, Polygon and others are all from JTS. +This means that you can use all the capabilities of JTS to operate on Geometry instances you obtain from the database. If, for example, you perform a search for geometries in a certain area, you will be able to +iterate over the results and for each geometry returned, call JTS methods on that class. For example, you could call geometry.

+
+
+

But +The spatial queries implemented are:

+
+
+
    +
  • +

    Contain

    +
  • +
  • +

    Cover

    +
  • +
  • +

    Covered By

    +
  • +
  • +

    Cross

    +
  • +
  • +

    Disjoint

    +
  • +
  • +

    Intersect

    +
  • +
  • +

    Intersect Window

    +
  • +
  • +

    Overlap

    +
  • +
  • +

    Touch

    +
  • +
  • +

    Within

    +
  • +
  • +

    Within Distance

    +
  • +
+
+
+
+
+
+

Examples

+
+
+

Importing a shapefile

+
+

Neo4j-Spatial includes a utility for importing ESRI Shapefile data. The ShapefileImporter will create a new Layer for each Shapefile imported, and will store each Geometry as WKB in a single property of a single node. All attributes of the Feature will be stored as additional properties of that node. For more information on how this is implemented, you can refer to the classes WKBGeometryEncoder. However, you do not need to know any of that to use this feature. Consider the simple code below.

+
+
+
+
ShapefileImporter importer = new ShapefileImporter(database);
+importer.importFile("shp/highway.shp", "highway", StandardCharsets.UTF_8);
+
+
+
+

This code will import the layer 'highway' from the file 'shp/highway.shp' which is included in the source distribution. This layer will be indexed using an RTree and can therefore be queried using any of the supported spatial operations. See the example on querying layers below.

+
+
+
+

Importing an Open Street Map file

+
+

This is more complex because the current OSMImporter class runs in two phases, the first requiring a batch-inserter on the database. +The is ongoing work to allow for a non-batch-inserter on the entire process, and possibly when you have read this that will already be available. +Refer to the unit tests in classes TestDynamicLayers and TestOSMImport for the latest code for importing OSM data. For example:

+
+
+
+
OSMImporter importer = new OSMImporter("map.osm");
+importer.setCharset(StandardCharsets.UTF_8);
+importer.importFile(db, "map.osm");
+importer.reIndex(db);
+
+
+
+

This code will import the map.osm Open Street Map file, populating the database with a little over 200 geometries, including streets, buildings and points of interest.

+
+
+
+

Executing a spatial query

+
+

Assuming you imported the map.osm file as in the previous example, you can now perform spatial searches on the data. The following example will search within a rectangle for a whatever geometries it can find:

+
+
+
+
SpatialDatabaseService spatial = new SpatialDatabaseService(
+                new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
+try (Transaction tx = database.beginTx()) {
+        Layer layer = spatial.getLayer(tx, "map.osm");
+        LayerIndexReader spatialIndex = layer.getIndex();
+        System.out.println("Have " + spatialIndex.count(tx) + " geometries in " + spatialIndex.getBoundingBox(tx));
+
+        Envelope bbox = new Envelope(12.94, 12.96, 56.04, 56.06);
+        List<SpatialDatabaseRecord> results = GeoPipeline
+                        .startIntersectWindowSearch(tx, layer, bbox)
+                        .toSpatialDatabaseRecordList();
+
+        doGeometryTestsOnResults(bbox, results);
+        tx.commit();
+}
+
+
+
+

For more examples of query code, refer to the test code in the LayerTest and the SpatialTest classes. +Also review the classes in the org.neo4j.gis.spatial.query package for the full range or search queries currently implemented.

+
+
+
+

Export a shapefile

+
+

The ESRI Shapefile that we imported in the first example above was actually created by Neo4j-Spatial. It is possible to use the results of a query, or a DynamicLayer, to create a new Shapefile using the ShapefileExporter. If we were to export the complete layer created from importing a shapefile, we would not achieve much, but we could use this capability to create shapefiles that are subsets of other layers, or that are created from data in another format.

+
+
+
+
SpatialDatabaseService spatial = new SpatialDatabaseService(
+                new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
+String wayLayerName;
+try (Transaction tx = database.beginTx()) {
+        OSMLayer layer = (OSMLayer) spatial.getLayer(tx, "map.osm");
+        DynamicLayerConfig wayLayer = layer.addSimpleDynamicLayer(tx, Constants.GTYPE_LINESTRING);
+        wayLayerName = wayLayer.getName();
+        tx.commit();
+}
+ShapefileExporter shpExporter = new ShapefileExporter(database);
+shpExporter.exportLayer(wayLayerName);
+
+
+
+

This example shows how we can import an OSM dataset, which contains data with a number of different Geometry types, and then by using a DynamicLayer to select for only geometries of type 'LineString', we can export all the OSM ways to a Shapefile. This is particularly important when you consider that ESRI Shapefile format does not allow more than one Geometry type per shapefile.

+
+
+
+
SpatialDatabaseService spatial = new SpatialDatabaseService(
+                new IndexManager((GraphDatabaseAPI) graphDb, SecurityContext.AUTH_DISABLED));
+Envelope bbox = new Envelope(12.94, 12.96, 56.04, 56.06);
+List<SpatialDatabaseRecord> results;
+try (Transaction tx = database.beginTx()) {
+        Layer layer = spatial.getLayer(tx, "map.osm");
+        LayerIndexReader spatialIndex = layer.getIndex();
+        System.out.println("Have " + spatialIndex.count(tx) + " geometries in " + spatialIndex.getBoundingBox(tx));
+
+        results = GeoPipeline
+                        .startIntersectWindowSearch(tx, layer, bbox)
+                        .toSpatialDatabaseRecordList();
+
+        spatial.createResultsLayer(tx, "results", results);
+        tx.commit();
+
+}
+ShapefileExporter shpExporter = new ShapefileExporter(database);
+shpExporter.exportLayer("results");
+
+
+
+

This time we import the same OSM model, but query for all geometries inside an envelope, and export that to a new Shapefile.

+
+
+
+

GeoPipes

+
+

Base Layers

+
+

These are the different base layers being used for the following examples:

+
+
+

OsmLayer:

+
+
+
+osmLayer +
+
+
+

IntersectionLayer:

+
+
+
+intersectionLayer +
+
+
+

LinesLayer:

+
+
+
+linesLayer +
+
+
+

BoxesLayer:

+
+
+
+boxesLayer +
+
+
+

ConcaveLayer:

+
+
+
+concaveLayer +
+
+
+

EqualLayer:

+
+
+
+equalLayer +
+
+
+
+
+

GeoPipes Examples

+
+

Below follows a non-exhaustive lists of interesting GeoPipes that can be chained together to contruct a geoprocessing system in while trying to keep the processing as lazy as possible using iterators through the different steps.

+
+
+

Search within geometry

+
+

This pipe performs a search within a geometry in this example, both OSM street geometries should be found in when searching with an enclosing rectangle Envelope.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline
+                .startWithinSearch(tx, osmLayer,
+                                osmLayer.getGeometryFactory().toGeometry(new Envelope(10, 20, 50, 60)));
+
+
+
+
+

Intersecting windows

+
+

The FilterIntersectWindow pipe finds geometries that intersects a given rectangle. This pipeline:

+
+
+
+
GeoPipeline pipeline = GeoPipeline
+                .start(tx, boxesLayer)
+                .windowIntersectionFilter(new Envelope(0, 10, 0, 10));
+
+
+
+

will output:

+
+
+
+intersecting windows +
+
+
+
+

Intersect All

+
+

The IntersectAll pipe intersects geometries of every item contained in the pipeline. +It groups every item in the pipeline in a single item containing the geometry output of the intersection.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, intersectionLayer).intersectAll();
+
+
+
+

Output:

+
+
+
+intersect all +
+
+
+
+

Unite All

+
+

The Union All pipe unites geometries of every item contained in the pipeline. This pipe groups every item in the pipeline in a single item containing the geometry output of the union.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, intersectionLayer).unionAll();
+
+
+
+

Output:

+
+
+
+unite all +
+
+
+
+

Extract Points

+
+

The Extract Points pipe extracts every point from a geometry.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, boxesLayer).extractPoints();
+
+
+
+
+extract points +
+
+
+
+

Filter by cql using complex cql

+
+

This filter will apply the provided CQL expression to the different geometries and only let the matching ones pass.

+
+
+
Example:
+
+
long counter = GeoPipeline.start(tx, osmLayer)
+                .cqlFilter(tx, "highway is not null and geometryType(the_geom) = 'LineString'").count();
+
+
+
+
+

Boundary

+
+

The boundary pipe calculates boundary of every geometry in the pipeline.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, boxesLayer).toBoundary();
+
+
+
+

Output:

+
+
+
+boundary +
+
+
+
+

Centroid

+
+

The Centroid pipe calculates geometry centroid.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, boxesLayer).toCentroid();
+
+
+
+

Output:

+
+
+
+centroid +
+
+
+
+

Convex Hull

+
+

The ConvexHull pipe calculates geometry convex hull.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, concaveLayer).toConvexHull();
+
+
+
+

Output:

+
+
+
+convex hull +
+
+
+
+

Densify

+
+

The Densify pipe inserts extra vertices along the line segments in the geometry. The densified geometry contains no line segment which is longer than the given distance tolerance.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, concaveLayer).densify(5).extractPoints();
+
+
+
+

Output:

+
+
+
+densify +
+
+
+
+

Envelope

+
+

The Envelope pipe computes the minimum bounding box of item geometry.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline
+                .start(tx, linesLayer)
+                .toEnvelope();
+
+
+
+

Output:

+
+
+
+envelope +
+
+
+
+

Export to GML

+
+

This pipe exports every geometry as a GML snippet.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, boxesLayer).createGML();
+for (GeoPipeFlow flow : pipeline) {
+        System.out.println(flow.getProperties().get("GML"));
+}
+
+
+
+

Output:

+
+
+
+
<gml:Polygon>
+        <gml:outerBoundaryIs>
+                <gml:LinearRing>
+                        <gml:coordinates>
+                                12.0,26.0 12.0,27.0 13.0,27.0 13.0,26.0 12.0,26.0
+                        </gml:coordinates>
+                </gml:LinearRing>
+        </gml:outerBoundaryIs>
+</gml:Polygon>
+<gml:Polygon>
+<gml:outerBoundaryIs>
+        <gml:LinearRing>
+                <gml:coordinates>
+                        2.0,3.0 2.0,5.0 6.0,5.0 6.0,3.0 2.0,3.0
+                </gml:coordinates>
+        </gml:LinearRing>
+</gml:outerBoundaryIs>
+</gml:Polygon>
+
+
+
+
+

Intersection

+
+

The Intersection pipe computes a geometry representing the intersection between item geometry and the given geometry.

+
+
+
Example:
+
+
WKTReader reader = new WKTReader(intersectionLayer.getGeometryFactory());
+Geometry geometry = reader.read("POLYGON ((3 3, 3 5, 7 7, 7 3, 3 3))");
+GeoPipeline pipeline = GeoPipeline.start(tx, intersectionLayer).intersect(geometry);
+
+
+
+

Output:

+
+
+
+intersection +
+
+
+
+

Union

+
+

The Union pipe unites item geometry with a given geometry.

+
+
+
Example:
+
+
WKTReader reader = new WKTReader(intersectionLayer.getGeometryFactory());
+Geometry geometry = reader.read("POLYGON ((3 3, 3 5, 7 7, 7 3, 3 3))");
+SearchFilter filter = new SearchIntersectWindow(intersectionLayer, new Envelope(7, 10, 7, 10));
+GeoPipeline pipeline = GeoPipeline.start(tx, intersectionLayer, filter).union(geometry);
+
+
+
+

Output:

+
+
+
+union +
+
+
+
+

Start Point

+
+

The StartPoint pipe finds the starting point of item geometry.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline
+                .start(tx, linesLayer)
+                .toStartPoint();
+
+
+
+

Output:

+
+
+
+start point +
+
+
+
+

End Point

+
+

The EndPoint pipe finds the ending point of item geometry.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline
+                .start(tx, linesLayer)
+                .toEndPoint();
+
+
+
+

Output:

+
+
+
+end point +
+
+
+
+

Max

+
+

The Max pipe computes the maximum value of the specified property and discard items with a value less than the maximum.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, boxesLayer)
+                .calculateArea()
+                .getMax("Area");
+
+
+
+

Output:

+
+
+
+max +
+
+
+
+

Min

+
+

The Min pipe computes the minimum value of the specified property and discard items with a value greater than the minimum.

+
+
+
Example:
+
+
GeoPipeline pipeline = GeoPipeline.start(tx, boxesLayer)
+                .calculateArea()
+                .getMin("Area");
+
+
+
+

Output:

+
+
+
+min +
+
+
+
+

Break up all geometries into points and make density islands

+
+

A more complex Open Street Map example.

+
+
+
Example:
+
+
//step1
+GeoPipeline pipeline = OSMGeoPipeline.startOsm(tx, osmLayer)
+                //step2
+                .extractOsmPoints()
+                //step3
+                .groupByDensityIslands(0.0005)
+                //step4
+                .toConvexHull()
+                //step5
+                .toBuffer(0.0004);
+
+
+
+

Output:

+
+
+
+step1 break up all geometries into points and make density islands +
+
Figure 1. Step1
+
+
+
+step2 break up all geometries into points and make density islands +
+
Figure 2. Step2
+
+
+
+step3 break up all geometries into points and make density islands +
+
Figure 3. Step3
+
+
+
+step4 break up all geometries into points and make density islands +
+
Figure 4. Step4
+
+
+
+step5 break up all geometries into points and make density islands +
+
Figure 5. Step5
+
+
+
+
+
+
+

Appendix: Integrations

+
+ +
+
+
+

Neo4j Spatial in uDig

+
+
+

Neo4j Spatial supports Geotools, and therefor also Geotools based platforms like GeoServer and uDig. For information on setting up and running Neo4j Spatial in GeoServer, see the wiki page [geoserver].

+
+
+

uDig SDK

+
+

Here we will discuss how to set up a development environment for developing code for uDig making use of Neo4j Spatial. For example, if you want to build a uDig plugin that makes use of Neo4j for the data model, or if you have a custom extension to Neo4j Spatial, and want to visualize the data in uDig.

+
+
+
+

Setting up uDig SDK

+
+

Follow the uDig SDK Quickstart instructions on the uDig wiki at http://udig.refractions.net/confluence/display/DEV/1+SDK+Quickstart. It is best to follow those instructions in detail, since all steps are explained and alternatives given. However, for the impatient and foolish, we include simplified instructions here:

+
+
+ +
+
+
+

Adding Neo4j Spatial

+
+

Versions and Dependencies:

+
+
+
    +
  • +

    Note that the trunk version of Neo4j Spatial requires uDig 1.2.1, which was not yet released at the time of writing. However, there was a SNAPSHOT build available at http://udig.refractions.net/files/downloads/branches, dated 10th September. If you cannot find a SNAPSHOT SDK at that date, or more recent, then look in the normal location, or consider building the SDK yourself according to the instructions athttp://udig.refractions.net/confluence/display/ADMIN/02+Development+Environment

    +
  • +
  • +

    If you wish to use uDig 1.2.0 official release, then you need to use an older version of Neo4j Spatial. Specifically any version earlier than the upgrade to GeoTools 2.7 which occurred on the 1st October. +Adding Neo4j Spatial to uDig:

    +
  • +
  • +

    Install Maven2 support for Eclipse:

    +
  • +
  • +

    Go to 'Help→Install New Software' and add a new update site for M2Eclipse using URL http://m2eclipse.sonatype.org/sites/m2e

    +
  • +
  • +

    Select and install the maven integration for eclipse

    +
  • +
  • +

    Get the source code for Neo4j Spatial using: git clone git://github.com/neo4j/neo4j-spatial.git

    +
  • +
  • +

    If you need to use the GeoTools 2.6 version of Neo4j Spatial:

    +
  • +
  • +

    Switch to the end of September version using the command: git checkout 04a0ae

    +
  • +
  • +

    If you plan to develop Neo4j Spatial code from this point, consider making a branch first by going to the newly created directory, typing 'git branch yourbranchname' followed by 'git checkout yourbranchname'

    +
  • +
  • +

    Compile and test the code using: mvn test

    +
  • +
  • +

    This should download all dependencies and run some test cases to make sure everything is OK

    +
  • +
  • +

    Get the source code for eu.udig.catalog.neo4j using: git clone git://gitorious.org/udig-community/neo4j.git eu.udig.catalog.neo4j

    +
  • +
  • +

    you can branch this as well

    +
  • +
  • +

    Start eclipse and import the two projects into the workspace

    +
  • +
  • +

    Use 'File→Import→General→Existing Projects into Workspace'

    +
  • +
  • +

    Browse to the location of your git projects and select and import the neo4j-spatial and eu.udig.catalog.neo4j projects

    +
  • +
  • +

    Create a libs project that wraps some dependent jars

    +
  • +
  • +

    Use 'New Project→Plug-in Development→Plug-in from existing JAR archives'

    +
  • +
  • +

    The files you need to add would have been installed by maven during the 'mvn test' of neo4j-spatial above (on Windows possibly in your User\Username\.m2\repository folder) but can also be downloaded separately

    +
  • +
  • +

    Select the JAR files to add to the libs project:

    +
  • +
  • +

    neo4j-kernel-1.2-1.2.jar

    +
  • +
  • +

    neo4j-index-1.2-1.2.jar

    +
  • +
  • +

    neo4j-graph-algo-0.7-1.2.jar

    +
  • +
  • +

    geronimo-jta_1.1_spec-1.1.1.jar

    +
  • +
  • +

    org.apache.servicemix.bundles.lucene-3.0.1_2.jar

    +
  • +
  • +

    json-simple-1.1.jar

    +
  • +
  • +

    Choose a name like 'org.neo4j.spatial.libs' and create the project

    +
  • +
  • +

    Open the new libs projects MANIFEST.MF file and exit the exports to include all packages in the jars

    +
  • +
+
+
+
+

Testing sample layers in uDig

+
+

When Neo4j Spatial was first installed above, you used 'mvn test' to run some test cases. This will have created a neo4j database with one or more layers, which you can use to display in uDig. However, we will take more control over this by specifically re-running one of the test cases, so we know exactly which layers will be visible.

+
+
+
    +
  • +

    Navigate to the test code for TestDynamicLayers, right click and choose 'Run as → JUnit Test'

    +
  • +
  • +

    This test case will load an OSM dataset for the city of Malmö in Sweden. It will then assign a number of 'dynamic layers', which are pre-configured filters of the dataset, designed to look like separate layers in the GIS, but in reality being views of the same connected graph in the database.

    +
  • +
  • +

    The test will also export these layers as both Shapefile and PNG images for testing purposes in neo4j-spatial\target\export. We will not need them for uDig, since we will be streaming the data directly into the view in uDig. However, you can compare these in uDig later to see the differences.

    +
  • +
  • +

    Open the run configurations dialog and look at the run configuration for uDig that was created earlier when you first ran uDig.

    +
  • +
  • +

    Check the plugins tab and make sure that the two new plugins in your workspace are selected:

    +
  • +
  • +

    org.neo4j.spatial

    +
  • +
  • +

    org.neo4j.spatial.libs

    +
  • +
  • +

    eu.udig.catalog.neo4j

    +
  • +
  • +

    Click 'Validate plugins' and optionally 'Add required plugins' to ensure that all dependencies are met (if needed edit the manifest.mf files of both eu.udig.catalog.neo4j and neo4j-spatial to mark the management dependencies optional: org.neo4j.kernel.impl.management;resolution:=optional, and org.neo4j.kernel.management;resolution:=optional)

    +
  • +
  • +

    Some errors in the projects may be due to invalid characters, such as in 'crossing_bygg_förstadsgatan'. Simply replace it with another name.

    +
  • +
  • +

    Optionally check 'clear workspace' on the main tab

    +
  • +
  • +

    Click the 'Run' button to run uDig with Neo4j support enabled

    +
  • +
  • +

    Once it has started, the eu.udig.catalog.neo4j plugin will scan your home directory for neo4j databases. In the catalog view you should see the one created by the unit test code. If you do not find one, you can import one:

    +
  • +
  • +

    Right click in the catalog view and choose 'Import→Data→Files'

    +
  • +
  • +

    Select the neostore-id file in the database directory to add this database to the catalog

    +
  • +
  • +

    Select one or more layers in the catalog and add them to the current map.

    +
  • +
  • +

    If you select the map2.osm layer, you will get a single layer containing all geometry types recognized as Geometries in the database

    +
  • +
+
+
+
+Udig map2 +
+
+
+
    +
  • +

    If you select one of the other layers, you will get a filtered view of only that data

    +
  • +
  • +

    Adding multiple layers to the map allows you to reorder the layers and modify their styling separately.

    +
  • +
  • +

    Optionally change the projection to look better for northern latitudes:

    +
  • +
  • +

    Click on the projection button below the map, which initially says 'WGS84'

    +
  • +
  • +

    Type 'RT90' into the filter, and then select 'RT90 2.5 gon V (3021)'. This is a common Swedish projection and so will work well for Malmö.

    +
  • +
+
+
+
+Udig map2 rt90 +
+
+
+
    +
  • +

    Try set the style for the map2.osm layer using the sample style file in the Neo4j Spatial source code: neo.sld.xml

    +
  • +
  • +

    Turn off all layers but map2.osm in the layers view, to keep the view simple

    +
  • +
  • +

    Right-click on the map2.osm layer in the layers view and choose 'Style'

    +
  • +
  • +

    Go to the 'XML' option, and mark all XML and delete it

    +
  • +
  • +

    Open neo.sld.xml in a text editor, select all the text and copy and paste it into the Style editor

    +
  • +
  • +

    Click apply and see the map redraw with more complex styling

    +
  • +
+
+
+
+Udig map2 styled +
+
+
+
+

Update: New automatic styles for OSM

+
+

As of the 20th October (commit 506ee4), OSM layers now publish default styles to the GIS. The initial styles are quite simple, but this does allow for nicer out the box experience. Currently, the code will only work in a development environment with certain directory structure, but we’ll make it more generic soon.

+
+
+
+Udig multi styled layers +
+
+
+
+
+
+

Neo4j Spatial Geoserver Plugin

+
+
+ + + + + +
+
Important
+
+Tested with: GeoServer 2.1.1 +
+
+
+

Building

+
+
+
mvn clean install
+
+
+
+
+

Deployment into Geoserver

+
+
    +
  • +

    unzip the target/xxxx-server-plugin.zip into $GEOSERVER_HOME/webapps/geoserver/WEB-INF/lib

    +
  • +
  • +

    restart geoserver

    +
  • +
  • +

    configure a new workspace

    +
  • +
  • +

    configure a new datasource neo4j in your workspace. Point the "The directory path of the Neo4j database:" parameter to the relative (form the GeoServer working dir) or aboslute path to a Neo4j Spatial database with layers (see Neo4j Spatial)

    +
  • +
  • +

    in Layers, do "Add new resource" and choose your Neo4j datastore to see the exisitng Neo4j Spatial layers and add them.

    +
  • +
+
+
+
+

Testing in GeoServer trunk

+
+
    +
  • +

    check out the geoserver source

    +
  • +
+
+
+
+
svn co https://svn.codehaus.org/geoserver/trunk geoserver-trunk
+
+
+
+
    +
  • +

    build the source

    +
  • +
+
+
+
+
cd geoserver-trunk
+mvn clean install
+
+
+
+ +
+
+
+
cd src/web/app
+mvn jetty:run
+
+
+
+ +
+
+
+
    <profile>
+      <id>neo4j</id>
+      <dependencies>
+        <dependency>
+          <groupId>org.neo4j</groupId>
+          <artifactId>neo4j-spatial</artifactId>
+          <version>0.7-SNAPSHOT</version>
+        </dependency>
+      </dependencies>
+    </profile>
+
+
+
+
    +
  • +

    start the GeoServer webapp again with the added neo4j profile

    +
  • +
+
+
+
+
cd $GEOSERVER_SRC/src/web/app
+mvn jetty:run -Pneo4j
+
+
+
+ +
+
+

For more info head over to Neo4j Wiki on Geoserver

+
+
+
+
+
+

Neo4j Spatial in GeoServer

+
+
+

Neo4j Spatial includes built-in support for a GeoTools data store. This means it is, in principle, directly usable inside GeoServer. This document will discuss how to get it up and running in GeoServer. If you are interested in a desktop GIS, take a look at Neo4j Spatial in uDig.

+
+
+

Installing Neo4j-Spatial in GeoServer

+
+

Installing GeoServer

+
+

First of all you need to download and install geoserver from http://geoserver.org/display/GEOS/Download. For detailed installation instructions, refer to the official GeoServer documentation athttp://docs.geoserver.org/stable/en/user/installation/index.html +Here we provide very basic instructions, with factors to consider regarding installing GeoServer with Neo4j Spatial:

+
+
+
    +
  • +

    Choose the right version for the version of Neo4j Spatial you are going to use:

    +
  • +
  • +

    Neo4j-Spatial trunk is currently coded to work with GeoTools 2.7, so this means we need to load it into GeoServer 2.1 or later. The version we tested against was geoserver-2.1-beta1

    +
  • +
  • +

    If you want to deploy on the stable release of GeoServer 2.0.2, then you can use an older Neo4j-Spatial, or modify the code to work with GeoTools 2.6 (the differences are small). The last version of Neo4j Spatial that works with the GeoServer 2.0 series, is from the end of September 2010.

    +
  • +
  • +

    If you already have a servlet container running, you can download the WAR version, otherwise download the BIN version (or the Windows installer if on Windows). Install and run using the included instructions in geoserver.

    +
  • +
  • +

    Once installed, open a web browser on http://localhost:8080/geoserver/web/ (or other location is you configured it differently) to test that geoserver is runnning correctly.

    +
  • +
+
+
+
+

Adding Neo4j Spatial

+
+

Install Neo4j-Spatial by copying the following JAR files into the in the GeoServer directory webapps/geoserver/WEB-INF/lib:

+
+
+
    +
  • +

    json-simple-1.1.jar

    +
  • +
  • +

    geronimo-jta_1.1_spec-1.1.1.jar

    +
  • +
  • +

    neo4j-kernel-1.2-1.2.M03.jar

    +
  • +
  • +

    neo4j-index-1.2-1.2.M03.jar

    +
  • +
  • +

    neo4j-spatial.jar

    +
  • +
+
+
+

Getting this last jar can be achieved by using maven to compile from source, or by simply browsing to the maven repository website at http://m2.neo4j.org/org/neo4j/neo4j-spatial/

+
+
+

At the time of writing the latest JAR was:

+
+
+ +
+
+
+
+

Visualizing Neo4j Spatial data in GeoServer

+
+

These instructions lead you through getting started with adding some test layers from Neo4j Spatial to the running GeoServer

+
+
+

Login as 'admin' with password 'geoserver', unless you have changed those in the security/users.properties file during installation

+
+
+
+Geoserver 1 logged in +
+
+
+
+

Add a data source

+
+

Go to 'Stores' and click 'Add a new Store'

+
+
+
+Geoserver 2 add new store +
+
+
+

Name the data source and add the path to the Neo4j Database you have your layers in

+
+
+
+Geoserver 3 path to neo4j db +
+
+
+
    +
  • +

    One option for the database location is a database created using the unit tests in Neo4j Spatial. The rest of this wiki assumes that you ran the TestDynamicLayers unit test which loads an OSM dataset for the city of Malmö in Sweden, and then creates a number of Dynamic Layers (or views) on this data, which we can publish in GeoServer.

    +
  • +
  • +

    If you do use the unit test for the sample database, then the location of the database will be in the target/var/neo4j-db directory of the Neo4j Source code.

    +
  • +
  • +

    Type the URL as the full path to the neostore-id file in the database directory

    +
  • +
+
+
+
+

Publish from data source

+
+

Now we will publish a layer from the data source

+
+
+

Click 'Neo4j' under 'Vector Data Sources' and then click 'Add new layer'

+
+
+
+Geoserver 5 choose new layer +
+
+
+

Choose the 'Neo4j Data Store'

+
+
+
+Geoserver 6 choose new layer2 +
+
+
+

Choose a layer and click 'Publish' +GeoServer seems to have trouble detecting the CRS and bounds, so we have to enter this manually

+
+
+
+Geoserver 6 publish layer settings +
+
+
+
    +
  • +

    Type CRS manually into the 'Declared SRS' field. For OSM data (as published by the TestDynamicLayers code) use EPSG:4326

    +
  • +
  • +

    Click 'Compute from data' to get the native bounding box

    +
  • +
  • +

    Copy and paste all these fields into the Lat/Lon bounding box (for some reason, 'Compute from native bounds' is not working)

    +
  • +
  • +

    Save the layer

    +
  • +
  • +

    Repeat this for as many layers as you want to publish

    +
  • +
+
+
+
+Geoserver 7 four layers published +
+
+
+
+

Previewing layers

+
+

Preview one of the published layers

+
+
+

Go to the layer preview page and select the layer and 'OpenLayers' or other option for preview.

+
+
+
+Geoserver 8 single layer preview +
+
+
+

Preview multiple layers

+
+
+
    +
  • +

    Add a layer group, by clicking on 'layer groups'

    +
  • +
+
+
+
+Geoserver 9 layer groups +
+
+
+
    +
  • +

    and then click 'add new layer group'.

    +
  • +
+
+
+
+Geoserver 10 layer group settings +
+
+
+
    +
  • +

    Enter the name, and copy the bounds from one of the layers you plan to add. The 'Generate Bounds' function does not seem to work.

    +
  • +
  • +

    Enter the CRS as the same as the one used in one of the layers you plan to add.

    +
  • +
  • +

    Then click 'Add Layer' and select a layer to add to the group. Repeat this for as many layers as you plan to add.

    +
  • +
  • +

    Save the group and then go to the Layer Preview and you will see the group as an option to preview.

    +
  • +
+
+
+
+Geoserver 11 layer preview list +
+
+
+
    +
  • +

    Select 'OpenLayers' to preview in a dynamic map that can be zoomed in.

    +
  • +
+
+
+
+Geoserver 12 layer group preview +
+
+
+
+

Controlling layer style

+
+

If you want to change the styles of the layers, one option is to go back to the layers list, select a layer, click the display tab and edit the styling information. This will also affect the preview in openlayers.

+
+
+
+Geoserver 13 layers preview with styles +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 29cadd6e..257d5cee 100644 --- a/pom.xml +++ b/pom.xml @@ -2,8 +2,8 @@ - 5.20.0 - 17 + 5.24.2 + 21 org.neo4j.maven.skins default-skin 2 @@ -20,7 +20,7 @@ 4.0.0 neo4j-spatial org.neo4j - 5.20.1-SNAPSHOT + 5.24.2-SNAPSHOT Neo4j - Spatial Components Spatial utilities and components for Neo4j https://components.neo4j.org/${project.artifactId}/${project.version} diff --git a/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java b/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java index 38baaf93..85bceb9a 100644 --- a/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java +++ b/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java @@ -229,20 +229,4 @@ public Style getStyle(String typeName) { } return result; } - - private EditableLayer getEditableLayer(String typeName) throws IOException { - try (Transaction tx = database.beginTx()) { - Layer layer = spatialDatabase.getLayer(tx, typeName); - if (layer == null) { - throw new IOException("Layer not found: " + typeName); - } - - if (!(layer instanceof EditableLayer)) { - throw new IOException("Cannot create a FeatureWriter on a read-only layer: " + layer); - } - tx.commit(); - - return (EditableLayer) layer; - } - } } diff --git a/src/main/java/org/neo4j/gis/spatial/DynamicLayerConfig.java b/src/main/java/org/neo4j/gis/spatial/DynamicLayerConfig.java index 547efa78..f94dbf67 100644 --- a/src/main/java/org/neo4j/gis/spatial/DynamicLayerConfig.java +++ b/src/main/java/org/neo4j/gis/spatial/DynamicLayerConfig.java @@ -20,7 +20,6 @@ package org.neo4j.gis.spatial; import java.io.File; -import java.io.PrintStream; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -169,12 +168,6 @@ public String[] getNames() { public int getNodeCount() { return nodeCount; } - - public void describeUsage(PrintStream out) { - for (String name : names.keySet()) { - out.println(name + "\t" + names.get(name)); - } - } } /** diff --git a/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseRecord.java b/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseRecord.java index 7f3712a6..ae0070b5 100644 --- a/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseRecord.java +++ b/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseRecord.java @@ -71,6 +71,7 @@ public void refreshGeomNode(Transaction tx) { * @deprecated This method is of questionable value, since it is better to * query the geometry object directly, outside the result */ + @Deprecated public int getType() { //TODO: Get the type from the geometryEncoder return SpatialDatabaseService.convertJtsClassToGeometryType(getGeometry().getClass()); diff --git a/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java b/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java index 61433229..09545387 100644 --- a/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java +++ b/src/main/java/org/neo4j/gis/spatial/SpatialDatabaseService.java @@ -50,7 +50,6 @@ import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; -import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.Transaction; /** @@ -283,17 +282,6 @@ public Layer findLayerContainingGeometryNode(Transaction tx, Node geometryNode) return null; } - private Layer getLayerFromChild(Transaction tx, Node child, RelationshipType relType) { - Relationship indexRel = child.getSingleRelationship(relType, Direction.INCOMING); - if (indexRel != null) { - Node layerNode = indexRel.getStartNode(); - if (layerNode.hasProperty(PROP_LAYER)) { - return LayerUtilities.makeLayerFromNode(tx, indexManager, layerNode); - } - } - return null; - } - public boolean containsLayer(Transaction tx, String name) { return getLayer(tx, name) != null; } diff --git a/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java b/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java index 5bf27ac7..923f1f11 100644 --- a/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java +++ b/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java @@ -24,11 +24,10 @@ import org.locationtech.jts.io.WKBReader; import org.locationtech.jts.io.WKBWriter; import org.neo4j.gis.spatial.encoders.AbstractSinglePropertyEncoder; -import org.neo4j.gis.spatial.encoders.Configurable; import org.neo4j.graphdb.Entity; import org.neo4j.graphdb.Transaction; -public class WKBGeometryEncoder extends AbstractSinglePropertyEncoder implements Configurable { +public class WKBGeometryEncoder extends AbstractSinglePropertyEncoder { @Override public Geometry decodeGeometry(Entity container) { diff --git a/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java b/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java index 08df651b..18230130 100644 --- a/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java +++ b/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java @@ -24,11 +24,10 @@ import org.locationtech.jts.io.WKTReader; import org.locationtech.jts.io.WKTWriter; import org.neo4j.gis.spatial.encoders.AbstractSinglePropertyEncoder; -import org.neo4j.gis.spatial.encoders.Configurable; import org.neo4j.graphdb.Entity; import org.neo4j.graphdb.Transaction; -public class WKTGeometryEncoder extends AbstractSinglePropertyEncoder implements Configurable { +public class WKTGeometryEncoder extends AbstractSinglePropertyEncoder { @Override public Geometry decodeGeometry(Entity container) { diff --git a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java index 6020eeab..08a0acf8 100644 --- a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java +++ b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java @@ -45,7 +45,7 @@ public boolean equals(Object obj) { return false; } - protected void save(Transaction tx, Node node) { + protected void save(Node node) { node.setProperty("from", this.from); node.setProperty("to", this.to); node.setProperty("type", this.type); diff --git a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java index d43d4685..01c98e4f 100644 --- a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java +++ b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java @@ -79,7 +79,7 @@ private void save(Transaction tx) { } for (PropertyMapper mapper : toSave) { Node node = tx.createNode(); - mapper.save(tx, node); + mapper.save(node); layer.getLayerNode(tx).createRelationshipTo(node, SpatialRelationshipTypes.PROPERTY_MAPPING); } } @@ -89,14 +89,6 @@ private void addPropertyMapper(Transaction tx, PropertyMapper mapper) { save(tx); } - private PropertyMapper removePropertyMapper(Transaction tx, String to) { - PropertyMapper mapper = getPropertyMappers(tx).remove(to); - if (mapper != null) { - save(tx); - } - return mapper; - } - public PropertyMapper getPropertyMapper(Transaction tx, String to) { return getPropertyMappers(tx).get(to); } diff --git a/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java b/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java index 18dab6ff..08581185 100644 --- a/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java +++ b/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java @@ -38,14 +38,12 @@ */ public class SearchCQL implements SearchFilter { - private final Transaction tx; private final Neo4jFeatureBuilder featureBuilder; private final Layer layer; private final org.geotools.api.filter.Filter filter; private final Envelope filterEnvelope; public SearchCQL(Transaction tx, Layer layer, org.geotools.api.filter.Filter filter) { - this.tx = tx; this.layer = layer; this.featureBuilder = Neo4jFeatureBuilder.fromLayer(tx, layer); this.filter = filter; @@ -53,7 +51,6 @@ public SearchCQL(Transaction tx, Layer layer, org.geotools.api.filter.Filter fil } public SearchCQL(Transaction tx, Layer layer, String cql) { - this.tx = tx; this.layer = layer; this.featureBuilder = Neo4jFeatureBuilder.fromLayer(tx, layer); try { diff --git a/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java b/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java index 50a7ac0a..2a379347 100644 --- a/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java +++ b/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java @@ -248,11 +248,9 @@ private String description() { private class IndexRemover implements Runnable { private final IndexDefinition index; - private Exception e; private IndexRemover(IndexDefinition index) { this.index = index; - this.e = null; } @Override @@ -266,8 +264,7 @@ public void run() { } tx.commit(); } - } catch (Exception e) { - this.e = e; + } catch (Exception ignored) { } } } diff --git a/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java b/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java index 22557f21..2fb7904f 100644 --- a/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java +++ b/src/main/java/org/neo4j/gis/spatial/osm/OSMImporter.java @@ -504,7 +504,7 @@ void logNodesFound(long currentTime) { LogCounter found = nodeFindStats.get(type); double rate = 0.0f; if (found.totalTime > 0) { - rate = (1000.0 * (float) found.count / (float) found.totalTime); + rate = (1000.0 * found.count / found.totalTime); } System.out.println("\t" + type + ": \t" + found.count + "/" + (found.totalTime / 1000) @@ -532,7 +532,7 @@ void logNodeAddition(LinkedHashMap tags, if (currentTime - logTime > 1432) { System.out.println( new Date(currentTime) + ": Saving " + type + " " + count + " \t(" + (1000.0 * (float) count - / (float) (currentTime - firstLogTime)) + " " + type + "/second)"); + / (currentTime - firstLogTime)) + " " + type + "/second)"); logTime = currentTime; } } diff --git a/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java b/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java index 02f7fc2e..f6818b8d 100644 --- a/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java +++ b/src/main/java/org/neo4j/gis/spatial/procedures/SpatialProcedures.java @@ -72,6 +72,7 @@ import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.procs.ProcedureSignature; import org.neo4j.internal.kernel.api.security.SecurityContext; +import org.neo4j.kernel.api.CypherScope; import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.procedure.GlobalProcedures; import org.neo4j.kernel.internal.GraphDatabaseAPI; @@ -160,11 +161,12 @@ public Stream listProcedures() { GlobalProcedures procedures = ((GraphDatabaseAPI) db).getDependencyResolver() .resolveDependency(GlobalProcedures.class); Stream.Builder builder = Stream.builder(); - for (ProcedureSignature proc : procedures.getCurrentView().getAllProcedures()) { - if (proc.name().namespace()[0].equals("spatial")) { - builder.accept(new NameResult(proc.name().toString(), proc.toString())); - } - } + + procedures.getCurrentView().getAllProcedures(CypherScope.CYPHER_5) + .filter(proc -> proc.name().namespace()[0].equals("spatial")) + .map(proc -> new NameResult(proc.name().toString(), proc.toString())) + .forEach(builder::accept); + return builder.build(); } diff --git a/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java b/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java index 11b357d7..a2d6c55f 100644 --- a/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java +++ b/src/main/java/org/neo4j/gis/spatial/rtree/RTreeIndex.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.annotation.Nonnull; import org.json.simple.JSONObject; import org.json.simple.JSONValue; @@ -227,7 +228,7 @@ public void add(Transaction tx, List geomNodes) { } nodesToAdd.addAll(geomNodes); detachGeometryNodes(tx, false, getIndexRoot(tx), new NullListener()); - deleteTreeBelow(tx, getIndexRoot(tx)); + deleteTreeBelow(getIndexRoot(tx)); buildRtreeFromScratch(tx, getIndexRoot(tx), decodeGeometryNodeEnvelopes(nodesToAdd), 0.7); countSaved = false; totalGeometryCount = nodesToAdd.size(); @@ -1058,13 +1059,9 @@ private static Node chooseIndexNodeWithSmallestArea(List indexNodes) { } private static int countChildren(Node indexNode, RelationshipType relationshipType) { - int counter = 0; try (var relationships = indexNode.getRelationships(Direction.OUTGOING, relationshipType)) { - for (Relationship ignored : relationships) { - counter++; - } + return (int) StreamSupport.stream(relationships.spliterator(), false).count(); } - return counter; } /** @@ -1399,7 +1396,7 @@ private static double getArea(Envelope e) { return e.getArea(); } - private static void deleteTreeBelow(Transaction ignored, Node rootNode) { + private static void deleteTreeBelow(Node rootNode) { try (var relationships = rootNode.getRelationships(Direction.OUTGOING, RTreeRelationshipTypes.RTREE_CHILD)) { for (Relationship relationship : relationships) { deleteRecursivelySubtree(relationship.getEndNode(), relationship); diff --git a/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java b/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java index c0019de6..d1d5dea7 100644 --- a/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java +++ b/src/test/java/org/neo4j/gis/spatial/RTreeBulkInsertTest.java @@ -43,9 +43,10 @@ import java.util.Random; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.StreamSupport; + import org.apache.commons.io.FileUtils; import org.geotools.api.feature.simple.SimpleFeatureType; -import org.geotools.api.referencing.FactoryException; import org.geotools.api.referencing.crs.CoordinateReferenceSystem; import org.geotools.data.neo4j.Neo4jFeatureBuilder; import org.geotools.referencing.crs.DefaultEngineeringCRS; @@ -100,7 +101,7 @@ public void before() throws IOException { } @AfterEach - public void after() throws IOException { + public void after() { doCleanShutdown(); } @@ -190,8 +191,6 @@ private EditableLayer getOrCreateSimplePointLayer(String name, String index, Str @Test public void shouldInsertSimpleRTree() { int width = 20; - int blockSize = 10000; - CoordinateReferenceSystem crs = DefaultEngineeringCRS.GENERIC_2D; EditableLayer layer = getOrCreateSimplePointLayer("Coordinates", "rtree", "lon", "lat"); List nodes = new ArrayList<>(); try (Transaction tx = db.beginTx()) { @@ -348,7 +347,6 @@ public void verifyStructure() { private class ZOrderIndexMaker implements IndexMaker { - private final SpatialDatabaseService spatial = spatial(); private final String name; private final String insertMode; private final IndexTestConfig config; @@ -552,23 +550,22 @@ public void shouldInsertManyNodesInBulkWithHilbert_very_small() { } @Test - public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_very_small_10() - throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_very_small_10() { insertManyNodesIndividually(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("very_small")); } @Test - public void shouldInsertManyNodesIndividuallyGreenesSplit_very_small_10() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyGreenesSplit_very_small_10() { insertManyNodesIndividually(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("very_small")); } @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_very_small_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_very_small_10() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("very_small")); } @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_very_small_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_very_small_10() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("very_small")); } @@ -608,23 +605,23 @@ public void shouldInsertManyNodesInBulkWithHilbert_small() { @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_small_10() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_small_10() { insertManyNodesIndividually(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("small")); } @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesIndividuallyGreenesSplit_small_10() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyGreenesSplit_small_10() { insertManyNodesIndividually(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("small")); } @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_small_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_small_10() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("small")); } @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_small_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_small_10() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("small")); } @@ -634,23 +631,23 @@ public void shouldInsertManyNodesInBulkWithGreenesSplit_small_10() throws Factor @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_small_100() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_small_100() { insertManyNodesIndividually(RTreeIndex.QUADRATIC_SPLIT, 5000, 100, testConfigs.get("small")); } @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesIndividuallyGreenesSplit_small_100() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyGreenesSplit_small_100() { insertManyNodesIndividually(RTreeIndex.GREENES_SPLIT, 5000, 100, testConfigs.get("small")); } @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_small_100() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_small_100() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 100, testConfigs.get("small")); } @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_small_100() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_small_100() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 100, testConfigs.get("small")); } @@ -690,35 +687,35 @@ public void shouldInsertManyNodesInBulkWithHilbert_medium() { @Disabled @Test - public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_medium_10() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_medium_10() { insertManyNodesIndividually(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("medium")); } @Disabled @Test - public void shouldInsertManyNodesIndividuallyGreenesSplit_medium_10() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyGreenesSplit_medium_10() { insertManyNodesIndividually(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("medium")); } @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_10() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("medium")); } @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_10() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("medium")); } @Disabled @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_10_merge() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_10_merge() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("medium"), true); } @Disabled @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_10_merge() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_10_merge() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("medium"), true); } @@ -728,36 +725,36 @@ public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_10_merge() throws @Disabled @Test - public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_medium_100() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyWithQuadraticSplit_medium_100() { insertManyNodesIndividually(RTreeIndex.QUADRATIC_SPLIT, 5000, 100, testConfigs.get("medium")); } @Disabled @Test - public void shouldInsertManyNodesIndividuallyGreenesSplit_medium_100() throws FactoryException, IOException { + public void shouldInsertManyNodesIndividuallyGreenesSplit_medium_100() { insertManyNodesIndividually(RTreeIndex.GREENES_SPLIT, 5000, 100, testConfigs.get("medium")); } @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_100() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_100() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 100, testConfigs.get("medium")); } @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_100() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_100() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 100, testConfigs.get("medium")); } @Disabled @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_100_merge() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_medium_100_merge() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 100, testConfigs.get("medium"), true); } @Disabled @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_100_merge() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_medium_100_merge() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 100, testConfigs.get("medium"), true); } @@ -797,25 +794,25 @@ public void shouldInsertManyNodesInBulkWithHilbert_large() { @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_large_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_large_10() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 10, testConfigs.get("large")); } @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_large_10() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_large_10() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 10, testConfigs.get("large")); } @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesInBulkWithQuadraticSplit_large_100() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithQuadraticSplit_large_100() { insertManyNodesInBulk(RTreeIndex.QUADRATIC_SPLIT, 5000, 100, testConfigs.get("large")); } @Disabled // takes too long, change to @Test when benchmarking @Test - public void shouldInsertManyNodesInBulkWithGreenesSplit_large_100() throws FactoryException, IOException { + public void shouldInsertManyNodesInBulkWithGreenesSplit_large_100() { insertManyNodesInBulk(RTreeIndex.GREENES_SPLIT, 5000, 100, testConfigs.get("large")); } @@ -967,7 +964,6 @@ public void shouldInsertManyNodesIndividuallyAndGenerateImagesForAnimation() thr int currBlock = 1; while (currBlock < nodes.size()) { List slice = nodes.subList(prevBlock, currBlock); - long startIndexing = System.currentTimeMillis(); try (Transaction tx = db.beginTx()) { for (String node : slice) { layer.add(tx, tx.getNodeByElementId(node)); @@ -1108,10 +1104,6 @@ public void shouldInsertManyNodesInBulkAndGenerateImagesForAnimation() throws IO @Disabled @Test public void shouldAccessIndexAfterBulkInsertion() { - // Use these two lines if you want to examine the output. -// File dbPath = new File("target/var/BulkTest"); -// GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabase(dbPath.getCanonicalPath()); - SpatialDatabaseService sdbs = spatial(); EditableLayer layer = getOrCreateSimplePointLayer("Coordinates", "rtree", "lon", "lat"); final long numNodes = 100000; @@ -1186,7 +1178,6 @@ public void shouldBuildTreeFromScratch() throws Exception { //GraphDatabaseService db = this.databases.database("BultTest2"); GraphDatabaseService db = this.db; - SpatialDatabaseService sdbs = spatial(); GeometryEncoder encoder = new SimplePointEncoder(); Method decodeEnvelopes = RTreeIndex.class.getDeclaredMethod("decodeEnvelopes", List.class); @@ -1331,88 +1322,6 @@ private List populateSquareTestData(int width) { return nodes; } - private List populateSquareTestDataHeavy(int width) { - List nodes = populateSquareTestData(width); - Random rand = new Random(42); - - for (int i = 0; i < width / 2; i++) { - try (Transaction tx = db.beginTx()) { - for (int j = 0; j < width / 2; j++) { - Node node = tx.createNode(); - node.addLabel(Label.label("Coordinates")); - - node.setProperty("lat", ((double) rand.nextInt(width / 10) / (double) width)); - node.setProperty("lon", ((double) rand.nextInt(width / 10) / (double) width)); - nodes.add(node.getElementId()); - } - tx.commit(); - } - } - java.util.Collections.shuffle(nodes, new Random(8)); - return nodes; - } - - private List populateSquareWithStreets(int width) { - List nodes = new ArrayList<>(); - double squareValue = 0.25; - for (int i = 1; i < 4; i += 2) { - try (Transaction tx = db.beginTx()) { - for (int j = (int) squareValue * width; j < 2 * squareValue * width; j++) { - Node node = tx.createNode(); - node.addLabel(Label.label("Coordinates")); - node.setProperty("lat", i * squareValue); - node.setProperty("lon", (j + squareValue) / width + squareValue); - nodes.add(node.getElementId()); - Node node2 = tx.createNode(); - node2.addLabel(Label.label("Coordinates")); - node2.setProperty("lat", (j + squareValue) / width + squareValue); - node2.setProperty("lon", i * squareValue); - nodes.add(node2.getElementId()); - - } - tx.commit(); - } - } - for (int i = 0; i < width; i++) { - try (Transaction tx = db.beginTx()) { - - Node node = tx.createNode(); - node.addLabel(Label.label("Coordinates")); - node.setProperty("lat", ((double) i / (double) width)); - node.setProperty("lon", ((double) i / (double) width)); - nodes.add(node.getElementId()); - Node node2 = tx.createNode(); - node2.addLabel(Label.label("Coordinates")); - node2.setProperty("lat", ((double) (width - i) / (double) width)); - node2.setProperty("lon", ((double) i / (double) width)); - nodes.add(node2.getElementId()); - tx.commit(); - } - } - java.util.Collections.shuffle(nodes, new Random(8)); - return nodes; - } - - - private static void searchForPos(int numNodes, GraphDatabaseService db) { - System.out.println("Searching with spatial.withinDistance"); - long start = System.currentTimeMillis(); - try (Transaction tx = db.beginTx()) { // 'points',{longitude:15.0,latitude:60.0},100 - Result result = tx.execute( - "CALL spatial.withinDistance('Coordinates',{longitude:0.5, latitude:0.5},1000.0) yield node"); - int i = 0; - ResourceIterator thing = result.columnAs("node"); - while (thing.hasNext()) { - assertNotNull(thing.next()); - i++; - } - //assertEquals(i, numNodes); - tx.commit(); - } - System.out.println("\t" + (System.currentTimeMillis() - start) + "ms"); - - } - private List setup(String name, String index, int width) { long start = System.currentTimeMillis(); List nodes = populateSquareTestData(width); @@ -1484,8 +1393,7 @@ private List queryRTree(Layer layer, TreeMonitor monitor, TestStats stats) if (layer.getIndex() instanceof RTreeIndex) { getRTreeIndexStats((RTreeIndex) layer.getIndex(), monitor, stats, true, nodes.size()); } else if (layer.getIndex() instanceof ExplicitIndexBackedPointIndex) { - getExplicitIndexBackedIndexStats((ExplicitIndexBackedPointIndex) layer.getIndex(), stats, true, - nodes.size()); + getExplicitIndexBackedIndexStats((ExplicitIndexBackedPointIndex) layer.getIndex(), stats); } return nodes; } @@ -1525,10 +1433,8 @@ private void getRTreeIndexStats(RTreeIndex index, TreeMonitor monitor, TestStats int matched = monitor.getCaseCounts().get("Geometry Matches"); int indexSize = 0; try (Transaction tx = db.beginTx()) { - for (Node ignored : index.getAllIndexInternalNodes(tx)) { - indexSize++; - } - tx.commit(); + indexSize += StreamSupport.stream(index.getAllIndexInternalNodes(tx).spliterator(), false).count(); + tx.commit(); } stats.put("Index Size", indexSize); stats.put("Found", matched); @@ -1561,8 +1467,7 @@ private void getRTreeIndexStats(RTreeIndex index, TreeMonitor monitor, TestStats } } - private static void getExplicitIndexBackedIndexStats(ExplicitIndexBackedPointIndex index, TestStats stats, - boolean assertTouches, long countGeometries) { + private static void getExplicitIndexBackedIndexStats(ExplicitIndexBackedPointIndex index, TestStats stats) { IndexTestConfig config = stats.config; ExplicitIndexBackedMonitor monitor = index.getMonitor(); long touched = monitor.getHits() + monitor.getMisses(); @@ -1581,7 +1486,6 @@ private static void getExplicitIndexBackedIndexStats(ExplicitIndexBackedPointInd private class TimedLogger { - String title; long count; long gap; long start; @@ -1592,7 +1496,6 @@ private TimedLogger(String title, long count) { } private TimedLogger(String title, long count, long gap) { - this.title = title; this.count = count; this.gap = gap; this.start = System.currentTimeMillis(); @@ -1783,10 +1686,6 @@ public void put(String key, Object value) { data.put(key, value); } - public void get(String key) { - data.get(key); - } - private static String[] headerArray() { return new String[]{"Size Name", "Insert Mode", "Split Mode", "Data Width", "Data Size", "Block Size", "Max Node References"};