Ticket #16769: get_tile_url.patch

File get_tile_url.patch, 29.5 KB (added by wiktorn, 3 years ago)
  • 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 00aa032f3..5e4ee9b9c 100644
    public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi 
    3333    private Random rand;
    3434    private String[] randomParts;
    3535    private final Map<String, String> headers = new HashMap<>();
     36    private boolean inverse_zoom = false;
     37    private int zoom_offset = 0;
    3638
    3739    // CHECKSTYLE.OFF: SingleSpaceSeparator
    3840    private static final String COOKIE_HEADER   = "Cookie";
    39     private static final String PATTERN_ZOOM    = "\\{(?:(\\d+)-)?z(?:oom)?([+-]\\d+)?\\}";
    40     private static final String PATTERN_X       = "\\{x\\}";
    41     private static final String PATTERN_Y       = "\\{y\\}";
    42     private static final String PATTERN_Y_YAHOO = "\\{!y\\}";
    43     private static final String PATTERN_NEG_Y   = "\\{-y\\}";
    44     private static final String PATTERN_SWITCH  = "\\{switch:([^}]+)\\}";
    45     private static final String PATTERN_HEADER  = "\\{header\\(([^,]+),([^}]+)\\)\\}";
     41    private static final Pattern PATTERN_ZOOM    = Pattern.compile("\\{(?:(\\d+)-)?z(?:oom)?([+-]\\d+)?\\}");
     42    private static final Pattern PATTERN_X       = Pattern.compile("\\{x\\}");
     43    private static final Pattern PATTERN_Y       = Pattern.compile("\\{y\\}");
     44    private static final Pattern PATTERN_Y_YAHOO = Pattern.compile("\\{!y\\}");
     45    private static final Pattern PATTERN_NEG_Y   = Pattern.compile("\\{-y\\}");
     46    private static final Pattern PATTERN_SWITCH  = Pattern.compile("\\{switch:([^}]+)\\}");
     47    private static final Pattern PATTERN_HEADER  = Pattern.compile("\\{header\\(([^,]+),([^}]+)\\)\\}");
     48    private static final Pattern PATTERN_PARAM  = Pattern.compile("\\{((?:\\d+-)?z(?:oom)?(:?[+-]\\d+)?|x|y|!y|-y|switch:([^}]+))\\}");
    4649    // CHECKSTYLE.ON: SingleSpaceSeparator
    4750
    48     private static final String[] ALL_PATTERNS = {
     51    private static final Pattern[] ALL_PATTERNS = {
    4952        PATTERN_HEADER, PATTERN_ZOOM, PATTERN_X, PATTERN_Y, PATTERN_Y_YAHOO, PATTERN_NEG_Y, PATTERN_SWITCH
    5053    };
    5154
    public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi 
    6467
    6568    private void handleTemplate() {
    6669        // Capturing group pattern on switch values
    67         Matcher m = Pattern.compile(".*"+PATTERN_SWITCH+".*").matcher(baseUrl);
    68         if (m.matches()) {
     70        Matcher m = PATTERN_SWITCH.matcher(baseUrl);
     71        if (m.find()) {
    6972            rand = new Random();
    7073            randomParts = m.group(1).split(",");
    7174        }
    72         Pattern pattern = Pattern.compile(PATTERN_HEADER);
    7375        StringBuffer output = new StringBuffer();
    74         Matcher matcher = pattern.matcher(baseUrl);
     76        Matcher matcher = PATTERN_HEADER.matcher(baseUrl);
    7577        while (matcher.find()) {
    7678            headers.put(matcher.group(1), matcher.group(2));
    7779            matcher.appendReplacement(output, "");
    7880        }
    7981        matcher.appendTail(output);
    8082        baseUrl = output.toString();
     83        m = PATTERN_ZOOM.matcher(this.baseUrl);
     84        if (m.find()) {
     85            if (m.group(1) != null) {
     86                inverse_zoom = true;
     87                zoom_offset = Integer.parseInt(m.group(1));
     88            }
     89            if (m.group(2) != null) {
     90                String ofs = m.group(2);
     91                if (ofs.startsWith("+"))
     92                    ofs = ofs.substring(1);
     93                zoom_offset += Integer.parseInt(ofs);
     94            }
     95        }
     96
    8197    }
    8298
    8399    @Override
    public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi 
    87103
    88104    @Override
    89105    public String getTileUrl(int zoom, int tilex, int tiley) {
    90         int finalZoom = zoom;
    91         Matcher m = Pattern.compile(".*"+PATTERN_ZOOM+".*").matcher(this.baseUrl);
    92         if (m.matches()) {
    93             if (m.group(1) != null) {
    94                 finalZoom = Integer.parseInt(m.group(1))-zoom;
    95             }
    96             if (m.group(2) != null) {
    97                 String ofs = m.group(2);
    98                 if (ofs.startsWith("+"))
    99                     ofs = ofs.substring(1);
    100                 finalZoom += Integer.parseInt(ofs);
     106        StringBuffer url = new StringBuffer(baseUrl.length());
     107        Matcher matcher = PATTERN_PARAM.matcher(baseUrl);
     108        while (matcher.find()) {
     109            String replacement = "replace";
     110            switch (matcher.group(1)) {
     111            case "z": // PATTERN_ZOOM
     112            case "zoom":
     113                replacement = Integer.toString((inverse_zoom ? -1 * zoom : zoom) + zoom_offset);
     114                break;
     115            case "x": // PATTERN_X
     116                replacement = Integer.toString(tilex);
     117                break;
     118            case "y": // PATTERN_Y
     119                replacement = Integer.toString(tiley);
     120                break;
     121            case "!y": // PATTERN_Y_YAHOO
     122                replacement = Integer.toString((int) Math.pow(2, zoom-1)-1-tiley);
     123                break;
     124            case "-y": // PATTERN_NEG_Y
     125                replacement = Integer.toString((int) Math.pow(2, zoom)-1-tiley);
     126                break;
     127            case "switch:":
     128                replacement = randomParts[rand.nextInt(randomParts.length)];
     129                break;
     130            default:
     131                // handle switch/zoom here, as group will contain parameters and switch will not work
     132                if (PATTERN_ZOOM.matcher("{" + matcher.group(1) + "}").matches()) {
     133                    replacement = Integer.toString((inverse_zoom ? -1 * zoom : zoom) + zoom_offset);
     134                } else if (PATTERN_SWITCH.matcher("{" + matcher.group(1) + "}").matches()) {
     135                    replacement = randomParts[rand.nextInt(randomParts.length)];
     136                } else {
     137                    replacement = '{' + matcher.group(1) + '}';
     138                }
    101139            }
     140            matcher.appendReplacement(url, replacement);
    102141        }
    103         String r = this.baseUrl
    104             .replaceAll(PATTERN_ZOOM, Integer.toString(finalZoom))
    105             .replaceAll(PATTERN_X, Integer.toString(tilex))
    106             .replaceAll(PATTERN_Y, Integer.toString(tiley))
    107             .replaceAll(PATTERN_Y_YAHOO, Integer.toString((int) Math.pow(2, zoom-1)-1-tiley))
    108             .replaceAll(PATTERN_NEG_Y, Integer.toString((int) Math.pow(2, zoom)-1-tiley));
    109         if (rand != null) {
    110             r = r.replaceAll(PATTERN_SWITCH, randomParts[rand.nextInt(randomParts.length)]);
    111         }
    112         return r;
     142        matcher.appendTail(url);
     143        return url.toString().replace(" ", "%20");
    113144    }
    114145
    115146    /**
    public class TemplatedTMSTileSource extends TMSTileSource implements TemplatedTi 
    121152        Matcher m = Pattern.compile("\\{[^}]*\\}").matcher(url);
    122153        while (m.find()) {
    123154            boolean isSupportedPattern = false;
    124             for (String pattern : ALL_PATTERNS) {
    125                 if (m.group().matches(pattern)) {
     155            for (Pattern pattern : ALL_PATTERNS) {
     156                if (pattern.matcher(m.group()).matches()) {
    126157                    isSupportedPattern = true;
    127158                    break;
    128159                }
  • src/org/openstreetmap/josm/data/imagery/AbstractWMSTileSource.java

    diff --git src/org/openstreetmap/josm/data/imagery/AbstractWMSTileSource.java src/org/openstreetmap/josm/data/imagery/AbstractWMSTileSource.java
    index 5f668e5f8..b57591494 100644
    public abstract class AbstractWMSTileSource extends TMSTileSource { 
    225225        double s = se.getY();
    226226        double e = se.getX();
    227227
    228         return (
    229                 switchLatLon ?
    230                         String.format("%s,%s,%s,%s",
    231                                 LATLON_FORMAT.format(s), LATLON_FORMAT.format(w), LATLON_FORMAT.format(n), LATLON_FORMAT.format(e))
    232                         :
    233                         String.format("%s,%s,%s,%s",
    234                                 LATLON_FORMAT.format(w), LATLON_FORMAT.format(s), LATLON_FORMAT.format(e), LATLON_FORMAT.format(n))
     228        return switchLatLon ?
     229                getBboxstr(s, w, n, e)
     230                : getBboxstr(w, s, e, n);
     231    }
    235232
    236                 );
     233    private final String getBboxstr(double x1, double x2, double x3, double x4) {
     234        return new StringBuilder(64)
     235                .append(LATLON_FORMAT.format(x1))
     236                .append(',')
     237                .append(LATLON_FORMAT.format(x2))
     238                .append(',')
     239                .append(LATLON_FORMAT.format(x3))
     240                .append(',')
     241                .append(LATLON_FORMAT.format(x4))
     242                .toString();
    237243    }
    238244}
  • src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java

    diff --git src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
    index cfe64156f..80e82164b 100644
    public class TemplatedWMSTileSource extends AbstractWMSTileSource implements Tem 
    5050        PATTERN_HEADER, PATTERN_PROJ, PATTERN_WKID, PATTERN_BBOX, PATTERN_W, PATTERN_S, PATTERN_E, PATTERN_N, PATTERN_WIDTH, PATTERN_HEIGHT
    5151    };
    5252
     53    private final boolean switchLatLon;
    5354    /**
    5455     * Creates a tile source based on imagery info
    5556     * @param info imagery info
    public class TemplatedWMSTileSource extends AbstractWMSTileSource implements Tem 
    6162        this.headers.putAll(info.getCustomHttpHeaders());
    6263        handleTemplate();
    6364        initProjection();
     65        // Bounding box coordinates have to be switched for WMS 1.3.0 EPSG:4326.
     66        //
     67        // Background:
     68        //
     69        // bbox=x_min,y_min,x_max,y_max
     70        //
     71        //      SRS=... is WMS 1.1.1
     72        //      CRS=... is WMS 1.3.0
     73        //
     74        // The difference:
     75        //      For SRS x is east-west and y is north-south
     76        //      For CRS x and y are as specified by the EPSG
     77        //          E.g. [1] lists lat as first coordinate axis and lot as second, so it is switched for EPSG:4326.
     78        //          For most other EPSG code there seems to be no difference.
     79        // CHECKSTYLE.OFF: LineLength
     80        // [1] https://www.epsg-registry.org/report.htm?type=selection&entity=urn:ogc:def:crs:EPSG::4326&reportDetail=short&style=urn:uuid:report-style:default-with-code&style_name=OGP%20Default%20With%20Code&title=EPSG:4326
     81        // CHECKSTYLE.ON: LineLength
     82        if (baseUrl.toLowerCase(Locale.US).contains("crs=epsg:4326")) {
     83            switchLatLon = true;
     84        } else if (baseUrl.toLowerCase(Locale.US).contains("crs=")) {
     85            // assume WMS 1.3.0
     86            switchLatLon = ProjectionRegistry.getProjection().switchXY();
     87        } else {
     88            switchLatLon = false;
     89        }
    6490    }
    6591
    6692    @Override
    public class TemplatedWMSTileSource extends AbstractWMSTileSource implements Tem 
    85111            myProjCode = "CRS:84";
    86112        }
    87113
    88         // Bounding box coordinates have to be switched for WMS 1.3.0 EPSG:4326.
    89         //
    90         // Background:
    91         //
    92         // bbox=x_min,y_min,x_max,y_max
    93         //
    94         //      SRS=... is WMS 1.1.1
    95         //      CRS=... is WMS 1.3.0
    96         //
    97         // The difference:
    98         //      For SRS x is east-west and y is north-south
    99         //      For CRS x and y are as specified by the EPSG
    100         //          E.g. [1] lists lat as first coordinate axis and lot as second, so it is switched for EPSG:4326.
    101         //          For most other EPSG code there seems to be no difference.
    102         // CHECKSTYLE.OFF: LineLength
    103         // [1] https://www.epsg-registry.org/report.htm?type=selection&entity=urn:ogc:def:crs:EPSG::4326&reportDetail=short&style=urn:uuid:report-style:default-with-code&style_name=OGP%20Default%20With%20Code&title=EPSG:4326
    104         // CHECKSTYLE.ON: LineLength
    105         boolean switchLatLon = false;
    106         if (baseUrl.toLowerCase(Locale.US).contains("crs=epsg:4326")) {
    107             switchLatLon = true;
    108         } else if (baseUrl.toLowerCase(Locale.US).contains("crs=")) {
    109             // assume WMS 1.3.0
    110             switchLatLon = ProjectionRegistry.getProjection().switchXY();
    111         }
    112         String bbox = getBbox(zoom, tilex, tiley, switchLatLon);
    113 
    114114        // Using StringBuffer and generic PATTERN_PARAM matcher gives 2x performance improvement over replaceAll
    115115        StringBuffer url = new StringBuffer(baseUrl.length());
    116116        Matcher matcher = PATTERN_PARAM.matcher(baseUrl);
    public class TemplatedWMSTileSource extends AbstractWMSTileSource implements Tem 
    124124                replacement = myProjCode.startsWith("EPSG:") ? myProjCode.substring(5) : myProjCode;
    125125                break;
    126126            case "bbox":
    127                 replacement = bbox;
     127                replacement = getBbox(zoom, tilex, tiley, switchLatLon);
    128128                break;
    129129            case "w":
    130130                replacement = LATLON_FORMAT.format(w);
  • src/org/openstreetmap/josm/data/imagery/WMSEndpointTileSource.java

    diff --git src/org/openstreetmap/josm/data/imagery/WMSEndpointTileSource.java src/org/openstreetmap/josm/data/imagery/WMSEndpointTileSource.java
    index bf635b64f..008fc553c 100644
    public class WMSEndpointTileSource extends AbstractWMSTileSource implements Temp 
    5959
    6060    @Override
    6161    public String getTileUrl(int zoom, int tilex, int tiley) {
    62         String bbox = getBbox(zoom, tilex, tiley, !wmsi.belowWMS130() && getTileProjection().switchXY());
    63 
    6462        // Using StringBuffer and generic PATTERN_PARAM matcher gives 2x performance improvement over replaceAll
    6563        StringBuffer url = new StringBuffer(urlPattern.length());
    6664        Matcher matcher = PATTERN_PARAM.matcher(urlPattern);
    public class WMSEndpointTileSource extends AbstractWMSTileSource implements Temp 
    7169                replacement = getServerCRS();
    7270                break;
    7371            case "bbox":
    74                 replacement = bbox;
     72                replacement = getBbox(zoom, tilex, tiley, !wmsi.belowWMS130() && getTileProjection().switchXY());
    7573                break;
    7674            case "width":
    7775            case "height":
  • new file test/performance/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourcePerformanceTest.java

    diff --git test/performance/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourcePerformanceTest.java test/performance/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourcePerformanceTest.java
    new file mode 100644
    index 000000000..f7423e545
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.gui.jmapviewer.tilesources;
     3
     4import org.junit.BeforeClass;
     5import org.junit.Test;
     6import org.openstreetmap.josm.JOSMFixture;
     7import org.openstreetmap.josm.PerformanceTestUtils;
     8import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer;
     9import org.openstreetmap.josm.data.imagery.ImageryInfo;
     10
     11public class TemplatedTMSTileSourcePerformanceTest {
     12
     13    private static final int TEST_RUNS = 1;
     14    private final static int TIMES = 10_000;
     15
     16    /**
     17     * Prepare the test.
     18     */
     19    @BeforeClass
     20    public static void createJOSMFixture() {
     21        JOSMFixture.createPerformanceTestFixture().init(true);
     22    }
     23
     24    @Test
     25    public void testGetTileUrl() {
     26        ImageryInfo testImageryTMS = new ImageryInfo("test imagery",
     27                "https://maps{switch:1,2,3,4}.wien.gv.at/basemap/geolandbasemap/normal/google3857/{zoom}/{y}/{x}.png",
     28                "tms", null, null);
     29        TemplatedTMSTileSource tmsTs = new TemplatedTMSTileSource(testImageryTMS);
     30
     31        for (int testRun = 0; testRun < TEST_RUNS; testRun++) {
     32            PerformanceTestTimer tmsTimer = PerformanceTestUtils.startTimer("TemplatedTMSTileSource#getUrl(String)");
     33            for (int i = 0; i < TIMES ; i++) {
     34                tmsTs.getTileUrl(i % 20, i, i);
     35            }
     36            tmsTimer.done();
     37        }
     38        System.exit(0);
     39
     40    }
     41
     42    public static void main(String args[]) {
     43        createJOSMFixture();
     44        new TemplatedTMSTileSourcePerformanceTest().testGetTileUrl();
     45    }
     46}
  • new file test/performance/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourcePerformanceTest.java

    diff --git test/performance/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourcePerformanceTest.java test/performance/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourcePerformanceTest.java
    new file mode 100644
    index 000000000..5f663f262
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.imagery;
     3
     4import org.junit.BeforeClass;
     5import org.junit.Test;
     6import org.openstreetmap.josm.JOSMFixture;
     7import org.openstreetmap.josm.PerformanceTestUtils;
     8import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer;
     9import org.openstreetmap.josm.data.projection.Projections;
     10
     11public class TemplatedWMSTileSourcePerformanceTest {
     12
     13    private static final int TEST_RUNS = 1;
     14    private final static int TIMES = 10_000;
     15
     16    /**
     17     * Prepare the test.
     18     */
     19    @BeforeClass
     20    public static void createJOSMFixture() {
     21        JOSMFixture.createPerformanceTestFixture().init(true);
     22    }
     23
     24    @Test
     25    public void testGetTileUrl() {
     26        ImageryInfo testImageryWMS = new ImageryInfo("test imagery",
     27                "https://services.slip.wa.gov.au/public/services/SLIP_Public_Services/Transport/MapServer/WMSServer?LAYERS=8&"
     28                + "TRANSPARENT=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&FORMAT=image%2Fpng&SRS={proj}&BBOX={bbox}&"
     29                + "WIDTH={width}&HEIGHT={height}",
     30                "wms",
     31                null,
     32                null);
     33        TemplatedWMSTileSource wmsTs = new TemplatedWMSTileSource(testImageryWMS, Projections.getProjectionByCode("EPSG:3857"));
     34
     35        for (int testRun = 0; testRun < TEST_RUNS; testRun++) {
     36            PerformanceTestTimer wmsTimer = PerformanceTestUtils.startTimer("TemplatedWMSTileSource#getUrl(String)");
     37            for (int i = 0; i < TIMES ; i++) {
     38                wmsTs.getTileUrl(i % 20, i, i);
     39            }
     40            wmsTimer.done();
     41        }
     42
     43        System.exit(0);
     44    }
     45
     46    public static void main(String args[]) {
     47        createJOSMFixture();
     48        new TemplatedWMSTileSourcePerformanceTest().testGetTileUrl();
     49    }
     50
     51
     52}
  • new file test/unit/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourceTest.java

    diff --git test/unit/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourceTest.java test/unit/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSourceTest.java
    new file mode 100644
    index 000000000..4f15b3a60
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.gui.jmapviewer.tilesources;
     3
     4import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertTrue;
     6
     7import java.text.MessageFormat;
     8import java.util.Arrays;
     9import java.util.Collection;
     10import java.util.function.Predicate;
     11import java.util.stream.Stream;
     12
     13import org.apache.commons.lang3.tuple.Triple;
     14import org.junit.Test;
     15import org.openstreetmap.josm.data.imagery.ImageryInfo;
     16
     17
     18/**
     19 *
     20 * Tests for TemplaedTMSTileSource
     21 */
     22public class TemplatedTMSTileSourceTest {
     23
     24    private final static Collection<String> TMS_IMAGERIES = Arrays.asList(new String[]{
     25            "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z={zoom}&x={x}&y={-y}",
     26            /*
     27             *  generate for example with:
     28             *  $ curl https://josm.openstreetmap.de/maps | \
     29             *    xmlstarlet sel -N 'josm=http://josm.openstreetmap.de/maps-1.0' -t -v "//josm:entry[josm:type='tms']/josm:url" -n  | \
     30             *    sed -e 's/\&amp;/\&/g' -e 's/^/"/' -e 's/$/",/'
     31             */
     32    });
     33
     34    /**
     35     * triple of:
     36     *  * baseUrl
     37     *  * expected tile url for zoom=1, x=2, y=3
     38     *  * expected tile url for zoom=3, x=2, y=1
     39     */
     40    @SuppressWarnings("unchecked")
     41    private Collection<Triple<String, String, String>> TEST_DATA = Arrays.asList(new Triple[] {
     42            Triple.of("http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z={zoom}&x={x}&y={-y}", "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z=1&x=2&y=-2", "http://imagico.de/map/osmim_tiles.php?layer=S2A_R136_N41_20150831T093006&z=3&x=2&y=6"),
     43            /*
     44             * generate with main method below once TMS_IMAGERIES is filled in
     45             */
     46    });
     47
     48    /**
     49     * Check standard template
     50     */
     51    @Test
     52    public void testGetTileUrl() {
     53        checkGetTileUrl(
     54                "http://localhost/{z}/{x}/{y}",
     55                "http://localhost/1/2/3",
     56                "http://localhost/3/1/2"
     57                );
     58    }
     59
     60
     61    /**
     62     * Check template with positive zoom index
     63     */
     64    @Test
     65    public void testGetTileUrl_positive_zoom() {
     66        checkGetTileUrl(
     67                "http://localhost/{zoom+5}/{x}/{y}",
     68                "http://localhost/6/2/3",
     69                "http://localhost/8/1/2"
     70                );
     71    }
     72
     73    /**
     74     * Check template with negative zoom index
     75     */
     76    @Test
     77    public void testGetTileUrl_negative_zoom() {
     78        checkGetTileUrl(
     79                "http://localhost/{zoom-5}/{x}/{y}",
     80                "http://localhost/-4/2/3",
     81                "http://localhost/-2/1/2"
     82                );
     83    }
     84
     85    /**
     86     * Check template with inverse zoom index
     87     */
     88    @Test
     89    public void testGetTileUrl_inverse_negative_zoom() {
     90        checkGetTileUrl(
     91                "http://localhost/{5-zoom}/{x}/{y}",
     92                "http://localhost/4/2/3",
     93                "http://localhost/2/1/2"
     94                );
     95    }
     96
     97    /**
     98     * Check template with inverse zoom index and negative zoom index
     99     */
     100    @Test
     101    public void testGetTileUrl_both_offsets() {
     102        checkGetTileUrl(
     103                "http://localhost/{10-zoom-5}/{x}/{y}",
     104                "http://localhost/4/2/3",
     105                "http://localhost/2/1/2"
     106                );
     107    }
     108
     109    /**
     110     * Test template with switch
     111     */
     112    @Test
     113    public void testGetTileUrl_switch() {
     114        ImageryInfo testImageryTMS = new ImageryInfo("test imagery", "http://{switch:a,b,c}.localhost/{10-zoom-5}/{x}/{y}", "tms", null, null);
     115        TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
     116        assertTrue(
     117                Stream.of(
     118                        "http://a.localhost/4/2/3",
     119                        "http://b.localhost/4/2/3",
     120                        "http://c.localhost/4/2/3"
     121                        )
     122                .anyMatch(Predicate.isEqual(ts.getTileUrl(1, 2, 3)))
     123                );
     124
     125        assertTrue(
     126                Stream.of(
     127                        "http://a.localhost/3/3/4",
     128                        "http://b.localhost/3/3/4",
     129                        "http://c.localhost/3/3/4"
     130                        )
     131                .anyMatch(Predicate.isEqual(ts.getTileUrl(2, 3, 4)))
     132                );
     133        assertTrue(
     134                Stream.of(
     135                        "http://a.localhost/2/4/5",
     136                        "http://b.localhost/2/4/5",
     137                        "http://c.localhost/2/4/5"
     138                        )
     139                .anyMatch(Predicate.isEqual(ts.getTileUrl(3, 4, 5)))
     140                );
     141        assertTrue(
     142                Stream.of(
     143                        "http://a.localhost/1/5/6",
     144                        "http://b.localhost/1/5/6",
     145                        "http://c.localhost/1/5/6"
     146                        )
     147                .anyMatch(Predicate.isEqual(ts.getTileUrl(4, 5, 6)))
     148                );
     149    }
     150
     151    @Test
     152    public void testGetTileUrl_yahoo() {
     153        checkGetTileUrl(
     154                "http://localhost/{z}/{x}/{!y}",
     155                "http://localhost/1/2/-3",
     156                "http://localhost/3/1/1"
     157                );
     158
     159    }
     160
     161    @Test
     162    public void testGetTileUrl_negative_y() {
     163        checkGetTileUrl(
     164                "http://localhost/{z}/{x}/{-y}",
     165                "http://localhost/1/2/-2",
     166                "http://localhost/3/1/5"
     167                );
     168
     169    }
     170
     171    private void checkGetTileUrl(String url, String expected123, String expected312) {
     172        ImageryInfo testImageryTMS = new ImageryInfo("test imagery", url, "tms", null, null);
     173        TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
     174        assertEquals(expected123, ts.getTileUrl(1, 2, 3));
     175        assertEquals(expected312, ts.getTileUrl(3, 1, 2));
     176    }
     177    /**
     178     * Tests all entries in TEST_DATA. This test will fail if {switch:...} template is used
     179     */
     180    @Test
     181    public void testAllUrls() {
     182        for(Triple<String, String, String> test: TEST_DATA) {
     183            ImageryInfo testImageryTMS = new ImageryInfo("test imagery", test.getLeft(), "tms", null, null);
     184            TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
     185            assertEquals(test.getMiddle(), ts.getTileUrl(1, 2, 3));
     186            assertEquals(test.getRight(), ts.getTileUrl(3, 2, 1));
     187        }
     188    }
     189
     190    public static void main(String[] args) {
     191        for(String url: TMS_IMAGERIES) {
     192            ImageryInfo testImageryTMS = new ImageryInfo("test imagery", url, "tms", null, null);
     193            TemplatedTMSTileSource ts = new TemplatedTMSTileSource(testImageryTMS);
     194            System.out.println(MessageFormat.format("Triple.of(\"{0}\", \"{1}\", \"{2}\"),", url, ts.getTileUrl(1, 2, 3), ts.getTileUrl(3, 2, 1)));
     195        }
     196    }
     197
     198}
  • test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java

    diff --git test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java test/unit/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSourceTest.java
    index 89b29d045..02910cf97 100644
    public class TemplatedWMSTileSourceTest { 
    151151        verifyLocation(source, new LatLon(60, 18.1));
    152152    }
    153153
     154    /**
     155     * Test getTileUrl
     156     */
     157    @Test
     158    public void testGetTileUrl() {
     159        // "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Imagery_Dates/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=0&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE",
     160        Projection projection = Projections.getProjectionByCode("EPSG:4326");
     161        ProjectionRegistry.setProjection(projection);
     162        ImageryInfo testImageryWMS = new ImageryInfo("test imagery",
     163                "https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Imagery_Dates/MapServer/WMSServer?SERVICE=WMS&VERSION=1.3.0&"
     164                + "REQUEST=GetMap&CRS={proj}&BBOX={bbox}&WIDTH={width}&HEIGHT={height}&LAYERS=0&STYLES=&FORMAT=image/png32&DPI=96&"
     165                + "MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE",
     166                "wms",
     167                null,
     168                null);
     169        TemplatedWMSTileSource ts = new TemplatedWMSTileSource(testImageryWMS, projection);
     170        assertEquals("https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Imagery_Dates/MapServer/WMSServer?SERVICE=WMS&"
     171                + "VERSION=1.3.0&REQUEST=GetMap&CRS=EPSG:4326&BBOX=-1349.9999381,539.9999691,-989.9999536,899.9999536&WIDTH=512&"
     172                + "HEIGHT=512&LAYERS=0&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE",
     173                ts.getTileUrl(1, 2, 3));
     174        assertEquals("https://maps.six.nsw.gov.au/arcgis/services/public/NSW_Imagery_Dates/MapServer/WMSServer?SERVICE=WMS&"
     175                + "VERSION=1.3.0&REQUEST=GetMap&CRS=EPSG:4326&BBOX=-89.9999923,-0.0000077,0.0000039,89.9999884&WIDTH=512&HEIGHT=512&"
     176                + "LAYERS=0&STYLES=&FORMAT=image/png32&DPI=96&MAP_RESOLUTION=96&FORMAT_OPTIONS=dpi:96&TRANSPARENT=TRUE",
     177                ts.getTileUrl(3, 2, 1));
     178        testImageryWMS = new ImageryInfo("test imagery",
     179                "https://services.slip.wa.gov.au/public/services/SLIP_Public_Services/Transport/MapServer/WMSServer?LAYERS=8&"
     180                + "TRANSPARENT=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&FORMAT=image%2Fpng&SRS={proj}&BBOX={bbox}&"
     181                + "WIDTH={width}&HEIGHT={height}",
     182                "wms",
     183                null,
     184                null);
     185        ts = new TemplatedWMSTileSource(testImageryWMS, projection);
     186        assertEquals("https://services.slip.wa.gov.au/public/services/SLIP_Public_Services/Transport/MapServer/WMSServer?LAYERS=8&"
     187                + "TRANSPARENT=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&FORMAT=image%2Fpng&SRS=EPSG:4326&"
     188                + "BBOX=539.9999691,-1349.9999381,899.9999536,-989.9999536&WIDTH=512&HEIGHT=512",
     189                ts.getTileUrl(1, 2, 3));
     190        assertEquals("https://services.slip.wa.gov.au/public/services/SLIP_Public_Services/Transport/MapServer/WMSServer?LAYERS=8&"
     191                + "TRANSPARENT=TRUE&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&STYLES=&FORMAT=image%2Fpng&SRS=EPSG:4326&"
     192                + "BBOX=-0.0000077,-89.9999923,89.9999884,0.0000039&WIDTH=512&HEIGHT=512", ts.getTileUrl(3, 2, 1));
     193    }
     194
    154195    private void verifyMercatorTile(TemplatedWMSTileSource source, int x, int y, int z) {
    155196        TemplatedTMSTileSource verifier = new TemplatedTMSTileSource(testImageryTMS);
    156197        LatLon result = getTileLatLon(source, x, y, z);