diff --git a/doc-examples/pom.xml b/doc-examples/pom.xml new file mode 100644 index 000000000..857334753 --- /dev/null +++ b/doc-examples/pom.xml @@ -0,0 +1,110 @@ + + + + + 4.0.0 + + org.neo4j + neo4j-spatial + 2025.10.1-SNAPSHOT + + + neo4j-spatial-doc-examples + + + + info.picocli + picocli + 4.7.7 + + + org.neo4j + neo4j-spatial-test-utils + 2025.10.1-SNAPSHOT + + + org.neo4j + neo4j-spatial-cli-tools + 2025.10.1-SNAPSHOT + + + org.neo4j + neo4j-spatial-server-plugin + 2025.10.1-SNAPSHOT + + + org.neo4j + neo4j-kernel-test-utils + ${neo4j.version} + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.6.2 + + java + + + user.dir + ${project.parent.basedir} + + + + + + generate-api-docs + compile + exec + + java + ${project.parent.basedir} + + -classpath + + org.neo4j.spatial.doc.examples.ApiDocGenerator + + + + + generate-examples + compile + exec + + java + ${project.parent.basedir} + + -classpath + + org.neo4j.spatial.doc.examples.ApiExampleGenerator + + + + + + + + diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/DocGeneratorTest.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/ApiDocGenerator.java similarity index 71% rename from server-plugin/src/test/java/org/neo4j/gis/spatial/DocGeneratorTest.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/ApiDocGenerator.java index e9d5e5a62..a4c87bd65 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/DocGeneratorTest.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/ApiDocGenerator.java @@ -17,7 +17,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gis.spatial; +package org.neo4j.spatial.doc.examples; + +import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME; import com.fasterxml.jackson.core.JsonProcessingException; import java.io.BufferedWriter; @@ -31,10 +33,9 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.Callable; import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.neo4j.doc.domain.examples.Mapper; -import org.neo4j.exceptions.KernelException; +import org.neo4j.configuration.GraphDatabaseSettings; import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.internal.kernel.api.procs.DescribedSignature; @@ -45,41 +46,70 @@ import org.neo4j.kernel.api.procedure.GlobalProcedures; import org.neo4j.kernel.api.procedure.ProcedureView; import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.spatial.doc.examples.utils.Mapper; +import org.neo4j.test.TestDatabaseManagementServiceBuilder; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; -public class DocGeneratorTest extends AbstractApiTest { +@Command(name = "ApiDocGenerator", mixinStandardHelpOptions = true) +public class ApiDocGenerator implements Callable { - public static final String GENERATED_COMMENT = "// This file is generated by DocGeneratorTest, do not edit it manually\n"; + @Option(names = {"-i", "--index-file"}, description = "The file location of the doc index") + private Path indexFile = Path.of("./docs/docs/modules/ROOT/partials/generated/api/index.adoc"); - @Override - protected void registerApiProceduresAndFunctions() throws KernelException { - registerProceduresAndFunctions(SpatialFunctions.class); - registerProceduresAndFunctions(SpatialProcedures.class); - } + @Option(names = {"-n", "--nav-file"}, description = "The file location of the navigation") + private Path navFile = Path.of("./docs/docs/modules/ROOT/partials/generated/api-nav.adoc"); - @Test - public void generateDocStub() throws IOException { + @Option(names = {"-p", + "--partials-root"}, description = "The root directory where the partials for the api doc should be placed in") + private Path partialsRoot = Path.of("./docs/docs/modules/ROOT/partials/generated/api"); - GlobalProcedures procedures = ((GraphDatabaseAPI) db).getDependencyResolver() - .resolveDependency(GlobalProcedures.class); - ProcedureView currentView = procedures.getCurrentView(); + @Option(names = {"-c", + "--customizable-root"}, description = "The root directory where the customizable doc file is placed in.") + private Path customizableRoot = Path.of("./docs/docs/modules/ROOT/pages/api"); - Structure root = new Structure(null); + public static final String GENERATED_COMMENT = + "// This file is generated by " + ApiDocGenerator.class.getName() + ", do not edit it manually\n"; - Stream.of( - currentView.getAllNonAggregatingFunctions(QueryLanguage.CYPHER_5), - currentView.getAllAggregatingFunctions(QueryLanguage.CYPHER_5), - currentView.getAllProcedures(QueryLanguage.CYPHER_5) - ) - .flatMap(stream -> stream) - .forEach(root::add); + public static void main(String... args) { + int exitCode = new CommandLine(new ApiDocGenerator()).execute(args); + System.exit(exitCode); + } - Structure spatial = root.nested.get("spatial"); - spatial.writeDoc(); - spatial.writeNav(); - spatial.writeSingleDocs(); + @Override + public Integer call() throws Exception { + try (var neo4j = new TestDatabaseManagementServiceBuilder() + .setConfig(GraphDatabaseSettings.procedure_unrestricted, List.of("spatial.*")) + .impermanent() + .build()) { + + GlobalProcedures procedures = ((GraphDatabaseAPI) neo4j.database(DEFAULT_DATABASE_NAME)) + .getDependencyResolver() + .resolveDependency(GlobalProcedures.class); + procedures.registerProcedure(SpatialProcedures.class); + procedures.registerFunction(SpatialFunctions.class); + + ProcedureView currentView = procedures.getCurrentView(); + Structure root = new Structure(null); + Stream.of( + currentView.getAllNonAggregatingFunctions(QueryLanguage.CYPHER_5), + currentView.getAllAggregatingFunctions(QueryLanguage.CYPHER_5), + currentView.getAllProcedures(QueryLanguage.CYPHER_5) + ) + .flatMap(stream -> stream) + .forEach(root::add); + + Structure spatial = root.nested.get("spatial"); + spatial.writeDoc(); + spatial.writeNav(); + spatial.writeSingleDocs(); + } + System.out.println("API-Doc successfully generated!"); + return 0; } - private static class Structure { + private class Structure { String namespace; Map nested = new TreeMap<>(); @@ -104,18 +134,16 @@ public void add(DescribedSignature sig) { } public void writeDoc() throws IOException { - Path file = Path.of("../docs/docs/modules/ROOT/partials/generated/api/index.adoc"); - Files.createDirectories(file.getParent()); - try (BufferedWriter writer = Files.newBufferedWriter(file)) { + Files.createDirectories(indexFile.getParent()); + try (BufferedWriter writer = Files.newBufferedWriter(indexFile)) { writer.append(GENERATED_COMMENT); writer.append(generateIndex()); } } public void writeNav() throws IOException { - Path file = Path.of("../docs/docs/modules/ROOT/partials/generated/api-nav.adoc"); - Files.createDirectories(file.getParent()); - try (BufferedWriter writer = Files.newBufferedWriter(file)) { + Files.createDirectories(navFile.getParent()); + try (BufferedWriter writer = Files.newBufferedWriter(navFile)) { writer.append(GENERATED_COMMENT); writer.append(this.generateNav()); } @@ -133,13 +161,11 @@ public void writeSingleDocs() throws IOException { private void writeSingleDoc(DescribedSignature entry) throws IOException { String fqname = entry.name().toString(); - Path customizedFile = Path.of("../docs/docs/modules/ROOT/pages/api", namespace, fqname + ".adoc"); + Path customizedFile = customizableRoot.resolve( namespace).resolve( fqname + ".adoc"); if (!Files.exists(customizedFile)) { Files.createDirectories(customizedFile.getParent()); - String commented = Files.exists( - Path.of("../docs/docs/modules/ROOT/partials/generated/api", namespace, - fqname + "-examples.adoc")) - ? "" : "// "; + String commented = + Files.exists(partialsRoot.resolve(namespace).resolve(fqname + "-examples.adoc")) ? "" : "// "; Files.writeString(customizedFile, "include::partial$generated/api/" + namespace + "/" + fqname + ".adoc[]\n\n" + commented + "== Examples\n" @@ -148,7 +174,7 @@ private void writeSingleDoc(DescribedSignature entry) throws IOException { + "-examples.adoc[]\n"); } - Path file = Path.of("../docs/docs/modules/ROOT/partials/generated/api", namespace, fqname + ".adoc"); + Path file = partialsRoot.resolve(namespace).resolve(fqname + ".adoc"); Files.createDirectories(file.getParent()); List inputs; diff --git a/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/ApiExampleGenerator.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/ApiExampleGenerator.java new file mode 100644 index 000000000..c11e909ee --- /dev/null +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/ApiExampleGenerator.java @@ -0,0 +1,551 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j Spatial. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.neo4j.spatial.doc.examples; + +import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.stream.Stream; +import org.neo4j.configuration.GraphDatabaseSettings; +import org.neo4j.dbms.api.DatabaseManagementService; +import org.neo4j.exceptions.KernelException; +import org.neo4j.gis.spatial.functions.SpatialFunctions; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.kernel.api.procedure.GlobalProcedures; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.spatial.doc.examples.domain.Example; +import org.neo4j.spatial.doc.examples.domain.ExampleCypher; +import org.neo4j.spatial.doc.examples.domain.ExamplesRepository; +import org.neo4j.test.GraphDatabaseServiceCleaner; +import org.neo4j.test.TestDatabaseManagementServiceBuilder; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; + +@Command(name = "ApiExampleGenerator", mixinStandardHelpOptions = true) +public class ApiExampleGenerator implements Callable { + + @Option(names = {"-p", + "--partials-root"}, description = "The root directory where the examples should be placed in") + private Path partialsRoot = Path.of("./docs/docs/modules/ROOT/partials/generated/api"); + + private GraphDatabaseService db; + private ExamplesRepository examples; + + public static void main(String... args) { + int exitCode = new CommandLine(new ApiExampleGenerator()).execute(args); + System.exit(exitCode); + } + + @Override + public Integer call() throws Exception { + ApiExampleGenerator generator = new ApiExampleGenerator(); + try (var neo4j = new TestDatabaseManagementServiceBuilder() + .setConfig(GraphDatabaseSettings.procedure_unrestricted, List.of("spatial.*")) + .impermanent() + .build()) { + + generator.setUp(neo4j); + generator.generateAllExamples(); + generator.writeDocumentation(); + } + System.out.println("Examples successfully generated!"); + return 0; + } + + private void setUp(DatabaseManagementService neo4j) throws KernelException { + db = neo4j.database(DEFAULT_DATABASE_NAME); + + GlobalProcedures procedures = ((GraphDatabaseAPI) db) + .getDependencyResolver() + .resolveDependency(GlobalProcedures.class); + procedures.registerProcedure(SpatialProcedures.class); + procedures.registerFunction(SpatialFunctions.class); + + examples = new ExamplesRepository(); + } + + private Example docExample(String signature, String title) { + GraphDatabaseServiceCleaner.cleanDatabaseContent(db); + return examples.docExample(signature, title, db); + } + + private void generateAllExamples() { + generateSpatialFunctionExamples(); + generateSpatialProcedureExamples(); + } + + private void generateSpatialFunctionExamples() { + + docExample("spatial.asGeometry", "Creates a point geometry") + .runCypher( + "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asGeometry(geom) AS geometry"); + + docExample("spatial.asMap", "Creates a point geometry as map") + .runCypher( + "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asMap(geom) AS geometry"); + + docExample("spatial.asGeometry", "Creates a point geometry from a map") + .runCypher("WITH spatial.asGeometry({latitude: 5.0, longitude: 4.0}) AS geometry RETURN geometry"); + + docExample("spatial.wktToGeoJson", "1. Converts a WKT POINT") + .runCypher("RETURN spatial.wktToGeoJson('POINT (30 10)') as json"); + + docExample("spatial.wktToGeoJson", "2. Converts a WKT LINESTRING") + .runCypher("RETURN spatial.wktToGeoJson('LINESTRING (30 10, 10 30, 40 40)') as json"); + + docExample("spatial.wktToGeoJson", "3. Converts a WKT POLYGON") + .runCypher("RETURN spatial.wktToGeoJson('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))') as json"); + + docExample("spatial.wktToGeoJson", "4. Converts a WKT POLYGON with a hole") + .runCypher( + "RETURN spatial.wktToGeoJson('POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))') as json"); + + docExample("spatial.wktToGeoJson", "5a. Converts a WKT MULTIPOINT") + .runCypher( + "RETURN spatial.wktToGeoJson('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))') as json"); + + docExample("spatial.wktToGeoJson", "5b. Converts a WKT MULTIPOINT") + .runCypher("RETURN spatial.wktToGeoJson('MULTIPOINT (10 40, 40 30, 20 20, 30 10)') as json"); + + docExample("spatial.wktToGeoJson", "6. Converts a WKT MULTILINESTRING") + .runCypher( + "RETURN spatial.wktToGeoJson('MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))') as json"); + + docExample("spatial.wktToGeoJson", "7a. Converts a WKT MULTIPOLYGON") + .runCypher( + "RETURN spatial.wktToGeoJson('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))') as json"); + + docExample("spatial.wktToGeoJson", "7b. Converts a WKT MULTIPOLYGON") + .runCypher( + "RETURN spatial.wktToGeoJson('MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))') as json"); + + docExample("spatial.wktToGeoJson", "8. FConverts a WKT GEOMETRYCOLLECTION") + .runCypher( + "RETURN spatial.wktToGeoJson('GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)))') as json"); + + docExample("spatial.neo4jGeometryToWkt", "Converting a point to WKT") + .runCypher("RETURN spatial.neo4jGeometryToWkt(point({longitude: 1, latitude: 2})) as wkt"); + + docExample("spatial.neo4jGeometryToWkt", "Converting a point array to WKT") + .runCypher( + "RETURN spatial.neo4jGeometryToWkt([point({longitude: 1, latitude: 2}), point({longitude: 3, latitude: 4}) ]) as wkt"); + + docExample("spatial.extractAttributes", "Extracts attributes from a layer node") + .runCypher(""" + CALL spatial.addPointLayer('attr_layer') YIELD node + WITH node + CREATE (n:Point {longitude: 10.0, latitude: 20.0, name: 'test_point', type: 'landmark', elevation: 100}) + WITH n + CALL spatial.addNode('attr_layer', n) YIELD node as added_node + WITH n + RETURN spatial.extractAttributes('attr_layer', n) as attributes + """); + + docExample("spatial.extractAttributes", "Extracts attributes from a WKT layer node") + .runCypher(""" + CALL spatial.addLayer('wkt_attr_layer', 'WKT', '') YIELD node + WITH node + CREATE (n:Geometry {geometry: 'POINT (30 10)', name: 'test_wkt_point', category: 'marker', id: 42}) + WITH n + CALL spatial.addNode('wkt_attr_layer', n) YIELD node as added_node + WITH n + RETURN spatial.extractAttributes('wkt_attr_layer', n) as attributes + """); + + docExample("spatial.decodeGeometry", "Using both functions together") + .additionalSignature("spatial.extractAttributes") + .runCypher(""" + CALL spatial.addPointLayer('combined_layer') YIELD node + WITH node + CREATE (n:Point {longitude: 5.5, latitude: 45.5, city: 'TestCity', population: 50000}) + WITH n + CALL spatial.addNode('combined_layer', n) YIELD node as added_node + WITH n + RETURN + spatial.decodeGeometry('combined_layer', n) as geometry, + spatial.extractAttributes('combined_layer', n) as attributes + """); + + docExample("spatial.nodeAsWKT", "Converting a layer node to WKT") + .runCypher(""" + CALL spatial.addPointLayer('wkt_layer') YIELD node + WITH node + CREATE (n:Point {longitude: 10.0, latitude: 20.0}) + WITH n + CALL spatial.addNode('wkt_layer', n) YIELD node as added_node + WITH n + RETURN spatial.nodeAsWKT('wkt_layer', n) as wkt + """); + } + + private void generateSpatialProcedureExamples() { + docExample("spatial.decodeGeometry", "Decode a geometry from a node property") + .additionalSignature("spatial.addWKTLayer") + .runCypher("CALL spatial.addWKTLayer('geom','geom')", + config -> config.setTitle("Create a WKT layer")) + .runCypher( + "CREATE (n:Node {geom:'POINT(4.0 5.0)'}) RETURN spatial.decodeGeometry('geom',n) AS geometry", + config -> config.setTitle("Decode a geometry")); + + docExample("spatial.addNativePointLayerWithConfig", "Create a native point layer with a configuration") + .runCypher("CALL spatial.addNativePointLayerWithConfig('geom','pos:mbr','hilbert')"); + + docExample("spatial.addNativePointLayerXY", "Create a native point layer") + .additionalSignature("spatial.withinDistance") + .additionalSignature("spatial.addNode") + .runCypher("CALL spatial.addNativePointLayerXY('geom','x','y')") + .runCypher( + "CREATE (n:Node {id: 42, x: 5.0, y: 4.0}) WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node", + config -> config.skipResult().setComment("create a node and add it to the index")) + .runCypher("CALL spatial.withinDistance('geom',point({latitude:4.1,longitude:5.1}),100)", + config -> config.setComment("Find node within distance")); + + docExample("spatial.addPointLayerWithConfig", "Create a point layer with X and Y properties") + .runCypher("CALL spatial.addPointLayerWithConfig('geom','lon:lat')"); + + docExample("spatial.addLayerWithEncoder", "Create a `SimplePointEncoder`") + .runCypher("CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','')"); + + docExample("spatial.addLayerWithEncoder", + "Create a `SimplePointEncoder` with a customized encoder configuration") + .runCypher("CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','x:y:mbr')", + config -> config.setComment(""" + Configures the encoder to use the nodes `x` property instead of `longitude`, + the `y` property instead of `latitude` + and the `mbr` property instead of `bbox`. + """)); + + docExample("spatial.addLayerWithEncoder", "Create a `NativePointEncoder`") + .runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','')"); + + docExample("spatial.addLayerWithEncoder", + "Create a `NativePointEncoder` with a customized encoder configuration") + .runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr')", + config -> config.setComment(""" + Configures the encoder to use the nodes `pos` property instead of `location` + and the `mbr` property instead of `bbox`. + """)); + + docExample("spatial.addLayerWithEncoder", + "Create a `NativePointEncoder` with a customized encoder configuration using Cartesian coordinates") + .runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr:Cartesian')", + config -> config.setComment(""" + Configures the encoder to use the nodes `pos` property instead of `location`, + the `mbr` property instead of `bbox` and Cartesian coordinates. + """)); + + docExample("spatial.layers", "Add and Remove a layer") + .additionalSignature("spatial.removeLayer") + .runCypher("CALL spatial.addWKTLayer('geom','wkt')") + .runCypher("CALL spatial.layers()") + .runCypher("CALL spatial.removeLayer('geom')") + .runCypher("CALL spatial.layers()"); + + docExample("spatial.getFeatureAttributes", "Get the feature attributes of a layer") + .additionalSignature("spatial.setFeatureAttributes") + .runCypher("CALL spatial.addWKTLayer('geom','wkt')") + .runCypher("CALL spatial.getFeatureAttributes('geom')") + .runCypher("CALL spatial.setFeatureAttributes('geom',['name','type','color'])") + .runCypher("CALL spatial.getFeatureAttributes('geom')"); + + docExample("spatial.layerTypes", "List the available layer types") + .runCypher("CALL spatial.layerTypes()"); + + Stream.of("Simple", "Native").forEach(encoder -> + Stream.of("Geohash", "ZOrder", "Hilbert", "RTree").forEach(indexType -> { + String procName = + encoder.equals("Native") ? "spatial.addNativePointLayer" : "spatial.addPointLayer"; + if (!indexType.equals("RTree")) { + procName += indexType; + } + String layerName = ("my-" + encoder + "-" + indexType + "-layer").toLowerCase(); + + docExample(procName, "Create a layer to index a node") + .runCypher("CALL " + procName + "('" + layerName + "')") + .runCypher( + "CREATE (n:Node {id: 42, latitude:60.1,longitude:15.2}) SET n.location=point(n) RETURN n", + config -> config.skipResult().setComment("Create a node to index")) + .runCypher("MATCH (n:Node) WITH n CALL spatial.addNode('" + layerName + + "',n) YIELD node RETURN node", + config -> config.setComment("Index node")) + .runCypher( + "CALL spatial.withinDistance('" + layerName + "',{lon:15.0,lat:60.0},100)", + config -> config.setComment("Find node within distance")); + + })); + + docExample("spatial.addPointLayerXY", "Create a point layer with X and Y properties") + .additionalSignature("spatial.addNodes") + .additionalSignature("spatial.removeNode") + .additionalSignature("spatial.removeNode.byId") + .runCypher("CALL spatial.addPointLayerXY('geom','lon','lat')", ExampleCypher::skipResult) + .runCypher( + "CREATE (n1:Node {id: 1, lat:60.1,lon:15.2}),(n2:Node {id: 2, lat:60.1,lon:15.3}) WITH n1,n2 CALL spatial.addNodes('geom',[n1,n2]) YIELD count RETURN n1,n2,count", + config -> config.setComment("Add two nodes to the layer")) + .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", + config -> config.setComment("Find nodes within distance")) + .runCypher(""" + MATCH (node) WHERE node.id = 1 + CALL spatial.removeNode('geom', node) YIELD nodeId + RETURN nodeId + """, + config -> config.skipResult().setComment("Remove node 1")) + .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)") + .runCypher(""" + MATCH (node) WHERE node.id = 2 + CALL spatial.removeNode.byId('geom', elementId(node)) YIELD nodeId + RETURN nodeId + """, + config -> config.skipResult().setComment("Remove node 2")) + .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)"); + + docExample("spatial.addLayer", "Add the same node to multiple layers") + .runCypher(""" + UNWIND range(1,$count) as i + CREATE (n:Point { + id: i, + point1: point( { latitude: 56.0, longitude: 12.0 } ), + point2: point( { latitude: 57.0, longitude: 13.0 } ) + })""", + config -> config.skipResult().setParams(Map.of("count", 100)).setTitle("Create some nodes")) + .runCypher(""" + CALL spatial.addLayer( + 'point1', + 'NativePoint', + 'point1:point1BB', + '{"referenceRelationshipType": "RTREE_P1_TYPE"}' + ) + """, config -> config.skipResult().setComment(""" + Create a layer `point1` to index property `point1` of node `Point`. + Save the bounding box in the property `point1BB` of the `Point` node. + Associate the node with the index layer via relationship type `RTREE_P1_TYPE`. + """)) + .runCypher(""" + CALL spatial.addLayer( + 'point2', + 'NativePoint', + 'point2:point2BB', + '{"referenceRelationshipType": "RTREE_P2_TYPE"}' + ) + """, config -> config.skipResult().setComment(""" + Create a layer `point2` to index property `point2` of node `Point`. + Save the bounding box in the property `point2BB` of the `Point` node. + Associate the node with the index layer via relationship type `RTREE_P2_TYPE`. + """)) + .runCypher(""" + MATCH (p:Point) + WITH (count(p) / 10) AS pages, collect(p) AS nodes + UNWIND range(0, pages) AS i CALL { + WITH i, nodes + CALL spatial.addNodes('point1', nodes[(i * 10)..((i + 1) * 10)]) YIELD count + RETURN count AS count + } IN TRANSACTIONS OF 1 ROWS + RETURN sum(count) AS count + """, config -> config.setComment("Index the nodes in layer `point1` in chunks of 10")) + .runCypher(""" + MATCH (p:Point) + WITH (count(p) / 10) AS pages, collect(p) AS nodes + UNWIND range(0, pages) AS i CALL { + WITH i, nodes + CALL spatial.addNodes('point2', nodes[(i * 10)..((i + 1) * 10)]) YIELD count + RETURN count AS count + } IN TRANSACTIONS OF 1 ROWS + RETURN sum(count) AS count + """, + config -> config.setComment("Index the nodes in layer `point2` in chunks of 10")); + + docExample("spatial.importShapefile", "Import a shape-file") + .runCypher("CALL spatial.importShapefile('example-data/shp/highway.shp')") + .runCypher("CALL spatial.layers()"); + + docExample("spatial.importShapefileToLayer", "Import a shape-file") + .runCypher("CALL spatial.addWKTLayer('geom','wkt')", ExampleCypher::skipResult) + .runCypher("CALL spatial.importShapefileToLayer('geom', 'example-data/shp/highway.shp')") + .runCypher("CALL spatial.layers()"); + + docExample("spatial.importOSM", "Import an OSM file") + .runCypher("CALL spatial.importOSM('example-data/osm/example.osm')") + .runCypher("CALL spatial.layers()"); + + docExample("spatial.importOSMToLayer", "Import an OSM file") + .runCypher("CALL spatial.addLayer('geom','OSM','')", ExampleCypher::skipResult) + .runCypher("CALL spatial.importOSMToLayer('geom','example-data/osm/example.osm')") + .runCypher("CALL spatial.layers()"); + + docExample("spatial.bbox", "Find geometries in a bounding box") + .runCypher("CALL spatial.addPointLayer('geom')", ExampleCypher::skipResult) + .runCypher(""" + CREATE (n:Node {id: 1, latitude:60.1,longitude:15.2}) + WITH n CALL spatial.addNode('geom',n) YIELD node + RETURN node + """, ExampleCypher::skipResult) + .runCypher("CALL spatial.bbox('geom',{lon:15.0,lat:60.0}, {lon:15.3, lat:61.0})", + c -> c.setComment("Find node within bounding box")); + + String polygon = "POLYGON((15.3 60.2, 15.3 60.4, 15.7 60.4, 15.7 60.2, 15.3 60.2))"; + docExample("spatial.intersects", "Find geometries in a polygon") + .runCypher("CALL spatial.addPointLayer('geom')", ExampleCypher::skipResult) + .runCypher(""" + UNWIND [ {name:'a',latitude:60.1,longitude:15.2}, {name:'b',latitude:60.3,longitude:15.5} ] as point + CREATE (n:Node) + SET n += point + WITH n + CALL spatial.addNode('geom',n) YIELD node + RETURN node.name as name + """) + .runCypher( + "CALL spatial.intersects('geom','" + polygon + "') YIELD node\n RETURN node.name as name"); + + String lineString = "LINESTRING (15.2 60.1, 15.3 60.1)"; + docExample("spatial.addWKT", "Add a WKT geometry to a layer") + .additionalSignature("spatial.addWKTLayer") + .runCypher("CALL spatial.addWKTLayer('geom', 'wkt')", ExampleCypher::skipResult) + .runCypher("CALL spatial.addWKT('geom',$wkt)", + c -> c.setParams(Map.of("wkt", lineString))) + .runCypher("CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)"); + + var points = List.of("POINT (15.2 60.1)", "POINT (25.2 30.1)"); + docExample("spatial.addWKTs", "Add multiple WKT geometries to a layer") + .additionalSignature("spatial.closest") + .runCypher("CALL spatial.addLayer('geom','geohash','lon:lat')", ExampleCypher::skipResult) + .runCypher("CALL spatial.addWKTs('geom',$wkt)", + c -> c.setParams(Map.of("wkt", points))) + .runCypher("CALL spatial.closest('geom',{lon:15.0, lat:60.0}, 1.0)"); + + docExample("spatial.cql", "Find geometries using CQL") + .runCypher("CALL spatial.addWKTLayer('geom','wkt') YIELD node") + .runCypher(""" + CREATE (n1:Node {wkt: 'POINT(15.2 60.1)', name: 'point1'}) + CREATE (n2:Node {wkt: 'POINT(25.2 30.1)', name: 'point2'}) + WITH n1, n2 + CALL spatial.addNode('geom', n1) YIELD node as added1 + WITH n2, added1 + CALL spatial.addNode('geom', n2) YIELD node as added2 + RETURN added1, added2 + """, + config -> config.skipResult().setComment("Create and add nodes with different coordinates")) + .runCypher("CALL spatial.cql('geom', 'name = \\'point1\\'') YIELD node RETURN node.name as name"); + + docExample("spatial.getFeatureCount", "Get the number of features in a layer") + .runCypher("CALL spatial.addPointLayer('count_layer') YIELD node") + .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", + config -> config.setComment("Get count of empty layer")) + .runCypher(""" + CREATE (n1:Point {latitude: 60.1, longitude: 15.2, name: 'point1'}) + CREATE (n2:Point {latitude: 60.3, longitude: 15.5, name: 'point2'}) + WITH n1, n2 + CALL spatial.addNode('count_layer', n1) YIELD node as added1 + WITH n2, added1 + CALL spatial.addNode('count_layer', n2) YIELD node as added2 + RETURN added1, added2 + """, config -> config.skipResult().setComment("Add two points to the layer")) + .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", + config -> config.setComment("Get count after adding points")); + + docExample("spatial.getLayerBoundingBox", "Get the bounding box of a layer") + .runCypher("CALL spatial.addPointLayer('bbox_layer', 'rtree', 'wgs84') YIELD node") + .runCypher(""" + CREATE (n1:Point {latitude: 60.0, longitude: 15.0, name: 'southwest'}) + CREATE (n2:Point {latitude: 61.0, longitude: 16.0, name: 'northeast'}) + WITH n1, n2 + CALL spatial.addNode('bbox_layer', n1) YIELD node as added1 + WITH n2, added1 + CALL spatial.addNode('bbox_layer', n2) YIELD node as added2 + RETURN added1, added2 + """, config -> config.skipResult().setComment("Add points at opposite corners")) + .runCypher("CALL spatial.getLayerBoundingBox('bbox_layer') YIELD minX, minY, maxX, maxY, crs"); + + docExample("spatial.layerMeta", "Get metadata about a layer") + .runCypher("CALL spatial.addPointLayer('meta_layer', 'rtree', 'wgs84') YIELD node") + .runCypher(""" + CREATE (n1:Point {latitude: 60.0, longitude: 15.0, name: 'southwest'}) + WITH n1 + CALL spatial.addNode('meta_layer', n1) YIELD node + RETURN node + """, config -> config.skipResult().setComment("Add points at opposite corners")) + .runCypher( + "CALL spatial.layerMeta('meta_layer') YIELD name, geometryType, crs, hasComplexAttributes, extraAttributes"); + + docExample("spatial.updateWKT", "Update a node's WKT geometry") + .runCypher("CALL spatial.addWKTLayer('update_layer', 'wkt') YIELD node") + .runCypher(""" + CREATE (n:Node {wkt: 'POINT(15.2 60.1)', name: 'updatable_point'}) + WITH n + CALL spatial.addNode('update_layer', n) YIELD node as added_node + RETURN n, added_node + """, config1 -> config1.skipResult().setComment("Create and add a node with initial WKT")) + .runCypher(""" + MATCH (n:Node {name: 'updatable_point'}) + CALL spatial.updateWKT('update_layer', n, 'POINT(25.5 65.5)') YIELD node + RETURN node.wkt as wkt + """, + config1 -> config1.setComment("Update the node's WKT geometry")) + .runCypher(""" + CALL spatial.withinDistance('update_layer', {longitude: 25.5, latitude: 65.5}, 1) YIELD node + RETURN node.name as name + """, + config -> config.setComment("Verify the updated geometry is indexed correctly")); + + docExample("spatial.getFeatureCount", "Count features in different layer types") + .runCypher("CALL spatial.addPointLayer('count_layer')", + config1 -> config1.skipResult().setComment("Create a point layer")) + .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", + config1 -> config1.setComment("Count features in empty layer")) + .runCypher(""" + CREATE (n:Node {latitude: 60.1, longitude: 15.2, name: 'first'}) + WITH n + CALL spatial.addNode('count_layer', n) YIELD node + RETURN node + """, config1 -> config1.skipResult().setComment("Add one node to the layer")) + .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", + config1 -> config1.setComment("Count after adding one feature")) + .runCypher(""" + UNWIND range(1,3) as i + CREATE (n:Node {id: i, latitude: (60.0 + i * 0.1), longitude: (15.0 + i * 0.1)}) + WITH collect(n) as nodes + CALL spatial.addNodes('count_layer', nodes) YIELD count + RETURN count + """, config -> config.skipResult().setComment("Add multiple nodes at once")) + .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", + config -> config.setComment("Count after adding multiple features")) + .runCypher("CALL spatial.addWKTLayer('wkt_layer', 'wkt')", + config -> config.skipResult().setComment("Create a WKT layer")) + .runCypher("CALL spatial.getFeatureCount('wkt_layer') YIELD count", + config -> config.setComment("Count features in empty WKT layer")) + .runCypher("CALL spatial.addWKT('wkt_layer', 'POINT(15.2 60.1)') YIELD node RETURN node", + config -> config.skipResult().setComment("Add a WKT point")) + .runCypher( + "CALL spatial.addWKT('wkt_layer', 'LINESTRING (15.2 60.1, 15.3 60.1)') YIELD node RETURN node", + config -> config.skipResult().setComment("Add a WKT linestring")) + .runCypher("CALL spatial.getFeatureCount('wkt_layer') YIELD count", + config -> config.setComment("Count features in WKT layer")); + } + + private void writeDocumentation() throws IOException { + examples.write(partialsRoot); + } +} diff --git a/server-plugin/src/test/java/org/neo4j/doc/tools/DocNode.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/DocNode.java similarity index 97% rename from server-plugin/src/test/java/org/neo4j/doc/tools/DocNode.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/DocNode.java index 47064be81..5c62180da 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/tools/DocNode.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/DocNode.java @@ -18,9 +18,8 @@ * along with this program. If not, see . */ -package org.neo4j.doc.tools; +package org.neo4j.spatial.doc.examples.domain; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -39,7 +38,7 @@ import org.neo4j.kernel.impl.core.NodeEntity; /** - * A {@link Node} implementation that represents the labal and properties of a {@link NodeEntity}. + * A {@link Node} implementation that represents the label and properties of a {@link NodeEntity}. */ public class DocNode extends AbstractNodeEntity { @@ -99,7 +98,7 @@ public Iterable getPropertyKeys() { @Override public Map getProperties(String... keys) { - var result = new HashMap(); + var result = new TreeMap(); for (String key : keys) { result.put(key, propertyMap.get(key)); } diff --git a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/Example.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/Example.java similarity index 75% rename from server-plugin/src/test/java/org/neo4j/doc/domain/examples/Example.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/Example.java index 69c169019..6ee832ccf 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/Example.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/Example.java @@ -18,16 +18,13 @@ * along with this program. If not, see . */ -package org.neo4j.doc.domain.examples; +package org.neo4j.spatial.doc.examples.domain; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.function.Consumer; -import org.assertj.core.api.Assertions; -import org.assertj.core.api.InstanceOfAssertFactories; -import org.neo4j.doc.tools.DocNode; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.kernel.impl.core.NodeEntity; @@ -80,19 +77,6 @@ public Example runCypher(String cypher, Consumer customizer) { return this; } - public Example assertSingleResult(String field, Consumer assertions) { - Assertions.assertThat(lastResult).singleElement() - .asInstanceOf(InstanceOfAssertFactories.map(String.class, Object.class)) - .extracting(field) - .satisfies(assertions); - return this; - } - - public Example assertResult(Consumer>> assertions) { - assertions.accept(lastResult); - return this; - } - CharSequence generateCypherBlocks() { if (queries.isEmpty()) { return ""; @@ -113,14 +97,4 @@ public List> getLastResult() { return lastResult; } - public Map getLastSingleResult() { - if (lastResult== null || lastResult.size() != 1){ - throw new IllegalStateException("Expected a single result, but got " + lastResult); - } - return lastResult.get(0); - } - - public Object getLastSingleResult(String field) { - return getLastSingleResult().get(field); - } } diff --git a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/ExampleCypher.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/ExampleCypher.java similarity index 93% rename from server-plugin/src/test/java/org/neo4j/doc/domain/examples/ExampleCypher.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/ExampleCypher.java index c07f6131b..2fb372db8 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/ExampleCypher.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/ExampleCypher.java @@ -18,13 +18,14 @@ * along with this program. If not, see . */ -package org.neo4j.doc.domain.examples; +package org.neo4j.spatial.doc.examples.domain; import com.fasterxml.jackson.core.JsonProcessingException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeSet; +import org.neo4j.spatial.doc.examples.utils.Mapper; import org.neo4j.values.storable.Value; /** @@ -37,7 +38,7 @@ public class ExampleCypher { private String comment; private String title; private List> result; - private boolean storeResult; + private boolean storeResult = true; public ExampleCypher(String cypher) { this.cypher = cypher; @@ -90,8 +91,8 @@ public boolean isStoreResult() { return storeResult; } - public ExampleCypher storeResult() { - this.storeResult = true; + public ExampleCypher skipResult() { + this.storeResult = false; return this; } @@ -138,7 +139,7 @@ private String generateResult() { } StringBuilder writer = new StringBuilder(); writer.append(".Result\n\n"); - var columns = new TreeSet<>(result.get(0).keySet()); + var columns = new TreeSet<>(result.getFirst().keySet()); writer.append("[opts=\"header\",cols=\"") .append(columns.size()) diff --git a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/Examples.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/Examples.java similarity index 79% rename from server-plugin/src/test/java/org/neo4j/doc/domain/examples/Examples.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/Examples.java index 32d3d0afa..957a34c28 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/Examples.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/Examples.java @@ -18,15 +18,15 @@ * along with this program. If not, see . */ -package org.neo4j.doc.domain.examples; +package org.neo4j.spatial.doc.examples.domain; import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Comparator; import java.util.List; +import org.neo4j.spatial.doc.examples.ApiExampleGenerator; /** * Represents a collection of examples for a specific procedure or fucntion. @@ -36,17 +36,21 @@ */ public record Examples(String fqname, List examples) { - public void writeExamples() throws IOException { + public static final String GENERATED_COMMENT = + "// This file is generated by " + ApiExampleGenerator.class.getName() + ", do not edit it manually\n"; + + + public void writeExamples(Path root) throws IOException { var index = fqname.lastIndexOf("."); var namespace = index < 0 ? "" : fqname.substring(0, index); - Path path = Paths.get("../docs/docs/modules/ROOT/partials/generated/api", namespace, - fqname + "-examples.adoc"); + Path path = root.resolve(namespace).resolve(fqname + "-examples.adoc"); Files.createDirectories(path.getParent()); examples.sort(Comparator.comparing(example -> example.title)); try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.append(GENERATED_COMMENT); for (Example example : examples) { if (example.title != null) { writer.write("=== " + example.title + "\n\n"); diff --git a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/ExamplesRepository.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/ExamplesRepository.java similarity index 82% rename from server-plugin/src/test/java/org/neo4j/doc/domain/examples/ExamplesRepository.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/ExamplesRepository.java index 0268c48cb..15405684c 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/ExamplesRepository.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/domain/ExamplesRepository.java @@ -18,9 +18,10 @@ * along with this program. If not, see . */ -package org.neo4j.doc.domain.examples; +package org.neo4j.spatial.doc.examples.domain; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -29,14 +30,9 @@ public class ExamplesRepository { - static Map examples = new HashMap<>(); - private GraphDatabaseService db; + private final Map examples = new HashMap<>(); - public ExamplesRepository(GraphDatabaseService db) { - this.db = db; - } - - public Example docExample(@Nonnull String signature, @Nonnull String title) { + public Example docExample(@Nonnull String signature, @Nonnull String title, GraphDatabaseService db) { Example example = new Example(db, title, this); add(signature, example); return example; @@ -47,9 +43,9 @@ public void add(@Nonnull String signature, @Nonnull Example example) { .examples().add(example); } - public void write() throws IOException { + public void write(Path root) throws IOException { for (Examples examples : examples.values()) { - examples.writeExamples(); + examples.writeExamples(root); } } } diff --git a/server-plugin/src/test/java/org/neo4j/doc/tools/AsciiDocGenerator.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/tools/AsciiDocGenerator.java similarity index 89% rename from server-plugin/src/test/java/org/neo4j/doc/tools/AsciiDocGenerator.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/tools/AsciiDocGenerator.java index c23374de5..3ea32d441 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/tools/AsciiDocGenerator.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/tools/AsciiDocGenerator.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.doc.tools; +package org.neo4j.spatial.doc.examples.tools; import java.io.File; import java.io.FileOutputStream; @@ -52,7 +52,6 @@ public abstract class AsciiDocGenerator { protected GraphDatabaseService graph; protected static final String SNIPPET_MARKER = "@@"; protected Map snippets = new HashMap(); - private static final Map counters = new HashMap(); public AsciiDocGenerator(final String title, final String section) { this.section = section; @@ -191,24 +190,4 @@ private static String sourceSnippet(String tagName, Class source) { + "include::example$" + source.getSimpleName() + ".java[tags=" + tagName + "]\n" + "----\n"; } - - public void addGithubTestSourceLink(String key, Class source, - String dir) { - githubLink(key, source, dir, "test"); - } - - public void addGithubSourceLink(String key, Class source, String dir) { - githubLink(key, source, dir, "main"); - } - - private void githubLink(String key, Class source, String dir, - String mainOrTest) { - String path = "https://github.com/neo4j/neo4j/blob/{neo4j-git-tag}/"; - if (dir != null) { - path += dir + "/"; - } - path += "src/" + mainOrTest + "/java/" + getPath(source); - path += "[" + source.getSimpleName() + ".java]\n"; - addSnippet(key, path); - } } diff --git a/server-plugin/src/test/java/org/neo4j/doc/tools/JavaTestDocsGenerator.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/tools/JavaTestDocsGenerator.java similarity index 97% rename from server-plugin/src/test/java/org/neo4j/doc/tools/JavaTestDocsGenerator.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/tools/JavaTestDocsGenerator.java index be13b46c4..18c796cdf 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/tools/JavaTestDocsGenerator.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/tools/JavaTestDocsGenerator.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.doc.tools; +package org.neo4j.spatial.doc.examples.tools; import java.io.File; import java.io.IOException; diff --git a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/Mapper.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/utils/Mapper.java similarity index 94% rename from server-plugin/src/test/java/org/neo4j/doc/domain/examples/Mapper.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/utils/Mapper.java index e11c83306..41eabcc6e 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/domain/examples/Mapper.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/utils/Mapper.java @@ -18,14 +18,13 @@ * along with this program. If not, see . */ -package org.neo4j.doc.domain.examples; +package org.neo4j.spatial.doc.examples.utils; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import org.neo4j.doc.tools.NodeCypherSerializer; import org.neo4j.graphdb.Node; public class Mapper { diff --git a/server-plugin/src/test/java/org/neo4j/doc/tools/NodeCypherSerializer.java b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/utils/NodeCypherSerializer.java similarity index 97% rename from server-plugin/src/test/java/org/neo4j/doc/tools/NodeCypherSerializer.java rename to doc-examples/src/main/java/org/neo4j/spatial/doc/examples/utils/NodeCypherSerializer.java index b7e696f86..8ff3bb990 100644 --- a/server-plugin/src/test/java/org/neo4j/doc/tools/NodeCypherSerializer.java +++ b/doc-examples/src/main/java/org/neo4j/spatial/doc/examples/utils/NodeCypherSerializer.java @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -package org.neo4j.doc.tools; +package org.neo4j.spatial.doc.examples.utils; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; @@ -26,7 +26,6 @@ import java.io.IOException; import java.util.Map; import java.util.TreeMap; -import org.neo4j.doc.domain.examples.Mapper; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractJavaDocTestBase.java b/doc-examples/src/test/java/org/neo4j/spatial/doc/examples/AbstractJavaDocTestBase.java similarity index 95% rename from server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractJavaDocTestBase.java rename to doc-examples/src/test/java/org/neo4j/spatial/doc/examples/AbstractJavaDocTestBase.java index baf66c080..7237836e5 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractJavaDocTestBase.java +++ b/doc-examples/src/test/java/org/neo4j/spatial/doc/examples/AbstractJavaDocTestBase.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gis.spatial; +package org.neo4j.spatial.doc.examples; import java.io.IOException; import java.util.Map; @@ -26,9 +26,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.RegisterExtension; import org.neo4j.dbms.api.DatabaseManagementService; -import org.neo4j.doc.tools.JavaTestDocsGenerator; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; +import org.neo4j.spatial.doc.examples.tools.JavaTestDocsGenerator; import org.neo4j.test.GraphDatabaseServiceCleaner; import org.neo4j.test.GraphDescription; import org.neo4j.test.GraphHolder; diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java b/doc-examples/src/test/java/org/neo4j/spatial/doc/examples/GeoPipesDocTest.java similarity index 99% rename from server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java rename to doc-examples/src/test/java/org/neo4j/spatial/doc/examples/GeoPipesDocTest.java index a1f9c6687..4a442fb91 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java +++ b/doc-examples/src/test/java/org/neo4j/spatial/doc/examples/GeoPipesDocTest.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gis.spatial.pipes; +package org.neo4j.spatial.doc.examples; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -57,11 +57,9 @@ import org.locationtech.jts.io.WKTReader; import org.neo4j.annotations.documented.Documented; import org.neo4j.configuration.GraphDatabaseSettings; -import org.neo4j.doc.tools.JavaTestDocsGenerator; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; -import org.neo4j.gis.spatial.AbstractJavaDocTestBase; import org.neo4j.gis.spatial.Constants; import org.neo4j.gis.spatial.EditableLayerImpl; import org.neo4j.gis.spatial.SpatialDatabaseService; @@ -70,6 +68,8 @@ import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.osm.OSMImporter; +import org.neo4j.gis.spatial.pipes.GeoPipeFlow; +import org.neo4j.gis.spatial.pipes.GeoPipeline; import org.neo4j.gis.spatial.pipes.filtering.FilterCQL; import org.neo4j.gis.spatial.pipes.osm.OSMGeoPipeline; import org.neo4j.gis.spatial.procedures.SpatialProcedures; @@ -82,6 +82,7 @@ import org.neo4j.spatial.api.SearchFilter; import org.neo4j.spatial.api.layer.Layer; import org.neo4j.spatial.cli.tools.StyledImageExporter; +import org.neo4j.spatial.doc.examples.tools.JavaTestDocsGenerator; import org.neo4j.spatial.geotools.common.utilities.RenderingUtils; import org.neo4j.test.TestData.Title; @@ -1009,7 +1010,7 @@ private static void load() throws Exception { @SuppressWarnings("SameParameterValue") private static void loadTestOsmData(String layerName, int commitInterval) throws Exception { - String osmPath = "./" + layerName; + String osmPath = "../server-plugin/" + layerName; LOGGER.info("\n=== Loading layer " + layerName + " from " + osmPath + " ==="); OSMImporter importer = new OSMImporter(layerName); @@ -1073,8 +1074,6 @@ public static void init() throws Exception { databases = neo4j.databaseManagementService(); db = databases.database(DEFAULT_DATABASE_NAME); load(); - StyledImageExporter exporter = new StyledImageExporter(driver, DEFAULT_DATABASE_NAME); - exporter.setExportDir("target/docs/images/"); } @AfterAll diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestsForDocs.java b/doc-examples/src/test/java/org/neo4j/spatial/doc/examples/TestsForDocs.java similarity index 78% rename from server-plugin/src/test/java/org/neo4j/gis/spatial/TestsForDocs.java rename to doc-examples/src/test/java/org/neo4j/spatial/doc/examples/TestsForDocs.java index fb3d798a3..08b21d2a4 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestsForDocs.java +++ b/doc-examples/src/test/java/org/neo4j/spatial/doc/examples/TestsForDocs.java @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gis.spatial; +package org.neo4j.spatial.doc.examples; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -34,6 +34,11 @@ import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; +import org.neo4j.gis.spatial.Constants; +import org.neo4j.gis.spatial.DynamicLayerConfig; +import org.neo4j.gis.spatial.ShapefileImporter; +import org.neo4j.gis.spatial.SpatialDatabaseService; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.osm.OSMDataset; import org.neo4j.gis.spatial.osm.OSMDataset.Way; @@ -41,9 +46,9 @@ import org.neo4j.gis.spatial.osm.OSMImporter; import org.neo4j.gis.spatial.osm.OSMLayer; import org.neo4j.gis.spatial.pipes.GeoPipeline; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; @@ -52,6 +57,8 @@ import org.neo4j.spatial.api.layer.Layer; import org.neo4j.spatial.cli.tools.ShapefileExporter; import org.neo4j.spatial.geotools.plugin.Neo4jSpatialDataStore; +import org.neo4j.spatial.testutils.Neo4jTestCase; +import org.neo4j.test.GraphDatabaseServiceCleaner; /** * Some test code written specifically for the user manual. This normally means @@ -64,16 +71,14 @@ public class TestsForDocs extends Neo4jTestCase { private static final Logger LOGGER = Logger.getLogger(TestsForDocs.class.getName()); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @BeforeEach public void setUp() { - try (Transaction tx = graphDb().beginTx()) { - tx.getAllRelationships().forEach(Relationship::delete); - tx.commit(); - } - try (Transaction tx = graphDb().beginTx()) { - tx.getAllNodes().forEach(Node::delete); - tx.commit(); - } + GraphDatabaseServiceCleaner.cleanDatabaseContent(graphDb()); } private void checkIndexAndFeatureCount(String layerName) throws IOException { @@ -84,13 +89,10 @@ private void checkIndexAndFeatureCount(String layerName) throws IOException { if (layer.getIndex().count(tx) < 1) { LOGGER.fine("Warning: index count zero: " + layer.getName()); } - LOGGER.fine( - "Layer '" + layer.getName() + "' has " + layer.getIndex().count(tx) + " entries in the index"); tx.commit(); } DataStore store = new Neo4jSpatialDataStore(driver, DEFAULT_DATABASE_NAME); SimpleFeatureCollection features = store.getFeatureSource(layerName).getFeatures(); - LOGGER.fine("Layer '" + layerName + "' has " + features.size() + " features"); try (Transaction tx = graphDb().beginTx()) { Layer layer = spatial.getLayer(tx, layerName, true); assertEquals(layer.getIndex().count(tx), features.size(), @@ -109,7 +111,6 @@ private void checkOSMAPI(Transaction tx, OSMLayer layer) { OSMDataset osm = OSMDataset.fromLayer(tx, layer); Node wayNode = osm.getAllWayNodes(tx).iterator().next(); Way way = osm.getWayFrom(wayNode); - LOGGER.fine("Got first way " + way); for (WayPoint n : way.getWayPoints()) { Way w = n.getWay(); String wayId = w.getNode().getElementId(); @@ -122,19 +123,15 @@ private void checkOSMAPI(Transaction tx, OSMLayer layer) { mostCount = waysFound.get(wayId); } } - LOGGER.fine("Found " + waysFound.size() + " ways overlapping '" + way + "'"); - for (String wayId : waysFound.keySet()) { - LOGGER.fine("\t" + wayId + ":\t" + waysFound.get(wayId) + (wayId.equals(way.getNode().getElementId()) - ? "\t(original way)" : "")); - } assertTrue(way.equals(osm.getWayFromId(tx, mostCommon)), "Start way should be most found way"); } - private static void importMapOSM(GraphDatabaseService db) throws Exception { + private void importMapOSM() throws Exception { + GraphDatabaseService db = graphDb(); // START SNIPPET: importOsm tag::importOsm[] OSMImporter importer = new OSMImporter("map.osm"); importer.setCharset(StandardCharsets.UTF_8); - importer.importFile(db, "map.osm"); + importer.importFile(db, "../server-plugin/map.osm"); importer.reIndex(db); // END SNIPPET: importOsm end::importOsm[] } @@ -144,16 +141,14 @@ private static void importMapOSM(GraphDatabaseService db) throws Exception { */ @Test public void testImportOSM() throws Exception { - LOGGER.fine("\n=== Simple test map.osm ==="); - importMapOSM(graphDb()); GraphDatabaseService database = graphDb(); + importMapOSM(); // START SNIPPET: searchBBox tag::searchBBox[] SpatialDatabaseService spatial = new SpatialDatabaseService( - new IndexManagerImpl((GraphDatabaseAPI) graphDb(), SecurityContext.AUTH_DISABLED)); + new IndexManagerImpl((GraphDatabaseAPI) database, SecurityContext.AUTH_DISABLED)); try (Transaction tx = database.beginTx()) { Layer layer = spatial.getLayer(tx, "map.osm", true); SpatialIndexReader spatialIndex = layer.getIndex(); - LOGGER.fine("Have " + spatialIndex.count(tx) + " geometries in " + spatialIndex.getBoundingBox(tx)); Envelope bbox = new Envelope(12.94, 12.96, 56.04, 56.06); List results = GeoPipeline @@ -170,12 +165,11 @@ public void testImportOSM() throws Exception { @Test public void testImportShapefile() throws Exception { - LOGGER.fine("\n=== Test Import Shapefile ==="); GraphDatabaseService database = graphDb(); // START SNIPPET: importShapefile tag::importShapefile[] ShapefileImporter importer = new ShapefileImporter(database); - importer.importFile("shp/highway.shp", "highway", StandardCharsets.UTF_8); + importer.importFile("../example-data/shp/highway.shp", "highway", StandardCharsets.UTF_8); // END SNIPPET: importShapefile end::importShapefile[] checkIndexAndFeatureCount("highway"); @@ -183,12 +177,11 @@ public void testImportShapefile() throws Exception { @Test public void testExportShapefileFromOSM() throws Exception { - LOGGER.fine("\n=== Test import map.osm, create DynamicLayer and export shapefile ==="); - importMapOSM(graphDb()); + importMapOSM(); GraphDatabaseService database = graphDb(); // START SNIPPET: exportShapefileFromOSM tag::exportShapefileFromOSM[] SpatialDatabaseService spatial = new SpatialDatabaseService( - new IndexManagerImpl((GraphDatabaseAPI) graphDb(), SecurityContext.AUTH_DISABLED)); + new IndexManagerImpl((GraphDatabaseAPI) database, SecurityContext.AUTH_DISABLED)); String wayLayerName; try (Transaction tx = database.beginTx()) { OSMLayer layer = (OSMLayer) spatial.getLayer(tx, "map.osm", false); @@ -204,23 +197,18 @@ public void testExportShapefileFromOSM() throws Exception { @Test public void testExportShapefileFromQuery() throws Exception { - LOGGER.fine("\n=== Test import map.osm, create DynamicLayer and export shapefile ==="); - importMapOSM(graphDb()); + importMapOSM(); GraphDatabaseService database = graphDb(); // START SNIPPET: exportShapefileFromQuery tag::exportShapefileFromQuery[] SpatialDatabaseService spatial = new SpatialDatabaseService( - new IndexManagerImpl((GraphDatabaseAPI) graphDb(), SecurityContext.AUTH_DISABLED)); + new IndexManagerImpl((GraphDatabaseAPI) database, SecurityContext.AUTH_DISABLED)); Envelope bbox = new Envelope(12.94, 12.96, 56.04, 56.06); List results; try (Transaction tx = database.beginTx()) { Layer layer = spatial.getLayer(tx, "map.osm", true); - SpatialIndexReader spatialIndex = layer.getIndex(); - LOGGER.fine("Have " + spatialIndex.count(tx) + " geometries in " + spatialIndex.getBoundingBox(tx)); - results = GeoPipeline .startIntersectWindowSearch(tx, layer, bbox) .toSpatialDatabaseRecordList(); - spatial.createResultsLayer(tx, "results", results); tx.commit(); @@ -232,9 +220,7 @@ public void testExportShapefileFromQuery() throws Exception { } private static void doGeometryTestsOnResults(Envelope bbox, List results) { - LOGGER.fine("Found " + results.size() + " geometries in " + bbox); - Geometry geometry = results.get(0).getGeometry(); - LOGGER.fine("First geometry is " + geometry); + Geometry geometry = results.getFirst().getGeometry(); geometry.buffer(2); } diff --git a/docs/docs/modules/ROOT/examples/GeoPipesDocTest.java b/docs/docs/modules/ROOT/examples/GeoPipesDocTest.java index 9aa61b6dd..68b1510dc 120000 --- a/docs/docs/modules/ROOT/examples/GeoPipesDocTest.java +++ b/docs/docs/modules/ROOT/examples/GeoPipesDocTest.java @@ -1 +1 @@ -../../../../../server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesDocTest.java \ No newline at end of file +../../../../../doc-examples/src/test/java/org/neo4j/spatial/doc/examples/GeoPipesDocTest.java \ No newline at end of file diff --git a/docs/docs/modules/ROOT/examples/TestsForDocs.java b/docs/docs/modules/ROOT/examples/TestsForDocs.java index a49dfcc63..f5753bd8f 120000 --- a/docs/docs/modules/ROOT/examples/TestsForDocs.java +++ b/docs/docs/modules/ROOT/examples/TestsForDocs.java @@ -1 +1 @@ -../../../../../server-plugin/src/test/java/org/neo4j/gis/spatial/TestsForDocs.java \ No newline at end of file +../../../../../doc-examples/src/test/java/org/neo4j/spatial/doc/examples/TestsForDocs.java \ No newline at end of file diff --git a/docs/docs/modules/ROOT/partials/generated/api-nav.adoc b/docs/docs/modules/ROOT/partials/generated/api-nav.adoc index 5ebdcb397..601e0936e 100644 --- a/docs/docs/modules/ROOT/partials/generated/api-nav.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api-nav.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually ** xref:api/spatial/spatial.addLayer.adoc[] ** xref:api/spatial/spatial.addLayerWithEncoder.adoc[] ** xref:api/spatial/spatial.addNativePointLayer.adoc[] diff --git a/docs/docs/modules/ROOT/partials/generated/api/index.adoc b/docs/docs/modules/ROOT/partials/generated/api/index.adoc index 9456748a7..310a62c80 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/index.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/index.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually == spatial [.procedures,opts=header,cols='5a,1a'] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial.addNode/spatial.addNode.byId.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial.addNode/spatial.addNode.byId.adoc index cabe95f87..73f8aea70 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial.addNode/spatial.addNode.byId.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial.addNode/spatial.addNode.byId.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNode.byId :description: This section contains reference documentation for the spatial.addNode.byId procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial.addNodes/spatial.addNodes.byId.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial.addNodes/spatial.addNodes.byId.adoc index cb1c63532..1a19ed2a9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial.addNodes/spatial.addNodes.byId.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial.addNodes/spatial.addNodes.byId.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNodes.byId :description: This section contains reference documentation for the spatial.addNodes.byId procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId-examples.adoc index bd5343e5e..82a300e79 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a point layer with X and Y properties [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId.adoc index 17561d32e..c64365c27 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNode/spatial.removeNode.byId.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.removeNode.byId :description: This section contains reference documentation for the spatial.removeNode.byId procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNodes/spatial.removeNodes.byId.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNodes/spatial.removeNodes.byId.adoc index 9f22adf8f..e2557d87a 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNodes/spatial.removeNodes.byId.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial.removeNodes/spatial.removeNodes.byId.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.removeNodes.byId :description: This section contains reference documentation for the spatial.removeNodes.byId procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer-examples.adoc index 9d87e8b3f..ae55fd4b7 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Add the same node to multiple layers .Create some nodes diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer.adoc index ed9ec86d4..8c6a6bbef 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addLayer :description: This section contains reference documentation for the spatial.addLayer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder-examples.adoc index 7d0e674e7..3fdadb111 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a `NativePointEncoder` [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder.adoc index 0acb5bc3b..62265fa3b 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addLayerWithEncoder.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addLayerWithEncoder :description: This section contains reference documentation for the spatial.addLayerWithEncoder procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer-examples.adoc index 699fc79c1..b99b2f25e 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer.adoc index c5bcf684d..0b920b2b9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNativePointLayer :description: This section contains reference documentation for the spatial.addNativePointLayer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash-examples.adoc index 5ed228865..1ae3ed650 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] @@ -46,7 +47,7 @@ MATCH (n:Node) WITH n CALL spatial.addNode('my-native-geohash-layer',n) YIELD no a| [source] ---- -(:SpatialIndex_geohash_my-native-geohash-layer:Node { +(:Node:SpatialIndex_geohash_my-native-geohash-layer { _spatialindex_geohash_my-native-geohash-layer: "1110001001100110011110101101011110001011101011010001000100011101", bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, @@ -79,7 +80,7 @@ a| a| [source] ---- -(:SpatialIndex_geohash_my-native-geohash-layer:Node { +(:Node:SpatialIndex_geohash_my-native-geohash-layer { _spatialindex_geohash_my-native-geohash-layer: "1110001001100110011110101101011110001011101011010001000100011101", bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash.adoc index 7b4c099f9..3c2b304a0 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerGeohash.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNativePointLayerGeohash :description: This section contains reference documentation for the spatial.addNativePointLayerGeohash procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert-examples.adoc index eb5640207..c17004680 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] @@ -46,7 +47,7 @@ MATCH (n:Node) WITH n CALL spatial.addNode('my-native-hilbert-layer',n) YIELD no a| [source] ---- -(:SpatialIndex_hilbert_my-native-hilbert-layer:Node { +(:Node:SpatialIndex_hilbert_my-native-hilbert-layer { _spatialindex_hilbert_my-native-hilbert-layer: 9664325, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, @@ -79,7 +80,7 @@ a| a| [source] ---- -(:SpatialIndex_hilbert_my-native-hilbert-layer:Node { +(:Node:SpatialIndex_hilbert_my-native-hilbert-layer { _spatialindex_hilbert_my-native-hilbert-layer: 9664325, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert.adoc index 88ea41828..08def74f9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerHilbert.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNativePointLayerHilbert :description: This section contains reference documentation for the spatial.addNativePointLayerHilbert procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig-examples.adoc index fc53b570f..4bc1fa02c 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a native point layer with a configuration [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig.adoc index e06473ccb..853ca48e6 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerWithConfig.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNativePointLayerWithConfig :description: This section contains reference documentation for the spatial.addNativePointLayerWithConfig procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY-examples.adoc index e16b7d4fd..af78083b7 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a native point layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY.adoc index 63827fd4b..716742df8 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerXY.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNativePointLayerXY :description: This section contains reference documentation for the spatial.addNativePointLayerXY procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder-examples.adoc index 4c52d2fd8..3f3680564 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] @@ -46,7 +47,7 @@ MATCH (n:Node) WITH n CALL spatial.addNode('my-native-zorder-layer',n) YIELD nod a| [source] ---- -(:SpatialIndex_zorder_my-native-zorder-layer:Node { +(:Node:SpatialIndex_zorder_my-native-zorder-layer { _spatialindex_zorder_my-native-zorder-layer: 4771024, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, @@ -79,7 +80,7 @@ a| a| [source] ---- -(:SpatialIndex_zorder_my-native-zorder-layer:Node { +(:Node:SpatialIndex_zorder_my-native-zorder-layer { _spatialindex_zorder_my-native-zorder-layer: 4771024, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder.adoc index 66574901c..2461f9bfe 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNativePointLayerZOrder.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNativePointLayerZOrder :description: This section contains reference documentation for the spatial.addNativePointLayerZOrder procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode-examples.adoc index e16b7d4fd..af78083b7 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a native point layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode.adoc index ae6bd9276..dbb7875e6 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNode.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNode :description: This section contains reference documentation for the spatial.addNode procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes-examples.adoc index bd5343e5e..82a300e79 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a point layer with X and Y properties [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes.adoc index d0d719aec..9b80145d0 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addNodes.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addNodes :description: This section contains reference documentation for the spatial.addNodes procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer-examples.adoc index 7f17b4504..bb6c6f0c2 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer.adoc index 810cde14d..0dea287b2 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addPointLayer :description: This section contains reference documentation for the spatial.addPointLayer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash-examples.adoc index 4ed79cbe4..aafca7e1a 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] @@ -46,7 +47,7 @@ MATCH (n:Node) WITH n CALL spatial.addNode('my-simple-geohash-layer',n) YIELD no a| [source] ---- -(:SpatialIndex_geohash_my-simple-geohash-layer:Node { +(:Node:SpatialIndex_geohash_my-simple-geohash-layer { _spatialindex_geohash_my-simple-geohash-layer: "1110001001100110011110101101011110001011101011010001000100011101", bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, @@ -79,7 +80,7 @@ a| a| [source] ---- -(:SpatialIndex_geohash_my-simple-geohash-layer:Node { +(:Node:SpatialIndex_geohash_my-simple-geohash-layer { _spatialindex_geohash_my-simple-geohash-layer: "1110001001100110011110101101011110001011101011010001000100011101", bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash.adoc index b07ec9aff..1ac7af923 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerGeohash.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addPointLayerGeohash :description: This section contains reference documentation for the spatial.addPointLayerGeohash procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert-examples.adoc index 6a44062b7..75839ba5e 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] @@ -46,7 +47,7 @@ MATCH (n:Node) WITH n CALL spatial.addNode('my-simple-hilbert-layer',n) YIELD no a| [source] ---- -(:SpatialIndex_hilbert_my-simple-hilbert-layer:Node { +(:Node:SpatialIndex_hilbert_my-simple-hilbert-layer { _spatialindex_hilbert_my-simple-hilbert-layer: 9664325, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, @@ -79,7 +80,7 @@ a| a| [source] ---- -(:SpatialIndex_hilbert_my-simple-hilbert-layer:Node { +(:Node:SpatialIndex_hilbert_my-simple-hilbert-layer { _spatialindex_hilbert_my-simple-hilbert-layer: 9664325, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert.adoc index 763139dcf..f054456d9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerHilbert.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addPointLayerHilbert :description: This section contains reference documentation for the spatial.addPointLayerHilbert procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig-examples.adoc index 56cfb12f4..bd77550b5 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a point layer with X and Y properties [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig.adoc index df7fc84e7..7b8f2cff4 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerWithConfig.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addPointLayerWithConfig :description: This section contains reference documentation for the spatial.addPointLayerWithConfig procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY-examples.adoc index bd5343e5e..82a300e79 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a point layer with X and Y properties [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY.adoc index 8494f6cb3..de51a9872 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerXY.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addPointLayerXY :description: This section contains reference documentation for the spatial.addPointLayerXY procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder-examples.adoc index 63156106e..fda754a6b 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a layer to index a node [source,cypher] @@ -46,7 +47,7 @@ MATCH (n:Node) WITH n CALL spatial.addNode('my-simple-zorder-layer',n) YIELD nod a| [source] ---- -(:SpatialIndex_zorder_my-simple-zorder-layer:Node { +(:Node:SpatialIndex_zorder_my-simple-zorder-layer { _spatialindex_zorder_my-simple-zorder-layer: 4771024, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, @@ -79,7 +80,7 @@ a| a| [source] ---- -(:SpatialIndex_zorder_my-simple-zorder-layer:Node { +(:Node:SpatialIndex_zorder_my-simple-zorder-layer { _spatialindex_zorder_my-simple-zorder-layer: 4771024, bbox: [ 15.2, 60.1, 15.2, 60.1 ], gtype: 1, diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder.adoc index ddd87877d..5b47819ef 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addPointLayerZOrder.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addPointLayerZOrder :description: This section contains reference documentation for the spatial.addPointLayerZOrder procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT-examples.adoc index e64394acd..b7e29a080 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Add a WKT geometry to a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT.adoc index 8dd533186..7672ca105 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKT.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addWKT :description: This section contains reference documentation for the spatial.addWKT procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer-examples.adoc index 96ef838cf..7aa29540d 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Add a WKT geometry to a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer.adoc index f20c62840..966a162e5 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTLayer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addWKTLayer :description: This section contains reference documentation for the spatial.addWKTLayer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs-examples.adoc index 4dc7408a6..e380842d5 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Add multiple WKT geometries to a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs.adoc index 6012da5f0..32e13175d 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.addWKTs.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.addWKTs :description: This section contains reference documentation for the spatial.addWKTs procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asExternalGeometry.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asExternalGeometry.adoc index a00b068ad..4dafb3c25 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asExternalGeometry.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asExternalGeometry.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.asExternalGeometry :description: This section contains reference documentation for the spatial.asExternalGeometry procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry-examples.adoc index ad24ae6a5..5010cb765 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Creates a point geometry [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry.adoc index da12e4474..9ef37c6b2 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asGeometry.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.asGeometry :description: This section contains reference documentation for the spatial.asGeometry function. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap-examples.adoc index 0b0be6ffd..d42b9b529 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Creates a point geometry as map [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap.adoc index 377105e33..1bd83ea5c 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.asMap.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.asMap :description: This section contains reference documentation for the spatial.asMap function. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox-examples.adoc index dc6ee537e..dd1b6bbef 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Find geometries in a bounding box [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox.adoc index 9facb93f5..8065b3936 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.bbox.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.bbox :description: This section contains reference documentation for the spatial.bbox procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest-examples.adoc index 4dc7408a6..e380842d5 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Add multiple WKT geometries to a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest.adoc index 18586ab46..f24d571a8 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.closest.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.closest :description: This section contains reference documentation for the spatial.closest procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql-examples.adoc index 96a148482..118e3208f 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Find geometries using CQL [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql.adoc index 77232eb68..fec56cac9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.cql.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.cql :description: This section contains reference documentation for the spatial.cql procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry-examples.adoc index a58ee4e36..37e0df311 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Decode a geometry from a node property .Create a WKT layer @@ -39,3 +40,35 @@ CREATE (n:Node {geom:'POINT(4.0 5.0)'}) RETURN spatial.decodeGeometry('geom',n) |point({x: 4.0, y: 5.0, crs: 'cartesian'}) |=== +=== Using both functions together + +[source,cypher] +---- +CALL spatial.addPointLayer('combined_layer') YIELD node +WITH node +CREATE (n:Point {longitude: 5.5, latitude: 45.5, city: 'TestCity', population: 50000}) +WITH n +CALL spatial.addNode('combined_layer', n) YIELD node as added_node +WITH n +RETURN + spatial.decodeGeometry('combined_layer', n) as geometry, + spatial.extractAttributes('combined_layer', n) as attributes + +---- + +.Result + +[opts="header",cols="2"] +|=== +|attributes|geometry +a| +[source] +---- +{ + "city" : "TestCity", + "population" : 50000 +} +---- +|point({x: 5.5, y: 45.5, crs: 'cartesian'}) +|=== + diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry.adoc index 273e5f12c..9558b856a 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.decodeGeometry.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.decodeGeometry :description: This section contains reference documentation for the spatial.decodeGeometry function. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes-examples.adoc index f37c48d0c..b27b33bdc 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Extracts attributes from a WKT layer node [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes.adoc index abf10597a..1e5c83004 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.extractAttributes.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.extractAttributes :description: This section contains reference documentation for the spatial.extractAttributes function. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes-examples.adoc index e88e8246b..e90047e14 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Get the feature attributes of a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes.adoc index f58c997db..f95c74b93 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureAttributes.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.getFeatureAttributes :description: This section contains reference documentation for the spatial.getFeatureAttributes procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount-examples.adoc index 348f4b2a6..3b31bc333 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Count features in different layer types Create a point layer diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount.adoc index bf6babd1f..8d6da54d6 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getFeatureCount.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.getFeatureCount :description: This section contains reference documentation for the spatial.getFeatureCount procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox-examples.adoc index 3b93135f0..416893303 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Get the bounding box of a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox.adoc index 781677c77..8f02914d0 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.getLayerBoundingBox.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.getLayerBoundingBox :description: This section contains reference documentation for the spatial.getLayerBoundingBox procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM-examples.adoc index 73ede9744..303ea0d2c 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM-examples.adoc @@ -1,8 +1,9 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Import an OSM file [source,cypher] ---- -CALL spatial.importOSM('map.osm') +CALL spatial.importOSM('example-data/osm/example.osm') ---- .Result @@ -13,7 +14,7 @@ CALL spatial.importOSM('map.osm') a| [source] ---- -55 +2 ---- |=== @@ -28,6 +29,6 @@ CALL spatial.layers() [opts="header",cols="2"] |=== |name|signature -|map.osm|EditableLayer(name='map.osm', encoder=GeometryEncoder(bbox='bbox')) +|example.osm|EditableLayer(name='example.osm', encoder=GeometryEncoder(bbox='bbox')) |=== diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM.adoc index 0ec785d61..2ec4cc058 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSM.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.importOSM :description: This section contains reference documentation for the spatial.importOSM procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer-examples.adoc index 8c2e5ab2b..c25fa56fa 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Import an OSM file [source,cypher] @@ -7,7 +8,7 @@ CALL spatial.addLayer('geom','OSM','') [source,cypher] ---- -CALL spatial.importOSMToLayer('geom','map.osm') +CALL spatial.importOSMToLayer('geom','example-data/osm/example.osm') ---- .Result @@ -18,7 +19,7 @@ CALL spatial.importOSMToLayer('geom','map.osm') a| [source] ---- -55 +2 ---- |=== diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer.adoc index 9c208b55e..22319d552 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importOSMToLayer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.importOSMToLayer :description: This section contains reference documentation for the spatial.importOSMToLayer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile-examples.adoc index 6b057836c..bd3e09dec 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile-examples.adoc @@ -1,8 +1,9 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Import a shape-file [source,cypher] ---- -CALL spatial.importShapefile('shp/highway.shp') +CALL spatial.importShapefile('example-data/shp/highway.shp') ---- .Result diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile.adoc index 07f7f5f84..97db112d2 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefile.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.importShapefile :description: This section contains reference documentation for the spatial.importShapefile procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer-examples.adoc index f62f672bf..84bced4ca 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Import a shape-file [source,cypher] @@ -7,7 +8,7 @@ CALL spatial.addWKTLayer('geom','wkt') [source,cypher] ---- -CALL spatial.importShapefileToLayer('geom', 'shp/highway.shp') +CALL spatial.importShapefileToLayer('geom', 'example-data/shp/highway.shp') ---- .Result diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer.adoc index 55b3de70c..4f48a3d0c 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.importShapefileToLayer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.importShapefileToLayer :description: This section contains reference documentation for the spatial.importShapefileToLayer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects-examples.adoc index 3a434866c..d5850a607 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Find geometries in a polygon [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects.adoc index 42728c463..737c117c9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.intersects.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.intersects :description: This section contains reference documentation for the spatial.intersects procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layer.adoc index c66e737a0..1d6f1f7a8 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.layer :description: This section contains reference documentation for the spatial.layer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta-examples.adoc index f6789bbb4..682e09412 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Get metadata about a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta.adoc index c9476ea79..429c145c1 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerMeta.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.layerMeta :description: This section contains reference documentation for the spatial.layerMeta procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes-examples.adoc index 3d1ffd5c0..0eb1739ee 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === List the available layer types [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes.adoc index 8924bf1ee..176e31ed6 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layerTypes.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.layerTypes :description: This section contains reference documentation for the spatial.layerTypes procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers-examples.adoc index 21d0514fb..0401d6727 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Add and Remove a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers.adoc index 114e0099f..808a30b9b 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.layers.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.layers :description: This section contains reference documentation for the spatial.layers procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt-examples.adoc index 21cee40ca..6cf58123c 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Converting a point array to WKT [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt.adoc index 4ef4e5e3a..4ed687721 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.neo4jGeometryToWkt.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.neo4jGeometryToWkt :description: This section contains reference documentation for the spatial.neo4jGeometryToWkt function. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT-examples.adoc index db65017fb..dfd393a9e 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Converting a layer node to WKT [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT.adoc index e7266fee0..e8c34c98d 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.nodeAsWKT.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.nodeAsWKT :description: This section contains reference documentation for the spatial.nodeAsWKT function. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.procedures.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.procedures.adoc index 0a4ae01cd..6ae7eda33 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.procedures.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.procedures.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.procedures :description: This section contains reference documentation for the spatial.procedures procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer-examples.adoc index 21d0514fb..0401d6727 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Add and Remove a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer.adoc index d33936eaa..882608cba 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeLayer.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.removeLayer :description: This section contains reference documentation for the spatial.removeLayer procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode-examples.adoc index bd5343e5e..82a300e79 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a point layer with X and Y properties [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode.adoc index f5767bca5..471a1f4c9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNode.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.removeNode :description: This section contains reference documentation for the spatial.removeNode procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNodes.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNodes.adoc index 706f70066..cca049fdf 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNodes.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.removeNodes.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.removeNodes :description: This section contains reference documentation for the spatial.removeNodes procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes-examples.adoc index e88e8246b..e90047e14 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Get the feature attributes of a layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes.adoc index 008bee17a..fba5c8172 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.setFeatureAttributes.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.setFeatureAttributes :description: This section contains reference documentation for the spatial.setFeatureAttributes procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT-examples.adoc index 2db1c5a07..28c1ec147 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Update a node's WKT geometry [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT.adoc index 9c12639dd..db12fc867 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.updateWKT.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.updateWKT :description: This section contains reference documentation for the spatial.updateWKT procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.upgrade.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.upgrade.adoc index 7a7f0e395..c4fcc697d 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.upgrade.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.upgrade.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.upgrade :description: This section contains reference documentation for the spatial.upgrade procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance-examples.adoc index e16b7d4fd..af78083b7 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === Create a native point layer [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance.adoc index 65b5563c4..7702eb35d 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.withinDistance.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.withinDistance :description: This section contains reference documentation for the spatial.withinDistance procedure. diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson-examples.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson-examples.adoc index 71b93e077..374a176e2 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson-examples.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson-examples.adoc @@ -1,3 +1,4 @@ +// This file is generated by org.neo4j.spatial.doc.examples.ApiExampleGenerator, do not edit it manually === 1. Converts a WKT POINT [source,cypher] diff --git a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson.adoc b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson.adoc index 300b15889..de83a7eb9 100644 --- a/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson.adoc +++ b/docs/docs/modules/ROOT/partials/generated/api/spatial/spatial.wktToGeoJson.adoc @@ -1,4 +1,4 @@ -// This file is generated by DocGeneratorTest, do not edit it manually +// This file is generated by org.neo4j.spatial.doc.examples.ApiDocGenerator, do not edit it manually = spatial.wktToGeoJson :description: This section contains reference documentation for the spatial.wktToGeoJson function. diff --git a/example-data/osm/example.osm b/example-data/osm/example.osm new file mode 100644 index 000000000..379a0e913 --- /dev/null +++ b/example-data/osm/example.osm @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server-plugin/shp/highway.dbf b/example-data/shp/highway.dbf similarity index 100% rename from server-plugin/shp/highway.dbf rename to example-data/shp/highway.dbf diff --git a/server-plugin/shp/highway.fix b/example-data/shp/highway.fix similarity index 100% rename from server-plugin/shp/highway.fix rename to example-data/shp/highway.fix diff --git a/server-plugin/shp/highway.prj b/example-data/shp/highway.prj similarity index 100% rename from server-plugin/shp/highway.prj rename to example-data/shp/highway.prj diff --git a/server-plugin/shp/highway.qix b/example-data/shp/highway.qix similarity index 100% rename from server-plugin/shp/highway.qix rename to example-data/shp/highway.qix diff --git a/server-plugin/shp/highway.shp b/example-data/shp/highway.shp similarity index 100% rename from server-plugin/shp/highway.shp rename to example-data/shp/highway.shp diff --git a/server-plugin/shp/highway.shx b/example-data/shp/highway.shx similarity index 100% rename from server-plugin/shp/highway.shx rename to example-data/shp/highway.shx diff --git a/pom.xml b/pom.xml index 5faf7349e..50a5e3d1a 100644 --- a/pom.xml +++ b/pom.xml @@ -28,8 +28,10 @@ api geotools-common geotools-plugin + test-utils server-plugin cli-tools + doc-examples diff --git a/server-plugin/pom.xml b/server-plugin/pom.xml index f128192d7..7550403e8 100644 --- a/server-plugin/pom.xml +++ b/server-plugin/pom.xml @@ -146,6 +146,12 @@ ${spatial.test.osm.version} test + + org.neo4j + neo4j-spatial-test-utils + 2025.10.1-SNAPSHOT + test + org.neo4j.spatial shp-test-data diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java index 23f8d9e02..908d92197 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/LayerSignatureTest.java @@ -21,20 +21,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.function.Consumer; +import java.util.List; import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.layer.Layer; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class LayerSignatureTest extends Neo4jTestCase implements Constants { private SpatialDatabaseService spatial; + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @BeforeEach public void setup() throws Exception { spatial = new SpatialDatabaseService( @@ -89,13 +97,6 @@ private Layer testLayerSignature(String signature, Function return layer; } - private void inTx(Consumer txFunction) { - try (Transaction tx = graphDb().beginTx()) { - txFunction.accept(tx); - tx.commit(); - } - } - @Test public void testDynamicLayer() { Layer layer = testLayerSignature( diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/LayersTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/LayersTest.java index e24386fde..9c350e9b5 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/LayersTest.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/LayersTest.java @@ -29,7 +29,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; -import java.util.function.Consumer; import java.util.logging.Logger; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; @@ -40,12 +39,14 @@ import org.neo4j.gis.spatial.encoders.SimpleGraphEncoder; import org.neo4j.gis.spatial.encoders.SimplePointEncoder; import org.neo4j.gis.spatial.encoders.SimplePropertyEncoder; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.index.LayerGeohashPointIndex; import org.neo4j.gis.spatial.index.LayerRTreeIndex; import org.neo4j.gis.spatial.osm.OSMGeometryEncoder; import org.neo4j.gis.spatial.osm.OSMLayer; import org.neo4j.gis.spatial.pipes.GeoPipeline; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.gis.spatial.rtree.ProgressLoggingListener; import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Node; @@ -60,11 +61,17 @@ import org.neo4j.spatial.api.layer.EditableLayer; import org.neo4j.spatial.api.layer.Layer; import org.neo4j.spatial.cli.tools.ShapefileExporter; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class LayersTest extends Neo4jTestCase { private static final Logger LOGGER = Logger.getLogger(LayersTest.class.getName()); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @Test public void testBasicLayerOperations() { String layerName = "test"; @@ -380,11 +387,4 @@ public void testIndexAccessAfterBulkInsertion() { tx.commit(); } } - - private void inTx(Consumer txFunction) { - try (Transaction tx = graphDb().beginTx()) { - txFunction.accept(tx); - tx.commit(); - } - } } diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestUtils.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestUtils.java deleted file mode 100644 index 4e44b7169..000000000 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestUtils.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j Spatial. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.gis.spatial; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import org.neo4j.gis.spatial.index.IndexManagerImpl; -import org.neo4j.gis.spatial.rtree.RTreeIndex; -import org.neo4j.gis.spatial.rtree.RTreeRelationshipTypes; -import org.neo4j.graphdb.Direction; -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.Relationship; -import org.neo4j.graphdb.Transaction; -import org.neo4j.internal.kernel.api.security.SecurityContext; -import org.neo4j.kernel.internal.GraphDatabaseAPI; -import org.neo4j.spatial.api.layer.Layer; - -public class Neo4jTestUtils { - - public static int countIterable(Iterable iterable) { - int counter = 0; - Iterator itr = iterable.iterator(); - while (itr.hasNext()) { - itr.next(); - counter++; - } - return counter; - } - - public static void debugIndexTree(GraphDatabaseService db, String layerName) { - try (Transaction tx = db.beginTx()) { - SpatialDatabaseService spatial = new SpatialDatabaseService( - new IndexManagerImpl((GraphDatabaseAPI) db, SecurityContext.AUTH_DISABLED)); - Layer layer = spatial.getLayer(tx, layerName, true); - RTreeIndex index = (RTreeIndex) layer.getIndex(); - printTree(index.getIndexRoot(tx), 0); - tx.commit(); - } - - } - - private static String arrayString(double[] test) { - StringBuffer sb = new StringBuffer(); - for (double d : test) { - addToArrayString(sb, d); - } - sb.append("]"); - return sb.toString(); - } - - private static void addToArrayString(StringBuffer sb, Object obj) { - if (sb.length() == 0) { - sb.append("["); - } else { - sb.append(","); - } - sb.append(obj); - } - - public static void printTree(Node root, int depth) { - StringBuffer tab = new StringBuffer(); - for (int i = 0; i < depth; i++) { - tab.append(" "); - } - - if (root.hasProperty(Constants.PROP_BBOX)) { - System.out.println(tab + "INDEX: " + root + " BBOX[" + arrayString( - (double[]) root.getProperty(Constants.PROP_BBOX)) + "]"); - } else { - System.out.println(tab + "INDEX: " + root); - } - - StringBuffer data = new StringBuffer(); - for (Relationship rel : root.getRelationships(Direction.OUTGOING, RTreeRelationshipTypes.RTREE_REFERENCE)) { - if (data.length() > 0) { - data.append(", "); - } else { - data.append("DATA: "); - } -// data.append(rel.getEndNode().toString()); - data.append(rel.getEndNode().toString() + " BBOX[" + arrayString((double[]) rel.getEndNode().getProperty - (Constants - .PROP_BBOX)) + "]"); - } - - if (data.length() > 0) { - System.out.println(" " + tab + data); - } - - for (Relationship rel : root.getRelationships(Direction.OUTGOING, RTreeRelationshipTypes.RTREE_CHILD)) { - printTree(rel.getEndNode(), depth + 1); - } - } - - public static void assertCollection(Collection collection, T... expectedItems) { - String collectionString = join(", ", collection.toArray()); - assertEquals(expectedItems.length, collection.size(), collectionString); - for (T item : expectedItems) { - assertTrue(collection.contains(item)); - } - } - - public static Collection asCollection(Iterable iterable) { - List list = new ArrayList(); - for (T item : iterable) { - list.add(item); - } - return list; - } - - private static String join(String delimiter, T... items) { - StringBuffer buffer = new StringBuffer(); - for (T item : items) { - if (buffer.length() > 0) { - buffer.append(delimiter); - } - buffer.append(item.toString()); - } - return buffer.toString(); - } - - public static void printDatabaseStats(GraphDatabaseService db, File path) { - System.out.println("Database stats:"); - System.out.println("\tTotal disk usage: " + (databaseDiskUsage(path)) / (1024.0 * 1024.0) + "MB"); - System.out.println("\tTotal # nodes: " + getNumberOfNodes(db)); - System.out.println("\tTotal # rels: " + getNumberOfRelationships(db)); - } - - private static long calculateDiskUsage(File file) { - if (file.isDirectory()) { - long count = 0; - for (File sub : file.listFiles()) { - count += calculateDiskUsage(sub); - } - return count; - } - return file.length(); - } - - private static long databaseDiskUsage(File path) { - return calculateDiskUsage(path); - } - - private static long getNumberOfNodes(GraphDatabaseService db) { - try (Transaction tx = db.beginTx()) { - return (Long) tx.execute("MATCH (n) RETURN count(n)").columnAs("count(n)").next(); - } - } - - private static long getNumberOfRelationships(GraphDatabaseService db) { - try (Transaction tx = db.beginTx()) { - return (Long) tx.execute("MATCH ()-[r]->() RETURN count(r)").columnAs("count(r)").next(); - } - } -} diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestDynamicLayers.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestDynamicLayers.java index 6df5fc3e3..63304e06a 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestDynamicLayers.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestDynamicLayers.java @@ -23,10 +23,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME; -import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.List; import org.geotools.api.data.DataSourceException; import org.geotools.api.data.DataStore; import org.geotools.data.simple.SimpleFeatureCollection; @@ -34,9 +34,11 @@ import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.Polygon; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.osm.OSMImporter; import org.neo4j.gis.spatial.osm.OSMLayer; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.gis.spatial.rtree.NullListener; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; @@ -46,9 +48,15 @@ import org.neo4j.spatial.cli.tools.ShapefileExporter; import org.neo4j.spatial.cli.tools.StyledImageExporter; import org.neo4j.spatial.geotools.plugin.Neo4jSpatialDataStore; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class TestDynamicLayers extends Neo4jTestCase implements Constants { + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @Test public void testShapefileExport_Map1() throws Exception { runShapefileExport("map.osm"); @@ -61,14 +69,14 @@ public void testShapefileExport_Map2() throws Exception { @Test public void testImageExport_HighwayShp() throws Exception { - runDynamicShapefile("highway.shp"); + runDynamicShapefile("../example-data/shp/highway.shp", "highway.shp"); } @SuppressWarnings("SameParameterValue") - private void runDynamicShapefile(String shpFile) throws Exception { + private void runDynamicShapefile(String shpFile, String layerName) throws Exception { printDatabaseStats(); - loadTestShpData(shpFile); - checkLayer(shpFile); + loadTestShpData(layerName, shpFile); + checkLayer(layerName); printDatabaseStats(); // Define dynamic layers @@ -76,7 +84,7 @@ private void runDynamicShapefile(String shpFile) throws Exception { try (Transaction tx = graphDb().beginTx()) { SpatialDatabaseService spatial = new SpatialDatabaseService( new IndexManagerImpl((GraphDatabaseAPI) graphDb(), SecurityContext.AUTH_DISABLED)); - DynamicLayer shpLayer = spatial.asDynamicLayer(tx, spatial.getLayer(tx, shpFile, false)); + DynamicLayer shpLayer = spatial.asDynamicLayer(tx, spatial.getLayer(tx, layerName, false)); layers.add(shpLayer.addLayerConfig(tx, "CQL0-highway", GTYPE_GEOMETRY, "highway is not null")); layers.add(shpLayer.addLayerConfig(tx, "CQL1-highway", GTYPE_POINT, "geometryType(the_geom) = 'MultiLineString'")); @@ -101,7 +109,7 @@ private void runDynamicShapefile(String shpFile) throws Exception { // Now export the layers to files // First prepare the SHP and PNG exporters StyledImageExporter imageExporter = new StyledImageExporter(driver, DEFAULT_DATABASE_NAME); - imageExporter.setExportDir("target/export/" + shpFile); + imageExporter.setExportDir("target/export/" + layerName); imageExporter.setZoom(3.0); imageExporter.setOffset(-0.05, -0.05); imageExporter.setSize(1024, 768); @@ -279,11 +287,10 @@ private void loadTestOsmData(String layerName) throws Exception { importer.reIndex(graphDb(), 1000); } - private void loadTestShpData(String layerName) throws IOException { - String shpPath = "shp" + File.separator + layerName; - System.out.println("\n=== Loading layer " + layerName + " from " + shpPath + " ==="); + private void loadTestShpData(String layerName, String file) throws IOException { + System.out.println("\n=== Loading layer " + layerName + " from " + file + " ==="); ShapefileImporter importer = new ShapefileImporter(graphDb(), new NullListener(), 1000); - importer.importFile(shpPath, layerName, StandardCharsets.UTF_8); + importer.importFile(file, layerName, StandardCharsets.UTF_8); } private Envelope checkLayer(String layerName) { @@ -300,7 +307,6 @@ private Envelope checkLayer(String layerName) { } assertNotNull(bbox, "Layer index envelope should not be null"); System.out.println("Layer has bounding box: " + bbox); - Neo4jTestUtils.debugIndexTree(graphDb(), layerName); return bbox; } } diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestIntersectsPathQueries.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestIntersectsPathQueries.java index 6cb48eafb..6f4aee576 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestIntersectsPathQueries.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestIntersectsPathQueries.java @@ -46,10 +46,12 @@ import org.locationtech.jts.simplify.TopologyPreservingSimplifier; import org.neo4j.dbms.api.DatabaseManagementService; import org.neo4j.dbms.api.DatabaseManagementServiceBuilder; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.osm.OSMImporter; import org.neo4j.gis.spatial.pipes.GeoPipeline; import org.neo4j.gis.spatial.pipes.processing.OrthodromicDistance; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; @@ -57,11 +59,18 @@ import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.layer.Layer; +import org.neo4j.spatial.testutils.Neo4jTestCase; +import org.neo4j.spatial.testutils.SpatialTestUtils; public class TestIntersectsPathQueries extends Neo4jTestCase { private static final Logger LOGGER = Logger.getLogger(TestIntersectsPathQueries.class.getName()); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + /** * This test case is designed to capture the conditions described in the bug * report at https://github.com/neo4j/spatial/issues/112. The report claims @@ -238,8 +247,8 @@ private void runTestPointSetGeoptimaIntersection(String tracePath, String dbRoot assertNotNull(layer.getIndex(), "Layer index should not be null"); assertNotNull(layer.getIndex().getBoundingBox(tx), "Layer index envelope should not be null"); Envelope bbox = Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx)); - TestOSMImport.debugEnvelope(bbox, layerName, Constants.PROP_BBOX); - indexCount = TestOSMImport.checkIndexCount(tx, layer); + SpatialTestUtils.debugEnvelope(bbox, layerName, Constants.PROP_BBOX); + indexCount = SpatialTestUtils.checkIndexCount(tx, layer); tx.commit(); } TestOSMImport.checkFeatureCount(driver, indexCount, layerName); diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java index c03225fcf..9233fcf60 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestOSMImportBase.java @@ -34,6 +34,7 @@ import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; import org.neo4j.driver.Driver; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.osm.OSMDataset; import org.neo4j.gis.spatial.osm.OSMDataset.Way; @@ -42,6 +43,7 @@ import org.neo4j.gis.spatial.osm.OSMLayer; import org.neo4j.gis.spatial.osm.OSMRelation; import org.neo4j.gis.spatial.pipes.osm.OSMGeoPipeline; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; @@ -50,13 +52,19 @@ import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.SpatialRecord; -import org.neo4j.spatial.api.layer.Layer; import org.neo4j.spatial.geotools.plugin.Neo4jSpatialDataStore; +import org.neo4j.spatial.testutils.Neo4jTestCase; +import org.neo4j.spatial.testutils.SpatialTestUtils; public class TestOSMImportBase extends Neo4jTestCase { private static final Logger LOGGER = Logger.getLogger(TestOSMImportBase.class.getName()); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + protected static String checkOSMFile(String osm) { File osmFile = new File(osm); if (!osmFile.exists()) { @@ -79,9 +87,9 @@ protected static void checkOSMLayer(Driver driver, GraphDatabaseService db, Stri Assertions.assertNotNull(layer.getIndex().getBoundingBox(tx), "OSM Layer index envelope should not be null"); Envelope bbox = Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx)); - debugEnvelope(bbox, layerName, Constants.PROP_BBOX); + SpatialTestUtils.debugEnvelope(bbox, layerName, Constants.PROP_BBOX); // ((RTreeIndex)layer.getIndex()).debugIndexTree(); - indexCount = checkIndexCount(tx, layer); + indexCount = SpatialTestUtils.checkIndexCount(tx, layer); checkChangesetsAndUsers(tx, layer); checkOSMSearch(tx, layer); tx.commit(); @@ -131,21 +139,6 @@ private static void runWithinSearch(Transaction tx, OSMLayer layer, Geometry sea } } - public static void debugEnvelope(Envelope bbox, String layer, String name) { - System.out.println("Layer '" + layer + "' has envelope '" + name + "': " + bbox); - System.out.println("\tX: [" + bbox.getMinX() + ":" + bbox.getMaxX() + "]"); - System.out.println("\tY: [" + bbox.getMinY() + ":" + bbox.getMaxY() + "]"); - } - - public static int checkIndexCount(Transaction tx, Layer layer) { - if (layer.getIndex().count(tx) < 1) { - System.out.println("Warning: index count zero: " + layer.getName()); - } - System.out.println( - "Layer '" + layer.getName() + "' has " + layer.getIndex().count(tx) + " entries in the index"); - return layer.getIndex().count(tx); - } - public static void checkFeatureCount(Driver driver, int indexCount, String layerName) throws IOException { DataStore store = new Neo4jSpatialDataStore(driver, DEFAULT_DATABASE_NAME); SimpleFeatureCollection features = store.getFeatureSource(layerName).getFeatures(); diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestRemove.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestRemove.java index 85cf00e63..ead2ee31b 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestRemove.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestRemove.java @@ -19,20 +19,29 @@ */ package org.neo4j.gis.spatial; +import java.util.List; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.neo4j.gis.spatial.encoders.WKTGeometryEncoder; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.layer.EditableLayer; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class TestRemove extends Neo4jTestCase { private static final String layerName = "TestRemove"; + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @Test public void testAddMoreThanMaxNodeRefThenDeleteAll() { SpatialDatabaseService spatial = new SpatialDatabaseService( diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSimplePointLayer.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSimplePointLayer.java index 5c4caf8bb..dc2fc1d0d 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSimplePointLayer.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSimplePointLayer.java @@ -30,7 +30,6 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.CoordinateList; @@ -38,10 +37,12 @@ import org.locationtech.jts.geom.Point; import org.neo4j.gis.spatial.encoders.neo4j.Neo4jCRS; import org.neo4j.gis.spatial.encoders.neo4j.Neo4jPoint; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.pipes.GeoPipeFlow; import org.neo4j.gis.spatial.pipes.GeoPipeline; import org.neo4j.gis.spatial.pipes.processing.OrthodromicDistance; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; @@ -53,12 +54,18 @@ import org.neo4j.spatial.api.layer.Layer; import org.neo4j.spatial.cli.tools.ShapefileExporter; import org.neo4j.spatial.cli.tools.StyledImageExporter; +import org.neo4j.spatial.testutils.Neo4jTestCase; import org.opentest4j.AssertionFailedError; public class TestSimplePointLayer extends Neo4jTestCase { private static final Coordinate testOrigin = new Coordinate(13.0, 55.6); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @Test public void testNearestNeighborSearchOnEmptyLayer() { String layerName = "test"; @@ -474,11 +481,4 @@ private void assertIndexCountSameAs(String layerName, int count) { tx.commit(); } } - - private void inTx(Consumer txFunction) { - try (Transaction tx = graphDb().beginTx()) { - txFunction.accept(tx); - tx.commit(); - } - } } diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java index 2ce72e835..9692c459b 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatial.java @@ -25,15 +25,18 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.logging.Logger; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.neo4j.gis.spatial.filter.SearchIntersect; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.osm.OSMDataset; import org.neo4j.gis.spatial.osm.OSMImporter; import org.neo4j.gis.spatial.osm.OSMLayer; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; @@ -43,6 +46,7 @@ import org.neo4j.spatial.api.SpatialRecords; import org.neo4j.spatial.api.index.SpatialIndexReader; import org.neo4j.spatial.api.layer.Layer; +import org.neo4j.spatial.testutils.Neo4jTestCase; /** *

@@ -88,6 +92,11 @@ public class TestSpatial extends Neo4jTestCase { private final HashMap> layerTestGeometries = new HashMap<>(); private final HashMap geomStats = new HashMap<>(); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @BeforeEach public void setUp() { // TODO: Rather load this from a configuration file, properties file or JRuby test code diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java index 5b35666bb..ce50fa378 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialQueries.java @@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.util.List; import java.util.logging.Logger; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Test; @@ -30,19 +31,27 @@ import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.pipes.GeoPipeline; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.SpatialRecord; import org.neo4j.spatial.api.layer.EditableLayer; import org.neo4j.spatial.api.layer.Layer; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class TestSpatialQueries extends Neo4jTestCase { private static final Logger LOGGER = Logger.getLogger(TestSpatialQueries.class.getName()); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + /** * This test case is designed to capture the conditions described in the bug * report at https://github.com/neo4j/neo4j-spatial/issues/11. There are diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java index 261d661bd..beafd3ff1 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/TestSpatialUtils.java @@ -32,21 +32,29 @@ import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; import org.neo4j.gis.spatial.SpatialTopologyUtils.PointResult; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; import org.neo4j.gis.spatial.osm.OSMDataset; import org.neo4j.gis.spatial.osm.OSMImporter; import org.neo4j.gis.spatial.osm.OSMLayer; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.SpatialRecord; import org.neo4j.spatial.api.layer.EditableLayer; import org.neo4j.spatial.api.layer.Layer; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class TestSpatialUtils extends Neo4jTestCase { private static final Logger LOGGER = Logger.getLogger(TestSpatialUtils.class.getName()); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @Test public void testJTSLinearRef() { SpatialDatabaseService spatial = new SpatialDatabaseService( diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java index e35c28bcc..ecf00fb44 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/functions/SpatialFunctionsTest.java @@ -32,11 +32,11 @@ import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.neo4j.doc.domain.examples.ExampleCypher; import org.neo4j.exceptions.KernelException; -import org.neo4j.gis.spatial.AbstractApiTest; import org.neo4j.gis.spatial.procedures.SpatialProcedures; +import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.spatial.Geometry; +import org.neo4j.spatial.testutils.AbstractApiTest; public class SpatialFunctionsTest extends AbstractApiTest { @@ -58,28 +58,22 @@ public void create_point_and_pass_as_param() { @Test public void create_point_geometry_return() { - docExample("spatial.asGeometry", "Creates a point geometry") - .runCypher( - "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asGeometry(geom) AS geometry", - ExampleCypher::storeResult) - .assertSingleResult("geometry", geometry -> { - assertInstanceOf(Geometry.class, geometry, "Should be Geometry type"); - }); + Object geometry = executeObject( + "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asGeometry(geom) AS geometry", + "geometry"); + assertInstanceOf(Geometry.class, geometry, "Should be Geometry type"); } @Test public void create_point_geometry_ad_map() { - docExample("spatial.asMap", "Creates a point geometry as map") - .runCypher( - "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asMap(geom) AS geometry", - ExampleCypher::storeResult) - .assertSingleResult("geometry", geometry -> { - Assertions.assertThat(geometry) - .asInstanceOf(InstanceOfAssertFactories.MAP) - .containsExactly( - Assertions.entry("type", "Point"), - Assertions.entry("coordinate", new double[]{4.0d, 5.0d})); - }); + Object geometry = executeObject( + "WITH point({latitude: 5.0, longitude: 4.0}) as geom RETURN spatial.asMap(geom) AS geometry", + "geometry"); + Assertions.assertThat(geometry) + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsExactly( + Assertions.entry("type", "Point"), + Assertions.entry("coordinate", new double[]{4.0d, 5.0d})); } @Test @@ -95,12 +89,9 @@ WITH point({latitude: 5.0, longitude: 4.0}) as geom @Test public void literal_geometry_return() { - docExample("spatial.asGeometry", "Creates a point geometry from a map") - .runCypher("WITH spatial.asGeometry({latitude: 5.0, longitude: 4.0}) AS geometry RETURN geometry", - ExampleCypher::storeResult) - .assertSingleResult("geometry", geometry -> { - assertInstanceOf(Geometry.class, geometry, "Should be Geometry type"); - }); + Object geometry = executeObject( + "WITH spatial.asGeometry({latitude: 5.0, longitude: 4.0}) AS geometry RETURN geometry", "geometry"); + assertInstanceOf(Geometry.class, geometry, "Should be Geometry type"); } /** @@ -113,319 +104,295 @@ class WktToGeoJson { @Test public void testPoint() { - docExample("spatial.wktToGeoJson", "1. Converts a WKT POINT") - .runCypher("RETURN spatial.wktToGeoJson('POINT (30 10)') as json", ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "Point", - "coordinates", List.of(30., 10.) - ))); - }); + String wkt = "POINT (30 10)"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "Point", + "coordinates", List.of(30., 10.) + ))); } @Test public void testLineString() { - docExample("spatial.wktToGeoJson", "2. Converts a WKT LINESTRING") - .runCypher("RETURN spatial.wktToGeoJson('LINESTRING (30 10, 10 30, 40 40)') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "LineString", - "coordinates", List.of(List.of(30., 10.), List.of(10., 30.), List.of(40., 40.)) - ))); - }); + String wkt = "LINESTRING (30 10, 10 30, 40 40)"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "LineString", + "coordinates", List.of(List.of(30., 10.), List.of(10., 30.), List.of(40., 40.)) + ))); } @Test public void testPolygon() { - docExample("spatial.wktToGeoJson", "3. Converts a WKT POLYGON") - .runCypher("RETURN spatial.wktToGeoJson('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "Polygon", - "coordinates", - List.of( // Polygon - List.of( // LineString - List.of(30., 10.), - List.of(40., 40.), - List.of(20., 40.), - List.of(10., 20.), - List.of(30., 10.) - ) - ) - ))); - }); + String wkt = "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "Polygon", + "coordinates", + List.of( // Polygon + List.of( // LineString + List.of(30., 10.), + List.of(40., 40.), + List.of(20., 40.), + List.of(10., 20.), + List.of(30., 10.) + ) + ) + ))); } @Test public void testPolygonWithHole() { - docExample("spatial.wktToGeoJson", "4. Converts a WKT POLYGON with a hole") - .runCypher( - "RETURN spatial.wktToGeoJson('POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "Polygon", - "coordinates", - List.of( // Polygon - List.of( // LineString - List.of(35., 10.), - List.of(45., 45.), - List.of(15., 40.), - List.of(10., 20.), - List.of(35., 10.) - ), - List.of( // hole - List.of(20., 30.), - List.of(35., 35.), - List.of(30., 20.), - List.of(20., 30.) - ) - ) - ))); - }); + String wkt = "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),\n" + + "(20 30, 35 35, 30 20, 20 30))"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "Polygon", + "coordinates", + List.of( // Polygon + List.of( // LineString + List.of(35., 10.), + List.of(45., 45.), + List.of(15., 40.), + List.of(10., 20.), + List.of(35., 10.) + ), + List.of( // hole + List.of(20., 30.), + List.of(35., 35.), + List.of(30., 20.), + List.of(20., 30.) + ) + ) + ))); } @Test public void testMultiPoint() { - docExample("spatial.wktToGeoJson", "5a. Converts a WKT MULTIPOINT") - .runCypher("RETURN spatial.wktToGeoJson('MULTIPOINT ((10 40), (40 30), (20 20), (30 10))') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "MultiPoint", - "coordinates", List.of( - List.of(10., 40.), - List.of(40., 30.), - List.of(20., 20.), - List.of(30., 10.) - )))); - }); + String wkt = "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "MultiPoint", + "coordinates", List.of( + List.of(10., 40.), + List.of(40., 30.), + List.of(20., 20.), + List.of(30., 10.) + )))); } @Test public void testMultiPoint2() { - docExample("spatial.wktToGeoJson", "5b. Converts a WKT MULTIPOINT") - .runCypher("RETURN spatial.wktToGeoJson('MULTIPOINT (10 40, 40 30, 20 20, 30 10)') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "MultiPoint", - "coordinates", List.of( - List.of(10., 40.), - List.of(40., 30.), - List.of(20., 20.), - List.of(30., 10.) - )))); - }); + String wkt = "MULTIPOINT (10 40, 40 30, 20 20, 30 10)"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "MultiPoint", + "coordinates", List.of( + List.of(10., 40.), + List.of(40., 30.), + List.of(20., 20.), + List.of(30., 10.) + )))); } @Test public void testMultiLineString() { - docExample("spatial.wktToGeoJson", "6. Converts a WKT MULTILINESTRING") - .runCypher( - "RETURN spatial.wktToGeoJson('MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "MultiLineString", - "coordinates", List.of( - List.of( // LineString - List.of(10., 10.), - List.of(20., 20.), - List.of(10., 40.) - ), - List.of( // LineString - List.of(40., 40.), - List.of(30., 30.), - List.of(40., 20.), - List.of(30., 10.) - ) - )))); - }); + String wkt = "MULTILINESTRING ((10 10, 20 20, 10 40),\n" + + "(40 40, 30 30, 40 20, 30 10))"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "MultiLineString", + "coordinates", List.of( + List.of( // LineString + List.of(10., 10.), + List.of(20., 20.), + List.of(10., 40.) + ), + List.of( // LineString + List.of(40., 40.), + List.of(30., 30.), + List.of(40., 20.), + List.of(30., 10.) + ) + )))); } @Test public void testMultiPolygon() { - docExample("spatial.wktToGeoJson", "7a. Converts a WKT MULTIPOLYGON") - .runCypher( - "RETURN spatial.wktToGeoJson('MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "MultiPolygon", - "coordinates", List.of( // MultiPolygon - List.of( // Polygon - List.of( // LineString - List.of(30., 20.), - List.of(45., 40.), - List.of(10., 40.), - List.of(30., 20.) - ) - ), - List.of( // Polygon - List.of( // LineString - List.of(15., 5.), - List.of(40., 10.), - List.of(10., 20.), - List.of(5., 10.), - List.of(15., 5.) - ) - ) - )))); - }); + String wkt = "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)),\n" + + "((15 5, 40 10, 10 20, 5 10, 15 5)))"; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "MultiPolygon", + "coordinates", List.of( // MultiPolygon + List.of( // Polygon + List.of( // LineString + List.of(30., 20.), + List.of(45., 40.), + List.of(10., 40.), + List.of(30., 20.) + ) + ), + List.of( // Polygon + List.of( // LineString + List.of(15., 5.), + List.of(40., 10.), + List.of(10., 20.), + List.of(5., 10.), + List.of(15., 5.) + ) + ) + ) + ))); } @Test public void testMultiPolygon2() { - docExample("spatial.wktToGeoJson", "7b. Converts a WKT MULTIPOLYGON") - .runCypher( - "RETURN spatial.wktToGeoJson('MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "MultiPolygon", - "coordinates", List.of( // MultiPolygon - List.of( // Polygon - List.of( // LineString - List.of(40., 40.), - List.of(20., 45.), - List.of(45., 30.), - List.of(40., 40.) - ) - ), - List.of( // Polygon - List.of( // LineString - List.of(20., 35.), - List.of(10., 30.), - List.of(10., 10.), - List.of(30., 5.), - List.of(45., 20.), - List.of(20., 35.) - ), - List.of( // hole - List.of(30., 20.), - List.of(20., 15.), - List.of(20., 25.), - List.of(30., 20.) - ) - ) - )))); - }); + String wkt = """ + MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), + ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), + (30 20, 20 15, 20 25, 30 20)))"""; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "MultiPolygon", + "coordinates", List.of( // MultiPolygon + List.of( // Polygon + List.of( // LineString + List.of(40., 40.), + List.of(20., 45.), + List.of(45., 30.), + List.of(40., 40.) + ) + ), + List.of( // Polygon + List.of( // LineString + List.of(20., 35.), + List.of(10., 30.), + List.of(10., 10.), + List.of(30., 5.), + List.of(45., 20.), + List.of(20., 35.) + ), + List.of( // hole + List.of(30., 20.), + List.of(20., 15.), + List.of(20., 25.), + List.of(30., 20.) + ) + ) + ) + ))); } @Test public void testGeometryCollection() { - docExample("spatial.wktToGeoJson", "8. FConverts a WKT GEOMETRYCOLLECTION") - .runCypher( - "RETURN spatial.wktToGeoJson('GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)))') as json", - ExampleCypher::storeResult) - .assertSingleResult("json", json -> { - assertThat(json, equalTo(Map.of( - "type", "GeometryCollection", - "geometries", List.of( - Map.of( // Point - "type", "Point", - "coordinates", List.of(40., 10.) - ), - Map.of( // LineString - "type", "LineString", - "coordinates", List.of( - List.of(10., 10.), - List.of(20., 20.), - List.of(10., 40.) - ) - ), - Map.of( // Polygon - "type", "Polygon", - "coordinates", List.of( - List.of( // LineString - List.of(40., 40.), - List.of(20., 45.), - List.of(45., 30.), - List.of(40., 40.) - ) - ) - ) - ) - ))); - }); + String wkt = """ + GEOMETRYCOLLECTION (POINT (40 10), + LINESTRING (10 10, 20 20, 10 40), + POLYGON ((40 40, 20 45, 45 30, 40 40)))"""; + Object json = executeObject("return spatial.wktToGeoJson($wkt) as json", Map.of("wkt", wkt), + "json"); + assertThat(json, equalTo(Map.of( + "type", "GeometryCollection", + "geometries", List.of( + Map.of( // Point + "type", "Point", + "coordinates", List.of(40., 10.) + ), + Map.of( // LineString + "type", "LineString", + "coordinates", List.of( + List.of(10., 10.), + List.of(20., 20.), + List.of(10., 40.) + ) + ), + Map.of( // Polygon + "type", "Polygon", + "coordinates", List.of( + List.of( // LineString + List.of(40., 40.), + List.of(20., 45.), + List.of(45., 30.), + List.of(40., 40.) + ) + ) + ) + ) + ))); } } @Test public void testPointToWkt() { - docExample("spatial.neo4jGeometryToWkt", "Converting a point to WKT") - .runCypher("RETURN spatial.neo4jGeometryToWkt(point({longitude: 1, latitude: 2})) as wkt", - ExampleCypher::storeResult) - .assertSingleResult("wkt", wkt -> { - assertThat(wkt, equalTo("POINT ( 1 2 )")); - }); + Object wkt = executeObject("return spatial.neo4jGeometryToWkt(point({longitude: 1, latitude: 2})) as wkt", + "wkt"); + assertThat(wkt, equalTo("POINT ( 1 2 )")); } @Test public void testPointArrayToWkt() { - docExample("spatial.neo4jGeometryToWkt", "Converting a point array to WKT") - .runCypher( - "RETURN spatial.neo4jGeometryToWkt([point({longitude: 1, latitude: 2}), point({longitude: 3, latitude: 4}) ]) as wkt", - ExampleCypher::storeResult) - .assertSingleResult("wkt", wkt -> { - assertThat(wkt, equalTo("LINESTRING (1 2, 3 4)")); - }); + Object wkt = executeObject( + "return spatial.neo4jGeometryToWkt([point({longitude: 1, latitude: 2}), point({longitude: 3, latitude: 4}) ]) as wkt", + "wkt"); + assertThat(wkt, equalTo("LINESTRING (1 2, 3 4)")); } @Test public void testExtractAttributes() { - docExample("spatial.extractAttributes", "Extracts attributes from a layer node") - .runCypher(""" - CALL spatial.addPointLayer('attr_layer') YIELD node - WITH node - CREATE (n:Point {longitude: 10.0, latitude: 20.0, name: 'test_point', type: 'landmark', elevation: 100}) - WITH n - CALL spatial.addNode('attr_layer', n) YIELD node as added_node - WITH n - RETURN spatial.extractAttributes('attr_layer', n) as attributes - """, - ExampleCypher::storeResult) - .assertSingleResult("attributes", attributes -> { - Assertions.assertThat(attributes) - .asInstanceOf(InstanceOfAssertFactories.MAP) - .containsKeys("name", "type", "elevation") - .containsEntry("name", "test_point") - .containsEntry("type", "landmark") - .containsEntry("elevation", 100L); - }); + Object attributes = executeObject( + """ + CALL spatial.addPointLayer('attr_layer') YIELD node + WITH node + CREATE (n:Point {longitude: 10.0, latitude: 20.0, name: 'test_point', type: 'landmark', elevation: 100}) + WITH n + CALL spatial.addNode('attr_layer', n) YIELD node as added_node + WITH n + RETURN spatial.extractAttributes('attr_layer', n) as attributes + """, + "attributes"); + Assertions.assertThat(attributes) + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsKeys("name", "type", "elevation") + .containsEntry("name", "test_point") + .containsEntry("type", "landmark") + .containsEntry("elevation", 100L); } @Test public void testExtractAttributesWithWKT() { - docExample("spatial.extractAttributes", "Extracts attributes from a WKT layer node") - .runCypher(""" - CALL spatial.addLayer('wkt_attr_layer', 'WKT', '') YIELD node - WITH node - CREATE (n:Geometry {geometry: 'POINT (30 10)', name: 'test_wkt_point', category: 'marker', id: 42}) - WITH n - CALL spatial.addNode('wkt_attr_layer', n) YIELD node as added_node - WITH n - RETURN spatial.extractAttributes('wkt_attr_layer', n) as attributes - """, - ExampleCypher::storeResult) - .assertSingleResult("attributes", attributes -> { - Assertions.assertThat(attributes) - .asInstanceOf(InstanceOfAssertFactories.MAP) - .containsKeys("name", "category", "id") - .containsEntry("name", "test_wkt_point") - .containsEntry("category", "marker") - .containsEntry("id", 42L); - }); + Object attributes = executeObject( + """ + CALL spatial.addLayer('wkt_attr_layer', 'WKT', '') YIELD node + WITH node + CREATE (n:Geometry {geometry: 'POINT (30 10)', name: 'test_wkt_point', category: 'marker', id: 42}) + WITH n + CALL spatial.addNode('wkt_attr_layer', n) YIELD node as added_node + WITH n + RETURN spatial.extractAttributes('wkt_attr_layer', n) as attributes + """, + "attributes"); + Assertions.assertThat(attributes) + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsKeys("name", "category", "id") + .containsEntry("name", "test_wkt_point") + .containsEntry("category", "marker") + .containsEntry("id", 42L); } @Test public void testDecodeGeometryAndExtractAttributesTogether() { - docExample("spatial.decodeGeometry", "Using both functions together") - .additionalSignature("spatial.extractAttributes") - .runCypher(""" + try (Transaction tx = db.beginTx()) { + var result = tx.execute(""" CALL spatial.addPointLayer('combined_layer') YIELD node WITH node CREATE (n:Point {longitude: 5.5, latitude: 45.5, city: 'TestCity', population: 50000}) @@ -435,35 +402,29 @@ public void testDecodeGeometryAndExtractAttributesTogether() { RETURN spatial.decodeGeometry('combined_layer', n) as geometry, spatial.extractAttributes('combined_layer', n) as attributes - """, - ExampleCypher::storeResult) - .assertSingleResult("geometry", geometry -> { - assertInstanceOf(Geometry.class, geometry, "Should be Geometry type"); - }) - .assertSingleResult("attributes", attributes -> { - Assertions.assertThat(attributes) - .asInstanceOf(InstanceOfAssertFactories.MAP) - .containsKeys("city", "population") - .containsEntry("city", "TestCity") - .containsEntry("population", 50000L); - }); + """).next(); + assertInstanceOf(Geometry.class, result.get("geometry"), "Should be Geometry type"); + Assertions.assertThat(result.get("attributes")) + .asInstanceOf(InstanceOfAssertFactories.MAP) + .containsKeys("city", "population") + .containsEntry("city", "TestCity") + .containsEntry("population", 50000L); + } } @Test public void testNodeAsWKT() { - docExample("spatial.nodeAsWKT", "Converting a layer node to WKT") - .runCypher(""" - CALL spatial.addPointLayer('wkt_layer') YIELD node - WITH node - CREATE (n:Point {longitude: 10.0, latitude: 20.0}) - WITH n - CALL spatial.addNode('wkt_layer', n) YIELD node as added_node - WITH n - RETURN spatial.nodeAsWKT('wkt_layer', n) as wkt - """, - ExampleCypher::storeResult) - .assertSingleResult("wkt", wkt -> { - assertThat(wkt, equalTo("POINT (10 20)")); - }); + Object wkt = executeObject( + """ + CALL spatial.addPointLayer('wkt_layer') YIELD node + WITH node + CREATE (n:Point {longitude: 10.0, latitude: 20.0}) + WITH n + CALL spatial.addNode('wkt_layer', n) YIELD node as added_node + WITH n + RETURN spatial.nodeAsWKT('wkt_layer', n) as wkt + """, + "wkt"); + assertThat(wkt, equalTo("POINT (10 20)")); } } diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java index 8e675eb0f..669c29a30 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/pipes/GeoPipesPerformanceTest.java @@ -22,20 +22,23 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.logging.Logger; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.locationtech.jts.geom.Coordinate; -import org.neo4j.gis.spatial.Neo4jTestCase; import org.neo4j.gis.spatial.SimplePointLayer; import org.neo4j.gis.spatial.SpatialDatabaseRecord; import org.neo4j.gis.spatial.SpatialDatabaseService; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.index.IndexManagerImpl; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.kernel.api.security.SecurityContext; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.layer.Layer; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class GeoPipesPerformanceTest extends Neo4jTestCase { @@ -44,6 +47,11 @@ public class GeoPipesPerformanceTest extends Neo4jTestCase { private final int records = 10000; private final int chunkSize = records / 10; + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } + @BeforeEach public void setUp() throws Exception { loadSamplePointData(); diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java b/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java index cf7eb7e9a..2aea3a750 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java +++ b/server-plugin/src/test/java/org/neo4j/gis/spatial/procedures/SpatialProceduresTest.java @@ -42,7 +42,6 @@ import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.logging.Logger; @@ -50,15 +49,11 @@ import java.util.stream.Stream; import org.assertj.core.api.Assertions; import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.neo4j.doc.domain.examples.Example; -import org.neo4j.doc.domain.examples.ExampleCypher; import org.neo4j.exceptions.KernelException; -import org.neo4j.gis.spatial.AbstractApiTest; import org.neo4j.gis.spatial.SpatialDatabaseService; import org.neo4j.gis.spatial.SpatialRelationshipTypes; import org.neo4j.gis.spatial.functions.SpatialFunctions; @@ -72,10 +67,10 @@ import org.neo4j.graphdb.spatial.Geometry; import org.neo4j.graphdb.spatial.Point; import org.neo4j.kernel.api.KernelTransaction; -import org.neo4j.kernel.api.procedure.GlobalProcedures; import org.neo4j.kernel.impl.coreapi.InternalTransaction; import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.spatial.api.layer.Layer; +import org.neo4j.spatial.testutils.AbstractApiTest; public class SpatialProceduresTest extends AbstractApiTest { @@ -139,23 +134,6 @@ public static void testResult(GraphDatabaseService db, String call, Consumer params, - Consumer resultConsumer) { - try (Transaction tx = db.beginTx()) { - Map p = (params == null) ? Map.of() : params; - resultConsumer.accept(tx.execute(call, p)); - tx.commit(); - } - } - - public static void registerProceduresAndFunctions(GraphDatabaseService db, Class procedure) - throws KernelException { - GlobalProcedures procedures = ((GraphDatabaseAPI) db).getDependencyResolver() - .resolveDependency(GlobalProcedures.class); - procedures.registerProcedure(procedure); - procedures.registerFunction(procedure); - } - private static Layer makeLayerOfVariousTypes(SpatialDatabaseService spatial, Transaction tx, String name, int index) { return switch (index % 3) { @@ -323,16 +301,11 @@ public void create_point_and_return() { @Test public void create_node_decode_to_geometry() { - docExample("spatial.decodeGeometry", "Decode a geometry from a node property") - .additionalSignature("spatial.addWKTLayer") - .runCypher("CALL spatial.addWKTLayer('geom','geom')", - config -> config.storeResult().setTitle("Create a WKT layer")) - .runCypher( - "CREATE (n:Node {geom:'POINT(4.0 5.0)'}) RETURN spatial.decodeGeometry('geom',n) AS geometry", - config -> config.storeResult().setTitle("Decode a geometry")) - .assertSingleResult("geometry", geometry -> { - assertInstanceOf(Geometry.class, geometry, "Should be Geometry type"); - }); + execute("CALL spatial.addWKTLayer('geom','geom')"); + Object geometry = executeObject( + "CREATE (n:Node {geom:'POINT(4.0 5.0)'}) RETURN spatial.decodeGeometry('geom',n) AS geometry", + "geometry"); + assertInstanceOf(Geometry.class, geometry, "Should be Geometry type"); } @Test @@ -349,56 +322,49 @@ public void create_node_and_convert_to_geometry() { @Test public void testAddNativePointLayerWithConfig() { - docExample("spatial.addNativePointLayerWithConfig", "Create a native point layer with a configuration") - .runCypher("CALL spatial.addNativePointLayerWithConfig('geom','pos:mbr','hilbert')", - ExampleCypher::storeResult) - .assertSingleResult("node", o -> { - Assertions.assertThat(o).isInstanceOf(Node.class); - Node node = (Node) o; - assertEquals("geom", node.getProperty(PROP_LAYER)); - assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER)); - assertEquals("hilbert", node.getProperty(PROP_INDEX_TYPE)); - assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG)); - }); + testCall(db, "CALL spatial.addNativePointLayerWithConfig('geom','pos:mbr','hilbert')", (result) -> { + Node node = (Node) result.get("node"); + assertEquals("geom", node.getProperty(PROP_LAYER)); + assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER)); + assertEquals("hilbert", + node.getProperty(PROP_INDEX_TYPE)); + assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG)); + }); } @Test public void testAddNativePointLayerXY() { - docExample("spatial.addNativePointLayerXY", "Create a native point layer") - .additionalSignature("spatial.withinDistance") - .additionalSignature("spatial.addNode") - .runCypher("CALL spatial.addNativePointLayerXY('geom','x','y')", - ExampleCypher::storeResult) - .assertSingleResult("node", o -> { - Assertions.assertThat(o).isInstanceOf(Node.class); - Node node = (Node) o; - assertEquals("geom", node.getProperty(PROP_LAYER)); - assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER)); - assertEquals("rtree", node.getProperty(PROP_INDEX_TYPE)); - assertEquals("x:y", node.getProperty(PROP_GEOMENCODER_CONFIG)); - }) - .runCypher( - "CREATE (n:Node {id: 42, x: 5.0, y: 4.0}) WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node", - config -> config.setComment("create a node and add it to the index")) - .runCypher("CALL spatial.withinDistance('geom',point({latitude:4.1,longitude:5.1}),100)", - config -> config.storeResult().setComment("Find node within distance")) - .assertSingleResult("node", o -> assertEquals(42L, ((Node) o).getProperty("id"))); + testCall(db, "CALL spatial.addNativePointLayerXY('geom','x','y')", (result) -> { + var o = result.get("node"); + Assertions.assertThat(o).isInstanceOf(Node.class); + Node node = (Node) o; + assertEquals("geom", node.getProperty(PROP_LAYER)); + assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER)); + assertEquals("rtree", node.getProperty(PROP_INDEX_TYPE)); + assertEquals("x:y", node.getProperty(PROP_GEOMENCODER_CONFIG)); + }); + + execute( + "CREATE (n:Node {id: 42, x: 5.0, y: 4.0}) WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node"); + + testCall(db, "CALL spatial.withinDistance('geom',point({latitude:4.1,longitude:5.1}),100)", (result) -> { + var o = result.get("node"); + Assertions.assertThat(o).isInstanceOf(Node.class); + assertEquals(42L, ((Node) o).getProperty("id")); + }); } @Test public void create_a_pointlayer_with_x_and_y() { testCall(db, "CALL spatial.addPointLayerXY('geom','lon','lat')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); } @Test public void create_a_pointlayer_with_config() { - docExample("spatial.addPointLayerWithConfig", "Create a point layer with X and Y properties") - .runCypher("CALL spatial.addPointLayerWithConfig('geom','lon:lat')", ExampleCypher::storeResult) - .assertSingleResult("node", node -> { - assertEquals("geom", ((Node) node).getProperty(PROP_LAYER)); - }); + testCall(db, "CALL spatial.addPointLayerWithConfig('geom','lon:lat')", + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); } @Test @@ -406,19 +372,7 @@ public void create_a_pointlayer_with_config_on_existing_wkt_layer() { execute("CALL spatial.addWKTLayer('geom','wkt')"); try { testCall(db, "CALL spatial.addPointLayerWithConfig('geom','lon:lat')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); - fail("Expected exception to be thrown"); - } catch (Exception e) { - Assertions.assertThat(e.getMessage()).contains("Layer already exists: 'geom'"); - } - } - - @Test - public void create_a_pointlayer_with_config_on_existing_osm_layer() { - execute("CALL spatial.addLayer('geom','OSM','')"); - try { - testCall(db, "CALL spatial.addPointLayerWithConfig('geom','lon:lat')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); fail("Expected exception to be thrown"); } catch (Exception e) { Assertions.assertThat(e.getMessage()).contains("Layer already exists: 'geom'"); @@ -428,31 +382,31 @@ public void create_a_pointlayer_with_config_on_existing_osm_layer() { @Test public void create_a_pointlayer_with_rtree() { testCall(db, "CALL spatial.addPointLayer('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); } @Test public void create_a_pointlayer_with_geohash() { testCall(db, "CALL spatial.addPointLayerGeohash('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); } @Test public void create_a_pointlayer_with_zorder() { testCall(db, "CALL spatial.addPointLayerZOrder('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); } @Test public void create_a_pointlayer_with_hilbert() { testCall(db, "CALL spatial.addPointLayerHilbert('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); } @Test public void create_and_delete_a_pointlayer_with_rtree() { testCall(db, "CALL spatial.addPointLayer('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); testCallCount(db, "CALL spatial.layers()", null, 1); execute("CALL spatial.removeLayer('geom')"); testCallCount(db, "CALL spatial.layers()", null, 0); @@ -461,7 +415,7 @@ public void create_and_delete_a_pointlayer_with_rtree() { @Test public void create_and_delete_a_pointlayer_with_geohash() { testCall(db, "CALL spatial.addPointLayerGeohash('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); testCallCount(db, "CALL spatial.layers()", null, 1); execute("CALL spatial.removeLayer('geom')"); testCallCount(db, "CALL spatial.layers()", null, 0); @@ -470,7 +424,7 @@ public void create_and_delete_a_pointlayer_with_geohash() { @Test public void create_and_delete_a_pointlayer_with_zorder() { testCall(db, "CALL spatial.addPointLayerZOrder('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); testCallCount(db, "CALL spatial.layers()", null, 1); execute("CALL spatial.removeLayer('geom')"); testCallCount(db, "CALL spatial.layers()", null, 0); @@ -479,7 +433,7 @@ public void create_and_delete_a_pointlayer_with_zorder() { @Test public void create_and_delete_a_pointlayer_with_hilbert() { testCall(db, "CALL spatial.addPointLayerHilbert('geom')", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); testCallCount(db, "CALL spatial.layers()", null, 1); execute("CALL spatial.removeLayer('geom')"); testCallCount(db, "CALL spatial.layers()", null, 0); @@ -487,96 +441,68 @@ public void create_and_delete_a_pointlayer_with_hilbert() { @Test public void create_a_simple_pointlayer_using_named_encoder() { - Example example = docExample("spatial.addLayerWithEncoder", "Create a `SimplePointEncoder`"); - example.runCypher("CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','')", - ExampleCypher::storeResult) - .assertSingleResult("node", o -> { - Assertions.assertThat(o).isInstanceOf(Node.class); - Node node = (Node) o; - assertEquals("geom", node.getProperty(PROP_LAYER)); - assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER)); - assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); - assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG)); - }); + testCall(db, "CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','')", (r) -> { + Node node = (Node) r.get("node"); + assertEquals("geom", node.getProperty(PROP_LAYER)); + assertEquals("SimplePointEncoder", + node.getProperty(PROP_GEOMENCODER)); + assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); + assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG)); + }); } @Test public void create_a_simple_pointlayer_using_named_and_configured_encoder() { - Example example = docExample("spatial.addLayerWithEncoder", - "Create a `SimplePointEncoder` with a customized encoder configuration"); - example.runCypher("CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','x:y:mbr')", - config -> config.storeResult().setComment(""" - Configures the encoder to use the nodes `x` property instead of `longitude`, - the `y` property instead of `latitude` - and the `mbr` property instead of `bbox`. - """)) - .assertSingleResult("node", o -> { - Assertions.assertThat(o).isInstanceOf(Node.class); - Node node = (Node) o; - assertEquals("geom", node.getProperty(PROP_LAYER)); - assertEquals("SimplePointEncoder", node.getProperty(PROP_GEOMENCODER)); - assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); - assertEquals("x:y:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG)); - }); + testCall(db, "CALL spatial.addLayerWithEncoder('geom','SimplePointEncoder','x:y:mbr')", (r) -> { + Node node = (Node) r.get("node"); + assertEquals("geom", node.getProperty(PROP_LAYER)); + assertEquals("SimplePointEncoder", + node.getProperty(PROP_GEOMENCODER)); + assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); + assertEquals("x:y:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG)); + }); } @Test public void create_a_native_pointlayer_using_named_encoder() { - Example example = docExample("spatial.addLayerWithEncoder", "Create a `NativePointEncoder`"); - example.runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','')", - ExampleCypher::storeResult) - .assertSingleResult("node", o -> { - Assertions.assertThat(o).isInstanceOf(Node.class); - Node node = (Node) o; - assertEquals("geom", node.getProperty(PROP_LAYER)); - assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER)); - assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); - assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG)); - }); + testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','')", (r) -> { + Node node = (Node) r.get("node"); + assertEquals("geom", node.getProperty(PROP_LAYER)); + assertEquals("NativePointEncoder", + node.getProperty(PROP_GEOMENCODER)); + assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); + assertFalse(node.hasProperty(PROP_GEOMENCODER_CONFIG)); + }); } @Test public void create_a_native_pointlayer_using_named_and_configured_encoder() { - Example example = docExample("spatial.addLayerWithEncoder", - "Create a `NativePointEncoder` with a customized encoder configuration"); - example.runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr')", - config -> config.storeResult().setComment(""" - Configures the encoder to use the nodes `pos` property instead of `location` - and the `mbr` property instead of `bbox`. - """)) - .assertSingleResult("node", o -> { - Assertions.assertThat(o).isInstanceOf(Node.class); - Node node = (Node) o; - assertEquals("geom", node.getProperty(PROP_LAYER)); - assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER)); - assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); - assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG)); - }); + testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr')", (r) -> { + Node node = (Node) r.get("node"); + assertEquals("geom", node.getProperty(PROP_LAYER)); + assertEquals("NativePointEncoder", + node.getProperty(PROP_GEOMENCODER)); + assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); + assertEquals("pos:mbr", node.getProperty(PROP_GEOMENCODER_CONFIG)); + }); } @Test public void create_a_native_pointlayer_using_named_and_configured_encoder_with_cartesian() { - Example example = docExample("spatial.addLayerWithEncoder", - "Create a `NativePointEncoder` with a customized encoder configuration using Cartesian coordinates"); - example.runCypher("CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr:Cartesian')", - config -> config.storeResult().setComment(""" - Configures the encoder to use the nodes `pos` property instead of `location`, - the `mbr` property instead of `bbox` and Cartesian coordinates. - """)) - .assertSingleResult("node", o -> { - Assertions.assertThat(o).isInstanceOf(Node.class); - Node node = (Node) o; - assertEquals("geom", node.getProperty(PROP_LAYER)); - assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER)); - assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); - assertEquals("pos:mbr:Cartesian", node.getProperty(PROP_GEOMENCODER_CONFIG)); - }); + testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr:Cartesian')", (r) -> { + Node node = (Node) r.get("node"); + assertEquals("geom", node.getProperty(PROP_LAYER)); + assertEquals("NativePointEncoder", + node.getProperty(PROP_GEOMENCODER)); + assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); + assertEquals("pos:mbr:Cartesian", node.getProperty(PROP_GEOMENCODER_CONFIG)); + }); } @Test public void create_a_native_pointlayer_using_named_and_configured_encoder_with_geographic() { testCall(db, "CALL spatial.addLayerWithEncoder('geom','NativePointEncoder','pos:mbr:WGS-84')", (r) -> { - Node node = dump((Node) r.get("node")); + Node node = (Node) r.get("node"); assertEquals("geom", node.getProperty(PROP_LAYER)); assertEquals("NativePointEncoder", node.getProperty(PROP_GEOMENCODER)); assertEquals("SimplePointLayer", node.getProperty(PROP_LAYER_TYPE)); @@ -587,7 +513,7 @@ public void create_a_native_pointlayer_using_named_and_configured_encoder_with_g @Test public void create_a_wkt_layer_using_know_format() { testCall(db, "CALL spatial.addLayer('geom','WKT',null)", - (r) -> assertEquals("geom", (dump((Node) r.get("node"))).getProperty(PROP_LAYER))); + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); } @Test @@ -605,14 +531,10 @@ public void list_layer_names() { @Test public void add_and_remove_layer() { - docExample("spatial.layers", "Add and Remove a layer") - .additionalSignature("spatial.removeLayer") - .runCypher("CALL spatial.addWKTLayer('geom','wkt')", ExampleCypher::storeResult) - .runCypher("CALL spatial.layers()", ExampleCypher::storeResult) - .assertResult(res -> Assertions.assertThat(res).hasSize(1)) - .runCypher("CALL spatial.removeLayer('geom')", ExampleCypher::storeResult) - .runCypher("CALL spatial.layers()", ExampleCypher::storeResult) - .assertResult(res -> Assertions.assertThat(res).isEmpty()); + execute("CALL spatial.addWKTLayer('geom','wkt')"); + testCallCount(db, "CALL spatial.layers()", null, 1); + execute("CALL spatial.removeLayer('geom')"); + testCallCount(db, "CALL spatial.layers()", null, 0); } @Test @@ -637,15 +559,11 @@ public void add_and_remove_multiple_layers() { @Test public void get_and_set_feature_attributes() { - docExample("spatial.getFeatureAttributes", "Get the feature attributes of a layer") - .additionalSignature("spatial.setFeatureAttributes") - .runCypher("CALL spatial.addWKTLayer('geom','wkt')", ExampleCypher::storeResult) - .runCypher("CALL spatial.getFeatureAttributes('geom')", ExampleCypher::storeResult) - .assertResult(res -> Assertions.assertThat(res).hasSize(0)) - .runCypher("CALL spatial.setFeatureAttributes('geom',['name','type','color'])", - ExampleCypher::storeResult) - .runCypher("CALL spatial.getFeatureAttributes('geom')", ExampleCypher::storeResult) - .assertResult(res -> Assertions.assertThat(res).hasSize(3)); + execute("CALL spatial.addWKTLayer('geom','wkt')"); + testCallCount(db, "CALL spatial.layers()", null, 1); + testCallCount(db, "CALL spatial.getFeatureAttributes('geom')", null, 0); + execute("CALL spatial.setFeatureAttributes('geom',['name','type','color'])"); + testCallCount(db, "CALL spatial.getFeatureAttributes('geom')", null, 3); } @Test @@ -678,36 +596,34 @@ public void list_spatial_procedures() { @Test public void list_layer_types() { - Example example = docExample("spatial.layerTypes", "List the available layer types"); - example.runCypher("CALL spatial.layerTypes()", ExampleCypher::storeResult) - .assertResult(res -> { - Assertions.assertThat(res).hasSize(12); - // Convert result list to a Map keyed by 'id' for easy lookup - Map> layerTypes = res.stream() - .collect(Collectors.toMap(r -> r.get("id").toString(), r -> r)); - - assertLayerType(layerTypes, "SimplePoint", "SimplePointEncoder", "SimplePointLayer", "rtree", - "longitude:latitude"); - assertLayerType(layerTypes, "NativePoint", "NativePointEncoder", "SimplePointLayer", "rtree", - "location"); - assertLayerType(layerTypes, "NativePoints", "NativePointsEncoder", "EditableLayer", "rtree", - "geometry"); - assertLayerType(layerTypes, "WKT", "WKTGeometryEncoder", "EditableLayer", "rtree", "geometry"); - assertLayerType(layerTypes, "WKB", "WKBGeometryEncoder", "EditableLayer", "rtree", "geometry"); - assertLayerType(layerTypes, "Geohash", "SimplePointEncoder", "SimplePointLayer", "geohash", - "longitude:latitude"); - assertLayerType(layerTypes, "ZOrder", "SimplePointEncoder", "SimplePointLayer", "zorder", - "longitude:latitude"); - assertLayerType(layerTypes, "Hilbert", "SimplePointEncoder", "SimplePointLayer", "hilbert", - "longitude:latitude"); - assertLayerType(layerTypes, "NativeGeohash", "NativePointEncoder", "SimplePointLayer", "geohash", - "location"); - assertLayerType(layerTypes, "NativeZOrder", "NativePointEncoder", "SimplePointLayer", "zorder", - "location"); - assertLayerType(layerTypes, "NativeHilbert", "NativePointEncoder", "SimplePointLayer", "hilbert", - "location"); - assertLayerType(layerTypes, "OSM", "OSMGeometryEncoder", "OSMLayer", "rtree", "geometry"); - }); + testResult(db, "CALL spatial.layerTypes()", (res) -> { + // Convert the result list to a Map keyed by 'id' for an easy lookup + Map> layerTypes = res.stream() + .collect(Collectors.toMap(r -> r.get("id").toString(), r -> r)); + Assertions.assertThat(layerTypes).hasSize(12); + + assertLayerType(layerTypes, "SimplePoint", "SimplePointEncoder", "SimplePointLayer", "rtree", + "longitude:latitude"); + assertLayerType(layerTypes, "NativePoint", "NativePointEncoder", "SimplePointLayer", "rtree", + "location"); + assertLayerType(layerTypes, "NativePoints", "NativePointsEncoder", "EditableLayer", "rtree", + "geometry"); + assertLayerType(layerTypes, "WKT", "WKTGeometryEncoder", "EditableLayer", "rtree", "geometry"); + assertLayerType(layerTypes, "WKB", "WKBGeometryEncoder", "EditableLayer", "rtree", "geometry"); + assertLayerType(layerTypes, "Geohash", "SimplePointEncoder", "SimplePointLayer", "geohash", + "longitude:latitude"); + assertLayerType(layerTypes, "ZOrder", "SimplePointEncoder", "SimplePointLayer", "zorder", + "longitude:latitude"); + assertLayerType(layerTypes, "Hilbert", "SimplePointEncoder", "SimplePointLayer", "hilbert", + "longitude:latitude"); + assertLayerType(layerTypes, "NativeGeohash", "NativePointEncoder", "SimplePointLayer", "geohash", + "location"); + assertLayerType(layerTypes, "NativeZOrder", "NativePointEncoder", "SimplePointLayer", "zorder", + "location"); + assertLayerType(layerTypes, "NativeHilbert", "NativePointEncoder", "SimplePointLayer", "hilbert", + "location"); + assertLayerType(layerTypes, "OSM", "OSMGeometryEncoder", "OSMLayer", "rtree", "geometry"); + }); } private void assertLayerType(Map> layerTypes, String id, String encoder, String layer, @@ -724,11 +640,12 @@ private void assertLayerType(Map> layerTypes, String @Test public void find_layer() { - Example example = docExample("spatial.layer", "Find an existing layer"); - Example example1 = example.runCypher("CALL spatial.addWKTLayer('geom','wkt')", - config -> config.setComment("Create a WKT layer")); - example1.runCypher("CALL spatial.layer('geom')", ExampleCypher::storeResult) - .assertSingleResult("node", r -> assertEquals("geom", ((Node) r).getProperty(PROP_LAYER))); + String wkt = "LINESTRING (15.2 60.1, 15.3 60.1)"; + execute("CALL spatial.addWKTLayer('geom','wkt')"); + execute("CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", wkt)); + + testCall(db, "CALL spatial.layer('geom')", + (r) -> assertEquals("geom", ((Node) r.get("node")).getProperty(PROP_LAYER))); testCallFails(db, "CALL spatial.layer('badname')", null, "No such layer 'badname'"); } @@ -779,17 +696,11 @@ public void testPointLayer(String encoder, String indexType) { procName += indexType; } String layerName = ("my-" + encoder + "-" + indexType + "-layer").toLowerCase(); + execute("CALL " + procName + "('" + layerName + "')"); + execute("CREATE (n:Node {id: 42, latitude:60.1,longitude:15.2}) SET n.location=point(n) RETURN n"); - Example example = docExample(procName, "Create a layer to index a node"); - example.runCypher("CALL " + procName + "('" + layerName + "')", ExampleCypher::storeResult) - .runCypher("CREATE (n:Node {id: 42, latitude:60.1,longitude:15.2}) SET n.location=point(n) RETURN n", - config -> config.setComment("Create a node to index")) - .runCypher("MATCH (n:Node) WITH n CALL spatial.addNode('" + layerName + "',n) YIELD node RETURN node", - config -> config.storeResult().setComment("Index node").storeResult()) - .assertSingleResult("node", o -> assertEquals(42L, ((Node) o).getProperty("id"))) - .runCypher("CALL spatial.withinDistance('" + layerName + "',{lon:15.0,lat:60.0},100)", - config -> config.storeResult().setComment("Find node within distance")) - .assertSingleResult("node", o -> assertEquals(42L, ((Node) o).getProperty("id"))); + testCall(db, "MATCH (n:Node) WITH n CALL spatial.addNode('" + layerName + "',n) YIELD node RETURN node", + (result) -> assertEquals(42L, ((Node) result.get("node")).getProperty("id"))); testResult(db, "CALL spatial.layers()", (res) -> { while (res.hasNext()) { @@ -873,42 +784,50 @@ public void add_a_node_to_the_spatial_index_short_with_geohash() { @Test public void add_two_nodes_to_the_spatial_layer() { - docExample("spatial.addPointLayerXY", "Create a point layer with X and Y properties") - .additionalSignature("spatial.addNodes") - .additionalSignature("spatial.removeNode") - .additionalSignature("spatial.removeNode.byId") - .runCypher("CALL spatial.addPointLayerXY('geom','lon','lat')") - .runCypher( - "CREATE (n1:Node {id: 1, lat:60.1,lon:15.2}),(n2:Node {id: 2, lat:60.1,lon:15.3}) WITH n1,n2 CALL spatial.addNodes('geom',[n1,n2]) YIELD count RETURN n1,n2,count", - config -> config.storeResult().setComment("Add two nodes to the layer")) - .assertSingleResult("n1", o -> assertEquals(60.1, ((Node) o).getProperty("lat"))) - .assertSingleResult("n2", o -> assertEquals(60.1, ((Node) o).getProperty("lat"))) - .assertSingleResult("count", o -> assertEquals(2L, o)) - .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", - config -> config.storeResult().setComment("Find nodes within distance")) - .assertResult(res -> { - assertEquals(2, res.size()); - assertEquals(1L, ((Node) res.get(0).get("node")).getProperty("id")); - assertEquals(2L, ((Node) res.get(1).get("node")).getProperty("id")); - }) - .runCypher(""" - MATCH (node) WHERE node.id = 1 - CALL spatial.removeNode('geom', node) YIELD nodeId - RETURN nodeId - """, - config -> config.setComment("Remove node 1")) - .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", - ExampleCypher::storeResult) - .assertSingleResult("node", o -> assertEquals(2L, ((Node) o).getProperty("id"))) - .runCypher(""" - MATCH (node) WHERE node.id = 2 - CALL spatial.removeNode.byId('geom', elementId(node)) YIELD nodeId - RETURN nodeId - """, - config -> config.setComment("Remove node 2")) - .runCypher("CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", - ExampleCypher::storeResult) - .assertResult(res -> Assertions.assertThat(res).isEmpty()); + execute("CALL spatial.addPointLayerXY('geom','lon','lat')"); + String node1; + String node2; + try (Transaction tx = db.beginTx()) { + Result result = tx.execute( + "CREATE (n1:Node {lat:60.1,lon:15.2}),(n2:Node {lat:60.1,lon:15.3}) WITH n1,n2 CALL spatial.addNodes('geom',[n1,n2]) YIELD count RETURN n1,n2,count"); + Map row = result.next(); + node1 = ((Node) row.get("n1")).getElementId(); + node2 = ((Node) row.get("n2")).getElementId(); + long count = (Long) row.get("count"); + assertEquals(2L, count); + result.close(); + tx.commit(); + } + testResult(db, "CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", res -> { + assertTrue(res.hasNext()); + assertEquals(node1, ((Node) res.next().get("node")).getElementId()); + assertTrue(res.hasNext()); + assertEquals(node2, ((Node) res.next().get("node")).getElementId()); + assertFalse(res.hasNext()); + }); + try (Transaction tx = db.beginTx()) { + Node node = (Node) tx.execute("MATCH (node) WHERE elementId(node) = $nodeId RETURN node", + Map.of("nodeId", node1)).next().get("node"); + Result removeResult = tx.execute("CALL spatial.removeNode('geom',$node) YIELD nodeId RETURN nodeId", + Map.of("node", node)); + assertEquals(node1, removeResult.next().get("nodeId")); + removeResult.close(); + tx.commit(); + } + testResult(db, "CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", res -> { + assertTrue(res.hasNext()); + assertEquals(node2, ((Node) res.next().get("node")).getElementId()); + assertFalse(res.hasNext()); + }); + try (Transaction tx = db.beginTx()) { + Result removeResult = tx.execute("CALL spatial.removeNode.byId('geom',$nodeId) YIELD nodeId RETURN nodeId", + Map.of("nodeId", node2)); + assertEquals(node2, removeResult.next().get("nodeId")); + removeResult.close(); + tx.commit(); + } + testResult(db, "CALL spatial.withinDistance('geom',{lon:15.0,lat:60.0},100)", + res -> assertFalse(res.hasNext())); } @Test @@ -916,11 +835,12 @@ public void add_many_nodes_to_the_simple_point_layer_using_addNodes() { // Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison int count = 1000; execute("CALL spatial.addLayer('simple_poi','SimplePoint','')"); - String query = "UNWIND range(1,$count) as i\n" + - "CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)})\n" + - "WITH collect(n) as points\n" + - "CALL spatial.addNodes('simple_poi',points) YIELD count\n" + - "RETURN count"; + String query = """ + UNWIND range(1,$count) as i + CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) + WITH collect(n) as points + CALL spatial.addNodes('simple_poi',points) YIELD count + RETURN count"""; testCountQuery("addNodes", query, count, "count", Map.of("count", count)); testRemoveNodes("simple_poi", count); } @@ -930,11 +850,12 @@ public void add_many_nodes_to_the_simple_point_layer_using_addNode() { // Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison int count = 1000; execute("CALL spatial.addLayer('simple_poi','SimplePoint','')"); - String query = "UNWIND range(1,$count) as i\n" + - "CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)})\n" + - "WITH n\n" + - "CALL spatial.addNode('simple_poi',n) YIELD node\n" + - "RETURN count(node)"; + String query = """ + UNWIND range(1,$count) as i + CREATE (n:Point {id:i, latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) + WITH n + CALL spatial.addNode('simple_poi',n) YIELD node + RETURN count(node)"""; testCountQuery("addNode", query, count, "count(node)", Map.of("count", count)); testRemoveNode("simple_poi", count); } @@ -944,83 +865,29 @@ public void add_many_nodes_to_the_native_point_layer_using_addNodes() { // Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison int count = 1000; execute("CALL spatial.addLayer('native_poi','NativePoint','')"); - String query = "UNWIND range(1,$count) as i\n" + - "WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location\n" + - "CREATE (n:Point {id: i, location:location})\n" + - "WITH collect(n) as points\n" + - "CALL spatial.addNodes('native_poi',points) YIELD count\n" + - "RETURN count"; + String query = """ + UNWIND range(1,$count) as i + WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location + CREATE (n:Point {id: i, location:location}) + WITH collect(n) as points + CALL spatial.addNodes('native_poi',points) YIELD count + RETURN count"""; testCountQuery("addNodes", query, count, "count", Map.of("count", count)); testRemoveNodes("native_poi", count); } - @Test - public void add_node_to_multiple_indexes_in_chunks() { - Example example = docExample("spatial.addLayer", "Add the same node to multiple layers"); - Example example1 = example.runCypher(""" - UNWIND range(1,$count) as i - CREATE (n:Point { - id: i, - point1: point( { latitude: 56.0, longitude: 12.0 } ), - point2: point( { latitude: 57.0, longitude: 13.0 } ) - })""", config -> config.setParams(Map.of("count", 100)).setTitle("Create some nodes")); - Example example2 = example1.runCypher(""" - CALL spatial.addLayer( - 'point1', - 'NativePoint', - 'point1:point1BB', - '{"referenceRelationshipType": "RTREE_P1_TYPE"}' - ) - """, config1 -> config1.setComment(""" - Create a layer `point1` to index property `point1` of node `Point`. - Save the bounding box in the property `point1BB` of the `Point` node. - Associate the node with the index layer via relationship type `RTREE_P1_TYPE`. - """)); - Example example3 = example2.runCypher(""" - CALL spatial.addLayer( - 'point2', - 'NativePoint', - 'point2:point2BB', - '{"referenceRelationshipType": "RTREE_P2_TYPE"}' - ) - """, config -> config.setComment(""" - Create a layer `point2` to index property `point2` of node `Point`. - Save the bounding box in the property `point2BB` of the `Point` node. - Associate the node with the index layer via relationship type `RTREE_P2_TYPE`. - """)); - Example example4 = example3.runCypher(""" - MATCH (p:Point) - WITH (count(p) / 10) AS pages, collect(p) AS nodes - UNWIND range(0, pages) AS i CALL { - WITH i, nodes - CALL spatial.addNodes('point1', nodes[(i * 10)..((i + 1) * 10)]) YIELD count - RETURN count AS count - } IN TRANSACTIONS OF 1 ROWS - RETURN sum(count) AS count - """, config1 -> config1.storeResult().setComment("Index the nodes in layer `point1` in chunks of 10")); - example4.runCypher(""" - MATCH (p:Point) - WITH (count(p) / 10) AS pages, collect(p) AS nodes - UNWIND range(0, pages) AS i CALL { - WITH i, nodes - CALL spatial.addNodes('point2', nodes[(i * 10)..((i + 1) * 10)]) YIELD count - RETURN count AS count - } IN TRANSACTIONS OF 1 ROWS - RETURN sum(count) AS count - """, config -> config.storeResult().setComment("Index the nodes in layer `point2` in chunks of 10")); - } - @Test public void add_many_nodes_to_the_native_point_layer_using_addNode() { // Playing with this number in both tests leads to rough benchmarking of the addNode/addNodes comparison int count = 1000; execute("CALL spatial.addLayer('native_poi','NativePoint','')"); - String query = "UNWIND range(1,$count) as i\n" + - "WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location\n" + - "CREATE (n:Point {id: i, location:location})\n" + - "WITH n\n" + - "CALL spatial.addNode('native_poi',n) YIELD node\n" + - "RETURN count(node)"; + String query = """ + UNWIND range(1,$count) as i + WITH i, Point({latitude:(56.0+toFloat(i)/100.0),longitude:(12.0+toFloat(i)/100.0)}) AS location + CREATE (n:Point {id: i, location:location}) + WITH n + CALL spatial.addNode('native_poi',n) YIELD node + RETURN count(node)"""; testCountQuery("addNode", query, count, "count(node)", Map.of("count", count)); testRemoveNode("native_poi", count); } @@ -1063,36 +930,32 @@ private void testRemoveNodes(String layer, int count) { @Test public void import_shapefile() { - docExample("spatial.importShapefile", "Import a shape-file") - .runCypher("CALL spatial.importShapefile('shp/highway.shp')", ExampleCypher::storeResult) - .assertSingleResult("count", count -> assertEquals(143L, count)) - .runCypher("CALL spatial.layers()", ExampleCypher::storeResult) - .assertResult(r -> Assertions.assertThat(r).hasSize(1)); + testCountQuery("importShapefile", "CALL spatial.importShapefile('../example-data/shp/highway.shp')", 143, + "count", null); + testCallCount(db, "CALL spatial.layers()", null, 1); } @Test public void import_shapefile_without_extension() { - testCountQuery("importShapefile", "CALL spatial.importShapefile('shp/highway')", 143, "count", null); + testCountQuery("importShapefile", "CALL spatial.importShapefile('../example-data/shp/highway')", 143, "count", + null); testCallCount(db, "CALL spatial.layers()", null, 1); } @Test public void import_shapefile_to_layer() { - docExample("spatial.importShapefileToLayer", "Import a shape-file") - .runCypher("CALL spatial.addWKTLayer('geom','wkt')") - .runCypher("CALL spatial.importShapefileToLayer('geom', 'shp/highway.shp')", ExampleCypher::storeResult) - .assertSingleResult("count", count -> assertEquals(143L, count)) - .runCypher("CALL spatial.layers()", ExampleCypher::storeResult) - .assertResult(r -> Assertions.assertThat(r).hasSize(1)); + execute("CALL spatial.addWKTLayer('geom','wkt')"); + testCountQuery("importShapefileToLayer", + "CALL spatial.importShapefileToLayer('geom','../example-data/shp/highway.shp')", 143, + "count", null); + testCallCount(db, "CALL spatial.layers()", null, 1); } + @Test public void import_osm() { - docExample("spatial.importOSM", "Import an OSM file") - .runCypher("CALL spatial.importOSM('map.osm')", ExampleCypher::storeResult) - .assertSingleResult("count", count -> assertEquals(55L, count)) - .runCypher("CALL spatial.layers()", ExampleCypher::storeResult) - .assertResult(r -> Assertions.assertThat(r).hasSize(1)); + testCountQuery("importOSM", "CALL spatial.importOSM('map.osm')", 55, "count", null); + testCallCount(db, "CALL spatial.layers()", null, 1); } @Test @@ -1111,12 +974,9 @@ public void import_osm_without_extension() { @Test public void import_osm_to_layer() { - docExample("spatial.importOSMToLayer", "Import an OSM file") - .runCypher("CALL spatial.addLayer('geom','OSM','')") - .runCypher("CALL spatial.importOSMToLayer('geom','map.osm')", ExampleCypher::storeResult) - .assertSingleResult("count", count -> assertEquals(55L, count)) - .runCypher("CALL spatial.layers()", ExampleCypher::storeResult) - .assertResult(r -> Assertions.assertThat(r).hasSize(1)); + execute("CALL spatial.addLayer('geom','OSM','')"); + testCountQuery("importOSMToLayer", "CALL spatial.importOSMToLayer('geom','map.osm')", 55, "count", null); + testCallCount(db, "CALL spatial.layers()", null, 1); } @Test @@ -1138,15 +998,6 @@ public void import_osm_twice_should_pass_with_different_layers() { 217); } - @Disabled - @Test - public void import_cracow_to_layer() { - execute("CALL spatial.addLayer('geom','OSM','')"); - testCountQuery("importCracowToLayer", "CALL spatial.importOSMToLayer('geom','issue-347/cra.osm')", 256253, - "count", null); - testCallCount(db, "CALL spatial.layers()", null, 1); - } - @Test public void import_osm_to_layer_without_changesets() { execute("CALL spatial.addLayer('osm_example','OSM','')"); @@ -1195,7 +1046,7 @@ public void import_osm_and_polygons_withinDistance() { Node node = (Node) r.get("node"); double distance = (Double) r.get("distance"); Node osmNode = (Node) r.get("osmNode"); - @SuppressWarnings("rawtypes") Map props = (Map) r.get("props"); + @SuppressWarnings({"rawtypes", "unchecked"}) Map props = (Map) r.get("props"); LOGGER.fine( "(node[" + node.getElementId() + "])<-[:GEOM {distance:" + distance + "}]-(osmNode[" + osmNode.getElementId() + "] " + props + ") "); @@ -1219,7 +1070,7 @@ public void import_osm_and_polygons_withinDistance() { assertThat("Point has 2D coordinates", ((Point) geometry).getCoordinate().getCoordinate().length, equalTo(2)); } else if (geometry instanceof Map) { - Map map = (Map) geometry; + @SuppressWarnings("unchecked") Map map = (Map) geometry; assertThat("Geometry should contain a type", map, hasKey("type")); assertThat("Geometry should contain coordinates", map, hasKey("coordinates")); assertThat("Geometry should not be a point", map.get("type"), not(equalTo("Point"))); @@ -1230,23 +1081,6 @@ public void import_osm_and_polygons_withinDistance() { }); } - private void testCountQuery(String name, String query, long count, String column, Map params) { - // warmup - try (Transaction tx = db.beginTx()) { - Result results = tx.execute("EXPLAIN " + query, params == null ? Map.of() : params); - results.close(); - tx.commit(); - } - long start = System.currentTimeMillis(); - testResult(db, query, params, res -> { - assertTrue(res.hasNext(), "Expected a single result"); - long c = (Long) res.next().get(column); - assertFalse(res.hasNext(), "Expected a single result"); - assertEquals(count, c, "Expected count of " + count + " nodes but got " + c); - }); - LOGGER.fine(name + " query took " + (System.currentTimeMillis() - start) + "ms - " + params); - } - @Test public void find_geometries_in_a_bounding_box_short() { execute("CALL spatial.addPointLayerXY('geom','lon','lat')"); @@ -1259,34 +1093,22 @@ public void find_geometries_in_a_bounding_box_short() { @Test public void find_geometries_in_a_bounding_box() { - docExample("spatial.bbox", "Find geometries in a bounding box") - .runCypher("CALL spatial.addPointLayer('geom')") - .runCypher(""" - CREATE (n:Node {id: 1, latitude:60.1,longitude:15.2}) - WITH n CALL spatial.addNode('geom',n) YIELD node - RETURN node - """) - .runCypher("CALL spatial.bbox('geom',{lon:15.0,lat:60.0}, {lon:15.3, lat:61.0})", - c -> c.storeResult().setComment("Find node within bounding box")) - .assertSingleResult("node", o -> assertEquals(1L, ((Node) o).getProperty("id"))); + execute("CALL spatial.addPointLayer('geom')"); + Node node = createNode( + "CREATE (n:Node {latitude:60.1,longitude:15.2}) WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node", + "node"); + testCall(db, "CALL spatial.bbox('geom',{lon:15.0,lat:60.0}, {lon:15.3, lat:61.0})", + r -> assertEquals(node, r.get("node"))); } @Test public void find_geometries_in_a_polygon() { + execute("CALL spatial.addPointLayer('geom')"); + executeWrite( + "UNWIND [{name:'a',latitude:60.1,longitude:15.2},{name:'b',latitude:60.3,longitude:15.5}] as point CREATE (n:Node) SET n += point WITH n CALL spatial.addNode('geom',n) YIELD node RETURN node.name as name"); String polygon = "POLYGON((15.3 60.2, 15.3 60.4, 15.7 60.4, 15.7 60.2, 15.3 60.2))"; - docExample("spatial.intersects", "Find geometries in a polygon") - .runCypher("CALL spatial.addPointLayer('geom')") - .runCypher(""" - UNWIND [ {name:'a',latitude:60.1,longitude:15.2}, {name:'b',latitude:60.3,longitude:15.5} ] as point - CREATE (n:Node) - SET n += point - WITH n - CALL spatial.addNode('geom',n) YIELD node - RETURN node.name as name - """, ExampleCypher::storeResult) - .runCypher("CALL spatial.intersects('geom','" + polygon + "') YIELD node\n RETURN node.name as name", - ExampleCypher::storeResult) - .assertSingleResult("name", name -> assertEquals("b", name)); + testCall(db, "CALL spatial.intersects('geom','" + polygon + "') YIELD node RETURN node.name as name", + r -> assertEquals("b", r.get("name"))); } @Test @@ -1352,12 +1174,7 @@ public void find_geometries_in_a_polygon_hilbert() { @Test public void create_a_WKT_layer() { testCall(db, "CALL spatial.addWKTLayer('geom','wkt')", - r -> assertEquals("wkt", dump(((Node) r.get("node"))).getProperty(PROP_GEOMENCODER_CONFIG))); - } - - private static Node dump(Node n) { - LOGGER.fine("id " + n.getElementId() + " props " + n.getAllProperties()); - return n; + r -> assertEquals("wkt", ((Node) r.get("node")).getProperty(PROP_GEOMENCODER_CONFIG))); } @Test @@ -1366,31 +1183,24 @@ public void add_a_WKT_geometry_to_a_layer() { execute("CALL spatial.addWKTLayer('geom','wkt')"); testCall(db, "CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", lineString), - r -> assertEquals(lineString, dump(((Node) r.get("node"))).getProperty("wkt"))); + r -> assertEquals(lineString, ((Node) r.get("node")).getProperty("wkt"))); } @Test public void find_geometries_close_to_a_point_wkt() { String lineString = "LINESTRING (15.2 60.1, 15.3 60.1)"; - docExample("spatial.addWKT", "Add a WKT geometry to a layer") - .additionalSignature("spatial.addWKTLayer") - .runCypher("CALL spatial.addWKTLayer('geom', 'wkt')") - .runCypher("CALL spatial.addWKT('geom',$wkt)", - c -> c.setParams(Map.of("wkt", lineString)).storeResult()) - .runCypher("CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)", ExampleCypher::storeResult) - .assertSingleResult("node", node -> assertEquals(lineString, (((Node) node)).getProperty("wkt"))); + execute("CALL spatial.addLayer('geom','WKT','wkt')"); + execute("CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", lineString)); + testCall(db, "CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)", + r -> assertEquals(lineString, ((Node) r.get("node")).getProperty("wkt"))); } @Test public void find_geometries_close_to_a_point_geohash() { - var points = List.of("POINT (15.2 60.1)", "POINT (25.2 30.1)"); - docExample("spatial.addWKTs", "Add multiple WKT geometries to a layer") - .additionalSignature("spatial.closest") - .runCypher("CALL spatial.addLayer('geom','geohash','lon:lat')") - .runCypher("CALL spatial.addWKTs('geom',$wkt)", - c -> c.setParams(Map.of("wkt", points)).storeResult()) - .runCypher("CALL spatial.closest('geom',{lon:15.0, lat:60.0}, 1.0)", ExampleCypher::storeResult) - .assertResult(list -> Assertions.assertThat(list).hasSize(1)); + String lineString = "POINT (15.2 60.1)"; + execute("CALL spatial.addLayer('geom','geohash','lon:lat')"); + execute("CALL spatial.addWKT('geom',$wkt)", Map.of("wkt", lineString)); + testCallCount(db, "CALL spatial.closest('geom',{lon:15.2, lat:60.1}, 1.0)", null, 1); } @Test @@ -1428,12 +1238,11 @@ public void testNativePoints() { @Test public void testNativePoints3D() { execute("CREATE (node:Foo { points: [point({latitude: 5.0, longitude: 4.0, height: 1.0}), point({latitude: 6.0, longitude: 5.0, height: 2.0})]})"); - Exception exception = assertThrows(QueryExecutionException.class, () -> { - execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" + - " MATCH (n:Foo)" + - " WITH collect(n) AS nodes" + - " CALL spatial.addNodes('line', nodes) YIELD count RETURN count"); - }); + Exception exception = assertThrows(QueryExecutionException.class, + () -> execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" + + " MATCH (n:Foo)" + + " WITH collect(n) AS nodes" + + " CALL spatial.addNodes('line', nodes) YIELD count RETURN count")); assertEquals( "Failed to invoke procedure `spatial.addNodes`: Caused by: java.lang.IllegalStateException: Trying to decode geometry with wrong CRS: layer configured to crs=7203, but geometry has crs=4979", @@ -1453,12 +1262,11 @@ public void testNativePointsCartesian() { @Test public void testNativePointsCartesian3D() { execute("CREATE (node:Foo { points: [point({x: 5.0, y: 4.0, z: 1}), point({x: 6.0, y: 5.0, z: 2})]})"); - Exception exception = assertThrows(QueryExecutionException.class, () -> { - execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" + - " MATCH (n:Foo)" + - " WITH collect(n) AS nodes" + - " CALL spatial.addNodes('line', nodes) YIELD count RETURN count"); - }); + Exception exception = assertThrows(QueryExecutionException.class, + () -> execute("CALL spatial.addLayer('line','NativePoints','points:bbox:Cartesian') YIELD node" + + " MATCH (n:Foo)" + + " WITH collect(n) AS nodes" + + " CALL spatial.addNodes('line', nodes) YIELD count RETURN count")); assertEquals( "Failed to invoke procedure `spatial.addNodes`: Caused by: java.lang.IllegalStateException: Trying to decode geometry with wrong CRS: layer configured to crs=7203, but geometry has crs=9157", @@ -1467,9 +1275,8 @@ public void testNativePointsCartesian3D() { @Test public void testCQLQuery() { - docExample("spatial.cql", "Find geometries using CQL") - .runCypher("CALL spatial.addWKTLayer('geom','wkt') YIELD node", ExampleCypher::storeResult) - .runCypher(""" + execute("CALL spatial.addWKTLayer('geom','wkt') YIELD node"); + execute(""" CREATE (n1:Node {wkt: 'POINT(15.2 60.1)', name: 'point1'}) CREATE (n2:Node {wkt: 'POINT(25.2 30.1)', name: 'point2'}) WITH n1, n2 @@ -1477,20 +1284,18 @@ public void testCQLQuery() { WITH n2, added1 CALL spatial.addNode('geom', n2) YIELD node as added2 RETURN added1, added2 - """, config -> config.setComment("Create and add nodes with different coordinates")) - .runCypher("CALL spatial.cql('geom', 'name = \\'point1\\'') YIELD node RETURN node.name as name", - ExampleCypher::storeResult) - .assertSingleResult("name", name -> assertEquals("point1", name)); + """); + Object name = executeObject( + "CALL spatial.cql('geom', 'name = \\'point1\\'') YIELD node RETURN node.name as name", "name"); + assertEquals("point1", name); } @Test public void testGetFeatureCount() { - docExample("spatial.getFeatureCount", "Get the number of features in a layer") - .runCypher("CALL spatial.addPointLayer('count_layer') YIELD node", ExampleCypher::storeResult) - .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", - config -> config.storeResult().setComment("Get count of empty layer")) - .assertSingleResult("count", count -> assertEquals(0L, count)) - .runCypher(""" + execute("CALL spatial.addPointLayer('count_layer') YIELD node"); + var count = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count"); + assertEquals(0L, count); + execute(""" CREATE (n1:Point {latitude: 60.1, longitude: 15.2, name: 'point1'}) CREATE (n2:Point {latitude: 60.3, longitude: 15.5, name: 'point2'}) WITH n1, n2 @@ -1498,18 +1303,15 @@ public void testGetFeatureCount() { WITH n2, added1 CALL spatial.addNode('count_layer', n2) YIELD node as added2 RETURN added1, added2 - """, config -> config.setComment("Add two points to the layer")) - .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", - config -> config.storeResult().setComment("Get count after adding points")) - .assertSingleResult("count", count -> assertEquals(2L, count)); + """); + var count2 = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count"); + assertEquals(2L, count2); } @Test public void testGetLayerBoundingBox() { - docExample("spatial.getLayerBoundingBox", "Get the bounding box of a layer") - .runCypher("CALL spatial.addPointLayer('bbox_layer', 'rtree', 'wgs84') YIELD node", - ExampleCypher::storeResult) - .runCypher(""" + execute("CALL spatial.addPointLayer('bbox_layer', 'rtree', 'wgs84') YIELD node"); + execute(""" CREATE (n1:Point {latitude: 60.0, longitude: 15.0, name: 'southwest'}) CREATE (n2:Point {latitude: 61.0, longitude: 16.0, name: 'northeast'}) WITH n1, n2 @@ -1517,102 +1319,91 @@ public void testGetLayerBoundingBox() { WITH n2, added1 CALL spatial.addNode('bbox_layer', n2) YIELD node as added2 RETURN added1, added2 - """, config -> config.setComment("Add points at opposite corners")) - .runCypher("CALL spatial.getLayerBoundingBox('bbox_layer') YIELD minX, minY, maxX, maxY, crs", - ExampleCypher::storeResult) - .assertSingleResult("minX", minX -> assertThat((Double) minX, closeTo(15.0, 0.0001))) - .assertSingleResult("minY", minY -> assertThat((Double) minY, closeTo(60.0, 0.0001))) - .assertSingleResult("maxX", maxX -> assertThat((Double) maxX, closeTo(16.0, 0.0001))) - .assertSingleResult("maxY", maxY -> assertThat((Double) maxY, closeTo(61.0, 0.0001))) - .assertSingleResult("crs", crs -> assertEquals("WGS84(DD)", crs)); + """); + testCall(db, "CALL spatial.getLayerBoundingBox('bbox_layer') YIELD minX, minY, maxX, maxY, crs", (result) -> { + assertThat((Double) result.get("minX"), closeTo(15.0, 0.0001)); + assertThat((Double) result.get("minY"), closeTo(60.0, 0.0001)); + assertThat((Double) result.get("maxX"), closeTo(16.0, 0.0001)); + assertThat((Double) result.get("maxY"), closeTo(61.0, 0.0001)); + assertEquals("WGS84(DD)", result.get("crs")); + }); } @Test public void testLayerMeta() { - docExample("spatial.layerMeta", "Get metadata about a layer") - .runCypher("CALL spatial.addPointLayer('meta_layer', 'rtree', 'wgs84') YIELD node", - ExampleCypher::storeResult) - .runCypher(""" + execute("CALL spatial.addPointLayer('meta_layer', 'rtree', 'wgs84') YIELD node"); + execute(""" CREATE (n1:Point {latitude: 60.0, longitude: 15.0, name: 'southwest'}) WITH n1 CALL spatial.addNode('meta_layer', n1) YIELD node RETURN node - """, config -> config.setComment("Add points at opposite corners")) - .runCypher( - "CALL spatial.layerMeta('meta_layer') YIELD name, geometryType, crs, hasComplexAttributes, extraAttributes", - ExampleCypher::storeResult) - .assertSingleResult("name", name -> assertEquals("meta_layer", name)) - .assertSingleResult("geometryType", - geomType -> assertEquals("org.locationtech.jts.geom.Point", geomType)) - .assertSingleResult("crs", crs -> Assertions.assertThat((String) crs).contains("WGS84(DD)")) - .assertSingleResult("hasComplexAttributes", hasComplex -> assertFalse((Boolean) hasComplex)); + """); + try (Transaction tx = db.beginTx()) { + var result = tx.execute( + "CALL spatial.layerMeta('meta_layer') YIELD name, geometryType, crs, hasComplexAttributes, extraAttributes") + .next(); + assertEquals("meta_layer", result.get("name")); + assertEquals("org.locationtech.jts.geom.Point", result.get("geometryType")); + Assertions.assertThat((String) result.get("crs")).contains("WGS84(DD)"); + assertFalse((Boolean) result.get("hasComplexAttributes")); + } } @Test public void testUpdateWKT() { - docExample("spatial.updateWKT", "Update a node's WKT geometry") - .runCypher("CALL spatial.addWKTLayer('update_layer', 'wkt') YIELD node", ExampleCypher::storeResult) - .runCypher(""" + execute("CALL spatial.addWKTLayer('update_layer', 'wkt') YIELD node"); + execute(""" CREATE (n:Node {wkt: 'POINT(15.2 60.1)', name: 'updatable_point'}) WITH n CALL spatial.addNode('update_layer', n) YIELD node as added_node RETURN n, added_node - """, config -> config.setComment("Create and add a node with initial WKT")) - .runCypher(""" + """); + Object wkt = executeObject(""" MATCH (n:Node {name: 'updatable_point'}) CALL spatial.updateWKT('update_layer', n, 'POINT(25.5 65.5)') YIELD node RETURN node.wkt as wkt - """, - config -> config.storeResult().setComment("Update the node's WKT geometry")) - .assertSingleResult("wkt", wkt -> assertEquals("POINT (25.5 65.5)", wkt)) - .runCypher(""" + """, "wkt"); + assertEquals("POINT (25.5 65.5)", wkt); + Object name = executeObject(""" CALL spatial.withinDistance('update_layer', {longitude: 25.5, latitude: 65.5}, 1) YIELD node RETURN node.name as name - """, - config -> config.storeResult().setComment("Verify the updated geometry is indexed correctly")) - .assertSingleResult("name", name -> assertEquals("updatable_point", name)); + """, "name"); + assertEquals("updatable_point", name); } @Test public void test_spatial_getFeatureCount() { - docExample("spatial.getFeatureCount", "Count features in different layer types") - .runCypher("CALL spatial.addPointLayer('count_layer')", - config -> config.setComment("Create a point layer")) - .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", - config -> config.storeResult().setComment("Count features in empty layer")) - .assertSingleResult("count", count -> assertEquals(0L, count)) - .runCypher(""" + execute("CALL spatial.addPointLayer('count_layer')"); + Object count = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count"); + assertEquals(0L, count); + + execute(""" CREATE (n:Node {latitude: 60.1, longitude: 15.2, name: 'first'}) WITH n CALL spatial.addNode('count_layer', n) YIELD node RETURN node - """, config -> config.setComment("Add one node to the layer")) - .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", - config -> config.storeResult().setComment("Count after adding one feature")) - .assertSingleResult("count", count -> assertEquals(1L, count)) - .runCypher(""" + """); + Object count2 = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count"); + assertEquals(1L, count2); + + execute(""" UNWIND range(1,3) as i CREATE (n:Node {id: i, latitude: (60.0 + i * 0.1), longitude: (15.0 + i * 0.1)}) WITH collect(n) as nodes CALL spatial.addNodes('count_layer', nodes) YIELD count RETURN count - """, config -> config.setComment("Add multiple nodes at once")) - .runCypher("CALL spatial.getFeatureCount('count_layer') YIELD count", - config -> config.storeResult().setComment("Count after adding multiple features")) - .assertSingleResult("count", count -> assertEquals(4L, count)) - .runCypher("CALL spatial.addWKTLayer('wkt_layer', 'wkt')", - config -> config.setComment("Create a WKT layer")) - .runCypher("CALL spatial.getFeatureCount('wkt_layer') YIELD count", - config -> config.storeResult().setComment("Count features in empty WKT layer")) - .assertSingleResult("count", count -> assertEquals(0L, count)) - .runCypher("CALL spatial.addWKT('wkt_layer', 'POINT(15.2 60.1)') YIELD node RETURN node", - config -> config.setComment("Add a WKT point")) - .runCypher( - "CALL spatial.addWKT('wkt_layer', 'LINESTRING (15.2 60.1, 15.3 60.1)') YIELD node RETURN node", - config -> config.setComment("Add a WKT linestring")) - .runCypher("CALL spatial.getFeatureCount('wkt_layer') YIELD count", - config -> config.storeResult().setComment("Count features in WKT layer")) - .assertSingleResult("count", count -> assertEquals(2L, count)); + """); + Object count3 = executeObject("CALL spatial.getFeatureCount('count_layer') YIELD count", "count"); + assertEquals(4L, count3); + + execute("CALL spatial.addWKTLayer('wkt_layer', 'wkt')"); + Object count4 = executeObject("CALL spatial.getFeatureCount('wkt_layer') YIELD count", "count"); + assertEquals(0L, count4); + + execute("CALL spatial.addWKT('wkt_layer', 'POINT(15.2 60.1)') YIELD node RETURN node"); + execute("CALL spatial.addWKT('wkt_layer', 'LINESTRING (15.2 60.1, 15.3 60.1)') YIELD node RETURN node"); + Object count5 = executeObject("CALL spatial.getFeatureCount('wkt_layer') YIELD count", "count"); + assertEquals(2L, count5); // Test error for non-existent layer testCallFails(db, "CALL spatial.getFeatureCount('non_existent_layer')", null, diff --git a/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java b/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java index 6aa93f51a..009fced27 100644 --- a/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java +++ b/server-plugin/src/test/java/org/neo4j/spatial/geotools/plugin/Neo4jSpatialDataStoreTest.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.logging.Logger; import org.assertj.core.api.Assertions; @@ -46,15 +47,21 @@ import org.neo4j.driver.GraphDatabase; import org.neo4j.driver.exceptions.ClientException; import org.neo4j.gis.spatial.LogListener; -import org.neo4j.gis.spatial.Neo4jTestCase; +import org.neo4j.gis.spatial.functions.SpatialFunctions; import org.neo4j.gis.spatial.osm.OSMImporter; +import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.harness.Neo4jBuilder; import org.neo4j.harness.Neo4jBuilders; +import org.neo4j.spatial.testutils.Neo4jTestCase; public class Neo4jSpatialDataStoreTest extends Neo4jTestCase { private static final Logger LOGGER = Logger.getLogger(Neo4jSpatialDataStoreTest.class.getName()); + @Override + protected List> loadProceduresAndFunctions() { + return List.of(SpatialFunctions.class, SpatialProcedures.class); + } @BeforeEach public void setup() throws Exception { diff --git a/test-utils/pom.xml b/test-utils/pom.xml new file mode 100644 index 000000000..64d873223 --- /dev/null +++ b/test-utils/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + org.neo4j + neo4j-spatial + 2025.10.1-SNAPSHOT + + + neo4j-spatial-test-utils + + + + org.neo4j + neo4j-spatial-api + 2025.10.1-SNAPSHOT + + + org.neo4j.test + neo4j-harness + ${neo4j.version} + + + org.junit.jupiter + junit-jupiter-engine + 5.13.4 + + + org.geotools + gt-main + ${geotools.version} + + + org.neo4j.community + it-test-support + ${neo4j.version} + + + diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractApiTest.java b/test-utils/src/main/java/org/neo4j/spatial/testutils/AbstractApiTest.java similarity index 76% rename from server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractApiTest.java rename to test-utils/src/main/java/org/neo4j/spatial/testutils/AbstractApiTest.java index 3ec23a2cf..ec3006de8 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/AbstractApiTest.java +++ b/test-utils/src/main/java/org/neo4j/spatial/testutils/AbstractApiTest.java @@ -18,8 +18,11 @@ * along with this program. If not, see . */ -package org.neo4j.gis.spatial; +package org.neo4j.spatial.testutils; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME; import java.io.File; @@ -28,18 +31,16 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import org.junit.jupiter.api.AfterAll; +import java.util.function.Consumer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.neo4j.configuration.GraphDatabaseSettings; import org.neo4j.dbms.api.DatabaseManagementService; -import org.neo4j.doc.domain.examples.Example; -import org.neo4j.doc.domain.examples.ExamplesRepository; import org.neo4j.exceptions.KernelException; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Transaction; import org.neo4j.internal.helpers.collection.Iterators; import org.neo4j.io.fs.FileUtils; @@ -51,7 +52,6 @@ public abstract class AbstractApiTest { private DatabaseManagementService databases; protected GraphDatabaseService db; - static ExamplesRepository examples; @BeforeEach public void setUp() throws KernelException, IOException { @@ -62,7 +62,6 @@ public void setUp() throws KernelException, IOException { .impermanent() .build(); db = databases.database(DEFAULT_DATABASE_NAME); - examples = new ExamplesRepository(db); registerApiProceduresAndFunctions(); } @@ -124,14 +123,27 @@ protected Object executeObject(String call, Map params, String c return obj; } - - protected Example docExample(@Nonnull String signature, @Nonnull String title) { - return examples.docExample(signature, title); + public static void testResult(GraphDatabaseService db, String call, Map params, + Consumer resultConsumer) { + try (Transaction tx = db.beginTx()) { + Map p = (params == null) ? Map.of() : params; + resultConsumer.accept(tx.execute(call, p)); + tx.commit(); + } } - @AfterAll - public static void generateDocumentation() throws IOException { - examples.write(); + protected void testCountQuery(String name, String query, long count, String column, Map params) { + try (Transaction tx = db.beginTx()) { + Result results = tx.execute("EXPLAIN " + query, params == null ? Map.of() : params); + results.close(); + tx.commit(); + } + testResult(db, query, params, res -> { + assertTrue(res.hasNext(), "Expected a single result"); + long c = (Long) res.next().get(column); + assertFalse(res.hasNext(), "Expected a single result"); + assertEquals(count, c, "Expected count of " + count + " nodes but got " + c); + }); } } diff --git a/server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestCase.java b/test-utils/src/main/java/org/neo4j/spatial/testutils/Neo4jTestCase.java similarity index 82% rename from server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestCase.java rename to test-utils/src/main/java/org/neo4j/spatial/testutils/Neo4jTestCase.java index 39c4039a6..910e2e9d3 100644 --- a/server-plugin/src/test/java/org/neo4j/gis/spatial/Neo4jTestCase.java +++ b/test-utils/src/main/java/org/neo4j/spatial/testutils/Neo4jTestCase.java @@ -17,38 +17,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.neo4j.gis.spatial; - -import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME; +package org.neo4j.spatial.testutils; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.neo4j.configuration.GraphDatabaseInternalSettings; import org.neo4j.configuration.GraphDatabaseSettings; -import org.neo4j.dbms.api.DatabaseManagementService; import org.neo4j.driver.AuthTokens; import org.neo4j.driver.Driver; import org.neo4j.driver.GraphDatabase; -import org.neo4j.gis.spatial.functions.SpatialFunctions; -import org.neo4j.gis.spatial.procedures.SpatialProcedures; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Transaction; import org.neo4j.graphdb.config.Setting; import org.neo4j.harness.Neo4j; import org.neo4j.harness.Neo4jBuilder; import org.neo4j.harness.Neo4jBuilders; /** - * Base class for the meta model tests. + * Base class for the metamodel tests. */ public abstract class Neo4jTestCase { - static final Map, Object> NORMAL_CONFIG = new HashMap<>(); + protected static final Map, Object> NORMAL_CONFIG = new HashMap<>(); static final List closeThreads = new ArrayList<>(); static { @@ -61,7 +58,7 @@ public abstract class Neo4jTestCase { NORMAL_CONFIG.put(GraphDatabaseInternalSettings.trace_cursors, true); } - static final Map, Object> LARGE_CONFIG = new HashMap<>(); + protected static final Map, Object> LARGE_CONFIG = new HashMap<>(); static { //LARGE_CONFIG.put( GraphDatabaseSettings.nodestore_mapped_memory_size.name(), "100M" ); @@ -72,11 +69,11 @@ public abstract class Neo4jTestCase { LARGE_CONFIG.put(GraphDatabaseSettings.pagecache_memory, 100000000L); } - private DatabaseManagementService databases; - private GraphDatabaseService graphDb; private Neo4j neo4j; protected Driver driver; + protected abstract List> loadProceduresAndFunctions(); + /** * Configurable options for text cases, with or without deleting the previous database, and with * or without using the BatchInserter for higher creation speeds. Note that tests that need to @@ -84,12 +81,15 @@ public abstract class Neo4jTestCase { */ @SuppressWarnings("unchecked") @BeforeEach - void setUpDatabase() throws Exception { + void setUpDatabase() { Neo4jBuilder neo4jBuilder = Neo4jBuilders .newInProcessBuilder(getDbPath()) - .withConfig(GraphDatabaseSettings.procedure_unrestricted, List.of("spatial.*")) - .withProcedure(SpatialProcedures.class) - .withFunction(SpatialFunctions.class); + .withConfig(GraphDatabaseSettings.procedure_unrestricted, List.of("spatial.*")); + + loadProceduresAndFunctions().forEach(aClass -> { + neo4jBuilder.withProcedure(aClass); + neo4jBuilder.withFunction(aClass); + }); String largeMode = System.getProperty("spatial.test.large"); if (largeMode != null && largeMode.equalsIgnoreCase("true")) { @@ -122,11 +122,18 @@ static Path getDbPath() { return Path.of("target", "neo4j-db"); } - void printDatabaseStats() { - Neo4jTestUtils.printDatabaseStats(graphDb(), getDbPath().toFile()); + protected void printDatabaseStats() { + SpatialTestUtils.printDatabaseStats(graphDb(), getDbPath().toFile()); } protected GraphDatabaseService graphDb() { - return neo4j.databaseManagementService().database(DEFAULT_DATABASE_NAME); + return neo4j.databaseManagementService().database(GraphDatabaseSettings.DEFAULT_DATABASE_NAME); + } + + protected void inTx(Consumer txFunction) { + try (Transaction tx = graphDb().beginTx()) { + txFunction.accept(tx); + tx.commit(); + } } } diff --git a/test-utils/src/main/java/org/neo4j/spatial/testutils/SpatialTestUtils.java b/test-utils/src/main/java/org/neo4j/spatial/testutils/SpatialTestUtils.java new file mode 100644 index 000000000..59abff694 --- /dev/null +++ b/test-utils/src/main/java/org/neo4j/spatial/testutils/SpatialTestUtils.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j Spatial. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.neo4j.spatial.testutils; + +import java.io.File; +import java.util.Objects; +import org.locationtech.jts.geom.Envelope; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Transaction; +import org.neo4j.spatial.api.layer.Layer; + +// TODO do we need this one? +public class SpatialTestUtils { + + public static void debugEnvelope(Envelope bbox, String layer, String name) { + System.out.println("Layer '" + layer + "' has envelope '" + name + "': " + bbox); + System.out.println("\tX: [" + bbox.getMinX() + ":" + bbox.getMaxX() + "]"); + System.out.println("\tY: [" + bbox.getMinY() + ":" + bbox.getMaxY() + "]"); + } + + public static int checkIndexCount(Transaction tx, Layer layer) { + if (layer.getIndex().count(tx) < 1) { + System.out.println("Warning: index count zero: " + layer.getName()); + } + System.out.println( + "Layer '" + layer.getName() + "' has " + layer.getIndex().count(tx) + " entries in the index"); + return layer.getIndex().count(tx); + } + + public static void printDatabaseStats(GraphDatabaseService db, File path) { + System.out.println("Database stats:"); + System.out.println("\tTotal disk usage: " + (databaseDiskUsage(path)) / (1024.0 * 1024.0) + "MB"); + System.out.println("\tTotal # nodes: " + getNumberOfNodes(db)); + System.out.println("\tTotal # rels: " + getNumberOfRelationships(db)); + } + + private static long calculateDiskUsage(File file) { + if (file.isDirectory()) { + long count = 0; + for (File sub : Objects.requireNonNull(file.listFiles())) { + count += calculateDiskUsage(sub); + } + return count; + } + return file.length(); + } + + private static long databaseDiskUsage(File path) { + return calculateDiskUsage(path); + } + + private static long getNumberOfNodes(GraphDatabaseService db) { + try (Transaction tx = db.beginTx()) { + return (Long) tx.execute("MATCH (n) RETURN count(n)").columnAs("count(n)").next(); + } + } + + private static long getNumberOfRelationships(GraphDatabaseService db) { + try (Transaction tx = db.beginTx()) { + return (Long) tx.execute("MATCH ()-[r]->() RETURN count(r)").columnAs("count(r)").next(); + } + } + +}