forked from davidmoten/rtree
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLatLongExampleTest.java
139 lines (113 loc) · 5.47 KB
/
LatLongExampleTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package com.github.davidmoten.rtree;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.Test;
import com.github.davidmoten.grumpy.core.Position;
import com.github.davidmoten.rtree.geometry.Geometries;
import com.github.davidmoten.rtree.geometry.Point;
import com.github.davidmoten.rtree.geometry.Rectangle;
import rx.Observable;
import rx.functions.Func1;
public class LatLongExampleTest {
private static final Point sydney = Geometries.point(151.2094, -33.86);
private static final Point canberra = Geometries.point(149.1244, -35.3075);
private static final Point brisbane = Geometries.point(153.0278, -27.4679);
private static final Point bungendore = Geometries.point(149.4500, -35.2500);
@Test
public void testLatLongExample() {
// This is to demonstrate how to use rtree to to do distance searches
// with Lat Long points
// Let's find all cities within 300km of Canberra
RTree<String, Point> tree = RTree.star().create();
tree = tree.add("Sydney", sydney);
tree = tree.add("Brisbane", brisbane);
// Now search for all locations within 300km of Canberra
final double distanceKm = 300;
List<Entry<String, Point>> list = search(tree, canberra, distanceKm)
// get the result
.toList().toBlocking().single();
// should have returned Sydney only
assertEquals(1, list.size());
assertEquals("Sydney", list.get(0).value());
}
public static <T> Observable<Entry<T, Point>> search(RTree<T, Point> tree, Point lonLat,
final double distanceKm) {
// First we need to calculate an enclosing lat long rectangle for this
// distance then we refine on the exact distance
final Position from = Position.create(lonLat.y(), lonLat.x());
Rectangle bounds = createBounds(from, distanceKm);
return tree
// do the first search using the bounds
.search(bounds)
// refine using the exact distance
.filter(new Func1<Entry<T, Point>, Boolean>() {
@Override
public Boolean call(Entry<T, Point> entry) {
Point p = entry.geometry();
Position position = Position.create(p.y(), p.x());
return from.getDistanceToKm(position) < distanceKm;
}
});
}
@Test
public void testSearchLatLongCircles() {
RTree<GeoCircleValue<String>, Rectangle> tree = RTree.star().create();
// create circles around these major towns
GeoCircleValue<String> sydneyCircle = createGeoCircleValue(sydney, 100, "Sydney");
GeoCircleValue<String> canberraCircle = createGeoCircleValue(canberra, 50, "Canberra");
GeoCircleValue<String> brisbaneCircle = createGeoCircleValue(brisbane, 200, "Brisbane");
// add the circles to the RTree using the bounding box of the circle as
// the geometry
tree = add(tree, sydneyCircle);
tree = add(tree, canberraCircle);
tree = add(tree, brisbaneCircle);
// now find the circles that contain bungendore (which is 30km from
// Canberra)
final Point location = bungendore;
String result = tree.search(location)
// filter on the exact distance from the centre of the GeoCircle
.filter(new Func1<Entry<GeoCircleValue<String>, Rectangle>, Boolean>() {
Position from = Position.create(location.y(), location.x());
@Override
public Boolean call(Entry<GeoCircleValue<String>, Rectangle> entry) {
Position centre = Position.create(entry.value().lat, entry.value().lon);
return from.getDistanceToKm(centre) < entry.value().radiusKm;
}
})
// do the search (only expect one value)
.toBlocking().single()
// get the name of the GoeCircleValue returned
.value().value;
assertEquals("Canberra", result);
}
private static Rectangle createBounds(final Position from, final double distanceKm) {
// this calculates a pretty accurate bounding box. Depending on the
// performance you require you wouldn't have to be this accurate because
// accuracy is enforced later
Position north = from.predict(distanceKm, 0);
Position south = from.predict(distanceKm, 180);
Position east = from.predict(distanceKm, 90);
Position west = from.predict(distanceKm, 270);
return Geometries.rectangle(west.getLon(), south.getLat(), east.getLon(), north.getLat());
}
private static <T> GeoCircleValue<T> createGeoCircleValue(Point point, double radiusKm,
T value) {
return new GeoCircleValue<T>(point.y(), point.x(), radiusKm, value);
}
private static <T> RTree<GeoCircleValue<T>, Rectangle> add(
RTree<GeoCircleValue<T>, Rectangle> tree, GeoCircleValue<T> c) {
return tree.add(c, createBounds(Position.create(c.lat, c.lon), c.radiusKm));
}
private static class GeoCircleValue<T> {
GeoCircleValue(float lat, float lon, double radiusKm, T value) {
this.lat = lat;
this.lon = lon;
this.radiusKm = radiusKm;
this.value = value;
}
float lat;
float lon;
double radiusKm;
T value;
}
}