source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/BoundingXYVisitor.java@ 6608

Last change on this file since 6608 was 6608, checked in by xeen, 10 years ago

improve zoom to selection/conflict when the objects in question are small (fixes #4413)

  • Property svn:eol-style set to native
File size: 6.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.visitor;
3
4import java.util.Collection;
5
6import org.openstreetmap.josm.Main;
7import org.openstreetmap.josm.data.Bounds;
8import org.openstreetmap.josm.data.ProjectionBounds;
9import org.openstreetmap.josm.data.coor.CachedLatLon;
10import org.openstreetmap.josm.data.coor.EastNorth;
11import org.openstreetmap.josm.data.coor.LatLon;
12import org.openstreetmap.josm.data.osm.Node;
13import org.openstreetmap.josm.data.osm.OsmPrimitive;
14import org.openstreetmap.josm.data.osm.Relation;
15import org.openstreetmap.josm.data.osm.RelationMember;
16import org.openstreetmap.josm.data.osm.Way;
17
18/**
19 * Calculates the total bounding rectangle of a series of {@link OsmPrimitive} objects, using the
20 * EastNorth values as reference.
21 * @author imi
22 */
23public class BoundingXYVisitor extends AbstractVisitor {
24
25 private ProjectionBounds bounds = null;
26
27 @Override
28 public void visit(Node n) {
29 visit(n.getEastNorth());
30 }
31
32 @Override
33 public void visit(Way w) {
34 if (w.isIncomplete()) return;
35 for (Node n : w.getNodes()) {
36 visit(n);
37 }
38 }
39
40 @Override
41 public void visit(Relation e) {
42 // only use direct members
43 for (RelationMember m : e.getMembers()) {
44 if (!m.isRelation()) {
45 m.getMember().accept(this);
46 }
47 }
48 }
49
50 public void visit(Bounds b) {
51 if(b != null)
52 {
53 visit(b.getMin());
54 visit(b.getMax());
55 }
56 }
57
58 public void visit(ProjectionBounds b) {
59 if(b != null)
60 {
61 visit(b.getMin());
62 visit(b.getMax());
63 }
64 }
65
66 public void visit(LatLon latlon) {
67 if(latlon != null)
68 {
69 if(latlon instanceof CachedLatLon) {
70 visit(((CachedLatLon)latlon).getEastNorth());
71 } else {
72 visit(Main.getProjection().latlon2eastNorth(latlon));
73 }
74 }
75 }
76
77 public void visit(EastNorth eastNorth) {
78 if (eastNorth != null) {
79 if (bounds == null) {
80 bounds = new ProjectionBounds(eastNorth);
81 } else {
82 bounds.extend(eastNorth);
83 }
84 }
85 }
86
87 public boolean hasExtend()
88 {
89 return bounds != null && !bounds.getMin().equals(bounds.getMax());
90 }
91
92 /**
93 * @return The bounding box or <code>null</code> if no coordinates have passed
94 */
95 public ProjectionBounds getBounds() {
96 return bounds;
97 }
98
99 /**
100 * Enlarges the calculated bounding box by 0.002 degrees.
101 * If the bounding box has not been set (<code>min</code> or <code>max</code>
102 * equal <code>null</code>) this method does not do anything.
103 */
104 public void enlargeBoundingBox() {
105 enlargeBoundingBox(Main.pref.getDouble("edit.zoom-enlarge-bbox", 0.002));
106 }
107
108 /**
109 * Enlarges the calculated bounding box by the specified number of degrees.
110 * If the bounding box has not been set (<code>min</code> or <code>max</code>
111 * equal <code>null</code>) this method does not do anything.
112 *
113 * @param enlargeDegree
114 */
115 public void enlargeBoundingBox(double enlargeDegree) {
116 if (bounds == null)
117 return;
118 LatLon minLatlon = Main.getProjection().eastNorth2latlon(bounds.getMin());
119 LatLon maxLatlon = Main.getProjection().eastNorth2latlon(bounds.getMax());
120 bounds = new ProjectionBounds(
121 Main.getProjection().latlon2eastNorth(new LatLon(minLatlon.lat() - enlargeDegree, minLatlon.lon() - enlargeDegree)),
122 Main.getProjection().latlon2eastNorth(new LatLon(maxLatlon.lat() + enlargeDegree, maxLatlon.lon() + enlargeDegree)));
123 }
124
125 /**
126 * Enlarges the bounding box up to <code>maxEnlargePercent</code>, depending on
127 * its size. If the bounding box is small, it will be enlarged more in relation
128 * to its beginning size. The larger the bounding box, the smaller the change,
129 * down to the minimum of 1% enlargement.
130 *
131 * Warning: if the bounding box only contains a single node, no expansion takes
132 * place because a node has no width/height. Use <code>enlargeToMinDegrees</code>
133 * instead.
134 *
135 * Example: You specify enlargement to be up to 100%.
136 *
137 * Bounding box is a small house: enlargement will be 95–100%, i.e.
138 * making enough space so that the house fits twice on the screen in
139 * each direction.
140 *
141 * Bounding box is a large landuse, like a forest: Enlargement will
142 * be 1–10%, i.e. just add a little border around the landuse.
143 *
144 * If the bounding box has not been set (<code>min</code> or <code>max</code>
145 * equal <code>null</code>) this method does not do anything.
146 *
147 * @param maxEnlargePercent
148 */
149 public void enlargeBoundingBoxLogarithmically(double maxEnlargePercent) {
150 if (bounds == null)
151 return;
152
153 double diffEast = bounds.getMax().east() - bounds.getMin().east();
154 double diffNorth = bounds.getMax().north() - bounds.getMin().north();
155
156 double enlargeEast = Math.min(maxEnlargePercent - 10*Math.log(diffEast), 1)/100;
157 double enlargeNorth = Math.min(maxEnlargePercent - 10*Math.log(diffNorth), 1)/100;
158 System.out.println(enlargeEast);
159
160 visit(bounds.getMin().add(-enlargeEast/2, -enlargeNorth/2));
161 visit(bounds.getMax().add(+enlargeEast/2, +enlargeNorth/2));
162 }
163
164
165 /**
166 * Specify a degree larger than 0 in order to make the bounding box at least
167 * the specified amount of degrees high and wide. The value is ignored if the
168 * bounding box is already larger than the specified amount.
169 *
170 * If the bounding box has not been set (<code>min</code> or <code>max</code>
171 * equal <code>null</code>) this method does not do anything.
172 *
173 * If the bounding box contains objects and is to be enlarged, the objects
174 * will be centered within the new bounding box.
175 *
176 * @param minDegrees
177 */
178 public void enlargeToMinDegrees(double minDegrees) {
179 if (bounds == null)
180 return;
181
182 EastNorth minEnlarge = Main.getProjection().latlon2eastNorth(new LatLon(0, minDegrees));
183
184 visit(bounds.getMin().add(-minEnlarge.east()/2, -minEnlarge.north()/2));
185 visit(bounds.getMax().add(+minEnlarge.east()/2, +minEnlarge.north()/2));
186 }
187
188
189 @Override public String toString() {
190 return "BoundingXYVisitor["+bounds+"]";
191 }
192
193 public void computeBoundingBox(Collection<? extends OsmPrimitive> primitives) {
194 if (primitives == null) return;
195 for (OsmPrimitive p: primitives) {
196 if (p == null) {
197 continue;
198 }
199 p.accept(this);
200 }
201 }
202}
Note: See TracBrowser for help on using the repository browser.