public int countImagesInSector(Sector sector, int levelNumber) {
    if (sector == null) {
      String msg = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    Level targetLevel = this.levels.getLastLevel();
    if (levelNumber >= 0) {
      for (int i = levelNumber; i < this.getLevels().getLastLevel().getLevelNumber(); i++) {
        if (this.levels.isLevelEmpty(i)) continue;

        targetLevel = this.levels.getLevel(i);
        break;
      }
    }

    // Collect all the tiles intersecting the input sector.
    LatLon delta = targetLevel.getTileDelta();
    Angle latOrigin = this.levels.getTileOrigin().getLatitude();
    Angle lonOrigin = this.levels.getTileOrigin().getLongitude();
    final int nwRow = Tile.computeRow(delta.getLatitude(), sector.getMaxLatitude(), latOrigin);
    final int nwCol = Tile.computeColumn(delta.getLongitude(), sector.getMinLongitude(), lonOrigin);
    final int seRow = Tile.computeRow(delta.getLatitude(), sector.getMinLatitude(), latOrigin);
    final int seCol = Tile.computeColumn(delta.getLongitude(), sector.getMaxLongitude(), lonOrigin);

    int numRows = nwRow - seRow + 1;
    int numCols = seCol - nwCol + 1;

    return numRows * numCols;
  }
  public int computeLevelForResolution(Sector sector, Globe globe, double resolution) {
    if (sector == null) {
      String message = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(message);
      throw new IllegalStateException(message);
    }

    if (globe == null) {
      String message = Logging.getMessage("nullValue.GlobeIsNull");
      Logging.logger().severe(message);
      throw new IllegalStateException(message);
    }

    double texelSize = 0;
    Level targetLevel = this.levels.getLastLevel();
    for (int i = 0; i < this.getLevels().getLastLevel().getLevelNumber(); i++) {
      if (this.levels.isLevelEmpty(i)) continue;

      texelSize = this.levels.getLevel(i).getTexelSize();
      if (texelSize > resolution) continue;

      targetLevel = this.levels.getLevel(i);
      break;
    }

    Logging.logger()
        .info(
            Logging.getMessage(
                "layers.TiledImageLayer.LevelSelection", targetLevel.getLevelNumber(), texelSize));
    return targetLevel.getLevelNumber();
  }
  /**
   * Resolves a reference to a local element identified by address and identifier, where {@code
   * linkBase} identifies a document, including the current document, and {@code linkRef} is the id
   * of the desired element.
   *
   * <p>If {@code linkBase} refers to a local COLLADA file and {@code linkRef} is non-null, the
   * return value is the element identified by {@code linkRef}. If {@code linkRef} is null, the
   * return value is a parsed {@link ColladaRoot} for the COLLADA file identified by {@code
   * linkBase}. Otherwise, {@code linkBase} is returned.
   *
   * @param linkBase the address of the document containing the requested element.
   * @param linkRef the element's identifier.
   * @return the requested element, or null if the element is not found.
   * @throws IllegalArgumentException if the address is null.
   */
  protected Object resolveLocalReference(String linkBase, String linkRef) {
    if (linkBase == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    try {
      File file = new File(linkBase);

      if (!file.exists()) return null;

      // Determine whether the file is a COLLADA document. If not, just return the file path.
      if (!WWIO.isContentType(file, ColladaConstants.COLLADA_MIME_TYPE))
        return file.toURI().toString();

      // Attempt to open and parse the COLLADA file.
      ColladaRoot refRoot = ColladaRoot.createAndParse(file);
      // An exception is thrown if parsing fails, so no need to check for null.

      // Add the parsed file to the session cache so it doesn't have to be parsed again.
      WorldWind.getSessionCache().put(linkBase, refRoot);

      // Now check the newly opened COLLADA file for the referenced item, if a reference was
      // specified.
      if (linkRef != null) return refRoot.getItemByID(linkRef);
      else return refRoot;
    } catch (Exception e) {
      String message =
          Logging.getMessage("generic.UnableToResolveReference", linkBase + "/" + linkRef);
      Logging.logger().warning(message);
      return null;
    }
  }
  /**
   * Create an instance.
   *
   * @param tc the current {@link KMLTraversalContext}.
   * @param placemark the <i>Placemark</i> element containing the <i>Point</i>.
   * @param geom the {@link gov.nasa.worldwind.ogc.kml.KMLPoint} geometry.
   * @throws NullPointerException if the geometry is null.
   * @throws IllegalArgumentException if the parent placemark or the traversal context is null.
   */
  public KMLModelPlacemarkImpl(
      KMLTraversalContext tc, KMLPlacemark placemark, KMLAbstractGeometry geom) {
    if (tc == null) {
      String msg = Logging.getMessage("nullValue.TraversalContextIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    if (placemark == null) {
      String msg = Logging.getMessage("nullValue.ParentIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    if (geom == null) {
      String msg = Logging.getMessage("nullValue.GeometryIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    this.model = (KMLModel) geom;
    this.parent = placemark;

    this.resourceMap = this.createResourceMap(this.model);
  }
  protected static AVList wmsGetParamsFromCapsDoc(Capabilities caps, AVList params) {
    if (caps == null) {
      String message = Logging.getMessage("nullValue.WMSCapabilities");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (params == null) {
      String message = Logging.getMessage("nullValue.LayerConfigParams");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    try {
      DataConfigurationUtils.getWMSLayerParams(caps, formatOrderPreference, params);
    } catch (IllegalArgumentException e) {
      String message = Logging.getMessage("WMS.MissingLayerParameters");
      Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
      throw new IllegalArgumentException(message, e);
    } catch (WWRuntimeException e) {
      String message = Logging.getMessage("WMS.MissingCapabilityValues");
      Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
      throw new IllegalArgumentException(message, e);
    }

    setFallbacks(params);

    // Setup WMS URL builder.
    params.setValue(AVKey.WMS_VERSION, caps.getVersion());
    params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(params));
    // Setup default WMS tiled image layer behaviors.
    params.setValue(AVKey.USE_TRANSPARENT_TEXTURES, true);

    return params;
  }
    public void run() {
      if (Thread.currentThread().isInterrupted())
        return; // the task was cancelled because it's a duplicate or for some other reason

      try {
        this.placemark.retrieveModel(this.address);
      } catch (IOException e) {
        String message = Logging.getMessage("generic.ExceptionWhileReading", e.getMessage());
        Logging.logger().warning(message);
      } catch (XMLStreamException e) {
        String message =
            Logging.getMessage("generic.ExceptionAttemptingToParseXml", e.getMessage());
        Logging.logger().warning(message);
      }
    }
  /**
   * Resolves a reference to a local or remote file or element. If the link refers to an element in
   * the current document, this method returns that element. If the link refers to a remote
   * document, this method will initiate asynchronous retrieval of the document, and return a URL of
   * the downloaded document in the file cache, if it is available locally. If the link identifies a
   * COLLADA document, the document will be returned as a parsed ColladaRoot.
   *
   * @param link the address of the document or element to resolve. This may be a full URL, a URL
   *     fragment that identifies an element in the current document ("#myElement"), or a URL and a
   *     fragment identifier ("http://server.com/model.dae#myElement").
   * @return the requested element, or null if the element is not found.
   * @throws IllegalArgumentException if the address is null.
   */
  public Object resolveReference(String link) {
    if (link == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    try {
      String[] linkParts = link.split("#");
      String linkBase = linkParts[0];
      String linkRef = linkParts.length > 1 ? linkParts[1] : null;

      // See if it's a reference to an internal element.
      if (WWUtil.isEmpty(linkBase) && !WWUtil.isEmpty(linkRef)) return this.getItemByID(linkRef);

      // Interpret the path relative to the current document.
      String path = this.getSupportFilePath(linkBase);
      if (path == null) path = linkBase;

      // See if it's an already found and parsed COLLADA file.
      Object o = WorldWind.getSessionCache().get(path);
      if (o != null && o instanceof ColladaRoot)
        return linkRef != null ? ((ColladaRoot) o).getItemByID(linkRef) : o;

      URL url = WWIO.makeURL(path);
      if (url == null) {
        // See if the reference can be resolved to a local file.
        o = this.resolveLocalReference(path, linkRef);
      }

      // If we didn't find a local file, treat it as a remote reference.
      if (o == null) o = this.resolveRemoteReference(path, linkRef);

      if (o != null) return o;

      // If the reference was not resolved as a remote reference, look for a local element
      // identified by the
      // reference string. This handles the case of malformed internal references that omit the #
      // sign at the
      // beginning of the reference.
      return this.getItemByID(link);
    } catch (Exception e) {
      String message = Logging.getMessage("generic.UnableToResolveReference", link);
      Logging.logger().warning(message);
    }

    return null;
  }
    /**
     * Construct a request task for a specified network link resource.
     *
     * @param placemark the placemark for which to construct the request task.
     * @param address the address of the resource to request.
     */
    protected RequestTask(KMLModelPlacemarkImpl placemark, String address) {
      if (placemark == null) {
        String message = Logging.getMessage("nullValue.ObjectIsNull");
        Logging.logger().severe(message);
        throw new IllegalArgumentException(message);
      }

      if (address == null) {
        String message = Logging.getMessage("nullValue.PathIsNull");
        Logging.logger().severe(message);
        throw new IllegalArgumentException(message);
      }

      this.placemark = placemark;
      this.address = address;
    }
  public boolean isLayerInView(DrawContext dc) {
    if (dc == null) {
      String message = Logging.getMessage("nullValue.DrawContextIsNull");
      Logging.logger().severe(message);
      throw new IllegalStateException(message);
    }

    if (dc.getView() == null) {
      String message = Logging.getMessage("layers.AbstractLayer.NoViewSpecifiedInDrawingContext");
      Logging.logger().severe(message);
      throw new IllegalStateException(message);
    }

    return !(dc.getVisibleSector() != null
        && !this.levels.getSector().intersects(dc.getVisibleSector()));
  }
  @Override
  public BufferedImage composeImageForSector(
      Sector sector,
      int canvasWidth,
      int canvasHeight,
      double aspectRatio,
      int levelNumber,
      String mimeType,
      boolean abortOnError,
      BufferedImage image,
      int timeout)
      throws Exception {
    if (sector == null) {
      String message = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    ComposeImageTile tile =
        new ComposeImageTile(
            sector, mimeType, this.getLevels().getLastLevel(), canvasWidth, canvasHeight);
    try {
      if (image == null)
        image = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_RGB);

      downloadImage(tile, mimeType, timeout);
      Thread.sleep(1); // generates InterruptedException if thread has been interupted

      BufferedImage tileImage = ImageIO.read(tile.getFile());
      Thread.sleep(1); // generates InterruptedException if thread has been interupted

      ImageUtil.mergeImage(sector, tile.getSector(), aspectRatio, tileImage, image);
      Thread.sleep(1); // generates InterruptedException if thread has been interupted

      this.firePropertyChange(AVKey.PROGRESS, 0d, 1d);
    } catch (InterruptedIOException e) {
      throw e;
    } catch (Exception e) {
      if (abortOnError) throw e;

      String message = Logging.getMessage("generic.ExceptionWhileRequestingImage", tile.getPath());
      Logging.logger().log(java.util.logging.Level.WARNING, message, e);
    }

    return image;
  }
  /**
   * Resolves a reference to a remote element identified by address and identifier, where {@code
   * linkBase} identifies a remote document, and {@code linkRef} is the id of the desired element.
   * This method retrieves resources asynchronously using the {@link
   * gov.nasa.worldwind.cache.FileStore}.
   *
   * <p>The return value is null if the file is not yet available in the FileStore. If {@code
   * linkBase} refers to a COLLADA file and {@code linkRef} is non-null, the return value is the
   * element identified by {@code linkRef}. If {@code linkBase} refers to a COLLADA file and {@code
   * linkRef} is null, the return value is a parsed {@link ColladaRoot} for the COLLADA file
   * identified by {@code linkBase}. Otherwise the return value is a {@link URL} to the file in the
   * file cache.
   *
   * @param linkBase the address of the document containing the requested element.
   * @param linkRef the element's identifier.
   * @return URL to the requested file, parsed ColladaRoot, or COLLADA element. Returns null if the
   *     document is not yet available in the FileStore.
   * @throws IllegalArgumentException if the {@code linkBase} is null.
   */
  public Object resolveRemoteReference(String linkBase, String linkRef) {
    if (linkBase == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    try {
      // See if it's in the cache. If not, requestFile will start another thread to retrieve it and
      // return null.
      URL url = WorldWind.getDataFileStore().requestFile(linkBase);
      if (url == null) return null;

      // It's in the cache. If it's a COLLADA file try to parse it so we can search for the
      // specified reference.
      // If it's not COLLADA, just return the url for the cached file.
      String contentType = WorldWind.getDataFileStore().getContentType(linkBase);
      if (contentType == null) {
        String suffix = WWIO.getSuffix(linkBase.split(";")[0]); // strip of trailing garbage
        if (!WWUtil.isEmpty(suffix)) contentType = WWIO.makeMimeTypeForSuffix(suffix);
      }

      if (!this.canParseContentType(contentType)) return url;

      // If the file is a COLLADA document, attempt to open it. We can't open it as a File with
      // createAndParse
      // because the ColladaRoot that will be created needs to have the remote address in order to
      // resolve any
      // relative references within it.
      ColladaRoot refRoot = this.parseCachedColladaFile(url, linkBase);

      // Add the parsed file to the session cache so it doesn't have to be parsed again.
      WorldWind.getSessionCache().put(linkBase, refRoot);

      // Now check the newly opened COLLADA file for the referenced item, if a reference was
      // specified.
      if (linkRef != null) return refRoot.getItemByID(linkRef);
      else return refRoot;
    } catch (Exception e) {
      String message =
          Logging.getMessage("generic.UnableToResolveReference", linkBase + "/" + linkRef);
      Logging.logger().warning(message);
      return null;
    }
  }
 /** Closes the event stream associated with this context's XML event reader. */
 protected void closeEventStream() {
   try {
     this.eventStream.close();
     this.eventStream = null;
   } catch (IOException e) {
     String message = Logging.getMessage("generic.ExceptionClosingXmlEventReader");
     Logging.logger().warning(message);
   }
 }
  /**
   * Specifies this shape's geographic position. The position's altitude is relative to this shape's
   * altitude mode.
   *
   * @param position this shape's geographic position.
   * @throws IllegalArgumentException if the position is null.
   */
  public void setPosition(Position position) {
    if (position == null) {
      String message = Logging.getMessage("nullValue.PositionIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.position = position;
  }
  protected static AVList wmsRestorableStateToParams(String stateInXml) {
    if (stateInXml == null) {
      String message = Logging.getMessage("nullValue.StringIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    RestorableSupport rs;
    try {
      rs = RestorableSupport.parse(stateInXml);
    } catch (Exception e) {
      // Parsing the document specified by stateInXml failed.
      String message = Logging.getMessage("generic.ExceptionAttemptingToParseStateXml", stateInXml);
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message, e);
    }

    AVList params = new AVListImpl();
    wmsRestoreStateToParams(rs, null, params);
    return params;
  }
  /**
   * Create a new <code>ColladaRoot</code> for a {@link ColladaDoc} instance. A ColladaDoc
   * represents COLLADA files from either files or input streams.
   *
   * @param docSource the ColladaDoc instance representing the COLLADA document.
   * @throws IllegalArgumentException if the document source is null.
   * @throws IOException if an error occurs while reading the COLLADA document.
   */
  public ColladaRoot(ColladaDoc docSource) throws IOException {
    super(ColladaConstants.COLLADA_NAMESPACE);

    if (docSource == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.colladaDoc = docSource;
    this.initialize();
  }
  public Double[] getLayerExtremeElevations(WMSCapabilities caps, String[] layerNames) {
    if (caps == null) {
      String message = Logging.getMessage("nullValue.WMSCapabilities");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (layerNames == null) {
      String message = Logging.getMessage("nullValue.WMSLayerNames");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    Double extremeMin = null;
    Double extremeMax = null;

    for (String name : layerNames) {
      WMSLayerCapabilities layer = caps.getLayerByName(name);
      if (layer == null) continue;

      Double min = layer.getExtremeElevationMin();
      if (min != null && (extremeMin == null || min.compareTo(min) > 0)) extremeMin = min;

      Double max = layer.getExtremeElevationMax();
      if (max != null && (extremeMax == null || max.compareTo(max) > 0)) extremeMax = max;
    }

    if (extremeMin != null || extremeMax != null) {
      Double[] extremes = new Double[] {null, null};

      if (extremeMin != null) extremes[0] = extremeMin;
      if (extremeMax != null) extremes[1] = extremeMax;

      return extremes;
    }

    return null;
  }
  /**
   * Create a new <code>ColladaRoot</code> for a {@link URL}.
   *
   * @param docSource the URL of the document.
   * @throws IllegalArgumentException if the document source is null.
   * @throws IOException if an error occurs while reading the Collada document.
   */
  public ColladaRoot(URL docSource) throws IOException {
    super(ColladaConstants.COLLADA_NAMESPACE);

    if (docSource == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    URLConnection conn = docSource.openConnection();
    this.colladaDoc = new ColladaInputStream(conn.getInputStream(), WWIO.makeURI(docSource));

    this.initialize();
  }
  private BufferedImage requestImage(MercatorTextureTile tile, String mimeType)
      throws URISyntaxException {
    String pathBase = tile.getPath().substring(0, tile.getPath().lastIndexOf("."));
    String suffix = WWIO.makeSuffixForMimeType(mimeType);
    String path = pathBase + suffix;
    URL url = WorldWind.getDataFileStore().findFile(path, false);

    if (url == null) // image is not local
    return null;

    if (WWIO.isFileOutOfDate(url, tile.getLevel().getExpiryTime())) {
      // The file has expired. Delete it.
      WorldWind.getDataFileStore().removeFile(url);
      String message = Logging.getMessage("generic.DataFileExpired", url);
      Logging.logger().fine(message);
    } else {
      try {
        File imageFile = new File(url.toURI());
        BufferedImage image = ImageIO.read(imageFile);
        if (image == null) {
          String message = Logging.getMessage("generic.ImageReadFailed", imageFile);
          throw new RuntimeException(message);
        }

        this.levels.unmarkResourceAbsent(tile);
        return image;
      } catch (IOException e) {
        // Assume that something's wrong with the file and delete it.
        gov.nasa.worldwind.WorldWind.getDataFileStore().removeFile(url);
        this.levels.markResourceAbsent(tile);
        String message = Logging.getMessage("generic.DeletedCorruptDataFile", url);
        Logging.logger().info(message);
      }
    }

    return null;
  }
  public WMSTiledImageLayer(String stateInXml) {
    this(wmsRestorableStateToParams(stateInXml));

    RestorableSupport rs;
    try {
      rs = RestorableSupport.parse(stateInXml);
    } catch (Exception e) {
      // Parsing the document specified by stateInXml failed.
      String message = Logging.getMessage("generic.ExceptionAttemptingToParseStateXml", stateInXml);
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message, e);
    }

    this.doRestoreState(rs, null);
  }
  public Long getLayerLatestLastUpdateTime(WMSCapabilities caps, String[] layerNames) {
    if (caps == null) {
      String message = Logging.getMessage("nullValue.WMSCapabilities");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (layerNames == null) {
      String message = Logging.getMessage("nullValue.WMSLayerNames");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    String lastUpdate = null;

    for (String name : layerNames) {
      WMSLayerCapabilities layer = this.getLayerByName(name);
      if (layer == null) continue;

      String update = layer.getLastUpdate();
      if (update != null
          && update.length() > 0
          && (lastUpdate == null || update.compareTo(lastUpdate) > 0)) lastUpdate = update;
    }

    if (lastUpdate != null) {
      try {
        return Long.parseLong(lastUpdate);
      } catch (NumberFormatException e) {
        String message = Logging.getMessage("generic.ConversionError", lastUpdate);
        Logging.logger().warning(message);
      }
    }

    return null;
  }
  protected static AVList wmsGetParamsFromDocument(Element domElement, AVList params) {
    if (domElement == null) {
      String message = Logging.getMessage("nullValue.DocumentIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (params == null) params = new AVListImpl();

    LayerConfiguration.getWMSTiledImageLayerParams(domElement, params);
    BasicTiledImageLayer.getParamsFromDocument(domElement, params);

    params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder(params));

    return params;
  }
  public MercatorTiledImageLayer(LevelSet levelSet) {
    if (levelSet == null) {
      String message = Logging.getMessage("nullValue.LevelSetIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    this.levels =
        new LevelSet(levelSet); // the caller's levelSet may change internally, so we copy it.

    this.createTopLevelTiles();

    this.setPickEnabled(
        false); // textures are assumed to be terrain unless specifically indicated otherwise.
    this.tileCountName = this.getName() + " Tiles";
  }
  private MercatorTextureTile[][] getTilesInSector(Sector sector, int levelNumber) {
    if (sector == null) {
      String msg = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(msg);
      throw new IllegalArgumentException(msg);
    }

    Level targetLevel = this.levels.getLastLevel();
    if (levelNumber >= 0) {
      for (int i = levelNumber; i < this.getLevels().getLastLevel().getLevelNumber(); i++) {
        if (this.levels.isLevelEmpty(i)) continue;

        targetLevel = this.levels.getLevel(i);
        break;
      }
    }

    // Collect all the tiles intersecting the input sector.
    LatLon delta = targetLevel.getTileDelta();
    Angle latOrigin = this.levels.getTileOrigin().getLatitude();
    Angle lonOrigin = this.levels.getTileOrigin().getLongitude();
    final int nwRow = Tile.computeRow(delta.getLatitude(), sector.getMaxLatitude(), latOrigin);
    final int nwCol = Tile.computeColumn(delta.getLongitude(), sector.getMinLongitude(), lonOrigin);
    final int seRow = Tile.computeRow(delta.getLatitude(), sector.getMinLatitude(), latOrigin);
    final int seCol = Tile.computeColumn(delta.getLongitude(), sector.getMaxLongitude(), lonOrigin);

    int numRows = nwRow - seRow + 1;
    int numCols = seCol - nwCol + 1;
    MercatorTextureTile[][] sectorTiles = new MercatorTextureTile[numRows][numCols];

    for (int row = nwRow; row >= seRow; row--) {
      for (int col = nwCol; col <= seCol; col++) {
        TileKey key =
            new TileKey(targetLevel.getLevelNumber(), row, col, targetLevel.getCacheName());
        Sector tileSector = this.levels.computeSectorForKey(key);
        MercatorSector mSector = MercatorSector.fromSector(tileSector); // TODO: check
        sectorTiles[nwRow - row][col - nwCol] =
            new MercatorTextureTile(mSector, targetLevel, row, col);
      }
    }

    return sectorTiles;
  }
  /**
   * Creates a Collada root for an untyped source. The source must be either a {@link File} or a
   * {@link String} identifying either a file path or a {@link URL}. Null is returned if the source
   * type is not recognized.
   *
   * @param docSource either a {@link File} or a {@link String} identifying a file path or {@link
   *     URL}.
   * @return a new {@link ColladaRoot} for the specified source, or null if the source type is not
   *     supported.
   * @throws IllegalArgumentException if the source is null.
   * @throws IOException if an error occurs while reading the source.
   */
  public static ColladaRoot create(Object docSource) throws IOException {
    if (docSource == null) {
      String message = Logging.getMessage("nullValue.DocumentSourceIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    if (docSource instanceof File) {
      return new ColladaRoot((File) docSource);
    } else if (docSource instanceof URL) {
      return new ColladaRoot((URL) docSource);
    } else if (docSource instanceof String) {
      File file = new File((String) docSource);
      if (file.exists()) return new ColladaRoot(file);

      URL url = WWIO.makeURL(docSource);
      if (url != null) return new ColladaRoot(url);
    } else if (docSource instanceof InputStream) {
      return new ColladaRoot((InputStream) docSource);
    }

    return null;
  }
  public BufferedImage composeImageForSector(
      Sector sector,
      int imageWidth,
      int imageHeight,
      int levelNumber,
      String mimeType,
      boolean abortOnError,
      BufferedImage image) {
    if (sector == null) {
      String message = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(message);
      throw new IllegalStateException(message);
    }

    if (levelNumber < 0) {
      levelNumber = this.levels.getLastLevel().getLevelNumber();
    } else if (levelNumber > this.levels.getLastLevel().getLevelNumber()) {
      Logging.logger()
          .warning(
              Logging.getMessage(
                  "generic.LevelRequestedGreaterThanMaxLevel",
                  levelNumber,
                  this.levels.getLastLevel().getLevelNumber()));
      levelNumber = this.levels.getLastLevel().getLevelNumber();
    }

    MercatorTextureTile[][] tiles = this.getTilesInSector(sector, levelNumber);

    if (tiles.length == 0 || tiles[0].length == 0) {
      Logging.logger().severe(Logging.getMessage("layers.TiledImageLayer.NoImagesAvailable"));
      return null;
    }

    if (image == null)
      image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);

    Graphics2D g = image.createGraphics();

    for (MercatorTextureTile[] row : tiles) {
      for (MercatorTextureTile tile : row) {
        if (tile == null) continue;

        BufferedImage tileImage;
        try {
          tileImage = this.getImage(tile, mimeType);

          double sh =
              ((double) imageHeight / (double) tileImage.getHeight())
                  * (tile.getSector().getDeltaLat().divide(sector.getDeltaLat()));
          double sw =
              ((double) imageWidth / (double) tileImage.getWidth())
                  * (tile.getSector().getDeltaLon().divide(sector.getDeltaLon()));

          double dh =
              imageHeight
                  * (-tile.getSector().getMaxLatitude().subtract(sector.getMaxLatitude()).degrees
                      / sector.getDeltaLat().degrees);
          double dw =
              imageWidth
                  * (tile.getSector().getMinLongitude().subtract(sector.getMinLongitude()).degrees
                      / sector.getDeltaLon().degrees);

          AffineTransform txf = g.getTransform();
          g.translate(dw, dh);
          g.scale(sw, sh);
          g.drawImage(tileImage, 0, 0, null);
          g.setTransform(txf);
        } catch (Exception e) {
          if (abortOnError) throw new RuntimeException(e);

          String message =
              Logging.getMessage("generic.ExceptionWhileRequestingImage", tile.getPath());
          Logging.logger().log(java.util.logging.Level.WARNING, message, e);
        }
      }
    }

    return image;
  }