source: josm/trunk/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java@ 12466

Last change on this file since 12466 was 11860, checked in by Don-vip, 7 years ago

see #7427 - checkstyle

  • Property svn:eol-style set to native
File size: 7.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.text.DecimalFormat;
7import java.text.DecimalFormatSymbols;
8import java.text.NumberFormat;
9import java.util.Locale;
10import java.util.Map;
11import java.util.Set;
12import java.util.TreeSet;
13import java.util.concurrent.ConcurrentHashMap;
14import java.util.regex.Matcher;
15import java.util.regex.Pattern;
16
17import org.openstreetmap.gui.jmapviewer.interfaces.TemplatedTileSource;
18import org.openstreetmap.josm.Main;
19import org.openstreetmap.josm.data.coor.EastNorth;
20import org.openstreetmap.josm.data.projection.Projection;
21import org.openstreetmap.josm.gui.layer.WMSLayer;
22import org.openstreetmap.josm.tools.CheckParameterUtil;
23
24/**
25 * Tile Source handling WMS providers
26 *
27 * @author Wiktor Niesiobędzki
28 * @since 8526
29 */
30public class TemplatedWMSTileSource extends AbstractWMSTileSource implements TemplatedTileSource {
31 private final Map<String, String> headers = new ConcurrentHashMap<>();
32 private final Set<String> serverProjections;
33 // CHECKSTYLE.OFF: SingleSpaceSeparator
34 private static final Pattern PATTERN_HEADER = Pattern.compile("\\{header\\(([^,]+),([^}]+)\\)\\}");
35 private static final Pattern PATTERN_PROJ = Pattern.compile("\\{proj\\}");
36 private static final Pattern PATTERN_WKID = Pattern.compile("\\{wkid\\}");
37 private static final Pattern PATTERN_BBOX = Pattern.compile("\\{bbox\\}");
38 private static final Pattern PATTERN_W = Pattern.compile("\\{w\\}");
39 private static final Pattern PATTERN_S = Pattern.compile("\\{s\\}");
40 private static final Pattern PATTERN_E = Pattern.compile("\\{e\\}");
41 private static final Pattern PATTERN_N = Pattern.compile("\\{n\\}");
42 private static final Pattern PATTERN_WIDTH = Pattern.compile("\\{width\\}");
43 private static final Pattern PATTERN_HEIGHT = Pattern.compile("\\{height\\}");
44 private static final Pattern PATTERN_PARAM = Pattern.compile("\\{([^}]+)\\}");
45 // CHECKSTYLE.ON: SingleSpaceSeparator
46
47 private static final NumberFormat latLonFormat = new DecimalFormat("###0.0000000", new DecimalFormatSymbols(Locale.US));
48
49 private static final Pattern[] ALL_PATTERNS = {
50 PATTERN_HEADER, PATTERN_PROJ, PATTERN_WKID, PATTERN_BBOX, PATTERN_W, PATTERN_S, PATTERN_E, PATTERN_N, PATTERN_WIDTH, PATTERN_HEIGHT
51 };
52
53 /**
54 * Creates a tile source based on imagery info
55 * @param info imagery info
56 * @param tileProjection the tile projection
57 */
58 public TemplatedWMSTileSource(ImageryInfo info, Projection tileProjection) {
59 super(info, tileProjection);
60 this.serverProjections = new TreeSet<>(info.getServerProjections());
61 handleTemplate();
62 initProjection();
63 }
64
65 @Override
66 public int getDefaultTileSize() {
67 return WMSLayer.PROP_IMAGE_SIZE.get();
68 }
69
70 @Override
71 public String getTileUrl(int zoom, int tilex, int tiley) {
72 String myProjCode = getServerCRS();
73
74 EastNorth nw = getTileEastNorth(tilex, tiley, zoom);
75 EastNorth se = getTileEastNorth(tilex + 1, tiley + 1, zoom);
76
77 double w = nw.getX();
78 double n = nw.getY();
79
80 double s = se.getY();
81 double e = se.getX();
82
83 if ("EPSG:4326".equals(myProjCode) && !serverProjections.contains(myProjCode) && serverProjections.contains("CRS:84")) {
84 myProjCode = "CRS:84";
85 }
86
87 // Bounding box coordinates have to be switched for WMS 1.3.0 EPSG:4326.
88 //
89 // Background:
90 //
91 // bbox=x_min,y_min,x_max,y_max
92 //
93 // SRS=... is WMS 1.1.1
94 // CRS=... is WMS 1.3.0
95 //
96 // The difference:
97 // For SRS x is east-west and y is north-south
98 // For CRS x and y are as specified by the EPSG
99 // E.g. [1] lists lat as first coordinate axis and lot as second, so it is switched for EPSG:4326.
100 // For most other EPSG code there seems to be no difference.
101 // CHECKSTYLE.OFF: LineLength
102 // [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
103 // CHECKSTYLE.ON: LineLength
104 boolean switchLatLon = false;
105 if (baseUrl.toLowerCase(Locale.US).contains("crs=epsg:4326")) {
106 switchLatLon = true;
107 } else if (baseUrl.toLowerCase(Locale.US).contains("crs=")) {
108 // assume WMS 1.3.0
109 switchLatLon = Main.getProjection().switchXY();
110 }
111 String bbox;
112 if (switchLatLon) {
113 bbox = String.format("%s,%s,%s,%s", latLonFormat.format(s), latLonFormat.format(w), latLonFormat.format(n), latLonFormat.format(e));
114 } else {
115 bbox = String.format("%s,%s,%s,%s", latLonFormat.format(w), latLonFormat.format(s), latLonFormat.format(e), latLonFormat.format(n));
116 }
117
118 // Using StringBuffer and generic PATTERN_PARAM matcher gives 2x performance improvement over replaceAll
119 StringBuffer url = new StringBuffer(baseUrl.length());
120 Matcher matcher = PATTERN_PARAM.matcher(baseUrl);
121 while (matcher.find()) {
122 String replacement;
123 switch (matcher.group(1)) {
124 case "proj":
125 replacement = myProjCode;
126 break;
127 case "wkid":
128 replacement = myProjCode.startsWith("EPSG:") ? myProjCode.substring(5) : myProjCode;
129 break;
130 case "bbox":
131 replacement = bbox;
132 break;
133 case "w":
134 replacement = latLonFormat.format(w);
135 break;
136 case "s":
137 replacement = latLonFormat.format(s);
138 break;
139 case "e":
140 replacement = latLonFormat.format(e);
141 break;
142 case "n":
143 replacement = latLonFormat.format(n);
144 break;
145 case "width":
146 case "height":
147 replacement = String.valueOf(getTileSize());
148 break;
149 default:
150 replacement = '{' + matcher.group(1) + '}';
151 }
152 matcher.appendReplacement(url, replacement);
153 }
154 matcher.appendTail(url);
155 return url.toString().replace(" ", "%20");
156 }
157
158 @Override
159 public String getTileId(int zoom, int tilex, int tiley) {
160 return getTileUrl(zoom, tilex, tiley);
161 }
162
163 @Override
164 public Map<String, String> getHeaders() {
165 return headers;
166 }
167
168 /**
169 * Checks if url is acceptable by this Tile Source
170 * @param url URL to check
171 */
172 public static void checkUrl(String url) {
173 CheckParameterUtil.ensureParameterNotNull(url, "url");
174 Matcher m = PATTERN_PARAM.matcher(url);
175 while (m.find()) {
176 boolean isSupportedPattern = false;
177 for (Pattern pattern : ALL_PATTERNS) {
178 if (pattern.matcher(m.group()).matches()) {
179 isSupportedPattern = true;
180 break;
181 }
182 }
183 if (!isSupportedPattern) {
184 throw new IllegalArgumentException(
185 tr("{0} is not a valid WMS argument. Please check this server URL:\n{1}", m.group(), url));
186 }
187 }
188 }
189
190 private void handleTemplate() {
191 // Capturing group pattern on switch values
192 StringBuffer output = new StringBuffer();
193 Matcher matcher = PATTERN_HEADER.matcher(this.baseUrl);
194 while (matcher.find()) {
195 headers.put(matcher.group(1), matcher.group(2));
196 matcher.appendReplacement(output, "");
197 }
198 matcher.appendTail(output);
199 this.baseUrl = output.toString();
200 }
201}
Note: See TracBrowser for help on using the repository browser.