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

Last change on this file since 13660 was 13537, checked in by Don-vip, 6 years ago

see #14655 - checkstyle

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