001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.imagery; 003 004import java.io.IOException; 005import java.util.List; 006import java.util.Map; 007import java.util.concurrent.ConcurrentHashMap; 008import java.util.regex.Matcher; 009import java.util.regex.Pattern; 010import java.util.stream.Collectors; 011 012import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource; 013import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 014import org.openstreetmap.josm.data.projection.Projection; 015import org.openstreetmap.josm.gui.layer.WMSLayer; 016import org.openstreetmap.josm.io.imagery.WMSImagery; 017import org.openstreetmap.josm.io.imagery.WMSImagery.WMSGetCapabilitiesException; 018import org.openstreetmap.josm.tools.CheckParameterUtil; 019 020/** 021 * Class representing ImageryType.WMS_ENDPOINT tile source. 022 * It differs from standard WMS tile source that this tile source fetches GetCapabilities from server and 023 * uses most of the parameters from there 024 * 025 * @author Wiktor Niesiobedzki 026 * @since 13733 027 */ 028public class WMSEndpointTileSource extends AbstractWMSTileSource implements TemplatedTileSource { 029 030 private final WMSImagery wmsi; 031 private final List<DefaultLayer> layers; 032 private final String urlPattern; 033 private static final Pattern PATTERN_PARAM = Pattern.compile("\\{([^}]+)\\}"); 034 private final Map<String, String> headers = new ConcurrentHashMap<>(); 035 036 /** 037 * Create WMSEndpointTileSource tile source 038 * @param info WMS_ENDPOINT ImageryInfo 039 * @param tileProjection server projection that should be used by this tile source 040 */ 041 public WMSEndpointTileSource(ImageryInfo info, Projection tileProjection) { 042 super(info, tileProjection); 043 CheckParameterUtil.ensureThat(info.getImageryType() == ImageryType.WMS_ENDPOINT, "imageryType"); 044 try { 045 wmsi = new WMSImagery(info.getUrl(), info.getCustomHttpHeaders()); 046 } catch (IOException | WMSGetCapabilitiesException e) { 047 throw new IllegalArgumentException(e); 048 } 049 layers = info.getDefaultLayers(); 050 initProjection(); 051 urlPattern = wmsi.buildGetMapUrl(layers, info.isTransparent()); 052 this.headers.putAll(info.getCustomHttpHeaders()); 053 } 054 055 @Override 056 public int getDefaultTileSize() { 057 return WMSLayer.PROP_IMAGE_SIZE.get(); 058 } 059 060 @Override 061 public String getTileUrl(int zoom, int tilex, int tiley) { 062 // Using StringBuffer and generic PATTERN_PARAM matcher gives 2x performance improvement over replaceAll 063 StringBuffer url = new StringBuffer(urlPattern.length()); 064 Matcher matcher = PATTERN_PARAM.matcher(urlPattern); 065 while (matcher.find()) { 066 String replacement; 067 switch (matcher.group(1)) { 068 case "proj": 069 replacement = getServerCRS(); 070 break; 071 case "bbox": 072 replacement = getBbox(zoom, tilex, tiley, !wmsi.belowWMS130() && getTileProjection().switchXY()); 073 break; 074 case "width": 075 case "height": 076 replacement = String.valueOf(getTileSize()); 077 break; 078 default: 079 replacement = '{' + matcher.group(1) + '}'; 080 } 081 matcher.appendReplacement(url, replacement); 082 } 083 matcher.appendTail(url); 084 return url.toString(); 085 } 086 087 /** 088 * Returns list of EPSG codes that current layer selection supports. 089 * @return list of EPSG codes that current layer selection supports (this may differ from layer to layer) 090 */ 091 public List<String> getServerProjections() { 092 return wmsi.getLayers(layers).stream().flatMap(x -> x.getCrs().stream()).distinct().collect(Collectors.toList()); 093 } 094 095 @Override 096 public Map<String, String> getHeaders() { 097 return headers; 098 } 099}