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

Last change on this file since 227 was 227, checked in by framm, 17 years ago

Support new 0.4 attributes "user" and "visible"; support config option "osm-server.additional-versions" containig a comma separated list of version numbers that should be accepted when reading OSM files - defaults to 0.3 to allow reading 0.3 files.

File size: 9.3 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.lang.reflect.Array;
9import java.text.ParseException;
10import java.util.Arrays;
11import java.util.Collection;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.LinkedList;
15import java.util.Map;
16import java.util.Map.Entry;
17
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.coor.LatLon;
21import org.openstreetmap.josm.data.osm.DataSet;
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 private static class OsmPrimitiveData extends OsmPrimitive {
72 @Override public void visit(Visitor visitor) {}
73 public int compareTo(OsmPrimitive o) {return 0;}
74
75 public void copyTo(OsmPrimitive osm) {
76 osm.id = id;
77 osm.keys = keys;
78 osm.modified = modified;
79 osm.selected = selected;
80 osm.deleted = deleted;
81 osm.timestamp = timestamp;
82 osm.user = user;
83 osm.visible = visible;
84 }
85 }
86
87 /**
88 * Data structure for the remaining segment objects
89 * Maps the raw attributes to key/value pairs.
90 */
91 private Map<OsmPrimitiveData, long[]> segs = new HashMap<OsmPrimitiveData, long[]>();
92
93 /**
94 * Data structure for the remaining way objects
95 */
96 private Map<OsmPrimitiveData, Collection<Long>> ways = new HashMap<OsmPrimitiveData, Collection<Long>>();
97
98 /**
99 * List of protocol versions that will be accepted on reading
100 */
101 private HashSet<String> allowedVersions = new HashSet<String>();
102
103 private class Parser extends MinML2 {
104 /**
105 * The current osm primitive to be read.
106 */
107 private OsmPrimitive current;
108
109 @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
110 try {
111 if (qName.equals("osm")) {
112 if (atts == null)
113 throw new SAXException(tr("Unknown version"));
114 if (!allowedVersions.contains(atts.getValue("version")))
115 throw new SAXException(tr("Unknown version")+": "+atts.getValue("version"));
116 } else if (qName.equals("node")) {
117 current = new Node(new LatLon(getDouble(atts, "lat"), getDouble(atts, "lon")));
118 readCommon(atts, current);
119 nodes.put(current.id, (Node)current);
120 } else if (qName.equals("segment")) {
121 current = new OsmPrimitiveData();
122 readCommon(atts, current);
123 segs.put((OsmPrimitiveData)current, new long[]{getLong(atts, "from"), getLong(atts, "to")});
124 } else if (qName.equals("way")) {
125 current = new OsmPrimitiveData();
126 readCommon(atts, current);
127 ways.put((OsmPrimitiveData)current, new LinkedList<Long>());
128 } else if (qName.equals("seg")) {
129 Collection<Long> list = ways.get(current);
130 if (list == null)
131 throw new SAXException(tr("Found <seg> tag on non-way."));
132 long id = getLong(atts, "id");
133 if (id == 0)
134 throw new SAXException(tr("Incomplete segment with id=0"));
135 list.add(id);
136 } else if (qName.equals("tag"))
137 current.put(atts.getValue("k"), atts.getValue("v"));
138 } catch (NumberFormatException x) {
139 x.printStackTrace(); // SAXException does not chain correctly
140 throw new SAXException(x.getMessage(), x);
141 } catch (NullPointerException x) {
142 x.printStackTrace(); // SAXException does not chain correctly
143 throw new SAXException(tr("NullPointerException. Possible some missing tags."), x);
144 }
145 }
146
147 private double getDouble(Attributes atts, String value) {
148 return Double.parseDouble(atts.getValue(value));
149 }
150 }
151
152 /**
153 * Constructor initializes list of allowed protocol versions.
154 */
155 public OsmReader() {
156 // first add the main server version
157 allowedVersions.add(Main.pref.get("osm-server.version", "0.4"));
158 // now also add all compatible versions
159 String[] additionalVersions =
160 Main.pref.get("osm-server.additional-versions", "0.3").split("/,/");
161 allowedVersions.addAll(Arrays.asList(additionalVersions));
162 }
163
164 /**
165 * Read out the common attributes from atts and put them into this.current.
166 */
167 void readCommon(Attributes atts, OsmPrimitive current) throws SAXException {
168 current.id = getLong(atts, "id");
169 if (current.id == 0)
170 throw new SAXException(tr("Illegal object with id=0"));
171
172 String time = atts.getValue("timestamp");
173 if (time != null && time.length() != 0) {
174 try {
175 current.timestamp = DateParser.parse(time);
176 } catch (ParseException e) {
177 e.printStackTrace();
178 throw new SAXException(tr("Couldn''t read time format \"{0}\".",time));
179 }
180 }
181
182 // user attribute added in 0.4 API
183 String user = atts.getValue("user");
184 if (user != null) {
185 // do not store literally; get object reference for string
186 current.user = User.get(user);
187 }
188
189 // visible attribute added in 0.4 API
190 String visible = atts.getValue("visible");
191 if (visible != null) {
192 current.visible = Boolean.parseBoolean(visible);
193 }
194
195 String action = atts.getValue("action");
196 if (action == null)
197 return;
198 if (action.equals("delete"))
199 current.delete(true);
200 else if (action.startsWith("modify"))
201 current.modified = true;
202 }
203 private long getLong(Attributes atts, String value) throws SAXException {
204 String s = atts.getValue(value);
205 if (s == null)
206 throw new SAXException(tr("Missing required attirbute \"{0}\".",value));
207 return Long.parseLong(s);
208 }
209
210 private void createSegments() {
211 for (Entry<OsmPrimitiveData, long[]> e : segs.entrySet()) {
212 Node from = findNode(e.getValue()[0]);
213 Node to = findNode(e.getValue()[1]);
214 if (from == null || to == null)
215 continue; //TODO: implement support for incomplete nodes.
216 Segment s = new Segment(from, to);
217 e.getKey().copyTo(s);
218 segments.put(s.id, s);
219 adder.visit(s);
220 }
221 }
222
223 private Node findNode(long id) {
224 Node n = nodes.get(id);
225 if (n != null)
226 return n;
227 for (Node node : references.nodes)
228 if (node.id == id)
229 return node;
230 for (Node node : Main.ds.nodes)
231 if (node.id == id)
232 return node;
233 return null;
234 }
235
236 private Segment findSegment(long id) {
237 Segment s = segments.get(id);
238 if (s != null)
239 return s;
240 for (Segment seg : references.segments)
241 if (seg.id == id)
242 return seg;
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 */
275 public static DataSet parseDataSet(InputStream source, DataSet ref, PleaseWaitDialog pleaseWaitDlg) throws SAXException, IOException {
276 OsmReader osm = new OsmReader();
277 osm.references = ref == null ? new DataSet() : ref;
278
279 // phase 1: Parse nodes and read in raw segments and ways
280 osm.new Parser().parse(new InputStreamReader(source, "UTF-8"));
281 if (pleaseWaitDlg != null) {
282 pleaseWaitDlg.progress.setValue(0);
283 pleaseWaitDlg.currentAction.setText(tr("Preparing data..."));
284 }
285 for (Node n : osm.nodes.values())
286 osm.adder.visit(n);
287
288 try {
289 osm.createSegments();
290 osm.createWays();
291 } catch (NumberFormatException e) {
292 e.printStackTrace();
293 throw new SAXException(tr("Illformed Node id"));
294 }
295
296 // clear all negative ids (new to this file)
297 for (OsmPrimitive o : osm.ds.allPrimitives())
298 if (o.id < 0)
299 o.id = 0;
300
301 return osm.ds;
302 }
303}
Note: See TracBrowser for help on using the repository browser.