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

Last change on this file since 13421 was 13374, checked in by bastiK, 6 years ago

fixed #15852 - add missing fields in ImageryInfo copy constructor (including tileSize)

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