[626] | 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 |
|
---|
[2115] | 8 | import org.openstreetmap.josm.data.coor.CoordinateFormat;
|
---|
[1523] | 9 | import org.openstreetmap.josm.data.osm.Changeset;
|
---|
[626] | 10 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
| 11 | import org.openstreetmap.josm.data.osm.DataSource;
|
---|
[1523] | 12 | import org.openstreetmap.josm.data.osm.Node;
|
---|
| 13 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
[1670] | 14 | import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
---|
[626] | 15 | import org.openstreetmap.josm.data.osm.Relation;
|
---|
| 16 | import org.openstreetmap.josm.data.osm.RelationMember;
|
---|
[2115] | 17 | import org.openstreetmap.josm.data.osm.Tagged;
|
---|
[626] | 18 | import org.openstreetmap.josm.data.osm.Way;
|
---|
| 19 | import org.openstreetmap.josm.data.osm.visitor.Visitor;
|
---|
[1499] | 20 | import org.openstreetmap.josm.tools.DateUtils;
|
---|
[626] | 21 |
|
---|
| 22 | /**
|
---|
| 23 | * Save the dataset into a stream as osm intern xml format. This is not using any
|
---|
| 24 | * xml library for storing.
|
---|
| 25 | * @author imi
|
---|
| 26 | */
|
---|
| 27 | public class OsmWriter extends XmlWriter implements Visitor {
|
---|
| 28 |
|
---|
[1587] | 29 | public final String DEFAULT_API_VERSION = "0.6";
|
---|
[1670] | 30 |
|
---|
[1169] | 31 | /**
|
---|
[1523] | 32 | * The counter for newly created objects. Starts at -1 and goes down.
|
---|
[1169] | 33 | */
|
---|
| 34 | private long newIdCounter = -1;
|
---|
[1670] | 35 |
|
---|
[1169] | 36 | /**
|
---|
| 37 | * All newly created ids and their primitive that uses it. This is a back reference
|
---|
| 38 | * map to allow references to use the correnct primitives.
|
---|
| 39 | */
|
---|
| 40 | public HashMap<OsmPrimitive, Long> usedNewIds = new HashMap<OsmPrimitive, Long>();
|
---|
[626] | 41 |
|
---|
[1523] | 42 | private boolean osmConform;
|
---|
| 43 | private boolean withBody = true;
|
---|
| 44 | private String version;
|
---|
| 45 | private Changeset changeset;
|
---|
[1670] | 46 |
|
---|
[1523] | 47 | public OsmWriter(PrintWriter out, boolean osmConform, String version) {
|
---|
| 48 | super(out);
|
---|
| 49 | this.osmConform = osmConform;
|
---|
[1587] | 50 | this.version = (version == null ? DEFAULT_API_VERSION : version);
|
---|
[1523] | 51 | }
|
---|
[1670] | 52 |
|
---|
[1523] | 53 | public void setWithBody(boolean wb) {
|
---|
| 54 | this.withBody = wb;
|
---|
| 55 | }
|
---|
| 56 | public void setChangeset(Changeset cs) {
|
---|
| 57 | this.changeset = cs;
|
---|
| 58 | }
|
---|
| 59 | public void setVersion(String v) {
|
---|
| 60 | this.version = v;
|
---|
| 61 | }
|
---|
[1670] | 62 |
|
---|
[1523] | 63 | public void header() {
|
---|
| 64 | out.println("<?xml version='1.0' encoding='UTF-8'?>");
|
---|
| 65 | out.print("<osm version='");
|
---|
| 66 | out.print(version);
|
---|
| 67 | out.println("' generator='JOSM'>");
|
---|
| 68 | }
|
---|
| 69 | public void footer() {
|
---|
| 70 | out.println("</osm>");
|
---|
| 71 | }
|
---|
[626] | 72 |
|
---|
[1523] | 73 | public void writeContent(DataSet ds) {
|
---|
| 74 | for (Node n : ds.nodes)
|
---|
[1670] | 75 | if (shouldWrite(n)) {
|
---|
[1523] | 76 | visit(n);
|
---|
[1670] | 77 | }
|
---|
[1523] | 78 | for (Way w : ds.ways)
|
---|
[1670] | 79 | if (shouldWrite(w)) {
|
---|
[1523] | 80 | visit(w);
|
---|
[1670] | 81 | }
|
---|
[1523] | 82 | for (Relation e : ds.relations)
|
---|
[1670] | 83 | if (shouldWrite(e)) {
|
---|
[1523] | 84 | visit(e);
|
---|
[1670] | 85 | }
|
---|
[1169] | 86 | }
|
---|
[626] | 87 |
|
---|
[1523] | 88 | private boolean shouldWrite(OsmPrimitive osm) {
|
---|
[2273] | 89 | return !osm.isNew() || !osm.isDeleted();
|
---|
[1169] | 90 | }
|
---|
[626] | 91 |
|
---|
[1523] | 92 | public void writeDataSources(DataSet ds) {
|
---|
| 93 | for (DataSource s : ds.dataSources) {
|
---|
| 94 | out.println(" <bounds minlat='"
|
---|
| 95 | + s.bounds.min.lat()+"' minlon='"
|
---|
| 96 | + s.bounds.min.lon()+"' maxlat='"
|
---|
| 97 | + s.bounds.max.lat()+"' maxlon='"
|
---|
| 98 | + s.bounds.max.lon()
|
---|
| 99 | +"' origin='"+XmlWriter.encode(s.origin)+"' />");
|
---|
[1169] | 100 | }
|
---|
| 101 | }
|
---|
[626] | 102 |
|
---|
[1169] | 103 | public void visit(Node n) {
|
---|
| 104 | if (n.incomplete) return;
|
---|
| 105 | addCommon(n, "node");
|
---|
[1640] | 106 | out.print(" lat='"+n.getCoor().lat()+"' lon='"+n.getCoor().lon()+"'");
|
---|
[1523] | 107 | if (!withBody) {
|
---|
[1670] | 108 | out.println("/>");
|
---|
[1523] | 109 | } else {
|
---|
| 110 | addTags(n, "node", true);
|
---|
| 111 | }
|
---|
[1169] | 112 | }
|
---|
[626] | 113 |
|
---|
[1169] | 114 | public void visit(Way w) {
|
---|
| 115 | if (w.incomplete) return;
|
---|
| 116 | addCommon(w, "way");
|
---|
[1523] | 117 | if (!withBody) {
|
---|
[1670] | 118 | out.println("/>");
|
---|
[1523] | 119 | } else {
|
---|
| 120 | out.println(">");
|
---|
[1898] | 121 | for (Node n : w.getNodes()) {
|
---|
[1523] | 122 | out.println(" <nd ref='"+getUsedId(n)+"' />");
|
---|
[1670] | 123 | }
|
---|
[1523] | 124 | addTags(w, "way", false);
|
---|
| 125 | }
|
---|
[1169] | 126 | }
|
---|
[626] | 127 |
|
---|
[1169] | 128 | public void visit(Relation e) {
|
---|
| 129 | if (e.incomplete) return;
|
---|
| 130 | addCommon(e, "relation");
|
---|
[1523] | 131 | if (!withBody) {
|
---|
[1670] | 132 | out.println("/>");
|
---|
[1523] | 133 | } else {
|
---|
| 134 | out.println(">");
|
---|
[1925] | 135 | for (RelationMember em : e.getMembers()) {
|
---|
[1523] | 136 | out.print(" <member type='");
|
---|
[1938] | 137 | out.print(OsmPrimitiveType.from(em.getMember()).getAPIName());
|
---|
| 138 | out.println("' ref='"+getUsedId(em.getMember())+"' role='" +
|
---|
[1930] | 139 | XmlWriter.encode(em.getRole()) + "' />");
|
---|
[1523] | 140 | }
|
---|
| 141 | addTags(e, "relation", false);
|
---|
[1169] | 142 | }
|
---|
| 143 | }
|
---|
[626] | 144 |
|
---|
[1523] | 145 | public void visit(Changeset cs) {
|
---|
[2115] | 146 | out.print(" <changeset ");
|
---|
| 147 | out.print(" id='"+cs.getId()+"'");
|
---|
| 148 | if (cs.getUser() != null) {
|
---|
| 149 | out.print(" user='"+cs.getUser().getName() +"'");
|
---|
| 150 | out.print(" uid='"+cs.getUser().getId() +"'");
|
---|
| 151 | }
|
---|
| 152 | if (cs.getCreatedAt() != null) {
|
---|
| 153 | out.print(" created_at='"+DateUtils.fromDate(cs.getCreatedAt()) +"'");
|
---|
| 154 | }
|
---|
| 155 | if (cs.getClosedAt() != null) {
|
---|
| 156 | out.print(" closed_at='"+DateUtils.fromDate(cs.getClosedAt()) +"'");
|
---|
| 157 | }
|
---|
| 158 | out.print(" open='"+ (cs.isOpen() ? "true" : "false") +"'");
|
---|
| 159 | if (cs.getMin() != null) {
|
---|
| 160 | out.print(" min_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
|
---|
| 161 | out.print(" min_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
|
---|
| 162 | }
|
---|
| 163 | if (cs.getMax() != null) {
|
---|
| 164 | out.print(" max_lon='"+ cs.getMin().lonToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
|
---|
| 165 | out.print(" max_lat='"+ cs.getMin().latToString(CoordinateFormat.DECIMAL_DEGREES) +"'");
|
---|
| 166 | }
|
---|
| 167 | out.println(">");
|
---|
| 168 | addTags(cs, "changeset", false); // also writes closing </changeset>
|
---|
[1523] | 169 | }
|
---|
[626] | 170 |
|
---|
[1169] | 171 | /**
|
---|
| 172 | * Return the id for the given osm primitive (may access the usedId map)
|
---|
| 173 | */
|
---|
| 174 | private long getUsedId(OsmPrimitive osm) {
|
---|
[2273] | 175 | if (!osm.isNew())
|
---|
[2025] | 176 | return osm.getId();
|
---|
[1169] | 177 | if (usedNewIds.containsKey(osm))
|
---|
| 178 | return usedNewIds.get(osm);
|
---|
| 179 | usedNewIds.put(osm, newIdCounter);
|
---|
| 180 | return osmConform ? 0 : newIdCounter--;
|
---|
| 181 | }
|
---|
[626] | 182 |
|
---|
[2115] | 183 | private void addTags(Tagged osm, String tagname, boolean tagOpen) {
|
---|
[1843] | 184 | if (osm.hasKeys()) {
|
---|
[1670] | 185 | if (tagOpen) {
|
---|
[1169] | 186 | out.println(">");
|
---|
[1670] | 187 | }
|
---|
[2115] | 188 | for (Entry<String, String> e : osm.getKeys().entrySet()) {
|
---|
[2025] | 189 | if ((osm instanceof Changeset) || !("created_by".equals(e.getKey()))) {
|
---|
[1720] | 190 | out.println(" <tag k='"+ XmlWriter.encode(e.getKey()) +
|
---|
| 191 | "' v='"+XmlWriter.encode(e.getValue())+ "' />");
|
---|
[2025] | 192 | }
|
---|
[1670] | 193 | }
|
---|
[1169] | 194 | out.println(" </" + tagname + ">");
|
---|
[1670] | 195 | } else if (tagOpen) {
|
---|
[1169] | 196 | out.println(" />");
|
---|
[1670] | 197 | } else {
|
---|
[1169] | 198 | out.println(" </" + tagname + ">");
|
---|
[1670] | 199 | }
|
---|
[1169] | 200 | }
|
---|
| 201 |
|
---|
| 202 | /**
|
---|
| 203 | * Add the common part as the form of the tag as well as the XML attributes
|
---|
| 204 | * id, action, user, and visible.
|
---|
| 205 | */
|
---|
| 206 | private void addCommon(OsmPrimitive osm, String tagname) {
|
---|
[1523] | 207 | long id = getUsedId(osm);
|
---|
| 208 | out.print(" <"+tagname);
|
---|
| 209 | if (id != 0) {
|
---|
[1670] | 210 | out.print(" id='"+getUsedId(osm)+"'");
|
---|
[1523] | 211 | }
|
---|
[1169] | 212 | if (!osmConform) {
|
---|
| 213 | String action = null;
|
---|
[2025] | 214 | if (osm.isDeleted()) {
|
---|
[1169] | 215 | action = "delete";
|
---|
[2025] | 216 | } else if (osm.isModified()) {
|
---|
[1169] | 217 | action = "modify";
|
---|
[1670] | 218 | }
|
---|
| 219 | if (action != null) {
|
---|
[1169] | 220 | out.print(" action='"+action+"'");
|
---|
[1670] | 221 | }
|
---|
[1169] | 222 | }
|
---|
[1499] | 223 | if (!osm.isTimestampEmpty()) {
|
---|
| 224 | out.print(" timestamp='"+DateUtils.fromDate(osm.getTimestamp())+"'");
|
---|
[1169] | 225 | }
|
---|
| 226 | // user and visible added with 0.4 API
|
---|
[2291] | 227 | if (osm.getUser() != null) {
|
---|
| 228 | if(osm.getUser().isLocalUser()) {
|
---|
| 229 | out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+"'");
|
---|
| 230 | } else if (osm.getUser().isOsmUser()) {
|
---|
[2070] | 231 | // uid added with 0.6
|
---|
[2291] | 232 | out.print(" uid='"+ osm.getUser().getId()+"'");
|
---|
| 233 | out.print(" user='"+XmlWriter.encode(osm.getUser().getName())+"'");
|
---|
[2070] | 234 | }
|
---|
[1169] | 235 | }
|
---|
[2025] | 236 | out.print(" visible='"+osm.isVisible()+"'");
|
---|
[2070] | 237 | if (osm.getVersion() != 0) {
|
---|
| 238 | out.print(" version='"+osm.getVersion()+"'");
|
---|
[1670] | 239 | }
|
---|
[2025] | 240 | if (this.changeset != null && this.changeset.getId() != 0) {
|
---|
| 241 | out.print(" changeset='"+this.changeset.getId()+"'" );
|
---|
[1670] | 242 | }
|
---|
[1169] | 243 | }
|
---|
[1670] | 244 |
|
---|
[1524] | 245 | public void close() {
|
---|
| 246 | out.close();
|
---|
| 247 | }
|
---|
[1670] | 248 |
|
---|
[1524] | 249 | public void flush() {
|
---|
| 250 | out.flush();
|
---|
| 251 | }
|
---|
[626] | 252 | }
|
---|