| 1 | // License: GPL. See LICENSE file for details. |
|---|
| 2 | package org.openstreetmap.josm.io; |
|---|
| 3 | |
|---|
| 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
|---|
| 5 | |
|---|
| 6 | import java.util.ArrayList; |
|---|
| 7 | import java.util.Collection; |
|---|
| 8 | import java.util.HashMap; |
|---|
| 9 | import java.util.List; |
|---|
| 10 | import java.util.Map; |
|---|
| 11 | |
|---|
| 12 | import org.openstreetmap.josm.data.osm.Changeset; |
|---|
| 13 | import org.openstreetmap.josm.data.osm.DataSet; |
|---|
| 14 | import org.openstreetmap.josm.data.osm.Node; |
|---|
| 15 | import org.openstreetmap.josm.data.osm.OsmPrimitive; |
|---|
| 16 | import org.openstreetmap.josm.data.osm.OsmPrimitiveType; |
|---|
| 17 | import org.openstreetmap.josm.data.osm.PrimitiveId; |
|---|
| 18 | import org.openstreetmap.josm.data.osm.Relation; |
|---|
| 19 | import org.openstreetmap.josm.data.osm.RelationMember; |
|---|
| 20 | import org.openstreetmap.josm.data.osm.RelationMemberData; |
|---|
| 21 | import org.openstreetmap.josm.data.osm.SimplePrimitiveId; |
|---|
| 22 | import org.openstreetmap.josm.data.osm.Way; |
|---|
| 23 | |
|---|
| 24 | /** |
|---|
| 25 | * Abstract Reader, allowing other implementations than OsmReader (PbfReader in PBF plugin for example) |
|---|
| 26 | * @author Vincent |
|---|
| 27 | * |
|---|
| 28 | */ |
|---|
| 29 | public abstract class AbstractReader { |
|---|
| 30 | |
|---|
| 31 | /** |
|---|
| 32 | * The dataset to add parsed objects to. |
|---|
| 33 | */ |
|---|
| 34 | protected DataSet ds = new DataSet(); |
|---|
| 35 | |
|---|
| 36 | protected Changeset uploadChangeset; |
|---|
| 37 | |
|---|
| 38 | /** the map from external ids to read OsmPrimitives. External ids are |
|---|
| 39 | * longs too, but in contrast to internal ids negative values are used |
|---|
| 40 | * to identify primitives unknown to the OSM server |
|---|
| 41 | */ |
|---|
| 42 | protected final Map<PrimitiveId, OsmPrimitive> externalIdMap = new HashMap<PrimitiveId, OsmPrimitive>(); |
|---|
| 43 | |
|---|
| 44 | /** |
|---|
| 45 | * Data structure for the remaining way objects |
|---|
| 46 | */ |
|---|
| 47 | protected final Map<Long, Collection<Long>> ways = new HashMap<Long, Collection<Long>>(); |
|---|
| 48 | |
|---|
| 49 | /** |
|---|
| 50 | * Data structure for relation objects |
|---|
| 51 | */ |
|---|
| 52 | protected final Map<Long, Collection<RelationMemberData>> relations = new HashMap<Long, Collection<RelationMemberData>>(); |
|---|
| 53 | |
|---|
| 54 | /** |
|---|
| 55 | * Replies the parsed data set |
|---|
| 56 | * |
|---|
| 57 | * @return the parsed data set |
|---|
| 58 | */ |
|---|
| 59 | public DataSet getDataSet() { |
|---|
| 60 | return ds; |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | /** |
|---|
| 64 | * Processes the parsed nodes after parsing. Just adds them to |
|---|
| 65 | * the dataset |
|---|
| 66 | * |
|---|
| 67 | */ |
|---|
| 68 | protected void processNodesAfterParsing() { |
|---|
| 69 | for (OsmPrimitive primitive: externalIdMap.values()) { |
|---|
| 70 | if (primitive instanceof Node) { |
|---|
| 71 | this.ds.addPrimitive(primitive); |
|---|
| 72 | } |
|---|
| 73 | } |
|---|
| 74 | } |
|---|
| 75 | |
|---|
| 76 | /** |
|---|
| 77 | * Processes the ways after parsing. Rebuilds the list of nodes of each way and |
|---|
| 78 | * adds the way to the dataset |
|---|
| 79 | * |
|---|
| 80 | * @throws IllegalDataException thrown if a data integrity problem is detected |
|---|
| 81 | */ |
|---|
| 82 | protected void processWaysAfterParsing() throws IllegalDataException{ |
|---|
| 83 | for (Long externalWayId: ways.keySet()) { |
|---|
| 84 | Way w = (Way)externalIdMap.get(new SimplePrimitiveId(externalWayId, OsmPrimitiveType.WAY)); |
|---|
| 85 | List<Node> wayNodes = new ArrayList<Node>(); |
|---|
| 86 | for (long id : ways.get(externalWayId)) { |
|---|
| 87 | Node n = (Node)externalIdMap.get(new SimplePrimitiveId(id, OsmPrimitiveType.NODE)); |
|---|
| 88 | if (n == null) { |
|---|
| 89 | if (id <= 0) |
|---|
| 90 | throw new IllegalDataException ( |
|---|
| 91 | tr("Way with external ID ''{0}'' includes missing node with external ID ''{1}''.", |
|---|
| 92 | externalWayId, |
|---|
| 93 | id)); |
|---|
| 94 | // create an incomplete node if necessary |
|---|
| 95 | // |
|---|
| 96 | n = (Node)ds.getPrimitiveById(id,OsmPrimitiveType.NODE); |
|---|
| 97 | if (n == null) { |
|---|
| 98 | n = new Node(id); |
|---|
| 99 | ds.addPrimitive(n); |
|---|
| 100 | } |
|---|
| 101 | } |
|---|
| 102 | if (n.isDeleted()) { |
|---|
| 103 | System.out.println(tr("Deleted node {0} is part of way {1}", id, w.getId())); |
|---|
| 104 | } else { |
|---|
| 105 | wayNodes.add(n); |
|---|
| 106 | } |
|---|
| 107 | } |
|---|
| 108 | w.setNodes(wayNodes); |
|---|
| 109 | if (w.hasIncompleteNodes()) { |
|---|
| 110 | System.out.println(tr("Way {0} with {1} nodes has incomplete nodes because at least one node was missing in the loaded data.", |
|---|
| 111 | externalWayId, w.getNodesCount())); |
|---|
| 112 | } |
|---|
| 113 | ds.addPrimitive(w); |
|---|
| 114 | } |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | /** |
|---|
| 118 | * Completes the parsed relations with its members. |
|---|
| 119 | * |
|---|
| 120 | * @throws IllegalDataException thrown if a data integrity problem is detected, i.e. if a |
|---|
| 121 | * relation member refers to a local primitive which wasn't available in the data |
|---|
| 122 | * |
|---|
| 123 | */ |
|---|
| 124 | protected void processRelationsAfterParsing() throws IllegalDataException { |
|---|
| 125 | |
|---|
| 126 | // First add all relations to make sure that when relation reference other relation, the referenced will be already in dataset |
|---|
| 127 | for (Long externalRelationId : relations.keySet()) { |
|---|
| 128 | Relation relation = (Relation) externalIdMap.get( |
|---|
| 129 | new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION) |
|---|
| 130 | ); |
|---|
| 131 | ds.addPrimitive(relation); |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | for (Long externalRelationId : relations.keySet()) { |
|---|
| 135 | Relation relation = (Relation) externalIdMap.get( |
|---|
| 136 | new SimplePrimitiveId(externalRelationId, OsmPrimitiveType.RELATION) |
|---|
| 137 | ); |
|---|
| 138 | List<RelationMember> relationMembers = new ArrayList<RelationMember>(); |
|---|
| 139 | for (RelationMemberData rm : relations.get(externalRelationId)) { |
|---|
| 140 | OsmPrimitive primitive = null; |
|---|
| 141 | |
|---|
| 142 | // lookup the member from the map of already created primitives |
|---|
| 143 | primitive = externalIdMap.get(new SimplePrimitiveId(rm.getMemberId(), rm.getMemberType())); |
|---|
| 144 | |
|---|
| 145 | if (primitive == null) { |
|---|
| 146 | if (rm.getMemberId() <= 0) |
|---|
| 147 | // relation member refers to a primitive with a negative id which was not |
|---|
| 148 | // found in the data. This is always a data integrity problem and we abort |
|---|
| 149 | // with an exception |
|---|
| 150 | // |
|---|
| 151 | throw new IllegalDataException( |
|---|
| 152 | tr("Relation with external id ''{0}'' refers to a missing primitive with external id ''{1}''.", |
|---|
| 153 | externalRelationId, |
|---|
| 154 | rm.getMemberId())); |
|---|
| 155 | |
|---|
| 156 | // member refers to OSM primitive which was not present in the parsed data |
|---|
| 157 | // -> create a new incomplete primitive and add it to the dataset |
|---|
| 158 | // |
|---|
| 159 | primitive = ds.getPrimitiveById(rm.getMemberId(), rm.getMemberType()); |
|---|
| 160 | if (primitive == null) { |
|---|
| 161 | switch (rm.getMemberType()) { |
|---|
| 162 | case NODE: |
|---|
| 163 | primitive = new Node(rm.getMemberId()); break; |
|---|
| 164 | case WAY: |
|---|
| 165 | primitive = new Way(rm.getMemberId()); break; |
|---|
| 166 | case RELATION: |
|---|
| 167 | primitive = new Relation(rm.getMemberId()); break; |
|---|
| 168 | default: throw new AssertionError(); // can't happen |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | ds.addPrimitive(primitive); |
|---|
| 172 | externalIdMap.put(new SimplePrimitiveId(rm.getMemberId(), rm.getMemberType()), primitive); |
|---|
| 173 | } |
|---|
| 174 | } |
|---|
| 175 | if (primitive.isDeleted()) { |
|---|
| 176 | System.out.println(tr("Deleted member {0} is used by relation {1}", primitive.getId(), relation.getId())); |
|---|
| 177 | } else { |
|---|
| 178 | relationMembers.add(new RelationMember(rm.getRole(), primitive)); |
|---|
| 179 | } |
|---|
| 180 | } |
|---|
| 181 | relation.setMembers(relationMembers); |
|---|
| 182 | } |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | protected void processChangesetAfterParsing() { |
|---|
| 186 | if (uploadChangeset != null) { |
|---|
| 187 | for (Map.Entry<String, String> e : uploadChangeset.getKeys().entrySet()) { |
|---|
| 188 | ds.addChangeSetTag(e.getKey(), e.getValue()); |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | protected final void prepareDataSet() throws IllegalDataException { |
|---|
| 194 | try { |
|---|
| 195 | ds.beginUpdate(); |
|---|
| 196 | processNodesAfterParsing(); |
|---|
| 197 | processWaysAfterParsing(); |
|---|
| 198 | processRelationsAfterParsing(); |
|---|
| 199 | processChangesetAfterParsing(); |
|---|
| 200 | } finally { |
|---|
| 201 | ds.endUpdate(); |
|---|
| 202 | } |
|---|
| 203 | } |
|---|
| 204 | } |
|---|