diff --git a/src/org/openstreetmap/josm/io/imagery/WMSImagery.java b/src/org/openstreetmap/josm/io/imagery/WMSImagery.java
index d4c500e..baf6f5b 100644
a
|
b
|
import java.util.ArrayList;
|
10 | 10 | import java.util.Collection; |
11 | 11 | import java.util.Collections; |
12 | 12 | import java.util.HashSet; |
| 13 | import java.util.Iterator; |
13 | 14 | import java.util.List; |
14 | 15 | import java.util.Locale; |
| 16 | import java.util.NoSuchElementException; |
15 | 17 | import java.util.Set; |
16 | 18 | import java.util.regex.Pattern; |
| 19 | import java.util.stream.Collectors; |
| 20 | import java.util.stream.Stream; |
| 21 | import java.util.stream.StreamSupport; |
17 | 22 | |
18 | 23 | import javax.imageio.ImageIO; |
19 | 24 | import javax.xml.parsers.DocumentBuilder; |
… |
… |
import org.openstreetmap.josm.data.Bounds;
|
24 | 29 | import org.openstreetmap.josm.data.imagery.ImageryInfo; |
25 | 30 | import org.openstreetmap.josm.data.projection.Projections; |
26 | 31 | import org.openstreetmap.josm.tools.HttpClient; |
27 | | import org.openstreetmap.josm.tools.Predicate; |
28 | 32 | import org.openstreetmap.josm.tools.Utils; |
29 | 33 | import org.w3c.dom.Document; |
30 | 34 | import org.w3c.dom.Element; |
… |
… |
import org.xml.sax.EntityResolver;
|
34 | 38 | import org.xml.sax.InputSource; |
35 | 39 | import org.xml.sax.SAXException; |
36 | 40 | |
| 41 | /** |
| 42 | * This class represents the capabilites of a WMS imagery server. |
| 43 | */ |
37 | 44 | public class WMSImagery { |
38 | 45 | |
| 46 | private static final class ChildIterator implements Iterator<Element> { |
| 47 | private Element child; |
| 48 | |
| 49 | ChildIterator(Element parent) { |
| 50 | child = advanceToElement(parent.getFirstChild()); |
| 51 | } |
| 52 | |
| 53 | private static Element advanceToElement(Node firstChild) { |
| 54 | Node node = firstChild; |
| 55 | while (node != null && !(node instanceof Element)) { |
| 56 | node = node.getNextSibling(); |
| 57 | } |
| 58 | return (Element) node; |
| 59 | } |
| 60 | |
| 61 | @Override |
| 62 | public boolean hasNext() { |
| 63 | return child != null; |
| 64 | } |
| 65 | |
| 66 | @Override |
| 67 | public Element next() { |
| 68 | if (!hasNext()) { |
| 69 | throw new NoSuchElementException("No next sibling."); |
| 70 | } |
| 71 | Element next = child; |
| 72 | child = advanceToElement(child.getNextSibling()); |
| 73 | return next; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * An exception that is thrown if there was an error while getting the capabilities of the WMS server. |
| 79 | */ |
39 | 80 | public static class WMSGetCapabilitiesException extends Exception { |
40 | 81 | private final String incomingData; |
41 | 82 | |
… |
… |
public class WMSImagery {
|
61 | 102 | } |
62 | 103 | |
63 | 104 | /** |
64 | | * Returns the answer from WMS server. |
65 | | * @return the answer from WMS server |
| 105 | * The data that caused this exception. |
| 106 | * @return The server response to the capabilites request. |
66 | 107 | */ |
67 | 108 | public String getIncomingData() { |
68 | 109 | return incomingData; |
… |
… |
public class WMSImagery {
|
78 | 119 | * @return the list of layers |
79 | 120 | */ |
80 | 121 | public List<LayerDetails> getLayers() { |
81 | | return layers; |
| 122 | return Collections.unmodifiableList(layers); |
82 | 123 | } |
83 | 124 | |
84 | 125 | /** |
… |
… |
public class WMSImagery {
|
97 | 138 | return Collections.unmodifiableList(formats); |
98 | 139 | } |
99 | 140 | |
| 141 | /** |
| 142 | * Gets the preffered format for this imagery layer. |
| 143 | * @return The preffered format as mime type. |
| 144 | */ |
100 | 145 | public String getPreferredFormats() { |
101 | | return formats.contains("image/jpeg") ? "image/jpeg" |
102 | | : formats.contains("image/png") ? "image/png" |
103 | | : formats.isEmpty() ? null |
104 | | : formats.get(0); |
| 146 | if (formats.contains("image/jpeg")) { |
| 147 | return "image/jpeg"; |
| 148 | } else if (formats.contains("image/png")) { |
| 149 | return "image/png"; |
| 150 | } else if (formats.isEmpty()) { |
| 151 | return null; |
| 152 | } else { |
| 153 | return formats.get(0); |
| 154 | } |
105 | 155 | } |
106 | 156 | |
107 | 157 | String buildRootUrl() { |
… |
… |
public class WMSImagery {
|
128 | 178 | } |
129 | 179 | |
130 | 180 | public String buildGetMapUrl(Collection<LayerDetails> selectedLayers, String format) { |
131 | | return buildRootUrl() |
132 | | + "FORMAT=" + format + (imageFormatHasTransparency(format) ? "&TRANSPARENT=TRUE" : "") |
| 181 | return buildRootUrl() + "FORMAT=" + format + (imageFormatHasTransparency(format) ? "&TRANSPARENT=TRUE" : "") |
133 | 182 | + "&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=" |
134 | | + Utils.join(",", Utils.transform(selectedLayers, new Utils.Function<LayerDetails, String>() { |
135 | | @Override |
136 | | public String apply(LayerDetails x) { |
137 | | return x.ident; |
138 | | } |
139 | | })) |
| 183 | + Utils.join(",", Utils.transform(selectedLayers, x -> x.ident)) |
140 | 184 | + "&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"; |
141 | 185 | } |
142 | 186 | |
143 | | public void attemptGetCapabilities(String serviceUrlStr) throws MalformedURLException, IOException, WMSGetCapabilitiesException { |
| 187 | public void attemptGetCapabilities(String serviceUrlStr) throws IOException, WMSGetCapabilitiesException { |
144 | 188 | URL getCapabilitiesUrl = null; |
145 | 189 | try { |
146 | 190 | if (!Pattern.compile(".*GetCapabilities.*", Pattern.CASE_INSENSITIVE).matcher(serviceUrlStr).matches()) { |
… |
… |
public class WMSImagery {
|
161 | 205 | } |
162 | 206 | serviceUrl = new URL(serviceUrlStr); |
163 | 207 | } catch (HeadlessException e) { |
| 208 | Main.warn(e); |
164 | 209 | return; |
165 | 210 | } |
166 | 211 | |
… |
… |
public class WMSImagery {
|
191 | 236 | child = getChild(child, "Request"); |
192 | 237 | child = getChild(child, "GetMap"); |
193 | 238 | |
194 | | formats = new ArrayList<>(Utils.filter(Utils.transform(getChildren(child, "Format"), |
195 | | new Utils.Function<Element, String>() { |
196 | | @Override |
197 | | public String apply(Element x) { |
198 | | return x.getTextContent(); |
199 | | } |
200 | | }), |
201 | | new Predicate<String>() { |
202 | | @Override |
203 | | public boolean evaluate(String format) { |
204 | | boolean isFormatSupported = isImageFormatSupported(format); |
205 | | if (!isFormatSupported) { |
206 | | Main.info("Skipping unsupported image format {0}", format); |
207 | | } |
208 | | return isFormatSupported; |
209 | | } |
210 | | } |
211 | | )); |
| 239 | formats = getChildrenStream(child, "Format") |
| 240 | .map(x -> x.getTextContent()) |
| 241 | .filter(WMSImagery::isImageFormatSupportedWarn) |
| 242 | .collect(Collectors.toList()); |
212 | 243 | |
213 | 244 | child = getChild(child, "DCPType"); |
214 | 245 | child = getChild(child, "HTTP"); |
… |
… |
public class WMSImagery {
|
230 | 261 | } |
231 | 262 | } |
232 | 263 | |
| 264 | private static boolean isImageFormatSupportedWarn(String format) { |
| 265 | boolean isFormatSupported = isImageFormatSupported(format); |
| 266 | if (!isFormatSupported) { |
| 267 | Main.info("Skipping unsupported image format {0}", format); |
| 268 | } |
| 269 | return isFormatSupported; |
| 270 | } |
| 271 | |
233 | 272 | static boolean isImageFormatSupported(final String format) { |
234 | 273 | return ImageIO.getImageReadersByMIMEType(format).hasNext() |
235 | 274 | // handles image/tiff image/tiff8 image/geotiff image/geotiff8 |
236 | | || (format.startsWith("image/tiff") || format.startsWith("image/geotiff")) && ImageIO.getImageReadersBySuffix("tiff").hasNext() |
| 275 | || (format.startsWith("image/tiff") || format.startsWith("image/geotiff")) |
| 276 | && ImageIO.getImageReadersBySuffix("tiff").hasNext() |
237 | 277 | || format.startsWith("image/png") && ImageIO.getImageReadersBySuffix("png").hasNext() |
238 | 278 | || format.startsWith("image/svg") && ImageIO.getImageReadersBySuffix("svg").hasNext() |
239 | 279 | || format.startsWith("image/bmp") && ImageIO.getImageReadersBySuffix("bmp").hasNext(); |
… |
… |
public class WMSImagery {
|
275 | 315 | |
276 | 316 | // Parse the CRS/SRS pulled out of this layer's XML element |
277 | 317 | // I think CRS and SRS are the same at this point |
278 | | List<Element> crsChildren = getChildren(element, "CRS"); |
279 | | crsChildren.addAll(getChildren(element, "SRS")); |
280 | | for (Element child : crsChildren) { |
281 | | String crs = (String) getContent(child); |
282 | | if (!crs.isEmpty()) { |
283 | | String upperCase = crs.trim().toUpperCase(Locale.ENGLISH); |
284 | | crsList.add(upperCase); |
285 | | } |
286 | | } |
| 318 | getChildrenStream(element) |
| 319 | .filter(child -> "CRS".equals(child.getNodeName()) || "SRS".equals(child.getNodeName())) |
| 320 | .map(child -> (String) getContent(child)) |
| 321 | .filter(crs -> !crs.isEmpty()) |
| 322 | .map(crs -> crs.trim().toUpperCase(Locale.ENGLISH)) |
| 323 | .forEach(crsList::add); |
287 | 324 | |
288 | 325 | // Check to see if any of the specified projections are supported by JOSM |
289 | 326 | boolean josmSupportsThisLayer = false; |
… |
… |
public class WMSImagery {
|
350 | 387 | return content.toString().trim(); |
351 | 388 | } |
352 | 389 | |
353 | | private static List<Element> getChildren(Element parent, String name) { |
354 | | List<Element> retVal = new ArrayList<>(); |
355 | | if (parent != null) { |
356 | | for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
357 | | if (child instanceof Element && name.equals(child.getNodeName())) { |
358 | | retVal.add((Element) child); |
359 | | } |
360 | | } |
| 390 | private static Stream<Element> getChildrenStream(Element parent) { |
| 391 | if (parent == null) { |
| 392 | // ignore missing elements |
| 393 | return Stream.empty(); |
| 394 | } else { |
| 395 | Iterable<Element> it = () -> new ChildIterator(parent); |
| 396 | return StreamSupport.stream(it.spliterator(), false); |
361 | 397 | } |
362 | | return retVal; |
| 398 | } |
| 399 | |
| 400 | private static Stream<Element> getChildrenStream(Element parent, String name) { |
| 401 | return getChildrenStream(parent).filter(child -> name.equals(child.getNodeName())); |
| 402 | } |
| 403 | |
| 404 | private static List<Element> getChildren(Element parent, String name) { |
| 405 | return getChildrenStream(parent, name).collect(Collectors.toList()); |
363 | 406 | } |
364 | 407 | |
365 | 408 | private static Element getChild(Element parent, String name) { |
366 | | if (parent == null) |
367 | | return null; |
368 | | for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
369 | | if (child instanceof Element && name.equals(child.getNodeName())) |
370 | | return (Element) child; |
371 | | } |
372 | | return null; |
| 409 | return getChildrenStream(parent, name).findFirst().orElse(null); |
373 | 410 | } |
374 | 411 | |
| 412 | /** |
| 413 | * The details of a layer of this wms server. |
| 414 | */ |
375 | 415 | public static class LayerDetails { |
376 | 416 | |
| 417 | /** |
| 418 | * The layer name |
| 419 | */ |
377 | 420 | public final String name; |
378 | 421 | public final String ident; |
| 422 | /** |
| 423 | * The child layers of this layer |
| 424 | */ |
379 | 425 | public final List<LayerDetails> children; |
| 426 | /** |
| 427 | * The bounds this layer can be used for |
| 428 | */ |
380 | 429 | public final Bounds bounds; |
381 | 430 | public final Set<String> crsList; |
382 | 431 | public final boolean supported; |
383 | 432 | |
384 | | public LayerDetails(String name, String ident, Set<String> crsList, |
385 | | boolean supportedLayer, Bounds bounds, |
386 | | List<LayerDetails> childLayers) { |
| 433 | public LayerDetails(String name, String ident, Set<String> crsList, boolean supportedLayer, Bounds bounds, |
| 434 | List<LayerDetails> childLayers) { |
387 | 435 | this.name = name; |
388 | 436 | this.ident = ident; |
389 | 437 | this.supported = supportedLayer; |