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

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

fix #9440 - Consistent definition of ways treated as areas

  • Property svn:eol-style set to native
File size: 9.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm;
3
4import org.openstreetmap.josm.Main;
5import org.openstreetmap.josm.data.coor.EastNorth;
6import org.openstreetmap.josm.data.coor.LatLon;
7import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
8import org.openstreetmap.josm.data.osm.visitor.Visitor;
9import org.openstreetmap.josm.data.projection.Projections;
10
11/**
12 * One node data, consisting of one world coordinate waypoint.
13 *
14 * @author imi
15 */
16public final class Node extends OsmPrimitive implements INode {
17
18 /*
19 * We "inline" lat/lon rather than using a LatLon-object => reduces memory footprint
20 */
21 private double lat = Double.NaN;
22 private double lon = Double.NaN;
23
24 /*
25 * the cached projected coordinates
26 */
27 private double east = Double.NaN;
28 private double north = Double.NaN;
29
30 private boolean isLatLonKnown() {
31 return !Double.isNaN(lat) && !Double.isNaN(lon);
32 }
33
34 @Override
35 public final void setCoor(LatLon coor) {
36 updateCoor(coor, null);
37 }
38
39 @Override
40 public final void setEastNorth(EastNorth eastNorth) {
41 updateCoor(null, eastNorth);
42 }
43
44 private void updateCoor(LatLon coor, EastNorth eastNorth) {
45 if (getDataSet() != null) {
46 boolean locked = writeLock();
47 try {
48 getDataSet().fireNodeMoved(this, coor, eastNorth);
49 } finally {
50 writeUnlock(locked);
51 }
52 } else {
53 setCoorInternal(coor, eastNorth);
54 }
55 }
56
57 @Override
58 public final LatLon getCoor() {
59 if (!isLatLonKnown()) return null;
60 return new LatLon(lat,lon);
61 }
62
63 /**
64 * <p>Replies the projected east/north coordinates.</p>
65 *
66 * <p>Uses the {@link Main#getProjection() global projection} to project the lan/lon-coordinates.
67 * Internally caches the projected coordinates.</p>
68 *
69 * <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must
70 * {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>
71 *
72 * <p>Replies {@code null} if this node doesn't know lat/lon-coordinates, i.e. because it is an incomplete node.
73 *
74 * @return the east north coordinates or {@code null}
75 * @see #invalidateEastNorthCache()
76 *
77 */
78 @Override
79 public final EastNorth getEastNorth() {
80 if (!isLatLonKnown()) return null;
81
82 if (getDataSet() == null)
83 // there is no dataset that listens for projection changes
84 // and invalidates the cache, so we don't use the cache at all
85 return Projections.project(new LatLon(lat, lon));
86
87 if (Double.isNaN(east) || Double.isNaN(north)) {
88 // projected coordinates haven't been calculated yet,
89 // so fill the cache of the projected node coordinates
90 EastNorth en = Projections.project(new LatLon(lat, lon));
91 this.east = en.east();
92 this.north = en.north();
93 }
94 return new EastNorth(east, north);
95 }
96
97 /**
98 * To be used only by Dataset.reindexNode
99 */
100 protected void setCoorInternal(LatLon coor, EastNorth eastNorth) {
101 if (coor != null) {
102 this.lat = coor.lat();
103 this.lon = coor.lon();
104 invalidateEastNorthCache();
105 } else if (eastNorth != null) {
106 LatLon ll = Projections.inverseProject(eastNorth);
107 this.lat = ll.lat();
108 this.lon = ll.lon();
109 this.east = eastNorth.east();
110 this.north = eastNorth.north();
111 } else {
112 this.lat = Double.NaN;
113 this.lon = Double.NaN;
114 invalidateEastNorthCache();
115 if (isVisible()) {
116 setIncomplete(true);
117 }
118 }
119 }
120
121 protected Node(long id, boolean allowNegative) {
122 super(id, allowNegative);
123 }
124
125 /**
126 * Constructs a new local {@code Node} with id 0.
127 */
128 public Node() {
129 this(0, false);
130 }
131
132 /**
133 * Constructs an incomplete {@code Node} object with the given id.
134 * @param id The id. Must be >= 0
135 * @throws IllegalArgumentException if id < 0
136 */
137 public Node(long id) throws IllegalArgumentException {
138 super(id, false);
139 }
140
141 /**
142 * Constructs a new {@code Node} with the given id and version.
143 * @param id The id. Must be >= 0
144 * @param version The version
145 * @throws IllegalArgumentException if id < 0
146 */
147 public Node(long id, int version) throws IllegalArgumentException {
148 super(id, version, false);
149 }
150
151 /**
152 * Constructs an identical clone of the argument.
153 * @param clone The node to clone
154 * @param clearMetadata If {@code true}, clears the OSM id and other metadata as defined by {@link #clearOsmMetadata}. If {@code false}, does nothing
155 */
156 public Node(Node clone, boolean clearMetadata) {
157 super(clone.getUniqueId(), true /* allow negative IDs */);
158 cloneFrom(clone);
159 if (clearMetadata) {
160 clearOsmMetadata();
161 }
162 }
163
164 /**
165 * Constructs an identical clone of the argument (including the id).
166 * @param clone The node to clone, including its id
167 */
168 public Node(Node clone) {
169 this(clone, false);
170 }
171
172 /**
173 * Constructs a new {@code Node} with the given lat/lon with id 0.
174 * @param latlon The {@link LatLon} coordinates
175 */
176 public Node(LatLon latlon) {
177 super(0, false);
178 setCoor(latlon);
179 }
180
181 /**
182 * Constructs a new {@code Node} with the given east/north with id 0.
183 * @param eastNorth The {@link EastNorth} coordinates
184 */
185 public Node(EastNorth eastNorth) {
186 super(0, false);
187 setEastNorth(eastNorth);
188 }
189
190 @Override
191 void setDataset(DataSet dataSet) {
192 super.setDataset(dataSet);
193 if (!isIncomplete() && isVisible() && (getCoor() == null || getEastNorth() == null))
194 throw new DataIntegrityProblemException("Complete node with null coordinates: " + toString());
195 }
196
197 @Override
198 public void accept(Visitor visitor) {
199 visitor.visit(this);
200 }
201
202 @Override
203 public void accept(PrimitiveVisitor visitor) {
204 visitor.visit(this);
205 }
206
207 @Override
208 public void cloneFrom(OsmPrimitive osm) {
209 boolean locked = writeLock();
210 try {
211 super.cloneFrom(osm);
212 setCoor(((Node)osm).getCoor());
213 } finally {
214 writeUnlock(locked);
215 }
216 }
217
218 /**
219 * Merges the technical and semantical attributes from <code>other</code> onto this.
220 *
221 * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code>
222 * have an assigend OSM id, the IDs have to be the same.
223 *
224 * @param other the other primitive. Must not be null.
225 * @throws IllegalArgumentException thrown if other is null.
226 * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not
227 * @throws DataIntegrityProblemException thrown if other is new and other.getId() != this.getId()
228 */
229 @Override
230 public void mergeFrom(OsmPrimitive other) {
231 boolean locked = writeLock();
232 try {
233 super.mergeFrom(other);
234 if (!other.isIncomplete()) {
235 setCoor(((Node)other).getCoor());
236 }
237 } finally {
238 writeUnlock(locked);
239 }
240 }
241
242 @Override public void load(PrimitiveData data) {
243 boolean locked = writeLock();
244 try {
245 super.load(data);
246 setCoor(((NodeData)data).getCoor());
247 } finally {
248 writeUnlock(locked);
249 }
250 }
251
252 @Override public NodeData save() {
253 NodeData data = new NodeData();
254 saveCommonAttributes(data);
255 if (!isIncomplete()) {
256 data.setCoor(getCoor());
257 }
258 return data;
259 }
260
261 @Override
262 public String toString() {
263 String coorDesc = isLatLonKnown() ? "lat="+lat+",lon="+lon : "";
264 return "{Node id=" + getUniqueId() + " version=" + getVersion() + " " + getFlagsAsString() + " " + coorDesc+"}";
265 }
266
267 @Override
268 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
269 if (!(other instanceof Node))
270 return false;
271 if (! super.hasEqualSemanticAttributes(other))
272 return false;
273 Node n = (Node)other;
274 LatLon coor = getCoor();
275 LatLon otherCoor = n.getCoor();
276 if (coor == null && otherCoor == null)
277 return true;
278 else if (coor != null && otherCoor != null)
279 return coor.equalsEpsilon(otherCoor);
280 else
281 return false;
282 }
283
284 @Override
285 public int compareTo(OsmPrimitive o) {
286 return o instanceof Node ? Long.valueOf(getUniqueId()).compareTo(o.getUniqueId()) : 1;
287 }
288
289 @Override
290 public String getDisplayName(NameFormatter formatter) {
291 return formatter.format(this);
292 }
293
294 @Override
295 public OsmPrimitiveType getType() {
296 return OsmPrimitiveType.NODE;
297 }
298
299 @Override
300 public BBox getBBox() {
301 return new BBox(this);
302 }
303
304 @Override
305 public void updatePosition() {
306 }
307
308 @Override
309 public boolean isDrawable() {
310 // Not possible to draw a node without coordinates.
311 return super.isDrawable() && isLatLonKnown();
312 }
313
314 /**
315 * Check whether this node connects 2 ways.
316 *
317 * @return true if isReferredByWays(2) returns true
318 * @see #isReferredByWays(int)
319 */
320 public boolean isConnectionNode() {
321 return isReferredByWays(2);
322 }
323
324 /**
325 * Invoke to invalidate the internal cache of projected east/north coordinates.
326 * Coordinates are reprojected on demand when the {@link #getEastNorth()} is invoked
327 * next time.
328 */
329 public void invalidateEastNorthCache() {
330 this.east = Double.NaN;
331 this.north = Double.NaN;
332 }
333
334 @Override
335 public boolean concernsArea() {
336 // A node cannot be an area
337 return false;
338 }
339}
Note: See TracBrowser for help on using the repository browser.