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

Last change on this file since 12382 was 12382, checked in by michael2402, 7 years ago

More documentation for the tools package

  • Property svn:eol-style set to native
File size: 6.0 KB
RevLine 
[7193]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.
[7509]9 *
[7193]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.
[7509]13 *
[7193]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.)
[7509]17 *
[7193]18 * @param <T> the property (like land/water or nation)
19 */
20public class GeoPropertyIndex<T> {
[7509]21
[7193]22 private final int maxLevel;
23 private final GeoProperty<T> geoProp;
24 private final GPLevel<T> root;
25 private GPLevel<T> lastLevelUsed;
[7509]26
[7193]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
[8470]32 * @param maxLevel max level
[7193]33 */
34 public GeoPropertyIndex(GeoProperty<T> geoProp, int maxLevel) {
35 this.geoProp = geoProp;
36 this.maxLevel = maxLevel;
[9070]37 this.root = new GPLevel<>(0, new BBox(-180, -90, 180, 90), null, this);
[7193]38 this.lastLevelUsed = root;
39 }
[7509]40
[7193]41 /**
42 * Look up the property for a certain point.
[7592]43 * This gives the same result as {@link GeoProperty#get(LatLon)}, but
[7193]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 }
[7509]51
[12382]52 /**
53 * Gets the index of the given coordinate. Only used internally
54 * @param ll The lat/lon coordinate
55 * @param level The scale level
56 * @return The index for that position
57 */
[7193]58 public static int index(LatLon ll, int level) {
[10316]59 long noParts = 1L << level;
[8510]60 long x = ((long) ((ll.lon() + 180.0) * noParts / 360.0)) & 1;
61 long y = ((long) ((ll.lat() + 90.0) * noParts / 180.0)) & 1;
[7193]62 return (int) (2 * x + y);
63 }
64
65 protected static class GPLevel<T> {
66 private final T val;
67 private final int level;
68 private final BBox bbox;
69 private final GPLevel<T> parent;
70 private final GeoPropertyIndex<T> owner;
71
72 // child order by index is sw, nw, se, ne
73 private GPLevel<T>[] children;
74
75 public GPLevel(int level, BBox bbox, GPLevel<T> parent, GeoPropertyIndex<T> owner) {
76 this.level = level;
77 this.bbox = bbox;
78 this.parent = parent;
79 this.owner = owner;
80 this.val = owner.geoProp.get(bbox);
81 }
82
83 public T get(LatLon ll) {
[7353]84 if (isInside(ll))
[7193]85 return getBounded(ll);
86 if (DEBUG) System.err.print("up["+level+"]");
87 return parent.get(ll);
88 }
[7509]89
[7193]90 private T getBounded(LatLon ll) {
91 if (DEBUG) System.err.print("GPLevel["+level+"]"+bbox+" ");
[7353]92 if (!isInside(ll)) {
[7193]93 throw new AssertionError("Point "+ll+" should be inside "+bbox);
94 }
95 if (val != null) {
96 if (DEBUG) System.err.println(" hit! "+val);
97 owner.lastLevelUsed = this;
98 return val;
99 }
100 if (level >= owner.maxLevel) {
101 if (DEBUG) System.err.println(" max level reached !");
102 return owner.geoProp.get(ll);
103 }
[7509]104
[7193]105 if (children == null) {
106 @SuppressWarnings("unchecked")
[7596]107 GPLevel<T>[] tmp = new GPLevel[4];
[7193]108 this.children = tmp;
109 }
[7509]110
[7193]111 int idx = index(ll, level+1);
112 if (children[idx] == null) {
113 double lon1, lat1;
114 switch (idx) {
[7509]115 case 0:
[7193]116 lon1 = bbox.getTopLeftLon();
117 lat1 = bbox.getBottomRightLat();
118 break;
119 case 1:
120 lon1 = bbox.getTopLeftLon();
121 lat1 = bbox.getTopLeftLat();
122 break;
[7509]123 case 2:
[7193]124 lon1 = bbox.getBottomRightLon();
125 lat1 = bbox.getBottomRightLat();
126 break;
[7509]127 case 3:
[7193]128 lon1 = bbox.getBottomRightLon();
129 lat1 = bbox.getTopLeftLat();
130 break;
131 default:
132 throw new AssertionError();
133 }
134 if (DEBUG) System.err.println(" - new with idx "+idx);
135 LatLon center = bbox.getCenter();
[8510]136 BBox b = new BBox(lon1, lat1, center.lon(), center.lat());
[7193]137 children[idx] = new GPLevel<>(level + 1, b, this, owner);
138 }
139 return children[idx].getBounded(ll);
140 }
[7509]141
[7353]142 /**
143 * Checks, if a point is inside this tile.
144 * Makes sure, that neighboring tiles do not overlap, i.e. a point exactly
145 * on the border of two tiles must be inside exactly one of the tiles.
146 * @param ll the coordinates of the point
147 * @return true, if it is inside of the box
148 */
149 boolean isInside(LatLon ll) {
[7509]150 return bbox.getTopLeftLon() <= ll.lon() &&
[8416]151 (ll.lon() < bbox.getBottomRightLon() || (ll.lon() == 180.0 && bbox.getBottomRightLon() == 180.0)) &&
[7509]152 bbox.getBottomRightLat() <= ll.lat() &&
[8416]153 (ll.lat() < bbox.getTopLeftLat() || (ll.lat() == 90.0 && bbox.getTopLeftLat() == 90.0));
[7353]154 }
[7193]155
[11259]156 @Override
157 public String toString() {
158 return "GPLevel [val=" + val + ", level=" + level + ", bbox=" + bbox + ']';
159 }
[7193]160 }
[11259]161
162 @Override
163 public String toString() {
164 return "GeoPropertyIndex [maxLevel=" + maxLevel + ", geoProp=" + geoProp + ", root=" + root + ", lastLevelUsed="
165 + lastLevelUsed + ']';
166 }
[7193]167}
Note: See TracBrowser for help on using the repository browser.