Package org.geotools.gce.imagemosaic.jdbc

Source Code of org.geotools.gce.imagemosaic.jdbc.ImageMosaicJDBCReader

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2008, Open Source Geospatial Foundation (OSGeo)
*
*    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.geotools.gce.imagemosaic.jdbc;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageReadParam;
import javax.media.jai.RenderedImageAdapter;

import org.geotools.coverage.CoverageFactoryFinder;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.GridEnvelope2D;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.OverviewPolicy;
import org.geotools.coverage.processing.Operations;
import org.geotools.data.DataSourceException;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.parameter.Parameter;
import org.geotools.referencing.CRS;
import org.geotools.referencing.operation.BufferedCoordinateOperationFactory;
import org.geotools.resources.image.ImageUtilities;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.grid.Format;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationFactory;
import org.opengis.referencing.operation.TransformException;

import com.vividsolutions.jts.geom.Envelope;

/**
* This reader is responsible for providing access to images and image pyramids stored in a JDBC
* datbase as tiles.
*
* All jdbc databases which are able to handle blobs are supported.
*
* Additonally, spatial extensions for mysql,postgis,db2 and oracle are supported
*
*
* @author mcr
* @since 2.5
*
*
*
*
* @source $URL$
*/
public class ImageMosaicJDBCReader extends AbstractGridCoverage2DReader {
    private final static Logger LOGGER = Logging.getLogger(ImageMosaicJDBCReader.class.getPackage()
            .getName());

    protected final static CoordinateOperationFactory operationFactory = new BufferedCoordinateOperationFactory(
            new Hints(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE));

    private final JDBCAccess jdbcAccess;

    private Config config;

    private static Set<AxisDirection> UPDirections;

    private static Set<AxisDirection> LEFTDirections;

    protected static int DEFAULT_IMAGE_TYPE = BufferedImage.TYPE_3BYTE_BGR;

    // class initializer
    {
        LEFTDirections = new HashSet<AxisDirection>();
        LEFTDirections.add(AxisDirection.DISPLAY_LEFT);
        LEFTDirections.add(AxisDirection.EAST);
        LEFTDirections.add(AxisDirection.GEOCENTRIC_X);
        LEFTDirections.add(AxisDirection.COLUMN_POSITIVE);

        UPDirections = new HashSet<AxisDirection>();
        UPDirections.add(AxisDirection.DISPLAY_UP);
        UPDirections.add(AxisDirection.NORTH);
        UPDirections.add(AxisDirection.GEOCENTRIC_Y);
        UPDirections.add(AxisDirection.ROW_POSITIVE);

    }

    /**
     * @param source
     *            The source object.
     * @param uHints
     * @throws IOException
     * @throws MalformedURLException
     */
    public ImageMosaicJDBCReader(Object source, Hints uHints) throws IOException,
            MalformedURLException {
        this.source = source;

        URL url = ImageMosaicJDBCFormat.getURLFromSource(source);

        if (url == null) {
            throw new MalformedURLException(source.toString());
        }

        try {
            config = Config.readFrom(url);
        } catch (Exception e) {
            LOGGER.severe(e.getMessage());
            throw new IOException(e);
        }

        // /////////////////////////////////////////////////////////////////////
        //
        // Forcing longitude first since the geotiff specification seems to
        // assume that we have first longitude the latitude.
        //
        // /////////////////////////////////////////////////////////////////////
        if (uHints != null) {
            // prevent the use from reordering axes
            this.hints.add(uHints);
        }

        coverageName = config.getCoverageName();
        this.coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(this.hints);

        // /////////////////////////////////////////////////////////////////////
        //
        // Load tiles informations, especially the bounds, which will be
        // reused
        //
        // /////////////////////////////////////////////////////////////////////
        try {
            jdbcAccess = JDBCAccessFactory.getJDBCAcess(config);
        } catch (Exception e1) {
            LOGGER.severe(e1.getLocalizedMessage());
            throw new IOException(e1);
        }

        // get the crs if able to
        final Object tempCRS = this.hints.get(Hints.DEFAULT_COORDINATE_REFERENCE_SYSTEM);

        if (tempCRS != null) {
            this.crs = (CoordinateReferenceSystem) tempCRS;
            LOGGER.log(Level.WARNING, new StringBuffer("Using forced coordinate reference system ")
                    .append(crs.toWKT()).toString());
        } else if (config.getCoordsys() != null) {
            String srsString = config.getCoordsys();

            try {
                crs = CRS.decode(srsString, false);
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, "Could not find " + srsString, e);

                return;
            }
        } else {
            CoordinateReferenceSystem tempcrs = jdbcAccess.getLevelInfo(0).getCrs();

            if (tempcrs == null) {
                crs = AbstractGridFormat.getDefaultCRS();
                LOGGER.log(Level.WARNING, new StringBuffer(
                        "Unable to find a CRS for this coverage, using a default one: ").append(
                        crs.toWKT()).toString());
            } else {
                crs = tempcrs;
            }
        }

