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, 8 weeks 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.