source: josm/trunk/src/org/openstreetmap/josm/data/imagery/ImageryInfo.java @ 11527

Last change on this file since 11527 was 11527, checked in by stoecker, 10 months ago

drop imagery entries which have an id, but are no longer in default list

  • Property svn:eol-style set to native
File size: 39.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Image;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.List;
12import java.util.Locale;
13import java.util.Map;
14import java.util.Objects;
15import java.util.Set;
16import java.util.TreeSet;
17import java.util.regex.Matcher;
18import java.util.regex.Pattern;
19import java.util.stream.Collectors;
20
21import javax.swing.ImageIcon;
22
23import org.openstreetmap.gui.jmapviewer.interfaces.Attributed;
24import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
25import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;
26import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik;
27import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.data.Bounds;
30import org.openstreetmap.josm.data.Preferences.pref;
31import org.openstreetmap.josm.io.Capabilities;
32import org.openstreetmap.josm.io.OsmApi;
33import org.openstreetmap.josm.tools.CheckParameterUtil;
34import org.openstreetmap.josm.tools.ImageProvider;
35import org.openstreetmap.josm.tools.LanguageInfo;
36import org.openstreetmap.josm.tools.MultiMap;
37
38/**
39 * Class that stores info about an image background layer.
40 *
41 * @author Frederik Ramm
42 */
43public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed {
44
45    /**
46     * Type of imagery entry.
47     */
48    public enum ImageryType {
49        /** A WMS (Web Map Service) entry. **/
50        WMS("wms"),
51        /** A TMS (Tile Map Service) entry. **/
52        TMS("tms"),
53        /** An HTML proxy (previously used for Yahoo imagery) entry. **/
54        HTML("html"),
55        /** TMS entry for Microsoft Bing. */
56        BING("bing"),
57        /** TMS entry for Russian company <a href="https://wiki.openstreetmap.org/wiki/WikiProject_Russia/kosmosnimki">ScanEx</a>. **/
58        SCANEX("scanex"),
59        /** A WMS endpoint entry only stores the WMS server info, without layer, which are chosen later by the user. **/
60        WMS_ENDPOINT("wms_endpoint"),
61        /** WMTS stores GetCapabilities URL. Does not store any information about the layer **/
62        WMTS("wmts");
63
64
65        private final String typeString;
66
67        ImageryType(String urlString) {
68            this.typeString = urlString;
69        }
70
71        /**
72         * Returns the unique string identifying this type.
73         * @return the unique string identifying this type
74         * @since 6690
75         */
76        public final String getTypeString() {
77            return typeString;
78        }
79
80        /**
81         * Returns the imagery type from the given type string.
82         * @param s The type string
83         * @return the imagery type matching the given type string
84         */
85        public static ImageryType fromString(String s) {
86            for (ImageryType type : ImageryType.values()) {
87                if (type.getTypeString().equals(s)) {
88                    return type;
89                }
90            }
91            return null;
92        }
93    }
94
95    /**
96     * Multi-polygon bounds for imagery backgrounds.
97     * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location.
98     */
99    public static class ImageryBounds extends Bounds {
100
101        /**
102         * Constructs a new {@code ImageryBounds} from string.
103         * @param asString The string containing the list of shapes defining this bounds
104         * @param separator The shape separator in the given string, usually a comma
105         */
106        public ImageryBounds(String asString, String separator) {
107            super(asString, separator);
108        }
109
110        private List<Shape> shapes = new ArrayList<>();
111
112        /**
113         * Adds a new shape to this bounds.
114         * @param shape The shape to add
115         */
116        public final void addShape(Shape shape) {
117            this.shapes.add(shape);
118        }
119
120        /**
121         * Sets the list of shapes defining this bounds.
122         * @param shapes The list of shapes defining this bounds.
123         */
124        public final void setShapes(List<Shape> shapes) {
125            this.shapes = shapes;
126        }
127
128        /**
129         * Returns the list of shapes defining this bounds.
130         * @return The list of shapes defining this bounds
131         */
132        public final List<Shape> getShapes() {
133            return shapes;
134        }
135
136        @Override
137        public int hashCode() {
138            return Objects.hash(super.hashCode(), shapes);
139        }
140
141        @Override
142        public boolean equals(Object o) {
143            if (this == o) return true;
144            if (o == null || getClass() != o.getClass()) return false;
145            if (!super.equals(o)) return false;
146            ImageryBounds that = (ImageryBounds) o;
147            return Objects.equals(shapes, that.shapes);
148        }
149    }
150
151    /** original name of the imagery entry in case of translation call, for multiple languages English when possible */
152    private String origName;
153    /** (original) language of the translated name entry */
154    private String langName;
155    /** whether this is a entry activated by default or not */
156    private boolean defaultEntry;
157    /** The data part of HTTP cookies header in case the service requires cookies to work */
158    private String cookies;
159    /** Whether this service requires a explicit EULA acceptance before it can be activated */
160    private String eulaAcceptanceRequired;
161    /** type of the imagery servics - WMS, TMS, ... */
162    private ImageryType imageryType = ImageryType.WMS;
163    private double pixelPerDegree;
164    /** maximum zoom level for TMS imagery */
165    private int defaultMaxZoom;
166    /** minimum zoom level for TMS imagery */
167    private int defaultMinZoom;
168    /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */
169    private ImageryBounds bounds;
170    /** projections supported by WMS servers */
171    private List<String> serverProjections = Collections.emptyList();
172    /** description of the imagery entry, should contain notes what type of data it is */
173    private String description;
174    /** language of the description entry */
175    private String langDescription;
176    /** Text of a text attribution displayed when using the imagery */
177    private String attributionText;
178    /** Link behing the text attribution displayed when using the imagery */
179    private String attributionLinkURL;
180    /** Image of a graphical attribution displayed when using the imagery */
181    private String attributionImage;
182    /** Link behind the graphical attribution displayed when using the imagery */
183    private String attributionImageURL;
184    /** Text with usage terms displayed when using the imagery */
185    private String termsOfUseText;
186    /** Link behind the text with usage terms displayed when using the imagery */
187    private String termsOfUseURL;
188    /** country code of the imagery (for country specific imagery) */
189    private String countryCode = "";
190    /** mirrors of different type for this entry */
191    private List<ImageryInfo> mirrors;
192    /** icon used in menu */
193    private String icon;
194    private boolean isGeoreferenceValid;
195    private boolean isEpsg4326To3857Supported;
196    /** which layers should be activated by default on layer addition. **/
197    private Collection<DefaultLayer> defaultLayers = Collections.emptyList();
198    // when adding a field, also adapt the ImageryInfo(ImageryInfo)
199    // and ImageryInfo(ImageryPreferenceEntry) constructor, equals method, and ImageryPreferenceEntry
200
201    /**
202     * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
203     */
204    public static class ImageryPreferenceEntry {
205        @pref String name;
206        @pref String id;
207        @pref String type;
208        @pref String url;
209        @pref double pixel_per_eastnorth;
210        @pref String eula;
211        @pref String attribution_text;
212        @pref String attribution_url;
213        @pref String logo_image;
214        @pref String logo_url;
215        @pref String terms_of_use_text;
216        @pref String terms_of_use_url;
217        @pref String country_code = "";
218        @pref int max_zoom;
219        @pref int min_zoom;
220        @pref String cookies;
221        @pref String bounds;
222        @pref String shapes;
223        @pref String projections;
224        @pref String icon;
225        @pref String description;
226        @pref MultiMap<String, String> noTileHeaders;
227        @pref MultiMap<String, String> noTileChecksums;
228        @pref int tileSize = -1;
229        @pref Map<String, String> metadataHeaders;
230        @pref boolean valid_georeference;
231        @pref boolean supports_epsg_4326_to_3857_conversion;
232        // TODO: disabled until change of layers is implemented
233        // @pref String default_layers;
234
235        /**
236         * Constructs a new empty WMS {@code ImageryPreferenceEntry}.
237         */
238        public ImageryPreferenceEntry() {
239            // Do nothing
240        }
241
242        /**
243         * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}.
244         * @param i The corresponding imagery info
245         */
246        public ImageryPreferenceEntry(ImageryInfo i) {
247            name = i.name;
248            id = i.id;
249            type = i.imageryType.getTypeString();
250            url = i.url;
251            pixel_per_eastnorth = i.pixelPerDegree;
252            eula = i.eulaAcceptanceRequired;
253            attribution_text = i.attributionText;
254            attribution_url = i.attributionLinkURL;
255            logo_image = i.attributionImage;
256            logo_url = i.attributionImageURL;
257            terms_of_use_text = i.termsOfUseText;
258            terms_of_use_url = i.termsOfUseURL;
259            country_code = i.countryCode;
260            max_zoom = i.defaultMaxZoom;
261            min_zoom = i.defaultMinZoom;
262            cookies = i.cookies;
263            icon = i.icon;
264            description = i.description;
265            if (i.bounds != null) {
266                bounds = i.bounds.encodeAsString(",");
267                StringBuilder shapesString = new StringBuilder();
268                for (Shape s : i.bounds.getShapes()) {
269                    if (shapesString.length() > 0) {
270                        shapesString.append(';');
271                    }
272                    shapesString.append(s.encodeAsString(","));
273                }
274                if (shapesString.length() > 0) {
275                    shapes = shapesString.toString();
276                }
277            }
278            projections = i.serverProjections.stream().collect(Collectors.joining(","));
279            if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) {
280                noTileHeaders = new MultiMap<>(i.noTileHeaders);
281            }
282
283            if (i.noTileChecksums != null && !i.noTileChecksums.isEmpty()) {
284                noTileChecksums = new MultiMap<>(i.noTileChecksums);
285            }
286
287            if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) {
288                metadataHeaders = i.metadataHeaders;
289            }
290
291            tileSize = i.getTileSize();
292
293            valid_georeference = i.isGeoreferenceValid();
294            supports_epsg_4326_to_3857_conversion = i.isEpsg4326To3857Supported();
295            // TODO disabled until change of layers is implemented
296            // default_layers = i.defaultLayers.stream().collect(Collectors.joining(","));
297        }
298
299        @Override
300        public String toString() {
301            StringBuilder s = new StringBuilder("ImageryPreferenceEntry [name=").append(name);
302            if (id != null) {
303                s.append(" id=").append(id);
304            }
305            s.append(']');
306            return s.toString();
307        }
308    }
309
310    /**
311     * Constructs a new WMS {@code ImageryInfo}.
312     */
313    public ImageryInfo() {
314        super();
315    }
316
317    /**
318     * Constructs a new WMS {@code ImageryInfo} with a given name.
319     * @param name The entry name
320     */
321    public ImageryInfo(String name) {
322        super(name);
323    }
324
325    /**
326     * Constructs a new WMS {@code ImageryInfo} with given name and extended URL.
327     * @param name The entry name
328     * @param url The entry extended URL
329     */
330    public ImageryInfo(String name, String url) {
331        this(name);
332        setExtendedUrl(url);
333    }
334
335    /**
336     * Constructs a new WMS {@code ImageryInfo} with given name, extended and EULA URLs.
337     * @param name The entry name
338     * @param url The entry URL
339     * @param eulaAcceptanceRequired The EULA URL
340     */
341    public ImageryInfo(String name, String url, String eulaAcceptanceRequired) {
342        this(name);
343        setExtendedUrl(url);
344        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
345    }
346
347    /**
348     * Constructs a new {@code ImageryInfo} with given name, url, extended and EULA URLs.
349     * @param name The entry name
350     * @param url The entry URL
351     * @param type The entry imagery type. If null, WMS will be used as default
352     * @param eulaAcceptanceRequired The EULA URL
353     * @param cookies The data part of HTTP cookies header in case the service requires cookies to work
354     * @throws IllegalArgumentException if type refers to an unknown imagery type
355     */
356    public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) {
357        this(name);
358        setExtendedUrl(url);
359        ImageryType t = ImageryType.fromString(type);
360        this.cookies = cookies;
361        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
362        if (t != null) {
363            this.imageryType = t;
364        } else if (type != null && !type.trim().isEmpty()) {
365            throw new IllegalArgumentException("unknown type: "+type);
366        }
367    }
368
369    public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies, String id) {
370        this(name, url, type, eulaAcceptanceRequired, cookies);
371        setId(id);
372    }
373
374    /**
375     * Constructs a new {@code ImageryInfo} from an imagery preference entry.
376     * @param e The imagery preference entry
377     */
378    public ImageryInfo(ImageryPreferenceEntry e) {
379        super(e.name, e.url, e.id);
380        CheckParameterUtil.ensureParameterNotNull(e.name, "name");
381        CheckParameterUtil.ensureParameterNotNull(e.url, "url");
382        description = e.description;
383        cookies = e.cookies;
384        eulaAcceptanceRequired = e.eula;
385        imageryType = ImageryType.fromString(e.type);
386        if (imageryType == null) throw new IllegalArgumentException("unknown type");
387        pixelPerDegree = e.pixel_per_eastnorth;
388        defaultMaxZoom = e.max_zoom;
389        defaultMinZoom = e.min_zoom;
390        if (e.bounds != null) {
391            bounds = new ImageryBounds(e.bounds, ",");
392            if (e.shapes != null) {
393                try {
394                    for (String s : e.shapes.split(";")) {
395                        bounds.addShape(new Shape(s, ","));
396                    }
397                } catch (IllegalArgumentException ex) {
398                    Main.warn(ex);
399                }
400            }
401        }
402        if (e.projections != null && !e.projections.isEmpty()) {
403            // split generates null element on empty string which gives one element Array[null]
404            serverProjections = Arrays.asList(e.projections.split(","));
405        }
406        attributionText = e.attribution_text;
407        attributionLinkURL = e.attribution_url;
408        attributionImage = e.logo_image;
409        attributionImageURL = e.logo_url;
410        termsOfUseText = e.terms_of_use_text;
411        termsOfUseURL = e.terms_of_use_url;
412        countryCode = e.country_code;
413        icon = e.icon;
414        if (e.noTileHeaders != null) {
415            noTileHeaders = e.noTileHeaders.toMap();
416        }
417        if (e.noTileChecksums != null) {
418            noTileChecksums = e.noTileChecksums.toMap();
419        }
420        setTileSize(e.tileSize);
421        metadataHeaders = e.metadataHeaders;
422        isEpsg4326To3857Supported = e.supports_epsg_4326_to_3857_conversion;
423        isGeoreferenceValid = e.valid_georeference;
424        // TODO disabled until change of layers is implemented
425        // defaultLayers = Arrays.asList(e.default_layers.split(","));
426    }
427
428    /**
429     * Constructs a new {@code ImageryInfo} from an existing one.
430     * @param i The other imagery info
431     */
432    public ImageryInfo(ImageryInfo i) {
433        super(i.name, i.url, i.id);
434        this.defaultEntry = i.defaultEntry;
435        this.cookies = i.cookies;
436        this.eulaAcceptanceRequired = null;
437        this.imageryType = i.imageryType;
438        this.pixelPerDegree = i.pixelPerDegree;
439        this.defaultMaxZoom = i.defaultMaxZoom;
440        this.defaultMinZoom = i.defaultMinZoom;
441        this.bounds = i.bounds;
442        this.serverProjections = i.serverProjections;
443        this.attributionText = i.attributionText;
444        this.attributionLinkURL = i.attributionLinkURL;
445        this.attributionImage = i.attributionImage;
446        this.attributionImageURL = i.attributionImageURL;
447        this.termsOfUseText = i.termsOfUseText;
448        this.termsOfUseURL = i.termsOfUseURL;
449        this.countryCode = i.countryCode;
450        this.icon = i.icon;
451        this.description = i.description;
452        this.noTileHeaders = i.noTileHeaders;
453        this.noTileChecksums = i.noTileChecksums;
454        this.metadataHeaders = i.metadataHeaders;
455        this.isEpsg4326To3857Supported = i.isEpsg4326To3857Supported;
456        this.isGeoreferenceValid = i.isGeoreferenceValid;
457        this.defaultLayers = i.defaultLayers;
458    }
459
460    @Override
461    public int hashCode() {
462        return Objects.hash(url, imageryType);
463    }
464
465    /**
466     * Check if this object equals another ImageryInfo with respect to the properties
467     * that get written to the preference file.
468     *
469     * The field {@link #pixelPerDegree} is ignored.
470     *
471     * @param other the ImageryInfo object to compare to
472     * @return true if they are equal
473     */
474    public boolean equalsPref(ImageryInfo other) {
475        if (other == null) {
476            return false;
477        }
478
479        return
480                Objects.equals(this.name, other.name) &&
481                Objects.equals(this.id, other.id) &&
482                Objects.equals(this.url, other.url) &&
483                Objects.equals(this.cookies, other.cookies) &&
484                Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) &&
485                Objects.equals(this.imageryType, other.imageryType) &&
486                Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) &&
487                Objects.equals(this.defaultMinZoom, other.defaultMinZoom) &&
488                Objects.equals(this.bounds, other.bounds) &&
489                Objects.equals(this.serverProjections, other.serverProjections) &&
490                Objects.equals(this.attributionText, other.attributionText) &&
491                Objects.equals(this.attributionLinkURL, other.attributionLinkURL) &&
492                Objects.equals(this.attributionImageURL, other.attributionImageURL) &&
493                Objects.equals(this.attributionImage, other.attributionImage) &&
494                Objects.equals(this.termsOfUseText, other.termsOfUseText) &&
495                Objects.equals(this.termsOfUseURL, other.termsOfUseURL) &&
496                Objects.equals(this.countryCode, other.countryCode) &&
497                Objects.equals(this.icon, other.icon) &&
498                Objects.equals(this.description, other.description) &&
499                Objects.equals(this.noTileHeaders, other.noTileHeaders) &&
500                Objects.equals(this.noTileChecksums, other.noTileChecksums) &&
501                Objects.equals(this.metadataHeaders, other.metadataHeaders) &&
502                Objects.equals(this.defaultLayers, other.defaultLayers);
503    }
504
505    @Override
506    public boolean equals(Object o) {
507        if (this == o) return true;
508        if (o == null || getClass() != o.getClass()) return false;
509        ImageryInfo that = (ImageryInfo) o;
510        return imageryType == that.imageryType && Objects.equals(url, that.url);
511    }
512
513    @Override
514    public String toString() {
515        return "ImageryInfo{" +
516                "name='" + name + '\'' +
517                ", countryCode='" + countryCode + '\'' +
518                ", url='" + url + '\'' +
519                ", imageryType=" + imageryType +
520                '}';
521    }
522
523    @Override
524    public int compareTo(ImageryInfo in) {
525        int i = countryCode.compareTo(in.countryCode);
526        if (i == 0) {
527            i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH));
528        }
529        if (i == 0) {
530            i = url.compareTo(in.url);
531        }
532        if (i == 0) {
533            i = Double.compare(pixelPerDegree, in.pixelPerDegree);
534        }
535        return i;
536    }
537
538    public boolean equalsBaseValues(ImageryInfo in) {
539        return url.equals(in.url);
540    }
541
542    /**
543     * Sets the pixel per degree value.
544     * @param ppd The ppd value
545     * @see #getPixelPerDegree()
546     */
547    public void setPixelPerDegree(double ppd) {
548        this.pixelPerDegree = ppd;
549    }
550
551    /**
552     * Sets the maximum zoom level.
553     * @param defaultMaxZoom The maximum zoom level
554     */
555    public void setDefaultMaxZoom(int defaultMaxZoom) {
556        this.defaultMaxZoom = defaultMaxZoom;
557    }
558
559    /**
560     * Sets the minimum zoom level.
561     * @param defaultMinZoom The minimum zoom level
562     */
563    public void setDefaultMinZoom(int defaultMinZoom) {
564        this.defaultMinZoom = defaultMinZoom;
565    }
566
567    /**
568     * Sets the imagery polygonial bounds.
569     * @param b The imagery bounds (non-rectangular)
570     */
571    public void setBounds(ImageryBounds b) {
572        this.bounds = b;
573    }
574
575    /**
576     * Returns the imagery polygonial bounds.
577     * @return The imagery bounds (non-rectangular)
578     */
579    public ImageryBounds getBounds() {
580        return bounds;
581    }
582
583    @Override
584    public boolean requiresAttribution() {
585        return attributionText != null || attributionImage != null || termsOfUseText != null || termsOfUseURL != null;
586    }
587
588    @Override
589    public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) {
590        return attributionText;
591    }
592
593    @Override
594    public String getAttributionLinkURL() {
595        return attributionLinkURL;
596    }
597
598    @Override
599    public Image getAttributionImage() {
600        ImageIcon i = ImageProvider.getIfAvailable(attributionImage);
601        if (i != null) {
602            return i.getImage();
603        }
604        return null;
605    }
606
607    @Override
608    public String getAttributionImageURL() {
609        return attributionImageURL;
610    }
611
612    @Override
613    public String getTermsOfUseText() {
614        return termsOfUseText;
615    }
616
617    @Override
618    public String getTermsOfUseURL() {
619        return termsOfUseURL;
620    }
621
622    /**
623     * Set the attribution text
624     * @param text The text
625     * @see #getAttributionText(int, ICoordinate, ICoordinate)
626     */
627    public void setAttributionText(String text) {
628        attributionText = text;
629    }
630
631    /**
632     * Set the attribution image
633     * @param url The url of the image.
634     * @see #getAttributionImageURL()
635     */
636    public void setAttributionImageURL(String url) {
637        attributionImageURL = url;
638    }
639
640    /**
641     * Set the image for the attribution
642     * @param res The image resource
643     * @see #getAttributionImage()
644     */
645    public void setAttributionImage(String res) {
646        attributionImage = res;
647    }
648
649    /**
650     * Sets the URL the attribution should link to.
651     * @param url The url.
652     * @see #getAttributionLinkURL()
653     */
654    public void setAttributionLinkURL(String url) {
655        attributionLinkURL = url;
656    }
657
658    /**
659     * Sets the text to display to the user as terms of use.
660     * @param text The text
661     * @see #getTermsOfUseText()
662     */
663    public void setTermsOfUseText(String text) {
664        termsOfUseText = text;
665    }
666
667    /**
668     * Sets a url that links to the terms of use text.
669     * @param text The url.
670     * @see #getTermsOfUseURL()
671     */
672    public void setTermsOfUseURL(String text) {
673        termsOfUseURL = text;
674    }
675
676    /**
677     * Sets the extended URL of this entry.
678     * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info
679     */
680    public void setExtendedUrl(String url) {
681        CheckParameterUtil.ensureParameterNotNull(url);
682
683        // Default imagery type is WMS
684        this.url = url;
685        this.imageryType = ImageryType.WMS;
686
687        defaultMaxZoom = 0;
688        defaultMinZoom = 0;
689        for (ImageryType type : ImageryType.values()) {
690            Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url);
691            if (m.matches()) {
692                this.url = m.group(3);
693                this.imageryType = type;
694                if (m.group(2) != null) {
695                    defaultMaxZoom = Integer.parseInt(m.group(2));
696                }
697                if (m.group(1) != null) {
698                    defaultMinZoom = Integer.parseInt(m.group(1));
699                }
700                break;
701            }
702        }
703
704        if (serverProjections.isEmpty()) {
705            serverProjections = new ArrayList<>();
706            Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)\\}.*").matcher(url.toUpperCase(Locale.ENGLISH));
707            if (m.matches()) {
708                for (String p : m.group(1).split(",")) {
709                    serverProjections.add(p);
710                }
711            }
712        }
713    }
714
715    /**
716     * Returns the entry name.
717     * @return The entry name
718     * @since 6968
719     */
720    public String getOriginalName() {
721        return this.origName != null ? this.origName : this.name;
722    }
723
724    /**
725     * Sets the entry name and handle translation.
726     * @param language The used language
727     * @param name The entry name
728     * @since 8091
729     */
730    public void setName(String language, String name) {
731        boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
732        if (LanguageInfo.isBetterLanguage(langName, language)) {
733            this.name = isdefault ? tr(name) : name;
734            this.langName = language;
735        }
736        if (origName == null || isdefault) {
737            this.origName = name;
738        }
739    }
740
741    /**
742     * Store the id of this info to the preferences and clear it afterwards.
743     */
744    public void clearId() {
745        if (this.id != null) {
746            Collection<String> newAddedIds = new TreeSet<>(Main.pref.getCollection("imagery.layers.addedIds"));
747            newAddedIds.add(this.id);
748            Main.pref.putCollection("imagery.layers.addedIds", newAddedIds);
749        }
750        setId(null);
751    }
752
753    /**
754     * Determines if this entry is enabled by default.
755     * @return {@code true} if this entry is enabled by default, {@code false} otherwise
756     */
757    public boolean isDefaultEntry() {
758        return defaultEntry;
759    }
760
761    /**
762     * Sets the default state of this entry.
763     * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise
764     */
765    public void setDefaultEntry(boolean defaultEntry) {
766        this.defaultEntry = defaultEntry;
767    }
768
769    /**
770     * Return the data part of HTTP cookies header in case the service requires cookies to work
771     * @return the cookie data part
772     */
773    @Override
774    public String getCookies() {
775        return this.cookies;
776    }
777
778    /**
779     * Gets the pixel per degree value
780     * @return The ppd value.
781     */
782    public double getPixelPerDegree() {
783        return this.pixelPerDegree;
784    }
785
786    /**
787     * Returns the maximum zoom level.
788     * @return The maximum zoom level
789     */
790    @Override
791    public int getMaxZoom() {
792        return this.defaultMaxZoom;
793    }
794
795    /**
796     * Returns the minimum zoom level.
797     * @return The minimum zoom level
798     */
799    @Override
800    public int getMinZoom() {
801        return this.defaultMinZoom;
802    }
803
804    /**
805     * Returns the description text when existing.
806     * @return The description
807     * @since 8065
808     */
809    public String getDescription() {
810        return this.description;
811    }
812
813    /**
814     * Sets the description text when existing.
815     * @param language The used language
816     * @param description the imagery description text
817     * @since 8091
818     */
819    public void setDescription(String language, String description) {
820        boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
821        if (LanguageInfo.isBetterLanguage(langDescription, language)) {
822            this.description = isdefault ? tr(description) : description;
823            this.langDescription = language;
824        }
825    }
826
827    /**
828     * Returns a tool tip text for display.
829     * @return The text
830     * @since 8065
831     */
832    public String getToolTipText() {
833        String desc = getDescription();
834        if (desc != null && !desc.isEmpty()) {
835            return "<html>" + getName() + "<br>" + desc + "</html>";
836        }
837        return getName();
838    }
839
840    /**
841     * Returns the EULA acceptance URL, if any.
842     * @return The URL to an EULA text that has to be accepted before use, or {@code null}
843     */
844    public String getEulaAcceptanceRequired() {
845        return eulaAcceptanceRequired;
846    }
847
848    /**
849     * Sets the EULA acceptance URL.
850     * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use
851     */
852    public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
853        this.eulaAcceptanceRequired = eulaAcceptanceRequired;
854    }
855
856    /**
857     * Returns the ISO 3166-1-alpha-2 country code.
858     * @return The country code (2 letters)
859     */
860    public String getCountryCode() {
861        return countryCode;
862    }
863
864    /**
865     * Sets the ISO 3166-1-alpha-2 country code.
866     * @param countryCode The country code (2 letters)
867     */
868    public void setCountryCode(String countryCode) {
869        this.countryCode = countryCode;
870    }
871
872    /**
873     * Returns the entry icon.
874     * @return The entry icon
875     */
876    public String getIcon() {
877        return icon;
878    }
879
880    /**
881     * Sets the entry icon.
882     * @param icon The entry icon
883     */
884    public void setIcon(String icon) {
885        this.icon = icon;
886    }
887
888    /**
889     * Get the projections supported by the server. Only relevant for
890     * WMS-type ImageryInfo at the moment.
891     * @return null, if no projections have been specified; the list
892     * of supported projections otherwise.
893     */
894    public List<String> getServerProjections() {
895        return Collections.unmodifiableList(serverProjections);
896    }
897
898    /**
899     * Sets the list of collections the server supports
900     * @param serverProjections The list of supported projections
901     */
902    public void setServerProjections(Collection<String> serverProjections) {
903        CheckParameterUtil.ensureParameterNotNull(serverProjections, "serverProjections");
904        this.serverProjections = new ArrayList<>(serverProjections);
905    }
906
907    /**
908     * Returns the extended URL, containing in addition of service URL, its type and min/max zoom info.
909     * @return The extended URL
910     */
911    public String getExtendedUrl() {
912        return imageryType.getTypeString() + (defaultMaxZoom != 0
913            ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url;
914    }
915
916    /**
917     * Gets a unique toolbar key to store this layer as toolbar item
918     * @return The kay.
919     */
920    public String getToolbarName() {
921        String res = name;
922        if (pixelPerDegree != 0) {
923            res += "#PPD="+pixelPerDegree;
924        }
925        return res;
926    }
927
928    /**
929     * Gets the name that should be displayed in the menu to add this imagery layer.
930     * @return The text.
931     */
932    public String getMenuName() {
933        String res = name;
934        if (pixelPerDegree != 0) {
935            res += " ("+pixelPerDegree+')';
936        }
937        return res;
938    }
939
940    /**
941     * Determines if this entry requires attribution.
942     * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise
943     */
944    public boolean hasAttribution() {
945        return attributionText != null;
946    }
947
948    /**
949     * Copies attribution from another {@code ImageryInfo}.
950     * @param i The other imagery info to get attribution from
951     */
952    public void copyAttribution(ImageryInfo i) {
953        this.attributionImage = i.attributionImage;
954        this.attributionImageURL = i.attributionImageURL;
955        this.attributionText = i.attributionText;
956        this.attributionLinkURL = i.attributionLinkURL;
957        this.termsOfUseText = i.termsOfUseText;
958        this.termsOfUseURL = i.termsOfUseURL;
959    }
960
961    /**
962     * Applies the attribution from this object to a tile source.
963     * @param s The tile source
964     */
965    public void setAttribution(AbstractTileSource s) {
966        if (attributionText != null) {
967            if ("osm".equals(attributionText)) {
968                s.setAttributionText(new Mapnik().getAttributionText(0, null, null));
969            } else {
970                s.setAttributionText(attributionText);
971            }
972        }
973        if (attributionLinkURL != null) {
974            if ("osm".equals(attributionLinkURL)) {
975                s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL());
976            } else {
977                s.setAttributionLinkURL(attributionLinkURL);
978            }
979        }
980        if (attributionImage != null) {
981            ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage);
982            if (i != null) {
983                s.setAttributionImage(i.getImage());
984            }
985        }
986        if (attributionImageURL != null) {
987            s.setAttributionImageURL(attributionImageURL);
988        }
989        if (termsOfUseText != null) {
990            s.setTermsOfUseText(termsOfUseText);
991        }
992        if (termsOfUseURL != null) {
993            if ("osm".equals(termsOfUseURL)) {
994                s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL());
995            } else {
996                s.setTermsOfUseURL(termsOfUseURL);
997            }
998        }
999    }
1000
1001    /**
1002     * Returns the imagery type.
1003     * @return The imagery type
1004     */
1005    public ImageryType getImageryType() {
1006        return imageryType;
1007    }
1008
1009    /**
1010     * Sets the imagery type.
1011     * @param imageryType The imagery type
1012     */
1013    public void setImageryType(ImageryType imageryType) {
1014        this.imageryType = imageryType;
1015    }
1016
1017    /**
1018     * Returns true if this layer's URL is matched by one of the regular
1019     * expressions kept by the current OsmApi instance.
1020     * @return {@code true} is this entry is blacklisted, {@code false} otherwise
1021     */
1022    public boolean isBlacklisted() {
1023        Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
1024        return capabilities != null && capabilities.isOnImageryBlacklist(this.url);
1025    }
1026
1027    /**
1028     * Sets the map of &lt;header name, header value&gt; that if any of this header
1029     * will be returned, then this tile will be treated as "no tile at this zoom level"
1030     *
1031     * @param noTileHeaders Map of &lt;header name, header value&gt; which will be treated as "no tile at this zoom level"
1032     * @since 9613
1033     */
1034    public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) {
1035       if (noTileHeaders == null || noTileHeaders.isEmpty()) {
1036           this.noTileHeaders = null;
1037       } else {
1038            this.noTileHeaders = noTileHeaders.toMap();
1039       }
1040    }
1041
1042    @Override
1043    public Map<String, Set<String>> getNoTileHeaders() {
1044        return noTileHeaders;
1045    }
1046
1047    /**
1048     * Sets the map of &lt;checksum type, checksum value&gt; that if any tile with that checksum
1049     * will be returned, then this tile will be treated as "no tile at this zoom level"
1050     *
1051     * @param noTileChecksums Map of &lt;checksum type, checksum value&gt; which will be treated as "no tile at this zoom level"
1052     * @since 9613
1053     */
1054    public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) {
1055        if (noTileChecksums == null || noTileChecksums.isEmpty()) {
1056            this.noTileChecksums = null;
1057        } else {
1058            this.noTileChecksums = noTileChecksums.toMap();
1059        }
1060    }
1061
1062    @Override
1063    public Map<String, Set<String>> getNoTileChecksums() {
1064        return noTileChecksums;
1065    }
1066
1067    /**
1068     * Returns the map of &lt;header name, metadata key&gt; indicating, which HTTP headers should
1069     * be moved to metadata
1070     *
1071     * @param metadataHeaders map of &lt;header name, metadata key&gt; indicating, which HTTP headers should be moved to metadata
1072     * @since 8418
1073     */
1074    public void setMetadataHeaders(Map<String, String> metadataHeaders) {
1075        if (metadataHeaders == null || metadataHeaders.isEmpty()) {
1076            this.metadataHeaders = null;
1077        } else {
1078            this.metadataHeaders = metadataHeaders;
1079        }
1080    }
1081
1082    /**
1083     * Gets the flag if epsg 4326 to 3857 is supported
1084     * @return The flag.
1085     */
1086    public boolean isEpsg4326To3857Supported() {
1087        return isEpsg4326To3857Supported;
1088    }
1089
1090    /**
1091     * Sets the flag that epsg 4326 to 3857 is supported
1092     * @param isEpsg4326To3857Supported The flag.
1093     */
1094    public void setEpsg4326To3857Supported(boolean isEpsg4326To3857Supported) {
1095        this.isEpsg4326To3857Supported = isEpsg4326To3857Supported;
1096    }
1097
1098    /**
1099     * Gets the flag if the georeference is valid.
1100     * @return <code>true</code> if it is valid.
1101     */
1102    public boolean isGeoreferenceValid() {
1103        return isGeoreferenceValid;
1104    }
1105
1106    /**
1107     * Sets an indicator that the georeference is valid
1108     * @param isGeoreferenceValid <code>true</code> if it is marked as valid.
1109     */
1110    public void setGeoreferenceValid(boolean isGeoreferenceValid) {
1111        this.isGeoreferenceValid = isGeoreferenceValid;
1112    }
1113
1114    /**
1115     * Adds a mirror entry. Mirror entries are completed with the data from the master entry
1116     * and only describe another method to access identical data.
1117     *
1118     * @param entry the mirror to be added
1119     * @since 9658
1120     */
1121    public void addMirror(ImageryInfo entry) {
1122       if (mirrors == null) {
1123           mirrors = new ArrayList<>();
1124       }
1125       mirrors.add(entry);
1126    }
1127
1128    /**
1129     * Returns the mirror entries. Entries are completed with master entry data.
1130     *
1131     * @return the list of mirrors
1132     * @since 9658
1133     */
1134    public List<ImageryInfo> getMirrors() {
1135       List<ImageryInfo> l = new ArrayList<>();
1136       if (mirrors != null) {
1137           for (ImageryInfo i : mirrors) {
1138               ImageryInfo n = new ImageryInfo(this);
1139               if (i.defaultMaxZoom != 0) {
1140                   n.defaultMaxZoom = i.defaultMaxZoom;
1141               }
1142               if (i.defaultMinZoom != 0) {
1143                   n.defaultMinZoom = i.defaultMinZoom;
1144               }
1145               n.setServerProjections(i.getServerProjections());
1146               n.url = i.url;
1147               n.imageryType = i.imageryType;
1148               if (i.getTileSize() != 0) {
1149                   n.setTileSize(i.getTileSize());
1150               }
1151               l.add(n);
1152           }
1153       }
1154       return l;
1155    }
1156
1157    /**
1158     * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider)
1159     * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer
1160     * to work on
1161     * @return Collection of the layer names
1162     */
1163    public Collection<DefaultLayer> getDefaultLayers() {
1164        return defaultLayers;
1165    }
1166
1167    /**
1168     * Sets the default layers that user will work with
1169     * @param layers set the list of default layers
1170     */
1171    public void setDefaultLayers(Collection<DefaultLayer> layers) {
1172        if (ImageryType.WMTS.equals(this.imageryType)) {
1173            CheckParameterUtil.ensureThat(layers == null ||
1174                    layers.isEmpty() ||
1175                    layers.iterator().next() instanceof WMTSDefaultLayer, "Incorrect default layer");
1176        }
1177        this.defaultLayers = layers;
1178    }
1179}
Note: See TracBrowser for help on using the repository browser.