source: josm/trunk/src/org/openstreetmap/josm/data/osm/Node.java@ 13033

Last change on this file since 13033 was 13012, checked in by bastiK, 7 years ago

remove trivial overrides, where this doesn't actually break binary compatibility

(This is only a problem if signature or return type of overriding method differs,
generic type T counting as Object.)

  • Property svn:eol-style set to native
File size: 13.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import java.awt.geom.Area;
5import java.util.Collection;
6import java.util.List;
7import java.util.Objects;
8import java.util.Set;
9import java.util.TreeSet;
10import java.util.function.Predicate;
11
12import org.openstreetmap.josm.Main;
13import org.openstreetmap.josm.data.coor.EastNorth;
14import org.openstreetmap.josm.data.coor.LatLon;
15import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
16import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
17import org.openstreetmap.josm.data.projection.Projecting;
18import org.openstreetmap.josm.tools.CheckParameterUtil;
19import org.openstreetmap.josm.tools.Utils;
20
21/**
22 * One node data, consisting of one world coordinate waypoint.
23 *
24 * @author imi
25 */
26public final class Node extends OsmPrimitive implements INode {
27
28 /*
29 * We "inline" lat/lon rather than using a LatLon-object => reduces memory footprint
30 */
31 private double lat = Double.NaN;
32 private double lon = Double.NaN;
33
34 /*
35 * the cached projected coordinates
36 */
37 private double east = Double.NaN;
38 private double north = Double.NaN;
39 /**
40 * The cache key to use for {@link #east} and {@link #north}.
41 */
42 private Object eastNorthCacheKey;
43
44 @Override
45 public void setCoor(LatLon coor) {
46 updateCoor(coor, null);
47 }
48
49 @Override
50 public void setEastNorth(EastNorth eastNorth) {
51 updateCoor(null, eastNorth);
52 }
53
54 private void updateCoor(LatLon coor, EastNorth eastNorth) {
55 if (getDataSet() != null) {
56 boolean locked = writeLock();
57 try {
58 getDataSet().fireNodeMoved(this, coor, eastNorth);
59 } finally {
60 writeUnlock(locked);
61 }
62 } else {
63 setCoorInternal(coor, eastNorth);
64 }
65 }
66
67 /**
68 * Returns lat/lon coordinates of this node, or {@code null} unless {@link #isLatLonKnown()}
69 * @return lat/lon coordinates of this node, or {@code null} unless {@link #isLatLonKnown()}
70 */
71 @Override
72 public LatLon getCoor() {
73 if (!isLatLonKnown()) {
74 return null;
75 } else {
76 return new LatLon(lat, lon);
77 }
78 }
79
80 @Override
81 public double lat() {
82 return lat;
83 }
84
85 @Override
86 public double lon() {
87 return lon;
88 }
89
90 /**
91 * Replies the projected east/north coordinates.
92 * <p>
93 * Uses the {@link Main#getProjection() global projection} to project the lat/lon-coordinates.
94 * <p>
95 * Method {@link org.openstreetmap.josm.data.coor.ILatLon#getEastNorth()} of
96 * implemented interface <code>ILatLon</code> is deprecated, but this method is not.
97 * @return the east north coordinates or {@code null} if {@link #isLatLonKnown()}
98 * is false.
99 */
100 @SuppressWarnings("deprecation")
101 @Override
102 public EastNorth getEastNorth() {
103 return getEastNorth(Main.getProjection());
104 }
105
106 @Override
107 public EastNorth getEastNorth(Projecting projection) {
108 if (!isLatLonKnown()) return null;
109
110 if (Double.isNaN(east) || Double.isNaN(north) || !Objects.equals(projection.getCacheKey(), eastNorthCacheKey)) {
111 // projected coordinates haven't been calculated yet,
112 // so fill the cache of the projected node coordinates
113 EastNorth en = projection.latlon2eastNorth(this);
114 this.east = en.east();
115 this.north = en.north();
116 this.eastNorthCacheKey = projection.getCacheKey();
117 }
118 return new EastNorth(east, north);
119 }
120
121 /**
122 * To be used only by Dataset.reindexNode
123 * @param coor lat/lon
124 * @param eastNorth east/north
125 */
126 void setCoorInternal(LatLon coor, EastNorth eastNorth) {
127 if (coor != null) {
128 this.lat = coor.lat();
129 this.lon = coor.lon();
130 invalidateEastNorthCache();
131 } else if (eastNorth != null) {
132 LatLon ll = Main.getProjection().eastNorth2latlon(eastNorth);
133 this.lat = ll.lat();
134 this.lon = ll.lon();
135 this.east = eastNorth.east();
136 this.north = eastNorth.north();
137 this.eastNorthCacheKey = Main.getProjection().getCacheKey();
138 } else {
139 this.lat = Double.NaN;
140 this.lon = Double.NaN;
141 invalidateEastNorthCache();
142 if (isVisible()) {
143 setIncomplete(true);
144 }
145 }
146 }
147
148 protected Node(long id, boolean allowNegative) {
149 super(id, allowNegative);
150 }
151
152 /**
153 * Constructs a new local {@code Node} with id 0.
154 */
155 public Node() {
156 this(0, false);
157 }
158
159 /**
160 * Constructs an incomplete {@code Node} object with the given id.
161 * @param id The id. Must be &gt;= 0
162 * @throws IllegalArgumentException if id &lt; 0
163 */
164 public Node(long id) {
165 super(id, false);
166 }
167
168 /**
169 * Constructs a new {@code Node} with the given id and version.
170 * @param id The id. Must be &gt;= 0
171 * @param version The version
172 * @throws IllegalArgumentException if id &lt; 0
173 */
174 public Node(long id, int version) {
175 super(id, version, false);
176 }
177
178 /**
179 * Constructs an identical clone of the argument.
180 * @param clone The node to clone
181 * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
182 * If {@code false}, does nothing
183 */
184 public Node(Node clone, boolean clearMetadata) {
185 super(clone.getUniqueId(), true /* allow negative IDs */);
186 cloneFrom(clone);
187 if (clearMetadata) {
188 clearOsmMetadata();
189 }
190 }
191
192 /**
193 * Constructs an identical clone of the argument (including the id).
194 * @param clone The node to clone, including its id
195 */
196 public Node(Node clone) {
197 this(clone, false);
198 }
199
200 /**
201 * Constructs a new {@code Node} with the given lat/lon with id 0.
202 * @param latlon The {@link LatLon} coordinates
203 */
204 public Node(LatLon latlon) {
205 super(0, false);
206 setCoor(latlon);
207 }
208
209 /**
210 * Constructs a new {@code Node} with the given east/north with id 0.
211 * @param eastNorth The {@link EastNorth} coordinates
212 */
213 public Node(EastNorth eastNorth) {
214 super(0, false);
215 setEastNorth(eastNorth);
216 }
217
218 @Override
219 void setDataset(DataSet dataSet) {
220 super.setDataset(dataSet);
221 if (!isIncomplete() && isVisible() && !isLatLonKnown())
222 throw new DataIntegrityProblemException("Complete node with null coordinates: " + toString());
223 }
224
225 /**
226 * @deprecated no longer supported
227 */
228 @Override
229 @Deprecated
230 public void accept(org.openstreetmap.josm.data.osm.visitor.Visitor visitor) {
231 visitor.visit(this);
232 }
233
234 @Override
235 public void accept(OsmPrimitiveVisitor visitor) {
236 visitor.visit(this);
237 }
238
239 @Override
240 public void accept(PrimitiveVisitor visitor) {
241 visitor.visit(this);
242 }
243
244 @Override
245 public void cloneFrom(OsmPrimitive osm) {
246 if (!(osm instanceof Node))
247 throw new IllegalArgumentException("Not a node: " + osm);
248 boolean locked = writeLock();
249 try {
250 super.cloneFrom(osm);
251 setCoor(((Node) osm).getCoor());
252 } finally {
253 writeUnlock(locked);
254 }
255 }
256
257 /**
258 * Merges the technical and semantical attributes from <code>other</code> onto this.
259 *
260 * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code>
261 * have an assigend OSM id, the IDs have to be the same.
262 *
263 * @param other the other primitive. Must not be null.
264 * @throws IllegalArgumentException if other is null.
265 * @throws DataIntegrityProblemException if either this is new and other is not, or other is new and this is not
266 * @throws DataIntegrityProblemException if other is new and other.getId() != this.getId()
267 */
268 @Override
269 public void mergeFrom(OsmPrimitive other) {
270 if (!(other instanceof Node))
271 throw new IllegalArgumentException("Not a node: " + other);
272 boolean locked = writeLock();
273 try {
274 super.mergeFrom(other);
275 if (!other.isIncomplete()) {
276 setCoor(((Node) other).getCoor());
277 }
278 } finally {
279 writeUnlock(locked);
280 }
281 }
282
283 @Override
284 public void load(PrimitiveData data) {
285 if (!(data instanceof NodeData))
286 throw new IllegalArgumentException("Not a node data: " + data);
287 boolean locked = writeLock();
288 try {
289 super.load(data);
290 setCoor(((NodeData) data).getCoor());
291 } finally {
292 writeUnlock(locked);
293 }
294 }
295
296 @Override
297 public NodeData save() {
298 NodeData data = new NodeData();
299 saveCommonAttributes(data);
300 if (!isIncomplete()) {
301 data.setCoor(getCoor());
302 }
303 return data;
304 }
305
306 @Override
307 public String toString() {
308 String coorDesc = isLatLonKnown() ? "lat="+lat+",lon="+lon : "";
309 return "{Node id=" + getUniqueId() + " version=" + getVersion() + ' ' + getFlagsAsString() + ' ' + coorDesc+'}';
310 }
311
312 @Override
313 public boolean hasEqualSemanticAttributes(OsmPrimitive other, boolean testInterestingTagsOnly) {
314 return (other instanceof Node)
315 && hasEqualSemanticFlags(other)
316 && hasEqualCoordinates((Node) other)
317 && super.hasEqualSemanticAttributes(other, testInterestingTagsOnly);
318 }
319
320 private boolean hasEqualCoordinates(Node other) {
321 final LatLon c1 = getCoor();
322 final LatLon c2 = other.getCoor();
323 return (c1 == null && c2 == null) || (c1 != null && c2 != null && c1.equalsEpsilon(c2));
324 }
325
326 @Override
327 public int compareTo(OsmPrimitive o) {
328 return o instanceof Node ? Long.compare(getUniqueId(), o.getUniqueId()) : 1;
329 }
330
331 @Override
332 public String getDisplayName(NameFormatter formatter) {
333 return formatter.format(this);
334 }
335
336 @Override
337 public OsmPrimitiveType getType() {
338 return OsmPrimitiveType.NODE;
339 }
340
341 @Override
342 public BBox getBBox() {
343 return new BBox(lon, lat);
344 }
345
346 @Override
347 protected void addToBBox(BBox box, Set<PrimitiveId> visited) {
348 box.add(lon, lat);
349 }
350
351 @Override
352 public void updatePosition() {
353 // Do nothing
354 }
355
356 @Override
357 public boolean isDrawable() {
358 // Not possible to draw a node without coordinates.
359 return super.isDrawable() && isLatLonKnown();
360 }
361
362 /**
363 * Check whether this node connects 2 ways.
364 *
365 * @return true if isReferredByWays(2) returns true
366 * @see #isReferredByWays(int)
367 */
368 public boolean isConnectionNode() {
369 return isReferredByWays(2);
370 }
371
372 /**
373 * Invoke to invalidate the internal cache of projected east/north coordinates.
374 * Coordinates are reprojected on demand when the {@link #getEastNorth()} is invoked
375 * next time.
376 */
377 public void invalidateEastNorthCache() {
378 this.east = Double.NaN;
379 this.north = Double.NaN;
380 this.eastNorthCacheKey = null;
381 }
382
383 @Override
384 public boolean concernsArea() {
385 // A node cannot be an area
386 return false;
387 }
388
389 /**
390 * Tests whether {@code this} node is connected to {@code otherNode} via at most {@code hops} nodes
391 * matching the {@code predicate} (which may be {@code null} to consider all nodes).
392 * @param otherNodes other nodes
393 * @param hops number of hops
394 * @param predicate predicate to match
395 * @return {@code true} if {@code this} node mets the conditions
396 */
397 public boolean isConnectedTo(final Collection<Node> otherNodes, final int hops, Predicate<Node> predicate) {
398 CheckParameterUtil.ensureParameterNotNull(otherNodes);
399 CheckParameterUtil.ensureThat(!otherNodes.isEmpty(), "otherNodes must not be empty!");
400 CheckParameterUtil.ensureThat(hops >= 0, "hops must be non-negative!");
401 return hops == 0
402 ? isConnectedTo(otherNodes, hops, predicate, null)
403 : isConnectedTo(otherNodes, hops, predicate, new TreeSet<>());
404 }
405
406 private boolean isConnectedTo(final Collection<Node> otherNodes, final int hops, Predicate<Node> predicate, Set<Node> visited) {
407 if (otherNodes.contains(this)) {
408 return true;
409 }
410 if (hops > 0 && visited != null) {
411 visited.add(this);
412 for (final Way w : Utils.filteredCollection(this.getReferrers(), Way.class)) {
413 for (final Node n : w.getNodes()) {
414 final boolean containsN = visited.contains(n);
415 visited.add(n);
416 if (!containsN && (predicate == null || predicate.test(n))
417 && n.isConnectedTo(otherNodes, hops - 1, predicate, visited)) {
418 return true;
419 }
420 }
421 }
422 }
423 return false;
424 }
425
426 @Override
427 public boolean isOutsideDownloadArea() {
428 if (isNewOrUndeleted() || getDataSet() == null)
429 return false;
430 Area area = getDataSet().getDataSourceArea();
431 if (area == null)
432 return false;
433 LatLon coor = getCoor();
434 return coor != null && !coor.isIn(area);
435 }
436
437 /**
438 * Replies the set of referring ways.
439 * @return the set of referring ways
440 * @since 12031
441 */
442 public List<Way> getParentWays() {
443 return getFilteredList(getReferrers(), Way.class);
444 }
445}
Note: See TracBrowser for help on using the repository browser.