1 import gov.nasa.worldwind.avlist.AVKey;
2 import gov.nasa.worldwind.avlist.AVList;
3 import gov.nasa.worldwind.avlist.AVListImpl;
4 import gov.nasa.worldwind.geom.Angle;
5 import gov.nasa.worldwind.geom.LatLon;
6 import gov.nasa.worldwind.layers.Mercator.BasicMercatorTiledImageLayer;
7 import gov.nasa.worldwind.layers.Mercator.MercatorSector;
8 import gov.nasa.worldwind.util.LevelSet;
9 import gov.nasa.worldwind.util.Tile;
10 import gov.nasa.worldwind.util.TileUrlBuilder;
11
12 import java.awt.image.BufferedImage;
13 import java.net.MalformedURLException;
14 import java.net.URL;
15
16 public class GoogleEarthLayer extends BasicMercatorTiledImageLayer {
17 public static enum Dataset {
18 AERIAL("Aerial", "a", ".jpg",
19 "http://khm%d.google.com/kh/v=57&x=%d&y=%d&z=%d&s=Galile");
20
21 public final String label;
22 public final String dataset;
23 public final String formatSuffix;
24 public final String urlFormat;
25
26 private Dataset(String label, String dataset, String formatSuffix,
27 String urlFormat) {
28 this.label = label;
29 this.dataset = dataset;
30 this.formatSuffix = formatSuffix;
31 this.urlFormat = urlFormat;
32 }
33 }
34
35 private final Dataset dataset;
36
37 public GoogleEarthLayer() {
38 this(Dataset.AERIAL);
39 }
40
41 public GoogleEarthLayer(Dataset dataset) {
42 super(makeLevels(dataset));
43 if (dataset == null)
44 throw new NullPointerException("Dataset cannot be null");
45 this.dataset = dataset;
46 this.setValue(AVKey.DISPLAY_NAME, "Google Earth " + dataset.label);
47 this.setSplitScale(1.3);
48 }
49
50 protected static LevelSet makeLevels(Dataset dataset) {
51 AVList params = new AVListImpl();
52
53 params.setValue(AVKey.TILE_WIDTH, 256);
54 params.setValue(AVKey.TILE_HEIGHT, 256);
55 params
56 .setValue(AVKey.DATA_CACHE_NAME, "Google Earth7 "
57 + dataset.label);
58 params.setValue(AVKey.SERVICE,
59 "http://kh0.google.com/kh?n=404&v=17&t=t");
60 params.setValue(AVKey.DATASET_NAME, dataset.dataset);
61 params.setValue(AVKey.FORMAT_SUFFIX, dataset.formatSuffix);
62 params.setValue(AVKey.NUM_LEVELS, 16);
63 params.setValue(AVKey.NUM_EMPTY_LEVELS, 0);
64 params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(Angle
65 .fromDegrees(22.5d), Angle.fromDegrees(45d)));
66 params.setValue(AVKey.SECTOR, new MercatorSector(-1.0, 1.0,
67 Angle.NEG180, Angle.POS180));
68 params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(dataset));
69 params.setValue(AVKey.DISPLAY_NAME, "Google Maps " + dataset.label);
70
71 return new LevelSet(params);
72 }
73
74 public static class URLBuilder implements TileUrlBuilder {
75
76 private Dataset dataset;
77
78 public URLBuilder(Dataset dataset) {
79 this.dataset = dataset;
80 }
81
82 public URL getURL(Tile tile, String imageFormat)
83 throws MalformedURLException {
84 LatLon center = tile.getSector().getCentroid();
85 String urlString = computeTileUrl(dataset.urlFormat, center
86 .getLatitude().degrees, center.getLongitude().degrees, tile
87 .getLevelNumber() + 3);
88 System.out.println("Need:" + urlString);
89 URL url = new URL(urlString);
90 return url;
91 }
92
93 private static String computeTileUrl(String urlFormat, double lat, double lon, int zoom) {
94
95 if (lon > 180.0) {
96 lon -= 360.0;
97 }
98
99 lon = (180.0 + lon) / 360.0;
100 lat = 0.5
101 - Math.log(Math.tan((Math.PI / 4.0)
102 + ((Math.PI * lat) / (2.0 * 180.0)))) / (2.0 * Math.PI);
103
104 int scale = 1 << (int) zoom;
105
106 // can just truncate to integer, this looses the fractional
107 // "pixel offset"
108 int x = (int) (lon * scale);
109 int y = (int) (lat * scale);
110 return String.format(urlFormat, (int) (Math.random() * 4), x, y, zoom);
111 }
112
113 }
114
115 @Override
116 protected boolean isTileValid(BufferedImage image) {
117 // return false if the tile is white (this will mark the tile as absent)
118 boolean white = true;
119 // JPEG compression will cause white to be not quite white
120 String lowercaseFormat = getDataset().formatSuffix.toLowerCase();
121 int threshold = lowercaseFormat.contains("jpg")
122 || lowercaseFormat.contains("jpeg") ? 200 : 250;
123 for (int x = 0; x < image.getWidth(); x++) {
124 for (int y = 0; y < image.getHeight(); y++) {
125 int rgb = image.getRGB(x, y);
126 white = isWhite(rgb, threshold);
127 if (!white)
128 break;
129 }
130 if (!white)
131 break;
132 }
133 return !white;
134 }
135
136 private boolean isWhite(int rgb, int threshold) {
137 int r = (rgb >> 16) & 0xff;
138 int g = (rgb >> 8) & 0xff;
139 int b = (rgb >> 0) & 0xff;
140 return r + b + g > threshold * 3;
141 }
142
143 public Dataset getDataset() {
144 return dataset;
145 }
146 }