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