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

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