diff --git a/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java b/src/org/openstreetmap/gui/jmapviewer/tilesources/BingAerialTileSource.java
index 47447f9..9a7b29a 100644
|
a
|
b
|
|
| 9 | 9 | import java.util.ArrayList; |
| 10 | 10 | import java.util.List; |
| 11 | 11 | import java.util.Locale; |
| 12 | | import java.util.concurrent.Callable; |
| 13 | | import java.util.concurrent.ExecutionException; |
| 14 | | import java.util.concurrent.Future; |
| 15 | | import java.util.concurrent.FutureTask; |
| 16 | | import java.util.concurrent.TimeUnit; |
| | 12 | import java.util.concurrent.ExecutorService; |
| | 13 | import java.util.concurrent.CompletableFuture; |
| | 14 | import java.util.concurrent.Executors; |
| | 15 | import java.util.function.Supplier; |
| 17 | 16 | import java.util.regex.Pattern; |
| 18 | 17 | |
| 19 | 18 | import javax.imageio.ImageIO; |
| … |
… |
|
| 43 | 42 | public class BingAerialTileSource extends TMSTileSource { |
| 44 | 43 | |
| 45 | 44 | private static final String API_KEY = "Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU"; |
| 46 | | private static volatile Future<List<Attribution>> attributions; // volatile is required for getAttribution(), see below. |
| | 45 | private static volatile List<Attribution> attributions; // volatile is required for getAttribution(), see below. |
| 47 | 46 | private static String imageUrlTemplate; |
| 48 | 47 | private static Integer imageryZoomMax; |
| 49 | 48 | private static String[] subdomains; |
| … |
… |
|
| 52 | 51 | private static final Pattern quadkeyPattern = Pattern.compile("\\{quadkey\\}"); |
| 53 | 52 | private static final Pattern culturePattern = Pattern.compile("\\{culture\\}"); |
| 54 | 53 | private String brandLogoUri; |
| | 54 | private ExecutorService executor; |
| 55 | 55 | |
| 56 | 56 | /** |
| 57 | 57 | * Constructs a new {@code BingAerialTileSource}. |
| 58 | 58 | */ |
| 59 | 59 | public BingAerialTileSource() { |
| 60 | 60 | super(new TileSourceInfo("Bing Aerial Maps", null, null)); |
| | 61 | attributions = new ArrayList<>(); |
| | 62 | executor = Executors.newCachedThreadPool(); |
| 61 | 63 | } |
| 62 | 64 | |
| 63 | 65 | /** |
| … |
… |
public BingAerialTileSource() {
|
| 66 | 68 | */ |
| 67 | 69 | public BingAerialTileSource(TileSourceInfo info) { |
| 68 | 70 | super(info); |
| | 71 | attributions = new ArrayList<>(); |
| | 72 | executor = Executors.newCachedThreadPool(); |
| 69 | 73 | } |
| 70 | 74 | |
| 71 | 75 | protected static class Attribution { |
| … |
… |
public BingAerialTileSource(TileSourceInfo info) {
|
| 79 | 83 | @Override |
| 80 | 84 | public String getTileUrl(int zoom, int tilex, int tiley) throws IOException { |
| 81 | 85 | // make sure that attribution is loaded. otherwise subdomains is null. |
| 82 | | if (getAttribution() == null) |
| | 86 | if (attributions.isEmpty()) |
| 83 | 87 | throw new IOException("Attribution is not loaded yet"); |
| 84 | 88 | |
| 85 | 89 | int t = (zoom + tilex + tiley) % subdomains.length; |
| … |
… |
public String getTermsOfUseURL() {
|
| 234 | 238 | return "http://opengeodata.org/microsoft-imagery-details"; |
| 235 | 239 | } |
| 236 | 240 | |
| 237 | | protected Callable<List<Attribution>> getAttributionLoaderCallable() { |
| 238 | | return new Callable<List<Attribution>>() { |
| | 241 | protected List<Attribution> getAttribution() { |
| | 242 | final Supplier<List<Attribution>> loader = new Supplier<List<Attribution>>() { |
| 239 | 243 | |
| 240 | 244 | @Override |
| 241 | | public List<Attribution> call() throws Exception { |
| 242 | | int waitTimeSec = 1; |
| 243 | | while (true) { |
| 244 | | try { |
| 245 | | InputSource xml = new InputSource(getAttributionUrl().openStream()); |
| 246 | | List<Attribution> r = parseAttributionText(xml); |
| 247 | | return r; |
| 248 | | } catch (IOException ex) { |
| 249 | | System.err.println("Could not connect to Bing API. Will retry in " + waitTimeSec + " seconds."); |
| 250 | | Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSec)); |
| 251 | | waitTimeSec *= 2; |
| 252 | | } |
| | 245 | public List<Attribution> get() { |
| | 246 | try { |
| | 247 | InputSource xml = new InputSource(getAttributionUrl().openStream()); |
| | 248 | List<Attribution> r = parseAttributionText(xml); |
| | 249 | return r; |
| | 250 | } catch (IOException ex) { |
| | 251 | System.err.println("Could not connect to Bing API."); |
| 253 | 252 | } |
| | 253 | return null; |
| 254 | 254 | } |
| 255 | 255 | }; |
| 256 | | } |
| 257 | | |
| 258 | | protected List<Attribution> getAttribution() { |
| 259 | | if (attributions == null) { |
| 260 | | // see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html |
| 261 | | synchronized (BingAerialTileSource.class) { |
| 262 | | if (attributions == null) { |
| 263 | | final FutureTask<List<Attribution>> loader = new FutureTask<>(getAttributionLoaderCallable()); |
| 264 | | new Thread(loader, "bing-attribution-loader").start(); |
| 265 | | attributions = loader; |
| 266 | | } |
| 267 | | } |
| 268 | | } |
| 269 | | try { |
| 270 | | return attributions.get(); |
| 271 | | } catch (ExecutionException ex) { |
| 272 | | throw new RuntimeException(ex.getCause()); |
| 273 | | } catch (InterruptedException ign) { |
| 274 | | System.err.println("InterruptedException: " + ign.getMessage()); |
| 275 | | } |
| 276 | | return null; |
| | 256 | CompletableFuture.supplyAsync(loader, executor).thenAccept(attr -> { |
| | 257 | attributions.clear(); |
| | 258 | attributions.addAll(attr); |
| | 259 | }); |
| | 260 | return attributions; |
| 277 | 261 | } |
| 278 | 262 | |
| 279 | 263 | @Override |