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

Last change on this file since 11347 was 11347, checked in by wiktorn, 7 years ago

Fix mis-reporting unsupported projections

String.split() on empty string generates one element array with empty
element. This would then foul, that Imagery has some projections
defined.

Closes: #14066

  • Property svn:eol-style set to native
File size: 39.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.Main;
29import org.openstreetmap.josm.data.Bounds;
30import org.openstreetmap.josm.data.Preferences.pref;
31import org.openstreetmap.josm.io.Capabilities;
32import org.openstreetmap.josm.io.OsmApi;
33import org.openstreetmap.josm.tools.CheckParameterUtil;
34import org.openstreetmap.josm.tools.ImageProvider;
35import org.openstreetmap.josm.tools.LanguageInfo;
36import org.openstreetmap.josm.tools.MultiMap;
37
38/**
39 * Class that stores info about an image background layer.
40 *
41 * @author Frederik Ramm
42 */
43public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed {
44
45 /**
46 * Type of imagery entry.
47 */
48 public enum ImageryType {
49 /** A WMS (Web Map Service) entry. **/
50 WMS("wms"),
51 /** A TMS (Tile Map Service) entry. **/
52 TMS("tms"),
53 /** An HTML proxy (previously used for Yahoo imagery) entry. **/
54 HTML("html"),
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 /** The data part of HTTP cookies header in case the service requires cookies to work */
158 private String cookies;
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 behing 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 /** mirrors of different type for this entry */
191 private List<ImageryInfo> mirrors;
192 /** icon used in menu */
193 private String icon;
194 private boolean isGeoreferenceValid;
195 private boolean isEpsg4326To3857Supported;
196 /** which layers should be activated by default on layer addition. **/
197 private Collection<DefaultLayer> defaultLayers = Collections.emptyList();
198 // when adding a field, also adapt the ImageryInfo(ImageryInfo)
199 // and ImageryInfo(ImageryPreferenceEntry) constructor, equals method, and ImageryPreferenceEntry
200
201 /**
202 * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
203 */
204 public static class ImageryPreferenceEntry {
205 @pref String name;
206 @pref String id;
207 @pref String type;
208 @pref String url;
209 @pref double pixel_per_eastnorth;
210 @pref String eula;
211 @pref String attribution_text;
212 @pref String attribution_url;
213 @pref String logo_image;
214 @pref String logo_url;
215 @pref String terms_of_use_text;
216 @pref String terms_of_use_url;
217 @pref String country_code = "";
218 @pref int max_zoom;
219 @pref int min_zoom;
220 @pref String cookies;
221 @pref String bounds;
222 @pref String shapes;
223 @pref String projections;
224 @pref String icon;
225 @pref String description;
226 @pref MultiMap<String, String> noTileHeaders;
227 @pref MultiMap<String, String> noTileChecksums;
228 @pref int tileSize = -1;
229 @pref Map<String, String> metadataHeaders;
230 @pref boolean valid_georeference;
231 @pref boolean supports_epsg_4326_to_3857_conversion;
232 // TODO: disabled until change of layers is implemented
233 // @pref String default_layers;
234
235 /**
236 * Constructs a new empty WMS {@code ImageryPreferenceEntry}.
237 */
238 public ImageryPreferenceEntry() {
239 // Do nothing
240 }
241
242 /**
243 * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}.
244 * @param i The corresponding imagery info
245 */
246 public ImageryPreferenceEntry(ImageryInfo i) {
247 name = i.name;
248 id = i.id;
249 type = i.imageryType.getTypeString();
250 url = i.url;
251 pixel_per_eastnorth = i.pixelPerDegree;
252 eula = i.eulaAcceptanceRequired;
253 attribution_text = i.attributionText;
254 attribution_url = i.attributionLinkURL;
255 logo_image = i.attributionImage;
256 logo_url = i.attributionImageURL;
257 terms_of_use_text = i.termsOfUseText;
258 terms_of_use_url = i.termsOfUseURL;
259 country_code = i.countryCode;
260 max_zoom = i.defaultMaxZoom;
261 min_zoom = i.defaultMinZoom;
262 cookies = i.cookies;
263 icon = i.icon;
264 description = i.description;
265 if (i.bounds != null) {
266 bounds = i.bounds.encodeAsString(",");
267 StringBuilder shapesString = new StringBuilder();
268 for (Shape s : i.bounds.getShapes()) {
269 if (shapesString.length() > 0) {
270 shapesString.append(';');
271 }
272 shapesString.append(s.encodeAsString(","));
273 }
274 if (shapesString.length() > 0) {
275 shapes = shapesString.toString();
276 }
277 }
278 projections = i.serverProjections.stream().collect(Collectors.joining(","));
279 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) {
280 noTileHeaders = new MultiMap<>(i.noTileHeaders);
281 }
282
283 if (i.noTileChecksums != null && !i.noTileChecksums.isEmpty()) {
284 noTileChecksums = new MultiMap<>(i.noTileChecksums);
285 }
286
287 if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) {
288 metadataHeaders = i.metadataHeaders;
289 }
290
291 tileSize = i.getTileSize();
292
293 valid_georeference = i.isGeoreferenceValid();
294 supports_epsg_4326_to_3857_conversion = i.isEpsg4326To3857Supported();
295 // TODO disabled until change of layers is implemented
296 // default_layers = i.defaultLayers.stream().collect(Collectors.joining(","));
297 }
298
299 @Override
300 public String toString() {
301 StringBuilder s = new StringBuilder("ImageryPreferenceEntry [name=").append(name);
302 if (id != null) {
303 s.append(" id=").append(id);
304 }
305 s.append(']');
306 return s.toString();
307 }
308 }
309
310 /**
311 * Constructs a new WMS {@code ImageryInfo}.
312 */
313 public ImageryInfo() {
314 super();
315 }
316
317 /**
318 * Constructs a new WMS {@code ImageryInfo} with a given name.
319 * @param name The entry name
320 */
321 public ImageryInfo(String name) {
322 super(name);
323 }
324
325 /**
326 * Constructs a new WMS {@code ImageryInfo} with given name and extended URL.
327 * @param name The entry name
328 * @param url The entry extended URL
329 */
330 public ImageryInfo(String name, String url) {
331 this(name);
332 setExtendedUrl(url);
333 }
334
335 /**
336 * Constructs a new WMS {@code ImageryInfo} with given name, extended and EULA URLs.
337 * @param name The entry name
338 * @param url The entry URL
339 * @param eulaAcceptanceRequired The EULA URL
340 */
341 public ImageryInfo(String name, String url, String eulaAcceptanceRequired) {
342 this(name);
343 setExtendedUrl(url);
344 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
345 }
346
347 /**
348 * Constructs a new {@code ImageryInfo} with given name, url, extended and EULA URLs.
349 * @param name The entry name
350 * @param url The entry URL
351 * @param type The entry imagery type. If null, WMS will be used as default
352 * @param eulaAcceptanceRequired The EULA URL
353 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work
354 * @throws IllegalArgumentException if type refers to an unknown imagery type
355 */
356 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) {
357 this(name);
358 setExtendedUrl(url);
359 ImageryType t = ImageryType.fromString(type);
360 this.cookies = cookies;
361 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
362 if (t != null) {
363 this.imageryType = t;
364 } else if (type != null && !type.trim().isEmpty()) {
365 throw new IllegalArgumentException("unknown type: "+type);
366 }
367 }
368
369 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies, String id) {
370 this(name, url, type, eulaAcceptanceRequired, cookies);
371 setId(id);
372 }
373
374 /**
375 * Constructs a new {@code ImageryInfo} from an imagery preference entry.
376 * @param e The imagery preference entry
377 */
378 public ImageryInfo(ImageryPreferenceEntry e) {
379 super(e.name, e.url, e.id);
380 CheckParameterUtil.ensureParameterNotNull(e.name, "name");
381 CheckParameterUtil.ensureParameterNotNull(e.url, "url");
382 description = e.description;
383 cookies = e.cookies;
384 eulaAcceptanceRequired = e.eula;
385 imageryType = ImageryType.fromString(e.type);
386 if (imageryType == null) throw new IllegalArgumentException("unknown type");
387 pixelPerDegree = e.pixel_per_eastnorth;
388 defaultMaxZoom = e.max_zoom;
389 defaultMinZoom = e.min_zoom;
390 if (e.bounds != null) {
391 bounds = new ImageryBounds(e.bounds, ",");
392 if (e.shapes != null) {
393 try {
394 for (String s : e.shapes.split(";")) {
395 bounds.addShape(new Shape(s, ","));
396 }
397 } catch (IllegalArgumentException ex) {
398 Main.warn(ex);
399 }
400 }
401 }
402 if (e.projections != null && !e.projections.isEmpty()) {
403 // split generates null element on empty string which gives one element Array[null]
404 serverProjections = Arrays.asList(e.projections.split(","));
405 }
406 attributionText = e.attribution_text;
407 attributionLinkURL = e.attribution_url;
408 attributionImage = e.logo_image;
409 attributionImageURL = e.logo_url;
410 termsOfUseText = e.terms_of_use_text;
411 termsOfUseURL = e.terms_of_use_url;
412 countryCode = e.country_code;
413 icon = e.icon;
414 if (e.noTileHeaders != null) {
415 noTileHeaders = e.noTileHeaders.toMap();
416 }
417 if (e.noTileChecksums != null) {
418 noTileChecksums = e.noTileChecksums.toMap();
419 }
420 setTileSize(e.tileSize);
421 metadataHeaders = e.metadataHeaders;
422 isEpsg4326To3857Supported = e.supports_epsg_4326_to_3857_conversion;
423 isGeoreferenceValid = e.valid_georeference;
424 // TODO disabled until change of layers is implemented
425 // defaultLayers = Arrays.asList(e.default_layers.split(","));
426 }
427
428 /**
429 * Constructs a new {@code ImageryInfo} from an existing one.
430 * @param i The other imagery info
431 */
432 public ImageryInfo(ImageryInfo i) {
433 super(i.name, i.url, i.id);
434 this.defaultEntry = i.defaultEntry;
435 this.cookies = i.cookies;
436 this.eulaAcceptanceRequired = null;
437 this.imageryType = i.imageryType;
438 this.pixelPerDegree = i.pixelPerDegree;
439 this.defaultMaxZoom = i.defaultMaxZoom;
440 this.defaultMinZoom = i.defaultMinZoom;
441 this.bounds = i.bounds;
442 this.serverProjections = i.serverProjections;
443 this.attributionText = i.attributionText;
444 this.attributionLinkURL = i.attributionLinkURL;
445 this.attributionImage = i.attributionImage;
446 this.attributionImageURL = i.attributionImageURL;
447 this.termsOfUseText = i.termsOfUseText;
448 this.termsOfUseURL = i.termsOfUseURL;
449 this.countryCode = i.countryCode;
450 this.icon = i.icon;
451 this.description = i.description;
452 this.noTileHeaders = i.noTileHeaders;
453 this.noTileChecksums = i.noTileChecksums;
454 this.metadataHeaders = i.metadataHeaders;
455 this.isEpsg4326To3857Supported = i.isEpsg4326To3857Supported;
456 this.isGeoreferenceValid = i.isGeoreferenceValid;
457 this.defaultLayers = i.defaultLayers;
458 }
459
460 @Override
461 public int hashCode() {
462 return Objects.hash(url, imageryType);
463 }
464
465 /**
466 * Check if this object equals another ImageryInfo with respect to the properties
467 * that get written to the preference file.
468 *
469 * The field {@link #pixelPerDegree} is ignored.
470 *
471 * @param other the ImageryInfo object to compare to
472 * @return true if they are equal
473 */
474 public boolean equalsPref(ImageryInfo other) {
475 if (other == null) {
476 return false;
477 }
478
479 return
480 Objects.equals(this.name, other.name) &&
481 Objects.equals(this.id, other.id) &&
482 Objects.equals(this.url, other.url) &&
483 Objects.equals(this.cookies, other.cookies) &&
484 Objects.equals(this.eulaAcceptanceRequired, other.eulaAcceptanceRequired) &&
485 Objects.equals(this.imageryType, other.imageryType) &&
486 Objects.equals(this.defaultMaxZoom, other.defaultMaxZoom) &&
487 Objects.equals(this.defaultMinZoom, other.defaultMinZoom) &&
488 Objects.equals(this.bounds, other.bounds) &&
489 Objects.equals(this.serverProjections, other.serverProjections) &&
490 Objects.equals(this.attributionText, other.attributionText) &&
491 Objects.equals(this.attributionLinkURL, other.attributionLinkURL) &&
492 Objects.equals(this.attributionImageURL, other.attributionImageURL) &&
493 Objects.equals(this.attributionImage, other.attributionImage) &&
494 Objects.equals(this.termsOfUseText, other.termsOfUseText) &&
495 Objects.equals(this.termsOfUseURL, other.termsOfUseURL) &&
496 Objects.equals(this.countryCode, other.countryCode) &&
497 Objects.equals(this.icon, other.icon) &&
498 Objects.equals(this.description, other.description) &&
499 Objects.equals(this.noTileHeaders, other.noTileHeaders) &&
500 Objects.equals(this.noTileChecksums, other.noTileChecksums) &&
501 Objects.equals(this.metadataHeaders, other.metadataHeaders) &&
502 Objects.equals(this.defaultLayers, other.defaultLayers);
503 }
504
505 @Override
506 public boolean equals(Object o) {
507 if (this == o) return true;
508 if (o == null || getClass() != o.getClass()) return false;
509 ImageryInfo that = (ImageryInfo) o;
510 return imageryType == that.imageryType && Objects.equals(url, that.url);
511 }
512
513 @Override
514 public String toString() {
515 return "ImageryInfo{" +
516 "name='" + name + '\'' +
517 ", countryCode='" + countryCode + '\'' +
518 ", url='" + url + '\'' +
519 ", imageryType=" + imageryType +
520 '}';
521 }
522
523 @Override
524 public int compareTo(ImageryInfo in) {
525 int i = countryCode.compareTo(in.countryCode);
526 if (i == 0) {
527 i = name.toLowerCase(Locale.ENGLISH).compareTo(in.name.toLowerCase(Locale.ENGLISH));
528 }
529 if (i == 0) {
530 i = url.compareTo(in.url);
531 }
532 if (i == 0) {
533 i = Double.compare(pixelPerDegree, in.pixelPerDegree);
534 }
535 return i;
536 }
537
538 public boolean equalsBaseValues(ImageryInfo in) {
539 return url.equals(in.url);
540 }
541
542 /**
543 * Sets the pixel per degree value.
544 * @param ppd The ppd value
545 * @see #getPixelPerDegree()
546 */
547 public void setPixelPerDegree(double ppd) {
548 this.pixelPerDegree = ppd;
549 }
550
551 /**
552 * Sets the maximum zoom level.
553 * @param defaultMaxZoom The maximum zoom level
554 */
555 public void setDefaultMaxZoom(int defaultMaxZoom) {
556 this.defaultMaxZoom = defaultMaxZoom;
557 }
558
559 /**
560 * Sets the minimum zoom level.
561 * @param defaultMinZoom The minimum zoom level
562 */
563 public void setDefaultMinZoom(int defaultMinZoom) {
564 this.defaultMinZoom = defaultMinZoom;
565 }
566
567 /**
568 * Sets the imagery polygonial bounds.
569 * @param b The imagery bounds (non-rectangular)
570 */
571 public void setBounds(ImageryBounds b) {
572 this.bounds = b;
573 }
574
575 /**
576 * Returns the imagery polygonial bounds.
577 * @return The imagery bounds (non-rectangular)
578 */
579 public ImageryBounds getBounds() {
580 return bounds;
581 }
582
583 @Override
584 public boolean requiresAttribution() {
585 return attributionText != null || attributionImage != null || termsOfUseText != null || termsOfUseURL != null;
586 }
587
588 @Override
589 public String getAttributionText(int zoom, ICoordinate topLeft, ICoordinate botRight) {
590 return attributionText;
591 }
592
593 @Override
594 public String getAttributionLinkURL() {
595 return attributionLinkURL;
596 }
597
598 @Override
599 public Image getAttributionImage() {
600 ImageIcon i = ImageProvider.getIfAvailable(attributionImage);
601 if (i != null) {
602 return i.getImage();
603 }
604 return null;
605 }
606
607 @Override
608 public String getAttributionImageURL() {
609 return attributionImageURL;
610 }
611
612 @Override
613 public String getTermsOfUseText() {
614 return termsOfUseText;
615 }
616
617 @Override
618 public String getTermsOfUseURL() {
619 return termsOfUseURL;
620 }
621
622 /**
623 * Set the attribution text
624 * @param text The text
625 * @see #getAttributionText(int, ICoordinate, ICoordinate)
626 */
627 public void setAttributionText(String text) {
628 attributionText = text;
629 }
630
631 /**
632 * Set the attribution image
633 * @param url The url of the image.
634 * @see #getAttributionImageURL()
635 */
636 public void setAttributionImageURL(String url) {
637 attributionImageURL = url;
638 }
639
640 /**
641 * Set the image for the attribution
642 * @param res The image resource
643 * @see #getAttributionImage()
644 */
645 public void setAttributionImage(String res) {
646 attributionImage = res;
647 }
648
649 /**
650 * Sets the URL the attribution should link to.
651 * @param url The url.
652 * @see #getAttributionLinkURL()
653 */
654 public void setAttributionLinkURL(String url) {
655 attributionLinkURL = url;
656 }
657
658 /**
659 * Sets the text to display to the user as terms of use.
660 * @param text The text
661 * @see #getTermsOfUseText()
662 */
663 public void setTermsOfUseText(String text) {
664 termsOfUseText = text;
665 }
666
667 /**
668 * Sets a url that links to the terms of use text.
669 * @param text The url.
670 * @see #getTermsOfUseURL()
671 */
672 public void setTermsOfUseURL(String text) {
673 termsOfUseURL = text;
674 }
675
676 /**
677 * Sets the extended URL of this entry.
678 * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info
679 */
680 public void setExtendedUrl(String url) {
681 CheckParameterUtil.ensureParameterNotNull(url);
682
683 // Default imagery type is WMS
684 this.url = url;
685 this.imageryType = ImageryType.WMS;
686
687 defaultMaxZoom = 0;
688 defaultMinZoom = 0;
689 for (ImageryType type : ImageryType.values()) {
690 Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url);
691 if (m.matches()) {
692 this.url = m.group(3);
693 this.imageryType = type;
694 if (m.group(2) != null) {
695 defaultMaxZoom = Integer.parseInt(m.group(2));
696 }
697 if (m.group(1) != null) {
698 defaultMinZoom = Integer.parseInt(m.group(1));
699 }
700 break;
701 }
702 }
703
704 if (serverProjections.isEmpty()) {
705 serverProjections = new ArrayList<>();
706 Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)\\}.*").matcher(url.toUpperCase(Locale.ENGLISH));
707 if (m.matches()) {
708 for (String p : m.group(1).split(",")) {
709 serverProjections.add(p);
710 }
711 }
712 }
713 }
714
715 /**
716 * Returns the entry name.
717 * @return The entry name
718 * @since 6968
719 */
720 public String getOriginalName() {
721 return this.origName != null ? this.origName : this.name;
722 }
723
724 /**
725 * Sets the entry name and handle translation.
726 * @param language The used language
727 * @param name The entry name
728 * @since 8091
729 */
730 public void setName(String language, String name) {
731 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
732 if (LanguageInfo.isBetterLanguage(langName, language)) {
733 this.name = isdefault ? tr(name) : name;
734 this.langName = language;
735 }
736 if (origName == null || isdefault) {
737 this.origName = name;
738 }
739 }
740
741 /**
742 * Store the id of this info to the preferences and clear it afterwards.
743 */
744 public void clearId() {
745 if (this.id != null) {
746 Collection<String> newAddedIds = new TreeSet<>(Main.pref.getCollection("imagery.layers.addedIds"));
747 newAddedIds.add(this.id);
748 Main.pref.putCollection("imagery.layers.addedIds", newAddedIds);
749 }
750 setId(null);
751 }
752
753 /**
754 * Determines if this entry is enabled by default.
755 * @return {@code true} if this entry is enabled by default, {@code false} otherwise
756 */
757 public boolean isDefaultEntry() {
758 return defaultEntry;
759 }
760
761 /**
762 * Sets the default state of this entry.
763 * @param defaultEntry {@code true} if this entry has to be enabled by default, {@code false} otherwise
764 */
765 public void setDefaultEntry(boolean defaultEntry) {
766 this.defaultEntry = defaultEntry;
767 }
768
769 /**
770 * Return the data part of HTTP cookies header in case the service requires cookies to work
771 * @return the cookie data part
772 */
773 @Override
774 public String getCookies() {
775 return this.cookies;
776 }
777
778 /**
779 * Gets the pixel per degree value
780 * @return The ppd value.
781 */
782 public double getPixelPerDegree() {
783 return this.pixelPerDegree;
784 }
785
786 /**
787 * Returns the maximum zoom level.
788 * @return The maximum zoom level
789 */
790 @Override
791 public int getMaxZoom() {
792 return this.defaultMaxZoom;
793 }
794
795 /**
796 * Returns the minimum zoom level.
797 * @return The minimum zoom level
798 */
799 @Override
800 public int getMinZoom() {
801 return this.defaultMinZoom;
802 }
803
804 /**
805 * Returns the description text when existing.
806 * @return The description
807 * @since 8065
808 */
809 public String getDescription() {
810 return this.description;
811 }
812
813 /**
814 * Sets the description text when existing.
815 * @param language The used language
816 * @param description the imagery description text
817 * @since 8091
818 */
819 public void setDescription(String language, String description) {
820 boolean isdefault = LanguageInfo.getJOSMLocaleCode(null).equals(language);
821 if (LanguageInfo.isBetterLanguage(langDescription, language)) {
822 this.description = isdefault ? tr(description) : description;
823 this.langDescription = language;
824 }
825 }
826
827 /**
828 * Returns a tool tip text for display.
829 * @return The text
830 * @since 8065
831 */
832 public String getToolTipText() {
833 String desc = getDescription();
834 if (desc != null && !desc.isEmpty()) {
835 return "<html>" + getName() + "<br>" + desc + "</html>";
836 }
837 return getName();
838 }
839
840 /**
841 * Returns the EULA acceptance URL, if any.
842 * @return The URL to an EULA text that has to be accepted before use, or {@code null}
843 */
844 public String getEulaAcceptanceRequired() {
845 return eulaAcceptanceRequired;
846 }
847
848 /**
849 * Sets the EULA acceptance URL.
850 * @param eulaAcceptanceRequired The URL to an EULA text that has to be accepted before use
851 */
852 public void setEulaAcceptanceRequired(String eulaAcceptanceRequired) {
853 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
854 }
855
856 /**
857 * Returns the ISO 3166-1-alpha-2 country code.
858 * @return The country code (2 letters)
859 */
860 public String getCountryCode() {
861 return countryCode;
862 }
863
864 /**
865 * Sets the ISO 3166-1-alpha-2 country code.
866 * @param countryCode The country code (2 letters)
867 */
868 public void setCountryCode(String countryCode) {
869 this.countryCode = countryCode;
870 }
871
872 /**
873 * Returns the entry icon.
874 * @return The entry icon
875 */
876 public String getIcon() {
877 return icon;
878 }
879
880 /**
881 * Sets the entry icon.
882 * @param icon The entry icon
883 */
884 public void setIcon(String icon) {
885 this.icon = icon;
886 }
887
888 /**
889 * Get the projections supported by the server. Only relevant for
890 * WMS-type ImageryInfo at the moment.
891 * @return null, if no projections have been specified; the list
892 * of supported projections otherwise.
893 */
894 public List<String> getServerProjections() {
895 return Collections.unmodifiableList(serverProjections);
896 }
897
898 /**
899 * Sets the list of collections the server supports
900 * @param serverProjections The list of supported projections
901 */
902 public void setServerProjections(Collection<String> serverProjections) {
903 CheckParameterUtil.ensureParameterNotNull(serverProjections, "serverProjections");
904 this.serverProjections = new ArrayList<>(serverProjections);
905 }
906
907 /**
908 * Returns the extended URL, containing in addition of service URL, its type and min/max zoom info.
909 * @return The extended URL
910 */
911 public String getExtendedUrl() {
912 return imageryType.getTypeString() + (defaultMaxZoom != 0
913 ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url;
914 }
915
916 /**
917 * Gets a unique toolbar key to store this layer as toolbar item
918 * @return The kay.
919 */
920 public String getToolbarName() {
921 String res = name;
922 if (pixelPerDegree != 0) {
923 res += "#PPD="+pixelPerDegree;
924 }
925 return res;
926 }
927
928 /**
929 * Gets the name that should be displayed in the menu to add this imagery layer.
930 * @return The text.
931 */
932 public String getMenuName() {
933 String res = name;
934 if (pixelPerDegree != 0) {
935 res += " ("+pixelPerDegree+')';
936 }
937 return res;
938 }
939
940 /**
941 * Determines if this entry requires attribution.
942 * @return {@code true} if some attribution text has to be displayed, {@code false} otherwise
943 */
944 public boolean hasAttribution() {
945 return attributionText != null;
946 }
947
948 /**
949 * Copies attribution from another {@code ImageryInfo}.
950 * @param i The other imagery info to get attribution from
951 */
952 public void copyAttribution(ImageryInfo i) {
953 this.attributionImage = i.attributionImage;
954 this.attributionImageURL = i.attributionImageURL;
955 this.attributionText = i.attributionText;
956 this.attributionLinkURL = i.attributionLinkURL;
957 this.termsOfUseText = i.termsOfUseText;
958 this.termsOfUseURL = i.termsOfUseURL;
959 }
960
961 /**
962 * Applies the attribution from this object to a tile source.
963 * @param s The tile source
964 */
965 public void setAttribution(AbstractTileSource s) {
966 if (attributionText != null) {
967 if ("osm".equals(attributionText)) {
968 s.setAttributionText(new Mapnik().getAttributionText(0, null, null));
969 } else {
970 s.setAttributionText(attributionText);
971 }
972 }
973 if (attributionLinkURL != null) {
974 if ("osm".equals(attributionLinkURL)) {
975 s.setAttributionLinkURL(new Mapnik().getAttributionLinkURL());
976 } else {
977 s.setAttributionLinkURL(attributionLinkURL);
978 }
979 }
980 if (attributionImage != null) {
981 ImageIcon i = ImageProvider.getIfAvailable(null, attributionImage);
982 if (i != null) {
983 s.setAttributionImage(i.getImage());
984 }
985 }
986 if (attributionImageURL != null) {
987 s.setAttributionImageURL(attributionImageURL);
988 }
989 if (termsOfUseText != null) {
990 s.setTermsOfUseText(termsOfUseText);
991 }
992 if (termsOfUseURL != null) {
993 if ("osm".equals(termsOfUseURL)) {
994 s.setTermsOfUseURL(new Mapnik().getTermsOfUseURL());
995 } else {
996 s.setTermsOfUseURL(termsOfUseURL);
997 }
998 }
999 }
1000
1001 /**
1002 * Returns the imagery type.
1003 * @return The imagery type
1004 */
1005 public ImageryType getImageryType() {
1006 return imageryType;
1007 }
1008
1009 /**
1010 * Sets the imagery type.
1011 * @param imageryType The imagery type
1012 */
1013 public void setImageryType(ImageryType imageryType) {
1014 this.imageryType = imageryType;
1015 }
1016
1017 /**
1018 * Returns true if this layer's URL is matched by one of the regular
1019 * expressions kept by the current OsmApi instance.
1020 * @return {@code true} is this entry is blacklisted, {@code false} otherwise
1021 */
1022 public boolean isBlacklisted() {
1023 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities();
1024 return capabilities != null && capabilities.isOnImageryBlacklist(this.url);
1025 }
1026
1027 /**
1028 * Sets the map of &lt;header name, header value&gt; that if any of this header
1029 * will be returned, then this tile will be treated as "no tile at this zoom level"
1030 *
1031 * @param noTileHeaders Map of &lt;header name, header value&gt; which will be treated as "no tile at this zoom level"
1032 * @since 9613
1033 */
1034 public void setNoTileHeaders(MultiMap<String, String> noTileHeaders) {
1035 if (noTileHeaders == null) {
1036 this.noTileHeaders = null;
1037 } else {
1038 this.noTileHeaders = noTileHeaders.toMap();
1039 }
1040 }
1041
1042 @Override
1043 public Map<String, Set<String>> getNoTileHeaders() {
1044 return noTileHeaders;
1045 }
1046
1047 /**
1048 * Sets the map of &lt;checksum type, checksum value&gt; that if any tile with that checksum
1049 * will be returned, then this tile will be treated as "no tile at this zoom level"
1050 *
1051 * @param noTileChecksums Map of &lt;checksum type, checksum value&gt; which will be treated as "no tile at this zoom level"
1052 * @since 9613
1053 */
1054 public void setNoTileChecksums(MultiMap<String, String> noTileChecksums) {
1055 if (noTileChecksums == null) {
1056 this.noTileChecksums = null;
1057 } else {
1058 this.noTileChecksums = noTileChecksums.toMap();
1059 }
1060 }
1061
1062 @Override
1063 public Map<String, Set<String>> getNoTileChecksums() {
1064 return noTileChecksums;
1065 }
1066
1067 /**
1068 * Returns the map of &lt;header name, metadata key&gt; indicating, which HTTP headers should
1069 * be moved to metadata
1070 *
1071 * @param metadataHeaders map of &lt;header name, metadata key&gt; indicating, which HTTP headers should be moved to metadata
1072 * @since 8418
1073 */
1074 public void setMetadataHeaders(Map<String, String> metadataHeaders) {
1075 this.metadataHeaders = metadataHeaders;
1076 }
1077
1078 /**
1079 * Gets the flag if epsg 4326 to 3857 is supported
1080 * @return The flag.
1081 */
1082 public boolean isEpsg4326To3857Supported() {
1083 return isEpsg4326To3857Supported;
1084 }
1085
1086 /**
1087 * Sets the flag that epsg 4326 to 3857 is supported
1088 * @param isEpsg4326To3857Supported The flag.
1089 */
1090 public void setEpsg4326To3857Supported(boolean isEpsg4326To3857Supported) {
1091 this.isEpsg4326To3857Supported = isEpsg4326To3857Supported;
1092 }
1093
1094 /**
1095 * Gets the flag if the georeference is valid.
1096 * @return <code>true</code> if it is valid.
1097 */
1098 public boolean isGeoreferenceValid() {
1099 return isGeoreferenceValid;
1100 }
1101
1102 /**
1103 * Sets an indicator that the georeference is valid
1104 * @param isGeoreferenceValid <code>true</code> if it is marked as valid.
1105 */
1106 public void setGeoreferenceValid(boolean isGeoreferenceValid) {
1107 this.isGeoreferenceValid = isGeoreferenceValid;
1108 }
1109
1110 /**
1111 * Adds a mirror entry. Mirror entries are completed with the data from the master entry
1112 * and only describe another method to access identical data.
1113 *
1114 * @param entry the mirror to be added
1115 * @since 9658
1116 */
1117 public void addMirror(ImageryInfo entry) {
1118 if (mirrors == null) {
1119 mirrors = new ArrayList<>();
1120 }
1121 mirrors.add(entry);
1122 }
1123
1124 /**
1125 * Returns the mirror entries. Entries are completed with master entry data.
1126 *
1127 * @return the list of mirrors
1128 * @since 9658
1129 */
1130 public List<ImageryInfo> getMirrors() {
1131 List<ImageryInfo> l = new ArrayList<>();
1132 if (mirrors != null) {
1133 for (ImageryInfo i : mirrors) {
1134 ImageryInfo n = new ImageryInfo(this);
1135 if (i.defaultMaxZoom != 0) {
1136 n.defaultMaxZoom = i.defaultMaxZoom;
1137 }
1138 if (i.defaultMinZoom != 0) {
1139 n.defaultMinZoom = i.defaultMinZoom;
1140 }
1141 n.setServerProjections(i.getServerProjections());
1142 n.url = i.url;
1143 n.imageryType = i.imageryType;
1144 if (i.getTileSize() != 0) {
1145 n.setTileSize(i.getTileSize());
1146 }
1147 l.add(n);
1148 }
1149 }
1150 return l;
1151 }
1152
1153 /**
1154 * Returns default layers that should be shown for this Imagery (if at all supported by imagery provider)
1155 * If no layer is set to default and there is more than one imagery available, then user will be asked to choose the layer
1156 * to work on
1157 * @return Collection of the layer names
1158 */
1159 public Collection<DefaultLayer> getDefaultLayers() {
1160 return defaultLayers;
1161 }
1162
1163 /**
1164 * Sets the default layers that user will work with
1165 * @param layers set the list of default layers
1166 */
1167 public void setDefaultLayers(Collection<DefaultLayer> layers) {
1168 if (ImageryType.WMTS.equals(this.imageryType)) {
1169 CheckParameterUtil.ensureThat(layers == null ||
1170 layers.isEmpty() ||
1171 layers.iterator().next() instanceof WMTSDefaultLayer, "Incorrect default layer");
1172 }
1173 this.defaultLayers = layers;
1174 }
1175}
Note: See TracBrowser for help on using the repository browser.