[BAEL-3485] - Java Range lookup problem
This commit is contained in:
parent
726d880d51
commit
bc463d59b4
algorithms-searching/src
main/java/com/baeldung/algorithms/quadtree
test/java/com/baeldung/algorithms/quadtree
@ -0,0 +1,24 @@
|
|||||||
|
package com.baeldung.algorithms.quadtree;
|
||||||
|
|
||||||
|
public class Point {
|
||||||
|
private float x;
|
||||||
|
private float y;
|
||||||
|
|
||||||
|
public Point(float x, float y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[" + x + " , " + y + "]";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package com.baeldung.algorithms.quadtree;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class QuadTree {
|
||||||
|
private static final int MAX_POINTS = 3;
|
||||||
|
private Region area;
|
||||||
|
private List<Point> points = new ArrayList<>();
|
||||||
|
private List<QuadTree> quadTrees = new ArrayList<>();
|
||||||
|
private StringBuilder searchTraversePath;
|
||||||
|
|
||||||
|
public QuadTree(Region area) {
|
||||||
|
this.area = area;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean addPoint(Point point) {
|
||||||
|
if (this.area.containsPoint(point)) {
|
||||||
|
if (this.points.size() < MAX_POINTS) {
|
||||||
|
this.points.add(point);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (this.quadTrees.size() == 0) {
|
||||||
|
createQuadrants();
|
||||||
|
}
|
||||||
|
return addPointToOneQuadrant(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean addPointToOneQuadrant(Point point) {
|
||||||
|
boolean isPointAdded;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
isPointAdded = this.quadTrees.get(i)
|
||||||
|
.addPoint(point);
|
||||||
|
if (isPointAdded)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createQuadrants() {
|
||||||
|
Region region;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
region = this.area.getQuadrant(i);
|
||||||
|
quadTrees.add(new QuadTree(region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Point> search(Region searchRegion, List<Point> matches, String depthIndicator) {
|
||||||
|
searchTraversePath = new StringBuilder();
|
||||||
|
if (matches == null) {
|
||||||
|
matches = new ArrayList<Point>();
|
||||||
|
searchTraversePath.append(depthIndicator)
|
||||||
|
.append("Search Boundary =")
|
||||||
|
.append(searchRegion)
|
||||||
|
.append("\n");
|
||||||
|
}
|
||||||
|
if (!this.area.doesOverlap(searchRegion)) {
|
||||||
|
return matches;
|
||||||
|
} else {
|
||||||
|
for (Point point : points) {
|
||||||
|
if (searchRegion.containsPoint(point)) {
|
||||||
|
searchTraversePath.append(depthIndicator)
|
||||||
|
.append("Found match " + point)
|
||||||
|
.append("\n");
|
||||||
|
matches.add(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.quadTrees.size() > 0) {
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
searchTraversePath.append(depthIndicator)
|
||||||
|
.append("Q")
|
||||||
|
.append(i)
|
||||||
|
.append("-->")
|
||||||
|
.append(quadTrees.get(i).area)
|
||||||
|
.append("\n");
|
||||||
|
quadTrees.get(i)
|
||||||
|
.search(searchRegion, matches, depthIndicator + "\t");
|
||||||
|
this.searchTraversePath.append(quadTrees.get(i)
|
||||||
|
.printSearchTraversePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String printTree(String depthIndicator) {
|
||||||
|
String str = "";
|
||||||
|
if (depthIndicator == "") {
|
||||||
|
str += "Root-->" + area.toString() + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Point point : points) {
|
||||||
|
str += depthIndicator + point.toString() + "\n";
|
||||||
|
}
|
||||||
|
for (int i = 0; i < quadTrees.size(); i++) {
|
||||||
|
str += depthIndicator + "Q" + String.valueOf(i) + "-->" + quadTrees.get(i).area.toString() + "\n";
|
||||||
|
str += quadTrees.get(i)
|
||||||
|
.printTree(depthIndicator + "\t");
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String printSearchTraversePath() {
|
||||||
|
return searchTraversePath.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.baeldung.algorithms.quadtree;
|
||||||
|
|
||||||
|
public class Region {
|
||||||
|
private float x1;
|
||||||
|
private float y1;
|
||||||
|
private float x2;
|
||||||
|
private float y2;
|
||||||
|
|
||||||
|
public Region(float x1, float y1, float x2, float y2) {
|
||||||
|
if (x1 >= x2 || y1 >= y2)
|
||||||
|
throw new IllegalArgumentException("(x1,y1) should be lesser than (x2,y2)");
|
||||||
|
this.x1 = x1;
|
||||||
|
this.y1 = y1;
|
||||||
|
this.x2 = x2;
|
||||||
|
this.y2 = y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Region getQuadrant(int quadrantIndex) {
|
||||||
|
float quadrantWidth = (this.x2 - this.x1) / 2;
|
||||||
|
float quadrantHeight = (this.y2 - this.y1) / 2;
|
||||||
|
|
||||||
|
// 0=SW, 1=NW, 2=NE, 3=SE
|
||||||
|
switch (quadrantIndex) {
|
||||||
|
case 0:
|
||||||
|
return new Region(x1, y1, x1 + quadrantWidth, y1 + quadrantHeight);
|
||||||
|
case 1:
|
||||||
|
return new Region(x1, y1 + quadrantHeight, x1 + quadrantWidth, y2);
|
||||||
|
case 2:
|
||||||
|
return new Region(x1 + quadrantWidth, y1 + quadrantHeight, x2, y2);
|
||||||
|
case 3:
|
||||||
|
return new Region(x1 + quadrantWidth, y1, x2, y1 + quadrantHeight);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsPoint(Point point) {
|
||||||
|
// Consider left and top side to be inclusive for points on border
|
||||||
|
return point.getX() >= this.x1
|
||||||
|
&& point.getX() < this.x2
|
||||||
|
&& point.getY() >= this.y1
|
||||||
|
&& point.getY() < this.y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean doesOverlap(Region testRegion) {
|
||||||
|
// Is test region completely to left of my region?
|
||||||
|
if (testRegion.getX2() < this.getX1()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Is test region completely to right of my region?
|
||||||
|
if (testRegion.getX1() > this.getX2()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Is test region completely above my region?
|
||||||
|
if (testRegion.getY1() > this.getY2()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Is test region completely below my region?
|
||||||
|
if (testRegion.getY2() < this.getY1()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[Region (x1=" + x1 + ", y1=" + y1 + "), (x2=" + x2 + ", y2=" + y2 + ")]";
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX1() {
|
||||||
|
return x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY1() {
|
||||||
|
return y1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX2() {
|
||||||
|
return x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY2() {
|
||||||
|
return y2;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
60
algorithms-searching/src/test/java/com/baeldung/algorithms/quadtree/QuadTreeSearchTest.java
Normal file
60
algorithms-searching/src/test/java/com/baeldung/algorithms/quadtree/QuadTreeSearchTest.java
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package com.baeldung.algorithms.quadtree;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class QuadTreeSearchTest {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(QuadTreeSearchTest.class);
|
||||||
|
|
||||||
|
private static QuadTree quadTree;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUp() {
|
||||||
|
Region area = new Region(0, 0, 400, 400);
|
||||||
|
quadTree = new QuadTree(area);
|
||||||
|
|
||||||
|
float[][] points = new float[][] { { 21, 25 }, { 55, 53 }, { 70, 318 }, { 98, 302 },
|
||||||
|
{ 49, 229 }, { 135, 229 }, { 224, 292 }, { 206, 321 }, { 197, 258 }, { 245, 238 } };
|
||||||
|
|
||||||
|
for (int i = 0; i < points.length; i++) {
|
||||||
|
Point point = new Point(points[i][0], points[i][1]);
|
||||||
|
quadTree.addPoint(point);
|
||||||
|
}
|
||||||
|
LOGGER.debug("\n" + quadTree.printTree(""));
|
||||||
|
LOGGER.debug("==============================================");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenQuadTree_whenSearchingForRange_thenReturn1MatchingItem() {
|
||||||
|
Region searchArea = new Region(200, 200, 250, 250);
|
||||||
|
List<Point> result = quadTree.search(searchArea, null, "");
|
||||||
|
LOGGER.debug(result.toString());
|
||||||
|
LOGGER.debug(quadTree.printSearchTraversePath());
|
||||||
|
|
||||||
|
Assert.assertEquals(1, result.size());
|
||||||
|
Assert.assertArrayEquals(new float[] { 245, 238 },
|
||||||
|
new float[]{result.get(0).getX(), result.get(0).getY() }, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenQuadTree_whenSearchingForRange_thenReturn2MatchingItems() {
|
||||||
|
Region searchArea = new Region(0, 0, 100, 100);
|
||||||
|
List<Point> result = quadTree.search(searchArea, null, "");
|
||||||
|
LOGGER.debug(result.toString());
|
||||||
|
LOGGER.debug(quadTree.printSearchTraversePath());
|
||||||
|
|
||||||
|
Assert.assertEquals(2, result.size());
|
||||||
|
Assert.assertArrayEquals(new float[] { 21, 25 },
|
||||||
|
new float[]{result.get(0).getX(), result.get(0).getY() }, 0);
|
||||||
|
Assert.assertArrayEquals(new float[] { 55, 53 },
|
||||||
|
new float[]{result.get(1).getX(), result.get(1).getY() }, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user