001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.imagery; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Image; 007import java.io.StringReader; 008import java.util.ArrayList; 009import java.util.Arrays; 010import java.util.Collection; 011import java.util.Collections; 012import java.util.EnumMap; 013import java.util.List; 014import java.util.Locale; 015import java.util.Map; 016import java.util.Objects; 017import java.util.Optional; 018import java.util.Set; 019import java.util.TreeSet; 020import java.util.concurrent.ConcurrentHashMap; 021import java.util.concurrent.TimeUnit; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024import java.util.stream.Collectors; 025 026import javax.json.Json; 027import javax.json.JsonObject; 028import javax.json.JsonReader; 029import javax.json.stream.JsonCollectors; 030import javax.swing.ImageIcon; 031 032import org.openstreetmap.gui.jmapviewer.interfaces.Attributed; 033import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 034import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource; 035import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik; 036import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo; 037import org.openstreetmap.josm.data.Bounds; 038import org.openstreetmap.josm.data.StructUtils; 039import org.openstreetmap.josm.data.StructUtils.StructEntry; 040import org.openstreetmap.josm.io.Capabilities; 041import org.openstreetmap.josm.io.OsmApi; 042import org.openstreetmap.josm.spi.preferences.Config; 043import org.openstreetmap.josm.spi.preferences.IPreferences; 044import org.openstreetmap.josm.tools.CheckParameterUtil; 045import org.openstreetmap.josm.tools.ImageProvider; 046import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 047import org.openstreetmap.josm.tools.LanguageInfo; 048import org.openstreetmap.josm.tools.Logging; 049import org.openstreetmap.josm.tools.MultiMap; 050import org.openstreetmap.josm.tools.Utils; 051 052/** 053 * Class that stores info about an image background layer. 054 * 055 * @author Frederik Ramm 056 */ 057public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed { 058 059 /** 060 * Type of imagery entry. 061 */ 062 public enum ImageryType { 063 /** A WMS (Web Map Service) entry. **/ 064 WMS("wms"), 065 /** A TMS (Tile Map Service) entry. **/ 066 TMS("tms"), 067 /** TMS entry for Microsoft Bing. */ 068 BING("bing"), 069 /** TMS entry for Russian company <a href="https://wiki.openstreetmap.org/wiki/WikiProject_Russia/kosmosnimki">ScanEx</a>. **/ 070 SCANEX("scanex"), 071 /** A WMS endpoint entry only stores the WMS server info, without layer, which are chosen later by the user. **/ 072 WMS_ENDPOINT("wms_endpoint"), 073 /** WMTS stores GetCapabilities URL. Does not store any information about the layer **/ 074 WMTS("wmts"); 075 076 private final String typeString; 077 078 ImageryType(String typeString) { 079 this.typeString = typeString; 080 } 081 082 /** 083 * Returns the unique string identifying this type. 084 * @return the unique string identifying this type 085 * @since 6690 086 */ 087 public final String getTypeString() { 088 return typeString; 089 } 090 091 /** 092 * Returns the imagery type from the given type string. 093 * @param s The type string 094 * @return the imagery type matching the given type string 095 */ 096 public static ImageryType fromString(String s) { 097 for (ImageryType type : ImageryType.values()) { 098 if (type.getTypeString().equals(s)) { 099 return type; 100 } 101 } 102 return null; 103 } 104 } 105 106 /** 107 * Category of imagery entry. 108 * @since 13792 109 */ 110 public enum ImageryCategory { 111 /** A aerial or satellite photo. **/ 112 PHOTO(/* ICON(data/imagery/) */ "photo", tr("Aerial or satellite photo")), 113 /** A map. **/ 114 MAP(/* ICON(data/imagery/) */ "map", tr("Map")), 115 /** A historic or otherwise outdated map. */ 116 HISTORICMAP(/* ICON(data/imagery/) */ "historicmap", tr("Historic or otherwise outdated map")), 117 /** A map based on OSM data. **/ 118 OSMBASEDMAP(/* ICON(data/imagery/) */ "osmbasedmap", tr("Map based on OSM data")), 119 /** A historic or otherwise outdated aerial or satellite photo. **/ 120 HISTORICPHOTO(/* ICON(data/imagery/) */ "historicphoto", tr("Historic or otherwise outdated aerial or satellite photo")), 121 /** Any other type of imagery **/ 122 OTHER(/* ICON(data/imagery/) */ "other", tr("Imagery not matching any other category")); 123 124 private final String category; 125 private final String description; 126 private static final Map<ImageSizes, Map<ImageryCategory, ImageIcon>> iconCache = 127 Collections.synchronizedMap(new EnumMap<>(ImageSizes.class)); 128 129 ImageryCategory(String category, String description) { 130 this.category = category; 131 this.description = description; 132 } 133 134 /** 135 * Returns the unique string identifying this category. 136 * @return the unique string identifying this category 137 */ 138 public final String getCategoryString() { 139 return category; 140 } 141 142 /** 143 * Returns the description of this category. 144 * @return the description of this category 145 */ 146 public final String getDescription() { 147 return description; 148 } 149 150 /** 151 * Returns the category icon at the given size. 152 * @param size icon wanted size 153 * @return the category icon at the given size 154 * @since 15049 155 */ 156 public final ImageIcon getIcon(ImageSizes size) { 157 return iconCache 158 .computeIfAbsent(size, x -> Collections.synchronizedMap(new EnumMap<>(ImageryCategory.class))) 159 .computeIfAbsent(this, x -> ImageProvider.get("data/imagery", x.category, size)); 160 } 161 162 /** 163 * Returns the imagery category from the given category string. 164 * @param s The category string 165 * @return the imagery category matching the given category string 166 */ 167 public static ImageryCategory fromString(String s) { 168 for (ImageryCategory category : ImageryCategory.values()) { 169 if (category.getCategoryString().equals(s)) { 170 return category; 171 } 172 } 173 return null; 174 } 175 } 176 177 /** 178 * Multi-polygon bounds for imagery backgrounds. 179 * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location. 180 */ 181 public static class ImageryBounds extends Bounds { 182 183 /** 184 * Constructs a new {@code ImageryBounds} from string. 185 * @param asString The string containing the list of shapes defining this bounds 186 * @param separator The shape separator in the given string, usually a comma 187 */ 188 public ImageryBounds(String asString, String separator) { 189 super(asString, separator); 190 } 191 192 private List<Shape> shapes = new ArrayList<>(); 193 194 /** 195 * Adds a new shape to this bounds. 196 * @param shape The shape to add 197 */ 198 public final void addShape(Shape shape) { 199 this.shapes.add(shape); 200 } 201 202 /** 203 * Sets the list of shapes defining this bounds. 204 * @param shapes The list of shapes defining this bounds. 205 */ 206 public final void setShapes(List<Shape> shapes) { 207 this.shapes = shapes; 208 } 209 210 /** 211 * Returns the list of shapes defining this bounds. 212 * @return The list of shapes defining this bounds 213 */ 214 public final List<Shape> getShapes() { 215 return shapes; 216 } 217 218 @Override 219 public int hashCode() { 220 return Objects.hash(super.hashCode(), shapes); 221 } 222 223 @Override 224 public boolean equals(Object o) { 225 if (this == o) return true; 226 if (o == null || getClass() != o.getClass()) return false; 227 if (!super.equals(o)) return false; 228 ImageryBounds that = (ImageryBounds) o; 229 return Objects.equals(shapes, that.shapes); 230 } 231 } 232 233 /** original name of the imagery entry in case of translation call, for multiple languages English when possible */ 234 private String origName; 235 /** (original) language of the translated name entry */ 236 private String langName; 237 /** whether this is a entry activated by default or not */ 238 private boolean defaultEntry; 239 /** Whether this service requires a explicit EULA acceptance before it can be activated */ 240 private String eulaAcceptanceRequired; 241 /** type of the imagery servics - WMS, TMS, ... */ 242 private ImageryType imageryType = ImageryType.WMS; 243 private double pixelPerDegree; 244 /** maximum zoom level for TMS imagery */ 245 private int defaultMaxZoom; 246 /** minimum zoom level for TMS imagery */ 247 private int defaultMinZoom; 248 /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */ 249 private ImageryBounds bounds; 250 /** projections supported by WMS servers */ 251 private List<String> serverProjections = Collections.emptyList(); 252 /** description of the imagery entry, should contain notes what type of data it is */ 253 private String description; 254 /** language of the description entry */ 255 private String langDescription; 256 /** Text of a text attribution displayed when using the imagery */ 257 private String attributionText; 258 /** Link to a reference stating the permission for OSM usage */ 259 private String permissionReferenceURL; 260 /** Link behind the text attribution displayed when using the imagery */ 261 private String attributionLinkURL; 262 /** Image of a graphical attribution displayed when using the imagery */ 263 private String attributionImage; 264 /** Link behind the graphical attribution displayed when using the imagery */ 265 private String attributionImageURL; 266 /** Text with usage terms displayed when using the imagery */ 267 private String termsOfUseText; 268 /** Link behind the text with usage terms displayed when using the imagery */ 269 private String termsOfUseURL; 270 /** country code of the imagery (for country specific imagery) */ 271 private String countryCode = ""; 272 /** 273 * creation date of the imagery (in the form YYYY-MM-DD;YYYY-MM-DD, where 274 * DD and MM as well as a second date are optional) 275 * @since 11570 276 */ 277 private String date; 278 /** 279 * marked as best in other editors 280 * @since 11575 281 */ 282 private boolean bestMarked; 283 /** 284 * marked as overlay 285 * @since 13536 286 */ 287 private boolean overlay; 288 /** 289 * list of old IDs, only for loading, not handled anywhere else 290 * @since 13536 291 */ 292 private Collection<String> oldIds; 293 /** mirrors of different type for this entry */ 294 private List<ImageryInfo> mirrors; 295 /** icon used in menu */ 296 private String icon; 297 /** is the geo reference correct - don't offer offset handling */ 298 private boolean isGeoreferenceValid; 299 /** which layers should be activated by default on layer addition. **/ 300 private List<DefaultLayer> defaultLayers = new ArrayList<>(); 301 /** HTTP headers **/ 302 private Map<String, String> customHttpHeaders = new ConcurrentHashMap<>(); 303 /** Should this map be transparent **/ 304 private boolean transparent = true; 305 private int minimumTileExpire = (int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get()); 306 /** category of the imagery */ 307 private ImageryCategory category; 308 /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */ 309 private String categoryOriginalString; 310 /** when adding a field, also adapt the: 311 * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object} 312 * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor} 313 * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor} 314 * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor} 315 * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method} 316 **/ 317 318 /** 319 * Auxiliary class to save an {@link ImageryInfo} object in the preferences. 320 */ 321 public static class ImageryPreferenceEntry { 322 @StructEntry String name; 323 @StructEntry String d; 324 @StructEntry String id; 325 @StructEntry String type; 326 @StructEntry String url; 327 @StructEntry double pixel_per_eastnorth; 328 @StructEntry String eula; 329 @StructEntry String attribution_text; 330 @StructEntry String attribution_url; 331 @StructEntry String permission_reference_url; 332 @StructEntry String logo_image; 333 @StructEntry String logo_url; 334 @StructEntry String terms_of_use_text; 335 @StructEntry String terms_of_use_url; 336 @StructEntry String country_code = ""; 337 @StructEntry String date; 338 @StructEntry int max_zoom; 339 @StructEntry int min_zoom; 340 @StructEntry String cookies; 341 @StructEntry String bounds; 342 @StructEntry String shapes; 343 @StructEntry String projections; 344 @StructEntry String icon; 345 @StructEntry String description; 346 @StructEntry MultiMap<String, String> noTileHeaders; 347 @StructEntry MultiMap<String, String> noTileChecksums; 348 @StructEntry int tileSize = -1; 349 @StructEntry Map<String, String> metadataHeaders; 350 @StructEntry boolean valid_georeference; 351 @StructEntry boolean bestMarked; 352 @StructEntry boolean modTileFeatures; 353 @StructEntry boolean overlay; 354 @StructEntry String default_layers; 355 @StructEntry Map<String, String> customHttpHeaders; 356 @StructEntry boolean transparent; 357 @StructEntry int minimumTileExpire; 358 @StructEntry String category; 359 360 /** 361 * Constructs a new empty WMS {@code ImageryPreferenceEntry}. 362 */ 363 public ImageryPreferenceEntry() { 364 // Do nothing 365 } 366 367 /** 368 * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}. 369 * @param i The corresponding imagery info 370 */ 371 public ImageryPreferenceEntry(ImageryInfo i) { 372 name = i.name; 373 id = i.id; 374 type = i.imageryType.getTypeString(); 375 url = i.url; 376 pixel_per_eastnorth = i.pixelPerDegree; 377 eula = i.eulaAcceptanceRequired; 378 attribution_text = i.attributionText; 379 attribution_url = i.attributionLinkURL; 380 permission_reference_url = i.permissionReferenceURL; 381 date = i.date; 382 bestMarked = i.bestMarked; 383 overlay = i.overlay; 384 logo_image = i.attributionImage; 385 logo_url = i.attributionImageURL; 386 terms_of_use_text = i.termsOfUseText; 387 terms_of_use_url = i.termsOfUseURL; 388 country_code = i.countryCode; 389 max_zoom = i.defaultMaxZoom; 390 min_zoom = i.defaultMinZoom; 391 cookies = i.cookies; 392 icon = i.icon; 393 description = i.description; 394 category = i.category != null ? i.category.getCategoryString() : null; 395 if (i.bounds != null) { 396 bounds = i.bounds.encodeAsString(","); 397 StringBuilder shapesString = new StringBuilder(); 398 for (Shape s : i.bounds.getShapes()) { 399 if (shapesString.length() > 0) { 400 shapesString.append(';'); 401 } 402 shapesString.append(s.encodeAsString(",")); 403 } 404 if (shapesString.length() > 0) { 405 shapes = shapesString.toString(); 406 } 407 } 408 if (!i.serverProjections.isEmpty()) { 409 projections = i.serverProjections.stream().collect(Collectors.joining(",")); 410 } 411 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) { 412 noTileHeaders = new MultiMap<>(i.noTileHeaders); 413 } 414 415 if (i.noTileChecksums != null && !i.noTileChecksums.isEmpty()) { 416 noTileChecksums = new MultiMap<>(i.noTileChecksums); 417 } 418 419 if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) { 420 metadataHeaders = i.metadataHeaders; 421 } 422 423 tileSize = i.getTileSize(); 424 425 valid_georeference = i.isGeoreferenceValid(); 426 modTileFeatures = i.isModTileFeatures(); 427 if (!i.defaultLayers.isEmpty()) { 428 default_layers = i.defaultLayers.stream().map(DefaultLayer::toJson).collect(JsonCollectors.toJsonArray()).toString(); 429 } 430 customHttpHeaders = i.customHttpHeaders; 431 transparent = i.isTransparent(); 432 minimumTileExpire = i.minimumTileExpire; 433 } 434 435 @Override 436 public String toString() { 437 StringBuilder s = new StringBuilder("ImageryPreferenceEntry [name=").append(name); 438 if (id != null) { 439 s.append(" id=").append(id); 440 } 441 s.append(']'); 442 return s.toString(); 443 } 444 } 445 446 /** 447 * Constructs a new WMS {@code ImageryInfo}. 448 */ 449 public ImageryInfo() { 450 super(); 451 } 452 453 /** 454 * Constructs a new WMS {@code ImageryInfo} with a given name. 455 * @param name The entry name 456 */ 457 public ImageryInfo(String name) { 458 super(name); 459 } 460 461 /** 462 * Constructs a new WMS {@code ImageryInfo} with given name and extended URL. 463 * @param name The entry name 464 * @param url The entry extended URL 465 */ 466 public ImageryInfo(String name, String url) { 467 this(name); 468 setExtendedUrl(url); 469 } 470 471 /** 472 * Constructs a new WMS {@code ImageryInfo} with given name, extended and EULA URLs. 473 * @param name The entry name 474 * @param url The entry URL 475 * @param eulaAcceptanceRequired The EULA URL 476 */ 477 public ImageryInfo(String name, String url, String eulaAcceptanceRequired) { 478 this(name); 479 setExtendedUrl(url); 480 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 481 } 482 483 /** 484 * Constructs a new {@code ImageryInfo} with given name, url, extended and EULA URLs. 485 * @param name The entry name 486 * @param url The entry URL 487 * @param type The entry imagery type. If null, WMS will be used as default 488 * @param eulaAcceptanceRequired The EULA URL 489 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work 490 * @throws IllegalArgumentException if type refers to an unknown imagery type 491 */ 492 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) { 493 this(name); 494 setExtendedUrl(url); 495 ImageryType t = ImageryType.fromString(type); 496 this.cookies = cookies; 497 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 498 if (t != null) { 499 this.imageryType = t; 500 } else if (type != null && !type.isEmpty()) { 501 throw new IllegalArgumentException("unknown type: "+type); 502 } 503 } 504 505 /** 506 * Constructs a new {@code ImageryInfo} with given name, url, id, extended and EULA URLs. 507 * @param name The entry name 508 * @param url The entry URL 509 * @param type The entry imagery type. If null, WMS will be used as default 510 * @param eulaAcceptanceRequired The EULA URL 511 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work 512 * @param id tile id 513 * @throws IllegalArgumentException if type refers to an unknown imagery type 514 */ 515 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies, String id) { 516 this(name, url, type, eulaAcceptanceRequired, cookies); 517 setId(id); 518 } 519 520 /** 521 * Constructs a new {@code ImageryInfo} from an imagery preference entry. 522 * @param e The imagery preference entry 523 */ 524 public ImageryInfo(ImageryPreferenceEntry e) { 525 super(e.name, e.url, e.id); 526 CheckParameterUtil.ensureParameterNotNull(e.name, "name"); 527 CheckParameterUtil.ensureParameterNotNull(e.url, "url"); 528 description = e.description; 529 cookies = e.cookies; 530 eulaAcceptanceRequired = e.eula; 531 imageryType = ImageryType.fromString(e.type); 532 if (imageryType == null) throw new IllegalArgumentException("unknown type"); 533 pixelPerDegree = e.pixel_per_eastnorth; 534 defaultMaxZoom = e.max_zoom; 535 defaultMinZoom = e.min_zoom; 536 if (e.bounds != null) { 537 bounds = new ImageryBounds(e.bounds, ","); 538 if (e.shapes != null) { 539 try { 540 for (String s : e.shapes.split(";")) { 541 bounds.addShape(new Shape(s, ",")); 542 } 543 } catch (IllegalArgumentException ex) { 544 Logging.warn(ex); 545 } 546 } 547 } 548 if (e.projections != null && !e.projections.isEmpty()) { 549 // split generates null element on empty string which gives one element Array[null] 550 serverProjections = Arrays.asList(e.projections.split(",")); 551 } 552 attributionText = e.attribution_text; 553 attributionLinkURL = e.attribution_url; 554 permissionReferenceURL = e.permission_reference_url; 555 attributionImage = e.logo_image; 556 attributionImageURL = e.logo_url; 557 date = e.date; 558 bestMarked = e.bestMarked; 559 overlay = e.overlay; 560 termsOfUseText = e.terms_of_use_text; 561 termsOfUseURL = e.terms_of_use_url; 562 countryCode = e.country_code; 563 icon = e.icon; 564 if (e.noTileHeaders != null) { 565 noTileHeaders = e.noTileHeaders.toMap(); 566 } 567 if (e.noTileChecksums != null) { 568 noTileChecksums = e.noTileChecksums.toMap(); 569 } 570 setTileSize(e.tileSize); 571 metadataHeaders = e.metadataHeaders; 572 isGeoreferenceValid = e.valid_georeference; 573 modTileFeatures = e.modTileFeatures; 574 if (e.default_layers != null) { 575 try (JsonReader jsonReader = Json.createReader(new StringReader(e.default_layers))) { 576 defaultLayers = jsonReader. 577 readArray(). 578 stream(). 579 map(x -> DefaultLayer.fromJson((JsonObject) x, imageryType)). 580 collect(Collectors.toList()); 581 } 582 } 583 customHttpHeaders = e.customHttpHeaders; 584 transparent = e.transparent; 585 minimumTileExpire = e.minimumTileExpire; 586 category = ImageryCategory.fromString(e.category); 587 } 588 589 /** 590 * Constructs a new {@code ImageryInfo} from an existing one. 591 * @param i The other imagery info 592 */ 593 public ImageryInfo(ImageryInfo i) { 594 super(i.name, i.url, i.id); 595 this.noTileHeaders = i.noTileHeaders; 596 this.noTileChecksums = i.noTileChecksums; 597 this.minZoom = i.minZoom; 598 this.maxZoom = i.maxZoom; 599 this.cookies = i.cookies; 600 this.tileSize = i.tileSize; 601 this.metadataHeaders = i.metadataHeaders; 602 this.modTileFeatures = i.modTileFeatures; 603 604 this.origName = i.origName; 605 this.langName = i.langName; 606 this.defaultEntry = i.defaultEntry; 607 this.eulaAcceptanceRequired = null; 608 this.imageryType = i.imageryType; 609 this.pixelPerDegree = i.pixelPerDegree; 610 this.defaultMaxZoom = i.defaultMaxZoom; 611 this.defaultMinZoom = i.defaultMinZoom; 612 this.bounds = i.bounds; 613 this.serverProjections = i.serverProjections; 614 this.description = i.description; 615 this.langDescription = i.langDescription; 616 this.attributionText = i.attributionText; 617 this.permissionReferenceURL = i.permissionReferenceURL; 618 this.attributionLinkURL = i.attributionLinkURL; 619 this.attributionImage = i.attributionImage; 620 this.attributionImageURL = i.attributionImageURL; 621 this.termsOfUseText = i.termsOfUseText; 622 this.termsOfUseURL = i.termsOfUseURL; 623 this.countryCode = i.countryCode; 624 this.date = i.date; 625 this.bestMarked = i.bestMarked; 626 this.overlay = i.overlay; 627 // do not copy field {@code mirrors} 628 this.icon = i.icon; 629 this.isGeoreferenceValid = i.isGeoreferenceValid; 630 this.defaultLayers = i.defaultLayers; 631 this.customHttpHeaders = i.customHttpHeaders; 632 this.transparent = i.transparent; 633 this.minimumTileExpire = i.minimumTileExpire; 634 this.category = i.category; 635 } 636 637 @Override 638 public int hashCode() { 639 return Objects.hash(url, imageryType); 640 } 641 642 /** 643 * Check if this object equals another ImageryInfo with respect to the properties 644 * that get written to the preference file. 645 * 646 * The field {@link #pixelPerDegree} is ignored. 647 * 648 * @param other the ImageryInfo object to compare to 649 * @return true if they are equal 650 */ 651 public boolean equalsPref(ImageryInfo other) { 652 if (other == null) { 653 return false; 654 } 655 656 // CHECKSTYLE.OFF: BooleanExpressionComplexity 657 return 658 Objects.equals(this.name, other.name) && 659 Objects.equals(this.id, other.id) && 660 Objects.equals(this.url, other.url) && 661 Objects.equals(this.modTileFeatures, other.modTileFeatures) && 662 Objects.equals(this.bestMarked, other.bestMarked) && 663 Objects.equals(this.overlay, other.overlay) && 664 Objects.equals(this.isGeoreferenceValid, other.isGeoreferenceValid) && 665 Objects.equals(this.cookies, other.cookies) && 666 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) && 667 Objects.equals(this.imageryType, other.imageryType) && 668 Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) && 669 Objects.equals(this.defaultMinZoom, other.defaultMinZoom) && 670 Objects.equals(this.bounds, other.bounds) && 671 Objects.equals(this.serverProjections, other.serverProjections) && 672 Objects.equals(this.attributionText, other.attributionText) && 673 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) && 674 Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) && 675 Objects.equals(this.attributionImageURL, other.attributionImageURL) && 676 Objects.equals(this.attributionImage, other.attributionImage) && 677 Objects.equals(this.termsOfUseText, other.termsOfUseText) && 678 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) && 679 Objects.equals(this.countryCode, other.countryCode) && 680 Objects.equals(this.date, other.date) && 681 Objects.equals(this.icon, other.icon) && 682 Objects.equals(this.description, other.description) && 683 Objects.equals(this.noTileHeaders, other.noTileHeaders) && 684 Objects.equals(this.noTileChecksums, other.noTileChecksums) && 685 Objects.equals(this.metadataHeaders, other.metadataHeaders) && 686 Objects.equals(this.defaultLayers, other.defaultLayers) && 687 Objects.equals(this.customHttpHeaders, other.customHttpHeaders) && 688 Objects.equals(this.transparent, other.transparent) && 689 Objects.equals(this.minimumTileExpire, other.minimumTileExpire) && 690 Objects.equals(this.category, other.category); 691 // CHECKSTYLE.ON: BooleanExpressionComplexity 692 } 693 694 @Override 695 public boolean equals(Object o) { 696 if (this == o) return true; 697 if (o == null || getClass() != o.getClass()) return false; 698 ImageryInfo that = (ImageryInfo) o; 699 return imageryType == that.imageryType && Objects.equals(url, that.url); 700 } 701 702 @Override 703 public String toString() { 704 return "ImageryInfo{" + 705 "name='" + name + '\'' + 706 ", countryCode='" + countryCode + '\'' + 707 ", url='" + url + '\'' + 708 ", imageryType=" + imageryType + 709 '}'; 710 } 711 712 @Override 713 public int compareTo(ImageryInfo in) { 714 int i = countryCode.compareTo(in.countryCode); 715 if (i == 0) { 716 i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH)); 717 } 718 if (i == 0) { 719 i = url.compareTo(in.url); 720 } 721 if (i == 0) { 722 i = Double.compare(pixelPerDegree, in.pixelPerDegree); 723 } 724 return i; 725 } 726 727 /** 728 * Determines if URL is equal to given imagery info. 729 * @param in imagery info 730 * @return {@code true} if URL is equal to given imagery info 731 */ 732 public boolean equalsBaseValues(ImageryInfo in) { 733 return url.equals(in.url); 734 } 735 736 /** 737 * Sets the pixel per degree value. 738 * @param ppd The ppd value 739 * @see #getPixelPerDegree() 740 */ 741 public void setPixelPerDegree(double ppd) { 742 this.pixelPerDegree = ppd; 743 } 744 745 /** 746 * Sets the maximum zoom level. 747 * @param defaultMaxZoom The maximum zoom level 748 */ 749 public void setDefaultMaxZoom(int defaultMaxZoom) { 750 this.defaultMaxZoom = defaultMaxZoom; 751 } 752 753 /** 754 * Sets the minimum zoom level. 755 * @param defaultMinZoom The minimum zoom level 756 */ 757 public void setDefaultMinZoom(int defaultMinZoom) { 758 this.defaultMinZoom = defaultMinZoom; 759 } 760 761 /** 762 * Sets the imagery polygonial bounds. 763 * @param b The imagery bounds (non-rectangular) 764 */ 765 public void setBounds(ImageryBounds b) { 766 this.bounds = b; 767 } 768 769 /** 770 * Returns the imagery polygonial bounds. 771 * @return The imagery bounds (non-rectangular) 772 */ 773 public ImageryBounds getBounds() { 774 return bounds; 775 } 776 777 @Override 778 public boolean requiresAttribution() { 779 return attributionText != null || attributionLinkURL != null || attributionImage != null 780 || termsOfUseText != null || termsOfUseURL != null; 781 } 782 783 @Override 784 public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { 785 return attributionText; 786 } 787 788 @Override 789 public String getAttributionLinkURL() { 790 return attributionLinkURL; 791 } 792 793 /** 794 * Return the permission reference URL. 795 * @return The url 796 * @see #setPermissionReferenceURL 797 * @since 11975 798 */ 799 public String getPermissionReferenceURL() { 800 return permissionReferenceURL; 801 } 802 803 @Override 804 public Image getAttributionImage() { 805 ImageIcon i = ImageProvider.getIfAvailable(attributionImage); 806 if (i != null) { 807 return i.getImage(); 808 } 809 return null; 810 } 811 812 /** 813 * Return the raw attribution logo information (an URL to the image). 814 * @return The url text 815 * @since 12257 816 */ 817 public String getAttributionImageRaw() { 818 return attributionImage; 819 } 820 821 @Override 822 public String getAttributionImageURL() { 823 return attributionImageURL; 824 } 825 826 @Override 827 public String getTermsOfUseText() { 828 return termsOfUseText; 829 } 830 831 @Override 832 public String getTermsOfUseURL() { 833 return termsOfUseURL; 834 } 835 836 /** 837 * Set the attribution text 838 * @param text The text 839 * @see #getAttributionText(int, ICoordinate, ICoordinate) 840 */ 841 public void setAttributionText(String text) { 842 attributionText = text; 843 } 844 845 /** 846 * Set the attribution image 847 * @param url The url of the image. 848 * @see #getAttributionImageURL() 849 */ 850 public void setAttributionImageURL(String url) { 851 attributionImageURL = url; 852 } 853 854 /** 855 * Set the image for the attribution 856 * @param res The image resource 857 * @see #getAttributionImage() 858 */ 859 public void setAttributionImage(String res) { 860 attributionImage = res; 861 } 862 863 /** 864 * Sets the URL the attribution should link to. 865 * @param url The url. 866 * @see #getAttributionLinkURL() 867 */ 868 public void setAttributionLinkURL(String url) { 869 attributionLinkURL = url; 870 } 871 872 /** 873 * Sets the permission reference URL. 874 * @param url The url. 875 * @see #getPermissionReferenceURL() 876 * @since 11975 877 */ 878 public void setPermissionReferenceURL(String url) { 879 permissionReferenceURL = url; 880 } 881 882 /** 883 * Sets the text to display to the user as terms of use. 884 * @param text The text 885 * @see #getTermsOfUseText() 886 */ 887 public void setTermsOfUseText(String text) { 888 termsOfUseText = text; 889 } 890 891 /** 892 * Sets a url that links to the terms of use text. 893 * @param text The url. 894 * @see #getTermsOfUseURL() 895 */ 896 public void setTermsOfUseURL(String text) { 897 termsOfUseURL = text; 898 } 899 900 /** 901 * Sets the extended URL of this entry. 902 * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info 903 */ 904 public void setExtendedUrl(String url) { 905 CheckParameterUtil.ensureParameterNotNull(url); 906 907 // Default imagery type is WMS 908 this.url = url; 909 this.imageryType = ImageryType.WMS; 910 911 defaultMaxZoom = 0; 912 defaultMinZoom = 0; 913 for (ImageryType type : ImageryType.values()) { 914 Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url); 915 if (m.matches()) { 916 this.url = m.group(3); 917 this.imageryType = type; 918 if (m.group(2) != null) { 919 defaultMaxZoom = Integer.parseInt(m.group(2)); 920 } 921 if (m.group(1) != null) { 922 defaultMinZoom = Integer.parseInt(m.group(1)); 923 } 924 break; 925 } 926 } 927 928 if (serverProjections.isEmpty()) { 929 serverProjections = new ArrayList<>(); 930 Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)\\}.*").matcher(url.toUpperCase(Locale.ENGLISH)); 931 if (m.matches()) { 932 for (String p : m.group(1).split(",")) { 933 serverProjections.add(p); 934 } 935 } 936 } 937 } 938 939 /** 940 * Returns the entry name. 941 * @return The entry name 942 * @since 6968 943 */ 944 public String getOriginalName() { 945 return this.origName != null ? this.origName : this.name; 946 } 947 948 /** 949 * Sets the entry name and handle translation. 950 * @param language The used language 951 * @param name The entry name 952 * @since 8091 953 */ 954 public void setName(String language, String name) { 955 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 956 if (LanguageInfo.isBetterLanguage(langName, language)) { 957 this.name = isdefault ? tr(name) : name; 958 this.langName = language; 959 } 960 if (origName == null || isdefault) { 961 this.origName = name; 962 } 963 } 964 965 /** 966 * Store the id of this info to the preferences and clear it afterwards. 967 */ 968 public void clearId() { 969 if (this.id != null) { 970 Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds")); 971 newAddedIds.add(this.id); 972 Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds)); 973 } 974 setId(null); 975 } 976 977 /** 978 * Determines if this entry is enabled by default. 979 * @return {@code true} if this entry is enabled by default, {@code false} otherwise 980 */ 981 public boolean isDefaultEntry() { 982 return defaultEntry; 983 } 984 985 /** 986 * Sets the default state of this entry. 987 * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise 988 */ 989 public void setDefaultEntry(boolean defaultEntry) { 990 this.defaultEntry = defaultEntry; 991 } 992 993 /** 994 * Gets the pixel per degree value 995 * @return The ppd value. 996 */ 997 public double getPixelPerDegree() { 998 return this.pixelPerDegree; 999 } 1000 1001 /** 1002 * Returns the maximum zoom level. 1003 * @return The maximum zoom level 1004 */ 1005 @Override 1006 public int getMaxZoom() { 1007 return this.defaultMaxZoom; 1008 } 1009 1010 /** 1011 * Returns the minimum zoom level. 1012 * @return The minimum zoom level 1013 */ 1014 @Override 1015 public int getMinZoom() { 1016 return this.defaultMinZoom; 1017 } 1018 1019 /** 1020 * Returns the description text when existing. 1021 * @return The description 1022 * @since 8065 1023 */ 1024 public String getDescription() { 1025 return this.description; 1026 } 1027 1028 /** 1029 * Sets the description text when existing. 1030 * @param language The used language 1031 * @param description the imagery description text 1032 * @since 8091 1033 */ 1034 public void setDescription(String language, String description) { 1035 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 1036 if (LanguageInfo.isBetterLanguage(langDescription, language)) { 1037 this.description = isdefault ? tr(description) : description; 1038 this.langDescription = language; 1039 } 1040 } 1041 1042 /** 1043 * Return the sorted list of activated Imagery IDs. 1044 * @return sorted list of activated Imagery IDs 1045 * @since 13536 1046 */ 1047 public static Collection<String> getActiveIds() { 1048 ArrayList<String> ids = new ArrayList<>(); 1049 IPreferences pref = Config.getPref(); 1050 if (pref != null) { 1051 List<ImageryPreferenceEntry> entries = StructUtils.getListOfStructs( 1052 pref, "imagery.entries", null, ImageryPreferenceEntry.class); 1053 if (entries != null) { 1054 for (ImageryPreferenceEntry prefEntry : entries) { 1055 if (prefEntry.id != null && !prefEntry.id.isEmpty()) 1056 ids.add(prefEntry.id); 1057 } 1058 Collections.sort(ids); 1059 } 1060 } 1061 return ids; 1062 } 1063 1064 /** 1065 * Returns a tool tip text for display. 1066 * @return The text 1067 * @since 8065 1068 */ 1069 public String getToolTipText() { 1070 StringBuilder res = new StringBuilder(getName()); 1071 boolean html = false; 1072 String dateStr = getDate(); 1073 if (dateStr != null && !dateStr.isEmpty()) { 1074 res.append("<br>").append(tr("Date of imagery: {0}", dateStr)); 1075 html = true; 1076 } 1077 if (category != null && category.getDescription() != null) { 1078 res.append("<br>").append(tr("Imagery category: {0}", category.getDescription())); 1079 html = true; 1080 } 1081 if (bestMarked) { 1082 res.append("<br>").append(tr("This imagery is marked as best in this region in other editors.")); 1083 html = true; 1084 } 1085 if (overlay) { 1086 res.append("<br>").append(tr("This imagery is an overlay.")); 1087 html = true; 1088 } 1089 String desc = getDescription(); 1090 if (desc != null && !desc.isEmpty()) { 1091 res.append("<br>").append(Utils.escapeReservedCharactersHTML(desc)); 1092 html = true; 1093 } 1094 if (html) { 1095 res.insert(0, "<html>").append("</html>"); 1096 } 1097 return res.toString(); 1098 } 1099 1100 /** 1101 * Returns the EULA acceptance URL, if any. 1102 * @return The URL to an EULA text that has to be accepted before use, or {@code null} 1103 */ 1104 public String getEulaAcceptanceRequired() { 1105 return eulaAcceptanceRequired; 1106 } 1107 1108 /** 1109 * Sets the EULA acceptance URL. 1110 * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use 1111 */ 1112 public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) { 1113 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 1114 } 1115 1116 /** 1117 * Returns the ISO 3166-1-alpha-2 country code. 1118 * @return The country code (2 letters) 1119 */ 1120 public String getCountryCode() { 1121 return countryCode; 1122 } 1123 1124 /** 1125 * Sets the ISO 3166-1-alpha-2 country code. 1126 * @param countryCode The country code (2 letters) 1127 */ 1128 public void setCountryCode(String countryCode) { 1129 this.countryCode = countryCode; 1130 } 1131 1132 /** 1133 * Returns the date information. 1134 * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where 1135 * DD and MM as well as a second date are optional) 1136 * @since 11570 1137 */ 1138 public String getDate() { 1139 return date; 1140 } 1141 1142 /** 1143 * Sets the date information. 1144 * @param date The date information 1145 * @since 11570 1146 */ 1147 public void setDate(String date) { 1148 this.date = date; 1149 } 1150 1151 /** 1152 * Returns the entry icon. 1153 * @return The entry icon 1154 */ 1155 public String getIcon() { 1156 return icon; 1157 } 1158 1159 /** 1160 * Sets the entry icon. 1161 * @param icon The entry icon 1162 */ 1163 public void setIcon(String icon) { 1164 this.icon = icon; 1165 } 1166 1167 /** 1168 * Get the projections supported by the server. Only relevant for 1169 * WMS-type ImageryInfo at the moment. 1170 * @return null, if no projections have been specified; the list 1171 * of supported projections otherwise. 1172 */ 1173 public List<String> getServerProjections() { 1174 return Collections.unmodifiableList(serverProjections); 1175 } 1176 1177 /** 1178 * Sets the list of collections the server supports 1179 * @param serverProjections The list of supported projections 1180 */ 1181 public void setServerProjections(Collection<String> serverProjections) { 1182 CheckParameterUtil.ensureParameterNotNull(serverProjections, "serverProjections"); 1183 this.serverProjections = new ArrayList<>(serverProjections); 1184 } 1185 1186 /** 1187 * Returns the extended URL, containing in addition of service URL, its type and min/max zoom info. 1188 * @return The extended URL 1189 */ 1190 public String getExtendedUrl() { 1191 return imageryType.getTypeString() + (defaultMaxZoom != 0 1192 ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url; 1193 } 1194 1195 /** 1196 * Gets a unique toolbar key to store this layer as toolbar item 1197 * @return The kay. 1198 */ 1199 public String getToolbarName() { 1200 String res = name; 1201 if (pixelPerDegree != 0) { 1202 res += "#PPD="+pixelPerDegree; 1203 } 1204 return res; 1205 } 1206 1207 /** 1208 * Gets the name that should be displayed in the menu to add this imagery layer. 1209 * @return The text. 1210 */ 1211 public String getMenuName() { 1212 String res = name; 1213 if (pixelPerDegree != 0) { 1214 res += " ("+pixelPerDegree+')'; 1215 } 1216 return res; 1217 } 1218 1219 /** 1220 * Determines if this entry requires attribution. 1221 * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise 1222 */ 1223 public boolean hasAttribution() { 1224 return attributionText != null; 1225 } 1226 1227 /** 1228 * Copies attribution from another {@code ImageryInfo}. 1229 * @param i The other imagery info to get attribution from 1230 */ 1231 public void copyAttribution(ImageryInfo i) { 1232 this.attributionImage = i.attributionImage; 1233 this.attributionImageURL = i.attributionImageURL; 1234 this.attributionText = i.attributionText; 1235 this.attributionLinkURL = i.attributionLinkURL; 1236 this.termsOfUseText = i.termsOfUseText; 1237 this.termsOfUseURL = i.termsOfUseURL; 1238 } 1239 1240 /** 1241 * Applies the attribution from this object to a tile source. 1242 * @param s The tile source 1243 */ 1244 public void setAttribution(AbstractTileSource s) { 1245 if (attributionText != null) { 1246 if ("osm".equals(attributionText)) { 1247 s.setAttributionText(new Mapnik().getAttributionText(0, null, null)); 1248 } else { 1249 s.setAttributionText(attributionText); 1250 } 1251 } 1252 if (attributionLinkURL != null) { 1253 if ("osm".equals(attributionLinkURL)) { 1254 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL()); 1255 } else { 1256 s.setAttributionLinkURL(attributionLinkURL); 1257 } 1258 } 1259 if (attributionImage != null) { 1260 ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage); 1261 if (i != null) { 1262 s.setAttributionImage(i.getImage()); 1263 } 1264 } 1265 if (attributionImageURL != null) { 1266 s.setAttributionImageURL(attributionImageURL); 1267 } 1268 if (termsOfUseText != null) { 1269 s.setTermsOfUseText(termsOfUseText); 1270 } 1271 if (termsOfUseURL != null) { 1272 if ("osm".equals(termsOfUseURL)) { 1273 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL()); 1274 } else { 1275 s.setTermsOfUseURL(termsOfUseURL); 1276 } 1277 } 1278 } 1279 1280 /** 1281 * Returns the imagery type. 1282 * @return The imagery type 1283 */ 1284 public ImageryType getImageryType() { 1285 return imageryType; 1286 } 1287 1288 /** 1289 * Sets the imagery type. 1290 * @param imageryType The imagery type 1291 */ 1292 public void setImageryType(ImageryType imageryType) { 1293 this.imageryType = imageryType; 1294 } 1295 1296 /** 1297 * Returns the imagery category. 1298 * @return The imagery category 1299 * @since 13792 1300 */ 1301 public ImageryCategory getImageryCategory() { 1302 return category; 1303 } 1304 1305 /** 1306 * Sets the imagery category. 1307 * @param category The imagery category 1308 * @since 13792 1309 */ 1310 public void setImageryCategory(ImageryCategory category) { 1311 this.category = category; 1312 } 1313 1314 /** 1315 * Returns the imagery category original string (don't use except for error checks). 1316 * @return The imagery category original string 1317 * @since 13792 1318 */ 1319 public String getImageryCategoryOriginalString() { 1320 return categoryOriginalString; 1321 } 1322 1323 /** 1324 * Sets the imagery category original string (don't use except for error checks). 1325 * @param categoryOriginalString The imagery category original string 1326 * @since 13792 1327 */ 1328 public void setImageryCategoryOriginalString(String categoryOriginalString) { 1329 this.categoryOriginalString = categoryOriginalString; 1330 } 1331 1332 /** 1333 * Returns true if this layer's URL is matched by one of the regular 1334 * expressions kept by the current OsmApi instance. 1335 * @return {@code true} is this entry is blacklisted, {@code false} otherwise 1336 */ 1337 public boolean isBlacklisted() { 1338 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities(); 1339 return capabilities != null && capabilities.isOnImageryBlacklist(this.url); 1340 } 1341 1342 /** 1343 * Sets the map of <header name, header value> that if any of this header 1344 * will be returned, then this tile will be treated as "no tile at this zoom level" 1345 * 1346 * @param noTileHeaders Map of <header name, header value> which will be treated as "no tile at this zoom level" 1347 * @since 9613 1348 */ 1349 public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) { 1350 if (noTileHeaders == null || noTileHeaders.isEmpty()) { 1351 this.noTileHeaders = null; 1352 } else { 1353 this.noTileHeaders = noTileHeaders.toMap(); 1354 } 1355 } 1356 1357 @Override 1358 public Map<String, Set<String>> getNoTileHeaders() { 1359 return noTileHeaders; 1360 } 1361 1362 /** 1363 * Sets the map of <checksum type, checksum value> that if any tile with that checksum 1364 * will be returned, then this tile will be treated as "no tile at this zoom level" 1365 * 1366 * @param noTileChecksums Map of <checksum type, checksum value> which will be treated as "no tile at this zoom level" 1367 * @since 9613 1368 */ 1369 public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) { 1370 if (noTileChecksums == null || noTileChecksums.isEmpty()) { 1371 this.noTileChecksums = null; 1372 } else { 1373 this.noTileChecksums = noTileChecksums.toMap(); 1374 } 1375 } 1376 1377 @Override 1378 public Map<String, Set<String>> getNoTileChecksums() { 1379 return noTileChecksums; 1380 } 1381 1382 /** 1383 * Returns the map of <header name, metadata key> indicating, which HTTP headers should 1384 * be moved to metadata 1385 * 1386 * @param metadataHeaders map of <header name, metadata key> indicating, which HTTP headers should be moved to metadata 1387 * @since 8418 1388 */ 1389 public void setMetadataHeaders(Map<String, String> metadataHeaders) { 1390 if (metadataHeaders == null || metadataHeaders.isEmpty()) { 1391 this.metadataHeaders = null; 1392 } else { 1393 this.metadataHeaders = metadataHeaders; 1394 } 1395 } 1396 1397 /** 1398 * Gets the flag if the georeference is valid. 1399 * @return <code>true</code> if it is valid. 1400 */ 1401 public boolean isGeoreferenceValid() { 1402 return isGeoreferenceValid; 1403 } 1404 1405 /** 1406 * Sets an indicator that the georeference is valid 1407 * @param isGeoreferenceValid <code>true</code> if it is marked as valid. 1408 */ 1409 public void setGeoreferenceValid(boolean isGeoreferenceValid) { 1410 this.isGeoreferenceValid = isGeoreferenceValid; 1411 } 1412 1413 /** 1414 * Returns the status of "best" marked status in other editors. 1415 * @return <code>true</code> if it is marked as best. 1416 * @since 11575 1417 */ 1418 public boolean isBestMarked() { 1419 return bestMarked; 1420 } 1421 1422 /** 1423 * Returns the overlay indication. 1424 * @return <code>true</code> if it is an overlay. 1425 * @since 13536 1426 */ 1427 public boolean isOverlay() { 1428 return overlay; 1429 } 1430 1431 /** 1432 * Sets an indicator that in other editors it is marked as best imagery 1433 * @param bestMarked <code>true</code> if it is marked as best in other editors. 1434 * @since 11575 1435 */ 1436 public void setBestMarked(boolean bestMarked) { 1437 this.bestMarked = bestMarked; 1438 } 1439 1440 /** 1441 * Sets overlay indication 1442 * @param overlay <code>true</code> if it is an overlay. 1443 * @since 13536 1444 */ 1445 public void setOverlay(boolean overlay) { 1446 this.overlay = overlay; 1447 } 1448 1449 /** 1450 * Adds an old Id. 1451 * 1452 * @param id the Id to be added 1453 * @since 13536 1454 */ 1455 public void addOldId(String id) { 1456 if (oldIds == null) { 1457 oldIds = new ArrayList<>(); 1458 } 1459 oldIds.add(id); 1460 } 1461 1462 /** 1463 * Get old Ids. 1464 * 1465 * @return collection of ids 1466 * @since 13536 1467 */ 1468 public Collection<String> getOldIds() { 1469 return oldIds; 1470 } 1471 1472 /** 1473 * Adds a mirror entry. Mirror entries are completed with the data from the master entry 1474 * and only describe another method to access identical data. 1475 * 1476 * @param entry the mirror to be added 1477 * @since 9658 1478 */ 1479 public void addMirror(ImageryInfo entry) { 1480 if (mirrors == null) { 1481 mirrors = new ArrayList<>(); 1482 } 1483 mirrors.add(entry); 1484 } 1485 1486 /** 1487 * Returns the mirror entries. Entries are completed with master entry data. 1488 * 1489 * @return the list of mirrors 1490 * @since 9658 1491 */ 1492 public List<ImageryInfo> getMirrors() { 1493 List<ImageryInfo> l = new ArrayList<>(); 1494 if (mirrors != null) { 1495 int num = 1; 1496 for (ImageryInfo i : mirrors) { 1497 ImageryInfo n = new ImageryInfo(this); 1498 if (i.defaultMaxZoom != 0) { 1499 n.defaultMaxZoom = i.defaultMaxZoom; 1500 } 1501 if (i.defaultMinZoom != 0) { 1502 n.defaultMinZoom = i.defaultMinZoom; 1503 } 1504 n.setServerProjections(i.getServerProjections()); 1505 n.url = i.url; 1506 n.imageryType = i.imageryType; 1507 if (i.getTileSize() != 0) { 1508 n.setTileSize(i.getTileSize()); 1509 } 1510 if (n.id != null) { 1511 n.id = n.id + "_mirror"+num; 1512 } 1513 if (num > 1) { 1514 n.name = tr("{0} mirror server {1}", n.name, num); 1515 if (n.origName != null) { 1516 n.origName += " mirror server " + num; 1517 } 1518 } else { 1519 n.name = tr("{0} mirror server", n.name); 1520 if (n.origName != null) { 1521 n.origName += " mirror server"; 1522 } 1523 } 1524 l.add(n); 1525 ++num; 1526 } 1527 } 1528 return l; 1529 } 1530 1531 /** 1532 * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider) 1533 * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer 1534 * to work on 1535 * @return Collection of the layer names 1536 */ 1537 public List<DefaultLayer> getDefaultLayers() { 1538 return defaultLayers; 1539 } 1540 1541 /** 1542 * Sets the default layers that user will work with 1543 * @param layers set the list of default layers 1544 */ 1545 public void setDefaultLayers(List<DefaultLayer> layers) { 1546 this.defaultLayers = layers; 1547 } 1548 1549 /** 1550 * Returns custom HTTP headers that should be sent with request towards imagery provider 1551 * @return headers 1552 */ 1553 public Map<String, String> getCustomHttpHeaders() { 1554 if (customHttpHeaders == null) { 1555 return Collections.emptyMap(); 1556 } 1557 return customHttpHeaders; 1558 } 1559 1560 /** 1561 * Sets custom HTTP headers that should be sent with request towards imagery provider 1562 * @param customHttpHeaders http headers 1563 */ 1564 public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) { 1565 this.customHttpHeaders = customHttpHeaders; 1566 } 1567 1568 /** 1569 * Determines if this imagery should be transparent. 1570 * @return should this imagery be transparent 1571 */ 1572 public boolean isTransparent() { 1573 return transparent; 1574 } 1575 1576 /** 1577 * Sets whether imagery should be transparent. 1578 * @param transparent set to true if imagery should be transparent 1579 */ 1580 public void setTransparent(boolean transparent) { 1581 this.transparent = transparent; 1582 } 1583 1584 /** 1585 * Returns minimum tile expiration in seconds. 1586 * @return minimum tile expiration in seconds 1587 */ 1588 public int getMinimumTileExpire() { 1589 return minimumTileExpire; 1590 } 1591 1592 /** 1593 * Sets minimum tile expiration in seconds. 1594 * @param minimumTileExpire minimum tile expiration in seconds 1595 */ 1596 public void setMinimumTileExpire(int minimumTileExpire) { 1597 this.minimumTileExpire = minimumTileExpire; 1598 } 1599 1600 /** 1601 * Get a string representation of this imagery info suitable for the {@code source} changeset tag. 1602 * @return English name, if known 1603 * @since 13890 1604 */ 1605 public String getSourceName() { 1606 if (ImageryType.BING == getImageryType()) { 1607 return "Bing"; 1608 } else { 1609 if (id != null) { 1610 // Retrieve english name, unfortunately not saved in preferences 1611 Optional<ImageryInfo> infoEn = ImageryLayerInfo.allDefaultLayers.stream().filter(x -> id.equals(x.getId())).findAny(); 1612 if (infoEn.isPresent()) { 1613 return infoEn.get().getOriginalName(); 1614 } 1615 } 1616 return getOriginalName(); 1617 } 1618 } 1619}