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