/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2011-2012, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.storage.coverage; import java.awt.*; import java.util.Collection; import java.util.Map; import java.util.UUID; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.storage.DataStoreException; import org.apache.sis.geometry.GeneralDirectPosition; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.internal.referencing.j2d.AffineTransform2D; import org.apache.sis.util.Classes; import org.geotoolkit.metadata.iso.spatial.PixelTranslation; import org.geotoolkit.referencing.operation.matrix.GeneralMatrix; import org.opengis.geometry.DirectPosition; import org.opengis.geometry.Envelope; import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.MathTransform; /** * Default mosaic grid. * * @author Johann Sorel (Geomatys) * @module */ public abstract class AbstractGridMosaic implements GridMosaic{ private final String id; private final Pyramid pyramid; private final DirectPosition upperLeft; private final Dimension gridSize; private final Dimension tileSize; private final double scale; public AbstractGridMosaic(Pyramid pyramid, DirectPosition upperLeft, Dimension gridSize, Dimension tileSize, double scale) { this(null,pyramid,upperLeft,gridSize,tileSize,scale); } public AbstractGridMosaic(String id, Pyramid pyramid, DirectPosition upperLeft, Dimension gridSize, Dimension tileSize, double scale) { this.pyramid = pyramid; this.upperLeft = new GeneralDirectPosition(upperLeft); this.scale = scale; this.gridSize = (Dimension) gridSize.clone(); this.tileSize = (Dimension) tileSize.clone(); if(id == null){ this.id = UUID.randomUUID().toString(); }else{ this.id = id; } } /** * {@inheritDoc} */ @Override public String getId() { return id; } /** * {@inheritDoc} */ @Override public Pyramid getPyramid() { return pyramid; } /** * {@inheritDoc} */ @Override public DirectPosition getUpperLeftCorner() { return new GeneralDirectPosition(upperLeft); //defensive copy } /** * {@inheritDoc} */ @Override public Dimension getGridSize() { return (Dimension) gridSize.clone(); //defensive copy } /** * {@inheritDoc} */ @Override public Dimension getTileSize() { return (Dimension) tileSize.clone(); //defensive copy } /** * {@inheritDoc} */ @Override public double getScale() { return scale; } /** * {@inheritDoc} */ @Override public Envelope getEnvelope(final int col, final int row) { final GeneralDirectPosition ul = new GeneralDirectPosition(getUpperLeftCorner()); final int xAxis = Math.max(CoverageUtilities.getMinOrdinate(ul.getCoordinateReferenceSystem()), 0); final int yAxis = xAxis + 1; final double minX = ul.getOrdinate(xAxis); final double maxY = ul.getOrdinate(yAxis); final double spanX = tileSize.width * scale; final double spanY = tileSize.height * scale; final GeneralEnvelope envelope = new GeneralEnvelope(ul,ul); envelope.setRange(xAxis, minX + col*spanX, minX + (col+1)*spanX); envelope.setRange(yAxis, maxY - (row+1)*spanY, maxY - row*spanY); return envelope; } /** * {@inheritDoc} */ @Override public Envelope getEnvelope() { final GeneralDirectPosition ul = new GeneralDirectPosition(getUpperLeftCorner()); final int xAxis = Math.max(CoverageUtilities.getMinOrdinate(ul.getCoordinateReferenceSystem()), 0); final int yAxis = xAxis + 1; final double minX = ul.getOrdinate(xAxis); final double maxY = ul.getOrdinate(yAxis); final double spanX = getTileSize().width * getGridSize().width * getScale(); final double spanY = getTileSize().height* getGridSize().height* getScale(); final GeneralEnvelope envelope = new GeneralEnvelope(ul,ul); envelope.setRange(xAxis, minX, minX + spanX); envelope.setRange(yAxis, maxY - spanY, maxY ); return envelope; } /** * {@inheritDoc} */ @Override public boolean isMissing(int col, int row) { return false; } /** * {@inheritDoc} */ @Override public BlockingQueue<Object> getTiles(Collection<? extends Point> positions, Map hints) throws DataStoreException{ return getTiles(this, positions, hints); } /** * {@inheritDoc} */ @Override public Rectangle getDataArea() { Point start = null; exitStart: for (int y = 0; y < gridSize.height; y++) { for (int x = 0; x < gridSize.width; x++) { if (!isMissing(x,y)) { start = new Point(x,y); continue exitStart; } } } if (start != null) { Point end = new Point(gridSize.width-1, gridSize.height-1); exitEnd: for (int y = gridSize.height-1; y >= start.y; y--) { for (int x = gridSize.width-1; x >= start.x; x--) { if (!isMissing(x, y)) { end = new Point(x, y); continue exitEnd; } } } assert end.x >= start.x; assert end.y >= start.y; return new Rectangle(start.x, start.y, end.x - start.x, end.y - start.y); } else { //all mosaic tiles are missing return null; } } @Override public String toString() { final StringBuilder sb = new StringBuilder(Classes.getShortClassName(this)); sb.append(" scale = ").append(getScale()); sb.append(" gridSize[").append(getGridSize().width).append(',').append(getGridSize().height).append(']'); sb.append(" tileSize[").append(getTileSize().width).append(',').append(getTileSize().height).append(']'); return sb.toString(); } /** * Grid to CRS N dimension. CORNER transform * * @param mosaic not null * @param location not null * @return MathTransform never null */ public static MathTransform getTileGridToCRS(GridMosaic mosaic, Point location){ return getTileGridToCRS(mosaic, location, PixelInCell.CELL_CORNER); } /** * Grid to CRS N dimension. CORNER transform * * @param mosaic not null * @param location not null * @param orientation pixel orientation * @return MathTransform never null */ public static MathTransform getTileGridToCRS(GridMosaic mosaic, Point location, PixelInCell orientation){ final DirectPosition upperleft = mosaic.getUpperLeftCorner(); return getTileGridToCRSND(mosaic, location, upperleft.getDimension(), orientation); } /** * Grid to CRS N dimension. CORNER Transform. * This allows to create a transform ignoring last axis transform. * * @param mosaic not null * @param location not null * @param nbDim : number of dimension wanted. value must be in range [2...crsNbDim] * @return MathTransform never null */ public static MathTransform getTileGridToCRSND(GridMosaic mosaic, Point location, int nbDim){ return getTileGridToCRSND(mosaic, location, nbDim, PixelInCell.CELL_CORNER); } /** * Grid to CRS N dimension. * This allows to create a transform ignoring last axis transform. * * @param mosaic not null * @param location not null * @param nbDim : number of dimension wanted. value must be in range [2...crsNbDim] * @param orientation pixel orientation * @return MathTransform never null */ public static MathTransform getTileGridToCRSND(GridMosaic mosaic, Point location, int nbDim, PixelInCell orientation){ final AffineTransform2D trs2d = getTileGridToCRS2D(mosaic, location, orientation); final DirectPosition upperleft = mosaic.getUpperLeftCorner(); if(upperleft.getDimension()==2){ return trs2d; }else{ final int dim = nbDim+1; final GeneralMatrix gm = new GeneralMatrix(dim); gm.setElement(0, 0, trs2d.getScaleX()); gm.setElement(1, 1, trs2d.getScaleY()); gm.setElement(0, dim-1, trs2d.getTranslateX()); gm.setElement(1, dim-1, trs2d.getTranslateY()); for(int i=2;i<dim-1;i++){ gm.setElement(i, i, 1); gm.setElement(i, dim-1, upperleft.getOrdinate(i)); } return MathTransforms.linear(gm); } } /** * Grid to CRS 2D part. * Transform correspond to the CORNER. * * @param mosaic not null * @param location not null * @return AffineTransform2D never null. */ public static AffineTransform2D getTileGridToCRS2D(GridMosaic mosaic, Point location){ return getTileGridToCRS2D(mosaic, location, PixelInCell.CELL_CORNER); } /** * Grid to CRS 2D part. * * @param mosaic not null * @param location not null * @param orientation pixel orientation * @return AffineTransform2D never null. */ public static AffineTransform2D getTileGridToCRS2D(GridMosaic mosaic, Point location, PixelInCell orientation){ final Dimension tileSize = mosaic.getTileSize(); final DirectPosition upperleft = mosaic.getUpperLeftCorner(); final double scale = mosaic.getScale(); final double offsetX = upperleft.getOrdinate(0) + location.x * (scale * tileSize.width) ; final double offsetY = upperleft.getOrdinate(1) - location.y * (scale * tileSize.height); AffineTransform2D transform2D = new AffineTransform2D(scale, 0, 0, -scale, offsetX, offsetY); if (orientation.equals(PixelInCell.CELL_CENTER)) { return (AffineTransform2D) PixelTranslation.translate(transform2D, PixelInCell.CELL_CORNER, PixelInCell.CELL_CENTER); } return transform2D; } public static BlockingQueue<Object> getTiles(GridMosaic mosaic, Collection<? extends Point> positions, Map hints) throws DataStoreException{ final ArrayBlockingQueue queue = new ArrayBlockingQueue(positions.size()+1); for(Point p : positions){ final TileReference t = mosaic.getTile(p.x, p.y, hints); queue.offer(t); } queue.offer(END_OF_QUEUE); return queue; } }