dynamicLayers = new ArrayList<>();
- try (var layers = tx.findNodes(LABEL_LAYER)) {
- while (layers.hasNext()) {
- Node node = layers.next();
- if (!node.getProperty(PROP_LAYER_CLASS, "").toString().startsWith("DefaultLayer")) {
- Layer layer = LayerUtilities.makeLayerFromNode(tx, indexManager, node);
- if (layer instanceof DynamicLayer) {
- dynamicLayers.add((DynamicLayer) LayerUtilities.makeLayerFromNode(tx, indexManager, node));
- }
- }
- }
- }
- for (DynamicLayer layer : dynamicLayers) {
- for (String dynLayerName : layer.getLayerNames(tx)) {
- if (name.equals(dynLayerName)) {
- return layer.getLayer(tx, dynLayerName);
- }
- }
- }
- return null;
- }
-
- /**
- * Convert a layer into a DynamicLayer. This will expose the ability to add
- * views, or 'dynamic layers' to the layer.
- *
- * @return new DynamicLayer version of the original layer
- */
- public DynamicLayer asDynamicLayer(Transaction tx, Layer layer) {
- if (layer instanceof DynamicLayer) {
- return (DynamicLayer) layer;
- } else {
- Node node = layer.getLayerNode(tx);
- node.setProperty(PROP_LAYER_CLASS, DynamicLayer.class.getCanonicalName());
- return (DynamicLayer) LayerUtilities.makeLayerFromNode(tx, indexManager, node);
- }
- }
-
- public DefaultLayer getOrCreateDefaultLayer(Transaction tx, String name) {
- return (DefaultLayer) getOrCreateLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class, "");
- }
-
- public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String format, String propertyNameConfig) {
- Class extends GeometryEncoder> geClass = WKBGeometryEncoder.class;
- if (format != null && format.toUpperCase().startsWith("WKT")) {
- geClass = WKTGeometryEncoder.class;
- }
- return (EditableLayer) getOrCreateLayer(tx, name, geClass, EditableLayerImpl.class, propertyNameConfig);
- }
-
- public EditableLayer getOrCreateEditableLayer(Transaction tx, String name) {
- return getOrCreateEditableLayer(tx, name, "WKB", "");
- }
-
- public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String wktProperty) {
- return getOrCreateEditableLayer(tx, name, "WKT", wktProperty);
- }
-
- public static final String RTREE_INDEX_NAME = "rtree";
- public static final String GEOHASH_INDEX_NAME = "geohash";
-
- public Class extends LayerIndexReader> resolveIndexClass(String index) {
- if (index == null) {
- return LayerRTreeIndex.class;
- }
- switch (index.toLowerCase()) {
- case RTREE_INDEX_NAME:
- return LayerRTreeIndex.class;
- case GEOHASH_INDEX_NAME:
- return LayerGeohashPointIndex.class;
- case "zorder":
- return LayerZOrderPointIndex.class;
- case "hilbert":
- return LayerHilbertPointIndex.class;
- }
- throw new IllegalArgumentException("Unknown index: " + index);
- }
-
- public EditableLayer getOrCreateSimplePointLayer(Transaction tx, String name, String index, String xProperty, String yProperty) {
- return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, xProperty, yProperty);
- }
-
- public EditableLayer getOrCreateNativePointLayer(Transaction tx, String name, String index, String locationProperty) {
- return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, locationProperty);
- }
-
- public EditableLayer getOrCreatePointLayer(Transaction tx, String name, Class extends LayerIndexReader> indexClass, Class extends GeometryEncoder> encoderClass, String... encoderConfig) {
- Layer layer = getLayer(tx, name);
- if (layer == null) {
- return (EditableLayer) createLayer(tx, name, encoderClass, SimplePointLayer.class, indexClass, makeEncoderConfig(encoderConfig), DefaultGeographicCRS.WGS84);
- } else if (layer instanceof EditableLayer) {
- return (EditableLayer) layer;
- } else {
- throw new SpatialDatabaseException("Existing layer '" + layer + "' is not of the expected type: " + EditableLayer.class);
- }
- }
-
- public Layer getOrCreateLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoder, Class extends Layer> layerClass, String config) {
- Layer layer = getLayer(tx, name);
- if (layer == null) {
- layer = createLayer(tx, name, geometryEncoder, layerClass, null, config);
- } else if (!(layerClass == null || layerClass.isInstance(layer))) {
- throw new SpatialDatabaseException("Existing layer '" + layer + "' is not of the expected type: " + layerClass);
- }
- return layer;
- }
-
- public Layer getOrCreateLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoder, Class extends Layer> layerClass) {
- return getOrCreateLayer(tx, name, geometryEncoder, layerClass, "");
- }
-
- /**
- * This method will find the Layer when given a geometry node that this layer contains. This method
- * used to make use of knowledge of the RTree, traversing backwards up the tree to find the layer node, which is fast. However, for reasons of clean abstraction,
- * this has been refactored to delegate the logic to the layer, so that each layer can do this in an
- * implementation specific way. Now we simply iterate through the layers datasets and the first one
- * to return true on the SpatialDataset.containsGeometryNode(Transaction,Node) method is returned.
- *
- * We can consider removing this method for a few reasons:
- * * It is non-deterministic if more than one layer contains the same geometry
- * * None of the current code appears to use this method
- *
- * @param geometryNode to start search
- * @return Layer object containing this geometry
- */
- public Layer findLayerContainingGeometryNode(Transaction tx, Node geometryNode) {
- for (String layerName : getLayerNames(tx)) {
- Layer layer = getLayer(tx, layerName);
- if (layer.getDataset().containsGeometryNode(tx, geometryNode)) {
- return layer;
- }
- }
- return null;
- }
-
- private Layer getLayerFromChild(Transaction tx, Node child, RelationshipType relType) {
- Relationship indexRel = child.getSingleRelationship(relType, Direction.INCOMING);
- if (indexRel != null) {
- Node layerNode = indexRel.getStartNode();
- if (layerNode.hasProperty(PROP_LAYER)) {
- return LayerUtilities.makeLayerFromNode(tx, indexManager, layerNode);
- }
- }
- return null;
- }
-
- public boolean containsLayer(Transaction tx, String name) {
- return getLayer(tx, name) != null;
- }
-
- public Layer createWKBLayer(Transaction tx, String name) {
- return createLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class);
- }
-
- public SimplePointLayer createSimplePointLayer(Transaction tx, String name) {
- return createSimplePointLayer(tx, name, (String[]) null);
- }
-
- public SimplePointLayer createSimplePointLayer(Transaction tx, String name, String xProperty, String yProperty) {
- return createSimplePointLayer(tx, name, xProperty, yProperty, null);
- }
-
- public SimplePointLayer createSimplePointLayer(Transaction tx, String name, String... xybProperties) {
- return createPointLayer(tx, name, LayerRTreeIndex.class, SimplePointEncoder.class, xybProperties);
- }
-
- public SimplePointLayer createNativePointLayer(Transaction tx, String name) {
- return createNativePointLayer(tx, name, (String[]) null);
- }
-
- public SimplePointLayer createNativePointLayer(Transaction tx, String name, String locationProperty, String bboxProperty) {
- return createNativePointLayer(tx, name, locationProperty, bboxProperty, null);
- }
-
- public SimplePointLayer createNativePointLayer(Transaction tx, String name, String... encoderConfig) {
- return createPointLayer(tx, name, LayerRTreeIndex.class, NativePointEncoder.class, encoderConfig);
- }
-
- public SimplePointLayer createPointLayer(Transaction tx, String name, Class extends LayerIndexReader> indexClass, Class extends GeometryEncoder> encoderClass, String... encoderConfig) {
- return (SimplePointLayer) createLayer(tx, name, encoderClass, SimplePointLayer.class, indexClass,
- makeEncoderConfig(encoderConfig), org.geotools.referencing.crs.DefaultGeographicCRS.WGS84);
- }
-
- public String makeEncoderConfig(String... args) {
- StringBuilder sb = new StringBuilder();
- if (args != null) {
- for (String arg : args) {
- if (arg != null) {
- if (sb.length() > 0)
- sb.append(":");
- sb.append(arg);
- }
- }
- }
- return sb.toString();
- }
-
- public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass, Class extends Layer> layerClass) {
- return createLayer(tx, name, geometryEncoderClass, layerClass, null, null);
- }
-
- public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
- Class extends Layer> layerClass, Class extends LayerIndexReader> indexClass,
- String encoderConfig) {
- return createLayer(tx, name, geometryEncoderClass, layerClass, indexClass, encoderConfig, null);
- }
-
- public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
- Class extends Layer> layerClass, Class extends LayerIndexReader> indexClass,
- String encoderConfig, CoordinateReferenceSystem crs) {
- if (containsLayer(tx, name))
- throw new SpatialDatabaseException("Layer " + name + " already exists");
-
- Layer layer = LayerUtilities.makeLayerAndNode(tx, indexManager, name, geometryEncoderClass, layerClass, indexClass);
- if (encoderConfig != null && encoderConfig.length() > 0) {
- GeometryEncoder encoder = layer.getGeometryEncoder();
- if (encoder instanceof Configurable) {
- ((Configurable) encoder).setConfiguration(encoderConfig);
- layer.getLayerNode(tx).setProperty(PROP_GEOMENCODER_CONFIG, encoderConfig);
- } else {
- System.out.println("Warning: encoder configuration '" + encoderConfig + "' passed to non-configurable encoder: " + geometryEncoderClass);
- }
- }
- if (crs != null && layer instanceof EditableLayer) {
- ((EditableLayer) layer).setCoordinateReferenceSystem(tx, crs);
- }
- return layer;
- }
-
- public void deleteLayer(Transaction tx, String name, Listener monitor) {
- Layer layer = getLayer(tx, name);
- if (layer == null) throw new SpatialDatabaseException("Layer " + name + " does not exist");
- layer.delete(tx, monitor);
- }
-
- public static int convertGeometryNameToType(String geometryName) {
- if (geometryName == null) return GTYPE_GEOMETRY;
- try {
- return convertJtsClassToGeometryType((Class extends Geometry>) Class.forName("org.locationtech.jts.geom." + geometryName));
- } catch (ClassNotFoundException e) {
- System.err.println("Unrecognized geometry '" + geometryName + "': " + e);
- return GTYPE_GEOMETRY;
- }
- }
-
- public static String convertGeometryTypeToName(Integer geometryType) {
- return convertGeometryTypeToJtsClass(geometryType).getName().replace("org.locationtech.jts.geom.", "");
- }
-
- public static Class extends Geometry> convertGeometryTypeToJtsClass(Integer geometryType) {
- switch (geometryType) {
- case GTYPE_POINT:
- return Point.class;
- case GTYPE_LINESTRING:
- return LineString.class;
- case GTYPE_POLYGON:
- return Polygon.class;
- case GTYPE_MULTIPOINT:
- return MultiPoint.class;
- case GTYPE_MULTILINESTRING:
- return MultiLineString.class;
- case GTYPE_MULTIPOLYGON:
- return MultiPolygon.class;
- default:
- return Geometry.class;
- }
- }
-
- public static int convertJtsClassToGeometryType(Class extends Geometry> jtsClass) {
- if (jtsClass.equals(Point.class)) {
- return GTYPE_POINT;
- } else if (jtsClass.equals(LineString.class)) {
- return GTYPE_LINESTRING;
- } else if (jtsClass.equals(Polygon.class)) {
- return GTYPE_POLYGON;
- } else if (jtsClass.equals(MultiPoint.class)) {
- return GTYPE_MULTIPOINT;
- } else if (jtsClass.equals(MultiLineString.class)) {
- return GTYPE_MULTILINESTRING;
- } else if (jtsClass.equals(MultiPolygon.class)) {
- return GTYPE_MULTIPOLYGON;
- } else {
- return GTYPE_GEOMETRY;
- }
- }
-
- /**
- * Create a new layer from the results of a previous query. This actually
- * copies the resulting geometries and their attributes into entirely new
- * geometries using WKBGeometryEncoder. This means it is independent of the
- * format of the original data. As a consequence it will have lost any
- * domain specific capabilities of the original graph, if any. Use it only
- * if you want a copy of the geometries themselves, and nothing more. One
- * common use case would be to create a temporary layer of the results of a
- * query than you wish to now export to a format that only supports
- * geometries, like Shapefile, or the PNG images produced by the
- * ImageExporter.
- *
- * @param layerName name of new layer to create
- * @param results collection of SpatialDatabaseRecords to add to new layer
- * @return new Layer with copy of all geometries
- */
- public Layer createResultsLayer(Transaction tx, String layerName, List results) {
- EditableLayer layer = (EditableLayer) createWKBLayer(tx, layerName);
- for (SpatialDatabaseRecord record : results) {
- layer.add(tx, record.getGeometry());
- }
- return layer;
- }
-
-
- /**
- * Support mapping a String (ex: 'SimplePoint') to the respective GeometryEncoder and Layer classes
- * to allow for more streamlined method for creating Layers
- * This was added to help support Spatial Cypher project.
- */
- public static class RegisteredLayerType {
- String typeName;
- Class extends GeometryEncoder> geometryEncoder;
- Class extends Layer> layerClass;
- Class extends LayerIndexReader> layerIndexClass;
- String defaultConfig;
- org.geotools.referencing.crs.AbstractCRS crs;
-
- RegisteredLayerType(String typeName, Class extends GeometryEncoder> geometryEncoder,
- Class extends Layer> layerClass, AbstractCRS crs,
- Class extends LayerIndexReader> layerIndexClass, String defaultConfig) {
- this.typeName = typeName;
- this.geometryEncoder = geometryEncoder;
- this.layerClass = layerClass;
- this.layerIndexClass = layerIndexClass;
- this.crs = crs;
- this.defaultConfig = defaultConfig;
- }
-
- /**
- * For external expression of the configuration of this geometry encoder
- *
- * @return descriptive signature of encoder, type and configuration
- */
- String getSignature() {
- return "RegisteredLayerType(name='" + typeName + "', geometryEncoder=" +
- geometryEncoder.getSimpleName() + ", layerClass=" + layerClass.getSimpleName() +
- ", index=" + layerIndexClass.getSimpleName() +
- ", crs='" + crs.getName(null) + "', defaultConfig='" + defaultConfig + "')";
- }
- }
-
- private static final Map registeredLayerTypes = new LinkedHashMap<>();
-
- static {
- addRegisteredLayerType(new RegisteredLayerType("SimplePoint", SimplePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "longitude:latitude"));
- addRegisteredLayerType(new RegisteredLayerType("Geohash", SimplePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerGeohashPointIndex.class, "longitude:latitude"));
- addRegisteredLayerType(new RegisteredLayerType("ZOrder", SimplePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerZOrderPointIndex.class, "longitude:latitude"));
- addRegisteredLayerType(new RegisteredLayerType("Hilbert", SimplePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerHilbertPointIndex.class, "longitude:latitude"));
- addRegisteredLayerType(new RegisteredLayerType("NativePoint", NativePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "location"));
- addRegisteredLayerType(new RegisteredLayerType("NativeGeohash", NativePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerGeohashPointIndex.class, "location"));
- addRegisteredLayerType(new RegisteredLayerType("NativeZOrder", NativePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerZOrderPointIndex.class, "location"));
- addRegisteredLayerType(new RegisteredLayerType("NativeHilbert", NativePointEncoder.class,
- SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerHilbertPointIndex.class, "location"));
- addRegisteredLayerType(new RegisteredLayerType("WKT", WKTGeometryEncoder.class, EditableLayerImpl.class,
- DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "geometry"));
- addRegisteredLayerType(new RegisteredLayerType("WKB", WKBGeometryEncoder.class, EditableLayerImpl.class,
- DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "geometry"));
- addRegisteredLayerType(new RegisteredLayerType("OSM", OSMGeometryEncoder.class, OSMLayer.class,
- DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "geometry"));
- }
-
- private static void addRegisteredLayerType(RegisteredLayerType type) {
- registeredLayerTypes.put(type.typeName.toLowerCase(), type);
- }
-
- public Layer getOrCreateRegisteredTypeLayer(Transaction tx, String name, String type, String config) {
- RegisteredLayerType registeredLayerType = registeredLayerTypes.get(type.toLowerCase());
- return getOrCreateRegisteredTypeLayer(tx, name, registeredLayerType, config);
- }
-
- public Layer getOrCreateRegisteredTypeLayer(Transaction tx, String name, RegisteredLayerType registeredLayerType, String config) {
- return getOrCreateLayer(tx, name, registeredLayerType.geometryEncoder, registeredLayerType.layerClass,
- (config == null) ? registeredLayerType.defaultConfig : config);
- }
-
- public Map getRegisteredLayerTypes() {
- Map results = new LinkedHashMap<>();
- registeredLayerTypes.forEach((s, definition) -> results.put(s, definition.getSignature()));
- return results;
- }
-
- public Class extends Layer> suggestLayerClassForEncoder(Class extends GeometryEncoder> encoderClass) {
- for (RegisteredLayerType type : registeredLayerTypes.values()) {
- if (type.geometryEncoder == encoderClass) {
- return type.layerClass;
- }
- }
- return EditableLayerImpl.class;
- }
+ public final IndexManager indexManager;
+
+ public SpatialDatabaseService(IndexManager indexManager) {
+ this.indexManager = indexManager;
+ }
+
+ public static void assertNotOldModel(Transaction tx) {
+ Node oldReferenceNode = ReferenceNodes.findDeprecatedReferenceNode(tx, "spatial_root");
+ if (oldReferenceNode != null) {
+ throw new IllegalStateException(
+ "Old reference node exists - please upgrade the spatial database to the new format");
+ }
+ }
+
+ public List upgradeFromOldModel(Transaction tx) {
+ ArrayList layersConverted = new ArrayList<>();
+ Node oldReferenceNode = ReferenceNodes.findDeprecatedReferenceNode(tx, "spatial_root");
+ if (oldReferenceNode != null) {
+ List layers = new ArrayList<>();
+
+ try (var relationships = oldReferenceNode.getRelationships(Direction.OUTGOING,
+ SpatialRelationshipTypes.LAYER)) {
+ for (Relationship relationship : relationships) {
+ layers.add(relationship.getEndNode());
+ }
+ }
+
+ for (Node layer : layers) {
+ Relationship fromRoot = layer.getSingleRelationship(SpatialRelationshipTypes.LAYER, Direction.INCOMING);
+ fromRoot.delete();
+ layer.addLabel(LABEL_LAYER);
+ layersConverted.add((String) layer.getProperty(PROP_LAYER));
+ }
+
+ try (var relationships = oldReferenceNode.getRelationships()) {
+ if (relationships.iterator().hasNext()) {
+ throw new IllegalStateException(
+ "Cannot upgrade - ReferenceNode 'spatial_root' still has relationships other than layers");
+ }
+ }
+
+ oldReferenceNode.delete();
+ }
+ indexManager.makeIndexFor(tx, "SpatialLayers", LABEL_LAYER, PROP_LAYER);
+ return layersConverted;
+ }
+
+ public String[] getLayerNames(Transaction tx) {
+ assertNotOldModel(tx);
+ List names = new ArrayList<>();
+
+ try (var layers = tx.findNodes(LABEL_LAYER)) {
+ while (layers.hasNext()) {
+ Layer layer = LayerUtilities.makeLayerFromNode(tx, indexManager, layers.next());
+ if (layer instanceof DynamicLayer) {
+ names.addAll(((DynamicLayer) layer).getLayerNames(tx));
+ } else {
+ names.add(layer.getName());
+ }
+ }
+ }
+
+ return names.toArray(new String[0]);
+ }
+
+ public Layer getLayer(Transaction tx, String name) {
+ assertNotOldModel(tx);
+ try (var layers = tx.findNodes(LABEL_LAYER)) {
+ while (layers.hasNext()) {
+ Node node = layers.next();
+ if (name.equals(node.getProperty(PROP_LAYER))) {
+ return LayerUtilities.makeLayerFromNode(tx, indexManager, node);
+ }
+ }
+ }
+ return getDynamicLayer(tx, name);
+ }
+
+ public Layer getDynamicLayer(Transaction tx, String name) {
+ assertNotOldModel(tx);
+ ArrayList dynamicLayers = new ArrayList<>();
+ try (var layers = tx.findNodes(LABEL_LAYER)) {
+ while (layers.hasNext()) {
+ Node node = layers.next();
+ if (!node.getProperty(PROP_LAYER_CLASS, "").toString().startsWith("DefaultLayer")) {
+ Layer layer = LayerUtilities.makeLayerFromNode(tx, indexManager, node);
+ if (layer instanceof DynamicLayer) {
+ dynamicLayers.add((DynamicLayer) LayerUtilities.makeLayerFromNode(tx, indexManager, node));
+ }
+ }
+ }
+ }
+ for (DynamicLayer layer : dynamicLayers) {
+ for (String dynLayerName : layer.getLayerNames(tx)) {
+ if (name.equals(dynLayerName)) {
+ return layer.getLayer(tx, dynLayerName);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Convert a layer into a DynamicLayer. This will expose the ability to add
+ * views, or 'dynamic layers' to the layer.
+ *
+ * @return new DynamicLayer version of the original layer
+ */
+ public DynamicLayer asDynamicLayer(Transaction tx, Layer layer) {
+ if (layer instanceof DynamicLayer) {
+ return (DynamicLayer) layer;
+ } else {
+ Node node = layer.getLayerNode(tx);
+ node.setProperty(PROP_LAYER_CLASS, DynamicLayer.class.getCanonicalName());
+ return (DynamicLayer) LayerUtilities.makeLayerFromNode(tx, indexManager, node);
+ }
+ }
+
+ public DefaultLayer getOrCreateDefaultLayer(Transaction tx, String name) {
+ return (DefaultLayer) getOrCreateLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class, "");
+ }
+
+ public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String format,
+ String propertyNameConfig) {
+ Class extends GeometryEncoder> geClass = WKBGeometryEncoder.class;
+ if (format != null && format.toUpperCase().startsWith("WKT")) {
+ geClass = WKTGeometryEncoder.class;
+ }
+ return (EditableLayer) getOrCreateLayer(tx, name, geClass, EditableLayerImpl.class, propertyNameConfig);
+ }
+
+ public EditableLayer getOrCreateEditableLayer(Transaction tx, String name) {
+ return getOrCreateEditableLayer(tx, name, "WKB", "");
+ }
+
+ public EditableLayer getOrCreateEditableLayer(Transaction tx, String name, String wktProperty) {
+ return getOrCreateEditableLayer(tx, name, "WKT", wktProperty);
+ }
+
+ public static final String RTREE_INDEX_NAME = "rtree";
+ public static final String GEOHASH_INDEX_NAME = "geohash";
+
+ public Class extends LayerIndexReader> resolveIndexClass(String index) {
+ if (index == null) {
+ return LayerRTreeIndex.class;
+ }
+ switch (index.toLowerCase()) {
+ case RTREE_INDEX_NAME:
+ return LayerRTreeIndex.class;
+ case GEOHASH_INDEX_NAME:
+ return LayerGeohashPointIndex.class;
+ case "zorder":
+ return LayerZOrderPointIndex.class;
+ case "hilbert":
+ return LayerHilbertPointIndex.class;
+ }
+ throw new IllegalArgumentException("Unknown index: " + index);
+ }
+
+ public EditableLayer getOrCreateSimplePointLayer(Transaction tx, String name, String index, String xProperty,
+ String yProperty) {
+ return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, xProperty,
+ yProperty);
+ }
+
+ public EditableLayer getOrCreateNativePointLayer(Transaction tx, String name, String index,
+ String locationProperty) {
+ return getOrCreatePointLayer(tx, name, resolveIndexClass(index), SimplePointEncoder.class, locationProperty);
+ }
+
+ public EditableLayer getOrCreatePointLayer(Transaction tx, String name,
+ Class extends LayerIndexReader> indexClass, Class extends GeometryEncoder> encoderClass,
+ String... encoderConfig) {
+ Layer layer = getLayer(tx, name);
+ if (layer == null) {
+ return (EditableLayer) createLayer(tx, name, encoderClass, SimplePointLayer.class, indexClass,
+ makeEncoderConfig(encoderConfig), DefaultGeographicCRS.WGS84);
+ } else if (layer instanceof EditableLayer) {
+ return (EditableLayer) layer;
+ } else {
+ throw new SpatialDatabaseException(
+ "Existing layer '" + layer + "' is not of the expected type: " + EditableLayer.class);
+ }
+ }
+
+ public Layer getOrCreateLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoder,
+ Class extends Layer> layerClass, String config) {
+ Layer layer = getLayer(tx, name);
+ if (layer == null) {
+ layer = createLayer(tx, name, geometryEncoder, layerClass, null, config);
+ } else if (!(layerClass == null || layerClass.isInstance(layer))) {
+ throw new SpatialDatabaseException(
+ "Existing layer '" + layer + "' is not of the expected type: " + layerClass);
+ }
+ return layer;
+ }
+
+ public Layer getOrCreateLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoder,
+ Class extends Layer> layerClass) {
+ return getOrCreateLayer(tx, name, geometryEncoder, layerClass, "");
+ }
+
+ /**
+ * This method will find the Layer when given a geometry node that this layer contains. This method
+ * used to make use of knowledge of the RTree, traversing backwards up the tree to find the layer node, which is
+ * fast. However, for reasons of clean abstraction,
+ * this has been refactored to delegate the logic to the layer, so that each layer can do this in an
+ * implementation specific way. Now we simply iterate through the layers datasets and the first one
+ * to return true on the SpatialDataset.containsGeometryNode(Transaction,Node) method is returned.
+ *
+ * We can consider removing this method for a few reasons:
+ * * It is non-deterministic if more than one layer contains the same geometry
+ * * None of the current code appears to use this method
+ *
+ * @param geometryNode to start search
+ * @return Layer object containing this geometry
+ */
+ public Layer findLayerContainingGeometryNode(Transaction tx, Node geometryNode) {
+ for (String layerName : getLayerNames(tx)) {
+ Layer layer = getLayer(tx, layerName);
+ if (layer.getDataset().containsGeometryNode(tx, geometryNode)) {
+ return layer;
+ }
+ }
+ return null;
+ }
+
+ private Layer getLayerFromChild(Transaction tx, Node child, RelationshipType relType) {
+ Relationship indexRel = child.getSingleRelationship(relType, Direction.INCOMING);
+ if (indexRel != null) {
+ Node layerNode = indexRel.getStartNode();
+ if (layerNode.hasProperty(PROP_LAYER)) {
+ return LayerUtilities.makeLayerFromNode(tx, indexManager, layerNode);
+ }
+ }
+ return null;
+ }
+
+ public boolean containsLayer(Transaction tx, String name) {
+ return getLayer(tx, name) != null;
+ }
+
+ public Layer createWKBLayer(Transaction tx, String name) {
+ return createLayer(tx, name, WKBGeometryEncoder.class, EditableLayerImpl.class);
+ }
+
+ public SimplePointLayer createSimplePointLayer(Transaction tx, String name) {
+ return createSimplePointLayer(tx, name, (String[]) null);
+ }
+
+ public SimplePointLayer createSimplePointLayer(Transaction tx, String name, String xProperty, String yProperty) {
+ return createSimplePointLayer(tx, name, xProperty, yProperty, null);
+ }
+
+ public SimplePointLayer createSimplePointLayer(Transaction tx, String name, String... xybProperties) {
+ return createPointLayer(tx, name, LayerRTreeIndex.class, SimplePointEncoder.class, xybProperties);
+ }
+
+ public SimplePointLayer createNativePointLayer(Transaction tx, String name) {
+ return createNativePointLayer(tx, name, (String[]) null);
+ }
+
+ public SimplePointLayer createNativePointLayer(Transaction tx, String name, String locationProperty,
+ String bboxProperty) {
+ return createNativePointLayer(tx, name, locationProperty, bboxProperty, null);
+ }
+
+ public SimplePointLayer createNativePointLayer(Transaction tx, String name, String... encoderConfig) {
+ return createPointLayer(tx, name, LayerRTreeIndex.class, NativePointEncoder.class, encoderConfig);
+ }
+
+ public SimplePointLayer createPointLayer(Transaction tx, String name, Class extends LayerIndexReader> indexClass,
+ Class extends GeometryEncoder> encoderClass, String... encoderConfig) {
+ return (SimplePointLayer) createLayer(tx, name, encoderClass, SimplePointLayer.class, indexClass,
+ makeEncoderConfig(encoderConfig), org.geotools.referencing.crs.DefaultGeographicCRS.WGS84);
+ }
+
+ public String makeEncoderConfig(String... args) {
+ StringBuilder sb = new StringBuilder();
+ if (args != null) {
+ for (String arg : args) {
+ if (arg != null) {
+ if (sb.length() > 0) {
+ sb.append(":");
+ }
+ sb.append(arg);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
+ Class extends Layer> layerClass) {
+ return createLayer(tx, name, geometryEncoderClass, layerClass, null, null);
+ }
+
+ public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
+ Class extends Layer> layerClass, Class extends LayerIndexReader> indexClass,
+ String encoderConfig) {
+ return createLayer(tx, name, geometryEncoderClass, layerClass, indexClass, encoderConfig, null);
+ }
+
+ public Layer createLayer(Transaction tx, String name, Class extends GeometryEncoder> geometryEncoderClass,
+ Class extends Layer> layerClass, Class extends LayerIndexReader> indexClass,
+ String encoderConfig, CoordinateReferenceSystem crs) {
+ if (containsLayer(tx, name)) {
+ throw new SpatialDatabaseException("Layer " + name + " already exists");
+ }
+
+ Layer layer = LayerUtilities.makeLayerAndNode(tx, indexManager, name, geometryEncoderClass, layerClass,
+ indexClass);
+ if (encoderConfig != null && encoderConfig.length() > 0) {
+ GeometryEncoder encoder = layer.getGeometryEncoder();
+ if (encoder instanceof Configurable) {
+ ((Configurable) encoder).setConfiguration(encoderConfig);
+ layer.getLayerNode(tx).setProperty(PROP_GEOMENCODER_CONFIG, encoderConfig);
+ } else {
+ System.out.println(
+ "Warning: encoder configuration '" + encoderConfig + "' passed to non-configurable encoder: "
+ + geometryEncoderClass);
+ }
+ }
+ if (crs != null && layer instanceof EditableLayer) {
+ ((EditableLayer) layer).setCoordinateReferenceSystem(tx, crs);
+ }
+ return layer;
+ }
+
+ public void deleteLayer(Transaction tx, String name, Listener monitor) {
+ Layer layer = getLayer(tx, name);
+ if (layer == null) {
+ throw new SpatialDatabaseException("Layer " + name + " does not exist");
+ }
+ layer.delete(tx, monitor);
+ }
+
+ public static int convertGeometryNameToType(String geometryName) {
+ if (geometryName == null) {
+ return GTYPE_GEOMETRY;
+ }
+ try {
+ return convertJtsClassToGeometryType(
+ (Class extends Geometry>) Class.forName("org.locationtech.jts.geom." + geometryName));
+ } catch (ClassNotFoundException e) {
+ System.err.println("Unrecognized geometry '" + geometryName + "': " + e);
+ return GTYPE_GEOMETRY;
+ }
+ }
+
+ public static String convertGeometryTypeToName(Integer geometryType) {
+ return convertGeometryTypeToJtsClass(geometryType).getName().replace("org.locationtech.jts.geom.", "");
+ }
+
+ public static Class extends Geometry> convertGeometryTypeToJtsClass(Integer geometryType) {
+ switch (geometryType) {
+ case GTYPE_POINT:
+ return Point.class;
+ case GTYPE_LINESTRING:
+ return LineString.class;
+ case GTYPE_POLYGON:
+ return Polygon.class;
+ case GTYPE_MULTIPOINT:
+ return MultiPoint.class;
+ case GTYPE_MULTILINESTRING:
+ return MultiLineString.class;
+ case GTYPE_MULTIPOLYGON:
+ return MultiPolygon.class;
+ default:
+ return Geometry.class;
+ }
+ }
+
+ public static int convertJtsClassToGeometryType(Class extends Geometry> jtsClass) {
+ if (jtsClass.equals(Point.class)) {
+ return GTYPE_POINT;
+ } else if (jtsClass.equals(LineString.class)) {
+ return GTYPE_LINESTRING;
+ } else if (jtsClass.equals(Polygon.class)) {
+ return GTYPE_POLYGON;
+ } else if (jtsClass.equals(MultiPoint.class)) {
+ return GTYPE_MULTIPOINT;
+ } else if (jtsClass.equals(MultiLineString.class)) {
+ return GTYPE_MULTILINESTRING;
+ } else if (jtsClass.equals(MultiPolygon.class)) {
+ return GTYPE_MULTIPOLYGON;
+ } else {
+ return GTYPE_GEOMETRY;
+ }
+ }
+
+ /**
+ * Create a new layer from the results of a previous query. This actually
+ * copies the resulting geometries and their attributes into entirely new
+ * geometries using WKBGeometryEncoder. This means it is independent of the
+ * format of the original data. As a consequence it will have lost any
+ * domain specific capabilities of the original graph, if any. Use it only
+ * if you want a copy of the geometries themselves, and nothing more. One
+ * common use case would be to create a temporary layer of the results of a
+ * query than you wish to now export to a format that only supports
+ * geometries, like Shapefile, or the PNG images produced by the
+ * ImageExporter.
+ *
+ * @param layerName name of new layer to create
+ * @param results collection of SpatialDatabaseRecords to add to new layer
+ * @return new Layer with copy of all geometries
+ */
+ public Layer createResultsLayer(Transaction tx, String layerName, List results) {
+ EditableLayer layer = (EditableLayer) createWKBLayer(tx, layerName);
+ for (SpatialDatabaseRecord record : results) {
+ layer.add(tx, record.getGeometry());
+ }
+ return layer;
+ }
+
+
+ /**
+ * Support mapping a String (ex: 'SimplePoint') to the respective GeometryEncoder and Layer classes
+ * to allow for more streamlined method for creating Layers
+ * This was added to help support Spatial Cypher project.
+ */
+ public static class RegisteredLayerType {
+
+ String typeName;
+ Class extends GeometryEncoder> geometryEncoder;
+ Class extends Layer> layerClass;
+ Class extends LayerIndexReader> layerIndexClass;
+ String defaultConfig;
+ org.geotools.referencing.crs.AbstractCRS crs;
+
+ RegisteredLayerType(String typeName, Class extends GeometryEncoder> geometryEncoder,
+ Class extends Layer> layerClass, AbstractCRS crs,
+ Class extends LayerIndexReader> layerIndexClass, String defaultConfig) {
+ this.typeName = typeName;
+ this.geometryEncoder = geometryEncoder;
+ this.layerClass = layerClass;
+ this.layerIndexClass = layerIndexClass;
+ this.crs = crs;
+ this.defaultConfig = defaultConfig;
+ }
+
+ /**
+ * For external expression of the configuration of this geometry encoder
+ *
+ * @return descriptive signature of encoder, type and configuration
+ */
+ String getSignature() {
+ return "RegisteredLayerType(name='" + typeName + "', geometryEncoder=" +
+ geometryEncoder.getSimpleName() + ", layerClass=" + layerClass.getSimpleName() +
+ ", index=" + layerIndexClass.getSimpleName() +
+ ", crs='" + crs.getName(null) + "', defaultConfig='" + defaultConfig + "')";
+ }
+ }
+
+ private static final Map registeredLayerTypes = new LinkedHashMap<>();
+
+ static {
+ addRegisteredLayerType(new RegisteredLayerType("SimplePoint", SimplePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "longitude:latitude"));
+ addRegisteredLayerType(new RegisteredLayerType("Geohash", SimplePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerGeohashPointIndex.class,
+ "longitude:latitude"));
+ addRegisteredLayerType(new RegisteredLayerType("ZOrder", SimplePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerZOrderPointIndex.class, "longitude:latitude"));
+ addRegisteredLayerType(new RegisteredLayerType("Hilbert", SimplePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerHilbertPointIndex.class,
+ "longitude:latitude"));
+ addRegisteredLayerType(new RegisteredLayerType("NativePoint", NativePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "location"));
+ addRegisteredLayerType(new RegisteredLayerType("NativeGeohash", NativePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerGeohashPointIndex.class, "location"));
+ addRegisteredLayerType(new RegisteredLayerType("NativeZOrder", NativePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerZOrderPointIndex.class, "location"));
+ addRegisteredLayerType(new RegisteredLayerType("NativeHilbert", NativePointEncoder.class,
+ SimplePointLayer.class, DefaultGeographicCRS.WGS84, LayerHilbertPointIndex.class, "location"));
+ addRegisteredLayerType(new RegisteredLayerType("WKT", WKTGeometryEncoder.class, EditableLayerImpl.class,
+ DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "geometry"));
+ addRegisteredLayerType(new RegisteredLayerType("WKB", WKBGeometryEncoder.class, EditableLayerImpl.class,
+ DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "geometry"));
+ addRegisteredLayerType(new RegisteredLayerType("OSM", OSMGeometryEncoder.class, OSMLayer.class,
+ DefaultGeographicCRS.WGS84, LayerRTreeIndex.class, "geometry"));
+ }
+
+ private static void addRegisteredLayerType(RegisteredLayerType type) {
+ registeredLayerTypes.put(type.typeName.toLowerCase(), type);
+ }
+
+ public Layer getOrCreateRegisteredTypeLayer(Transaction tx, String name, String type, String config) {
+ RegisteredLayerType registeredLayerType = registeredLayerTypes.get(type.toLowerCase());
+ return getOrCreateRegisteredTypeLayer(tx, name, registeredLayerType, config);
+ }
+
+ public Layer getOrCreateRegisteredTypeLayer(Transaction tx, String name, RegisteredLayerType registeredLayerType,
+ String config) {
+ return getOrCreateLayer(tx, name, registeredLayerType.geometryEncoder, registeredLayerType.layerClass,
+ (config == null) ? registeredLayerType.defaultConfig : config);
+ }
+
+ public Map getRegisteredLayerTypes() {
+ Map results = new LinkedHashMap<>();
+ registeredLayerTypes.forEach((s, definition) -> results.put(s, definition.getSignature()));
+ return results;
+ }
+
+ public Class extends Layer> suggestLayerClassForEncoder(Class extends GeometryEncoder> encoderClass) {
+ for (RegisteredLayerType type : registeredLayerTypes.values()) {
+ if (type.geometryEncoder == encoderClass) {
+ return type.layerClass;
+ }
+ }
+ return EditableLayerImpl.class;
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/SpatialDataset.java b/src/main/java/org/neo4j/gis/spatial/SpatialDataset.java
index 1b8569132..8fc4034ff 100644
--- a/src/main/java/org/neo4j/gis/spatial/SpatialDataset.java
+++ b/src/main/java/org/neo4j/gis/spatial/SpatialDataset.java
@@ -19,9 +19,8 @@
*/
package org.neo4j.gis.spatial;
-import org.neo4j.graphdb.Node;
-
import org.locationtech.jts.geom.Geometry;
+import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
/**
@@ -39,46 +38,46 @@
*/
public interface SpatialDataset {
- /**
- * Provides a method for iterating over all nodes that represent geometries in this dataset.
- * This is similar to the getAllNodes() methods from GraphDatabaseService but will only return
- * nodes that this dataset considers its own, and can be passed to the GeometryEncoder to
- * generate a Geometry. There is no restriction on a node belonging to multiple datasets, or
- * multiple layers within the same dataset.
- *
- * @return iterable over geometry nodes in the dataset
- * @param tx
- */
- public Iterable getAllGeometryNodes(Transaction tx);
+ /**
+ * Provides a method for iterating over all nodes that represent geometries in this dataset.
+ * This is similar to the getAllNodes() methods from GraphDatabaseService but will only return
+ * nodes that this dataset considers its own, and can be passed to the GeometryEncoder to
+ * generate a Geometry. There is no restriction on a node belonging to multiple datasets, or
+ * multiple layers within the same dataset.
+ *
+ * @param tx
+ * @return iterable over geometry nodes in the dataset
+ */
+ public Iterable getAllGeometryNodes(Transaction tx);
- /**
- * Provides a method for iterating over all geometries in this dataset. This is similar to the
- * getAllGeometryNodes() method but internally converts the Node to a Geometry.
- *
- * @return iterable over geometries in the dataset
- */
- public Iterable< ? extends Geometry> getAllGeometries(Transaction tx);
+ /**
+ * Provides a method for iterating over all geometries in this dataset. This is similar to the
+ * getAllGeometryNodes() method but internally converts the Node to a Geometry.
+ *
+ * @return iterable over geometries in the dataset
+ */
+ public Iterable extends Geometry> getAllGeometries(Transaction tx);
- /**
- * Return the geometry encoder used by this SpatialDataset to convert individual geometries to
- * and from the database structure.
- *
- * @return GeometryEncoder for this dataset
- */
- public GeometryEncoder getGeometryEncoder();
+ /**
+ * Return the geometry encoder used by this SpatialDataset to convert individual geometries to
+ * and from the database structure.
+ *
+ * @return GeometryEncoder for this dataset
+ */
+ public GeometryEncoder getGeometryEncoder();
- /**
- * Each dataset can have one or more layers. This methods provides a way to iterate over all
- * layers.
- *
- * @return iterable over all Layers that can be viewed from this dataset
- */
- public Iterable< ? extends Layer> getLayers();
+ /**
+ * Each dataset can have one or more layers. This methods provides a way to iterate over all
+ * layers.
+ *
+ * @return iterable over all Layers that can be viewed from this dataset
+ */
+ public Iterable extends Layer> getLayers();
- /**
- * Does the dataset (or layer) contain the geometry specified by this node.
- *
- * @return boolean true/false if the geometry node is in this Dataset or Layer
- */
- public boolean containsGeometryNode(Transaction tx, Node node);
+ /**
+ * Does the dataset (or layer) contain the geometry specified by this node.
+ *
+ * @return boolean true/false if the geometry node is in this Dataset or Layer
+ */
+ public boolean containsGeometryNode(Transaction tx, Node node);
}
diff --git a/src/main/java/org/neo4j/gis/spatial/SpatialRecord.java b/src/main/java/org/neo4j/gis/spatial/SpatialRecord.java
index 3b5718989..0fa406e23 100644
--- a/src/main/java/org/neo4j/gis/spatial/SpatialRecord.java
+++ b/src/main/java/org/neo4j/gis/spatial/SpatialRecord.java
@@ -20,7 +20,6 @@
package org.neo4j.gis.spatial;
import java.util.Map;
-
import org.locationtech.jts.geom.Geometry;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
@@ -28,7 +27,7 @@
public interface SpatialRecord {
String getId();
-
+
Geometry getGeometry();
boolean hasProperty(Transaction tx, String name);
diff --git a/src/main/java/org/neo4j/gis/spatial/SpatialTopologyUtils.java b/src/main/java/org/neo4j/gis/spatial/SpatialTopologyUtils.java
index 436d58f90..8d3ecbde6 100644
--- a/src/main/java/org/neo4j/gis/spatial/SpatialTopologyUtils.java
+++ b/src/main/java/org/neo4j/gis/spatial/SpatialTopologyUtils.java
@@ -19,297 +19,311 @@
*/
package org.neo4j.gis.spatial;
-import org.locationtech.jts.geom.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import org.geotools.geometry.jts.ReferencedEnvelope;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Envelope;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.Point;
import org.locationtech.jts.linearref.LengthIndexedLine;
import org.locationtech.jts.linearref.LinearLocation;
import org.locationtech.jts.linearref.LocationIndexedLine;
-import org.geotools.geometry.jts.ReferencedEnvelope;
import org.neo4j.gis.spatial.filter.SearchIntersect;
import org.neo4j.graphdb.Transaction;
-import java.util.*;
-
/**
* This class is a temporary location for collecting a number of spatial
* utilities before we have decided on a more complete analysis structure. Do
* not rely on this API remaining constant.
*/
public class SpatialTopologyUtils {
- /**
- * Inner class associating points and resulting geometry records to
- * facilitate the result set returned.
- */
- public static class PointResult implements Map.Entry, Comparable {
- private final Point point;
- private SpatialDatabaseRecord record;
- private final double distance;
- private PointResult(Point point, SpatialDatabaseRecord record, double distance) {
- this.point = point;
- this.record = record;
- this.distance = distance;
- }
+ /**
+ * Inner class associating points and resulting geometry records to
+ * facilitate the result set returned.
+ */
+ public static class PointResult implements Map.Entry, Comparable {
+
+ private final Point point;
+ private SpatialDatabaseRecord record;
+ private final double distance;
+
+ private PointResult(Point point, SpatialDatabaseRecord record, double distance) {
+ this.point = point;
+ this.record = record;
+ this.distance = distance;
+ }
- public Point getKey() {
- return point;
- }
+ public Point getKey() {
+ return point;
+ }
- public SpatialDatabaseRecord getValue() {
- return record;
- }
+ public SpatialDatabaseRecord getValue() {
+ return record;
+ }
- public double getDistance() {
- return distance;
- }
+ public double getDistance() {
+ return distance;
+ }
- public SpatialDatabaseRecord setValue(SpatialDatabaseRecord value) {
- return this.record = value;
- }
+ public SpatialDatabaseRecord setValue(SpatialDatabaseRecord value) {
+ return this.record = value;
+ }
- public int compareTo(PointResult other) {
- return Double.compare(this.distance, other.distance);
- }
+ public int compareTo(PointResult other) {
+ return Double.compare(this.distance, other.distance);
+ }
- public String toString() {
- return "Point[" + point + "] distance[" + distance + "] record[" + record + "]";
- }
- }
+ public String toString() {
+ return "Point[" + point + "] distance[" + distance + "] record[" + record + "]";
+ }
+ }
- public static List findClosestEdges(Transaction tx, Point point, Layer layer) {
- return findClosestEdges(tx, point, layer, 0.0);
- }
+ public static List findClosestEdges(Transaction tx, Point point, Layer layer) {
+ return findClosestEdges(tx, point, layer, 0.0);
+ }
- public static List findClosestEdges(Transaction tx, Point point, Layer layer, double distance) {
- if (layer.getIndex().isEmpty(tx)) {
- return new ArrayList<>(0);
- } else {
- ReferencedEnvelope env = new ReferencedEnvelope(
- Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx)),
- layer.getCoordinateReferenceSystem(tx));
- if (distance <= 0.0)
- distance = env.getSpan(0) / 100.0;
- Envelope search = new Envelope(point.getCoordinate());
- search.expandBy(distance);
- GeometryFactory factory = layer.getGeometryFactory();
- return findClosestEdges(tx, point, layer, factory.toGeometry(search));
- }
- }
+ public static List findClosestEdges(Transaction tx, Point point, Layer layer, double distance) {
+ if (layer.getIndex().isEmpty(tx)) {
+ return new ArrayList<>(0);
+ } else {
+ ReferencedEnvelope env = new ReferencedEnvelope(
+ Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx)),
+ layer.getCoordinateReferenceSystem(tx));
+ if (distance <= 0.0) {
+ distance = env.getSpan(0) / 100.0;
+ }
+ Envelope search = new Envelope(point.getCoordinate());
+ search.expandBy(distance);
+ GeometryFactory factory = layer.getGeometryFactory();
+ return findClosestEdges(tx, point, layer, factory.toGeometry(search));
+ }
+ }
- /**
- * Find geometries in the given layer that are closest to the given point while applying the filter
- * currently only handles point and linestrings (projecting them to a point) TODO Craig for other geoms
- *
- * @return list of point results containing the matched point on the geometry, the spatial record and the distance each
- */
- public static List findClosestEdges(Transaction tx, Point point, Layer layer, Geometry filter) {
- ArrayList results = new ArrayList<>();
+ /**
+ * Find geometries in the given layer that are closest to the given point while applying the filter
+ * currently only handles point and linestrings (projecting them to a point) TODO Craig for other geoms
+ *
+ * @return list of point results containing the matched point on the geometry, the spatial record and the distance each
+ */
+ public static List findClosestEdges(Transaction tx, Point point, Layer layer, Geometry filter) {
+ ArrayList results = new ArrayList<>();
- Iterator records = layer.getIndex().search(tx, new SearchIntersect(layer, filter));
- while (records.hasNext()) {
- SpatialDatabaseRecord record = records.next();
- Geometry geom = record.getGeometry();
- if (geom instanceof LineString) {
- LocationIndexedLine line = new LocationIndexedLine(geom);
- LinearLocation here = line.project(point.getCoordinate());
- Coordinate snap = line.extractPoint(here);
- double distance = snap.distance(point.getCoordinate());
- results.add(new PointResult(layer.getGeometryFactory()
- .createPoint(snap), record, distance));
- } else if (geom instanceof Point) {
- Point here = (Point) geom;
- results.add(new PointResult(here, record, here.distance(point)));
- }
- }
- Collections.sort(results);
- return results;
- }
+ Iterator records = layer.getIndex().search(tx, new SearchIntersect(layer, filter));
+ while (records.hasNext()) {
+ SpatialDatabaseRecord record = records.next();
+ Geometry geom = record.getGeometry();
+ if (geom instanceof LineString) {
+ LocationIndexedLine line = new LocationIndexedLine(geom);
+ LinearLocation here = line.project(point.getCoordinate());
+ Coordinate snap = line.extractPoint(here);
+ double distance = snap.distance(point.getCoordinate());
+ results.add(new PointResult(layer.getGeometryFactory()
+ .createPoint(snap), record, distance));
+ } else if (geom instanceof Point) {
+ Point here = (Point) geom;
+ results.add(new PointResult(here, record, here.distance(point)));
+ }
+ }
+ Collections.sort(results);
+ return results;
+ }
- /**
- * Create a Point located at the specified 'measure' distance along a
- * Geometry. This is achieved through using the JTS
- * LengthIndexedLine.extractPoint(measure) method for finding the
- * coordinates at the specified measure along the geometry. It is equivalent
- * to Oracle's SDO_LRS.LOCATE_PT.
- *
- * @param layer Layer the geometry is contained by, and is used to access the
- * GeometryFactory for creating the Point
- * @param geometry Geometry to measure
- * @param measure the distance along the geometry
- * @return Point at 'measure' distance along the geometry
- * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
- * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
- */
- public static Point locatePoint(Layer layer, Geometry geometry, double measure) {
- return layer.getGeometryFactory().createPoint(locatePoint(geometry, measure));
- }
+ /**
+ * Create a Point located at the specified 'measure' distance along a
+ * Geometry. This is achieved through using the JTS
+ * LengthIndexedLine.extractPoint(measure) method for finding the
+ * coordinates at the specified measure along the geometry. It is equivalent
+ * to Oracle's SDO_LRS.LOCATE_PT.
+ *
+ * @param layer Layer the geometry is contained by, and is used to access the
+ * GeometryFactory for creating the Point
+ * @param geometry Geometry to measure
+ * @param measure the distance along the geometry
+ * @return Point at 'measure' distance along the geometry
+ * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
+ * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
+ */
+ public static Point locatePoint(Layer layer, Geometry geometry, double measure) {
+ return layer.getGeometryFactory().createPoint(locatePoint(geometry, measure));
+ }
- /**
- * Find the coordinate at the specified 'measure' distance along a
- * Geometry. This is achieved through using the JTS
- * LengthIndexedLine.extractPoint(measure) method for finding the
- * coordinates at the specified measure along the geometry. It is equivalent
- * to Oracle's SDO_LRS.LOCATE_PT.
- *
- * @param geometry Geometry to measure
- * @param measure the distance along the geometry
- * @return Coordinate at 'measure' distance along the geometry
- * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
- * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
- */
- public static Coordinate locatePoint(Geometry geometry, double measure) {
- return new LengthIndexedLine(geometry).extractPoint(measure);
- }
+ /**
+ * Find the coordinate at the specified 'measure' distance along a
+ * Geometry. This is achieved through using the JTS
+ * LengthIndexedLine.extractPoint(measure) method for finding the
+ * coordinates at the specified measure along the geometry. It is equivalent
+ * to Oracle's SDO_LRS.LOCATE_PT.
+ *
+ * @param geometry Geometry to measure
+ * @param measure the distance along the geometry
+ * @return Coordinate at 'measure' distance along the geometry
+ * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
+ * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
+ */
+ public static Coordinate locatePoint(Geometry geometry, double measure) {
+ return new LengthIndexedLine(geometry).extractPoint(measure);
+ }
- /**
- * Create a Point located at the specified 'measure' distance along a
- * Geometry, and offset to the left of the Geometry by the specified offset
- * distance. This is achieved through using the JTS
- * LengthIndexedLine.extractPoint(measure) method for finding the
- * coordinates at the specified measure along the geometry. It is equivalent
- * to Oracle's SDO_LRS.LOCATE_PT.
- *
- * @param layer Layer the geometry is contained by, and is used to access the
- * GeometryFactory for creating the Point
- * @param geometry Geometry to measure
- * @param measure the distance along the geometry
- * @param offset the distance offset to the left (or right for negative numbers)
- * @return Point at 'measure' distance along the geometry, and offset
- * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
- * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
- */
- public static Point locatePoint(Layer layer, Geometry geometry, double measure, double offset) {
- return layer.getGeometryFactory().createPoint(locatePoint(geometry, measure, offset));
- }
+ /**
+ * Create a Point located at the specified 'measure' distance along a
+ * Geometry, and offset to the left of the Geometry by the specified offset
+ * distance. This is achieved through using the JTS
+ * LengthIndexedLine.extractPoint(measure) method for finding the
+ * coordinates at the specified measure along the geometry. It is equivalent
+ * to Oracle's SDO_LRS.LOCATE_PT.
+ *
+ * @param layer Layer the geometry is contained by, and is used to access the
+ * GeometryFactory for creating the Point
+ * @param geometry Geometry to measure
+ * @param measure the distance along the geometry
+ * @param offset the distance offset to the left (or right for negative numbers)
+ * @return Point at 'measure' distance along the geometry, and offset
+ * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
+ * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
+ */
+ public static Point locatePoint(Layer layer, Geometry geometry, double measure, double offset) {
+ return layer.getGeometryFactory().createPoint(locatePoint(geometry, measure, offset));
+ }
- /**
- * Find the coordinate located at the specified 'measure' distance along a
- * Geometry, and offset to the left of the Geometry by the specified offset
- * distance. This is achieved through using the JTS
- * LengthIndexedLine.extractPoint(measure) method for finding the
- * coordinates at the specified measure along the geometry. It is equivalent
- * to Oracle's SDO_LRS.LOCATE_PT.
- *
- * @param geometry Geometry to measure
- * @param measure the distance along the geometry
- * @param offset the distance offset to the left (or right for negative numbers)
- * @return Point at 'measure' distance along the geometry, and offset
- * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
- * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
- */
- public static Coordinate locatePoint(Geometry geometry, double measure, double offset) {
- return new LengthIndexedLine(geometry).extractPoint(measure, offset);
- }
+ /**
+ * Find the coordinate located at the specified 'measure' distance along a
+ * Geometry, and offset to the left of the Geometry by the specified offset
+ * distance. This is achieved through using the JTS
+ * LengthIndexedLine.extractPoint(measure) method for finding the
+ * coordinates at the specified measure along the geometry. It is equivalent
+ * to Oracle's SDO_LRS.LOCATE_PT.
+ *
+ * @param geometry Geometry to measure
+ * @param measure the distance along the geometry
+ * @param offset the distance offset to the left (or right for negative numbers)
+ * @return Point at 'measure' distance along the geometry, and offset
+ * @see http://download.oracle.com/docs/cd/B13789_01/appdev.101/b10826/sdo_lrs_ref.htm#i85478
+ * @see http://www.vividsolutions.com/jts/javadoc/com/vividsolutions/jts/linearref/LengthIndexedLine.html
+ */
+ public static Coordinate locatePoint(Geometry geometry, double measure, double offset) {
+ return new LengthIndexedLine(geometry).extractPoint(measure, offset);
+ }
- /**
- * Adjust the size and position of a ReferencedEnvelope using fractions of
- * the current size. For example:
- *
- *
- * bounds = adjustBounds(bounds, 0.3, new double[] { -0.1, 0.1 });
- *
- *
- * This will zoom in to show 30% of the height and width, and will also
- * move the visible window 10% to the left and 10% up.
- *
- * @param bounds current envelope
- * @param zoomFactor fraction of size to zoom in by
- * @param offsetFactor fraction of size to offset visible window by
- * @return adjusted envelope
- */
- public static ReferencedEnvelope adjustBounds(ReferencedEnvelope bounds,
- double zoomFactor, double[] offsetFactor) {
- if (offsetFactor == null || offsetFactor.length < bounds.getDimension()) {
- offsetFactor = new double[bounds.getDimension()];
- }
- ReferencedEnvelope scaled = new ReferencedEnvelope(bounds);
- if (Math.abs(zoomFactor - 1.0) > 0.01) {
- double[] min = scaled.getLowerCorner().getCoordinate();
- double[] max = scaled.getUpperCorner().getCoordinate();
- for (int i = 0; i < scaled.getDimension(); i++) {
- double span = scaled.getSpan(i);
- double delta = (span - span * zoomFactor) / 2.0;
- double shift = span * offsetFactor[i];
+ /**
+ * Adjust the size and position of a ReferencedEnvelope using fractions of
+ * the current size. For example:
+ *
+ *
+ * bounds = adjustBounds(bounds, 0.3, new double[] { -0.1, 0.1 });
+ *
+ *
+ * This will zoom in to show 30% of the height and width, and will also
+ * move the visible window 10% to the left and 10% up.
+ *
+ * @param bounds current envelope
+ * @param zoomFactor fraction of size to zoom in by
+ * @param offsetFactor fraction of size to offset visible window by
+ * @return adjusted envelope
+ */
+ public static ReferencedEnvelope adjustBounds(ReferencedEnvelope bounds,
+ double zoomFactor, double[] offsetFactor) {
+ if (offsetFactor == null || offsetFactor.length < bounds.getDimension()) {
+ offsetFactor = new double[bounds.getDimension()];
+ }
+ ReferencedEnvelope scaled = new ReferencedEnvelope(bounds);
+ if (Math.abs(zoomFactor - 1.0) > 0.01) {
+ double[] min = scaled.getLowerCorner().getCoordinate();
+ double[] max = scaled.getUpperCorner().getCoordinate();
+ for (int i = 0; i < scaled.getDimension(); i++) {
+ double span = scaled.getSpan(i);
+ double delta = (span - span * zoomFactor) / 2.0;
+ double shift = span * offsetFactor[i];
// System.out.println("Have offset["+i+"]: "+shift);
- min[i] += shift + delta;
- max[i] += shift - delta;
- }
- scaled = new ReferencedEnvelope(min[0], max[0], min[1], max[1],
- scaled.getCoordinateReferenceSystem());
- }
- return scaled;
- }
+ min[i] += shift + delta;
+ max[i] += shift - delta;
+ }
+ scaled = new ReferencedEnvelope(min[0], max[0], min[1], max[1],
+ scaled.getCoordinateReferenceSystem());
+ }
+ return scaled;
+ }
- public static Envelope adjustBounds(Envelope bounds, double zoomFactor, double[] offset) {
- if (offset == null || offset.length < 2) {
- offset = new double[]{0, 0};
- }
- Envelope scaled = new Envelope(bounds);
- if (Math.abs(zoomFactor - 1.0) > 0.01) {
- double[] min = new double[]{scaled.getMinX(), scaled.getMinY()};
- double[] max = new double[]{scaled.getMaxX(), scaled.getMaxY()};
- for (int i = 0; i < 2; i++) {
- double shift = offset[i];
+ public static Envelope adjustBounds(Envelope bounds, double zoomFactor, double[] offset) {
+ if (offset == null || offset.length < 2) {
+ offset = new double[]{0, 0};
+ }
+ Envelope scaled = new Envelope(bounds);
+ if (Math.abs(zoomFactor - 1.0) > 0.01) {
+ double[] min = new double[]{scaled.getMinX(), scaled.getMinY()};
+ double[] max = new double[]{scaled.getMaxX(), scaled.getMaxY()};
+ for (int i = 0; i < 2; i++) {
+ double shift = offset[i];
// System.out.println("Have offset["+i+"]: "+shift);
- double span = (i == 0) ? scaled.getWidth() : scaled.getHeight();
- double delta = (span - span * zoomFactor) / 2.0;
- min[i] += shift + delta;
- max[i] += shift - delta;
- }
- scaled = new Envelope(min[0], max[0], min[1], max[1]);
- }
- return scaled;
- }
+ double span = (i == 0) ? scaled.getWidth() : scaled.getHeight();
+ double delta = (span - span * zoomFactor) / 2.0;
+ min[i] += shift + delta;
+ max[i] += shift - delta;
+ }
+ scaled = new Envelope(min[0], max[0], min[1], max[1]);
+ }
+ return scaled;
+ }
- /**
- * Create an Envelope that should approximately include the specified number
- * of geometries, based on a simple linear calculation of the geometry
- * density. If the layer has fewer geometries, then the layer bounds will be
- * returned. If the limit is set to zero (or negative), a point Envelope
- * will be returned.
- *
- * @param layer the layer whose geometry density is to be used to estimate the
- * size of the envelope
- * @param point the coordinate around which to build the envelope
- * @param limit the number of geometries to be included in the envelope
- * @return an envelope designed to include the estimated number of
- * geometries
- */
- public static Envelope createEnvelopeForGeometryDensityEstimate(Transaction tx, Layer layer, Coordinate point, int limit) {
- if (limit < 1) {
- return new Envelope(point);
- }
- int count = layer.getIndex().count(tx);
- if (count > limit) {
- return createEnvelopeForGeometryDensityEstimate(tx, layer, point, (double) limit / (double) count);
- } else {
- return Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx));
- }
- }
+ /**
+ * Create an Envelope that should approximately include the specified number
+ * of geometries, based on a simple linear calculation of the geometry
+ * density. If the layer has fewer geometries, then the layer bounds will be
+ * returned. If the limit is set to zero (or negative), a point Envelope
+ * will be returned.
+ *
+ * @param layer the layer whose geometry density is to be used to estimate the
+ * size of the envelope
+ * @param point the coordinate around which to build the envelope
+ * @param limit the number of geometries to be included in the envelope
+ * @return an envelope designed to include the estimated number of
+ * geometries
+ */
+ public static Envelope createEnvelopeForGeometryDensityEstimate(Transaction tx, Layer layer, Coordinate point,
+ int limit) {
+ if (limit < 1) {
+ return new Envelope(point);
+ }
+ int count = layer.getIndex().count(tx);
+ if (count > limit) {
+ return createEnvelopeForGeometryDensityEstimate(tx, layer, point, (double) limit / (double) count);
+ } else {
+ return Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx));
+ }
+ }
- /**
- * Create an Envelope that should approximately include the specified number
- * of geometries, based on a simple linear calculation of the geometry
- * density. If the layer has fewer geometries, then the layer bounds will be
- * returned. If the limit is set to zero (or negative), a point Envelope
- * will be returned.
- *
*
- * @param tx the Neo4j transaction to extract necessary data from the database
- * @param layer the layer whose geometry density is to be used to estimate the size of the envelope
- * @param point the coordinate around which to build the envelope
- * @param fraction the fractional number of geometries to be included in the envelope
- * @return an envelope designed to include the estimated number of geometries
- */
- public static Envelope createEnvelopeForGeometryDensityEstimate(Transaction tx, Layer layer, Coordinate point, double fraction) {
- if (fraction < 0.0) {
- return new Envelope(point);
- }
- Envelope bbox = Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx));
- double width = bbox.getWidth() * fraction;
- double height = bbox.getWidth() * fraction;
- Envelope extent = new Envelope(point);
- extent.expandToInclude(point.x - width / 2.0, point.y - height / 2.0);
- extent.expandToInclude(point.x + width / 2.0, point.y + height / 2.0);
- return extent;
- }
+ /**
+ * Create an Envelope that should approximately include the specified number
+ * of geometries, based on a simple linear calculation of the geometry
+ * density. If the layer has fewer geometries, then the layer bounds will be
+ * returned. If the limit is set to zero (or negative), a point Envelope
+ * will be returned.
+ *
*
+ *
+ * @param tx the Neo4j transaction to extract necessary data from the database
+ * @param layer the layer whose geometry density is to be used to estimate the size of the envelope
+ * @param point the coordinate around which to build the envelope
+ * @param fraction the fractional number of geometries to be included in the envelope
+ * @return an envelope designed to include the estimated number of geometries
+ */
+ public static Envelope createEnvelopeForGeometryDensityEstimate(Transaction tx, Layer layer, Coordinate point,
+ double fraction) {
+ if (fraction < 0.0) {
+ return new Envelope(point);
+ }
+ Envelope bbox = Utilities.fromNeo4jToJts(layer.getIndex().getBoundingBox(tx));
+ double width = bbox.getWidth() * fraction;
+ double height = bbox.getWidth() * fraction;
+ Envelope extent = new Envelope(point);
+ extent.expandToInclude(point.x - width / 2.0, point.y - height / 2.0);
+ extent.expandToInclude(point.x + width / 2.0, point.y + height / 2.0);
+ return extent;
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/Utilities.java b/src/main/java/org/neo4j/gis/spatial/Utilities.java
index c9ec95f3f..a2e5a4759 100644
--- a/src/main/java/org/neo4j/gis/spatial/Utilities.java
+++ b/src/main/java/org/neo4j/gis/spatial/Utilities.java
@@ -21,7 +21,8 @@
import java.util.Comparator;
import java.util.Iterator;
-
+import org.geotools.api.filter.Filter;
+import org.geotools.api.geometry.BoundingBox;
import org.geotools.filter.AndImpl;
import org.geotools.filter.GeometryFilterImpl;
import org.geotools.filter.LiteralExpressionImpl;
@@ -33,13 +34,10 @@
import org.geotools.filter.spatial.OverlapsImpl;
import org.geotools.filter.spatial.TouchesImpl;
import org.geotools.filter.spatial.WithinImpl;
+import org.locationtech.jts.geom.Geometry;
import org.neo4j.gis.spatial.rtree.Envelope;
import org.neo4j.gis.spatial.rtree.EnvelopeDecoder;
import org.neo4j.graphdb.Node;
-import org.geotools.api.filter.Filter;
-
-import org.locationtech.jts.geom.Geometry;
-import org.geotools.api.geometry.BoundingBox;
public class Utilities {
@@ -69,16 +67,17 @@ public static org.neo4j.gis.spatial.rtree.Envelope extractEnvelopeFromFilter(Fil
}
@SuppressWarnings("rawtypes")
- private static org.neo4j.gis.spatial.rtree.Envelope extractEnvelopeFromFilter(Filter filter, boolean inspectAndFilters) {
+ private static org.neo4j.gis.spatial.rtree.Envelope extractEnvelopeFromFilter(Filter filter,
+ boolean inspectAndFilters) {
if (filter instanceof BBOXImpl) {
return extractEnvelopeFromBBox((BBOXImpl) filter);
} else if (filter instanceof IntersectsImpl ||
- filter instanceof ContainsImpl ||
- filter instanceof CrossesImpl ||
- filter instanceof EqualsImpl ||
- filter instanceof OverlapsImpl ||
- filter instanceof TouchesImpl ||
- filter instanceof WithinImpl) {
+ filter instanceof ContainsImpl ||
+ filter instanceof CrossesImpl ||
+ filter instanceof EqualsImpl ||
+ filter instanceof OverlapsImpl ||
+ filter instanceof TouchesImpl ||
+ filter instanceof WithinImpl) {
return extractEnvelopeFromGeometryFilter((GeometryFilterImpl) filter);
} else if (filter instanceof AndImpl && inspectAndFilters) {
AndImpl andFilter = (AndImpl) filter;
@@ -114,19 +113,20 @@ private static Envelope extractEnvelopeFromLiteralExpression(LiteralExpressionIm
}
}
- private static Envelope extractEnvelopeFromBBox(BBOXImpl boundingBox) {
+ private static Envelope extractEnvelopeFromBBox(BBOXImpl boundingBox) {
BoundingBox bbox = boundingBox.getBounds();
- return new Envelope(bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox.getMaxY());
- }
+ return new Envelope(bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox.getMaxY());
+ }
/**
* Comparator for comparing nodes by compaing the xMin on their evelopes.
*/
public static class ComparatorOnXMin implements Comparator {
+
final private EnvelopeDecoder decoder;
- public ComparatorOnXMin(EnvelopeDecoder decoder){
+ public ComparatorOnXMin(EnvelopeDecoder decoder) {
this.decoder = decoder;
}
@@ -140,9 +140,10 @@ public int compare(Node o1, Node o2) {
* Comparator or comparing nodes by coparing the yMin on their envelopes.
*/
public static class ComparatorOnYMin implements Comparator {
+
final private EnvelopeDecoder decoder;
- public ComparatorOnYMin(EnvelopeDecoder decoder){
+ public ComparatorOnYMin(EnvelopeDecoder decoder) {
this.decoder = decoder;
}
diff --git a/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java b/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java
index 82e197a23..80477c811 100644
--- a/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java
+++ b/src/main/java/org/neo4j/gis/spatial/WKBGeometryEncoder.java
@@ -30,23 +30,23 @@
public class WKBGeometryEncoder extends AbstractSinglePropertyEncoder implements Configurable {
- public Geometry decodeGeometry(Entity container) {
- try {
- WKBReader reader = new WKBReader(layer.getGeometryFactory());
- return reader.read((byte[]) container.getProperty(geomProperty));
- } catch (ParseException e) {
- throw new SpatialDatabaseException(e.getMessage(), e);
- }
- }
+ public Geometry decodeGeometry(Entity container) {
+ try {
+ WKBReader reader = new WKBReader(layer.getGeometryFactory());
+ return reader.read((byte[]) container.getProperty(geomProperty));
+ } catch (ParseException e) {
+ throw new SpatialDatabaseException(e.getMessage(), e);
+ }
+ }
- @Override
- protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
- WKBWriter writer = new WKBWriter();
- container.setProperty(geomProperty, writer.write(geometry));
- }
+ @Override
+ protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
+ WKBWriter writer = new WKBWriter();
+ container.setProperty(geomProperty, writer.write(geometry));
+ }
- @Override
- public String getSignature() {
- return "WKB" + super.getSignature();
- }
-}
\ No newline at end of file
+ @Override
+ public String getSignature() {
+ return "WKB" + super.getSignature();
+ }
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java b/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java
index 5195dbcd1..49ef11ab0 100644
--- a/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java
+++ b/src/main/java/org/neo4j/gis/spatial/WKTGeometryEncoder.java
@@ -30,23 +30,23 @@
public class WKTGeometryEncoder extends AbstractSinglePropertyEncoder implements Configurable {
- public Geometry decodeGeometry(Entity container) {
- try {
- WKTReader reader = new WKTReader(layer.getGeometryFactory());
- return reader.read((String) container.getProperty(geomProperty));
- } catch (ParseException e) {
- throw new SpatialDatabaseException(e.getMessage(), e);
- }
- }
+ public Geometry decodeGeometry(Entity container) {
+ try {
+ WKTReader reader = new WKTReader(layer.getGeometryFactory());
+ return reader.read((String) container.getProperty(geomProperty));
+ } catch (ParseException e) {
+ throw new SpatialDatabaseException(e.getMessage(), e);
+ }
+ }
- @Override
- protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
- WKTWriter writer = new WKTWriter();
- container.setProperty(geomProperty, writer.write(geometry));
- }
+ @Override
+ protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
+ WKTWriter writer = new WKTWriter();
+ container.setProperty(geomProperty, writer.write(geometry));
+ }
- @Override
- public String getSignature() {
- return "WKT" + super.getSignature();
- }
-}
\ No newline at end of file
+ @Override
+ public String getSignature() {
+ return "WKT" + super.getSignature();
+ }
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java
index 755de4389..5635e9f21 100644
--- a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java
+++ b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMapper.java
@@ -20,11 +20,11 @@
package org.neo4j.gis.spatial.attributes;
import java.util.HashMap;
-
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
public abstract class PropertyMapper {
+
private String from;
private String to;
protected String type;
@@ -64,7 +64,8 @@ public String from() {
}
public String key() {
- return new StringBuffer().append(from).append("-").append(to).append("-").append(type).append("-").append(params).toString();
+ return new StringBuffer().append(from).append("-").append(to).append("-").append(type).append("-")
+ .append(params).toString();
}
public static PropertyMapper fromNode(Node node) {
@@ -103,6 +104,7 @@ public Object map(Object value) {
}
private static class DeltaLongMapper extends PropertyMapper {
+
protected long reference;
public DeltaLongMapper(String from, String to, String type, long reference) {
@@ -134,13 +136,13 @@ public Object map(Object value) {
private static class MapMapper extends PropertyMapper {
- private HashMap map = new HashMap();
+ private HashMap map = new HashMap();
public MapMapper(String from, String to, String type, String params) {
super(from, to, type, params);
- for(String param:params.split(",")){
+ for (String param : params.split(",")) {
String[] fields = param.split(":");
- map.put(fields[0],fields[1]);
+ map.put(fields[0], fields[1]);
}
}
diff --git a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java
index a5b6eab01..c1780e709 100644
--- a/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java
+++ b/src/main/java/org/neo4j/gis/spatial/attributes/PropertyMappingManager.java
@@ -28,78 +28,81 @@
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
-import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.graphdb.Transaction;
public class PropertyMappingManager {
- private final Layer layer;
- private LinkedHashMap propertyMappers;
- public PropertyMappingManager(Layer layer) {
- this.layer = layer;
- }
+ private final Layer layer;
+ private LinkedHashMap propertyMappers;
- private LinkedHashMap getPropertyMappers(Transaction tx) {
- if (propertyMappers == null) {
- propertyMappers = new LinkedHashMap<>();
- for (PropertyMapper mapper : loadMappers(tx).values()) {
- addPropertyMapper(tx, mapper);
- }
- }
- return propertyMappers;
- }
+ public PropertyMappingManager(Layer layer) {
+ this.layer = layer;
+ }
- private Map loadMappers(Transaction tx) {
- HashMap mappers = new HashMap<>();
- try (var relationships = layer.getLayerNode(tx).getRelationships(Direction.OUTGOING, SpatialRelationshipTypes.PROPERTY_MAPPING)) {
- for (Relationship rel : relationships) {
- Node node = rel.getEndNode();
- mappers.put(node, PropertyMapper.fromNode(node));
- }
- }
- return mappers;
- }
+ private LinkedHashMap getPropertyMappers(Transaction tx) {
+ if (propertyMappers == null) {
+ propertyMappers = new LinkedHashMap<>();
+ for (PropertyMapper mapper : loadMappers(tx).values()) {
+ addPropertyMapper(tx, mapper);
+ }
+ }
+ return propertyMappers;
+ }
- private void save(Transaction tx) {
- ArrayList toSave = new ArrayList<>(getPropertyMappers(tx).values());
- ArrayList toDelete = new ArrayList<>();
- for (Map.Entry entry : loadMappers(tx).entrySet()) {
- if (!toSave.remove(entry.getValue())) {
- toDelete.add(entry.getKey());
- }
- }
- for (Node node : toDelete) {
- try(var relationships = node.getRelationships()){
- for (Relationship rel : relationships) {
- rel.delete();
- }
- }
- node.delete();
- }
- for (PropertyMapper mapper : toSave) {
- Node node = tx.createNode();
- mapper.save(tx, node);
- layer.getLayerNode(tx).createRelationshipTo(node, SpatialRelationshipTypes.PROPERTY_MAPPING);
- }
- }
+ private Map loadMappers(Transaction tx) {
+ HashMap mappers = new HashMap<>();
+ try (var relationships = layer.getLayerNode(tx)
+ .getRelationships(Direction.OUTGOING, SpatialRelationshipTypes.PROPERTY_MAPPING)) {
+ for (Relationship rel : relationships) {
+ Node node = rel.getEndNode();
+ mappers.put(node, PropertyMapper.fromNode(node));
+ }
+ }
+ return mappers;
+ }
- private void addPropertyMapper(Transaction tx, PropertyMapper mapper) {
- getPropertyMappers(tx).put(mapper.to(), mapper);
- save(tx);
- }
+ private void save(Transaction tx) {
+ ArrayList toSave = new ArrayList<>(getPropertyMappers(tx).values());
+ ArrayList toDelete = new ArrayList<>();
+ for (Map.Entry entry : loadMappers(tx).entrySet()) {
+ if (!toSave.remove(entry.getValue())) {
+ toDelete.add(entry.getKey());
+ }
+ }
+ for (Node node : toDelete) {
+ try (var relationships = node.getRelationships()) {
+ for (Relationship rel : relationships) {
+ rel.delete();
+ }
+ }
+ node.delete();
+ }
+ for (PropertyMapper mapper : toSave) {
+ Node node = tx.createNode();
+ mapper.save(tx, node);
+ layer.getLayerNode(tx).createRelationshipTo(node, SpatialRelationshipTypes.PROPERTY_MAPPING);
+ }
+ }
- private PropertyMapper removePropertyMapper(Transaction tx, String to) {
- PropertyMapper mapper = getPropertyMappers(tx).remove(to);
- if (mapper != null) save(tx);
- return mapper;
- }
+ private void addPropertyMapper(Transaction tx, PropertyMapper mapper) {
+ getPropertyMappers(tx).put(mapper.to(), mapper);
+ save(tx);
+ }
- public PropertyMapper getPropertyMapper(Transaction tx, String to) {
- return getPropertyMappers(tx).get(to);
- }
+ private PropertyMapper removePropertyMapper(Transaction tx, String to) {
+ PropertyMapper mapper = getPropertyMappers(tx).remove(to);
+ if (mapper != null) {
+ save(tx);
+ }
+ return mapper;
+ }
- public void addPropertyMapper(Transaction tx, String from, String to, String type, String params) {
- addPropertyMapper(tx, PropertyMapper.fromParams(from, to, type, params));
- }
+ public PropertyMapper getPropertyMapper(Transaction tx, String to) {
+ return getPropertyMappers(tx).get(to);
+ }
+
+ public void addPropertyMapper(Transaction tx, String from, String to, String type, String params) {
+ addPropertyMapper(tx, PropertyMapper.fromParams(from, to, type, params));
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/AbstractSinglePropertyEncoder.java b/src/main/java/org/neo4j/gis/spatial/encoders/AbstractSinglePropertyEncoder.java
index c1871a635..6c6cded67 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/AbstractSinglePropertyEncoder.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/AbstractSinglePropertyEncoder.java
@@ -10,10 +10,12 @@ public abstract class AbstractSinglePropertyEncoder extends AbstractGeometryEnco
public void setConfiguration(String configuration) {
if (configuration != null && configuration.trim().length() > 0) {
String[] fields = configuration.split(":");
- if (fields.length > 0)
+ if (fields.length > 0) {
geomProperty = fields[0];
- if (fields.length > 1)
+ }
+ if (fields.length > 1) {
bboxProperty = fields[1];
+ }
}
}
@@ -26,4 +28,4 @@ public String getConfiguration() {
public String getSignature() {
return "GeometryEncoder(geom='" + geomProperty + "', bbox='" + bboxProperty + "')";
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/Configurable.java b/src/main/java/org/neo4j/gis/spatial/encoders/Configurable.java
index 63ed6f48a..232eff48c 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/Configurable.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/Configurable.java
@@ -20,6 +20,8 @@
package org.neo4j.gis.spatial.encoders;
public interface Configurable {
+
String getConfiguration();
+
void setConfiguration(String configuration);
}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/NativePointEncoder.java b/src/main/java/org/neo4j/gis/spatial/encoders/NativePointEncoder.java
index 13fa58d09..5da93bef1 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/NativePointEncoder.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/NativePointEncoder.java
@@ -34,60 +34,73 @@
* Simple encoder that stores point geometries as one Neo4j Point property.
*/
public class NativePointEncoder extends AbstractGeometryEncoder implements Configurable {
- private static final String DEFAULT_GEOM = "location";
- private static GeometryFactory geometryFactory;
- private String locationProperty = DEFAULT_GEOM;
- private Neo4jCRS crs = Neo4jCRS.findCRS("WGS-84");
- protected GeometryFactory getGeometryFactory() {
- if (geometryFactory == null) geometryFactory = new GeometryFactory();
- return geometryFactory;
- }
+ private static final String DEFAULT_GEOM = "location";
+ private static GeometryFactory geometryFactory;
+ private String locationProperty = DEFAULT_GEOM;
+ private Neo4jCRS crs = Neo4jCRS.findCRS("WGS-84");
- @Override
- protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
- int gtype = SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass());
- if (gtype == GTYPE_POINT) {
- container.setProperty("gtype", gtype);
- Neo4jPoint neo4jPoint = new Neo4jPoint((Point) geometry, crs);
- container.setProperty(locationProperty, neo4jPoint);
- } else {
- throw new IllegalArgumentException("Cannot store non-Point types as Native Neo4j properties: " + SpatialDatabaseService.convertGeometryTypeToName(gtype));
- }
+ protected GeometryFactory getGeometryFactory() {
+ if (geometryFactory == null) {
+ geometryFactory = new GeometryFactory();
+ }
+ return geometryFactory;
+ }
- }
+ @Override
+ protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
+ int gtype = SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass());
+ if (gtype == GTYPE_POINT) {
+ container.setProperty("gtype", gtype);
+ Neo4jPoint neo4jPoint = new Neo4jPoint((Point) geometry, crs);
+ container.setProperty(locationProperty, neo4jPoint);
+ } else {
+ throw new IllegalArgumentException("Cannot store non-Point types as Native Neo4j properties: "
+ + SpatialDatabaseService.convertGeometryTypeToName(gtype));
+ }
- @Override
- public Geometry decodeGeometry(Entity container) {
- org.neo4j.graphdb.spatial.Point point = ((org.neo4j.graphdb.spatial.Point) container.getProperty(locationProperty));
- if (point.getCRS().getCode() != crs.getCode()) {
- throw new IllegalStateException("Trying to decode geometry with wrong CRS: layer configured to crs=" + crs + ", but geometry has crs=" + point.getCRS().getCode());
- }
- double[] coordinate = point.getCoordinate().getCoordinate();
- if (crs.dimensions() == 3) {
- return getGeometryFactory().createPoint(new Coordinate(coordinate[0], coordinate[1], coordinate[2]));
- } else {
- return getGeometryFactory().createPoint(new Coordinate(coordinate[0], coordinate[1]));
- }
- }
+ }
- @Override
- public String getConfiguration() {
- return locationProperty + ":" + bboxProperty + ": " + crs.getCode();
- }
+ @Override
+ public Geometry decodeGeometry(Entity container) {
+ org.neo4j.graphdb.spatial.Point point = ((org.neo4j.graphdb.spatial.Point) container.getProperty(
+ locationProperty));
+ if (point.getCRS().getCode() != crs.getCode()) {
+ throw new IllegalStateException("Trying to decode geometry with wrong CRS: layer configured to crs=" + crs
+ + ", but geometry has crs=" + point.getCRS().getCode());
+ }
+ double[] coordinate = point.getCoordinate().getCoordinate();
+ if (crs.dimensions() == 3) {
+ return getGeometryFactory().createPoint(new Coordinate(coordinate[0], coordinate[1], coordinate[2]));
+ } else {
+ return getGeometryFactory().createPoint(new Coordinate(coordinate[0], coordinate[1]));
+ }
+ }
- @Override
- public void setConfiguration(String configuration) {
- if (configuration != null && configuration.trim().length() > 0) {
- String[] fields = configuration.split(":");
- if (fields.length > 0) locationProperty = fields[0];
- if (fields.length > 1) bboxProperty = fields[1];
- if (fields.length > 2) crs = Neo4jCRS.findCRS(fields[2]);
- }
- }
+ @Override
+ public String getConfiguration() {
+ return locationProperty + ":" + bboxProperty + ": " + crs.getCode();
+ }
- @Override
- public String getSignature() {
- return "NativePointEncoder(geometry='" + locationProperty + "', bbox='" + bboxProperty + "', crs=" + crs.getCode() + ")";
- }
+ @Override
+ public void setConfiguration(String configuration) {
+ if (configuration != null && configuration.trim().length() > 0) {
+ String[] fields = configuration.split(":");
+ if (fields.length > 0) {
+ locationProperty = fields[0];
+ }
+ if (fields.length > 1) {
+ bboxProperty = fields[1];
+ }
+ if (fields.length > 2) {
+ crs = Neo4jCRS.findCRS(fields[2]);
+ }
+ }
+ }
+
+ @Override
+ public String getSignature() {
+ return "NativePointEncoder(geometry='" + locationProperty + "', bbox='" + bboxProperty + "', crs="
+ + crs.getCode() + ")";
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/SimpleGraphEncoder.java b/src/main/java/org/neo4j/gis/spatial/encoders/SimpleGraphEncoder.java
index aa05032dc..e756b920d 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/SimpleGraphEncoder.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/SimpleGraphEncoder.java
@@ -25,7 +25,11 @@
import org.locationtech.jts.geom.GeometryFactory;
import org.neo4j.gis.spatial.AbstractGeometryEncoder;
import org.neo4j.gis.spatial.SpatialDatabaseException;
-import org.neo4j.graphdb.*;
+import org.neo4j.graphdb.Direction;
+import org.neo4j.graphdb.Entity;
+import org.neo4j.graphdb.Node;
+import org.neo4j.graphdb.RelationshipType;
+import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.traversal.Evaluators;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.kernel.impl.traversal.MonoDirectionalTraversalDescription;
@@ -37,53 +41,57 @@
* @TODO: Consider generalizing this code and making a general linked list geometry store available in the library
*/
public class SimpleGraphEncoder extends AbstractGeometryEncoder {
- private GeometryFactory geometryFactory;
- protected enum SimpleRelationshipTypes implements RelationshipType {
- FIRST, NEXT;
- }
+ private GeometryFactory geometryFactory;
- private GeometryFactory getGeometryFactory() {
- if (geometryFactory == null) geometryFactory = new GeometryFactory();
- return geometryFactory;
- }
+ protected enum SimpleRelationshipTypes implements RelationshipType {
+ FIRST, NEXT;
+ }
- private Node testIsNode(Entity container) {
- if (!(container instanceof Node)) {
- throw new SpatialDatabaseException("Cannot decode non-node geometry: " + container);
- }
- return (Node) container;
- }
+ private GeometryFactory getGeometryFactory() {
+ if (geometryFactory == null) {
+ geometryFactory = new GeometryFactory();
+ }
+ return geometryFactory;
+ }
- @Override
- protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
- Node node = testIsNode(container);
- node.setProperty("gtype", GTYPE_LINESTRING);
- Node prev = null;
- for (Coordinate coord : geometry.getCoordinates()) {
- Node point = tx.createNode();
- point.setProperty("x", coord.x);
- point.setProperty("y", coord.y);
- point.setProperty("z", coord.z);
- if (prev == null) {
- node.createRelationshipTo(point, SimpleRelationshipTypes.FIRST);
- } else {
- prev.createRelationshipTo(point, SimpleRelationshipTypes.NEXT);
- }
- prev = point;
- }
- }
+ private Node testIsNode(Entity container) {
+ if (!(container instanceof Node)) {
+ throw new SpatialDatabaseException("Cannot decode non-node geometry: " + container);
+ }
+ return (Node) container;
+ }
- public Geometry decodeGeometry(Entity container) {
- Node node = testIsNode(container);
- CoordinateList coordinates = new CoordinateList();
- TraversalDescription td = new MonoDirectionalTraversalDescription().depthFirst()
- .relationships(SimpleRelationshipTypes.FIRST, Direction.OUTGOING)
- .relationships(SimpleRelationshipTypes.NEXT, Direction.OUTGOING).breadthFirst()
- .evaluator(Evaluators.excludeStartPosition());
- for (Node point : td.traverse(node).nodes()) {
- coordinates.add(new Coordinate((Double) point.getProperty("x"), (Double) point.getProperty("y"), (Double) point.getProperty("z")), false);
- }
- return getGeometryFactory().createLineString(coordinates.toCoordinateArray());
- }
-}
\ No newline at end of file
+ @Override
+ protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
+ Node node = testIsNode(container);
+ node.setProperty("gtype", GTYPE_LINESTRING);
+ Node prev = null;
+ for (Coordinate coord : geometry.getCoordinates()) {
+ Node point = tx.createNode();
+ point.setProperty("x", coord.x);
+ point.setProperty("y", coord.y);
+ point.setProperty("z", coord.z);
+ if (prev == null) {
+ node.createRelationshipTo(point, SimpleRelationshipTypes.FIRST);
+ } else {
+ prev.createRelationshipTo(point, SimpleRelationshipTypes.NEXT);
+ }
+ prev = point;
+ }
+ }
+
+ public Geometry decodeGeometry(Entity container) {
+ Node node = testIsNode(container);
+ CoordinateList coordinates = new CoordinateList();
+ TraversalDescription td = new MonoDirectionalTraversalDescription().depthFirst()
+ .relationships(SimpleRelationshipTypes.FIRST, Direction.OUTGOING)
+ .relationships(SimpleRelationshipTypes.NEXT, Direction.OUTGOING).breadthFirst()
+ .evaluator(Evaluators.excludeStartPosition());
+ for (Node point : td.traverse(node).nodes()) {
+ coordinates.add(new Coordinate((Double) point.getProperty("x"), (Double) point.getProperty("y"),
+ (Double) point.getProperty("z")), false);
+ }
+ return getGeometryFactory().createLineString(coordinates.toCoordinateArray());
+ }
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/SimplePointEncoder.java b/src/main/java/org/neo4j/gis/spatial/encoders/SimplePointEncoder.java
index c3ae1ec6b..1d91fbff3 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/SimplePointEncoder.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/SimplePointEncoder.java
@@ -31,52 +31,61 @@
* Simple encoder that stores point geometries as two x/y properties.
*/
public class SimplePointEncoder extends AbstractGeometryEncoder implements Configurable {
- public static final String DEFAULT_X = "longitude";
- public static final String DEFAULT_Y = "latitude";
- protected GeometryFactory geometryFactory;
- protected String xProperty = DEFAULT_X;
- protected String yProperty = DEFAULT_Y;
- protected GeometryFactory getGeometryFactory() {
- if (geometryFactory == null) geometryFactory = new GeometryFactory();
- return geometryFactory;
- }
+ public static final String DEFAULT_X = "longitude";
+ public static final String DEFAULT_Y = "latitude";
+ protected GeometryFactory geometryFactory;
+ protected String xProperty = DEFAULT_X;
+ protected String yProperty = DEFAULT_Y;
- @Override
- protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
- container.setProperty(
- "gtype",
- SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass()));
- Coordinate[] coords = geometry.getCoordinates();
- container.setProperty(xProperty, coords[0].x);
- container.setProperty(yProperty, coords[0].y);
- }
+ protected GeometryFactory getGeometryFactory() {
+ if (geometryFactory == null) {
+ geometryFactory = new GeometryFactory();
+ }
+ return geometryFactory;
+ }
- @Override
- public Geometry decodeGeometry(Entity container) {
- double x = ((Number) container.getProperty(xProperty)).doubleValue();
- double y = ((Number) container.getProperty(yProperty)).doubleValue();
- Coordinate coordinate = new Coordinate(x, y);
- return getGeometryFactory().createPoint(coordinate);
- }
+ @Override
+ protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
+ container.setProperty(
+ "gtype",
+ SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass()));
+ Coordinate[] coords = geometry.getCoordinates();
+ container.setProperty(xProperty, coords[0].x);
+ container.setProperty(yProperty, coords[0].y);
+ }
- @Override
- public String getConfiguration() {
- return xProperty + ":" + yProperty + ":" + bboxProperty;
- }
+ @Override
+ public Geometry decodeGeometry(Entity container) {
+ double x = ((Number) container.getProperty(xProperty)).doubleValue();
+ double y = ((Number) container.getProperty(yProperty)).doubleValue();
+ Coordinate coordinate = new Coordinate(x, y);
+ return getGeometryFactory().createPoint(coordinate);
+ }
- @Override
- public void setConfiguration(String configuration) {
- if (configuration != null && configuration.trim().length() > 0) {
- String[] fields = configuration.split(":");
- if (fields.length > 0) xProperty = fields[0];
- if (fields.length > 1) yProperty = fields[1];
- if (fields.length > 2) bboxProperty = fields[2];
- }
- }
+ @Override
+ public String getConfiguration() {
+ return xProperty + ":" + yProperty + ":" + bboxProperty;
+ }
- @Override
- public String getSignature() {
- return "SimplePointEncoder(x='" + xProperty + "', y='" + yProperty + "', bbox='" + bboxProperty + "')";
- }
+ @Override
+ public void setConfiguration(String configuration) {
+ if (configuration != null && configuration.trim().length() > 0) {
+ String[] fields = configuration.split(":");
+ if (fields.length > 0) {
+ xProperty = fields[0];
+ }
+ if (fields.length > 1) {
+ yProperty = fields[1];
+ }
+ if (fields.length > 2) {
+ bboxProperty = fields[2];
+ }
+ }
+ }
+
+ @Override
+ public String getSignature() {
+ return "SimplePointEncoder(x='" + xProperty + "', y='" + yProperty + "', bbox='" + bboxProperty + "')";
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/SimplePropertyEncoder.java b/src/main/java/org/neo4j/gis/spatial/encoders/SimplePropertyEncoder.java
index 4c0a66f75..1354bed98 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/SimplePropertyEncoder.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/SimplePropertyEncoder.java
@@ -35,33 +35,36 @@
* @TODO: Consider switching from Float to Double according to Davide Savazzi
*/
public class SimplePropertyEncoder extends AbstractGeometryEncoder {
- protected GeometryFactory geometryFactory;
- protected GeometryFactory getGeometryFactory() {
- if (geometryFactory == null) geometryFactory = new GeometryFactory();
- return geometryFactory;
- }
+ protected GeometryFactory geometryFactory;
- @Override
- protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
- container.setProperty("gtype", SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass()));
- Coordinate[] coords = geometry.getCoordinates();
- float[] data = new float[coords.length * 2];
- for (int i = 0; i < coords.length; i++) {
- data[i * 2 + 0] = (float) coords[i].x;
- data[i * 2 + 1] = (float) coords[i].y;
- }
+ protected GeometryFactory getGeometryFactory() {
+ if (geometryFactory == null) {
+ geometryFactory = new GeometryFactory();
+ }
+ return geometryFactory;
+ }
- container.setProperty("data", data);
- }
+ @Override
+ protected void encodeGeometryShape(Transaction tx, Geometry geometry, Entity container) {
+ container.setProperty("gtype", SpatialDatabaseService.convertJtsClassToGeometryType(geometry.getClass()));
+ Coordinate[] coords = geometry.getCoordinates();
+ float[] data = new float[coords.length * 2];
+ for (int i = 0; i < coords.length; i++) {
+ data[i * 2 + 0] = (float) coords[i].x;
+ data[i * 2 + 1] = (float) coords[i].y;
+ }
- @Override
- public Geometry decodeGeometry(Entity container) {
- float[] data = (float[]) container.getProperty("data");
- Coordinate[] coordinates = new Coordinate[data.length / 2];
- for (int i = 0; i < data.length / 2; i++) {
- coordinates[i] = new Coordinate(data[2 * i + 0], data[2 * i + 1]);
- }
- return getGeometryFactory().createLineString(coordinates);
- }
-}
\ No newline at end of file
+ container.setProperty("data", data);
+ }
+
+ @Override
+ public Geometry decodeGeometry(Entity container) {
+ float[] data = (float[]) container.getProperty("data");
+ Coordinate[] coordinates = new Coordinate[data.length / 2];
+ for (int i = 0; i < data.length / 2; i++) {
+ coordinates[i] = new Coordinate(data[2 * i + 0], data[2 * i + 1]);
+ }
+ return getGeometryFactory().createLineString(coordinates);
+ }
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jCRS.java b/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jCRS.java
index 28886f8a9..df388faa3 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jCRS.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jCRS.java
@@ -22,44 +22,45 @@
import org.neo4j.values.storable.CoordinateReferenceSystem;
public class Neo4jCRS implements org.neo4j.graphdb.spatial.CRS {
- protected final CoordinateReferenceSystem crs;
- public Neo4jCRS(CoordinateReferenceSystem crs) {
- this.crs = crs;
- }
+ protected final CoordinateReferenceSystem crs;
- @Override
- public int getCode() {
- return crs.getCode();
- }
+ public Neo4jCRS(CoordinateReferenceSystem crs) {
+ this.crs = crs;
+ }
- @Override
- public String getType() {
- return crs.getType();
- }
+ @Override
+ public int getCode() {
+ return crs.getCode();
+ }
- @Override
- public String getHref() {
- return crs.getHref();
- }
+ @Override
+ public String getType() {
+ return crs.getType();
+ }
- public int dimensions() {
- return crs.getDimension();
- }
+ @Override
+ public String getHref() {
+ return crs.getHref();
+ }
- public static Neo4jCRS findCRS(String crs) {
- switch (crs) {
- case "WGS-84": // name in Neo4j CRS table
- case "WGS84(DD)": // name in geotools crs library
- return makeCRS(4326);
- case "Cartesian":
- return makeCRS(7203);
- default:
- throw new IllegalArgumentException("Cypher type system does not support CRS: " + crs);
- }
- }
+ public int dimensions() {
+ return crs.getDimension();
+ }
- public static Neo4jCRS makeCRS(final int code) {
- return new Neo4jCRS(CoordinateReferenceSystem.get(code));
- }
+ public static Neo4jCRS findCRS(String crs) {
+ switch (crs) {
+ case "WGS-84": // name in Neo4j CRS table
+ case "WGS84(DD)": // name in geotools crs library
+ return makeCRS(4326);
+ case "Cartesian":
+ return makeCRS(7203);
+ default:
+ throw new IllegalArgumentException("Cypher type system does not support CRS: " + crs);
+ }
+ }
+
+ public static Neo4jCRS makeCRS(final int code) {
+ return new Neo4jCRS(CoordinateReferenceSystem.get(code));
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jGeometry.java b/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jGeometry.java
index dd8805279..1e1272bb1 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jGeometry.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jGeometry.java
@@ -19,41 +19,43 @@
*/
package org.neo4j.gis.spatial.encoders.neo4j;
-import org.neo4j.graphdb.spatial.CRS;
-import org.neo4j.graphdb.spatial.Coordinate;
-
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
+import org.neo4j.graphdb.spatial.CRS;
+import org.neo4j.graphdb.spatial.Coordinate;
public class Neo4jGeometry implements org.neo4j.graphdb.spatial.Geometry {
- protected final String geometryType;
- protected final CRS crs;
- protected final List coordinates;
-
- public Neo4jGeometry(String geometryType, List coordinates, CRS crs) {
- this.geometryType = geometryType;
- this.coordinates = coordinates;
- this.crs = crs;
- }
-
- public String getGeometryType() {
- return this.geometryType;
- }
-
- public List getCoordinates() {
- return this.coordinates;
- }
-
- public CRS getCRS() {
- return this.crs;
- }
-
- public static String coordinateString(List coordinates) {
- return coordinates.stream().map(c -> Arrays.stream(c.getCoordinate()).mapToObj(Double::toString).collect(Collectors.joining(", "))).collect(Collectors.joining(", "));
- }
-
- public String toString() {
- return geometryType + "(" + coordinateString(coordinates) + ")[" + crs + "]";
- }
+
+ protected final String geometryType;
+ protected final CRS crs;
+ protected final List coordinates;
+
+ public Neo4jGeometry(String geometryType, List coordinates, CRS crs) {
+ this.geometryType = geometryType;
+ this.coordinates = coordinates;
+ this.crs = crs;
+ }
+
+ public String getGeometryType() {
+ return this.geometryType;
+ }
+
+ public List getCoordinates() {
+ return this.coordinates;
+ }
+
+ public CRS getCRS() {
+ return this.crs;
+ }
+
+ public static String coordinateString(List coordinates) {
+ return coordinates.stream()
+ .map(c -> Arrays.stream(c.getCoordinate()).mapToObj(Double::toString).collect(Collectors.joining(", ")))
+ .collect(Collectors.joining(", "));
+ }
+
+ public String toString() {
+ return geometryType + "(" + coordinateString(coordinates) + ")[" + crs + "]";
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jPoint.java b/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jPoint.java
index e550cd094..c83f973a6 100644
--- a/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jPoint.java
+++ b/src/main/java/org/neo4j/gis/spatial/encoders/neo4j/Neo4jPoint.java
@@ -1,28 +1,28 @@
package org.neo4j.gis.spatial.encoders.neo4j;
+import java.util.ArrayList;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Point;
-import java.util.ArrayList;
-
public class Neo4jPoint extends Neo4jGeometry implements org.neo4j.graphdb.spatial.Point {
- private final org.neo4j.graphdb.spatial.Coordinate coordinate;
- public Neo4jPoint(double[] coordinate, Neo4jCRS crs) {
- super("Point", new ArrayList<>(), crs);
- this.coordinate = new org.neo4j.graphdb.spatial.Coordinate(coordinate);
- this.coordinates.add(this.coordinate);
- }
+ private final org.neo4j.graphdb.spatial.Coordinate coordinate;
+
+ public Neo4jPoint(double[] coordinate, Neo4jCRS crs) {
+ super("Point", new ArrayList<>(), crs);
+ this.coordinate = new org.neo4j.graphdb.spatial.Coordinate(coordinate);
+ this.coordinates.add(this.coordinate);
+ }
- public Neo4jPoint(Coordinate coord, Neo4jCRS crs) {
- super("Point", new ArrayList<>(), crs);
- this.coordinate = (crs.dimensions() == 3) ?
- new org.neo4j.graphdb.spatial.Coordinate(coord.x, coord.y, coord.z) :
- new org.neo4j.graphdb.spatial.Coordinate(coord.x, coord.y);
- this.coordinates.add(this.coordinate);
- }
+ public Neo4jPoint(Coordinate coord, Neo4jCRS crs) {
+ super("Point", new ArrayList<>(), crs);
+ this.coordinate = (crs.dimensions() == 3) ?
+ new org.neo4j.graphdb.spatial.Coordinate(coord.x, coord.y, coord.z) :
+ new org.neo4j.graphdb.spatial.Coordinate(coord.x, coord.y);
+ this.coordinates.add(this.coordinate);
+ }
- public Neo4jPoint(Point point, Neo4jCRS crs) {
- this(point.getCoordinate(), crs);
- }
+ public Neo4jPoint(Point point, Neo4jCRS crs) {
+ this(point.getCoordinate(), crs);
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/filter/AbstractSearchIntersection.java b/src/main/java/org/neo4j/gis/spatial/filter/AbstractSearchIntersection.java
index 236972be7..93b4e0a2e 100644
--- a/src/main/java/org/neo4j/gis/spatial/filter/AbstractSearchIntersection.java
+++ b/src/main/java/org/neo4j/gis/spatial/filter/AbstractSearchIntersection.java
@@ -19,18 +19,17 @@
*/
package org.neo4j.gis.spatial.filter;
-import org.neo4j.gis.spatial.rtree.filter.AbstractSearchEnvelopeIntersection;
+import org.locationtech.jts.geom.Geometry;
import org.neo4j.gis.spatial.Layer;
import org.neo4j.gis.spatial.Utilities;
+import org.neo4j.gis.spatial.rtree.filter.AbstractSearchEnvelopeIntersection;
import org.neo4j.graphdb.Node;
-import org.locationtech.jts.geom.Geometry;
-
/**
* @author Craig Taverner
*/
public abstract class AbstractSearchIntersection extends AbstractSearchEnvelopeIntersection {
-
+
protected Geometry referenceGeometry;
protected Layer layer;
@@ -44,4 +43,4 @@ protected Geometry decode(Node geomNode) {
return layer.getGeometryEncoder().decodeGeometry(geomNode);
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java b/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java
index 7a55ab00a..9e7307061 100644
--- a/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java
+++ b/src/main/java/org/neo4j/gis/spatial/filter/SearchCQL.java
@@ -19,6 +19,7 @@
*/
package org.neo4j.gis.spatial.filter;
+import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.data.neo4j.Neo4jFeatureBuilder;
import org.geotools.filter.text.cql2.CQLException;
import org.geotools.filter.text.ecql.ECQL;
@@ -30,7 +31,6 @@
import org.neo4j.gis.spatial.rtree.filter.SearchFilter;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
-import org.geotools.api.feature.simple.SimpleFeature;
/**
* Find geometries that have at least one point in common with the given
@@ -38,41 +38,41 @@
*/
public class SearchCQL implements SearchFilter {
- private final Transaction tx;
- private final Neo4jFeatureBuilder featureBuilder;
- private final Layer layer;
- private final org.geotools.api.filter.Filter filter;
- private final Envelope filterEnvelope;
+ private final Transaction tx;
+ private final Neo4jFeatureBuilder featureBuilder;
+ private final Layer layer;
+ private final org.geotools.api.filter.Filter filter;
+ private final Envelope filterEnvelope;
- public SearchCQL(Transaction tx, Layer layer, org.geotools.api.filter.Filter filter) {
- this.tx = tx;
- this.layer = layer;
- this.featureBuilder = Neo4jFeatureBuilder.fromLayer(tx, layer);
- this.filter = filter;
- this.filterEnvelope = Utilities.extractEnvelopeFromFilter(filter);
- }
+ public SearchCQL(Transaction tx, Layer layer, org.geotools.api.filter.Filter filter) {
+ this.tx = tx;
+ this.layer = layer;
+ this.featureBuilder = Neo4jFeatureBuilder.fromLayer(tx, layer);
+ this.filter = filter;
+ this.filterEnvelope = Utilities.extractEnvelopeFromFilter(filter);
+ }
- public SearchCQL(Transaction tx, Layer layer, String cql) {
- this.tx = tx;
- this.layer = layer;
- this.featureBuilder = Neo4jFeatureBuilder.fromLayer(tx, layer);
- try {
- this.filter = ECQL.toFilter(cql);
- this.filterEnvelope = Utilities.extractEnvelopeFromFilter(filter);
- } catch (CQLException e) {
- throw new SpatialDatabaseException("CQLException: " + e.getMessage());
- }
- }
+ public SearchCQL(Transaction tx, Layer layer, String cql) {
+ this.tx = tx;
+ this.layer = layer;
+ this.featureBuilder = Neo4jFeatureBuilder.fromLayer(tx, layer);
+ try {
+ this.filter = ECQL.toFilter(cql);
+ this.filterEnvelope = Utilities.extractEnvelopeFromFilter(filter);
+ } catch (CQLException e) {
+ throw new SpatialDatabaseException("CQLException: " + e.getMessage());
+ }
+ }
- @Override
- public boolean needsToVisit(Envelope envelope) {
- return filterEnvelope == null || filterEnvelope.intersects(envelope);
- }
+ @Override
+ public boolean needsToVisit(Envelope envelope) {
+ return filterEnvelope == null || filterEnvelope.intersects(envelope);
+ }
- @Override
- public boolean geometryMatches(Transaction tx, Node geomNode) {
- SimpleFeature feature = featureBuilder.buildFeature(tx, new SpatialDatabaseRecord(this.layer, geomNode));
- return filter.evaluate(feature);
- }
+ @Override
+ public boolean geometryMatches(Transaction tx, Node geomNode) {
+ SimpleFeature feature = featureBuilder.buildFeature(tx, new SpatialDatabaseRecord(this.layer, geomNode));
+ return filter.evaluate(feature);
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersect.java b/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersect.java
index 25cb05593..a687acd5f 100644
--- a/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersect.java
+++ b/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersect.java
@@ -19,16 +19,15 @@
*/
package org.neo4j.gis.spatial.filter;
-import org.neo4j.gis.spatial.rtree.Envelope;
+import org.locationtech.jts.geom.Geometry;
import org.neo4j.gis.spatial.Layer;
+import org.neo4j.gis.spatial.rtree.Envelope;
import org.neo4j.graphdb.Node;
-import org.locationtech.jts.geom.Geometry;
-
/**
* Find geometries that have at least one point in common with the given geometry
- *
+ *
* @author Davide Savazzi
* @author Craig Taverner
*/
@@ -43,4 +42,4 @@ protected boolean onEnvelopeIntersection(Node geomNode, Envelope geomEnvelope) {
return geometry.intersects(referenceGeometry);
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersectWindow.java b/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersectWindow.java
index bf9619137..9a15249f6 100644
--- a/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersectWindow.java
+++ b/src/main/java/org/neo4j/gis/spatial/filter/SearchIntersectWindow.java
@@ -19,17 +19,16 @@
*/
package org.neo4j.gis.spatial.filter;
-import org.neo4j.gis.spatial.index.Envelope;
-import org.neo4j.gis.spatial.rtree.filter.AbstractSearchEnvelopeIntersection;
+import org.locationtech.jts.geom.Geometry;
import org.neo4j.gis.spatial.Layer;
import org.neo4j.gis.spatial.Utilities;
+import org.neo4j.gis.spatial.index.Envelope;
+import org.neo4j.gis.spatial.rtree.filter.AbstractSearchEnvelopeIntersection;
import org.neo4j.graphdb.Node;
-import org.locationtech.jts.geom.Geometry;
-
/**
* Find geometries that intersect with the specified search window.
- *
+ *
* @author Craig Taverner
*/
public class SearchIntersectWindow extends AbstractSearchEnvelopeIntersection {
@@ -37,9 +36,9 @@ public class SearchIntersectWindow extends AbstractSearchEnvelopeIntersection {
private Layer layer;
private Geometry windowGeom;
- public SearchIntersectWindow(Layer layer, Envelope envelope) {
- this(layer, Utilities.fromNeo4jToJts(envelope));
- }
+ public SearchIntersectWindow(Layer layer, Envelope envelope) {
+ this(layer, Utilities.fromNeo4jToJts(envelope));
+ }
public SearchIntersectWindow(Layer layer, org.locationtech.jts.geom.Envelope other) {
super(layer.getGeometryEncoder(), Utilities.fromJtsToNeo4j(other));
@@ -56,4 +55,4 @@ protected boolean onEnvelopeIntersection(Node geomNode, org.neo4j.gis.spatial.rt
return geometry.intersects(windowGeom);
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/neo4j/gis/spatial/filter/SearchRecords.java b/src/main/java/org/neo4j/gis/spatial/filter/SearchRecords.java
index 2d55450b6..ab8836216 100644
--- a/src/main/java/org/neo4j/gis/spatial/filter/SearchRecords.java
+++ b/src/main/java/org/neo4j/gis/spatial/filter/SearchRecords.java
@@ -19,47 +19,46 @@
*/
package org.neo4j.gis.spatial.filter;
+import java.util.Iterator;
import org.neo4j.gis.spatial.Layer;
import org.neo4j.gis.spatial.SpatialDatabaseRecord;
import org.neo4j.gis.spatial.rtree.filter.SearchResults;
import org.neo4j.graphdb.Node;
-import java.util.Iterator;
-
public class SearchRecords implements Iterable, Iterator {
- private final SearchResults results;
- private final Iterator nodeIterator;
- private final Layer layer;
+ private final SearchResults results;
+ private final Iterator nodeIterator;
+ private final Layer layer;
- public SearchRecords(Layer layer, SearchResults results) {
- this.layer = layer;
- this.results = results;
- nodeIterator = results.iterator();
- }
+ public SearchRecords(Layer layer, SearchResults results) {
+ this.layer = layer;
+ this.results = results;
+ nodeIterator = results.iterator();
+ }
- @Override
- public Iterator iterator() {
- return this;
- }
+ @Override
+ public Iterator iterator() {
+ return this;
+ }
- @Override
- public boolean hasNext() {
- return nodeIterator.hasNext();
- }
+ @Override
+ public boolean hasNext() {
+ return nodeIterator.hasNext();
+ }
- @Override
- public SpatialDatabaseRecord next() {
- return new SpatialDatabaseRecord(layer, nodeIterator.next());
- }
+ @Override
+ public SpatialDatabaseRecord next() {
+ return new SpatialDatabaseRecord(layer, nodeIterator.next());
+ }
- @Override
- public void remove() {
- throw new UnsupportedOperationException("Cannot remove from results");
- }
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("Cannot remove from results");
+ }
- public int count() {
- return results.count();
- }
+ public int count() {
+ return results.count();
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedMonitor.java b/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedMonitor.java
index 19c62bb57..4fc14351e 100644
--- a/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedMonitor.java
+++ b/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedMonitor.java
@@ -20,22 +20,23 @@
package org.neo4j.gis.spatial.index;
public class ExplicitIndexBackedMonitor {
- long hits = 0L;
- long misses = 0;
- public void hit() {
- hits++;
- }
+ long hits = 0L;
+ long misses = 0;
- public void miss() {
- misses++;
- }
+ public void hit() {
+ hits++;
+ }
- public long getHits() {
- return hits;
- }
+ public void miss() {
+ misses++;
+ }
- public long getMisses() {
- return misses;
- }
+ public long getHits() {
+ return hits;
+ }
+
+ public long getMisses() {
+ return misses;
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedPointIndex.java b/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedPointIndex.java
index 58ba3c435..72bd3920d 100644
--- a/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedPointIndex.java
+++ b/src/main/java/org/neo4j/gis/spatial/index/ExplicitIndexBackedPointIndex.java
@@ -23,7 +23,6 @@
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
-import java.util.stream.Stream;
import org.neo4j.gis.spatial.Layer;
import org.neo4j.gis.spatial.filter.SearchRecords;
import org.neo4j.gis.spatial.rtree.Envelope;
@@ -55,183 +54,185 @@
*/
public abstract class ExplicitIndexBackedPointIndex implements LayerIndexReader, SpatialIndexWriter {
- protected Layer layer;
- private PropertyEncodingNodeIndex index;
- private final ExplicitIndexBackedMonitor monitor = new ExplicitIndexBackedMonitor();
-
- protected abstract String indexTypeName();
-
- @Override
- public void init(Transaction tx, IndexManager indexManager, Layer layer) {
- this.layer = layer;
- String indexName = "_SpatialIndex_" + indexTypeName() + "_" + layer.getName();
- Label label = Label.label("SpatialIndex_" + indexTypeName() + "_" + layer.getName());
- this.index = new PropertyEncodingNodeIndex<>(indexManager, indexName, label, indexName.toLowerCase());
- this.index.initialize(tx);
- }
-
- @Override
- public Layer getLayer() {
- return layer;
- }
-
- @Override
- public SearchRecords search(Transaction tx, SearchFilter filter) {
- return new SearchRecords(layer, searchIndex(tx, filter));
- }
-
- @Override
- public void add(Transaction tx, Node geomNode) {
- index.add(geomNode, getIndexValueFor(tx, geomNode));
- }
-
- protected abstract E getIndexValueFor(Transaction tx, Node geomNode);
-
- @Override
- public void add(Transaction tx, List geomNodes) {
- for (Node node : geomNodes) {
- add(tx, node);
- }
- }
-
- @Override
- public void remove(Transaction tx, String geomNodeId, boolean deleteGeomNode, boolean throwExceptionIfNotFound) {
- try {
- Node geomNode = tx.getNodeByElementId(geomNodeId);
- if (geomNode != null) {
- index.remove(geomNode);
- if (deleteGeomNode) {
- try (var relationships = geomNode.getRelationships()) {
- for (Relationship rel : relationships) {
- rel.delete();
- }
- }
- geomNode.delete();
- }
- }
- } catch (NotFoundException nfe) {
- if (throwExceptionIfNotFound) {
- throw nfe;
- }
- }
- }
-
- @Override
- public void removeAll(Transaction tx, boolean deleteGeomNodes, Listener monitor) {
- if (deleteGeomNodes) {
- for (Node node : getAllIndexedNodes(tx)) {
- remove(tx, node.getElementId(), true, true);
- }
- }
- index.delete(tx);
- }
-
- @Override
- public void clear(Transaction tx, Listener monitor) {
- removeAll(tx, false, monitor);
- }
-
- @Override
- public EnvelopeDecoder getEnvelopeDecoder() {
- return layer.getGeometryEncoder();
- }
-
- @Override
- public boolean isEmpty(Transaction tx) {
- return true;
- }
-
- @Override
- public int count(Transaction ignore) {
- return 0;
- }
-
- @Override
- public Envelope getBoundingBox(Transaction tx) {
- return null;
- }
-
- @Override
- public boolean isNodeIndexed(Transaction tx, String nodeId) {
- return false;
- }
-
- @Override
- public Iterable getAllIndexedNodes(Transaction tx) {
- return index.queryAll(tx);
- }
-
- @Override
- public SearchResults searchIndex(Transaction tx, SearchFilter filter) {
- Iterator indexHits = index.query(tx, searcherFor(tx, filter));
- return new SearchResults(() -> new FilteredIndexIterator(tx, indexHits, filter));
- }
-
- private class FilteredIndexIterator implements Iterator {
- private final Transaction tx;
- private final Iterator inner;
- private final SearchFilter filter;
- private Node next = null;
-
- private FilteredIndexIterator(Transaction tx, Iterator inner, SearchFilter filter) {
- this.tx = tx;
- this.inner = inner;
- this.filter = filter;
- prefetch();
- }
-
- private void prefetch() {
- next = null;
- while (inner.hasNext()) {
- Node node = inner.next();
- if (filter.geometryMatches(tx, node)) {
- next = node;
- monitor.hit();
- break;
- } else {
- monitor.miss();
- }
- }
- }
-
- @Override
- public boolean hasNext() {
- return next != null;
- }
-
- @Override
- public Node next() {
- Node node = next;
- if (node == null) {
- throw new NoSuchElementException(); // GeoPipes relies on this behaviour instead of hasNext()
- } else {
- prefetch();
- return node;
- }
- }
- }
-
- /**
- * Create a class capable of performing a specific search based on a custom 2D to 1D conversion.
- */
- protected abstract Neo4jIndexSearcher searcherFor(Transaction tx, SearchFilter filter);
-
- public interface Neo4jIndexSearcher {
- Iterator search(KernelTransaction ktx, Label label, String propertyKey);
- }
-
- @Override
- public void addMonitor(TreeMonitor monitor) {
-
- }
-
- public ExplicitIndexBackedMonitor getMonitor() {
- return this.monitor;
- }
-
- @Override
- public void configure(Map config) {
-
- }
+ protected Layer layer;
+ private PropertyEncodingNodeIndex index;
+ private final ExplicitIndexBackedMonitor monitor = new ExplicitIndexBackedMonitor();
+
+ protected abstract String indexTypeName();
+
+ @Override
+ public void init(Transaction tx, IndexManager indexManager, Layer layer) {
+ this.layer = layer;
+ String indexName = "_SpatialIndex_" + indexTypeName() + "_" + layer.getName();
+ Label label = Label.label("SpatialIndex_" + indexTypeName() + "_" + layer.getName());
+ this.index = new PropertyEncodingNodeIndex<>(indexManager, indexName, label, indexName.toLowerCase());
+ this.index.initialize(tx);
+ }
+
+ @Override
+ public Layer getLayer() {
+ return layer;
+ }
+
+ @Override
+ public SearchRecords search(Transaction tx, SearchFilter filter) {
+ return new SearchRecords(layer, searchIndex(tx, filter));
+ }
+
+ @Override
+ public void add(Transaction tx, Node geomNode) {
+ index.add(geomNode, getIndexValueFor(tx, geomNode));
+ }
+
+ protected abstract E getIndexValueFor(Transaction tx, Node geomNode);
+
+ @Override
+ public void add(Transaction tx, List geomNodes) {
+ for (Node node : geomNodes) {
+ add(tx, node);
+ }
+ }
+
+ @Override
+ public void remove(Transaction tx, String geomNodeId, boolean deleteGeomNode, boolean throwExceptionIfNotFound) {
+ try {
+ Node geomNode = tx.getNodeByElementId(geomNodeId);
+ if (geomNode != null) {
+ index.remove(geomNode);
+ if (deleteGeomNode) {
+ try (var relationships = geomNode.getRelationships()) {
+ for (Relationship rel : relationships) {
+ rel.delete();
+ }
+ }
+ geomNode.delete();
+ }
+ }
+ } catch (NotFoundException nfe) {
+ if (throwExceptionIfNotFound) {
+ throw nfe;
+ }
+ }
+ }
+
+ @Override
+ public void removeAll(Transaction tx, boolean deleteGeomNodes, Listener monitor) {
+ if (deleteGeomNodes) {
+ for (Node node : getAllIndexedNodes(tx)) {
+ remove(tx, node.getElementId(), true, true);
+ }
+ }
+ index.delete(tx);
+ }
+
+ @Override
+ public void clear(Transaction tx, Listener monitor) {
+ removeAll(tx, false, monitor);
+ }
+
+ @Override
+ public EnvelopeDecoder getEnvelopeDecoder() {
+ return layer.getGeometryEncoder();
+ }
+
+ @Override
+ public boolean isEmpty(Transaction tx) {
+ return true;
+ }
+
+ @Override
+ public int count(Transaction ignore) {
+ return 0;
+ }
+
+ @Override
+ public Envelope getBoundingBox(Transaction tx) {
+ return null;
+ }
+
+ @Override
+ public boolean isNodeIndexed(Transaction tx, String nodeId) {
+ return false;
+ }
+
+ @Override
+ public Iterable getAllIndexedNodes(Transaction tx) {
+ return index.queryAll(tx);
+ }
+
+ @Override
+ public SearchResults searchIndex(Transaction tx, SearchFilter filter) {
+ Iterator indexHits = index.query(tx, searcherFor(tx, filter));
+ return new SearchResults(() -> new FilteredIndexIterator(tx, indexHits, filter));
+ }
+
+ private class FilteredIndexIterator implements Iterator {
+
+ private final Transaction tx;
+ private final Iterator inner;
+ private final SearchFilter filter;
+ private Node next = null;
+
+ private FilteredIndexIterator(Transaction tx, Iterator inner, SearchFilter filter) {
+ this.tx = tx;
+ this.inner = inner;
+ this.filter = filter;
+ prefetch();
+ }
+
+ private void prefetch() {
+ next = null;
+ while (inner.hasNext()) {
+ Node node = inner.next();
+ if (filter.geometryMatches(tx, node)) {
+ next = node;
+ monitor.hit();
+ break;
+ } else {
+ monitor.miss();
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public Node next() {
+ Node node = next;
+ if (node == null) {
+ throw new NoSuchElementException(); // GeoPipes relies on this behaviour instead of hasNext()
+ } else {
+ prefetch();
+ return node;
+ }
+ }
+ }
+
+ /**
+ * Create a class capable of performing a specific search based on a custom 2D to 1D conversion.
+ */
+ protected abstract Neo4jIndexSearcher searcherFor(Transaction tx, SearchFilter filter);
+
+ public interface Neo4jIndexSearcher {
+
+ Iterator search(KernelTransaction ktx, Label label, String propertyKey);
+ }
+
+ @Override
+ public void addMonitor(TreeMonitor monitor) {
+
+ }
+
+ public ExplicitIndexBackedMonitor getMonitor() {
+ return this.monitor;
+ }
+
+ @Override
+ public void configure(Map config) {
+
+ }
}
diff --git a/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java b/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java
index 1d567c621..b047eb994 100644
--- a/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java
+++ b/src/main/java/org/neo4j/gis/spatial/index/IndexManager.java
@@ -1,5 +1,7 @@
package org.neo4j.gis.spatial.index;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.schema.IndexDefinition;
@@ -10,241 +12,246 @@
import org.neo4j.kernel.impl.api.security.RestrictedAccessMode;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
public class IndexManager {
- private final GraphDatabaseAPI db;
- private final SecurityContext securityContext;
-
- public static class IndexAccessMode extends RestrictedAccessMode {
- public static SecurityContext withIndexCreate(SecurityContext securityContext) {
- return securityContext.withMode(new IndexAccessMode(securityContext));
- }
-
- private IndexAccessMode(SecurityContext securityContext) {
- super(securityContext.mode(), Static.SCHEMA);
- }
-
- @Override
- public PermissionState allowsTokenCreates(PrivilegeAction action) {
- return PermissionState.EXPLICIT_GRANT;
- }
-
- @Override
- public boolean allowsSchemaWrites() {
- return true;
- }
-
- @Override
- public PermissionState allowsSchemaWrites(PrivilegeAction action) {
- return PermissionState.EXPLICIT_GRANT;
- }
- }
-
- public IndexManager(GraphDatabaseAPI db, SecurityContext securityContext) {
- this.db = db;
- this.securityContext = IndexAccessMode.withIndexCreate(securityContext);
- }
-
- /**
- * Blocking call that spawns a thread to create an index and then waits for that thread to finish.
- * This is highly likely to cause deadlocks on index checks, so be careful where it is used.
- * Best used if you can commit any other outer transaction first, then run this, and after that
- * start a new transaction. For example, see the OSMImport approaching to batching transactions.
- * It is possible to use this in procedures with outer transactions if you can ensure the outer
- * transactions are read-only.
- */
- public IndexDefinition indexFor(Transaction tx, String indexName, Label label, String propertyKey) {
- return indexFor(tx, indexName, label, propertyKey, true);
- }
-
- /**
- * Non-blocking call that spawns a thread to create an index and then waits for that thread to finish.
- * Use this especially on indexes that are not immediately needed. Also use it if you have an outer
- * transaction that cannot be committed before making this call.
- */
- public void makeIndexFor(Transaction tx, String indexName, Label label, String propertyKey) {
- indexFor(tx, indexName, label, propertyKey, false);
- }
-
- private IndexDefinition indexFor(Transaction tx, String indexName, Label label, String propertyKey, boolean waitFor) {
- for (IndexDefinition exists : tx.schema().getIndexes(label)) {
- if (exists.getName().equals(indexName)) {
- return exists;
- }
- }
- String name = "IndexMaker(" + indexName + ")";
- Thread exists = findThread(name);
- if (exists != null) {
- throw new IllegalStateException("Already have thread: " + exists.getName());
- } else {
- IndexMaker indexMaker = new IndexMaker(indexName, label, propertyKey);
- Thread indexMakerThread = new Thread(indexMaker, name);
- if (waitFor) {
- indexMakerThread.start();
- try {
- indexMakerThread.join();
- if (indexMaker.e != null) {
- throw new RuntimeException("Failed to make index " + indexMaker.description(), indexMaker.e);
- }
- return indexMaker.index;
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to make index " + indexMaker.description(), e);
- }
- } else {
- return null;
- }
- }
- }
-
- public void deleteIndex(IndexDefinition index) {
- String name = "IndexRemover(" + index.getName() + ")";
- Thread exists = findThread(name);
- if (exists != null) {
- System.out.println("Already have thread: " + exists.getName());
- } else {
- IndexRemover indexRemover = new IndexRemover(index);
- Thread indexRemoverThread = new Thread(indexRemover, name);
- indexRemoverThread.start();
- }
- }
-
- public void waitForDeletions() {
- waitForThreads("IndexMaker");
- waitForThreads("IndexRemover");
- }
-
- private void waitForThreads(String prefix) {
- Thread found;
- while ((found = findThread(prefix)) != null) {
- try {
- found.join();
- } catch (InterruptedException e) {
- throw new RuntimeException("Wait for thread " + found.getName(), e);
- }
- }
- }
-
- private Thread findThread(String prefix) {
- ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
- Thread found = findThread(rootGroup, prefix);
- if (found != null) {
- System.out.println("Found thread in current group[" + rootGroup.getName() + "}: " + prefix);
- return found;
- }
- ThreadGroup parentGroup;
- while ((parentGroup = rootGroup.getParent()) != null) {
- rootGroup = parentGroup;
- }
- found = findThread(rootGroup, prefix);
- if (found != null) {
- System.out.println("Found thread in root group[" + rootGroup.getName() + "}: " + prefix);
- return found;
- }
- return null;
- }
-
- private Thread findThread(ThreadGroup group, String prefix) {
- Thread[] threads = new Thread[group.activeCount()];
- while (group.enumerate(threads, true) == threads.length) {
- threads = new Thread[threads.length * 2];
- }
- for (Thread thread : threads) {
- if (thread != null && thread.getName() != null && thread.getName().startsWith(prefix)) {
- return thread;
- }
- }
- return null;
- }
-
- private class IndexMaker implements Runnable {
- private final String indexName;
- private final Label label;
- private final String propertyKey;
- private Exception e;
- private IndexDefinition index;
-
- private IndexMaker(String indexName, Label label, String propertyKey) {
- this.indexName = indexName;
- this.label = label;
- this.propertyKey = propertyKey;
- this.e = null;
- }
-
- @Override
- public void run() {
- try {
- try (Transaction tx = db.beginTransaction(KernelTransaction.Type.EXPLICIT, securityContext)) {
- index = findIndex(tx);
- if (index == null) {
- index = tx.schema().indexFor(label).withName(indexName).on(propertyKey).create();
- }
- tx.commit();
- }
- try (Transaction tx = db.beginTransaction(KernelTransaction.Type.EXPLICIT, securityContext)) {
- tx.schema().awaitIndexOnline(indexName, 30, TimeUnit.SECONDS);
- }
- } catch (Exception e) {
- this.e = e;
- }
- }
-
- private IndexDefinition findIndex(Transaction tx) {
- for (IndexDefinition index : tx.schema().getIndexes()) {
- if (indexMatches(index)) {
- return index;
- }
- }
- return null;
- }
-
- private boolean indexMatches(IndexDefinition anIndex) {
- if (anIndex.getName().equals(indexName)) {
- try {
- List