source: josm/trunk/src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java@ 8418

Last change on this file since 8418 was 8418, checked in by wiktorn, 9 years ago

Add posibility to display HTTP headers in Show Tile Info dialog. Closes #11456

  • Property svn:eol-style set to native
File size: 10.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.imagery;
3
4import java.io.IOException;
5import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Objects;
14import java.util.Set;
15import java.util.TreeSet;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryPreferenceEntry;
19import org.openstreetmap.josm.io.CachedFile;
20import org.openstreetmap.josm.io.OfflineAccessException;
21import org.openstreetmap.josm.io.OnlineResource;
22import org.openstreetmap.josm.io.imagery.ImageryReader;
23import org.xml.sax.SAXException;
24
25/**
26 * Manages the list of imagery entries that are shown in the imagery menu.
27 */
28public class ImageryLayerInfo {
29
30 public static final ImageryLayerInfo instance = new ImageryLayerInfo();
31 private final List<ImageryInfo> layers = new ArrayList<>();
32 private final Map<String, ImageryInfo> layerIds = new HashMap<>();
33 private static final List<ImageryInfo> defaultLayers = new ArrayList<>();
34 private static final Map<String, ImageryInfo> defaultLayerIds = new HashMap<>();
35
36 private static final String[] DEFAULT_LAYER_SITES = {
37 Main.getJOSMWebsite()+"/maps"
38 };
39
40 /**
41 * Returns the list of imagery layers sites.
42 * @return the list of imagery layers sites
43 * @since 7434
44 */
45 public static Collection<String> getImageryLayersSites() {
46 return Main.pref.getCollection("imagery.layers.sites", Arrays.asList(DEFAULT_LAYER_SITES));
47 }
48
49 private ImageryLayerInfo() {
50 }
51
52 public ImageryLayerInfo(ImageryLayerInfo info) {
53 layers.addAll(info.layers);
54 }
55
56 public void clear() {
57 layers.clear();
58 layerIds.clear();
59 }
60
61 public void load() {
62 clear();
63 List<ImageryPreferenceEntry> entries = Main.pref.getListOfStructs("imagery.entries", null, ImageryPreferenceEntry.class);
64 if (entries != null) {
65 for (ImageryPreferenceEntry prefEntry : entries) {
66 try {
67 ImageryInfo i = new ImageryInfo(prefEntry);
68 add(i);
69 } catch (IllegalArgumentException e) {
70 Main.warn("Unable to load imagery preference entry:"+e);
71 }
72 }
73 Collections.sort(layers);
74 }
75 loadDefaults(false);
76 }
77
78 /**
79 * Loads the available imagery entries.
80 *
81 * The data is downloaded from the JOSM website (or loaded from cache).
82 * Entries marked as "default" are added to the user selection, if not
83 * already present.
84 *
85 * @param clearCache if true, clear the cache and start a fresh download.
86 */
87 public void loadDefaults(boolean clearCache) {
88 defaultLayers.clear();
89 defaultLayerIds.clear();
90 for (String source : getImageryLayersSites()) {
91 boolean online = true;
92 try {
93 OnlineResource.JOSM_WEBSITE.checkOfflineAccess(source, Main.getJOSMWebsite());
94 } catch (OfflineAccessException e) {
95 Main.warn(e.getMessage());
96 online = false;
97 }
98 if (clearCache && online) {
99 CachedFile.cleanup(source);
100 }
101 try {
102 ImageryReader reader = new ImageryReader(source);
103 Collection<ImageryInfo> result = reader.parse();
104 defaultLayers.addAll(result);
105 } catch (IOException ex) {
106 Main.error(ex, false);
107 } catch (SAXException ex) {
108 Main.error(ex);
109 }
110 }
111 while (defaultLayers.remove(null));
112 Collections.sort(defaultLayers);
113 buildIdMap(defaultLayers, defaultLayerIds);
114 updateEntriesFromDefaults();
115 buildIdMap(layers, layerIds);
116 }
117
118 /**
119 * Build the mapping of unique ids to {@link ImageryInfo}s.
120 * @param lst input list
121 * @param idMap output map
122 */
123 private static void buildIdMap(List<ImageryInfo> lst, Map<String, ImageryInfo> idMap) {
124 idMap.clear();
125 Set<String> notUnique = new HashSet<>();
126 for (ImageryInfo i : lst) {
127 if (i.getId() != null) {
128 if (idMap.containsKey(i.getId())) {
129 notUnique.add(i.getId());
130 Main.error("Id ''{0}'' is not unique - used by ''{1}'' and ''{2}''!",
131 i.getId(), i.getName(), idMap.get(i.getId()).getName());
132 continue;
133 }
134 idMap.put(i.getId(), i);
135 }
136 }
137 for (String i : notUnique) {
138 idMap.remove(i);
139 }
140 }
141
142 /**
143 * Update user entries according to the list of default entries.
144 */
145 public void updateEntriesFromDefaults() {
146 // add new default entries to the user selection
147 boolean changed = false;
148 Collection<String> knownDefaults = Main.pref.getCollection("imagery.layers.default");
149 Collection<String> newKnownDefaults = new TreeSet<>(knownDefaults);
150 for (ImageryInfo def : defaultLayers) {
151 // temporary migration code, so all user preferences will get updated with new settings from JOSM site (can be removed ~Dez. 2015)
152 if (def.getNoTileHeaders() != null || def.getTileSize() > 0 || def.getMetadataHeaders() != null ) {
153 for (ImageryInfo i: layers) {
154 if (isSimilar(def, i)) {
155 if (def.getNoTileHeaders() != null) {
156 i.setNoTileHeaders(def.getNoTileHeaders());
157 }
158 if (def.getTileSize() > 0) {
159 i.setTileSize(def.getTileSize());
160 }
161 if (def.getMetadataHeaders() != null && def.getMetadataHeaders().size() > 0) {
162 i.setMetadataHeaders(def.getMetadataHeaders());
163 }
164 changed = true;
165 }
166 }
167 }
168
169 if (def.isDefaultEntry()) {
170 boolean isKnownDefault = false;
171 for (String url : knownDefaults) {
172 if (isSimilar(url, def.getUrl())) {
173 isKnownDefault = true;
174 break;
175 }
176 }
177 boolean isInUserList = false;
178 if (!isKnownDefault) {
179 newKnownDefaults.add(def.getUrl());
180 for (ImageryInfo i : layers) {
181 if (isSimilar(def, i)) {
182 isInUserList = true;
183 break;
184 }
185 }
186 }
187 if (!isKnownDefault && !isInUserList) {
188 add(new ImageryInfo(def));
189 changed = true;
190 }
191 }
192 }
193 Main.pref.putCollection("imagery.layers.default", newKnownDefaults);
194
195 // Add ids to user entries without id.
196 // Only do this the first time for each id, so the user can have
197 // custom entries that don't get updated automatically
198 Collection<String> addedIds = Main.pref.getCollection("imagery.layers.addedIds");
199 Collection<String> newAddedIds = new TreeSet<>(addedIds);
200 for (ImageryInfo info : layers) {
201 for (ImageryInfo def : defaultLayers) {
202 if (isSimilar(def, info)) {
203 if (def.getId() != null && !addedIds.contains(def.getId())) {
204 if (!defaultLayerIds.containsKey(def.getId())) {
205 // ignore ids used more than once (have been purged from the map)
206 continue;
207 }
208 newAddedIds.add(def.getId());
209 if (info.getId() == null) {
210 info.setId(def.getId());
211 changed = true;
212 }
213 }
214 }
215 }
216 }
217 Main.pref.putCollection("imagery.layers.addedIds", newAddedIds);
218
219 // automatically update user entries with same id as a default entry
220 for (int i=0; i<layers.size(); i++) {
221 ImageryInfo info = layers.get(i);
222 if (info.getId() == null) {
223 continue;
224 }
225 ImageryInfo matchingDefault = defaultLayerIds.get(info.getId());
226 if (matchingDefault != null && !matchingDefault.equalsPref(info)) {
227 layers.set(i, matchingDefault);
228 changed = true;
229 }
230 }
231
232 if (changed) {
233 save();
234 }
235 }
236
237 private boolean isSimilar(ImageryInfo iiA, ImageryInfo iiB) {
238 if (iiA.getId() != null && iiB.getId() != null) return iiA.getId().equals(iiB.getId());
239 return isSimilar(iiA.getUrl(), iiB.getUrl());
240 }
241
242 // some additional checks to respect extended URLs in preferences (legacy workaround)
243 private boolean isSimilar(String a, String b) {
244 return Objects.equals(a, b) || (a != null && b != null && !a.isEmpty() && !b.isEmpty() && (a.contains(b) || b.contains(a)));
245 }
246
247 public void add(ImageryInfo info) {
248 layers.add(info);
249 }
250
251 public void remove(ImageryInfo info) {
252 layers.remove(info);
253 }
254
255 public void save() {
256 List<ImageryPreferenceEntry> entries = new ArrayList<>();
257 for (ImageryInfo info : layers) {
258 entries.add(new ImageryPreferenceEntry(info));
259 }
260 Main.pref.putListOfStructs("imagery.entries", entries, ImageryPreferenceEntry.class);
261 }
262
263 public List<ImageryInfo> getLayers() {
264 return Collections.unmodifiableList(layers);
265 }
266
267 public List<ImageryInfo> getDefaultLayers() {
268 return Collections.unmodifiableList(defaultLayers);
269 }
270
271 public static void addLayer(ImageryInfo info) {
272 instance.add(info);
273 instance.save();
274 }
275
276 public static void addLayers(Collection<ImageryInfo> infos) {
277 for (ImageryInfo i : infos) {
278 instance.add(i);
279 }
280 instance.save();
281 Collections.sort(instance.layers);
282 }
283
284 /**
285 * Get unique id for ImageryInfo.
286 *
287 * This takes care, that no id is used twice (due to a user error)
288 * @param info the ImageryInfo to look up
289 * @return null, if there is no id or the id is used twice,
290 * the corresponding id otherwise
291 */
292 public String getUniqueId(ImageryInfo info) {
293 if (info.getId() != null && layerIds.get(info.getId()) == info) {
294 return info.getId();
295 }
296 return null;
297 }
298}
Note: See TracBrowser for help on using the repository browser.