public static HashMap<String, Double> getRegionParamsFromGridCoverage(
      GridCoverage2D gridCoverage) {
    HashMap<String, Double> envelopeParams = new HashMap<String, Double>();

    Envelope envelope = gridCoverage.getEnvelope();

    DirectPosition lowerCorner = envelope.getLowerCorner();
    double[] westSouth = lowerCorner.getCoordinate();
    DirectPosition upperCorner = envelope.getUpperCorner();
    double[] eastNorth = upperCorner.getCoordinate();

    GridGeometry2D gridGeometry = gridCoverage.getGridGeometry();
    GridEnvelope2D gridRange = gridGeometry.getGridRange2D();
    int height = gridRange.height;
    int width = gridRange.width;

    AffineTransform gridToCRS = (AffineTransform) gridGeometry.getGridToCRS();
    double xRes = XAffineTransform.getScaleX0(gridToCRS);
    double yRes = XAffineTransform.getScaleY0(gridToCRS);

    envelopeParams.put(NORTH, eastNorth[1]);
    envelopeParams.put(SOUTH, westSouth[1]);
    envelopeParams.put(WEST, westSouth[0]);
    envelopeParams.put(EAST, eastNorth[0]);
    envelopeParams.put(XRES, xRes);
    envelopeParams.put(YRES, yRes);
    envelopeParams.put(ROWS, (double) height);
    envelopeParams.put(COLS, (double) width);

    return envelopeParams;
  }
Exemplo n.º 2
0
 private void worldToGrid(Coordinate c, int i)
     throws InvalidGridGeometryException, TransformException {
   DirectPosition2D world = new DirectPosition2D(c.x, c.y);
   GridCoordinates2D grid = gridGeometry.worldToGrid(world);
   px[i] = grid.x;
   py[i] = grid.y;
 }
Exemplo n.º 3
0
  private void storeResult(
      double[] interpolatedValues, HashMap<Integer, Coordinate> interpolatedCoordinatesMap)
      throws MismatchedDimensionException, Exception {

    WritableRandomIter outIter = RandomIterFactory.createWritable(outWR, null);

    Set<Integer> pointsToInterpolateIdSett = interpolatedCoordinatesMap.keySet();
    Iterator<Integer> idIterator = pointsToInterpolateIdSett.iterator();
    int c = 0;
    MathTransform transf = inInterpolationGrid.getCRSToGrid2D();

    final DirectPosition gridPoint = new DirectPosition2D();

    while (idIterator.hasNext()) {
      int id = idIterator.next();
      Coordinate coordinate = (Coordinate) interpolatedCoordinatesMap.get(id);

      DirectPosition point =
          new DirectPosition2D(
              inInterpolationGrid.getCoordinateReferenceSystem(), coordinate.x, coordinate.y);
      transf.transform(point, gridPoint);

      double[] gridCoord = gridPoint.getCoordinate();
      int x = (int) gridCoord[0];
      int y = (int) gridCoord[1];

      outIter.setSample(x, y, 0, checkResultValue(interpolatedValues[c]));
      c++;
    }

    RegionMap regionMap = CoverageUtilities.gridGeometry2RegionParamsMap(inInterpolationGrid);

    outGrid =
        CoverageUtilities.buildCoverage(
            "gridded", outWR, regionMap, inInterpolationGrid.getCoordinateReferenceSystem());
  }
