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

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

see #19724 - fix issues reported by Error-Prone 2.5.1 before it crashes

  • Property svn:eol-style set to native
File size: 33.0 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.io.StringReader;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.Collection;
10import java.util.Collections;
11import java.util.EnumMap;
12import java.util.List;
13import java.util.Locale;
14import java.util.Map;
15import java.util.Objects;
16import java.util.Optional;
17import java.util.concurrent.TimeUnit;
18import java.util.regex.Matcher;
19import java.util.regex.Pattern;
20import java.util.stream.Collectors;
21
22import javax.json.Json;
23import javax.json.JsonObject;
24import javax.json.JsonReader;
25import javax.swing.ImageIcon;
26
27import org.openstreetmap.josm.data.StructUtils.StructEntry;
28import org.openstreetmap.josm.data.sources.ISourceCategory;
29import org.openstreetmap.josm.data.sources.ISourceType;
30import org.openstreetmap.josm.data.sources.SourceBounds;
31import org.openstreetmap.josm.data.sources.SourceInfo;
32import org.openstreetmap.josm.data.sources.SourcePreferenceEntry;
33import org.openstreetmap.josm.tools.CheckParameterUtil;
34import org.openstreetmap.josm.tools.ImageProvider;
35import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
36import org.openstreetmap.josm.tools.Logging;
37import org.openstreetmap.josm.tools.MultiMap;
38import org.openstreetmap.josm.tools.StreamUtils;
39import org.openstreetmap.josm.tools.Utils;
40
41/**
42 * Class that stores info about an image background layer.
43 *
44 * @author Frederik Ramm
45 */
46public class ImageryInfo extends
47 SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType, ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> {
48
49 /**
50 * Type of imagery entry.
51 */
52 public enum ImageryType implements ISourceType<ImageryType> {
53 /** A WMS (Web Map Service) entry. **/
54 WMS("wms"),
55 /** A TMS (Tile Map Service) entry. **/
56 TMS("tms"),
57 /** TMS entry for Microsoft Bing. */
58 BING("bing"),
59 /** TMS entry for Russian company <a href="https://wiki.openstreetmap.org/wiki/WikiProject_Russia/kosmosnimki">ScanEx</a>. **/
60 SCANEX("scanex"),
61 /** A WMS endpoint entry only stores the WMS server info, without layer, which are chosen later by the user. **/
62 WMS_ENDPOINT("wms_endpoint"),
63 /** WMTS stores GetCapabilities URL. Does not store any information about the layer **/
64 WMTS("wmts");
65
66 private final String typeString;
67
68 ImageryType(String typeString) {
69 this.typeString = typeString;
70 }
71
72 /**
73 * Returns the unique string identifying this type.
74 * @return the unique string identifying this type
75 * @since 6690
76 */
77 @Override
78 public final String getTypeString() {
79 return typeString;
80 }
81
82 /**
83 * Returns the imagery type from the given type string.
84 * @param s The type string
85 * @return the imagery type matching the given type string
86 */
87 public static ImageryType fromString(String s) {
88 return Arrays.stream(ImageryType.values())
89 .filter(type -> type.getTypeString().equals(s))
90 .findFirst().orElse(null);
91 }
92
93 @Override
94 public ImageryType getFromString(String s) {
95 return fromString(s);
96 }
97
98 @Override
99 public ImageryType getDefault() {
100 return WMS;
101 }
102 }
103
104 /**
105 * Category of imagery entry.
106 * @since 13792
107 */
108 public enum ImageryCategory implements ISourceCategory<ImageryCategory> {
109 /** A aerial or satellite photo. **/
110 PHOTO(/* ICON(data/imagery/) */ "photo", tr("Aerial or satellite photo")),
111 /** A map of digital terrain model, digital surface model or contour lines. **/
112 ELEVATION(/* ICON(data/imagery/) */ "elevation", tr("Elevation map")),
113 /** A map. **/
114 MAP(/* ICON(data/imagery/) */ "map", tr("Map")),
115 /** A historic or otherwise outdated map. */
116 HISTORICMAP(/* ICON(data/imagery/) */ "historicmap", tr("Historic or otherwise outdated map")),
117 /** A map based on OSM data. **/
118 OSMBASEDMAP(/* ICON(data/imagery/) */ "osmbasedmap", tr("Map based on OSM data")),
119 /** A historic or otherwise outdated aerial or satellite photo. **/
120 HISTORICPHOTO(/* ICON(data/imagery/) */ "historicphoto", tr("Historic or otherwise outdated aerial or satellite photo")),
121 /** A map for quality assurance **/
122 QUALITY_ASSURANCE(/* ICON(data/imagery/) */ "qa", tr("Map for quality assurance")),
123 /** Any other type of imagery **/
124 OTHER(/* ICON(data/imagery/) */ "other", tr("Imagery not matching any other category"));
125
126 private final String category;
127 private final String description;
128 private static final Map<ImageSizes, Map<ImageryCategory, ImageIcon>> iconCache =
129 Collections.synchronizedMap(new EnumMap<>(ImageSizes.class));
130
131 ImageryCategory(String category, String description) {
132 this.category = category;
133 this.description = description;
134 }
135
136 /**
137 * Returns the unique string identifying this category.
138 * @return the unique string identifying this category
139 */
140 @Override
141 public final String getCategoryString() {
142 return category;
143 }
144
145 /**
146 * Returns the description of this category.
147 * @return the description of this category
148 */
149 @Override
150 public final String getDescription() {
151 return description;
152 }
153
154 /**
155 * Returns the category icon at the given size.
156 * @param size icon wanted size
157 * @return the category icon at the given size
158 * @since 15049
159 */
160 @Override
161 public final ImageIcon getIcon(ImageSizes size) {
162 return iconCache
163 .computeIfAbsent(size, x -> Collections.synchronizedMap(new EnumMap<>(ImageryCategory.class)))
164 .computeIfAbsent(this, x -> ImageProvider.get("data/imagery", x.category, size));
165 }
166
167 /**
168 * Returns the imagery category from the given category string.
169 * @param s The category string
170 * @return the imagery category matching the given category string
171 */
172 public static ImageryCategory fromString(String s) {
173 return Arrays.stream(ImageryCategory.values())
174 .filter(category -> category.getCategoryString().equals(s))
175 .findFirst().orElse(null);
176 }
177
178 @Override
179 public ImageryCategory getDefault() {
180 return OTHER;
181 }
182
183 @Override
184 public ImageryCategory getFromString(String s) {
185 return fromString(s);
186 }
187 }
188
189 /**
190 * Multi-polygon bounds for imagery backgrounds.
191 * Used to display imagery coverage in preferences and to determine relevant imagery entries based on edit location.
192 */
193 public static class ImageryBounds extends SourceBounds {
194
195 /**
196 * Constructs a new {@code ImageryBounds} from string.
197 * @param asString The string containing the list of shapes defining this bounds
198 * @param separator The shape separator in the given string, usually a comma
199 */
200 public ImageryBounds(String asString, String separator) {
201 super(asString, separator);
202 }
203 }
204
205 private double pixelPerDegree;
206 /** maximum zoom level for TMS imagery */
207 private int defaultMaxZoom;
208 /** minimum zoom level for TMS imagery */
209 private int defaultMinZoom;
210 /** projections supported by WMS servers */
211 private List<String> serverProjections = Collections.emptyList();
212 /**
213 * marked as best in other editors
214 * @since 11575
215 */
216 private boolean bestMarked;
217 /**
218 * marked as overlay
219 * @since 13536
220 */
221 private boolean overlay;
222
223 /** mirrors of different type for this entry */
224 protected List<ImageryInfo> mirrors;
225 /**
226 * Auxiliary class to save an {@link ImageryInfo} object in the preferences.
227 */
228 /** is the geo reference correct - don't offer offset handling */
229 private boolean isGeoreferenceValid;
230 /** Should this map be transparent **/
231 private boolean transparent = true;
232 private int minimumTileExpire = (int) TimeUnit.MILLISECONDS.toSeconds(TMSCachedTileLoaderJob.MINIMUM_EXPIRES.get());
233
234 /**
235 * The ImageryPreferenceEntry class for storing data in JOSM preferences.
236 *
237 * @author Frederik Ramm, modified by Taylor Smock
238 */
239 public static class ImageryPreferenceEntry extends SourcePreferenceEntry<ImageryInfo> {
240 @StructEntry String d;
241 @StructEntry double pixel_per_eastnorth;
242 @StructEntry int max_zoom;
243 @StructEntry int min_zoom;
244 @StructEntry String projections;
245 @StructEntry MultiMap<String, String> noTileHeaders;
246 @StructEntry MultiMap<String, String> noTileChecksums;
247 @StructEntry int tileSize = -1;
248 @StructEntry Map<String, String> metadataHeaders;
249 @StructEntry boolean valid_georeference;
250 @StructEntry boolean bestMarked;
251 @StructEntry boolean modTileFeatures;
252 @StructEntry boolean overlay;
253 @StructEntry boolean transparent;
254 @StructEntry int minimumTileExpire;
255
256 /**
257 * Constructs a new empty WMS {@code ImageryPreferenceEntry}.
258 */
259 public ImageryPreferenceEntry() {
260 super();
261 }
262
263 /**
264 * Constructs a new {@code ImageryPreferenceEntry} from a given {@code ImageryInfo}.
265 * @param i The corresponding imagery info
266 */
267 public ImageryPreferenceEntry(ImageryInfo i) {
268 super(i);
269 pixel_per_eastnorth = i.pixelPerDegree;
270 bestMarked = i.bestMarked;
271 overlay = i.overlay;
272 max_zoom = i.defaultMaxZoom;
273 min_zoom = i.defaultMinZoom;
274 if (!i.serverProjections.isEmpty()) {
275 projections = String.join(",", i.serverProjections);
276 }
277 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) {
278 noTileHeaders = new MultiMap<>(i.noTileHeaders);
279 }
280
281 if (i.noTileChecksums != null && !i.noTileChecksums.isEmpty()) {
282 noTileChecksums = new MultiMap<>(i.noTileChecksums);
283 }
284
285 if (i.metadataHeaders != null && !i.metadataHeaders.isEmpty()) {
286 metadataHeaders = i.metadataHeaders;
287 }
288
289 tileSize = i.getTileSize();
290
291 valid_georeference = i.isGeoreferenceValid();
292 modTileFeatures = i.isModTileFeatures();
293 transparent = i.isTransparent();
294 minimumTileExpire = i.minimumTileExpire;
295 }
296
297 @Override
298 public String toString() {
299 StringBuilder s = new StringBuilder("ImageryPreferenceEntry [name=").append(name);
300 if (id != null) {
301 s.append(" id=").append(id);
302 }
303 s.append(']');
304 return s.toString();
305 }
306 }
307
308 /**
309 * Constructs a new WMS {@code ImageryInfo}.
310 */
311 public ImageryInfo() {
312 super();
313 }
314
315 /**
316 * Constructs a new WMS {@code ImageryInfo} with a given name.
317 * @param name The entry name
318 */
319 public ImageryInfo(String name) {
320 super(name);
321 }
322
323 /**
324 * Constructs a new WMS {@code ImageryInfo} with given name and extended URL.
325 * @param name The entry name
326 * @param url The entry extended URL
327 */
328 public ImageryInfo(String name, String url) {
329 this(name);
330 setExtendedUrl(url);
331 }
332
333 /**
334 * Constructs a new WMS {@code ImageryInfo} with given name, extended and EULA URLs.
335 * @param name The entry name
336 * @param url The entry URL
337 * @param eulaAcceptanceRequired The EULA URL
338 */
339 public ImageryInfo(String name, String url, String eulaAcceptanceRequired) {
340 this(name);
341 setExtendedUrl(url);
342 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
343 }
344
345 /**
346 * Constructs a new {@code ImageryInfo} with given name, url, extended and EULA URLs.
347 * @param name The entry name
348 * @param url The entry URL
349 * @param type The entry imagery type. If null, WMS will be used as default
350 * @param eulaAcceptanceRequired The EULA URL
351 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work
352 * @throws IllegalArgumentException if type refers to an unknown imagery type
353 */
354 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) {
355 this(name);
356 setExtendedUrl(url);
357 ImageryType t = ImageryType.fromString(type);
358 this.cookies = cookies;
359 this.eulaAcceptanceRequired = eulaAcceptanceRequired;
360 if (t != null) {
361 this.sourceType = t;
362 } else if (type != null && !type.isEmpty()) {
363 throw new IllegalArgumentException("unknown type: "+type);
364 }
365 }
366
367 /**
368 * Constructs a new {@code ImageryInfo} with given name, url, id, extended and EULA URLs.
369 * @param name The entry name
370 * @param url The entry URL
371 * @param type The entry imagery type. If null, WMS will be used as default
372 * @param eulaAcceptanceRequired The EULA URL
373 * @param cookies The data part of HTTP cookies header in case the service requires cookies to work
374 * @param id tile id
375 * @throws IllegalArgumentException if type refers to an unknown imagery type
376 */
377 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies, String id) {
378 this(name, url, type, eulaAcceptanceRequired, cookies);
379 setId(id);
380 }
381
382 /**
383 * Constructs a new {@code ImageryInfo} from an imagery preference entry.
384 * @param e The imagery preference entry
385 */
386 public ImageryInfo(ImageryPreferenceEntry e) {
387 super(e.name, e.url, e.id);
388 CheckParameterUtil.ensureParameterNotNull(e.name, "name");
389 CheckParameterUtil.ensureParameterNotNull(e.url, "url");
390 description = e.description;
391 cookies = e.cookies;
392 eulaAcceptanceRequired = e.eula;
393 sourceType = ImageryType.fromString(e.type);
394 if (sourceType == null) throw new IllegalArgumentException("unknown type");
395 pixelPerDegree = e.pixel_per_eastnorth;
396 defaultMaxZoom = e.max_zoom;
397 defaultMinZoom = e.min_zoom;
398 if (e.bounds != null) {
399 bounds = new ImageryBounds(e.bounds, ",");
400 if (e.shapes != null) {
401 try {
402 for (String s : e.shapes.split(";", -1)) {
403 bounds.addShape(new Shape(s, ","));
404 }
405 } catch (IllegalArgumentException ex) {
406 Logging.warn(ex);
407 }
408 }
409 }
410 if (e.projections != null && !e.projections.isEmpty()) {
411 // split generates null element on empty string which gives one element Array[null]
412 setServerProjections(Arrays.asList(e.projections.split(",", -1)));
413 }
414 attributionText = Utils.intern(e.attribution_text);
415 attributionLinkURL = e.attribution_url;
416 permissionReferenceURL = e.permission_reference_url;
417 attributionImage = e.logo_image;
418 attributionImageURL = e.logo_url;
419 date = e.date;
420 bestMarked = e.bestMarked;
421 overlay = e.overlay;
422 termsOfUseText = e.terms_of_use_text;
423 termsOfUseURL = e.terms_of_use_url;
424 countryCode = Utils.intern(e.country_code);
425 icon = Utils.intern(e.icon);
426 if (e.noTileHeaders != null) {
427 noTileHeaders = e.noTileHeaders.toMap();
428 }
429 if (e.noTileChecksums != null) {
430 noTileChecksums = e.noTileChecksums.toMap();
431 }
432 setTileSize(e.tileSize);
433 metadataHeaders = e.metadataHeaders;
434 isGeoreferenceValid = e.valid_georeference;
435 modTileFeatures = e.modTileFeatures;
436 if (e.default_layers != null) {
437 try (JsonReader jsonReader = Json.createReader(new StringReader(e.default_layers))) {
438 defaultLayers = jsonReader.
439 readArray().
440 stream().
441 map(x -> DefaultLayer.fromJson((JsonObject) x, sourceType)).
442 collect(Collectors.toList());
443 }
444 }
445 setCustomHttpHeaders(e.customHttpHeaders);
446 transparent = e.transparent;
447 minimumTileExpire = e.minimumTileExpire;
448 category = ImageryCategory.fromString(e.category);
449 }
450
451 /**
452 * Constructs a new {@code ImageryInfo} from an existing one.
453 * @param i The other imagery info
454 */
455 public ImageryInfo(ImageryInfo i) {
456 super(i.name, i.url, i.id);
457 this.noTileHeaders = i.noTileHeaders;
458 this.noTileChecksums = i.noTileChecksums;
459 this.minZoom = i.minZoom;
460 this.maxZoom = i.maxZoom;
461 this.cookies = i.cookies;
462 this.tileSize = i.tileSize;
463 this.metadataHeaders = i.metadataHeaders;
464 this.modTileFeatures = i.modTileFeatures;
465
466 this.origName = i.origName;
467 this.langName = i.langName;
468 this.defaultEntry = i.defaultEntry;
469 this.eulaAcceptanceRequired = null;
470 this.sourceType = i.sourceType;
471 this.pixelPerDegree = i.pixelPerDegree;
472 this.defaultMaxZoom = i.defaultMaxZoom;
473 this.defaultMinZoom = i.defaultMinZoom;
474 this.bounds = i.bounds;
475 this.serverProjections = i.serverProjections;
476 this.description = i.description;
477 this.langDescription = i.langDescription;
478 this.attributionText = i.attributionText;
479 this.privacyPolicyURL = i.privacyPolicyURL;
480 this.permissionReferenceURL = i.permissionReferenceURL;
481 this.attributionLinkURL = i.attributionLinkURL;
482 this.attributionImage = i.attributionImage;
483 this.attributionImageURL = i.attributionImageURL;
484 this.termsOfUseText = i.termsOfUseText;
485 this.termsOfUseURL = i.termsOfUseURL;
486 this.countryCode = i.countryCode;
487 this.date = i.date;
488 this.bestMarked = i.bestMarked;
489 this.overlay = i.overlay;
490 // do not copy field {@code mirrors}
491 this.icon = Utils.intern(i.icon);
492 this.isGeoreferenceValid = i.isGeoreferenceValid;
493 setDefaultLayers(i.defaultLayers);
494 setCustomHttpHeaders(i.customHttpHeaders);
495 this.transparent = i.transparent;
496 this.minimumTileExpire = i.minimumTileExpire;
497 this.categoryOriginalString = Utils.intern(i.categoryOriginalString);
498 this.category = i.category;
499 }
500
501 /**
502 * Adds a mirror entry. Mirror entries are completed with the data from the master entry
503 * and only describe another method to access identical data.
504 *
505 * @param entry the mirror to be added
506 * @since 9658
507 */
508 public void addMirror(ImageryInfo entry) {
509 if (mirrors == null) {
510 mirrors = new ArrayList<>();
511 }
512 mirrors.add(entry);
513 }
514
515 /**
516 * Returns the mirror entries. Entries are completed with master entry data.
517 *
518 * @return the list of mirrors
519 * @since 9658
520 */
521 public List<ImageryInfo> getMirrors() {
522 List<ImageryInfo> l = new ArrayList<>();
523 if (mirrors != null) {
524 int num = 1;
525 for (ImageryInfo i : mirrors) {
526 ImageryInfo n = new ImageryInfo(this);
527 if (i.defaultMaxZoom != 0) {
528 n.defaultMaxZoom = i.defaultMaxZoom;
529 }
530 if (i.defaultMinZoom != 0) {
531 n.defaultMinZoom = i.defaultMinZoom;
532 }
533 n.setServerProjections(i.getServerProjections());
534 n.url = i.url;
535 n.sourceType = i.sourceType;
536 if (i.getTileSize() != 0) {
537 n.setTileSize(i.getTileSize());
538 }
539 if (i.getPrivacyPolicyURL() != null) {
540 n.setPrivacyPolicyURL(i.getPrivacyPolicyURL());
541 }
542 if (n.id != null) {
543 n.id = n.id + "_mirror" + num;
544 }
545 if (num > 1) {
546 n.name = tr("{0} mirror server {1}", n.name, num);
547 if (n.origName != null) {
548 n.origName += " mirror server " + num;
549 }
550 } else {
551 n.name = tr("{0} mirror server", n.name);
552 if (n.origName != null) {
553 n.origName += " mirror server";
554 }
555 }
556 l.add(n);
557 ++num;
558 }
559 }
560 return l;
561 }
562
563 /**
564 * Check if this object equals another ImageryInfo with respect to the properties
565 * that get written to the preference file.
566 *
567 * The field {@link #pixelPerDegree} is ignored.
568 *
569 * @param other the ImageryInfo object to compare to
570 * @return true if they are equal
571 */
572 @Override
573 public boolean equalsPref(SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType,
574 ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> other) {
575 if (!(other instanceof ImageryInfo)) {
576 return false;
577 }
578 ImageryInfo realOther = (ImageryInfo) other;
579
580 // CHECKSTYLE.OFF: BooleanExpressionComplexity
581 return super.equalsPref(realOther) &&
582 this.bestMarked == realOther.bestMarked &&
583 this.overlay == realOther.overlay &&
584 this.isGeoreferenceValid == realOther.isGeoreferenceValid &&
585 this.defaultMaxZoom == realOther.defaultMaxZoom &&
586 this.defaultMinZoom == realOther.defaultMinZoom &&
587 Objects.equals(this.serverProjections, realOther.serverProjections) &&
588 this.transparent == realOther.transparent &&
589 this.minimumTileExpire == realOther.minimumTileExpire;
590 // CHECKSTYLE.ON: BooleanExpressionComplexity
591 }
592
593 @Override
594 public int compareTo(SourceInfo<ImageryInfo.ImageryCategory, ImageryInfo.ImageryType,
595 ImageryInfo.ImageryBounds, ImageryInfo.ImageryPreferenceEntry> other) {
596 int i = super.compareTo(other);
597 if (other instanceof ImageryInfo) {
598 ImageryInfo in = (ImageryInfo) other;
599 if (i == 0) {
600 i = Double.compare(pixelPerDegree, in.pixelPerDegree);
601 }
602 }
603 return i;
604 }
605
606 /**
607 * Sets the pixel per degree value.
608 * @param ppd The ppd value
609 * @see #getPixelPerDegree()
610 */
611 public void setPixelPerDegree(double ppd) {
612 this.pixelPerDegree = ppd;
613 }
614
615 /**
616 * Sets the maximum zoom level.
617 * @param defaultMaxZoom The maximum zoom level
618 */
619 public void setDefaultMaxZoom(int defaultMaxZoom) {
620 this.defaultMaxZoom = defaultMaxZoom;
621 }
622
623 /**
624 * Sets the minimum zoom level.
625 * @param defaultMinZoom The minimum zoom level
626 */
627 public void setDefaultMinZoom(int defaultMinZoom) {
628 this.defaultMinZoom = defaultMinZoom;
629 }
630
631 @Override
632 public void setBounds(ImageryBounds b) {
633 // for binary compatibility
634 this.bounds = b;
635 }
636
637 @Override
638 public ImageryBounds getBounds() {
639 // for binary compatibility
640 return super.getBounds();
641 }
642
643 /**
644 * Sets the extended URL of this entry.
645 * @param url Entry extended URL containing in addition of service URL, its type and min/max zoom info
646 */
647 public void setExtendedUrl(String url) {
648 CheckParameterUtil.ensureParameterNotNull(url);
649
650 // Default imagery type is WMS
651 this.url = url;
652 this.sourceType = ImageryType.WMS;
653
654 defaultMaxZoom = 0;
655 defaultMinZoom = 0;
656 for (ImageryType type : ImageryType.values()) {
657 Matcher m = Pattern.compile(type.getTypeString()+"(?:\\[(?:(\\d+)[,-])?(\\d+)\\])?:(.*)").matcher(url);
658 if (m.matches()) {
659 this.url = m.group(3);
660 this.sourceType = type;
661 if (m.group(2) != null) {
662 defaultMaxZoom = Integer.parseInt(m.group(2));
663 }
664 if (m.group(1) != null) {
665 defaultMinZoom = Integer.parseInt(m.group(1));
666 }
667 break;
668 }
669 }
670
671 if (serverProjections.isEmpty()) {
672 Matcher m = Pattern.compile(".*\\{PROJ\\(([^)}]+)\\)\\}.*").matcher(url.toUpperCase(Locale.ENGLISH));
673 if (m.matches()) {
674 setServerProjections(Arrays.asList(m.group(1).split(",", -1)));
675 }
676 }
677 }
678
679 /**
680 * Gets the pixel per degree value
681 * @return The ppd value.
682 */
683 public double getPixelPerDegree() {
684 return this.pixelPerDegree;
685 }
686
687 /**
688 * Returns the maximum zoom level.
689 * @return The maximum zoom level
690 */
691 @Override
692 public int getMaxZoom() {
693 return this.defaultMaxZoom;
694 }
695
696 /**
697 * Returns the minimum zoom level.
698 * @return The minimum zoom level
699 */
700 @Override
701 public int getMinZoom() {
702 return this.defaultMinZoom;
703 }
704
705
706 /**
707 * Returns a tool tip text for display.
708 * @return The text
709 * @since 8065
710 */
711 @Override
712 public String getToolTipText() {
713 StringBuilder res = new StringBuilder(getName());
714 boolean html = false;
715 String dateStr = getDate();
716 if (dateStr != null && !dateStr.isEmpty()) {
717 res.append("<br>").append(tr("Date of imagery: {0}", dateStr));
718 html = true;
719 }
720 if (category != null && category.getDescription() != null) {
721 res.append("<br>").append(tr("Imagery category: {0}", category.getDescription()));
722 html = true;
723 }
724 if (bestMarked) {
725 res.append("<br>").append(tr("This imagery is marked as best in this region in other editors."));
726 html = true;
727 }
728 if (overlay) {
729 res.append("<br>").append(tr("This imagery is an overlay."));
730 html = true;
731 }
732 String desc = getDescription();
733 if (desc != null && !desc.isEmpty()) {
734 res.append("<br>").append(Utils.escapeReservedCharactersHTML(desc));
735 html = true;
736 }
737 if (html) {
738 res.insert(0, "<html>").append("</html>");
739 }
740 return res.toString();
741 }
742
743 /**
744 * Get the projections supported by the server. Only relevant for
745 * WMS-type ImageryInfo at the moment.
746 * @return null, if no projections have been specified; the list
747 * of supported projections otherwise.
748 */
749 public List<String> getServerProjections() {
750 return Collections.unmodifiableList(serverProjections);
751 }
752
753 /**
754 * Sets the list of collections the server supports
755 * @param serverProjections The list of supported projections
756 */
757 public void setServerProjections(Collection<String> serverProjections) {
758 CheckParameterUtil.ensureParameterNotNull(serverProjections, "serverProjections");
759 this.serverProjections = serverProjections.stream()
760 .map(String::intern)
761 .collect(StreamUtils.toUnmodifiableList());
762 }
763
764 /**
765 * Returns the extended URL, containing in addition of service URL, its type and min/max zoom info.
766 * @return The extended URL
767 */
768 public String getExtendedUrl() {
769 return sourceType.getTypeString() + (defaultMaxZoom != 0
770 ? ('['+(defaultMinZoom != 0 ? (Integer.toString(defaultMinZoom) + ',') : "")+defaultMaxZoom+']') : "") + ':' + url;
771 }
772
773 /**
774 * Gets a unique toolbar key to store this layer as toolbar item
775 * @return The kay.
776 */
777 public String getToolbarName() {
778 String res = name;
779 if (pixelPerDegree != 0) {
780 res += "#PPD="+pixelPerDegree;
781 }
782 return res;
783 }
784
785 /**
786 * Gets the name that should be displayed in the menu to add this imagery layer.
787 * @return The text.
788 */
789 public String getMenuName() {
790 String res = name;
791 if (pixelPerDegree != 0) {
792 res += " ("+pixelPerDegree+')';
793 }
794 return res;
795 }
796
797 /**
798 * Returns the imagery type.
799 * @return The imagery type
800 * @see SourceInfo#getSourceType
801 */
802 public ImageryType getImageryType() {
803 return super.getSourceType() != null ? super.getSourceType() : ImageryType.WMS.getDefault();
804 }
805
806 /**
807 * Sets the imagery type.
808 * @param imageryType The imagery type
809 * @see SourceInfo#setSourceType
810 */
811 public void setImageryType(ImageryType imageryType) {
812 super.setSourceType(imageryType);
813 }
814
815 /**
816 * Returns the imagery category.
817 * @return The imagery category
818 * @see SourceInfo#getSourceCategory
819 * @since 13792
820 */
821 public ImageryCategory getImageryCategory() {
822 return super.getSourceCategory();
823 }
824
825 /**
826 * Sets the imagery category.
827 * @param category The imagery category
828 * @see SourceInfo#setSourceCategory
829 * @since 13792
830 */
831 public void setImageryCategory(ImageryCategory category) {
832 super.setSourceCategory(category);
833 }
834
835 /**
836 * Returns the imagery category original string (don't use except for error checks).
837 * @return The imagery category original string
838 * @see SourceInfo#getSourceCategoryOriginalString
839 * @since 13792
840 */
841 public String getImageryCategoryOriginalString() {
842 return super.getSourceCategoryOriginalString();
843 }
844
845 /**
846 * Sets the imagery category original string (don't use except for error checks).
847 * @param categoryOriginalString The imagery category original string
848 * @see SourceInfo#setSourceCategoryOriginalString
849 * @since 13792
850 */
851 public void setImageryCategoryOriginalString(String categoryOriginalString) {
852 super.setSourceCategoryOriginalString(categoryOriginalString);
853 }
854
855 /**
856 * Gets the flag if the georeference is valid.
857 * @return <code>true</code> if it is valid.
858 */
859 public boolean isGeoreferenceValid() {
860 return isGeoreferenceValid;
861 }
862
863 /**
864 * Sets an indicator that the georeference is valid
865 * @param isGeoreferenceValid <code>true</code> if it is marked as valid.
866 */
867 public void setGeoreferenceValid(boolean isGeoreferenceValid) {
868 this.isGeoreferenceValid = isGeoreferenceValid;
869 }
870
871 /**
872 * Returns the status of "best" marked status in other editors.
873 * @return <code>true</code> if it is marked as best.
874 * @since 11575
875 */
876 public boolean isBestMarked() {
877 return bestMarked;
878 }
879
880 /**
881 * Returns the overlay indication.
882 * @return <code>true</code> if it is an overlay.
883 * @since 13536
884 */
885 public boolean isOverlay() {
886 return overlay;
887 }
888
889 /**
890 * Sets an indicator that in other editors it is marked as best imagery
891 * @param bestMarked <code>true</code> if it is marked as best in other editors.
892 * @since 11575
893 */
894 public void setBestMarked(boolean bestMarked) {
895 this.bestMarked = bestMarked;
896 }
897
898 /**
899 * Sets overlay indication
900 * @param overlay <code>true</code> if it is an overlay.
901 * @since 13536
902 */
903 public void setOverlay(boolean overlay) {
904 this.overlay = overlay;
905 }
906
907 /**
908 * Determines if this imagery should be transparent.
909 * @return should this imagery be transparent
910 */
911 public boolean isTransparent() {
912 return transparent;
913 }
914
915 /**
916 * Sets whether imagery should be transparent.
917 * @param transparent set to true if imagery should be transparent
918 */
919 public void setTransparent(boolean transparent) {
920 this.transparent = transparent;
921 }
922
923 /**
924 * Returns minimum tile expiration in seconds.
925 * @return minimum tile expiration in seconds
926 */
927 public int getMinimumTileExpire() {
928 return minimumTileExpire;
929 }
930
931 /**
932 * Sets minimum tile expiration in seconds.
933 * @param minimumTileExpire minimum tile expiration in seconds
934 */
935 public void setMinimumTileExpire(int minimumTileExpire) {
936 this.minimumTileExpire = minimumTileExpire;
937 }
938
939 /**
940 * Get a string representation of this imagery info suitable for the {@code source} changeset tag.
941 * @return English name, if known
942 * @since 13890
943 */
944 public String getSourceName() {
945 if (ImageryType.BING == getImageryType()) {
946 return "Bing";
947 } else {
948 if (id != null) {
949 // Retrieve english name, unfortunately not saved in preferences
950 Optional<ImageryInfo> infoEn = ImageryLayerInfo.allDefaultLayers.stream().filter(x -> id.equals(x.getId())).findAny();
951 if (infoEn.isPresent()) {
952 return infoEn.get().getOriginalName();
953 }
954 }
955 return getOriginalName();
956 }
957 }
958
959 /**
960 * Return the sorted list of activated source IDs.
961 * @return sorted list of activated source IDs
962 * @since 13536
963 */
964 public static Collection<String> getActiveIds() {
965 return getActiveIds(ImageryInfo.class);
966 }
967}
Note: See TracBrowser for help on using the repository browser.