1 | // License: BSD or GPL. For details, see Readme.txt file.
|
---|
2 | package org.openstreetmap.gui.jmapviewer.tilesources;
|
---|
3 |
|
---|
4 | import java.util.Random;
|
---|
5 |
|
---|
6 | import org.openstreetmap.gui.jmapviewer.OsmMercator;
|
---|
7 |
|
---|
8 | /*
|
---|
9 | * This tilesource uses different to OsmMercator projection.
|
---|
10 | *
|
---|
11 | * Earth is assumed an ellipsoid in this projection, unlike
|
---|
12 | * sphere in OsmMercator, so latitude calculation differs
|
---|
13 | * a lot.
|
---|
14 | *
|
---|
15 | * The longitude calculation is the same as in OsmMercator,
|
---|
16 | * we inherit it from AbstractTMSTileSource.
|
---|
17 | *
|
---|
18 | * TODO: correct getDistance() method.
|
---|
19 | */
|
---|
20 |
|
---|
21 | public class ScanexTileSource extends TMSTileSource {
|
---|
22 | private static final String DEFAULT_URL = "http://maps.kosmosnimki.ru";
|
---|
23 | private static final int DEFAULT_MAXZOOM = 14;
|
---|
24 | private static String API_KEY = "4018C5A9AECAD8868ED5DEB2E41D09F7";
|
---|
25 |
|
---|
26 | private enum ScanexLayer {
|
---|
27 | IRS("irs", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=BAC78D764F0443BD9AF93E7A998C9F5B"),
|
---|
28 | SPOT("spot", "/TileSender.ashx?ModeKey=tile&MapName=F7B8CF651682420FA1749D894C8AD0F6&LayerName=F51CE95441284AF6B2FC319B609C7DEC");
|
---|
29 |
|
---|
30 | private String name;
|
---|
31 | private String uri;
|
---|
32 |
|
---|
33 | ScanexLayer(String name, String uri) {
|
---|
34 | this.name = name;
|
---|
35 | this.uri = uri;
|
---|
36 | }
|
---|
37 | public String getName() {
|
---|
38 | return name;
|
---|
39 | }
|
---|
40 | public String getUri() {
|
---|
41 | return uri;
|
---|
42 | }
|
---|
43 | }
|
---|
44 |
|
---|
45 | /* IRS by default */
|
---|
46 | private ScanexLayer Layer = ScanexLayer.IRS;
|
---|
47 |
|
---|
48 | public ScanexTileSource(TileSourceInfo info) {
|
---|
49 | super(info);
|
---|
50 | String url = info.getUrl();
|
---|
51 |
|
---|
52 | for (ScanexLayer layer : ScanexLayer.values()) {
|
---|
53 | if (url.equalsIgnoreCase(layer.getName())) {
|
---|
54 | this.Layer = layer;
|
---|
55 | /*
|
---|
56 | * Override baseUrl and maxZoom in base class.
|
---|
57 | */
|
---|
58 | this.baseUrl = DEFAULT_URL;
|
---|
59 | if (maxZoom == 0)
|
---|
60 | this.maxZoom = DEFAULT_MAXZOOM;
|
---|
61 | break;
|
---|
62 | }
|
---|
63 | }
|
---|
64 | }
|
---|
65 |
|
---|
66 | @Override
|
---|
67 | public String getExtension() {
|
---|
68 | return("jpeg");
|
---|
69 | }
|
---|
70 |
|
---|
71 | @Override
|
---|
72 | public String getTilePath(int zoom, int tilex, int tiley) {
|
---|
73 | int tmp = (int)Math.pow(2.0, zoom - 1);
|
---|
74 |
|
---|
75 | tilex = tilex - tmp;
|
---|
76 | tiley = tmp - tiley - 1;
|
---|
77 |
|
---|
78 | return this.Layer.getUri() + "&apikey=" + API_KEY + "&x=" + tilex + "&y=" + tiley + "&z=" + zoom;
|
---|
79 | }
|
---|
80 |
|
---|
81 | @Override
|
---|
82 | public TileUpdate getTileUpdate() {
|
---|
83 | return TileUpdate.IfNoneMatch;
|
---|
84 | }
|
---|
85 |
|
---|
86 |
|
---|
87 | /*
|
---|
88 | * Latitude to Y and back calculations.
|
---|
89 | */
|
---|
90 | private static double RADIUS_E = 6378137; /* radius of Earth at equator, m */
|
---|
91 | private static double EQUATOR = 40075016.68557849; /* equator length, m */
|
---|
92 | private static double E = 0.0818191908426; /* eccentricity of Earth's ellipsoid */
|
---|
93 |
|
---|
94 | @Override
|
---|
95 | public int LatToY(double lat, int zoom) {
|
---|
96 | return (int )(latToTileY(lat, zoom) * OsmMercator.TILE_SIZE);
|
---|
97 | }
|
---|
98 |
|
---|
99 | @Override
|
---|
100 | public double YToLat(int y, int zoom) {
|
---|
101 | return tileYToLat((double )y / OsmMercator.TILE_SIZE, zoom);
|
---|
102 | }
|
---|
103 |
|
---|
104 | @Override
|
---|
105 | public double latToTileY(double lat, int zoom) {
|
---|
106 | double tmp = Math.tan(Math.PI/4 * (1 + lat/90));
|
---|
107 | double pow = Math.pow(Math.tan(Math.PI/4 + Math.asin(E * Math.sin(Math.toRadians(lat)))/2), E);
|
---|
108 |
|
---|
109 | return (EQUATOR/2 - (RADIUS_E * Math.log(tmp/pow))) * Math.pow(2.0, zoom) / EQUATOR;
|
---|
110 | }
|
---|
111 |
|
---|
112 | @Override
|
---|
113 | public double tileYToLat(int y, int zoom) {
|
---|
114 | return tileYToLat((double )y, zoom);
|
---|
115 | }
|
---|
116 |
|
---|
117 | /*
|
---|
118 | * To solve inverse formula latitude = f(y) we use
|
---|
119 | * Newton's method. We cache previous calculated latitude,
|
---|
120 | * because new one is usually close to the old one. In case
|
---|
121 | * if solution gets out of bounds, we reset to a new random
|
---|
122 | * value.
|
---|
123 | */
|
---|
124 | private double cached_lat = 0;
|
---|
125 | private double tileYToLat(double y, int zoom) {
|
---|
126 | double lat0, lat;
|
---|
127 |
|
---|
128 | lat = cached_lat;
|
---|
129 | do {
|
---|
130 | lat0 = lat;
|
---|
131 | lat = lat - Math.toDegrees(NextTerm(Math.toRadians(lat), y, zoom));
|
---|
132 | if (lat > OsmMercator.MAX_LAT || lat < OsmMercator.MIN_LAT) {
|
---|
133 | Random r = new Random();
|
---|
134 | lat = OsmMercator.MIN_LAT +
|
---|
135 | r.nextInt((int )(OsmMercator.MAX_LAT - OsmMercator.MIN_LAT));
|
---|
136 | }
|
---|
137 | } while ((Math.abs(lat0 - lat) > 0.000001));
|
---|
138 |
|
---|
139 | cached_lat = lat;
|
---|
140 |
|
---|
141 | return (lat);
|
---|
142 | }
|
---|
143 |
|
---|
144 | /* Next term in Newton's polynomial */
|
---|
145 | private double NextTerm(double lat, double y, int zoom) {
|
---|
146 | double sinl=Math.sin(lat);
|
---|
147 | double cosl=Math.cos(lat);
|
---|
148 | double ec, f, df;
|
---|
149 |
|
---|
150 | zoom = (int )Math.pow(2.0, zoom - 1);
|
---|
151 | ec = Math.exp((1 - y/zoom)*Math.PI);
|
---|
152 |
|
---|
153 | f = (Math.tan(Math.PI/4+lat/2) -
|
---|
154 | ec * Math.pow(Math.tan(Math.PI/4 + Math.asin(E * sinl)/2), E));
|
---|
155 | df = 1/(1 - sinl) - ec * E * cosl/((1 - E * sinl) *
|
---|
156 | (Math.sqrt (1 - E * E * sinl * sinl)));
|
---|
157 |
|
---|
158 | return (f/df);
|
---|
159 | }
|
---|
160 | }
|
---|