diff --git a/src/org/openstreetmap/josm/io/imagery/WMSImagery.java b/src/org/openstreetmap/josm/io/imagery/WMSImagery.java
index 7ecd654..4e1962e 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;
|
25 | 30 | import org.openstreetmap.josm.data.imagery.ImageryInfo; |
26 | 31 | import org.openstreetmap.josm.data.projection.Projections; |
27 | 32 | import org.openstreetmap.josm.tools.HttpClient; |
28 | | import org.openstreetmap.josm.tools.Predicate; |
29 | 33 | import org.openstreetmap.josm.tools.Utils; |
30 | 34 | import org.w3c.dom.Document; |
31 | 35 | import org.w3c.dom.Element; |
… |
… |
import org.xml.sax.EntityResolver;
|
35 | 39 | import org.xml.sax.InputSource; |
36 | 40 | import org.xml.sax.SAXException; |
37 | 41 | |
| 42 | /** |
| 43 | * This class represents the capabilites of a WMS imagery server. |
| 44 | */ |
38 | 45 | public class WMSImagery { |
39 | 46 | |
| 47 | private static final class ChildIterator implements Iterator<Element> { |
| 48 | private Element child; |
| 49 | |
| 50 | ChildIterator(Element parent) { |
| 51 | child = advanceToElement(parent.getFirstChild()); |
| 52 | } |
| 53 | |
| 54 | private static Element advanceToElement(Node firstChild) { |
| 55 | Node node = firstChild; |
| 56 | while (node != null && !(node instanceof Element)) { |
| 57 | node = node.getNextSibling(); |
| 58 | } |
| 59 | return (Element) node; |
| 60 | } |
| 61 | |
| 62 | @Override |
| 63 | public boolean hasNext() { |
| 64 | return child != null; |
| 65 | } |
| 66 | |
| 67 | @Override |
| 68 | public Element next() { |
| 69 | if (!hasNext()) { |
| 70 | throw new NoSuchElementException("No next sibling."); |
| 71 | } |
| 72 | Element next = child; |
| 73 | child = advanceToElement(child.getNextSibling()); |
| 74 | return next; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * An exception that is thrown if there was an error while getting the capabilities of the WMS server. |
| 80 | */ |
40 | 81 | public static class WMSGetCapabilitiesException extends Exception { |
41 | 82 | private final String incomingData; |
42 | 83 | |
| 84 | /** |
| 85 | * Create a new {@link WMSGetCapabilitiesException} |
| 86 | * @param cause The cause. |
| 87 | * @param incomingData The server response to the capabilites request. |
| 88 | */ |
43 | 89 | public WMSGetCapabilitiesException(Throwable cause, String incomingData) { |
44 | 90 | super(cause); |
45 | 91 | this.incomingData = incomingData; |
46 | 92 | } |
47 | 93 | |
| 94 | /** |
| 95 | * The data that caused this exception. |
| 96 | * @return The server response to the capabilites request. |
| 97 | */ |
48 | 98 | public String getIncomingData() { |
49 | 99 | return incomingData; |
50 | 100 | } |
… |
… |
public class WMSImagery {
|
59 | 109 | * @return the list of layers |
60 | 110 | */ |
61 | 111 | public List<LayerDetails> getLayers() { |
62 | | return layers; |
| 112 | return Collections.unmodifiableList(layers); |
63 | 113 | } |
64 | 114 | |
65 | 115 | /** |
… |
… |
public class WMSImagery {
|
78 | 128 | return Collections.unmodifiableList(formats); |
79 | 129 | } |
80 | 130 | |
| 131 | /** |
| 132 | * Gets the preffered format for this imagery layer. |
| 133 | * @return The preffered format as mime type. |
| 134 | */ |
81 | 135 | public String getPreferredFormats() { |
82 | | return formats.contains("image/jpeg") ? "image/jpeg" |
83 | | : formats.contains("image/png") ? "image/png" |
84 | | : formats.isEmpty() ? null |
85 | | : formats.get(0); |
| 136 | if (formats.contains("image/jpeg")) { |
| 137 | return "image/jpeg"; |
| 138 | } else if (formats.contains("image/png")) { |
| 139 | return "image/png"; |
| 140 | } else if (formats.isEmpty()) { |
| 141 | return null; |
| 142 | } else { |
| 143 | return formats.get(0); |
| 144 | } |
86 | 145 | } |
87 | 146 | |
88 | 147 | String buildRootUrl() { |
… |
… |
public class WMSImagery {
|
109 | 168 | } |
110 | 169 | |
111 | 170 | public String buildGetMapUrl(Collection<LayerDetails> selectedLayers, String format) { |
112 | | return buildRootUrl() |
113 | | + "FORMAT=" + format + (imageFormatHasTransparency(format) ? "&TRANSPARENT=TRUE" : "") |
| 171 | return buildRootUrl() + "FORMAT=" + format + (imageFormatHasTransparency(format) ? "&TRANSPARENT=TRUE" : "") |
114 | 172 | + "&VERSION=1.1.1&SERVICE=WMS&REQUEST=GetMap&LAYERS=" |
115 | | + Utils.join(",", Utils.transform(selectedLayers, new Utils.Function<LayerDetails, String>() { |
116 | | @Override |
117 | | public String apply(LayerDetails x) { |
118 | | return x.ident; |
119 | | } |
120 | | })) |
| 173 | + Utils.join(",", Utils.transform(selectedLayers, x -> x.ident)) |
121 | 174 | + "&STYLES=&SRS={proj}&WIDTH={width}&HEIGHT={height}&BBOX={bbox}"; |
122 | 175 | } |
123 | 176 | |
124 | | public void attemptGetCapabilities(String serviceUrlStr) throws MalformedURLException, IOException, WMSGetCapabilitiesException { |
| 177 | public void attemptGetCapabilities(String serviceUrlStr) throws IOException, WMSGetCapabilitiesException { |
125 | 178 | URL getCapabilitiesUrl = null; |
126 | 179 | try { |
127 | 180 | if (!Pattern.compile(".*GetCapabilities.*", Pattern.CASE_INSENSITIVE).matcher(serviceUrlStr).matches()) { |
… |
… |
public class WMSImagery {
|
142 | 195 | } |
143 | 196 | serviceUrl = new URL(serviceUrlStr); |
144 | 197 | } catch (HeadlessException e) { |
| 198 | Main.warn(e); |
145 | 199 | return; |
146 | 200 | } |
147 | 201 | |
… |
… |
public class WMSImagery {
|
169 | 223 | child = getChild(child, "Request"); |
170 | 224 | child = getChild(child, "GetMap"); |
171 | 225 | |
172 | | formats = new ArrayList<>(Utils.filter(Utils.transform(getChildren(child, "Format"), |
173 | | new Utils.Function<Element, String>() { |
174 | | @Override |
175 | | public String apply(Element x) { |
176 | | return x.getTextContent(); |
177 | | } |
178 | | }), |
179 | | new Predicate<String>() { |
180 | | @Override |
181 | | public boolean evaluate(String format) { |
182 | | boolean isFormatSupported = isImageFormatSupported(format); |
183 | | if (!isFormatSupported) { |
184 | | Main.info("Skipping unsupported image format {0}", format); |
185 | | } |
186 | | return isFormatSupported; |
187 | | } |
188 | | } |
189 | | )); |
| 226 | formats = getChildrenStream(child, "Format") |
| 227 | .map(x -> x.getTextContent()) |
| 228 | .filter(WMSImagery::isImageFormatSupportedWarn) |
| 229 | .collect(Collectors.toList()); |
190 | 230 | |
191 | 231 | child = getChild(child, "DCPType"); |
192 | 232 | child = getChild(child, "HTTP"); |
… |
… |
public class WMSImagery {
|
208 | 248 | } |
209 | 249 | } |
210 | 250 | |
| 251 | private static boolean isImageFormatSupportedWarn(String format) { |
| 252 | boolean isFormatSupported = isImageFormatSupported(format); |
| 253 | if (!isFormatSupported) { |
| 254 | Main.info("Skipping unsupported image format {0}", format); |
| 255 | } |
| 256 | return isFormatSupported; |
| 257 | } |
| 258 | |
211 | 259 | static boolean isImageFormatSupported(final String format) { |
212 | 260 | return ImageIO.getImageReadersByMIMEType(format).hasNext() |
213 | 261 | // handles image/tiff image/tiff8 image/geotiff image/geotiff8 |
214 | | || (format.startsWith("image/tiff") || format.startsWith("image/geotiff")) && ImageIO.getImageReadersBySuffix("tiff").hasNext() |
| 262 | || (format.startsWith("image/tiff") || format.startsWith("image/geotiff")) |
| 263 | && ImageIO.getImageReadersBySuffix("tiff").hasNext() |
215 | 264 | || format.startsWith("image/png") && ImageIO.getImageReadersBySuffix("png").hasNext() |
216 | 265 | || format.startsWith("image/svg") && ImageIO.getImageReadersBySuffix("svg").hasNext() |
217 | 266 | || format.startsWith("image/bmp") && ImageIO.getImageReadersBySuffix("bmp").hasNext(); |
… |
… |
public class WMSImagery {
|
253 | 302 | |
254 | 303 | // Parse the CRS/SRS pulled out of this layer's XML element |
255 | 304 | // I think CRS and SRS are the same at this point |
256 | | List<Element> crsChildren = getChildren(element, "CRS"); |
257 | | crsChildren.addAll(getChildren(element, "SRS")); |
258 | | for (Element child : crsChildren) { |
259 | | String crs = (String) getContent(child); |
260 | | if (!crs.isEmpty()) { |
261 | | String upperCase = crs.trim().toUpperCase(Locale.ENGLISH); |
262 | | crsList.add(upperCase); |
263 | | } |
264 | | } |
| 305 | getChildrenStream(element) |
| 306 | .filter(child -> "CRS".equals(child.getNodeName()) || "SRS".equals(child.getNodeName())) |
| 307 | .map(child -> (String) getContent(child)) |
| 308 | .filter(crs -> !crs.isEmpty()) |
| 309 | .map(crs -> crs.trim().toUpperCase(Locale.ENGLISH)) |
| 310 | .forEach(crsList::add); |
265 | 311 | |
266 | 312 | // Check to see if any of the specified projections are supported by JOSM |
267 | 313 | boolean josmSupportsThisLayer = false; |
… |
… |
public class WMSImagery {
|
328 | 374 | return content.toString().trim(); |
329 | 375 | } |
330 | 376 | |
| 377 | private static Stream<Element> getChildrenStream(Element parent) { |
| 378 | Iterable<Element> it = () -> new ChildIterator(parent); |
| 379 | return StreamSupport.stream(it.spliterator(), false); |
| 380 | } |
| 381 | |
| 382 | private static Stream<Element> getChildrenStream(Element parent, String name) { |
| 383 | return getChildrenStream(parent).filter(child -> name.equals(child.getNodeName())); |
| 384 | } |
| 385 | |
331 | 386 | private static List<Element> getChildren(Element parent, String name) { |
332 | | List<Element> retVal = new ArrayList<>(); |
333 | | for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
334 | | if (child instanceof Element && name.equals(child.getNodeName())) { |
335 | | retVal.add((Element) child); |
336 | | } |
337 | | } |
338 | | return retVal; |
| 387 | return getChildrenStream(parent, name).collect(Collectors.toList()); |
339 | 388 | } |
340 | 389 | |
341 | 390 | private static Element getChild(Element parent, String name) { |
342 | 391 | if (parent == null) |
343 | 392 | return null; |
344 | | for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
345 | | if (child instanceof Element && name.equals(child.getNodeName())) |
346 | | return (Element) child; |
347 | | } |
348 | | return null; |
| 393 | return getChildrenStream(parent, name).findFirst().orElse(null); |
349 | 394 | } |
350 | 395 | |
| 396 | /** |
| 397 | * The details of a layer of this wms server. |
| 398 | */ |
351 | 399 | public static class LayerDetails { |
352 | 400 | |
| 401 | /** |
| 402 | * The layer name |
| 403 | */ |
353 | 404 | public final String name; |
354 | 405 | public final String ident; |
| 406 | /** |
| 407 | * The child layers of this layer |
| 408 | */ |
355 | 409 | public final List<LayerDetails> children; |
| 410 | /** |
| 411 | * The bounds this layer can be used for |
| 412 | */ |
356 | 413 | public final Bounds bounds; |
357 | 414 | public final Set<String> crsList; |
358 | 415 | public final boolean supported; |
359 | 416 | |
360 | | public LayerDetails(String name, String ident, Set<String> crsList, |
361 | | boolean supportedLayer, Bounds bounds, |
362 | | List<LayerDetails> childLayers) { |
| 417 | public LayerDetails(String name, String ident, Set<String> crsList, boolean supportedLayer, Bounds bounds, |
| 418 | List<LayerDetails> childLayers) { |
363 | 419 | this.name = name; |
364 | 420 | this.ident = ident; |
365 | 421 | this.supported = supportedLayer; |