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

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

see #15251 - suppress (unwarranted) deprecation warning

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