source: josm/src/org/openstreetmap/josm/io/OsmReader.java@ 290

Last change on this file since 290 was 290, checked in by imi, 17 years ago
  • added support for multiple data layers
File size: 9.9 KB
Line 
1package org.openstreetmap.josm.io;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.io.IOException;
6import java.io.InputStream;
7import java.io.InputStreamReader;
8import java.text.ParseException;
9import java.util.Arrays;
10import java.util.Collection;
11import java.util.HashMap;
12import java.util.HashSet;
13import java.util.LinkedList;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.Bounds;
19import org.openstreetmap.josm.data.coor.LatLon;
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.DataSource;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.Segment;
25import org.openstreetmap.josm.data.osm.User;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.visitor.AddVisitor;
28import org.openstreetmap.josm.data.osm.visitor.Visitor;
29import org.openstreetmap.josm.gui.PleaseWaitDialog;
30import org.openstreetmap.josm.tools.DateParser;
31import org.xml.sax.Attributes;
32import org.xml.sax.SAXException;
33
34import uk.co.wilson.xml.MinML2;
35
36/**
37 * Parser for the Osm Api. Read from an input stream and construct a dataset out of it.
38 *
39 * Reading process takes place in three phases. During the first phase (including xml parse),
40 * all nodes are read and stored. Other information than nodes are stored in a raw list
41 *
42 * The second phase reads from the raw list all segments and create Segment objects.
43 *
44 * The third phase read all ways out of the remaining objects in the raw list.
45 *
46 * @author Imi
47 */
48public class OsmReader {
49
50 /**
51 * This is used as (readonly) source for finding missing references when not transferred in the
52 * file.
53 */
54 private DataSet references;
55
56 /**
57 * The dataset to add parsed objects to.
58 */
59 private DataSet ds = new DataSet();
60
61 /**
62 * The visitor to use to add the data to the set.
63 */
64 private AddVisitor adder = new AddVisitor(ds);
65
66 /**
67 * All read nodes after phase 1.
68 */
69 private Map<Long, Node> nodes = new HashMap<Long, Node>();
70
71 // TODO: What the hack? Is this really from me? Please, clean this up!
72 private static class OsmPrimitiveData extends OsmPrimitive {
73 @Override public void visit(Visitor visitor) {}
74 public int compareTo(OsmPrimitive o) {return 0;}
75
76 public void copyTo(OsmPrimitive osm) {
77 osm.id = id;
78 osm.keys = keys;
79 osm.modified = modified;
80 osm.selected = selected;
81 osm.deleted = deleted;
82 osm.timestamp = timestamp;
83 osm.user = user;
84 osm.visible = visible;
85 }
86 }
87
88 /**
89 * Data structure for the remaining segment objects
90 * Maps the raw attributes to key/value pairs.
91 */
92 private Map<OsmPrimitiveData, long[]> segs = new HashMap<OsmPrimitiveData, long[]>();
93
94 /**
95 * Data structure for the remaining way objects
96 */
97 private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>();
98
99 /**
100 * List of protocol versions that will be accepted on reading
101 */
102 private HashSet<String> allowedVersions = new HashSet<String>();
103
104 private class Parser extends MinML2 {
105 /**
106 * The current osm primitive to be read.
107 */
108 private OsmPrimitive current;
109
110 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
111 try {
112 if (qName.equals("osm")) {
113 if (atts == null)
114 throw new SAXException(tr("Unknown version"));
115 if (!allowedVersions.contains(atts.getValue("version")))
116 throw new SAXException(tr("Unknown version")+": "+atts.getValue("version"));
117 } else if (qName.equals("bound")) {
118 String bbox = atts.getValue("box");
119 String origin = atts.getValue("origin");
120 if (bbox != null) {
121 String[] b = bbox.split(",");
122 Bounds bounds = null;
123 if (b.length == 4)
124 bounds = new Bounds(
125 new LatLon(Double.parseDouble(b[0]),Double.parseDouble(b[1])),
126 new LatLon(Double.parseDouble(b[2]),Double.parseDouble(b[3])));
127 DataSource src = new DataSource(bounds, origin);
128 ds.dataSources.add(src);
129 }
130 } else if (qName.equals("node")) {
131 current = new Node(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon")));
132 readCommon(atts, current);
133 nodes.put(current.id, (Node)current);
134 } else if (qName.equals("segment")) {
135 current = new OsmPrimitiveData();
136 readCommon(atts, current);
137 segs.put((OsmPrimitiveData)current, new long[]{getLong(atts, "from"), getLong(atts, "to")});
138 } else if (qName.equals("way")) {
139 current = new OsmPrimitiveData();
140 readCommon(atts, current);
141 ways.put((OsmPrimitiveData)current, new LinkedList<Long>());
142 } else if (qName.equals("seg")) {
143 Collection<Long> list = ways.get(current);
144 if (list == null)
145 throw new SAXException(tr("Found <seg> tag on non-way."));
146 long id = getLong(atts, "id");
147 if (id == 0)
148 throw new SAXException(tr("Incomplete segment with id=0"));
149 list.add(id);
150 } else if (qName.equals("tag"))
151 current.put(atts.getValue("k"), atts.getValue("v"));
152 } catch (NumberFormatException x) {
153 x.printStackTrace(); // SAXException does not chain correctly
154 throw new SAXException(x.getMessage(), x);
155 } catch (NullPointerException x) {
156 x.printStackTrace(); // SAXException does not chain correctly
157 throw new SAXException(tr("NullPointerException, Possibly some missing tags."), x);
158 }
159 }
160
161 private double getDouble(Attributes atts, String value) {
162 return Double.parseDouble(atts.getValue(value));
163 }
164 }
165
166 /**
167 * Constructor initializes list of allowed protocol versions.
168 */
169 public OsmReader() {
170 // first add the main server version
171 allowedVersions.add(Main.pref.get("osm-server.version", "0.4"));
172 // now also add all compatible versions
173 String[] additionalVersions =
174 Main.pref.get("osm-server.additional-versions", "0.3").split("/,/");
175 allowedVersions.addAll(Arrays.asList(additionalVersions));
176 }
177
178 /**
179 * Read out the common attributes from atts and put them into this.current.
180 */
181 void readCommon(Attributes atts, OsmPrimitive current) throws SAXException {
182 current.id = getLong(atts, "id");
183 if (current.id == 0)
184 throw new SAXException(tr("Illegal object with id=0"));
185
186 String time = atts.getValue("timestamp");
187 if (time != null && time.length() != 0) {
188 try {
189 current.timestamp = DateParser.parse(time);
190 } catch (ParseException e) {
191 e.printStackTrace();
192 throw new SAXException(tr("Couldn't read time format \"{0}\".",time));
193 }
194 }
195
196 // user attribute added in 0.4 API
197 String user = atts.getValue("user");
198 if (user != null) {
199 // do not store literally; get object reference for string
200 current.user = User.get(user);
201 }
202
203 // visible attribute added in 0.4 API
204 String visible = atts.getValue("visible");
205 if (visible != null) {
206 current.visible = Boolean.parseBoolean(visible);
207 }
208
209 String action = atts.getValue("action");
210 if (action == null)
211 return;
212 if (action.equals("delete"))
213 current.delete(true);
214 else if (action.startsWith("modify"))
215 current.modified = true;
216 }
217 private long getLong(Attributes atts, String value) throws SAXException {
218 String s = atts.getValue(value);
219 if (s == null)
220 throw new SAXException(tr("Missing required attribute \"{0}\".",value));
221 return Long.parseLong(s);
222 }
223
224 private void createSegments() {
225 for (Entry<OsmPrimitiveData, long[]> e : segs.entrySet()) {
226 Node from = findNode(e.getValue()[0]);
227 Node to = findNode(e.getValue()[1]);
228 if (from == null || to == null)
229 continue; //TODO: implement support for incomplete nodes.
230 Segment s = new Segment(from, to);
231 e.getKey().copyTo(s);
232 segments.put(s.id, s);
233 adder.visit(s);
234 }
235 }
236
237 private Node findNode(long id) {
238 Node n = nodes.get(id);
239 if (n != null)
240 return n;
241 for (Node node : references.nodes)
242 if (node.id == id)
243 return node;
244 // TODO: This has to be changed to support multiple layers.
245 for (Node node : Main.ds.nodes)
246 if (node.id == id)
247 return new Node(node);
248 return null;
249 }
250
251 private Segment findSegment(long id) {
252 Segment s = segments.get(id);
253 if (s != null)
254 return s;
255 for (Segment seg : references.segments)
256 if (seg.id == id)
257 return seg;
258 // TODO: This has to be changed to support multiple layers.
259 for (Segment seg : Main.ds.segments)
260 if (seg.id == id)
261 return new Segment(seg);
262 return null;
263 }
264
265 private void createWays() {
266 for (Entry<OsmPrimitiveData, Collection<Long>> e : ways.entrySet()) {
267 Way w = new Way();
268 for (long id : e.getValue()) {
269 Segment s = findSegment(id);
270 if (s == null) {
271 s = new Segment(id); // incomplete line segment
272 adder.visit(s);
273 }
274 w.segments.add(s);
275 }
276 e.getKey().copyTo(w);
277 adder.visit(w);
278 }
279 }
280
281 /**
282 * All read segments after phase 2.
283 */
284 private Map<Long, Segment> segments = new HashMap<Long, Segment>();
285
286 /**
287 * Parse the given input source and return the dataset.
288 * @param ref The dataset that is search in for references first. If
289 * the Reference is not found here, Main.ds is searched and a copy of the
290 * elemet found there is returned.
291 */
292 public static DataSet parseDataSet(InputStream source, DataSet ref, PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException {
293 OsmReader osm = new OsmReader();
294 osm.references = ref == null ? new DataSet() : ref;
295
296 // phase 1: Parse nodes and read in raw segments and ways
297 osm.new Parser().parse(new InputStreamReader(source, "UTF-8"));
298 if (pleaseWaitDlg != null) {
299 pleaseWaitDlg.progress.setValue(0);
300 pleaseWaitDlg.currentAction.setText(tr("Preparing data..."));
301 }
302 for (Node n : osm.nodes.values())
303 osm.adder.visit(n);
304
305 try {
306 osm.createSegments();
307 osm.createWays();
308 } catch (NumberFormatException e) {
309 e.printStackTrace();
310 throw new SAXException(tr("Illformed Node id"));
311 }
312
313 // clear all negative ids (new to this file)
314 for (OsmPrimitive o : osm.ds.allPrimitives())
315 if (o.id < 0)
316 o.id = 0;
317
318 return osm.ds;
319 }
320}
Note: See TracBrowser for help on using the repository browser.