/**
   * Unzips the sole entry in the specified zip file, and saves it in a temporary directory, and
   * returns a File to the temporary location.
   *
   * @param path the path to the source file.
   * @param suffix the suffix to give the temp file.
   * @return a {@link File} for the temp file.
   * @throws IllegalArgumentException if the <code>path</code> is <code>null</code> or empty.
   */
  public static File unzipAndSaveToTempFile(String path, String suffix) {
    if (WWUtil.isEmpty(path)) {
      String message = Logging.getMessage("nullValue.PathIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    InputStream stream = null;

    try {
      stream = WWIO.openStream(path);

      ByteBuffer buffer = WWIO.readStreamToBuffer(stream);
      File file = WWIO.saveBufferToTempFile(buffer, WWIO.getFilename(path));

      buffer = WWIO.readZipEntryToBuffer(file, null);
      return WWIO.saveBufferToTempFile(buffer, suffix);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      WWIO.closeStream(stream, path);
    }

    return null;
  }
  public static VirtualEarthTile[] createTiles(
      Sector bbox /*int wwLevel, int wwRow, int wwCol*/, VirtualEarthLayer layer)
      throws WWRuntimeException {
    if (null == bbox) {
      String message = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(message);
      throw new WWRuntimeException(message);
    }

    if (null == layer) {
      String message = Logging.getMessage("nullValue.LayerIsNull");
      Logging.logger().severe(message);
      throw new WWRuntimeException(message);
    }

    int level = getZoomLevelByTrueViewRange(bbox.getDeltaLatDegrees());

    Point startPixel =
        LatLongToPixelXY(bbox.getMaxLatitude().degrees, bbox.getMinLongitude().degrees, level);
    Point endPixel =
        LatLongToPixelXY(bbox.getMinLatitude().degrees, bbox.getMaxLongitude().degrees, level);

    Point startTile = PixelXYToTileXY(startPixel.x, startPixel.y);
    Point endTile = PixelXYToTileXY(endPixel.x, endPixel.y);

    ArrayList<VirtualEarthTile> tileList = new ArrayList<VirtualEarthTile>();

    for (int y = startTile.y; y <= endTile.y; y++) {
      for (int x = startTile.x; x <= endTile.x; x++) {
        try {
          int ulPixelX = x * VE_MAX_TILE_SIZE;
          int ulPixelY = y * VE_MAX_TILE_SIZE;
          LatLon ul = PixelXYToLatLong(ulPixelX, ulPixelY, level);

          int lrPixelX = ulPixelX + VE_MAX_TILE_SIZE;
          int lrPixelY = ulPixelY + VE_MAX_TILE_SIZE;
          LatLon lr = PixelXYToLatLong(lrPixelX, lrPixelY, level);

          Sector tileSector = Sector.boundingSector(ul, lr);

          tileList.add(new VirtualEarthTile(x, y, level, layer, tileSector));
        } catch (Exception ex) {
          Logging.logger().log(Level.SEVERE, ex.getMessage(), ex);
        }
      }
    }

    VirtualEarthTile[] tiles = new VirtualEarthTile[tileList.size()];
    return tileList.toArray(tiles);
  }
  /**
   * Create a list of layers described by an array of XML layer description elements.
   *
   * <p>Any exceptions occurring during creation of the layers are logged and not re-thrown. The
   * layers associated with the exceptions are not included in the returned layer list.
   *
   * @param layerElements the XML elements describing the layers to create.
   * @param params any parameters to apply when creating the layers.
   * @return a layer list containing the specified layers.
   */
  protected LayerList createLayerList(Element[] layerElements, AVList params) {
    LayerList layerList = new LayerList();

    for (Element element : layerElements) {
      try {
        layerList.add(this.createFromLayerDocument(element, params));
      } catch (Exception e) {
        Logging.logger().log(java.util.logging.Level.WARNING, e.getMessage(), e);
        // keep going to create other layers
      }
    }

    return layerList;
  }
  /**
   * Create a collection of layer lists and their included layers described by an array of XML
   * layer-list description elements.
   *
   * <p>Any exceptions occurring during creation of the layer lists or their included layers are
   * logged and not re-thrown. The layers associated with the exceptions are not included in the
   * returned layer list.
   *
   * @param elements the XML elements describing the layer lists to create.
   * @param params any parameters to apply when creating the included layers.
   * @return an array containing the specified layer lists.
   */
  protected LayerList[] createLayerLists(Element[] elements, AVList params) {
    ArrayList<LayerList> layerLists = new ArrayList<LayerList>();

    for (Element element : elements) {
      try {
        String href = WWXML.getText(element, "@href");
        if (href != null && href.length() > 0) {
          Object o = this.createFromConfigSource(href, params);
          if (o == null) continue;

          if (o instanceof Layer) {
            LayerList ll = new LayerList();
            ll.add((Layer) o);
            o = ll;
          }

          if (o instanceof LayerList) {
            LayerList list = (LayerList) o;
            if (list != null && list.size() > 0) layerLists.add(list);
          } else if (o instanceof LayerList[]) {
            LayerList[] lists = (LayerList[]) o;
            if (lists != null && lists.length > 0) layerLists.addAll(Arrays.asList(lists));
          } else {
            String msg =
                Logging.getMessage("LayerFactory.UnexpectedTypeForLayer", o.getClass().getName());
            Logging.logger().log(java.util.logging.Level.WARNING, msg);
          }

          continue;
        }

        String title = WWXML.getText(element, "@title");
        Element[] children = WWXML.getElements(element, "./Layer", null);
        if (children != null && children.length > 0) {
          LayerList list = this.createLayerList(children, params);
          if (list != null && list.size() > 0) {
            layerLists.add(list);
            if (title != null && title.length() > 0) list.setValue(AVKey.DISPLAY_NAME, title);
          }
        }
      } catch (Exception e) {
        Logging.logger().log(java.util.logging.Level.WARNING, e.getMessage(), e);
        // keep going to create other layers
      }
    }

    return layerLists.toArray(new LayerList[layerLists.size()]);
  }
  private BufferWrapperRaster readTileRaster(MeshTile tile) {
    File file = new File(this.dataDescriptor.getFileStoreLocation(), tile.getPath());
    if (!file.exists()) return null;

    DataSource source = new BasicDataSource(file);
    source.setValue(AVKey.SECTOR, tile.getSector());

    BILRasterReader reader = new BILRasterReader();
    DataRaster[] rasters;
    try {
      rasters = reader.read(source);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }

    return (BufferWrapperRaster) rasters[0];
  }
  public boolean initialize(MapSource mapSource) throws IOException, WMSServiceException {
    if (null == mapSource) {
      String msg = Logging.getMessage("nullValue.MapSourceIsNull");
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }

    this.mapSource = mapSource;

    this.params = mapSource.getParameters();
    if (null == params) {
      String msg = Logging.getMessage("nullValue.AVListIsNull");
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }

    if (!params.hasKey(AVKey.FILE_NAME)) {
      String msg = Logging.getMessage("nullValue.ParamsIsNull");
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }

    this.sourceFile = new File(params.getStringValue(AVKey.FILE_NAME));
    if (!this.sourceFile.exists()) {
      String msg = Logging.getMessage("generic.FileNotFound", this.sourceFile.getAbsolutePath());
      Logging.logger().severe(msg);
      throw new FileNotFoundException(msg);
    }

    AVList fileParams = this.params.copy();

    try {
      this.readerFactory =
          (DataRasterReaderFactory)
              WorldWind.createConfigurationComponent(AVKey.DATA_RASTER_READER_FACTORY_CLASS_NAME);
    } catch (Exception e) {
      this.readerFactory = new BasicDataRasterReaderFactory();
    }
    DataRasterReader reader =
        this.readerFactory.findReaderFor(this.sourceFile, fileParams, readers);
    if (reader == null) {
      String msg = Logging.getMessage("nullValue.ReaderIsNull", this.sourceFile);
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }

    reader.readMetadata(this.sourceFile, fileParams);

    this.params.setValues(fileParams);

    if (!this.params.hasKey(AVKey.SECTOR)) {
      String msg = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }
    this.BBOX = (Sector) this.params.getValue(AVKey.SECTOR);

    if (0d == this.BBOX.getDeltaLatDegrees() || 0d == this.BBOX.getDeltaLonDegrees()) {
      String msg = Logging.getMessage("generic.SectorSizeInvalid");
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }

    int height = 0;
    if (!this.params.hasKey(AVKey.HEIGHT)) {
      String msg = Logging.getMessage("generic.InvalidHeight", 0);
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    } else {
      Object o = this.params.getValue(AVKey.HEIGHT);
      double d = Double.parseDouble("" + o);
      height = (int) d;
    }

    if (!this.params.hasKey(AVKey.WIDTH)) {
      String msg = Logging.getMessage("generic.InvalidWidth", 0);
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }

    this.isElevation =
        (this.params.hasKey(AVKey.PIXEL_FORMAT)
            && AVKey.ELEVATION.equals(this.params.getValue(AVKey.PIXEL_FORMAT)));

    if (this.params.hasKey(AVKey.MISSING_DATA_SIGNAL)) {
      try {
        Object o = this.params.getValue(AVKey.MISSING_DATA_SIGNAL);
        double d = Double.parseDouble("" + o);
        this.nodataSignal = (short) d;
      } catch (Exception e) {
        this.nodataSignal = (this.isElevation) ? Short.MIN_VALUE : 0;
      }
    } else {
      this.nodataSignal = (this.isElevation) ? Short.MIN_VALUE : 0;
    }

    if (this.params.hasKey(AVKey.MISSING_DATA_REPLACEMENT)) {
      try {
        Object o = this.params.getValue(AVKey.MISSING_DATA_REPLACEMENT);
        double d = Double.parseDouble("" + o);
        this.nodataReplacement = (short) d;
      } catch (Exception e) {
        Logging.logger().finest(e.getMessage());
        this.nodataReplacement = (this.isElevation) ? Short.MIN_VALUE : 0;
      }
    } else {
      this.nodataReplacement = (this.isElevation) ? Short.MIN_VALUE : 0;
    }

    if (this.isElevation) {
      if (this.params.hasKey(AVKey.ELEVATION_UNIT)) {
        try {
          String unit = this.params.getStringValue(AVKey.ELEVATION_UNIT);
          this.convertFeetToMeters = "feet".equalsIgnoreCase(unit);
        } catch (Exception e) {
          Logging.logger().finest(e.getMessage());
        }
      }
    }

    // if PIXEL_HEIGHT is specified, we are not overriding it
    // because UTM images will have different pixel size
    if (!this.params.hasKey(AVKey.PIXEL_HEIGHT)) {
      this.pixelHeight = this.BBOX.getDeltaLatDegrees() / (double) height;
    } else {
      try {
        Object o = this.params.getValue(AVKey.PIXEL_HEIGHT);
        this.pixelHeight = Double.parseDouble("" + o);
      } catch (Exception e) {
        Logging.logger().finest(e.getMessage());
      }
    }

    this.rasters = reader.read(this.sourceFile, this.params);

    if (null == this.rasters || 0 == this.rasters.length) {
      String msg = Logging.getMessage("nullValue.RasterIsNull");
      Logging.logger().severe(msg);
      throw new WMSServiceException(msg);
    }

    return true;
  }
    public ImageFormatter serviceRequest(IMapRequest req) throws IOException, WMSServiceException {
      ImageFormatter formatter = null;
      try {
        Sector reqExtent =
            (SingleFileLayer.this.isElevation)
                ? req.getExtentForElevationRequest()
                : req.getExtent();

        if (null == this.intersects(reqExtent, SingleFileLayer.this.getBBox())) {
          String msg =
              Logging.getMessage(
                  "WMS.Layer.OutOfCoverage",
                  reqExtent.toString(),
                  SingleFileLayer.this.getBBox().toString());
          Logging.logger().severe(msg);
          throw new WMSServiceException(msg);
        }

        if (req.getHeight() <= 0 || req.getWidth() <= 0) {
          String msg =
              Logging.getMessage("generic.InvalidImageSize", req.getWidth(), req.getHeight());
          Logging.logger().severe(msg);
          throw new WMSServiceException(msg);
        }

        DataRaster[] rasters = SingleFileLayer.this.rasters;
        if (null == rasters || 0 == rasters.length) {
          String msg = Logging.getMessage("nullValue.RasterIsNull");
          Logging.logger().severe(msg);
          throw new WMSServiceException(msg);
        }

        double missingDataReplacement = (double) SingleFileLayer.this.nodataReplacement;
        try {
          String s = req.getBGColor();
          if (null != s) {
            missingDataReplacement = Double.parseDouble(s);
          }
        } catch (Exception e) {
          missingDataReplacement = (double) SingleFileLayer.this.nodataReplacement;
        }

        DataRaster raster = rasters[0];
        if (raster instanceof BufferedImageRaster) {
          BufferedImageRaster reqRaster =
              new BufferedImageRaster(
                  req.getWidth(), req.getHeight(), Transparency.TRANSLUCENT, reqExtent);

          raster.drawOnTo(reqRaster);

          BufferedImage img = reqRaster.getBufferedImage();
          this.makeNoDataTransparent(
              img, SingleFileLayer.this.nodataSignal, (short) missingDataReplacement);

          formatter = new BufferedImageFormatter(img);
        } else if (raster instanceof ByteBufferRaster) {
          AVList reqParams = new AVListImpl();

          reqParams.setValue(AVKey.WIDTH, req.getWidth());
          reqParams.setValue(AVKey.HEIGHT, req.getHeight());
          reqParams.setValue(AVKey.SECTOR, reqExtent);
          reqParams.setValue(
              AVKey.BYTE_ORDER, AVKey.LITTLE_ENDIAN); // by default BIL is LITTLE ENDIAN
          reqParams.setValue(AVKey.PIXEL_FORMAT, AVKey.ELEVATION);

          String reqFormat = req.getFormat();
          if (null != reqFormat && reqFormat.endsWith("32")) {
            reqParams.setValue(AVKey.DATA_TYPE, AVKey.FLOAT32);
          } else {
            reqParams.setValue(AVKey.DATA_TYPE, AVKey.INT16);
          }
          reqParams.setValue(AVKey.MISSING_DATA_REPLACEMENT, missingDataReplacement);

          ByteBufferRaster reqRaster =
              new ByteBufferRaster(req.getWidth(), req.getHeight(), reqExtent, reqParams);

          raster.drawOnTo(reqRaster);

          if (SingleFileLayer.this.convertFeetToMeters) {
            this.convertFeetToMeters(reqRaster);
          }

          formatter = new DataRasterFormatter(reqRaster);
        } else {
          String msg =
              Logging.getMessage(
                  "generic.UnrecognizedImageSourceType", raster.getClass().getName());
          Logging.logger().severe(msg);
          throw new WMSServiceException(msg);
        }
      } catch (WMSServiceException wmsse) {
        throw wmsse;
      } catch (Exception ex) {
        Logging.logger()
            .log(
                java.util.logging.Level.SEVERE,
                SingleFileLayer.this.getThreadId() + ex.getMessage(),
                ex);
        // throw new WMSServiceException( s );
      } finally {
      }

      return formatter;
    }