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; |