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.ensure(info, "imageryType", x -> ImageryType.WMS_ENDPOINT.equals(x.getImageryType())); 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 String bbox = getBbox(zoom, tilex, tiley, !wmsi.belowWMS130() && getTileProjection().switchXY()); 063 064 // Using StringBuffer and generic PATTERN_PARAM matcher gives 2x performance improvement over replaceAll 065 StringBuffer url = new StringBuffer(urlPattern.length()); 066 Matcher matcher = PATTERN_PARAM.matcher(urlPattern); 067 while (matcher.find()) { 068 String replacement; 069 switch (matcher.group(1)) { 070 case "proj": 071 replacement = getServerCRS(); 072 break; 073 case "bbox": 074 replacement = bbox; 075 break; 076 case "width": 077 case "height": 078 replacement = String.valueOf(getTileSize()); 079 break; 080 default: 081 replacement = '{' + matcher.group(1) + '}'; 082 } 083 matcher.appendReplacement(url, replacement); 084 } 085 matcher.appendTail(url); 086 return url.toString(); 087 } 088 089 /** 090 * 091 * @return list of EPSG codes that current layer selection supports (this may differ from layer to layer) 092 */ 093 public List<String> getServerProjections() { 094 return wmsi.getLayers(layers).stream().flatMap(x -> x.getCrs().stream()).distinct().collect(Collectors.toList()); 095 } 096 097 @Override 098 public Map<String, String> getHeaders() { 099 return headers; 100 } 101}