| 1 | // License: GPL. Copyright 2007 by Immanuel Scholz and others |
|---|
| 2 | package org.openstreetmap.josm.io; |
|---|
| 3 | |
|---|
| 4 | import java.io.PrintWriter; |
|---|
| 5 | import java.util.HashMap; |
|---|
| 6 | import java.util.Map.Entry; |
|---|
| 7 | |
|---|
| 8 | import org.openstreetmap.josm.data.osm.Changeset; |
|---|
| 9 | import org.openstreetmap.josm.data.osm.DataSet; |
|---|
| 10 | import org.openstreetmap.josm.data.osm.DataSource; |
|---|
| 11 | import org.openstreetmap.josm.data.osm.Node; |
|---|
| 12 | import org.openstreetmap.josm.data.osm.OsmPrimitive; |
|---|
| 13 | import org.openstreetmap.josm.data.osm.OsmPrimitiveType; |
|---|
| 14 | import org.openstreetmap.josm.data.osm.Relation; |
|---|
| 15 | import org.openstreetmap.josm.data.osm.RelationMember; |
|---|
| 16 | import org.openstreetmap.josm.data.osm.Way; |
|---|
| 17 | import org.openstreetmap.josm.data.osm.visitor.Visitor; |
|---|
| 18 | import org.openstreetmap.josm.tools.DateUtils; |
|---|
| 19 | |
|---|
| 20 | /** |
|---|
| 21 | * Save the dataset into a stream as osm intern xml format. This is not using any |
|---|
| 22 | * xml library for storing. |
|---|
| 23 | * @author imi |
|---|
| 24 | */ |
|---|
| 25 | public class OsmWriter extends XmlWriter implements Visitor { |
|---|
| 26 | |
|---|
| 27 | public final String DEFAULT_API_VERSION = "0.6"; |
|---|
| 28 | |
|---|
| 29 | /** |
|---|
| 30 | * The counter for newly created objects. Starts at -1 and goes down. |
|---|
| 31 | */ |
|---|
| 32 | private long newIdCounter = -1; |
|---|
| 33 | |
|---|
| 34 | /** |
|---|
| 35 | * All newly created ids and their primitive that uses it. This is a back reference |
|---|
| 36 | * map to allow references to use the correnct primitives. |
|---|
| 37 | */ |
|---|
| 38 | public HashMap<OsmPrimitive, Long> usedNewIds = new HashMap<OsmPrimitive, Long>(); |
|---|
| 39 | |
|---|
| 40 | private boolean osmConform; |
|---|
| 41 | private boolean withBody = true; |
|---|
| 42 | private String version; |
|---|
| 43 | private Changeset changeset; |
|---|
| 44 | |
|---|
| 45 | public OsmWriter(PrintWriter out, boolean osmConform, String version) { |
|---|
| 46 | super(out); |
|---|
| 47 | this.osmConform = osmConform; |
|---|
| 48 | this.version = (version == null ? DEFAULT_API_VERSION : version); |
|---|
| 49 | } |
|---|
| 50 | |
|---|
| 51 | public void setWithBody(boolean wb) { |
|---|
| 52 | this.withBody = wb; |
|---|
| 53 | } |
|---|
| 54 | public void setChangeset(Changeset cs) { |
|---|
| 55 | this.changeset = cs; |
|---|
| 56 | } |
|---|
| 57 | public void setVersion(String v) { |
|---|
| 58 | this.version = v; |
|---|
| 59 | } |
|---|
| 60 | |
|---|
| 61 | public void header() { |
|---|
| 62 | out.println("<?xml version='1.0' encoding='UTF-8'?>"); |
|---|
| 63 | out.print("<osm version='"); |
|---|
| 64 | out.print(version); |
|---|
| 65 | out.println("' generator='JOSM'>"); |
|---|
| 66 | } |
|---|
| 67 | public void footer() { |
|---|
| 68 | out.println("</osm>"); |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | public void writeContent(DataSet ds) { |
|---|
| 72 | for (Node n : ds.nodes) |
|---|
| 73 | if (shouldWrite(n)) { |
|---|
| 74 | visit(n); |
|---|
| 75 | } |
|---|
| 76 | for (Way w : ds.ways) |
|---|
| 77 | if (shouldWrite(w)) { |
|---|
| 78 | visit(w); |
|---|
| 79 | } |
|---|
| 80 | for (Relation e : ds.relations) |
|---|
| 81 | if (shouldWrite(e)) { |
|---|
| 82 | visit(e); |
|---|
| 83 | } |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | private boolean shouldWrite(OsmPrimitive osm) { |
|---|
| 87 | return osm.id != 0 || !osm.deleted; |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | public void writeDataSources(DataSet ds) { |
|---|
| 91 | for (DataSource s : ds.dataSources) { |
|---|
| 92 | out.println(" <bounds minlat='" |
|---|
| 93 | + s.bounds.min.lat()+"' minlon='" |
|---|
| 94 | + s.bounds.min.lon()+"' maxlat='" |
|---|
| 95 | + s.bounds.max.lat()+"' maxlon='" |
|---|
| 96 | + s.bounds.max.lon() |
|---|
| 97 | +"' origin='"+XmlWriter.encode(s.origin)+"' />"); |
|---|
| 98 | } |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | public void visit(Node n) { |
|---|
| 102 | if (n.incomplete) return; |
|---|
| 103 | addCommon(n, "node"); |
|---|
| 104 | out.print(" lat='"+n.getCoor().lat()+"' lon='"+n.getCoor().lon()+"'"); |
|---|
| 105 | if (!withBody) { |
|---|
| 106 | out.println("/>"); |
|---|
| 107 | } else { |
|---|
| 108 | addTags(n, "node", true); |
|---|
| 109 | } |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | public void visit(Way w) { |
|---|
| 113 | if (w.incomplete) return; |
|---|
| 114 | addCommon(w, "way"); |
|---|
| 115 | if (!withBody) { |
|---|
| 116 | out.println("/>"); |
|---|
| 117 | } else { |
|---|
| 118 | out.println(">"); |
|---|
| 119 | for (Node n : w.nodes) { |
|---|
| 120 | out.println(" <nd ref='"+getUsedId(n)+"' />"); |
|---|
| 121 | } |
|---|
| 122 | addTags(w, "way", false); |
|---|
| 123 | } |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | public void visit(Relation e) { |
|---|
| 127 | if (e.incomplete) return; |
|---|
| 128 | addCommon(e, "relation"); |
|---|
| 129 | if (!withBody) { |
|---|
| 130 | out.println("/>"); |
|---|
| 131 | } else { |
|---|
| 132 | out.println(">"); |
|---|
| 133 | for (RelationMember em : e.members) { |
|---|
| 134 | out.print(" <member type='"); |
|---|
| 135 | out.print(OsmPrimitiveType.from(em.member).getAPIName()); |
|---|
| 136 | out.println("' ref='"+getUsedId(em.member)+"' role='" + |
|---|
| 137 | XmlWriter.encode(em.role == null ? "" : em.role) + "' />"); |
|---|
| 138 | } |
|---|
| 139 | addTags(e, "relation", false); |
|---|
| 140 | } |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | public void visit(Changeset cs) { |
|---|
| 144 | addCommon(cs, "changeset"); |
|---|
| 145 | out.println(">\n"); |
|---|
| 146 | addTags(cs, "changeset", false); |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | public final void footer(PrintWriter out) { |
|---|
| 150 | out.println("</osm>"); |
|---|
| 151 | } |
|---|
| 152 | |
|---|
| 153 | /** |
|---|
| 154 | * Return the id for the given osm primitive (may access the usedId map) |
|---|
| 155 | */ |
|---|
| 156 | private long getUsedId(OsmPrimitive osm) { |
|---|
| 157 | if (osm.id != 0) |
|---|
| 158 | return osm.id; |
|---|
| 159 | if (usedNewIds.containsKey(osm)) |
|---|
| 160 | return usedNewIds.get(osm); |
|---|
| 161 | usedNewIds.put(osm, newIdCounter); |
|---|
| 162 | return osmConform ? 0 : newIdCounter--; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | private void addTags(OsmPrimitive osm, String tagname, boolean tagOpen) { |
|---|
| 166 | if (osm.keys != null) { |
|---|
| 167 | if (tagOpen) { |
|---|
| 168 | out.println(">"); |
|---|
| 169 | } |
|---|
| 170 | for (Entry<String, String> e : osm.keys.entrySet()) { |
|---|
| 171 | if ((osm instanceof Changeset) || !("created_by".equals(e.getKey()))) |
|---|
| 172 | out.println(" <tag k='"+ XmlWriter.encode(e.getKey()) + |
|---|
| 173 | "' v='"+XmlWriter.encode(e.getValue())+ "' />"); |
|---|
| 174 | } |
|---|
| 175 | out.println(" </" + tagname + ">"); |
|---|
| 176 | } else if (tagOpen) { |
|---|
| 177 | out.println(" />"); |
|---|
| 178 | } else { |
|---|
| 179 | out.println(" </" + tagname + ">"); |
|---|
| 180 | } |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | /** |
|---|
| 184 | * Add the common part as the form of the tag as well as the XML attributes |
|---|
| 185 | * id, action, user, and visible. |
|---|
| 186 | */ |
|---|
| 187 | private void addCommon(OsmPrimitive osm, String tagname) { |
|---|
| 188 | long id = getUsedId(osm); |
|---|
| 189 | out.print(" <"+tagname); |
|---|
| 190 | if (id != 0) { |
|---|
| 191 | out.print(" id='"+getUsedId(osm)+"'"); |
|---|
| 192 | } |
|---|
| 193 | if (!osmConform) { |
|---|
| 194 | String action = null; |
|---|
| 195 | if (osm.deleted) { |
|---|
| 196 | action = "delete"; |
|---|
| 197 | } else if (osm.modified) { |
|---|
| 198 | action = "modify"; |
|---|
| 199 | } |
|---|
| 200 | if (action != null) { |
|---|
| 201 | out.print(" action='"+action+"'"); |
|---|
| 202 | } |
|---|
| 203 | } |
|---|
| 204 | if (!osm.isTimestampEmpty()) { |
|---|
| 205 | out.print(" timestamp='"+DateUtils.fromDate(osm.getTimestamp())+"'"); |
|---|
| 206 | } |
|---|
| 207 | // user and visible added with 0.4 API |
|---|
| 208 | if (osm.user != null) { |
|---|
| 209 | out.print(" user='"+XmlWriter.encode(osm.user.name)+"'"); |
|---|
| 210 | } |
|---|
| 211 | out.print(" visible='"+osm.visible+"'"); |
|---|
| 212 | if (osm.version != -1) { |
|---|
| 213 | out.print(" version='"+osm.version+"'"); |
|---|
| 214 | } |
|---|
| 215 | if (this.changeset != null && this.changeset.id != 0) { |
|---|
| 216 | out.print(" changeset='"+this.changeset.id+"'" ); |
|---|
| 217 | } |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | public void close() { |
|---|
| 221 | out.close(); |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | public void flush() { |
|---|
| 225 | out.flush(); |
|---|
| 226 | } |
|---|
| 227 | } |
|---|