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

Last change on this file since 16553 was 16553, checked in by Don-vip, 4 years ago

see #19334 - javadoc fixes + protected constructors for abstract classes

  • Property svn:eol-style set to native
File size: 11.7 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.List;
6import java.util.Objects;
7import java.util.Set;
8import java.util.stream.Collectors;
9
10import org.openstreetmap.josm.data.Bounds;
11import org.openstreetmap.josm.data.coor.EastNorth;
12import org.openstreetmap.josm.data.coor.LatLon;
13import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor;
14import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
15import org.openstreetmap.josm.data.projection.Projecting;
16import org.openstreetmap.josm.data.projection.ProjectionRegistry;
17
18/**
19 * One node data, consisting of one world coordinate waypoint.
20 *
21 * @author imi
22 */
23public final class Node extends OsmPrimitive implements INode {
24
25 static final UniqueIdGenerator idGenerator = new UniqueIdGenerator();
26
27 /*
28 * We "inline" lat/lon rather than using a LatLon-object => reduces memory footprint
29 */
30 private double lat = Double.NaN;
31 private double lon = Double.NaN;
32
33 /*
34 * the cached projected coordinates
35 */
36 private double east = Double.NaN;
37 private double north = Double.NaN;
38 /**
39 * The cache key to use for {@link #east} and {@link #north}.
40 */
41 private Object eastNorthCacheKey;
42
43 @Override
44 public void setCoor(LatLon coor) {
45 updateCoor(coor, null);
46 }
47
48 @Override
49 public void setEastNorth(EastNorth eastNorth) {
50 updateCoor(null, eastNorth);
51 }
52
53 private void updateCoor(LatLon coor, EastNorth eastNorth) {
54 if (getDataSet() != null) {
55 boolean locked = writeLock();
56 try {
57 getDataSet().fireNodeMoved(this, coor, eastNorth);
58 } finally {
59 writeUnlock(locked);
60 }
61 } else {
62 setCoorInternal(coor, eastNorth);
63 }
64 }
65
66 /**
67 * Returns lat/lon coordinates of this node, or {@code null} unless {@link #isLatLonKnown()}
68 * @return lat/lon coordinates of this node, or {@code null} unless {@link #isLatLonKnown()}
69 */
70 @Override
71 public LatLon getCoor() {
72 if (!isLatLonKnown()) {
73 return null;
74 } else {
75 return new LatLon(lat, lon);
76 }
77 }
78
79 @Override
80 public double lat() {
81 return lat;
82 }
83
84 @Override
85 public double lon() {
86 return lon;
87 }
88
89 @Override
90 public EastNorth getEastNorth(Projecting projection) {
91 if (!isLatLonKnown()) return null;
92
93 if (Double.isNaN(east) || Double.isNaN(north) || !Objects.equals(projection.getCacheKey(), eastNorthCacheKey)) {
94 // projected coordinates haven't been calculated yet,
95 // so fill the cache of the projected node coordinates
96 EastNorth en = projection.latlon2eastNorth(this);
97 this.east = en.east();
98 this.north = en.north();
99 this.eastNorthCacheKey = projection.getCacheKey();
100 }
101 return new EastNorth(east, north);
102 }
103
104 /**
105 * To be used only by Dataset.reindexNode
106 * @param coor lat/lon
107 * @param eastNorth east/north
108 */
109 void setCoorInternal(LatLon coor, EastNorth eastNorth) {
110 if (coor != null) {
111 this.lat = coor.lat();
112 this.lon = coor.lon();
113 invalidateEastNorthCache();
114 } else if (eastNorth != null) {
115 LatLon ll = ProjectionRegistry.getProjection().eastNorth2latlon(eastNorth);
116 this.lat = ll.lat();
117 this.lon = ll.lon();
118 this.east = eastNorth.east();
119 this.north = eastNorth.north();
120 this.eastNorthCacheKey = ProjectionRegistry.getProjection().getCacheKey();
121 } else {
122 this.lat = Double.NaN;
123 this.lon = Double.NaN;
124 invalidateEastNorthCache();
125 if (isVisible()) {
126 setIncomplete(true);
127 }
128 }
129 }
130
131 Node(long id, boolean allowNegative) {
132 super(id, allowNegative);
133 }
134
135 /**
136 * Constructs a new local {@code Node} with id 0.
137 */
138 public Node() {
139 this(0, false);
140 }
141
142 /**
143 * Constructs an incomplete {@code Node} object with the given id.
144 * @param id The id. Must be >= 0
145 * @throws IllegalArgumentException if id < 0
146 */
147 public Node(long id) {
148 super(id, false);
149 }
150
151 /**
152 * Constructs a new {@code Node} with the given id and version.
153 * @param id The id. Must be >= 0
154 * @param version The version
155 * @throws IllegalArgumentException if id < 0
156 */
157 public Node(long id, int version) {
158 super(id, version, false);
159 }
160
161 /**
162 * Constructs an identical clone of the argument.
163 * @param clone The node to clone
164 * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}.
165 * If {@code false}, does nothing
166 */
167 public Node(Node clone, boolean clearMetadata) {
168 super(clone.getUniqueId(), true /* allow negative IDs */);
169 cloneFrom(clone);
170 if (clearMetadata) {
171 clearOsmMetadata();
172 }
173 }
174
175 /**
176 * Constructs an identical clone of the argument (including the id).
177 * @param clone The node to clone, including its id
178 */
179 public Node(Node clone) {
180 this(clone, false);
181 }
182
183 /**
184 * Constructs a new {@code Node} with the given lat/lon with id 0.
185 * @param latlon The {@link LatLon} coordinates
186 */
187 public Node(LatLon latlon) {
188 super(0, false);
189 setCoor(latlon);
190 }
191
192 /**
193 * Constructs a new {@code Node} with the given east/north with id 0.
194 * @param eastNorth The {@link EastNorth} coordinates
195 */
196 public Node(EastNorth eastNorth) {
197 super(0, false);
198 setEastNorth(eastNorth);
199 }
200
201 @Override
202 void setDataset(DataSet dataSet) {
203 super.setDataset(dataSet);
204 if (!isIncomplete() && isVisible() && !isLatLonKnown())
205 throw new DataIntegrityProblemException("Complete node with null coordinates: " + toString());
206 }
207
208 @Override
209 public void accept(OsmPrimitiveVisitor visitor) {
210 visitor.visit(this);
211 }
212
213 @Override
214 public void accept(PrimitiveVisitor visitor) {
215 visitor.visit(this);
216 }
217
218 @Override
219 public void cloneFrom(OsmPrimitive osm, boolean copyChildren) {
220 if (!(osm instanceof Node))
221 throw new IllegalArgumentException("Not a node: " + osm);
222 boolean locked = writeLock();
223 try {
224 super.cloneFrom(osm, copyChildren);
225 setCoor(((Node) osm).getCoor());
226 } finally {
227 writeUnlock(locked);
228 }
229 }
230
231 /**
232 * Merges the technical and semantical attributes from <code>other</code> onto this.
233 *
234 * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code>
235 * have an assigend OSM id, the IDs have to be the same.
236 *
237 * @param other the other primitive. Must not be null.
238 * @throws IllegalArgumentException if other is null.
239 * @throws DataIntegrityProblemException if either this is new and other is not, or other is new and this is not
240 * @throws DataIntegrityProblemException if other is new and other.getId() != this.getId()
241 */
242 @Override
243 public void mergeFrom(OsmPrimitive other) {
244 if (!(other instanceof Node))
245 throw new IllegalArgumentException("Not a node: " + other);
246 boolean locked = writeLock();
247 try {
248 super.mergeFrom(other);
249 if (!other.isIncomplete()) {
250 setCoor(((Node) other).getCoor());
251 }
252 } finally {
253 writeUnlock(locked);
254 }
255 }
256
257 @Override
258 public void load(PrimitiveData data) {
259 if (!(data instanceof NodeData))
260 throw new IllegalArgumentException("Not a node data: " + data);
261 boolean locked = writeLock();
262 try {
263 super.load(data);
264 setCoor(((NodeData) data).getCoor());
265 } finally {
266 writeUnlock(locked);
267 }
268 }
269
270 @Override
271 public NodeData save() {
272 NodeData data = new NodeData();
273 saveCommonAttributes(data);
274 if (!isIncomplete()) {
275 data.setCoor(getCoor());
276 }
277 return data;
278 }
279
280 @Override
281 public String toString() {
282 String coorDesc = isLatLonKnown() ? "lat="+lat+",lon="+lon : "";
283 return "{Node id=" + getUniqueId() + " version=" + getVersion() + ' ' + getFlagsAsString() + ' ' + coorDesc+'}';
284 }
285
286 @Override
287 public boolean hasEqualSemanticAttributes(OsmPrimitive other, boolean testInterestingTagsOnly) {
288 return (other instanceof Node)
289 && hasEqualSemanticFlags(other)
290 && hasEqualCoordinates((Node) other)
291 && super.hasEqualSemanticAttributes(other, testInterestingTagsOnly);
292 }
293
294 private boolean hasEqualCoordinates(Node other) {
295 final LatLon c1 = getCoor();
296 final LatLon c2 = other.getCoor();
297 return (c1 == null && c2 == null) || (c1 != null && c2 != null && c1.equalsEpsilon(c2));
298 }
299
300 @Override
301 public OsmPrimitiveType getType() {
302 return OsmPrimitiveType.NODE;
303 }
304
305 @Override
306 public BBox getBBox() {
307 return new BBox(lon, lat);
308 }
309
310 @Override
311 protected void addToBBox(BBox box, Set<PrimitiveId> visited) {
312 box.add(lon, lat);
313 }
314
315 @Override
316 public void updatePosition() {
317 // Do nothing
318 }
319
320 @Override
321 public boolean isDrawable() {
322 // Not possible to draw a node without coordinates.
323 return super.isDrawable() && isLatLonKnown();
324 }
325
326 @Override
327 public boolean isReferredByWays(int n) {
328 return isNodeReferredByWays(n);
329 }
330
331 /**
332 * Invoke to invalidate the internal cache of projected east/north coordinates.
333 * Coordinates are reprojected on demand when the {@link #getEastNorth()} is invoked
334 * next time.
335 */
336 public void invalidateEastNorthCache() {
337 this.east = Double.NaN;
338 this.north = Double.NaN;
339 this.eastNorthCacheKey = null;
340 }
341
342 @Override
343 public boolean concernsArea() {
344 // A node cannot be an area
345 return false;
346 }
347
348 @Override
349 public boolean isOutsideDownloadArea() {
350 if (isNewOrUndeleted() || getDataSet() == null)
351 return false;
352 Area area = getDataSet().getDataSourceArea();
353 if (area == null)
354 return false;
355 LatLon coor = getCoor();
356 return coor != null && !coor.isIn(area);
357 }
358
359 /**
360 * Replies the set of referring ways.
361 * @return the set of referring ways
362 * @since 12031
363 */
364 public List<Way> getParentWays() {
365 return referrers(Way.class).collect(Collectors.toList());
366 }
367
368 /**
369 * Determines if this node is outside of the world. See also #13538.
370 * @return <code>true</code>, if the coordinate is outside the world, compared by using lat/lon and east/north
371 * @since 14960
372 */
373 public boolean isOutSideWorld() {
374 LatLon ll = getCoor();
375 if (ll != null) {
376 Bounds b = ProjectionRegistry.getProjection().getWorldBoundsLatLon();
377 if (lat() < b.getMinLat() || lat() > b.getMaxLat() || lon() < b.getMinLon() || lon() > b.getMaxLon()) {
378 return true;
379 }
380 if (!ProjectionRegistry.getProjection().latlon2eastNorth(ll).equalsEpsilon(getEastNorth(), 1.0)) {
381 // we get here if a node was moved or created left from -180 or right from +180
382 return true;
383 }
384 }
385 return false;
386 }
387
388 @Override
389 public UniqueIdGenerator getIdGenerator() {
390 return idGenerator;
391 }
392
393 @Override
394 protected void updateDirectionFlags() {
395 // Nodes do not need/have a direction, greatly improves performance, see #18886
396 }
397}
Note: See TracBrowser for help on using the repository browser.