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

Last change on this file since 16101 was 16101, checked in by simon04, 4 years ago

see #18896: ImageryInfo: use Utils.toUnmodifiableList and Utils.toUnmodifiableMap

  • Property svn:eol-style set to native
File size: 56.2 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.io.StringReader;
8import java.util.ArrayList;
9import java.util.Arrays;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.EnumMap;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Locale;
16import java.util.Map;
17import java.util.Objects;
18import java.util.Optional;
19import java.util.Set;
20import java.util.TreeSet;
21import java.util.concurrent.TimeUnit;
22import java.util.regex.Matcher;
23import java.util.regex.Pattern;
24import java.util.stream.Collectors;
25
26import javax.json.Json;
27import javax.json.JsonObject;
28import javax.json.JsonReader;
29import javax.json.stream.JsonCollectors;
30import javax.swing.ImageIcon;
31
32import org.openstreetmap.gui.jmapviewer.interfaces.Attributed;
33import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
34import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;
35import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik;
36import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo;
37import org.openstreetmap.josm.data.Bounds;
38import org.openstreetmap.josm.data.StructUtils;
39import org.openstreetmap.josm.data.StructUtils.StructEntry;
40import org.openstreetmap.josm.io.Capabilities;
41import org.openstreetmap.josm.io.OsmApi;
42import org.openstreetmap.josm.spi.preferences.Config;
43import org.openstreetmap.josm.spi.preferences.IPreferences;
44import org.openstreetmap.josm.tools.CheckParameterUtil;
45import org.openstreetmap.josm.tools.ImageProvider;
46import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
47import org.openstreetmap.josm.tools.LanguageInfo;
48import org.openstreetmap.josm.tools.Logging;
49import org.openstreetmap.josm.tools.MultiMap;
50import org.openstreetmap.josm.tools.Utils;
51
52/**
53 * Class that stores info about an image background layer.
54 *
55 * @author Frederik Ramm
56 */
57public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed {
58
59 /**
60 * Type of imagery entry.
61 */
62 public enum ImageryType {
63 /** A WMS (Web Map Service) entry. **/
64 WMS("wms"),
65 /** A TMS (Tile Map Service) entry. **/
66 TMS("tms"),
67 /** TMS entry for Microsoft Bing. */
68 BING("bing"),
69 /** TMS entry for Russian company <a href="https://wiki.openstreetmap.org/wiki/WikiProject_Russia/kosmosnimki">ScanEx</a>. **/
70 SCANEX("scanex"),
71 /** A WMS endpoint entry only stores the WMS server info, without layer, which are chosen later by the user. **/
72 WMS_ENDPOINT("wms_endpoint"),
73 /** WMTS stores GetCapabilities URL. Does not store any information about the layer **/
74 WMTS("wmts");
75
76 private final String typeString;
77
78 ImageryType(String typeString) {
79 this.typeString = typeString;
80 }
81
82 /**
83 * Returns the unique string identifying this type.
84 * @return the unique string identifying this type
85 * @since 6690
86 */
87 public final String getTypeString() {
88 return typeString;
89 }
90
91 /**
92 * Returns the imagery type from the given type string.
93 * @param s The type string
94 * @return the imagery type matching the given type string
95 */
96 public static ImageryType fromString(String s) {
97 for (ImageryType type : ImageryType.values()) {
98 if (type.getTypeString().equals(s)) {
99 return type;
100 }
101 }
102 return null;
103 }
104 }
105
106 /**
107 * Category of imagery entry.
108 * @since 13792
109 */
110 public enum ImageryCategory {
111 /** A aerial or satellite photo. **/
112 PHOTO(/* ICON(data/imagery/) */ "photo", tr("Aerial or satellite photo")),
113 /** A map of digital terrain model, digital surface model or contour lines. **/
114 ELEVATION(/* ICON(data/imagery/) */ "elevation", tr("Elevation map")),
115 /** A map. **/
116 MAP(/* ICON(data/imagery/) */ "map", tr("Map")),
117 /** A historic or otherwise outdated map. */
118 HISTORICMAP(/* ICON(data/imagery/) */ "historicmap", tr("Historic or otherwise outdated map")),
119 /** A map based on OSM data. **/
120 OSMBASEDMAP(/* ICON(data/imagery/) */ "osmbasedmap", tr("Map based on OSM data")),
121 /** A historic or otherwise outdated aerial or satellite photo. **/
122 HISTORICPHOTO(/* ICON(data/imagery/) */ "historicphoto", tr("Historic or otherwise outdated aerial or satellite photo")),
123 /** A map for quality assurance **/
124 QUALITY_ASSURANCE(/* ICON(data/imagery/) */ "qa", tr("Map for quality assurance")),
125 /** Any other type of imagery **/
126 OTHER(/* ICON(data/imagery/) */ "other", tr("Imagery not matching any other category"));
127
128 private final String category;
129 private final String description;
130 private static final Map<ImageSizes, Map<ImageryCategory, ImageIcon>> iconCache =
131 Collections.synchronizedMap(new EnumMap<>(ImageSizes.class));
132
133 ImageryCategory(String category, String description) {
134 this.category = category;
135 this.description = description;
136 }
137
138 /**
139 * Returns the unique string identifying this category.
140 * @return the unique string identifying this category
141 */
142 public final String getCategoryString() {
143 return category;
144 }
145
146 /**
147 * Returns the description of this category.
148 * @return the description of this category
149 */
150 public final String getDescription() {
151 return description;
152 }
153
154 /**
155 * Returns the category icon at the given size.
156 * @param size icon wanted size
157 * @return the category icon at the given size
158 * @since 15049
159 */
160 public final ImageIcon getIcon(ImageSizes size) {
161 return iconCache
162 .computeIfAbsent(size, x -> Collections.synchronizedMap(new EnumMap<>(ImageryCategory.class)))
163 .computeIfAbsent(this, x -> ImageProvider.get("data/imagery", x.category, size));
164 }
165
166 /**
167 * Returns the imagery category from the given category string.
168 * @param s The category string
169 * @return the imagery category matching the given category string
170 */
171 public static ImageryCategory fromString(String s) {
172 for (ImageryCategory category : ImageryCategory.values()) {
173 if (category.getCategoryString().equals(s)) {
174 return category;
175 }
176 }
177 return null;
178 }
179 }
180
181 /**
182 * Multi-polygon bounds for imagery backgrounds.
183 * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location.
184 */
185 public static class ImageryBounds extends Bounds {
186
187 /**
188 * Constructs a new {@code ImageryBounds} from string.
189 * @param asString The string containing the list of shapes defining this bounds
190 * @param separator The shape separator in the given string, usually a comma
191 */
192 public ImageryBounds(String asString, String separator) {
193 super(asString, separator);
194 }
195
196 private List<Shape> shapes = new ArrayList<>();
197
198 /**
199 * Adds a new shape to this bounds.
200 * @param shape The shape to add
201 */
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 bounds
217 */
218 public final List<Shape> getShapes() {
219 return shapes;
220 }
221
222 @Override
223 public int hashCode() {
224 return Objects.hash(super.hashCode(), shapes);
225 }
226
227 @Override
228 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 }
236
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 private double pixelPerDegree;
248 /** maximum zoom level for TMS imagery */
249 private int defaultMaxZoom;
250 /** minimum zoom level for TMS imagery */
251 private int defaultMinZoom;
252 /** display bounds of imagery, displayed in prefs and used for automatic imagery selection */
253 private ImageryBounds bounds;
254 /** projections supported by WMS servers */
255 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 a reference stating the permission for OSM usage */
263 private String permissionReferenceURL;
264 /** Link behind the text attribution displayed when using the imagery */
265 private String attributionLinkURL;
266 /** Image of a graphical attribution displayed when using the imagery */
267 private String attributionImage;
268 /** Link behind the graphical attribution displayed when using the imagery */
269 private String attributionImageURL;
270 /** Text with usage terms displayed when using the imagery */
271 private String termsOfUseText;
272 /** Link behind the text with usage terms displayed when using the imagery */
273 private String termsOfUseURL;
274 /** country code of the imagery (for country specific imagery) */
275 private String countryCode = "";
276 /**
277 * creation date of the imagery (in the form YYYY-MM-DD;YYYY-MM-DD, where
278 * DD and MM as well as a second date are optional).
279 *
280 * Also used as time filter for WMS time={time} parameter (such as Sentinel-2)
281 * @since 11570
282 */
283 private String date;
284 /**
285 * marked as best in other editors
286 * @since 11575
287 */
288 private boolean bestMarked;
289 /**
290 * marked as overlay
291 * @since 13536
292 */
293 private boolean overlay;
294 /**
295 * list of old IDs, only for loading, not handled anywhere else
296 * @since 13536
297 */
298 private Collection<String> oldIds;
299 /** mirrors of different type for this entry */
300 private List<ImageryInfo> mirrors;
301 /** icon used in menu */
302 private String icon;
303 /** is the geo reference correct - don't offer offset handling */
304 private boolean isGeoreferenceValid;
305 /** which layers should be activated by default on layer addition. **/
306 private List<DefaultLayer> defaultLayers = Collections.emptyList();
307 /** HTTP headers **/
308 private Map<String, String> customHttpHeaders = Collections.emptyMap();
309 /** Should this map be transparent **/
310 private boolean transparent = true;
311 private int minimumTileExpire = (int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get());
312 /** category of the imagery */
313 private ImageryCategory category;
314 /** category of the imagery (input string, not saved, copied or used otherwise except for error checks) */
315 private String categoryOriginalString;
316 /** when adding a field, also adapt the:
317 * {@link #ImageryPreferenceEntry ImageryPreferenceEntry object}
318 * {@link #ImageryPreferenceEntry#ImageryPreferenceEntry(ImageryInfo) ImageryPreferenceEntry constructor}
319 * {@link #ImageryInfo(ImageryPreferenceEntry) ImageryInfo constructor}
320 * {@link #ImageryInfo(ImageryInfo) ImageryInfo constructor}
321 * {@link #equalsPref(ImageryPreferenceEntry) equalsPref method}
322 **/
323
324 /**
325 * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
326 */
327 public static class ImageryPreferenceEntry {
328 @StructEntry String name;
329 @StructEntry String d;
330 @StructEntry String id;
331 @StructEntry String type;
332 @StructEntry String url;
333 @StructEntry double pixel_per_eastnorth;
334 @StructEntry String eula;
335 @StructEntry String attribution_text;
336 @StructEntry String attribution_url;
337 @StructEntry String permission_reference_url;
338 @StructEntry String logo_image;
339 @StructEntry String logo_url;
340 @StructEntry String terms_of_use_text;
341 @StructEntry String terms_of_use_url;
342 @StructEntry String country_code = "";
343 @StructEntry String date;
344 @StructEntry int max_zoom;
345 @StructEntry int min_zoom;
346 @StructEntry String cookies;
347 @StructEntry String bounds;
348 @StructEntry String shapes;
349 @StructEntry String projections;
350 @StructEntry String icon;
351 @StructEntry String description;
352 @StructEntry MultiMap<String, String> noTileHeaders;
353 @StructEntry MultiMap<String, String> noTileChecksums;
354 @StructEntry int tileSize = -1;
355 @StructEntry Map<String, String> metadataHeaders;
356 @StructEntry boolean valid_georeference;
357 @StructEntry boolean bestMarked;
358 @StructEntry boolean modTileFeatures;
359 @StructEntry boolean overlay;
360 @StructEntry String default_layers;
361 @StructEntry Map<String, String> customHttpHeaders;
362 @StructEntry boolean transparent;
363 @StructEntry int minimumTileExpire;
364 @StructEntry String category;
365
366 /**
367 * Constructs a new empty WMS {@code ImageryPreferenceEntry}.
368 */
369 public ImageryPreferenceEntry() {
370 // Do nothing
371 }
372
373 /**
374 * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}.
375 * @param i The corresponding imagery info
376 */
377 public ImageryPreferenceEntry(ImageryInfo i) {
378 name = i.name;
379 id = i.id;
380 type = i.imageryType.getTypeString();
381 url = i.url;
382 pixel_per_eastnorth = i.pixelPerDegree;
383 eula = i.eulaAcceptanceRequired;
384 attribution_text = i.attributionText;
385 attribution_url = i.attributionLinkURL;
386 permission_reference_url = i.permissionReferenceURL;
387 date = i.date;
388 bestMarked = i.bestMarked;
389 overlay = i.overlay;
390 logo_image = i.attributionImage;
391 logo_url = i.attributionImageURL;
392 terms_of_use_text = i.termsOfUseText;
393 terms_of_use_url = i.termsOfUseURL;
394 country_code = i.countryCode;
395 max_zoom = i.defaultMaxZoom;
396 min_zoom = i.defaultMinZoom;
397 cookies = i.cookies;
398 icon = intern(i.icon);
399 description = i.description;
400 category = i.category != null ? i.category.getCategoryString() : null;
401 if (i.bounds != null) {
402 bounds = i.bounds.encodeAsString(",");
403 StringBuilder shapesString = new StringBuilder();
404 for (Shape s : i.bounds.getShapes()) {
405 if (shapesString.length() > 0) {
406 shapesString.append(';');
407 }
408 shapesString.append(s.encodeAsString(","));
409 }
410 if (shapesString.length() > 0) {
411 shapes = shapesString.toString();
412 }
413 }
414 if (!i.serverProjections.isEmpty()) {
415 projections = String.join(",", i.serverProjections);
416 }
417 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) {
418 noTileHeaders = new MultiMap<>(i.noTileHeaders);
419 }
420
421 if (i.noTileChecksums != null && !i.noTileChecksums.isEmpty()) {
422 noTileChecksums = new MultiMap<>(i.noTileChecksums);
423 }
424
425 if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) {
426 metadataHeaders = i.metadataHeaders;
427 }
428
429 tileSize = i.getTileSize();
430
431 valid_georeference = i.isGeoreferenceValid();
432 modTileFeatures = i.isModTileFeatures();
433 if (!i.defaultLayers.isEmpty()) {
434 default_layers = i.defaultLayers.stream().map(DefaultLayer::toJson).collect(JsonCollectors.toJsonArray()).toString();
435 }
436 customHttpHeaders = i.customHttpHeaders;
437 transparent = i.isTransparent();
438 minimumTileExpire = i.minimumTileExpire;
439 }
440
441 @Override
442 public String toString() {
443 StringBuilder s = new StringBuilder("ImageryPreferenceEntry [name=").append(name);
444 if (id != null) {
445 s.append(" id=").append(id);
446 }
447 s.append(']');
448 return s.toString();
449 }
450 }
451
452 /**
453 * Constructs a new WMS {@code ImageryInfo}.
454 */
455 public ImageryInfo() {
456 super();
457 }
458
459 /**
460 * Constructs a new WMS {@code ImageryInfo} with a given name.
461 * @param name The entry name
462 */
463 public ImageryInfo(String name) {
464 super(name);
465 }
466
467 /**
468 * Constructs a new WMS {@code ImageryInfo} with given name and extended URL.
469 * @param name The entry name
470 * @param url The entry extended URL
471 */
472 public ImageryInfo(String name, String url) {
473 this(name);
474 setExtendedUrl(url);
475 }
476
477 /**
478 * Constructs a new WMS {@code ImageryInfo} with given name, extended and EULA URLs.
479 * @param name The entry name
480 * @param url The entry URL
481 * @param eulaAcceptanceRequired The EULA URL
482 */
483 public ImageryInfo(String name, String url, String eulaAcceptanceRequired) {
484 this(name);
485 setExtendedUrl(url);
486 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
487 }
488
489 /**
490 * Constructs a new {@code ImageryInfo} with given name, url, extended and EULA URLs.
491 * @param name The entry name
492 * @param url The entry URL
493 * @param type The entry imagery type. If null, WMS will be used as default
494 * @param eulaAcceptanceRequired The EULA URL
495 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work
496 * @throws IllegalArgumentException if type refers to an unknown imagery type
497 */
498 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) {
499 this(name);
500 setExtendedUrl(url);
501 ImageryType t = ImageryType.fromString(type);
502 this.cookies = cookies;
503 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
504 if (t != null) {
505 this.imageryType = t;
506 } else if (type != null && !type.isEmpty()) {
507 throw new IllegalArgumentException("unknown type: "+type);
508 }
509 }
510
511 /**
512 * Constructs a new {@code ImageryInfo} with given name, url, id, extended and EULA URLs.
513 * @param name The entry name
514 * @param url The entry URL
515 * @param type The entry imagery type. If null, WMS will be used as default
516 * @param eulaAcceptanceRequired The EULA URL
517 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work
518 * @param id tile id
519 * @throws IllegalArgumentException if type refers to an unknown imagery type
520 */
521 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies, String id) {
522 this(name, url, type, eulaAcceptanceRequired, cookies);
523 setId(id);
524 }
525
526 /**
527 * Constructs a new {@code ImageryInfo} from an imagery preference entry.
528 * @param e The imagery preference entry
529 */
530 public ImageryInfo(ImageryPreferenceEntry e) {
531 super(e.name, e.url, e.id);
532 CheckParameterUtil.ensureParameterNotNull(e.name, "name");
533 CheckParameterUtil.ensureParameterNotNull(e.url, "url");
534 description = e.description;
535 cookies = e.cookies;
536 eulaAcceptanceRequired = e.eula;
537 imageryType = ImageryType.fromString(e.type);
538 if (imageryType == null) throw new IllegalArgumentException("unknown type");
539 pixelPerDegree = e.pixel_per_eastnorth;
540 defaultMaxZoom = e.max_zoom;
541 defaultMinZoom = e.min_zoom;
542 if (e.bounds != null) {
543 bounds = new ImageryBounds(e.bounds, ",");
544 if (e.shapes != null) {
545 try {
546 for (String s : e.shapes.split(";")) {
547 bounds.addShape(new Shape(s, ","));
548 }
549 } catch (IllegalArgumentException ex) {
550 Logging.warn(ex);
551 }
552 }
553 }
554 if (e.projections != null && !e.projections.isEmpty()) {
555 // split generates null element on empty string which gives one element Array[null]
556 setServerProjections(Arrays.asList(e.projections.split(",")));
557 }
558 attributionText = intern(e.attribution_text);
559 attributionLinkURL = e.attribution_url;
560 permissionReferenceURL = e.permission_reference_url;
561 attributionImage = e.logo_image;
562 attributionImageURL = e.logo_url;
563 date = e.date;
564 bestMarked = e.bestMarked;
565 overlay = e.overlay;
566 termsOfUseText = e.terms_of_use_text;
567 termsOfUseURL = e.terms_of_use_url;
568 countryCode = intern(e.country_code);
569 icon = intern(e.icon);
570 if (e.noTileHeaders != null) {
571 noTileHeaders = e.noTileHeaders.toMap();
572 }
573 if (e.noTileChecksums != null) {
574 noTileChecksums = e.noTileChecksums.toMap();
575 }
576 setTileSize(e.tileSize);
577 metadataHeaders = e.metadataHeaders;
578 isGeoreferenceValid = e.valid_georeference;
579 modTileFeatures = e.modTileFeatures;
580 if (e.default_layers != null) {
581 try (JsonReader jsonReader = Json.createReader(new StringReader(e.default_layers))) {
582 defaultLayers = jsonReader.
583 readArray().
584 stream().
585 map(x -> DefaultLayer.fromJson((JsonObject) x, imageryType)).
586 collect(Collectors.toList());
587 }
588 }
589 setCustomHttpHeaders(e.customHttpHeaders);
590 transparent = e.transparent;
591 minimumTileExpire = e.minimumTileExpire;
592 category = ImageryCategory.fromString(e.category);
593 }
594
595 /**
596 * Constructs a new {@code ImageryInfo} from an existing one.
597 * @param i The other imagery info
598 */
599 public ImageryInfo(ImageryInfo i) {
600 super(i.name, i.url, i.id);
601 this.noTileHeaders = i.noTileHeaders;
602 this.noTileChecksums = i.noTileChecksums;
603 this.minZoom = i.minZoom;
604 this.maxZoom = i.maxZoom;
605 this.cookies = i.cookies;
606 this.tileSize = i.tileSize;
607 this.metadataHeaders = i.metadataHeaders;
608 this.modTileFeatures = i.modTileFeatures;
609
610 this.origName = i.origName;
611 this.langName = i.langName;
612 this.defaultEntry = i.defaultEntry;
613 this.eulaAcceptanceRequired = null;
614 this.imageryType = i.imageryType;
615 this.pixelPerDegree = i.pixelPerDegree;
616 this.defaultMaxZoom = i.defaultMaxZoom;
617 this.defaultMinZoom = i.defaultMinZoom;
618 this.bounds = i.bounds;
619 this.serverProjections = i.serverProjections;
620 this.description = i.description;
621 this.langDescription = i.langDescription;
622 this.attributionText = i.attributionText;
623 this.permissionReferenceURL = i.permissionReferenceURL;
624 this.attributionLinkURL = i.attributionLinkURL;
625 this.attributionImage = i.attributionImage;
626 this.attributionImageURL = i.attributionImageURL;
627 this.termsOfUseText = i.termsOfUseText;
628 this.termsOfUseURL = i.termsOfUseURL;
629 this.countryCode = i.countryCode;
630 this.date = i.date;
631 this.bestMarked = i.bestMarked;
632 this.overlay = i.overlay;
633 // do not copy field {@code mirrors}
634 this.icon = intern(i.icon);
635 this.isGeoreferenceValid = i.isGeoreferenceValid;
636 setDefaultLayers(i.defaultLayers);
637 setCustomHttpHeaders(i.customHttpHeaders);
638 this.transparent = i.transparent;
639 this.minimumTileExpire = i.minimumTileExpire;
640 this.categoryOriginalString = intern(i.categoryOriginalString);
641 this.category = i.category;
642 }
643
644 @Override
645 public int hashCode() {
646 return Objects.hash(url, imageryType);
647 }
648
649 /**
650 * Check if this object equals another ImageryInfo with respect to the properties
651 * that get written to the preference file.
652 *
653 * The field {@link #pixelPerDegree} is ignored.
654 *
655 * @param other the ImageryInfo object to compare to
656 * @return true if they are equal
657 */
658 public boolean equalsPref(ImageryInfo other) {
659 if (other == null) {
660 return false;
661 }
662
663 // CHECKSTYLE.OFF: BooleanExpressionComplexity
664 return
665 Objects.equals(this.name, other.name) &&
666 Objects.equals(this.id, other.id) &&
667 Objects.equals(this.url, other.url) &&
668 Objects.equals(this.modTileFeatures, other.modTileFeatures) &&
669 Objects.equals(this.bestMarked, other.bestMarked) &&
670 Objects.equals(this.overlay, other.overlay) &&
671 Objects.equals(this.isGeoreferenceValid, other.isGeoreferenceValid) &&
672 Objects.equals(this.cookies, other.cookies) &&
673 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) &&
674 Objects.equals(this.imageryType, other.imageryType) &&
675 Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) &&
676 Objects.equals(this.defaultMinZoom, other.defaultMinZoom) &&
677 Objects.equals(this.bounds, other.bounds) &&
678 Objects.equals(this.serverProjections, other.serverProjections) &&
679 Objects.equals(this.attributionText, other.attributionText) &&
680 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) &&
681 Objects.equals(this.permissionReferenceURL, other.permissionReferenceURL) &&
682 Objects.equals(this.attributionImageURL, other.attributionImageURL) &&
683 Objects.equals(this.attributionImage, other.attributionImage) &&
684 Objects.equals(this.termsOfUseText, other.termsOfUseText) &&
685 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) &&
686 Objects.equals(this.countryCode, other.countryCode) &&
687 Objects.equals(this.date, other.date) &&
688 Objects.equals(this.icon, other.icon) &&
689 Objects.equals(this.description, other.description) &&
690 Objects.equals(this.noTileHeaders, other.noTileHeaders) &&
691 Objects.equals(this.noTileChecksums, other.noTileChecksums) &&
692 Objects.equals(this.metadataHeaders, other.metadataHeaders) &&
693 Objects.equals(this.defaultLayers, other.defaultLayers) &&
694 Objects.equals(this.customHttpHeaders, other.customHttpHeaders) &&
695 Objects.equals(this.transparent, other.transparent) &&
696 Objects.equals(this.minimumTileExpire, other.minimumTileExpire) &&
697 Objects.equals(this.category, other.category);
698 // CHECKSTYLE.ON: BooleanExpressionComplexity
699 }
700
701 @Override
702 public boolean equals(Object o) {
703 if (this == o) return true;
704 if (o == null || getClass() != o.getClass()) return false;
705 ImageryInfo that = (ImageryInfo) o;
706 return imageryType == that.imageryType && Objects.equals(url, that.url);
707 }
708
709 private static final Map<String, String> localizedCountriesCache = new HashMap<>();
710 static {
711 localizedCountriesCache.put("", tr("Worldwide"));
712 }
713
714 /**
715 * Returns a localized name for the given country code, or "Worldwide" if empty.
716 * This function falls back on the English name, and uses the ISO code as a last-resortvalue.
717 *
718 * @param countryCode An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code
719 * @return The name of the country appropriate to the current locale.
720 * @see Locale#getDisplayCountry
721 * @since 15158
722 */
723 public static String getLocalizedCountry(String countryCode) {
724 return localizedCountriesCache.computeIfAbsent(countryCode, code -> new Locale("en", code).getDisplayCountry());
725 }
726
727 @Override
728 public String toString() {
729 // Used in imagery preferences filtering, so must be efficient
730 return new StringBuilder(name)
731 .append('[').append(countryCode)
732 // appending the localized country in toString() allows us to filter imagery preferences table with it!
733 .append("] ('").append(getLocalizedCountry(countryCode)).append(')')
734 .append(" - ").append(url)
735 .append(" - ").append(imageryType)
736 .toString();
737 }
738
739 @Override
740 public int compareTo(ImageryInfo in) {
741 int i = countryCode.compareTo(in.countryCode);
742 if (i == 0) {
743 i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH));
744 }
745 if (i == 0) {
746 i = url.compareTo(in.url);
747 }
748 if (i == 0) {
749 i = Double.compare(pixelPerDegree, in.pixelPerDegree);
750 }
751 return i;
752 }
753
754 /**
755 * Determines if URL is equal to given imagery info.
756 * @param in imagery info
757 * @return {@code true} if URL is equal to given imagery info
758 */
759 public boolean equalsBaseValues(ImageryInfo in) {
760 return url.equals(in.url);
761 }
762
763 /**
764 * Sets the pixel per degree value.
765 * @param ppd The ppd value
766 * @see #getPixelPerDegree()
767 */
768 public void setPixelPerDegree(double ppd) {
769 this.pixelPerDegree = ppd;
770 }
771
772 /**
773 * Sets the maximum zoom level.
774 * @param defaultMaxZoom The maximum zoom level
775 */
776 public void setDefaultMaxZoom(int defaultMaxZoom) {
777 this.defaultMaxZoom = defaultMaxZoom;
778 }
779
780 /**
781 * Sets the minimum zoom level.
782 * @param defaultMinZoom The minimum zoom level
783 */
784 public void setDefaultMinZoom(int defaultMinZoom) {
785 this.defaultMinZoom = defaultMinZoom;
786 }
787
788 /**
789 * Sets the imagery polygonial bounds.
790 * @param b The imagery bounds (non-rectangular)
791 */
792 public void setBounds(ImageryBounds b) {
793 this.bounds = b;
794 }
795
796 /**
797 * Returns the imagery polygonial bounds.
798 * @return The imagery bounds (non-rectangular)
799 */
800 public ImageryBounds getBounds() {
801 return bounds;
802 }
803
804 @Override
805 public boolean requiresAttribution() {
806 return attributionText != null || attributionLinkURL != null || attributionImage != null
807 || termsOfUseText != null || termsOfUseURL != null;
808 }
809
810 @Override
811 public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) {
812 return attributionText;
813 }
814
815 @Override
816 public String getAttributionLinkURL() {
817 return attributionLinkURL;
818 }
819
820 /**
821 * Return the permission reference URL.
822 * @return The url
823 * @see #setPermissionReferenceURL
824 * @since 11975
825 */
826 public String getPermissionReferenceURL() {
827 return permissionReferenceURL;
828 }
829
830 @Override
831 public Image getAttributionImage() {
832 ImageIcon i = ImageProvider.getIfAvailable(attributionImage);
833 if (i != null) {
834 return i.getImage();
835 }
836 return null;
837 }
838
839 /**
840 * Return the raw attribution logo information (an URL to the image).
841 * @return The url text
842 * @since 12257
843 */
844 public String getAttributionImageRaw() {
845 return attributionImage;
846 }
847
848 @Override
849 public String getAttributionImageURL() {
850 return attributionImageURL;
851 }
852
853 @Override
854 public String getTermsOfUseText() {
855 return termsOfUseText;
856 }
857
858 @Override
859 public String getTermsOfUseURL() {
860 return termsOfUseURL;
861 }
862
863 /**
864 * Set the attribution text
865 * @param text The text
866 * @see #getAttributionText(int, ICoordinate, ICoordinate)
867 */
868 public void setAttributionText(String text) {
869 attributionText = intern(text);
870 }
871
872 /**
873 * Set the attribution image
874 * @param url The url of the image.
875 * @see #getAttributionImageURL()
876 */
877 public void setAttributionImageURL(String url) {
878 attributionImageURL = url;
879 }
880
881 /**
882 * Set the image for the attribution
883 * @param res The image resource
884 * @see #getAttributionImage()
885 */
886 public void setAttributionImage(String res) {
887 attributionImage = res;
888 }
889
890 /**
891 * Sets the URL the attribution should link to.
892 * @param url The url.
893 * @see #getAttributionLinkURL()
894 */
895 public void setAttributionLinkURL(String url) {
896 attributionLinkURL = url;
897 }
898
899 /**
900 * Sets the permission reference URL.
901 * @param url The url.
902 * @see #getPermissionReferenceURL()
903 * @since 11975
904 */
905 public void setPermissionReferenceURL(String url) {
906 permissionReferenceURL = url;
907 }
908
909 /**
910 * Sets the text to display to the user as terms of use.
911 * @param text The text
912 * @see #getTermsOfUseText()
913 */
914 public void setTermsOfUseText(String text) {
915 termsOfUseText = text;
916 }
917
918 /**
919 * Sets a url that links to the terms of use text.
920 * @param text The url.
921 * @see #getTermsOfUseURL()
922 */
923 public void setTermsOfUseURL(String text) {
924 termsOfUseURL = text;
925 }
926
927 /**
928 * Sets the extended URL of this entry.
929 * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info
930 */
931 public void setExtendedUrl(String url) {
932 CheckParameterUtil.ensureParameterNotNull(url);
933
934 // Default imagery type is WMS
935 this.url = url;
936 this.imageryType = ImageryType.WMS;
937
938 defaultMaxZoom = 0;
939 defaultMinZoom = 0;
940 for (ImageryType type : ImageryType.values()) {
941 Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url);
942 if (m.matches()) {
943 this.url = m.group(3);
944 this.imageryType = type;
945 if (m.group(2) != null) {
946 defaultMaxZoom = Integer.parseInt(m.group(2));
947 }
948 if (m.group(1) != null) {
949 defaultMinZoom = Integer.parseInt(m.group(1));
950 }
951 break;
952 }
953 }
954
955 if (serverProjections.isEmpty()) {
956 Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)\\}.*").matcher(url.toUpperCase(Locale.ENGLISH));
957 if (m.matches()) {
958 setServerProjections(Arrays.asList(m.group(1).split(",")));
959 }
960 }
961 }
962
963 /**
964 * Returns the entry name.
965 * @return The entry name
966 * @since 6968
967 */
968 public String getOriginalName() {
969 return this.origName != null ? this.origName : this.name;
970 }
971
972 /**
973 * Sets the entry name and handle translation.
974 * @param language The used language
975 * @param name The entry name
976 * @since 8091
977 */
978 public void setName(String language, String name) {
979 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
980 if (LanguageInfo.isBetterLanguage(langName, language)) {
981 this.name = isdefault ? tr(name) : name;
982 this.langName = language;
983 }
984 if (origName == null || isdefault) {
985 this.origName = name;
986 }
987 }
988
989 /**
990 * Store the id of this info to the preferences and clear it afterwards.
991 */
992 public void clearId() {
993 if (this.id != null) {
994 Collection<String> newAddedIds = new TreeSet<>(Config.getPref().getList("imagery.layers.addedIds"));
995 newAddedIds.add(this.id);
996 Config.getPref().putList("imagery.layers.addedIds", new ArrayList<>(newAddedIds));
997 }
998 setId(null);
999 }
1000
1001 /**
1002 * Determines if this entry is enabled by default.
1003 * @return {@code true} if this entry is enabled by default, {@code false} otherwise
1004 */
1005 public boolean isDefaultEntry() {
1006 return defaultEntry;
1007 }
1008
1009 /**
1010 * Sets the default state of this entry.
1011 * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise
1012 */
1013 public void setDefaultEntry(boolean defaultEntry) {
1014 this.defaultEntry = defaultEntry;
1015 }
1016
1017 /**
1018 * Gets the pixel per degree value
1019 * @return The ppd value.
1020 */
1021 public double getPixelPerDegree() {
1022 return this.pixelPerDegree;
1023 }
1024
1025 /**
1026 * Returns the maximum zoom level.
1027 * @return The maximum zoom level
1028 */
1029 @Override
1030 public int getMaxZoom() {
1031 return this.defaultMaxZoom;
1032 }
1033
1034 /**
1035 * Returns the minimum zoom level.
1036 * @return The minimum zoom level
1037 */
1038 @Override
1039 public int getMinZoom() {
1040 return this.defaultMinZoom;
1041 }
1042
1043 /**
1044 * Returns the description text when existing.
1045 * @return The description
1046 * @since 8065
1047 */
1048 public String getDescription() {
1049 return this.description;
1050 }
1051
1052 /**
1053 * Sets the description text when existing.
1054 * @param language The used language
1055 * @param description the imagery description text
1056 * @since 8091
1057 */
1058 public void setDescription(String language, String description) {
1059 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
1060 if (LanguageInfo.isBetterLanguage(langDescription, language)) {
1061 this.description = isdefault ? tr(description) : description;
1062 this.langDescription = intern(language);
1063 }
1064 }
1065
1066 /**
1067 * Return the sorted list of activated Imagery IDs.
1068 * @return sorted list of activated Imagery IDs
1069 * @since 13536
1070 */
1071 public static Collection<String> getActiveIds() {
1072 ArrayList<String> ids = new ArrayList<>();
1073 IPreferences pref = Config.getPref();
1074 if (pref != null) {
1075 List<ImageryPreferenceEntry> entries = StructUtils.getListOfStructs(
1076 pref, "imagery.entries", null, ImageryPreferenceEntry.class);
1077 if (entries != null) {
1078 for (ImageryPreferenceEntry prefEntry : entries) {
1079 if (prefEntry.id != null && !prefEntry.id.isEmpty())
1080 ids.add(prefEntry.id);
1081 }
1082 Collections.sort(ids);
1083 }
1084 }
1085 return ids;
1086 }
1087
1088 /**
1089 * Returns a tool tip text for display.
1090 * @return The text
1091 * @since 8065
1092 */
1093 public String getToolTipText() {
1094 StringBuilder res = new StringBuilder(getName());
1095 boolean html = false;
1096 String dateStr = getDate();
1097 if (dateStr != null && !dateStr.isEmpty()) {
1098 res.append("<br>").append(tr("Date of imagery: {0}", dateStr));
1099 html = true;
1100 }
1101 if (category != null && category.getDescription() != null) {
1102 res.append("<br>").append(tr("Imagery category: {0}", category.getDescription()));
1103 html = true;
1104 }
1105 if (bestMarked) {
1106 res.append("<br>").append(tr("This imagery is marked as best in this region in other editors."));
1107 html = true;
1108 }
1109 if (overlay) {
1110 res.append("<br>").append(tr("This imagery is an overlay."));
1111 html = true;
1112 }
1113 String desc = getDescription();
1114 if (desc != null && !desc.isEmpty()) {
1115 res.append("<br>").append(Utils.escapeReservedCharactersHTML(desc));
1116 html = true;
1117 }
1118 if (html) {
1119 res.insert(0, "<html>").append("</html>");
1120 }
1121 return res.toString();
1122 }
1123
1124 /**
1125 * Returns the EULA acceptance URL, if any.
1126 * @return The URL to an EULA text that has to be accepted before use, or {@code null}
1127 */
1128 public String getEulaAcceptanceRequired() {
1129 return eulaAcceptanceRequired;
1130 }
1131
1132 /**
1133 * Sets the EULA acceptance URL.
1134 * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use
1135 */
1136 public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
1137 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
1138 }
1139
1140 /**
1141 * Returns the ISO 3166-1-alpha-2 country code.
1142 * @return The country code (2 letters)
1143 */
1144 public String getCountryCode() {
1145 return countryCode;
1146 }
1147
1148 /**
1149 * Sets the ISO 3166-1-alpha-2 country code.
1150 * @param countryCode The country code (2 letters)
1151 */
1152 public void setCountryCode(String countryCode) {
1153 this.countryCode = intern(countryCode);
1154 }
1155
1156 /**
1157 * Returns the date information.
1158 * @return The date (in the form YYYY-MM-DD;YYYY-MM-DD, where
1159 * DD and MM as well as a second date are optional)
1160 * @since 11570
1161 */
1162 public String getDate() {
1163 return date;
1164 }
1165
1166 /**
1167 * Sets the date information.
1168 * @param date The date information
1169 * @since 11570
1170 */
1171 public void setDate(String date) {
1172 this.date = date;
1173 }
1174
1175 /**
1176 * Returns the entry icon.
1177 * @return The entry icon
1178 */
1179 public String getIcon() {
1180 return icon;
1181 }
1182
1183 /**
1184 * Sets the entry icon.
1185 * @param icon The entry icon
1186 */
1187 public void setIcon(String icon) {
1188 this.icon = intern(icon);
1189 }
1190
1191 /**
1192 * Get the projections supported by the server. Only relevant for
1193 * WMS-type ImageryInfo at the moment.
1194 * @return null, if no projections have been specified; the list
1195 * of supported projections otherwise.
1196 */
1197 public List<String> getServerProjections() {
1198 return Collections.unmodifiableList(serverProjections);
1199 }
1200
1201 /**
1202 * Sets the list of collections the server supports
1203 * @param serverProjections The list of supported projections
1204 */
1205 public void setServerProjections(Collection<String> serverProjections) {
1206 CheckParameterUtil.ensureParameterNotNull(serverProjections, "serverProjections");
1207 this.serverProjections = serverProjections.stream()
1208 .map(String::intern)
1209 .collect(Collectors.collectingAndThen(Collectors.toList(), Utils::toUnmodifiableList));
1210 }
1211
1212 /**
1213 * Returns the extended URL, containing in addition of service URL, its type and min/max zoom info.
1214 * @return The extended URL
1215 */
1216 public String getExtendedUrl() {
1217 return imageryType.getTypeString() + (defaultMaxZoom != 0
1218 ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url;
1219 }
1220
1221 /**
1222 * Gets a unique toolbar key to store this layer as toolbar item
1223 * @return The kay.
1224 */
1225 public String getToolbarName() {
1226 String res = name;
1227 if (pixelPerDegree != 0) {
1228 res += "#PPD="+pixelPerDegree;
1229 }
1230 return res;
1231 }
1232
1233 /**
1234 * Gets the name that should be displayed in the menu to add this imagery layer.
1235 * @return The text.
1236 */
1237 public String getMenuName() {
1238 String res = name;
1239 if (pixelPerDegree != 0) {
1240 res += " ("+pixelPerDegree+')';
1241 }
1242 return res;
1243 }
1244
1245 /**
1246 * Determines if this entry requires attribution.
1247 * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise
1248 */
1249 public boolean hasAttribution() {
1250 return attributionText != null;
1251 }
1252
1253 /**
1254 * Copies attribution from another {@code ImageryInfo}.
1255 * @param i The other imagery info to get attribution from
1256 */
1257 public void copyAttribution(ImageryInfo i) {
1258 this.attributionImage = i.attributionImage;
1259 this.attributionImageURL = i.attributionImageURL;
1260 this.attributionText = i.attributionText;
1261 this.attributionLinkURL = i.attributionLinkURL;
1262 this.termsOfUseText = i.termsOfUseText;
1263 this.termsOfUseURL = i.termsOfUseURL;
1264 }
1265
1266 /**
1267 * Applies the attribution from this object to a tile source.
1268 * @param s The tile source
1269 */
1270 public void setAttribution(AbstractTileSource s) {
1271 if (attributionText != null) {
1272 if ("osm".equals(attributionText)) {
1273 s.setAttributionText(new Mapnik().getAttributionText(0, null, null));
1274 } else {
1275 s.setAttributionText(attributionText);
1276 }
1277 }
1278 if (attributionLinkURL != null) {
1279 if ("osm".equals(attributionLinkURL)) {
1280 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL());
1281 } else {
1282 s.setAttributionLinkURL(attributionLinkURL);
1283 }
1284 }
1285 if (attributionImage != null) {
1286 ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage);
1287 if (i != null) {
1288 s.setAttributionImage(i.getImage());
1289 }
1290 }
1291 if (attributionImageURL != null) {
1292 s.setAttributionImageURL(attributionImageURL);
1293 }
1294 if (termsOfUseText != null) {
1295 s.setTermsOfUseText(termsOfUseText);
1296 }
1297 if (termsOfUseURL != null) {
1298 if ("osm".equals(termsOfUseURL)) {
1299 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL());
1300 } else {
1301 s.setTermsOfUseURL(termsOfUseURL);
1302 }
1303 }
1304 }
1305
1306 /**
1307 * Returns the imagery type.
1308 * @return The imagery type
1309 */
1310 public ImageryType getImageryType() {
1311 return imageryType;
1312 }
1313
1314 /**
1315 * Sets the imagery type.
1316 * @param imageryType The imagery type
1317 */
1318 public void setImageryType(ImageryType imageryType) {
1319 this.imageryType = imageryType;
1320 }
1321
1322 /**
1323 * Returns the imagery category.
1324 * @return The imagery category
1325 * @since 13792
1326 */
1327 public ImageryCategory getImageryCategory() {
1328 return category;
1329 }
1330
1331 /**
1332 * Sets the imagery category.
1333 * @param category The imagery category
1334 * @since 13792
1335 */
1336 public void setImageryCategory(ImageryCategory category) {
1337 this.category = category;
1338 }
1339
1340 /**
1341 * Returns the imagery category original string (don't use except for error checks).
1342 * @return The imagery category original string
1343 * @since 13792
1344 */
1345 public String getImageryCategoryOriginalString() {
1346 return categoryOriginalString;
1347 }
1348
1349 /**
1350 * Sets the imagery category original string (don't use except for error checks).
1351 * @param categoryOriginalString The imagery category original string
1352 * @since 13792
1353 */
1354 public void setImageryCategoryOriginalString(String categoryOriginalString) {
1355 this.categoryOriginalString = intern(categoryOriginalString);
1356 }
1357
1358 /**
1359 * Returns true if this layer's URL is matched by one of the regular
1360 * expressions kept by the current OsmApi instance.
1361 * @return {@code true} is this entry is blacklisted, {@code false} otherwise
1362 */
1363 public boolean isBlacklisted() {
1364 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
1365 return capabilities != null && capabilities.isOnImageryBlacklist(this.url);
1366 }
1367
1368 /**
1369 * Sets the map of &lt;header name, header value&gt; that if any of this header
1370 * will be returned, then this tile will be treated as "no tile at this zoom level"
1371 *
1372 * @param noTileHeaders Map of &lt;header name, header value&gt; which will be treated as "no tile at this zoom level"
1373 * @since 9613
1374 */
1375 public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) {
1376 if (noTileHeaders == null || noTileHeaders.isEmpty()) {
1377 this.noTileHeaders = null;
1378 } else {
1379 this.noTileHeaders = noTileHeaders.toMap();
1380 }
1381 }
1382
1383 @Override
1384 public Map<String, Set<String>> getNoTileHeaders() {
1385 return noTileHeaders;
1386 }
1387
1388 /**
1389 * Sets the map of &lt;checksum type, checksum value&gt; that if any tile with that checksum
1390 * will be returned, then this tile will be treated as "no tile at this zoom level"
1391 *
1392 * @param noTileChecksums Map of &lt;checksum type, checksum value&gt; which will be treated as "no tile at this zoom level"
1393 * @since 9613
1394 */
1395 public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) {
1396 if (noTileChecksums == null || noTileChecksums.isEmpty()) {
1397 this.noTileChecksums = null;
1398 } else {
1399 this.noTileChecksums = noTileChecksums.toMap();
1400 }
1401 }
1402
1403 @Override
1404 public Map<String, Set<String>> getNoTileChecksums() {
1405 return noTileChecksums;
1406 }
1407
1408 /**
1409 * Returns the map of &lt;header name, metadata key&gt; indicating, which HTTP headers should
1410 * be moved to metadata
1411 *
1412 * @param metadataHeaders map of &lt;header name, metadata key&gt; indicating, which HTTP headers should be moved to metadata
1413 * @since 8418
1414 */
1415 public void setMetadataHeaders(Map<String, String> metadataHeaders) {
1416 if (metadataHeaders == null || metadataHeaders.isEmpty()) {
1417 this.metadataHeaders = null;
1418 } else {
1419 this.metadataHeaders = metadataHeaders;
1420 }
1421 }
1422
1423 /**
1424 * Gets the flag if the georeference is valid.
1425 * @return <code>true</code> if it is valid.
1426 */
1427 public boolean isGeoreferenceValid() {
1428 return isGeoreferenceValid;
1429 }
1430
1431 /**
1432 * Sets an indicator that the georeference is valid
1433 * @param isGeoreferenceValid <code>true</code> if it is marked as valid.
1434 */
1435 public void setGeoreferenceValid(boolean isGeoreferenceValid) {
1436 this.isGeoreferenceValid = isGeoreferenceValid;
1437 }
1438
1439 /**
1440 * Returns the status of "best" marked status in other editors.
1441 * @return <code>true</code> if it is marked as best.
1442 * @since 11575
1443 */
1444 public boolean isBestMarked() {
1445 return bestMarked;
1446 }
1447
1448 /**
1449 * Returns the overlay indication.
1450 * @return <code>true</code> if it is an overlay.
1451 * @since 13536
1452 */
1453 public boolean isOverlay() {
1454 return overlay;
1455 }
1456
1457 /**
1458 * Sets an indicator that in other editors it is marked as best imagery
1459 * @param bestMarked <code>true</code> if it is marked as best in other editors.
1460 * @since 11575
1461 */
1462 public void setBestMarked(boolean bestMarked) {
1463 this.bestMarked = bestMarked;
1464 }
1465
1466 /**
1467 * Sets overlay indication
1468 * @param overlay <code>true</code> if it is an overlay.
1469 * @since 13536
1470 */
1471 public void setOverlay(boolean overlay) {
1472 this.overlay = overlay;
1473 }
1474
1475 /**
1476 * Adds an old Id.
1477 *
1478 * @param id the Id to be added
1479 * @since 13536
1480 */
1481 public void addOldId(String id) {
1482 if (oldIds == null) {
1483 oldIds = new ArrayList<>();
1484 }
1485 oldIds.add(id);
1486 }
1487
1488 /**
1489 * Get old Ids.
1490 *
1491 * @return collection of ids
1492 * @since 13536
1493 */
1494 public Collection<String> getOldIds() {
1495 return oldIds;
1496 }
1497
1498 /**
1499 * Adds a mirror entry. Mirror entries are completed with the data from the master entry
1500 * and only describe another method to access identical data.
1501 *
1502 * @param entry the mirror to be added
1503 * @since 9658
1504 */
1505 public void addMirror(ImageryInfo entry) {
1506 if (mirrors == null) {
1507 mirrors = new ArrayList<>();
1508 }
1509 mirrors.add(entry);
1510 }
1511
1512 /**
1513 * Returns the mirror entries. Entries are completed with master entry data.
1514 *
1515 * @return the list of mirrors
1516 * @since 9658
1517 */
1518 public List<ImageryInfo> getMirrors() {
1519 List<ImageryInfo> l = new ArrayList<>();
1520 if (mirrors != null) {
1521 int num = 1;
1522 for (ImageryInfo i : mirrors) {
1523 ImageryInfo n = new ImageryInfo(this);
1524 if (i.defaultMaxZoom != 0) {
1525 n.defaultMaxZoom = i.defaultMaxZoom;
1526 }
1527 if (i.defaultMinZoom != 0) {
1528 n.defaultMinZoom = i.defaultMinZoom;
1529 }
1530 n.setServerProjections(i.getServerProjections());
1531 n.url = i.url;
1532 n.imageryType = i.imageryType;
1533 if (i.getTileSize() != 0) {
1534 n.setTileSize(i.getTileSize());
1535 }
1536 if (n.id != null) {
1537 n.id = n.id + "_mirror"+num;
1538 }
1539 if (num > 1) {
1540 n.name = tr("{0} mirror server {1}", n.name, num);
1541 if (n.origName != null) {
1542 n.origName += " mirror server " + num;
1543 }
1544 } else {
1545 n.name = tr("{0} mirror server", n.name);
1546 if (n.origName != null) {
1547 n.origName += " mirror server";
1548 }
1549 }
1550 l.add(n);
1551 ++num;
1552 }
1553 }
1554 return l;
1555 }
1556
1557 /**
1558 * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider)
1559 * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer
1560 * to work on
1561 * @return Collection of the layer names
1562 */
1563 public List<DefaultLayer> getDefaultLayers() {
1564 return defaultLayers;
1565 }
1566
1567 /**
1568 * Sets the default layers that user will work with
1569 * @param layers set the list of default layers
1570 */
1571 public void setDefaultLayers(List<DefaultLayer> layers) {
1572 this.defaultLayers = Utils.toUnmodifiableList(layers);
1573 }
1574
1575 /**
1576 * Returns custom HTTP headers that should be sent with request towards imagery provider
1577 * @return headers
1578 */
1579 public Map<String, String> getCustomHttpHeaders() {
1580 return customHttpHeaders;
1581 }
1582
1583 /**
1584 * Sets custom HTTP headers that should be sent with request towards imagery provider
1585 * @param customHttpHeaders http headers
1586 */
1587 public void setCustomHttpHeaders(Map<String, String> customHttpHeaders) {
1588 this.customHttpHeaders = Utils.toUnmodifiableMap(customHttpHeaders);
1589 }
1590
1591 /**
1592 * Determines if this imagery should be transparent.
1593 * @return should this imagery be transparent
1594 */
1595 public boolean isTransparent() {
1596 return transparent;
1597 }
1598
1599 /**
1600 * Sets whether imagery should be transparent.
1601 * @param transparent set to true if imagery should be transparent
1602 */
1603 public void setTransparent(boolean transparent) {
1604 this.transparent = transparent;
1605 }
1606
1607 /**
1608 * Returns minimum tile expiration in seconds.
1609 * @return minimum tile expiration in seconds
1610 */
1611 public int getMinimumTileExpire() {
1612 return minimumTileExpire;
1613 }
1614
1615 /**
1616 * Sets minimum tile expiration in seconds.
1617 * @param minimumTileExpire minimum tile expiration in seconds
1618 */
1619 public void setMinimumTileExpire(int minimumTileExpire) {
1620 this.minimumTileExpire = minimumTileExpire;
1621 }
1622
1623 /**
1624 * Get a string representation of this imagery info suitable for the {@code source} changeset tag.
1625 * @return English name, if known
1626 * @since 13890
1627 */
1628 public String getSourceName() {
1629 if (ImageryType.BING == getImageryType()) {
1630 return "Bing";
1631 } else {
1632 if (id != null) {
1633 // Retrieve english name, unfortunately not saved in preferences
1634 Optional<ImageryInfo> infoEn = ImageryLayerInfo.allDefaultLayers.stream().filter(x -> id.equals(x.getId())).findAny();
1635 if (infoEn.isPresent()) {
1636 return infoEn.get().getOriginalName();
1637 }
1638 }
1639 return getOriginalName();
1640 }
1641 }
1642
1643 private static String intern(String string) {
1644 return string == null ? null : string.intern();
1645 }
1646}
Note: See TracBrowser for help on using the repository browser.