Ticket #10454: 10454_no_tiles.patch
File 10454_no_tiles.patch, 39.2 KB (added by , 10 years ago) |
---|
-
data/maps.xsd
diff --git data/maps.xsd data/maps.xsd index d7b8c5d..2643b2d 100644
670 670 </xs:all> 671 671 </xs:complexType> 672 672 </xs:element> 673 <xs:element name="no-tile-header" minOccurs="0" maxOccurs="unbounded"> 674 <xs:complexType> 675 <xs:attribute name="name" type="xs:string" /> 676 <xs:attribute name="value" type="xs:string" /> 677 </xs:complexType> 678 </xs:element> 673 679 </xs:choice> 674 680 </xs:sequence> 675 681 <xs:attribute name="last-check" type="xs:date" use="optional" /> -
src/org/openstreetmap/gui/jmapviewer/Tile.java
diff --git src/org/openstreetmap/gui/jmapviewer/Tile.java src/org/openstreetmap/gui/jmapviewer/Tile.java index edd6554..bfd1eaa 100644
public class Tile { 331 331 loading = false; 332 332 loaded = true; 333 333 } 334 335 /** 336 * 337 * @return TileSource from which this tile comes 338 */ 339 public TileSource getTileSource() { 340 return source; 341 } 334 342 } -
src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java index b8680e8..d53d1d2 100644
2 2 package org.openstreetmap.gui.jmapviewer.interfaces; 3 3 4 4 import java.io.IOException; 5 import java.util.List; 6 import java.util.Map; 5 7 6 8 import org.openstreetmap.gui.jmapviewer.JMapViewer; 7 9 … … public interface TileSource extends Attributed { 155 157 * @return [MIN_LAT..MAX_LAT] 156 158 */ 157 159 double tileYToLat(int y, int zoom); 160 161 /** 162 * Determines, if the returned data from TileSource represent "no tile at this zoom level" situation. Detection 163 * algorithms differ per TileSource, so each TileSource should implement each own specific way. 164 * 165 * @param headers HTTP headers from response from TileSource server 166 * @param statusCode HTTP status code 167 * @param content byte array representing the data returned from the server 168 * @return true, if "no tile at this zoom level" situation detected 169 */ 170 public boolean isNoTileAtZoom(Map<String, List<String>> headers, int statusCode, byte[] content); 158 171 } -
src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractOsmTileSource.java index 90e892c..e3762f4 100644
import java.awt.Image; 6 6 import org.openstreetmap.gui.jmapviewer.Coordinate; 7 7 8 8 /** 9 * Abstract clas for OSM Tile sources9 * Abstract class for OSM Tile sources 10 10 */ 11 11 public abstract class AbstractOsmTileSource extends AbstractTMSTileSource { 12 12 … … public abstract class AbstractOsmTileSource extends AbstractTMSTileSource { 23 23 * are safe for file names; can be null 24 24 */ 25 25 public AbstractOsmTileSource(String name, String base_url, String id) { 26 super(name, base_url, id); 26 super(new TileSourceInfo(name, base_url, id)); 27 27 28 } 28 29 30 @Override 29 31 public int getMaxZoom() { 30 32 return 19; 31 33 } -
src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTMSTileSource.java index e3cfd19..027b550 100644
2 2 package org.openstreetmap.gui.jmapviewer.tilesources; 3 3 4 4 import java.io.IOException; 5 import java.util.List; 6 import java.util.Map; 7 import java.util.Map.Entry; 5 8 6 9 import org.openstreetmap.gui.jmapviewer.OsmMercator; 7 10 … … public abstract class AbstractTMSTileSource extends AbstractTileSource { 10 13 protected String name; 11 14 protected String baseUrl; 12 15 protected String id; 16 private Map<String, String> noTileHeaders; 13 17 14 public AbstractTMSTileSource( String name, String base_url, String id) {15 this.name = name;16 this.baseUrl = base_url;18 public AbstractTMSTileSource(TileSourceInfo info) { 19 this.name = info.getName(); 20 this.baseUrl = info.getUrl(); 17 21 if(baseUrl.endsWith("/")) { 18 22 baseUrl = baseUrl.substring(0,baseUrl.length()-1); 19 23 } 20 this.id = id; 24 this.id = info.getUrl(); 25 this.noTileHeaders = info.getNoTileHeaders(); 21 26 } 22 27 23 28 @Override … … public abstract class AbstractTMSTileSource extends AbstractTileSource { 122 127 public double tileXToLon(int x, int zoom) { 123 128 return OsmMercator.XToLon(x * OsmMercator.TILE_SIZE, zoom); 124 129 } 130 131 @Override 132 public boolean isNoTileAtZoom(Map<String, List<String>> headers, int statusCode, byte[] content) { 133 if(noTileHeaders != null) { 134 for (Entry<String, String> searchEntry: noTileHeaders.entrySet()) { 135 List<String> headerVals = headers.get(searchEntry.getKey()); 136 if (headerVals != null && headerVals.contains(searchEntry.getValue())) { 137 return true; 138 } 139 } 140 } 141 return super.isNoTileAtZoom(headers, statusCode, content); 142 } 125 143 } -
src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/AbstractTileSource.java index 66456a6..bfce358 100644
2 2 package org.openstreetmap.gui.jmapviewer.tilesources; 3 3 4 4 import java.awt.Image; 5 import java.util.List; 6 import java.util.Map; 5 7 6 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;7 8 import org.openstreetmap.gui.jmapviewer.Coordinate; 9 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 8 10 9 11 abstract public class AbstractTileSource implements TileSource { 10 12 … … abstract public class AbstractTileSource implements TileSource { 74 76 this.termsOfUseURL = termsOfUseURL; 75 77 } 76 78 79 public boolean isNoTileAtZoom(Map<String, List<String>> headers, int statusCode, byte[] content) { 80 // default handler - when HTTP 404 is returned, then treat this situation as no tile at this zoom level 81 return statusCode == 404; 82 } 77 83 } -
src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java index f8723b6..e2dce05 100644
public class BingAerialTileSource extends AbstractTMSTileSource { 52 52 * Constructs a new {@code BingAerialTileSource}. 53 53 */ 54 54 public BingAerialTileSource() { 55 this("Bing");55 super(new TileSourceInfo("Bing", null, null)); 56 56 } 57 57 58 /** 59 * Constructs a new {@code BingAerialTileSource}. 60 */ 61 public BingAerialTileSource(String id) { 62 super("Bing Aerial Maps", "http://example.com/", id); 58 public BingAerialTileSource(TileSourceInfo info) { 59 super(info); 63 60 } 64 61 65 62 protected class Attribution { -
src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/ScanexTileSource.java index be7128a..6acd716 100644
public class ScanexTileSource extends TMSTileSource { 45 45 /* IRS by default */ 46 46 private ScanexLayer Layer = ScanexLayer.IRS; 47 47 48 public ScanexTileSource(String name, String url, String id, int maxZoom) { 49 super(name, url, id, maxZoom); 48 public ScanexTileSource(TileSourceInfo info) { 49 super(info); 50 String url = info.getUrl(); 50 51 51 52 for (ScanexLayer layer : ScanexLayer.values()) { 52 53 if (url.equalsIgnoreCase(layer.getName())) { … … public class ScanexTileSource extends TMSTileSource { 77 78 return this.Layer.getUri() + "&apikey=" + API_KEY + "&x=" + tilex + "&y=" + tiley + "&z=" + zoom; 78 79 } 79 80 81 @Override 80 82 public TileUpdate getTileUpdate() { 81 83 return TileUpdate.IfNoneMatch; 82 84 } -
src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/TMSTileSource.java index 23c792d..21f2611 100644
1 1 // License: GPL. For details, see Readme.txt file. 2 2 package org.openstreetmap.gui.jmapviewer.tilesources; 3 3 4 4 5 public class TMSTileSource extends AbstractTMSTileSource { 5 6 6 7 protected int maxZoom; 7 8 protected int minZoom = 0; 8 9 9 public TMSTileSource(String name, String url, String id, int maxZoom) { 10 super(name, url, id); 11 this.maxZoom = maxZoom; 12 } 13 14 public TMSTileSource(String name, String url, String id, int minZoom, int maxZoom) { 15 super(name, url, id); 16 this.minZoom = minZoom; 17 this.maxZoom = maxZoom; 10 public TMSTileSource(TileSourceInfo info) { 11 super(info); 12 minZoom = info.getMinZoom(); 13 maxZoom = info.getMaxZoom(); 18 14 } 19 15 20 16 @Override -
src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java src/org/openstreetmap/gui/jmapviewer/tilesources/TemplatedTMSTileSource.java index 0b59c5d..4b43ff0 100644
1 1 // License: GPL. For details, see Readme.txt file. 2 2 package org.openstreetmap.gui.jmapviewer.tilesources; 3 3 4 import java.util.Map;5 4 import java.util.HashMap; 5 import java.util.Map; 6 6 import java.util.Random; 7 import java.util.regex.Pattern;8 7 import java.util.regex.Matcher; 8 import java.util.regex.Pattern; 9 9 10 10 public class TemplatedTMSTileSource extends TMSTileSource { 11 11 … … public class TemplatedTMSTileSource extends TMSTileSource { 27 27 PATTERN_SWITCH 28 28 }; 29 29 30 public TemplatedTMSTileSource(String name, String url, String id, int maxZoom) { 31 super(name, url, id, maxZoom); 32 handleTemplate(); 33 } 34 35 public TemplatedTMSTileSource(String name, String url, String id, int minZoom, int maxZoom) { 36 super(name, url, id, minZoom, maxZoom); 37 handleTemplate(); 38 } 39 40 public TemplatedTMSTileSource(String name, String url, String id, int minZoom, int maxZoom, String cookies) { 41 super(name, url, id, minZoom, maxZoom); 42 if (cookies != null) { 43 headers.put(COOKIE_HEADER, cookies); 30 public TemplatedTMSTileSource(TileSourceInfo info) { 31 super(info); 32 if (info.getCookies() != null) { 33 headers.put(COOKIE_HEADER, info.getCookies()); 44 34 } 45 35 handleTemplate(); 46 36 } -
new file src/org/openstreetmap/gui/jmapviewer/tilesources/TileSourceInfo.java
diff --git src/org/openstreetmap/gui/jmapviewer/tilesources/TileSourceInfo.java src/org/openstreetmap/gui/jmapviewer/tilesources/TileSourceInfo.java new file mode 100644 index 0000000..379c8f7
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.gui.jmapviewer.tilesources; 3 4 import java.util.Map; 5 6 public class TileSourceInfo { 7 /** id for this imagery entry, optional at the moment */ 8 protected String id; 9 /** URL of the imagery service */ 10 protected String url = null; 11 12 /** name of the imagery layer */ 13 protected String name; 14 15 /** headers meaning, that there is no tile at this zoom level */ 16 protected Map<String, String> notileHeaders; 17 18 /** minimum zoom level supported by the tile source */ 19 protected int minZoom; 20 21 /** maximum zoom level supported by the tile source */ 22 protected int maxZoom; 23 24 /** cookies that needs to be sent to tile source */ 25 protected String cookies; 26 27 28 /** 29 * Create a TileSourceInfo class 30 * 31 * @param name 32 * @param base_url 33 * @param id 34 */ 35 public TileSourceInfo(String name, String base_url, String id) { 36 this(name); 37 this.url = base_url; 38 this.id = id; 39 } 40 41 /** 42 * Create a TileSourceInfo class 43 * 44 * @param name 45 */ 46 public TileSourceInfo(String name) { 47 this.name = name; 48 } 49 50 /** 51 * Creates empty TileSourceInfo class 52 */ 53 public TileSourceInfo() { 54 } 55 56 /** 57 * 58 * @return name of the tile source 59 */ 60 public String getName() { 61 return name; 62 } 63 64 /** 65 * 66 * @return url of the tile source 67 */ 68 public String getUrl() { 69 return url; 70 } 71 72 /** 73 * 74 * @return map of headers, that when set, means that this is "no tile at this zoom level" situation 75 */ 76 public Map<String, String> getNoTileHeaders() { 77 return notileHeaders; 78 } 79 80 /** 81 * 82 * @return minimum zoom level supported by tile source 83 */ 84 public int getMinZoom() { 85 return minZoom; 86 } 87 88 /** 89 * 90 * @return maximum zoom level supported by tile source 91 */ 92 public int getMaxZoom() { 93 return maxZoom; 94 } 95 96 /** 97 * 98 * @return cookies to be sent along with request to tile source 99 */ 100 public String getCookies() { 101 return cookies; 102 } 103 104 } -
src/org/openstreetmap/josm/data/Preferences.java
diff --git src/org/openstreetmap/josm/data/Preferences.java src/org/openstreetmap/josm/data/Preferences.java index a686728..fb766c7 100644
import java.io.InputStream; 13 13 import java.io.OutputStreamWriter; 14 14 import java.io.PrintWriter; 15 15 import java.io.Reader; 16 import java.io.StringReader; 17 import java.io.StringWriter; 16 18 import java.lang.annotation.Retention; 17 19 import java.lang.annotation.RetentionPolicy; 18 20 import java.lang.reflect.Field; … … import java.nio.file.Files; 21 23 import java.util.ArrayList; 22 24 import java.util.Collection; 23 25 import java.util.Collections; 26 import java.util.HashMap; 24 27 import java.util.HashSet; 25 28 import java.util.Iterator; 26 29 import java.util.LinkedHashMap; … … import java.util.concurrent.CopyOnWriteArrayList; 37 40 import java.util.regex.Matcher; 38 41 import java.util.regex.Pattern; 39 42 43 import javax.json.Json; 44 import javax.json.JsonObject; 45 import javax.json.JsonObjectBuilder; 46 import javax.json.JsonReader; 47 import javax.json.JsonValue; 48 import javax.json.JsonWriter; 40 49 import javax.swing.JOptionPane; 41 50 import javax.swing.UIManager; 42 51 import javax.xml.XMLConstants; … … public class Preferences { 1265 1274 return vals; 1266 1275 } 1267 1276 1277 @SuppressWarnings("rawtypes") 1278 private static String mapToJson(Map map) { 1279 StringWriter stringWriter = new StringWriter(); 1280 try (JsonWriter writer = Json.createWriter(stringWriter)) { 1281 JsonObjectBuilder object = Json.createObjectBuilder(); 1282 for(Object o: map.entrySet()) { 1283 Entry e = (Entry) o; 1284 object.add(e.getKey().toString(), e.getValue().toString()); 1285 } 1286 writer.writeObject(object.build()); 1287 } 1288 return stringWriter.toString(); 1289 } 1290 1291 @SuppressWarnings({ "rawtypes", "unchecked" }) 1292 private static Map mapFromJson(String s) { 1293 Map ret = null; 1294 try (JsonReader reader = Json.createReader(new StringReader(s))) { 1295 JsonObject object = reader.readObject(); 1296 ret = new HashMap(object.size()); 1297 for (Entry<String, JsonValue> e: object.entrySet()) { 1298 ret.put(e.getKey(), e.getValue().toString()); 1299 } 1300 } 1301 return ret; 1302 } 1303 1268 1304 public static <T> Map<String,String> serializeStruct(T struct, Class<T> klass) { 1269 1305 T structPrototype; 1270 1306 try { … … public class Preferences { 1284 1320 Object defaultFieldValue = f.get(structPrototype); 1285 1321 if (fieldValue != null) { 1286 1322 if (f.getAnnotation(writeExplicitly.class) != null || !Objects.equals(fieldValue, defaultFieldValue)) { 1287 hash.put(f.getName().replace("_", "-"), fieldValue.toString()); 1323 String key = f.getName().replace("_", "-"); 1324 if (fieldValue instanceof Map) { 1325 hash.put(key, mapToJson((Map) fieldValue)); 1326 } else { 1327 hash.put(key, fieldValue.toString()); 1328 } 1288 1329 } 1289 1330 } 1290 1331 } catch (IllegalArgumentException | IllegalAccessException ex) { … … public class Preferences { 1331 1372 } 1332 1373 } else if (f.getType() == String.class) { 1333 1374 value = key_value.getValue(); 1334 } else 1375 } else if (f.getType().isAssignableFrom(Map.class)) { 1376 value = mapFromJson(key_value.getValue()); 1377 } 1378 else 1335 1379 throw new RuntimeException("unsupported preference primitive type"); 1336 1380 1337 1381 try { -
src/org/openstreetmap/josm/data/cache/BufferedImageCacheEntry.java
diff --git src/org/openstreetmap/josm/data/cache/BufferedImageCacheEntry.java src/org/openstreetmap/josm/data/cache/BufferedImageCacheEntry.java index 63a765f..ea9b51b 100644
public class BufferedImageCacheEntry extends CacheEntry { 42 42 if (img != null) 43 43 return img; 44 44 byte[] content = getContent(); 45 if (content != null ) {45 if (content != null && content.length > 0) { 46 46 img = ImageIO.read(new ByteArrayInputStream(content)); 47 47 48 48 if (writtenToDisk) -
src/org/openstreetmap/josm/data/cache/ICachedLoaderListener.java
diff --git src/org/openstreetmap/josm/data/cache/ICachedLoaderListener.java src/org/openstreetmap/josm/data/cache/ICachedLoaderListener.java index fe26c09..01c5199 100644
public interface ICachedLoaderListener { 19 19 * LoadResult.REJECTED when job was rejected because of full queue 20 20 * 21 21 * @param data 22 * @param attributes 22 23 * @param result 23 24 */ 24 public void loadingFinished(CacheEntry data, LoadResult result);25 public void loadingFinished(CacheEntry data, CacheEntryAttributes attributes, LoadResult result); 25 26 26 27 } -
src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
diff --git src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java index b4a0fbd..a31fddb 100644
import java.net.HttpURLConnection; 9 9 import java.net.URL; 10 10 import java.net.URLConnection; 11 11 import java.util.HashSet; 12 import java.util.List; 12 13 import java.util.Map; 13 14 import java.util.Random; 14 15 import java.util.Set; … … public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements 223 224 * 224 225 * @return cache object as empty, regardless of what remote resource has returned (ex. based on headers) 225 226 */ 226 protected boolean cacheAsEmpty( ) {227 protected boolean cacheAsEmpty(Map<String, List<String>> headers, int statusCode, byte[] content) { 227 228 return false; 228 229 } 229 230 … … public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements 279 280 } 280 281 try { 281 282 for (ICachedLoaderListener l: listeners) { 282 l.loadingFinished(cacheData, result);283 l.loadingFinished(cacheData, attributes, result); 283 284 } 284 285 } catch (Exception e) { 285 286 log.log(Level.WARNING, "JCS - Error while loading object from cache: {0}; {1}", new Object[]{e.getMessage(), getUrl()}); 286 287 Main.warn(e); 287 288 for (ICachedLoaderListener l: listeners) { 288 l.loadingFinished(cacheData, LoadResult.FAILURE);289 l.loadingFinished(cacheData, attributes, LoadResult.FAILURE); 289 290 } 290 291 291 292 } … … public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements 328 329 log.log(Level.FINE, "JCS - cache entry verified using HEAD request: {0}", getUrl()); 329 330 return true; 330 331 } 331 URLConnection urlConn = getURLConnection();332 HttpURLConnection urlConn = getURLConnection(); 332 333 333 334 if (isObjectLoadable() && 334 335 (now - attributes.getLastModification()) <= ABSOLUTE_EXPIRE_TIME_LIMIT) { … … public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements 337 338 if (isObjectLoadable() && attributes.getEtag() != null) { 338 339 urlConn.addRequestProperty("If-None-Match", attributes.getEtag()); 339 340 } 340 if (urlConn instanceof HttpURLConnection && ((HttpURLConnection)urlConn).getResponseCode() == 304) {341 if (urlConn.getResponseCode() == 304) { 341 342 // If isModifiedSince or If-None-Match has been set 342 343 // and the server answers with a HTTP 304 = "Not Modified" 343 344 log.log(Level.FINE, "JCS - IfModifiedSince/Etag test: local version is up to date: {0}", getUrl()); … … public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements 358 359 attributes = parseHeaders(urlConn); 359 360 360 361 for (int i = 0; i < 5; ++i) { 361 if (urlConn instanceof HttpURLConnection && ((HttpURLConnection)urlConn).getResponseCode() == 503) {362 if (urlConn.getResponseCode() == 503) { 362 363 Thread.sleep(5000+(new Random()).nextInt(5000)); 363 364 continue; 364 365 } 365 366 byte[] raw = read(urlConn); 366 367 367 if (!cacheAsEmpty() && raw != null && raw.length > 0) { 368 if (!cacheAsEmpty(urlConn.getHeaderFields(), urlConn.getResponseCode(), raw) && 369 raw != null && raw.length > 0) { 368 370 cacheData = createCacheEntry(raw); 369 371 cache.put(getCacheKey(), cacheData, attributes); 370 372 log.log(Level.FINE, "JCS - downloaded key: {0}, length: {1}, url: {2}", … … public abstract class JCSCachedTileLoaderJob<K, V extends CacheEntry> implements 399 401 400 402 private CacheEntryAttributes parseHeaders(URLConnection urlConn) { 401 403 CacheEntryAttributes ret = new CacheEntryAttributes(); 402 ret.setNoTileAtZoom("no-tile".equals(urlConn.getHeaderField("X-VE-Tile-Info")));403 404 404 405 Long lng = urlConn.getExpiration(); 405 406 if (lng.equals(0L)) { -
src/org/openstreetmap/josm/data/imagery/ImageryInfo.java
diff --git src/org/openstreetmap/josm/data/imagery/ImageryInfo.java src/org/openstreetmap/josm/data/imagery/ImageryInfo.java index e3c813f..608ebed 100644
import java.util.Arrays; 9 9 import java.util.Collection; 10 10 import java.util.Collections; 11 11 import java.util.List; 12 import java.util.Map; 12 13 import java.util.Objects; 13 14 import java.util.TreeSet; 14 15 import java.util.regex.Matcher; … … import org.openstreetmap.gui.jmapviewer.Coordinate; 20 21 import org.openstreetmap.gui.jmapviewer.interfaces.Attributed; 21 22 import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource; 22 23 import org.openstreetmap.gui.jmapviewer.tilesources.OsmTileSource.Mapnik; 24 import org.openstreetmap.gui.jmapviewer.tilesources.TileSourceInfo; 23 25 import org.openstreetmap.josm.Main; 24 26 import org.openstreetmap.josm.data.Bounds; 25 27 import org.openstreetmap.josm.data.Preferences.pref; … … import org.openstreetmap.josm.tools.LanguageInfo; 34 36 * 35 37 * @author Frederik Ramm 36 38 */ 37 public class ImageryInfo implements Comparable<ImageryInfo>, Attributed {39 public class ImageryInfo extends TileSourceInfo implements Comparable<ImageryInfo>, Attributed { 38 40 39 41 /** 40 42 * Type of imagery entry. … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 53 55 /** A WMS endpoint entry only stores the WMS server info, without layer, which are chosen later by the user. **/ 54 56 WMS_ENDPOINT("wms_endpoint"); 55 57 58 56 59 private final String typeString; 57 60 58 61 private ImageryType(String urlString) { … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 150 153 } 151 154 } 152 155 153 /** name of the imagery entry (gets translated by josm usually) */ 154 private String name; 156 155 157 /** original name of the imagery entry in case of translation call, for multiple languages English when possible */ 156 158 private String origName; 157 159 /** (original) language of the translated name entry */ 158 160 private String langName; 159 /** id for this imagery entry, optional at the moment */160 private String id;161 /** URL of the imagery service */162 private String url = null;163 161 /** whether this is a entry activated by default or not */ 164 162 private boolean defaultEntry = false; 165 163 /** The data part of HTTP cookies header in case the service requires cookies to work */ … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 198 196 /** icon used in menu */ 199 197 private String icon; 200 198 // when adding a field, also adapt the ImageryInfo(ImageryInfo) constructor 199 private Map<String, String> noTileHeaders; 201 200 202 201 /** 203 202 * Auxiliary class to save an {@link ImageryInfo} object in the preferences. … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 224 223 @pref String projections; 225 224 @pref String icon; 226 225 @pref String description; 226 @pref Map<String, String> noTileHeaders; 227 227 228 228 /** 229 229 * Constructs a new empty WMS {@code ImageryPreferenceEntry}. … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 277 277 } 278 278 projections = val.toString(); 279 279 } 280 if (i.noTileHeaders != null && !i.noTileHeaders.isEmpty()) { 281 noTileHeaders = i.noTileHeaders; 282 } 280 283 } 281 284 282 285 @Override … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 294 297 * Constructs a new WMS {@code ImageryInfo}. 295 298 */ 296 299 public ImageryInfo() { 300 super(); 297 301 } 298 302 299 303 /** … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 301 305 * @param name The entry name 302 306 */ 303 307 public ImageryInfo(String name) { 304 this.name=name;308 super(name); 305 309 } 306 310 307 311 /** … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 310 314 * @param url The entry extended URL 311 315 */ 312 316 public ImageryInfo(String name, String url) { 313 this .name=name;317 this(name); 314 318 setExtendedUrl(url); 315 319 } 316 320 … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 321 325 * @param eulaAcceptanceRequired The EULA URL 322 326 */ 323 327 public ImageryInfo(String name, String url, String eulaAcceptanceRequired) { 324 this .name=name;328 this(name); 325 329 setExtendedUrl(url); 326 330 this.eulaAcceptanceRequired = eulaAcceptanceRequired; 327 331 } … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 336 340 * @throws IllegalArgumentException if type refers to an unknown imagery type 337 341 */ 338 342 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies) { 339 this .name=name;343 this(name); 340 344 setExtendedUrl(url); 341 345 ImageryType t = ImageryType.fromString(type); 342 346 this.cookies=cookies; … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 348 352 } 349 353 } 350 354 355 public ImageryInfo(String name, String url, String type, String eulaAcceptanceRequired, String cookies, String id) { 356 this(name, url, type, eulaAcceptanceRequired, cookies); 357 setId(id); 358 } 359 351 360 /** 352 361 * Constructs a new {@code ImageryInfo} from an imagery preference entry. 353 362 * @param e The imagery preference entry … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 389 398 termsOfUseURL = e.terms_of_use_url; 390 399 countryCode = e.country_code; 391 400 icon = e.icon; 401 if (e.noTileHeaders != null) { 402 noTileHeaders = e.noTileHeaders; 403 } 392 404 } 393 405 394 406 /** … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 687 699 * Returns the entry name. 688 700 * @return The entry name 689 701 */ 702 @Override 690 703 public String getName() { 691 704 return this.name; 692 705 } … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 758 771 * Returns the entry URL. 759 772 * @return The entry URL 760 773 */ 774 @Override 761 775 public String getUrl() { 762 776 return this.url; 763 777 } … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 790 804 * Return the data part of HTTP cookies header in case the service requires cookies to work 791 805 * @return the cookie data part 792 806 */ 807 @Override 793 808 public String getCookies() { 794 809 return this.cookies; 795 810 } … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 802 817 * Returns the maximum zoom level. 803 818 * @return The maximum zoom level 804 819 */ 820 @Override 805 821 public int getMaxZoom() { 806 822 return this.defaultMaxZoom; 807 823 } … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 810 826 * Returns the minimum zoom level. 811 827 * @return The minimum zoom level 812 828 */ 829 @Override 813 830 public int getMinZoom() { 814 831 return this.defaultMinZoom; 815 832 } … … public class ImageryInfo implements Comparable<ImageryInfo>, Attributed { 1025 1042 Capabilities capabilities = OsmApi.getOsmApi().getCapabilities(); 1026 1043 return capabilities != null && capabilities.isOnImageryBlacklist(this.url); 1027 1044 } 1045 1046 public void setNoTileHeaders(Map<String, String> noTileHeaders) { 1047 this.noTileHeaders = noTileHeaders; 1048 } 1049 1050 @Override 1051 public Map<String, String> getNoTileHeaders() { 1052 return noTileHeaders; 1053 } 1028 1054 } -
src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java
diff --git src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java src/org/openstreetmap/josm/data/imagery/ImageryLayerInfo.java index 96f8b50..f641d17 100644
public class ImageryLayerInfo { 148 148 Collection<String> knownDefaults = Main.pref.getCollection("imagery.layers.default"); 149 149 Collection<String> newKnownDefaults = new TreeSet<>(knownDefaults); 150 150 for (ImageryInfo def : defaultLayers) { 151 // temporary migration code, so all user preferences will get updated with new settings from JOSM site 152 if(def.getNoTileHeaders() != null) { 153 for(ImageryInfo i: layers) { 154 if (isSimilar(def, i)) { 155 i.setNoTileHeaders(def.getNoTileHeaders()); 156 changed = true; 157 } 158 } 159 } 160 151 161 if (def.isDefaultEntry()) { 152 162 boolean isKnownDefault = false; 153 163 for (String url : knownDefaults) { -
src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
diff --git src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java index a627c90..9a473d2 100644
import java.io.ByteArrayInputStream; 5 5 import java.io.IOException; 6 6 import java.net.URL; 7 7 import java.util.HashSet; 8 import java.util.List; 8 9 import java.util.Map; 9 10 import java.util.Set; 10 11 import java.util.concurrent.ConcurrentHashMap; … … import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource; 26 27 import org.openstreetmap.josm.Main; 27 28 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry; 28 29 import org.openstreetmap.josm.data.cache.CacheEntry; 30 import org.openstreetmap.josm.data.cache.CacheEntryAttributes; 29 31 import org.openstreetmap.josm.data.cache.ICachedLoaderListener; 30 32 import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob; 31 33 import org.openstreetmap.josm.data.preferences.IntegerProperty; … … public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe 193 195 if (cacheData != null) { 194 196 byte[] content = cacheData.getContent(); 195 197 try { 196 return content != null || cacheData.getImage() != null || cacheAsEmpty();198 return content != null || cacheData.getImage() != null || isNoTileAtZoom(); 197 199 } catch (IOException e) { 198 200 log.log(Level.WARNING, "JCS TMS - error loading from cache for tile {0}: {1}", new Object[] {tile.getKey(), e.getMessage()}); 199 201 } … … public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe 202 204 } 203 205 204 206 private boolean isNoTileAtZoom() { 207 if (attributes == null) { 208 log.warning("Cache attributes are null"); 209 } 205 210 return attributes != null && attributes.isNoTileAtZoom(); 206 211 } 207 212 208 213 @Override 209 protected boolean cacheAsEmpty() { 210 return isNoTileAtZoom(); 214 protected boolean cacheAsEmpty(Map<String, List<String>> headers, int statusCode, byte[] content) { 215 if (tile.getTileSource().isNoTileAtZoom(headers, statusCode, content)) { 216 attributes.setNoTileAtZoom(true); 217 return true; 218 } 219 return false; 211 220 } 212 221 213 222 private boolean handleNoTileAtZoom() { … … public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe 241 250 } 242 251 243 252 @Override 244 public void loadingFinished(CacheEntry object, LoadResult result) { 253 public void loadingFinished(CacheEntry object, CacheEntryAttributes attributes, LoadResult result) { 254 this.attributes = attributes; // as we might get notification from other object than our selfs, pass attributes along 245 255 Set<TileLoaderListener> listeners; 246 256 synchronized (inProgress) { 247 257 listeners = inProgress.remove(getCacheKey()); … … public class TMSCachedTileLoaderJob extends JCSCachedTileLoaderJob<String, Buffe 315 325 316 326 @Override 317 327 protected boolean handleNotFound() { 328 if (tile.getSource().isNoTileAtZoom(null, 404, null)) { 318 329 tile.setError("No tile at this zoom level"); 319 330 tile.putValue("tile-info", "no-tile"); 320 331 return true; 321 332 } 333 return false; 334 } 322 335 323 336 /** 324 337 * For TMS use BaseURL as settings discovery, so for different paths, we will have different settings (useful for developer servers) -
src/org/openstreetmap/josm/gui/layer/TMSLayer.java
diff --git src/org/openstreetmap/josm/gui/layer/TMSLayer.java src/org/openstreetmap/josm/gui/layer/TMSLayer.java index c749488..5e2073d 100644
public class TMSLayer extends ImageryLayer implements ImageObserver, TileLoaderL 277 277 278 278 private static class CachedAttributionBingAerialTileSource extends BingAerialTileSource { 279 279 280 public CachedAttributionBingAerialTileSource( String id) {281 super(i d);280 public CachedAttributionBingAerialTileSource(ImageryInfo info) { 281 super(info); 282 282 } 283 283 284 284 class BingAttributionData extends CacheCustomContent<IOException> { … … public class TMSLayer extends ImageryLayer implements ImageObserver, TileLoaderL 336 336 public static TileSource getTileSource(ImageryInfo info) { 337 337 if (info.getImageryType() == ImageryType.TMS) { 338 338 checkUrl(info.getUrl()); 339 TMSTileSource t = new TemplatedTMSTileSource(info.getName(), info.getUrl(), info.getId(), info.getMinZoom(), info.getMaxZoom(), 340 info.getCookies()); 339 TMSTileSource t = new TemplatedTMSTileSource(info); 341 340 info.setAttribution(t); 342 341 return t; 343 } else if (info.getImageryType() == ImageryType.BING) 344 return new CachedAttributionBingAerialTileSource(info.getId()); 345 else if (info.getImageryType() == ImageryType.SCANEX) { 346 return new ScanexTileSource(info.getName(), info.getUrl(), info.getId(), info.getMaxZoom()); 342 } else if (info.getImageryType() == ImageryType.BING) { 343 //return new CachedAttributionBingAerialTileSource(info.getId()); 344 return new CachedAttributionBingAerialTileSource(info); 345 } else if (info.getImageryType() == ImageryType.SCANEX) { 346 //return new ScanexTileSource(info.getName(), info.getUrl(), info.getId(), info.getMaxZoom()); 347 return new ScanexTileSource(info); 347 348 } 348 349 return null; 349 350 } -
src/org/openstreetmap/josm/io/imagery/ImageryReader.java
diff --git src/org/openstreetmap/josm/io/imagery/ImageryReader.java src/org/openstreetmap/josm/io/imagery/ImageryReader.java index 6978c5f..15ed396 100644
import java.io.IOException; 5 5 import java.io.InputStream; 6 6 import java.util.ArrayList; 7 7 import java.util.Arrays; 8 import java.util.HashMap; 8 9 import java.util.List; 10 import java.util.Map; 9 11 import java.util.Objects; 10 12 import java.util.Stack; 11 13 … … public class ImageryReader { 38 40 CODE, 39 41 BOUNDS, 40 42 SHAPE, 43 NO_TILE, 41 44 UNKNOWN, // element is not recognized in the current context 42 45 } 43 46 … … public class ImageryReader { 83 86 // language of last element, does only work for simple ENTRY_ATTRIBUTE's 84 87 private String lang; 85 88 private List<String> projections; 89 private Map<String, String> noTileHeaders; 86 90 87 91 @Override 88 92 public void startDocument() { … … public class ImageryReader { 94 98 entry = null; 95 99 bounds = null; 96 100 projections = null; 101 noTileHeaders = null; 97 102 } 98 103 99 104 @Override … … public class ImageryReader { 149 154 } else if ("projections".equals(qName)) { 150 155 projections = new ArrayList<>(); 151 156 newState = State.PROJECTIONS; 157 } else if ("no-tile-header".equals(qName)) { 158 noTileHeaders = new HashMap<>(); 159 noTileHeaders.put(atts.getValue("name"), atts.getValue("value")); 160 newState = State.NO_TILE; 152 161 } 153 162 break; 154 163 case BOUNDS: … … public class ImageryReader { 307 316 entry.setServerProjections(projections); 308 317 projections = null; 309 318 break; 319 case NO_TILE: 320 entry.setNoTileHeaders(noTileHeaders); 321 noTileHeaders = null; 322 break; 323 310 324 } 311 325 } 312 326 }