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

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

PMD - VariableNamingConventions

  • 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 LATLON_FORMAT = 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",
114 LATLON_FORMAT.format(s), LATLON_FORMAT.format(w), LATLON_FORMAT.format(n), LATLON_FORMAT.format(e));
115 } else {
116 bbox = String.format("%s,%s,%s,%s",
117 LATLON_FORMAT.format(w), LATLON_FORMAT.format(s), LATLON_FORMAT.format(e), LATLON_FORMAT.format(n));
118 }
119
120 // Using StringBuffer and generic PATTERN_PARAM matcher gives 2x performance improvement over replaceAll
121 StringBuffer url = new StringBuffer(baseUrl.length());
122 Matcher matcher = PATTERN_PARAM.matcher(baseUrl);
123 while (matcher.find()) {
124 String replacement;
125 switch (matcher.group(1)) {
126 case "proj":
127 replacement = myProjCode;
128 break;
129 case "wkid":
130 replacement = myProjCode.startsWith("EPSG:") ? myProjCode.substring(5) : myProjCode;
131 break;
132 case "bbox":
133 replacement = bbox;
134 break;
135 case "w":
136 replacement = LATLON_FORMAT.format(w);
137 break;
138 case "s":
139 replacement = LATLON_FORMAT.format(s);
140 break;
141 case "e":
142 replacement = LATLON_FORMAT.format(e);
143 break;
144 case "n":
145 replacement = LATLON_FORMAT.format(n);
146 break;
147 case "width":
148 case "height":
149 replacement = String.valueOf(getTileSize());
150 break;
151 default:
152 replacement = '{' + matcher.group(1) + '}';
153 }
154 matcher.appendReplacement(url, replacement);
155 }
156 matcher.appendTail(url);
157 return url.toString().replace(" ", "%20");
158 }
159
160 @Override
161 public String getTileId(int zoom, int tilex, int tiley) {
162 return getTileUrl(zoom, tilex, tiley);
163 }
164
165 @Override
166 public Map<String, String> getHeaders() {
167 return headers;
168 }
169
170 /**
171 * Checks if url is acceptable by this Tile Source
172 * @param url URL to check
173 */
174 public static void checkUrl(String url) {
175 CheckParameterUtil.ensureParameterNotNull(url, "url");
176 Matcher m = PATTERN_PARAM.matcher(url);
177 while (m.find()) {
178 boolean isSupportedPattern = false;
179 for (Pattern pattern : ALL_PATTERNS) {
180 if (pattern.matcher(m.group()).matches()) {
181 isSupportedPattern = true;
182 break;
183 }
184 }
185 if (!isSupportedPattern) {
186 throw new IllegalArgumentException(
187 tr("{0} is not a valid WMS argument. Please check this server URL:\n{1}", m.group(), url));
188 }
189 }
190 }
191
192 private void handleTemplate() {
193 // Capturing group pattern on switch values
194 StringBuffer output = new StringBuffer();
195 Matcher matcher = PATTERN_HEADER.matcher(this.baseUrl);
196 while (matcher.find()) {
197 headers.put(matcher.group(1), matcher.group(2));
198 matcher.appendReplacement(output, "");
199 }
200 matcher.appendTail(output);
201 this.baseUrl = output.toString();
202 }
203}
Note: See TracBrowser for help on using the repository browser.