Changeset 13733 in josm for trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java
- Timestamp:
- 2018-05-12T14:18:57+02:00 (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java
r13542 r13733 51 51 import org.openstreetmap.josm.data.coor.EastNorth; 52 52 import org.openstreetmap.josm.data.coor.LatLon; 53 import org.openstreetmap.josm.data.imagery.GetCapabilitiesParseHelper.TransferMode; 54 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 53 55 import org.openstreetmap.josm.data.projection.Projection; 54 56 import org.openstreetmap.josm.data.projection.Projections; … … 126 128 } 127 129 128 private static class TileMatrixSet { 130 /** 131 * 132 * class representing WMTS TileMatrixSet 133 * This connects projection and TileMatrix (how the map is divided in tiles) 134 * 135 */ 136 public static class TileMatrixSet { 129 137 130 138 private final List<TileMatrix> tileMatrix; … … 154 162 return "TileMatrixSet [crs=" + crs + ", identifier=" + identifier + ']'; 155 163 } 164 165 /** 166 * 167 * @return identifier of this TileMatrixSet 168 */ 169 public String getIdentifier() { 170 return identifier; 171 } 156 172 } 157 173 … … 162 178 } 163 179 164 private static class Layer { 180 /** 181 * Class representing WMTS Layer information 182 * 183 */ 184 public static class Layer { 165 185 private String format; 166 186 private String identifier; … … 202 222 + tileMatrixSet + ", baseUrl=" + baseUrl + ", style=" + style + ']'; 203 223 } 224 225 /** 226 * 227 * @return identifier of this layer 228 */ 229 public String getIdentifier() { 230 return identifier; 231 } 232 233 /** 234 * 235 * @return style of this layer 236 */ 237 public String getStyle() { 238 return style; 239 } 240 241 /** 242 * 243 * @return 244 */ 245 public TileMatrixSet getTileMatrixSet() { 246 return tileMatrixSet; 247 } 248 } 249 250 /** 251 * Exception thrown when praser doesn't find expected information in GetCapabilities document 252 * 253 */ 254 public static class WMTSGetCapabilitiesException extends Exception { 255 256 /** 257 * @param cause description of cause 258 */ 259 public WMTSGetCapabilitiesException(String cause) { 260 super(cause); 261 } 204 262 } 205 263 … … 211 269 super(Main.parent, tr("Select WMTS layer"), tr("Add layers"), tr("Cancel")); 212 270 this.layers = groupLayersByNameAndTileMatrixSet(layers); 213 //getLayersTable(layers, Main.getProjection()) 214 this.list = new JTable( 215 new AbstractTableModel() { 216 @Override 217 public Object getValueAt(int rowIndex, int columnIndex) { 218 switch (columnIndex) { 219 case 0: 220 return SelectLayerDialog.this.layers.get(rowIndex).getValue() 221 .stream() 222 .map(Layer::getUserTitle) 223 .collect(Collectors.joining(", ")); //this should be only one 224 case 1: 225 return SelectLayerDialog.this.layers.get(rowIndex).getValue() 226 .stream() 227 .map(x -> x.tileMatrixSet.crs) 228 .collect(Collectors.joining(", ")); 229 case 2: 230 return SelectLayerDialog.this.layers.get(rowIndex).getValue() 231 .stream() 232 .map(x -> x.tileMatrixSet.identifier) 233 .collect(Collectors.joining(", ")); //this should be only one 234 default: 235 throw new IllegalArgumentException(); 236 } 237 } 238 239 @Override 240 public int getRowCount() { 241 return SelectLayerDialog.this.layers.size(); 242 } 243 244 @Override 245 public int getColumnCount() { 246 return 3; 247 } 248 249 @Override 250 public String getColumnName(int column) { 251 switch (column) { 252 case 0: return tr("Layer name"); 253 case 1: return tr("Projection"); 254 case 2: return tr("Matrix set identifier"); 255 default: 256 throw new IllegalArgumentException(); 257 } 258 } 259 }); 260 this.list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 261 this.list.setAutoCreateRowSorter(true); 262 this.list.setRowSelectionAllowed(true); 263 this.list.setColumnSelectionAllowed(false); 271 this.list = getLayerSelectionPanel(this.layers); 264 272 JPanel panel = new JPanel(new GridBagLayout()); 265 273 panel.add(new JScrollPane(this.list), GBC.eol().fill()); … … 273 281 } 274 282 Layer selectedLayer = layers.get(list.convertRowIndexToModel(index)).getValue().get(0); 275 return new WMTSDefaultLayer(selectedLayer.identifier, selectedLayer.tileMatrixSet.identifier); 276 } 277 278 private static List<Entry<String, List<Layer>>> groupLayersByNameAndTileMatrixSet(Collection<Layer> layers) { 279 Map<String, List<Layer>> layerByName = layers.stream().collect( 280 Collectors.groupingBy(x -> x.identifier + '\u001c' + x.tileMatrixSet.identifier)); 281 return layerByName.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList()); 282 } 283 return new DefaultLayer(ImageryType.WMTS, selectedLayer.identifier, selectedLayer.style, selectedLayer.tileMatrixSet.identifier); 284 } 285 283 286 } 284 287 … … 292 295 private ScaleList nativeScaleList; 293 296 294 private final WMTSDefaultLayer defaultLayer;297 private final DefaultLayer defaultLayer; 295 298 296 299 private Projection tileProjection; … … 300 303 * @param info imagery info 301 304 * @throws IOException if any I/O error occurs 305 * @throws WMTSGetCapabilitiesException 302 306 * @throws IllegalArgumentException if any other error happens for the given imagery info 303 307 */ 304 public WMTSTileSource(ImageryInfo info) throws IOException {308 public WMTSTileSource(ImageryInfo info) throws IOException, WMTSGetCapabilitiesException { 305 309 super(info); 306 310 CheckParameterUtil.ensureThat(info.getDefaultLayers().size() < 2, "At most 1 default layer for WMTS is supported"); 307 311 this.headers.putAll(info.getCustomHttpHeaders()); 308 312 this.baseUrl = GetCapabilitiesParseHelper.normalizeCapabilitiesUrl(handleTemplate(info.getUrl())); 309 this.layers = getCapabilities(); 313 WMTSCapabilities capabilities = getCapabilities(baseUrl, headers); 314 this.layers = capabilities.getLayers(); 315 this.baseUrl = capabilities.getBaseUrl(); 316 this.transferMode = capabilities.getTransferMode(); 310 317 if (info.getDefaultLayers().isEmpty()) { 311 318 Logging.warn(tr("No default layer selected, choosing first layer.")); 312 319 if (!layers.isEmpty()) { 313 320 Layer first = layers.iterator().next(); 314 this.defaultLayer = new WMTSDefaultLayer(first.identifier, first.tileMatrixSet.identifier);321 this.defaultLayer = new DefaultLayer(info.getImageryType(), first.identifier, first.style, first.tileMatrixSet.identifier); 315 322 } else { 316 323 this.defaultLayer = null; 317 324 } 318 325 } else { 319 DefaultLayer defLayer = info.getDefaultLayers().iterator().next(); 320 if (defLayer instanceof WMTSDefaultLayer) { 321 this.defaultLayer = (WMTSDefaultLayer) defLayer; 322 } else { 323 this.defaultLayer = null; 324 } 326 this.defaultLayer = info.getDefaultLayers().iterator().next(); 325 327 } 326 328 if (this.layers.isEmpty()) … … 343 345 // only one tile matrix set with matching projection - no point in asking 344 346 Layer selectedLayer = ls.get(0); 345 return new WMTSDefaultLayer(selectedLayer.identifier, selectedLayer.tileMatrixSet.identifier);347 return new DefaultLayer(ImageryType.WMTS, selectedLayer.identifier, selectedLayer.style, selectedLayer.tileMatrixSet.identifier); 346 348 } 347 349 } … … 366 368 } 367 369 368 /** 370 371 /** 372 * @param url of the getCapabilities document 373 * @param headers HTTP headers to set when calling getCapabilities url 369 374 * @return capabilities 370 375 * @throws IOException in case of any I/O error 376 * @throws WMTSGetCapabilitiesException 371 377 * @throws IllegalArgumentException in case of any other error 372 378 */ 373 p rivate Collection<Layer> getCapabilities() throws IOException {374 try (CachedFile cf = new CachedFile( baseUrl); InputStream in = cf.setHttpHeaders(headers).379 public static WMTSCapabilities getCapabilities(String url, Map<String, String> headers) throws IOException, WMTSGetCapabilitiesException { 380 try (CachedFile cf = new CachedFile(url); InputStream in = cf.setHttpHeaders(headers). 375 381 setMaxAge(Config.getPref().getLong("wmts.capabilities.cache.max_age", 7 * CachedFile.DAYS)). 376 382 setCachingStrategy(CachedFile.CachingStrategy.IfModifiedSince). … … 379 385 if (data.length == 0) { 380 386 cf.clear(); 381 throw new IllegalArgumentException("Could not read data from: " + baseUrl);387 throw new IllegalArgumentException("Could not read data from: " + url); 382 388 } 383 389 384 390 try { 385 391 XMLStreamReader reader = GetCapabilitiesParseHelper.getReader(new ByteArrayInputStream(data)); 386 Collection<Layer> ret = new ArrayList<>(); 392 WMTSCapabilities ret = null; 393 Collection<Layer> layers = null; 387 394 for (int event = reader.getEventType(); reader.hasNext(); event = reader.next()) { 388 395 if (event == XMLStreamReader.START_ELEMENT) { 389 396 if (GetCapabilitiesParseHelper.QN_OWS_OPERATIONS_METADATA.equals(reader.getName())) { 390 parseOperationMetadata(reader);397 ret = parseOperationMetadata(reader); 391 398 } 392 399 393 400 if (QN_CONTENTS.equals(reader.getName())) { 394 ret= parseContents(reader);401 layers = parseContents(reader); 395 402 } 396 403 } 397 404 } 405 if (ret == null) { 406 /* 407 * see #12168 - create dummy operation metadata - not all WMTS services provide this information 408 * 409 * WMTS Standard: 410 * > Resource oriented architecture style HTTP encodings SHALL not be described in the OperationsMetadata section. 411 * 412 * And OperationMetada is not mandatory element. So REST mode is justifiable 413 */ 414 ret = new WMTSCapabilities(url, TransferMode.REST); 415 } 416 if (layers == null) { 417 throw new WMTSGetCapabilitiesException(tr("WMTS Capabilties document did not contain layers in url: {0}", url)); 418 } 419 ret.addLayers(layers); 398 420 return ret; 399 421 } catch (XMLStreamException e) { … … 456 478 supportedMimeTypes.add("image/jpgpng"); // used by ESRI 457 479 supportedMimeTypes.add("image/png8"); // used by geoserver 480 if (supportedMimeTypes.contains("image/jpeg")) { 481 supportedMimeTypes.add("image/jpg"); // sometimes mispelled by Arcgis 482 } 458 483 Collection<String> unsupportedFormats = new ArrayList<>(); 459 484 … … 639 664 /** 640 665 * Parses OperationMetadata section. Returns when reader is on OperationsMetadata closing tag. 641 * Sets this.baseUrl and this.transferMode666 * return WMTSCapabilities with baseUrl and transferMode 642 667 * 643 668 * @param reader StAX reader instance 669 * @return WMTSCapabilities with baseUrl and transferMode set 644 670 * @throws XMLStreamException See {@link XMLStreamReader} 645 671 */ 646 private voidparseOperationMetadata(XMLStreamReader reader) throws XMLStreamException {672 private static WMTSCapabilities parseOperationMetadata(XMLStreamReader reader) throws XMLStreamException { 647 673 for (int event = reader.getEventType(); 648 674 reader.hasNext() && !(event == XMLStreamReader.END_ELEMENT && … … 657 683 GetCapabilitiesParseHelper.QN_OWS_GET 658 684 )) { 659 this.baseUrl = reader.getAttributeValue(GetCapabilitiesParseHelper.XLINK_NS_URL, "href"); 660 this.transferMode = GetCapabilitiesParseHelper.getTransferMode(reader); 661 } 662 } 685 return new WMTSCapabilities( 686 reader.getAttributeValue(GetCapabilitiesParseHelper.XLINK_NS_URL, "href"), 687 GetCapabilitiesParseHelper.getTransferMode(reader) 688 ); 689 } 690 } 691 return null; 663 692 } 664 693 … … 671 700 return; 672 701 List<Layer> matchingLayers = layers.stream().filter( 673 l -> l.identifier.equals(defaultLayer. layerName) && l.tileMatrixSet.crs.equals(proj.toCode()))702 l -> l.identifier.equals(defaultLayer.getLayerName()) && l.tileMatrixSet.crs.equals(proj.toCode())) 674 703 .collect(Collectors.toList()); 675 704 if (matchingLayers.size() > 1) { … … 686 715 this.tileProjection = null; 687 716 for (Layer layer : layers) { 688 if (!layer.identifier.equals(defaultLayer. layerName)) {717 if (!layer.identifier.equals(defaultLayer.getLayerName())) { 689 718 continue; 690 719 } … … 922 951 } 923 952 953 public static JTable getLayerSelectionPanel(List<Entry<String, List<Layer>>> layers) { 954 JTable list = new JTable( 955 new AbstractTableModel() { 956 @Override 957 public Object getValueAt(int rowIndex, int columnIndex) { 958 switch (columnIndex) { 959 case 0: 960 return layers.get(rowIndex).getValue() 961 .stream() 962 .map(Layer::getUserTitle) 963 .collect(Collectors.joining(", ")); //this should be only one 964 case 1: 965 return layers.get(rowIndex).getValue() 966 .stream() 967 .map(x -> x.tileMatrixSet.crs) 968 .collect(Collectors.joining(", ")); 969 case 2: 970 return layers.get(rowIndex).getValue() 971 .stream() 972 .map(x -> x.tileMatrixSet.identifier) 973 .collect(Collectors.joining(", ")); //this should be only one 974 default: 975 throw new IllegalArgumentException(); 976 } 977 } 978 979 @Override 980 public int getRowCount() { 981 return layers.size(); 982 } 983 984 @Override 985 public int getColumnCount() { 986 return 3; 987 } 988 989 @Override 990 public String getColumnName(int column) { 991 switch (column) { 992 case 0: return tr("Layer name"); 993 case 1: return tr("Projection"); 994 case 2: return tr("Matrix set identifier"); 995 default: 996 throw new IllegalArgumentException(); 997 } 998 } 999 }); 1000 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 1001 list.setAutoCreateRowSorter(true); 1002 list.setRowSelectionAllowed(true); 1003 list.setColumnSelectionAllowed(false); 1004 return list; 1005 } 1006 1007 public static List<Entry<String, List<Layer>>> groupLayersByNameAndTileMatrixSet(Collection<Layer> layers) { 1008 Map<String, List<Layer>> layerByName = layers.stream().collect( 1009 Collectors.groupingBy(x -> x.identifier + '\u001c' + x.tileMatrixSet.identifier)); 1010 return layerByName.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList()); 1011 } 1012 1013 924 1014 /** 925 1015 * @return set of projection codes that this TileSource supports
Note:
See TracChangeset
for help on using the changeset viewer.