001// License: 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 a lot. 013 * 014 * The longitude calculation is the same as in OsmMercator, 015 * we inherit it from AbstractTMSTileSource. 016 * 017 * TODO: correct getDistance() method. 018 */ 019public class ScanexTileSource extends TMSTileSource { 020 private static final String DEFAULT_URL = "http://maps.kosmosnimki.ru"; 021 private static final int DEFAULT_MAXZOOM = 14; 022 private static final String API_KEY = "4018C5A9AECAD8868ED5DEB2E41D09F7"; 023 024 // Latitude to Y and back calculations. 025 026 /** radius of Earth at equator, m */ 027 private static double RADIUS_E = 6378137; 028 /** equator length, m */ 029 private static double EQUATOR = 40075016.68557849; 030 /** eccentricity of Earth's ellipsoid */ 031 private static double E = 0.0818191908426; 032 033 private enum ScanexLayer { 034 IRS("irs", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=BAC78D764F0443BD9AF93E7A998C9F5B"), 035 SPOT("spot", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=F51CE95441284AF6B2FC319B609C7DEC"); 036 037 private String name; 038 private String uri; 039 040 ScanexLayer(String name, String uri) { 041 this.name = name; 042 this.uri = uri; 043 } 044 045 public String getName() { 046 return name; 047 } 048 049 public String getUri() { 050 return uri; 051 } 052 } 053 054 /** IRS by default */ 055 private ScanexLayer layer = ScanexLayer.IRS; 056 057 /** cached latitude used in {@link #tileYToLat(double, int)} */ 058 private double cachedLat; 059 060 /** 061 * Constructs a new {@code ScanexTileSource}. 062 * @param info tile source info 063 */ 064 public ScanexTileSource(TileSourceInfo info) { 065 super(info); 066 String url = info.getUrl(); 067 068 for (ScanexLayer slayer : ScanexLayer.values()) { 069 if (url.equalsIgnoreCase(slayer.getName())) { 070 this.layer = slayer; 071 // Override baseUrl and maxZoom in base class. 072 this.baseUrl = DEFAULT_URL; 073 if (maxZoom == 0) 074 this.maxZoom = DEFAULT_MAXZOOM; 075 break; 076 } 077 } 078 } 079 080 @Override 081 public String getExtension() { 082 return "jpeg"; 083 } 084 085 @Override 086 public String getTilePath(int zoom, int tilex, int tiley) { 087 int tmp = (int) Math.pow(2.0, zoom - 1); 088 089 tilex = tilex - tmp; 090 tiley = tmp - tiley - 1; 091 092 return this.layer.getUri() + "&apikey=" + API_KEY + "&x=" + tilex + "&y=" + tiley + "&z=" + zoom; 093 } 094 095 /* 096 * To solve inverse formula latitude = f(y) we use 097 * Newton's method. We cache previous calculated latitude, 098 * because new one is usually close to the old one. In case 099 * if solution gets out of bounds, we reset to a new random value. 100 */ 101 private double tileYToLat(double y, int zoom) { 102 double lat0; 103 double lat = cachedLat; 104 do { 105 lat0 = lat; 106 lat = lat - Math.toDegrees(nextTerm(Math.toRadians(lat), y, zoom)); 107 if (lat > OsmMercator.MAX_LAT || lat < OsmMercator.MIN_LAT) { 108 Random r = new Random(); 109 lat = OsmMercator.MIN_LAT + 110 r.nextInt((int) (OsmMercator.MAX_LAT - OsmMercator.MIN_LAT)); 111 } 112 } while (Math.abs(lat0 - lat) > 0.000001); 113 114 cachedLat = lat; 115 116 return lat; 117 } 118 119 /* Next term in Newton's polynomial */ 120 private static double nextTerm(double lat, double y, int zoom) { 121 double sinl = Math.sin(lat); 122 double cosl = Math.cos(lat); 123 124 zoom = (int) Math.pow(2.0, zoom - 1); 125 double ec = Math.exp((1 - y/zoom)*Math.PI); 126 127 double f = Math.tan(Math.PI/4+lat/2) - 128 ec * Math.pow(Math.tan(Math.PI/4 + Math.asin(E * sinl)/2), E); 129 double df = 1/(1 - sinl) - ec * E * cosl/((1 - E * sinl) * 130 (Math.sqrt(1 - E * E * sinl * sinl))); 131 132 return f/df; 133 } 134}