source: josm/trunk/src/org/openstreetmap/josm/tools/GeoPropertyIndex.java@ 11929

Last change on this file since 11929 was 11264, checked in by bastiK, 7 years ago

refactor GeoProperty implementation of RightAndLefthandTraffic to generic separate class DefaultGeoProperty (see #10387)

  • Property svn:eol-style set to native
File size: 5.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import org.openstreetmap.josm.data.coor.LatLon;
5import org.openstreetmap.josm.data.osm.BBox;
6
7/**
8 * Fast index to look up properties of the earth surface.
9 *
10 * It is expected that there is a relatively slow method to look up the property
11 * for a certain coordinate and that there are larger areas with a uniform
12 * property.
13 *
14 * This index tries to find rectangles with uniform property and caches them.
15 * Rectangles are subdivided, if there are different properties within.
16 * (Up to a maximum level, when the slow method is used again.)
17 *
18 * @param <T> the property (like land/water or nation)
19 */
20public class GeoPropertyIndex<T> {
21
22 private final int maxLevel;
23 private final GeoProperty<T> geoProp;
24 private final GPLevel<T> root;
25 private GPLevel<T> lastLevelUsed;
26
27 private static final boolean DEBUG = false;
28
29 /**
30 * Create new GeoPropertyIndex.
31 * @param geoProp the input property that should be made faster by this index
32 * @param maxLevel max level
33 */
34 public GeoPropertyIndex(GeoProperty<T> geoProp, int maxLevel) {
35 this.geoProp = geoProp;
36 this.maxLevel = maxLevel;
37 this.root = new GPLevel<>(0, new BBox(-180, -90, 180, 90), null, this);
38 this.lastLevelUsed = root;
39 }
40
41 /**
42 * Look up the property for a certain point.
43 * This gives the same result as {@link GeoProperty#get(LatLon)}, but
44 * should be faster.
45 * @param ll the point coordinates
46 * @return property value at that point
47 */
48 public T get(LatLon ll) {
49 return lastLevelUsed.get(ll);
50 }
51
52 public static int index(LatLon ll, int level) {
53 long noParts = 1L << level;
54 long x = ((long) ((ll.lon() + 180.0) * noParts / 360.0)) & 1;
55 long y = ((long) ((ll.lat() + 90.0) * noParts / 180.0)) & 1;
56 return (int) (2 * x + y);
57 }
58
59 protected static class GPLevel<T> {
60 private final T val;
61 private final int level;
62 private final BBox bbox;
63 private final GPLevel<T> parent;
64 private final GeoPropertyIndex<T> owner;
65
66 // child order by index is sw, nw, se, ne
67 private GPLevel<T>[] children;
68
69 public GPLevel(int level, BBox bbox, GPLevel<T> parent, GeoPropertyIndex<T> owner) {
70 this.level = level;
71 this.bbox = bbox;
72 this.parent = parent;
73 this.owner = owner;
74 this.val = owner.geoProp.get(bbox);
75 }
76
77 public T get(LatLon ll) {
78 if (isInside(ll))
79 return getBounded(ll);
80 if (DEBUG) System.err.print("up["+level+"]");
81 return parent.get(ll);
82 }
83
84 private T getBounded(LatLon ll) {
85 if (DEBUG) System.err.print("GPLevel["+level+"]"+bbox+" ");
86 if (!isInside(ll)) {
87 throw new AssertionError("Point "+ll+" should be inside "+bbox);
88 }
89 if (val != null) {
90 if (DEBUG) System.err.println(" hit! "+val);
91 owner.lastLevelUsed = this;
92 return val;
93 }
94 if (level >= owner.maxLevel) {
95 if (DEBUG) System.err.println(" max level reached !");
96 return owner.geoProp.get(ll);
97 }
98
99 if (children == null) {
100 @SuppressWarnings("unchecked")
101 GPLevel<T>[] tmp = new GPLevel[4];
102 this.children = tmp;
103 }
104
105 int idx = index(ll, level+1);
106 if (children[idx] == null) {
107 double lon1, lat1;
108 switch (idx) {
109 case 0:
110 lon1 = bbox.getTopLeftLon();
111 lat1 = bbox.getBottomRightLat();
112 break;
113 case 1:
114 lon1 = bbox.getTopLeftLon();
115 lat1 = bbox.getTopLeftLat();
116 break;
117 case 2:
118 lon1 = bbox.getBottomRightLon();
119 lat1 = bbox.getBottomRightLat();
120 break;
121 case 3:
122 lon1 = bbox.getBottomRightLon();
123 lat1 = bbox.getTopLeftLat();
124 break;
125 default:
126 throw new AssertionError();
127 }
128 if (DEBUG) System.err.println(" - new with idx "+idx);
129 LatLon center = bbox.getCenter();
130 BBox b = new BBox(lon1, lat1, center.lon(), center.lat());
131 children[idx] = new GPLevel<>(level + 1, b, this, owner);
132 }
133 return children[idx].getBounded(ll);
134 }
135
136 /**
137 * Checks, if a point is inside this tile.
138 * Makes sure, that neighboring tiles do not overlap, i.e. a point exactly
139 * on the border of two tiles must be inside exactly one of the tiles.
140 * @param ll the coordinates of the point
141 * @return true, if it is inside of the box
142 */
143 boolean isInside(LatLon ll) {
144 return bbox.getTopLeftLon() <= ll.lon() &&
145 (ll.lon() < bbox.getBottomRightLon() || (ll.lon() == 180.0 && bbox.getBottomRightLon() == 180.0)) &&
146 bbox.getBottomRightLat() <= ll.lat() &&
147 (ll.lat() < bbox.getTopLeftLat() || (ll.lat() == 90.0 && bbox.getTopLeftLat() == 90.0));
148 }
149
150 @Override
151 public String toString() {
152 return "GPLevel [val=" + val + ", level=" + level + ", bbox=" + bbox + ']';
153 }
154 }
155
156 @Override
157 public String toString() {
158 return "GeoPropertyIndex [maxLevel=" + maxLevel + ", geoProp=" + geoProp + ", root=" + root + ", lastLevelUsed="
159 + lastLevelUsed + ']';
160 }
161}
Note: See TracBrowser for help on using the repository browser.