Skip to content

Commit

Permalink
Introduces the visitor pattern in spatial indices.
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexisDrogoul committed Jul 31, 2021
1 parent 7a717ec commit c2bbfe5
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 41 deletions.
6 changes: 3 additions & 3 deletions msi.gama.core/src/msi/gama/common/util/PoolUtils.java
Expand Up @@ -13,19 +13,19 @@
public class PoolUtils {

static Set<ObjectPool> POOLS = new LinkedHashSet<>();
static boolean POOL = GamaPreferences.External.USE_POOLING.getValue();
static public boolean POOL = GamaPreferences.External.USE_POOLING.getValue();
static {
DEBUG.OFF();
GamaPreferences.External.USE_POOLING.onChange(v -> {
POOLS.forEach((p) -> p.dispose());
POOLS.forEach(ObjectPool::dispose);
POOL = v;
});
}

public static void WriteStats() {
if (!DEBUG.IS_ON()) return;
DEBUG.SECTION("Pool statistics");
POOLS.forEach((p) -> {
POOLS.forEach(p -> {
long percentage = p.accessed == 0 ? 100 : 100 - (long) (p.created * 100d / p.accessed);
DEBUG.OUT(p.name, 30, "instances created " + p.created + " / instances asked " + p.accessed + " = "
+ percentage + "% of coverage");
Expand Down
Expand Up @@ -43,7 +43,8 @@ public CompoundSpatialIndex(final Envelope bounds, final boolean parallel) {
uniqueIndexes = Collector.getOrderedSet();
uniqueIndexes.add(rootIndex);
final double biggest = Math.max(bounds.getWidth(), bounds.getHeight());
steps = new double[] { biggest / 20, biggest / 10, biggest / 2, biggest, biggest * Math.sqrt(2) };
steps = new double[] { biggest / 100, biggest / 50, biggest / 20, biggest / 10, biggest / 2, biggest,
biggest * Math.sqrt(2) };
}

private ISpatialIndex findSpatialIndex(final IPopulation<? extends IAgent> s) {
Expand Down
120 changes: 83 additions & 37 deletions msi.gama.core/src/msi/gama/metamodel/topology/GamaQuadTree.java
Expand Up @@ -13,14 +13,17 @@

import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Envelope;

import com.google.common.collect.Ordering;

import msi.gama.common.geometry.Envelope3D;
import msi.gama.common.geometry.IIntersectable;
import msi.gama.metamodel.agent.IAgent;
import msi.gama.metamodel.shape.GamaPoint;
import msi.gama.metamodel.shape.IShape;
import msi.gama.metamodel.topology.filter.IAgentFilter;
import msi.gama.runtime.IScope;
Expand Down Expand Up @@ -55,9 +58,6 @@ public class GamaQuadTree implements ISpatialIndex {
final boolean parallel;

public static GamaQuadTree create(final Envelope envelope, final boolean parallel) {
// if (GamaPreferences.GRID_OPTIMIZATION.getValue())
// return new GamaParallelQuadTree(envelope);
// else
return new GamaQuadTree(envelope, parallel);
}

Expand All @@ -68,10 +68,6 @@ private GamaQuadTree(final Envelope bounds, final boolean sync) {
this.parallel = sync;
root = new QuadNode(new Envelope(bounds));
minSize = bounds.getWidth() / 100d;
// GamaPreferences.External.QUADTREE_SYNCHRONIZATION.onChange((v) -> {
// parallel = v;
// root.synchronizeChanged();
// });
}

@Override
Expand All @@ -83,7 +79,7 @@ public void dispose() {
public void insert(final IAgent agent) {
if (agent == null) return;
if (agent.isPoint()) {
root.add((Coordinate) agent.getLocation(), agent);
root.add(agent.getLocation(), agent);
} else {
root.add(agent.getEnvelope(), agent);
}
Expand Down Expand Up @@ -118,6 +114,22 @@ protected Collection<IAgent> findIntersects(final IScope scope, final IShape sou
}
}

protected Collection<IAgent> findIntersectsNew(final IScope scope, final IShape source, final Envelope r,
final IAgentFilter filter) {
// Adresses Issue 722 by explicitly shuffling the results with GAMA
// random procedures and removing duplicates
try (final ICollector<IAgent> list = Collector.getOrderedSet()) {
root.visitIntersects(r, a -> {
if (filter.accept(scope, source, a)) { list.add(a); }
});
// root.findIntersects(r, list);
// if (list.isEmpty()) return GamaListFactory.create();
// filter.filter(scope, source, list);
list.shuffleInPlaceWith(scope.getRandom());
return list.items();
}
}

@Override
public Collection<IAgent> allAtDistance(final IScope scope, final IShape source, final double dist,
final IAgentFilter f) {
Expand All @@ -135,6 +147,19 @@ public Collection<IAgent> allAtDistance(final IScope scope, final IShape source,
}
}

public void visitAllAtDistance(final IScope scope, final IShape source, final double dist, final IAgentFilter f,
final Consumer<IAgent> action) {
final Envelope3D env = Envelope3D.of(source.getEnvelope());
env.expandBy(dist * Maths.SQRT2);
try {
root.visitIntersects(env, a -> {
if (f.accept(scope, source, a) && source.euclidianDistanceTo(a) <= dist) { action.accept(a); }
});
} finally {
env.dispose();
}
}

@Override
public Collection<IAgent> firstAtDistance(final IScope scope, final IShape source, final double dist,
final IAgentFilter f, final int number, final Collection<IAgent> alreadyChosen) {
Expand All @@ -160,6 +185,7 @@ public IAgent firstAtDistance(final IScope scope, final IShape source, final dou
final Envelope3D env = Envelope3D.of(source.getEnvelope());
env.expandBy(exp);
try {

final Collection<IAgent> in_square = findIntersects(scope, source, env, f);
if (in_square.isEmpty()) return null;
double min_distance = dist;
Expand All @@ -177,6 +203,28 @@ public IAgent firstAtDistance(final IScope scope, final IShape source, final dou
}
}

public IAgent firstAtDistanceNew(final IScope scope, final IShape source, final double dist, final IAgentFilter f) {
final Envelope3D env = Envelope3D.of(source.getEnvelope());
env.expandBy(dist * Maths.SQRT2);
try (final ICollector<IAgent> visited = Collector.getOrderedSet()) {
double[] min_distance = { dist };
IAgent[] min_agent = { null };
root.visitIntersects(env, a -> {
if (f.accept(scope, source, a) && !visited.contains(a)) {
visited.add(a);
final double dd = source.euclidianDistanceTo(a);
if (dd < min_distance[0]) {
min_distance[0] = dd;
min_agent[0] = a;
}
}
});
return min_agent[0];
} finally {
env.dispose();
}
}

@Override
public Collection<IAgent> allInEnvelope(final IScope scope, final IShape source, final Envelope envelope,
final IAgentFilter f, final boolean contained) {
Expand All @@ -198,7 +246,7 @@ private class QuadNode {
private volatile QuadNode[] nodes = null;
// ** Addresses part of Issue 722 -- Need to keep the agents ordered
// (by insertion order) **
private IMap<IAgent, Envelope3D> objects;
private IMap<IAgent, IIntersectable> objects;
private final boolean canSplit;

public QuadNode(final Envelope bounds) {
Expand All @@ -210,22 +258,11 @@ public QuadNode(final Envelope bounds) {
canSplit = hw > minSize && hh > minSize;
}

private IMap<IAgent, Envelope3D> getOrCreateObjects() {
private IMap<IAgent, IIntersectable> getOrCreateObjects() {
if (objects == null) { objects = parallel ? GamaMapFactory.concurrentMap() : GamaMapFactory.create(); }
return objects;
}

// public void synchronizeChanged() {
// synchronized (objects) {
// objects = parallel ? GamaMapFactory.synchronizedMap(objects) : objects;
// }
// if (nodes != null) {
// for (final QuadNode n : nodes) {
// n.synchronizeChanged();
// }
// }
// }

public void dispose() {
if (objects != null) {
objects.forEach((a, e) -> {
Expand All @@ -245,7 +282,7 @@ public void dispose() {
public void remove(final Coordinate p, final IShape a) {
if (nodes == null) {
if (objects != null) {
final Envelope3D env = objects.remove(a);
final IIntersectable env = objects.remove(a);
if (env != null) { env.dispose(); }
}
} else {
Expand All @@ -267,10 +304,10 @@ public boolean shouldSplit() {
return canSplit && nodes == null && objects != null && objects.size() >= maxCapacity;
}

public void add(final Coordinate p, final IAgent a) {
public void add(final GamaPoint p, final IAgent a) {
if (shouldSplit()) { split(); }
if (nodes == null) {
getOrCreateObjects().put(a, Envelope3D.of(p));
getOrCreateObjects().put(a, p);
} else {
nodes[quadrant(p)].add(p, a);
}
Expand Down Expand Up @@ -303,12 +340,12 @@ public void split() {
new QuadNode(new Envelope(minx, halfx, halfy, maxy)),
new QuadNode(new Envelope(halfx, maxx, halfy, maxy)) };
if (objects != null) {
for (final Map.Entry<IAgent, Envelope3D> entry : objects.entrySet()) {
for (final Map.Entry<IAgent, IIntersectable> entry : objects.entrySet()) {
final IAgent agent = entry.getKey();
if (agent != null && !agent.dead()) {
final IShape g = agent.getGeometry();
if (g.isPoint()) {
add((Coordinate) g.getLocation(), agent);
add(g.getLocation(), agent);
} else {
add(g.getEnvelope(), agent);
}
Expand All @@ -319,19 +356,28 @@ public void split() {
}
}

public void findIntersects(final Envelope r, final Collection<IAgent> result) {
if (bounds.intersects(r)) {
if (objects != null) {
for (final Map.Entry<IAgent, Envelope3D> entry : objects.entrySet()) {
final Envelope3D env = entry.getValue();
if (env != null && env.intersects(r)) { result.add(entry.getKey()); }
}
public void visitIntersects(final Envelope envelope, final Consumer<IAgent> action) {
if (!bounds.intersects(envelope)) return;
if (nodes == null && objects != null) {
objects.forEach((a, e) -> {
if (e != null && e.intersects(envelope)) { action.accept(a); }
});
} else if (nodes != null) {
for (final QuadNode node : nodes) {
node.visitIntersects(envelope, action);
}
}
}

if (nodes != null) {
for (final QuadNode node : nodes) {
node.findIntersects(r, result);
}
public void findIntersects(final Envelope r, final Collection<IAgent> result) {
if (!bounds.intersects(r)) return;
if (nodes == null && objects != null) {
objects.forEach((a, e) -> {
if (e != null && e.intersects(r)) { result.add(a); }
});
} else {
for (final QuadNode node : nodes) {
node.findIntersects(r, result);
}
}

Expand Down

0 comments on commit c2bbfe5

Please sign in to comment.