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

Last change on this file since 4229 was 4229, checked in by jttt, 13 years ago

Fix #6581 JOSM can't parse OSM data

  • Property svn:eol-style set to native
File size: 9.2 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
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 if(coor != null){
37 updateCoor(coor, null);
38 }
39 }
40
41 @Override
42 public final void setEastNorth(EastNorth eastNorth) {
43 if(eastNorth != null) {
44 updateCoor(null, eastNorth);
45 }
46 }
47
48 private void updateCoor(LatLon coor, EastNorth eastNorth) {
49 if (getDataSet() != null) {
50 boolean locked = writeLock();
51 try {
52 getDataSet().fireNodeMoved(this, coor, eastNorth);
53 } finally {
54 writeUnlock(locked);
55 }
56 } else {
57 setCoorInternal(coor, eastNorth);
58 }
59 }
60
61 @Override
62 public final LatLon getCoor() {
63 if (!isLatLonKnown()) return null;
64 return new LatLon(lat,lon);
65 }
66
67 /**
68 * <p>Replies the projected east/north coordinates.</p>
69 *
70 * <p>Uses the {@link Main#getProjection() global projection} to project the lan/lon-coordinates.
71 * Internally caches the projected coordinates.</p>
72 *
73 * <p><strong>Caveat:</strong> doesn't listen to projection changes. Clients must
74 * {@link #invalidateEastNorthCache() invalidate the internal cache}.</p>
75 *
76 * <p>Replies {@code null} if this node doesn't know lat/lon-coordinates, i.e. because it is an incomplete node.
77 *
78 * @return the east north coordinates or {@code null}
79 * @see #invalidateEastNorthCache()
80 *
81 */
82 @Override
83 public final EastNorth getEastNorth() {
84 if (!isLatLonKnown()) return null;
85
86 if (getDataSet() == null)
87 // there is no dataset that listens for projection changes
88 // and invalidates the cache, so we don't use the cache at all
89 return Projections.project(new LatLon(lat, lon));
90
91 if (Double.isNaN(east) || Double.isNaN(north)) {
92 // projected coordinates haven't been calculated yet,
93 // so fill the cache of the projected node coordinates
94 EastNorth en = Projections.project(new LatLon(lat, lon));
95 this.east = en.east();
96 this.north = en.north();
97 }
98 return new EastNorth(east, north);
99 }
100
101 /**
102 * To be used only by Dataset.reindexNode
103 */
104 protected void setCoorInternal(LatLon coor, EastNorth eastNorth) {
105 if (coor != null) {
106 this.lat = coor.lat();
107 this.lon = coor.lon();
108 invalidateEastNorthCache();
109 } else if (eastNorth != null) {
110 LatLon ll = Projections.inverseProject(eastNorth);
111 this.lat = ll.lat();
112 this.lon = ll.lon();
113 this.east = eastNorth.east();
114 this.north = eastNorth.north();
115 } else
116 throw new IllegalArgumentException();
117 }
118
119 protected Node(long id, boolean allowNegative) {
120 super(id, allowNegative);
121 }
122
123 /**
124 * Create a new local node.
125 *
126 */
127 public Node() {
128 this(0, false);
129 }
130
131 /**
132 * Create an incomplete Node object
133 */
134 public Node(long id) {
135 super(id, false);
136 }
137
138 /**
139 * Create new node
140 * @param id
141 * @param version
142 */
143 public Node(long id, int version) {
144 super(id, version, false);
145 }
146
147 /**
148 *
149 * @param clone
150 * @param clearId If true, set version to 0 and id to new unique value
151 */
152 public Node(Node clone, boolean clearId) {
153 super(clone.getUniqueId(), true /* allow negative IDs */);
154 cloneFrom(clone);
155 if (clearId) {
156 clearOsmId();
157 }
158 }
159
160 /**
161 * Create an identical clone of the argument (including the id)
162 */
163 public Node(Node clone) {
164 this(clone, false);
165 }
166
167 public Node(LatLon latlon) {
168 super(0, false);
169 setCoor(latlon);
170 }
171
172 public Node(EastNorth eastNorth) {
173 super(0, false);
174 setEastNorth(eastNorth);
175 }
176
177 @Override
178 void setDataset(DataSet dataSet) {
179 super.setDataset(dataSet);
180 if (!isIncomplete() && (getCoor() == null || getEastNorth() == null))
181 throw new DataIntegrityProblemException("Complete node with null coordinates: " + toString() + get3892DebugInfo());
182 }
183
184 @Override public void visit(Visitor visitor) {
185 visitor.visit(this);
186 }
187
188 @Override public void visit(PrimitiveVisitor visitor) {
189 visitor.visit(this);
190 }
191
192 @Override public void cloneFrom(OsmPrimitive osm) {
193 boolean locked = writeLock();
194 try {
195 super.cloneFrom(osm);
196 setCoor(((Node)osm).getCoor());
197 } finally {
198 writeUnlock(locked);
199 }
200 }
201
202 /**
203 * Merges the technical and semantical attributes from <code>other</code> onto this.
204 *
205 * Both this and other must be new, or both must be assigned an OSM ID. If both this and <code>other</code>
206 * have an assigend OSM id, the IDs have to be the same.
207 *
208 * @param other the other primitive. Must not be null.
209 * @throws IllegalArgumentException thrown if other is null.
210 * @throws DataIntegrityProblemException thrown if either this is new and other is not, or other is new and this is not
211 * @throws DataIntegrityProblemException thrown if other is new and other.getId() != this.getId()
212 */
213 @Override
214 public void mergeFrom(OsmPrimitive other) {
215 boolean locked = writeLock();
216 try {
217 super.mergeFrom(other);
218 if (!other.isIncomplete()) {
219 setCoor(((Node)other).getCoor());
220 }
221 } finally {
222 writeUnlock(locked);
223 }
224 }
225
226 @Override public void load(PrimitiveData data) {
227 boolean locked = writeLock();
228 try {
229 super.load(data);
230 setCoor(((NodeData)data).getCoor());
231 } finally {
232 writeUnlock(locked);
233 }
234 }
235
236 @Override public NodeData save() {
237 NodeData data = new NodeData();
238 saveCommonAttributes(data);
239 if (!isIncomplete()) {
240 data.setCoor(getCoor());
241 }
242 return data;
243 }
244
245 @Override public String toString() {
246 String coorDesc = isLatLonKnown() ? "lat="+lat+",lon="+lon : "";
247 return "{Node id=" + getUniqueId() + " version=" + getVersion() + " " + getFlagsAsString() + " " + coorDesc+"}";
248 }
249
250 @Override
251 public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
252 if (other == null || ! (other instanceof Node) )
253 return false;
254 if (! super.hasEqualSemanticAttributes(other))
255 return false;
256 Node n = (Node)other;
257 LatLon coor = getCoor();
258 LatLon otherCoor = n.getCoor();
259 if (coor == null && otherCoor == null)
260 return true;
261 else if (coor != null && otherCoor != null)
262 return coor.equalsEpsilon(otherCoor);
263 else
264 return false;
265 }
266
267 @Override
268 public int compareTo(OsmPrimitive o) {
269 return o instanceof Node ? Long.valueOf(getUniqueId()).compareTo(o.getUniqueId()) : 1;
270 }
271
272 @Override
273 public String getDisplayName(NameFormatter formatter) {
274 return formatter.format(this);
275 }
276
277 @Override
278 public OsmPrimitiveType getType() {
279 return OsmPrimitiveType.NODE;
280 }
281
282 @Override
283 public BBox getBBox() {
284 return new BBox(this);
285 }
286
287 @Override
288 public void updatePosition() {
289 }
290
291 public boolean isConnectionNode() {
292 return isReferredByWays(2);
293 }
294
295 public String get3892DebugInfo() {
296 StringBuilder builder = new StringBuilder();
297 builder.append("Unexpected error. Please report it to http://josm.openstreetmap.de/ticket/3892\n");
298 builder.append(toString());
299 builder.append("\n");
300 if (isLatLonKnown()) {
301 builder.append("Coor is null\n");
302 } else {
303 builder.append(String.format("EastNorth: %s\n", getEastNorth()));
304 builder.append(Main.getProjection());
305 builder.append("\n");
306 }
307
308 return builder.toString();
309 }
310
311 /**
312 * Invoke to invalidate the internal cache of projected east/north coordinates.
313 * Coordinates are reprojected on demand when the {@link #getEastNorth()} is invoked
314 * next time.
315 */
316 public void invalidateEastNorthCache() {
317 this.east = Double.NaN;
318 this.north = Double.NaN;
319 }
320}
Note: See TracBrowser for help on using the repository browser.