VirtualEarthLayer
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.render.DrawContext; 7 import gov.nasa.worldwind.util.LevelSet; 8 import gov.nasa.worldwind.util.Tile; 9 import gov.nasa.worldwind.util.TileUrlBuilder; 10 import gov.nasa.worldwind.layers.Mercator.*; 11 12 import java.awt.image.BufferedImage; 13 import java.net.MalformedURLException; 14 import java.net.URL; 15 16 public class VirtualEarthLayer extends BasicMercatorTiledImageLayer 17 { 18 public static enum Dataset 19 { 20 AERIAL("Aerial", "a", ".jpg"), 21 HYBRID("Hybrid", "h", ".jpg"), 22 ROAD("Road", "r", ".png"); 23 24 public final String label; 25 public final String dataset; 26 public final String formatSuffix; 27 28 private Dataset(String label, String dataset, String formatSuffix) 29 { 30 this.label = label; 31 this.dataset = dataset; 32 this.formatSuffix = formatSuffix; 33 } 34 } 35 36 // private VirtualEarthLogo logo = new VirtualEarthLogo(); 37 private final Dataset dataset; 38 39 public VirtualEarthLayer() 40 { 41 this(Dataset.HYBRID); 42 } 43 44 public VirtualEarthLayer(Dataset dataset) 45 { 46 super(makeLevels(dataset)); 47 if (dataset == null) 48 throw new NullPointerException("Dataset cannot be null"); 49 this.dataset = dataset; 50 this.setValue(AVKey.DISPLAY_NAME, "Microsoft Virtual Earth " 51 + dataset.label); 52 // this.setSplitScale(1.3); 53 } 54 55 protected static LevelSet makeLevels(Dataset dataset) 56 { 57 AVList params = new AVListImpl(); 58 59 params.setValue(AVKey.TILE_WIDTH, 256); 60 params.setValue(AVKey.TILE_HEIGHT, 256); 61 params.setValue(AVKey.DATA_CACHE_NAME, "Earth/MS Virtual Earth Mercator/MSVE " 62 + dataset.label); 63 params.setValue(AVKey.SERVICE, 64 "http://a0.ortho.tiles.virtualearth.net/tiles/"); 65 params.setValue(AVKey.DATASET_NAME, dataset.dataset); 66 params.setValue(AVKey.FORMAT_SUFFIX, dataset.formatSuffix); 67 params.setValue(AVKey.NUM_LEVELS, 16); 68 params.setValue(AVKey.NUM_EMPTY_LEVELS, 0); 69 params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(Angle 70 .fromDegrees(22.5d), Angle.fromDegrees(45d))); 71 params.setValue(AVKey.SECTOR, new MercatorSector(-1.0, 1.0, 72 Angle.NEG180, Angle.POS180)); 73 params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder()); 74 params.setValue(AVKey.DISPLAY_NAME, "Microsoft Virtual Earth " 75 + dataset.label); 76 77 return new LevelSet(params); 78 } 79 80 private static class URLBuilder implements TileUrlBuilder 81 { 82 public URL getURL(Tile tile, String imageFormat) 83 throws MalformedURLException 84 { 85 String quadkey = tileToQuadKey(tile.getColumn(), tile.getRow(), 86 tile.getLevelNumber() + 2); 87 return new URL(tile.getLevel().getService() 88 + tile.getLevel().getDataset() + quadkey + ".jpeg?g=1"); 89 } 90 } 91 92 protected static String tileToQuadKey(int col, int row, int level) 93 { 94 String quad = ""; 95 for (int i = level; i >= 0; i--) 96 { 97 int mask = 1 << i; 98 int cell = 0; 99 if ((col & mask) != 0) 100 { 101 cell++; 102 } 103 if ((row & mask) == 0) 104 { 105 cell += 2; 106 } 107 quad += cell; 108 } 109 return quad; 110 } 111 112 @Override 113 public void render(DrawContext dc) 114 { 115 super.render(dc); 116 /* 117 if (isEnabled()) 118 { 119 dc.addOrderedRenderable(logo); 120 } 121 */ 122 } 123 124 @Override 125 protected boolean isTileValid(BufferedImage image) 126 { 127 //return false if the tile is white (this will mark the tile as absent) 128 boolean white = true; 129 //JPEG compression will cause white to be not quite white 130 String lowercaseFormat = getDataset().formatSuffix.toLowerCase(); 131 int threshold = lowercaseFormat.contains("jpg") 132 || lowercaseFormat.contains("jpeg") ? 200 : 250; 133 for (int x = 0; x < image.getWidth(); x++) 134 { 135 for (int y = 0; y < image.getHeight(); y++) 136 { 137 int rgb = image.getRGB(x, y); 138 white = isWhite(rgb, threshold); 139 if (!white) 140 break; 141 } 142 if (!white) 143 break; 144 } 145 return !white; 146 } 147 148 private boolean isWhite(int rgb, int threshold) 149 { 150 int r = (rgb >> 16) & 0xff; 151 int g = (rgb >> 8) & 0xff; 152 int b = (rgb >> 0) & 0xff; 153 return r + b + g > threshold * 3; 154 } 155 156 public Dataset getDataset() 157 { 158 return dataset; 159 } 160 }