source: josm/trunk/src/org/openstreetmap/josm/io/OsmWriter.java@ 5326

Last change on this file since 5326 was 5326, checked in by framm, 12 years ago

handle deleted nodes without lat/lon. apply patches from ticket; closes #7847

  • Property svn:eol-style set to native
File size: 9.8 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.io;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.PrintWriter;
7import java.util.ArrayList;
8import java.util.Collection;
9import java.util.Collections;
10import java.util.Comparator;
11import java.util.List;
12import java.util.Map.Entry;
13
14import org.openstreetmap.josm.data.coor.CoordinateFormat;
15import org.openstreetmap.josm.data.osm.Changeset;
16import org.openstreetmap.josm.data.osm.DataSet;
17import org.openstreetmap.josm.data.osm.DataSource;
18import org.openstreetmap.josm.data.osm.INode;
19import org.openstreetmap.josm.data.osm.IPrimitive;
20import org.openstreetmap.josm.data.osm.IRelation;
21import org.openstreetmap.josm.data.osm.IWay;
22import org.openstreetmap.josm.data.osm.Node;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.data.osm.Relation;
25import org.openstreetmap.josm.data.osm.Tagged;
26import org.openstreetmap.josm.data.osm.Way;
27import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
28import org.openstreetmap.josm.gui.layer.OsmDataLayer;
29import org.openstreetmap.josm.tools.DateUtils;
30
31/**
32 * Save the dataset into a stream as osm intern xml format. This is not using any
33 * xml library for storing.
34 * @author imi
35 */
36public class OsmWriter extends XmlWriter implements PrimitiveVisitor {
37
38 public static final String DEFAULT_API_VERSION = "0.6";
39
40 private boolean osmConform;
41 private boolean withBody = true;
42 private String version;
43 private Changeset changeset;
44
45 /**
46 * Do not call this directly. Use OsmWriterFactory instead.
47 */
48 protected OsmWriter(PrintWriter out, boolean osmConform, String version) {
49 super(out);
50 this.osmConform = osmConform;
51 this.version = (version == null ? DEFAULT_API_VERSION : version);
52 }
53
54 public void setWithBody(boolean wb) {
55 this.withBody = wb;
56 }
57 public void setChangeset(Changeset cs) {
58 this.changeset = cs;
59 }
60 public void setVersion(String v) {
61 this.version = v;
62 }
63
64 public void header() {
65 header(null);
66 }
67 public void header(Boolean upload) {
68 out.println("<?xml version='1.0' encoding='UTF-8'?>");
69 out.print("<osm version='");
70 out.print(version);
71 if (upload != null) {
72 out.print("' upload='");
73 out.print(upload);
74 }
75 out.println("' generator='JOSM'>");
76 }
77 public void footer() {
78 out.println("</osm>");
79 }
80
81 protected static final Comparator<OsmPrimitive> byIdComparator = new Comparator<OsmPrimitive>() {
82 @Override public int compare(OsmPrimitive o1, OsmPrimitive o2) {
83 return (o1.getUniqueId()<o2.getUniqueId() ? -1 : (o1.getUniqueId()==o2.getUniqueId() ? 0 : 1));
84 }
85 };
86
87 protected Collection<OsmPrimitive> sortById(Collection<? extends OsmPrimitive> primitives) {
88 List<OsmPrimitive> result = new ArrayList<OsmPrimitive>(primitives.size());
89 result.addAll(primitives);
90 Collections.sort(result, byIdComparator);
91 return result;
92 }
93
94 public void writeLayer(OsmDataLayer layer) {
95 header(!layer.isUploadDiscouraged());
96 writeDataSources(layer.data);
97 writeContent(layer.data);
98 footer();
99 }
100
101 public void writeContent(DataSet ds) {
102 for (OsmPrimitive n : sortById(ds.getNodes())) {
103 if (shouldWrite(n)) {
104 visit((Node)n);
105 }
106 }
107 for (OsmPrimitive w : sortById(ds.getWays())) {
108 if (shouldWrite(w)) {
109 visit((Way)w);
110 }
111 }
112 for (OsmPrimitive e: sortById(ds.getRelations())) {
113 if (shouldWrite(e)) {
114 visit((Relation)e);
115 }
116 }
117 }
118
119 protected boolean shouldWrite(OsmPrimitive osm) {
120 return !osm.isNewOrUndeleted() || !osm.isDeleted();
121 }
122
123 public void writeDataSources(DataSet ds) {
124 for (DataSource s : ds.dataSources) {
125 out.println(" <bounds minlat='"
126 + s.bounds.getMin().lat()+"' minlon='"
127 + s.bounds.getMin().lon()+"' maxlat='"
128 + s.bounds.getMax().lat()+"' maxlon='"
129 + s.bounds.getMax().lon()
130 +"' origin='"+XmlWriter.encode(s.origin)+"' />");
131 }
132 }
133
134 @Override
135 public void visit(INode n) {
136 if (n.isIncomplete()) return;
137 addCommon(n, "node");
138 if (n.getCoor() != null) {
139 out.print(" lat='"+n.getCoor().lat()+"' lon='"+n.getCoor().lon()+"'");
140 }
141 if (!withBody) {
142 out.println("/>");
143 } else {
144 addTags(n, "node", true);
145 }
146 }
147
148 @Override
149 public void visit(IWay w) {
150 if (w.isIncomplete()) return;
151 addCommon(w, "way");
152 if (!withBody) {
153 out.println("/>");
154 } else {
155 out.println(">");
156 for (int i=0; i<w.getNodesCount(); ++i) {
157 out.println(" <nd ref='"+w.getNodeId(i) +"' />");
158 }
159 addTags(w, "way", false);
160 }
161 }
162
163 @Override
164 public void visit(IRelation e) {
165 if (e.isIncomplete()) return;
166 addCommon(e, "relation");
167 if (!withBody) {
168 out.println("/>");
169 } else {
170 out.println(">");
171 for (int i=0; i<e.getMembersCount(); ++i) {
172 out.print(" <member type='");
173 out.print(e.getMemberType(i).getAPIName());
174 out.println("' ref='"+e.getMemberId(i)+"' role='" +
175 XmlWriter.encode(e.getRole(i)) + "' />");
176 }
177 addTags(e, "relation", false);
178 }
179 }
180
181 public void visit(Changeset cs) {
182 out.print(" <changeset ");
183 out.print(" id='"+cs.getId()+"'");
184 if (cs.getUser() != null) {
185 out.print(" user='"+cs.getUser().getName() +"'");
186 out.print(" uid='"+cs.getUser().getId() +"'");
187 }
188 if (cs.getCreatedAt() != null) {
189 out.print(" created_at='"+DateUtils.fromDate(cs.getCreatedAt()) +"'");
190 }
191 if (cs.getClosedAt() != null) {
192 out.print(" closed_at='"+DateUtils.fromDate(cs.getClosedAt()) +"'");
193 }
194 out.print(" open='"+ (cs.isOpen() ? "true" : "false") +"'");
195 if (cs.getMin() != null) {
196 out.print(" min_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
197 out.print(" min_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
198 }
199 if (cs.getMax() != null) {
200 out.print(" max_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
201 out.print(" max_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
202 }
203 out.println(">");
204 addTags(cs, "changeset", false); // also writes closing </changeset>
205 }
206
207 protected static final Comparator<Entry<String, String>> byKeyComparator = new Comparator<Entry<String,String>>() {
208 @Override public int compare(Entry<String, String> o1, Entry<String, String> o2) {
209 return o1.getKey().compareTo(o2.getKey());
210 }
211 };
212
213 protected void addTags(Tagged osm, String tagname, boolean tagOpen) {
214 if (osm.hasKeys()) {
215 if (tagOpen) {
216 out.println(">");
217 }
218 List<Entry<String, String>> entries = new ArrayList<Entry<String,String>>(osm.getKeys().entrySet());
219 Collections.sort(entries, byKeyComparator);
220 for (Entry<String, String> e : entries) {
221 if ((osm instanceof Changeset) || !("created_by".equals(e.getKey()))) {
222 out.println(" <tag k='"+ XmlWriter.encode(e.getKey()) +
223 "' v='"+XmlWriter.encode(e.getValue())+ "' />");
224 }
225 }
226 out.println(" </" + tagname + ">");
227 } else if (tagOpen) {
228 out.println(" />");
229 } else {
230 out.println(" </" + tagname + ">");
231 }
232 }
233
234 /**
235 * Add the common part as the form of the tag as well as the XML attributes
236 * id, action, user, and visible.
237 */
238 protected void addCommon(IPrimitive osm, String tagname) {
239 out.print(" <"+tagname);
240 if (osm.getUniqueId() != 0) {
241 out.print(" id='"+ osm.getUniqueId()+"'");
242 } else
243 throw new IllegalStateException(tr("Unexpected id 0 for osm primitive found"));
244 if (!osmConform) {
245 String action = null;
246 if (osm.isDeleted()) {
247 action = "delete";
248 } else if (osm.isModified()) {
249 action = "modify";
250 }
251 if (action != null) {
252 out.print(" action='"+action+"'");
253 }
254 }
255 if (!osm.isTimestampEmpty()) {
256 out.print(" timestamp='"+DateUtils.fromDate(osm.getTimestamp())+"'");
257 }
258 // user and visible added with 0.4 API
259 if (osm.getUser() != null) {
260 if(osm.getUser().isLocalUser()) {
261 out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+"'");
262 } else if (osm.getUser().isOsmUser()) {
263 // uid added with 0.6
264 out.print(" uid='"+ osm.getUser().getId()+"'");
265 out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+"'");
266 }
267 }
268 out.print(" visible='"+osm.isVisible()+"'");
269 if (osm.getVersion() != 0) {
270 out.print(" version='"+osm.getVersion()+"'");
271 }
272 if (this.changeset != null && this.changeset.getId() != 0) {
273 out.print(" changeset='"+this.changeset.getId()+"'" );
274 } else if (osm.getChangesetId() > 0 && !osm.isNew()) {
275 out.print(" changeset='"+osm.getChangesetId()+"'" );
276 }
277 }
278
279 public void close() {
280 out.close();
281 }
282
283 @Override
284 public void flush() {
285 out.flush();
286 }
287}
Note: See TracBrowser for help on using the repository browser.