source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/relations/MultipolygonCache.java@ 6362

Last change on this file since 6362 was 6362, checked in by Don-vip, 10 years ago

Checkstyle:

  • private constructors for util classes
  • final classes
  • missing "else" statements
  • import cleanup
File size: 12.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.visitor.paint.relations;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.HashMap;
7import java.util.Iterator;
8import java.util.List;
9import java.util.Map;
10
11import org.openstreetmap.josm.Main;
12import org.openstreetmap.josm.data.SelectionChangedListener;
13import org.openstreetmap.josm.data.osm.DataSet;
14import org.openstreetmap.josm.data.osm.Node;
15import org.openstreetmap.josm.data.osm.OsmPrimitive;
16import org.openstreetmap.josm.data.osm.Relation;
17import org.openstreetmap.josm.data.osm.Way;
18import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
19import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
20import org.openstreetmap.josm.data.osm.event.DataSetListener;
21import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
22import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
23import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
24import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
25import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
26import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
27import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
28import org.openstreetmap.josm.data.projection.Projection;
29import org.openstreetmap.josm.data.projection.ProjectionChangeListener;
30import org.openstreetmap.josm.gui.MapView;
31import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
32import org.openstreetmap.josm.gui.NavigatableComponent;
33import org.openstreetmap.josm.gui.layer.Layer;
34import org.openstreetmap.josm.gui.layer.OsmDataLayer;
35
36/*
37 * A memory cache for Multipolygon objects.
38 *
39 */
40public final class MultipolygonCache implements DataSetListener, LayerChangeListener, ProjectionChangeListener, SelectionChangedListener {
41
42 private static final MultipolygonCache instance = new MultipolygonCache();
43
44 private final Map<NavigatableComponent, Map<DataSet, Map<Relation, Multipolygon>>> cache;
45
46 private final Collection<PolyData> selectedPolyData;
47
48 private MultipolygonCache() {
49 this.cache = new HashMap<NavigatableComponent, Map<DataSet, Map<Relation, Multipolygon>>>();
50 this.selectedPolyData = new ArrayList<Multipolygon.PolyData>();
51 Main.addProjectionChangeListener(this);
52 DataSet.addSelectionListener(this);
53 MapView.addLayerChangeListener(this);
54 }
55
56 public static final MultipolygonCache getInstance() {
57 return instance;
58 }
59
60 public final Multipolygon get(NavigatableComponent nc, Relation r) {
61 return get(nc, r, false);
62 }
63
64 public final Multipolygon get(NavigatableComponent nc, Relation r, boolean forceRefresh) {
65 Multipolygon multipolygon = null;
66 if (nc != null && r != null) {
67 Map<DataSet, Map<Relation, Multipolygon>> map1 = cache.get(nc);
68 if (map1 == null) {
69 cache.put(nc, map1 = new HashMap<DataSet, Map<Relation, Multipolygon>>());
70 }
71 Map<Relation, Multipolygon> map2 = map1.get(r.getDataSet());
72 if (map2 == null) {
73 map1.put(r.getDataSet(), map2 = new HashMap<Relation, Multipolygon>());
74 }
75 multipolygon = map2.get(r);
76 if (multipolygon == null || forceRefresh) {
77 map2.put(r, multipolygon = new Multipolygon(r));
78 for (PolyData pd : multipolygon.getCombinedPolygons()) {
79 if (pd.selected) {
80 selectedPolyData.add(pd);
81 }
82 }
83 }
84 }
85 return multipolygon;
86 }
87
88 public final void clear(NavigatableComponent nc) {
89 Map<DataSet, Map<Relation, Multipolygon>> map = cache.remove(nc);
90 if (map != null) {
91 map.clear();
92 map = null;
93 }
94 }
95
96 public final void clear(DataSet ds) {
97 for (Map<DataSet, Map<Relation, Multipolygon>> map1 : cache.values()) {
98 Map<Relation, Multipolygon> map2 = map1.remove(ds);
99 if (map2 != null) {
100 map2.clear();
101 map2 = null;
102 }
103 }
104 }
105
106 public final void clear() {
107 cache.clear();
108 }
109
110 private final Collection<Map<Relation, Multipolygon>> getMapsFor(DataSet ds) {
111 List<Map<Relation, Multipolygon>> result = new ArrayList<Map<Relation, Multipolygon>>();
112 for (Map<DataSet, Map<Relation, Multipolygon>> map : cache.values()) {
113 Map<Relation, Multipolygon> map2 = map.get(ds);
114 if (map2 != null) {
115 result.add(map2);
116 }
117 }
118 return result;
119 }
120
121 private static final boolean isMultipolygon(OsmPrimitive p) {
122 return p instanceof Relation && ((Relation) p).isMultipolygon();
123 }
124
125 private final void updateMultipolygonsReferringTo(AbstractDatasetChangedEvent event) {
126 updateMultipolygonsReferringTo(event, event.getPrimitives(), event.getDataset());
127 }
128
129 private final void updateMultipolygonsReferringTo(
130 final AbstractDatasetChangedEvent event, Collection<? extends OsmPrimitive> primitives, DataSet ds) {
131 updateMultipolygonsReferringTo(event, primitives, ds, null);
132 }
133
134 private final Collection<Map<Relation, Multipolygon>> updateMultipolygonsReferringTo(
135 AbstractDatasetChangedEvent event, Collection<? extends OsmPrimitive> primitives,
136 DataSet ds, Collection<Map<Relation, Multipolygon>> initialMaps) {
137 Collection<Map<Relation, Multipolygon>> maps = initialMaps;
138 if (primitives != null) {
139 for (OsmPrimitive p : primitives) {
140 if (isMultipolygon(p)) {
141 if (maps == null) {
142 maps = getMapsFor(ds);
143 }
144 processEvent(event, (Relation) p, maps);
145
146 } else if (p instanceof Way && p.getDataSet() != null) {
147 for (OsmPrimitive ref : p.getReferrers()) {
148 if (isMultipolygon(ref)) {
149 if (maps == null) {
150 maps = getMapsFor(ds);
151 }
152 processEvent(event, (Relation) ref, maps);
153 }
154 }
155 } else if (p instanceof Node && p.getDataSet() != null) {
156 maps = updateMultipolygonsReferringTo(event, p.getReferrers(), ds, maps);
157 }
158 }
159 }
160 return maps;
161 }
162
163 private final void processEvent(AbstractDatasetChangedEvent event, Relation r, Collection<Map<Relation, Multipolygon>> maps) {
164 if (event instanceof NodeMovedEvent || event instanceof WayNodesChangedEvent) {
165 dispatchEvent(event, r, maps);
166 } else if (event instanceof PrimitivesRemovedEvent) {
167 if (event.getPrimitives().contains(r)) {
168 removeMultipolygonFrom(r, maps);
169 }
170 } else {
171 // Default (non-optimal) action: remove multipolygon from cache
172 removeMultipolygonFrom(r, maps);
173 }
174 }
175
176 private final void dispatchEvent(AbstractDatasetChangedEvent event, Relation r, Collection<Map<Relation, Multipolygon>> maps) {
177 for (Map<Relation, Multipolygon> map : maps) {
178 Multipolygon m = map.get(r);
179 if (m != null) {
180 for (PolyData pd : m.getCombinedPolygons()) {
181 if (event instanceof NodeMovedEvent) {
182 pd.nodeMoved((NodeMovedEvent) event);
183 } else if (event instanceof WayNodesChangedEvent) {
184 pd.wayNodesChanged((WayNodesChangedEvent)event);
185 }
186 }
187 }
188 }
189 }
190
191 private final void removeMultipolygonFrom(Relation r, Collection<Map<Relation, Multipolygon>> maps) {
192 for (Map<Relation, Multipolygon> map : maps) {
193 map.remove(r);
194 }
195 }
196
197 @Override
198 public void primitivesAdded(PrimitivesAddedEvent event) {
199 // Do nothing
200 }
201
202 @Override
203 public void primitivesRemoved(PrimitivesRemovedEvent event) {
204 updateMultipolygonsReferringTo(event);
205 }
206
207 @Override
208 public void tagsChanged(TagsChangedEvent event) {
209 // Do nothing
210 }
211
212 @Override
213 public void nodeMoved(NodeMovedEvent event) {
214 updateMultipolygonsReferringTo(event);
215 }
216
217 @Override
218 public void wayNodesChanged(WayNodesChangedEvent event) {
219 updateMultipolygonsReferringTo(event);
220 }
221
222 @Override
223 public void relationMembersChanged(RelationMembersChangedEvent event) {
224 updateMultipolygonsReferringTo(event);
225 }
226
227 @Override
228 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
229 // Do nothing
230 }
231
232 @Override
233 public void dataChanged(DataChangedEvent event) {
234 // Do not call updateMultipolygonsReferringTo as getPrimitives()
235 // can return all the data set primitives for this event
236 Collection<Map<Relation, Multipolygon>> maps = null;
237 for (OsmPrimitive p : event.getPrimitives()) {
238 if (isMultipolygon(p)) {
239 if (maps == null) {
240 maps = getMapsFor(event.getDataset());
241 }
242 for (Map<Relation, Multipolygon> map : maps) {
243 // DataChangedEvent is sent after downloading incomplete members (see #7131),
244 // without having received RelationMembersChangedEvent or PrimitivesAddedEvent
245 // OR when undoing a move of a large number of nodes (see #7195),
246 // without having received NodeMovedEvent
247 // This ensures concerned multipolygons will be correctly redrawn
248 map.remove(p);
249 }
250 }
251 }
252 }
253
254 @Override
255 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
256 // Do nothing
257 }
258
259 @Override
260 public void layerAdded(Layer newLayer) {
261 // Do nothing
262 }
263
264 @Override
265 public void layerRemoved(Layer oldLayer) {
266 if (oldLayer instanceof OsmDataLayer) {
267 clear(((OsmDataLayer) oldLayer).data);
268 }
269 }
270
271 @Override
272 public void projectionChanged(Projection oldValue, Projection newValue) {
273 clear();
274 }
275
276 @Override
277 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
278
279 for (Iterator<PolyData> it = selectedPolyData.iterator(); it.hasNext();) {
280 it.next().selected = false;
281 it.remove();
282 }
283
284 DataSet ds = null;
285 Collection<Map<Relation, Multipolygon>> maps = null;
286 for (OsmPrimitive p : newSelection) {
287 if (p instanceof Way && p.getDataSet() != null) {
288 if (ds == null) {
289 ds = p.getDataSet();
290 }
291 for (OsmPrimitive ref : p.getReferrers()) {
292 if (isMultipolygon(ref)) {
293 if (maps == null) {
294 maps = getMapsFor(ds);
295 }
296 for (Map<Relation, Multipolygon> map : maps) {
297 Multipolygon multipolygon = map.get(ref);
298 if (multipolygon != null) {
299 for (PolyData pd : multipolygon.getCombinedPolygons()) {
300 if (pd.getWayIds().contains(p.getUniqueId())) {
301 pd.selected = true;
302 selectedPolyData.add(pd);
303 }
304 }
305 }
306 }
307 }
308 }
309 }
310 }
311 }
312}
Note: See TracBrowser for help on using the repository browser.