From 2bbedf3681a103acec758fb8a8895b54a83fcc8e Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Wed, 12 Jun 2024 14:26:40 +0200 Subject: [PATCH] Spatial index in graph view (#202) * Move spatial index to Graph so it can be used in graph views * Fix * Add documentation --------- Co-authored-by: Eduardo Ramos --- .../org/gephi/graph/api/Configuration.java | 2 +- src/main/java/org/gephi/graph/api/Graph.java | 7 +++ .../java/org/gephi/graph/api/GraphModel.java | 7 --- src/main/java/org/gephi/graph/api/Rect2D.java | 55 ++++++++++++++++++- .../org/gephi/graph/api/SpatialIndex.java | 12 ++++ .../org/gephi/graph/impl/GraphModelImpl.java | 8 --- .../java/org/gephi/graph/impl/GraphStore.java | 27 ++++----- .../gephi/graph/impl/GraphViewDecorator.java | 14 +++++ .../gephi/graph/impl/UndirectedDecorator.java | 18 +++--- .../org/gephi/graph/impl/BasicGraphStore.java | 5 ++ 10 files changed, 110 insertions(+), 45 deletions(-) diff --git a/src/main/java/org/gephi/graph/api/Configuration.java b/src/main/java/org/gephi/graph/api/Configuration.java index 70c00675..3b14ba87 100644 --- a/src/main/java/org/gephi/graph/api/Configuration.java +++ b/src/main/java/org/gephi/graph/api/Configuration.java @@ -322,7 +322,7 @@ public boolean isEnableEdgeProperties() { * If enabled, the spatial index is updated while node positions are updated. If * unused, disabling it is recommended as it adds some overhead. *

- * The spatial index can be retrieved from {@link GraphModel#getSpatialIndex()}. + * The spatial index can be retrieved from {@link Graph#getSpatialIndex()}. *

* Default is false. * diff --git a/src/main/java/org/gephi/graph/api/Graph.java b/src/main/java/org/gephi/graph/api/Graph.java index 10ad9b24..921dba72 100644 --- a/src/main/java/org/gephi/graph/api/Graph.java +++ b/src/main/java/org/gephi/graph/api/Graph.java @@ -557,4 +557,11 @@ public interface Graph { * @return graph lock */ GraphLock getLock(); + + /** + * Returns the spatial index. + * + * @return spatial index + */ + SpatialIndex getSpatialIndex(); } diff --git a/src/main/java/org/gephi/graph/api/GraphModel.java b/src/main/java/org/gephi/graph/api/GraphModel.java index ba545069..64383bbd 100644 --- a/src/main/java/org/gephi/graph/api/GraphModel.java +++ b/src/main/java/org/gephi/graph/api/GraphModel.java @@ -683,13 +683,6 @@ public static interface DefaultColumns { */ public GraphObserver createGraphObserver(Graph graph, boolean withGraphDiff); - /** - * Returns the spatial index. - * - * @return spatial index - */ - public SpatialIndex getSpatialIndex(); - /** * Returns the time format used to display time. * diff --git a/src/main/java/org/gephi/graph/api/Rect2D.java b/src/main/java/org/gephi/graph/api/Rect2D.java index 53f909ad..bc2b34fb 100644 --- a/src/main/java/org/gephi/graph/api/Rect2D.java +++ b/src/main/java/org/gephi/graph/api/Rect2D.java @@ -66,18 +66,39 @@ public Rect2D(float minX, float minY, float maxX, float maxY) { this.maxY = maxY; } + /** + * Return the rectangle's width. + * + * @return the rectangle's width + */ public float width() { return maxX - minX; } + /** + * Return the rectangle's height. + * + * @return the rectangle's height + */ public float height() { return maxY - minY; } + /** + * Return the rectangle's center, as an array where the first element is the x + * coordinate and the second element is the y coordinate. + * + * @return the rectangle's center + */ public float[] center() { return new float[] { (maxX + minX) / 2, (maxY + minY) / 2 }; } + /** + * Return the rectangle's radius. + * + * @return the rectangle's radius + */ public float radius() { float width = width(); float height = height(); @@ -92,11 +113,17 @@ public String toString() { return toString(FORMAT); } - public String toString(NumberFormat formatter) { + private String toString(NumberFormat formatter) { return "(" + formatter.format(minX) + " " + formatter.format(minY) + ") < " + "(" + formatter .format(maxX) + " " + formatter.format(maxY) + ")"; } + /** + * Returns true if this rectangle contains the given rectangle. + * + * @param rect the rectangle to check + * @return true if this rectangle contains, false otherwise + */ public boolean contains(Rect2D rect) { if (rect == this) { return true; @@ -105,6 +132,12 @@ public boolean contains(Rect2D rect) { return contains(rect.minX, rect.minY, rect.maxX, rect.maxY); } + /** + * Returns true if this rectangle intersects the given rectangle. + * + * @param rect the rectangle to check + * @return true if this rectangle intersects, false otherwise + */ public boolean intersects(Rect2D rect) { if (rect == this) { return true; @@ -113,10 +146,30 @@ public boolean intersects(Rect2D rect) { return intersects(rect.minX, rect.minY, rect.maxX, rect.maxY); } + /** + * Returns true if this rectangle contains the given rectangle. + * + * @param minX the x coordinate of the minimum corner + * @param minY the y coordinate of the minimum corner + * @param maxX the x coordinate of the maximum corner + * @param maxY the y coordinate of the maximum corner + * + * @return true if this rectangle contains, false otherwise + */ public boolean contains(float minX, float minY, float maxX, float maxY) { return this.minX <= minX && this.minY <= minY && this.maxX >= maxX && this.maxY >= maxY; } + /** + * Returns true if this rectangle intersects the given rectangle. + * + * @param minX the x coordinate of the minimum corner + * @param minY the y coordinate of the minimum corner + * @param maxX the x coordinate of the maximum corner + * @param maxY the y coordinate of the maximum corner + * + * @return true if this rectangle intersects, false otherwise + */ public boolean intersects(float minX, float minY, float maxX, float maxY) { return this.minX <= maxX && minX <= this.maxX && this.maxY >= minY && maxY >= this.minY; } diff --git a/src/main/java/org/gephi/graph/api/SpatialIndex.java b/src/main/java/org/gephi/graph/api/SpatialIndex.java index 882852ec..4efb291e 100644 --- a/src/main/java/org/gephi/graph/api/SpatialIndex.java +++ b/src/main/java/org/gephi/graph/api/SpatialIndex.java @@ -22,7 +22,19 @@ */ public interface SpatialIndex { + /** + * Returns the nodes in the given area. + * + * @param rect area to query + * @return nodes in the area + */ NodeIterable getNodesInArea(Rect2D rect); + /** + * Returns the edges in the given area. + * + * @param rect area to query + * @return edges in the area + */ EdgeIterable getEdgesInArea(Rect2D rect); } diff --git a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java index 2304493e..062fba91 100644 --- a/src/main/java/org/gephi/graph/impl/GraphModelImpl.java +++ b/src/main/java/org/gephi/graph/impl/GraphModelImpl.java @@ -369,14 +369,6 @@ public TimeIndex getEdgeTimeIndex(GraphView view) { return null; } - @Override - public SpatialIndex getSpatialIndex() { - if (!configuration.isEnableSpatialIndex()) { - throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); - } - return store.spatialIndex; - } - @Override public GraphObserver createGraphObserver(Graph graph, boolean withGraphDiff) { store.autoWriteLock(); diff --git a/src/main/java/org/gephi/graph/impl/GraphStore.java b/src/main/java/org/gephi/graph/impl/GraphStore.java index 3d6a114f..3d8b015c 100644 --- a/src/main/java/org/gephi/graph/impl/GraphStore.java +++ b/src/main/java/org/gephi/graph/impl/GraphStore.java @@ -23,23 +23,8 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import org.gephi.graph.api.Configuration; -import org.gephi.graph.api.DirectedGraph; -import org.gephi.graph.api.DirectedSubgraph; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeIterable; -import org.gephi.graph.api.ElementIterable; -import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphModel; -import org.gephi.graph.api.GraphView; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.Node; -import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.Origin; -import org.gephi.graph.api.Subgraph; -import org.gephi.graph.api.Table; -import org.gephi.graph.api.TimeFormat; -import org.gephi.graph.api.TimeRepresentation; + +import org.gephi.graph.api.*; import org.gephi.graph.api.types.IntervalSet; import org.gephi.graph.api.types.TimestampSet; @@ -721,6 +706,14 @@ public GraphLockImpl getLock() { return lock; } + @Override + public SpatialIndex getSpatialIndex() { + if (spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return spatialIndex; + } + protected void autoReadLock() { if (configuration.isEnableAutoLocking()) { readLock(); diff --git a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java index 266ffa7e..db87b820 100644 --- a/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java +++ b/src/main/java/org/gephi/graph/impl/GraphViewDecorator.java @@ -752,6 +752,14 @@ public Graph getRootGraph() { return graphStore; } + @Override + public SpatialIndex getSpatialIndex() { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } + return this; + } + void checkWriteLock() { if (graphStore.lock != null) { graphStore.lock.checkHoldWriteLock(); @@ -821,12 +829,18 @@ boolean isUndirectedToIgnore(final EdgeImpl edge) { @Override public NodeIterable getNodesInArea(Rect2D rect) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } Iterator iterator = graphStore.spatialIndex.getNodesInArea(rect).iterator(); return new NodeIterableWrapper(new NodeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } @Override public EdgeIterable getEdgesInArea(Rect2D rect) { + if (graphStore.spatialIndex == null) { + throw new UnsupportedOperationException("Spatial index is disabled (from Configuration)"); + } Iterator iterator = graphStore.spatialIndex.getEdgesInArea(rect).iterator(); return new EdgeIterableWrapper(new EdgeViewIterator(iterator), graphStore.spatialIndex.nodesTree.lock); } diff --git a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java index 92f60a41..91bc601e 100644 --- a/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java +++ b/src/main/java/org/gephi/graph/impl/UndirectedDecorator.java @@ -17,17 +17,8 @@ import java.util.Collection; import java.util.Set; -import org.gephi.graph.api.Edge; -import org.gephi.graph.api.EdgeIterable; -import org.gephi.graph.api.Graph; -import org.gephi.graph.api.GraphModel; -import org.gephi.graph.api.GraphView; -import org.gephi.graph.api.Interval; -import org.gephi.graph.api.Node; -import org.gephi.graph.api.NodeIterable; -import org.gephi.graph.api.Subgraph; -import org.gephi.graph.api.UndirectedGraph; -import org.gephi.graph.api.UndirectedSubgraph; + +import org.gephi.graph.api.*; public class UndirectedDecorator implements UndirectedGraph, UndirectedSubgraph { @@ -422,4 +413,9 @@ public void not() { public Graph getRootGraph() { return this; } + + @Override + public SpatialIndex getSpatialIndex() { + return store.getSpatialIndex(); + } } diff --git a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java index d4dffa29..17b45f9c 100644 --- a/src/test/java/org/gephi/graph/impl/BasicGraphStore.java +++ b/src/test/java/org/gephi/graph/impl/BasicGraphStore.java @@ -1712,4 +1712,9 @@ public void doBreak() { // Not used because no locking } } + + @Override + public SpatialIndex getSpatialIndex() { + return null; + } }