Skip to content

Commit

Permalink
Improved read-transactions in Neo4jSpatialDataStore
Browse files Browse the repository at this point in the history
  • Loading branch information
craigtaverner committed Jul 29, 2016
1 parent 1432eea commit 7a7dedc
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 38 deletions.
47 changes: 26 additions & 21 deletions src/main/java/org/geotools/data/neo4j/Neo4jSpatialDataStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,23 @@ public Neo4jSpatialDataStore(GraphDatabaseService database) {
* @return layer names
*/
public String[] getTypeNames() throws IOException {
if (typeNames == null) {
List<String> notEmptyTypes = new ArrayList<String>();
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<String> notEmptyTypes = new ArrayList<String>();
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;
}

/**
Expand Down Expand Up @@ -260,13 +263,15 @@ public FeatureWriter<SimpleFeatureType, SimpleFeature> 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -76,30 +77,39 @@ 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;
}
}

/**
*
*/
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());
}
}

/**
Expand Down
80 changes: 80 additions & 0 deletions src/test/java/org/neo4j/gis/spatial/Neo4jSpatialDataStoreTest.java
Original file line number Diff line number Diff line change
@@ -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"));
}


}

0 comments on commit 7a7dedc

Please sign in to comment.