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

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

see #15182 - deprecate Main.getLayerManager(). Replacement: gui.MainApplication.getLayerManager()

  • Property svn:eol-style set to native
File size: 12.2 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.Iterator;
7import java.util.List;
8import java.util.Map;
9import java.util.concurrent.ConcurrentHashMap;
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.MainApplication;
31import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
32import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
33import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
34import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
35import org.openstreetmap.josm.gui.layer.OsmDataLayer;
36
37/**
38 * A memory cache for {@link Multipolygon} objects.
39 * @since 4623
40 */
41public final class MultipolygonCache implements DataSetListener, LayerChangeListener, ProjectionChangeListener, SelectionChangedListener {
42
43 private static final MultipolygonCache INSTANCE = new MultipolygonCache();
44
45 private final Map<DataSet, Map<Relation, Multipolygon>> cache;
46
47 private final Collection<PolyData> selectedPolyData;
48
49 private MultipolygonCache() {
50 this.cache = new ConcurrentHashMap<>(); // see ticket 11833
51 this.selectedPolyData = new ArrayList<>();
52 Main.addProjectionChangeListener(this);
53 DataSet.addSelectionListener(this);
54 MainApplication.getLayerManager().addLayerChangeListener(this);
55 }
56
57 /**
58 * Replies the unique instance.
59 * @return the unique instance
60 */
61 public static MultipolygonCache getInstance() {
62 return INSTANCE;
63 }
64
65 /**
66 * Gets a multipolygon from cache.
67 * @param r The multipolygon relation
68 * @return A multipolygon object for the given relation, or {@code null}
69 * @since 11779
70 */
71 public Multipolygon get(Relation r) {
72 return get(r, false);
73 }
74
75 /**
76 * Gets a multipolygon from cache.
77 * @param r The multipolygon relation
78 * @param forceRefresh if {@code true}, a new object will be created even of present in cache
79 * @return A multipolygon object for the given relation, or {@code null}
80 * @since 11779
81 */
82 public Multipolygon get(Relation r, boolean forceRefresh) {
83 Multipolygon multipolygon = null;
84 if (r != null) {
85 Map<Relation, Multipolygon> map2 = cache.get(r.getDataSet());
86 if (map2 == null) {
87 map2 = new ConcurrentHashMap<>();
88 cache.put(r.getDataSet(), map2);
89 }
90 multipolygon = map2.get(r);
91 if (multipolygon == null || forceRefresh) {
92 multipolygon = new Multipolygon(r);
93 map2.put(r, multipolygon);
94 for (PolyData pd : multipolygon.getCombinedPolygons()) {
95 if (pd.isSelected()) {
96 selectedPolyData.add(pd);
97 }
98 }
99 }
100 }
101 return multipolygon;
102 }
103
104 /**
105 * Clears the cache for the given dataset.
106 * @param ds the data set
107 */
108 public void clear(DataSet ds) {
109 Map<Relation, Multipolygon> map2 = cache.remove(ds);
110 if (map2 != null) {
111 map2.clear();
112 }
113 }
114
115 /**
116 * Clears the whole cache.
117 */
118 public void clear() {
119 cache.clear();
120 }
121
122 private Collection<Map<Relation, Multipolygon>> getMapsFor(DataSet ds) {
123 List<Map<Relation, Multipolygon>> result = new ArrayList<>();
124 Map<Relation, Multipolygon> map2 = cache.get(ds);
125 if (map2 != null) {
126 result.add(map2);
127 }
128 return result;
129 }
130
131 private static boolean isMultipolygon(OsmPrimitive p) {
132 return p instanceof Relation && ((Relation) p).isMultipolygon();
133 }
134
135 private void updateMultipolygonsReferringTo(AbstractDatasetChangedEvent event) {
136 updateMultipolygonsReferringTo(event, event.getPrimitives(), event.getDataset());
137 }
138
139 private void updateMultipolygonsReferringTo(
140 final AbstractDatasetChangedEvent event, Collection<? extends OsmPrimitive> primitives, DataSet ds) {
141 updateMultipolygonsReferringTo(event, primitives, ds, null);
142 }
143
144 private Collection<Map<Relation, Multipolygon>> updateMultipolygonsReferringTo(
145 AbstractDatasetChangedEvent event, Collection<? extends OsmPrimitive> primitives,
146 DataSet ds, Collection<Map<Relation, Multipolygon>> initialMaps) {
147 Collection<Map<Relation, Multipolygon>> maps = initialMaps;
148 if (primitives != null) {
149 for (OsmPrimitive p : primitives) {
150 if (isMultipolygon(p)) {
151 if (maps == null) {
152 maps = getMapsFor(ds);
153 }
154 processEvent(event, (Relation) p, maps);
155
156 } else if (p instanceof Way && p.getDataSet() != null) {
157 for (OsmPrimitive ref : p.getReferrers()) {
158 if (isMultipolygon(ref)) {
159 if (maps == null) {
160 maps = getMapsFor(ds);
161 }
162 processEvent(event, (Relation) ref, maps);
163 }
164 }
165 } else if (p instanceof Node && p.getDataSet() != null) {
166 maps = updateMultipolygonsReferringTo(event, p.getReferrers(), ds, maps);
167 }
168 }
169 }
170 return maps;
171 }
172
173 private static void processEvent(AbstractDatasetChangedEvent event, Relation r, Collection<Map<Relation, Multipolygon>> maps) {
174 if (event instanceof NodeMovedEvent || event instanceof WayNodesChangedEvent) {
175 dispatchEvent(event, r, maps);
176 } else if (event instanceof PrimitivesRemovedEvent) {
177 if (event.getPrimitives().contains(r)) {
178 removeMultipolygonFrom(r, maps);
179 }
180 } else {
181 // Default (non-optimal) action: remove multipolygon from cache
182 removeMultipolygonFrom(r, maps);
183 }
184 }
185
186 private static void dispatchEvent(AbstractDatasetChangedEvent event, Relation r, Collection<Map<Relation, Multipolygon>> maps) {
187 for (Map<Relation, Multipolygon> map : maps) {
188 Multipolygon m = map.get(r);
189 if (m != null) {
190 for (PolyData pd : m.getCombinedPolygons()) {
191 if (event instanceof NodeMovedEvent) {
192 pd.nodeMoved((NodeMovedEvent) event);
193 } else if (event instanceof WayNodesChangedEvent) {
194 final boolean oldClosedStatus = pd.isClosed();
195 pd.wayNodesChanged((WayNodesChangedEvent) event);
196 if (pd.isClosed() != oldClosedStatus) {
197 removeMultipolygonFrom(r, maps); // see ticket #13591
198 return;
199 }
200 }
201 }
202 }
203 }
204 }
205
206 private static void removeMultipolygonFrom(Relation r, Collection<Map<Relation, Multipolygon>> maps) {
207 for (Map<Relation, Multipolygon> map : maps) {
208 map.remove(r);
209 }
210 // Erase style cache for polygon members
211 for (OsmPrimitive member : r.getMemberPrimitivesList()) {
212 member.clearCachedStyle();
213 }
214 }
215
216 @Override
217 public void primitivesAdded(PrimitivesAddedEvent event) {
218 // Do nothing
219 }
220
221 @Override
222 public void primitivesRemoved(PrimitivesRemovedEvent event) {
223 updateMultipolygonsReferringTo(event);
224 }
225
226 @Override
227 public void tagsChanged(TagsChangedEvent event) {
228 updateMultipolygonsReferringTo(event);
229 }
230
231 @Override
232 public void nodeMoved(NodeMovedEvent event) {
233 updateMultipolygonsReferringTo(event);
234 }
235
236 @Override
237 public void wayNodesChanged(WayNodesChangedEvent event) {
238 updateMultipolygonsReferringTo(event);
239 }
240
241 @Override
242 public void relationMembersChanged(RelationMembersChangedEvent event) {
243 updateMultipolygonsReferringTo(event);
244 }
245
246 @Override
247 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
248 // Do nothing
249 }
250
251 @Override
252 public void dataChanged(DataChangedEvent event) {
253 // Do not call updateMultipolygonsReferringTo as getPrimitives()
254 // can return all the data set primitives for this event
255 Collection<Map<Relation, Multipolygon>> maps = null;
256 for (OsmPrimitive p : event.getPrimitives()) {
257 if (isMultipolygon(p)) {
258 if (maps == null) {
259 maps = getMapsFor(event.getDataset());
260 }
261 for (Map<Relation, Multipolygon> map : maps) {
262 // DataChangedEvent is sent after downloading incomplete members (see #7131),
263 // without having received RelationMembersChangedEvent or PrimitivesAddedEvent
264 // OR when undoing a move of a large number of nodes (see #7195),
265 // without having received NodeMovedEvent
266 // This ensures concerned multipolygons will be correctly redrawn
267 map.remove(p);
268 }
269 }
270 }
271 }
272
273 @Override
274 public void layerAdded(LayerAddEvent e) {
275 // Do nothing
276 }
277
278 @Override
279 public void layerOrderChanged(LayerOrderChangeEvent e) {
280 // Do nothing
281 }
282
283 @Override
284 public void layerRemoving(LayerRemoveEvent e) {
285 if (e.getRemovedLayer() instanceof OsmDataLayer) {
286 clear(((OsmDataLayer) e.getRemovedLayer()).data);
287 }
288 }
289
290 @Override
291 public void projectionChanged(Projection oldValue, Projection newValue) {
292 clear();
293 }
294
295 @Override
296 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
297
298 for (Iterator<PolyData> it = selectedPolyData.iterator(); it.hasNext();) {
299 it.next().setSelected(false);
300 it.remove();
301 }
302
303 DataSet ds = null;
304 Collection<Map<Relation, Multipolygon>> maps = null;
305 for (OsmPrimitive p : newSelection) {
306 if (p instanceof Way && p.getDataSet() != null) {
307 if (ds == null) {
308 ds = p.getDataSet();
309 }
310 for (OsmPrimitive ref : p.getReferrers()) {
311 if (isMultipolygon(ref)) {
312 if (maps == null) {
313 maps = getMapsFor(ds);
314 }
315 for (Map<Relation, Multipolygon> map : maps) {
316 Multipolygon multipolygon = map.get(ref);
317 if (multipolygon != null) {
318 for (PolyData pd : multipolygon.getCombinedPolygons()) {
319 if (pd.getWayIds().contains(p.getUniqueId())) {
320 pd.setSelected(true);
321 selectedPolyData.add(pd);
322 }
323 }
324 }
325 }
326 }
327 }
328 }
329 }
330 }
331}
Note: See TracBrowser for help on using the repository browser.