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

Revision 4229, 9.2 KB checked in by jttt, 10 months ago (diff)

Fix #6581 JOSM can't parse OSM data

  • Property svn:eol-style set to native
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.