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