Ticket #24941: 24941-1-java11.patch

File 24941-1-java11.patch, 192.9 KB (added by gaben, 2 weeks ago)

Java 11 bump plus cleanup, Maven _should be_ (fingers crossed) as good as Ant

  • .checkstyle

    diff --git .checkstyle .checkstyle
    index 66b7500..25cd9c8 100644
     
    55    <additional-data name="protect-config-file" value="false"/>
    66  </local-check-config>
    77  <fileset name="all" enabled="true" check-config-name="JMapViewer" local="true">
    8     <file-match-pattern match-pattern="." include-pattern="true"/>
     8    <file-match-pattern match-pattern="\.java$" include-pattern="true"/>
    99  </fileset>
    1010  <filter name="FilesFromPackage" enabled="true">
    1111    <filter-data value="src/org/openstreetmap/gui/jmapviewer/images"/>
  • .classpath

    diff --git .classpath .classpath
    index cf1198a..2c27265 100644
     
    66                        <attribute name="test" value="true"/>
    77                </attributes>
    88        </classpathentry>
    9         <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
    10         <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
     9        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
     10                <attributes>
     11                        <attribute name="module" value="true"/>
     12                </attributes>
     13        </classpathentry>
     14        <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
    1115        <classpathentry kind="output" path="bin"/>
    1216</classpath>
  • .gitignore

    diff --git .gitignore .gitignore
    index 6befe71..44747be 100644
     
    22JMapViewer.jar
    33JMapViewer_Demo.jar
    44JMapViewer_src.jar
    5 JMapViewer.zip
    65/bin/
    76/bintest/
    87/javadoc/
    98/report/
    10 /tiles/
    119/checkstyle-jmapviewer.xml
    12 /findbugs-jmapviewer.xml
    1310/spotbugs-jmapviewer.xml
    1411*.exec
    1512
    1613# Maven output
    1714/target/
     15/releases/*-SNAPSHOT/
    1816
    1917# SVN working copy metadata (this tree is an svn checkout)
    2018/.svn/
  • .project

    diff --git .project .project
    index ad7e557..8a037ad 100644
     
    2727                </buildCommand>
    2828        </buildSpec>
    2929        <natures>
    30                 <nature>org.sonar.ide.eclipse.core.sonarNature</nature>
    3130                <nature>org.eclipse.jdt.core.javanature</nature>
    3231                <nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
    3332                <nature>edu.umd.cs.findbugs.plugin.eclipse.findbugsNature</nature>
  • .settings/edu.umd.cs.findbugs.core.prefs

    diff --git .settings/edu.umd.cs.findbugs.core.prefs .settings/edu.umd.cs.findbugs.core.prefs
    index e406091..99abdf4 100644
     
    11#FindBugs User Preferences
    22#Sat Aug 01 22:46:10 CEST 2015
    3 cloud_id=edu.umd.cs.findbugs.cloud.doNothingCloud
    43detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
    54detectorAtomicityProblem=AtomicityProblem|true
    65detectorBadAppletConstructor=BadAppletConstructor|false
  • .settings/org.eclipse.jdt.core.prefs

    diff --git .settings/org.eclipse.jdt.core.prefs .settings/org.eclipse.jdt.core.prefs
    index c4e4a7f..a04d4e1 100644
    org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonN  
    1515org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
    1616org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
    1717org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
    18 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
    19 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
     18org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
    2019org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
    21 org.eclipse.jdt.core.compiler.compliance=1.8
     20org.eclipse.jdt.core.compiler.compliance=11
    2221org.eclipse.jdt.core.compiler.debug.lineNumber=generate
    2322org.eclipse.jdt.core.compiler.debug.localVariable=generate
    2423org.eclipse.jdt.core.compiler.debug.sourceFile=generate
    … … org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning  
    121120org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
    122121org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
    123122org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
    124 org.eclipse.jdt.core.compiler.source=1.8
     123org.eclipse.jdt.core.compiler.release=enabled
     124org.eclipse.jdt.core.compiler.source=11
    125125org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
    126126org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
    127127org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
  • deleted file .settings/org.sonar.ide.eclipse.core.prefs

    diff --git .settings/org.sonar.ide.eclipse.core.prefs .settings/org.sonar.ide.eclipse.core.prefs
    deleted file mode 100644
    index 4ab0dd3..0000000
    + -  
    1 eclipse.preferences.version=1
    2 extraProperties=
    3 lastAnalysisDate=1438527074033
    4 projectKey=jmapviewer
    5 serverUrl=https\://josm.openstreetmap.de/sonar
    6 version=2
  • CONTRIBUTING.md

    diff --git CONTRIBUTING.md CONTRIBUTING.md
    index ee9f19a..416fa57 100644
     
    11# Contributing
     2## Requirements
     3* JDK 11 or later
    24## How to test
    35* Unit tests: `mvn test`
    46* Manual testing: `mvn compile exec:java -Dexec.mainClass=org.openstreetmap.gui.jmapviewer.Demo`
    … … See [https://josm.openstreetmap.de/wiki/DevelopersGuide/PatchGuide](https://josm  
    2224* `ant test` -> `mvn test`
    2325* `ant svn_info` -> manual pom.xml editing or `mvn release:update-versions -DdevelopmentVersion=$(svn propget ReleaseVersion .)-SNAPSHOT`
    2426* `ant pack` -> `mvn package` (note: slightly different source filename, doesn't merge source and class jars)
    25 * `ant create_run_jar` -> not migrated (use `mvn exec:java -Dexec.mainClass="org.openstreetmap.gui.jmapviewer.Demo"` instead)
     27* `ant create_run_jar` -> not needed, the jar from `mvn package` is runnable
    2628* `ant spotbugs` -> `mvn spotbugs:check`
    2729* `ant checkstyle` -> `mvn checkstyle:checkstyle`
    2830* `ant javadoc` -> `mvn javadoc:javadoc`
    29 * `ant create_release_zip` -> `mvn jar:jar`
    30 * `ant create_source_release_zip` -> `mvn source:jar`
     31* `ant create_release_zip`, `ant create_source_release_zip` -> `mvn -Prelease package`, written to `releases/${version}`
    3132* `ant checkdepsupdate` -> `mvn versions:display-dependency-updates` (note: please try to keep the version information in sync)
  • Readme.txt

    diff --git Readme.txt Readme.txt
    index 9346474..3e8d6f6 100644
    JMapViewer  
    2121(c) 2015-2019, Wiktor Niesiobędzki
    2222(c) 2017, Robert Scott
    2323
    24 This work bases partly on the JOSM plugin "Slippy Map Chooser" by Tim Haussmann
     24This work is partly based on the JOSM plugin "Slippy Map Chooser" by Tim Haussmann
    2525
    2626License: GPL
    2727
    … … FAQ:  
    2929
    30301. What is JMapViewer?
    3131
    32 JMapViewer is a Java Swing component for integrating OSM maps in to your Java
    33 application. JMapViewer allows you to set markers on the map or zoom to a specific
     32JMapViewer is a Java Swing component for integrating OSM maps into your Java
     33application. JMapViewer lets you set markers on the map or zoom to a specific
    3434location on the map.
    3535
    36362. How does JMapViewer work?
    3737
    38 JMapViewer loads bitmap tiles from the OpenStreetmap tile server (Mapnik renderer).
    39 Therefore any application using JMapViewer requires a working Internet connection.   
     38JMapViewer loads bitmap tiles from the OpenStreetMap tile server (Mapnik renderer).
     39Therefore, any application using JMapViewer requires a working Internet connection.
    4040
    41413. How do I use JMapViewer in my application?
    4242
    43 You can just create an instance of the class org.openstreetmap.gui.jmapviewer.JMapViewer
    44 using the default constructor and add it to your panel/frame/windows.
    45 For more details please see the Demo class in the same package.
     43Just create an instance of the class org.openstreetmap.gui.jmapviewer.JMapViewer
     44using the default constructor and add it to your panel, frame or window.
     45For more details, please see the Demo class in the same package.
     46
     474. What do I need to run JMapViewer?
     48
     49Java 11 or later. JMapViewer only uses the java.desktop and java.logging modules of the
     50Java runtime, and no other libraries.
  • build.xml

    diff --git build.xml build.xml
    index 3d07301..a4cd871 100644
     
    55         xmlns:mvn="antlib:org.apache.maven.resolver.ant"
    66>
    77
    8     <property name="java.lang.version" value="8" />
    9     <dirname property="base.dir" file="${ant.file.jmapviewer}"/>
    10     <property name="maven.artifact.resolver.version" value="1.5.1"/>
     8    <!-- Keep in sync with maven.compiler.release and jmapviewer.buildJdk in pom.xml -->
     9    <property name="java.lang.version" value="11" />
     10    <property name="java.build.version" value="11" />
     11    <fail message="Building JMapViewer needs JDK ${java.build.version} or later, the classes are compiled for Java ${java.lang.version}">
     12        <condition><not><javaversion atleast="${java.build.version}"/></not></condition>
     13    </fail>
     14    <property name="maven.artifact.resolver.version" value="2.0.0"/>
    1115    <property name="maven.artifact.resolver.parent.dir" value="${user.home}/.m2/repository/org/apache/maven/resolver/maven-resolver-ant-tasks/${maven.artifact.resolver.version}/"/>
    1216    <property name="jacoco.includes" value="org.openstreetmap.gui.jmapviewer.*" />
    1317    <property name="jacoco.inclbootstrapclasses" value="false" />
    1418    <property name="jacoco.inclnolocationclasses" value="false" />
    15     <!-- For Java specific stuff by version -->
    16     <condition property="isJava9"><matches string="${ant.java.version}" pattern="(1.)?(9|[2-9][0-9])" /></condition>
    17     <condition property="isJava23"><matches string="${ant.java.version}" pattern="2[3-9]|[3-9][0-9]" /></condition>
    18     <!-- Disable jacoco 0.8.11 on Java 23+, see https://www.jacoco.org/jacoco/trunk/doc/changes.html for latest supported version -->
     19    <!-- Disable jacoco on the Java versions it does not support yet, see https://www.jacoco.org/jacoco/trunk/doc/changes.html.
     20         Update the pattern together with version.maven.jacoco in pom.xml. -->
     21    <condition property="isJavaUnsupportedByJacoco"><matches string="${ant.java.version}" pattern="2[8-9]|[3-9][0-9]" /></condition>
    1922    <condition property="coverageByDefault">
    2023        <not>
    21             <isset property="isJava23"/>
     24            <isset property="isJavaUnsupportedByJacoco"/>
    2225        </not>
    2326    </condition>
    2427    <path id="test.classpath">
    … …  
    5053    </target>
    5154
    5255    <target name="build" depends="clean">
    53         <!-- compile module info - we need to compile everything to ensure that all requires are there -->
    54         <javac srcdir="src" destdir="bin" release="9" debug="true" includeantruntime="false" encoding="UTF-8" if:set="isJava9">
    55             <include name="module-info.java" />
    56             <include name="org/openstreetmap/gui/jmapviewer/**" />
    57         </javac>
    58 
    59         <!-- Remove the files compiled with Java 9 so that the next compile stage will compile to the appropriate version -->
    60         <delete>
    61             <fileset dir="bin">
    62                 <include name="org/openstreetmap/gui/jmapviewer/**" />
    63             </fileset>
    64         </delete>
    65 
    6656        <javac srcdir="src" destdir="bin" release="${java.lang.version}" debug="true" includeantruntime="false" encoding="UTF-8">
    67             <include name="org/openstreetmap/gui/jmapviewer/**" />
     57            <compilerarg value="-Xlint:all"/>
     58            <compilerarg value="-Xlint:-serial"/>
    6859        </javac>
    6960
    7061        <copy todir="bin">
    … …  
    8172            <env key="LANG" value="en_US"/>
    8273        </exec>
    8374    </target>
    84    
     75
    8576    <target name="pack" depends="build">
    8677        <!-- Create the JAR file containing the compiled class files -->
    87         <jar destfile="JMapViewer.jar" filesetmanifest="mergewithoutmain">
    88             <fileset dir="bin" includes="module-info.class,**/jmapviewer/**" />
    89         </jar>
     78        <jar destfile="JMapViewer.jar" basedir="bin" filesetmanifest="mergewithoutmain"/>
    9079        <!-- Create the JAR file containing the source java files -->
    91         <jar destfile="JMapViewer_src.jar" filesetmanifest="mergewithoutmain">
    92             <fileset dir="src" includes="module-info.java,**/jmapviewer/**" />
    93         </jar>
     80        <jar destfile="JMapViewer_src.jar" basedir="src" filesetmanifest="mergewithoutmain"/>
    9481    </target>
    95    
    96     <!-- if you want to build outside of svn, use "ant clean build [pack]" -->
    97    
     82
    9883    <target name="create_run_jar" description="Create a JAR file that can be used to execute the JMapViewer demo app. Requires JMapViewer.jar to be present.">
    9984        <jar destfile="JMapViewer_Demo.jar" filesetmanifest="mergewithoutmain">
    10085            <manifest>
    … …  
    11297        </zip>
    11398        <delete>
    11499            <fileset dir="." includes="JMapViewer*.jar"/>
    115         </delete> 
     100        </delete>
    116101    </target>
    117    
     102
    118103    <target name="create_source_release_zip" description="Create a release zip file containing the source files">
     104        <!-- Same content as tools/assembly/source-release.xml, so that the unpacked zip builds with Ant and Maven -->
    119105        <zip destfile="releases/${svnReleaseVersion}/JMapViewer-${svnReleaseVersion}-Source.zip">
    120             <zipfileset file="Readme.txt" prefix="jmapviewer-${svnReleaseVersion}"/>
    121             <zipfileset file="build.xml" prefix="jmapviewer-${svnReleaseVersion}"/>
    122             <zipfileset file="Gpl.txt" prefix="jmapviewer-${svnReleaseVersion}"/>
    123             <zipfileset dir="src" includes="**/jmapviewer/**" prefix="jmapviewer-${svnReleaseVersion}/src"/>
     106            <zipfileset dir="." includes="Readme.txt,Gpl.txt,build.xml,pom.xml,src/**,test/**,tools/**"
     107                        prefix="jmapviewer-${svnReleaseVersion}"/>
    124108        </zip>
    125109    </target>
    126110
    … …  
    128112        <taskdef resource="com/puppycrawl/tools/checkstyle/ant/checkstyle-ant-task.properties"
    129113            classpathref="checkstyle.classpath"/>
    130114        <checkstyle config="${basedir}/tools/checkstyle/jmapviewer_checks.xml">
    131             <!-- Exclude the module-info since checkstyle currently cannot parse it -->
    132             <fileset dir="${basedir}/src" includes="**/*.java" excludes="module-info.java" />
     115            <property key="config_loc" value="${basedir}/tools/checkstyle"/>
     116            <fileset dir="${basedir}/src" includes="**/*.java" />
     117            <fileset dir="${basedir}/test" includes="**/*.java" />
    133118            <formatter type="xml" toFile="checkstyle-jmapviewer.xml"/>
    134119        </checkstyle>
    135120    </target>
    136121
    137122    <target name="spotbugs" depends="pack,resolve">
    138         <taskdef name="spotbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" 
     123        <taskdef name="spotbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
    139124            classpathref="spotbugs.classpath"/>
    140125        <property name="spotbugs-classpath" refid="spotbugs.classpath"/>
    141126        <spotbugs output="xml"
    … …  
    149134    </target>
    150135
    151136    <target name="javadoc">
    152         <javadoc destdir="javadoc" 
     137        <javadoc destdir="javadoc"
    153138                sourcepath="src"
    154                 encoding="UTF-8"   
     139                encoding="UTF-8"
    155140                packagenames="org.openstreetmap.gui.jmapviewer.*"
    156141                windowtitle="JMapViewer"
    157142                use="true"
    158143                private="true"
    159144                linksource="true"
    160145                author="false">
    161             <link href="https://docs.oracle.com/javase/8/docs/api"/>
     146            <link href="https://docs.oracle.com/en/java/javase/${java.lang.version}/docs/api"/>
    162147            <doctitle><![CDATA[<h2>JMapViewer - Javadoc</h2>]]></doctitle>
    163148            <bottom><![CDATA[<a href="https://josm.openstreetmap.de/">JMapViewer</a>]]></bottom>
    164             <arg value="-html5" if:set="isJava9" />
     149            <arg value="--release"/>
     150            <arg value="${java.lang.version}"/>
    165151        </javadoc>
    166152    </target>
    167153
    … …  
    204190                <mvn:dependency groupId="com.github.spotbugs" artifactId="spotbugs" version="${pom.properties.version.maven.spotbugs}" scope="compile"/>
    205191                <mvn:dependency groupId="com.github.spotbugs" artifactId="spotbugs-ant" version="${pom.properties.version.maven.spotbugs}" scope="compile"/>
    206192            </mvn:dependencies>
    207             <mvn:path refid="spotbugs.classpath" classpath="compile"/>
     193            <!-- SpotBugs needs its runtime dependencies (jaxen) to load its own plugin descriptor -->
     194            <mvn:path refid="spotbugs.classpath" classpath="runtime"/>
    208195        </mvn:resolve>
    209196    </target>
    210197
    211     <target name="test" depends="clean, build, resolve">
     198    <target name="test" depends="build, resolve">
    212199        <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml" classpathref="jacoco.classpath"/>
    213200        <javac srcdir="test" destdir="bintest"
    214             target="${java.lang.version}" source="${java.lang.version}" debug="on"
     201            release="${java.lang.version}" debug="on"
    215202            includeantruntime="false" createMissingPackageInfoClass="false" encoding="UTF-8">
    216203            <compilerarg value="-Xlint:all"/>
    217204            <compilerarg value="-Xlint:-serial"/>
  • pom.xml

    diff --git pom.xml pom.xml
    index 926c75d..5cae331 100644
     
    1111    <url>https://josm.openstreetmap.de/browser/osm/applications/viewer/jmapviewer</url>
    1212    <properties>
    1313        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    14         <maven.compiler.release>8</maven.compiler.release>
    15         <version.maven.spotbugs>4.8.6</version.maven.spotbugs>
    16         <version.maven.jacoco>0.8.12</version.maven.jacoco>
    17         <version.maven.checkstyle>9.3</version.maven.checkstyle>
     14        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
     15        <maven.compiler.release>11</maven.compiler.release>
     16        <!-- The JDK needed to run the build tools; the classes are compiled for maven.compiler.release -->
     17        <jmapviewer.buildJdk>11</jmapviewer.buildJdk>
     18        <jmapviewer.mainClass>org.openstreetmap.gui.jmapviewer.Demo</jmapviewer.mainClass>
     19        <!-- Overwritten by the JaCoCo agent, see maven-surefire-plugin -->
     20        <argLine></argLine>
     21        <version.maven.spotbugs>4.10.4</version.maven.spotbugs>
     22        <version.maven.jacoco>0.8.15</version.maven.jacoco>
     23        <!-- 10.x is the last line that runs on Java 11, see jmapviewer.buildJdk -->
     24        <version.maven.checkstyle>10.26.1</version.maven.checkstyle>
    1825        <sonar.host.url>https://josm.openstreetmap.de/sonar</sonar.host.url>
    1926        <sonar.projectKey>jmapviewer</sonar.projectKey>
    2027    </properties>
    … …  
    97104            <dependency>
    98105                <groupId>org.junit</groupId>
    99106                <artifactId>junit-bom</artifactId>
    100                 <version>5.10.2</version>
     107                <!-- JUnit 6 needs Java 17, while the tests are compiled for maven.compiler.release -->
     108                <version>5.14.4</version>
    101109                <type>pom</type>
    102110                <scope>import</scope>
    103111            </dependency>
    … …  
    105113    </dependencyManagement>
    106114
    107115    <dependencies>
    108         <dependency>
    109             <groupId>com.github.spotbugs</groupId>
    110             <artifactId>spotbugs-annotations</artifactId>
    111             <version>${version.maven.spotbugs}</version>
    112             <scope>test</scope>
    113         </dependency>
    114         <dependency>
    115             <groupId>org.junit.platform</groupId>
    116             <artifactId>junit-platform-launcher</artifactId>
    117             <scope>test</scope>
    118         </dependency>
    119         <dependency>
    120             <groupId>org.junit.platform</groupId>
    121             <artifactId>junit-platform-suite</artifactId>
    122             <scope>test</scope>
    123         </dependency>
    124116        <dependency>
    125117            <groupId>org.junit.jupiter</groupId>
    126118            <artifactId>junit-jupiter-api</artifactId>
    … …  
    132124            <scope>test</scope>
    133125        </dependency>
    134126        <dependency>
    135             <groupId>org.junit.jupiter</groupId>
    136             <artifactId>junit-jupiter-params</artifactId>
     127            <!-- Surefire brings its own, but the Ant "junitlauncher" task runs off the test classpath -->
     128            <groupId>org.junit.platform</groupId>
     129            <artifactId>junit-platform-launcher</artifactId>
    137130            <scope>test</scope>
    138131        </dependency>
    139132    </dependencies>
    … …  
    143136        <testSourceDirectory>${project.basedir}/test</testSourceDirectory>
    144137        <finalName>${project.name}-${project.version}</finalName>
    145138        <resources>
     139            <!-- Images live next to the sources, as the source layout predates the Maven conventions -->
    146140            <resource>
    147141                <directory>${project.build.sourceDirectory}</directory>
    148142                <includes>
    … …  
    155149        <plugins>
    156150            <plugin>
    157151                <groupId>org.apache.maven.plugins</groupId>
    158                 <artifactId>maven-jar-plugin</artifactId>
    159                 <version>3.4.2</version>
    160                 <configuration>
    161                     <outputDirectory>releases/${project.version}</outputDirectory>
    162                 </configuration>
    163             </plugin>
    164             <plugin>
    165                 <groupId>org.apache.maven.plugins</groupId>
    166                 <artifactId>maven-source-plugin</artifactId>
    167                 <version>3.3.1</version>
    168                 <configuration>
    169                     <outputDirectory>releases/${project.version}</outputDirectory>
    170                 </configuration>
     152                <artifactId>maven-enforcer-plugin</artifactId>
     153                <version>3.6.3</version>
    171154                <executions>
    172155                    <execution>
    173                         <id>attach-sources</id>
    174                         <phase>package</phase>
     156                        <id>enforce</id>
    175157                        <goals>
    176                             <goal>jar-no-fork</goal>
     158                            <goal>enforce</goal>
    177159                        </goals>
     160                        <configuration>
     161                            <rules>
     162                                <requireMavenVersion>
     163                                    <version>3.6.3</version>
     164                                </requireMavenVersion>
     165                                <requireJavaVersion>
     166                                    <version>${jmapviewer.buildJdk}</version>
     167                                </requireJavaVersion>
     168                            </rules>
     169                        </configuration>
    178170                    </execution>
    179171                </executions>
    180172            </plugin>
     173            <plugin>
     174                <groupId>org.apache.maven.plugins</groupId>
     175                <artifactId>maven-resources-plugin</artifactId>
     176                <version>3.5.0</version>
     177            </plugin>
    181178            <plugin>
    182179                <groupId>org.apache.maven.plugins</groupId>
    183180                <artifactId>maven-compiler-plugin</artifactId>
    184                 <version>3.13.0</version>
     181                <version>3.16.0</version>
     182                <configuration>
     183                    <compilerArgs>
     184                        <arg>-Xlint:all</arg>
     185                        <arg>-Xlint:-serial</arg>
     186                    </compilerArgs>
     187                </configuration>
    185188                <executions>
    186189                    <execution>
    187                         <id>default-compile</id>
     190                        <id>default-testCompile</id>
    188191                        <configuration>
    189                             <release>11</release>
     192                            <!-- The tests live in the same packages as the classes they test, so they run on the classpath -->
     193                            <useModulePath>false</useModulePath>
    190194                        </configuration>
    191195                    </execution>
     196                </executions>
     197            </plugin>
     198            <plugin>
     199                <groupId>org.apache.maven.plugins</groupId>
     200                <artifactId>maven-surefire-plugin</artifactId>
     201                <version>3.6.0</version>
     202                <configuration>
     203                    <!-- @{argLine} picks up the JaCoCo agent; the argLine property is empty when JaCoCo is skipped -->
     204                    <argLine>@{argLine} -Dfile.encoding=UTF-8</argLine>
     205                    <useModulePath>false</useModulePath>
     206                </configuration>
     207            </plugin>
     208            <plugin>
     209                <groupId>org.apache.maven.plugins</groupId>
     210                <artifactId>maven-jar-plugin</artifactId>
     211                <version>3.5.1</version>
     212                <configuration>
     213                    <archive>
     214                        <manifest>
     215                            <!-- Makes the jar runnable, replacing the Ant-only JMapViewer_Demo.jar -->
     216                            <mainClass>${jmapviewer.mainClass}</mainClass>
     217                        </manifest>
     218                    </archive>
     219                </configuration>
     220            </plugin>
     221            <plugin>
     222                <groupId>org.apache.maven.plugins</groupId>
     223                <artifactId>maven-source-plugin</artifactId>
     224                <version>3.4.0</version>
     225                <executions>
    192226                    <execution>
    193                         <id>base-compile</id>
     227                        <id>attach-sources</id>
     228                        <phase>package</phase>
    194229                        <goals>
    195                             <goal>compile</goal>
     230                            <goal>jar-no-fork</goal>
    196231                        </goals>
    197                         <configuration>
    198                             <release>8</release>
    199                             <excludes>
    200                                 <exclude>module-info.java</exclude>
    201                             </excludes>
    202                         </configuration>
    203232                    </execution>
    204233                </executions>
    205234            </plugin>
     235            <plugin>
     236                <groupId>org.apache.maven.plugins</groupId>
     237                <artifactId>maven-javadoc-plugin</artifactId>
     238                <version>3.12.0</version>
     239                <configuration>
     240                    <show>private</show>
     241                    <linksource>true</linksource>
     242                    <windowtitle>${project.name}</windowtitle>
     243                    <doctitle>&lt;h2&gt;${project.name} - Javadoc&lt;/h2&gt;</doctitle>
     244                    <bottom>&lt;a href="https://josm.openstreetmap.de/"&gt;${project.name}&lt;/a&gt;</bottom>
     245                </configuration>
     246            </plugin>
     247            <plugin>
     248                <groupId>org.apache.maven.plugins</groupId>
     249                <artifactId>maven-clean-plugin</artifactId>
     250                <version>3.5.0</version>
     251                <configuration>
     252                    <!-- The reports that the quality plugins write outside of target/ -->
     253                    <filesets>
     254                        <fileset>
     255                            <directory>${project.basedir}</directory>
     256                            <includes>
     257                                <include>checkstyle-jmapviewer.xml</include>
     258                                <include>spotbugs-jmapviewer.xml</include>
     259                            </includes>
     260                        </fileset>
     261                    </filesets>
     262                </configuration>
     263            </plugin>
     264            <plugin>
     265                <groupId>org.codehaus.mojo</groupId>
     266                <artifactId>exec-maven-plugin</artifactId>
     267                <version>3.6.4</version>
     268                <configuration>
     269                    <mainClass>${jmapviewer.mainClass}</mainClass>
     270                </configuration>
     271            </plugin>
     272            <plugin>
     273                <groupId>org.codehaus.mojo</groupId>
     274                <artifactId>versions-maven-plugin</artifactId>
     275                <version>2.22.0</version>
     276            </plugin>
    206277            <!-- Start optional plugins (these can be removed by Linux distributions for building) -->
    207278            <plugin>
    208279                <groupId>org.jacoco</groupId>
    … …  
    214285                        <goals>
    215286                            <goal>prepare-agent</goal>
    216287                        </goals>
     288                        <configuration>
     289                            <!-- Measure JMapViewer only, not JUnit and the other classes on the test classpath -->
     290                            <includes>
     291                                <include>org.openstreetmap.gui.jmapviewer.*</include>
     292                            </includes>
     293                        </configuration>
    217294                    </execution>
    218295                    <execution>
    219296                        <id>default-report</id>
    … …  
    226303            <plugin>
    227304                <groupId>com.github.spotbugs</groupId>
    228305                <artifactId>spotbugs-maven-plugin</artifactId>
    229                 <version>4.8.6.2</version>
     306                <!-- 4.10.4.1 and later require Maven 3.8.9, see maven-enforcer-plugin -->
     307                <version>4.10.3.0</version>
     308                <dependencies>
     309                    <dependency>
     310                        <groupId>com.github.spotbugs</groupId>
     311                        <artifactId>spotbugs</artifactId>
     312                        <version>${version.maven.spotbugs}</version>
     313                    </dependency>
     314                </dependencies>
    230315                <configuration>
    231316                    <xmlOutput>true</xmlOutput>
     317                    <spotbugsXmlOutputDirectory>${project.basedir}</spotbugsXmlOutputDirectory>
    232318                    <spotbugsXmlOutputFilename>spotbugs-jmapviewer.xml</spotbugsXmlOutputFilename>
    233319                    <effort>max</effort>
    234320                </configuration>
    235                 <!-- This is commented out until all the current bugs are fixed. TODO uncomment!
    236                 <executions>
    237                     <execution>
    238                         <id>validate</id>
    239                         <phase>validate</phase>
    240                         <goals>
    241                             <goal>check</goal>
    242                         </goals>
    243                     </execution>
    244                 </executions>
    245                 -->
    246321            </plugin>
    247322            <plugin>
    248323                <groupId>org.apache.maven.plugins</groupId>
    249324                <artifactId>maven-checkstyle-plugin</artifactId>
    250                 <version>3.4.0</version>
     325                <version>3.6.0</version>
    251326                <configuration>
    252327                    <configLocation>${project.basedir}/tools/checkstyle/jmapviewer_checks.xml</configLocation>
    253328                    <includeTestSourceDirectory>true</includeTestSourceDirectory>
    254329                    <outputFile>${project.basedir}/checkstyle-jmapviewer.xml</outputFile>
    255                     <!-- checkstyle cannot parse module-info.java yet -->
    256                     <excludes>module-info.java</excludes>
     330                    <!-- The checks are declared with severity "warning", so the build has to fail on those -->
     331                    <violationSeverity>warning</violationSeverity>
     332                    <!-- Lets the rules reference the suppressions file the same way under Maven, Ant and Eclipse -->
     333                    <propertyExpansion>config_loc=${project.basedir}/tools/checkstyle</propertyExpansion>
    257334                </configuration>
    258335                <dependencies>
    259336                    <dependency>
    … …  
    272349                    </execution>
    273350                </executions>
    274351            </plugin>
    275             <plugin>
    276                 <groupId>org.apache.maven.plugins</groupId>
    277                 <artifactId>maven-enforcer-plugin</artifactId>
    278                 <version>3.5.0</version>
    279                 <executions>
    280                     <execution>
    281                         <id>enforce-maven</id>
    282                         <goals>
    283                             <goal>enforce</goal>
    284                         </goals>
    285                         <configuration>
    286                             <rules>
    287                                 <requireMavenVersion>
    288                                     <version>3.6.3</version>
    289                                 </requireMavenVersion>
    290                             </rules>
    291                         </configuration>
    292                     </execution>
    293                 </executions>
    294             </plugin>
     352            <!-- End optional plugins -->
    295353        </plugins>
    296354    </build>
    297355
     356    <profiles>
     357        <!-- Builds the release zips below releases/, like the Ant "create_*_release_zip" targets -->
     358        <profile>
     359            <id>release</id>
     360            <build>
     361                <plugins>
     362                    <plugin>
     363                        <groupId>org.apache.maven.plugins</groupId>
     364                        <artifactId>maven-assembly-plugin</artifactId>
     365                        <version>3.8.0</version>
     366                        <configuration>
     367                            <outputDirectory>${project.basedir}/releases/${project.version}</outputDirectory>
     368                            <attach>false</attach>
     369                        </configuration>
     370                        <executions>
     371                            <execution>
     372                                <id>release-zip</id>
     373                                <phase>package</phase>
     374                                <goals>
     375                                    <goal>single</goal>
     376                                </goals>
     377                                <configuration>
     378                                    <appendAssemblyId>false</appendAssemblyId>
     379                                    <descriptors>
     380                                        <descriptor>${project.basedir}/tools/assembly/release.xml</descriptor>
     381                                    </descriptors>
     382                                </configuration>
     383                            </execution>
     384                            <execution>
     385                                <id>source-release-zip</id>
     386                                <phase>package</phase>
     387                                <goals>
     388                                    <goal>single</goal>
     389                                </goals>
     390                                <configuration>
     391                                    <descriptors>
     392                                        <descriptor>${project.basedir}/tools/assembly/source-release.xml</descriptor>
     393                                    </descriptors>
     394                                </configuration>
     395                            </execution>
     396                        </executions>
     397                    </plugin>
     398                </plugins>
     399            </build>
     400        </profile>
     401    </profiles>
     402
    298403    <reporting>
    299404        <plugins>
    300405            <plugin>
    301406                <groupId>org.apache.maven.plugins</groupId>
    302407                <artifactId>maven-surefire-report-plugin</artifactId>
    303                 <version>3.3.1</version>
     408                <version>3.6.0</version>
    304409            </plugin>
    305410            <plugin>
    306411                <groupId>org.jacoco</groupId>
  • src/module-info.java

    diff --git src/module-info.java src/module-info.java
    index f116957..2d95f0a 100644
     
    11// License: GPL. For details, see Readme.txt file.
    22module org.openstreetmap.gui.jmapviewer {
    3     requires java.desktop;
    4     requires java.logging;
     3    // java.desktop and java.logging types are part of the public API (JMapViewer is a JPanel, FeatureAdapter returns loggers)
     4    requires transitive java.desktop;
     5    requires transitive java.logging;
    56    exports org.openstreetmap.gui.jmapviewer;
    67    exports org.openstreetmap.gui.jmapviewer.checkBoxTree;
    78    exports org.openstreetmap.gui.jmapviewer.events;
  • src/org/openstreetmap/gui/jmapviewer/AbstractLayer.java

    diff --git src/org/openstreetmap/gui/jmapviewer/AbstractLayer.java src/org/openstreetmap/gui/jmapviewer/AbstractLayer.java
    index 4e5216e..57fd3db 100644
    public class AbstractLayer {  
    3636        this(parent, name, null, style);
    3737    }
    3838
     39    @SuppressWarnings("this-escape")
    3940    public AbstractLayer(LayerGroup parent, String name, String description, Style style) {
    4041        setParent(parent);
    4142        setName(name);
  • src/org/openstreetmap/gui/jmapviewer/AttributionSupport.java

    diff --git src/org/openstreetmap/gui/jmapviewer/AttributionSupport.java src/org/openstreetmap/gui/jmapviewer/AttributionSupport.java
    index 9d67ae6..4e003c7 100644
    import java.awt.Rectangle;  
    1212import java.awt.font.TextAttribute;
    1313import java.awt.geom.Rectangle2D;
    1414import java.awt.image.ImageObserver;
    15 import java.util.HashMap;
    1615import java.util.Map;
    1716
    1817import org.openstreetmap.gui.jmapviewer.interfaces.Attributed;
    1918import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
    2019
     20/**
     21 * Paints the attribution of a tile source onto the map and handles clicks on it.
     22 */
    2123public class AttributionSupport {
    2224    public static final Font ATTR_FONT = new Font("Arial", Font.PLAIN, 10);
    23     public static final Font ATTR_LINK_FONT;
     25    public static final Font ATTR_LINK_FONT = ATTR_FONT.deriveFont(Map.of(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON));
    2426    private static final int PADDING = 5;
    2527
    2628    private Attributed source;
    … … public class AttributionSupport {  
    3335    protected Rectangle attrToUBounds;
    3436    protected Rectangle attrImageBounds;
    3537
    36     static {
    37         Map<TextAttribute, Integer> aUnderline = new HashMap<>();
    38         aUnderline.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON);
    39         ATTR_LINK_FONT = ATTR_FONT.deriveFont(aUnderline);
     38    /**
     39     * Constructs a new {@code AttributionSupport}, to be initialized with {@link #initialize(Attributed)}.
     40     */
     41    public AttributionSupport() {
     42        // Nothing to do
    4043    }
    4144
    4245    public void initialize(Attributed source) {
    4346        this.source = source;
    44         boolean requireAttr = source.requiresAttribution();
    45         if (requireAttr) {
     47        if (source.requiresAttribution()) {
    4648            attrImage = source.getAttributionImage();
    4749            attrTermsText = source.getTermsOfUseText();
    4850            attrTermsUrl = source.getTermsOfUseURL();
    … … public class AttributionSupport {  
    5153            }
    5254        } else {
    5355            attrImage = null;
     56            attrTermsText = null;
    5457            attrTermsUrl = null;
    5558        }
    5659    }
    … … public class AttributionSupport {  
    7578
    7679        if (attrTermsText != null) {
    7780            Rectangle2D termsStringBounds = g.getFontMetrics().getStringBounds(attrTermsText, g);
    78             int textRealHeight = (int) termsStringBounds.getHeight();
    79             termsTextHeight = textRealHeight;
     81            termsTextHeight = (int) termsStringBounds.getHeight();
    8082            int termsTextWidth = (int) termsStringBounds.getWidth();
    81             attrToUBounds = new Rectangle(PADDING, textY - termsTextHeight + fontDescent, termsTextWidth, textRealHeight);
     83            attrToUBounds = new Rectangle(PADDING, textY - termsTextHeight + fontDescent, termsTextWidth, termsTextHeight);
    8284            drawShadedText(g, attrTermsText, PADDING, textY);
    8385        } else {
    8486            attrToUBounds = null;
    … … public class AttributionSupport {  
    9092            int imgHeight = attrImage.getHeight(observer);
    9193            int y = textY - imgHeight - termsTextHeight;
    9294            attrImageBounds = new Rectangle(PADDING, y, imgWidth, imgHeight);
    93             g.drawImage(attrImage, PADDING, y, null);
     95            g.drawImage(attrImage, PADDING, y, observer);
    9496        } else {
    9597            attrImageBounds = null;
    9698        }
    … … public class AttributionSupport {  
    139141    }
    140142
    141143    public boolean handleAttribution(Point p, boolean click) {
    142         if (source == null || !source.requiresAttribution())
     144        if (p == null || source == null || !source.requiresAttribution())
    143145            return false;
    144146
    145147        if (attrTextBounds != null && attrTextBounds.contains(p)) {
    … … public class AttributionSupport {  
    154156            String attributionImageURL = source.getAttributionImageURL();
    155157            if (attributionImageURL != null) {
    156158                if (click) {
    157                     FeatureAdapter.openLink(source.getAttributionImageURL());
     159                    FeatureAdapter.openLink(attributionImageURL);
    158160                }
    159161                return true;
    160162            }
    … … public class AttributionSupport {  
    169171        }
    170172        return false;
    171173    }
    172 
    173174}
  • src/org/openstreetmap/gui/jmapviewer/Coordinate.java

    diff --git src/org/openstreetmap/gui/jmapviewer/Coordinate.java src/org/openstreetmap/gui/jmapviewer/Coordinate.java
    index 3c9c2e2..ea6c6fb 100644
    import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;  
    1313 * A geographical coordinate consisting of latitude and longitude.
    1414 *
    1515 * @author Jan Peter Stotz
    16  *
    1716 */
    1817public class Coordinate implements ICoordinate, Serializable {
     18    // Java 14: annotate with @Serial
    1919    private static final long serialVersionUID = 1L;
    2020    private double x;
    2121    private double y;
    … … public class Coordinate implements ICoordinate, Serializable {  
    2525     * @param lat latitude in degrees
    2626     * @param lon longitude in degrees
    2727     */
     28    @SuppressWarnings("this-escape")
    2829    public Coordinate(double lat, double lon) {
    2930        setLat(lat);
    3031        setLon(lon);
    … … public class Coordinate implements ICoordinate, Serializable {  
    5051        x = lon;
    5152    }
    5253
     54    // Java 14: annotate with @Serial
    5355    private void writeObject(ObjectOutputStream out) throws IOException {
    5456        out.writeObject(x);
    5557        out.writeObject(y);
    5658    }
    5759
     60    // Java 14: annotate with @Serial
    5861    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    5962        x = (Double) in.readObject();
    6063        y = (Double) in.readObject();
    … … public class Coordinate implements ICoordinate, Serializable {  
    7376    @Override
    7477    public boolean equals(Object o) {
    7578        if (this == o) return true;
    76         if (o == null || !this.getClass().equals(o.getClass())) return false;
     79        if (o == null || !getClass().equals(o.getClass())) return false;
    7780        Coordinate that = (Coordinate) o;
    7881        return Double.compare(that.x, x) == 0 && Double.compare(that.y, y) == 0;
    7982    }
  • src/org/openstreetmap/gui/jmapviewer/DefaultMapController.java

    diff --git src/org/openstreetmap/gui/jmapviewer/DefaultMapController.java src/org/openstreetmap/gui/jmapviewer/DefaultMapController.java
    index ae79b0d..4fc9207 100644
    import java.util.Locale;  
    1414 * mouse button and zooming by double click or by mouse wheel.
    1515 *
    1616 * @author Jan Peter Stotz
    17  *
    1817 */
    1918public class DefaultMapController extends JMapController implements MouseListener, MouseMotionListener,
    20 MouseWheelListener {
     19        MouseWheelListener {
    2120
    2221    private static final int MOUSE_BUTTONS_MASK = MouseEvent.BUTTON3_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK
    23     | MouseEvent.BUTTON2_DOWN_MASK;
     22            | MouseEvent.BUTTON2_DOWN_MASK;
    2423
    2524    private static final int MAC_MOUSE_BUTTON3_MASK = MouseEvent.CTRL_DOWN_MASK | MouseEvent.BUTTON1_DOWN_MASK;
    2625
    … … MouseWheelListener {  
    5150        // Is only the selected mouse button pressed?
    5251        if ((e.getModifiersEx() & MOUSE_BUTTONS_MASK) == movementMouseButtonMask
    5352                || (isPlatformOsx() && e.getModifiersEx() == MAC_MOUSE_BUTTON3_MASK)) {
    54             Point p = e.getPoint();
    55             if (lastDragPoint != null) {
    56                 int diffx = lastDragPoint.x - p.x;
    57                 int diffy = lastDragPoint.y - p.y;
    58                 map.moveMap(diffx, diffy);
    59             }
    60             lastDragPoint = p;
     53            dragTo(e.getPoint());
     54        }
     55    }
     56
     57    /**
     58     * Moves the map by the distance from the previous drag point to {@code p}.
     59     * @param p the current mouse position
     60     */
     61    private void dragTo(Point p) {
     62        if (lastDragPoint != null) {
     63            map.moveMap(lastDragPoint.x - p.x, lastDragPoint.y - p.y);
    6164        }
     65        lastDragPoint = p;
    6266    }
    6367
    6468    @Override
    … … MouseWheelListener {  
    125129     */
    126130    public void setMovementMouseButton(int movementMouseButton) {
    127131        this.movementMouseButton = movementMouseButton;
     132        // Java 14: use a switch expression
    128133        switch (movementMouseButton) {
    129134            case MouseEvent.BUTTON1:
    130135                movementMouseButtonMask = MouseEvent.BUTTON1_DOWN_MASK;
    … … MouseWheelListener {  
    168173
    169174    @Override
    170175    public void mouseMoved(MouseEvent e) {
    171         // Mac OSX simulates with  ctrl + mouse 1  the second mouse button hence no dragging events get fired.
    172         //
     176        // Mac OSX simulates the second mouse button with ctrl + mouse 1, hence no dragging events get fired.
    173177        if (isPlatformOsx()) {
    174178            if (!movementEnabled || !isMoving)
    175179                return;
    176180            // Is only the selected mouse button pressed?
    177181            if (e.getModifiersEx() == MouseEvent.CTRL_DOWN_MASK) {
    178                 Point p = e.getPoint();
    179                 if (lastDragPoint != null) {
    180                     int diffx = lastDragPoint.x - p.x;
    181                     int diffy = lastDragPoint.y - p.y;
    182                     map.moveMap(diffx, diffy);
    183                 }
    184                 lastDragPoint = p;
     182                dragTo(e.getPoint());
    185183            }
    186184        }
    187185    }
    188186
    189187    /**
    190      * Replies true if we are currently running on OSX
     188     * Determines whether we are currently running on OSX.
    191189     *
    192190     * @return true if we are currently running on OSX
    193191     */
  • src/org/openstreetmap/gui/jmapviewer/Demo.java

    diff --git src/org/openstreetmap/gui/jmapviewer/Demo.java src/org/openstreetmap/gui/jmapviewer/Demo.java
    index 551005b..b3d1917 100644
    import javax.swing.JComboBox;  
    1313import javax.swing.JFrame;
    1414import javax.swing.JLabel;
    1515import javax.swing.JPanel;
     16import javax.swing.SwingUtilities;
    1617
    1718import org.openstreetmap.gui.jmapviewer.events.JMVCommandEvent;
    1819import org.openstreetmap.gui.jmapviewer.interfaces.JMapViewerEventListener;
    … … import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;  
    2627 * Demonstrates the usage of {@link JMapViewer}
    2728 *
    2829 * @author Jan Peter Stotz
    29  *
    3030 */
    31 public class Demo extends JFrame implements JMapViewerEventListener {
     31public final class Demo extends JFrame implements JMapViewerEventListener {
    3232
     33    // Java 14: annotate with @Serial
    3334    private static final long serialVersionUID = 1L;
    3435
    3536    private final JMapViewerTree treeMap;
    … … public class Demo extends JFrame implements JMapViewerEventListener {  
    6263        JPanel helpPanel = new JPanel();
    6364
    6465        mperpLabelName = new JLabel("Meters/Pixels: ");
    65         mperpLabelValue = new JLabel(String.format("%s", map().getMeterPerPixel()));
     66        mperpLabelValue = new JLabel(String.valueOf(map().getMeterPerPixel()));
    6667
    6768        zoomLabel = new JLabel("Zoom: ");
    68         zoomValue = new JLabel(String.format("%s", map().getZoom()));
     69        zoomValue = new JLabel(String.valueOf(map().getZoom()));
    6970
    7071        add(panel, BorderLayout.NORTH);
    7172        add(helpPanel, BorderLayout.SOUTH);
    7273        panel.add(panelTop, BorderLayout.NORTH);
    7374        panel.add(panelBottom, BorderLayout.SOUTH);
    74         JLabel helpLabel = new JLabel("Use right mouse button to move,\n "
    75                 + "left double click or mouse wheel to zoom.");
     75        JLabel helpLabel = new JLabel("Use right mouse button to move, left double click or mouse wheel to zoom.");
    7676        helpPanel.add(helpLabel);
    7777        JButton button = new JButton("setDisplayToFitMapMarkers");
    7878        button.addActionListener(e -> map().setDisplayToFitMapMarkers());
    … … public class Demo extends JFrame implements JMapViewerEventListener {  
    8282                new BingAerialTileSource(),
    8383        });
    8484        tileSourceSelector.addItemListener(e -> map().setTileSource((TileSource) e.getItem()));
    85         JComboBox<TileLoader> tileLoaderSelector;
    86         tileLoaderSelector = new JComboBox<>(new TileLoader[] {new OsmTileLoader(map())});
     85        JComboBox<TileLoader> tileLoaderSelector = new JComboBox<>(new TileLoader[] {new OsmTileLoader(map())});
    8786        tileLoaderSelector.addItemListener(e -> map().setTileLoader((TileLoader) e.getItem()));
    8887        map().setTileLoader((TileLoader) tileLoaderSelector.getSelectedItem());
    8988        panelTop.add(tileSourceSelector);
    … … public class Demo extends JFrame implements JMapViewerEventListener {  
    9291        showMapMarker.setSelected(map().getMapMarkersVisible());
    9392        showMapMarker.addActionListener(e -> map().setMapMarkerVisible(showMapMarker.isSelected()));
    9493        panelBottom.add(showMapMarker);
    95         ///
    9694        final JCheckBox showTreeLayers = new JCheckBox("Tree Layers visible");
    9795        showTreeLayers.addActionListener(e -> treeMap.setTreeVisible(showTreeLayers.isSelected()));
    9896        panelBottom.add(showTreeLayers);
    99         ///
    10097        final JCheckBox showToolTip = new JCheckBox("ToolTip visible");
    10198        showToolTip.addActionListener(e -> map().setToolTipText(null));
    10299        panelBottom.add(showToolTip);
    103         ///
    104100        final JCheckBox showTileGrid = new JCheckBox("Tile grid visible");
    105101        showTileGrid.setSelected(map().isTileGridVisible());
    106102        showTileGrid.addActionListener(e -> map().setTileGridVisible(showTileGrid.isSelected()));
    … … public class Demo extends JFrame implements JMapViewerEventListener {  
    121117
    122118        add(treeMap, BorderLayout.CENTER);
    123119
    124         //
    125120        LayerGroup germanyGroup = new LayerGroup("Germany");
    126121        Layer germanyWestLayer = germanyGroup.addLayer("Germany West");
    127122        Layer germanyEastLayer = germanyGroup.addLayer("Germany East");
    … … public class Demo extends JFrame implements JMapViewerEventListener {  
    153148        Layer wales = treeMap.addLayer("UK");
    154149        map().addMapRectangle(new MapRectangleImpl(wales, "Wales", c(53.35, -4.57), c(51.64, -2.63)));
    155150
    156         // map.setDisplayPosition(new Coordinate(49.807, 8.6), 11);
    157         // map.setTileGridVisible(true);
    158 
    159151        map().addMouseListener(new MouseAdapter() {
    160152            @Override
    161153            public void mouseClicked(MouseEvent e) {
    … … public class Demo extends JFrame implements JMapViewerEventListener {  
    170162            public void mouseMoved(MouseEvent e) {
    171163                Point p = e.getPoint();
    172164                boolean cursorHand = map().getAttribution().handleAttributionCursor(p);
    173                 if (cursorHand) {
    174                     map().setCursor(new Cursor(Cursor.HAND_CURSOR));
    175                 } else {
    176                     map().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    177                 }
     165                map().setCursor(Cursor.getPredefinedCursor(cursorHand ? Cursor.HAND_CURSOR : Cursor.DEFAULT_CURSOR));
    178166                if (showToolTip.isSelected()) map().setToolTipText(map().getPosition(p).toString());
    179167            }
    180168        });
    … … public class Demo extends JFrame implements JMapViewerEventListener {  
    192180     * @param args Main program arguments
    193181     */
    194182    public static void main(String[] args) {
    195         new Demo().setVisible(true);
     183        SwingUtilities.invokeLater(() -> new Demo().setVisible(true));
    196184    }
    197185
    198186    private void updateZoomParameters() {
     187        // the map fires events while the constructor is still running, before the labels exist
    199188        if (mperpLabelValue != null)
    200             mperpLabelValue.setText(String.format("%s", map().getMeterPerPixel()));
     189            mperpLabelValue.setText(String.valueOf(map().getMeterPerPixel()));
    201190        if (zoomValue != null)
    202             zoomValue.setText(String.format("%s", map().getZoom()));
     191            zoomValue.setText(String.valueOf(map().getZoom()));
    203192    }
    204193
    205194    @Override
    206195    public void processCommand(JMVCommandEvent command) {
    207         if (command.getCommand().equals(JMVCommandEvent.COMMAND.ZOOM) ||
    208                 command.getCommand().equals(JMVCommandEvent.COMMAND.MOVE)) {
     196        if (command.getCommand() == JMVCommandEvent.COMMAND.ZOOM || command.getCommand() == JMVCommandEvent.COMMAND.MOVE) {
    209197            updateZoomParameters();
    210198        }
    211199    }
  • src/org/openstreetmap/gui/jmapviewer/FeatureAdapter.java

    diff --git src/org/openstreetmap/gui/jmapviewer/FeatureAdapter.java src/org/openstreetmap/gui/jmapviewer/FeatureAdapter.java
    index 44650a3..b820917 100644
    public final class FeatureAdapter {  
    6161    /**
    6262     * Translation support.
    6363     */
     64    @FunctionalInterface
    6465    public interface TranslationAdapter {
    6566        /**
    6667         * Translates some text for the current locale.
    … … public final class FeatureAdapter {  
    7677         * @return the translated string.
    7778         */
    7879        String tr(String text, Object... objects);
    79         // TODO: more i18n functions
    8080    }
    8181
    8282    /**
    … … public final class FeatureAdapter {  
    217217
    218218    /**
    219219     * Reads an image using the current {@link ImageAdapter}.
    220      * @param url image URI to read
     220     * @param uri image URI to read
    221221     * @return a <code>BufferedImage</code> containing the decoded contents of the input, or <code>null</code>.
    222222     * @throws java.net.MalformedURLException if the URI could not be converted to a URL
    223223     * @throws IOException if an error occurs during reading.
    224224     */
    225     public static BufferedImage readImage(URI url) throws IOException {
    226         return imageAdapter.read(url.toURL(), false, false);
     225    public static BufferedImage readImage(URI uri) throws IOException {
     226        return imageAdapter.read(uri.toURL(), false, false);
    227227    }
    228228
    229229    /**
    … … public final class FeatureAdapter {  
    270270    }
    271271
    272272    /**
    273      * Get settings value for a certain key and provide a default value.
     273     * Get the integer settings value for a certain key and provide a default value.
    274274     * @param key the identifier for the setting
    275275     * @param def the default value. For each call of get() with a given key, the
    276      * default value must be the same. {@code def} may be null.
     276     * default value must be the same.
    277277     * @return the corresponding value if the property has been set before, {@code def} otherwise
     278     * @throws NumberFormatException if the value that has been set is not an integer
    278279     */
    279280    public static int getIntSetting(String key, int def) {
    280281        return Integer.parseInt(settingsAdapter.get(key, Integer.toString(def)));
    … … public final class FeatureAdapter {  
    294295     * Default API key support that relies on system property named {@code <imageryId>.api-key}.
    295296     */
    296297    public static class DefaultApiKeyAdapter implements ApiKeyAdapter {
     298        /**
     299         * Constructs a new {@code DefaultApiKeyAdapter}.
     300         */
     301        public DefaultApiKeyAdapter() {
     302            // Nothing to do
     303        }
     304
    297305        @Override
    298306        public String retrieveApiKey(String imageryId) {
    299307            return System.getProperty(imageryId + ".api-key");
    … … public final class FeatureAdapter {  
    304312     * Default browser support that relies on Java Desktop API.
    305313     */
    306314    public static class DefaultBrowserAdapter implements BrowserAdapter {
     315        /**
     316         * Constructs a new {@code DefaultBrowserAdapter}.
     317         */
     318        public DefaultBrowserAdapter() {
     319            // Nothing to do
     320        }
     321
    307322        @Override
    308323        public void openLink(String url) {
    309324            if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
    … … public final class FeatureAdapter {  
    322337     * Default image support that relies on Java Image IO API.
    323338     */
    324339    public static class DefaultImageAdapter implements ImageAdapter {
     340        /**
     341         * Constructs a new {@code DefaultImageAdapter}.
     342         */
     343        public DefaultImageAdapter() {
     344            // Nothing to do
     345        }
     346
    325347        @Override
    326348        public BufferedImage read(URL input, boolean readMetadata, boolean enforceTransparency) throws IOException {
    327349            return ImageIO.read(input);
    … … public final class FeatureAdapter {  
    329351    }
    330352
    331353    /**
    332      * Default "translation" support that do not really translates strings, but only takes care of formatting arguments.
     354     * Default "translation" support that does not really translate strings, but only takes care of formatting arguments.
    333355     */
    334356    public static class DefaultTranslationAdapter implements TranslationAdapter {
     357        /**
     358         * Constructs a new {@code DefaultTranslationAdapter}.
     359         */
     360        public DefaultTranslationAdapter() {
     361            // Nothing to do
     362        }
     363
    335364        @Override
    336365        public String tr(String text, Object... objects) {
    337366            return MessageFormat.format(text, objects);
    … … public final class FeatureAdapter {  
    342371     * Default logging support that relies on Java Logging API.
    343372     */
    344373    public static class DefaultLoggingAdapter implements LoggingAdapter {
     374        /**
     375         * Constructs a new {@code DefaultLoggingAdapter}.
     376         */
     377        public DefaultLoggingAdapter() {
     378            // Nothing to do
     379        }
     380
    345381        @Override
    346382        public Logger getLogger(String name) {
    347383            return Logger.getLogger(name);
    … … public final class FeatureAdapter {  
    354390    public static class DefaultSettingsAdapter implements SettingsAdapter {
    355391        private final Map<String, String> settings = new TreeMap<>();
    356392
     393        /**
     394         * Constructs a new {@code DefaultSettingsAdapter}.
     395         */
     396        public DefaultSettingsAdapter() {
     397            // Nothing to do
     398        }
     399
    357400        @Override
    358401        public String get(String key, String def) {
    359402            return settings.getOrDefault(key, def);
  • src/org/openstreetmap/gui/jmapviewer/JMapController.java

    diff --git src/org/openstreetmap/gui/jmapviewer/JMapController.java src/org/openstreetmap/gui/jmapviewer/JMapController.java
    index 8de5300..76d5c91 100644
    public abstract class JMapController {  
    2121
    2222    protected JMapViewer map;
    2323
    24     public JMapController(JMapViewer map) {
     24    /**
     25     * Constructs a new {@code JMapController} and registers it as listener of the map,
     26     * for each of the listener interfaces that it implements.
     27     * @param map the map to control
     28     */
     29    @SuppressWarnings("this-escape")
     30    protected JMapController(JMapViewer map) {
    2531        this.map = map;
     32        // Java 16: use instanceof pattern matching instead of the casts
    2633        if (this instanceof MouseListener)
    2734            map.addMouseListener((MouseListener) this);
    2835        if (this instanceof MouseWheelListener)
    … … public abstract class JMapController {  
    3037        if (this instanceof MouseMotionListener)
    3138            map.addMouseMotionListener((MouseMotionListener) this);
    3239    }
    33 
    3440}
  • src/org/openstreetmap/gui/jmapviewer/JMapViewer.java

    diff --git src/org/openstreetmap/gui/jmapviewer/JMapViewer.java src/org/openstreetmap/gui/jmapviewer/JMapViewer.java
    index d998d03..3411279 100644
    import java.awt.Font;  
    66import java.awt.Graphics;
    77import java.awt.Insets;
    88import java.awt.Point;
    9 import java.awt.event.MouseEvent;
    109import java.io.IOException;
    1110import java.net.URL;
    1211import java.util.ArrayList;
    … … import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource;  
    4241 */
    4342public class JMapViewer extends JPanel implements TileLoaderListener {
    4443
     44    // Java 14: annotate with @Serial
    4545    private static final long serialVersionUID = 1L;
    4646
    4747    /** whether debug mode is enabled or not */
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    5353    /**
    5454     * Vectors for clock-wise tile painting
    5555     */
    56     private static final Point[] move = {new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1)};
     56    private static final Point[] MOVE = {new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1)};
    5757
    5858    /** Maximum zoom level */
    5959    public static final int MAX_ZOOM = 24;
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    110110     * Creates a standard {@link JMapViewer} instance that can be controlled via
    111111     * mouse: hold right mouse button for moving, double click left mouse button
    112112     * or use mouse wheel for zooming. Loaded tiles are stored in a
    113      * {@link MemoryTileCache} and the tile loader uses 4 parallel threads for
    114      * retrieving the tiles.
     113     * {@link MemoryTileCache} and retrieved by an {@link OsmTileLoader}.
    115114     */
     115    @SuppressWarnings("this-escape")
    116116    public JMapViewer() {
    117117        this(new MemoryTileCache());
    118118        new DefaultMapController(this);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    121121    /**
    122122     * Creates a new {@link JMapViewer} instance.
    123123     * @param tileCache The cache where to store tiles
    124      * @param downloadThreadCount not used anymore
    125      * @deprecated use {@link #JMapViewer(TileCache)}
    126      */
    127     @Deprecated
    128     public JMapViewer(TileCache tileCache, int downloadThreadCount) {
    129         this(tileCache);
    130     }
    131 
    132     /**
    133      * Creates a new {@link JMapViewer} instance.
    134      * @param tileCache The cache where to store tiles
    135      *
    136124     */
     125    @SuppressWarnings("this-escape")
    137126    public JMapViewer(TileCache tileCache) {
    138127        tileSource = new OsmTileSource.Mapnik();
    139128        tileController = new TileController(tileSource, tileCache, this);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    151140        setDisplayPosition(new Coordinate(50, 9), 3);
    152141    }
    153142
    154     @Override
    155     public String getToolTipText(MouseEvent event) {
    156         return super.getToolTipText(event);
    157     }
    158 
    159143    protected void initializeZoomSlider() {
    160144        zoomSlider = new JSlider(MIN_ZOOM, tileController.getTileSource().getMaxZoom());
    161145        zoomSlider.setOrientation(JSlider.VERTICAL);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    309293
    310294        if (markers && mapMarkerList != null) {
    311295            synchronized (this) {
    312                 for (MapMarker marker : mapMarkerList) {
     296                for (MapMarker marker : snapshot(mapMarkerList)) {
    313297                    if (marker.isVisible()) {
    314298                        Point p = tileSource.latLonToXY(marker.getCoordinate(), mapZoomMax);
    315299                        xMax = Math.max(xMax, p.x);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    323307
    324308        if (rectangles && mapRectangleList != null) {
    325309            synchronized (this) {
    326                 for (MapRectangle rectangle : mapRectangleList) {
     310                for (MapRectangle rectangle : snapshot(mapRectangleList)) {
    327311                    if (rectangle.isVisible()) {
    328312                        Point bottomRight = tileSource.latLonToXY(rectangle.getBottomRight(), mapZoomMax);
    329313                        Point topLeft = tileSource.latLonToXY(rectangle.getTopLeft(), mapZoomMax);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    338322
    339323        if (polygons && mapPolygonList != null) {
    340324            synchronized (this) {
    341                 for (MapPolygon polygon : mapPolygonList) {
     325                for (MapPolygon polygon : snapshot(mapPolygonList)) {
    342326                    if (polygon.isVisible()) {
    343327                        for (ICoordinate c : polygon.getPoints()) {
    344328                            Point p = tileSource.latLonToXY(c, mapZoomMax);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    473457    }
    474458
    475459    /**
    476      * Calculates the position on the map of a given coordinate
     460     * Calculates the y position on the map of a coordinate that is shifted by an offset in latitude.
    477461     *
    478462     * @param lat Latitude
    479463     * @param lon longitude
    480464     * @param offset Offset respect Latitude
    481465     * @param checkOutside check if the point is outside the displayed area
    482      * @return Integer the radius in pixels
     466     * @return the y position in pixels, or <code>null</code> if it is not visible and checkOutside is set to <code>true</code>
    483467     */
    484468    public Integer getLatOffset(double lat, double lon, double offset, boolean checkOutside) {
    485469        Point p = tileSource.latLonToXY(lat + offset, lon, zoom);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    491475    }
    492476
    493477    /**
    494      * Calculates the position on the map of a given coordinate
     478     * Calculates the radius of a map marker on the map.
    495479     *
    496480     * @param marker MapMarker object that define the x,y coordinate
    497      * @param p coordinate
    498      * @return Integer the radius in pixels
     481     * @param p position of the marker on the map
     482     * @return the radius in pixels, or <code>null</code> if it cannot be determined
    499483     */
    500484    public Integer getRadius(MapMarker marker, Point p) {
    501485        if (marker.getMarkerStyle() == MapMarker.STYLE.FIXED)
    502486            return (int) marker.getRadius();
    503487        else if (p != null) {
    504             Integer radius = getLatOffset(marker.getLat(), marker.getLon(), marker.getRadius(), false);
    505             radius = radius == null ? null : (p.y - radius);
    506             return radius;
     488            Integer offset = getLatOffset(marker.getLat(), marker.getLon(), marker.getRadius(), false);
     489            return offset == null ? null : (p.y - offset);
    507490        } else
    508491            return null;
    509492    }
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    593576            } else {
    594577                iMove = 0;
    595578            }
    596         } // calculate the visibility borders
     579        }
     580        // calculate the visibility borders
    597581        int xMin = -tilesize;
    598582        int yMin = -tilesize;
    599583        int xMax = getWidth();
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    617601                        Tile tile;
    618602                        if (scrollWrapEnabled) {
    619603                            // in case tilex is out of bounds, grab the tile to use for wrapping
    620                             int tilexWrap = ((tilex % gridLength) + gridLength) % gridLength;
     604                            int tilexWrap = Math.floorMod(tilex, gridLength);
    621605                            tile = tileController.getTile(tilexWrap, tiley, zoom);
    622606                        } else {
    623607                            tile = tileController.getTile(tilex, tiley, zoom);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    630614                        }
    631615                        painted = true;
    632616                    }
    633                     Point p = move[iMove];
     617                    Point p = MOVE[iMove];
    634618                    posx += p.x * tilesize;
    635619                    posy += p.y * tilesize;
    636620                    tilex += p.x;
    637621                    tiley += p.y;
    638622                }
    639                 iMove = (iMove + 1) % move.length;
     623                iMove = (iMove + 1) % MOVE.length;
    640624            }
    641625        }
    642626        // outer border of the map
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    648632            g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
    649633        }
    650634
    651         // g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20);
    652 
    653635        // keep x-coordinates from growing without bound if scroll-wrap is enabled
    654636        if (scrollWrapEnabled) {
    655637            center.x = center.x % mapSize;
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    657639
    658640        if (mapPolygonsVisible && mapPolygonList != null) {
    659641            synchronized (this) {
    660                 for (MapPolygon polygon : mapPolygonList) {
     642                for (MapPolygon polygon : snapshot(mapPolygonList)) {
    661643                    if (polygon.isVisible())
    662644                        paintPolygon(g, polygon);
    663645                }
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    666648
    667649        if (mapRectanglesVisible && mapRectangleList != null) {
    668650            synchronized (this) {
    669                 for (MapRectangle rectangle : mapRectangleList) {
     651                for (MapRectangle rectangle : snapshot(mapRectangleList)) {
    670652                    if (rectangle.isVisible())
    671653                        paintRectangle(g, rectangle);
    672654                }
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    675657
    676658        if (mapMarkersVisible && mapMarkerList != null) {
    677659            synchronized (this) {
    678                 for (MapMarker marker : mapMarkerList) {
     660                for (MapMarker marker : snapshot(mapMarkerList)) {
    679661                    if (marker.isVisible())
    680662                        paintMarker(g, marker);
    681663                }
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    685667        attribution.paintAttribution(g, getWidth(), getHeight(), getPosition(0, 0), getPosition(getWidth(), getHeight()), zoom, this);
    686668    }
    687669
     670    /**
     671     * Copies a list of map objects, so that it can be iterated while other threads change it.
     672     * Copying a {@link Collections#synchronizedList} is atomic, unlike iterating over it.
     673     * @param <T> type of the map objects
     674     * @param list list to copy
     675     * @return a copy of the list
     676     */
     677    private static <T> List<T> snapshot(List<T> list) {
     678        return new ArrayList<>(list);
     679    }
     680
    688681    /**
    689682     * Paint a single marker.
    690683     * @param g Graphics used for painting
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    818811        center.x += x;
    819812        center.y += y;
    820813        repaint();
    821         this.fireJMVEvent(new JMVCommandEvent(COMMAND.MOVE, this));
     814        fireJMVEvent(new JMVCommandEvent(COMMAND.MOVE, this));
    822815    }
    823816
    824817    /**
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    870863                || zoom == this.zoom)
    871864            return;
    872865        ICoordinate zoomPos = getPosition(mapPoint);
    873         tileController.cancelOutstandingJobs(); // Clearing outstanding load
    874         // requests
     866        tileController.cancelOutstandingJobs(); // Clearing outstanding load requests
    875867        setDisplayPosition(mapPoint, zoomPos, zoom);
    876868
    877         this.fireJMVEvent(new JMVCommandEvent(COMMAND.ZOOM, this));
     869        fireJMVEvent(new JMVCommandEvent(COMMAND.ZOOM, this));
    878870    }
    879871
    880872    /**
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    10671059        repaint();
    10681060    }
    10691061
    1070     /**
    1071      * Sets whether zoom controls are displayed or not.
    1072      * @param visible {@code true} if zoom controls are displayed, {@code false} otherwise
    1073      * @deprecated use {@link #setZoomControlsVisible(boolean)}
    1074      */
    1075     @Deprecated
    1076     public void setZoomContolsVisible(boolean visible) {
    1077         setZoomControlsVisible(visible);
    1078     }
    1079 
    10801062    /**
    10811063     * Sets whether zoom controls are displayed or not.
    10821064     * @param visible {@code true} if zoom controls are displayed, {@code false} otherwise
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    11821164    }
    11831165
    11841166    /**
    1185      * Returns the zoom controls apparence style (horizontal/vertical).
     1167     * Returns the zoom controls appearance style (horizontal/vertical).
    11861168     * @return {@link ZOOM_BUTTON_STYLE#VERTICAL} or {@link ZOOM_BUTTON_STYLE#HORIZONTAL}
    11871169     */
    11881170    public ZOOM_BUTTON_STYLE getZoomButtonStyle() {
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    11901172    }
    11911173
    11921174    /**
    1193      * Sets the zoom controls apparence style (horizontal/vertical).
     1175     * Sets the zoom controls appearance style (horizontal/vertical).
    11941176     * @param style {@link ZOOM_BUTTON_STYLE#VERTICAL} or {@link ZOOM_BUTTON_STYLE#HORIZONTAL}
    11951177     */
    11961178    public void setZoomButtonStyle(ZOOM_BUTTON_STYLE style) {
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    11981180        if (zoomSlider == null || zoomInButton == null || zoomOutButton == null) {
    11991181            return;
    12001182        }
     1183        // Java 14: use a switch expression with arrow labels
    12011184        switch (style) {
    12021185        case VERTICAL:
    12031186            zoomSlider.setBounds(10, 27, 30, 150);
    … … public class JMapViewer extends JPanel implements TileLoaderListener {  
    12671250     * @param evt event to dispatch
    12681251     */
    12691252    private void fireJMVEvent(JMVCommandEvent evt) {
    1270         Object[] listeners = evtListenerList.getListenerList();
    1271         for (int i = 0; i < listeners.length; i += 2) {
    1272             if (listeners[i] == JMapViewerEventListener.class) {
    1273                 ((JMapViewerEventListener) listeners[i + 1]).processCommand(evt);
    1274             }
     1253        // getListeners returns the most recently added listener first, notify in the order they were added
     1254        JMapViewerEventListener[] listeners = evtListenerList.getListeners(JMapViewerEventListener.class);
     1255        for (int i = listeners.length - 1; i >= 0; i--) {
     1256            listeners[i].processCommand(evt);
    12751257        }
    12761258    }
    12771259}
  • src/org/openstreetmap/gui/jmapviewer/JMapViewerRuntimeException.java

    diff --git src/org/openstreetmap/gui/jmapviewer/JMapViewerRuntimeException.java src/org/openstreetmap/gui/jmapviewer/JMapViewerRuntimeException.java
    index fc6222a..6746a65 100644
    public class JMapViewerRuntimeException extends RuntimeException {  
    1212     * @see Exception#Exception()
    1313     */
    1414    public JMapViewerRuntimeException() {
    15         super();
    1615    }
    1716
    1817    /**
  • src/org/openstreetmap/gui/jmapviewer/JMapViewerTree.java

    diff --git src/org/openstreetmap/gui/jmapviewer/JMapViewerTree.java src/org/openstreetmap/gui/jmapviewer/JMapViewerTree.java
    index 20e44d7..1ff002e 100644
    import java.awt.BorderLayout;  
    55import java.awt.Dimension;
    66import java.awt.event.MouseAdapter;
    77import java.awt.event.MouseEvent;
    8 import java.util.List;
    98
    109import javax.swing.JLabel;
    1110import javax.swing.JMenuItem;
    … … import org.openstreetmap.gui.jmapviewer.interfaces.MapObject;  
    2423 * @author galo
    2524 */
    2625public class JMapViewerTree extends JPanel {
    27     /** Serial Version UID */
     26    // Java 14: annotate with @Serial
    2827    private static final long serialVersionUID = 3050203054402323972L;
    2928
    3029    private final JMapViewer map;
    … … public class JMapViewerTree extends JPanel {  
    3635        this(name, false);
    3736    }
    3837
     38    @SuppressWarnings("this-escape")
    3939    public JMapViewerTree(String name, boolean treeVisible) {
    40         super();
    4140        splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
    4241
    4342        tree = new CheckBoxTree(name);
    … … public class JMapViewerTree extends JPanel {  
    4948        splitPane.setOneTouchExpandable(true);
    5049        splitPane.setDividerLocation(150);
    5150
    52         //Provide minimum sizes for the two components in the split pane
    53         Dimension minimumSize = new Dimension(100, 50);
    54         //tree.setMinimumSize(minimumSize);
    55         map.setMinimumSize(minimumSize);
     51        map.setMinimumSize(new Dimension(100, 50));
    5652        createRefresh();
    5753        setLayout(new BorderLayout());
    5854        setTreeVisible(treeVisible);
    … … public class JMapViewerTree extends JPanel {  
    7167                if (e.isPopupTrigger()) {
    7268                    AbstractLayer layer = ((CheckBoxNodePanel) e.getComponent()).getData().getAbstractLayer();
    7369                    if (layer != null)
    74                         JMapViewerTree.this.createPopupMenu(layer).show(e.getComponent(), e.getX(), e.getY());
     70                        createPopupMenu(layer).show(e.getComponent(), e.getX(), e.getY());
    7571                }
    7672            }
    7773        });
    … … public class JMapViewerTree extends JPanel {  
    8177        JMenuItem menuItemShow = new JMenuItem("show texts");
    8278        JMenuItem menuItemHide = new JMenuItem("hide texts");
    8379
    84         //Create the popup menu.
    8580        JPopupMenu popup = new JPopupMenu();
    8681
    8782        // Create items
    … … public class JMapViewerTree extends JPanel {  
    107102
    108103    private static void setVisibleTexts(AbstractLayer layer, boolean visible) {
    109104        layer.setVisibleTexts(visible);
     105        // Java 16: use instanceof pattern matching instead of the casts
    110106        if (layer instanceof LayerGroup) {
    111107            LayerGroup group = (LayerGroup) layer;
    112108            if (group.getLayers() != null)
    … … public class JMapViewerTree extends JPanel {  
    118114
    119115    public Layer addLayer(String name) {
    120116        Layer layer = new Layer(name);
    121         this.addLayer(layer);
     117        addLayer(layer);
    122118        return layer;
    123119    }
    124120
    … … public class JMapViewerTree extends JPanel {  
    128124    }
    129125
    130126    public JMapViewerTree addLayer(MapObject element) {
    131         //element.getLayer().add(element);
    132127        return addLayer(element.getLayer());
    133128    }
    134129
    … … public class JMapViewerTree extends JPanel {  
    137132        return element.getLayer();
    138133    }
    139134
    140     public static int size(List<?> list) {
    141         return list == null ? 0 : list.size();
    142     }
    143 
    144135    public JMapViewer getViewer() {
    145136        return map;
    146137    }
    … … public class JMapViewerTree extends JPanel {  
    149140        return tree;
    150141    }
    151142
    152     public void addMapObject(MapObject o){
    153 
    154     }
    155 
    156143    public void setTreeVisible(boolean visible) {
    157144        removeAll();
    158145        revalidate();
    … … public class JMapViewerTree extends JPanel {  
    172159            }
    173160
    174161            @Override
    175             public void treeNodesInserted(TreeModelEvent arg0) {
    176                 // TODO Auto-generated method stub
     162            public void treeNodesInserted(TreeModelEvent e) {
     163                // Nothing to do
    177164            }
    178165
    179166            @Override
    180             public void treeNodesRemoved(TreeModelEvent arg0) {
    181                 // TODO Auto-generated method stub
     167            public void treeNodesRemoved(TreeModelEvent e) {
     168                // Nothing to do
    182169            }
    183170
    184171            @Override
    185             public void treeStructureChanged(TreeModelEvent arg0) {
    186                 // TODO Auto-generated method stub
     172            public void treeStructureChanged(TreeModelEvent e) {
     173                // Nothing to do
    187174            }
    188175        });
    189176    }
  • src/org/openstreetmap/gui/jmapviewer/LayerGroup.java

    diff --git src/org/openstreetmap/gui/jmapviewer/LayerGroup.java src/org/openstreetmap/gui/jmapviewer/LayerGroup.java
    index 47b158b..e26dc6c 100644
    public class LayerGroup extends AbstractLayer {  
    2727    }
    2828
    2929    public LayerGroup(LayerGroup parent, String name, String description, Style style) {
    30         super(name, description, style);
     30        super(parent, name, description, style);
    3131    }
    3232
    3333    public List<AbstractLayer> getLayers() {
  • src/org/openstreetmap/gui/jmapviewer/MapMarkerCircle.java

    diff --git src/org/openstreetmap/gui/jmapviewer/MapMarkerCircle.java src/org/openstreetmap/gui/jmapviewer/MapMarkerCircle.java
    index 0fc018d..8c2863a 100644
    import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;  
    1616 * color.
    1717 *
    1818 * @author Jan Peter Stotz
    19  *
    2019 */
    2120public class MapMarkerCircle extends MapObjectImpl implements MapMarker {
    2221
    … … public class MapMarkerCircle extends MapObjectImpl implements MapMarker {  
    128127
    129128    @Override
    130129    public void paint(Graphics g, Point position, int radius) {
    131         int sizeH = radius;
    132         int size = sizeH * 2;
     130        int size = radius * 2;
    133131
     132        // Java 16: use instanceof pattern matching instead of the casts
    134133        if (g instanceof Graphics2D && getBackColor() != null) {
    135134            Graphics2D g2 = (Graphics2D) g;
    136135            Composite oldComposite = g2.getComposite();
    137136            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
    138137            g2.setPaint(getBackColor());
    139             g.fillOval(position.x - sizeH, position.y - sizeH, size, size);
     138            g.fillOval(position.x - radius, position.y - radius, size, size);
    140139            g2.setComposite(oldComposite);
    141140        }
    142141        g.setColor(getColor());
    143         g.drawOval(position.x - sizeH, position.y - sizeH, size, size);
     142        g.drawOval(position.x - radius, position.y - radius, size, size);
    144143
    145144        if (getLayer() == null || getLayer().isVisibleTexts()) paintText(g, position);
    146145    }
  • src/org/openstreetmap/gui/jmapviewer/MapMarkerDot.java

    diff --git src/org/openstreetmap/gui/jmapviewer/MapMarkerDot.java src/org/openstreetmap/gui/jmapviewer/MapMarkerDot.java
    index 2765a33..fa28c08 100644
    import java.awt.Color;  
    66import org.openstreetmap.gui.jmapviewer.interfaces.MapMarker;
    77
    88/**
    9  * A simple implementation of the {@link MapMarker} interface. Each map marker
    10  * is painted as a circle with a black border line and filled with a specified
    11  * color.
     9 * A {@link MapMarker} painted as a small circle of fixed size ({@link #DOT_RADIUS}), by default with a
     10 * black border line and filled yellow.
    1211 *
    1312 * @author Jan Peter Stotz
    14  *
    1513 */
    1614public class MapMarkerDot extends MapMarkerCircle {
    1715
    … … public class MapMarkerDot extends MapMarkerCircle {  
    3331        this(layer, name, coord, getDefaultStyle());
    3432    }
    3533
     34    @SuppressWarnings("this-escape")
    3635    public MapMarkerDot(Color color, double lat, double lon) {
    3736        this(null, null, lat, lon);
    3837        setColor(color);
  • src/org/openstreetmap/gui/jmapviewer/MapObjectImpl.java

    diff --git src/org/openstreetmap/gui/jmapviewer/MapObjectImpl.java src/org/openstreetmap/gui/jmapviewer/MapObjectImpl.java
    index d89bb97..41cd087 100644
    public abstract class MapObjectImpl {  
    2424    }
    2525
    2626    public MapObjectImpl(Layer layer, String name, Style style) {
    27         super();
    2827        this.layer = layer;
    2928        this.name = name;
    3029        this.style = style;
    … … public abstract class MapObjectImpl {  
    5251
    5352    public Color getColor() {
    5453        Style styleAssigned = getStyleAssigned();
    55         return styleAssigned == null ? null : getStyleAssigned().getColor();
     54        return styleAssigned == null ? null : styleAssigned.getColor();
    5655    }
    5756
    5857    public void setColor(Color color) {
    … … public abstract class MapObjectImpl {  
    6261
    6362    public Color getBackColor() {
    6463        Style styleAssigned = getStyleAssigned();
    65         return styleAssigned == null ? null : getStyleAssigned().getBackColor();
     64        return styleAssigned == null ? null : styleAssigned.getBackColor();
    6665    }
    6766
    6867    public void setBackColor(Color backColor) {
    … … public abstract class MapObjectImpl {  
    7271
    7372    public Stroke getStroke() {
    7473        Style styleAssigned = getStyleAssigned();
    75         return styleAssigned == null ? null : getStyleAssigned().getStroke();
     74        return styleAssigned == null ? null : styleAssigned.getStroke();
    7675    }
    7776
    7877    public void setStroke(Stroke stroke) {
    … … public abstract class MapObjectImpl {  
    8281
    8382    public Font getFont() {
    8483        Style styleAssigned = getStyleAssigned();
    85         return styleAssigned == null ? null : getStyleAssigned().getFont();
     84        return styleAssigned == null ? null : styleAssigned.getFont();
    8685    }
    8786
    8887    public void setFont(Font font) {
    … … public abstract class MapObjectImpl {  
    121120    public void paintText(Graphics g, Point position) {
    122121        if (name != null && g != null && position != null) {
    123122            if (getFont() == null) {
    124                 Font f = getDefaultFont();
    125                 setFont(new Font(f.getName(), Font.BOLD, f.getSize()));
     123                setFont(getDefaultFont());
    126124            }
    127125            g.setColor(Color.DARK_GRAY);
    128126            g.setFont(getFont());
  • src/org/openstreetmap/gui/jmapviewer/MapPolygonImpl.java

    diff --git src/org/openstreetmap/gui/jmapviewer/MapPolygonImpl.java src/org/openstreetmap/gui/jmapviewer/MapPolygonImpl.java
    index fe62723..4cc9887 100644
    public class MapPolygonImpl extends MapObjectImpl implements MapPolygon {  
    7474        Color oldColor = g.getColor();
    7575        g.setColor(getColor());
    7676
     77        // Java 16: use instanceof pattern matching instead of the casts
    7778        Stroke oldStroke = null;
    7879        if (g instanceof Graphics2D) {
    7980            Graphics2D g2 = (Graphics2D) g;
  • src/org/openstreetmap/gui/jmapviewer/MapRectangleImpl.java

    diff --git src/org/openstreetmap/gui/jmapviewer/MapRectangleImpl.java src/org/openstreetmap/gui/jmapviewer/MapRectangleImpl.java
    index 6ba140b..5d569c7 100644
    public class MapRectangleImpl extends MapObjectImpl implements MapRectangle {  
    5252        // Prepare graphics
    5353        Color oldColor = g.getColor();
    5454        g.setColor(getColor());
     55        // Java 16: use instanceof pattern matching instead of the casts
    5556        Stroke oldStroke = null;
    5657        if (g instanceof Graphics2D) {
    5758            Graphics2D g2 = (Graphics2D) g;
    … … public class MapRectangleImpl extends MapObjectImpl implements MapRectangle {  
    5960            g2.setStroke(getStroke());
    6061        }
    6162        // Draw
    62         g.drawRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
     63        int width = bottomRight.x - topLeft.x;
     64        int height = bottomRight.y - topLeft.y;
     65        g.drawRect(topLeft.x, topLeft.y, width, height);
    6366        // Restore graphics
    6467        g.setColor(oldColor);
    6568        if (g instanceof Graphics2D) {
    6669            ((Graphics2D) g).setStroke(oldStroke);
    6770        }
    68         int width = bottomRight.x-topLeft.x;
    69         int height = bottomRight.y-topLeft.y;
    7071        Point p = new Point(topLeft.x+(width/2), topLeft.y+(height/2));
    7172        if (getLayer() == null || getLayer().isVisibleTexts()) paintText(g, p);
    7273    }
  • src/org/openstreetmap/gui/jmapviewer/MemoryTileCache.java

    diff --git src/org/openstreetmap/gui/jmapviewer/MemoryTileCache.java src/org/openstreetmap/gui/jmapviewer/MemoryTileCache.java
    index 97106fa..e32df67 100644
     
    11// License: GPL. For details, see Readme.txt file.
    22package org.openstreetmap.gui.jmapviewer;
    33
    4 import java.util.Collection;
    54import java.util.HashMap;
     5import java.util.List;
    66import java.util.Map;
    7 import java.util.logging.Level;
    8 import java.util.logging.Logger;
    97import java.util.stream.Collectors;
    108
    119import org.openstreetmap.gui.jmapviewer.interfaces.TileCache;
    … … import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;  
    2018 */
    2119public class MemoryTileCache implements TileCache {
    2220
    23     private static final Logger LOGGER = Logger.getLogger(MemoryTileCache.class.getName());
    2421    /**
    25      * @deprecated since 2.20 (create a class specific logger)
    26      */
    27     @Deprecated
    28     protected static final Logger log = LOGGER;
    29 
    30     /**
    31      * Default cache size
     22     * Maximum number of tiles in the cache
    3223     */
    3324    protected int cacheSize;
    3425
    … … public class MemoryTileCache implements TileCache {  
    5950    @Override
    6051    public synchronized void addTile(Tile tile) {
    6152        CacheEntry entry = createCacheEntry(tile);
    62         if (hash.put(tile.getKey(), entry) == null) {
    63             // only if hash hadn't had the element, add it to LRU
    64             lruTiles.addFirst(entry);
    65             if (hash.size() > cacheSize || lruTiles.getElementCount() > cacheSize) {
    66                 removeOldEntries();
    67             }
     53        CacheEntry replaced = hash.put(tile.getKey(), entry);
     54        if (replaced != null) {
     55            // the replaced entry must leave the LRU list too, or the list keeps it (and its image) forever
     56            lruTiles.removeEntry(replaced);
    6857        }
     58        lruTiles.addFirst(entry);
     59        removeOldEntries();
    6960    }
    7061
    7162    @Override
    … … public class MemoryTileCache implements TileCache {  
    8172     * Removes the least recently used tiles
    8273     */
    8374    protected synchronized void removeOldEntries() {
    84         try {
    85             while (lruTiles.getElementCount() > cacheSize) {
    86                 removeEntry(lruTiles.getLastElement());
    87             }
    88         } catch (NullPointerException e) {
    89             LOGGER.log(Level.WARNING, e.getMessage(), e);
     75        // with a negative cache size, the loop would go on with an empty list and fail on its null last element
     76        while (lruTiles.getElementCount() > Math.max(cacheSize, 0)) {
     77            removeEntry(lruTiles.getLastElement());
    9078        }
    9179    }
    9280
    … … public class MemoryTileCache implements TileCache {  
    10795
    10896    @Override
    10997    public synchronized void clearErrorTiles() {
    110         Collection<CacheEntry> toRemove = hash.values().stream().filter(cacheEntry -> cacheEntry.tile.hasError()).collect(Collectors.toList());
    111         hash.values().removeAll(toRemove);
    112         toRemove.forEach(lruTiles::removeEntry);
     98        // Java 16: use Stream.toList()
     99        List<CacheEntry> toRemove = hash.values().stream().filter(cacheEntry -> cacheEntry.tile.hasError()).collect(Collectors.toList());
     100        toRemove.forEach(this::removeEntry);
    113101    }
    114102
    115103    @Override
    … … public class MemoryTileCache implements TileCache {  
    125113    /**
    126114     * Changes the maximum number of {@link Tile} objects that this cache holds.
    127115     *
    128      * @param cacheSize
    129      *            new maximum number of tiles
     116     * @param cacheSize new maximum number of tiles
    130117     */
    131118    public synchronized void setCacheSize(int cacheSize) {
    132119        this.cacheSize = cacheSize;
    133         if (hash.size() > cacheSize)
    134             removeOldEntries();
     120        removeOldEntries();
    135121    }
    136122
    137123    /**
    … … public class MemoryTileCache implements TileCache {  
    155141
    156142    /**
    157143     * Special implementation of a double linked list for {@link CacheEntry}
    158      * elements. It supports element removal in constant time - in difference to
    159      * the Java implementation which needs O(n).
     144     * elements. It supports element removal in constant time - unlike
     145     * {@link java.util.LinkedList}, which needs O(n).
    160146     *
    161147     * @author Jan Peter Stotz
    162148     */
  • src/org/openstreetmap/gui/jmapviewer/OsmMercator.java

    diff --git src/org/openstreetmap/gui/jmapviewer/OsmMercator.java src/org/openstreetmap/gui/jmapviewer/OsmMercator.java
    index b5354a0..3476381 100644
    public class OsmMercator {  
    1515    /**
    1616     * default tile size
    1717     */
    18     public static final int DEFAUL_TILE_SIZE = 256;
     18    public static final int DEFAULT_TILE_SIZE = 256;
     19    /**
     20     * default tile size
     21     * @deprecated use {@link #DEFAULT_TILE_SIZE}, this is the misspelled name
     22     */
     23    @Deprecated
     24    public static final int DEFAUL_TILE_SIZE = DEFAULT_TILE_SIZE;
    1925    /** maximum latitude (north) for mercator display */
    2026    public static final double MAX_LAT = 85.05112877980659;
    2127    /** minimum latitude (south) for mercator display */
    22     public static final double MIN_LAT = -85.05112877980659;
     28    public static final double MIN_LAT = -MAX_LAT;
    2329    /** equatorial earth radius for EPSG:3857 (Mercator) */
    2430    public static final double EARTH_RADIUS = 6_378_137;
    2531
    … … public class OsmMercator {  
    2935    public static final OsmMercator MERCATOR_256 = new OsmMercator();
    3036
    3137    /** tile size of the displayed tiles. Use long so all calculations will be in 64bit to properly handle zooms above 22 for 256 tile size */
    32     private long tileSize = DEFAUL_TILE_SIZE;
     38    private long tileSize = DEFAULT_TILE_SIZE;
    3339
    3440    /**
    3541     * Creates instance with default tile size of 256
    … … public class OsmMercator {  
    4551        this.tileSize = tileSize;
    4652    }
    4753
     54    /**
     55     * Returns the radius of the earth in pixels at the given zoom level.
     56     * @param aZoomlevel zoom level
     57     * @return radius in pixels
     58     */
    4859    public double radius(int aZoomlevel) {
    49         return (tileSize * (1 << aZoomlevel)) / (2.0 * Math.PI);
     60        return getMaxPixels(aZoomlevel) / (2.0 * Math.PI);
    5061    }
    5162
    5263    /**
    53      * Returns the absolut number of pixels in y or x, defined as: 2^Zoomlevel *
     64     * Returns the absolute number of pixels in y or x, defined as: 2^Zoomlevel *
    5465     * tileSize where tileSize is the width of a tile in pixels
    5566     *
    5667     * @param aZoomlevel zoom level to request pixel data
    … … public class OsmMercator {  
    6071        return tileSize * (1L << aZoomlevel);
    6172    }
    6273
     74    /**
     75     * Returns the x offset of the map space origin from longitude 0, in pixels.
     76     * @param aZoomlevel zoom level
     77     * @return false easting in pixels
     78     */
    6379    public long falseEasting(int aZoomlevel) {
    6480        return getMaxPixels(aZoomlevel) / 2;
    6581    }
    6682
     83    /**
     84     * Returns the y offset of the map space origin from the equator, in pixels.
     85     * @param aZoomlevel zoom level
     86     * @return false northing in pixels (negative)
     87     */
    6788    public long falseNorthing(int aZoomlevel) {
    68         return -1 * getMaxPixels(aZoomlevel) / 2;
     89        return -getMaxPixels(aZoomlevel) / 2;
    6990    }
    7091
    7192    /**
    … … public class OsmMercator {  
    7596     * @param y1 the first y coordinate
    7697     * @param x2 the second x coordinate
    7798     * @param y2 the second y coordinate
    78      *
    7999     * @param zoomLevel the zoom level
    80100     * @return the distance
    81101     */
    … … public class OsmMercator {  
    130150     */
    131151    public double lonToX(double aLongitude, int aZoomlevel) {
    132152        long mp = getMaxPixels(aZoomlevel);
    133         double x = (mp * (aLongitude + 180L)) / 360L;
     153        double x = mp * (aLongitude + 180) / 360;
    134154        return Math.min(x, mp);
    135155    }
    136156
    … … public class OsmMercator {  
    139159     * <p>
    140160     * Mathematical optimization<br>
    141161     * <code>
    142      * log(u) := log((1.0 + sin(toRadians(aLat))) / (1.0 - sin(toRadians(aLat))<br>
    143      *
    144      * y = -1 * (radius(aZoomlevel) / 2 * log(u)))) - falseNorthing(aZoomlevel))<br>
    145      * y = -1 * (getMaxPixel(aZoomlevel) / 2 * PI / 2 * log(u)) - -1 * getMaxPixel(aZoomLevel) / 2<br>
    146      * y = getMaxPixel(aZoomlevel) / (-4 * PI) * log(u)) + getMaxPixel(aZoomLevel) / 2<br>
    147      * y = getMaxPixel(aZoomlevel) * ((log(u) / (-4 * PI)) + 1/2)<br>
     162     * log(u) := log((1.0 + sin(toRadians(aLat))) / (1.0 - sin(toRadians(aLat))))<br>
     163     * y = -1 * (radius(aZoomlevel) / 2 * log(u)) - falseNorthing(aZoomlevel)<br>
     164     * y = -1 * (getMaxPixels(aZoomlevel) / (2 * PI) / 2 * log(u)) - -1 * getMaxPixels(aZoomLevel) / 2<br>
     165     * y = getMaxPixels(aZoomlevel) / (-4 * PI) * log(u) + getMaxPixels(aZoomLevel) / 2<br>
     166     * y = getMaxPixels(aZoomlevel) * ((log(u) / (-4 * PI)) + 1/2)<br>
    148167     * </code>
    149168     * </p>
    150169     * @param aLat
    … … public class OsmMercator {  
    195214     * @return [MIN_LAT..MAX_LAT] is about [-85..85]
    196215     */
    197216    public double yToLat(long aY, int aZoomlevel) {
    198         double latitude = (Math.PI / 2) - (2 * Math.atan(Math.exp(-1.0 * (aY + falseNorthing(aZoomlevel)) / radius(aZoomlevel))));
    199         return -1 * Math.toDegrees(latitude);
     217        double latitude = (Math.PI / 2) - (2 * Math.atan(Math.exp(-(aY + falseNorthing(aZoomlevel)) / radius(aZoomlevel))));
     218        return -Math.toDegrees(latitude);
    200219    }
    201 
    202220}
  • src/org/openstreetmap/gui/jmapviewer/OsmTileLoader.java

    diff --git src/org/openstreetmap/gui/jmapviewer/OsmTileLoader.java src/org/openstreetmap/gui/jmapviewer/OsmTileLoader.java
    index e872e16..17fd6a2 100644
    import java.net.URLConnection;  
    1111import java.util.HashMap;
    1212import java.util.Map;
    1313import java.util.Map.Entry;
    14 import java.util.concurrent.Executors;
     14import java.util.concurrent.LinkedBlockingQueue;
    1515import java.util.concurrent.ThreadPoolExecutor;
     16import java.util.concurrent.TimeUnit;
    1617import java.util.logging.Level;
    1718import java.util.logging.Logger;
    1819
    … … public class OsmTileLoader implements TileLoader {  
    3233    /** Setting key for number of threads */
    3334    public static final String THREADS_SETTING = "jmapviewer.osm-tile-loader.threads";
    3435    private static final int DEFAULT_THREADS_NUMBER = 8;
    35     private static int nThreads = DEFAULT_THREADS_NUMBER;
    36     static {
     36
     37    /** Fixed size pool, shared by all instances */
     38    private static final ThreadPoolExecutor JOB_DISPATCHER = createFixedPool(threadCount());
     39
     40    /** Guards resizing {@link #JOB_DISPATCHER}, which takes two steps */
     41    private static final Object POOL_SIZE_LOCK = new Object();
     42
     43    private static ThreadPoolExecutor createFixedPool(int threads) {
     44        return new ThreadPoolExecutor(threads, threads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
     45    }
     46
     47    private static int threadCount() {
     48        // a failure here would fail the class initialization, so that no tile could be loaded at all
    3749        try {
    38             nThreads = FeatureAdapter.getIntSetting(THREADS_SETTING, DEFAULT_THREADS_NUMBER);
    39         } catch (Exception e) {
     50            int threads = FeatureAdapter.getIntSetting(THREADS_SETTING, DEFAULT_THREADS_NUMBER);
     51            if (threads >= 1) {
     52                return threads;
     53            }
     54            LOG.log(Level.SEVERE, "Invalid {0}: {1}", new Object[] {THREADS_SETTING, threads});
     55        } catch (RuntimeException e) {
    4056            LOG.log(Level.SEVERE, e.getMessage(), e);
    4157        }
     58        return DEFAULT_THREADS_NUMBER;
    4259    }
    4360
    44     private static final ThreadPoolExecutor jobDispatcher = (ThreadPoolExecutor) Executors.newFixedThreadPool(nThreads);
    45 
    4661    private final class OsmTileJob implements TileJob {
    4762        private final Tile tile;
    48         private InputStream input;
    4963        private boolean force;
    5064
    5165        private OsmTileJob(Tile tile) {
    … … public class OsmTileLoader implements TileLoader {  
    7084                if ("no-tile".equals(tile.getValue("tile-info"))) {
    7185                    tile.setError(tr("No tiles at this zoom level"));
    7286                } else {
    73                     input = conn.getInputStream();
    74                     try {
     87                    try (InputStream input = conn.getInputStream()) {
    7588                        tile.loadImage(input);
    76                     } finally {
    77                         input.close();
    78                         input = null;
    7989                    }
    8090                }
    8191                tile.setLoaded(true);
    … … public class OsmTileLoader implements TileLoader {  
    8393            } catch (IOException e) {
    8494                tile.setError(e.getMessage());
    8595                listener.tileLoadingFinished(tile, false);
    86                 if (input == null) {
    87                     try {
    88                         LOG.log(Level.SEVERE, "Failed loading " + tile.getUrl() +": "
    89                                 +e.getClass() + ": " + e.getMessage());
    90                     } catch (IOException ioe) {
    91                         LOG.log(Level.SEVERE, ioe.getMessage(), ioe);
    92                     }
     96                try {
     97                    LOG.log(Level.SEVERE, "Failed loading " + tile.getUrl() + ": " + e.getClass() + ": " + e.getMessage());
     98                } catch (IOException ioe) {
     99                    LOG.log(Level.SEVERE, ioe.getMessage(), ioe);
    93100                }
    94101            } finally {
    95102                tile.loading = false;
    … … public class OsmTileLoader implements TileLoader {  
    105112        @Override
    106113        public void submit(boolean force) {
    107114            this.force = force;
    108             jobDispatcher.execute(this);
     115            JOB_DISPATCHER.execute(this);
    109116        }
    110117    }
    111118
    … … public class OsmTileLoader implements TileLoader {  
    127134        this(listener, null);
    128135    }
    129136
     137    /**
     138     * Constructs a new {@code OsmTileLoader}.
     139     * @param listener tile loader listener
     140     * @param headers HTTP headers to send in addition to (or instead of) the default ones, can be null
     141     */
    130142    public OsmTileLoader(TileLoaderListener listener, Map<String, String> headers) {
    131143        this.headers.put("Accept", "text/html, image/png, image/jpeg, image/gif, */*");
    132         this.headers.put("User-Agent", "JMapViewer Java/"+System.getProperty("java.version"));
     144        this.headers.put("User-Agent", "JMapViewer Java/" + System.getProperty("java.version"));
    133145        if (headers != null) {
    134146            this.headers.putAll(headers);
    135147        }
    … … public class OsmTileLoader implements TileLoader {  
    142154    }
    143155
    144156    protected URLConnection loadTileFromOsm(Tile tile) throws IOException {
    145         URL url;
    146         url = new URL(tile.getUrl());
    147         URLConnection urlConn = url.openConnection();
     157        // Java 20: URL(String) is deprecated, use new URI(...).toURL() (it rejects some URLs that are accepted now)
     158        URLConnection urlConn = new URL(tile.getUrl()).openConnection();
     159        // Java 16: use instanceof pattern matching instead of the casts
    148160        if (urlConn instanceof HttpURLConnection) {
    149161            prepareHttpUrlConnection((HttpURLConnection) urlConn);
    150162        }
    … … public class OsmTileLoader implements TileLoader {  
    161173            tile.putValue("tile-info", str);
    162174        }
    163175
    164         Long lng = urlConn.getExpiration();
    165         if (lng.equals(0L)) {
     176        long expires = urlConn.getExpiration();
     177        if (expires == 0) {
    166178            try {
    167179                str = urlConn.getHeaderField("Cache-Control");
    168180                if (str != null) {
    169181                    for (String token: str.split(",", -1)) {
     182                        // the directives are usually separated by ", "
     183                        token = token.strip();
    170184                        if (token.startsWith("max-age=")) {
    171                             lng = Long.parseLong(token.substring(8)) * 1000 +
     185                            expires = Long.parseLong(token.substring(8)) * 1000 +
    172186                                    System.currentTimeMillis();
    173187                        }
    174188                    }
    175189                }
    176190            } catch (NumberFormatException e) {
    177191                // ignore malformed Cache-Control headers
    178                 if (JMapViewer.debug) {
    179                     LOG.log(Level.FINE, e.getMessage(), e);
    180                 }
     192                LOG.log(Level.FINE, e.getMessage(), e);
    181193            }
    182194        }
    183         if (!lng.equals(0L)) {
    184             tile.putValue("expires", lng.toString());
     195        if (expires != 0) {
     196            tile.putValue("expires", Long.toString(expires));
    185197        }
    186198    }
    187199
    … … public class OsmTileLoader implements TileLoader {  
    202214
    203215    @Override
    204216    public boolean hasOutstandingTasks() {
    205         return jobDispatcher.getTaskCount() > jobDispatcher.getCompletedTaskCount();
     217        return JOB_DISPATCHER.getTaskCount() > JOB_DISPATCHER.getCompletedTaskCount();
    206218    }
    207219
    208220    @Override
    209221    public void cancelOutstandingTasks() {
    210         jobDispatcher.getQueue().clear();
     222        JOB_DISPATCHER.getQueue().clear();
    211223    }
    212224
    213225    /**
    214226     * Sets the maximum number of concurrent connections the tile loader will do
    215227     * @param num number of concurrent connections
     228     * @throws IllegalArgumentException if {@code num} is less than 1
    216229     */
    217230    public static void setConcurrentConnections(int num) {
    218         jobDispatcher.setMaximumPoolSize(num);
     231        // checked first, setCorePoolSize(0) would succeed and leave the pool half resized
     232        if (num < 1) {
     233            throw new IllegalArgumentException("At least one connection is needed: " + num);
     234        }
     235        // The pool has a fixed size, so both sizes have to change. The maximum may never be below the core size.
     236        synchronized (POOL_SIZE_LOCK) {
     237            if (num > JOB_DISPATCHER.getMaximumPoolSize()) {
     238                JOB_DISPATCHER.setMaximumPoolSize(num);
     239                JOB_DISPATCHER.setCorePoolSize(num);
     240            } else {
     241                JOB_DISPATCHER.setCorePoolSize(num);
     242                JOB_DISPATCHER.setMaximumPoolSize(num);
     243            }
     244        }
    219245    }
    220246}
  • src/org/openstreetmap/gui/jmapviewer/Projected.java

    diff --git src/org/openstreetmap/gui/jmapviewer/Projected.java src/org/openstreetmap/gui/jmapviewer/Projected.java
    index e861c50..bb8c8b3 100644
    import java.util.Objects;  
    1111import org.openstreetmap.gui.jmapviewer.interfaces.IProjected;
    1212
    1313/**
    14  * Projected coordinates represented by an encapsulates a Point2D.Double value.
     14 * Projected coordinates, stored as a {@link Point2D.Double}.
    1515 */
    1616public class Projected implements IProjected, Serializable {
     17    // Java 14: annotate with @Serial
    1718    private static final long serialVersionUID = 1L;
    1819    private transient Point2D.Double data;
    1920
    … … public class Projected implements IProjected, Serializable {  
    3637        return data.y;
    3738    }
    3839
     40    // Java 14: annotate with @Serial
    3941    private void writeObject(ObjectOutputStream out) throws IOException {
    4042        out.writeObject(data.x);
    4143        out.writeObject(data.y);
    4244    }
    4345
     46    // Java 14: annotate with @Serial
    4447    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    4548        data = new Point2D.Double();
    4649        data.x = (Double) in.readObject();
    … … public class Projected implements IProjected, Serializable {  
    6164    public boolean equals(Object obj) {
    6265        if (this == obj)
    6366            return true;
    64         if (obj == null || !this.getClass().equals(obj.getClass()))
     67        if (obj == null || !getClass().equals(obj.getClass()))
    6568            return false;
    6669        final Projected other = (Projected) obj;
    6770        return Objects.equals(data, other.data);
  • src/org/openstreetmap/gui/jmapviewer/Style.java

    diff --git src/org/openstreetmap/gui/jmapviewer/Style.java src/org/openstreetmap/gui/jmapviewer/Style.java
    index dc7b647..6b4e9b8 100644
     
    11// License: GPL. For details, see Readme.txt file.
    22package org.openstreetmap.gui.jmapviewer;
    33
    4 import java.awt.AlphaComposite;
    54import java.awt.Color;
    65import java.awt.Font;
    76import java.awt.Stroke;
    87
    98public class Style {
    10     private static final AlphaComposite TRANSPARENCY = AlphaComposite.getInstance(AlphaComposite.SRC_OVER);
    11     private static final AlphaComposite OPAQUE = AlphaComposite.getInstance(AlphaComposite.SRC);
    12 
    139    private Color color;
    1410    private Color backColor;
    1511    private Stroke stroke;
    1612    private Font font;
    1713
    1814    public Style() {
    19         super();
    2015    }
    2116
    2217    public Style(Color color, Color backColor, Stroke stroke, Font font) {
    23         super();
    2418        this.color = color;
    2519        this.backColor = backColor;
    2620        this.stroke = stroke;
    … … public class Style {  
    5852    public void setFont(Font font) {
    5953        this.font = font;
    6054    }
    61 
    62     private static AlphaComposite getAlphaComposite(Color color) {
    63         return color.getAlpha() == 255 ? OPAQUE : TRANSPARENCY;
    64     }
    65 
    66     public AlphaComposite getAlphaComposite() {
    67         return getAlphaComposite(color);
    68     }
    69 
    70     public AlphaComposite getBackAlphaComposite() {
    71         return getAlphaComposite(backColor);
    72     }
    7355}
  • src/org/openstreetmap/gui/jmapviewer/Tile.java

    diff --git src/org/openstreetmap/gui/jmapviewer/Tile.java src/org/openstreetmap/gui/jmapviewer/Tile.java
    index 3c2502b..11b8cfd 100644
    import java.io.InputStream;  
    1010import java.util.HashMap;
    1111import java.util.Map;
    1212import java.util.Objects;
    13 import java.util.concurrent.Callable;
     13import java.util.function.Supplier;
    1414import java.util.logging.Level;
    1515
    1616import javax.imageio.ImageIO;
    … … public class Tile {  
    8989        }
    9090    }
    9191
    92     private static class CachedCallable<V> implements Callable<V> {
     92    /**
     93     * A supplier that asks the wrapped supplier only once, on first use.
     94     * @param <V> the type of the supplied value
     95     */
     96    private static final class Lazy<V> implements Supplier<V> {
    9397        private V result;
    94         private final Callable<V> callable;
    95 
    96         /**
    97          * Wraps callable so it is evaluated only once
    98          * @param callable to cache
    99          */
    100         CachedCallable(Callable<V> callable) {
    101             this.callable = callable;
     98        private final Supplier<V> supplier;
     99
     100        Lazy(Supplier<V> supplier) {
     101            this.supplier = supplier;
    102102        }
    103103
    104104        @Override
    105         public synchronized V call() {
    106             try {
    107                 if (result == null) {
    108                     result = callable.call();
    109                 }
    110                 return result;
    111             } catch (Exception e) {
    112                 // this should not happen here
    113                 throw new JMapViewerRuntimeException(e);
     105        public V get() {
     106            if (result == null) {
     107                result = supplier.get();
    114108            }
     109            return result;
    115110        }
    116111    }
    117112
    … … public class Tile {  
    121116     * @param cache Tile cache
    122117     */
    123118    public void loadPlaceholderFromCache(TileCache cache) {
    124         /*
    125          *  use LazyTask as creation of BufferedImage is very expensive
    126          *  this way we can avoid object creation until we're sure it's needed
    127          */
    128         final CachedCallable<BufferedImage> tmpImage = new CachedCallable<>(() ->
     119        // creation of BufferedImage is very expensive, so it is avoided until we're sure it's needed
     120        Lazy<BufferedImage> tmpImage = new Lazy<>(() ->
    129121                new BufferedImage(source.getTileSize(), source.getTileSize(), BufferedImage.TYPE_INT_ARGB));
    130122
    131123        for (int zoomDiff = 1; zoomDiff < 5; zoomDiff++) {
    … … public class Tile {  
    136128                int factor = 1 << zoomDiff;
    137129                int xtileHigh = xtile << zoomDiff;
    138130                int ytileHigh = ytile << zoomDiff;
    139                 final double scale = 1.0 / factor;
    140 
    141                 /*
    142                  * use LazyTask for graphics to avoid evaluation of tmpImage, until we have
    143                  * something to draw
    144                  */
    145                 CachedCallable<Graphics2D> graphics = new CachedCallable<>(() -> {
    146                     Graphics2D g = (Graphics2D) tmpImage.call().getGraphics();
     131                double scale = 1.0 / factor;
     132
     133                // lazy, to avoid the evaluation of tmpImage until we have something to draw
     134                Lazy<Graphics2D> graphics = new Lazy<>(() -> {
     135                    Graphics2D g = (Graphics2D) tmpImage.get().getGraphics();
    147136                    g.setTransform(AffineTransform.getScaleInstance(scale, scale));
    148137                    return g;
    149138                });
    … … public class Tile {  
    154143                        Tile tile = cache.getTile(source, xtileHigh + x, ytileHigh + y, zoomHigh);
    155144                        if (tile != null && tile.isLoaded()) {
    156145                            paintedTileCount++;
    157                             tile.paint(graphics.call(), x * source.getTileSize(), y * source.getTileSize());
     146                            tile.paint(graphics.get(), x * source.getTileSize(), y * source.getTileSize());
    158147                        }
    159148                    }
    160149                }
    161150                if (paintedTileCount == factor * factor) {
    162                     image = tmpImage.call();
     151                    image = tmpImage.get();
    163152                    return;
    164153                }
    165154            }
    … … public class Tile {  
    168157            if (zoomLow >= JMapViewer.MIN_ZOOM) {
    169158                int xtileLow = xtile >> zoomDiff;
    170159                int ytileLow = ytile >> zoomDiff;
    171                 final int factor = 1 << zoomDiff;
    172                 final double scale = factor;
    173                 CachedCallable<Graphics2D> graphics = new CachedCallable<>(() -> {
    174                     Graphics2D g = (Graphics2D) tmpImage.call().getGraphics();
     160                int factor = 1 << zoomDiff;
     161                Lazy<Graphics2D> graphics = new Lazy<>(() -> {
     162                    Graphics2D g = (Graphics2D) tmpImage.get().getGraphics();
    175163                    AffineTransform at = new AffineTransform();
    176164                    int translateX = (xtile % factor) * source.getTileSize();
    177165                    int translateY = (ytile % factor) * source.getTileSize();
    178                     at.setTransform(scale, 0, 0, scale, -translateX, -translateY);
     166                    at.setTransform(factor, 0, 0, factor, -translateX, -translateY);
    179167                    g.setTransform(at);
    180168                    return g;
    181169                });
    182170
    183171                Tile tile = cache.getTile(source, xtileLow, ytileLow, zoomLow);
    184172                if (tile != null && tile.isLoaded()) {
    185                     tile.paint(graphics.call(), 0, 0);
    186                     image = tmpImage.call();
     173                    tile.paint(graphics.get(), 0, 0);
     174                    image = tmpImage.get();
    187175                    return;
    188176                }
    189177            }
    … … public class Tile {  
    322310
    323311    /**
    324312     * Compares this object with <code>obj</code> based on
    325      * the fields {@link #xtile}, {@link #ytile} and
    326      * {@link #zoom}.
    327      * The {@link #source} field is ignored.
     313     * the fields {@link #xtile}, {@link #ytile}, {@link #zoom}
     314     * and {@link #source}.
    328315     */
    329316    @Override
    330317    public boolean equals(Object obj) {
    331318        if (this == obj)
    332319            return true;
    333         if (obj == null || !this.getClass().equals(obj.getClass()))
     320        if (obj == null || !getClass().equals(obj.getClass()))
    334321            return false;
    335322        final Tile other = (Tile) obj;
    336323        return xtile == other.xtile
    … … public class Tile {  
    344331    }
    345332
    346333    public String getStatus() {
    347         if (this.error)
     334        if (error)
    348335            return "error";
    349         if (this.loaded)
     336        if (loaded)
    350337            return "loaded";
    351         if (this.loading)
     338        if (loading)
    352339            return "loading";
    353340        return "new";
    354341    }
    … … public class Tile {  
    393380    }
    394381
    395382    /**
    396      * returns the metadata of the Tile
     383     * Returns the metadata of the Tile
    397384     *
    398385     * @param key metadata key that should be returned
    399386     * @return null if no such metadata exists, or the value of the metadata
    … … public class Tile {  
    404391    }
    405392
    406393    /**
    407      *
    408394     * @return metadata of the tile
    409395     */
    410396    public Map<String, String> getMetadata() {
    … … public class Tile {  
    415401    }
    416402
    417403    /**
    418      * indicate that loading process for this tile has started
     404     * Indicates that loading process for this tile has started
    419405     */
    420406    public void initLoading() {
    421407        error = false;
    … … public class Tile {  
    423409    }
    424410
    425411    /**
    426      * indicate that loading process for this tile has ended
     412     * Indicates that loading process for this tile has ended
    427413     */
    428414    public void finishLoading() {
    429415        loading = false;
    … … public class Tile {  
    431417    }
    432418
    433419    /**
    434      *
    435420     * @return TileSource from which this tile comes
    436421     */
    437422    public TileSource getTileSource() {
    … … public class Tile {  
    439424    }
    440425
    441426    /**
    442      * indicate that loading process for this tile has been canceled
     427     * Indicates that loading process for this tile has been canceled
    443428     */
    444429    public void loadingCanceled() {
    445430        loading = false;
  • src/org/openstreetmap/gui/jmapviewer/TileController.java

    diff --git src/org/openstreetmap/gui/jmapviewer/TileController.java src/org/openstreetmap/gui/jmapviewer/TileController.java
    index 136f11b..46f2a4d 100644
    import org.openstreetmap.gui.jmapviewer.interfaces.TileLoader;  
    66import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
    77import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
    88
     9/**
     10 * Wires a {@link TileSource}, a {@link TileCache} and a {@link TileLoader} together, to get the tiles that a map shows.
     11 */
    912public class TileController {
    1013    protected TileLoader tileLoader;
    1114    protected TileCache tileCache;
    1215    protected TileSource tileSource;
    1316
     17    /**
     18     * Constructs a new {@code TileController}, which loads the tiles with an {@link OsmTileLoader}.
     19     * @param source the tile source
     20     * @param tileCache the tile cache
     21     * @param listener the listener notified when a tile has been loaded
     22     */
    1423    public TileController(TileSource source, TileCache tileCache, TileLoaderListener listener) {
    1524        this.tileSource = source;
    1625        this.tileLoader = new OsmTileLoader(listener);
    … … public class TileController {  
    1827    }
    1928
    2029    /**
    21      * retrieves a tile from the cache. If the tile is not present in the cache
    22      * a load job is added to the working queue of {@link TileLoader}.
     30     * Retrieves a tile from the cache. If the tile is not present in the cache, a new tile is added
     31     * to the cache. If the tile is not loaded yet, a load job is added to the working queue of {@link TileLoader}.
    2332     *
    2433     * @param tilex the X position of the tile
    2534     * @param tiley the Y position of the tile
    2635     * @param zoom the zoom level of the tile
    27      * @return specified tile from the cache or <code>null</code> if the tile
    28      *         was not found in the cache.
     36     * @return the specified tile, or <code>null</code> if the position is outside of the map
    2937     */
    3038    public Tile getTile(int tilex, int tiley, int zoom) {
    3139        int max = 1 << zoom;
    … … public class TileController {  
    3745            tileCache.addTile(tile);
    3846            tile.loadPlaceholderFromCache(tileCache);
    3947        }
    40         if (tile.error) {
     48        if (tile.hasError()) {
    4149            tile.loadPlaceholderFromCache(tileCache);
    4250        }
    4351        if (!tile.isLoaded()) {
    … … public class TileController {  
    7684
    7785    /**
    7886     * Removes all jobs from the queue that are currently not being processed.
    79      *
    8087     */
    8188    public void cancelOutstandingJobs() {
    8289        tileLoader.cancelOutstandingTasks();
  • src/org/openstreetmap/gui/jmapviewer/TileRange.java

    diff --git src/org/openstreetmap/gui/jmapviewer/TileRange.java src/org/openstreetmap/gui/jmapviewer/TileRange.java
    index 61c5f29..25b2eca 100644
    public class TileRange {  
    4141    }
    4242
    4343    protected double tilesSpanned() {
    44         return Math.sqrt(1.0 * this.size());
     44        return Math.sqrt(size());
    4545    }
    4646
    4747    /**
    48      * Returns size
    49      * @return size
    50      * @throws ArithmeticException – if the result overflows an int (see {@link Math#multiplyExact(int, int)})
     48     * Returns the number of tiles in this range.
     49     * @return the number of tiles
     50     * @throws ArithmeticException if the result overflows an int (see {@link Math#multiplyExact(int, int)})
    5151     */
    52     public int size() throws ArithmeticException {
     52    public int size() {
    5353        int xSpan = maxX - minX + 1;
    5454        int ySpan = maxY - minY + 1;
    5555        return Math.multiplyExact(xSpan, ySpan);
    5656    }
    5757}
    58 
  • src/org/openstreetmap/gui/jmapviewer/TileXY.java

    diff --git src/org/openstreetmap/gui/jmapviewer/TileXY.java src/org/openstreetmap/gui/jmapviewer/TileXY.java
    index ca671cc..733d125 100644
     
    22package org.openstreetmap.gui.jmapviewer;
    33
    44/**
    5  * @author w
     5 * Tile coordinates at a zoom level. They are fractional for points inside a tile.
    66 *
     7 * @author w
    78 */
    89public class TileXY {
    910    /**
    … … public class TileXY {  
    1718    private final double y;
    1819
    1920    /**
    20      * Returns an instance of coordinates.
     21     * Constructs a new {@code TileXY}.
    2122     *
    2223     * @param x number of the tile
    2324     * @param y number of the tile
    … … public class TileXY {  
    2829    }
    2930
    3031    /**
    31      * @return x index of the tile as integer
     32     * @return x index of the tile as integer, rounded towards zero
    3233     */
    3334    public int getXIndex() {
    34         return x < 0 ? (int) Math.ceil(x) : (int) Math.floor(x);
     35        return (int) x;
    3536    }
    3637
    3738    /**
    38      * @return y index of the tile as integer
     39     * @return y index of the tile as integer, rounded towards zero
    3940     */
    4041    public int getYIndex() {
    41         return y < 0 ? (int) Math.ceil(y) : (int) Math.floor(y);
     42        return (int) y;
    4243    }
    4344
    4445    /**
  • src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeEditor.java

    diff --git src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeEditor.java src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeEditor.java
    index c12645a..f2d908f 100644
    import javax.swing.tree.TreePath;  
    1919 * @author galo
    2020 */
    2121public class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
    22     /** SerialVersionUID */
     22    // Java 14: annotate with @Serial
    2323    private static final long serialVersionUID = -8921320784224636657L;
    2424
    2525    private final CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
    … … public class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEd  
    3232
    3333    @Override
    3434    public Object getCellEditorValue() {
    35         final CheckBoxNodePanel panel = renderer.getPanel();
    36         /*final CheckBoxNodeData checkBoxNode =
    37             new CheckBoxNodeData(panel.label.getText(), panel.check.isSelected());
    38         return checkBoxNode;
    39         CheckBoxNodeData data = search(theTree.rootNode(), panel.label.getText());
    40         data.setSelected(panel.check.isSelected());*/
    41         return panel.getData();
     35        return renderer.getPanel().getData();
    4236    }
    43     /*public CheckBoxNodeData search(DefaultMutableTreeNode node, String name){
    44         CheckBoxNodeData data = CheckBoxTree.data(node);
    45         if(data.getText().equals(name)) return data;
    46         else{
    47             data = null;
    48             for(int i=0; i<node.getChildCount() && data==null; i++){
    49                 data = search((DefaultMutableTreeNode)node.getChildAt(i), name);
    50             }
    51             return data;
    52         }
    53     }*/
    5437
    5538    public void addNodeListener(MouseAdapter listener) {
    5639        renderer.addNodeListener(listener);
    … … public class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEd  
    5841
    5942    @Override
    6043    public boolean isCellEditable(final EventObject event) {
     44        // Java 16: use instanceof pattern matching instead of the casts
    6145        if (!(event instanceof MouseEvent)) return false;
    6246        final MouseEvent mouseEvent = (MouseEvent) event;
    6347
    … … public class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEd  
    8872                fireEditingStopped();
    8973            }
    9074        };
     75        // Java 16: use instanceof pattern matching instead of the casts
    9176        if (editor instanceof CheckBoxNodePanel) {
    9277            final CheckBoxNodePanel panel = (CheckBoxNodePanel) editor;
    9378            panel.check.addItemListener(itemListener);
  • src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodePanel.java

    diff --git src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodePanel.java src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodePanel.java
    index b55363e..b0e2378 100644
    import javax.swing.JPanel;  
    1414 * @author galo
    1515 */
    1616public class CheckBoxNodePanel extends JPanel {
    17     /** Serial Version UID */
     17    // Java 14: annotate with @Serial
    1818    private static final long serialVersionUID = -7236481597785619029L;
    1919    private final JLabel label = new JLabel();
    2020    private CheckBoxNodeData data;
    2121    public final JCheckBox check = new JCheckBox();
    2222
     23    @SuppressWarnings("this-escape")
    2324    public CheckBoxNodePanel() {
    24         this.check.setMargin(new Insets(0, 0, 0, 0));
     25        check.setMargin(new Insets(0, 0, 0, 0));
    2526        setLayout(new BorderLayout());
    2627        add(check, BorderLayout.WEST);
    2728        add(label, BorderLayout.CENTER);
  • src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeRenderer.java

    diff --git src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeRenderer.java src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxNodeRenderer.java
    index 54e2642..817ec9c 100644
    public class CheckBoxNodeRenderer implements TreeCellRenderer {  
    5858        final Object value, final boolean selected, final boolean expanded,
    5959        final boolean leaf, final int row, final boolean hasFocus) {
    6060        CheckBoxNodeData data = null;
     61        // Java 16: use instanceof pattern matching instead of the casts
    6162        if (value instanceof DefaultMutableTreeNode) {
    6263            final DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
    6364            final Object userObject = node.getUserObject();
    … … public class CheckBoxNodeRenderer implements TreeCellRenderer {  
    6667            }
    6768        }
    6869
    69         //final String stringValue =
    70         //    tree.convertValueToText(value, selected, expanded, leaf, row, false);
    71         //panel.label.setText(stringValue);
    72 
    7370        panel.setSelected(Boolean.FALSE);
    7471
    7572        panel.setEnabled(tree.isEnabled());
    … … public class CheckBoxNodeRenderer implements TreeCellRenderer {  
    9289                selected, expanded, leaf, row, hasFocus);
    9390        }
    9491
    95         //panel.label.setText(data.getText());
    9692        panel.setData(data);
    9793        panel.setSelected(data.isSelected());
    9894
    9995        return panel;
    10096    }
    101 /*
    102     private JPopupMenu createPopupMenu(final AbstractLayer layer) {
    103         JMenuItem menuItem;
    104 
    105         //Create the popup menu.
    106         if (layer.isVisibleTexts()) menuItem = new JMenuItem("hide texts");
    107         else menuItem = new JMenuItem("show texts");
    108         JPopupMenu popup = new JPopupMenu();
    109         popup.add(menuItem);
    110         menuItem.addActionListener(new ActionListener(){
    111             @Override
    112             public void actionPerformed(ActionEvent arg0) {
    113                 setVisibleTexts(layer, !layer.isVisibleTexts());
    114             }
    115         });
    116 
    117         return popup;
    118     }
    119 
    120     private void setVisibleTexts(AbstractLayer layer, boolean visible) {
    121         layer.setVisibleTexts(visible);
    122         if (layer instanceof LayerGroup) {
    123             LayerGroup group = (LayerGroup) layer;
    124             if (group.getLayers() != null)
    125                 for (AbstractLayer al : group.getLayers()) {
    126                     setVisibleTexts(al, visible);
    127                 }
    128         }
    129     }
    130 */
    13197}
  • src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxTree.java

    diff --git src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxTree.java src/org/openstreetmap/gui/jmapviewer/checkBoxTree/CheckBoxTree.java
    index 261a15e..21bd428 100644
     
    11// License: GPL. For details, see Readme.txt file.
    22package org.openstreetmap.gui.jmapviewer.checkBoxTree;
    33
    4 import java.awt.BorderLayout;
    54import java.awt.event.MouseAdapter;
    65import java.util.Objects;
    76
    8 import javax.swing.JFrame;
    9 import javax.swing.JScrollPane;
    107import javax.swing.JTree;
    118import javax.swing.event.TreeModelEvent;
    129import javax.swing.event.TreeModelListener;
    … … import org.openstreetmap.gui.jmapviewer.LayerGroup;  
    2320 * @author galo
    2421 */
    2522public class CheckBoxTree extends JTree {
    26     /** Serial Version UID */
     23    // Java 14: annotate with @Serial
    2724    private static final long serialVersionUID = 6943401106938034256L;
    2825
    2926    private final CheckBoxNodeEditor editor;
    … … public class CheckBoxTree extends JTree {  
    4037        this(new DefaultMutableTreeNode(root));
    4138    }
    4239
     40    @SuppressWarnings("this-escape")
    4341    public CheckBoxTree(DefaultMutableTreeNode node) {
    4442        super(new DefaultTreeModel(node));
    4543
    … … public class CheckBoxTree extends JTree {  
    6866
    6967            @Override
    7068            public void treeNodesInserted(final TreeModelEvent e) {
    71                 //System.out.println("nodes inserted");
     69                // Nothing to do
    7270            }
    7371
    7472            @Override
    7573            public void treeNodesRemoved(final TreeModelEvent e) {
    76                 //System.out.println("nodes removed");
     74                // Nothing to do
    7775            }
    7876
    7977            @Override
    8078            public void treeStructureChanged(final TreeModelEvent e) {
    81                 //System.out.println("structure changed");
     79                // Nothing to do
    8280            }
    8381        });
    8482    }
    … … public class CheckBoxTree extends JTree {  
    8785        editor.addNodeListener(listener);
    8886    }
    8987
    90     public static void main(final String[] args) {
    91         final DefaultMutableTreeNode root = new DefaultMutableTreeNode(new CheckBoxNodeData("Root", Boolean.TRUE));
    92 
    93         final DefaultMutableTreeNode accessibility =
    94             add(root, "Accessibility", true);
    95         add(accessibility, "Move system caret with focus/selection changes", false);
    96         add(accessibility, "Always expand alt text for images", true);
    97         root.add(accessibility);
    98 
    99         final DefaultMutableTreeNode browsing =
    100             new DefaultMutableTreeNode(new CheckBoxNodeData("Browsing", null));
    101         add(browsing, "Notify when downloads complete", true);
    102         add(browsing, "Disable script debugging", true);
    103         add(browsing, "Use AutoComplete", true);
    104         add(browsing, "Browse in a new process", false);
    105         root.add(browsing);
    106 
    107         final CheckBoxTree tree = new CheckBoxTree(root);
    108         ((DefaultMutableTreeNode) tree.getModel().getRoot()).add(new DefaultMutableTreeNode(new CheckBoxNodeData("gggg", null)));
    109         ((DefaultTreeModel) tree.getModel()).reload();
    110         // show the tree on screen
    111         final JFrame frame = new JFrame("CheckBox Tree");
    112         final JScrollPane scrollPane = new JScrollPane(tree);
    113         frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
    114         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    115         frame.setSize(300, 150);
    116         frame.setVisible(true);
    117     }
    118 
    11988    private static Boolean childStatus(DefaultMutableTreeNode node) {
    12089        Boolean status = data(node.getChildAt(0)).isSelected();
    12190        for (int i = 1; i < node.getChildCount() && status != null; i++) {
    122             if (!status.equals(
    123                     data(node.getChildAt(i)).isSelected()
    124                     ))
     91            if (!status.equals(data(node.getChildAt(i)).isSelected()))
    12592                return null;
    12693        }
    12794        return status;
    … … public class CheckBoxTree extends JTree {  
    144111    private static void nodeChanged(DefaultMutableTreeNode node) {
    145112        if (node != null) {
    146113            changeParents(node);
    147             setChildrens(node, data(node).isSelected());
     114            setChildrens(node);
    148115        }
    149116    }
    150117
    151     private static void setChildrens(DefaultMutableTreeNode node, Boolean value) {
     118    private static void setChildrens(DefaultMutableTreeNode node) {
    152119        for (int i = 0; i < node.getChildCount(); i++) {
    153120            DefaultMutableTreeNode childNode = node(node.getChildAt(i));
    154121            // Fix NPE from #20557
    … … public class CheckBoxTree extends JTree {  
    156123            final CheckBoxNodeData nodeData = data(node);
    157124            if (childData != null && nodeData != null &&
    158125                    !Objects.equals(childData.isSelected(), nodeData.isSelected())) {
    159                 data(childNode).setSelected(data(node).isSelected());
    160                 setChildrens(childNode, value);
     126                childData.setSelected(nodeData.isSelected());
     127                setChildrens(childNode);
    161128            }
    162129        }
    163130    }
    … … public class CheckBoxTree extends JTree {  
    186153        return data(node(node));
    187154    }
    188155
    189     private static DefaultMutableTreeNode add(final DefaultMutableTreeNode parent, final String text, final boolean checked) {
    190         final CheckBoxNodeData data = new CheckBoxNodeData(text, checked);
    191         final DefaultMutableTreeNode node = new DefaultMutableTreeNode(data);
    192         parent.add(node);
    193         return node;
    194     }
    195 
    196156    public static CheckBoxNodeData createNodeData(AbstractLayer layer) {
    197157        return new CheckBoxNodeData(layer);
    198158    }
    … … public class CheckBoxTree extends JTree {  
    201161        return new DefaultMutableTreeNode(createNodeData(layer));
    202162    }
    203163
    204     /*public DefaultMutableTreeNode addLayerGroup(LayerGroup group) {
    205         if (group != null){
    206             if (group.getParent() == null){
    207                 return add(rootNode(), group);
    208             } else {
    209                 DefaultMutableTreeNode parentGroup = searchNode(group.getParent());
    210                 if(parentGroup==null) parentGroup = addLayerGroup(group.getParent());
    211                 DefaultMutableTreeNode node = add(parentGroup, group);
    212                 return node;
    213             }
    214         }else return null;
    215     }*/
    216 
    217164    public Layer addLayer(String name) {
    218165        Layer layer = new Layer(name);
    219166        addLayer(layer);
    … … public class CheckBoxTree extends JTree {  
    240187        DefaultMutableTreeNode node = createNode(layer);
    241188        parent.add(node);
    242189        ((DefaultTreeModel) getModel()).reload();
    243         //System.out.println("Created node "+layer+" upper of "+data(parent));
    244190        return node;
    245191    }
    246192
    … … public class CheckBoxTree extends JTree {  
    249195    }
    250196
    251197    public DefaultMutableTreeNode searchNode(DefaultMutableTreeNode node, AbstractLayer layer) {
    252         CheckBoxNodeData data = CheckBoxTree.data(node);
     198        CheckBoxNodeData data = data(node);
    253199        if (data.getAbstractLayer() == layer) return node;
    254200        else {
    255201            DefaultMutableTreeNode found = null;
  • src/org/openstreetmap/gui/jmapviewer/events/JMVCommandEvent.java

    diff --git src/org/openstreetmap/gui/jmapviewer/events/JMVCommandEvent.java src/org/openstreetmap/gui/jmapviewer/events/JMVCommandEvent.java
    index 271187f..46df730 100644
    import java.util.EventObject;  
    88 * objects that register as a JMapViewerEventListener
    99 *
    1010 * @author Jason Huntley
    11  *
    1211 */
    1312public class JMVCommandEvent extends EventObject {
    1413    public enum COMMAND {
    … … public class JMVCommandEvent extends EventObject {  
    1615        ZOOM
    1716    }
    1817
    19     /**
    20      *
    21      */
     18    // Java 14: annotate with @Serial
    2219    private static final long serialVersionUID = 8701544867914969620L;
    2320
    2421    private COMMAND command;
    2522
     23    /**
     24     * Constructs a new {@code JMVCommandEvent}.
     25     * @param cmd the command
     26     * @param source the object on which the event initially occurred
     27     */
     28    @SuppressWarnings("this-escape")
    2629    public JMVCommandEvent(COMMAND cmd, Object source) {
    2730        super(source);
    28 
    2931        setCommand(cmd);
    3032    }
    3133
  • src/org/openstreetmap/gui/jmapviewer/interfaces/Attributed.java

    diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/Attributed.java src/org/openstreetmap/gui/jmapviewer/interfaces/Attributed.java
    index eb7e955..9163a46 100644
    package org.openstreetmap.gui.jmapviewer.interfaces;  
    33
    44import java.awt.Image;
    55
     6/**
     7 * Attribution information that has to be shown together with the data of a source.
     8 */
    69public interface Attributed {
    710    /**
    811     * @return True if the tile source requires attribution in text or image form.
    … … public interface Attributed {  
    2326    String getAttributionLinkURL();
    2427
    2528    /**
    26      * @return The URL for the attribution image. Null if no image should be displayed.
     29     * @return The attribution image. Null if no image should be displayed.
    2730     */
    2831    Image getAttributionImage();
    2932
  • src/org/openstreetmap/gui/jmapviewer/interfaces/MapRectangle.java

    diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/MapRectangle.java src/org/openstreetmap/gui/jmapviewer/interfaces/MapRectangle.java
    index 45bc5a3..95b28dd 100644
    public interface MapRectangle extends MapObject {  
    3131     * <code>bottomRight</code> are specifying the coordinates within <code>g</code>
    3232     *
    3333     * @param g graphics structure for painting
    34      * @param topLeft lop left edge of painting region
     34     * @param topLeft top left edge of painting region
    3535     * @param bottomRight bottom right edge of painting region
    3636     */
    3737    void paint(Graphics g, Point topLeft, Point bottomRight);
  • src/org/openstreetmap/gui/jmapviewer/interfaces/TemplatedTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/TemplatedTileSource.java src/org/openstreetmap/gui/jmapviewer/interfaces/TemplatedTileSource.java
    index 1037c9f..b405972 100644
    package org.openstreetmap.gui.jmapviewer.interfaces;  
    44import java.util.Map;
    55
    66/**
    7  * Interface for template tile sources, @see TemplatedTMSTileSource
     7 * Interface for template tile sources, see {@link org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource}.
    88 *
    99 * @author Wiktor Niesiobędzki
    1010 * @since 1.10
    1111 */
    1212public interface TemplatedTileSource extends TileSource {
    1313    /**
    14      *
    1514     * @return headers to be sent with http requests
    1615     */
    1716    Map<String, String> getHeaders();
  • src/org/openstreetmap/gui/jmapviewer/interfaces/TileCache.java

    diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/TileCache.java src/org/openstreetmap/gui/jmapviewer/interfaces/TileCache.java
    index 7ea76ae..7827b65 100644
    public interface TileCache {  
    3939    void addTile(Tile tile);
    4040
    4141    /**
    42      * @return the number of tiles hold by the cache
     42     * @return the number of tiles held by the cache
    4343     */
    4444    int getTileCount();
    4545
    … … public interface TileCache {  
    5353     * This is implementation specific; the default calls {@link #clear()}.
    5454     */
    5555    default void clearErrorTiles() {
    56         this.clear();
     56        clear();
    5757    }
    5858
    5959    /**
  • src/org/openstreetmap/gui/jmapviewer/interfaces/TileJob.java

    diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/TileJob.java src/org/openstreetmap/gui/jmapviewer/interfaces/TileJob.java
    index 925c5ba..210ad57 100644
    package org.openstreetmap.gui.jmapviewer.interfaces;  
    1010public interface TileJob extends Runnable {
    1111
    1212    /**
    13      * submits download job to backend.
     13     * Submits the download job to the backend.
    1414     */
    1515    void submit();
    1616
    1717    /**
    18      * submits download job to backend.
     18     * Submits the download job to the backend.
    1919     * @param force true if the load should skip all the caches (local &amp; remote)
    2020     */
    2121    void submit(boolean force);
  • src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoader.java

    diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoader.java src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoader.java
    index a4a58cb..5e9627e 100644
    public interface TileLoader {  
    2222    TileJob createTileLoaderJob(Tile tile);
    2323
    2424    /**
    25      * cancels all outstanding tasks in the queue. This should rollback the state of the tiles in the queue
     25     * Cancels all outstanding tasks in the queue. This should rollback the state of the tiles in the queue
    2626     * to loading = false / loaded = false
    2727     */
    2828    void cancelOutstandingTasks();
  • src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java
    index b88fd91..af53614 100644
    public interface TileSource extends Attributed {  
    9797    double getDistance(double la1, double lo1, double la2, double lo2);
    9898
    9999    /**
    100      * Transforms longitude and latitude to pixel space (as if all tiles at specified zoom level where joined).
    101      * @param lon longitude
     100     * Transforms longitude and latitude to pixel space (as if all tiles at specified zoom level were joined).
    102101     * @param lat latitude
     102     * @param lon longitude
    103103     * @param zoom zoom level
    104104     * @return the pixel coordinates
    105105     */
    106106    Point latLonToXY(double lat, double lon, int zoom);
    107107
    108108    /**
    109      * Transforms longitude and latitude to pixel space (as if all tiles at specified zoom level where joined).
     109     * Transforms longitude and latitude to pixel space (as if all tiles at specified zoom level were joined).
    110110     * @param point point
    111111     * @param zoom zoom level
    112112     * @return the pixel coordinates
    … … public interface TileSource extends Attributed {  
    136136
    137137    /**
    138138     * Transforms longitude and latitude to tile indices.
    139      * @param lon longitude
    140139     * @param lat latitude
     140     * @param lon longitude
    141141     * @param zoom zoom level
    142142     * @return x and y tile indices
    143143     */
    … … public interface TileSource extends Attributed {  
    164164    }
    165165
    166166    /**
    167      * Determines to longitude and latitude of a tile.
     167     * Determines the longitude and latitude of a tile.
    168168     * (Refers to the tile origin - upper left tile corner)
    169169     * @param tile Tile
    170170     * @return WGS84 coordinates of given tile
    … … public interface TileSource extends Attributed {  
    204204    int getTileYMax(int zoom);
    205205
    206206    /**
    207      * Get minimum y index of tile for specified zoom level
     207     * Get minimum y index of tile for specified zoom level.
    208208     * @param zoom zoom level
    209209     * @return minimum y index of tile for specified zoom level
    210210     */
    … … public interface TileSource extends Attributed {  
    212212
    213213    /**
    214214     * Determines, if the returned data from TileSource represent "no tile at this zoom level" situation. Detection
    215      * algorithms differ per TileSource, so each TileSource should implement each own specific way.
     215     * algorithms differ per TileSource, so each TileSource should implement its own specific way.
    216216     *
    217217     * @param headers HTTP headers from response from TileSource server
    218218     * @param statusCode HTTP status code
    … … public interface TileSource extends Attributed {  
    232232    /**
    233233     * Convert tile indices (x/y/zoom) into projected coordinates of the tile origin.
    234234     * @param x x tile index
    235      * @param y z tile index
     235     * @param y y tile index
    236236     * @param zoom zoom level
    237237     * @return projected coordinates of the tile origin
    238238     */
  • src/org/openstreetmap/gui/jmapviewer/package.html

    diff --git src/org/openstreetmap/gui/jmapviewer/package.html src/org/openstreetmap/gui/jmapviewer/package.html
    index 37540a6..420ec87 100644
     
    11<html>
    22<head><title>org.openstreetmap.gui.jmapviewer</title></head>
    33<body>
    4 <p>This package and all sub-packages are belonging to the Java
    5 component <a href="http://wiki.openstreetmap.org/wiki/JMapViewer">JMapViewer</a>
     4<p>This package and all its sub-packages belong to the Java
     5component <a href="https://wiki.openstreetmap.org/wiki/JMapViewer">JMapViewer</a>.
    66</p>
    7 <p>JMapViewer is designed to run as stand-alone component without
     7<p>JMapViewer is designed to run as a stand-alone component without
    88any further requirements. Therefore <b>please do not add any code that
    9 depends on other libraries or applications</b>. Only functions and methods
    10 provided by the runtime library of Java 8 should be used.</p>
     9depends on other libraries or applications</b>. Only the Java 11 modules
     10<code>java.desktop</code> and <code>java.logging</code> should be used.</p>
    1111</body>
    1212</html>
  • src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java
    index 49976f3..9230046 100644
    public abstract class AbstractOsmTileSource extends TMSTileSource {  
    2323     * @param id unique id for the tile source; contains only characters that
    2424     * are safe for file names; can be null
    2525     */
    26     public AbstractOsmTileSource(String name, String baseUrl, String id) {
     26    protected AbstractOsmTileSource(String name, String baseUrl, String id) {
    2727        super(new TileSourceInfo(name, baseUrl, id));
    2828    }
    2929
  • src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java
    index 89dd0d8..5626e68 100644
    import org.openstreetmap.gui.jmapviewer.OsmMercator;  
    1919 * Class generalizing all tile based tile sources
    2020 *
    2121 * @author Wiktor Niesiobędzki
    22  *
    2322 */
    2423public abstract class AbstractTMSTileSource extends AbstractTileSource {
    2524
    … … public abstract class AbstractTMSTileSource extends AbstractTileSource {  
    5655     */
    5756    @Override
    5857    public int getDefaultTileSize() {
    59         return OsmMercator.DEFAUL_TILE_SIZE;
     58        return OsmMercator.DEFAULT_TILE_SIZE;
    6059    }
    6160
    6261    @Override
    … … public abstract class AbstractTMSTileSource extends AbstractTileSource {  
    9291     * @param tilex tile number in x axis
    9392     * @param tiley tile number in y axis
    9493     * @return String containing path part of URL of the tile
    95      * @throws IOException when subclass cannot return the tile URL
    9694     */
    97     public String getTilePath(int zoom, int tilex, int tiley) throws IOException {
     95    public String getTilePath(int zoom, int tilex, int tiley) {
    9896        return "/" + zoom + "/" + tilex + "/" + tiley + "." + getExtension();
    9997    }
    10098
    … … public abstract class AbstractTMSTileSource extends AbstractTileSource {  
    107105
    108106    @Override
    109107    public String getTileUrl(int zoom, int tilex, int tiley) throws IOException {
    110         return this.getBaseUrl() + getTilePath(zoom, tilex, tiley);
     108        return getBaseUrl() + getTilePath(zoom, tilex, tiley);
    111109    }
    112110
    113111    @Override
    … … public abstract class AbstractTMSTileSource extends AbstractTileSource {  
    115113        return getName();
    116114    }
    117115
    118     /*
    119      * Most tilesources use OsmMercator projection.
    120      */
    121116    @Override
    122117    public int getTileSize() {
    123118        if (tileSize <= 0) {
    … … public abstract class AbstractTMSTileSource extends AbstractTileSource {  
    168163                try {
    169164                    md = MessageDigest.getInstance(searchEntry.getKey());
    170165                } catch (NoSuchAlgorithmException e) {
    171                     FeatureAdapter.getLogger(this.getClass()).log(Level.FINER, searchEntry.getKey() + " algorithm was not found", e);
    172                     break;
    173                 }
    174                 byte[] byteDigest = md.digest(content);
    175                 final int len = byteDigest.length;
    176 
    177                 char[] hexChars = new char[len * 2];
    178                 int j = 0;
    179                 for (final int v : byteDigest) {
    180                     int vn = (v & 0xf0) >> 4;
    181                     hexChars[j++] = (char) (vn + (vn >= 10 ? ('a' - 10) : '0'));
    182                     vn = (v & 0xf);
    183                     hexChars[j++] = (char) (vn + (vn >= 10 ? ('a' - 10) : '0'));
     166                    FeatureAdapter.getLogger(getClass()).log(Level.FINER, searchEntry.getKey() + " algorithm was not found", e);
     167                    // the checksums of the other algorithms can still match
     168                    continue;
    184169                }
     170                String digest = toHex(md.digest(content));
    185171                for (String val: searchEntry.getValue()) {
    186                     if (new String(hexChars).equalsIgnoreCase(val)) {
     172                    if (digest.equalsIgnoreCase(val)) {
    187173                        return true;
    188174                    }
    189175                }
    … … public abstract class AbstractTMSTileSource extends AbstractTileSource {  
    210196
    211197    @Override
    212198    public String getTileId(int zoom, int tilex, int tiley) {
    213         return this.baseUrl + "/" + zoom + "/" + tilex + "/" + tiley;
     199        return baseUrl + "/" + zoom + "/" + tilex + "/" + tiley;
    214200    }
    215201
    216202    @Override
    … … public abstract class AbstractTMSTileSource extends AbstractTileSource {  
    218204        return modTileFeatures;
    219205    }
    220206
     207    /**
     208     * Encodes bytes as lower case hexadecimal digits.
     209     * @param bytes the bytes to encode
     210     * @return two hexadecimal digits per byte
     211     */
     212    // Java 17: replace with java.util.HexFormat.of().formatHex(bytes)
     213    private static String toHex(byte[] bytes) {
     214        char[] hexChars = new char[bytes.length * 2];
     215        int j = 0;
     216        for (byte b : bytes) {
     217            hexChars[j++] = Character.forDigit((b >> 4) & 0xf, 16);
     218            hexChars[j++] = Character.forDigit(b & 0xf, 16);
     219        }
     220        return new String(hexChars);
     221    }
     222
    221223    private static int getTileMax(int zoom) {
    222224        return (int) Math.pow(2.0, zoom) - 1;
    223225    }
  • src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java
    index 6a93554..d9502c2 100644
    import java.util.Map;  
    88import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
    99import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
    1010
     11/**
     12 * Base class for tile sources, which holds the attribution.
     13 */
    1114public abstract class AbstractTileSource implements TileSource {
    1215
    1316    protected String attributionText;
    … … public abstract class AbstractTileSource implements TileSource {  
    1720    protected String termsOfUseText;
    1821    protected String termsOfUseURL;
    1922
     23    /**
     24     * Constructs a new {@code AbstractTileSource}.
     25     */
     26    protected AbstractTileSource() {
     27        // Nothing to do
     28    }
     29
    2030    @Override
    2131    public boolean requiresAttribution() {
    2232        return attributionText != null || attributionLinkURL != null || attributionImage != null
  • src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
    index 21ba10f..a89eb79 100644
    package org.openstreetmap.gui.jmapviewer.tilesources;  
    33
    44import java.awt.Image;
    55import java.io.IOException;
     6import java.io.InputStream;
    67import java.net.MalformedURLException;
    78import java.net.URI;
    89import java.net.URISyntaxException;
    … … import java.util.concurrent.FutureTask;  
    1718import java.util.concurrent.TimeUnit;
    1819import java.util.logging.Level;
    1920import java.util.logging.Logger;
    20 import java.util.regex.Pattern;
    2121import java.util.stream.Collectors;
    2222
    2323import javax.xml.XMLConstants;
    … … public class BingAerialTileSource extends TMSTileSource {  
    6565    /** Original Bing API key created by Potlatch2 developers in 2010 */
    6666    private static final String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU";
    6767
    68     private static final Pattern PATTERN_SUBDOMAIN = Pattern.compile("\\{subdomain}");
    69     private static final Pattern PATTERN_QUADKEY = Pattern.compile("\\{quadkey}");
    70     private static final Pattern PATTERN_CULTURE = Pattern.compile("\\{culture}");
    71 
    7268    private volatile Future<List<Attribution>> attributions; // volatile is required for getAttribution(), see below.
    7369    private String imageUrlTemplate;
    7470    private int imageryZoomMax = Integer.MIN_VALUE;
    … … public class BingAerialTileSource extends TMSTileSource {  
    9288        super(info);
    9389    }
    9490
     91    /**
     92     * The attribution of an imagery provider in an area, for a range of zoom levels.
     93     */
    9594    protected static class Attribution {
    9695        private String attributionText;
    9796        private int minZoom;
    9897        private int maxZoom;
    9998        private Coordinate min;
    10099        private Coordinate max;
     100
     101        /**
     102         * Constructs a new {@code Attribution}.
     103         */
     104        protected Attribution() {
     105            // Filled in by the parser
     106        }
    101107    }
    102108
    103109    @Override
    … … public class BingAerialTileSource extends TMSTileSource {  
    106112        if (getAttribution() == null)
    107113            throw new IOException("Attribution is not loaded yet");
    108114
    109         int t = (zoom + tilex + tiley) % subdomains.length;
    110         String subdomain = subdomains[t];
    111 
    112         String url = imageUrlTemplate;
    113         url = PATTERN_SUBDOMAIN.matcher(url).replaceAll(subdomain);
    114         url = PATTERN_QUADKEY.matcher(url).replaceAll(computeQuadTree(zoom, tilex, tiley));
    115 
    116         return url;
     115        String subdomain = subdomains[(zoom + tilex + tiley) % subdomains.length];
     116        return imageUrlTemplate
     117                .replace("{subdomain}", subdomain)
     118                .replace("{quadkey}", TemplatedTMSTileSource.xyzToQuadKey(tilex, tiley, zoom));
    117119    }
    118120
    119121    /**
    … … public class BingAerialTileSource extends TMSTileSource {  
    132134        try {
    133135            return new URI(FeatureAdapter.getSetting(METADATA_API_SETTING, METADATA_API_URL)
    134136                    .replace(API_KEY_PLACEHOLDER, getKey())
    135                     .replace(API_KEY_LAYER, this.layer)).toURL();
     137                    .replace(API_KEY_LAYER, layer)).toURL();
    136138        } catch (URISyntaxException e) {
    137139            MalformedURLException malformedURLException = new MalformedURLException(e.getMessage());
    138140            malformedURLException.initCause(e);
    … … public class BingAerialTileSource extends TMSTileSource {  
    158160        String key = FeatureAdapter.getSetting(API_KEY_SETTING, API_KEY);
    159161        if (API_KEY.equals(key)) { // If the API key has not been customized, we try to retrieve the API key
    160162            try {
    161                 String rkey = FeatureAdapter.retrieveApiKey(this.getId());
     163                String rkey = FeatureAdapter.retrieveApiKey(getId());
    162164                if (rkey != null)
    163165                    key = rkey;
    164166            } catch (IOException ioException) {
    165                 FeatureAdapter.getLogger(this.getClass()).log(Level.WARNING, "Failed to retrieve api key", ioException);
     167                LOG.log(Level.WARNING, "Failed to retrieve api key", ioException);
    166168            }
    167169        }
    168170        return key;
    … … public class BingAerialTileSource extends TMSTileSource {  
    259261            if (imageResource != null) {
    260262                return FeatureAdapter.readImage(imageResource);
    261263            } else {
    262                 // Some Linux distributions (like Debian) will remove Bing logo from sources, so get it at runtime
    263                 for (int i = 0; i < 5 && (getAttribution() == null || getAttribution().isEmpty()); i++) {
    264                     // Makes sure attribution is loaded
    265                     if (JMapViewer.debug) {
    266                         LOG.log(Level.FINE, "Bing attribution attempt {0}", (i + 1));
    267                     }
    268                 }
     264                // Some Linux distributions (like Debian) will remove Bing logo from sources, so get it at runtime.
     265                // The logo URI is part of the attribution data, which this starts loading if it is not loaded yet.
     266                getAttribution();
    269267                if (brandLogoUri != null && !brandLogoUri.isEmpty()) {
    270268                    LOG.log(Level.FINE, "Reading Bing logo from {0}", brandLogoUri);
    271269                    return FeatureAdapter.readImage(new URI(brandLogoUri));
    272270                }
    273271            }
    274272        } catch (URISyntaxException | IOException e) {
    275             LOG.log(Level.SEVERE, String.format("Error while retrieving Bing logo: %s", e.getMessage()));
     273            LOG.log(Level.SEVERE, "Error while retrieving Bing logo: {0}", e.getMessage());
    276274        }
    277275        return null;
    278276    }
    … … public class BingAerialTileSource extends TMSTileSource {  
    296294        return () -> {
    297295            int waitTimeSec = 1;
    298296            while (true) {
    299                 try {
    300                     InputSource xml = new InputSource(getAttributionUrl().openStream());
    301                     List<Attribution> r = parseAttributionText(xml);
     297                try (InputStream in = getAttributionUrl().openStream()) {
     298                    List<Attribution> r = parseAttributionText(new InputSource(in));
    302299                    LOG.log(Level.FINE, "Successfully loaded Bing attribution data.");
    303300                    return r;
    304301                } catch (IllegalArgumentException | IOException ex) {
    … … public class BingAerialTileSource extends TMSTileSource {  
    320317            // see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
    321318            synchronized (BingAerialTileSource.class) {
    322319                if (attributions == null) {
    323                   final FutureTask<List<Attribution>> loader = new FutureTask<>(getAttributionLoaderCallable());
    324                   new Thread(loader, "bing-attribution-loader").start();
    325                   attributions = loader;
     320                    final FutureTask<List<Attribution>> loader = new FutureTask<>(getAttributionLoaderCallable());
     321                    new Thread(loader, "bing-attribution-loader").start();
     322                    attributions = loader;
    326323                }
    327324            }
    328325        }
    … … public class BingAerialTileSource extends TMSTileSource {  
    361358    private void setImageUrlTemplate(String template) {
    362359        String noHttpTemplate = template.replace("http://ecn.{subdomain}.tiles.virtualearth.net/",
    363360                "https://ecn.{subdomain}.tiles.virtualearth.net/");
    364         this.imageUrlTemplate = PATTERN_CULTURE.matcher(noHttpTemplate).replaceAll(Locale.getDefault().toString());
     361        imageUrlTemplate = noHttpTemplate.replace("{culture}", Locale.getDefault().toString());
    365362    }
    366363
    367364    private void setImageryZoomMax(int maxZoom) {
    … … public class BingAerialTileSource extends TMSTileSource {  
    374371            subdomains[i] = subdomainTxt.item(i).getNodeValue();
    375372        }
    376373    }
    377 
    378     private static String computeQuadTree(int zoom, int tilex, int tiley) {
    379         StringBuilder k = new StringBuilder();
    380         for (int i = zoom; i > 0; i--) {
    381             char digit = 48;
    382             int mask = 1 << (i - 1);
    383             if ((tilex & mask) != 0) {
    384                 digit += (char) 1;
    385             }
    386             if ((tiley & mask) != 0) {
    387                 digit += (char) 2;
    388             }
    389             k.append(digit);
    390         }
    391         return k.toString();
    392     }
    393374}
  • src/org/openstreetmap/gui/jmapviewer/tilesources/OsmTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/OsmTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/OsmTileSource.java
    index 352e1d0..ed7cfc3 100644
    import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;  
    88/**
    99 * OSM Tile source.
    1010 */
    11 public class OsmTileSource {
     11public final class OsmTileSource {
     12
     13    private OsmTileSource() {
     14        // Hide default constructor for the holder of the tile source classes
     15    }
    1216
    1317    /**
    1418     * The default "Mapnik" OSM tile source.
    … … public class OsmTileSource {  
    3135
    3236        @Override
    3337        public String getBaseUrl() {
    34             String url = String.format(this.baseUrl, SERVER[serverNum]);
     38            String url = String.format(baseUrl, SERVER[serverNum]);
    3539            serverNum = (serverNum + 1) % SERVER.length;
    3640            return url;
    3741        }
    … … public class OsmTileSource {  
    5761
    5862        @Override
    5963        public String getBaseUrl() {
    60             String url = String.format(this.baseUrl, SERVER[serverNum]);
     64            String url = String.format(baseUrl, SERVER[serverNum]);
    6165            serverNum = (serverNum + 1) % SERVER.length;
    6266            return url;
    6367        }
    … … public class OsmTileSource {  
    7781
    7882        @Override
    7983        public String getTileUrl(int zoom, int tilex, int tiley) throws IOException {
    80             return this.getBaseUrl() + getTilePath(zoom, tilex, tiley) + "?apikey=" + getApiKey();
     84            return getBaseUrl() + getTilePath(zoom, tilex, tiley) + "?apikey=" + getApiKey();
    8185        }
    8286
    8387        @Override
    … … public class OsmTileSource {  
    115119
    116120        @Override
    117121        public String getAttributionLinkURL() {
    118             return "https://öpnvkarte.de/<";
     122            return "https://öpnvkarte.de/";
    119123        }
    120124    }
    121125}
  • src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java
    index 49b573d..cddb8e2 100644
     
    44package org.openstreetmap.gui.jmapviewer.tilesources;
    55
    66import java.awt.Point;
    7 import java.util.Random;
     7import java.util.concurrent.ThreadLocalRandom;
    88
    99import org.openstreetmap.gui.jmapviewer.Coordinate;
    1010import org.openstreetmap.gui.jmapviewer.OsmMercator;
    … … import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;  
    1818 * sphere in OsmMercator, so latitude calculation differs a lot.
    1919 * <p>
    2020 * The longitude calculation is the same as in OsmMercator,
    21  * we inherit it from AbstractTMSTileSource.
    22  *
    23  * TODO: correct getDistance() method.
     21 * we inherit it from TMSTileSource.
     22 * <p>
     23 * Note that {@link #getDistance(double, double, double, double)} is inherited as well, so it still
     24 * assumes a sphere.
    2425 */
    2526public class ScanexTileSource extends TMSTileSource {
    2627    private static final String DEFAULT_URL = "https://maps.kosmosnimki.ru";
    … … public class ScanexTileSource extends TMSTileSource {  
    2829    private static final String API_KEY = "4018C5A9AECAD8868ED5DEB2E41D09F7";
    2930
    3031    // Latitude to Y and back calculations.
    31     private static final double RADIUS_E = 6_378_137;   /* radius of Earth at equator, m */
    32     private static final double EQUATOR = 40075016.68557849; /* equator length, m */
    33     private static final double E = 0.0818191908426;  /* eccentricity of Earth's ellipsoid */
     32    /** radius of Earth at equator, m */
     33    private static final double RADIUS_E = 6_378_137;
     34    /** equator length, m */
     35    private static final double EQUATOR = 40075016.68557849;
     36    /** eccentricity of Earth's ellipsoid */
     37    private static final double E = 0.0818191908426;
    3438
    3539    private enum ScanexLayer {
    3640        IRS("irs", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=BAC78D764F0443BD9AF93E7A998C9F5B");
    … … public class ScanexTileSource extends TMSTileSource {  
    7478         * thus is extraneous.  But let it be there just in case if
    7579         * OsmMercator changes.
    7680         */
    77         this.tileSize = 256;
     81        tileSize = 256;
    7882
    7983        for (ScanexLayer slayer : ScanexLayer.values()) {
    8084            if (url.equalsIgnoreCase(slayer.getName())) {
    81                 this.layer = slayer;
     85                layer = slayer;
    8286                // Override baseUrl and maxZoom in base class.
    83                 this.baseUrl = DEFAULT_URL;
     87                baseUrl = DEFAULT_URL;
    8488                if (maxZoom == 0)
    85                     this.maxZoom = DEFAULT_MAXZOOM;
     89                    maxZoom = DEFAULT_MAXZOOM;
    8690                return;
    8791            }
    8892        }
    89         /* If not "irs" or "spot" keyword, then a custom URL. */
    90         TemplatedTMSTileSource.checkUrl(info.getUrl());
    91         this.templateSource = new TemplatedTMSTileSource(info);
     93        // If not the "irs" keyword, then a custom URL.
     94        TemplatedTMSTileSource.checkUrl(url);
     95        templateSource = new TemplatedTMSTileSource(info);
    9296    }
    9397
    9498    @Override
    … … public class ScanexTileSource extends TMSTileSource {  
    96100        return "jpeg";
    97101    }
    98102
    99    @Override
     103    @Override
    100104    public String getTileUrl(int zoom, int tilex, int tiley) {
    101         if (this.templateSource != null)
    102             return this.templateSource.getTileUrl(zoom, tilex, tiley);
     105        if (templateSource != null)
     106            return templateSource.getTileUrl(zoom, tilex, tiley);
    103107        else
    104             return this.getBaseUrl() + getTilePath(zoom, tilex, tiley);
     108            return getBaseUrl() + getTilePath(zoom, tilex, tiley);
    105109    }
    106110
    107111    @Override
    … … public class ScanexTileSource extends TMSTileSource {  
    111115        tilex = tilex - tmp;
    112116        tiley = tmp - tiley - 1;
    113117
    114         return this.layer.getUri() + "&apikey=" + API_KEY + "&x=" + tilex + "&y=" + tiley + "&z=" + zoom;
     118        return layer.getUri() + "&apikey=" + API_KEY + "&x=" + tilex + "&y=" + tiley + "&z=" + zoom;
    115119    }
    116120
    117121    @Override
    … … public class ScanexTileSource extends TMSTileSource {  
    153157        return (EQUATOR/2 - (RADIUS_E * Math.log(tmp/pow))) * Math.pow(2.0, zoom) / EQUATOR;
    154158    }
    155159
    156     /*
     160    /**
    157161     * To solve inverse formula latitude = f(y) we use
    158162     * Newton's method. We cache previous calculated latitude,
    159163     * because new one is usually close to the old one. In case
    … … public class ScanexTileSource extends TMSTileSource {  
    166170            lat0 = lat;
    167171            lat = lat - Math.toDegrees(nextTerm(Math.toRadians(lat), y, zoom));
    168172            if (lat > OsmMercator.MAX_LAT || lat < OsmMercator.MIN_LAT) {
    169                 Random r = new Random();
    170173                lat = OsmMercator.MIN_LAT +
    171                   r.nextInt((int) (OsmMercator.MAX_LAT - OsmMercator.MIN_LAT));
     174                  ThreadLocalRandom.current().nextInt((int) (OsmMercator.MAX_LAT - OsmMercator.MIN_LAT));
    172175            }
    173176        } while (Math.abs(lat0 - lat) > 0.000001);
    174177
    … … public class ScanexTileSource extends TMSTileSource {  
    177180        return lat;
    178181    }
    179182
    180     /* Next term in Newton's polynomial */
     183    /** Next term in Newton's polynomial */
    181184    private static double nextTerm(double lat, double y, int zoom) {
    182185        double sinl = Math.sin(lat);
    183186        double cosl = Math.cos(lat);
    184187
    185         zoom = (int) Math.pow(2.0, zoom - 1d);
    186         double ec = Math.exp((1 - y/zoom)*Math.PI);
     188        int halfTiles = (int) Math.pow(2.0, zoom - 1d);
     189        double ec = Math.exp((1 - y/halfTiles)*Math.PI);
    187190
    188191        double f = Math.tan(Math.PI/4+lat/2) -
    189192            ec * Math.pow(Math.tan(Math.PI/4 + Math.asin(E * sinl)/2), E);
  • src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java
    index 09645d7..eb7fa99 100644
    import org.openstreetmap.gui.jmapviewer.interfaces.IProjected;  
    1717 */
    1818public class TMSTileSource extends AbstractTMSTileSource {
    1919
     20    /** Length of the equator in the projected coordinates, in meters */
     21    private static final double MERCATOR_WIDTH = 2 * Math.PI * OsmMercator.EARTH_RADIUS;
     22
    2023    protected int maxZoom;
    2124    protected int minZoom;
    2225    protected OsmMercator osmMercator;
    … … public class TMSTileSource extends AbstractTMSTileSource {  
    2528     * Constructs a new {@code TMSTileSource}.
    2629     * @param info tile source information
    2730     */
     31    @SuppressWarnings("this-escape")
    2832    public TMSTileSource(TileSourceInfo info) {
    2933        super(info);
    3034        minZoom = info.getMinZoom();
    3135        maxZoom = info.getMaxZoom();
    32         this.osmMercator = new OsmMercator(this.getTileSize());
     36        osmMercator = new OsmMercator(getTileSize());
    3337    }
    3438
    3539    @Override
    … … public class TMSTileSource extends AbstractTMSTileSource {  
    8185
    8286    @Override
    8387    public IProjected tileXYtoProjected(int x, int y, int zoom) {
    84         double mercatorWidth = 2 * Math.PI * OsmMercator.EARTH_RADIUS;
    85         double f = mercatorWidth * getTileSize() / osmMercator.getMaxPixels(zoom);
    86         return new Projected(f * x - mercatorWidth / 2, -(f * y - mercatorWidth / 2));
     88        double f = MERCATOR_WIDTH * getTileSize() / osmMercator.getMaxPixels(zoom);
     89        return new Projected(f * x - MERCATOR_WIDTH / 2, -(f * y - MERCATOR_WIDTH / 2));
    8790    }
    8891
    8992    @Override
    9093    public TileXY projectedToTileXY(IProjected p, int zoom) {
    91         double mercatorWidth = 2 * Math.PI * OsmMercator.EARTH_RADIUS;
    92         double f = mercatorWidth * getTileSize() / osmMercator.getMaxPixels(zoom);
    93         return new TileXY((p.getEast() + mercatorWidth / 2) / f, (-p.getNorth() + mercatorWidth / 2) / f);
     94        double f = MERCATOR_WIDTH * getTileSize() / osmMercator.getMaxPixels(zoom);
     95        return new TileXY((p.getEast() + MERCATOR_WIDTH / 2) / f, (-p.getNorth() + MERCATOR_WIDTH / 2) / f);
    9496    }
    9597
    9698    @Override
  • src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java

    diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java
    index b64f131..b8337c4 100644
     
    22package org.openstreetmap.gui.jmapviewer.tilesources;
    33
    44import java.io.IOException;
    5 import java.util.Arrays;
    65import java.util.HashMap;
     6import java.util.List;
    77import java.util.Map;
    8 import java.util.Random;
     8import java.util.concurrent.ThreadLocalRandom;
    99import java.util.function.BiConsumer;
    1010import java.util.regex.Matcher;
    1111import java.util.regex.Pattern;
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    4949    private static final Pattern PATTERN_SWITCH  = Pattern.compile("\\{switch:([^}]+)}");
    5050    private static final Pattern PATTERN_HEADER  = Pattern.compile("\\{header\\(([^,]+),([^}]+)\\)}");
    5151    private static final Pattern PATTERN_API_KEY = Pattern.compile("\\{apikey}");
    52     private static final Pattern PATTERN_PARAM  = Pattern.compile("\\{((?:\\d+-)?z(?:oom)?(:?[+-]\\d+)?|x|y|!y|-y|quad|switch:([^}]+))}");
    53 
     52    private static final Pattern PATTERN_PARAM   = Pattern.compile("\\{((?:\\d+-)?z(?:oom)?(?:[+-]\\d+)?|x|y|!y|-y|quad|switch:([^}]+))}");
     53    private static final Pattern PATTERN_ANY     = Pattern.compile("\\{[^}]*}");
    5454    // CHECKSTYLE.ON: SingleSpaceSeparator
    5555
    56     private static final Pattern[] ALL_PATTERNS = {
     56    private static final List<Pattern> ALL_PATTERNS = List.of(
    5757            PATTERN_HEADER, PATTERN_ZOOM, PATTERN_X, PATTERN_Y, PATTERN_Y_YAHOO, PATTERN_NEG_Y, PATTERN_SWITCH, PATTERN_API_KEY,
    58             PATTERN_QUAD
    59     };
     58            PATTERN_QUAD);
    6059
    61     private Random rand;
    6260    private String[] randomParts;
    6361    private final Map<String, String> headers = new HashMap<>();
    64     private boolean inverse_zoom;
    65     private int zoom_offset;
     62    private boolean inverseZoom;
     63    private int zoomOffset;
    6664
    6765    /**
    6866     * Creates Templated TMS Tile Source based on ImageryInfo
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    7775        handleTemplate(info.getId());
    7876    }
    7977
    80     private void replacePattern(BiConsumer<Matcher, StringBuffer> replaceAction, Pattern... patterns) {
    81         for (Pattern p : patterns) {
    82             StringBuffer output = new StringBuffer();
    83             Matcher m = p.matcher(baseUrl);
    84             while (m.find()) {
    85                 replaceAction.accept(m, output);
    86             }
    87             m.appendTail(output);
    88             baseUrl = output.toString();
     78    private void replacePattern(BiConsumer<Matcher, StringBuilder> replaceAction, Pattern pattern) {
     79        StringBuilder output = new StringBuilder();
     80        Matcher m = pattern.matcher(baseUrl);
     81        while (m.find()) {
     82            replaceAction.accept(m, output);
    8983        }
     84        m.appendTail(output);
     85        baseUrl = output.toString();
    9086    }
    9187
    9288    private void handleTemplate(String imageryId) {
    9389        // Capturing group pattern on switch values
    9490        Matcher m = PATTERN_SWITCH.matcher(baseUrl);
    9591        if (m.find()) {
    96             rand = new Random();
    9792            randomParts = m.group(1).split(",", -1);
    9893        }
    9994        // Capturing group pattern on header values
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    105100        if (imageryId != null) {
    106101            replacePattern((matcher, output) -> {
    107102                try {
    108                     matcher.appendReplacement(output, FeatureAdapter.retrieveApiKey(imageryId));
     103                    // the key is literal text, '$' and '\' in it are no group references
     104                    matcher.appendReplacement(output, Matcher.quoteReplacement(FeatureAdapter.retrieveApiKey(imageryId)));
    109105                } catch (IOException | RuntimeException e) {
    110106                    throw new IllegalArgumentException(e);
    111107                }
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    115111        m = PATTERN_ZOOM.matcher(baseUrl);
    116112        if (m.find()) {
    117113            if (m.group(1) != null) {
    118                 inverse_zoom = true;
    119                 zoom_offset = Integer.parseInt(m.group(1));
     114                inverseZoom = true;
     115                zoomOffset = Integer.parseInt(m.group(1));
    120116            }
    121117            if (m.group(2) != null) {
    122                 String ofs = m.group(2);
    123                 if (ofs.startsWith("+"))
    124                     ofs = ofs.substring(1);
    125                 zoom_offset += Integer.parseInt(ofs);
     118                zoomOffset += Integer.parseInt(m.group(2));
    126119            }
    127120        }
    128121    }
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    134127
    135128    @Override
    136129    public String getTileUrl(int zoom, int tilex, int tiley) {
    137         StringBuffer url = new StringBuffer(baseUrl.length());
     130        StringBuilder url = new StringBuilder(baseUrl.length());
    138131        Matcher matcher = PATTERN_PARAM.matcher(baseUrl);
    139132        while (matcher.find()) {
    140133            final String replacement;
     134            // Java 14: use a switch expression, "case "z", "zoom" ->" replaces the fall through
    141135            switch (matcher.group(1)) {
    142136            case "z": // PATTERN_ZOOM
    143137            case "zoom":
    144                 replacement = Integer.toString((inverse_zoom ? -zoom : zoom) + zoom_offset);
     138                replacement = zoomParameter(zoom);
    145139                break;
    146140            case "x": // PATTERN_X
    147141                replacement = Integer.toString(tilex);
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    155149            case "-y": // PATTERN_NEG_Y
    156150                replacement = Integer.toString((int) Math.pow(2, zoom)-1-tiley);
    157151                break;
    158             case "switch:":
    159                 replacement = getRandomPart(randomParts);
    160                 break;
    161152            case "quad": // PATTERN_QUAD
    162153                replacement = xyzToQuadKey(tilex, tiley, zoom);
    163154                break;
    164155            default:
    165                 // handle switch/zoom here, as group will contain parameters and switch will not work
    166                 if (PATTERN_ZOOM.matcher("{" + matcher.group(1) + "}").matches()) {
    167                     replacement = Integer.toString((inverse_zoom ? -zoom : zoom) + zoom_offset);
    168                 } else if (PATTERN_SWITCH.matcher("{" + matcher.group(1) + "}").matches()) {
    169                     replacement = getRandomPart(randomParts);
    170                 } else {
    171                     replacement = '{' + matcher.group(1) + '}';
    172                 }
     156                // the zoom with an offset, or the switch, whose values are in group 2: their parameters defeat the switch labels
     157                replacement = matcher.group(2) != null ? getRandomPart(randomParts) : zoomParameter(zoom);
    173158            }
    174             matcher.appendReplacement(url, replacement);
     159            // the switch values are literal text, '$' and '\' in them are no group references
     160            matcher.appendReplacement(url, Matcher.quoteReplacement(replacement));
    175161        }
    176162        matcher.appendTail(url);
    177163        return url.toString().replace(" ", "%20");
    178164    }
    179165
     166    private String zoomParameter(int zoom) {
     167        return Integer.toString((inverseZoom ? -zoom : zoom) + zoomOffset);
     168    }
     169
    180170    /**
    181171     * Convert an x y z coordinate to a quadkey
    182172     * @param x The x coordinate
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    184174     * @param z The z coordinate
    185175     * @return The quadkey
    186176     */
    187     private static String xyzToQuadKey(int x, int y, int z) {
     177    static String xyzToQuadKey(int x, int y, int z) {
    188178        final char[] string = new char[z];
    189179        for (int i = z; i > 0; i--) {
    190180            char digit = '0';
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    200190        return String.valueOf(string);
    201191    }
    202192
     193    /**
     194     * Picks one of the values of a {switch:...} parameter.
     195     * @param parts the values to choose from
     196     * @return a randomly chosen value
     197     */
    203198    protected String getRandomPart(final String[] parts) {
    204         return parts[rand.nextInt(parts.length)];
     199        return parts[ThreadLocalRandom.current().nextInt(parts.length)];
    205200    }
    206201
    207202    /**
    … … public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi  
    209204     * @param url URL to check
    210205     */
    211206    public static void checkUrl(String url) {
    212         assert url != null && !"".equals(url) : "URL cannot be null or empty";
    213         Matcher m = Pattern.compile("\\{[^}]*}").matcher(url);
     207        assert url != null && !url.isEmpty() : "URL cannot be null or empty";
     208        Matcher m = PATTERN_ANY.matcher(url);
    214209        while (m.find()) {
    215             boolean isSupportedPattern = Arrays.stream(ALL_PATTERNS).anyMatch(pattern -> pattern.matcher(m.group()).matches());
     210            boolean isSupportedPattern = ALL_PATTERNS.stream().anyMatch(pattern -> pattern.matcher(m.group()).matches());
    216211            if (!isSupportedPattern) {
    217212                throw new IllegalArgumentException(
    218213                        m.group() + " is not a valid TMS argument. Please check this server URL:\n" + url);
  • test/org/openstreetmap/gui/jmapviewer/TileRangeTest.java

    diff --git test/org/openstreetmap/gui/jmapviewer/TileRangeTest.java test/org/openstreetmap/gui/jmapviewer/TileRangeTest.java
    index b91faff..2426b5a 100644
     
    11// License: GPL. For details, see Readme.txt file.
    22package org.openstreetmap.gui.jmapviewer;
    33
    4 
    54import static org.junit.jupiter.api.Assertions.assertEquals;
    65import static org.junit.jupiter.api.Assertions.assertThrows;
    76
    … … class TileRangeTest {  
    1817    @Test
    1918    void testSize() {
    2019        assertEquals(16, new TileRange(
    21                 new TileXY(3, 3), 
     20                new TileXY(3, 3),
    2221                new TileXY(6, 6), 10).size());
    2322    }
    2423
     24    /**
     25     * Fractional tile coordinates widen the range to whole tiles, both ends included.
     26     */
     27    @Test
     28    void testFractionalCorners() {
     29        assertEquals(9, new TileRange(new TileXY(0.5, 0.5), new TileXY(1.5, 1.5), 3).size());
     30    }
     31
    2532    /**
    2633     * Ensure that something exceptional happens when an integer overflow happens
    2734     */
  • test/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourceTest.java

    diff --git test/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourceTest.java test/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourceTest.java
    index 8e625b3..77e7919 100644
     
    22package org.openstreetmap.gui.jmapviewer.tilesources;
    33
    44import static org.junit.jupiter.api.Assertions.assertEquals;
     5import static org.junit.jupiter.api.Assertions.assertThrows;
    56import static org.junit.jupiter.api.Assertions.assertTrue;
    67
    7 import java.text.MessageFormat;
    8 import java.util.Arrays;
    9 import java.util.Collection;
    10 import java.util.Collections;
    11 import java.util.function.Predicate;
    12 import java.util.stream.Stream;
     8import java.util.List;
     9import java.util.Map;
     10import java.util.Set;
    1311
    1412import org.junit.jupiter.api.Test;
    1513
    1614/**
    1715 * Tests for TemplatedTMSTileSource
    1816 */
    19 public class TemplatedTMSTileSourceTest {
    20 
    21     private static final Collection<String> TMS_IMAGERIES = Collections.singleton("http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z={zoom}&x={x}&y={-y}"
    22             /*
    23              *  generate for example with:
    24              *  $ curl https://josm.openstreetmap.de/maps | \
    25              *    xmlstarlet sel -N 'josm=http://josm.openstreetmap.de/maps-1.0' -t -v "//josm:entry[josm:type='tms']/josm:url" -n  | \
    26              *    sed -e 's/\&amp;/\&/g' -e 's/^/"/' -e 's/$/",/'
    27              */);
     17class TemplatedTMSTileSourceTest {
    2818
    2919    /**
    3020     * triple of:
    … … public class TemplatedTMSTileSourceTest {  
    3222     *  * expected tile url for zoom=4, x=5, y=6
    3323     *  * expected tile url for zoom=3, x=2, y=1
    3424     */
    35     private final Collection<String[]> TEST_DATA = Arrays.asList(new String[][] {
    36         /*
    37          * generate with main method below once TMS_IMAGERIES is filled in
    38          */
     25    private static final List<String[]> TEST_DATA = List.<String[]>of(
    3926        new String[] {
    40                 "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z={zoom}&x={x}&y={-y}",
    41                 "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z=4&x=5&y=9",
    42                 "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z=3&x=2&y=6"
    43                 }
    44     });
     27            "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z={zoom}&x={x}&y={-y}",
     28            "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z=4&x=5&y=9",
     29            "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z=3&x=2&y=6"
     30        });
    4531
    4632    /**
    4733     * Check standard template
    … … public class TemplatedTMSTileSourceTest {  
    10490    }
    10591
    10692    /**
    107      * Test template with switch
     93     * Test template with API key
    10894     */
    10995    @Test
    11096    void testGetTileUrl_apiKey() {
    11197        System.setProperty("id1.api-key", "wololo");
    112         TileSourceInfo testImageryTMS = new TileSourceInfo("test imagery", "http://localhost/{zoom}/{x}/{y}?token={apikey}&foo=bar", "id1");
    113         TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
    114         assertEquals("http://localhost/4/5/6?token=wololo&foo=bar", ts.getTileUrl(4, 5, 6));
     98        try {
     99            TileSourceInfo testImageryTMS = new TileSourceInfo("test imagery", "http://localhost/{zoom}/{x}/{y}?token={apikey}&foo=bar", "id1");
     100            TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
     101            assertEquals("http://localhost/4/5/6?token=wololo&foo=bar", ts.getTileUrl(4, 5, 6));
     102        } finally {
     103            System.clearProperty("id1.api-key");
     104        }
    115105    }
    116106
    117107    /**
    … … public class TemplatedTMSTileSourceTest {  
    121111    void testGetTileUrl_switch() {
    122112        TileSourceInfo testImageryTMS = new TileSourceInfo("test imagery", "http://{switch:a,b,c}.localhost/{10-zoom-5}/{x}/{y}", "id1");
    123113        TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
    124         assertTrue(
    125                 Stream.of(
    126                         "http://a.localhost/4/2/3",
    127                         "http://b.localhost/4/2/3",
    128                         "http://c.localhost/4/2/3"
    129                         )
    130                 .anyMatch(Predicate.isEqual(ts.getTileUrl(1, 2, 3)))
    131                 );
     114        assertOneOfServers("/4/2/3", ts.getTileUrl(1, 2, 3));
     115        assertOneOfServers("/3/3/4", ts.getTileUrl(2, 3, 4));
     116        assertOneOfServers("/2/4/5", ts.getTileUrl(3, 4, 5));
     117        assertOneOfServers("/1/5/6", ts.getTileUrl(4, 5, 6));
     118    }
    132119
    133         assertTrue(
    134                 Stream.of(
    135                         "http://a.localhost/3/3/4",
    136                         "http://b.localhost/3/3/4",
    137                         "http://c.localhost/3/3/4"
    138                         )
    139                 .anyMatch(Predicate.isEqual(ts.getTileUrl(2, 3, 4)))
    140                 );
    141         assertTrue(
    142                 Stream.of(
    143                         "http://a.localhost/2/4/5",
    144                         "http://b.localhost/2/4/5",
    145                         "http://c.localhost/2/4/5"
    146                         )
    147                 .anyMatch(Predicate.isEqual(ts.getTileUrl(3, 4, 5)))
    148                 );
    149         assertTrue(
    150                 Stream.of(
    151                         "http://a.localhost/1/5/6",
    152                         "http://b.localhost/1/5/6",
    153                         "http://c.localhost/1/5/6"
    154                         )
    155                 .anyMatch(Predicate.isEqual(ts.getTileUrl(4, 5, 6)))
    156                 );
     120    private static void assertOneOfServers(String path, String url) {
     121        Set<String> expected = Set.of("http://a.localhost" + path, "http://b.localhost" + path, "http://c.localhost" + path);
     122        assertTrue(expected.contains(url), url);
    157123    }
    158124
     125    /**
     126     * Test template with Yahoo style y index
     127     */
    159128    @Test
    160129    void testGetTileUrl_yahoo() {
    161130        checkGetTileUrl(
    … … public class TemplatedTMSTileSourceTest {  
    163132                "http://localhost/4/5/1",
    164133                "http://localhost/3/1/1"
    165134                );
    166 
    167135    }
    168136
     137    /**
     138     * Test template with reversed (TMS) y index
     139     */
    169140    @Test
    170141    void testGetTileUrl_negative_y() {
    171142        checkGetTileUrl(
    … … public class TemplatedTMSTileSourceTest {  
    173144                "http://localhost/4/5/9",
    174145                "http://localhost/3/1/5"
    175146                );
    176 
    177147    }
    178148
     149    /**
     150     * Test template with quadkey
     151     */
    179152    @Test
    180153    void testGetTileUrl_quad() {
    181154        checkGetTileUrl(
    … … public class TemplatedTMSTileSourceTest {  
    183156                "http://localhost/0321",
    184157                "http://localhost/021"
    185158                );
     159    }
     160
     161    /**
     162     * Test that the header placeholders become headers and leave the URL
     163     */
     164    @Test
     165    void testHeaders() {
     166        TileSourceInfo info = new TileSourceInfo("test imagery", "http://localhost/{header(X-Test,value)}{z}/{x}/{y}", "id1");
     167        info.setCookies("a=b");
     168        TemplatedTMSTileSource ts = new TemplatedTMSTileSource(info);
     169        assertEquals(Map.of("X-Test", "value", "Cookie", "a=b"), ts.getHeaders());
     170        assertEquals("http://localhost/4/5/6", ts.getTileUrl(4, 5, 6));
     171    }
     172
     173    /**
     174     * Test that spaces are encoded
     175     */
     176    @Test
     177    void testSpacesAreEncoded() {
     178        checkGetTileUrl(
     179                "http://localhost/my layer/{z}/{x}/{y}",
     180                "http://localhost/my%20layer/4/5/6",
     181                "http://localhost/my%20layer/3/1/2"
     182                );
     183    }
    186184
     185    /**
     186     * Test the URL validation
     187     */
     188    @Test
     189    void testCheckUrl() {
     190        TemplatedTMSTileSource.checkUrl("http://{switch:a,b}.localhost/{zoom}/{x}/{-y}.png?{quad}");
     191        assertThrows(IllegalArgumentException.class, () -> TemplatedTMSTileSource.checkUrl("http://localhost/{unknown}/{x}/{y}"));
    187192    }
    188193
    189     private void checkGetTileUrl(String url, String expected123, String expected312) {
     194    private static void checkGetTileUrl(String url, String expected456, String expected312) {
    190195        TileSourceInfo testImageryTMS = new TileSourceInfo("test imagery", url, "id1");
    191196        TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
    192         assertEquals(expected123, ts.getTileUrl(4, 5, 6));
     197        assertEquals(expected456, ts.getTileUrl(4, 5, 6));
    193198        assertEquals(expected312, ts.getTileUrl(3, 1, 2));
    194199    }
    195200
    … … public class TemplatedTMSTileSourceTest {  
    205210            assertEquals(test[2], ts.getTileUrl(3, 2, 1));
    206211        }
    207212    }
    208 
    209     public static void main(String[] args) {
    210         for (String url: TMS_IMAGERIES) {
    211             TileSourceInfo testImageryTMS = new TileSourceInfo("test imagery", url, "id1");
    212             TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
    213             System.out.println(MessageFormat.format("new String[]{\"{0}\", \"{1}\", \"{2}\"},",
    214                     url, ts.getTileUrl(4, 5, 6), ts.getTileUrl(3, 2, 1)));
    215         }
    216     }
    217213}
  • new file tools/assembly/release.xml

    diff --git tools/assembly/release.xml tools/assembly/release.xml
    new file mode 100644
    index 0000000..62fc8ab
    - +  
     1<?xml version="1.0" encoding="UTF-8"?>
     2<!-- Binary release zip, equivalent to the Ant "create_release_zip" target.
     3     The JOSM Jenkins release job expects JMapViewer.jar and JMapViewer_src.jar inside. -->
     4<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
     5          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     6          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 http://maven.apache.org/xsd/assembly-2.2.0.xsd">
     7    <id>release</id>
     8    <formats>
     9        <format>zip</format>
     10    </formats>
     11    <includeBaseDirectory>false</includeBaseDirectory>
     12    <files>
     13        <file>
     14            <source>${project.build.directory}/${project.build.finalName}.jar</source>
     15            <destName>JMapViewer.jar</destName>
     16        </file>
     17        <file>
     18            <source>${project.build.directory}/${project.build.finalName}-sources.jar</source>
     19            <destName>JMapViewer_src.jar</destName>
     20        </file>
     21        <file>
     22            <source>${project.basedir}/Readme.txt</source>
     23        </file>
     24        <file>
     25            <source>${project.basedir}/Gpl.txt</source>
     26        </file>
     27    </files>
     28</assembly>
  • new file tools/assembly/source-release.xml

    diff --git tools/assembly/source-release.xml tools/assembly/source-release.xml
    new file mode 100644
    index 0000000..cc009b8
    - +  
     1<?xml version="1.0" encoding="UTF-8"?>
     2<!-- Source release zip, equivalent to the Ant "create_source_release_zip" target.
     3     It ships pom.xml, the tests and the tools/ directory as well, so that the unpacked zip
     4     can be built with Maven as well as with Ant. -->
     5<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
     6          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     7          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 http://maven.apache.org/xsd/assembly-2.2.0.xsd">
     8    <id>Source</id>
     9    <formats>
     10        <format>zip</format>
     11    </formats>
     12    <baseDirectory>jmapviewer-${project.version}</baseDirectory>
     13    <files>
     14        <file>
     15            <source>${project.basedir}/Readme.txt</source>
     16        </file>
     17        <file>
     18            <source>${project.basedir}/Gpl.txt</source>
     19        </file>
     20        <file>
     21            <source>${project.basedir}/build.xml</source>
     22        </file>
     23        <file>
     24            <source>${project.basedir}/pom.xml</source>
     25        </file>
     26    </files>
     27    <fileSets>
     28        <fileSet>
     29            <directory>${project.basedir}/src</directory>
     30            <outputDirectory>src</outputDirectory>
     31        </fileSet>
     32        <fileSet>
     33            <directory>${project.basedir}/test</directory>
     34            <outputDirectory>test</outputDirectory>
     35        </fileSet>
     36        <fileSet>
     37            <directory>${project.basedir}/tools</directory>
     38            <outputDirectory>tools</outputDirectory>
     39        </fileSet>
     40    </fileSets>
     41</assembly>
  • tools/checkstyle/jmapviewer_checks.xml

    diff --git tools/checkstyle/jmapviewer_checks.xml tools/checkstyle/jmapviewer_checks.xml
    index 10e401a..bc6bf54 100644
     
    11<?xml version="1.0" encoding="UTF-8"?>
    2 <!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
    3 
    4 <!--
    5     This configuration file was written by the eclipse-cs plugin configuration editor
    6 -->
    7 <!--
    8     Checkstyle-Configuration: JMapViewer
    9     Description:
    10 JMapViewer Checkstyle rules
    11 -->
     2<!DOCTYPE module PUBLIC "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "https://checkstyle.org/dtds/configuration_1_3.dtd">
     3<!-- JMapViewer checkstyle rules, used by Maven, Ant and eclipse-cs (.checkstyle) -->
    124<module name="Checker">
    135  <property name="severity" value="warning"/>
    146  <property name="charset" value="UTF-8"/>
     7  <!-- Checkstyle 10 cannot parse module declarations; drop this filter once the build needs JDK 21 (Checkstyle 14+) -->
     8  <module name="BeforeExecutionExclusionFileFilter">
     9    <property name="fileNamePattern" value="module\-info\.java$"/>
     10  </module>
    1511  <module name="TreeWalker">
    1612    <module name="MissingDeprecated"/>
    1713    <module name="MissingOverride"/>
    … … JMapViewer Checkstyle rules  
    2016      <property name="allowSamelineMultipleAnnotations" value="true"/>
    2117    </module>
    2218    <module name="AnnotationUseStyle"/>
    23     <module name="JavadocStyle">
    24       <property name="checkFirstSentence" value="false"/>
    25       <property name="checkHtml" value="false"/>
    26     </module>
    2719    <module name="NonEmptyAtclauseDescription"/>
    2820    <module name="AtclauseOrder"/>
    2921    <module name="AvoidStarImport"/>
    … … JMapViewer Checkstyle rules  
    10496  </module>
    10597  <module name="FileTabCharacter"/>
    10698  <module name="LineLength">
    107     <property name="fileExtensions" value="java" />
     99    <property name="fileExtensions" value=".java"/>
    108100    <property name="max" value="145"/>
    109101    <property name="tabWidth" value="4"/>
    110102  </module>
    … … JMapViewer Checkstyle rules  
    112104    <property name="lineSeparator" value="lf"/>
    113105  </module>
    114106  <module name="SuppressionFilter">
    115     <property name="file" value="tools/checkstyle/jmapviewer_filters.xml"/>
    116     <property name="optional" value="true"/>
     107    <property name="file" value="${config_loc}/jmapviewer_filters.xml"/>
    117108  </module>
    118109  <module name="SuppressWithPlainTextCommentFilter">
    119110    <property name="offCommentFormat" value="CHECKSTYLE\.OFF\: ([\w\|]+)"/>
  • tools/checkstyle/jmapviewer_filters.xml

    diff --git tools/checkstyle/jmapviewer_filters.xml tools/checkstyle/jmapviewer_filters.xml
    index 14abc8d..be622b7 100644
     
    11<?xml version="1.0" encoding="UTF-8"?>
    22<!DOCTYPE suppressions PUBLIC
    3      "-//Puppy Crawl//DTD Suppressions 1.0//EN"
    4      "http://www.puppycrawl.com/dtds/suppressions_1_0.dtd">
     3    "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
     4    "https://checkstyle.org/dtds/suppressions_1_2.dtd">
    55<suppressions>
    6   <suppress checks="." files="\.classpath" />
    7   <suppress checks="." files="\.project" />
    8   <suppress checks="HeaderCheck" files="ScanexTileSource\.java" />
    9 </suppressions>
    10  No newline at end of file
     6  <!-- ScanexTileSource is dual licensed (BSD or GPL) -->
     7  <suppress checks="HeaderCheck" files="ScanexTileSource\.java"/>
     8</suppressions>