Changeset 34329 in osm for applications/editors/josm


Ignore:
Timestamp:
2018-06-20T06:54:45+02:00 (6 years ago)
Author:
renerr18
Message:

StreetsideLayer and StreetsideData changes in correspondence with current Mapillary & JOSM versions

Location:
applications/editors/josm/plugins/MicrosoftStreetside
Files:
1 deleted
11 edited
1 copied

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/MicrosoftStreetside/.classpath

    r34321 r34329  
    11<?xml version="1.0" encoding="UTF-8"?>
    22<classpath>
    3         <!--  -classpathentry including="data/**|images/**|LICENSE|LICENSE_*" kind="src" path=""/>
    4         <classpathentry kind="src" path="src"/>
    5         <classpathentry including="**/*.po" kind="src" path="poSrc"/>
    6         <classpathentry kind="src" path="test/data"/>
    7         <classpathentry kind="src" path="test/unit"/>
     3        <classpathentry including="data/**|images/**|LICENSE|LICENSE_*" kind="src" output="bin" path="">
     4                <attributes>
     5                        <attribute name="gradle_scope" value="main"/>
     6                        <attribute name="gradle_used_by_scope" value="main,test"/>
     7                </attributes>
     8        </classpathentry>
     9        <classpathentry kind="src" output="bin" path="src">
     10                <attributes>
     11                        <attribute name="gradle_scope" value="minJosmVersion"/>
     12                        <attribute name="gradle_used_by_scope" value="minJosmVersion"/>
     13                </attributes>
     14        </classpathentry>
     15        <classpathentry kind="src" output="bin" path="test/unit">
     16                <attributes>
     17                        <attribute name="gradle_scope" value="test"/>
     18                        <attribute name="gradle_used_by_scope" value="test"/>
     19                </attributes>
     20        </classpathentry>
     21        <classpathentry including="**/*.po" kind="src" output="bin" path="poSrc">
     22                <attributes>
     23                        <attribute name="gradle_scope" value="main"/>
     24                        <attribute name="gradle_used_by_scope" value="main,test"/>
     25                </attributes>
     26        </classpathentry>
     27        <classpathentry kind="src" output="bin" path="test/data">
     28                <attributes>
     29                        <attribute name="gradle_scope" value="test"/>
     30                        <attribute name="gradle_used_by_scope" value="test"/>
     31                </attributes>
     32        </classpathentry>
    833        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
    934        <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
    10         <classpathentry kind="output" path="bin"/ -->
    11        
    12         <classpathentry including="data/**|images/**|LICENSE|LICENSE_*" kind="src" output="bin/main" path="">
    13         <attributes>
    14             <attribute name="gradle_scope" value="main"/>
    15             <attribute name="gradle_used_by_scope" value="main,test"/>
    16         </attributes>
    17     </classpathentry>
    18     <classpathentry kind="src" output="bin/minJosmVersion" path="src">
    19         <attributes>
    20             <attribute name="gradle_scope" value="minJosmVersion"/>
    21             <attribute name="gradle_used_by_scope" value="minJosmVersion"/>
    22         </attributes>
    23     </classpathentry>
    24     <classpathentry kind="src" output="bin/test" path="test/unit">
    25         <attributes>
    26             <attribute name="gradle_scope" value="test"/>
    27             <attribute name="gradle_used_by_scope" value="test"/>
    28         </attributes>
    29     </classpathentry>
    30     <classpathentry including="**/*.po" kind="src" output="bin/main" path="poSrc">
    31         <attributes>
    32             <attribute name="gradle_scope" value="main"/>
    33             <attribute name="gradle_used_by_scope" value="main,test"/>
    34         </attributes>
    35     </classpathentry>
    36     <classpathentry kind="src" output="bin/test" path="test/data">
    37         <attributes>
    38             <attribute name="gradle_scope" value="test"/>
    39             <attribute name="gradle_used_by_scope" value="test"/>
    40         </attributes>
    41     </classpathentry>
    42     <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
    43     <classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
    44     <classpathentry kind="output" path="bin/default"/>
     35        <classpathentry kind="output" path="bin/default"/>
    4536</classpath>
  • applications/editors/josm/plugins/MicrosoftStreetside/build.gradle

    r34324 r34329  
    22  id "org.sonarqube" version "2.6.2"
    33  id "org.kordamp.markdown.convert" version "1.1.0"
    4   id 'org.openstreetmap.josm' version "0.5.0"
     4  id 'org.openstreetmap.josm' version "0.4.4"
    55  id "com.github.ben-manes.versions" version "0.17.0"
    6   id 'com.github.spotbugs' version '1.6.2'
     6  id 'com.github.spotbugs' version '1.6.1'
    77  id "net.ltgt.errorprone" version "0.0.14"
    88
    99  id 'eclipse'
    10   id 'idea'
    1110  id 'jacoco'
    1211  id 'java'
     
    4140  compile 'us.monoid.web:resty:0.3.2'
    4241  testImplementation ('org.openstreetmap.josm:josm-unittest'){changing=true}
     42  testImplementation 'com.github.tomakehurst:wiremock:2.17.0'
    4343  testImplementation 'junit:junit:4.12'
    4444}
     
    7373  debugPort = 7051
    7474  manifest {
     75    // See https://floscher.github.io/gradle-josm-plugin/kdoc/current/gradle-josm-plugin/org.openstreetmap.josm.gradle.plugin.config/-josm-manifest/old-version-download-link.html
     76    //oldVersionDownloadLink 13643, 'v1.5.14', new URL("https://github.com/JOSM/Mapillary/releases/download/v1.5.14/Mapillary.jar")
     77    //oldVersionDownloadLink 13558, 'v1.5.12+pre13643', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.12%2Bpre13643/Mapillary.jar')
     78    //oldVersionDownloadLink 12987, 'v1.5.10', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.10/Mapillary.jar')
     79    //oldVersionDownloadLink 12675, 'v1.5.7', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.7/Mapillary.jar')
     80    //oldVersionDownloadLink 12128, 'v1.5.5', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.5/Mapillary.jar')
     81    //oldVersionDownloadLink 10824, 'v1.5.3', new URL('https://github.com/JOSM/Mapillary/releases/download/v1.5.3/Mapillary.jar')
    7582  }
    7683  i18n {
    77     //pathTransformer = getGithubPathTransformer('JOSM/Mapillary')
     84    pathTransformer = getGithubPathTransformer('spatialdev/MicrosoftStreetside')
    7885  }
    7986}
     
    121128  }
    122129}
    123 
  • applications/editors/josm/plugins/MicrosoftStreetside/build.xml

    r34324 r34329  
    1414        <property name="plugin.main.version" value="13860" />
    1515       
    16     <property name="plugin.author" value="renerr18" />
     16  <property name="plugin.author" value="renerr18" />
    1717        <property name="plugin.class" value="org.openstreetmap.josm.plugins.streetside.StreetsidePlugin" />
    1818        <property name="plugin.description" value="View high resolution Microsoft Streetside 360 degree imagery in JOSM." />
    1919        <property name="plugin.icon" value="images/streetside-logo-white.png" />
    20         <!--property name="plugin.link" value="https://github.com/JOSM/MicrosoftStreetside"/-->
     20        <property name="plugin.link" value="https://github.com/spatialdev/MicrosoftStreetside"/>
    2121        <property name="plugin.requires" value="apache-commons;apache-http;"/>
    2222
     
    3434        <path id="ivy.lib.path" path="ant/ivy-2.4.0.jar" />
    3535        <taskdef resource="org/apache/ivy/ant/antlib.xml" uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path" />
    36 
    37         <!-- ** include targets that all plugins have in common ** -->
    38         <import file="../build-common.xml" />
    3936
    4037        <fileset id="plugin.requires.jars" dir="${plugin.dist.dir}">
     
    6259
    6360        <target name="install-plugin" depends="clean, dist, install">
    64                 <echo>Installed MicrosoftStreetside plugin</echo>
     61                <echo>Installed Microsoft Streetside plugin</echo>
    6562        </target>
    6663
  • applications/editors/josm/plugins/MicrosoftStreetside/config/codecov/codecov.yml

    r34317 r34329  
    99  model_and_api:
    1010    paths:
    11       - src/org/openstreetmap/josm/plugins/ms-streetside-josm-plugin/utils/api/
    12       - src/org/openstreetmap/josm/plugins/ms-streetside-josm-plugin/model/
     11      - src/org/openstreetmap/josm/plugins/streetside/utils/api/
     12      - src/org/openstreetmap/josm/plugins/streetside/model/
  • applications/editors/josm/plugins/MicrosoftStreetside/config/pmd/ruleset.xml

    r34317 r34329  
    11<?xml version="1.0"?>
    2 <ruleset name="Ruleset for jms-streetside-josm-plugin"
     2<ruleset name="Ruleset for ms-streetside-josm-plugin"
    33    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
    44    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  • applications/editors/josm/plugins/MicrosoftStreetside/gradle/tool-config.gradle

    r34317 r34329  
    1 def pmdVersion = "5.8.1"
    2 def spotbugsVersion = "3.1.1"
    3 def jacocoVersion = "0.7.9"
    4 def errorproneVersion = "2.2.0"
     1def pmdVersion = "5.8.0" // TODO: Update to PMD 6
     2def spotbugsVersion = "3.1.3"
     3def jacocoVersion = "0.8.1"
     4def errorproneVersion = "2.3.1"
    55
    66// Set up ErrorProne (currently only for JDK8, until JDK9 is supported)
  • applications/editors/josm/plugins/MicrosoftStreetside/gradle/wrapper/gradle-wrapper.properties

    r34324 r34329  
    11distributionBase=GRADLE_USER_HOME
    22distributionPath=wrapper/dists
    3 distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
     3distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip
    44zipStoreBase=GRADLE_USER_HOME
    55zipStorePath=wrapper/dists
  • applications/editors/josm/plugins/MicrosoftStreetside/ivy.xml

    r34321 r34329  
    11<ivy-module version="2.0">
    22        <info organisation="org.openstreetmap.josm.plugins" module="MicrosoftStreetside" revision="0.0.1" />
    3         <!--  configurations defaultconf="default" defaultconfmapping="default->default,sources,javadoc"-->
    4         <configurations defaultconf="default" defaultconfmapping="default->default">
    5                 <conf name="default" />
     3        <configurations defaultconf="default" defaultconfmapping="default->default,sources">
     4                        <conf name="default" />
    65        </configurations>
    76        <dependencies>
  • applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideData.java

    r34317 r34329  
    5959   */
    6060  protected StreetsideData() {
    61     selectedImage = null;
    62     bounds = new CopyOnWriteArrayList<>();
     61    this.selectedImage = null;
     62    this.bounds = new CopyOnWriteArrayList<>();
    6363
    6464  // Adds the basic set of listeners.
     
    135135   */
    136136  public void addMultiSelectedImage(final StreetsideAbstractImage image) {
    137     if (!multiSelectedImages.contains(image)) {
    138       if (getSelectedImage() == null) {
     137    if (!this.multiSelectedImages.contains(image)) {
     138      if (this.getSelectedImage() == null) {
    139139        this.setSelectedImage(image);
    140140      } else {
    141         multiSelectedImages.add(image);
     141        this.multiSelectedImages.add(image);
    142142      }
    143143    }
     
    152152   */
    153153  public void addMultiSelectedImage(Collection<StreetsideAbstractImage> images) {
    154     images.stream().filter(image -> !multiSelectedImages.contains(image)).forEach(image -> {
    155       if (getSelectedImage() == null) {
     154    images.stream().filter(image -> !this.multiSelectedImages.contains(image)).forEach(image -> {
     155      if (this.getSelectedImage() == null) {
    156156        this.setSelectedImage(image);
    157157      } else {
    158         multiSelectedImages.add(image);
     158        this.multiSelectedImages.add(image);
    159159      }
    160160    });
     
    190190   */
    191191  public StreetsideAbstractImage getHighlightedImage() {
    192     return highlightedImage;
     192    return this.highlightedImage;
    193193  }
    194194
     
    317317   */
    318318  public void setSelectedImage(StreetsideAbstractImage image, boolean zoom) {
    319     StreetsideAbstractImage oldImage = selectedImage;
    320     selectedImage = image;
    321     multiSelectedImages.clear();
     319    StreetsideAbstractImage oldImage = this.selectedImage;
     320    this.selectedImage = image;
     321    this.multiSelectedImages.clear();
    322322    final MapView mv = StreetsidePlugin.getMapView();
    323323    if (image != null) {
    324       multiSelectedImages.add(image);
     324      this.multiSelectedImages.add(image);
    325325      if (mv != null && image instanceof StreetsideImage) {
    326326        StreetsideImage streetsideImage = (StreetsideImage) image;
    327327
    328328        // Downloading thumbnails of surrounding pictures.
    329         StreetsideData.downloadSurroundingImages(streetsideImage);
     329        downloadSurroundingImages(streetsideImage);
    330330      }
    331331    }
     
    333333      mv.zoomTo(selectedImage.getMovingLatLon());
    334334    }
    335     fireSelectedImageChanged(oldImage, selectedImage);
     335    fireSelectedImageChanged(oldImage, this.selectedImage);
    336336    StreetsideLayer.invalidateInstance();
    337337  }
     
    379379   */
    380380  public Set<StreetsideAbstractImage> getMultiSelectedImages() {
    381     return multiSelectedImages;
     381    return this.multiSelectedImages;
    382382  }
    383383
  • applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsideLayer.java

    r34325 r34329  
    1212import java.awt.RenderingHints;
    1313import java.awt.TexturePaint;
     14import java.awt.event.ActionEvent;
    1415import java.awt.geom.Line2D;
     16import java.awt.geom.Path2D;
    1517import java.awt.image.BufferedImage;
    1618import java.util.Comparator;
     
    1820import java.util.Optional;
    1921
     22import javax.swing.AbstractAction;
    2023import javax.swing.Action;
    2124import javax.swing.Icon;
     25import javax.swing.JComponent;
     26import javax.swing.KeyStroke;
    2227
    2328import org.openstreetmap.josm.Main;
     
    3439import org.openstreetmap.josm.gui.layer.AbstractModifiableLayer;
    3540import org.openstreetmap.josm.gui.layer.Layer;
    36 import org.openstreetmap.josm.gui.layer.LayerManager;
    3741import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
    3842import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
    3943import org.openstreetmap.josm.plugins.streetside.cache.CacheUtils;
     44import org.openstreetmap.josm.plugins.streetside.gui.StreetsideChangesetDialog;
    4045import org.openstreetmap.josm.plugins.streetside.gui.StreetsideMainDialog;
    41 import org.openstreetmap.josm.plugins.streetside.history.StreetsideRecord;
    4246import org.openstreetmap.josm.plugins.streetside.io.download.StreetsideDownloader;
    4347import org.openstreetmap.josm.plugins.streetside.io.download.StreetsideDownloader.DOWNLOAD_MODE;
     
    5155import org.openstreetmap.josm.tools.I18n;
    5256import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
     57import org.openstreetmap.josm.tools.Logging;
     58
     59import org.openstreetmap.josm.plugins.streetside.history.StreetsideRecord;
    5360
    5461/**
     
    6168ActiveLayerChangeListener, StreetsideDataListener {
    6269
    63         /** The radius of the image marker */
    64         private static final int IMG_MARKER_RADIUS = 7;
    65         /** The radius of the circular sector that indicates the camera angle */
    66         private static final int CA_INDICATOR_RADIUS = 15;
    67         /** The angle of the circular sector that indicates the camera angle */
    68         private static final int CA_INDICATOR_ANGLE = 40;
    69         /** Length of the edge of the small sign, which indicates that traffic signs have been found in an image. */
    70         private static final int TRAFFIC_SIGN_SIZE = 6;
    71         /** A third of the height of the sign, for easier calculations */
    72         private static final double TRAFFIC_SIGN_HEIGHT_3RD = Math.sqrt(
    73                         Math.pow(StreetsideLayer.TRAFFIC_SIGN_SIZE, 2) - Math.pow(StreetsideLayer.TRAFFIC_SIGN_SIZE / 2d, 2)
    74                         ) / 3;
     70  /** The radius of the image marker */
     71  private static final int IMG_MARKER_RADIUS = 7;
     72  /** The radius of the circular sector that indicates the camera angle */
     73  private static final int CA_INDICATOR_RADIUS = 15;
     74  /** The angle of the circular sector that indicates the camera angle */
     75  private static final int CA_INDICATOR_ANGLE = 40;
     76  /** Length of the edge of the small sign, which indicates that traffic signs have been found in an image. */
     77  private static final int TRAFFIC_SIGN_SIZE = 6;
     78  /** A third of the height of the sign, for easier calculations */
     79  private static final double TRAFFIC_SIGN_HEIGHT_3RD = Math.sqrt(
     80    Math.pow(TRAFFIC_SIGN_SIZE, 2) - Math.pow(TRAFFIC_SIGN_SIZE / 2d, 2)
     81  ) / 3;
    7582
    7683        private static final DataSetListenerAdapter DATASET_LISTENER =
     
    102109        }
    103110
    104         /**
    105          * Initializes the Layer.
    106          */
    107         private void init() {
    108                 final DataSet ds = MainApplication.getLayerManager().getEditDataSet();
    109                 if (ds != null) {
    110                         ds.addDataSetListener(StreetsideLayer.DATASET_LISTENER);
    111                 }
    112                 MainApplication.getLayerManager().addLayer(this);
    113                 MainApplication.getLayerManager().addActiveLayerChangeListener(this);
    114                 if (!GraphicsEnvironment.isHeadless()) {
    115                         setMode(new SelectMode());
    116                         if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.OSM_AREA) {
    117                                 StreetsideDownloader.downloadOSMArea();
    118                         }
    119                         if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.VISIBLE_AREA) {
    120                                 mode.zoomChanged();
    121                         }
    122                 }
    123                 // Does not execute when in headless mode
    124                 if (Main.main != null && !StreetsideMainDialog.getInstance().isShowing()) {
    125                         StreetsideMainDialog.getInstance().showDialog();
    126                 }
    127                 if (StreetsidePlugin.getMapView() != null) {
    128                         StreetsideMainDialog.getInstance().getStreetsideImageDisplay().repaint();
    129 
    130                         // There is no delete image action for Streetside (Mapillary functionality here removed).
    131 
    132                         //getLocationChangeset().addChangesetListener(StreetsideChangesetDialog.getInstance());
     111  /**
     112   * Initializes the Layer.
     113   */
     114  private void init() {
     115    final DataSet ds = MainApplication.getLayerManager().getEditDataSet();
     116    if (ds != null) {
     117      ds.addDataSetListener(DATASET_LISTENER);
     118    }
     119    MainApplication.getLayerManager().addActiveLayerChangeListener(this);
     120    if (!GraphicsEnvironment.isHeadless()) {
     121      setMode(new SelectMode());
     122      if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.OSM_AREA) {
     123        MainApplication.worker.execute(StreetsideDownloader::downloadOSMArea);
     124      }
     125      if (StreetsideDownloader.getMode() == DOWNLOAD_MODE.VISIBLE_AREA) {
     126        mode.zoomChanged();
     127      }
     128    }
     129    // Does not execute when in headless mode
     130    if (Main.main != null && !StreetsideMainDialog.getInstance().isShowing()) {
     131      StreetsideMainDialog.getInstance().showDialog();
     132    }
     133    if (StreetsidePlugin.getMapView() != null) {
     134      StreetsideMainDialog.getInstance().streetsideImageDisplay.repaint();
     135      /*StreetsideMainDialog.getInstance()
     136        .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
     137        .put(KeyStroke.getKeyStroke("DELETE"), "StreetsideDel");
     138      StreetsideMainDialog.getInstance().getActionMap()
     139        .put("StreetsideDel", new DeleteImageAction());*/
     140
     141                        // There is no delete image action for Streetside (Streetside functionality here removed).
     142                        getLocationChangeset().addChangesetListener(StreetsideChangesetDialog.getInstance());
    133143                }
    134144                createHatchTexture();
     
    136146        }
    137147
    138         public static void invalidateInstance() {
    139                 if (StreetsideLayer.hasInstance()) {
    140                         StreetsideLayer.getInstance().invalidate();
    141                 }
    142         }
    143 
    144         /**
    145          * Changes the mode the the given one.
    146          *
    147          * @param mode The mode that is going to be activated.
    148          */
    149         public void setMode(AbstractMode mode) {
    150                 final MapView mv = StreetsidePlugin.getMapView();
    151                 if (this.mode != null && mv != null) {
    152                         mv.removeMouseListener(this.mode);
    153                         mv.removeMouseMotionListener(this.mode);
    154                         NavigatableComponent.removeZoomChangeListener(this.mode);
    155                 }
    156                 this.mode = mode;
    157                 if (mode != null && mv != null) {
    158                         mv.setNewCursor(mode.cursor, this);
    159                         mv.addMouseListener(mode);
    160                         mv.addMouseMotionListener(mode);
    161                         NavigatableComponent.addZoomChangeListener(mode);
    162                         StreetsideUtils.updateHelpText();
    163                 }
    164         }
    165 
    166         private static synchronized void clearInstance() {
    167                 StreetsideLayer.instance = null;
    168         }
    169 
    170         /**
    171          * Returns the unique instance of this class.
    172          *
    173          * @return The unique instance of this class.
    174          */
    175         public static synchronized StreetsideLayer getInstance() {
    176                 if (StreetsideLayer.instance != null) {
    177                         if (!MainApplication.getLayerManager().containsLayer(StreetsideLayer.instance)) {
    178                                 MainApplication.getLayerManager().addLayer(StreetsideLayer.instance);
    179                         }
    180                         return StreetsideLayer.instance;
    181                 }
    182                 final StreetsideLayer layer = new StreetsideLayer();
    183                 StreetsideLayer.instance = layer;
    184                 layer.init();
    185                 return layer;
    186         }
    187 
    188         /**
    189          * @return if the unique instance of this layer is currently instantiated and added to the {@link LayerManager}
    190          */
    191         public static boolean hasInstance() {
    192                 return StreetsideLayer.instance != null && MainApplication.getLayerManager().containsLayer(StreetsideLayer.instance);
    193         }
     148  public static void invalidateInstance() {
     149    if (hasInstance()) {
     150      getInstance().invalidate();
     151    }
     152  }
     153
     154  /**
     155   * Changes the mode the the given one.
     156   *
     157   * @param mode The mode that is going to be activated.
     158   */
     159  public void setMode(AbstractMode mode) {
     160    final MapView mv = StreetsidePlugin.getMapView();
     161    if (this.mode != null && mv != null) {
     162      mv.removeMouseListener(this.mode);
     163      mv.removeMouseMotionListener(this.mode);
     164      NavigatableComponent.removeZoomChangeListener(this.mode);
     165    }
     166    this.mode = mode;
     167    if (mode != null && mv != null) {
     168      mv.setNewCursor(mode.cursor, this);
     169      mv.addMouseListener(mode);
     170      mv.addMouseMotionListener(mode);
     171      NavigatableComponent.addZoomChangeListener(mode);
     172      StreetsideUtils.updateHelpText();
     173    }
     174  }
     175
     176  private static synchronized void clearInstance() {
     177    instance = null;
     178  }
     179
     180  /**
     181   * Returns the unique instance of this class.
     182   *
     183   * @return The unique instance of this class.
     184   */
     185  public static synchronized StreetsideLayer getInstance() {
     186    if (instance != null) {
     187      return instance;
     188    }
     189    final StreetsideLayer layer = new StreetsideLayer();
     190    layer.init();
     191    instance = layer; // Only set instance field after initialization is complete
     192    return instance;
     193  }
     194
     195  /**
     196   * @return if the unique instance of this layer is currently instantiated
     197   */
     198  public static boolean hasInstance() {
     199    return instance != null;
     200  }
    194201
    195202        /**
     
    200207         */
    201208        public StreetsideData getData() {
    202                 return data;
     209                return this.data;
    203210        }
    204211
    205         /**
    206          * Returns the n-nearest image, for n=1 the nearest one is returned, for n=2 the second nearest one and so on.
    207          * The "n-nearest image" is picked from the list of one image from every sequence that is nearest to the currently
    208          * selected image, excluding the sequence to which the selected image belongs.
    209          * @param n the index for picking from the list of "nearest images", beginning from 1
    210          * @return the n-nearest image to the currently selected image
    211          */
    212         public synchronized StreetsideImage getNNearestImage(final int n) {
    213                 return n >= 1 && n <= nearestImages.length ? nearestImages[n - 1] : null;
    214         }
    215 
    216         /**
    217            * Returns the {@link StreetsideLocationChangeset} object, which acts as the database of the
    218            * Layer.
    219            *
    220            * @return The {@link StreetsideData} object that stores the database.
    221            */
    222           public StreetsideLocationChangeset getLocationChangeset() {
    223             return locationChangeset;
    224           }
     212  /**
     213   * Returns the {@link StreetsideLocationChangeset} object, which acts as the database of the
     214   * Layer.
     215   *
     216   * @return The {@link StreetsideData} object that stores the database.
     217   */
     218  public StreetsideLocationChangeset getLocationChangeset() {
     219    return locationChangeset;
     220  }
     221
     222  /**
     223   * Returns the n-nearest image, for n=1 the nearest one is returned, for n=2 the second nearest one and so on.
     224   * The "n-nearest image" is picked from the list of one image from every sequence that is nearest to the currently
     225   * selected image, excluding the sequence to which the selected image belongs.
     226   * @param n the index for picking from the list of "nearest images", beginning from 1
     227   * @return the n-nearest image to the currently selected image
     228   */
     229  public synchronized StreetsideImage getNNearestImage(final int n) {
     230    return n >= 1 && n <= nearestImages.length ? nearestImages[n - 1] : null;
     231  }
     232
     233  @Override
     234  public synchronized void destroy() {
     235    clearInstance();
     236    setMode(null);
     237    StreetsideRecord.getInstance().reset();
     238    AbstractMode.resetThread();
     239    StreetsideDownloader.stopAll();
     240    if (StreetsideMainDialog.hasInstance()) {
     241      StreetsideMainDialog.getInstance().setImage(null);
     242      StreetsideMainDialog.getInstance().updateImage();
     243    }
     244    final MapView mv = StreetsidePlugin.getMapView();
     245    if (mv != null) {
     246      mv.removeMouseListener(mode);
     247      mv.removeMouseMotionListener(mode);
     248    }
     249    try {
     250      MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
     251      if (MainApplication.getLayerManager().getEditDataSet() != null) {
     252        MainApplication.getLayerManager().getEditDataSet().removeDataSetListener(DATASET_LISTENER);
     253      }
     254    } catch (IllegalArgumentException e) {
     255      // TODO: It would be ideal, to fix this properly. But for the moment let's catch this, for when a listener has already been removed.
     256    }
     257    super.destroy();
     258  }
    225259
    226260
    227261        @Override
    228         public synchronized void destroy() {
    229                 // TODO: Add destroy code for CubemapBuilder, et al.? @rrh
    230                 StreetsideLayer.clearInstance();
    231                 setMode(null);
    232                 StreetsideRecord.getInstance().reset();
    233                 AbstractMode.resetThread();
    234                 StreetsideDownloader.stopAll();
    235                 if (StreetsideMainDialog.hasInstance()) {
    236                         StreetsideMainDialog.getInstance().setImage(null);
    237                         StreetsideMainDialog.getInstance().updateImage();
    238                 }
    239                 final MapView mv = StreetsidePlugin.getMapView();
    240                 if (mv != null) {
    241                         mv.removeMouseListener(mode);
    242                         mv.removeMouseMotionListener(mode);
    243                 }
    244                 try {
    245                         MainApplication.getLayerManager().removeActiveLayerChangeListener(this);
    246                         if (MainApplication.getLayerManager().getEditDataSet() != null) {
    247                                 MainApplication.getLayerManager().getEditDataSet().removeDataSetListener(StreetsideLayer.DATASET_LISTENER);
    248                         }
    249                 } catch (final IllegalArgumentException e) {
    250                         // TODO: It would be ideal, to fix this properly. But for the moment let's catch this, for when a listener has already been removed.
    251                 }
    252                 super.destroy();
    253         }
    254 
    255         @Override
    256         public boolean isModified() {
    257                 // TODO: Add cubemap modification here? @rrh
    258                 return data.getImages().parallelStream().anyMatch(StreetsideAbstractImage::isModified);
    259         }
    260 
    261         @Override
    262         public void setVisible(boolean visible) {
    263                 super.setVisible(visible);
    264                 getData().getImages().parallelStream().forEach(img -> img.setVisible(visible));
    265                 if (MainApplication.getMap() != null) {
    266                         //StreetsideFilterDialog.getInstance().refresh();
    267                 }
    268         }
    269 
    270         /**
    271          * Initialize the hatch pattern used to paint the non-downloaded area.
    272          */
    273         private void createHatchTexture() {
    274                 final BufferedImage bi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_ARGB);
    275                 final Graphics2D big = bi.createGraphics();
    276                 big.setColor(StreetsideProperties.BACKGROUND.get());
    277                 final Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
    278                 big.setComposite(comp);
    279                 big.fillRect(0, 0, 15, 15);
    280                 big.setColor(StreetsideProperties.OUTSIDE_DOWNLOADED_AREA.get());
    281                 big.drawLine(0, 15, 15, 0);
    282                 final Rectangle r = new Rectangle(0, 0, 15, 15);
    283                 hatched = new TexturePaint(bi, r);
    284         }
    285 
    286         @Override
    287         public synchronized void paint(final Graphics2D g, final MapView mv, final Bounds box) {
    288                 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    289                 if (MainApplication.getLayerManager().getActiveLayer() == this) {
    290                         // paint remainder
    291                         g.setPaint(hatched);
    292                         g.fill(MapViewGeometryUtil.getNonDownloadedArea(mv, data.getBounds()));
    293                 }
    294 
    295                 // Draw the blue and red line
    296                 synchronized (StreetsideLayer.class) {
    297                         final StreetsideAbstractImage selectedImg = data.getSelectedImage();
    298                         for (int i = 0; i < nearestImages.length && selectedImg != null; i++) {
    299                                 if (i == 0) {
    300                                         g.setColor(Color.RED);
    301                                 } else {
    302                                         g.setColor(Color.BLUE);
    303                                 }
    304                                 final Point selected = mv.getPoint(selectedImg.getMovingLatLon());
    305                                 final Point p = mv.getPoint(nearestImages[i].getMovingLatLon());
    306                                 g.draw(new Line2D.Double(p.getX(), p.getY(), selected.getX(), selected.getY()));
    307                         }
    308                 }
    309 
    310                 // Draw sequence line
    311                 g.setStroke(new BasicStroke(2));
    312                 final StreetsideAbstractImage selectedImage = getData().getSelectedImage();
    313                 for (final StreetsideSequence seq : getData().getSequences()) {
    314                         if (seq.getImages().contains(selectedImage)) {
    315                                 g.setColor(
    316                                                 seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED : StreetsideColorScheme.SEQ_SELECTED
    317                                                 );
    318                         } else {
    319                                 g.setColor(
    320                                                 seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED : StreetsideColorScheme.SEQ_UNSELECTED
    321                                                 );
    322                         }
    323                         g.draw(MapViewGeometryUtil.getSequencePath(mv, seq));
    324                 }
    325                 /*for (final StreetsideAbstractImage imageAbs : data.getImages()) {
    326                         if (imageAbs.isVisible() && mv != null && mv.contains(mv.getPoint(imageAbs.getMovingLatLon()))) {
    327                                 drawImageMarker(g, imageAbs);
    328                         }
    329                 }*/
    330                 if (mode instanceof JoinMode) {
    331                         mode.paint(g, mv, box);
    332                 }
    333         }
    334 
    335         /*
    336          * Draws an image marker onto the given Graphics context.
    337          * @param g the Graphics context
    338          * @param img the image to be drawn onto the Graphics context
    339          */
    340         /*private void drawImageMarker(final Graphics2D g, final StreetsideAbstractImage img) {
    341                 if (img == null || img.getLatLon() == null) {
    342                         Logging.warn("An image is not painted, because it is null or has no LatLon!");
    343                         return;
    344                 }
    345                 final StreetsideAbstractImage selectedImg = getData().getSelectedImage();
    346                 final Point p = MainApplication.getMap().mapView.getPoint(img.getMovingLatLon());
    347 
    348                 // Determine colors
    349                 final Color markerC;
    350                 final Color directionC;
    351                 if (selectedImg != null && getData().getMultiSelectedImages().contains(img)) {
    352                         markerC = img instanceof StreetsideImportedImage
    353                                         ? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED
    354                                                         : StreetsideColorScheme.SEQ_HIGHLIGHTED;
    355                         directionC = img instanceof StreetsideImportedImage
    356                                         ? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED_CA
    357                                                         : StreetsideColorScheme.SEQ_HIGHLIGHTED_CA;
    358                 } else if (selectedImg != null && selectedImg.getSequence() != null && selectedImg.getSequence().equals(img.getSequence())) {
    359                         markerC = img instanceof StreetsideImportedImage
    360                                         ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED
    361                                                         : StreetsideColorScheme.SEQ_SELECTED;
    362                         directionC = img instanceof StreetsideImportedImage
    363                                         ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED_CA
    364                                                         : StreetsideColorScheme.SEQ_SELECTED_CA;
    365                 } else {
    366                         markerC = img instanceof StreetsideImportedImage
    367                                         ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED
    368                                                         : StreetsideColorScheme.SEQ_UNSELECTED;
    369                         directionC = img instanceof StreetsideImportedImage
    370                                         ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED_CA
    371                                                         : StreetsideColorScheme.SEQ_UNSELECTED_CA;
    372                 }
    373 
    374                 // Paint direction indicator
    375                 g.setColor(directionC);
    376                 g.fillArc(p.x - StreetsideLayer.CA_INDICATOR_RADIUS, p.y - StreetsideLayer.CA_INDICATOR_RADIUS, 2 * StreetsideLayer.CA_INDICATOR_RADIUS, 2 * StreetsideLayer.CA_INDICATOR_RADIUS, (int) (90 - img.getMovingHe() - StreetsideLayer.CA_INDICATOR_ANGLE / 2d), StreetsideLayer.CA_INDICATOR_ANGLE);
    377                 // Paint image marker
    378                 g.setColor(markerC);
    379                 g.fillOval(p.x - StreetsideLayer.IMG_MARKER_RADIUS, p.y - StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS);
    380 
    381                 // Paint highlight for selected or highlighted images
    382                 if (img.equals(getData().getHighlightedImage()) || getData().getMultiSelectedImages().contains(img)) {
    383                         g.setColor(Color.WHITE);
    384                         g.setStroke(new BasicStroke(2));
    385                         g.drawOval(p.x - StreetsideLayer.IMG_MARKER_RADIUS, p.y - StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS, 2 * StreetsideLayer.IMG_MARKER_RADIUS);
    386                 }
    387 
    388                 // TODO: reimplement detections for Bing Metadata? RRH
    389                 if (img instanceof StreetsideImage && !((StreetsideImage) img).getDetections().isEmpty()) {
     262  public boolean isModified() {
     263    return this.data.getImages().parallelStream().anyMatch(StreetsideAbstractImage::isModified);
     264  }
     265
     266  @Override
     267  public void setVisible(boolean visible) {
     268    super.setVisible(visible);
     269    getData().getImages().parallelStream().forEach(img -> img.setVisible(visible));
     270    if (MainApplication.getMap() != null) {
     271      //StreetsideFilterDialog.getInstance().refresh();
     272    }
     273  }
     274
     275  /**
     276   * Initialize the hatch pattern used to paint the non-downloaded area.
     277   */
     278  private void createHatchTexture() {
     279    BufferedImage bi = new BufferedImage(15, 15, BufferedImage.TYPE_INT_ARGB);
     280    Graphics2D big = bi.createGraphics();
     281    big.setColor(StreetsideProperties.BACKGROUND.get());
     282    Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
     283    big.setComposite(comp);
     284    big.fillRect(0, 0, 15, 15);
     285    big.setColor(StreetsideProperties.OUTSIDE_DOWNLOADED_AREA.get());
     286    big.drawLine(0, 15, 15, 0);
     287    Rectangle r = new Rectangle(0, 0, 15, 15);
     288    this.hatched = new TexturePaint(bi, r);
     289  }
     290
     291  @Override
     292  public synchronized void paint(final Graphics2D g, final MapView mv, final Bounds box) {
     293    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
     294    if (MainApplication.getLayerManager().getActiveLayer() == this) {
     295      // paint remainder
     296      g.setPaint(hatched);
     297      g.fill(MapViewGeometryUtil.getNonDownloadedArea(mv, this.data.getBounds()));
     298    }
     299
     300    // Draw the blue and red line
     301    synchronized (StreetsideLayer.class) {
     302      final StreetsideAbstractImage selectedImg = data.getSelectedImage();
     303      for (int i = 0; i < nearestImages.length && selectedImg != null; i++) {
     304        if (i == 0) {
     305          g.setColor(Color.RED);
     306        } else {
     307          g.setColor(Color.BLUE);
     308        }
     309        final Point selected = mv.getPoint(selectedImg.getMovingLatLon());
     310        final Point p = mv.getPoint(nearestImages[i].getMovingLatLon());
     311        g.draw(new Line2D.Double(p.getX(), p.getY(), selected.getX(), selected.getY()));
     312      }
     313    }
     314
     315    // Draw sequence line
     316    g.setStroke(new BasicStroke(2));
     317    final StreetsideAbstractImage selectedImage = getData().getSelectedImage();
     318    for (StreetsideSequence seq : getData().getSequences()) {
     319      if (seq.getImages().contains(selectedImage)) {
     320        g.setColor(
     321          seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED : StreetsideColorScheme.SEQ_SELECTED
     322        );
     323      } else {
     324        g.setColor(
     325          seq.getId() == null ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED : StreetsideColorScheme.SEQ_UNSELECTED
     326        );
     327      }
     328      g.draw(MapViewGeometryUtil.getSequencePath(mv, seq));
     329    }
     330    for (StreetsideAbstractImage imageAbs : this.data.getImages()) {
     331      if (imageAbs.isVisible() && mv != null && mv.contains(mv.getPoint(imageAbs.getMovingLatLon()))) {
     332        drawImageMarker(g, imageAbs);
     333      }
     334    }
     335    if (this.mode instanceof JoinMode) {
     336      this.mode.paint(g, mv, box);
     337    }
     338  }
     339
     340  /**
     341   * Draws an image marker onto the given Graphics context.
     342   * @param g the Graphics context
     343   * @param img the image to be drawn onto the Graphics context
     344   */
     345  private void drawImageMarker(final Graphics2D g, final StreetsideAbstractImage img) {
     346    if (img == null || img.getLatLon() == null) {
     347      Logging.warn("An image is not painted, because it is null or has no LatLon!");
     348      return;
     349    }
     350    final StreetsideAbstractImage selectedImg = getData().getSelectedImage();
     351    final Point p = MainApplication.getMap().mapView.getPoint(img.getMovingLatLon());
     352
     353    // Determine colors
     354    final Color markerC;
     355    final Color directionC;
     356    if (selectedImg != null && getData().getMultiSelectedImages().contains(img)) {
     357      markerC = img instanceof StreetsideImportedImage
     358        ? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED
     359        : StreetsideColorScheme.SEQ_HIGHLIGHTED;
     360      directionC = img instanceof StreetsideImportedImage
     361        ? StreetsideColorScheme.SEQ_IMPORTED_HIGHLIGHTED_CA
     362        : StreetsideColorScheme.SEQ_HIGHLIGHTED_CA;
     363    } else if (selectedImg != null && selectedImg.getSequence() != null && selectedImg.getSequence().equals(img.getSequence())) {
     364      markerC = img instanceof StreetsideImportedImage
     365        ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED
     366        : StreetsideColorScheme.SEQ_SELECTED;
     367      directionC = img instanceof StreetsideImportedImage
     368        ? StreetsideColorScheme.SEQ_IMPORTED_SELECTED_CA
     369        : StreetsideColorScheme.SEQ_SELECTED_CA;
     370    } else {
     371      markerC = img instanceof StreetsideImportedImage
     372        ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED
     373        : StreetsideColorScheme.SEQ_UNSELECTED;
     374      directionC = img instanceof StreetsideImportedImage
     375        ? StreetsideColorScheme.SEQ_IMPORTED_UNSELECTED_CA
     376        : StreetsideColorScheme.SEQ_UNSELECTED_CA;
     377    }
     378
     379    // Paint direction indicator
     380    g.setColor(directionC);
     381    g.fillArc(p.x - CA_INDICATOR_RADIUS, p.y - CA_INDICATOR_RADIUS, 2 * CA_INDICATOR_RADIUS, 2 * CA_INDICATOR_RADIUS, (int) (90 - img.getMovingHe() - CA_INDICATOR_ANGLE / 2d), CA_INDICATOR_ANGLE);
     382    // Paint image marker
     383    g.setColor(markerC);
     384    g.fillOval(p.x - IMG_MARKER_RADIUS, p.y - IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS);
     385
     386    // Paint highlight for selected or highlighted images
     387    if (img.equals(getData().getHighlightedImage()) || getData().getMultiSelectedImages().contains(img)) {
     388      g.setColor(Color.WHITE);
     389      g.setStroke(new BasicStroke(2));
     390      g.drawOval(p.x - IMG_MARKER_RADIUS, p.y - IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS, 2 * IMG_MARKER_RADIUS);
     391    }
     392
     393
     394                /*if (img instanceof StreetsideImage && !((StreetsideImage) img).getDetections().isEmpty()) {
    390395                        final Path2D trafficSign = new Path2D.Double();
    391396                        trafficSign.moveTo(p.getX() - StreetsideLayer.TRAFFIC_SIGN_SIZE / 2d, p.getY() - StreetsideLayer.TRAFFIC_SIGN_HEIGHT_3RD);
     
    398403                        g.setColor(Color.RED);
    399404                        g.draw(trafficSign);
    400                 }
    401         }*/
    402 
    403         @Override
    404         public Icon getIcon() {
    405                 return StreetsidePlugin.LOGO.setSize(ImageSizes.LAYER).get();
     405                }*/
    406406        }
    407407
    408         @Override
    409         public boolean isMergable(Layer other) {
    410                 return false;
    411         }
    412 
    413         @Override
    414         public void mergeFrom(Layer from) {
    415                 throw new UnsupportedOperationException(
    416                                 "This layer does not support merging yet");
    417         }
    418 
    419         @Override
    420         public Action[] getMenuEntries() {
    421                 return new Action[]{
    422                                 LayerListDialog.getInstance().createShowHideLayerAction(),
    423                                 LayerListDialog.getInstance().createDeleteLayerAction(),
    424                                 new LayerListPopup.InfoAction(this)
    425                 };
    426         }
    427 
    428         @Override
    429         public Object getInfoComponent() {
    430                 final IntSummaryStatistics seqSizeStats = getData().getSequences().stream().mapToInt(seq -> seq.getImages().size()).summaryStatistics();
    431                 return new StringBuilder(I18n.tr("Streetside layer"))
    432                                 .append("\n")
    433                                 .append(I18n.tr(
    434                                                 "{0} sequences, each containing between {1} and {2} images (ø {3})",
    435                                                 getData().getSequences().size(),
    436                                                 seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMin(),
    437                                                                 seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMax(),
    438                                                                                 seqSizeStats.getAverage()
    439                                                 ))
    440                                 .append("\n\n")
    441                                 .append(I18n.tr(
    442                                                 "{0} imported images",
    443                                                 getData().getImages().stream().filter(i -> i instanceof StreetsideImportedImage).count()
    444                                                 ))
    445                                 .append("\n+ ")
    446                                 .append(I18n.tr(
    447                                                 "{0} downloaded images",
    448                                                 getData().getImages().stream().filter(i -> i instanceof StreetsideImage).count()
    449                                                 ))
    450                                 .append("\n= ")
    451                                 .append(I18n.tr(
    452                                                 "{0} images in total",
    453                                                 getData().getImages().size()
    454                                                 )).toString();
    455         }
    456 
    457         @Override
    458         public String getToolTipText() {
    459                 return I18n.tr("{0} images in {1} sequences", getData().getImages().size(), getData().getSequences().size());
    460         }
    461 
    462         @Override
    463         public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
    464                 if (MainApplication.getLayerManager().getActiveLayer() == this) {
    465                         StreetsideUtils.updateHelpText();
    466                 }
    467 
    468                 if (MainApplication.getLayerManager().getEditLayer() != e.getPreviousDataLayer()) {
    469                         if (MainApplication.getLayerManager().getEditLayer() != null) {
    470                                 MainApplication.getLayerManager().getEditLayer().getDataSet().addDataSetListener(StreetsideLayer.DATASET_LISTENER);
    471                         }
    472                         if (e.getPreviousDataLayer() != null) {
    473                                 e.getPreviousDataLayer().getDataSet().removeDataSetListener(StreetsideLayer.DATASET_LISTENER);
    474                         }
    475                 }
    476         }
    477 
    478         @Override
    479         public void visitBoundingBox(BoundingXYVisitor v) {
    480         }
    481 
    482         /* (non-Javadoc)
    483          * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#imagesAdded()
    484          */
    485         @Override
    486         public void imagesAdded() {
    487                 // TODO: Never used - could this be of use? @rrh
    488                 updateNearestImages();
    489         }
    490 
    491         /* (non-Javadoc)
    492          * @see org.openstreetmap.josm.plugins.mapillary.StreetsideDataListener#selectedImageChanged(org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage, org.openstreetmap.josm.plugins.mapillary.MapillaryAbstractImage)
    493          */
    494         @Override
    495         public void selectedImageChanged(StreetsideAbstractImage oldImage, StreetsideAbstractImage newImage) {
    496                 updateNearestImages();
    497         }
    498 
    499         /**
    500          * Returns the closest images belonging to a different sequence and
    501          * different from the specified target image.
    502          *
    503          * @param target the image for which you want to find the nearest other images
    504          * @param limit the maximum length of the returned array
    505          * @return An array containing the closest images belonging to different sequences sorted by distance from target.
    506          */
    507         private StreetsideImage[] getNearestImagesFromDifferentSequences(StreetsideAbstractImage target, int limit) {
    508                 return data.getSequences().parallelStream()
    509                                 .filter(seq -> seq.getId() != null && !seq.getId().equals(target.getSequence().getId()))
    510                                 .map(seq -> { // Maps sequence to image from sequence that is nearest to target
    511                                         final Optional<StreetsideAbstractImage> resImg = seq.getImages().parallelStream()
    512                                                         .filter(img -> img instanceof StreetsideImage && img.isVisible())
    513                                                         .min(new NearestImgToTargetComparator(target));
    514                                         return resImg.orElse(null);
    515                                 })
    516                                 .filter(img -> // Filters out images too far away from target
    517                                 img != null &&
    518                                 img.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
    519                                 < StreetsideProperties.SEQUENCE_MAX_JUMP_DISTANCE.get()
    520                                                 )
    521                                 .sorted(new NearestImgToTargetComparator(target))
    522                                 .limit(limit)
    523                                 .toArray(StreetsideImage[]::new);
    524         }
    525 
    526         /*
    527          * Returns the closest images belonging to a different sequence and
    528          * different from the specified target image.
    529          *
    530          * @param target the image for which you want to find the nearest other images
    531          * @param limit the maximum length of the returned array
    532          * @return An array containing the closest images belonging to different sequences sorted by distance from target.
    533          */
    534         /*private StreetsideCubemap[] getNearestCubemapsFromDifferentSequences(StreetsideAbstractImage target, int limit) {
    535                 return data.getSequences().parallelStream()
    536                                 .filter(seq -> seq.getId() != null && !seq.getId().equals(target.getSequence().getId()))
    537                                 .map(seq -> { // Maps sequence to image from sequence that is nearest to target
    538                                         final Optional<StreetsideAbstractImage> resCb = seq.getImages().parallelStream()
    539                                                         .filter(cb -> cb instanceof StreetsideCubemap && cb.isVisible())
    540                                                         .min(new NearestCbToTargetComparator(target));
    541                                         return resCb.orElse(null);
    542                                 })
    543                                 .filter(cb -> // Filters out images too far away from target
    544                                 cb != null &&
    545                                 cb.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
    546                                 < StreetsideProperties.SEQUENCE_MAX_JUMP_DISTANCE.get()
    547                                                 )
    548                                 .sorted(new NearestCbToTargetComparator(target))
    549                                 .limit(limit)
    550                                 .toArray(StreetsideCubemap[]::new);
    551         }*/
    552 
    553         private synchronized void updateNearestImages() {
    554                 final StreetsideAbstractImage selected = data.getSelectedImage();
    555                 if (selected != null) {
    556                         // TODO: could this be used to pre-cache cubemaps? @rrh
    557                         nearestImages = getNearestImagesFromDifferentSequences(selected, 2);
    558                 } else {
    559                         nearestImages = new StreetsideImage[0];
    560                 }
    561                 if (MainApplication.isDisplayingMapView()) {
    562                         StreetsideMainDialog.getInstance().redButton.setEnabled(nearestImages.length >= 1);
    563                         StreetsideMainDialog.getInstance().blueButton.setEnabled(nearestImages.length >= 2);
    564                 }
    565                 if (nearestImages.length >= 1) {
    566                         CacheUtils.downloadPicture(nearestImages[0]);
    567                         // TODO: download/pre-caches cubemaps here?
    568                         //CacheUtils.downloadCubemap(nearestImages[0]);
    569                         if (nearestImages.length >= 2) {
    570                                 CacheUtils.downloadPicture(nearestImages[1]);
    571                                 // TODO: download/pre-caches cubemaps here?
    572                                 //CacheUtils.downloadCubemap(nearestImages[1]);
    573                         }
    574                 }
    575         }
    576 
    577         private static class NearestImgToTargetComparator implements Comparator<StreetsideAbstractImage> {
    578                 private final StreetsideAbstractImage target;
    579 
    580                 public NearestImgToTargetComparator(StreetsideAbstractImage target) {
    581                         this.target = target;
    582                 }
    583                 /* (non-Javadoc)
    584                  * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
    585                  */
    586                 @Override
    587                 public int compare(StreetsideAbstractImage img1, StreetsideAbstractImage img2) {
    588                         return (int) Math.signum(
    589                                         img1.getMovingLatLon().greatCircleDistance(target.getMovingLatLon()) -
    590                                         img2.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
    591                                         );
    592                 }
    593         }
    594 
    595         private static class NearestCbToTargetComparator implements Comparator<StreetsideAbstractImage> {
    596                 private final StreetsideAbstractImage target;
    597 
    598                 public NearestCbToTargetComparator(StreetsideAbstractImage target) {
    599                         this.target = target;
    600                 }
    601                 /* (non-Javadoc)
    602                  * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
    603                  */
    604                 @Override
    605                 public int compare(StreetsideAbstractImage img1, StreetsideAbstractImage img2) {
    606                         return (int) Math.signum(
    607                                         img1.getMovingLatLon().greatCircleDistance(target.getMovingLatLon()) -
    608                                         img2.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
    609                                         );
    610                 }
    611         }
     408  @Override
     409  public Icon getIcon() {
     410    return StreetsidePlugin.LOGO.setSize(ImageSizes.LAYER).get();
     411  }
     412
     413  @Override
     414  public boolean isMergable(Layer other) {
     415    return false;
     416  }
     417
     418  @Override
     419  public void mergeFrom(Layer from) {
     420    throw new UnsupportedOperationException(
     421      "This layer does not support merging yet");
     422  }
     423
     424  @Override
     425  public Action[] getMenuEntries() {
     426    return new Action[]{
     427      LayerListDialog.getInstance().createShowHideLayerAction(),
     428      LayerListDialog.getInstance().createDeleteLayerAction(),
     429      new LayerListPopup.InfoAction(this)
     430    };
     431  }
     432
     433  @Override
     434  public Object getInfoComponent() {
     435    IntSummaryStatistics seqSizeStats = getData().getSequences().stream().mapToInt(seq -> seq.getImages().size()).summaryStatistics();
     436    return new StringBuilder(I18n.tr("Streetside layer"))
     437      .append('\n')
     438      .append(I18n.tr(
     439        "{0} sequences, each containing between {1} and {2} images (ø {3})",
     440        getData().getSequences().size(),
     441        seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMin(),
     442        seqSizeStats.getCount() <= 0 ? 0 : seqSizeStats.getMax(),
     443        seqSizeStats.getAverage()
     444      ))
     445      .append("\n\n")
     446      .append(I18n.tr(
     447        "{0} imported images",
     448        getData().getImages().stream().filter(i -> i instanceof StreetsideImportedImage).count()
     449      ))
     450      .append("\n+ ")
     451      .append(I18n.tr(
     452        "{0} downloaded images",
     453        getData().getImages().stream().filter(i -> i instanceof StreetsideImage).count()
     454      ))
     455      .append("\n= ")
     456      .append(I18n.tr(
     457        "{0} images in total",
     458        getData().getImages().size()
     459      )).toString();
     460  }
     461
     462  @Override
     463  public String getToolTipText() {
     464    return I18n.tr("{0} images in {1} sequences", getData().getImages().size(), getData().getSequences().size());
     465  }
     466
     467  @Override
     468  public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
     469    if (MainApplication.getLayerManager().getActiveLayer() == this) {
     470      StreetsideUtils.updateHelpText();
     471    }
     472
     473    if (MainApplication.getLayerManager().getEditLayer() != e.getPreviousDataLayer()) {
     474      if (MainApplication.getLayerManager().getEditLayer() != null) {
     475        MainApplication.getLayerManager().getEditLayer().getDataSet().addDataSetListener(DATASET_LISTENER);
     476      }
     477      if (e.getPreviousDataLayer() != null) {
     478        e.getPreviousDataLayer().getDataSet().removeDataSetListener(DATASET_LISTENER);
     479      }
     480    }
     481  }
     482
     483  @Override
     484  public void visitBoundingBox(BoundingXYVisitor v) {
     485  }
     486
     487  /* (non-Javadoc)
     488   * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#imagesAdded()
     489   */
     490  @Override
     491  public void imagesAdded() {
     492    updateNearestImages();
     493  }
     494
     495  /* (non-Javadoc)
     496   * @see org.openstreetmap.josm.plugins.streetside.StreetsideDataListener#selectedImageChanged(org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage, org.openstreetmap.josm.plugins.streetside.StreetsideAbstractImage)
     497   */
     498  @Override
     499  public void selectedImageChanged(StreetsideAbstractImage oldImage, StreetsideAbstractImage newImage) {
     500    updateNearestImages();
     501  }
     502
     503  /**
     504   * Returns the closest images belonging to a different sequence and
     505   * different from the specified target image.
     506   *
     507   * @param target the image for which you want to find the nearest other images
     508   * @param limit the maximum length of the returned array
     509   * @return An array containing the closest images belonging to different sequences sorted by distance from target.
     510   */
     511  private StreetsideImage[] getNearestImagesFromDifferentSequences(StreetsideAbstractImage target, int limit) {
     512    return data.getSequences().parallelStream()
     513      .filter(seq -> seq.getId() != null && !seq.getId().equals(target.getSequence().getId()))
     514      .map(seq -> { // Maps sequence to image from sequence that is nearest to target
     515        Optional<StreetsideAbstractImage> resImg = seq.getImages().parallelStream()
     516          .filter(img -> img instanceof StreetsideImage && img.isVisible())
     517          .min(new NearestImgToTargetComparator(target));
     518        return resImg.orElse(null);
     519      })
     520      .filter(img -> // Filters out images too far away from target
     521        img != null &&
     522        img.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
     523          < StreetsideProperties.SEQUENCE_MAX_JUMP_DISTANCE.get()
     524       )
     525      .sorted(new NearestImgToTargetComparator(target))
     526      .limit(limit)
     527      .toArray(StreetsideImage[]::new);
     528  }
     529
     530  private synchronized void updateNearestImages() {
     531    final StreetsideAbstractImage selected = data.getSelectedImage();
     532    if (selected != null) {
     533      nearestImages = getNearestImagesFromDifferentSequences(selected, 2);
     534    } else {
     535      nearestImages = new StreetsideImage[0];
     536    }
     537    if (MainApplication.isDisplayingMapView()) {
     538      StreetsideMainDialog.getInstance().redButton.setEnabled(nearestImages.length >= 1);
     539      StreetsideMainDialog.getInstance().blueButton.setEnabled(nearestImages.length >= 2);
     540    }
     541    if (nearestImages.length >= 1) {
     542      CacheUtils.downloadPicture(nearestImages[0]);
     543      if (nearestImages.length >= 2) {
     544        CacheUtils.downloadPicture(nearestImages[1]);
     545      }
     546    }
     547  }
     548
     549  /**
     550   * Action used to delete images.
     551   *
     552   * @author nokutu
     553   */
     554  /*private class DeleteImageAction extends AbstractAction {
     555
     556    private static final long serialVersionUID = -982809854631863962L;
     557
     558    @Override
     559    public void actionPerformed(ActionEvent e) {
     560      if (instance != null)
     561        StreetsideRecord.getInstance().addCommand(
     562          new CommandDelete(getData().getMultiSelectedImages()));
     563    }
     564  }*/
     565
     566  private static class NearestImgToTargetComparator implements Comparator<StreetsideAbstractImage> {
     567    private final StreetsideAbstractImage target;
     568
     569    public NearestImgToTargetComparator(StreetsideAbstractImage target) {
     570      this.target = target;
     571    }
     572    /* (non-Javadoc)
     573     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     574     */
     575    @Override
     576    public int compare(StreetsideAbstractImage img1, StreetsideAbstractImage img2) {
     577      return (int) Math.signum(
     578        img1.getMovingLatLon().greatCircleDistance(target.getMovingLatLon()) -
     579        img2.getMovingLatLon().greatCircleDistance(target.getMovingLatLon())
     580      );
     581    }
     582  }
    612583}
  • applications/editors/josm/plugins/MicrosoftStreetside/src/org/openstreetmap/josm/plugins/streetside/StreetsidePlugin.java

    r34325 r34329  
    4949      MainMenu.add(MainApplication.getMenu().moreToolsMenu, WALK_ACTION, false);
    5050      //MainMenu.add(MainApplication.getMenu().imagerySubMenu, new MapObjectLayerAction(), false);
     51      //MainMenu.add(MainApplication.getMenu().imagerySubMenu, new MapObjectLayerAction(), false);
    5152    }
    5253  }
     
    6768
    6869  static StreetsideDataListener[] getStreetsideDataListeners() {
    69         return new StreetsideDataListener[]{/*UPLOAD_ACTION,*/ WALK_ACTION, ZOOM_ACTION, CubemapBuilder.getInstance()};
     70        return new StreetsideDataListener[]{WALK_ACTION, ZOOM_ACTION, CubemapBuilder.getInstance()};
    7071  }
    7172
Note: See TracChangeset for help on using the changeset viewer.