Exemplo n.º 4
0
  /**
   * Applies the band select operation to a grid coverage.
   *
   * @param cropEnvelope the target envelope; always not null
   * @param cropROI the target ROI shape; nullable
   * @param roiTolerance; as read from op's params
   * @param sourceCoverage is the source {@link GridCoverage2D} that we want to crop.
   * @param hints A set of rendering hints, or {@code null} if none.
   * @param sourceGridToWorldTransform is the 2d grid-to-world transform for the source coverage.
   * @return The result as a grid coverage.
   */
  private static GridCoverage2D buildResult(
      final GeneralEnvelope cropEnvelope,
      final Geometry cropROI,
      final double roiTolerance,
      final boolean forceMosaic,
      final Hints hints,
      final GridCoverage2D sourceCoverage,
      final AffineTransform sourceGridToWorldTransform) {

    //
    // Getting the source coverage and its child geolocation objects
    //
    final RenderedImage sourceImage = sourceCoverage.getRenderedImage();
    final GridGeometry2D sourceGridGeometry = ((GridGeometry2D) sourceCoverage.getGridGeometry());
    final GridEnvelope2D sourceGridRange = sourceGridGeometry.getGridRange2D();

    //
    // Now we try to understand if we have a simple scale and translate or a
    // more elaborated grid-to-world transformation n which case a simple
    // crop could not be enough, but we may need a more elaborated chain of
    // operation in order to do a good job. As an instance if we
    // have a rotation which is not multiple of PI/2 we have to use
    // the mosaic with a ROI
    //
    final boolean isSimpleTransform =
        CoverageUtilities.isSimpleGridToWorldTransform(sourceGridToWorldTransform, EPS);

    // Do we need to explode the Palette to RGB(A)?
    //
    int actionTaken = 0;

    // //
    //
    // Layout
    //
    // //
    final RenderingHints targetHints = new RenderingHints(null);
    if (hints != null) targetHints.add(hints);
    final ImageLayout layout = initLayout(sourceImage, targetHints);
    targetHints.put(JAI.KEY_IMAGE_LAYOUT, layout);

    //
    // prepare the processor to use for this operation
    //
    final JAI processor = OperationJAI.getJAI(targetHints);
    final boolean useProvidedProcessor = !processor.equals(JAI.getDefaultInstance());

    try {

      if (cropROI != null) {
        // replace the cropEnvelope with the envelope of the intersection
        // of the ROI and the cropEnvelope.
        // Remember that envelope(intersection(roi,cropEnvelope)) != intersection(cropEnvelope,
        // envelope(roi))
        final Polygon modelSpaceROI = FeatureUtilities.getPolygon(cropEnvelope, GFACTORY);
        Geometry intersection = IntersectUtils.intersection(cropROI, modelSpaceROI);
        Envelope2D e2d =
            JTS.getEnvelope2D(
                intersection.getEnvelopeInternal(), cropEnvelope.getCoordinateReferenceSystem());
        GeneralEnvelope ge = new GeneralEnvelope((org.opengis.geometry.Envelope) e2d);
        cropEnvelope.setEnvelope(ge);
      }

      // //
      //
      // Build the new range by keeping into
      // account translation of grid geometry constructor for respecting
      // OGC PIXEL-IS-CENTER ImageDatum assumption.
      //
      // //
      final AffineTransform sourceWorldToGridTransform = sourceGridToWorldTransform.createInverse();

      // //
      //
      // finalRasterArea will hold the smallest rectangular integer raster area that contains the
      // floating point raster
      // area which we obtain when applying the world-to-grid transform to the cropEnvelope. Note
      // that we need to intersect
      // such an area with the area covered by the source coverage in order to be sure we do not try
      // to crop outside the
      // bounds of the source raster.
      //
      // //
      final Rectangle2D finalRasterAreaDouble =
          XAffineTransform.transform(
              sourceWorldToGridTransform, cropEnvelope.toRectangle2D(), null);
      final Rectangle finalRasterArea = finalRasterAreaDouble.getBounds();

      // intersection with the original range in order to not try to crop outside the image bounds
      Rectangle.intersect(finalRasterArea, sourceGridRange, finalRasterArea);
      if (finalRasterArea.isEmpty())
        throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP));

      // //
      //
      // It is worth to point out that doing a crop the G2W transform
      // should not change while the envelope might change as
      // a consequence of the rounding of the underlying image datum
      // which uses integer factors or in case the G2W is very
      // complex. Note that we will always strive to
      // conserve the original grid-to-world transform.
      //
      // //

      // we do not have to crop in this case (should not really happen at
      // this time)
      if (finalRasterArea.equals(sourceGridRange) && isSimpleTransform && cropROI == null)
        return sourceCoverage;

      // //
      //
      // if I get here I have something to crop
      // using the world-to-grid transform for going from envelope to the
      // new grid range.
      //
      // //
      final double minX = finalRasterArea.getMinX();
      final double minY = finalRasterArea.getMinY();
      final double width = finalRasterArea.getWidth();
      final double height = finalRasterArea.getHeight();

      // //
      //
      // Check if we need to use mosaic or crop
      //
      // //
      final PlanarImage croppedImage;
      final ParameterBlock pbj = new ParameterBlock();
      pbj.addSource(sourceImage);
      java.awt.Polygon rasterSpaceROI = null;
      String operatioName = null;
      if (!isSimpleTransform || cropROI != null) {
        // /////////////////////////////////////////////////////////////////////
        //
        // We don't have a simple scale and translate transform, JAI
        // crop MAY NOT suffice. Let's decide whether or not we'll use
        // the Mosaic.
        //
        // /////////////////////////////////////////////////////////////////////
        Polygon modelSpaceROI = FeatureUtilities.getPolygon(cropEnvelope, GFACTORY);

        // //
        //
        // Now convert this polygon back into a shape for the source
        // raster space.
        //
        // //
        final List<Point2D> points = new ArrayList<Point2D>(5);
        rasterSpaceROI =
            FeatureUtilities.convertPolygonToPointArray(
                modelSpaceROI, ProjectiveTransform.create(sourceWorldToGridTransform), points);
        if (rasterSpaceROI == null || rasterSpaceROI.getBounds().isEmpty())
          if (finalRasterArea.isEmpty())
            throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP));
        final boolean doMosaic =
            forceMosaic
                ? true
                : decideJAIOperation(roiTolerance, rasterSpaceROI.getBounds2D(), points);
        if (doMosaic || cropROI != null) {
          // prepare the params for the mosaic
          final ROI[] roiarr;
          try {
            if (cropROI != null) {
              final LiteShape2 cropRoiLS2 =
                  new LiteShape2(
                      cropROI, ProjectiveTransform.create(sourceWorldToGridTransform), null, false);
              ROI cropRS = new ROIShape(cropRoiLS2);
              Rectangle2D rt = cropRoiLS2.getBounds2D();
              if (!hasIntegerBounds(rt)) {
                // Approximate Geometry
                Geometry geo = (Geometry) cropRoiLS2.getGeometry().clone();
                transformGeometry(geo);
                cropRS = new ROIShape(new LiteShape2(geo, null, null, false));
              }
              roiarr = new ROI[] {cropRS};
            } else {
              final ROIShape roi = new ROIShape(rasterSpaceROI);
              roiarr = new ROI[] {roi};
            }
          } catch (FactoryException ex) {
            throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP), ex);
          }
          pbj.add(MosaicDescriptor.MOSAIC_TYPE_OVERLAY);
          pbj.add(null);
          pbj.add(roiarr);
          pbj.add(null);
          pbj.add(CoverageUtilities.getBackgroundValues(sourceCoverage));

          // prepare the final layout
          final Rectangle bounds = rasterSpaceROI.getBounds2D().getBounds();
          Rectangle.intersect(bounds, sourceGridRange, bounds);
          if (bounds.isEmpty()) throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP));

          // we do not have to crop in this case (should not really happen at
          // this time)
          if (!doMosaic && bounds.getBounds().equals(sourceGridRange) && isSimpleTransform)
            return sourceCoverage;

          // nice trick, we use the layout to do the actual crop
          final Rectangle boundsInt = bounds.getBounds();
          layout.setMinX(boundsInt.x);
          layout.setWidth(boundsInt.width);
          layout.setMinY(boundsInt.y);
          layout.setHeight(boundsInt.height);
          operatioName = "Mosaic";
        }
      }

      // do we still have to set the operation name? If so that means we have to go for crop.
      if (operatioName == null) {
        // executing the crop
        pbj.add((float) minX);
        pbj.add((float) minY);
        pbj.add((float) width);
        pbj.add((float) height);
        operatioName = "GTCrop";
      }
      // //
      //
      // Apply operation
      //
      // //
      if (!useProvidedProcessor) {
        croppedImage = JAI.create(operatioName, pbj, targetHints);
      } else {
        croppedImage = processor.createNS(operatioName, pbj, targetHints);
      }

      // conserve the input grid to world transformation
      Map sourceProperties = sourceCoverage.getProperties();
      Map properties = null;
      if (sourceProperties != null && !sourceProperties.isEmpty()) {
        properties = new HashMap(sourceProperties);
      }
      if (rasterSpaceROI != null) {
        if (properties != null) {
          properties.put("GC_ROI", rasterSpaceROI);
        } else {
          properties = Collections.singletonMap("GC_ROI", rasterSpaceROI);
        }
      }

      return new GridCoverageFactory(hints)
          .create(
              sourceCoverage.getName(),
              croppedImage,
              new GridGeometry2D(
                  new GridEnvelope2D(croppedImage.getBounds()),
                  sourceGridGeometry.getGridToCRS2D(PixelOrientation.CENTER),
                  sourceCoverage.getCoordinateReferenceSystem()),
              (GridSampleDimension[])
                  (actionTaken == 1 ? null : sourceCoverage.getSampleDimensions().clone()),
              new GridCoverage[] {sourceCoverage},
              properties);

    } catch (TransformException e) {
      throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP), e);
    } catch (NoninvertibleTransformException e) {
      throw new CannotCropException(Errors.format(ErrorKeys.CANT_CROP), e);
    }
  }
