001// License: BSD or GPL. For details, see Readme.txt file. 002package org.openstreetmap.gui.jmapviewer.tilesources; 003 004import java.util.Random; 005 006import org.openstreetmap.gui.jmapviewer.OsmMercator; 007 008/* 009 * This tilesource uses different to OsmMercator projection. 010 * 011 * Earth is assumed an ellipsoid in this projection, unlike 012 * sphere in OsmMercator, so latitude calculation differs 013 * a lot. 014 * 015 * The longitude calculation is the same as in OsmMercator, 016 * we inherit it from AbstractTMSTileSource. 017 * 018 * TODO: correct getDistance() method. 019 */ 020 021public class ScanexTileSource extends TMSTileSource { 022 private static final String DEFAULT_URL = "http://maps.kosmosnimki.ru"; 023 private static final int DEFAULT_MAXZOOM = 14; 024 private static String API_KEY = "4018C5A9AECAD8868ED5DEB2E41D09F7"; 025 026 private enum ScanexLayer { 027 IRS("irs", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=BAC78D764F0443BD9AF93E7A998C9F5B"), 028 SPOT("spot", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=F51CE95441284AF6B2FC319B609C7DEC"); 029 030 private String name; 031 private String uri; 032 033 ScanexLayer(String name, String uri) { 034 this.name = name; 035 this.uri = uri; 036 } 037 public String getName() { 038 return name; 039 } 040 public String getUri() { 041 return uri; 042 } 043 } 044 045 /* IRS by default */ 046 private ScanexLayer Layer = ScanexLayer.IRS; 047 048 public ScanexTileSource(String name, String url, int maxZoom) { 049 super(name, url, maxZoom); 050 051 for (ScanexLayer layer : ScanexLayer.values()) { 052 if (url.equalsIgnoreCase(layer.getName())) { 053 this.Layer = layer; 054 /* 055 * Override baseUrl and maxZoom in base class. 056 */ 057 this.baseUrl = DEFAULT_URL; 058 if (maxZoom == 0) 059 this.maxZoom = DEFAULT_MAXZOOM; 060 break; 061 } 062 } 063 } 064 065 @Override 066 public String getExtension() { 067 return("jpeg"); 068 } 069 070 @Override 071 public String getTilePath(int zoom, int tilex, int tiley) { 072 int tmp = (int)Math.pow(2.0, zoom - 1); 073 074 tilex = tilex - tmp; 075 tiley = tmp - tiley - 1; 076 077 return this.Layer.getUri() + "&apikey=" + API_KEY + "&x=" + tilex + "&y=" + tiley + "&z=" + zoom; 078 } 079 080 public TileUpdate getTileUpdate() { 081 return TileUpdate.IfNoneMatch; 082 } 083 084 085 /* 086 * Latitude to Y and back calculations. 087 */ 088 private static double RADIUS_E = 6378137; /* radius of Earth at equator, m */ 089 private static double EQUATOR = 40075016.68557849; /* equator length, m */ 090 private static double E = 0.0818191908426; /* eccentricity of Earth's ellipsoid */ 091 092 @Override 093 public int LatToY(double lat, int zoom) { 094 return (int )(latToTileY(lat, zoom) * OsmMercator.TILE_SIZE); 095 } 096 097 @Override 098 public double YToLat(int y, int zoom) { 099 return tileYToLat((double )y / OsmMercator.TILE_SIZE, zoom); 100 } 101 102 @Override 103 public double latToTileY(double lat, int zoom) { 104 double tmp = Math.tan(Math.PI/4 * (1 + lat/90)); 105 double pow = Math.pow(Math.tan(Math.PI/4 + Math.asin(E * Math.sin(Math.toRadians(lat)))/2), E); 106 107 return (EQUATOR/2 - (RADIUS_E * Math.log(tmp/pow))) * Math.pow(2.0, zoom) / EQUATOR; 108 } 109 110 @Override 111 public double tileYToLat(int y, int zoom) { 112 return tileYToLat((double )y, zoom); 113 } 114 115 /* 116 * To solve inverse formula latitude = f(y) we use 117 * Newton's method. We cache previous calculated latitude, 118 * because new one is usually close to the old one. In case 119 * if solution gets out of bounds, we reset to a new random 120 * value. 121 */ 122 private double cached_lat = 0; 123 private double tileYToLat(double y, int zoom) { 124 double lat0, lat; 125 126 lat = cached_lat; 127 do { 128 lat0 = lat; 129 lat = lat - Math.toDegrees(NextTerm(Math.toRadians(lat), y, zoom)); 130 if (lat > OsmMercator.MAX_LAT || lat < OsmMercator.MIN_LAT) { 131 Random r = new Random(); 132 lat = OsmMercator.MIN_LAT + 133 r.nextInt((int )(OsmMercator.MAX_LAT - OsmMercator.MIN_LAT)); 134 } 135 } while ((Math.abs(lat0 - lat) > 0.000001)); 136 137 cached_lat = lat; 138 139 return (lat); 140 } 141 142 /* Next term in Newton's polynomial */ 143 private double NextTerm(double lat, double y, int zoom) { 144 double sinl=Math.sin(lat); 145 double cosl=Math.cos(lat); 146 double ec, f, df; 147 148 zoom = (int )Math.pow(2.0, zoom - 1); 149 ec = Math.exp((1 - y/zoom)*Math.PI); 150 151 f = (Math.tan(Math.PI/4+lat/2) - 152 ec * Math.pow(Math.tan(Math.PI/4 + Math.asin(E * sinl)/2), E)); 153 df = 1/(1 - sinl) - ec * E * cosl/((1 - E * sinl) * 154 (Math.sqrt (1 - E * E * sinl * sinl))); 155 156 return (f/df); 157 } 158}