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

Last change on this file since 11858 was 11858, checked in by bastiK, 7 years ago

fixed #7427 - Support reprojection (warping) of imagery layer

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