diff --git a/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java b/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java index 19fd50347..c9d01b843 100644 --- a/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java +++ b/src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java @@ -97,20 +97,23 @@ public Neo4jSpatialDataStore(GraphDatabaseService database) { * @return layer names */ public String[] getTypeNames() throws IOException { - if (typeNames == null) { - List notEmptyTypes = new ArrayList(); - String[] allTypeNames = spatialDatabase.getLayerNames(); - for (int i = 0; i < allTypeNames.length; i++) { - // discard empty layers - System.out.print( "loading layer " + allTypeNames[i] ); - Layer layer = spatialDatabase.getLayer(allTypeNames[i]); - if (!layer.getIndex().isEmpty()) { - notEmptyTypes.add(allTypeNames[i]); - } - } - typeNames = notEmptyTypes.toArray(new String[] {}); - } - return typeNames; + if (typeNames == null) { + try (Transaction tx = database.beginTx()) { + List notEmptyTypes = new ArrayList(); + String[] allTypeNames = spatialDatabase.getLayerNames(); + for (int i = 0; i < allTypeNames.length; i++) { + // discard empty layers + System.out.print("loading layer " + allTypeNames[i]); + Layer layer = spatialDatabase.getLayer(allTypeNames[i]); + if (!layer.getIndex().isEmpty()) { + notEmptyTypes.add(allTypeNames[i]); + } + } + typeNames = notEmptyTypes.toArray(new String[]{}); + tx.success(); + } + } + return typeNames; } /** @@ -260,13 +263,15 @@ public FeatureWriter getFeatureWriter(String t public ReferencedEnvelope getBounds(String typeName) { ReferencedEnvelope result = boundsIndex.get(typeName); - if (result == null) { - Envelope bbox = Utilities.fromNeo4jToJts( - spatialDatabase.getLayer(typeName).getIndex().getBoundingBox()); - result = convertEnvelopeToRefEnvelope(typeName, bbox); - boundsIndex.put(typeName, result); - } - return result; + if (result == null) { + Layer layer = spatialDatabase.getLayer(typeName); + if (layer != null) { + Envelope bbox = Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox()); + result = convertEnvelopeToRefEnvelope(typeName, bbox); + boundsIndex.put(typeName, result); + } + } + return result; } public SpatialDatabaseService getSpatialDatabaseService() { diff --git a/src/main/java/org/geotools/data/neo4j/Neo4jSpatialFeatureReader.java b/src/main/java/org/geotools/data/neo4j/Neo4jSpatialFeatureReader.java index a444a4783..ef8aca265 100644 --- a/src/main/java/org/geotools/data/neo4j/Neo4jSpatialFeatureReader.java +++ b/src/main/java/org/geotools/data/neo4j/Neo4jSpatialFeatureReader.java @@ -28,6 +28,7 @@ import org.geotools.feature.simple.SimpleFeatureBuilder; import org.neo4j.gis.spatial.Layer; import org.neo4j.gis.spatial.SpatialDatabaseRecord; +import org.neo4j.graphdb.Transaction; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; @@ -76,7 +77,12 @@ public SimpleFeatureType getFeatureType() { * */ public boolean hasNext() throws IOException { - return results != null && results.hasNext(); + if (results == null) return false; + try (Transaction tx = layer.getSpatialDatabase().getDatabase().beginTx()) { + boolean ans = results.hasNext(); + tx.success(); + return ans; + } } /** @@ -84,22 +90,26 @@ public boolean hasNext() throws IOException { */ public SimpleFeature next() throws IOException, IllegalArgumentException, NoSuchElementException { if (results == null) return null; - - SpatialDatabaseRecord record = results.next(); - if (record == null) return null; - - builder.reset(); - builder.set(FEATURE_PROP_GEOM, record.getGeometry()); - - if (extraPropertyNames != null) { - for (int i = 0; i < extraPropertyNames.length; i++) { - if (record.hasProperty(extraPropertyNames[i])) { - builder.set(extraPropertyNames[i], record.getProperty(extraPropertyNames[i])); - } - } - } - - return builder.buildFeature(record.getId()); + + try (Transaction tx = layer.getSpatialDatabase().getDatabase().beginTx()) { + SpatialDatabaseRecord record = results.next(); + if (record == null) return null; + + builder.reset(); + + builder.set(FEATURE_PROP_GEOM, record.getGeometry()); + + if (extraPropertyNames != null) { + for (int i = 0; i < extraPropertyNames.length; i++) { + if (record.hasProperty(extraPropertyNames[i])) { + builder.set(extraPropertyNames[i], record.getProperty(extraPropertyNames[i])); + } + } + } + tx.success(); + + return builder.buildFeature(record.getId()); + } } /** diff --git a/src/test/java/org/neo4j/gis/spatial/Neo4jSpatialDataStoreTest.java b/src/test/java/org/neo4j/gis/spatial/Neo4jSpatialDataStoreTest.java new file mode 100644 index 000000000..3a4b6ce33 --- /dev/null +++ b/src/test/java/org/neo4j/gis/spatial/Neo4jSpatialDataStoreTest.java @@ -0,0 +1,80 @@ +package org.neo4j.gis.spatial; + +import org.geotools.data.neo4j.Neo4jSpatialDataStore; +import org.geotools.data.simple.SimpleFeatureCollection; +import org.geotools.data.simple.SimpleFeatureSource; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.referencing.crs.DefaultGeographicCRS; +import org.junit.Before; +import org.junit.Test; +import org.neo4j.gis.spatial.osm.OSMImporter; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; + +import javax.xml.stream.XMLStreamException; +import java.io.IOException; +import java.nio.charset.Charset; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +public class Neo4jSpatialDataStoreTest { + + public GraphDatabaseService graph; + + @Before + public void setup() throws IOException, XMLStreamException { + this.graph = new TestGraphDatabaseFactory().newImpermanentDatabase(); + OSMImporter importer = new OSMImporter("map", new ConsoleListener()); + importer.setCharset(Charset.forName("UTF-8")); + importer.setVerbose(false); + importer.importFile(graph, "map.osm"); + importer.reIndex(graph); + } + + @Test + public void shouldOpenDataStore() { + Neo4jSpatialDataStore store = new Neo4jSpatialDataStore(graph); + ReferencedEnvelope bounds = store.getBounds("map"); + assertThat(bounds, equalTo(new ReferencedEnvelope(12.7856667, 13.2873561, 55.9254241, 56.2179056, DefaultGeographicCRS.WGS84))); + } + + @Test + public void shouldOpenDataStoreOnNonSpatialDatabase() { + GraphDatabaseService db = new TestGraphDatabaseFactory().newImpermanentDatabase(); + Neo4jSpatialDataStore store = new Neo4jSpatialDataStore(db); + ReferencedEnvelope bounds = store.getBounds("map"); + // TODO: rather should throw a descriptive exception + assertThat(bounds, equalTo(null)); + } + + @Test + public void shouldBeAbleToListLayers() throws IOException { + Neo4jSpatialDataStore store = new Neo4jSpatialDataStore(graph); + String[] layers = store.getTypeNames(); + assertThat("Expected one layer", layers.length, equalTo(1)); + assertThat(layers[0], equalTo("map")); + } + + @Test + public void shouldBeAbleToGetSchemaForLayer() throws IOException { + Neo4jSpatialDataStore store = new Neo4jSpatialDataStore(graph); + SimpleFeatureType schema = store.getSchema("map"); + assertThat("Expected 25 attributes", schema.getAttributeCount(), equalTo(25)); + assertThat("Expected geometry attribute to be called 'the_geom'", schema.getAttributeDescriptors().get(0).getLocalName(), equalTo("the_geom")); + } + + @Test + public void shouldBeAbleToGetFeatureSourceForLayer() throws IOException { + Neo4jSpatialDataStore store = new Neo4jSpatialDataStore(graph); + SimpleFeatureSource source = store.getFeatureSource("map"); + SimpleFeatureCollection features = source.getFeatures(); + assertThat("Expected 217 features", features.size(), equalTo(217)); + SimpleFeature feature = features.features().next(); + assertThat("Expected first feature to have name 'Nybrodalsvägen'", feature.getAttribute("name").toString(), equalTo("Nybrodalsvägen")); + } + + +}