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

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

fix #18172 - Add new imagery categories "elevation" and "qa"

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