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

Last change on this file since 13753 was 13753, checked in by wiktorn, 6 years ago

Do not return null map, empty map instead.

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