        if (jdbcAccess.getNumOverviews() == -1) {
            String msg = "No levels found fond for coverage: " + config.getCoverageName();
            LOGGER.severe(msg);
            throw new IOException(msg);
        }

        Envelope env = jdbcAccess.getLevelInfo(0).getEnvelope();

        if (env == null) {
            String msg = "Coverage: " + config.getCoverageName() + " is not caluclated";
            LOGGER.severe(msg);
            throw new IOException(msg);
        }

        this.originalEnvelope = new GeneralEnvelope(new Rectangle2D.Double(env.getMinX(), env
                .getMinY(), env.getMaxX() - env.getMinX(), env.getMaxY() - env.getMinY()));
        this.originalEnvelope.setCoordinateReferenceSystem(crs);

        highestRes = jdbcAccess.getLevelInfo(0).getResolution();
        numOverviews = jdbcAccess.getNumOverviews();
        overViewResolutions = new double[numOverviews][];

        for (int i = 0; i < numOverviews; i++)
            overViewResolutions[i] = jdbcAccess.getLevelInfo(i + 1).getResolution();

        originalGridRange = new GridEnvelope2D(new Rectangle((int) Math.round(originalEnvelope
                .getSpan(0)
                / highestRes[0]), (int) Math.round(originalEnvelope.getSpan(1) / highestRes[1])));
    }

    /**
     * Constructor.
     *
     * @param source
     *            The source object.
     * @throws IOException
     * @throws UnsupportedEncodingException
     *
     */
    public ImageMosaicJDBCReader(Object source) throws IOException {
        this(source, null);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.opengis.coverage.grid.GridCoverageReader#getFormat()
     */
    public Format getFormat() {
        return new ImageMosaicJDBCFormat();
    }

    private void logRequestParams(GeneralParameterValue[] params) {
        LOGGER.info("----PARAMS START-------");

        for (int i = 0; i < params.length; i++) {
            Parameter<Object> p = (Parameter<Object>) params[i];
            LOGGER.info(p.getDescriptor().getName().toString() + ": " + p.getValue());
        }

        LOGGER.info("----PARAMS END-------");
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.opengis.coverage.grid.GridCoverageReader#read(org.opengis.parameter.GeneralParameterValue
     * [])
     */
    public GridCoverage2D read(GeneralParameterValue[] params) throws IOException {
        logRequestParams(params);

        ImageMosaicJDBCReaderState state = new ImageMosaicJDBCReaderState();
        Date start = new Date();

        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine("Reading mosaic from " + coverageName);
            LOGGER.fine(new StringBuffer("Highest res ").append(highestRes[0]).append(" ").append(
                    highestRes[1]).toString());
        }

        // /////////////////////////////////////////////////////////////////////
        //
        // Checking params
        //
        // /////////////////////////////////////////////////////////////////////
        Color outputTransparentColor = (Color) ImageMosaicJDBCFormat.OUTPUT_TRANSPARENT_COLOR
                .getDefaultValue();
       
        Color backgroundColor = (Color) ImageMosaicJDBCFormat.BACKGROUND_COLOR
        .getDefaultValue();


        Rectangle dim = null;

        if (params != null) {
            for (GeneralParameterValue generalParameterValue : params) {
                Parameter<Object> param = (Parameter<Object>) generalParameterValue;

                if (param.getDescriptor().getName().getCode().equals(
                        AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString())) {
                    final GridGeometry2D gg = (GridGeometry2D) param.getValue();
                    state.setRequestedEnvelope((GeneralEnvelope) gg.getEnvelope());
                    dim = gg.getGridRange2D().getBounds();
                } else if (param.getDescriptor().getName().getCode().equals(
                        ImageMosaicJDBCFormat.OUTPUT_TRANSPARENT_COLOR.getName().toString())) {
                    outputTransparentColor = (Color) param.getValue();
                } else if (param.getDescriptor().getName().getCode().equals(
                        ImageMosaicJDBCFormat.BACKGROUND_COLOR.getName().toString())) {
                    backgroundColor = (Color) param.getValue();                   
                }
            }
        }

        // /////////////////////////////////////////////////////////////////////
        //
        // Loading tiles trying to optimize as much as possible
        //
        // /////////////////////////////////////////////////////////////////////
        GridCoverage2D coverage = loadTiles(backgroundColor,outputTransparentColor, dim, state);
        LOGGER.info("Mosaic Reader needs : " + ((new Date()).getTime() - start.getTime())
                + " millisecs");

        return coverage;
    }

    /**
     * transforms (if neccessairy) the requested envelope into the CRS used by this reader.
     *
     * @throws DataSourceException
     */
    private void transformRequestEnvelope(ImageMosaicJDBCReaderState state)
            throws DataSourceException {

        if (CRS.equalsIgnoreMetadata(state.getRequestedEnvelope().getCoordinateReferenceSystem(),
                this.crs)) {
            state.setRequestEnvelopeTransformed(state.getRequestedEnvelope());

            return; // and finish
        }

        try {
            /** Buffered factory for coordinate operations. */

            // transforming the envelope back to the dataset crs in
            CoordinateOperation op = operationFactory.createOperation(state.getRequestedEnvelope()
                    .getCoordinateReferenceSystem(), crs);

            if (op.getMathTransform().isIdentity()) { // Identity Transform ?
                state.setRequestEnvelopeTransformed(state.getRequestedEnvelope());
                return; // and finish
            }

            state.setRequestEnvelopeTransformed(CRS.transform(op, state
                    .getRequestedEnvelope()));
            state.getRequestEnvelopeTransformed().setCoordinateReferenceSystem(crs);

            if (config.getIgnoreAxisOrder()==false) { // check for axis order required
                int indexX = indexOfX(crs);
                int indexY = indexOfY(crs);
                int indexRequestedX = indexOfX(state.getRequestedEnvelope()
                        .getCoordinateReferenceSystem());
                int indexRequestedY = indexOfY(state.getRequestedEnvelope()
                        .getCoordinateReferenceSystem());
   
                // x Axis problem ???
                if (indexX == indexRequestedY && indexY == indexRequestedX) {
                    state.setXAxisSwitch(true);
                    Rectangle2D tmp = new Rectangle2D.Double(state.getRequestEnvelopeTransformed()
                            .getMinimum(1), state.getRequestEnvelopeTransformed().getMinimum(0), state
                            .getRequestEnvelopeTransformed().getSpan(1), state
                            .getRequestEnvelopeTransformed().getSpan(0));
                    state.setRequestEnvelopeTransformed(new GeneralEnvelope(tmp));
                    state.getRequestEnvelopeTransformed().setCoordinateReferenceSystem(crs);
                } else if (indexX == indexRequestedX && indexY == indexRequestedY) {
                    // everything is fine
                } else {
                    throw new DataSourceException("Unable to resolve the X Axis problem");
                }
            }

            if (LOGGER.isLoggable(Level.FINE)) {
                LOGGER.fine(new StringBuffer("Reprojected envelope ").append(
                        state.getRequestedEnvelope().toString()).append(" crs ")
                        .append(crs.toWKT()).toString());
            }
        } catch (Exception e) {
            throw new DataSourceException("Unable to create a coverage for this source", e);
        }
    }

    /**
     * @param backgroundColor
     *          the background color
     * @param outputTransparentColor
     *          the transparent color
     * @param pixelDimension
     * @return the gridcoverage as the final result
     * @throws IOException
     */
    private GridCoverage2D loadTiles(Color backgroundColor,Color outputTransparentColor, Rectangle pixelDimension,
            ImageMosaicJDBCReaderState state) throws IOException {
        if (LOGGER.isLoggable(Level.FINE)) {
            LOGGER.fine(new StringBuffer("Creating mosaic to comply with envelope ").append(
                    (state.getRequestedEnvelope() != null) ? state.getRequestedEnvelope()
                            .toString() : null).append(" crs ").append(crs.toWKT()).append(" dim ")
                    .append((pixelDimension == null) ? " null" : pixelDimension.toString())
                    .toString());
        }

        transformRequestEnvelope(state);

        // /////////////////////////////////////////////////////////////////////
        //
        // Check if we have something to load by intersecting the requested
        // envelope with the bounds of the data set. If not, give warning
        //
        // /////////////////////////////////////////////////////////////////////
        if (!state.getRequestEnvelopeTransformed().intersects(this.originalEnvelope, true)) {
            LOGGER
                    .warning("The requested envelope does not intersect the envelope of this mosaic");
            LOGGER.warning(state.getRequestEnvelopeTransformed().toString());
            LOGGER.warning(originalEnvelope.toString());

//            return coverageFactory.create(coverageName, getEmptyImage((int) pixelDimension
//                    .getWidth(), (int) pixelDimension.getHeight(), backgroundColor, outputTransparentColor), state
//                    .getRequestedEnvelope());
            return null;
        }

        // /////////////////////////////////////////////////////////////////////
        //
        // Load feaures from the index
        // In case there are no features under the requested bbox which is legal
        // in case the mosaic is not a real sqare, we return a fake mosaic.
        //
        // /////////////////////////////////////////////////////////////////////
        final ImageReadParam readP = new ImageReadParam();
        final Integer imageChoice;

        if (pixelDimension != null) {
            try {
                imageChoice = setReadParams(OverviewPolicy.getDefaultPolicy(), readP, state
                        .getRequestEnvelopeTransformed(), pixelDimension);
                readP.setSourceSubsampling(1, 1, 0, 0);
            } catch (TransformException e) {
                LOGGER.severe(e.getLocalizedMessage());

                return coverageFactory.create(coverageName, getEmptyImage((int) pixelDimension
                        .getWidth(), (int) pixelDimension.getHeight(), backgroundColor,outputTransparentColor),
                        state.getRequestedEnvelope());
            }
        } else {
            imageChoice = new Integer(0);
        }

        ImageLevelInfo info = jdbcAccess.getLevelInfo(imageChoice.intValue());
        LOGGER.info("Coverage " + info.getCoverageName() + " using spatial table "
                + info.getSpatialTableName() + ", image table " + info.getTileTableName());

        ImageComposerThread imageComposerThread = new ImageComposerThread(backgroundColor,outputTransparentColor,
                pixelDimension, state.getRequestEnvelopeTransformed(), info, state.getTileQueue(),
                config, state.isXAxisSwitch(), coverageFactory);
        imageComposerThread.start();

        jdbcAccess.startTileDecoders(pixelDimension, state.getRequestEnvelopeTransformed(), info,
                state.getTileQueue(), coverageFactory);

        try {
            imageComposerThread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        GridCoverage2D result = imageComposerThread.getGridCoverage2D();

        return transformResult(result, pixelDimension, state);
    }

    private GridCoverage2D transformResult(GridCoverage2D coverage, Rectangle pixelDimension,
            ImageMosaicJDBCReaderState state) {
        if (state.getRequestEnvelopeTransformed() == state.getRequestedEnvelope()) {
            return coverage; // nothing to do
        }

        GridCoverage2D result = null;
        LOGGER.info("Image reprojection necessairy");
        // coverage.show();
        result = (GridCoverage2D) Operations.DEFAULT.resample(coverage, state
                .getRequestedEnvelope().getCoordinateReferenceSystem());
        // result.show();
        // result = (GridCoverage2D) Operations.DEFAULT.crop(result,
        // requestedEnvelope);
        // result.show();
        // result = (GridCoverage2D) Operations.DEFAULT.scale(result, 1, 1,
        // -result.getRenderedImage().getMinX(),
        // -result.getRenderedImage().getMinY());
        //
        // result.show();
        // double scalex = pixelDimension.getWidth() / result.getRenderedImage()
        // .getWidth();
        // double scaley = pixelDimension.getHeight() /
        // result.getRenderedImage()
        // .getHeight();
        // result = (GridCoverage2D) Operations.DEFAULT.scale(result, scalex,
        // scaley, 0, 0);

        // avoid lazy calculation
        // RenderedImageAdapter adapter = new
        // RenderedImageAdapter(result.getRenderedImage());
        // /BufferedImage resultImage = adapter.getAsBufferedImage();

        return coverageFactory.create(result.getName(), result.getRenderedImage(), result
                .getEnvelope());

    }

    /**
     * @param width
     * @param height
     * @param backgroundColor
     * @param outputTransparentColor
     * @return BufferdImage filled with outputTransparentColor
     */
    private BufferedImage getEmptyImage(int width, int height, Color backGroundcolor,Color outputTransparentColor) {
        BufferedImage emptyImage = new BufferedImage(width, height, DEFAULT_IMAGE_TYPE);
        Graphics2D g2D = (Graphics2D) emptyImage.getGraphics();
        Color save = g2D.getColor();
        g2D.setColor(backGroundcolor);
        g2D.fillRect(0, 0, emptyImage.getWidth(), emptyImage.getHeight());
        g2D.setColor(save);
        if (outputTransparentColor!=null)
            emptyImage = new RenderedImageAdapter(
                    ImageUtilities.maskColor(outputTransparentColor,emptyImage)).getAsBufferedImage();
        return emptyImage;
    }

    /**
     * @param crs
     *            CoordinateReference System
     * @return dimension index of y dir in crs
     */
    private int indexOfY(CoordinateReferenceSystem crs) {
        return indexOf(crs, UPDirections);
    }

    /**
     * @param crs
     *            CoordinateReference System
     * @return dimension index of X dir in crs
     */
    private int indexOfX(CoordinateReferenceSystem crs) {
        return indexOf(crs, LEFTDirections);
    }

    private int indexOf(CoordinateReferenceSystem crs, Set<AxisDirection> direction) {
        CoordinateSystem cs = crs.getCoordinateSystem();
        for (int index = 0; index < cs.getDimension(); index++) {
            CoordinateSystemAxis axis = cs.getAxis(index);
            if (direction.contains(axis.getDirection()))
                return index;
        }
        return -1;
    }
}
TOP

Related Classes of org.geotools.gce.imagemosaic.jdbc.ImageMosaicJDBCReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.