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