Exemplo n.º 5
0
  /**
   * This method reads in the TIFF image, constructs an appropriate CRS, determines the math
   * transform from raster to the CRS model, and constructs a GridCoverage.
   *
   * @param params currently ignored, potentially may be used for hints.
   * @return grid coverage represented by the image
   * @throws IOException on any IO related troubles
   */
  public GridCoverage2D read(GeneralParameterValue[] params) throws IOException {
    GeneralEnvelope requestedEnvelope = null;
    Rectangle dim = null;
    Color inputTransparentColor = null;
    OverviewPolicy overviewPolicy = null;
    int[] suggestedTileSize = null;
    if (params != null) {

      //
      // Checking params
      //
      if (params != null) {
        for (int i = 0; i < params.length; i++) {
          final ParameterValue param = (ParameterValue) params[i];
          final ReferenceIdentifier name = param.getDescriptor().getName();
          if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName())) {
            final GridGeometry2D gg = (GridGeometry2D) param.getValue();
            requestedEnvelope = new GeneralEnvelope((Envelope) gg.getEnvelope2D());
            dim = gg.getGridRange2D().getBounds();
            continue;
          }
          if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName())) {
            overviewPolicy = (OverviewPolicy) param.getValue();
            continue;
          }
          if (name.equals(AbstractGridFormat.INPUT_TRANSPARENT_COLOR.getName())) {
            inputTransparentColor = (Color) param.getValue();
            continue;
          }
          if (name.equals(AbstractGridFormat.SUGGESTED_TILE_SIZE.getName())) {
            String suggestedTileSize_ = (String) param.getValue();
            if (suggestedTileSize_ != null && suggestedTileSize_.length() > 0) {
              suggestedTileSize_ = suggestedTileSize_.trim();
              int commaPosition = suggestedTileSize_.indexOf(",");
              if (commaPosition < 0) {
                int tileDim = Integer.parseInt(suggestedTileSize_);
                suggestedTileSize = new int[] {tileDim, tileDim};
              } else {
                int tileW = Integer.parseInt(suggestedTileSize_.substring(0, commaPosition));
                int tileH = Integer.parseInt(suggestedTileSize_.substring(commaPosition + 1));
                suggestedTileSize = new int[] {tileW, tileH};
              }
            }
            continue;
          }
        }
      }
    }

    //
    // set params
    //
    Integer imageChoice = new Integer(0);
    final ImageReadParam readP = new ImageReadParam();
    try {
      imageChoice = setReadParams(overviewPolicy, readP, requestedEnvelope, dim);
    } catch (TransformException e) {
      new DataSourceException(e);
    }

    //
    // IMAGE READ OPERATION
    //
    Hints newHints = null;
    if (suggestedTileSize != null) {
      newHints = hints.clone();
      final ImageLayout layout = new ImageLayout();
      layout.setTileGridXOffset(0);
      layout.setTileGridYOffset(0);
      layout.setTileHeight(suggestedTileSize[1]);
      layout.setTileWidth(suggestedTileSize[0]);
      newHints.add(new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout));
    }
    final ParameterBlock pbjRead = new ParameterBlock();
    if (extOvrImgChoice >= 0 && imageChoice >= extOvrImgChoice) {
      pbjRead.add(
          ovrInStreamSPI.createInputStreamInstance(
              ovrSource, ImageIO.getUseCache(), ImageIO.getCacheDirectory()));
      pbjRead.add(imageChoice - extOvrImgChoice);
    } else {
      pbjRead.add(
          inStreamSPI != null
              ? inStreamSPI.createInputStreamInstance(
                  source, ImageIO.getUseCache(), ImageIO.getCacheDirectory())
              : ImageIO.createImageInputStream(source));
      pbjRead.add(imageChoice);
    }
    pbjRead.add(Boolean.FALSE);
    pbjRead.add(Boolean.FALSE);
    pbjRead.add(Boolean.FALSE);
    pbjRead.add(null);
    pbjRead.add(null);
    pbjRead.add(readP);
    pbjRead.add(READER_SPI.createReaderInstance());
    RenderedOp coverageRaster =
        JAI.create("ImageRead", pbjRead, newHints != null ? (RenderingHints) newHints : null);

    //
    // MASKING INPUT COLOR as indicated
    //
    if (inputTransparentColor != null) {
      coverageRaster =
          new ImageWorker(coverageRaster)
              .setRenderingHints(newHints)
              .makeColorTransparent(inputTransparentColor)
              .getRenderedOperation();
    }

    AffineTransform rasterToModel = getRescaledRasterToModel(coverageRaster);
    try {
      return createCoverage(coverageRaster, ProjectiveTransform.create(rasterToModel));
    } catch (Exception e) {
      // dispose and close file
      ImageUtilities.disposePlanarImageChain(coverageRaster);

      // rethrow
      if (e instanceof DataSourceException) {
        throw (DataSourceException) e;
      }
      throw new DataSourceException(e);
    }
  }
