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.util.ArrayList; 008import java.util.Arrays; 009import java.util.Collection; 010import java.util.Collections; 011import java.util.List; 012import java.util.Locale; 013import java.util.Map; 014import java.util.Objects; 015import java.util.TreeSet; 016import java.util.regex.Matcher; 017import java.util.regex.Pattern; 018 019import javax.swing.ImageIcon; 020 021import org.openstreetmap.gui.jmapviewer.OsmMercator; 022import org.openstreetmap.gui.jmapviewer.interfaces.Attributed; 023import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 024import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource; 025import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik; 026import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo; 027import org.openstreetmap.josm.Main; 028import org.openstreetmap.josm.data.Bounds; 029import org.openstreetmap.josm.data.Preferences.pref; 030import org.openstreetmap.josm.io.Capabilities; 031import org.openstreetmap.josm.io.OsmApi; 032import org.openstreetmap.josm.tools.CheckParameterUtil; 033import org.openstreetmap.josm.tools.ImageProvider; 034import org.openstreetmap.josm.tools.LanguageInfo; 035 036/** 037 * Class that stores info about an image background layer. 038 * 039 * @author Frederik Ramm 040 */ 041public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed { 042 043 /** 044 * Type of imagery entry. 045 */ 046 public enum ImageryType { 047 /** A WMS (Web Map Service) entry. **/ 048 WMS("wms"), 049 /** A TMS (Tile Map Service) entry. **/ 050 TMS("tms"), 051 /** An HTML proxy (previously used for Yahoo imagery) entry. **/ 052 HTML("html"), 053 /** TMS entry for Microsoft Bing. */ 054 BING("bing"), 055 /** TMS entry for Russian company <a href="https://wiki.openstreetmap.org/wiki/WikiProject_Russia/kosmosnimki">ScanEx</a>. **/ 056 SCANEX("scanex"), 057 /** A WMS endpoint entry only stores the WMS server info, without layer, which are chosen later by the user. **/ 058 WMS_ENDPOINT("wms_endpoint"), 059 /** WMTS stores GetCapabilities URL. Does not store any information about the layer **/ 060 WMTS("wmts"); 061 062 063 private final String typeString; 064 065 ImageryType(String urlString) { 066 this.typeString = urlString; 067 } 068 069 /** 070 * Returns the unique string identifying this type. 071 * @return the unique string identifying this type 072 * @since 6690 073 */ 074 public final String getTypeString() { 075 return typeString; 076 } 077 078 /** 079 * Returns the imagery type from the given type string. 080 * @param s The type string 081 * @return the imagery type matching the given type string 082 */ 083 public static ImageryType fromString(String s) { 084 for (ImageryType type : ImageryType.values()) { 085 if (type.getTypeString().equals(s)) { 086 return type; 087 } 088 } 089 return null; 090 } 091 } 092 093 /** 094 * Multi-polygon bounds for imagery backgrounds. 095 * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location. 096 */ 097 public static class ImageryBounds extends Bounds { 098 099 /** 100 * Constructs a new {@code ImageryBounds} from string. 101 * @param asString The string containing the list of shapes defining this bounds 102 * @param separator The shape separator in the given string, usually a comma 103 */ 104 public ImageryBounds(String asString, String separator) { 105 super(asString, separator); 106 } 107 108 private List<Shape> shapes = new ArrayList<>(); 109 110 /** 111 * Adds a new shape to this bounds. 112 * @param shape The shape to add 113 */ 114 public final void addShape(Shape shape) { 115 this.shapes.add(shape); 116 } 117 118 /** 119 * Sets the list of shapes defining this bounds. 120 * @param shapes The list of shapes defining this bounds. 121 */ 122 public final void setShapes(List<Shape> shapes) { 123 this.shapes = shapes; 124 } 125 126 /** 127 * Returns the list of shapes defining this bounds. 128 * @return The list of shapes defining this bounds 129 */ 130 public final List<Shape> getShapes() { 131 return shapes; 132 } 133 134 @Override 135 public int hashCode() { 136 final int prime = 31; 137 int result = super.hashCode(); 138 result = prime * result + ((shapes == null) ? 0 : shapes.hashCode()); 139 return result; 140 } 141 142 @Override 143 public boolean equals(Object obj) { 144 if (this == obj) 145 return true; 146 if (!super.equals(obj)) 147 return false; 148 if (getClass() != obj.getClass()) 149 return false; 150 ImageryBounds other = (ImageryBounds) obj; 151 if (shapes == null) { 152 if (other.shapes != null) 153 return false; 154 } else if (!shapes.equals(other.shapes)) 155 return false; 156 return true; 157 } 158 } 159 160 161 /** original name of the imagery entry in case of translation call, for multiple languages English when possible */ 162 private String origName; 163 /** (original) language of the translated name entry */ 164 private String langName; 165 /** whether this is a entry activated by default or not */ 166 private boolean defaultEntry; 167 /** The data part of HTTP cookies header in case the service requires cookies to work */ 168 private String cookies; 169 /** Whether this service requires a explicit EULA acceptance before it can be activated */ 170 private String eulaAcceptanceRequired; 171 /** type of the imagery servics - WMS, TMS, ... */ 172 private ImageryType imageryType = ImageryType.WMS; 173 private double pixelPerDegree; 174 /** maximum zoom level for TMS imagery */ 175 private int defaultMaxZoom; 176 /** minimum zoom level for TMS imagery */ 177 private int defaultMinZoom; 178 /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */ 179 private ImageryBounds bounds; 180 /** projections supported by WMS servers */ 181 private List<String> serverProjections; 182 /** description of the imagery entry, should contain notes what type of data it is */ 183 private String description; 184 /** language of the description entry */ 185 private String langDescription; 186 /** Text of a text attribution displayed when using the imagery */ 187 private String attributionText; 188 /** Link behing the text attribution displayed when using the imagery */ 189 private String attributionLinkURL; 190 /** Image of a graphical attribution displayed when using the imagery */ 191 private String attributionImage; 192 /** Link behind the graphical attribution displayed when using the imagery */ 193 private String attributionImageURL; 194 /** Text with usage terms displayed when using the imagery */ 195 private String termsOfUseText; 196 /** Link behind the text with usage terms displayed when using the imagery */ 197 private String termsOfUseURL; 198 /** country code of the imagery (for country specific imagery) */ 199 private String countryCode = ""; 200 /** icon used in menu */ 201 private String icon; 202 private boolean isGeoreferenceValid = false; 203 private boolean isEpsg4326To3857Supported = false; 204 // when adding a field, also adapt the ImageryInfo(ImageryInfo) 205 // and ImageryInfo(ImageryPreferenceEntry) constructor, equals method, and ImageryPreferenceEntry 206 207 /** 208 * Auxiliary class to save an {@link ImageryInfo} object in the preferences. 209 */ 210 public static class ImageryPreferenceEntry { 211 @pref String name; 212 @pref String id; 213 @pref String type; 214 @pref String url; 215 @pref double pixel_per_eastnorth; 216 @pref String eula; 217 @pref String attribution_text; 218 @pref String attribution_url; 219 @pref String logo_image; 220 @pref String logo_url; 221 @pref String terms_of_use_text; 222 @pref String terms_of_use_url; 223 @pref String country_code = ""; 224 @pref int max_zoom; 225 @pref int min_zoom; 226 @pref String cookies; 227 @pref String bounds; 228 @pref String shapes; 229 @pref String projections; 230 @pref String icon; 231 @pref String description; 232 @pref Map<String, String> noTileHeaders; 233 @pref int tileSize = OsmMercator.DEFAUL_TILE_SIZE; 234 @pref Map<String, String> metadataHeaders; 235 @pref boolean valid_georeference; 236 @pref boolean supports_epsg_4326_to_3857_conversion; 237 238 /** 239 * Constructs a new empty WMS {@code ImageryPreferenceEntry}. 240 */ 241 public ImageryPreferenceEntry() { 242 } 243 244 /** 245 * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}. 246 * @param i The corresponding imagery info 247 */ 248 public ImageryPreferenceEntry(ImageryInfo i) { 249 name = i.name; 250 id = i.id; 251 type = i.imageryType.getTypeString(); 252 url = i.url; 253 pixel_per_eastnorth = i.pixelPerDegree; 254 eula = i.eulaAcceptanceRequired; 255 attribution_text = i.attributionText; 256 attribution_url = i.attributionLinkURL; 257 logo_image = i.attributionImage; 258 logo_url = i.attributionImageURL; 259 terms_of_use_text = i.termsOfUseText; 260 terms_of_use_url = i.termsOfUseURL; 261 country_code = i.countryCode; 262 max_zoom = i.defaultMaxZoom; 263 min_zoom = i.defaultMinZoom; 264 cookies = i.cookies; 265 icon = i.icon; 266 description = i.description; 267 if (i.bounds != null) { 268 bounds = i.bounds.encodeAsString(","); 269 StringBuilder shapesString = new StringBuilder(); 270 for (Shape s : i.bounds.getShapes()) { 271 if (shapesString.length() > 0) { 272 shapesString.append(';'); 273 } 274 shapesString.append(s.encodeAsString(",")); 275 } 276 if (shapesString.length() > 0) { 277 shapes = shapesString.toString(); 278 } 279 } 280 if (i.serverProjections != null && !i.serverProjections.isEmpty()) { 281 StringBuilder val = new StringBuilder(); 282 for (String p : i.serverProjections) { 283 if (val.length() > 0) { 284 val.append(','); 285 } 286 val.append(p); 287 } 288 projections = val.toString(); 289 } 290 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) { 291 noTileHeaders = i.noTileHeaders; 292 } 293 294 if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) { 295 metadataHeaders = i.metadataHeaders; 296 } 297 298 tileSize = i.getTileSize(); 299 300 valid_georeference = i.isGeoreferenceValid(); 301 supports_epsg_4326_to_3857_conversion = i.isEpsg4326To3857Supported(); 302 303 } 304 305 @Override 306 public String toString() { 307 StringBuilder s = new StringBuilder("ImageryPreferenceEntry [name=").append(name); 308 if (id != null) { 309 s.append(" id=").append(id); 310 } 311 s.append(']'); 312 return s.toString(); 313 } 314 } 315 316 /** 317 * Constructs a new WMS {@code ImageryInfo}. 318 */ 319 public ImageryInfo() { 320 super(); 321 } 322 323 /** 324 * Constructs a new WMS {@code ImageryInfo} with a given name. 325 * @param name The entry name 326 */ 327 public ImageryInfo(String name) { 328 super(name); 329 } 330 331 /** 332 * Constructs a new WMS {@code ImageryInfo} with given name and extended URL. 333 * @param name The entry name 334 * @param url The entry extended URL 335 */ 336 public ImageryInfo(String name, String url) { 337 this(name); 338 setExtendedUrl(url); 339 } 340 341 /** 342 * Constructs a new WMS {@code ImageryInfo} with given name, extended and EULA URLs. 343 * @param name The entry name 344 * @param url The entry URL 345 * @param eulaAcceptanceRequired The EULA URL 346 */ 347 public ImageryInfo(String name, String url, String eulaAcceptanceRequired) { 348 this(name); 349 setExtendedUrl(url); 350 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 351 } 352 353 /** 354 * Constructs a new {@code ImageryInfo} with given name, url, extended and EULA URLs. 355 * @param name The entry name 356 * @param url The entry URL 357 * @param type The entry imagery type. If null, WMS will be used as default 358 * @param eulaAcceptanceRequired The EULA URL 359 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work 360 * @throws IllegalArgumentException if type refers to an unknown imagery type 361 */ 362 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) { 363 this(name); 364 setExtendedUrl(url); 365 ImageryType t = ImageryType.fromString(type); 366 this.cookies = cookies; 367 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 368 if (t != null) { 369 this.imageryType = t; 370 } else if (type != null && !type.trim().isEmpty()) { 371 throw new IllegalArgumentException("unknown type: "+type); 372 } 373 } 374 375 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies, String id) { 376 this(name, url, type, eulaAcceptanceRequired, cookies); 377 setId(id); 378 } 379 380 /** 381 * Constructs a new {@code ImageryInfo} from an imagery preference entry. 382 * @param e The imagery preference entry 383 */ 384 public ImageryInfo(ImageryPreferenceEntry e) { 385 super(e.name, e.url, e.id); 386 CheckParameterUtil.ensureParameterNotNull(e.name, "name"); 387 CheckParameterUtil.ensureParameterNotNull(e.url, "url"); 388 description = e.description; 389 cookies = e.cookies; 390 eulaAcceptanceRequired = e.eula; 391 imageryType = ImageryType.fromString(e.type); 392 if (imageryType == null) throw new IllegalArgumentException("unknown type"); 393 pixelPerDegree = e.pixel_per_eastnorth; 394 defaultMaxZoom = e.max_zoom; 395 defaultMinZoom = e.min_zoom; 396 if (e.bounds != null) { 397 bounds = new ImageryBounds(e.bounds, ","); 398 if (e.shapes != null) { 399 try { 400 for (String s : e.shapes.split(";")) { 401 bounds.addShape(new Shape(s, ",")); 402 } 403 } catch (IllegalArgumentException ex) { 404 Main.warn(ex); 405 } 406 } 407 } 408 if (e.projections != null) { 409 serverProjections = Arrays.asList(e.projections.split(",")); 410 } 411 attributionText = e.attribution_text; 412 attributionLinkURL = e.attribution_url; 413 attributionImage = e.logo_image; 414 attributionImageURL = e.logo_url; 415 termsOfUseText = e.terms_of_use_text; 416 termsOfUseURL = e.terms_of_use_url; 417 countryCode = e.country_code; 418 icon = e.icon; 419 if (e.noTileHeaders != null) { 420 noTileHeaders = e.noTileHeaders; 421 } 422 setTileSize(e.tileSize); 423 metadataHeaders = e.metadataHeaders; 424 isEpsg4326To3857Supported = e.supports_epsg_4326_to_3857_conversion; 425 isGeoreferenceValid = e.valid_georeference; 426 } 427 428 /** 429 * Constructs a new {@code ImageryInfo} from an existing one. 430 * @param i The other imagery info 431 */ 432 public ImageryInfo(ImageryInfo i) { 433 super(i.name, i.url, i.id); 434 this.defaultEntry = i.defaultEntry; 435 this.cookies = i.cookies; 436 this.eulaAcceptanceRequired = null; 437 this.imageryType = i.imageryType; 438 this.pixelPerDegree = i.pixelPerDegree; 439 this.defaultMaxZoom = i.defaultMaxZoom; 440 this.defaultMinZoom = i.defaultMinZoom; 441 this.bounds = i.bounds; 442 this.serverProjections = i.serverProjections; 443 this.attributionText = i.attributionText; 444 this.attributionLinkURL = i.attributionLinkURL; 445 this.attributionImage = i.attributionImage; 446 this.attributionImageURL = i.attributionImageURL; 447 this.termsOfUseText = i.termsOfUseText; 448 this.termsOfUseURL = i.termsOfUseURL; 449 this.countryCode = i.countryCode; 450 this.icon = i.icon; 451 this.description = i.description; 452 this.noTileHeaders = i.noTileHeaders; 453 this.metadataHeaders = i.metadataHeaders; 454 this.isEpsg4326To3857Supported = i.isEpsg4326To3857Supported; 455 this.isGeoreferenceValid = i.isGeoreferenceValid; 456 } 457 458 @Override 459 public boolean equals(Object o) { 460 if (this == o) return true; 461 if (o == null || getClass() != o.getClass()) return false; 462 463 ImageryInfo that = (ImageryInfo) o; 464 465 if (imageryType != that.imageryType) return false; 466 if (url != null ? !url.equals(that.url) : that.url != null) return false; 467 if (name != null ? !name.equals(that.name) : that.name != null) return false; 468 469 return true; 470 } 471 472 /** 473 * Check if this object equals another ImageryInfo with respect to the properties 474 * that get written to the preference file. 475 * 476 * The field {@link #pixelPerDegree} is ignored. 477 * 478 * @param other the ImageryInfo object to compare to 479 * @return true if they are equal 480 */ 481 public boolean equalsPref(ImageryInfo other) { 482 if (other == null) { 483 return false; 484 } 485 486 return 487 Objects.equals(this.name, other.name) && 488 Objects.equals(this.id, other.id) && 489 Objects.equals(this.url, other.url) && 490 Objects.equals(this.cookies, other.cookies) && 491 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) && 492 Objects.equals(this.imageryType, other.imageryType) && 493 Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) && 494 Objects.equals(this.defaultMinZoom, other.defaultMinZoom) && 495 Objects.equals(this.bounds, other.bounds) && 496 Objects.equals(this.serverProjections, other.serverProjections) && 497 Objects.equals(this.attributionText, other.attributionText) && 498 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) && 499 Objects.equals(this.attributionImageURL, other.attributionImageURL) && 500 Objects.equals(this.attributionImage, other.attributionImage) && 501 Objects.equals(this.termsOfUseText, other.termsOfUseText) && 502 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) && 503 Objects.equals(this.countryCode, other.countryCode) && 504 Objects.equals(this.icon, other.icon) && 505 Objects.equals(this.description, other.description) && 506 Objects.equals(this.noTileHeaders, other.noTileHeaders) && 507 Objects.equals(this.metadataHeaders, other.metadataHeaders); 508 } 509 510 @Override 511 public int hashCode() { 512 int result = url != null ? url.hashCode() : 0; 513 result = 31 * result + (imageryType != null ? imageryType.hashCode() : 0); 514 return result; 515 } 516 517 @Override 518 public String toString() { 519 return "ImageryInfo{" + 520 "name='" + name + '\'' + 521 ", countryCode='" + countryCode + '\'' + 522 ", url='" + url + '\'' + 523 ", imageryType=" + imageryType + 524 '}'; 525 } 526 527 @Override 528 public int compareTo(ImageryInfo in) { 529 int i = countryCode.compareTo(in.countryCode); 530 if (i == 0) { 531 i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH)); 532 } 533 if (i == 0) { 534 i = url.compareTo(in.url); 535 } 536 if (i == 0) { 537 i = Double.compare(pixelPerDegree, in.pixelPerDegree); 538 } 539 return i; 540 } 541 542 public boolean equalsBaseValues(ImageryInfo in) { 543 return url.equals(in.url); 544 } 545 546 public void setPixelPerDegree(double ppd) { 547 this.pixelPerDegree = ppd; 548 } 549 550 /** 551 * Sets the maximum zoom level. 552 * @param defaultMaxZoom The maximum zoom level 553 */ 554 public void setDefaultMaxZoom(int defaultMaxZoom) { 555 this.defaultMaxZoom = defaultMaxZoom; 556 } 557 558 /** 559 * Sets the minimum zoom level. 560 * @param defaultMinZoom The minimum zoom level 561 */ 562 public void setDefaultMinZoom(int defaultMinZoom) { 563 this.defaultMinZoom = defaultMinZoom; 564 } 565 566 /** 567 * Sets the imagery polygonial bounds. 568 * @param b The imagery bounds (non-rectangular) 569 */ 570 public void setBounds(ImageryBounds b) { 571 this.bounds = b; 572 } 573 574 /** 575 * Returns the imagery polygonial bounds. 576 * @return The imagery bounds (non-rectangular) 577 */ 578 public ImageryBounds getBounds() { 579 return bounds; 580 } 581 582 @Override 583 public boolean requiresAttribution() { 584 return attributionText != null || attributionImage != null || termsOfUseText != null || termsOfUseURL != null; 585 } 586 587 @Override 588 public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { 589 return attributionText; 590 } 591 592 @Override 593 public String getAttributionLinkURL() { 594 return attributionLinkURL; 595 } 596 597 @Override 598 public Image getAttributionImage() { 599 ImageIcon i = ImageProvider.getIfAvailable(attributionImage); 600 if (i != null) { 601 return i.getImage(); 602 } 603 return null; 604 } 605 606 @Override 607 public String getAttributionImageURL() { 608 return attributionImageURL; 609 } 610 611 @Override 612 public String getTermsOfUseText() { 613 return termsOfUseText; 614 } 615 616 @Override 617 public String getTermsOfUseURL() { 618 return termsOfUseURL; 619 } 620 621 public void setAttributionText(String text) { 622 attributionText = text; 623 } 624 625 public void setAttributionImageURL(String text) { 626 attributionImageURL = text; 627 } 628 629 public void setAttributionImage(String text) { 630 attributionImage = text; 631 } 632 633 public void setAttributionLinkURL(String text) { 634 attributionLinkURL = text; 635 } 636 637 public void setTermsOfUseText(String text) { 638 termsOfUseText = text; 639 } 640 641 public void setTermsOfUseURL(String text) { 642 termsOfUseURL = text; 643 } 644 645 /** 646 * Sets the extended URL of this entry. 647 * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info 648 */ 649 public void setExtendedUrl(String url) { 650 CheckParameterUtil.ensureParameterNotNull(url); 651 652 // Default imagery type is WMS 653 this.url = url; 654 this.imageryType = ImageryType.WMS; 655 656 defaultMaxZoom = 0; 657 defaultMinZoom = 0; 658 for (ImageryType type : ImageryType.values()) { 659 Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+),)?(\\d+)\\])?:(.*)").matcher(url); 660 if (m.matches()) { 661 this.url = m.group(3); 662 this.imageryType = type; 663 if (m.group(2) != null) { 664 defaultMaxZoom = Integer.parseInt(m.group(2)); 665 } 666 if (m.group(1) != null) { 667 defaultMinZoom = Integer.parseInt(m.group(1)); 668 } 669 break; 670 } 671 } 672 673 if (serverProjections == null || serverProjections.isEmpty()) { 674 try { 675 serverProjections = new ArrayList<>(); 676 Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)\\}.*").matcher(url.toUpperCase(Locale.ENGLISH)); 677 if (m.matches()) { 678 for (String p : m.group(1).split(",")) { 679 serverProjections.add(p); 680 } 681 } 682 } catch (Exception e) { 683 Main.warn(e); 684 } 685 } 686 } 687 688 /** 689 * Returns the entry name. 690 * @return The entry name 691 * @since 6968 692 */ 693 public String getOriginalName() { 694 return this.origName != null ? this.origName : this.name; 695 } 696 697 /** 698 * Sets the entry name and handle translation. 699 * @param language The used language 700 * @param name The entry name 701 * @since 8091 702 */ 703 public void setName(String language, String name) { 704 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 705 if (LanguageInfo.isBetterLanguage(langName, language)) { 706 this.name = isdefault ? tr(name) : name; 707 this.langName = language; 708 } 709 if (origName == null || isdefault) { 710 this.origName = name; 711 } 712 } 713 714 public void clearId() { 715 if (this.id != null) { 716 Collection<String> newAddedIds = new TreeSet<>(Main.pref.getCollection("imagery.layers.addedIds")); 717 newAddedIds.add(this.id); 718 Main.pref.putCollection("imagery.layers.addedIds", newAddedIds); 719 } 720 setId(null); 721 } 722 723 /** 724 * Determines if this entry is enabled by default. 725 * @return {@code true} if this entry is enabled by default, {@code false} otherwise 726 */ 727 public boolean isDefaultEntry() { 728 return defaultEntry; 729 } 730 731 /** 732 * Sets the default state of this entry. 733 * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise 734 */ 735 public void setDefaultEntry(boolean defaultEntry) { 736 this.defaultEntry = defaultEntry; 737 } 738 739 /** 740 * Return the data part of HTTP cookies header in case the service requires cookies to work 741 * @return the cookie data part 742 */ 743 @Override 744 public String getCookies() { 745 return this.cookies; 746 } 747 748 public double getPixelPerDegree() { 749 return this.pixelPerDegree; 750 } 751 752 /** 753 * Returns the maximum zoom level. 754 * @return The maximum zoom level 755 */ 756 @Override 757 public int getMaxZoom() { 758 return this.defaultMaxZoom; 759 } 760 761 /** 762 * Returns the minimum zoom level. 763 * @return The minimum zoom level 764 */ 765 @Override 766 public int getMinZoom() { 767 return this.defaultMinZoom; 768 } 769 770 /** 771 * Returns the description text when existing. 772 * @return The description 773 * @since 8065 774 */ 775 public String getDescription() { 776 return this.description; 777 } 778 779 /** 780 * Sets the description text when existing. 781 * @param language The used language 782 * @param description the imagery description text 783 * @since 8091 784 */ 785 public void setDescription(String language, String description) { 786 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 787 if (LanguageInfo.isBetterLanguage(langDescription, language)) { 788 this.description = isdefault ? tr(description) : description; 789 this.langDescription = language; 790 } 791 } 792 793 /** 794 * Returns a tool tip text for display. 795 * @return The text 796 * @since 8065 797 */ 798 public String getToolTipText() { 799 String desc = getDescription(); 800 if (desc != null && !desc.isEmpty()) { 801 return "<html>" + getName() + "<br>" + desc + "</html>"; 802 } 803 return getName(); 804 } 805 806 /** 807 * Returns the EULA acceptance URL, if any. 808 * @return The URL to an EULA text that has to be accepted before use, or {@code null} 809 */ 810 public String getEulaAcceptanceRequired() { 811 return eulaAcceptanceRequired; 812 } 813 814 /** 815 * Sets the EULA acceptance URL. 816 * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use 817 */ 818 public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) { 819 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 820 } 821 822 /** 823 * Returns the ISO 3166-1-alpha-2 country code. 824 * @return The country code (2 letters) 825 */ 826 public String getCountryCode() { 827 return countryCode; 828 } 829 830 /** 831 * Sets the ISO 3166-1-alpha-2 country code. 832 * @param countryCode The country code (2 letters) 833 */ 834 public void setCountryCode(String countryCode) { 835 this.countryCode = countryCode; 836 } 837 838 /** 839 * Returns the entry icon. 840 * @return The entry icon 841 */ 842 public String getIcon() { 843 return icon; 844 } 845 846 /** 847 * Sets the entry icon. 848 * @param icon The entry icon 849 */ 850 public void setIcon(String icon) { 851 this.icon = icon; 852 } 853 854 /** 855 * Get the projections supported by the server. Only relevant for 856 * WMS-type ImageryInfo at the moment. 857 * @return null, if no projections have been specified; the list 858 * of supported projections otherwise. 859 */ 860 public List<String> getServerProjections() { 861 if (serverProjections == null) 862 return Collections.emptyList(); 863 return Collections.unmodifiableList(serverProjections); 864 } 865 866 public void setServerProjections(Collection<String> serverProjections) { 867 this.serverProjections = new ArrayList<>(serverProjections); 868 } 869 870 /** 871 * Returns the extended URL, containing in addition of service URL, its type and min/max zoom info. 872 * @return The extended URL 873 */ 874 public String getExtendedUrl() { 875 return imageryType.getTypeString() + (defaultMaxZoom != 0 876 ? "["+(defaultMinZoom != 0 ? Integer.toString(defaultMinZoom) + ',' : "")+defaultMaxZoom+"]" : "") + ':' + url; 877 } 878 879 public String getToolbarName() { 880 String res = name; 881 if (pixelPerDegree != 0) { 882 res += "#PPD="+pixelPerDegree; 883 } 884 return res; 885 } 886 887 public String getMenuName() { 888 String res = name; 889 if (pixelPerDegree != 0) { 890 res += " ("+pixelPerDegree+')'; 891 } 892 return res; 893 } 894 895 /** 896 * Determines if this entry requires attribution. 897 * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise 898 */ 899 public boolean hasAttribution() { 900 return attributionText != null; 901 } 902 903 /** 904 * Copies attribution from another {@code ImageryInfo}. 905 * @param i The other imagery info to get attribution from 906 */ 907 public void copyAttribution(ImageryInfo i) { 908 this.attributionImage = i.attributionImage; 909 this.attributionImageURL = i.attributionImageURL; 910 this.attributionText = i.attributionText; 911 this.attributionLinkURL = i.attributionLinkURL; 912 this.termsOfUseText = i.termsOfUseText; 913 this.termsOfUseURL = i.termsOfUseURL; 914 } 915 916 /** 917 * Applies the attribution from this object to a tile source. 918 * @param s The tile source 919 */ 920 public void setAttribution(AbstractTileSource s) { 921 if (attributionText != null) { 922 if ("osm".equals(attributionText)) { 923 s.setAttributionText(new Mapnik().getAttributionText(0, null, null)); 924 } else { 925 s.setAttributionText(attributionText); 926 } 927 } 928 if (attributionLinkURL != null) { 929 if ("osm".equals(attributionLinkURL)) { 930 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL()); 931 } else { 932 s.setAttributionLinkURL(attributionLinkURL); 933 } 934 } 935 if (attributionImage != null) { 936 ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage); 937 if (i != null) { 938 s.setAttributionImage(i.getImage()); 939 } 940 } 941 if (attributionImageURL != null) { 942 s.setAttributionImageURL(attributionImageURL); 943 } 944 if (termsOfUseText != null) { 945 s.setTermsOfUseText(termsOfUseText); 946 } 947 if (termsOfUseURL != null) { 948 if ("osm".equals(termsOfUseURL)) { 949 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL()); 950 } else { 951 s.setTermsOfUseURL(termsOfUseURL); 952 } 953 } 954 } 955 956 /** 957 * Returns the imagery type. 958 * @return The imagery type 959 */ 960 public ImageryType getImageryType() { 961 return imageryType; 962 } 963 964 /** 965 * Sets the imagery type. 966 * @param imageryType The imagery type 967 */ 968 public void setImageryType(ImageryType imageryType) { 969 this.imageryType = imageryType; 970 } 971 972 /** 973 * Returns true if this layer's URL is matched by one of the regular 974 * expressions kept by the current OsmApi instance. 975 * @return {@code true} is this entry is blacklisted, {@code false} otherwise 976 */ 977 public boolean isBlacklisted() { 978 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities(); 979 return capabilities != null && capabilities.isOnImageryBlacklist(this.url); 980 } 981 982 /** 983 * Sets the map of <header name, header value> that if any of this header 984 * will be returned, then this tile will be treated as "no tile at this zoom level" 985 * 986 * @param noTileHeaders Map of <header name, header value> which will be treated as "no tile at this zoom level" 987 * @since 8344 988 */ 989 public void setNoTileHeaders(Map<String, String> noTileHeaders) { 990 this.noTileHeaders = noTileHeaders; 991 } 992 993 @Override 994 public Map<String, String> getNoTileHeaders() { 995 return noTileHeaders; 996 } 997 998 /** 999 * Returns the map of <header name, metadata key> indicating, which HTTP headers should 1000 * be moved to metadata 1001 * 1002 * @param metadataHeaders map of <header name, metadata key> indicating, which HTTP headers should be moved to metadata 1003 * @since 8418 1004 */ 1005 public void setMetadataHeaders(Map<String, String> metadataHeaders) { 1006 this.metadataHeaders = metadataHeaders; 1007 } 1008 1009 public boolean isEpsg4326To3857Supported() { 1010 return isEpsg4326To3857Supported; 1011 } 1012 1013 public void setEpsg4326To3857Supported(boolean isEpsg4326To3857Supported) { 1014 this.isEpsg4326To3857Supported = isEpsg4326To3857Supported; 1015 } 1016 1017 public boolean isGeoreferenceValid() { 1018 return isGeoreferenceValid; 1019 } 1020 1021 public void setGeoreferenceValid(boolean isGeoreferenceValid) { 1022 this.isGeoreferenceValid = isGeoreferenceValid; 1023 } 1024 1025}