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

Last change on this file since 13449 was 13173, checked in by Don-vip, 6 years ago

see #15310 - remove most of deprecated APIs

  • Property svn:eol-style set to native
File size: 12.2 KB
Line 
1// License: GPL. For details, see LICENSE file.
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.Comparator;
10import java.util.Date;
11import java.util.List;
12import java.util.Map.Entry;
13
14import org.openstreetmap.josm.data.DataSource;
15import org.openstreetmap.josm.data.coor.LatLon;
16import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
17import org.openstreetmap.josm.data.osm.AbstractPrimitive;
18import org.openstreetmap.josm.data.osm.Changeset;
19import org.openstreetmap.josm.data.osm.DataSet;
20import org.openstreetmap.josm.data.osm.DataSet.UploadPolicy;
21import org.openstreetmap.josm.data.osm.INode;
22import org.openstreetmap.josm.data.osm.IPrimitive;
23import org.openstreetmap.josm.data.osm.IRelation;
24import org.openstreetmap.josm.data.osm.IWay;
25import org.openstreetmap.josm.data.osm.Node;
26import org.openstreetmap.josm.data.osm.OsmPrimitive;
27import org.openstreetmap.josm.data.osm.Relation;
28import org.openstreetmap.josm.data.osm.Tagged;
29import org.openstreetmap.josm.data.osm.Way;
30import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
31import org.openstreetmap.josm.tools.date.DateUtils;
32
33/**
34 * Save the dataset into a stream as osm intern xml format. This is not using any xml library for storing.
35 * @author imi
36 * @since 59
37 */
38public class OsmWriter extends XmlWriter implements PrimitiveVisitor {
39
40 /** Default OSM API version */
41 public static final String DEFAULT_API_VERSION = "0.6";
42
43 private final boolean osmConform;
44 private boolean withBody = true;
45 private boolean withVisible = true;
46 private boolean isOsmChange;
47 private String version;
48 private Changeset changeset;
49
50 /**
51 * Constructs a new {@code OsmWriter}.
52 * Do not call this directly. Use {@link OsmWriterFactory} instead.
53 * @param out print writer
54 * @param osmConform if {@code true}, prevents modification attributes to be written to the common part
55 * @param version OSM API version (0.6)
56 */
57 protected OsmWriter(PrintWriter out, boolean osmConform, String version) {
58 super(out);
59 this.osmConform = osmConform;
60 this.version = version == null ? DEFAULT_API_VERSION : version;
61 }
62
63 /**
64 * Sets whether body must be written.
65 * @param wb if {@code true} body will be written.
66 */
67 public void setWithBody(boolean wb) {
68 this.withBody = wb;
69 }
70
71 /**
72 * Sets whether 'visible' attribute must be written.
73 * @param wv if {@code true} 'visible' attribute will be written.
74 * @since 12019
75 */
76 public void setWithVisible(boolean wv) {
77 this.withVisible = wv;
78 }
79
80 public void setIsOsmChange(boolean isOsmChange) {
81 this.isOsmChange = isOsmChange;
82 }
83
84 public void setChangeset(Changeset cs) {
85 this.changeset = cs;
86 }
87
88 public void setVersion(String v) {
89 this.version = v;
90 }
91
92 public void header() {
93 header(UploadPolicy.NORMAL);
94 }
95
96 public void header(UploadPolicy upload) {
97 out.println("<?xml version='1.0' encoding='UTF-8'?>");
98 out.print("<osm version='");
99 out.print(version);
100 if (upload != null && upload != UploadPolicy.NORMAL) {
101 out.print("' upload='");
102 out.print(upload.getXmlFlag());
103 }
104 out.println("' generator='JOSM'>");
105 }
106
107 public void footer() {
108 out.println("</osm>");
109 }
110
111 /**
112 * Sorts {@code -1} &rarr; {@code -infinity}, then {@code +1} &rarr; {@code +infinity}
113 */
114 protected static final Comparator<AbstractPrimitive> byIdComparator = (o1, o2) -> {
115 final long i1 = o1.getUniqueId();
116 final long i2 = o2.getUniqueId();
117 if (i1 < 0 && i2 < 0) {
118 return Long.compare(i2, i1);
119 } else {
120 return Long.compare(i1, i2);
121 }
122 };
123
124 protected <T extends OsmPrimitive> Collection<T> sortById(Collection<T> primitives) {
125 List<T> result = new ArrayList<>(primitives.size());
126 result.addAll(primitives);
127 result.sort(byIdComparator);
128 return result;
129 }
130
131 /**
132 * Writes the full OSM file for the given data set (header, data sources, osm data, footer).
133 * @param data OSM data set
134 * @since 12800
135 */
136 public void write(DataSet data) {
137 header(data.getUploadPolicy());
138 writeDataSources(data);
139 writeContent(data);
140 footer();
141 }
142
143 /**
144 * Writes the contents of the given dataset (nodes, then ways, then relations)
145 * @param ds The dataset to write
146 */
147 public void writeContent(DataSet ds) {
148 setWithVisible(UploadPolicy.NORMAL.equals(ds.getUploadPolicy()));
149 writeNodes(ds.getNodes());
150 writeWays(ds.getWays());
151 writeRelations(ds.getRelations());
152 }
153
154 /**
155 * Writes the given nodes sorted by id
156 * @param nodes The nodes to write
157 * @since 5737
158 */
159 public void writeNodes(Collection<Node> nodes) {
160 for (Node n : sortById(nodes)) {
161 if (shouldWrite(n)) {
162 visit(n);
163 }
164 }
165 }
166
167 /**
168 * Writes the given ways sorted by id
169 * @param ways The ways to write
170 * @since 5737
171 */
172 public void writeWays(Collection<Way> ways) {
173 for (Way w : sortById(ways)) {
174 if (shouldWrite(w)) {
175 visit(w);
176 }
177 }
178 }
179
180 /**
181 * Writes the given relations sorted by id
182 * @param relations The relations to write
183 * @since 5737
184 */
185 public void writeRelations(Collection<Relation> relations) {
186 for (Relation r : sortById(relations)) {
187 if (shouldWrite(r)) {
188 visit(r);
189 }
190 }
191 }
192
193 protected boolean shouldWrite(OsmPrimitive osm) {
194 return !osm.isNewOrUndeleted() || !osm.isDeleted();
195 }
196
197 public void writeDataSources(DataSet ds) {
198 for (DataSource s : ds.getDataSources()) {
199 out.println(" <bounds minlat='"
200 + DecimalDegreesCoordinateFormat.INSTANCE.latToString(s.bounds.getMin())
201 +"' minlon='"
202 + DecimalDegreesCoordinateFormat.INSTANCE.lonToString(s.bounds.getMin())
203 +"' maxlat='"
204 + DecimalDegreesCoordinateFormat.INSTANCE.latToString(s.bounds.getMax())
205 +"' maxlon='"
206 + DecimalDegreesCoordinateFormat.INSTANCE.lonToString(s.bounds.getMax())
207 +"' origin='"+XmlWriter.encode(s.origin)+"' />");
208 }
209 }
210
211 void writeLatLon(LatLon ll) {
212 if (ll != null) {
213 out.print(" lat='"+LatLon.cDdHighPecisionFormatter.format(ll.lat())+
214 "' lon='"+LatLon.cDdHighPecisionFormatter.format(ll.lon())+'\'');
215 }
216 }
217
218 @Override
219 public void visit(INode n) {
220 if (n.isIncomplete()) return;
221 addCommon(n, "node");
222 if (!withBody) {
223 out.println("/>");
224 } else {
225 writeLatLon(n.getCoor());
226 addTags(n, "node", true);
227 }
228 }
229
230 @Override
231 public void visit(IWay w) {
232 if (w.isIncomplete()) return;
233 addCommon(w, "way");
234 if (!withBody) {
235 out.println("/>");
236 } else {
237 out.println(">");
238 for (int i = 0; i < w.getNodesCount(); ++i) {
239 out.println(" <nd ref='"+w.getNodeId(i) +"' />");
240 }
241 addTags(w, "way", false);
242 }
243 }
244
245 @Override
246 public void visit(IRelation e) {
247 if (e.isIncomplete()) return;
248 addCommon(e, "relation");
249 if (!withBody) {
250 out.println("/>");
251 } else {
252 out.println(">");
253 for (int i = 0; i < e.getMembersCount(); ++i) {
254 out.print(" <member type='");
255 out.print(e.getMemberType(i).getAPIName());
256 out.println("' ref='"+e.getMemberId(i)+"' role='" +
257 XmlWriter.encode(e.getRole(i)) + "' />");
258 }
259 addTags(e, "relation", false);
260 }
261 }
262
263 public void visit(Changeset cs) {
264 out.print(" <changeset id='"+cs.getId()+'\'');
265 if (cs.getUser() != null) {
266 out.print(" user='"+ XmlWriter.encode(cs.getUser().getName()) +'\'');
267 out.print(" uid='"+cs.getUser().getId() +'\'');
268 }
269 Date createdDate = cs.getCreatedAt();
270 if (createdDate != null) {
271 out.print(" created_at='"+DateUtils.fromDate(createdDate) +'\'');
272 }
273 Date closedDate = cs.getClosedAt();
274 if (closedDate != null) {
275 out.print(" closed_at='"+DateUtils.fromDate(closedDate) +'\'');
276 }
277 out.print(" open='"+ (cs.isOpen() ? "true" : "false") +'\'');
278 if (cs.getMin() != null) {
279 out.print(" min_lon='"+ DecimalDegreesCoordinateFormat.INSTANCE.lonToString(cs.getMin()) +'\'');
280 out.print(" min_lat='"+ DecimalDegreesCoordinateFormat.INSTANCE.latToString(cs.getMin()) +'\'');
281 }
282 if (cs.getMax() != null) {
283 out.print(" max_lon='"+ DecimalDegreesCoordinateFormat.INSTANCE.lonToString(cs.getMin()) +'\'');
284 out.print(" max_lat='"+ DecimalDegreesCoordinateFormat.INSTANCE.latToString(cs.getMin()) +'\'');
285 }
286 out.println(">");
287 addTags(cs, "changeset", false); // also writes closing </changeset>
288 }
289
290 protected static final Comparator<Entry<String, String>> byKeyComparator = (o1, o2) -> o1.getKey().compareTo(o2.getKey());
291
292 protected void addTags(Tagged osm, String tagname, boolean tagOpen) {
293 if (osm.hasKeys()) {
294 if (tagOpen) {
295 out.println(">");
296 }
297 List<Entry<String, String>> entries = new ArrayList<>(osm.getKeys().entrySet());
298 entries.sort(byKeyComparator);
299 for (Entry<String, String> e : entries) {
300 out.println(" <tag k='"+ XmlWriter.encode(e.getKey()) +
301 "' v='"+XmlWriter.encode(e.getValue())+ "' />");
302 }
303 out.println(" </" + tagname + '>');
304 } else if (tagOpen) {
305 out.println(" />");
306 } else {
307 out.println(" </" + tagname + '>');
308 }
309 }
310
311 /**
312 * Add the common part as the form of the tag as well as the XML attributes
313 * id, action, user, and visible.
314 * @param osm osm primitive
315 * @param tagname XML tag matching osm primitive (node, way, relation)
316 */
317 protected void addCommon(IPrimitive osm, String tagname) {
318 out.print(" <"+tagname);
319 if (osm.getUniqueId() != 0) {
320 out.print(" id='"+ osm.getUniqueId()+'\'');
321 } else
322 throw new IllegalStateException(tr("Unexpected id 0 for osm primitive found"));
323 if (!isOsmChange) {
324 if (!osmConform) {
325 String action = null;
326 if (osm.isDeleted()) {
327 action = "delete";
328 } else if (osm.isModified()) {
329 action = "modify";
330 }
331 if (action != null) {
332 out.print(" action='"+action+'\'');
333 }
334 }
335 if (!osm.isTimestampEmpty()) {
336 out.print(" timestamp='"+DateUtils.fromTimestamp(osm.getRawTimestamp())+'\'');
337 }
338 // user and visible added with 0.4 API
339 if (osm.getUser() != null) {
340 if (osm.getUser().isLocalUser()) {
341 out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+'\'');
342 } else if (osm.getUser().isOsmUser()) {
343 // uid added with 0.6
344 out.print(" uid='"+ osm.getUser().getId()+'\'');
345 out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+'\'');
346 }
347 }
348 if (withVisible) {
349 out.print(" visible='"+osm.isVisible()+'\'');
350 }
351 }
352 if (osm.getVersion() != 0) {
353 out.print(" version='"+osm.getVersion()+'\'');
354 }
355 if (this.changeset != null && this.changeset.getId() != 0) {
356 out.print(" changeset='"+this.changeset.getId()+'\'');
357 } else if (osm.getChangesetId() > 0 && !osm.isNew()) {
358 out.print(" changeset='"+osm.getChangesetId()+'\'');
359 }
360 }
361}
Note: See TracBrowser for help on using the repository browser.