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

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

see #18749 - Intern strings to reduce memory footprint

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