source: osm/applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideData.java@ 35601

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

upgrade to JOSM 16548

File size: 14.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.plugins.streetside;
3
4import java.util.Arrays;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.List;
8import java.util.Objects;
9import java.util.Set;
10import java.util.concurrent.ConcurrentHashMap;
11import java.util.concurrent.CopyOnWriteArrayList;
12import java.util.stream.Collectors;
13
14import org.apache.commons.jcs3.access.CacheAccess;
15import org.openstreetmap.josm.data.Bounds;
16import org.openstreetmap.josm.data.Data;
17import org.openstreetmap.josm.data.DataSource;
18import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry;
19import org.openstreetmap.josm.gui.MainApplication;
20import org.openstreetmap.josm.gui.MapView;
21import org.openstreetmap.josm.plugins.streetside.cache.CacheUtils;
22import org.openstreetmap.josm.plugins.streetside.cache.Caches;
23import org.openstreetmap.josm.plugins.streetside.gui.StreetsideMainDialog;
24import org.openstreetmap.josm.plugins.streetside.gui.StreetsideViewerDialog;
25import org.openstreetmap.josm.plugins.streetside.gui.imageinfo.ImageInfoPanel;
26import org.openstreetmap.josm.plugins.streetside.utils.StreetsideProperties;
27
28/**
29 * Database class for all the {@link StreetsideAbstractImage} objects.
30 *
31 * @author nokutu
32 * @author renerr18 (extended for Streetside)
33 * @see StreetsideAbstractImage
34 * @see StreetsideSequence
35 */
36public class StreetsideData implements Data {
37 private final Set<StreetsideAbstractImage> images = ConcurrentHashMap.newKeySet();
38 /**
39 * The image currently selected, this is the one being shown.
40 */
41 private StreetsideAbstractImage selectedImage;
42 /**
43 * The image under the cursor.
44 */
45 private StreetsideAbstractImage highlightedImage;
46 /**
47 * All the images selected, can be more than one.
48 */
49 private final Set<StreetsideAbstractImage> multiSelectedImages = ConcurrentHashMap.newKeySet();
50 /**
51 * Listeners of the class.
52 */
53 private final List<StreetsideDataListener> listeners = new CopyOnWriteArrayList<>();
54 /**
55 * The bounds of the areas for which the pictures have been downloaded.
56 */
57 private final List<Bounds> bounds;
58
59 /**
60 * Creates a new object and adds the initial set of listeners.
61 */
62 protected StreetsideData() {
63 selectedImage = null;
64 bounds = new CopyOnWriteArrayList<>();
65
66 // Adds the basic set of listeners.
67 Arrays.stream(StreetsidePlugin.getStreetsideDataListeners()).forEach(this::addListener);
68 addListener(StreetsideViewerDialog.getInstance().getStreetsideViewerPanel());
69 addListener(StreetsideMainDialog.getInstance());
70 addListener(ImageInfoPanel.getInstance());
71 }
72
73 /**
74 * Adds an StreetsideImage to the object, and then repaints mapView.
75 *
76 * @param image The image to be added.
77 */
78 public void add(StreetsideAbstractImage image) {
79 add(image, true);
80 }
81
82 /**
83 * Adds a StreetsideImage to the object, but doesn't repaint mapView. This is
84 * needed for concurrency.
85 *
86 * @param image The image to be added.
87 * @param update Whether the map must be updated or not
88 * (updates are currently unsupported by Streetside).
89 */
90 public void add(StreetsideAbstractImage image, boolean update) {
91 images.add(image);
92 if (update) {
93 StreetsideLayer.invalidateInstance();
94 }
95 fireImagesAdded();
96 }
97
98 /**
99 * Adds a set of StreetsideImages to the object, and then repaints mapView.
100 *
101 * @param images The set of images to be added.
102 */
103 public void addAll(Collection<? extends StreetsideAbstractImage> images) {
104 addAll(images, true);
105 }
106
107 /**
108 * Adds a set of {link StreetsideAbstractImage} objects to this object.
109 *
110 * @param newImages The set of images to be added.
111 * @param update Whether the map must be updated or not.
112 */
113 public void addAll(Collection<? extends StreetsideAbstractImage> newImages, boolean update) {
114 images.addAll(newImages);
115 if (update) {
116 StreetsideLayer.invalidateInstance();
117 }
118 fireImagesAdded();
119 }
120
121 /**
122 * Adds a new listener.
123 *
124 * @param lis Listener to be added.
125 */
126 public final void addListener(final StreetsideDataListener lis) {
127 listeners.add(lis);
128 }
129
130 /**
131 * Adds a {@link StreetsideImage} object to the list of selected images, (when
132 * ctrl + click)
133 *
134 * @param image The {@link StreetsideImage} object to be added.
135 */
136 public void addMultiSelectedImage(final StreetsideAbstractImage image) {
137 if (!multiSelectedImages.contains(image)) {
138 if (getSelectedImage() == null) {
139 this.setSelectedImage(image);
140 } else {
141 multiSelectedImages.add(image);
142 }
143 }
144 StreetsideLayer.invalidateInstance();
145 }
146
147 /**
148 * Adds a set of {@code StreetsideAbstractImage} objects to the list of
149 * selected images.
150 *
151 * @param images A {@link Collection} object containing the set of images to be added.
152 */
153 public void addMultiSelectedImage(Collection<StreetsideAbstractImage> images) {
154 images.stream().filter(image -> !multiSelectedImages.contains(image)).forEach(image -> {
155 if (getSelectedImage() == null) {
156 this.setSelectedImage(image);
157 } else {
158 multiSelectedImages.add(image);
159 }
160 });
161 StreetsideLayer.invalidateInstance();
162 }
163
164 public List<Bounds> getBounds() {
165 return bounds;
166 }
167
168 /**
169 * Removes a listener.
170 *
171 * @param lis Listener to be removed.
172 */
173 public void removeListener(StreetsideDataListener lis) {
174 listeners.remove(lis);
175 }
176
177 /**
178 * Highlights the image under the cursor.
179 *
180 * @param image The image under the cursor.
181 */
182 public void setHighlightedImage(StreetsideAbstractImage image) {
183 highlightedImage = image;
184 }
185
186 /**
187 * Returns the image under the mouse cursor.
188 *
189 * @return The image under the mouse cursor.
190 */
191 public StreetsideAbstractImage getHighlightedImage() {
192 return highlightedImage;
193 }
194
195 /**
196 * Returns a Set containing all images.
197 *
198 * @return A Set object containing all images.
199 */
200 public Set<StreetsideAbstractImage> getImages() {
201 return images;
202 }
203
204 /**
205 * Returns a Set of all sequences, that the images are part of.
206 * @return all sequences that are contained in the Streetside data
207 */
208 public Set<StreetsideSequence> getSequences() {
209 return images.stream().map(StreetsideAbstractImage::getSequence).collect(Collectors.toSet());
210 }
211
212 /**
213 * Returns the StreetsideImage object that is currently selected.
214 *
215 * @return The selected StreetsideImage object.
216 */
217 public StreetsideAbstractImage getSelectedImage() {
218 return selectedImage;
219 }
220
221 private void fireImagesAdded() {
222 listeners.stream().filter(Objects::nonNull).forEach(StreetsideDataListener::imagesAdded);
223 }
224
225 /**
226 * If the selected StreetsideImage is part of a StreetsideSequence then the
227 * following visible StreetsideImage is selected. In case there is none, does
228 * nothing.
229 *
230 * @throws IllegalStateException if the selected image is null or the selected image doesn't
231 * belong to a sequence.
232 */
233 public void selectNext() {
234 selectNext(StreetsideProperties.MOVE_TO_IMG.get());
235 }
236
237 /**
238 * If the selected StreetsideImage is part of a StreetsideSequence then the
239 * following visible StreetsideImage is selected. In case there is none, does
240 * nothing.
241 *
242 * @param moveToPicture True if the view must me moved to the next picture.
243 * @throws IllegalStateException if the selected image is null or the selected image doesn't
244 * belong to a sequence.
245 */
246 public void selectNext(boolean moveToPicture) {
247 StreetsideAbstractImage tempImage = selectedImage;
248 if (selectedImage != null && selectedImage.getSequence() != null) {
249 while (tempImage.next() != null) {
250 tempImage = tempImage.next();
251 if (tempImage.isVisible()) {
252 setSelectedImage(tempImage, moveToPicture);
253 break;
254 }
255 }
256 }
257 }
258
259 /**
260 * If the selected StreetsideImage is part of a StreetsideSequence then the
261 * previous visible StreetsideImage is selected. In case there is none, does
262 * nothing.
263 *
264 * @throws IllegalStateException if the selected image is null or the selected image doesn't
265 * belong to a sequence.
266 */
267 public void selectPrevious() {
268 selectPrevious(StreetsideProperties.MOVE_TO_IMG.get());
269 }
270
271 /**
272 * If the selected StreetsideImage is part of a StreetsideSequence then the
273 * previous visible StreetsideImage is selected. In case there is none, does
274 * nothing. * @throws IllegalStateException if the selected image is null or
275 * the selected image doesn't belong to a sequence.
276 *
277 * @param moveToPicture True if the view must me moved to the previous picture.
278 * @throws IllegalStateException if the selected image is null or the selected image doesn't
279 * belong to a sequence.
280 */
281 public void selectPrevious(boolean moveToPicture) {
282 if (selectedImage != null && selectedImage.getSequence() != null) {
283 StreetsideAbstractImage tempImage = selectedImage;
284 while (tempImage.previous() != null) {
285 tempImage = tempImage.previous();
286 if (tempImage.isVisible()) {
287 setSelectedImage(tempImage, moveToPicture);
288 break;
289 }
290 }
291 }
292 }
293
294 /**
295 * Selects a new image.If the user does ctrl + click, this isn't triggered.
296 *
297 * @param image The StreetsideImage which is going to be selected
298 */
299 public void setSelectedImage(StreetsideAbstractImage image) {
300 setSelectedImage(image, false);
301 }
302
303 /**
304 * Selects a new image.If the user does ctrl+click, this isn't triggered. You
305 * can choose whether to center the view on the new image or not.
306 *
307 * @param image The {@link StreetsideImage} which is going to be selected.
308 * @param zoom True if the view must be centered on the image; false otherwise.
309 */
310 public void setSelectedImage(StreetsideAbstractImage image, boolean zoom) {
311 StreetsideAbstractImage oldImage = selectedImage;
312 selectedImage = image;
313 multiSelectedImages.clear();
314 final MapView mv = StreetsidePlugin.getMapView();
315 if (image != null) {
316 multiSelectedImages.add(image);
317 if (mv != null && image instanceof StreetsideImage) {
318 StreetsideImage streetsideImage = (StreetsideImage) image;
319
320 // Downloading thumbnails of surrounding pictures.
321 downloadSurroundingImages(streetsideImage);
322 }
323 }
324 if (mv != null && zoom && selectedImage != null) {
325 mv.zoomTo(selectedImage.getMovingLatLon());
326 }
327 fireSelectedImageChanged(oldImage, selectedImage);
328 StreetsideLayer.invalidateInstance();
329 }
330
331 /**
332 * Downloads surrounding images of this mapillary image in background threads
333 * @param streetsideImage the image for which the surrounding images should be downloaded
334 */
335 private static void downloadSurroundingImages (StreetsideImage streetsideImage) {
336 MainApplication.worker.execute(() -> {
337 final int prefetchCount = StreetsideProperties.PRE_FETCH_IMAGE_COUNT.get();
338 CacheAccess <String, BufferedImageCacheEntry> imageCache = Caches.ImageCache.getInstance().getCache();
339
340 StreetsideAbstractImage nextImage = streetsideImage.next();
341 StreetsideAbstractImage prevImage = streetsideImage.previous();
342
343 for (int i = 0; i < prefetchCount; i++) {
344 if (nextImage != null) {
345 if (nextImage instanceof StreetsideImage &&
346 imageCache.get(((StreetsideImage) nextImage).getId()) == null) {
347 CacheUtils.downloadPicture((StreetsideImage) nextImage);
348 }
349 nextImage = nextImage.next();
350 }
351 if (prevImage != null) {
352 if (prevImage instanceof StreetsideImage &&
353 imageCache.get(((StreetsideImage) prevImage).getId()) == null) {
354 CacheUtils.downloadPicture((StreetsideImage) prevImage);
355 }
356 prevImage = prevImage.previous();
357 }
358 }
359 });
360 }
361
362 /**
363 * Downloads surrounding images of this mapillary image in background threads
364 * @param streetsideImage the image for which the surrounding images should be downloaded
365 */
366 public static void downloadSurroundingCubemaps(StreetsideImage streetsideImage) {
367 MainApplication.worker.execute(() -> {
368 final int prefetchCount = StreetsideProperties.PRE_FETCH_IMAGE_COUNT.get();
369 CacheAccess<String, BufferedImageCacheEntry> imageCache = Caches.ImageCache.getInstance().getCache();
370
371 StreetsideAbstractImage nextImage = streetsideImage.next();
372 StreetsideAbstractImage prevImage = streetsideImage.previous();
373
374 for (int i = 0; i < prefetchCount; i++) {
375 if (nextImage != null) {
376 if (nextImage instanceof StreetsideImage && imageCache.get(((StreetsideImage) nextImage).getId()) == null) {
377 CacheUtils.downloadCubemap((StreetsideImage) nextImage);
378 }
379 nextImage = nextImage.next();
380 }
381 if (prevImage != null) {
382 if (prevImage instanceof StreetsideImage && imageCache.get(((StreetsideImage) prevImage).getId()) == null) {
383 CacheUtils.downloadCubemap((StreetsideImage) prevImage);
384 }
385 prevImage = prevImage.previous();
386 }
387 }
388 });
389 }
390
391 private void fireSelectedImageChanged(StreetsideAbstractImage oldImage, StreetsideAbstractImage newImage) {
392 listeners.stream().filter(Objects::nonNull).forEach(lis -> lis.selectedImageChanged(oldImage, newImage));
393 }
394
395 /**
396 * Returns a List containing all {@code StreetsideAbstractImage} objects
397 * selected with ctrl + click.
398 *
399 * @return A List object containing all the images selected.
400 */
401 public Set<StreetsideAbstractImage> getMultiSelectedImages() {
402 return multiSelectedImages;
403 }
404
405 /**
406 * Sets a new {@link Collection} object as the used set of images.
407 * Any images that are already present, are removed.
408 *
409 * @param newImages the new image list (previously set images are completely replaced)
410 */
411 public void setImages(Collection<StreetsideAbstractImage> newImages) {
412 synchronized (this) {
413 images.clear();
414 images.addAll(newImages);
415 }
416 }
417
418 @Override
419 public Collection<DataSource> getDataSources() {
420 return Collections.emptyList();
421 }
422}
Note: See TracBrowser for help on using the repository browser.