3333
3434import javax .swing .Action ;
3535import javax .swing .Icon ;
36+ import javax .swing .ImageIcon ;
3637
3738import org .openstreetmap .josm .actions .AutoScaleAction ;
3839import org .openstreetmap .josm .actions .ExpertToggleAction ;
4748import org .openstreetmap .josm .data .gpx .GpxData ;
4849import org .openstreetmap .josm .data .gpx .GpxImageEntry ;
4950import org .openstreetmap .josm .data .gpx .GpxTrack ;
51+ import org .openstreetmap .josm .data .imagery .street_level .IImageEntry ;
5052import org .openstreetmap .josm .data .osm .visitor .BoundingXYVisitor ;
53+ import org .openstreetmap .josm .data .preferences .NamedColorProperty ;
5154import org .openstreetmap .josm .gui .MainApplication ;
5255import org .openstreetmap .josm .gui .MapFrame ;
5356import org .openstreetmap .josm .gui .MapFrame .MapModeChangeListener ;
6265import org .openstreetmap .josm .gui .layer .JumpToMarkerActions .JumpToPreviousMarker ;
6366import org .openstreetmap .josm .gui .layer .Layer ;
6467import org .openstreetmap .josm .gui .layer .MainLayerManager .ActiveLayerChangeListener ;
68+ import org .openstreetmap .josm .gui .util .GuiHelper ;
6569import org .openstreetmap .josm .gui .util .imagery .Vector3D ;
6670import org .openstreetmap .josm .tools .ImageProvider ;
71+ import org .openstreetmap .josm .tools .ListenerList ;
6772import org .openstreetmap .josm .tools .Utils ;
6873
6974/**
7075 * Layer displaying geotagged pictures.
7176 * @since 99
7277 */
7378public class GeoImageLayer extends AbstractModifiableLayer implements
74- JumpToMarkerLayer , NavigatableComponent .ZoomChangeListener , ImageDataUpdateListener {
79+ JumpToMarkerLayer , NavigatableComponent .ZoomChangeListener , ImageDataUpdateListener ,
80+ IGeoImageLayer {
7581
7682 private static final List <Action > menuAdditions = new LinkedList <>();
7783
7884 private static volatile List <MapMode > supportedMapModes ;
7985
8086 private final ImageData data ;
87+ private final ListenerList <IGeoImageLayer .ImageChangeListener > imageChangeListeners = ListenerList .create ();
8188 GpxData gpxData ;
8289 GpxLayer gpxFauxLayer ;
8390 GpxData gpxFauxData ;
@@ -86,6 +93,19 @@ public class GeoImageLayer extends AbstractModifiableLayer implements
8693
8794 private final Icon icon = ImageProvider .get ("dialogs/geoimage/photo-marker" );
8895 private final Icon selectedIcon = ImageProvider .get ("dialogs/geoimage/photo-marker-selected" );
96+ private final Icon selectedIconNotImageViewer = generateSelectedIconNotImageViewer (this .selectedIcon );
97+
98+ private static Icon generateSelectedIconNotImageViewer (Icon selectedIcon ) {
99+ Color color = new NamedColorProperty ("geoimage.selected.not.image.viewer" , new Color (50 , 0 , 0 )).get ();
100+ BufferedImage bi = new BufferedImage (selectedIcon .getIconWidth (), selectedIcon .getIconHeight (), BufferedImage .TYPE_INT_ARGB );
101+ Graphics2D g2d = bi .createGraphics ();
102+ selectedIcon .paintIcon (null , g2d , 0 , 0 );
103+ g2d .setComposite (AlphaComposite .getInstance (AlphaComposite .SRC_ATOP , 0.5f ));
104+ g2d .setColor (color );
105+ g2d .fillRect (0 , 0 , selectedIcon .getIconWidth (), selectedIcon .getIconHeight ());
106+ g2d .dispose ();
107+ return new ImageIcon (bi );
108+ }
89109
90110 boolean useThumbs ;
91111 private final ExecutorService thumbsLoaderExecutor =
@@ -172,6 +192,14 @@ public GeoImageLayer(final List<ImageEntry> data, GpxData gpxData, final String
172192 this .useThumbs = useThumbs ;
173193 this .data .addImageDataUpdateListener (this );
174194 this .data .setLayer (this );
195+ synchronized (ImageViewerDialog .class ) {
196+ if (!ImageViewerDialog .hasInstance ()) {
197+ GuiHelper .runInEDTAndWait (ImageViewerDialog ::createInstance );
198+ }
199+ }
200+ if (getInvalidGeoImages ().size () == data .size ()) {
201+ ImageViewerDialog .getInstance ().displayImages (Collections .singletonList (this .data .getFirstImage ()));
202+ }
175203 }
176204
177205 private final class ImageMouseListener extends MouseAdapter {
@@ -232,8 +260,9 @@ public void mouseReleased(MouseEvent ev) {
232260 }
233261 } else {
234262 data .setSelectedImage (img );
235- ImageViewerDialog .getInstance ().displayImages (GeoImageLayer . this , Collections .singletonList (img ));
263+ ImageViewerDialog .getInstance ().displayImages (Collections .singletonList (img ));
236264 }
265+ GeoImageLayer .this .invalidate (); // Needed to update which image is being shown in the image viewer in the mapview
237266 }
238267 }
239268 }
@@ -247,11 +276,45 @@ public static void create(Collection<File> files, GpxLayer gpxLayer) {
247276 MainApplication .worker .execute (new ImagesLoader (files , gpxLayer ));
248277 }
249278
279+ @ Override
280+ public void clearSelection () {
281+ this .getImageData ().clearSelectedImage ();
282+ }
283+
284+ @ Override
285+ public boolean containsImage (IImageEntry <?> imageEntry ) {
286+ if (imageEntry instanceof ImageEntry ) {
287+ return this .data .getImages ().contains (imageEntry );
288+ }
289+ return false ;
290+ }
291+
250292 @ Override
251293 public Icon getIcon () {
252294 return ImageProvider .get ("dialogs/geoimage" , ImageProvider .ImageSizes .LAYER );
253295 }
254296
297+ @ Override
298+ public List <ImageEntry > getSelection () {
299+ return this .getImageData ().getSelectedImages ();
300+ }
301+
302+ @ Override
303+ public List <IImageEntry <?>> getInvalidGeoImages () {
304+ return this .getImageData ().getImages ().stream ().filter (entry -> entry .getPos () == null || entry .getExifCoor () == null
305+ || !entry .getExifCoor ().isValid () || !entry .getPos ().isValid ()).collect (toList ());
306+ }
307+
308+ @ Override
309+ public void addImageChangeListener (ImageChangeListener listener ) {
310+ this .imageChangeListeners .addListener (listener );
311+ }
312+
313+ @ Override
314+ public void removeImageChangeListener (ImageChangeListener listener ) {
315+ this .imageChangeListeners .removeListener (listener );
316+ }
317+
255318 /**
256319 * Register actions on the layer
257320 * @param addition the action to be added
@@ -451,6 +514,7 @@ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
451514 }
452515 }
453516
517+ final IImageEntry <?> currentImage = ImageViewerDialog .getCurrentImage ();
454518 for (ImageEntry e : data .getSelectedImages ()) {
455519 if (e != null && e .getPos () != null ) {
456520 Point p = mv .getPoint (e .getPos ());
@@ -465,10 +529,14 @@ public void paint(Graphics2D g, MapView mv, Bounds bounds) {
465529 if (useThumbs && e .hasThumbnail ()) {
466530 g .setColor (new Color (128 , 0 , 0 , 122 ));
467531 g .fillRect (p .x - imgDim .width / 2 , p .y - imgDim .height / 2 , imgDim .width , imgDim .height );
468- } else {
532+ } else if ( e . equals ( currentImage )) {
469533 selectedIcon .paintIcon (mv , g ,
470534 p .x - imgDim .width / 2 ,
471535 p .y - imgDim .height / 2 );
536+ } else {
537+ selectedIconNotImageViewer .paintIcon (mv , g ,
538+ p .x - imgDim .width / 2 ,
539+ p .y - imgDim .height / 2 );
472540 }
473541 }
474542 }
@@ -884,6 +952,7 @@ public void setUseThumbs(boolean useThumbs) {
884952 @ Override
885953 public void selectedImageChanged (ImageData data ) {
886954 showCurrentPhoto ();
955+ this .imageChangeListeners .fireEvent (e -> e .imageChanged (this , null , data .getSelectedImages ()));
887956 }
888957
889958 @ Override
0 commit comments