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

Last change on this file since 11856 was 11709, checked in by bastiK, 7 years ago

fixed #12731 - Add an option to completely prevent upload of a layer : e.g. "never" to upload=true/false

to set this option, add XML attribute upload='never' to .osm file

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