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