public static byte[] getImage(int x, int y, int zoom, HttpMapSource mapSource)
      throws IOException, InterruptedException, UnrecoverableDownloadException {

    MapSpace mapSpace = mapSource.getMapSpace();
    int maxTileIndex = mapSpace.getMaxPixels(zoom) / mapSpace.getTileSize();
    if (x > maxTileIndex)
      throw new RuntimeException("Invalid tile index x=" + x + " for zoom " + zoom);
    if (y > maxTileIndex)
      throw new RuntimeException("Invalid tile index y=" + y + " for zoom " + zoom);

    TileStore ts = TileStore.getInstance();

    // Thread.sleep(2000);

    // Test code for creating random download failures
    // if (Math.random()>0.7) throw new
    // IOException("intentionally download error");

    Settings s = Settings.getInstance();

    TileStoreEntry tile = null;
    if (s.tileStoreEnabled) {

      // Copy the file from the persistent tilestore instead of
      // downloading it from internet.
      tile = ts.getTile(x, y, zoom, mapSource);
      boolean expired = isTileExpired(tile);
      if (tile != null) {
        if (expired) {
          log.trace("Expired: " + mapSource.getName() + " " + tile);
        } else {
          log.trace("Tile of map source " + mapSource.getName() + " used from tilestore");
          byte[] data = tile.getData();
          notifyCachedTileUsed(data.length);
          return data;
        }
      }
    }
    byte[] data = null;
    if (tile == null) {
      data = downloadTileAndUpdateStore(x, y, zoom, mapSource);
      notifyTileDownloaded(data.length);
    } else {
      byte[] updatedData = updateStoredTile(tile, mapSource);
      if (updatedData != null) {
        data = updatedData;
        notifyTileDownloaded(data.length);
      } else {
        data = tile.getData();
        notifyCachedTileUsed(data.length);
      }
    }
    return data;
  }
  public BufferedImage getTileImage(int zoom, int x, int y, LoadMethod loadMethod)
      throws IOException, InterruptedException, TileException {
    BufferedImage image = null;
    Graphics2D g2 = null;
    try {
      ArrayList<BufferedImage> layerImages = new ArrayList<BufferedImage>(mapSources.length);
      int maxSize = mapSpace.getTileSize();
      for (int i = 0; i < mapSources.length; i++) {
        MapSource layerMapSource = mapSources[i];
        BufferedImage layerImage = layerMapSource.getTileImage(zoom, x, y, loadMethod);
        if (layerImage != null) {
          log.debug("Multi layer loading: " + layerMapSource + " " + x + " " + y + " " + zoom);
          layerImages.add(layerImage);
          int size = layerImage.getWidth();
          if (size > maxSize) {
            maxSize = size;
          }
        }
      }

      image = new BufferedImage(maxSize, maxSize, BufferedImage.TYPE_3BYTE_BGR);
      g2 = image.createGraphics();
      g2.setColor(getBackgroundColor());
      g2.fillRect(0, 0, maxSize, maxSize);

      for (int i = 0; i < layerImages.size(); i++) {
        BufferedImage layerImage = layerImages.get(i);
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getLayerAlpha(i)));
        g2.drawImage(layerImage, 0, 0, maxSize, maxSize, null);
      }
      return image;
    } finally {
      if (g2 != null) {
        g2.dispose();
      }
    }
  }