Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental feature to merge multiple OSM layers together #380

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@
public abstract class AbstractGeometryEncoder implements GeometryEncoder, Constants {

protected String bboxProperty = PROP_BBOX;

// Public methods
protected Layer layer;

@Override
public void init(Layer layer) {
public void init(Transaction tx, Layer layer) {
this.layer = layer;
}

Expand Down Expand Up @@ -116,8 +115,4 @@ public Object getAttribute(Node geomNode, String name) {
public String getSignature() {
return "GeometryEncoder(bbox='" + bboxProperty + "')";
}

// Attributes

protected Layer layer;
}
23 changes: 12 additions & 11 deletions src/main/java/org/neo4j/gis/spatial/DefaultLayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.neo4j.gis.spatial.rtree.filter.SearchFilter;
import org.neo4j.gis.spatial.utilities.GeotoolsAdapter;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;

/**
Expand All @@ -55,7 +56,12 @@
*/
public class DefaultLayer implements Constants, Layer, SpatialDataset {

// Public methods
private String name;
protected String layerNodeId;
private GeometryEncoder geometryEncoder;
private GeometryFactory geometryFactory;
protected LayerIndexReader indexReader;
protected SpatialIndexWriter indexWriter;

@Override
public String getName() {
Expand Down Expand Up @@ -231,7 +237,7 @@ public void initialize(Transaction tx, IndexManager indexManager, String name, N
} else {
this.geometryEncoder = new WKBGeometryEncoder();
}
this.geometryEncoder.init(this);
this.geometryEncoder.init(tx, this);

// index must be created *after* geometryEncoder
if (layerNode.hasProperty(PROP_INDEX_CLASS)) {
Expand Down Expand Up @@ -273,6 +279,10 @@ public Node getLayerNode(Transaction tx) {
public void delete(Transaction tx, Listener monitor) {
indexWriter.removeAll(tx, true, monitor);
Node layerNode = getLayerNode(tx);
for (Relationship rel : layerNode.getRelationships()) {
System.out.printf("Unexpected relationship in layer %s: %s%n", getName(), rel);
rel.delete();
}
layerNode.delete();
layerNodeId = null;
}
Expand All @@ -283,15 +293,6 @@ public void delete(Transaction tx, Listener monitor) {
// return spatialDatabase.getDatabase();
// }

// Attributes

//private SpatialDatabaseService spatialDatabase;
private String name;
protected String layerNodeId = null;
private GeometryEncoder geometryEncoder;
private GeometryFactory geometryFactory;
protected LayerIndexReader indexReader;
protected SpatialIndexWriter indexWriter;

@Override
public SpatialDataset getDataset() {
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/org/neo4j/gis/spatial/GeometryEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ public interface GeometryEncoder extends EnvelopeDecoder {
* that represents a layer. This node is expected to have a property containing the class name
* of the GeometryEncoder for that layer, and it will be constructed and passed the layer using
* this method, allowing the Layer and the GeometryEncoder to interact.
*
* @param layer recently created Layer class
*/
void init(Layer layer);
void init(Transaction tx, Layer layer);

/**
* This method is called to store a bounding box for the geometry to the database. It should write it to the
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/org/neo4j/gis/spatial/SpatialTopologyUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public static List<PointResult> findClosestEdges(Transaction tx, Point point, La
* @see <a
* href="https://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478">SDO_LRS.LOCATE_PT</a>
* @see <a
* href="https://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
* href="https://locationtech.github.io/jts/javadoc/org/locationtech/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
*/
public static Point locatePoint(Layer layer, Geometry geometry, double measure) {
return layer.getGeometryFactory().createPoint(locatePoint(geometry, measure));
Expand All @@ -171,7 +171,7 @@ public static Point locatePoint(Layer layer, Geometry geometry, double measure)
* @see <a
* href="https://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478">SDO_LRS.LOCATE_PT</a>
* @see <a
* href="https://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
* href="https://locationtech.github.io/jts/javadoc/org/locationtech/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
*/
public static Coordinate locatePoint(Geometry geometry, double measure) {
return new LengthIndexedLine(geometry).extractPoint(measure);
Expand All @@ -194,7 +194,7 @@ public static Coordinate locatePoint(Geometry geometry, double measure) {
* @see <a
* href="https://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478">SDO_LRS.LOCATE_PT</a>
* @see <a
* href="https://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
* href="https://locationtech.github.io/jts/javadoc/org/locationtech/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
*/
public static Point locatePoint(Layer layer, Geometry geometry, double measure, double offset) {
return layer.getGeometryFactory().createPoint(locatePoint(geometry, measure, offset));
Expand All @@ -215,7 +215,7 @@ public static Point locatePoint(Layer layer, Geometry geometry, double measure,
* @see <a
* href="https://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478">SDO_LRS.LOCATE_PT</a>
* @see <a
* href="https://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
* href="https://locationtech.github.io/jts/javadoc/org/locationtech/jts/linearref/LengthIndexedLine.html">LengthIndexedLine</a>
*/
public static Coordinate locatePoint(Geometry geometry, double measure, double offset) {
return new LengthIndexedLine(geometry).extractPoint(measure, offset);
Expand Down
96 changes: 96 additions & 0 deletions src/main/java/org/neo4j/gis/spatial/merge/MergeUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.gis.spatial.merge;

import java.util.ArrayList;
import org.locationtech.jts.geom.Geometry;
import org.neo4j.gis.spatial.EditableLayer;
import org.neo4j.gis.spatial.GeometryEncoder;
import org.neo4j.gis.spatial.encoders.Configurable;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;

public class MergeUtils {

public interface Mergeable {

long mergeFrom(Transaction tx, EditableLayer other);
}

private static boolean encodersIdentical(EditableLayer layer, EditableLayer mergeLayer) {
GeometryEncoder layerEncoder = layer.getGeometryEncoder();
GeometryEncoder mergeEncoder = mergeLayer.getGeometryEncoder();
Class<? extends GeometryEncoder> layerGeometryClass = layerEncoder.getClass();
Class<? extends GeometryEncoder> mergeGeometryClass = mergeEncoder.getClass();
if (layerGeometryClass.isAssignableFrom(mergeGeometryClass)) {
if (mergeEncoder instanceof Configurable && layerEncoder instanceof Configurable) {
String mergeConfig = ((Configurable) mergeEncoder).getConfiguration();
String layerConfig = ((Configurable) layerEncoder).getConfiguration();
return mergeConfig.equals(layerConfig);
} else {
// If one is configurable, but not the other, they are not identical
return !(mergeEncoder instanceof Configurable || layerEncoder instanceof Configurable);
}
}
return false;
}

public static long mergeLayerInto(Transaction tx, EditableLayer layer, EditableLayer mergeLayer) {
long count;
Class<? extends EditableLayer> layerClass = layer.getClass();
Class<? extends EditableLayer> mergeClass = mergeLayer.getClass();
if (layer instanceof Mergeable) {
count = ((Mergeable) layer).mergeFrom(tx, mergeLayer);
} else if (layerClass.isAssignableFrom(mergeClass)) {
if (encodersIdentical(layer, mergeLayer)) {
// With identical encoders, we can simply add the node as is, but must remove it first
ArrayList<Node> toAdd = new ArrayList<>();
for (Node node : mergeLayer.getIndex().getAllIndexedNodes(tx)) {
toAdd.add(node);
}
for (Node node : toAdd) {
// Remove each from the previous index before adding to the new index, so as not to have multiple incoming RTREE_REFERENCE
mergeLayer.removeFromIndex(tx, node.getElementId());
layer.add(tx, node);
}
count = toAdd.size();
} else {
// With differing encoders, we must decode and re-encode each geometry, so we also create new nodes and remove and delete the actual nodes later
ArrayList<Node> toRemove = new ArrayList<>();
GeometryEncoder fromEncoder = mergeLayer.getGeometryEncoder();
for (Node node : mergeLayer.getIndex().getAllIndexedNodes(tx)) {
toRemove.add(node);
Geometry geometry = fromEncoder.decodeGeometry(node);
layer.add(tx, geometry);
}
for (Node remove : toRemove) {
mergeLayer.removeFromIndex(tx, remove.getElementId());
remove.delete();
}
count = toRemove.size();
}
} else {
throw new IllegalArgumentException(String.format(
"Cannot merge '%s' into '%s': layer classes are not compatible: '%s' cannot be caste as '%s'",
mergeLayer.getName(), layer.getName(), mergeClass.getSimpleName(), layerClass.getSimpleName()));
}
return count;
}
}
Loading