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

Last change on this file since 11649 was 11649, checked in by Don-vip, 7 years ago

sonar - pmd:UseStringBufferForStringAppends

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