Ticket #19026: 19026.patch
File 19026.patch, 128.3 KB (added by , 5 years ago) |
---|
-
src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
3 3 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.Image;7 6 import java.io.StringReader; 8 import java.util.ArrayList;9 7 import java.util.Arrays; 10 8 import java.util.Collection; 11 9 import java.util.Collections; 12 10 import java.util.EnumMap; 13 import java.util.HashMap;14 11 import java.util.List; 15 12 import java.util.Locale; 16 13 import java.util.Map; 17 14 import java.util.Objects; 18 import java.util.Optional;19 import java.util.Set;20 import java.util.TreeSet;21 15 import java.util.concurrent.TimeUnit; 22 16 import java.util.regex.Matcher; 23 17 import java.util.regex.Pattern; … … 29 23 import javax.json.stream.JsonCollectors; 30 24 import javax.swing.ImageIcon; 31 25 32 import org.openstreetmap.gui.jmapviewer.interfaces.Attributed;33 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;34 import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;35 import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik;36 import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;37 import org.openstreetmap.josm.data.Bounds;38 import org.openstreetmap.josm.data.StructUtils;39 26 import org.openstreetmap.josm.data.StructUtils.StructEntry; 40 import org.openstreetmap.josm.io.Capabilities; 41 import org.openstreetmap.josm.io.OsmApi; 42 import org.openstreetmap.josm.spi.preferences.Config; 43 import org.openstreetmap.josm.spi.preferences.IPreferences; 27 import org.openstreetmap.josm.data.sources.ISourceCategory; 28 import org.openstreetmap.josm.data.sources.ISourceType; 29 import org.openstreetmap.josm.data.sources.SourceBounds; 30 import org.openstreetmap.josm.data.sources.SourceInfo; 31 import org.openstreetmap.josm.data.sources.SourcePreferenceEntry; 44 32 import org.openstreetmap.josm.tools.CheckParameterUtil; 45 33 import org.openstreetmap.josm.tools.ImageProvider; 46 34 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 47 import org.openstreetmap.josm.tools.LanguageInfo;48 35 import org.openstreetmap.josm.tools.Logging; 49 36 import org.openstreetmap.josm.tools.MultiMap; 50 37 import org.openstreetmap.josm.tools.Utils; … … 54 41 * 55 42 * @author Frederik Ramm 56 43 */ 57 public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed{44 public class ImageryInfo extends SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType, ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> { 58 45 59 46 /** 60 47 * Type of imagery entry. 61 48 */ 62 public enum ImageryType {49 public enum ImageryType implements ISourceType<ImageryType> { 63 50 /** A WMS (Web Map Service) entry. **/ 64 51 WMS("wms"), 65 52 /** A TMS (Tile Map Service) entry. **/ … … 84 71 * @return the unique string identifying this type 85 72 * @since 6690 86 73 */ 74 @Override 87 75 public final String getTypeString() { 88 76 return typeString; 89 77 } … … 93 81 * @param s The type string 94 82 * @return the imagery type matching the given type string 95 83 */ 96 public static ImageryType fromString(String s) {84 public static ImageryType getFromString(String s) { 97 85 for (ImageryType type : ImageryType.values()) { 98 86 if (type.getTypeString().equals(s)) { 99 87 return type; … … 101 89 } 102 90 return null; 103 91 } 92 93 @Override 94 public ImageryType fromString(String s) { 95 return getFromString(s); 96 } 97 98 @Override 99 public ImageryType getDefault() { 100 return WMS; 101 } 104 102 } 105 103 106 104 /** … … 107 105 * Category of imagery entry. 108 106 * @since 13792 109 107 */ 110 public enum ImageryCategory {108 public enum ImageryCategory implements ISourceCategory<ImageryCategory> { 111 109 /** A aerial or satellite photo. **/ 112 110 PHOTO(/* ICON(data/imagery/) */ "photo", tr("Aerial or satellite photo")), 113 111 /** A map of digital terrain model, digital surface model or contour lines. **/ … … 139 137 * Returns the unique string identifying this category. 140 138 * @return the unique string identifying this category 141 139 */ 140 @Override 142 141 public final String getCategoryString() { 143 142 return category; 144 143 } … … 147 146 * Returns the description of this category. 148 147 * @return the description of this category 149 148 */ 149 @Override 150 150 public final String getDescription() { 151 151 return description; 152 152 } … … 157 157 * @return the category icon at the given size 158 158 * @since 15049 159 159 */ 160 @Override 160 161 public final ImageIcon getIcon(ImageSizes size) { 161 162 return iconCache 162 163 .computeIfAbsent(size, x -> Collections.synchronizedMap(new EnumMap<>(ImageryCategory.class))) … … 168 169 * @param s The category string 169 170 * @return the imagery category matching the given category string 170 171 */ 171 public static ImageryCategory fromString(String s) {172 public static ImageryCategory getFromString(String s) { 172 173 for (ImageryCategory category : ImageryCategory.values()) { 173 174 if (category.getCategoryString().equals(s)) { 174 175 return category; … … 176 177 } 177 178 return null; 178 179 } 180 181 @Override 182 public ImageryCategory getDefault() { 183 return OTHER; 184 } 185 186 @Override 187 public ImageryCategory fromString(String s) { 188 return getFromString(s); 189 } 179 190 } 180 191 181 192 /** … … 182 193 * Multi-polygon bounds for imagery backgrounds. 183 194 * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location. 184 195 */ 185 public static class ImageryBounds extends Bounds {196 public static class ImageryBounds extends SourceBounds { 186 197 187 198 /** 188 199 * Constructs a new {@code ImageryBounds} from string. … … 192 203 public ImageryBounds(String asString, String separator) { 193 204 super(asString, separator); 194 205 } 195 196 private List<Shape> shapes = new ArrayList<>();197 198 /**199 * Adds a new shape to this bounds.200 * @param shape The shape to add201 */202 public final void addShape(Shape shape) {203 this.shapes.add(shape);204 }205 206 /**207 * Sets the list of shapes defining this bounds.208 * @param shapes The list of shapes defining this bounds.209 */210 public final void setShapes(List<Shape> shapes) {211 this.shapes = shapes;212 }213 214 /**215 * Returns the list of shapes defining this bounds.216 * @return The list of shapes defining this bounds217 */218 public final List<Shape> getShapes() {219 return shapes;220 }221 222 @Override223 public int hashCode() {224 return Objects.hash(super.hashCode(), shapes);225 }226 227 @Override228 public boolean equals(Object o) {229 if (this == o) return true;230 if (o == null || getClass() != o.getClass()) return false;231 if (!super.equals(o)) return false;232 ImageryBounds that = (ImageryBounds) o;233 return Objects.equals(shapes, that.shapes);234 }235 206 } 236 207 237 /** original name of the imagery entry in case of translation call, for multiple languages English when possible */238 private String origName;239 /** (original) language of the translated name entry */240 private String langName;241 /** whether this is a entry activated by default or not */242 private boolean defaultEntry;243 /** Whether this service requires a explicit EULA acceptance before it can be activated */244 private String eulaAcceptanceRequired;245 /** type of the imagery servics - WMS, TMS, ... */246 private ImageryType imageryType = ImageryType.WMS;247 208 private double pixelPerDegree; 248 209 /** maximum zoom level for TMS imagery */ 249 210 private int defaultMaxZoom; 250 211 /** minimum zoom level for TMS imagery */ 251 212 private int defaultMinZoom; 252 /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */253 private ImageryBounds bounds;254 213 /** projections supported by WMS servers */ 255 214 private List<String> serverProjections = Collections.emptyList(); 256 /** description of the imagery entry, should contain notes what type of data it is */257 private String description;258 /** language of the description entry */259 private String langDescription;260 /** Text of a text attribution displayed when using the imagery */261 private String attributionText;262 /** Link to the privacy policy of the operator */263 private String privacyPolicyURL;264 /** Link to a reference stating the permission for OSM usage */265 private String permissionReferenceURL;266 /** Link behind the text attribution displayed when using the imagery */267 private String attributionLinkURL;268 /** Image of a graphical attribution displayed when using the imagery */269 private String attributionImage;270 /** Link behind the graphical attribution displayed when using the imagery */271 private String attributionImageURL;272 /** Text with usage terms displayed when using the imagery */273 private String termsOfUseText;274 /** Link behind the text with usage terms displayed when using the imagery */275 private String termsOfUseURL;276 /** country code of the imagery (for country specific imagery) */277 private String countryCode = "";278 215 /** 279 * creation date of the imagery (in the form YYYY-MM-DD;YYYY-MM-DD, where280 * DD and MM as well as a second date are optional).281 *282 * Also used as time filter for WMS time={time} parameter (such as Sentinel-2)283 * @since 11570284 */285 private String date;286 /**287 216 * marked as best in other editors 288 217 * @since 11575 289 218 */ … … 294 223 */ 295 224 private boolean overlay; 296 225 /** 297 * list of old IDs, only for loading, not handled anywhere else 298 * @since 13536 299 */ 300 private Collection<String> oldIds; 301 /** mirrors of different type for this entry */ 302 private List<ImageryInfo> mirrors; 303 /** icon used in menu */ 304 private String icon; 226 * Auxiliary class to save an {@link ImageryInfo} object in the preferences. 227 */ 305 228 /** is the geo reference correct - don't offer offset handling */ 306 229 private boolean isGeoreferenceValid; 307 /** which layers should be activated by default on layer addition. **/308 private List<DefaultLayer> defaultLayers = Collections.emptyList();309 /** HTTP headers **/310 private Map<String, String> customHttpHeaders = Collections.emptyMap();311 230 /** Should this map be transparent **/ 312 231 private boolean transparent = true; 313 232 private int minimumTileExpire = (int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get()); 314 /** category of the imagery */315 private ImageryCategory category;316 /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */317 private String categoryOriginalString;318 /** when adding a field, also adapt the:319 * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object}320 * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor}321 * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor}322 * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor}323 * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method}324 **/325 233 326 /** 327 * Auxiliary class to save an {@link ImageryInfo} object in the preferences. 328 */ 329 public static class ImageryPreferenceEntry { 234 public static class ImageryPreferenceEntry extends SourcePreferenceEntry<ImageryInfo> { 330 235 @StructEntry String name; 331 236 @StructEntry String d; 332 237 @StructEntry String id; … … 379 284 public ImageryPreferenceEntry(ImageryInfo i) { 380 285 name = i.name; 381 286 id = i.id; 382 type = i. imageryType.getTypeString();287 type = i.sourceType.getTypeString(); 383 288 url = i.url; 384 289 pixel_per_eastnorth = i.pixelPerDegree; 385 290 eula = i.eulaAcceptanceRequired; … … 500 405 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) { 501 406 this(name); 502 407 setExtendedUrl(url); 503 ImageryType t = ImageryType. fromString(type);408 ImageryType t = ImageryType.getFromString(type); 504 409 this.cookies = cookies; 505 410 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 506 411 if (t != null) { 507 this. imageryType = t;412 this.sourceType = t; 508 413 } else if (type != null && !type.isEmpty()) { 509 414 throw new IllegalArgumentException("unknown type: "+type); 510 415 } … … 536 441 description = e.description; 537 442 cookies = e.cookies; 538 443 eulaAcceptanceRequired = e.eula; 539 imageryType = ImageryType.fromString(e.type);540 if ( imageryType == null) throw new IllegalArgumentException("unknown type");444 sourceType = ImageryType.getFromString(e.type); 445 if (sourceType == null) throw new IllegalArgumentException("unknown type"); 541 446 pixelPerDegree = e.pixel_per_eastnorth; 542 447 defaultMaxZoom = e.max_zoom; 543 448 defaultMinZoom = e.min_zoom; … … 584 489 defaultLayers = jsonReader. 585 490 readArray(). 586 491 stream(). 587 map(x -> DefaultLayer.fromJson((JsonObject) x, imageryType)).492 map(x -> DefaultLayer.fromJson((JsonObject) x, sourceType)). 588 493 collect(Collectors.toList()); 589 494 } 590 495 } … … 591 496 setCustomHttpHeaders(e.customHttpHeaders); 592 497 transparent = e.transparent; 593 498 minimumTileExpire = e.minimumTileExpire; 594 category = ImageryCategory. fromString(e.category);499 category = ImageryCategory.getFromString(e.category); 595 500 } 596 501 597 502 /** … … 613 518 this.langName = i.langName; 614 519 this.defaultEntry = i.defaultEntry; 615 520 this.eulaAcceptanceRequired = null; 616 this. imageryType = i.imageryType;521 this.sourceType = i.sourceType; 617 522 this.pixelPerDegree = i.pixelPerDegree; 618 523 this.defaultMaxZoom = i.defaultMaxZoom; 619 524 this.defaultMinZoom = i.defaultMinZoom; … … 644 549 this.category = i.category; 645 550 } 646 551 647 @Override648 public int hashCode() {649 return Objects.hash(url, imageryType);650 }651 652 552 /** 653 553 * Check if this object equals another ImageryInfo with respect to the properties 654 554 * that get written to the preference file. … … 658 558 * @param other the ImageryInfo object to compare to 659 559 * @return true if they are equal 660 560 */ 661 public boolean equalsPref(ImageryInfo other) { 662 if (other == null) { 561 @Override 562 public boolean equalsPref(SourceInfo<ImageryInfo.ImageryCategory,ImageryInfo.ImageryType,ImageryInfo.ImageryBounds,ImageryInfo.ImageryPreferenceEntry> other) { 563 if (!(other instanceof ImageryInfo)) { 663 564 return false; 664 565 } 566 ImageryInfo realOther = (ImageryInfo) other; 665 567 666 568 // CHECKSTYLE.OFF: BooleanExpressionComplexity 667 return 668 Objects.equals(this.name, other.name) && 669 Objects.equals(this.id, other.id) && 670 Objects.equals(this.url, other.url) && 671 Objects.equals(this.modTileFeatures, other.modTileFeatures) && 672 Objects.equals(this.bestMarked, other.bestMarked) && 673 Objects.equals(this.overlay, other.overlay) && 674 Objects.equals(this.isGeoreferenceValid, other.isGeoreferenceValid) && 675 Objects.equals(this.cookies, other.cookies) && 676 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) && 677 Objects.equals(this.imageryType, other.imageryType) && 678 Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) && 679 Objects.equals(this.defaultMinZoom, other.defaultMinZoom) && 680 Objects.equals(this.bounds, other.bounds) && 681 Objects.equals(this.serverProjections, other.serverProjections) && 682 Objects.equals(this.attributionText, other.attributionText) && 683 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) && 684 Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) && 685 Objects.equals(this.attributionImageURL, other.attributionImageURL) && 686 Objects.equals(this.attributionImage, other.attributionImage) && 687 Objects.equals(this.termsOfUseText, other.termsOfUseText) && 688 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) && 689 Objects.equals(this.countryCode, other.countryCode) && 690 Objects.equals(this.date, other.date) && 691 Objects.equals(this.icon, other.icon) && 692 Objects.equals(this.description, other.description) && 693 Objects.equals(this.noTileHeaders, other.noTileHeaders) && 694 Objects.equals(this.noTileChecksums, other.noTileChecksums) && 695 Objects.equals(this.metadataHeaders, other.metadataHeaders) && 696 Objects.equals(this.defaultLayers, other.defaultLayers) && 697 Objects.equals(this.customHttpHeaders, other.customHttpHeaders) && 698 Objects.equals(this.transparent, other.transparent) && 699 Objects.equals(this.minimumTileExpire, other.minimumTileExpire) && 700 Objects.equals(this.category, other.category); 569 return super.equalsPref(realOther) && 570 Objects.equals(this.bestMarked, realOther.bestMarked) && 571 Objects.equals(this.overlay, realOther.overlay) && 572 Objects.equals(this.isGeoreferenceValid, realOther.isGeoreferenceValid) && 573 Objects.equals(this.defaultMaxZoom, realOther.defaultMaxZoom) && 574 Objects.equals(this.defaultMinZoom, realOther.defaultMinZoom) && 575 Objects.equals(this.serverProjections, realOther.serverProjections) && 576 Objects.equals(this.transparent, realOther.transparent) && 577 Objects.equals(this.minimumTileExpire, realOther.minimumTileExpire); 701 578 // CHECKSTYLE.ON: BooleanExpressionComplexity 702 579 } 703 580 704 581 @Override 705 public boolean equals(Object o) { 706 if (this == o) return true; 707 if (o == null || getClass() != o.getClass()) return false; 708 ImageryInfo that = (ImageryInfo) o; 709 return imageryType == that.imageryType && Objects.equals(url, that.url); 710 } 711 712 private static final Map<String, String> localizedCountriesCache = new HashMap<>(); 713 static { 714 localizedCountriesCache.put("", tr("Worldwide")); 715 } 716 717 /** 718 * Returns a localized name for the given country code, or "Worldwide" if empty. 719 * This function falls back on the English name, and uses the ISO code as a last-resortvalue. 720 * 721 * @param countryCode An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code 722 * @return The name of the country appropriate to the current locale. 723 * @see Locale#getDisplayCountry 724 * @since 15158 725 */ 726 public static String getLocalizedCountry(String countryCode) { 727 return localizedCountriesCache.computeIfAbsent(countryCode, code -> new Locale("en", code).getDisplayCountry()); 728 } 729 730 @Override 731 public String toString() { 732 // Used in imagery preferences filtering, so must be efficient 733 return new StringBuilder(name) 734 .append('[').append(countryCode) 735 // appending the localized country in toString() allows us to filter imagery preferences table with it! 736 .append("] ('").append(getLocalizedCountry(countryCode)).append(')') 737 .append(" - ").append(url) 738 .append(" - ").append(imageryType) 739 .toString(); 740 } 741 742 @Override 743 public int compareTo(ImageryInfo in) { 744 int i = countryCode.compareTo(in.countryCode); 745 if (i == 0) { 746 i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH)); 582 public int compareTo(SourceInfo<ImageryInfo.ImageryCategory,ImageryInfo.ImageryType,ImageryInfo.ImageryBounds,ImageryInfo.ImageryPreferenceEntry> other) { 583 int i = super.compareTo(other); 584 if (other instanceof ImageryInfo) { 585 ImageryInfo in = (ImageryInfo) other; 586 if (i == 0) { 587 i = Double.compare(pixelPerDegree, in.pixelPerDegree); 588 } 747 589 } 748 if (i == 0) {749 i = url.compareTo(in.url);750 }751 if (i == 0) {752 i = Double.compare(pixelPerDegree, in.pixelPerDegree);753 }754 590 return i; 755 591 } 756 592 757 593 /** 758 * Determines if URL is equal to given imagery info.759 * @param in imagery info760 * @return {@code true} if URL is equal to given imagery info761 */762 public boolean equalsBaseValues(ImageryInfo in) {763 return url.equals(in.url);764 }765 766 /**767 594 * Sets the pixel per degree value. 768 595 * @param ppd The ppd value 769 596 * @see #getPixelPerDegree() … … 789 616 } 790 617 791 618 /** 792 * Sets the imagery polygonial bounds.793 * @param b The imagery bounds (non-rectangular)794 */795 public void setBounds(ImageryBounds b) {796 this.bounds = b;797 }798 799 /**800 * Returns the imagery polygonial bounds.801 * @return The imagery bounds (non-rectangular)802 */803 public ImageryBounds getBounds() {804 return bounds;805 }806 807 @Override808 public boolean requiresAttribution() {809 return attributionText != null || attributionLinkURL != null || attributionImage != null810 || termsOfUseText != null || termsOfUseURL != null;811 }812 813 @Override814 public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) {815 return attributionText;816 }817 818 @Override819 public String getAttributionLinkURL() {820 return attributionLinkURL;821 }822 823 /**824 * Return the permission reference URL.825 * @return The url826 * @see #setPermissionReferenceURL827 * @since 11975828 */829 public String getPermissionReferenceURL() {830 return permissionReferenceURL;831 }832 833 /**834 * Return the privacy policy URL.835 * @return The url836 * @see #setPrivacyPolicyURL837 * @since 16127838 */839 public String getPrivacyPolicyURL() {840 return privacyPolicyURL;841 }842 843 @Override844 public Image getAttributionImage() {845 ImageIcon i = ImageProvider.getIfAvailable(attributionImage);846 if (i != null) {847 return i.getImage();848 }849 return null;850 }851 852 /**853 * Return the raw attribution logo information (an URL to the image).854 * @return The url text855 * @since 12257856 */857 public String getAttributionImageRaw() {858 return attributionImage;859 }860 861 @Override862 public String getAttributionImageURL() {863 return attributionImageURL;864 }865 866 @Override867 public String getTermsOfUseText() {868 return termsOfUseText;869 }870 871 @Override872 public String getTermsOfUseURL() {873 return termsOfUseURL;874 }875 876 /**877 * Set the attribution text878 * @param text The text879 * @see #getAttributionText(int, ICoordinate, ICoordinate)880 */881 public void setAttributionText(String text) {882 attributionText = intern(text);883 }884 885 /**886 * Set the attribution image887 * @param url The url of the image.888 * @see #getAttributionImageURL()889 */890 public void setAttributionImageURL(String url) {891 attributionImageURL = url;892 }893 894 /**895 * Set the image for the attribution896 * @param res The image resource897 * @see #getAttributionImage()898 */899 public void setAttributionImage(String res) {900 attributionImage = res;901 }902 903 /**904 * Sets the URL the attribution should link to.905 * @param url The url.906 * @see #getAttributionLinkURL()907 */908 public void setAttributionLinkURL(String url) {909 attributionLinkURL = url;910 }911 912 /**913 * Sets the permission reference URL.914 * @param url The url.915 * @see #getPermissionReferenceURL()916 * @since 11975917 */918 public void setPermissionReferenceURL(String url) {919 permissionReferenceURL = url;920 }921 922 /**923 * Sets the privacy policy URL.924 * @param url The url.925 * @see #getPrivacyPolicyURL()926 * @since 16127927 */928 public void setPrivacyPolicyURL(String url) {929 privacyPolicyURL = url;930 }931 932 /**933 * Sets the text to display to the user as terms of use.934 * @param text The text935 * @see #getTermsOfUseText()936 */937 public void setTermsOfUseText(String text) {938 termsOfUseText = text;939 }940 941 /**942 * Sets a url that links to the terms of use text.943 * @param text The url.944 * @see #getTermsOfUseURL()945 */946 public void setTermsOfUseURL(String text) {947 termsOfUseURL = text;948 }949 950 /**951 619 * Sets the extended URL of this entry. 952 620 * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info 953 621 */ … … 956 624 957 625 // Default imagery type is WMS 958 626 this.url = url; 959 this. imageryType = ImageryType.WMS;627 this.sourceType = ImageryType.WMS; 960 628 961 629 defaultMaxZoom = 0; 962 630 defaultMinZoom = 0; … … 964 632 Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url); 965 633 if (m.matches()) { 966 634 this.url = m.group(3); 967 this. imageryType = type;635 this.sourceType = type; 968 636 if (m.group(2) != null) { 969 637 defaultMaxZoom = Integer.parseInt(m.group(2)); 970 638 } … … 982 650 } 983 651 } 984 652 } 985 986 653 /** 987 * Returns the entry name.988 * @return The entry name989 * @since 6968990 */991 public String getOriginalName() {992 return this.origName != null ? this.origName : this.name;993 }994 995 /**996 * Sets the entry name and handle translation.997 * @param language The used language998 * @param name The entry name999 * @since 80911000 */1001 public void setName(String language, String name) {1002 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);1003 if (LanguageInfo.isBetterLanguage(langName, language)) {1004 this.name = isdefault ? tr(name) : name;1005 this.langName = language;1006 }1007 if (origName == null || isdefault) {1008 this.origName = name;1009 }1010 }1011 1012 /**1013 * Store the id of this info to the preferences and clear it afterwards.1014 */1015 public void clearId() {1016 if (this.id != null) {1017 Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds"));1018 newAddedIds.add(this.id);1019 Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds));1020 }1021 setId(null);1022 }1023 1024 /**1025 * Determines if this entry is enabled by default.1026 * @return {@code true} if this entry is enabled by default, {@code false} otherwise1027 */1028 public boolean isDefaultEntry() {1029 return defaultEntry;1030 }1031 1032 /**1033 * Sets the default state of this entry.1034 * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise1035 */1036 public void setDefaultEntry(boolean defaultEntry) {1037 this.defaultEntry = defaultEntry;1038 }1039 1040 /**1041 654 * Gets the pixel per degree value 1042 655 * @return The ppd value. 1043 656 */ … … 1064 677 } 1065 678 1066 679 /** 1067 * Returns the description text when existing.1068 * @return The description1069 * @since 80651070 */1071 public String getDescription() {1072 return this.description;1073 }1074 1075 /**1076 * Sets the description text when existing.1077 * @param language The used language1078 * @param description the imagery description text1079 * @since 80911080 */1081 public void setDescription(String language, String description) {1082 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);1083 if (LanguageInfo.isBetterLanguage(langDescription, language)) {1084 this.description = isdefault ? tr(description) : description;1085 this.langDescription = intern(language);1086 }1087 }1088 1089 /**1090 * Return the sorted list of activated Imagery IDs.1091 * @return sorted list of activated Imagery IDs1092 * @since 135361093 */1094 public static Collection<String> getActiveIds() {1095 ArrayList<String> ids = new ArrayList<>();1096 IPreferences pref = Config.getPref();1097 if (pref != null) {1098 List<ImageryPreferenceEntry> entries = StructUtils.getListOfStructs(1099 pref, "imagery.entries", null, ImageryPreferenceEntry.class);1100 if (entries != null) {1101 for (ImageryPreferenceEntry prefEntry : entries) {1102 if (prefEntry.id != null && !prefEntry.id.isEmpty())1103 ids.add(prefEntry.id);1104 }1105 Collections.sort(ids);1106 }1107 }1108 return ids;1109 }1110 1111 /**1112 680 * Returns a tool tip text for display. 1113 681 * @return The text 1114 682 * @since 8065 1115 683 */ 684 @Override 1116 685 public String getToolTipText() { 1117 686 StringBuilder res = new StringBuilder(getName()); 1118 687 boolean html = false; … … 1143 712 } 1144 713 return res.toString(); 1145 714 } 1146 1147 715 /** 1148 * Returns the EULA acceptance URL, if any.1149 * @return The URL to an EULA text that has to be accepted before use, or {@code null}1150 */1151 public String getEulaAcceptanceRequired() {1152 return eulaAcceptanceRequired;1153 }1154 1155 /**1156 * Sets the EULA acceptance URL.1157 * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use1158 */1159 public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {1160 this.eulaAcceptanceRequired = eulaAcceptanceRequired;1161 }1162 1163 /**1164 * Returns the ISO 3166-1-alpha-2 country code.1165 * @return The country code (2 letters)1166 */1167 public String getCountryCode() {1168 return countryCode;1169 }1170 1171 /**1172 * Sets the ISO 3166-1-alpha-2 country code.1173 * @param countryCode The country code (2 letters)1174 */1175 public void setCountryCode(String countryCode) {1176 this.countryCode = intern(countryCode);1177 }1178 1179 /**1180 * Returns the date information.1181 * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where1182 * DD and MM as well as a second date are optional)1183 * @since 115701184 */1185 public String getDate() {1186 return date;1187 }1188 1189 /**1190 * Sets the date information.1191 * @param date The date information1192 * @since 115701193 */1194 public void setDate(String date) {1195 this.date = date;1196 }1197 1198 /**1199 * Returns the entry icon.1200 * @return The entry icon1201 */1202 public String getIcon() {1203 return icon;1204 }1205 1206 /**1207 * Sets the entry icon.1208 * @param icon The entry icon1209 */1210 public void setIcon(String icon) {1211 this.icon = intern(icon);1212 }1213 1214 /**1215 716 * Get the projections supported by the server. Only relevant for 1216 717 * WMS-type ImageryInfo at the moment. 1217 718 * @return null, if no projections have been specified; the list … … 1237 738 * @return The extended URL 1238 739 */ 1239 740 public String getExtendedUrl() { 1240 return imageryType.getTypeString() + (defaultMaxZoom != 0741 return sourceType.getTypeString() + (defaultMaxZoom != 0 1241 742 ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url; 1242 743 } 1243 744 … … 1265 766 return res; 1266 767 } 1267 768 1268 /**1269 * Determines if this entry requires attribution.1270 * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise1271 */1272 public boolean hasAttribution() {1273 return attributionText != null;1274 }1275 769 1276 770 /** 1277 * Copies attribution from another {@code ImageryInfo}.1278 * @param i The other imagery info to get attribution from1279 */1280 public void copyAttribution(ImageryInfo i) {1281 this.attributionImage = i.attributionImage;1282 this.attributionImageURL = i.attributionImageURL;1283 this.attributionText = i.attributionText;1284 this.attributionLinkURL = i.attributionLinkURL;1285 this.termsOfUseText = i.termsOfUseText;1286 this.termsOfUseURL = i.termsOfUseURL;1287 }1288 1289 /**1290 * Applies the attribution from this object to a tile source.1291 * @param s The tile source1292 */1293 public void setAttribution(AbstractTileSource s) {1294 if (attributionText != null) {1295 if ("osm".equals(attributionText)) {1296 s.setAttributionText(new Mapnik().getAttributionText(0, null, null));1297 } else {1298 s.setAttributionText(attributionText);1299 }1300 }1301 if (attributionLinkURL != null) {1302 if ("osm".equals(attributionLinkURL)) {1303 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL());1304 } else {1305 s.setAttributionLinkURL(attributionLinkURL);1306 }1307 }1308 if (attributionImage != null) {1309 ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage);1310 if (i != null) {1311 s.setAttributionImage(i.getImage());1312 }1313 }1314 if (attributionImageURL != null) {1315 s.setAttributionImageURL(attributionImageURL);1316 }1317 if (termsOfUseText != null) {1318 s.setTermsOfUseText(termsOfUseText);1319 }1320 if (termsOfUseURL != null) {1321 if ("osm".equals(termsOfUseURL)) {1322 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL());1323 } else {1324 s.setTermsOfUseURL(termsOfUseURL);1325 }1326 }1327 }1328 1329 /**1330 * Returns the imagery type.1331 * @return The imagery type1332 */1333 public ImageryType getImageryType() {1334 return imageryType;1335 }1336 1337 /**1338 * Sets the imagery type.1339 * @param imageryType The imagery type1340 */1341 public void setImageryType(ImageryType imageryType) {1342 this.imageryType = imageryType;1343 }1344 1345 /**1346 * Returns the imagery category.1347 * @return The imagery category1348 * @since 137921349 */1350 public ImageryCategory getImageryCategory() {1351 return category;1352 }1353 1354 /**1355 * Sets the imagery category.1356 * @param category The imagery category1357 * @since 137921358 */1359 public void setImageryCategory(ImageryCategory category) {1360 this.category = category;1361 }1362 1363 /**1364 * Returns the imagery category original string (don't use except for error checks).1365 * @return The imagery category original string1366 * @since 137921367 */1368 public String getImageryCategoryOriginalString() {1369 return categoryOriginalString;1370 }1371 1372 /**1373 * Sets the imagery category original string (don't use except for error checks).1374 * @param categoryOriginalString The imagery category original string1375 * @since 137921376 */1377 public void setImageryCategoryOriginalString(String categoryOriginalString) {1378 this.categoryOriginalString = intern(categoryOriginalString);1379 }1380 1381 /**1382 * Returns true if this layer's URL is matched by one of the regular1383 * expressions kept by the current OsmApi instance.1384 * @return {@code true} is this entry is blacklisted, {@code false} otherwise1385 */1386 public boolean isBlacklisted() {1387 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();1388 return capabilities != null && capabilities.isOnImageryBlacklist(this.url);1389 }1390 1391 /**1392 * Sets the map of <header name, header value> that if any of this header1393 * will be returned, then this tile will be treated as "no tile at this zoom level"1394 *1395 * @param noTileHeaders Map of <header name, header value> which will be treated as "no tile at this zoom level"1396 * @since 96131397 */1398 public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) {1399 if (noTileHeaders == null || noTileHeaders.isEmpty()) {1400 this.noTileHeaders = null;1401 } else {1402 this.noTileHeaders = noTileHeaders.toMap();1403 }1404 }1405 1406 @Override1407 public Map<String, Set<String>> getNoTileHeaders() {1408 return noTileHeaders;1409 }1410 1411 /**1412 * Sets the map of <checksum type, checksum value> that if any tile with that checksum1413 * will be returned, then this tile will be treated as "no tile at this zoom level"1414 *1415 * @param noTileChecksums Map of <checksum type, checksum value> which will be treated as "no tile at this zoom level"1416 * @since 96131417 */1418 public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) {1419 if (noTileChecksums == null || noTileChecksums.isEmpty()) {1420 this.noTileChecksums = null;1421 } else {1422 this.noTileChecksums = noTileChecksums.toMap();1423 }1424 }1425 1426 @Override1427 public Map<String, Set<String>> getNoTileChecksums() {1428 return noTileChecksums;1429 }1430 1431 /**1432 * Returns the map of <header name, metadata key> indicating, which HTTP headers should1433 * be moved to metadata1434 *1435 * @param metadataHeaders map of <header name, metadata key> indicating, which HTTP headers should be moved to metadata1436 * @since 84181437 */1438 public void setMetadataHeaders(Map<String, String> metadataHeaders) {1439 if (metadataHeaders == null || metadataHeaders.isEmpty()) {1440 this.metadataHeaders = null;1441 } else {1442 this.metadataHeaders = metadataHeaders;1443 }1444 }1445 1446 /**1447 771 * Gets the flag if the georeference is valid. 1448 772 * @return <code>true</code> if it is valid. 1449 773 */ … … 1496 820 } 1497 821 1498 822 /** 1499 * Adds an old Id.1500 *1501 * @param id the Id to be added1502 * @since 135361503 */1504 public void addOldId(String id) {1505 if (oldIds == null) {1506 oldIds = new ArrayList<>();1507 }1508 oldIds.add(id);1509 }1510 1511 /**1512 * Get old Ids.1513 *1514 * @return collection of ids1515 * @since 135361516 */1517 public Collection<String> getOldIds() {1518 return oldIds;1519 }1520 1521 /**1522 * Adds a mirror entry. Mirror entries are completed with the data from the master entry1523 * and only describe another method to access identical data.1524 *1525 * @param entry the mirror to be added1526 * @since 96581527 */1528 public void addMirror(ImageryInfo entry) {1529 if (mirrors == null) {1530 mirrors = new ArrayList<>();1531 }1532 mirrors.add(entry);1533 }1534 1535 /**1536 * Returns the mirror entries. Entries are completed with master entry data.1537 *1538 * @return the list of mirrors1539 * @since 96581540 */1541 public List<ImageryInfo> getMirrors() {1542 List<ImageryInfo> l = new ArrayList<>();1543 if (mirrors != null) {1544 int num = 1;1545 for (ImageryInfo i : mirrors) {1546 ImageryInfo n = new ImageryInfo(this);1547 if (i.defaultMaxZoom != 0) {1548 n.defaultMaxZoom = i.defaultMaxZoom;1549 }1550 if (i.defaultMinZoom != 0) {1551 n.defaultMinZoom = i.defaultMinZoom;1552 }1553 n.setServerProjections(i.getServerProjections());1554 n.url = i.url;1555 n.imageryType = i.imageryType;1556 if (i.getTileSize() != 0) {1557 n.setTileSize(i.getTileSize());1558 }1559 if (i.getPrivacyPolicyURL() != null) {1560 n.setPrivacyPolicyURL(i.getPrivacyPolicyURL());1561 }1562 if (n.id != null) {1563 n.id = n.id + "_mirror"+num;1564 }1565 if (num > 1) {1566 n.name = tr("{0} mirror server {1}", n.name, num);1567 if (n.origName != null) {1568 n.origName += " mirror server " + num;1569 }1570 } else {1571 n.name = tr("{0} mirror server", n.name);1572 if (n.origName != null) {1573 n.origName += " mirror server";1574 }1575 }1576 l.add(n);1577 ++num;1578 }1579 }1580 return l;1581 }1582 1583 /**1584 * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider)1585 * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer1586 * to work on1587 * @return Collection of the layer names1588 */1589 public List<DefaultLayer> getDefaultLayers() {1590 return defaultLayers;1591 }1592 1593 /**1594 * Sets the default layers that user will work with1595 * @param layers set the list of default layers1596 */1597 public void setDefaultLayers(List<DefaultLayer> layers) {1598 this.defaultLayers = Utils.toUnmodifiableList(layers);1599 }1600 1601 /**1602 * Returns custom HTTP headers that should be sent with request towards imagery provider1603 * @return headers1604 */1605 public Map<String, String> getCustomHttpHeaders() {1606 return customHttpHeaders;1607 }1608 1609 /**1610 * Sets custom HTTP headers that should be sent with request towards imagery provider1611 * @param customHttpHeaders http headers1612 */1613 public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) {1614 this.customHttpHeaders = Utils.toUnmodifiableMap(customHttpHeaders);1615 }1616 1617 /**1618 823 * Determines if this imagery should be transparent. 1619 824 * @return should this imagery be transparent 1620 825 */ … … 1646 851 this.minimumTileExpire = minimumTileExpire; 1647 852 } 1648 853 1649 /**1650 * Get a string representation of this imagery info suitable for the {@code source} changeset tag.1651 * @return English name, if known1652 * @since 138901653 */1654 public String getSourceName() {1655 if (ImageryType.BING == getImageryType()) {1656 return "Bing";1657 } else {1658 if (id != null) {1659 // Retrieve english name, unfortunately not saved in preferences1660 Optional<ImageryInfo> infoEn = ImageryLayerInfo.allDefaultLayers.stream().filter(x -> id.equals(x.getId())).findAny();1661 if (infoEn.isPresent()) {1662 return infoEn.get().getOriginalName();1663 }1664 }1665 return getOriginalName();1666 }1667 }1668 1669 private static String intern(String string) {1670 return string == null ? null : string.intern();1671 }1672 854 } -
src/org/openstreetmap/josm/data/sources/ICommonSource.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 /** 5 * This interface is used to ensure that a class can get a enum from a string. 6 * For various reasons, the fromString method cannot be implemented statically. 7 * 8 * @author Taylor Smock 9 * @since xxx 10 * 11 * @param <T> The enum type 12 */ 13 public interface ICommonSource<T extends Enum<T>> { 14 /** 15 * Get the default value for the Enum 16 * @return The default value 17 */ 18 public T getDefault(); 19 20 /** 21 * Returns the source category from the given category string. 22 * @param s The category string 23 * @return the source category matching the given category string 24 */ 25 public T fromString(String s); 26 } -
src/org/openstreetmap/josm/data/sources/ISourceCategory.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import javax.swing.ImageIcon; 5 6 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 7 8 /** 9 * @author Taylor Smock 10 * 11 * @param <T> The enum that is extending this interface 12 * @since xxx 13 */ 14 public interface ISourceCategory<T extends Enum<T>> extends ICommonSource<T> { 15 16 /** 17 * Returns the unique string identifying this category. 18 * @return the unique string identifying this category 19 */ 20 public String getCategoryString(); 21 22 /** 23 * Returns the description of this category. 24 * @return the description of this category 25 */ 26 public String getDescription(); 27 28 /** 29 * Returns the category icon at the given size. 30 * @param size icon wanted size 31 * @return the category icon at the given size 32 */ 33 public ImageIcon getIcon(ImageSizes size); 34 } -
src/org/openstreetmap/josm/data/sources/ISourceType.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 /** 5 * This interface should only be used for Enums 6 * @author Taylor Smock 7 * @since xxx 8 * 9 * @param <T> The source type (e.g., Imagery or otherwise -- should be the name of the class) 10 */ 11 public interface ISourceType<T extends Enum<T>> extends ICommonSource<T> { 12 13 /** 14 * Returns the unique string identifying this type. 15 * @return the unique string identifying this type 16 */ 17 public String getTypeString(); 18 } -
src/org/openstreetmap/josm/data/sources/SourceBounds.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import java.util.ArrayList; 5 import java.util.List; 6 import java.util.Objects; 7 8 import org.openstreetmap.josm.data.Bounds; 9 import org.openstreetmap.josm.data.imagery.Shape; 10 11 /** 12 * 13 * Multi-polygon bounds for source backgrounds. 14 * Used to display source coverage in preferences and to determine relevant source entries based on edit location. 15 * 16 * @author Frederik Ramm, extracted by Taylor Smock 17 * @since xxx (extracted from {@link org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds}) 18 */ 19 public class SourceBounds extends Bounds { 20 21 /** 22 * Constructs a new {@code SourceBounds} from string. 23 * @param asString The string containing the list of shapes defining this bounds 24 * @param separator The shape separator in the given string, usually a comma 25 */ 26 public SourceBounds(String asString, String separator) { 27 super(asString, separator); 28 } 29 30 private List<Shape> shapes = new ArrayList<>(); 31 32 /** 33 * Adds a new shape to this bounds. 34 * @param shape The shape to add 35 */ 36 public final void addShape(Shape shape) { 37 this.shapes.add(shape); 38 } 39 40 /** 41 * Sets the list of shapes defining this bounds. 42 * @param shapes The list of shapes defining this bounds. 43 */ 44 public final void setShapes(List<Shape> shapes) { 45 this.shapes = shapes; 46 } 47 48 /** 49 * Returns the list of shapes defining this bounds. 50 * @return The list of shapes defining this bounds 51 */ 52 public final List<Shape> getShapes() { 53 return shapes; 54 } 55 56 @Override 57 public int hashCode() { 58 return Objects.hash(super.hashCode(), shapes); 59 } 60 61 @Override 62 public boolean equals(Object o) { 63 if (this == o) return true; 64 if (o == null || getClass() != o.getClass()) return false; 65 if (!super.equals(o)) return false; 66 SourceBounds that = (SourceBounds) o; 67 return Objects.equals(shapes, that.shapes); 68 } 69 } -
src/org/openstreetmap/josm/data/sources/SourceInfo.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.Image; 7 import java.lang.reflect.ParameterizedType; 8 import java.util.ArrayList; 9 import java.util.Collection; 10 import java.util.Collections; 11 import java.util.HashMap; 12 import java.util.List; 13 import java.util.Locale; 14 import java.util.Map; 15 import java.util.Objects; 16 import java.util.Optional; 17 import java.util.Set; 18 import java.util.TreeSet; 19 20 import javax.swing.ImageIcon; 21 22 import org.openstreetmap.gui.jmapviewer.interfaces.Attributed; 23 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 24 import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource; 25 import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik; 26 import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo; 27 import org.openstreetmap.josm.data.StructUtils; 28 import org.openstreetmap.josm.data.imagery.DefaultLayer; 29 import org.openstreetmap.josm.data.imagery.ImageryInfo; 30 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 31 import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; 32 import org.openstreetmap.josm.io.Capabilities; 33 import org.openstreetmap.josm.io.OsmApi; 34 import org.openstreetmap.josm.spi.preferences.Config; 35 import org.openstreetmap.josm.spi.preferences.IPreferences; 36 import org.openstreetmap.josm.tools.ImageProvider; 37 import org.openstreetmap.josm.tools.LanguageInfo; 38 import org.openstreetmap.josm.tools.MultiMap; 39 import org.openstreetmap.josm.tools.Utils; 40 41 /** 42 * @author Taylor Smock 43 * @param <T> The SourceCategory type 44 * @param <U> The SourceType 45 * @param <V> The SourceBounds 46 * 47 * @since xxx 48 */ 49 public class SourceInfo<T extends ISourceCategory<?>, U extends ISourceType<?>, V extends SourceBounds, W extends SourcePreferenceEntry<?>> extends TileSourceInfo implements Comparable<SourceInfo<T, U, V, W>>, Attributed { 50 /** original name of the source entry in case of translation call, for multiple languages English when possible */ 51 protected String origName; 52 /** (original) language of the translated name entry */ 53 protected String langName; 54 /** whether this is a entry activated by default or not */ 55 protected boolean defaultEntry; 56 /** Whether this service requires a explicit EULA acceptance before it can be activated */ 57 protected String eulaAcceptanceRequired; 58 /** type of the services - WMS, TMS, ... */ 59 @SuppressWarnings("unchecked") 60 protected U sourceType = (U) ((U) ((Enum<?>)((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).getDeclaringClass().getEnumConstants()[0]).getDefault(); 61 /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */ 62 protected V bounds; 63 /** description of the imagery entry, should contain notes what type of data it is */ 64 protected String description; 65 /** language of the description entry */ 66 protected String langDescription; 67 /** Text of a text attribution displayed when using the imagery */ 68 protected String attributionText; 69 /** Link to the privacy policy of the operator */ 70 protected String privacyPolicyURL; 71 /** Link to a reference stating the permission for OSM usage */ 72 protected String permissionReferenceURL; 73 /** Link behind the text attribution displayed when using the imagery */ 74 protected String attributionLinkURL; 75 /** Image of a graphical attribution displayed when using the imagery */ 76 protected String attributionImage; 77 /** Link behind the graphical attribution displayed when using the imagery */ 78 protected String attributionImageURL; 79 /** Text with usage terms displayed when using the imagery */ 80 protected String termsOfUseText; 81 /** Link behind the text with usage terms displayed when using the imagery */ 82 protected String termsOfUseURL; 83 /** country code of the imagery (for country specific imagery) */ 84 protected String countryCode = ""; 85 /** 86 * creation date of the source (in the form YYYY-MM-DD;YYYY-MM-DD, where 87 * DD and MM as well as a second date are optional). 88 * 89 * Also used as time filter for WMS time={time} parameter (such as Sentinel-2) 90 * @since 11570 91 */ 92 protected String date; 93 /** 94 * list of old IDs, only for loading, not handled anywhere else 95 * @since 13536 96 */ 97 protected Collection<String> oldIds; 98 /** mirrors of different type for this entry */ 99 protected List<SourceInfo<T, U, V, W>> mirrors; 100 /** icon used in menu */ 101 protected String icon; 102 /** which layers should be activated by default on layer addition. **/ 103 protected List<DefaultLayer> defaultLayers = Collections.emptyList(); 104 /** HTTP headers **/ 105 protected Map<String, String> customHttpHeaders = Collections.emptyMap(); 106 /** category of the imagery */ 107 protected T category; 108 /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */ 109 protected String categoryOriginalString; 110 /** when adding a field, also adapt the: 111 * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object} 112 * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor} 113 * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor} 114 * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor} 115 * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method} 116 **/ 117 118 /** 119 * Creates empty SourceInfo class 120 */ 121 public SourceInfo() { 122 super(); 123 } 124 125 /** 126 * Create a SourceInfo class 127 * 128 * @param name name 129 */ 130 public SourceInfo(String name) { 131 super(name); 132 } 133 134 /** 135 * Create a SourceInfo class 136 * 137 * @param name name 138 * @param url base URL 139 * @param id unique id 140 */ 141 public SourceInfo(String name, String url, String id) { 142 super(name, url, id); 143 } 144 145 146 @Override 147 public int hashCode() { 148 return Objects.hash(url, sourceType); 149 } 150 151 /** 152 * Check if this object equals another SourceInfo with respect to the properties 153 * that get written to the preference file. 154 * 155 * This should be overridden and called in subclasses. 156 * 157 * @param other the SourceInfo object to compare to 158 * @return true if they are equal 159 */ 160 public boolean equalsPref(SourceInfo<T, U, V, W> other) { 161 if (other == null) { 162 return false; 163 } 164 165 // CHECKSTYLE.OFF: BooleanExpressionComplexity 166 return 167 Objects.equals(this.name, other.name) && 168 Objects.equals(this.id, other.id) && 169 Objects.equals(this.url, other.url) && 170 Objects.equals(this.modTileFeatures, other.modTileFeatures) && 171 Objects.equals(this.cookies, other.cookies) && 172 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) && 173 Objects.equals(this.sourceType, other.sourceType) && 174 Objects.equals(this.bounds, other.bounds) && 175 Objects.equals(this.attributionText, other.attributionText) && 176 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) && 177 Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) && 178 Objects.equals(this.attributionImageURL, other.attributionImageURL) && 179 Objects.equals(this.attributionImage, other.attributionImage) && 180 Objects.equals(this.termsOfUseText, other.termsOfUseText) && 181 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) && 182 Objects.equals(this.countryCode, other.countryCode) && 183 Objects.equals(this.date, other.date) && 184 Objects.equals(this.icon, other.icon) && 185 Objects.equals(this.description, other.description) && 186 Objects.equals(this.noTileHeaders, other.noTileHeaders) && 187 Objects.equals(this.noTileChecksums, other.noTileChecksums) && 188 Objects.equals(this.metadataHeaders, other.metadataHeaders) && 189 Objects.equals(this.defaultLayers, other.defaultLayers) && 190 Objects.equals(this.customHttpHeaders, other.customHttpHeaders) && 191 Objects.equals(this.category, other.category); 192 // CHECKSTYLE.ON: BooleanExpressionComplexity 193 } 194 195 @Override 196 public boolean equals(Object o) { 197 if (this == o) return true; 198 if (o == null || getClass() != o.getClass()) return false; 199 ImageryInfo that = (ImageryInfo) o; 200 return sourceType == that.sourceType && Objects.equals(url, that.url); 201 } 202 203 private static final Map<String, String> localizedCountriesCache = new HashMap<>(); 204 static { 205 localizedCountriesCache.put("", tr("Worldwide")); 206 } 207 208 /** 209 * Returns a localized name for the given country code, or "Worldwide" if empty. 210 * This function falls back on the English name, and uses the ISO code as a last-resortvalue. 211 * 212 * @param countryCode An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code 213 * @return The name of the country appropriate to the current locale. 214 * @see Locale#getDisplayCountry 215 * @since 15158 216 */ 217 public static String getLocalizedCountry(String countryCode) { 218 return localizedCountriesCache.computeIfAbsent(countryCode, code -> new Locale("en", code).getDisplayCountry()); 219 } 220 221 @Override 222 public String toString() { 223 // Used in imagery preferences filtering, so must be efficient 224 return new StringBuilder(name) 225 .append('[').append(countryCode) 226 // appending the localized country in toString() allows us to filter imagery preferences table with it! 227 .append("] ('").append(getLocalizedCountry(countryCode)).append(')') 228 .append(" - ").append(url) 229 .append(" - ").append(sourceType) 230 .toString(); 231 } 232 233 @Override 234 public int compareTo(SourceInfo<T, U, V, W> in) { 235 int i = countryCode.compareTo(in.countryCode); 236 if (i == 0) { 237 i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH)); 238 } 239 if (i == 0) { 240 i = url.compareTo(in.url); 241 } 242 return i; 243 } 244 245 /** 246 * Determines if URL is equal to given imagery info. 247 * @param in imagery info 248 * @return {@code true} if URL is equal to given imagery info 249 */ 250 public boolean equalsBaseValues(ImageryInfo in) { 251 return url.equals(in.url); 252 } 253 254 /** 255 * Sets the source polygonial bounds. 256 * @param b The source bounds (non-rectangular) 257 */ 258 public void setBounds(V b) { 259 this.bounds = b; 260 } 261 262 /** 263 * Returns the source polygonial bounds. 264 * @return The source bounds (non-rectangular) 265 */ 266 public V getBounds() { 267 return bounds; 268 } 269 270 @Override 271 public boolean requiresAttribution() { 272 return attributionText != null || attributionLinkURL != null || attributionImage != null 273 || termsOfUseText != null || termsOfUseURL != null; 274 } 275 276 @Override 277 public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { 278 return attributionText; 279 } 280 281 @Override 282 public String getAttributionLinkURL() { 283 return attributionLinkURL; 284 } 285 286 /** 287 * Return the permission reference URL. 288 * @return The url 289 * @see #setPermissionReferenceURL 290 * @since 11975 291 */ 292 public String getPermissionReferenceURL() { 293 return permissionReferenceURL; 294 } 295 296 /** 297 * Return the privacy policy URL. 298 * @return The url 299 * @see #setPrivacyPolicyURL 300 * @since 16127 301 */ 302 public String getPrivacyPolicyURL() { 303 return privacyPolicyURL; 304 } 305 306 @Override 307 public Image getAttributionImage() { 308 ImageIcon i = ImageProvider.getIfAvailable(attributionImage); 309 if (i != null) { 310 return i.getImage(); 311 } 312 return null; 313 } 314 315 /** 316 * Return the raw attribution logo information (an URL to the image). 317 * @return The url text 318 * @since 12257 319 */ 320 public String getAttributionImageRaw() { 321 return attributionImage; 322 } 323 324 @Override 325 public String getAttributionImageURL() { 326 return attributionImageURL; 327 } 328 329 @Override 330 public String getTermsOfUseText() { 331 return termsOfUseText; 332 } 333 334 @Override 335 public String getTermsOfUseURL() { 336 return termsOfUseURL; 337 } 338 339 /** 340 * Set the attribution text 341 * @param text The text 342 * @see #getAttributionText(int, ICoordinate, ICoordinate) 343 */ 344 public void setAttributionText(String text) { 345 attributionText = intern(text); 346 } 347 348 /** 349 * Set the attribution image 350 * @param url The url of the image. 351 * @see #getAttributionImageURL() 352 */ 353 public void setAttributionImageURL(String url) { 354 attributionImageURL = url; 355 } 356 357 /** 358 * Set the image for the attribution 359 * @param res The image resource 360 * @see #getAttributionImage() 361 */ 362 public void setAttributionImage(String res) { 363 attributionImage = res; 364 } 365 366 /** 367 * Sets the URL the attribution should link to. 368 * @param url The url. 369 * @see #getAttributionLinkURL() 370 */ 371 public void setAttributionLinkURL(String url) { 372 attributionLinkURL = url; 373 } 374 375 /** 376 * Sets the permission reference URL. 377 * @param url The url. 378 * @see #getPermissionReferenceURL() 379 * @since 11975 380 */ 381 public void setPermissionReferenceURL(String url) { 382 permissionReferenceURL = url; 383 } 384 385 /** 386 * Sets the privacy policy URL. 387 * @param url The url. 388 * @see #getPrivacyPolicyURL() 389 * @since 16127 390 */ 391 public void setPrivacyPolicyURL(String url) { 392 privacyPolicyURL = url; 393 } 394 395 /** 396 * Sets the text to display to the user as terms of use. 397 * @param text The text 398 * @see #getTermsOfUseText() 399 */ 400 public void setTermsOfUseText(String text) { 401 termsOfUseText = text; 402 } 403 404 /** 405 * Sets a url that links to the terms of use text. 406 * @param text The url. 407 * @see #getTermsOfUseURL() 408 */ 409 public void setTermsOfUseURL(String text) { 410 termsOfUseURL = text; 411 } 412 413 /** 414 * Returns the entry name. 415 * @return The entry name 416 * @since 6968 417 */ 418 public String getOriginalName() { 419 return this.origName != null ? this.origName : this.name; 420 } 421 422 /** 423 * Sets the entry name and handle translation. 424 * @param language The used language 425 * @param name The entry name 426 * @since 8091 427 */ 428 public void setName(String language, String name) { 429 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 430 if (LanguageInfo.isBetterLanguage(langName, language)) { 431 this.name = isdefault ? tr(name) : name; 432 this.langName = language; 433 } 434 if (origName == null || isdefault) { 435 this.origName = name; 436 } 437 } 438 439 /** 440 * Store the id of this info to the preferences and clear it afterwards. 441 */ 442 public void clearId() { 443 if (this.id != null) { 444 Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds")); 445 newAddedIds.add(this.id); 446 Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds)); 447 } 448 setId(null); 449 } 450 451 /** 452 * Determines if this entry is enabled by default. 453 * @return {@code true} if this entry is enabled by default, {@code false} otherwise 454 */ 455 public boolean isDefaultEntry() { 456 return defaultEntry; 457 } 458 459 /** 460 * Sets the default state of this entry. 461 * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise 462 */ 463 public void setDefaultEntry(boolean defaultEntry) { 464 this.defaultEntry = defaultEntry; 465 } 466 467 /** 468 * Returns the description text when existing. 469 * @return The description 470 * @since 8065 471 */ 472 public String getDescription() { 473 return this.description; 474 } 475 476 /** 477 * Sets the description text when existing. 478 * @param language The used language 479 * @param description the imagery description text 480 * @since 8091 481 */ 482 public void setDescription(String language, String description) { 483 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 484 if (LanguageInfo.isBetterLanguage(langDescription, language)) { 485 this.description = isdefault ? tr(description) : description; 486 this.langDescription = intern(language); 487 } 488 } 489 490 /** 491 * Return the sorted list of activated Imagery IDs. 492 * @return sorted list of activated Imagery IDs 493 * @since 13536 494 */ 495 public static <W> Collection<String> getActiveIds() { 496 ArrayList<String> ids = new ArrayList<>(); 497 IPreferences pref = Config.getPref(); 498 if (pref != null) { 499 List<W> entries = StructUtils.getListOfStructs( 500 pref, "imagery.entries", null, W.class); // TODO 501 if (entries != null) { 502 for (W prefEntry : entries) { 503 if (prefEntry.id != null && !prefEntry.id.isEmpty()) 504 ids.add(prefEntry.id); 505 } 506 Collections.sort(ids); 507 } 508 } 509 return ids; 510 } 511 512 /** 513 * Returns a tool tip text for display. 514 * @return The text 515 * @since 8065 516 */ 517 public String getToolTipText() { 518 StringBuilder res = new StringBuilder(getName()); 519 boolean html = false; 520 String dateStr = getDate(); 521 if (dateStr != null && !dateStr.isEmpty()) { 522 res.append("<br>").append(tr("Date of imagery: {0}", dateStr)); 523 html = true; 524 } 525 if (category != null && category.getDescription() != null) { 526 res.append("<br>").append(tr("Imagery category: {0}", category.getDescription())); 527 html = true; 528 } 529 String desc = getDescription(); 530 if (desc != null && !desc.isEmpty()) { 531 res.append("<br>").append(Utils.escapeReservedCharactersHTML(desc)); 532 html = true; 533 } 534 if (html) { 535 res.insert(0, "<html>").append("</html>"); 536 } 537 return res.toString(); 538 } 539 540 /** 541 * Returns the EULA acceptance URL, if any. 542 * @return The URL to an EULA text that has to be accepted before use, or {@code null} 543 */ 544 public String getEulaAcceptanceRequired() { 545 return eulaAcceptanceRequired; 546 } 547 548 /** 549 * Sets the EULA acceptance URL. 550 * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use 551 */ 552 public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) { 553 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 554 } 555 556 /** 557 * Returns the ISO 3166-1-alpha-2 country code. 558 * @return The country code (2 letters) 559 */ 560 public String getCountryCode() { 561 return countryCode; 562 } 563 564 /** 565 * Sets the ISO 3166-1-alpha-2 country code. 566 * @param countryCode The country code (2 letters) 567 */ 568 public void setCountryCode(String countryCode) { 569 this.countryCode = intern(countryCode); 570 } 571 572 /** 573 * Returns the date information. 574 * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where 575 * DD and MM as well as a second date are optional) 576 * @since 11570 577 */ 578 public String getDate() { 579 return date; 580 } 581 582 /** 583 * Sets the date information. 584 * @param date The date information 585 * @since 11570 586 */ 587 public void setDate(String date) { 588 this.date = date; 589 } 590 591 /** 592 * Returns the entry icon. 593 * @return The entry icon 594 */ 595 public String getIcon() { 596 return icon; 597 } 598 599 /** 600 * Sets the entry icon. 601 * @param icon The entry icon 602 */ 603 public void setIcon(String icon) { 604 this.icon = intern(icon); 605 } 606 607 /** 608 * Determines if this entry requires attribution. 609 * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise 610 */ 611 public boolean hasAttribution() { 612 return attributionText != null; 613 } 614 615 /** 616 * Copies attribution from another {@code ImageryInfo}. 617 * @param i The other imagery info to get attribution from 618 */ 619 public void copyAttribution(ImageryInfo i) { 620 this.attributionImage = i.attributionImage; 621 this.attributionImageURL = i.attributionImageURL; 622 this.attributionText = i.attributionText; 623 this.attributionLinkURL = i.attributionLinkURL; 624 this.termsOfUseText = i.termsOfUseText; 625 this.termsOfUseURL = i.termsOfUseURL; 626 } 627 628 /** 629 * Applies the attribution from this object to a tile source. 630 * @param s The tile source 631 */ 632 public void setAttribution(AbstractTileSource s) { 633 if (attributionText != null) { 634 if ("osm".equals(attributionText)) { 635 s.setAttributionText(new Mapnik().getAttributionText(0, null, null)); 636 } else { 637 s.setAttributionText(attributionText); 638 } 639 } 640 if (attributionLinkURL != null) { 641 if ("osm".equals(attributionLinkURL)) { 642 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL()); 643 } else { 644 s.setAttributionLinkURL(attributionLinkURL); 645 } 646 } 647 if (attributionImage != null) { 648 ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage); 649 if (i != null) { 650 s.setAttributionImage(i.getImage()); 651 } 652 } 653 if (attributionImageURL != null) { 654 s.setAttributionImageURL(attributionImageURL); 655 } 656 if (termsOfUseText != null) { 657 s.setTermsOfUseText(termsOfUseText); 658 } 659 if (termsOfUseURL != null) { 660 if ("osm".equals(termsOfUseURL)) { 661 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL()); 662 } else { 663 s.setTermsOfUseURL(termsOfUseURL); 664 } 665 } 666 } 667 668 /** 669 * Returns the source type. 670 * @return The source type 671 */ 672 public U getImageryType() { // TODO rename 673 return sourceType; 674 } 675 676 /** 677 * Sets the source type. 678 * @param imageryType The source type 679 */ 680 public void setImageryType(U imageryType) { // TODO rename 681 this.sourceType = imageryType; 682 } 683 684 /** 685 * Returns the imagery category. 686 * @return The imagery category 687 * @since 13792 688 */ 689 public T getImageryCategory() { // TODO rename 690 return category; 691 } 692 693 /** 694 * Sets the imagery category. 695 * @param category The imagery category 696 * @since 13792 697 */ 698 public void setImageryCategory(T category) { // TODO rename 699 this.category = category; 700 } 701 702 /** 703 * Returns the imagery category original string (don't use except for error checks). 704 * @return The imagery category original string 705 * @since 13792 706 */ 707 public String getImageryCategoryOriginalString() { 708 return categoryOriginalString; 709 } 710 711 /** 712 * Sets the imagery category original string (don't use except for error checks). 713 * @param categoryOriginalString The imagery category original string 714 * @since 13792 715 */ 716 public void setImageryCategoryOriginalString(String categoryOriginalString) { 717 this.categoryOriginalString = intern(categoryOriginalString); 718 } 719 720 /** 721 * Returns true if this layer's URL is matched by one of the regular 722 * expressions kept by the current OsmApi instance. 723 * @return {@code true} is this entry is blacklisted, {@code false} otherwise 724 */ 725 public boolean isBlacklisted() { 726 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities(); 727 return capabilities != null && capabilities.isOnImageryBlacklist(this.url); 728 } 729 730 /** 731 * Sets the map of <header name, header value> that if any of this header 732 * will be returned, then this tile will be treated as "no tile at this zoom level" 733 * 734 * @param noTileHeaders Map of <header name, header value> which will be treated as "no tile at this zoom level" 735 * @since 9613 736 */ 737 public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) { 738 if (noTileHeaders == null || noTileHeaders.isEmpty()) { 739 this.noTileHeaders = null; 740 } else { 741 this.noTileHeaders = noTileHeaders.toMap(); 742 } 743 } 744 745 @Override 746 public Map<String, Set<String>> getNoTileHeaders() { 747 return noTileHeaders; 748 } 749 750 /** 751 * Sets the map of <checksum type, checksum value> that if any tile with that checksum 752 * will be returned, then this tile will be treated as "no tile at this zoom level" 753 * 754 * @param noTileChecksums Map of <checksum type, checksum value> which will be treated as "no tile at this zoom level" 755 * @since 9613 756 */ 757 public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) { 758 if (noTileChecksums == null || noTileChecksums.isEmpty()) { 759 this.noTileChecksums = null; 760 } else { 761 this.noTileChecksums = noTileChecksums.toMap(); 762 } 763 } 764 765 @Override 766 public Map<String, Set<String>> getNoTileChecksums() { 767 return noTileChecksums; 768 } 769 770 /** 771 * Returns the map of <header name, metadata key> indicating, which HTTP headers should 772 * be moved to metadata 773 * 774 * @param metadataHeaders map of <header name, metadata key> indicating, which HTTP headers should be moved to metadata 775 * @since 8418 776 */ 777 public void setMetadataHeaders(Map<String, String> metadataHeaders) { 778 if (metadataHeaders == null || metadataHeaders.isEmpty()) { 779 this.metadataHeaders = null; 780 } else { 781 this.metadataHeaders = metadataHeaders; 782 } 783 } 784 785 /** 786 * Adds an old Id. 787 * 788 * @param id the Id to be added 789 * @since 13536 790 */ 791 public void addOldId(String id) { 792 if (oldIds == null) { 793 oldIds = new ArrayList<>(); 794 } 795 oldIds.add(id); 796 } 797 798 /** 799 * Get old Ids. 800 * 801 * @return collection of ids 802 * @since 13536 803 */ 804 public Collection<String> getOldIds() { 805 return oldIds; 806 } 807 808 /** 809 * Adds a mirror entry. Mirror entries are completed with the data from the master entry 810 * and only describe another method to access identical data. 811 * 812 * @param entry the mirror to be added 813 * @since 9658 814 */ 815 public void addMirror(SourceInfo<T, U, V, W> entry) { 816 if (mirrors == null) { 817 mirrors = new ArrayList<>(); 818 } 819 mirrors.add(entry); 820 } 821 822 /** 823 * Returns the mirror entries. Entries are completed with master entry data. 824 * 825 * @return the list of mirrors 826 * @since 9658 827 */ 828 public List<SourceInfo<T, U, V, W>> getMirrors() { 829 List<SourceInfo<T, U, V, W>> l = new ArrayList<>(); 830 if (mirrors != null) { 831 int num = 1; 832 for (SourceInfo<T, U, V, W> i : mirrors) { 833 ImageryInfo n = new ImageryInfo(this); 834 if (i.defaultMaxZoom != 0) { 835 n.defaultMaxZoom = i.defaultMaxZoom; 836 } 837 if (i.defaultMinZoom != 0) { 838 n.defaultMinZoom = i.defaultMinZoom; 839 } 840 n.setServerProjections(i.getServerProjections()); 841 n.url = i.url; 842 n.sourceType = i.sourceType; 843 if (i.getTileSize() != 0) { 844 n.setTileSize(i.getTileSize()); 845 } 846 if (i.getPrivacyPolicyURL() != null) { 847 n.setPrivacyPolicyURL(i.getPrivacyPolicyURL()); 848 } 849 if (n.id != null) { 850 n.id = n.id + "_mirror"+num; 851 } 852 if (num > 1) { 853 n.name = tr("{0} mirror server {1}", n.name, num); 854 if (n.origName != null) { 855 n.origName += " mirror server " + num; 856 } 857 } else { 858 n.name = tr("{0} mirror server", n.name); 859 if (n.origName != null) { 860 n.origName += " mirror server"; 861 } 862 } 863 l.add(n); 864 ++num; 865 } 866 } 867 return l; 868 } 869 870 /** 871 * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider) 872 * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer 873 * to work on 874 * @return Collection of the layer names 875 */ 876 public List<DefaultLayer> getDefaultLayers() { 877 return defaultLayers; 878 } 879 880 /** 881 * Sets the default layers that user will work with 882 * @param layers set the list of default layers 883 */ 884 public void setDefaultLayers(List<DefaultLayer> layers) { 885 this.defaultLayers = Utils.toUnmodifiableList(layers); 886 } 887 888 /** 889 * Returns custom HTTP headers that should be sent with request towards imagery provider 890 * @return headers 891 */ 892 public Map<String, String> getCustomHttpHeaders() { 893 return customHttpHeaders; 894 } 895 896 /** 897 * Sets custom HTTP headers that should be sent with request towards imagery provider 898 * @param customHttpHeaders http headers 899 */ 900 public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) { 901 this.customHttpHeaders = Utils.toUnmodifiableMap(customHttpHeaders); 902 } 903 904 /** 905 * Get a string representation of this imagery info suitable for the {@code source} changeset tag. 906 * @return English name, if known 907 * @since 13890 908 */ 909 public String getSourceName() { 910 if (ImageryType.BING == getImageryType()) { 911 return "Bing"; 912 } else { 913 if (id != null) { 914 // Retrieve english name, unfortunately not saved in preferences 915 Optional<SourceInfo<T, U, V, W>> infoEn = ImageryLayerInfo.allDefaultLayers.stream().filter(x -> id.equals(x.getId())).findAny(); 916 if (infoEn.isPresent()) { 917 return infoEn.get().getOriginalName(); 918 } 919 } 920 return getOriginalName(); 921 } 922 } 923 924 protected static String intern(String string) { 925 return string == null ? null : string.intern(); 926 } 927 } -
src/org/openstreetmap/josm/data/sources/SourcePreferenceEntry.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import java.util.Map; 5 6 import javax.json.stream.JsonCollectors; 7 8 import org.openstreetmap.josm.data.StructUtils.StructEntry; 9 import org.openstreetmap.josm.data.imagery.DefaultLayer; 10 import org.openstreetmap.josm.data.imagery.Shape; 11 import org.openstreetmap.josm.tools.MultiMap; 12 13 public class SourcePreferenceEntry<T extends SourceInfo<?, ?, ?, ?>> { 14 @StructEntry String name; 15 @StructEntry String d; 16 @StructEntry String id; 17 @StructEntry String type; 18 @StructEntry String url; 19 @StructEntry double pixel_per_eastnorth; 20 @StructEntry String eula; 21 @StructEntry String attribution_text; 22 @StructEntry String attribution_url; 23 @StructEntry String permission_reference_url; 24 @StructEntry String logo_image; 25 @StructEntry String logo_url; 26 @StructEntry String terms_of_use_text; 27 @StructEntry String terms_of_use_url; 28 @StructEntry String country_code = ""; 29 @StructEntry String date; 30 @StructEntry int max_zoom; 31 @StructEntry int min_zoom; 32 @StructEntry String cookies; 33 @StructEntry String bounds; 34 @StructEntry String shapes; 35 @StructEntry String projections; 36 @StructEntry String icon; 37 @StructEntry String description; 38 @StructEntry MultiMap<String, String> noTileHeaders; 39 @StructEntry MultiMap<String, String> noTileChecksums; 40 @StructEntry int tileSize = -1; 41 @StructEntry Map<String, String> metadataHeaders; 42 @StructEntry boolean valid_georeference; 43 @StructEntry boolean bestMarked; 44 @StructEntry boolean modTileFeatures; 45 @StructEntry boolean overlay; 46 @StructEntry String default_layers; 47 @StructEntry Map<String, String> customHttpHeaders; 48 @StructEntry boolean transparent; 49 @StructEntry int minimumTileExpire; 50 @StructEntry String category; 51 52 /** 53 * Constructs a new empty WMS {@code ImageryPreferenceEntry}. 54 */ 55 public SourcePreferenceEntry() { 56 // Do nothing 57 } 58 59 /** 60 * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}. 61 * @param i The corresponding imagery info 62 */ 63 public SourcePreferenceEntry(T i) { 64 name = i.name; 65 id = i.id; 66 type = i.sourceType.getTypeString(); 67 url = i.url; 68 pixel_per_eastnorth = i.pixelPerDegree; 69 eula = i.eulaAcceptanceRequired; 70 attribution_text = i.attributionText; 71 attribution_url = i.attributionLinkURL; 72 permission_reference_url = i.permissionReferenceURL; 73 date = i.date; 74 bestMarked = i.bestMarked; 75 overlay = i.overlay; 76 logo_image = i.attributionImage; 77 logo_url = i.attributionImageURL; 78 terms_of_use_text = i.termsOfUseText; 79 terms_of_use_url = i.termsOfUseURL; 80 country_code = i.countryCode; 81 max_zoom = i.defaultMaxZoom; 82 min_zoom = i.defaultMinZoom; 83 cookies = i.cookies; 84 icon = intern(i.icon); 85 description = i.description; 86 category = i.category != null ? i.category.getCategoryString() : null; 87 if (i.bounds != null) { 88 bounds = i.bounds.encodeAsString(","); 89 StringBuilder shapesString = new StringBuilder(); 90 for (Shape s : i.bounds.getShapes()) { 91 if (shapesString.length() > 0) { 92 shapesString.append(';'); 93 } 94 shapesString.append(s.encodeAsString(",")); 95 } 96 if (shapesString.length() > 0) { 97 shapes = shapesString.toString(); 98 } 99 } 100 if (!i.serverProjections.isEmpty()) { 101 projections = String.join(",", i.serverProjections); 102 } 103 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) { 104 noTileHeaders = new MultiMap<>(i.noTileHeaders); 105 } 106 107 if (i.noTileChecksums != null && !i.noTileChecksums.isEmpty()) { 108 noTileChecksums = new MultiMap<>(i.noTileChecksums); 109 } 110 111 if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) { 112 metadataHeaders = i.metadataHeaders; 113 } 114 115 tileSize = i.getTileSize(); 116 117 valid_georeference = i.isGeoreferenceValid(); 118 modTileFeatures = i.isModTileFeatures(); 119 if (!i.defaultLayers.isEmpty()) { 120 default_layers = i.defaultLayers.stream().map(DefaultLayer::toJson).collect(JsonCollectors.toJsonArray()).toString(); 121 } 122 customHttpHeaders = i.customHttpHeaders; 123 transparent = i.isTransparent(); 124 minimumTileExpire = i.minimumTileExpire; 125 } 126 127 @Override 128 public String toString(); 129 } -
src/org/openstreetmap/josm/data/sources/ICommonSource.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 /** 5 * This interface is used to ensure that a class can get a enum from a string. 6 * For various reasons, the fromString method cannot be implemented statically. 7 * 8 * @author Taylor Smock 9 * @since xxx 10 * 11 * @param <T> The enum type 12 */ 13 public interface ICommonSource<T extends Enum<T>> { 14 /** 15 * Get the default value for the Enum 16 * @return The default value 17 */ 18 public T getDefault(); 19 20 /** 21 * Returns the source category from the given category string. 22 * @param s The category string 23 * @return the source category matching the given category string 24 */ 25 public T fromString(String s); 26 } -
src/org/openstreetmap/josm/data/sources/ISourceCategory.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import javax.swing.ImageIcon; 5 6 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 7 8 /** 9 * @author Taylor Smock 10 * 11 * @param <T> The enum that is extending this interface 12 * @since xxx 13 */ 14 public interface ISourceCategory<T extends Enum<T>> extends ICommonSource<T> { 15 16 /** 17 * Returns the unique string identifying this category. 18 * @return the unique string identifying this category 19 */ 20 public String getCategoryString(); 21 22 /** 23 * Returns the description of this category. 24 * @return the description of this category 25 */ 26 public String getDescription(); 27 28 /** 29 * Returns the category icon at the given size. 30 * @param size icon wanted size 31 * @return the category icon at the given size 32 */ 33 public ImageIcon getIcon(ImageSizes size); 34 } -
src/org/openstreetmap/josm/data/sources/ISourceType.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 /** 5 * This interface should only be used for Enums 6 * @author Taylor Smock 7 * @since xxx 8 * 9 * @param <T> The source type (e.g., Imagery or otherwise -- should be the name of the class) 10 */ 11 public interface ISourceType<T extends Enum<T>> extends ICommonSource<T> { 12 13 /** 14 * Returns the unique string identifying this type. 15 * @return the unique string identifying this type 16 */ 17 public String getTypeString(); 18 } -
src/org/openstreetmap/josm/data/sources/SourceBounds.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import java.util.ArrayList; 5 import java.util.List; 6 import java.util.Objects; 7 8 import org.openstreetmap.josm.data.Bounds; 9 import org.openstreetmap.josm.data.imagery.Shape; 10 11 /** 12 * 13 * Multi-polygon bounds for source backgrounds. 14 * Used to display source coverage in preferences and to determine relevant source entries based on edit location. 15 * 16 * @author Frederik Ramm, extracted by Taylor Smock 17 * @since xxx (extracted from {@link org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds}) 18 */ 19 public class SourceBounds extends Bounds { 20 21 /** 22 * Constructs a new {@code SourceBounds} from string. 23 * @param asString The string containing the list of shapes defining this bounds 24 * @param separator The shape separator in the given string, usually a comma 25 */ 26 public SourceBounds(String asString, String separator) { 27 super(asString, separator); 28 } 29 30 private List<Shape> shapes = new ArrayList<>(); 31 32 /** 33 * Adds a new shape to this bounds. 34 * @param shape The shape to add 35 */ 36 public final void addShape(Shape shape) { 37 this.shapes.add(shape); 38 } 39 40 /** 41 * Sets the list of shapes defining this bounds. 42 * @param shapes The list of shapes defining this bounds. 43 */ 44 public final void setShapes(List<Shape> shapes) { 45 this.shapes = shapes; 46 } 47 48 /** 49 * Returns the list of shapes defining this bounds. 50 * @return The list of shapes defining this bounds 51 */ 52 public final List<Shape> getShapes() { 53 return shapes; 54 } 55 56 @Override 57 public int hashCode() { 58 return Objects.hash(super.hashCode(), shapes); 59 } 60 61 @Override 62 public boolean equals(Object o) { 63 if (this == o) return true; 64 if (o == null || getClass() != o.getClass()) return false; 65 if (!super.equals(o)) return false; 66 SourceBounds that = (SourceBounds) o; 67 return Objects.equals(shapes, that.shapes); 68 } 69 } -
src/org/openstreetmap/josm/data/sources/SourceInfo.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.Image; 7 import java.lang.reflect.ParameterizedType; 8 import java.util.ArrayList; 9 import java.util.Collection; 10 import java.util.Collections; 11 import java.util.HashMap; 12 import java.util.List; 13 import java.util.Locale; 14 import java.util.Map; 15 import java.util.Objects; 16 import java.util.Optional; 17 import java.util.Set; 18 import java.util.TreeSet; 19 20 import javax.swing.ImageIcon; 21 22 import org.openstreetmap.gui.jmapviewer.interfaces.Attributed; 23 import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 24 import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource; 25 import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik; 26 import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo; 27 import org.openstreetmap.josm.data.StructUtils; 28 import org.openstreetmap.josm.data.imagery.DefaultLayer; 29 import org.openstreetmap.josm.data.imagery.ImageryInfo; 30 import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType; 31 import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; 32 import org.openstreetmap.josm.io.Capabilities; 33 import org.openstreetmap.josm.io.OsmApi; 34 import org.openstreetmap.josm.spi.preferences.Config; 35 import org.openstreetmap.josm.spi.preferences.IPreferences; 36 import org.openstreetmap.josm.tools.ImageProvider; 37 import org.openstreetmap.josm.tools.LanguageInfo; 38 import org.openstreetmap.josm.tools.MultiMap; 39 import org.openstreetmap.josm.tools.Utils; 40 41 /** 42 * @author Taylor Smock 43 * @param <T> The SourceCategory type 44 * @param <U> The SourceType 45 * @param <V> The SourceBounds 46 * 47 * @since xxx 48 */ 49 public class SourceInfo<T extends ISourceCategory<?>, U extends ISourceType<?>, V extends SourceBounds, W extends SourcePreferenceEntry<?>> extends TileSourceInfo implements Comparable<SourceInfo<T, U, V, W>>, Attributed { 50 /** original name of the source entry in case of translation call, for multiple languages English when possible */ 51 protected String origName; 52 /** (original) language of the translated name entry */ 53 protected String langName; 54 /** whether this is a entry activated by default or not */ 55 protected boolean defaultEntry; 56 /** Whether this service requires a explicit EULA acceptance before it can be activated */ 57 protected String eulaAcceptanceRequired; 58 /** type of the services - WMS, TMS, ... */ 59 @SuppressWarnings("unchecked") 60 protected U sourceType = (U) ((U) ((Enum<?>)((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).getDeclaringClass().getEnumConstants()[0]).getDefault(); 61 /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */ 62 protected V bounds; 63 /** description of the imagery entry, should contain notes what type of data it is */ 64 protected String description; 65 /** language of the description entry */ 66 protected String langDescription; 67 /** Text of a text attribution displayed when using the imagery */ 68 protected String attributionText; 69 /** Link to the privacy policy of the operator */ 70 protected String privacyPolicyURL; 71 /** Link to a reference stating the permission for OSM usage */ 72 protected String permissionReferenceURL; 73 /** Link behind the text attribution displayed when using the imagery */ 74 protected String attributionLinkURL; 75 /** Image of a graphical attribution displayed when using the imagery */ 76 protected String attributionImage; 77 /** Link behind the graphical attribution displayed when using the imagery */ 78 protected String attributionImageURL; 79 /** Text with usage terms displayed when using the imagery */ 80 protected String termsOfUseText; 81 /** Link behind the text with usage terms displayed when using the imagery */ 82 protected String termsOfUseURL; 83 /** country code of the imagery (for country specific imagery) */ 84 protected String countryCode = ""; 85 /** 86 * creation date of the source (in the form YYYY-MM-DD;YYYY-MM-DD, where 87 * DD and MM as well as a second date are optional). 88 * 89 * Also used as time filter for WMS time={time} parameter (such as Sentinel-2) 90 * @since 11570 91 */ 92 protected String date; 93 /** 94 * list of old IDs, only for loading, not handled anywhere else 95 * @since 13536 96 */ 97 protected Collection<String> oldIds; 98 /** mirrors of different type for this entry */ 99 protected List<SourceInfo<T, U, V, W>> mirrors; 100 /** icon used in menu */ 101 protected String icon; 102 /** which layers should be activated by default on layer addition. **/ 103 protected List<DefaultLayer> defaultLayers = Collections.emptyList(); 104 /** HTTP headers **/ 105 protected Map<String, String> customHttpHeaders = Collections.emptyMap(); 106 /** category of the imagery */ 107 protected T category; 108 /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */ 109 protected String categoryOriginalString; 110 /** when adding a field, also adapt the: 111 * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object} 112 * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor} 113 * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor} 114 * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor} 115 * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method} 116 **/ 117 118 /** 119 * Creates empty SourceInfo class 120 */ 121 public SourceInfo() { 122 super(); 123 } 124 125 /** 126 * Create a SourceInfo class 127 * 128 * @param name name 129 */ 130 public SourceInfo(String name) { 131 super(name); 132 } 133 134 /** 135 * Create a SourceInfo class 136 * 137 * @param name name 138 * @param url base URL 139 * @param id unique id 140 */ 141 public SourceInfo(String name, String url, String id) { 142 super(name, url, id); 143 } 144 145 146 @Override 147 public int hashCode() { 148 return Objects.hash(url, sourceType); 149 } 150 151 /** 152 * Check if this object equals another SourceInfo with respect to the properties 153 * that get written to the preference file. 154 * 155 * This should be overridden and called in subclasses. 156 * 157 * @param other the SourceInfo object to compare to 158 * @return true if they are equal 159 */ 160 public boolean equalsPref(SourceInfo<T, U, V, W> other) { 161 if (other == null) { 162 return false; 163 } 164 165 // CHECKSTYLE.OFF: BooleanExpressionComplexity 166 return 167 Objects.equals(this.name, other.name) && 168 Objects.equals(this.id, other.id) && 169 Objects.equals(this.url, other.url) && 170 Objects.equals(this.modTileFeatures, other.modTileFeatures) && 171 Objects.equals(this.cookies, other.cookies) && 172 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) && 173 Objects.equals(this.sourceType, other.sourceType) && 174 Objects.equals(this.bounds, other.bounds) && 175 Objects.equals(this.attributionText, other.attributionText) && 176 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) && 177 Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) && 178 Objects.equals(this.attributionImageURL, other.attributionImageURL) && 179 Objects.equals(this.attributionImage, other.attributionImage) && 180 Objects.equals(this.termsOfUseText, other.termsOfUseText) && 181 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) && 182 Objects.equals(this.countryCode, other.countryCode) && 183 Objects.equals(this.date, other.date) && 184 Objects.equals(this.icon, other.icon) && 185 Objects.equals(this.description, other.description) && 186 Objects.equals(this.noTileHeaders, other.noTileHeaders) && 187 Objects.equals(this.noTileChecksums, other.noTileChecksums) && 188 Objects.equals(this.metadataHeaders, other.metadataHeaders) && 189 Objects.equals(this.defaultLayers, other.defaultLayers) && 190 Objects.equals(this.customHttpHeaders, other.customHttpHeaders) && 191 Objects.equals(this.category, other.category); 192 // CHECKSTYLE.ON: BooleanExpressionComplexity 193 } 194 195 @Override 196 public boolean equals(Object o) { 197 if (this == o) return true; 198 if (o == null || getClass() != o.getClass()) return false; 199 ImageryInfo that = (ImageryInfo) o; 200 return sourceType == that.sourceType && Objects.equals(url, that.url); 201 } 202 203 private static final Map<String, String> localizedCountriesCache = new HashMap<>(); 204 static { 205 localizedCountriesCache.put("", tr("Worldwide")); 206 } 207 208 /** 209 * Returns a localized name for the given country code, or "Worldwide" if empty. 210 * This function falls back on the English name, and uses the ISO code as a last-resortvalue. 211 * 212 * @param countryCode An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code 213 * @return The name of the country appropriate to the current locale. 214 * @see Locale#getDisplayCountry 215 * @since 15158 216 */ 217 public static String getLocalizedCountry(String countryCode) { 218 return localizedCountriesCache.computeIfAbsent(countryCode, code -> new Locale("en", code).getDisplayCountry()); 219 } 220 221 @Override 222 public String toString() { 223 // Used in imagery preferences filtering, so must be efficient 224 return new StringBuilder(name) 225 .append('[').append(countryCode) 226 // appending the localized country in toString() allows us to filter imagery preferences table with it! 227 .append("] ('").append(getLocalizedCountry(countryCode)).append(')') 228 .append(" - ").append(url) 229 .append(" - ").append(sourceType) 230 .toString(); 231 } 232 233 @Override 234 public int compareTo(SourceInfo<T, U, V, W> in) { 235 int i = countryCode.compareTo(in.countryCode); 236 if (i == 0) { 237 i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH)); 238 } 239 if (i == 0) { 240 i = url.compareTo(in.url); 241 } 242 return i; 243 } 244 245 /** 246 * Determines if URL is equal to given imagery info. 247 * @param in imagery info 248 * @return {@code true} if URL is equal to given imagery info 249 */ 250 public boolean equalsBaseValues(ImageryInfo in) { 251 return url.equals(in.url); 252 } 253 254 /** 255 * Sets the source polygonial bounds. 256 * @param b The source bounds (non-rectangular) 257 */ 258 public void setBounds(V b) { 259 this.bounds = b; 260 } 261 262 /** 263 * Returns the source polygonial bounds. 264 * @return The source bounds (non-rectangular) 265 */ 266 public V getBounds() { 267 return bounds; 268 } 269 270 @Override 271 public boolean requiresAttribution() { 272 return attributionText != null || attributionLinkURL != null || attributionImage != null 273 || termsOfUseText != null || termsOfUseURL != null; 274 } 275 276 @Override 277 public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) { 278 return attributionText; 279 } 280 281 @Override 282 public String getAttributionLinkURL() { 283 return attributionLinkURL; 284 } 285 286 /** 287 * Return the permission reference URL. 288 * @return The url 289 * @see #setPermissionReferenceURL 290 * @since 11975 291 */ 292 public String getPermissionReferenceURL() { 293 return permissionReferenceURL; 294 } 295 296 /** 297 * Return the privacy policy URL. 298 * @return The url 299 * @see #setPrivacyPolicyURL 300 * @since 16127 301 */ 302 public String getPrivacyPolicyURL() { 303 return privacyPolicyURL; 304 } 305 306 @Override 307 public Image getAttributionImage() { 308 ImageIcon i = ImageProvider.getIfAvailable(attributionImage); 309 if (i != null) { 310 return i.getImage(); 311 } 312 return null; 313 } 314 315 /** 316 * Return the raw attribution logo information (an URL to the image). 317 * @return The url text 318 * @since 12257 319 */ 320 public String getAttributionImageRaw() { 321 return attributionImage; 322 } 323 324 @Override 325 public String getAttributionImageURL() { 326 return attributionImageURL; 327 } 328 329 @Override 330 public String getTermsOfUseText() { 331 return termsOfUseText; 332 } 333 334 @Override 335 public String getTermsOfUseURL() { 336 return termsOfUseURL; 337 } 338 339 /** 340 * Set the attribution text 341 * @param text The text 342 * @see #getAttributionText(int, ICoordinate, ICoordinate) 343 */ 344 public void setAttributionText(String text) { 345 attributionText = intern(text); 346 } 347 348 /** 349 * Set the attribution image 350 * @param url The url of the image. 351 * @see #getAttributionImageURL() 352 */ 353 public void setAttributionImageURL(String url) { 354 attributionImageURL = url; 355 } 356 357 /** 358 * Set the image for the attribution 359 * @param res The image resource 360 * @see #getAttributionImage() 361 */ 362 public void setAttributionImage(String res) { 363 attributionImage = res; 364 } 365 366 /** 367 * Sets the URL the attribution should link to. 368 * @param url The url. 369 * @see #getAttributionLinkURL() 370 */ 371 public void setAttributionLinkURL(String url) { 372 attributionLinkURL = url; 373 } 374 375 /** 376 * Sets the permission reference URL. 377 * @param url The url. 378 * @see #getPermissionReferenceURL() 379 * @since 11975 380 */ 381 public void setPermissionReferenceURL(String url) { 382 permissionReferenceURL = url; 383 } 384 385 /** 386 * Sets the privacy policy URL. 387 * @param url The url. 388 * @see #getPrivacyPolicyURL() 389 * @since 16127 390 */ 391 public void setPrivacyPolicyURL(String url) { 392 privacyPolicyURL = url; 393 } 394 395 /** 396 * Sets the text to display to the user as terms of use. 397 * @param text The text 398 * @see #getTermsOfUseText() 399 */ 400 public void setTermsOfUseText(String text) { 401 termsOfUseText = text; 402 } 403 404 /** 405 * Sets a url that links to the terms of use text. 406 * @param text The url. 407 * @see #getTermsOfUseURL() 408 */ 409 public void setTermsOfUseURL(String text) { 410 termsOfUseURL = text; 411 } 412 413 /** 414 * Returns the entry name. 415 * @return The entry name 416 * @since 6968 417 */ 418 public String getOriginalName() { 419 return this.origName != null ? this.origName : this.name; 420 } 421 422 /** 423 * Sets the entry name and handle translation. 424 * @param language The used language 425 * @param name The entry name 426 * @since 8091 427 */ 428 public void setName(String language, String name) { 429 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 430 if (LanguageInfo.isBetterLanguage(langName, language)) { 431 this.name = isdefault ? tr(name) : name; 432 this.langName = language; 433 } 434 if (origName == null || isdefault) { 435 this.origName = name; 436 } 437 } 438 439 /** 440 * Store the id of this info to the preferences and clear it afterwards. 441 */ 442 public void clearId() { 443 if (this.id != null) { 444 Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds")); 445 newAddedIds.add(this.id); 446 Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds)); 447 } 448 setId(null); 449 } 450 451 /** 452 * Determines if this entry is enabled by default. 453 * @return {@code true} if this entry is enabled by default, {@code false} otherwise 454 */ 455 public boolean isDefaultEntry() { 456 return defaultEntry; 457 } 458 459 /** 460 * Sets the default state of this entry. 461 * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise 462 */ 463 public void setDefaultEntry(boolean defaultEntry) { 464 this.defaultEntry = defaultEntry; 465 } 466 467 /** 468 * Returns the description text when existing. 469 * @return The description 470 * @since 8065 471 */ 472 public String getDescription() { 473 return this.description; 474 } 475 476 /** 477 * Sets the description text when existing. 478 * @param language The used language 479 * @param description the imagery description text 480 * @since 8091 481 */ 482 public void setDescription(String language, String description) { 483 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language); 484 if (LanguageInfo.isBetterLanguage(langDescription, language)) { 485 this.description = isdefault ? tr(description) : description; 486 this.langDescription = intern(language); 487 } 488 } 489 490 /** 491 * Return the sorted list of activated Imagery IDs. 492 * @return sorted list of activated Imagery IDs 493 * @since 13536 494 */ 495 public static <W> Collection<String> getActiveIds() { 496 ArrayList<String> ids = new ArrayList<>(); 497 IPreferences pref = Config.getPref(); 498 if (pref != null) { 499 List<W> entries = StructUtils.getListOfStructs( 500 pref, "imagery.entries", null, W.class); // TODO 501 if (entries != null) { 502 for (W prefEntry : entries) { 503 if (prefEntry.id != null && !prefEntry.id.isEmpty()) 504 ids.add(prefEntry.id); 505 } 506 Collections.sort(ids); 507 } 508 } 509 return ids; 510 } 511 512 /** 513 * Returns a tool tip text for display. 514 * @return The text 515 * @since 8065 516 */ 517 public String getToolTipText() { 518 StringBuilder res = new StringBuilder(getName()); 519 boolean html = false; 520 String dateStr = getDate(); 521 if (dateStr != null && !dateStr.isEmpty()) { 522 res.append("<br>").append(tr("Date of imagery: {0}", dateStr)); 523 html = true; 524 } 525 if (category != null && category.getDescription() != null) { 526 res.append("<br>").append(tr("Imagery category: {0}", category.getDescription())); 527 html = true; 528 } 529 String desc = getDescription(); 530 if (desc != null && !desc.isEmpty()) { 531 res.append("<br>").append(Utils.escapeReservedCharactersHTML(desc)); 532 html = true; 533 } 534 if (html) { 535 res.insert(0, "<html>").append("</html>"); 536 } 537 return res.toString(); 538 } 539 540 /** 541 * Returns the EULA acceptance URL, if any. 542 * @return The URL to an EULA text that has to be accepted before use, or {@code null} 543 */ 544 public String getEulaAcceptanceRequired() { 545 return eulaAcceptanceRequired; 546 } 547 548 /** 549 * Sets the EULA acceptance URL. 550 * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use 551 */ 552 public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) { 553 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 554 } 555 556 /** 557 * Returns the ISO 3166-1-alpha-2 country code. 558 * @return The country code (2 letters) 559 */ 560 public String getCountryCode() { 561 return countryCode; 562 } 563 564 /** 565 * Sets the ISO 3166-1-alpha-2 country code. 566 * @param countryCode The country code (2 letters) 567 */ 568 public void setCountryCode(String countryCode) { 569 this.countryCode = intern(countryCode); 570 } 571 572 /** 573 * Returns the date information. 574 * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where 575 * DD and MM as well as a second date are optional) 576 * @since 11570 577 */ 578 public String getDate() { 579 return date; 580 } 581 582 /** 583 * Sets the date information. 584 * @param date The date information 585 * @since 11570 586 */ 587 public void setDate(String date) { 588 this.date = date; 589 } 590 591 /** 592 * Returns the entry icon. 593 * @return The entry icon 594 */ 595 public String getIcon() { 596 return icon; 597 } 598 599 /** 600 * Sets the entry icon. 601 * @param icon The entry icon 602 */ 603 public void setIcon(String icon) { 604 this.icon = intern(icon); 605 } 606 607 /** 608 * Determines if this entry requires attribution. 609 * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise 610 */ 611 public boolean hasAttribution() { 612 return attributionText != null; 613 } 614 615 /** 616 * Copies attribution from another {@code ImageryInfo}. 617 * @param i The other imagery info to get attribution from 618 */ 619 public void copyAttribution(ImageryInfo i) { 620 this.attributionImage = i.attributionImage; 621 this.attributionImageURL = i.attributionImageURL; 622 this.attributionText = i.attributionText; 623 this.attributionLinkURL = i.attributionLinkURL; 624 this.termsOfUseText = i.termsOfUseText; 625 this.termsOfUseURL = i.termsOfUseURL; 626 } 627 628 /** 629 * Applies the attribution from this object to a tile source. 630 * @param s The tile source 631 */ 632 public void setAttribution(AbstractTileSource s) { 633 if (attributionText != null) { 634 if ("osm".equals(attributionText)) { 635 s.setAttributionText(new Mapnik().getAttributionText(0, null, null)); 636 } else { 637 s.setAttributionText(attributionText); 638 } 639 } 640 if (attributionLinkURL != null) { 641 if ("osm".equals(attributionLinkURL)) { 642 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL()); 643 } else { 644 s.setAttributionLinkURL(attributionLinkURL); 645 } 646 } 647 if (attributionImage != null) { 648 ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage); 649 if (i != null) { 650 s.setAttributionImage(i.getImage()); 651 } 652 } 653 if (attributionImageURL != null) { 654 s.setAttributionImageURL(attributionImageURL); 655 } 656 if (termsOfUseText != null) { 657 s.setTermsOfUseText(termsOfUseText); 658 } 659 if (termsOfUseURL != null) { 660 if ("osm".equals(termsOfUseURL)) { 661 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL()); 662 } else { 663 s.setTermsOfUseURL(termsOfUseURL); 664 } 665 } 666 } 667 668 /** 669 * Returns the source type. 670 * @return The source type 671 */ 672 public U getImageryType() { // TODO rename 673 return sourceType; 674 } 675 676 /** 677 * Sets the source type. 678 * @param imageryType The source type 679 */ 680 public void setImageryType(U imageryType) { // TODO rename 681 this.sourceType = imageryType; 682 } 683 684 /** 685 * Returns the imagery category. 686 * @return The imagery category 687 * @since 13792 688 */ 689 public T getImageryCategory() { // TODO rename 690 return category; 691 } 692 693 /** 694 * Sets the imagery category. 695 * @param category The imagery category 696 * @since 13792 697 */ 698 public void setImageryCategory(T category) { // TODO rename 699 this.category = category; 700 } 701 702 /** 703 * Returns the imagery category original string (don't use except for error checks). 704 * @return The imagery category original string 705 * @since 13792 706 */ 707 public String getImageryCategoryOriginalString() { 708 return categoryOriginalString; 709 } 710 711 /** 712 * Sets the imagery category original string (don't use except for error checks). 713 * @param categoryOriginalString The imagery category original string 714 * @since 13792 715 */ 716 public void setImageryCategoryOriginalString(String categoryOriginalString) { 717 this.categoryOriginalString = intern(categoryOriginalString); 718 } 719 720 /** 721 * Returns true if this layer's URL is matched by one of the regular 722 * expressions kept by the current OsmApi instance. 723 * @return {@code true} is this entry is blacklisted, {@code false} otherwise 724 */ 725 public boolean isBlacklisted() { 726 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities(); 727 return capabilities != null && capabilities.isOnImageryBlacklist(this.url); 728 } 729 730 /** 731 * Sets the map of <header name, header value> that if any of this header 732 * will be returned, then this tile will be treated as "no tile at this zoom level" 733 * 734 * @param noTileHeaders Map of <header name, header value> which will be treated as "no tile at this zoom level" 735 * @since 9613 736 */ 737 public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) { 738 if (noTileHeaders == null || noTileHeaders.isEmpty()) { 739 this.noTileHeaders = null; 740 } else { 741 this.noTileHeaders = noTileHeaders.toMap(); 742 } 743 } 744 745 @Override 746 public Map<String, Set<String>> getNoTileHeaders() { 747 return noTileHeaders; 748 } 749 750 /** 751 * Sets the map of <checksum type, checksum value> that if any tile with that checksum 752 * will be returned, then this tile will be treated as "no tile at this zoom level" 753 * 754 * @param noTileChecksums Map of <checksum type, checksum value> which will be treated as "no tile at this zoom level" 755 * @since 9613 756 */ 757 public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) { 758 if (noTileChecksums == null || noTileChecksums.isEmpty()) { 759 this.noTileChecksums = null; 760 } else { 761 this.noTileChecksums = noTileChecksums.toMap(); 762 } 763 } 764 765 @Override 766 public Map<String, Set<String>> getNoTileChecksums() { 767 return noTileChecksums; 768 } 769 770 /** 771 * Returns the map of <header name, metadata key> indicating, which HTTP headers should 772 * be moved to metadata 773 * 774 * @param metadataHeaders map of <header name, metadata key> indicating, which HTTP headers should be moved to metadata 775 * @since 8418 776 */ 777 public void setMetadataHeaders(Map<String, String> metadataHeaders) { 778 if (metadataHeaders == null || metadataHeaders.isEmpty()) { 779 this.metadataHeaders = null; 780 } else { 781 this.metadataHeaders = metadataHeaders; 782 } 783 } 784 785 /** 786 * Adds an old Id. 787 * 788 * @param id the Id to be added 789 * @since 13536 790 */ 791 public void addOldId(String id) { 792 if (oldIds == null) { 793 oldIds = new ArrayList<>(); 794 } 795 oldIds.add(id); 796 } 797 798 /** 799 * Get old Ids. 800 * 801 * @return collection of ids 802 * @since 13536 803 */ 804 public Collection<String> getOldIds() { 805 return oldIds; 806 } 807 808 /** 809 * Adds a mirror entry. Mirror entries are completed with the data from the master entry 810 * and only describe another method to access identical data. 811 * 812 * @param entry the mirror to be added 813 * @since 9658 814 */ 815 public void addMirror(SourceInfo<T, U, V, W> entry) { 816 if (mirrors == null) { 817 mirrors = new ArrayList<>(); 818 } 819 mirrors.add(entry); 820 } 821 822 /** 823 * Returns the mirror entries. Entries are completed with master entry data. 824 * 825 * @return the list of mirrors 826 * @since 9658 827 */ 828 public List<SourceInfo<T, U, V, W>> getMirrors() { 829 List<SourceInfo<T, U, V, W>> l = new ArrayList<>(); 830 if (mirrors != null) { 831 int num = 1; 832 for (SourceInfo<T, U, V, W> i : mirrors) { 833 ImageryInfo n = new ImageryInfo(this); 834 if (i.defaultMaxZoom != 0) { 835 n.defaultMaxZoom = i.defaultMaxZoom; 836 } 837 if (i.defaultMinZoom != 0) { 838 n.defaultMinZoom = i.defaultMinZoom; 839 } 840 n.setServerProjections(i.getServerProjections()); 841 n.url = i.url; 842 n.sourceType = i.sourceType; 843 if (i.getTileSize() != 0) { 844 n.setTileSize(i.getTileSize()); 845 } 846 if (i.getPrivacyPolicyURL() != null) { 847 n.setPrivacyPolicyURL(i.getPrivacyPolicyURL()); 848 } 849 if (n.id != null) { 850 n.id = n.id + "_mirror"+num; 851 } 852 if (num > 1) { 853 n.name = tr("{0} mirror server {1}", n.name, num); 854 if (n.origName != null) { 855 n.origName += " mirror server " + num; 856 } 857 } else { 858 n.name = tr("{0} mirror server", n.name); 859 if (n.origName != null) { 860 n.origName += " mirror server"; 861 } 862 } 863 l.add(n); 864 ++num; 865 } 866 } 867 return l; 868 } 869 870 /** 871 * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider) 872 * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer 873 * to work on 874 * @return Collection of the layer names 875 */ 876 public List<DefaultLayer> getDefaultLayers() { 877 return defaultLayers; 878 } 879 880 /** 881 * Sets the default layers that user will work with 882 * @param layers set the list of default layers 883 */ 884 public void setDefaultLayers(List<DefaultLayer> layers) { 885 this.defaultLayers = Utils.toUnmodifiableList(layers); 886 } 887 888 /** 889 * Returns custom HTTP headers that should be sent with request towards imagery provider 890 * @return headers 891 */ 892 public Map<String, String> getCustomHttpHeaders() { 893 return customHttpHeaders; 894 } 895 896 /** 897 * Sets custom HTTP headers that should be sent with request towards imagery provider 898 * @param customHttpHeaders http headers 899 */ 900 public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) { 901 this.customHttpHeaders = Utils.toUnmodifiableMap(customHttpHeaders); 902 } 903 904 /** 905 * Get a string representation of this imagery info suitable for the {@code source} changeset tag. 906 * @return English name, if known 907 * @since 13890 908 */ 909 public String getSourceName() { 910 if (ImageryType.BING == getImageryType()) { 911 return "Bing"; 912 } else { 913 if (id != null) { 914 // Retrieve english name, unfortunately not saved in preferences 915 Optional<SourceInfo<T, U, V, W>> infoEn = ImageryLayerInfo.allDefaultLayers.stream().filter(x -> id.equals(x.getId())).findAny(); 916 if (infoEn.isPresent()) { 917 return infoEn.get().getOriginalName(); 918 } 919 } 920 return getOriginalName(); 921 } 922 } 923 924 protected static String intern(String string) { 925 return string == null ? null : string.intern(); 926 } 927 } -
src/org/openstreetmap/josm/data/sources/SourcePreferenceEntry.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.sources; 3 4 import java.util.Map; 5 6 import javax.json.stream.JsonCollectors; 7 8 import org.openstreetmap.josm.data.StructUtils.StructEntry; 9 import org.openstreetmap.josm.data.imagery.DefaultLayer; 10 import org.openstreetmap.josm.data.imagery.Shape; 11 import org.openstreetmap.josm.tools.MultiMap; 12 13 public class SourcePreferenceEntry<T extends SourceInfo<?, ?, ?, ?>> { 14 @StructEntry String name; 15 @StructEntry String d; 16 @StructEntry String id; 17 @StructEntry String type; 18 @StructEntry String url; 19 @StructEntry double pixel_per_eastnorth; 20 @StructEntry String eula; 21 @StructEntry String attribution_text; 22 @StructEntry String attribution_url; 23 @StructEntry String permission_reference_url; 24 @StructEntry String logo_image; 25 @StructEntry String logo_url; 26 @StructEntry String terms_of_use_text; 27 @StructEntry String terms_of_use_url; 28 @StructEntry String country_code = ""; 29 @StructEntry String date; 30 @StructEntry int max_zoom; 31 @StructEntry int min_zoom; 32 @StructEntry String cookies; 33 @StructEntry String bounds; 34 @StructEntry String shapes; 35 @StructEntry String projections; 36 @StructEntry String icon; 37 @StructEntry String description; 38 @StructEntry MultiMap<String, String> noTileHeaders; 39 @StructEntry MultiMap<String, String> noTileChecksums; 40 @StructEntry int tileSize = -1; 41 @StructEntry Map<String, String> metadataHeaders; 42 @StructEntry boolean valid_georeference; 43 @StructEntry boolean bestMarked; 44 @StructEntry boolean modTileFeatures; 45 @StructEntry boolean overlay; 46 @StructEntry String default_layers; 47 @StructEntry Map<String, String> customHttpHeaders; 48 @StructEntry boolean transparent; 49 @StructEntry int minimumTileExpire; 50 @StructEntry String category; 51 52 /** 53 * Constructs a new empty WMS {@code ImageryPreferenceEntry}. 54 */ 55 public SourcePreferenceEntry() { 56 // Do nothing 57 } 58 59 /** 60 * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}. 61 * @param i The corresponding imagery info 62 */ 63 public SourcePreferenceEntry(T i) { 64 name = i.name; 65 id = i.id; 66 type = i.sourceType.getTypeString(); 67 url = i.url; 68 pixel_per_eastnorth = i.pixelPerDegree; 69 eula = i.eulaAcceptanceRequired; 70 attribution_text = i.attributionText; 71 attribution_url = i.attributionLinkURL; 72 permission_reference_url = i.permissionReferenceURL; 73 date = i.date; 74 bestMarked = i.bestMarked; 75 overlay = i.overlay; 76 logo_image = i.attributionImage; 77 logo_url = i.attributionImageURL; 78 terms_of_use_text = i.termsOfUseText; 79 terms_of_use_url = i.termsOfUseURL; 80 country_code = i.countryCode; 81 max_zoom = i.defaultMaxZoom; 82 min_zoom = i.defaultMinZoom; 83 cookies = i.cookies; 84 icon = intern(i.icon); 85 description = i.description; 86 category = i.category != null ? i.category.getCategoryString() : null; 87 if (i.bounds != null) { 88 bounds = i.bounds.encodeAsString(","); 89 StringBuilder shapesString = new StringBuilder(); 90 for (Shape s : i.bounds.getShapes()) { 91 if (shapesString.length() > 0) { 92 shapesString.append(';'); 93 } 94 shapesString.append(s.encodeAsString(",")); 95 } 96 if (shapesString.length() > 0) { 97 shapes = shapesString.toString(); 98 } 99 } 100 if (!i.serverProjections.isEmpty()) { 101 projections = String.join(",", i.serverProjections); 102 } 103 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) { 104 noTileHeaders = new MultiMap<>(i.noTileHeaders); 105 } 106 107 if (i.noTileChecksums != null && !i.noTileChecksums.isEmpty()) { 108 noTileChecksums = new MultiMap<>(i.noTileChecksums); 109 } 110 111 if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) { 112 metadataHeaders = i.metadataHeaders; 113 } 114 115 tileSize = i.getTileSize(); 116 117 valid_georeference = i.isGeoreferenceValid(); 118 modTileFeatures = i.isModTileFeatures(); 119 if (!i.defaultLayers.isEmpty()) { 120 default_layers = i.defaultLayers.stream().map(DefaultLayer::toJson).collect(JsonCollectors.toJsonArray()).toString(); 121 } 122 customHttpHeaders = i.customHttpHeaders; 123 transparent = i.isTransparent(); 124 minimumTileExpire = i.minimumTileExpire; 125 } 126 127 @Override 128 public String toString(); 129 } -
src/org/openstreetmap/josm/io/imagery/ImageryReader.java
433 433 entry.addOldId(accumulator.toString()); 434 434 break; 435 435 case "type": 436 ImageryType type = ImageryType. fromString(accumulator.toString());436 ImageryType type = ImageryType.getFromString(accumulator.toString()); 437 437 if (type != null) 438 438 entry.setImageryType(type); 439 439 else … … 532 532 break; 533 533 case "category": 534 534 String cat = accumulator.toString(); 535 ImageryCategory category = ImageryCategory. fromString(cat);535 ImageryCategory category = ImageryCategory.getFromString(cat); 536 536 if (category != null) 537 537 entry.setImageryCategory(category); 538 538 entry.setImageryCategoryOriginalString(cat);