001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.osm; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.List; 007import java.util.stream.Collectors; 008 009import org.openstreetmap.josm.data.coor.EastNorth; 010import org.openstreetmap.josm.data.coor.LatLon; 011import org.openstreetmap.josm.tools.JosmRuntimeException; 012 013/** 014 * Stores primitives in quad buckets. This can be used to hold a collection of primitives, e.g. in a {@link DataSet} 015 * 016 * This class does not do any synchronization. 017 * @author Michael Zangl 018 * @since 12048 019 */ 020public class QuadBucketPrimitiveStore { 021 /** 022 * All nodes goes here, even when included in other data (ways etc). This enables the instant 023 * conversion of the whole DataSet by iterating over this data structure. 024 */ 025 private final QuadBuckets<Node> nodes = new QuadBuckets<>(); 026 027 /** 028 * All ways (Streets etc.) in the DataSet. 029 * 030 * The way nodes are stored only in the way list. 031 */ 032 private final QuadBuckets<Way> ways = new QuadBuckets<>(); 033 034 /** 035 * All relations/relationships 036 */ 037 private final Collection<Relation> relations = new ArrayList<>(); 038 039 /** 040 * Searches for nodes in the given bounding box. 041 * @param bbox the bounding box 042 * @return List of nodes in the given bbox. Can be empty but not null 043 */ 044 public List<Node> searchNodes(BBox bbox) { 045 return nodes.search(bbox); 046 } 047 048 /** 049 * Determines if the given node can be retrieved in the data set through its bounding box. Useful for dataset consistency test. 050 * For efficiency reasons this method does not lock the dataset, you have to lock it manually. 051 * 052 * @param n The node to search 053 * @return {@code true} if {@code n} ban be retrieved in this data set, {@code false} otherwise 054 * @since 7501 055 */ 056 public boolean containsNode(Node n) { 057 return nodes.contains(n); 058 } 059 060 /** 061 * Searches for ways in the given bounding box. 062 * @param bbox the bounding box 063 * @return List of ways in the given bbox. Can be empty but not null 064 */ 065 public List<Way> searchWays(BBox bbox) { 066 return ways.search(bbox); 067 } 068 069 /** 070 * Determines if the given way can be retrieved in the data set through its bounding box. Useful for dataset consistency test. 071 * For efficiency reasons this method does not lock the dataset, you have to lock it manually. 072 * 073 * @param w The way to search 074 * @return {@code true} if {@code w} ban be retrieved in this data set, {@code false} otherwise 075 * @since 7501 076 */ 077 public boolean containsWay(Way w) { 078 return ways.contains(w); 079 } 080 081 /** 082 * Searches for relations in the given bounding box. 083 * @param bbox the bounding box 084 * @return List of relations in the given bbox. Can be empty but not null 085 */ 086 public List<Relation> searchRelations(BBox bbox) { 087 // QuadBuckets might be useful here (don't forget to do reindexing after some of rm is changed) 088 return relations.stream() 089 .filter(r -> r.getBBox().intersects(bbox)) 090 .collect(Collectors.toList()); 091 } 092 093 /** 094 * Determines if the given relation can be retrieved in the data set through its bounding box. Useful for dataset consistency test. 095 * For efficiency reasons this method does not lock the dataset, you have to lock it manually. 096 * 097 * @param r The relation to search 098 * @return {@code true} if {@code r} ban be retrieved in this data set, {@code false} otherwise 099 * @since 7501 100 */ 101 public boolean containsRelation(Relation r) { 102 return relations.contains(r); 103 } 104 105 /** 106 * Adds a primitive to this quad bucket store 107 * 108 * @param primitive the primitive. 109 */ 110 public void addPrimitive(OsmPrimitive primitive) { 111 boolean success = false; 112 if (primitive instanceof Node) { 113 success = nodes.add((Node) primitive); 114 } else if (primitive instanceof Way) { 115 success = ways.add((Way) primitive); 116 } else if (primitive instanceof Relation) { 117 success = relations.add((Relation) primitive); 118 } 119 if (!success) { 120 throw new JosmRuntimeException("failed to add primitive: "+primitive); 121 } 122 } 123 124 protected void removePrimitive(OsmPrimitive primitive) { 125 boolean success = false; 126 if (primitive instanceof Node) { 127 success = nodes.remove(primitive); 128 } else if (primitive instanceof Way) { 129 success = ways.remove(primitive); 130 } else if (primitive instanceof Relation) { 131 success = relations.remove(primitive); 132 } 133 if (!success) { 134 throw new JosmRuntimeException("failed to remove primitive: "+primitive); 135 } 136 } 137 138 /** 139 * Re-index the relation after it's position was changed. 140 * @param node The node to re-index 141 * @param newCoor The new coordinates 142 * @param eastNorth The new east/north position 143 */ 144 protected void reindexNode(Node node, LatLon newCoor, EastNorth eastNorth) { 145 if (!nodes.remove(node)) 146 throw new JosmRuntimeException("Reindexing node failed to remove"); 147 node.setCoorInternal(newCoor, eastNorth); 148 if (!nodes.add(node)) 149 throw new JosmRuntimeException("Reindexing node failed to add"); 150 for (OsmPrimitive primitive: node.getReferrers()) { 151 if (primitive instanceof Way) { 152 reindexWay((Way) primitive); 153 } else { 154 reindexRelation((Relation) primitive); 155 } 156 } 157 } 158 159 /** 160 * Re-index the way after it's position was changed. 161 * @param way The way to re-index 162 */ 163 protected void reindexWay(Way way) { 164 BBox before = way.getBBox(); 165 if (!ways.remove(way)) 166 throw new JosmRuntimeException("Reindexing way failed to remove"); 167 way.updatePosition(); 168 if (!ways.add(way)) 169 throw new JosmRuntimeException("Reindexing way failed to add"); 170 if (!way.getBBox().equals(before)) { 171 for (OsmPrimitive primitive: way.getReferrers()) { 172 reindexRelation((Relation) primitive); 173 } 174 } 175 } 176 177 /** 178 * Re-index the relation after it's position was changed. 179 * @param relation The relation to re-index 180 */ 181 protected static void reindexRelation(Relation relation) { 182 BBox before = relation.getBBox(); 183 relation.updatePosition(); 184 if (!before.equals(relation.getBBox())) { 185 for (OsmPrimitive primitive: relation.getReferrers()) { 186 reindexRelation((Relation) primitive); 187 } 188 } 189 } 190 191 192 /** 193 * Removes all primitives from the this store. 194 */ 195 public void clear() { 196 nodes.clear(); 197 ways.clear(); 198 relations.clear(); 199 } 200}