package uk.me.parabola.util; import java.awt.*; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import uk.me.parabola.imgfmt.app.Area; import uk.me.parabola.imgfmt.app.Coord; public class QuadTreeNode { private static final int MAX_POINTS = 20; private Collection<Coord> points; private final Area bounds; private Area coveredBounds; public Area getCoveredBounds() { return coveredBounds; } private QuadTreeNode[] children; public static final class QuadTreePolygon { private final java.awt.geom.Area javaArea; private final Area bbox; public QuadTreePolygon(java.awt.geom.Area javaArea) { this.javaArea = javaArea; Rectangle bboxRect = javaArea.getBounds(); bbox = new Area(bboxRect.y, bboxRect.x, bboxRect.y + bboxRect.height, bboxRect.x + bboxRect.width); } public QuadTreePolygon(List<Coord> points) { this(Java2DConverter.createArea(points)); } public QuadTreePolygon(Collection<List<Coord>> polygonList) { this.javaArea = new java.awt.geom.Area(); for (List<Coord> polygon : polygonList) { javaArea.add(Java2DConverter.createArea(polygon)); } Rectangle bboxRect = javaArea.getBounds(); bbox = new Area(bboxRect.y, bboxRect.x, bboxRect.y + bboxRect.height, bboxRect.x + bboxRect.width); } public Area getBbox() { return bbox; } public java.awt.geom.Area getArea() { return javaArea; } } public QuadTreeNode(Area bounds) { this(bounds, Collections.<Coord>emptyList()); } public QuadTreeNode(Area bounds, Collection<Coord> points) { this.bounds = bounds; this.children = null; int minLat = Integer.MAX_VALUE; int maxLat = Integer.MIN_VALUE; int minLong = Integer.MAX_VALUE; int maxLong = Integer.MIN_VALUE; for (Coord c : points) { if (c.getLatitude() < minLat) { minLat = c.getLatitude(); } if (c.getLatitude() > maxLat) { maxLat = c.getLatitude(); } if (c.getLongitude() < minLong) { minLong = c.getLongitude(); } if (c.getLongitude() > maxLong) { maxLong = c.getLongitude(); } } coveredBounds = new Area(minLat, minLong, maxLat, maxLong); if (points.size() > MAX_POINTS) { this.points = points; split(); } else { this.points = new HashSet<Coord>(points); } } public Area getBounds() { return this.bounds; } public boolean add(Coord c) { if (coveredBounds == null) { coveredBounds = new Area(c.getLatitude(), c.getLongitude(), c.getLatitude(), c.getLongitude()); } else if (coveredBounds.contains(c) == false) { coveredBounds = new Area(Math.min(coveredBounds.getMinLat(), c.getLatitude()), Math.min(coveredBounds.getMinLong(), c.getLongitude()), Math.max(coveredBounds.getMaxLat(), c.getLatitude()), Math.max(coveredBounds.getMaxLong(), c.getLongitude())); } if (isLeaf()) { boolean added = points.add(c); if (points.size() > MAX_POINTS) split(); return added; } else { for (QuadTreeNode nodes : children) { if (nodes.getBounds().contains(c)) { return nodes.add(c); } } return false; } } public List<Coord> get(Area bbox, List<Coord> resultList) { if (isLeaf()) { if (bbox.getMinLat() <= coveredBounds.getMinLat() && bbox.getMaxLat() >= coveredBounds.getMaxLat() && bbox.getMinLong() <= coveredBounds.getMinLong() && bbox.getMaxLong() >= coveredBounds.getMaxLong()) { // the bounding box is contained completely in the bbox // => add all points without further check resultList.addAll(points); } else { // check each point for (Coord c : points) { if (bbox.contains(c)) { resultList.add(c); } } } } else { for (QuadTreeNode child : children) { if (bbox.intersects(child.getCoveredBounds())) { resultList = child.get(bbox, resultList); } } } return resultList; } public ArrayList<Coord> get(QuadTreePolygon polygon, ArrayList<Coord> resultList) { if (polygon.getBbox().intersects(getBounds())) { if (isLeaf()) { for (Coord c : points) { if (polygon.getArea().contains(c.getLongitude(), c.getLatitude())) { resultList.add(c); } } } else { for (QuadTreeNode child : children) { if (polygon.getBbox().intersects(child.getBounds())) { java.awt.geom.Area subArea = (java.awt.geom.Area) polygon .getArea().clone(); subArea.intersect(createArea(child.getBounds())); child.get(new QuadTreePolygon(subArea), resultList); } } } } return resultList; } private java.awt.geom.Area createArea(Area bbox) { return new java.awt.geom.Area(new Rectangle(bbox.getMinLong(), bbox.getMinLat(), bbox.getWidth(), bbox.getHeight())); } public boolean isLeaf() { return points != null; } private void split() { if (bounds.getHeight() <= 1 || bounds.getWidth() <= 1) { return; } int halfLat = (bounds.getMinLat() + bounds.getMaxLat()) / 2; int halfLong = (bounds.getMinLong() + bounds.getMaxLong()) / 2; children = new QuadTreeNode[4]; Area swBounds = new Area(bounds.getMinLat(), bounds.getMinLong(), halfLat, halfLong); Area nwBounds = new Area(halfLat + 1, bounds.getMinLong(), bounds.getMaxLat(), halfLong); Area seBounds = new Area(bounds.getMinLat(), halfLong + 1, halfLat, bounds.getMaxLong()); Area neBounds = new Area(halfLat + 1, halfLong + 1, bounds.getMaxLat(), bounds.getMaxLong()); children[0] = new QuadTreeNode(swBounds); children[1] = new QuadTreeNode(nwBounds); children[2] = new QuadTreeNode(seBounds); children[3] = new QuadTreeNode(neBounds); Collection<Coord> copyPoints = points; points = null; for (Coord c : copyPoints) { add(c); } } public void clear() { this.children = null; points = new HashSet<Coord>(); coveredBounds = new Area(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); } }