001// License: GPL. For details, see Readme.txt file. 002package org.openstreetmap.gui.jmapviewer.tilesources; 003 004import java.awt.Point; 005import java.io.IOException; 006import java.security.MessageDigest; 007import java.security.NoSuchAlgorithmException; 008import java.util.HashMap; 009import java.util.List; 010import java.util.Map; 011import java.util.Map.Entry; 012import java.util.Set; 013 014import org.openstreetmap.gui.jmapviewer.JMapViewer; 015import org.openstreetmap.gui.jmapviewer.OsmMercator; 016import org.openstreetmap.gui.jmapviewer.Tile; 017import org.openstreetmap.gui.jmapviewer.TileXY; 018import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate; 019 020/** 021 * Class generalizing all tile based tile sources 022 * 023 * @author Wiktor Niesiobędzki 024 * 025 */ 026public abstract class AbstractTMSTileSource extends AbstractTileSource { 027 028 protected String name; 029 protected String baseUrl; 030 protected String id; 031 private final Map<String, Set<String>> noTileHeaders; 032 private final Map<String, Set<String>> noTileChecksums; 033 private final Map<String, String> metadataHeaders; 034 protected boolean modTileFeatures; 035 protected int tileSize; 036 037 /** 038 * Creates an instance based on TileSource information 039 * 040 * @param info description of the Tile Source 041 */ 042 public AbstractTMSTileSource(TileSourceInfo info) { 043 this.name = info.getName(); 044 this.baseUrl = info.getUrl(); 045 if (baseUrl != null && baseUrl.endsWith("/")) { 046 baseUrl = baseUrl.substring(0, baseUrl.length()-1); 047 } 048 this.id = info.getUrl(); 049 this.noTileHeaders = info.getNoTileHeaders(); 050 this.noTileChecksums = info.getNoTileChecksums(); 051 this.metadataHeaders = info.getMetadataHeaders(); 052 this.modTileFeatures = info.isModTileFeatures(); 053 this.tileSize = info.getTileSize(); 054 } 055 056 /** 057 * @return default tile size to use, when not set in Imagery Preferences 058 */ 059 @Override 060 public int getDefaultTileSize() { 061 return OsmMercator.DEFAUL_TILE_SIZE; 062 } 063 064 @Override 065 public String getName() { 066 return name; 067 } 068 069 @Override 070 public String getId() { 071 return id; 072 } 073 074 @Override 075 public int getMaxZoom() { 076 return JMapViewer.MAX_ZOOM; 077 } 078 079 @Override 080 public int getMinZoom() { 081 return JMapViewer.MIN_ZOOM; 082 } 083 084 /** 085 * @return image extension, used for URL creation 086 */ 087 public String getExtension() { 088 return "png"; 089 } 090 091 /** 092 * @param zoom level of the tile 093 * @param tilex tile number in x axis 094 * @param tiley tile number in y axis 095 * @return String containg path part of URL of the tile 096 * @throws IOException when subclass cannot return the tile URL 097 */ 098 public String getTilePath(int zoom, int tilex, int tiley) throws IOException { 099 return "/" + zoom + "/" + tilex + "/" + tiley + "." + getExtension(); 100 } 101 102 /** 103 * @return Base part of the URL of the tile source 104 */ 105 public String getBaseUrl() { 106 return this.baseUrl; 107 } 108 109 @Override 110 public String getTileUrl(int zoom, int tilex, int tiley) throws IOException { 111 return this.getBaseUrl() + getTilePath(zoom, tilex, tiley); 112 } 113 114 @Override 115 public String toString() { 116 return getName(); 117 } 118 119 /* 120 * Most tilesources use OsmMercator projection. 121 */ 122 @Override 123 public int getTileSize() { 124 if (tileSize <= 0) { 125 return getDefaultTileSize(); 126 } 127 return tileSize; 128 } 129 130 @Override 131 public Point latLonToXY(ICoordinate point, int zoom) { 132 return latLonToXY(point.getLat(), point.getLon(), zoom); 133 } 134 135 @Override 136 public ICoordinate xyToLatLon(Point point, int zoom) { 137 return xyToLatLon(point.x, point.y, zoom); 138 } 139 140 @Override 141 public TileXY latLonToTileXY(ICoordinate point, int zoom) { 142 return latLonToTileXY(point.getLat(), point.getLon(), zoom); 143 } 144 145 @Override 146 public ICoordinate tileXYToLatLon(TileXY xy, int zoom) { 147 return tileXYToLatLon(xy.getXIndex(), xy.getYIndex(), zoom); 148 } 149 150 @Override 151 public ICoordinate tileXYToLatLon(Tile tile) { 152 return tileXYToLatLon(tile.getXtile(), tile.getYtile(), tile.getZoom()); 153 } 154 155 @Override 156 public int getTileXMax(int zoom) { 157 return getTileMax(zoom); 158 } 159 160 @Override 161 public int getTileXMin(int zoom) { 162 return 0; 163 } 164 165 @Override 166 public int getTileYMax(int zoom) { 167 return getTileMax(zoom); 168 } 169 170 @Override 171 public int getTileYMin(int zoom) { 172 return 0; 173 } 174 175 @Override 176 public boolean isNoTileAtZoom(Map<String, List<String>> headers, int statusCode, byte[] content) { 177 if (noTileHeaders != null && headers != null) { 178 for (Entry<String, Set<String>> searchEntry: noTileHeaders.entrySet()) { 179 List<String> headerVals = headers.get(searchEntry.getKey()); 180 if (headerVals != null) { 181 for (String headerValue: headerVals) { 182 for (String val: searchEntry.getValue()) { 183 if (headerValue.matches(val)) { 184 return true; 185 } 186 } 187 } 188 } 189 } 190 } 191 if (noTileChecksums != null && content != null) { 192 for (Entry<String, Set<String>> searchEntry: noTileChecksums.entrySet()) { 193 MessageDigest md = null; 194 try { 195 md = MessageDigest.getInstance(searchEntry.getKey()); 196 } catch (NoSuchAlgorithmException e) { 197 break; 198 } 199 byte[] byteDigest = md.digest(content); 200 final int len = byteDigest.length; 201 202 char[] hexChars = new char[len * 2]; 203 for (int i = 0, j = 0; i < len; i++) { 204 final int v = byteDigest[i]; 205 int vn = (v & 0xf0) >> 4; 206 hexChars[j++] = (char) (vn + (vn >= 10 ? 'a'-10 : '0')); 207 vn = (v & 0xf); 208 hexChars[j++] = (char) (vn + (vn >= 10 ? 'a'-10 : '0')); 209 } 210 for (String val: searchEntry.getValue()) { 211 if (new String(hexChars).equalsIgnoreCase(val)) { 212 return true; 213 } 214 } 215 } 216 } 217 return super.isNoTileAtZoom(headers, statusCode, content); 218 } 219 220 @Override 221 public Map<String, String> getMetadata(Map<String, List<String>> headers) { 222 Map<String, String> ret = new HashMap<>(); 223 if (metadataHeaders != null && headers != null) { 224 for (Entry<String, String> searchEntry: metadataHeaders.entrySet()) { 225 List<String> headerVals = headers.get(searchEntry.getKey()); 226 if (headerVals != null) { 227 for (String headerValue: headerVals) { 228 ret.put(searchEntry.getValue(), headerValue); 229 } 230 } 231 } 232 } 233 return ret; 234 } 235 236 @Override 237 public String getTileId(int zoom, int tilex, int tiley) { 238 return this.baseUrl + "/" + zoom + "/" + tilex + "/" + tiley; 239 } 240 241 @Override 242 public boolean isModTileFeatures() { 243 return modTileFeatures; 244 } 245 246 private static int getTileMax(int zoom) { 247 return (int) Math.pow(2.0, zoom) - 1; 248 } 249}