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