001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.imagery; 003 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.List; 007import java.util.Map; 008import java.util.concurrent.ConcurrentHashMap; 009import java.util.stream.Stream; 010 011import org.openstreetmap.josm.data.Bounds; 012 013/** 014 * The details of a layer of this WMS server. 015 */ 016public class LayerDetails { 017 private final Map<String, String> styles = new ConcurrentHashMap<>(); // name -> title 018 private final Collection<String> crs = new ArrayList<>(); 019 /** 020 * The layer name (WMS {@code Title}) 021 */ 022 private String title; 023 /** 024 * The layer name (WMS {@code Name}) 025 */ 026 private String name; 027 /** 028 * The layer abstract (WMS {@code Abstract}) 029 * @since 13199 030 */ 031 private String abstr; 032 private final LayerDetails parentLayer; 033 private Bounds bounds; 034 private List<LayerDetails> children = new ArrayList<>(); 035 036 /** 037 * Constructor pointing to parent layer. Set to null if this is topmost layer. 038 * This is needed to properly handle layer attributes inheritance. 039 * 040 * @param parentLayer parent layer 041 */ 042 public LayerDetails(LayerDetails parentLayer) { 043 this.parentLayer = parentLayer; 044 } 045 046 /** 047 * @return projections that are supported by this layer 048 */ 049 public Collection<String> getCrs() { 050 Collection<String> ret = new ArrayList<>(); 051 if (parentLayer != null) { 052 ret.addAll(parentLayer.getCrs()); 053 } 054 ret.addAll(crs); 055 return ret; 056 } 057 058 /** 059 * 060 * @return styles defined for this layer 061 */ 062 public Map<String, String> getStyles() { 063 Map<String, String> ret = new ConcurrentHashMap<>(); 064 if (parentLayer != null) { 065 ret.putAll(parentLayer.getStyles()); 066 } 067 ret.putAll(styles); 068 return ret; 069 } 070 071 /** 072 * @return title "Human readable" title of this layer 073 * @see LayerDetails#getName() 074 */ 075 public String getTitle() { 076 return title; 077 } 078 079 /** 080 * @param title set title of this layer 081 * @see LayerDetails#getName() 082 */ 083 public void setTitle(String title) { 084 this.title = title; 085 } 086 087 /** 088 * 089 * Citation from OGC WMS specification (WMS 1.3.0):<p><i> 090 * A number of elements have both a {@literal <Name>} and a {@literal <Title>}. The Name is a text string used for machine-to-machine 091 * communication while the Title is for the benefit of humans. For example, a dataset might have the descriptive Title 092 * “Maximum Atmospheric Temperature” and be requested using the abbreviated Name “ATMAX”.</i></p> 093 * 094 * And second citation:<p><i> 095 * If, and only if, a layer has a {@literal <Name>}, then it is a map layer that can be requested by using that Name in the 096 * LAYERS parameter of a GetMap request. A Layer that contains a {@literal <Name>} element is referred to as a “named 097 * layer” in this International Standard. If the layer has a Title but no Name, then that layer is only a category title for 098 * all the layers nested within.</i></p> 099 * @return name of this layer 100 */ 101 public String getName() { 102 return name; 103 } 104 105 /** 106 * @param name sets the name of this Layer 107 * @see LayerDetails#getName() 108 */ 109 public void setName(String name) { 110 this.name = name; 111 } 112 113 /** 114 * Add style to list of styles defined by this layer 115 * @param name machine-to-machine name of this style 116 * @param title human readable title of this style 117 */ 118 public void addStyle(String name, String title) { 119 this.styles.put(name, title == null ? "" : title); 120 } 121 122 /** 123 * Add projection supported by this layer 124 * @param crs projection code 125 */ 126 public void addCrs(String crs) { 127 this.crs.add(crs); 128 } 129 130 /** 131 * 132 * @return bounds within layer might be queried 133 */ 134 public Bounds getBounds() { 135 return bounds; 136 } 137 138 /** 139 * sets bounds of this layer 140 * @param bounds of this layer 141 */ 142 public void setBounds(Bounds bounds) { 143 this.bounds = bounds; 144 } 145 146 @Override 147 public String toString() { 148 String baseName = (title == null || title.isEmpty()) ? name : title; 149 return abstr == null || abstr.equalsIgnoreCase(baseName) ? baseName : baseName + " (" + abstr + ')'; 150 } 151 152 /** 153 * 154 * @return parent layer for his layer 155 */ 156 public LayerDetails getParent() { 157 return parentLayer; 158 } 159 160 /** 161 * sets children layers for this layer 162 * @param children children of this layer 163 */ 164 public void setChildren(List<LayerDetails> children) { 165 this.children = children; 166 167 } 168 169 /** 170 * 171 * @return children layers of this layer 172 */ 173 public List<LayerDetails> getChildren() { 174 return children; 175 } 176 177 /** 178 * if user may select this layer (is it possible to request it from server) 179 * @return true if user may select this layer, false if this layer is only grouping other layers 180 */ 181 public boolean isSelectable() { 182 return !(name == null || name.isEmpty()); 183 } 184 185 /** 186 * @return "Narrative description of the layer" 187 */ 188 public String getAbstract() { 189 return abstr; 190 } 191 192 /** 193 * Sets abstract of this layer 194 * @param abstr abstract of this layer 195 */ 196 public void setAbstract(String abstr) { 197 this.abstr = abstr; 198 } 199 200 /** 201 * @return flattened stream of this layer and its children (as well as recursively children of its children) 202 */ 203 public Stream<LayerDetails> flattened() { 204 return Stream.concat( 205 Stream.of(this), 206 getChildren().stream().flatMap(LayerDetails::flattened) 207 ); 208 } 209}