Exemplo n.º 6
0
    private void initialize() {

      AttributeDescriptor attributeDescriptor =
          featureCollection.getSchema().getDescriptor(attributeName);
      if (attributeDescriptor == null) {
        throw new RuntimeException(attributeName + " not found");
      }

      Class<?> attClass = attributeDescriptor.getType().getBinding();
      if (!Number.class.isAssignableFrom(attClass)) {
        throw new RuntimeException(attributeName + " is not numeric type");
      }

      try {
        checkTransform();

        gridGeometry =
            new GridGeometry2D(
                new GridEnvelope2D(0, 0, coverageWidth, coverageHeight), coverageEnvelope);

        // NOTE:  assumes transformation results in equal length scales across both axes!
        if (mimimumLengthPixels > 0) {
          Point2D s0 = new Point(0, 0);
          Point2D d0 = gridGeometry.getGridToCRS2D().transform(s0, null);
          Point2D s1 = new Point(mimimumLengthPixels, 0);
          Point2D d1 = gridGeometry.getGridToCRS2D().transform(s1, null);
          minimumLengthMeters = d1.distance(d0);
        } else {
          minimumLengthMeters = -1;
        }

      } catch (TransformException ex) {
        throw new RuntimeException("Unable to transform", ex);
      }

      createImage();

      String featureCollectionId = featureCollection.getSchema().getName().getURI();

      baselineFeaturesMap = new LinkedHashMap<Integer, LinkedList<SimpleFeature>>();

      AttributeRange attributeRange = null;

      LOGGER.log(
          Level.INFO,
          "Calculating attribute value range for {}:{}",
          new Object[] {featureCollectionId, attributeName});
      SimpleFeatureIterator iterator = null;
      try {
        iterator = featureCollection.features();
        double minimum = Double.MAX_VALUE;
        double maximum = -Double.MAX_VALUE;
        while (iterator.hasNext()) {
          SimpleFeature feature = iterator.next();
          double value = ((Number) feature.getAttribute(attributeName)).doubleValue();
          if (Math.abs(value) < 1e10) {
            if (value > maximum) {
              maximum = value;
            }
            if (value < minimum) {
              minimum = value;
            }
          }

          Object baselineIdObject = feature.getAttribute(Constants.BASELINE_ID_ATTR);
          // this is needed for older files w/o baseline ID, null is a valid map key
          Integer baselineId =
              baselineIdObject instanceof Number ? ((Number) baselineIdObject).intValue() : null;
          LinkedList<SimpleFeature> baselineFeatures = baselineFeaturesMap.get(baselineId);
          if (baselineFeatures == null) {
            baselineFeatures = new LinkedList<SimpleFeature>();
            baselineFeaturesMap.put(baselineId, baselineFeatures);
          }
          baselineFeatures.add(feature);
        }
        if (minimum < maximum) {
          attributeRange = new AttributeRange(minimum, maximum);
          LOGGER.log(
              Level.INFO,
              "Attribute value range for {}:{} {}",
              new Object[] {featureCollectionId, attributeName, attributeRange});
        }
      } finally {
        if (iterator != null) {
          iterator.close();
        }
      }
      if (attributeRange != null) {
        attributeRange =
            (attributeRange.min < 0)
                ? attributeRange.zeroInflect(invert)
                : invert
                    ? new AttributeRange(attributeRange.max, 0)
                    : new AttributeRange(0, attributeRange.max);
        colorMap = new JetColorMap(attributeRange);
      }
    }
 public void setRequestedGridGeometry(GridGeometry2D gridGeometry) {
   Utilities.ensureNonNull("girdGeometry", gridGeometry);
   requestedBBox = new ReferencedEnvelope((Envelope) gridGeometry.getEnvelope2D());
   requestedRasterArea = gridGeometry.getGridRange2D().getBounds();
   requestedGridToWorld = (AffineTransform) gridGeometry.getGridToCRS2D();
 }
    /**
     * Encodes the DomainSet as per the GML spec of the provided {@link GridCoverage2D}
     *
     * <p>e.g.:
     *
     * <pre>{@code
     * <gml:domainSet>
     *    <gml:Grid gml:id="gr0001_C0001" dimension="2">
     *       <gml:limits>
     *          <gml:GridEnvelope>
     *             <gml:low>1 1</gml:low>
     *             <gml:high>5 3</gml:high>
     *          </gml:GridEnvelope>
     *       </gml:limits>
     *       <gml:axisLabels>Lat Long</gml:axisLabels>
     *    </gml:Grid>
     * </gml:domainSet>
     * }</pre>
     *
     * @param gc2d the {@link GridCoverage2D} for which to encode the DomainSet.
     * @param srsName
     * @param axesSwap
     */
    public void handleDomainSet(
        GridGeometry2D gg2D, int gridDimension, String gcName, String srsName, boolean axesSwap) {
      // setup vars
      final String gridId = "grid00__" + gcName;

      // Grid Envelope
      final GridEnvelope gridEnvelope = gg2D.getGridRange();

      final StringBuilder lowSb = new StringBuilder();
      for (int i : gridEnvelope.getLow().getCoordinateValues()) {
        lowSb.append(i).append(' ');
      }
      final StringBuilder highSb = new StringBuilder();
      for (int i : gridEnvelope.getHigh().getCoordinateValues()) {
        highSb.append(i).append(' ');
      }

      // build the fragment
      final AttributesImpl gridAttrs = new AttributesImpl();
      gridAttrs.addAttribute("", "gml:id", "gml:id", "", gridId);
      gridAttrs.addAttribute("", "dimension", "dimension", "", String.valueOf(gridDimension));

      start("gml:domainSet");
      start("gml:RectifiedGrid", gridAttrs);
      start("gml:limits");

      // GridEnvelope
      start("gml:GridEnvelope");
      element("gml:low", lowSb.toString().trim());
      element("gml:high", highSb.toString().trim());
      end("gml:GridEnvelope");

      end("gml:limits");

      // Axis Label
      element("gml:axisLabels", "i j");

      final MathTransform2D transform = gg2D.getGridToCRS2D(PixelOrientation.CENTER);
      if (!(transform instanceof AffineTransform2D)) {
        throw new IllegalStateException("Invalid grid to worl provided:" + transform.toString());
      }
      final AffineTransform2D g2W = (AffineTransform2D) transform;

      // Origin
      // we use ULC as per our G2W transformation
      final AttributesImpl pointAttr = new AttributesImpl();
      pointAttr.addAttribute("", "gml:id", "gml:id", "", "p00_" + gcName);
      pointAttr.addAttribute("", "srsName", "srsName", "", srsName);
      start("gml:origin");
      start("gml:Point", pointAttr);
      element(
          "gml:pos",
          axesSwap
              ? g2W.getTranslateY() + " " + g2W.getTranslateX()
              : g2W.getTranslateX() + " " + g2W.getTranslateY());
      end("gml:Point");
      end("gml:origin");

      // Offsets
      final AttributesImpl offsetAttr = new AttributesImpl();
      offsetAttr.addAttribute("", "srsName", "srsName", "", srsName);

      // notice the orientation of the transformation I create. The origin of the coordinates
      // in this grid is not at UPPER LEFT like in our grid to world but at LOWER LEFT !!!
      element(
          "gml:offsetVector",
          Double.valueOf(axesSwap ? g2W.getShearX() : g2W.getScaleX())
              + " "
              + Double.valueOf(axesSwap ? g2W.getScaleX() : g2W.getShearX()),
          offsetAttr);
      element(
          "gml:offsetVector",
          Double.valueOf(axesSwap ? g2W.getScaleY() : g2W.getShearY())
              + " "
              + Double.valueOf(axesSwap ? g2W.getShearY() : g2W.getScaleY()),
          offsetAttr);
      end("gml:RectifiedGrid");
      end("gml:domainSet");
    }
  private ArcSDEGridCoverage2DReaderJAI.ReadParameters parseReadParams(
      final GeneralParameterValue[] params) throws IllegalArgumentException {
    if (params == null) {
      throw new IllegalArgumentException("No GeneralParameterValue given to read operation");
    }

    GeneralEnvelope reqEnvelope = null;
    GridEnvelope dim = null;
    OverviewPolicy overviewPolicy = null;

    // /////////////////////////////////////////////////////////////////////
    //
    // Checking params
    //
    // /////////////////////////////////////////////////////////////////////
    for (int i = 0; i < params.length; i++) {
      final ParameterValue<?> param = (ParameterValue<?>) params[i];
      final String name = param.getDescriptor().getName().getCode();
      if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString())) {
        final GridGeometry2D gg = (GridGeometry2D) param.getValue();
        reqEnvelope = new GeneralEnvelope((Envelope) gg.getEnvelope2D());

        final GeneralEnvelope coverageEnvelope = getOriginalEnvelope();
        CoordinateReferenceSystem nativeCrs = coverageEnvelope.getCoordinateReferenceSystem();
        CoordinateReferenceSystem requestCrs = reqEnvelope.getCoordinateReferenceSystem();
        if (!CRS.equalsIgnoreMetadata(nativeCrs, requestCrs)) {
          LOGGER.fine(
              "Request CRS and native CRS differ, "
                  + "reprojecting request envelope to native CRS");
          ReferencedEnvelope nativeCrsEnv;
          nativeCrsEnv = toNativeCrs(reqEnvelope, nativeCrs);
          reqEnvelope = new GeneralEnvelope(nativeCrsEnv);
        }

        dim = gg.getGridRange2D();
        continue;
      }
      if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName().toString())) {
        overviewPolicy = (OverviewPolicy) param.getValue();
        continue;
      }
    }

    if (reqEnvelope == null && dim == null) {
      reqEnvelope = getOriginalEnvelope();
      dim = getOriginalGridRange();
    }

    if (reqEnvelope == null) {
      reqEnvelope = getOriginalEnvelope();
    }
    if (dim == null) {
      final GeneralEnvelope adjustedGRange;
      try {
        MathTransform gridToWorld = getOriginalGridToWorld(PixelInCell.CELL_CENTER);
        MathTransform worldToGrid = gridToWorld.inverse();
        adjustedGRange = CRS.transform(worldToGrid, reqEnvelope);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
      int xmin = (int) Math.floor(adjustedGRange.getMinimum(0));
      int ymin = (int) Math.floor(adjustedGRange.getMinimum(1));
      int xmax = (int) Math.ceil(adjustedGRange.getMaximum(0));
      int ymax = (int) Math.ceil(adjustedGRange.getMaximum(1));
      dim = new GridEnvelope2D(xmin, ymin, xmax - xmin, ymax - ymin);
    }

    if (!reqEnvelope.intersects(getOriginalEnvelope(), true)) {
      throw new IllegalArgumentException(
          "The requested extend does not overlap the coverage extent: " + getOriginalEnvelope());
    }

    if (dim.getSpan(0) <= 0 || dim.getSpan(1) <= 0) {
      throw new IllegalArgumentException("The requested coverage dimension can't be null: " + dim);
    }

    if (overviewPolicy == null) {
      overviewPolicy = OverviewPolicy.NEAREST;
      LOGGER.finer("No overview policy requested, defaulting to " + overviewPolicy);
    }
    LOGGER.fine("Overview policy is " + overviewPolicy);

    ArcSDEGridCoverage2DReaderJAI.ReadParameters parsedParams =
        new ArcSDEGridCoverage2DReaderJAI.ReadParameters();
    parsedParams.requestedEnvelope = reqEnvelope;
    parsedParams.dim = dim;
    parsedParams.overviewPolicy = overviewPolicy;
    return parsedParams;
  }