|
|
|
svn_geotools
|
Author: groldan
Date: 2009-11-03 11:03:24 -0500 (Tue, 03 Nov 2009) New Revision: 34313 Added: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDEGridCoverage2DReaderJAI.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDERasterFormat.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/CompressionType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/GatherCoverageMetadataCommand.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterleaveType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterpolationType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/PyramidLevelInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterBandInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterCellType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterDatasetInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterQueryInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterUtils.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/BitmaskToNoDataConverter.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/DefaultTiledRasterReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/NativeTileReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/PromotingTileReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/RasterReaderFactory.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReaderFactory.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TiledRasterReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDEImageReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDETiledImageInputStream.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/gce/ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/gce/ArcSDEGridCoverage2DReaderJAILegacyOnlineTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/gce/ArcSDEGridCoverage2DReaderJAIOnlineTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/gce/RasterTestData.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/info/ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/info/RasterInfoTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/info/RasterUtilsTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/io/ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/raster/io/BitmaskToNoDataConverterTest.java Removed: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEImageReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDERasterFormat.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDETiledImageInputStream.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/BitmaskToNoDataConverter.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/CompressionType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/DefaultTiledRasterReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/GatherCoverageMetadataCommand.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterleaveType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterpolationType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/NativeTileReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PromotingTileReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PyramidLevelInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterBandInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterCellType.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterDatasetInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInputStream.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterQueryInfo.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterReaderFactory.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterUtils.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReader.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReaderFactory.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TiledRasterReader.java trunk/modules/plugin/arcsde/datastore/src/main/resources/org/geotools/arcsde/gce/ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAILegacyOnlineTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAIOnlineTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/BitmaskToNoDataConverterTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterInfoTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterTestData.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterUtilsTest.java Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactory.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDERasterFormatFactory.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEDataStore.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEQuery.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureCollection.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/SeToJTSGeometryFactory.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/TransactionFeatureWriter.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/filter/FilterToSQLSDE.java trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/util/ArcSDEUtils.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEDataStoreFactoryTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactoryTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreNonSpatialTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEJavaApiTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ClobTestData.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/FIDReaderTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/SDEJavaApiJoinTest.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestFeatureListener.java trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestProgressListener.java Log: GEOT-2616, package reorganization Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactory.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactory.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactory.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -64,8 +64,10 @@ * </p> * * @author Gabriel Roldan (OpenGeo) - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/ArcSDEJNDIDataStoreFactory.java $ * @version $Id$ * @since 2.5.7 */ Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDERasterFormatFactory.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDERasterFormatFactory.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/ArcSDERasterFormatFactory.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -22,7 +22,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -import org.geotools.arcsde.gce.ArcSDERasterFormat; +import org.geotools.arcsde.raster.gce.ArcSDERasterFormat; import org.geotools.coverage.grid.io.GridFormatFactorySpi; import org.geotools.util.logging.Logging; Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEDataStore.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEDataStore.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEDataStore.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -64,6 +64,7 @@ import org.geotools.feature.SchemaException; import org.geotools.geometry.jts.LiteCoordinateSequenceFactory; import org.geotools.util.logging.Logging; +import org.hsqldb.Session; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; @@ -378,7 +379,7 @@ // this is the one which's gonna close the connection when done final ArcSDEAttributeReader attReader; attReader = new ArcSDEAttributeReader(sdeQuery, geometryFactory, session); - + FeatureReader<SimpleFeatureType, SimpleFeature> reader; try { reader = new ArcSDEFeatureReader(attReader); Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEQuery.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEQuery.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSDEQuery.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -52,6 +52,7 @@ import org.geotools.filter.visitor.SimplifyingFilterVisitor; import org.geotools.filter.visitor.SimplifyingFilterVisitor.FIDValidator; import org.geotools.util.logging.Logging; +import org.hsqldb.Session; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.GeometryDescriptor; @@ -69,7 +70,6 @@ import com.esri.sde.sdk.client.SeQueryInfo; import com.esri.sde.sdk.client.SeSqlConstruct; import com.esri.sde.sdk.client.SeTable; -import com.esri.sde.sdk.geom.GeometryFactory; import com.vividsolutions.jts.geom.Envelope; /** @@ -881,8 +881,10 @@ * DOCUMENT ME! * * @author $author$ - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java + * /org/geotools/arcsde/data/ArcSDEQuery.java $ * @version $Revision: 1.9 $ */ public static class FilterSet { Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureCollection.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureCollection.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureCollection.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -26,6 +26,7 @@ import org.geotools.feature.FeatureCollection; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.util.logging.Logging; +import org.hsqldb.Session; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.GeometryDescriptor; Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/ArcSdeFeatureWriter.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -40,10 +40,10 @@ import org.geotools.data.jdbc.MutableFIDFeature; import org.geotools.factory.CommonFactoryFinder; import org.geotools.feature.simple.SimpleFeatureBuilder; -import org.geotools.feature.type.Types; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.util.Converters; import org.geotools.util.logging.Logging; +import org.hsqldb.Session; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; @@ -365,10 +365,10 @@ * @see FeatureWriter#write() */ public void write() throws IOException { - - //make the feature validate against its schema before inserting/updating + + // make the feature validate against its schema before inserting/updating feature.validate(); - + if (isNewlyCreated(feature)) { Number newId; try { Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/SeToJTSGeometryFactory.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/SeToJTSGeometryFactory.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/SeToJTSGeometryFactory.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -19,8 +19,10 @@ * creates JTS geometries directly by calling {@link SeRow#getGeometry(GeometryFactory, int)}, * instead of fetching an {@link SeShape} through {@link SeRow#getShape(int)} and then converting it * to a JTS geometry. This is work in progress and _experimental_, though. - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/data/SeToJTSGeometryFactory.java $ */ public class SeToJTSGeometryFactory implements GeometryFactory { @@ -73,7 +75,7 @@ } public void partOffsets(int[] partOffsets) { - //System.out.println(Arrays.toString(partOffsets)); + // System.out.println(Arrays.toString(partOffsets)); } /** Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/TransactionFeatureWriter.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/TransactionFeatureWriter.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/data/TransactionFeatureWriter.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -26,6 +26,7 @@ import org.geotools.data.FeatureWriter; import org.geotools.data.Transaction; import org.geotools.geometry.jts.ReferencedEnvelope; +import org.hsqldb.Session; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.filter.Filter; Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/filter/FilterToSQLSDE.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/filter/FilterToSQLSDE.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/filter/FilterToSQLSDE.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -228,12 +228,12 @@ String fidField = layerFidFieldName; try { - String sql = buildFilter(fids, fidField+" IN(", ")", ",", 1000, " OR "); + String sql = buildFilter(fids, fidField + " IN(", ")", ",", 1000, " OR "); if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("added fid filter: " + sql); } - + this.out.write(sql); } catch (Exception ex) { throw new RuntimeException(ex.getMessage(), ex); @@ -242,43 +242,37 @@ } // return a string - private String buildFilter(long[] fids, String prefix, String suffix, String separator, int groupSize, String groupSeparator) - { + private String buildFilter(long[] fids, String prefix, String suffix, String separator, + int groupSize, String groupSeparator) { final int count = fids.length; final int groups = count / groupSize; final int remainder = count % groupSize; final StringBuilder sql = new StringBuilder(); - for (int i=0; i<groups; i++) - { - if ( i > 0 ) - { + for (int i = 0; i < groups; i++) { + if (i > 0) { sql.append(groupSeparator); } sql.append(prefix); - addSubList(sql, fids, i*groupSize, (i+1)*groupSize, separator); + addSubList(sql, fids, i * groupSize, (i + 1) * groupSize, separator); sql.append(suffix); } - if ( remainder > 0 ) - { - if ( groups > 0 ) - { + if (remainder > 0) { + if (groups > 0) { sql.append(groupSeparator); - } + } sql.append(prefix); - addSubList(sql, fids, count-remainder, count, separator); + addSubList(sql, fids, count - remainder, count, separator); sql.append(suffix); } return sql.toString(); } - - private void addSubList(StringBuilder sql, long[] fids, int start, int end, String separator) - { - for (int i=start; i<end; i++) - { + + private void addSubList(StringBuilder sql, long[] fids, int start, int end, String separator) { + for (int i = start; i < end; i++) { sql.append(fids[i]); sql.append(separator); } - sql.setLength(sql.length()-separator.length()); + sql.setLength(sql.length() - separator.length()); } /** Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,686 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.WritableRaster; -import java.awt.image.renderable.ParameterBlock; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.imageio.ImageIO; -import javax.imageio.ImageTypeSpecifier; -import javax.media.jai.ImageLayout; -import javax.media.jai.InterpolationNearest; -import javax.media.jai.JAI; -import javax.media.jai.ParameterBlockJAI; -import javax.media.jai.operator.FormatDescriptor; -import javax.media.jai.operator.MosaicDescriptor; - -import org.geotools.coverage.CoverageFactoryFinder; -import org.geotools.coverage.GridSampleDimension; -import org.geotools.coverage.TypeMap; -import org.geotools.coverage.grid.GeneralGridEnvelope; -import org.geotools.coverage.grid.GridCoverage2D; -import org.geotools.coverage.grid.GridEnvelope2D; -import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; -import org.geotools.coverage.grid.io.OverviewPolicy; -import org.geotools.data.DataSourceException; -import org.geotools.data.DefaultServiceInfo; -import org.geotools.data.ServiceInfo; -import org.geotools.factory.Hints; -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.util.logging.Logging; -import org.opengis.coverage.ColorInterpretation; -import org.opengis.coverage.grid.Format; -import org.opengis.coverage.grid.GridCoverageReader; -import org.opengis.parameter.GeneralParameterValue; - -/** - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL: - * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org - * /geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java $ - */ -@SuppressWarnings( { "deprecation", "nls" }) -final class ArcSDEGridCoverage2DReaderJAI extends AbstractGridCoverage2DReader { - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - /** - * @see LoggingHelper#log(RenderedImage, Long, String) - */ - private static final boolean DEBUG_TO_DISK = Boolean - .getBoolean("org.geotools.arcsde.gce.debug"); - - private final ArcSDERasterFormat parent; - - private final RasterDatasetInfo rasterInfo; - - private DefaultServiceInfo serviceInfo; - - private RasterReaderFactory rasterReaderFactory; - - public ArcSDEGridCoverage2DReaderJAI(final ArcSDERasterFormat parent, - final RasterReaderFactory rasterReaderFactory, final RasterDatasetInfo rasterInfo, - final Hints hints) throws IOException { - // check it's a supported format - { - final int bitsPerSample = rasterInfo.getBand(0, 0).getCellType().getBitsPerSample(); - if (rasterInfo.getNumBands() > 1 && (bitsPerSample == 1 || bitsPerSample == 4)) { - throw new IllegalArgumentException(bitsPerSample - + "-bit rasters with more than one band are not supported"); - } - } - this.parent = parent; - this.rasterReaderFactory = rasterReaderFactory; - this.rasterInfo = rasterInfo; - - super.hints = hints; - super.coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(this.hints); - super.crs = rasterInfo.getCoverageCrs(); - super.originalEnvelope = rasterInfo.getOriginalEnvelope(); - - GeneralGridEnvelope gridRange = rasterInfo.getOriginalGridRange(); - super.originalGridRange = new GridEnvelope2D(gridRange.toRectangle()); - - super.coverageName = rasterInfo.getRasterTable(); - final int numLevels = rasterInfo.getNumPyramidLevels(0); - - // level 0 is not an overview, but the raster itself - super.numOverviews = numLevels - 1; - - // /// - // - // setting the higher resolution avalaible for this coverage - // - // /// - highestRes = super.getResolution(originalEnvelope, (Rectangle) originalGridRange, crs); - // // - // - // get information for the successive images - // - // // - // REVISIT may the different rasters in the raster dataset have different pyramid levels? I - // guess so - if (numOverviews > 0) { - overViewResolutions = new double[numOverviews][2]; - for (int pyramidLevel = 1; pyramidLevel <= numOverviews; pyramidLevel++) { - Rectangle levelGridRange = rasterInfo.getGridRange(0, pyramidLevel); - GeneralEnvelope levelEnvelope = rasterInfo.getGridEnvelope(0, pyramidLevel); - overViewResolutions[pyramidLevel - 1] = super.getResolution(levelEnvelope, - levelGridRange, crs); - } - } else { - overViewResolutions = null; - } - } - - /** - * @see GridCoverageReader#getFormat() - */ - public Format getFormat() { - return parent; - } - - @Override - public ServiceInfo getInfo() { - if (serviceInfo == null) { - serviceInfo = new DefaultServiceInfo(); - serviceInfo.setTitle(rasterInfo.getRasterTable()); - serviceInfo.setDescription(rasterInfo.toString()); - Set<String> keywords = new HashSet<String>(); - keywords.add("ArcSDE"); - serviceInfo.setKeywords(keywords); - } - return serviceInfo; - } - - /** - * @see GridCoverageReader#read(GeneralParameterValue[]) - */ - public GridCoverage2D read(GeneralParameterValue[] params) throws IOException { - - final GeneralEnvelope requestedEnvelope; - final Rectangle requestedDim; - final OverviewPolicy overviewPolicy; - { - final ReadParameters opParams = RasterUtils.parseReadParams(getOriginalEnvelope(), - params); - overviewPolicy = opParams.overviewPolicy; - requestedEnvelope = opParams.requestedEnvelope; - requestedDim = opParams.dim; - } - - /* - * For each raster in the raster dataset, obtain the tiles, pixel range, and resulting - * envelope - */ - final List<RasterQueryInfo> queries; - queries = findMatchingRasters(requestedEnvelope, requestedDim, overviewPolicy); - if (queries.isEmpty()) { - /* - * none of the rasters match the requested envelope. This may happen by the tiled nature - * of the raster dataset - */ - return createFakeCoverage(requestedEnvelope, requestedDim); - } - - final GeneralEnvelope resultEnvelope = getResultEnvelope(queries); - - final LoggingHelper log = new LoggingHelper(); - log.appendLoggingGeometries(LoggingHelper.REQ_ENV, requestedEnvelope); - log.appendLoggingGeometries(LoggingHelper.RES_ENV, resultEnvelope); - - /* - * Once we collected the matching rasters and their image subsets, find out where in the - * overall resulting mosaic they fit. If the rasters does not share the spatial resolution, - * the QueryInfo.resultDimension and QueryInfo.mosaicLocation width or height won't match - */ - final Rectangle mosaicGeometry; - mosaicGeometry = RasterUtils.setMosaicLocations(rasterInfo, resultEnvelope, queries); - - /* - * Gather the rendered images for each of the rasters that match the requested envelope - */ - final TiledRasterReader rasterReader = rasterReaderFactory.create(rasterInfo); - - try { - readAllTiledRasters(queries, rasterReader, log); - } finally { - // rasterReader.dispose(); - } - - log.log(LoggingHelper.REQ_ENV); - log.log(LoggingHelper.RES_ENV); - log.log(LoggingHelper.MOSAIC_ENV); - log.log(LoggingHelper.MOSAIC_EXPECTED); - - final RenderedImage coverageRaster = createMosaic(queries, mosaicGeometry, log); - assert mosaicGeometry.getWidth() == coverageRaster.getWidth(); - assert mosaicGeometry.getHeight() == coverageRaster.getHeight(); - - /* - * BUILDING COVERAGE - */ - GridSampleDimension[] bands = getSampleDimensions(coverageRaster); - - GridCoverage2D resultCoverage = coverageFactory.create(coverageName, coverageRaster, - resultEnvelope, bands, null, null); - - return resultCoverage; - } - - private GridSampleDimension[] getSampleDimensions(final RenderedImage coverageRaster) - throws IOException { - - GridSampleDimension[] bands = rasterInfo.getGridSampleDimensions(); - - // may the image have been promoted? build the correct band info then - final int imageBands = coverageRaster.getSampleModel().getNumBands(); - if (bands.length == 1 && imageBands > 1) { - LOGGER.fine(coverageName + " was promoted from 1 to " - + coverageRaster.getSampleModel().getNumBands() - + " bands, returning an appropriate set of GridSampleDimension"); - // stolen from super.createCoverage: - final ColorModel cm = coverageRaster.getColorModel(); - bands = new GridSampleDimension[imageBands]; - - // setting bands names. - for (int i = 0; i < imageBands; i++) { - final ColorInterpretation colorInterpretation; - colorInterpretation = TypeMap.getColorInterpretation(cm, i); - if (colorInterpretation == null) { - throw new IOException("Unrecognized sample dimension type"); - } - bands[i] = new GridSampleDimension(colorInterpretation.name()).geophysics(true); - } - } - - return bands; - } - - private void readAllTiledRasters(final List<RasterQueryInfo> queries, - final TiledRasterReader rasterReader, final LoggingHelper log) throws IOException { - - for (RasterQueryInfo queryInfo : queries) { - - final Long rasterId = queryInfo.getRasterId(); - - final RenderedImage rasterImage; - - try { - final int pyramidLevel = queryInfo.getPyramidLevel(); - final Rectangle matchingTiles = queryInfo.getMatchingTiles(); - // final Point imageLocation = queryInfo.getTiledImageSize().getLocation(); - rasterImage = rasterReader.read(rasterId, pyramidLevel, matchingTiles); - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Fetching data for " + queryInfo.toString(), e); - throw e; - } - - queryInfo.setResultImage(rasterImage); - - { - LOGGER.finer(queryInfo.toString()); - log.appendLoggingGeometries(LoggingHelper.MOSAIC_EXPECTED, queryInfo - .getMosaicLocation()); - log - .appendLoggingGeometries(LoggingHelper.MOSAIC_ENV, queryInfo - .getResultEnvelope()); - - final Rectangle tiledImageSize = queryInfo.getTiledImageSize(); - int width = rasterImage.getWidth(); - int height = rasterImage.getHeight(); - if (tiledImageSize.width != width || tiledImageSize.height != height) { - throw new IllegalStateException( - "Read image is not of the expected size. Image=" + width + "x" + height - + ", expected: " + tiledImageSize.width + "x" - + tiledImageSize.height); - } - // if (tiledImageSize.x != rasterImage.getMinX() - // || tiledImageSize.y != rasterImage.getMinY()) { - // throw new IllegalStateException("Read image is not at the expected location " - // + tiledImageSize.x + "," + tiledImageSize.y + ": " - // + rasterImage.getMinX() + "," + rasterImage.getMinY()); - // } - } - } - } - - /** - * Called when the requested envelope do overlap the coverage envelope but none of the rasters - * in the dataset do - * - * @param requestedEnvelope - * @param requestedDim - * @return - */ - private GridCoverage2D createFakeCoverage(GeneralEnvelope requestedEnvelope, - Rectangle requestedDim) { - - ImageTypeSpecifier its = rasterInfo.getRenderedImageSpec(0); - SampleModel sampleModel = its.getSampleModel(requestedDim.width, requestedDim.height); - ColorModel colorModel = its.getColorModel(); - - WritableRaster raster = Raster.createWritableRaster(sampleModel, null); - BufferedImage image = new BufferedImage(colorModel, raster, false, null); - return coverageFactory.create(coverageName, image, requestedEnvelope); - } - - private List<RasterQueryInfo> findMatchingRasters(final GeneralEnvelope requestedEnvelope, - final Rectangle requestedDim, final OverviewPolicy overviewPolicy) { - - final List<RasterQueryInfo> matchingQueries; - matchingQueries = RasterUtils.findMatchingRasters(rasterInfo, requestedEnvelope, - requestedDim, overviewPolicy); - - if (matchingQueries.isEmpty()) { - return matchingQueries; - } - - for (RasterQueryInfo match : matchingQueries) { - RasterUtils.fitRequestToRaster(requestedEnvelope, rasterInfo, match); - } - return matchingQueries; - } - - private GeneralEnvelope getResultEnvelope(final List<RasterQueryInfo> queryInfos) { - - GeneralEnvelope finalEnvelope = null; - - for (RasterQueryInfo rasterQueryInfo : queryInfos) { - // gather resulting envelope - if (finalEnvelope == null) { - finalEnvelope = new GeneralEnvelope(rasterQueryInfo.getResultEnvelope()); - } else { - finalEnvelope.add(rasterQueryInfo.getResultEnvelope()); - } - } - if (finalEnvelope == null) { - throw new IllegalStateException("Restult envelope is null, this shouldn't happen!! " - + "we checked the request overlaps the coverage envelope before!"); - } - return finalEnvelope; - } - - /** - * For each raster: crop->scale->translate->add to mosaic - * - * @param queries - * @param mosaicGeometry - * @return - * @throws IOException - */ - private RenderedImage createMosaic(final List<RasterQueryInfo> queries, - final Rectangle mosaicGeometry, final LoggingHelper log) throws IOException { - - List<RenderedImage> transformed = new ArrayList<RenderedImage>(queries.size()); - - /* - * Do we need to expand to RGB color space and then create a new colormapped image with the - * whole mosaic? - */ - boolean expandThenContractCM = queries.size() > 1 && rasterInfo.isColorMapped(); - if (expandThenContractCM) { - LOGGER.info("Creating mosaic out of " + queries.size() - + " colormapped rasters. The mosaic tiles will be expanded to " - + "\nRGB space and the resulting mosaic reduced to a new IndexColorModel"); - } - - for (RasterQueryInfo query : queries) { - RenderedImage image = query.getResultImage(); - log.log(image, query.getRasterId(), "01_original"); - - image = cropToRequiredDimension(image, query.getTiledImageSize(), query - .getResultDimensionInsideTiledImage()); - log.log(image, query.getRasterId(), "02_crop"); - - final Rectangle mosaicLocation = query.getMosaicLocation(); - // scale - Float scaleX = Float.valueOf((float) (mosaicLocation.getWidth() / image.getWidth())); - Float scaleY = Float.valueOf((float) (mosaicLocation.getHeight() / image.getHeight())); - Float translateX = Float.valueOf(0); - Float translateY = Float.valueOf(0); - // image.getData(); - if (!(Float.valueOf(1.0F).equals(scaleX) && Float.valueOf(1.0F).equals(scaleY))) { - ParameterBlock pb = new ParameterBlock(); - pb.addSource(image); - pb.add(scaleX); - pb.add(scaleY); - pb.add(translateX); - pb.add(translateY); - pb.add(new InterpolationNearest()); - - if (queries.size() > 0) { - try { - LOGGER.info("Forcing loading data for mosaic as per GEOT-"); - // image.getData(); - } catch (RuntimeException e) { - throw new DataSourceException("Error fetching arcsde raster", e); - } - } - - image = JAI.create("scale", pb); - log.log(image, query.getRasterId(), "03_scale"); - - int width = image.getWidth(); - int height = image.getHeight(); - - assert mosaicLocation.width == width; - assert mosaicLocation.height == height; - } - if (image.getMinX() != mosaicLocation.x || image.getMinY() != mosaicLocation.y) { - // translate - ParameterBlock pb = new ParameterBlock(); - pb.addSource(image); - pb.add(Float.valueOf(mosaicLocation.x - image.getMinX())); - pb.add(Float.valueOf(mosaicLocation.y - image.getMinY())); - pb.add(null); - - image = JAI.create("translate", pb); - log.log(image, query.getRasterId(), "04_translate"); - - assert image.getMinX() == mosaicLocation.x : image.getMinX() + " != " - + mosaicLocation.x; - assert image.getMinY() == mosaicLocation.y : image.getMinY() + " != " - + mosaicLocation.y; - assert image.getWidth() == mosaicLocation.width : image.getWidth() + " != " - + mosaicLocation.width; - assert image.getHeight() == mosaicLocation.height : image.getHeight() + " != " - + mosaicLocation.height; - } - if (expandThenContractCM) { - if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.finer("Creating color expanded version of tile for raster #" - + query.getRasterId()); - } - - /* - * reformat the image as a 4 band rgba backed by byte data - */ - image = FormatDescriptor.create(image, Integer.valueOf(DataBuffer.TYPE_BYTE), null); - - log.log(image, query.getRasterId(), "04_1_colorExpanded"); - } - - transformed.add(image); - } - - final RenderedImage mosaic; - if (queries.size() == 1) { - /* - * This is besides a very slight perf improvement needed because the JAI mosaic - * operation truncates floating point raster values to 0 and 1. REVISIT: If there's no - * workaround for that we should prevent raster catalogs made of floating point rasters - * and throw an exception as we could not really support that. - */ - mosaic = transformed.get(0); - } else { - /* - * adapted from RasterLayerResponse.java in the imagemosaic module - */ - ParameterBlockJAI mosaicParams = new ParameterBlockJAI("Mosaic"); - - // TODO: set background values to raster's no-data - // mosaicParams.setParameter("backgroundValues",backgroundValues); - - mosaicParams.setParameter("mosaicType", MosaicDescriptor.MOSAIC_TYPE_OVERLAY); - - final ImageLayout layout = new ImageLayout(mosaicGeometry.x, mosaicGeometry.y, - mosaicGeometry.width, mosaicGeometry.height); - layout.setTileWidth(mosaicGeometry.width); - layout.setTileHeight(mosaicGeometry.height); - final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); - // hints.put(JAI.KEY_SERIALIZE_DEEP_COPY, Boolean.TRUE); - - for (RenderedImage img : transformed) { - mosaicParams.addSource(img); - log.appendLoggingGeometries(LoggingHelper.MOSAIC_RESULT, img); - } - log.log(LoggingHelper.MOSAIC_RESULT); - - LOGGER.fine("Creating mosaic out of " + queries.size() + " raster tiles"); - mosaic = JAI.create("Mosaic", mosaicParams, hints); - - log.log(mosaic, 0L, "05_mosaic_result"); - } - return mosaic; - } - - /** - * Crops the image representing a full tile set to the required dimension and returns it, but - * keeps minx and miny being zero. - * - * @param fullTilesRaster - * @param tiledImageGridRagne - * @param cropTo - * @return - */ - private RenderedImage cropToRequiredDimension(final RenderedImage fullTilesRaster, - final Rectangle tiledImageGridRagne, final Rectangle cropTo) { - - // int minX = fullTilesRaster.getMinX(); - // int minY = fullTilesRaster.getMinY(); - // int width = fullTilesRaster.getWidth(); - // int height = fullTilesRaster.getHeight(); - - int minX = tiledImageGridRagne.x; - int minY = tiledImageGridRagne.y; - int width = tiledImageGridRagne.width; - int height = tiledImageGridRagne.height; - - Rectangle origDim = new Rectangle(minX, minY, width, height); - if (!origDim.contains(cropTo)) { - throw new IllegalArgumentException("Original image (" + origDim - + ") does not contain desired dimension (" + cropTo + ")"); - } else if (origDim.equals(cropTo)) { - if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.finer("No need to crop image, full tiled dimension and target one " - + "do match: original: " + width + "x" + height + ", target: " - + cropTo.width + "x" + cropTo.height); - } - return fullTilesRaster; - } - - ParameterBlock cropParams = new ParameterBlock(); - - cropParams.addSource(fullTilesRaster);// Source - cropParams.add(Float.valueOf(cropTo.x - tiledImageGridRagne.x)); // x origin for each band - cropParams.add(Float.valueOf(cropTo.y - tiledImageGridRagne.y)); // y origin for each band - cropParams.add(Float.valueOf(cropTo.width));// width for each band - cropParams.add(Float.valueOf(cropTo.height));// height for each band - - final RenderingHints hints = null; - RenderedImage image = JAI.create("Crop", cropParams, hints); - - assert cropTo.x - tiledImageGridRagne.x == image.getMinX(); - assert cropTo.y - tiledImageGridRagne.y == image.getMinY(); - assert cropTo.width == image.getWidth(); - assert cropTo.height == image.getHeight(); - - // assert cropTo.x == image.getMinX(); - // assert cropTo.y == image.getMinY(); - // assert cropTo.width == image.getWidth(); - // assert cropTo.height == image.getHeight(); - return image; - } - - static class ReadParameters { - GeneralEnvelope requestedEnvelope; - - Rectangle dim; - - OverviewPolicy overviewPolicy; - } - - /** - * A simple helper class to guard and easy logging the mosaic geometries in both geographical - * and pixel ranges - */ - private static class LoggingHelper { - - private static final File debugDir = new File(System.getProperty("user.home") - + File.separator + "arcsde_test"); - - static { - if (DEBUG_TO_DISK) { - debugDir.mkdir(); - } - } - - public Level GEOM_LEVEL = Level.FINER; - - public static String REQ_ENV = "Requested envelope"; - - public static String RES_ENV = "Resulting envelope"; - - public static String MOSAIC_ENV = "Resulting mosaiced envelopes"; - - public static String MOSAIC_EXPECTED = "Expected mosaic layout (in pixels)"; - - public static String MOSAIC_RESULT = "Resulting image mosaic layout (in pixels)"; - - private Map<String, StringBuilder> geoms = null; - - LoggingHelper() { - // not much to to - } - - private StringBuilder getGeom(String geomName) { - if (geoms == null) { - geoms = new HashMap<String, StringBuilder>(); - } - StringBuilder sb = geoms.get(geomName); - if (sb == null) { - sb = new StringBuilder("MULTIPOLYGON(\n"); - geoms.put(geomName, sb); - } - return sb; - } - - public void appendLoggingGeometries(String geomName, RenderedImage img) { - if (LOGGER.isLoggable(GEOM_LEVEL)) { - appendLoggingGeometries(geomName, new Rectangle(img.getMinX(), img.getMinY(), img - .getWidth(), img.getHeight())); - } - } - - public void appendLoggingGeometries(String geomName, Rectangle env) { - if (LOGGER.isLoggable(GEOM_LEVEL)) { - appendLoggingGeometries(geomName, new GeneralEnvelope(env)); - } - } - - public void appendLoggingGeometries(String geomName, GeneralEnvelope env) { - if (LOGGER.isLoggable(GEOM_LEVEL)) { - StringBuilder sb = getGeom(geomName); - sb.append(" ((" + env.getMinimum(0) + " " + env.getMinimum(1) + ", " - + env.getMaximum(0) + " " + env.getMinimum(1) + ", " + env.getMaximum(0) - + " " + env.getMaximum(1) + ", " + env.getMinimum(0) + " " - + env.getMaximum(1) + ", " + env.getMinimum(0) + " " + env.getMinimum(1) - + ")),"); - } - } - - public void log(String geomName) { - if (LOGGER.isLoggable(GEOM_LEVEL)) { - StringBuilder sb = getGeom(geomName); - sb.setLength(sb.length() - 1); - sb.append("\n)"); - LOGGER.log(GEOM_LEVEL, geomName + ":\n" + sb.toString()); - } - } - - public void log(RenderedImage image, Long rasterId, String fileName) { - if (DEBUG_TO_DISK) { - LOGGER.warning("BEWARE THE DEBUG FLAG IS TURNED ON! " - + "IF IN PRODUCTION THIS IS A SEVERE MISTAKE!!!"); - // ImageIO.write(FormatDescriptor.create(image, - // Integer.valueOf(DataBuffer.TYPE_BYTE), - // null), "TIFF", new File(debugDir, rasterId.longValue() + fileName + ".tiff")); - - try { - ImageIO.write(image, "TIFF", new File(debugDir, rasterId.longValue() + fileName - + ".tiff")); - } catch (IOException e) { - e.printStackTrace(); - } - } - - } - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEImageReader.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEImageReader.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEImageReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,189 +0,0 @@ -package org.geotools.arcsde.gce; - -import java.awt.Point; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.IndexColorModel; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.WritableRaster; -import java.io.IOException; -import java.util.Collections; -import java.util.Iterator; - -import javax.imageio.ImageReadParam; -import javax.imageio.ImageReader; -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.metadata.IIOMetadata; -import javax.imageio.spi.ImageReaderSpi; -import javax.media.jai.PlanarImage; - -import org.geotools.arcsde.gce.TileReader.TileInfo; - -import com.sun.media.imageioimpl.common.SimpleRenderedImage; - -public class ArcSDEImageReader extends ImageReader { - - private TileReader tileReader; - - private ImageTypeSpecifier typeSpec; - - protected ArcSDEImageReader(final ImageReaderSpi originatingProvider, - final ImageTypeSpecifier typeSpec) { - super(originatingProvider); - this.typeSpec = typeSpec; - } - - @Override - public int getWidth(int imageIndex) throws IOException { - int w = tileReader.getTilesWide() * tileReader.getTileWidth(); - return w; - } - - @Override - public int getHeight(int imageIndex) throws IOException { - int h = tileReader.getTilesHigh() * tileReader.getTileHeight(); - return h; - } - - @Override - public IIOMetadata getImageMetadata(int imageIndex) throws IOException { - return null; - } - - @Override - public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException { - return Collections.singleton(typeSpec).iterator(); - } - - @Override - public int getNumImages(boolean allowSearch) throws IOException { - return 1; - } - - @Override - public IIOMetadata getStreamMetadata() throws IOException { - return null; - } - - @Override - public RenderedImage readAsRenderedImage(int imageIndex, ImageReadParam param) - throws IOException { - // return read(imageIndex, param); - - RenderedImage image = new ArcSDETiledRenderedImage(tileReader, typeSpec); - - // BufferedImage bufferedImage = PlanarImage.wrapRenderedImage(image).getAsBufferedImage(); - // return bufferedImage; - return image; - } - - @Override - public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException { - if (param == null) { - param = getDefaultReadParam(); - } - - RenderedImage rendered = readAsRenderedImage(imageIndex, param); - - BufferedImage bufferedImage = PlanarImage.wrapRenderedImage(rendered).getAsBufferedImage(); - - return bufferedImage; - } - - @Override - public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { - this.tileReader = (TileReader) input; - } - - @SuppressWarnings("unchecked") - private static class ArcSDETiledRenderedImage extends SimpleRenderedImage { - - private TileReader tileReader; - - public ArcSDETiledRenderedImage(TileReader tileReader, ImageTypeSpecifier typeSpec) { - System.err.println(tileReader); - this.tileReader = tileReader; - super.colorModel = typeSpec.getColorModel(); - super.sampleModel = typeSpec.getSampleModel(); - super.height = tileReader.getTilesHigh() * tileReader.getTileHeight(); - super.width = tileReader.getTilesWide() * tileReader.getTileWidth(); - super.minX = 0; - super.minY = 0; - super.tileGridXOffset = 0; - super.tileGridYOffset = 0; - super.tileHeight = tileReader.getTileHeight(); - super.tileWidth = tileReader.getTileWidth(); - } - - private WritableRaster[][] tileCache = null; - - /** - * @see java.awt.image.RenderedImage#getTile(int, int) - */ - public Raster getTile(final int tileX, final int tileY) { - //System.err.printf("getTile(%d, %d)\n", tileX, tileY); - if (tileCache == null) { - tileCache = new WritableRaster[tileReader.getTilesWide()][tileReader.getTilesHigh()]; - } - - WritableRaster currentTile = tileCache[tileX][tileY]; - if (currentTile == null) { - final int tilesWide = tileReader.getTilesWide(); - final int tilesHigh = tileReader.getTilesHigh(); - - for (int ty = 0; ty < tilesHigh; ty++) { - for (int tx = 0; tx < tilesWide; tx++) { - - currentTile = tileCache[tx][ty]; - - if (currentTile == null) { - int x = tileXToX(tx); - int y = tileYToY(ty); - //System.err.println("fetching tile " + tx + "," + ty); - currentTile = fetchTile(x, y); - tileCache[tx][ty] = currentTile; - } - if (tx == tileX && ty == tileY) { - return currentTile; - } - } - } - } - //currentTile = tileCache[tileX][tileY]; - return currentTile; - } - - private WritableRaster fetchTile(final int xOrigin, final int yOrigin) { - final int numBands = sampleModel.getNumBands(); - - final SampleModel tileSampleModel = super.sampleModel.createCompatibleSampleModel( - tileWidth, tileHeight); - - DataBuffer dataBuffer = sampleModel.createDataBuffer(); - for (int bandN = 0; bandN < numBands; bandN++) { - TileInfo tileInfo; - try { - tileInfo = tileReader.next(); - } catch (IOException e) { - throw new RuntimeException(e); - } - - byte[] rawBandData = tileInfo.getTileData(); - - final int numPixels = tileWidth * tileHeight; - for (int pixelN = 0; pixelN < numPixels; pixelN++) { - int val = rawBandData[2 * pixelN + 1] & 0xFF; - dataBuffer.setElem(bandN, pixelN, val); - } - } - - WritableRaster currentTile; - currentTile = Raster.createWritableRaster(tileSampleModel, dataBuffer, new Point(xOrigin, yOrigin)); - return currentTile; - } - - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDERasterFormat.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDERasterFormat.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDERasterFormat.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,437 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.io.File; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.geotools.arcsde.jndi.SharedSessionPool; -import org.geotools.arcsde.session.ArcSDEConnectionConfig; -import org.geotools.arcsde.session.ISession; -import org.geotools.arcsde.session.ISessionPool; -import org.geotools.arcsde.session.SessionPoolFactory; -import org.geotools.arcsde.session.UnavailableConnectionException; -import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; -import org.geotools.coverage.grid.io.AbstractGridFormat; -import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams; -import org.geotools.data.DataSourceException; -import org.geotools.factory.GeoTools; -import org.geotools.factory.Hints; -import org.geotools.parameter.DefaultParameterDescriptorGroup; -import org.geotools.parameter.ParameterGroup; -import org.geotools.util.logging.Logging; -import org.opengis.coverage.grid.Format; -import org.opengis.coverage.grid.GridCoverageWriter; -import org.opengis.parameter.GeneralParameterDescriptor; - -/** - * An implementation of the ArcSDE Raster Format. Based on the ArcGrid module. - * - * @author Saul Farber (saul.farber) - * @author jeichar - * @author Simone Giannecchini (simboss) - * @author Gabriel Roldan (OpenGeo) - * @source $URL: - * http://svn.geotools.org/geotools/trunk/gt/modules/plugin/arcsde/datastore/src/main/java - * /org/geotools/arcsde/gce/ArcSDERasterFormat.java $ - */ -@SuppressWarnings( { "nls", "deprecation" }) -public final class ArcSDERasterFormat extends AbstractGridFormat implements Format { - - protected static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - /** - * Cache of raster metadata objects, where the keys are the URL's representing the full - * connection properties to a given ArcSDE raster, and the value the - * {@link ArcSDERasterGridCoverage2DReader}'s externalized state, so it is not needed to gather - * the raster properties each time. - */ - private final Map<String, RasterDatasetInfo> rasterInfos = new WeakHashMap<String, RasterDatasetInfo>(); - - private final Map<String, ArcSDEConnectionConfig> connectionConfigs = new WeakHashMap<String, ArcSDEConnectionConfig>(); - - private static final ArcSDERasterFormat instance = new ArcSDERasterFormat(); - - private boolean statisticsMandatory = true; - - /** - * Creates an instance and sets the metadata. - */ - private ArcSDERasterFormat() { - setInfo(); - } - - public static ArcSDERasterFormat getInstance() { - return instance; - } - - /** - * Sets the metadata information. - */ - private void setInfo() { - Map<String, String> info = new HashMap<String, String>(); - - info.put("name", "ArcSDE Raster"); - info.put("description", "ArcSDE Raster Format"); - info.put("vendor", "Geotools"); - info.put("docURL", ""); - info.put("version", GeoTools.getVersion().toString()); - mInfo = info; - - readParameters = new ParameterGroup(new DefaultParameterDescriptorGroup(mInfo, - new GeneralParameterDescriptor[] { READ_GRIDGEOMETRY2D, OVERVIEW_POLICY })); - } - - /** - * @param source - * either a {@link String} or {@link File} instance representing the connection URL - * @see AbstractGridFormat#getReader(Object source) - */ - @Override - public AbstractGridCoverage2DReader getReader(Object source) { - return getReader(source, null); - } - - /** - * @param source - * either a {@link String} or {@link File} instance representing the connection URL - * @see AbstractGridFormat#getReader(Object, Hints) - */ - @Override - public AbstractGridCoverage2DReader getReader(final Object source, final Hints hints) { - try { - if (source == null) { - throw new DataSourceException("No source set to read this coverage."); - } - - // this will be our connection string - final String coverageUrl = parseCoverageUrl(source); - - final ArcSDEConnectionConfig connectionConfig = getConnectionConfig(coverageUrl); - - final ISessionPool sessionPool = setupConnectionPool(connectionConfig); - - final RasterDatasetInfo rasterInfo = getRasterInfo(coverageUrl, sessionPool); - - final RasterReaderFactory rasterReaderFactory = new RasterReaderFactory(sessionPool); - - return new ArcSDEGridCoverage2DReaderJAI(this, rasterReaderFactory, rasterInfo, hints); - } catch (IOException dse) { - LOGGER - .log(Level.SEVERE, "Unable to creata ArcSDERasterReader for " + source + ".", - dse); - throw new RuntimeException(dse); - } - } - - private RasterDatasetInfo getRasterInfo(final String coverageUrl, ISessionPool connectionPool) - throws IOException { - - RasterDatasetInfo rasterInfo = rasterInfos.get(coverageUrl); - if (rasterInfo == null) { - synchronized (rasterInfos) { - rasterInfo = rasterInfos.get(coverageUrl); - if (rasterInfo == null) { - ISession scon; - try { - scon = connectionPool.getSession(false); - } catch (UnavailableConnectionException e) { - throw new RuntimeException(e); - } - try { - final String rasterTable; - { - String sdeUrl = coverageUrl; - if (sdeUrl.indexOf(";") != -1) { - /* - * We're not using any extra param anymore. Yet, be cautious cause a - * client may still be using urls with some old extra param, so just - * strip it - */ - sdeUrl = sdeUrl.substring(0, sdeUrl.indexOf(";")); - } - rasterTable = sdeUrl.substring(sdeUrl.indexOf("#") + 1); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Building ArcSDEGridCoverageReader2D for " - + rasterTable); - } - } - - GatherCoverageMetadataCommand command = new GatherCoverageMetadataCommand( - rasterTable, statisticsMandatory); - rasterInfo = scon.issue(command); - rasterInfos.put(coverageUrl, rasterInfo); - } finally { - scon.dispose(); - } - } - } - } - return rasterInfo; - } - - private ArcSDEConnectionConfig getConnectionConfig(final String coverageUrl) { - ArcSDEConnectionConfig sdeConfig; - sdeConfig = connectionConfigs.get(coverageUrl); - if (sdeConfig == null) { - synchronized (connectionConfigs) { - sdeConfig = connectionConfigs.get(coverageUrl); - if (sdeConfig == null) { - sdeConfig = sdeURLToConnectionConfig(new StringBuffer(coverageUrl)); - connectionConfigs.put(coverageUrl, sdeConfig); - } - } - } - return sdeConfig; - } - - /** - * @see AbstractGridFormat#getWriter(Object) - */ - @Override - public GridCoverageWriter getWriter(Object destination) { - // return new ArcGridWriter(destination); - return null; - } - - /** - * @param source - * either a {@link String} or {@link File} instance representing the connection URL - * @see AbstractGridFormat#accepts(Object input) - */ - @Override - public boolean accepts(Object input) { - StringBuffer url; - if (input instanceof File) { - url = new StringBuffer(((File) input).getPath()); - } else if (input instanceof String) { - url = new StringBuffer((String) input); - } else { - return false; - } - try { - sdeURLToConnectionConfig(url); - return true; - } catch (Exception e) { - return false; - } - } - - /** - * @see Format#getName() - */ - @Override - public String getName() { - return this.mInfo.get("name"); - } - - /** - * @see Format#getDescription() - */ - @Override - public String getDescription() { - return this.mInfo.get("description"); - } - - /** - * @see Format#getVendor() - */ - @Override - public String getVendor() { - return this.mInfo.get("vendor"); - } - - /** - * @see Format#getDocURL() - */ - @Override - public String getDocURL() { - return this.mInfo.get("docURL"); - } - - /** - * @see Format#getVersion() - */ - @Override - public String getVersion() { - return this.mInfo.get("version"); - } - - /** - * Retrieves the default instance for the {@link ArcSDERasterFormat} of the - * {@link GeoToolsWriteParams} to control the writing process. - * - * @return a default instance for the {@link ArcSDERasterFormat} of the - * {@link GeoToolsWriteParams} to control the writing process. - * @see AbstractGridFormat#getDefaultImageIOWriteParameters() - */ - @Override - public GeoToolsWriteParams getDefaultImageIOWriteParameters() { - throw new UnsupportedOperationException("ArcSDE Rasters are read only for now."); - } - - // //////////////// - - /** - * @param input - * either a {@link String} or a {@link File} instance representing the connection URL - * to a given coverage - * @return the connection URL as a string - */ - private String parseCoverageUrl(Object input) { - String coverageUrl; - if (input instanceof String) { - coverageUrl = (String) input; - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("connecting to ArcSDE Raster: " + coverageUrl); - } - } else if (input instanceof File) { - coverageUrl = ((File) input).getPath(); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("connectiong via file-hack to ArcSDE Raster: " + coverageUrl); - } - } else { - throw new IllegalArgumentException("Unsupported input type: " + input.getClass()); - } - return coverageUrl; - } - - /** - * Checks the input provided to this {@link ArcSDERasterGridCoverage2DReader} and sets all the - * other objects and flags accordingly. - * - * @param sdeUrl - * a url representing the connection parameters to an arcsde server instance provied - * to this {@link ArcSDERasterGridCoverage2DReader}. - * @throws IOException - */ - private ISessionPool setupConnectionPool(ArcSDEConnectionConfig sdeConfig) throws IOException { - - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Getting ArcSDE connection pool for " + sdeConfig); - } - - ISessionPool sessionPool; - sessionPool = SharedSessionPool.getInstance(sdeConfig, SessionPoolFactory.getInstance()); - - return sessionPool; - } - - /** - * @param sdeUrl - * - A StringBuffer containing a string of form - * 'sde://user:pass@sdehost:[port]/[dbname] - * @return a ConnectionConfig object representing these parameters - */ - public static ArcSDEConnectionConfig sdeURLToConnectionConfig(StringBuffer sdeUrl) { - // annoyingly, geoserver currently stores the user-entered SDE string as - // a File, and passes us the - // File object. The File object strips the 'sde://user...' into a - // 'sde:/user..'. So we need to check - // for both forms of the url. - String sdeHost, sdeUser, sdePass, sdeDBName; - int sdePort; - if (sdeUrl.indexOf("sde:/") == -1) { - throw new IllegalArgumentException( - "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName -- Got " - + sdeUrl); - } - if (sdeUrl.indexOf("sde://") == -1) { - sdeUrl.delete(0, 5); - } else { - sdeUrl.delete(0, 6); - } - - int idx = sdeUrl.indexOf(":"); - if (idx == -1) { - throw new IllegalArgumentException( - "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); - } - sdeUser = sdeUrl.substring(0, idx); - sdeUrl.delete(0, idx); - - idx = sdeUrl.indexOf("@"); - if (idx == -1) { - throw new IllegalArgumentException( - "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); - } - sdePass = sdeUrl.substring(1, idx); - sdeUrl.delete(0, idx); - - idx = sdeUrl.indexOf(":"); - if (idx == -1) { - // there's no "port" specification. Assume 5151; - sdePort = 5151; - - idx = sdeUrl.indexOf("/"); - if (idx == -1) { - throw new IllegalArgumentException( - "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); - } - sdeHost = sdeUrl.substring(1, idx).toString(); - sdeUrl.delete(0, idx); - } else { - sdeHost = sdeUrl.substring(1, idx).toString(); - sdeUrl.delete(0, idx); - - idx = sdeUrl.indexOf("/"); - if (idx == -1) { - throw new IllegalArgumentException( - "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); - } - sdePort = Integer.parseInt(sdeUrl.substring(1, idx).toString()); - sdeUrl.delete(0, idx); - } - - idx = sdeUrl.indexOf("#"); - if (idx == -1) { - throw new IllegalArgumentException( - "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); - } - sdeDBName = sdeUrl.substring(1, idx).toString(); - sdeUrl.delete(0, idx); - - Map<String, String> params = new HashMap<String, String>(); - params.put(ArcSDEConnectionConfig.SERVER_NAME_PARAM_NAME, sdeHost); - params.put(ArcSDEConnectionConfig.PORT_NUMBER_PARAM_NAME, String.valueOf(sdePort)); - params.put(ArcSDEConnectionConfig.INSTANCE_NAME_PARAM_NAME, sdeDBName); - params.put(ArcSDEConnectionConfig.USER_NAME_PARAM_NAME, sdeUser); - params.put(ArcSDEConnectionConfig.PASSWORD_PARAM_NAME, sdePass); - params.put(ArcSDEConnectionConfig.MIN_CONNECTIONS_PARAM_NAME, "1"); - params.put(ArcSDEConnectionConfig.MAX_CONNECTIONS_PARAM_NAME, "20"); - params.put(ArcSDEConnectionConfig.CONNECTION_TIMEOUT_PARAM_NAME, "-1");// do not wait - - ArcSDEConnectionConfig config = ArcSDEConnectionConfig.fromMap(params); - return config; - } - - /** - * Used by test code to indicate wether to fail when a raster lacks statistics, since we can't - * create statistics with the ArcSDE Java API - * - * @param statisticsMandatory - */ - void setStatisticsMandatory(final boolean statisticsMandatory) { - this.statisticsMandatory = statisticsMandatory; - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDETiledImageInputStream.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDETiledImageInputStream.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDETiledImageInputStream.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,139 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.io.IOException; - -import javax.imageio.stream.ImageInputStream; -import javax.imageio.stream.ImageInputStreamImpl; - -import org.geotools.arcsde.gce.TileReader.TileInfo; - -/** - * An {@link ImageInputStream} that reads ArcSDE raster tiles in a band interleaved order. - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL: - * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org - * /geotools/arcsde/gce/ArcSDETiledImageInputStream.java $ - */ -final class ArcSDETiledImageInputStream extends ImageInputStreamImpl implements ImageInputStream { - - private final TileReader tileReader; - - private final int tileDataLength; - - private byte[] currTileData; - - private int currTileDataIndex; - - private int currTileIndex = -1; - - private final int length; - - public ArcSDETiledImageInputStream(final TileReader tileReader) { - super(); - this.tileReader = tileReader; - final int bytesPerTile = tileReader.getBytesPerTile(); - this.tileDataLength = bytesPerTile; - this.currTileData = new byte[bytesPerTile]; - // force load at the first read invocation - this.currTileDataIndex = tileDataLength; - - final int tilesWide = tileReader.getTilesWide(); - final int tilesHigh = tileReader.getTilesHigh(); - final int numberOfBands = tileReader.getNumberOfBands(); - - length = bytesPerTile * tilesWide * tilesHigh * numberOfBands; - } - - /** - * Returns the computed lenght of the stream based on the tile dimensions, number of tiles, - * number of bands, and bits per sample - */ - @Override - public long length() { - return length; - } - - @Override - public int read() throws IOException { - final byte[] data = getTileData(); - if (data == null) { - close(); - return -1; - } - byte b = data[currTileDataIndex]; - ++currTileDataIndex; - ++streamPos; - return b; - } - - @Override - public int read(byte[] buff, int off, int len) throws IOException { - final byte[] data = getTileData(); - if (data == null) { - close(); - return -1; - } - final int available = data.length - currTileDataIndex; - final int count = Math.min(available, len); - System.arraycopy(data, currTileDataIndex, buff, off, count); - currTileDataIndex += count; - streamPos += count; - return count; - } - - /** - * Fetches a tile from the {@code tileReader} if necessary and returns the current tile data. - * <p> - * It is needed to fetch a new tile if {@link #currTileDataIndex} indicates all the current tile - * data has been already read. If so, {@code currTileDataIndex} is reset to 0. The {@code read} - * operations are responsible of incrementing {@code currTileDataIndex} depending on how many - * bytes have been consumed from the tile data returned by this method. - * </p> - * - * @return {@code null} if there's no more tiles to fetch, the current tile data otherwise - * @throws IOException - */ - private byte[] getTileData() throws IOException { - if (currTileDataIndex == tileDataLength) { - if (!tileReader.hasNext()) { - return null; - } - - currTileDataIndex = 0; - ++currTileIndex; - TileInfo tile = tileReader.next(); - currTileData = tile.getTileData(); - - if (!tileReader.hasNext()) { - tileReader.dispose(); - } - } - return currTileData; - } - - @Override - public void close() throws IOException { - tileReader.dispose(); - super.close(); - } -} \ No newline at end of file Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/BitmaskToNoDataConverter.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/BitmaskToNoDataConverter.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/BitmaskToNoDataConverter.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,234 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_U; - -import java.awt.Dimension; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * A helper class to set nodata values directly onto the arcsde tile's returned {@code byte[]} - * - * @author Gabriel Roldan - * @since 2.5.6 - * @version $Id$ - * - */ -class BitmaskToNoDataConverter { - - public static final BitmaskToNoDataConverter NO_ACTION_CONVERTER = new BitmaskToNoDataConverter( - 0, 0, null) { - - @Override - public void setNoData(Long bandId, byte[] tileData, byte[] bitMaskData) { - // no action - } - - @Override - public void setAll(Long bandId, byte[] tileData) { - // no action - } - - @Override - public void setNoData(Long bandId, int sampleN, byte[] tileData) { - // no action - } - - }; - - protected final int pixelsPerTile; - - protected final Map<Long, byte[]> byBandIdNoDataValues; - - protected final int bitsPerSample; - - /** - * - * @param pixelsPerTile - * @param bitsPerSample - * @param byBandIdNoDataValues - */ - private BitmaskToNoDataConverter(final int pixelsPerTile, final int bitsPerSample, - final Map<Long, byte[]> byBandIdNoDataValues) { - - this.pixelsPerTile = pixelsPerTile; - this.bitsPerSample = bitsPerSample; - this.byBandIdNoDataValues = byBandIdNoDataValues; - - } - - /** - * Creates a "nodata setter" for the given raster determined by the raster dataset and the - * raster index inside the dataset - * - * @param rasterInfo - * @param rasterIndex - * @return - */ - public static BitmaskToNoDataConverter getInstance(final RasterDatasetInfo rasterInfo, - final long rasterId) { - - final int rasterIndex = rasterInfo.getRasterIndex(rasterId); - final int numBands = rasterInfo.getNumBands(); - final RasterCellType targetType = rasterInfo.getTargetCellType(rasterIndex); - - Map<Long, byte[]> byBandIdNoDataValues = new HashMap<Long, byte[]>(); - - Dimension tileDimension = rasterInfo.getTileDimension(rasterIndex); - final int samplesPerTile = tileDimension.width * tileDimension.height; - - for (int bandN = 0; bandN < numBands; bandN++) { - long bandId = rasterInfo.getBand(rasterIndex, bandN).getBandId(); - Number noDataValue = rasterInfo.getNoDataValue(rasterIndex, bandN); - byte[] noDataValueBytes = toBytes(noDataValue, targetType); - byBandIdNoDataValues.put(Long.valueOf(bandId), noDataValueBytes); - } - - final int bitsPerSample = targetType.getBitsPerSample(); - BitmaskToNoDataConverter noDataSetter; - if (targetType == TYPE_8BIT_U) { - noDataSetter = new Unsigned8bitConverter(samplesPerTile, bitsPerSample, - byBandIdNoDataValues); - } else { - noDataSetter = new BitmaskToNoDataConverter(samplesPerTile, bitsPerSample, - byBandIdNoDataValues); - } - - return noDataSetter; - } - - static byte[] toBytes(final Number noDataValue, final RasterCellType targetType) { - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - DataOutputStream writer = new DataOutputStream(out); - - try { - switch (targetType) { - case TYPE_16BIT_S: - writer.writeShort(noDataValue.intValue()); - break; - case TYPE_16BIT_U: - writer.writeShort(noDataValue.intValue()); - break; - case TYPE_32BIT_REAL: - writer.writeFloat(noDataValue.floatValue()); - break; - case TYPE_32BIT_S: - writer.writeInt(noDataValue.intValue()); - break; - case TYPE_32BIT_U: - writer.writeInt(noDataValue.intValue()); - break; - case TYPE_64BIT_REAL: - writer.writeDouble(noDataValue.doubleValue()); - break; - case TYPE_8BIT_S: - writer.writeByte(noDataValue.byteValue()); - break; - case TYPE_8BIT_U: - writer.writeByte(noDataValue.intValue()); - break; - default: - throw new UnsupportedOperationException( - "No no-data converter exists for sample type " + targetType); - } - } catch (IOException e) { - throw new RuntimeException("Can't happen!", e); - } - - byte[] no_data_bytes = out.toByteArray(); - return no_data_bytes; - } - - /** - * Returns whether the sample N in the bitmask byte array is marked as a no-data pixel - */ - public final boolean isNoData(int sampleN, byte[] bitmaskData) { - boolean isNoData = ((bitmaskData[sampleN / 8] >> (7 - (sampleN % 8))) & 0x01) == 0x00; - return isNoData; - } - - /** - * Sets all the samples of {@code tileData} marked as no-data pixel in {@code bitmaskData} to - * the no-data value for band {@code bandId} - */ - public void setNoData(final Long bandId, final byte[] tileData, final byte[] bitmaskData) { - for (int sampleN = 0; sampleN < pixelsPerTile; sampleN++) { - if (isNoData(sampleN, bitmaskData)) { - setNoData(bandId, sampleN, tileData); - } - } - } - - /** - * Sets all the samples in {@code tileData} to the no-data value for the band {@code bandId} - * <p> - * Default implementation is to call {@link #setNoData(Long, int, byte[])} as many times as - * number of samples in a tile. Subclasses may override to optimize. - * </p> - */ - public void setAll(final Long bandId, final byte[] tileData) { - for (int sampleN = 0; sampleN < pixelsPerTile; sampleN++) { - setNoData(bandId, sampleN, tileData); - } - } - - /** - * Sets the sample N for the band {@code bandId} on {@code tileData} to the no-data value - */ - public void setNoData(final Long bandId, final int sampleN, final byte[] tileData) { - byte[] noData = byBandIdNoDataValues.get(bandId); - int pixArrayOffset = (sampleN * bitsPerSample) / 8; - System.arraycopy(noData, 0, tileData, pixArrayOffset, noData.length); - } - - /** - * A subclass that provides some optimization for the case where the target cell type is - * {@link RasterCellType#TYPE_8BIT_U} - */ - static final class Unsigned8bitConverter extends BitmaskToNoDataConverter { - - public Unsigned8bitConverter(final int samplesPerTile, final int bitsPerSample, - final Map<Long, byte[]> byBandIdNoDataValues) { - super(samplesPerTile, bitsPerSample, byBandIdNoDataValues); - } - - /** - * Overrides to use the faster {@link Arrays#fill(byte[], byte)} method rather than calling - * {@link #setNoData(Long, int, byte[])} {@code samplesPerTile} times - */ - @Override - public void setAll(Long bandId, byte[] tileData) { - byte noDataValue = byBandIdNoDataValues.get(bandId)[0]; - Arrays.fill(tileData, noDataValue); - } - - @Override - public void setNoData(Long bandId, int sampleN, byte[] tileData) { - byte noDataValue = byBandIdNoDataValues.get(bandId)[0]; - tileData[sampleN] = noDataValue; - } - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/CompressionType.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/CompressionType.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/CompressionType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,61 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.util.NoSuchElementException; - -import com.esri.sde.sdk.client.SeRaster; - -/** - * An enumeration that mirrors the different possible raster compression types in Arcsde (ie, - * {@code SeRaster#SE_COMPRESSION_*}) - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL$ - */ -enum CompressionType { - COMPRESSION_JP2, COMPRESSION_JPEG, COMPRESSION_LZ77, COMPRESSION_NONE; - static { - COMPRESSION_JP2.setSdeTypeId(SeRaster.SE_COMPRESSION_JP2); - COMPRESSION_JPEG.setSdeTypeId(SeRaster.SE_COMPRESSION_JPEG); - COMPRESSION_LZ77.setSdeTypeId(SeRaster.SE_COMPRESSION_LZ77); - COMPRESSION_NONE.setSdeTypeId(SeRaster.SE_COMPRESSION_NONE); - } - - private int typeId; - - private void setSdeTypeId(int typeId) { - this.typeId = typeId; - } - - public int getSeCompressionType() { - return this.typeId; - } - - public static CompressionType valueOf(final int seCompressionType) { - for (CompressionType type : CompressionType.values()) { - if (type.getSeCompressionType() == seCompressionType) { - return type; - } - } - throw new NoSuchElementException("Compression type " + seCompressionType - + " does not exist"); - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/DefaultTiledRasterReader.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/DefaultTiledRasterReader.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/DefaultTiledRasterReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,224 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Dimension; -import java.awt.Rectangle; -import java.awt.RenderingHints; -import java.awt.image.ColorModel; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.renderable.ParameterBlock; -import java.io.IOException; -import java.util.logging.Logger; - -import javax.imageio.ImageReadParam; -import javax.imageio.ImageReader; -import javax.imageio.ImageTypeSpecifier; -import javax.imageio.spi.ImageReaderSpi; -import javax.imageio.stream.ImageInputStream; -import javax.media.jai.ImageLayout; -import javax.media.jai.JAI; - -import org.geotools.arcsde.session.ISessionPool; -import org.geotools.util.logging.Logging; - -import com.esri.sde.sdk.client.SeQuery; - -/** - * The default implementation for {@link TiledRasterReader}. - * <p> - * This implementation holds a connection and an open {@link SeQuery query} until the reader is - * exhausted or {@link #dispose()} is called. - * </p> - * - * @author Gabriel Roldan - * @version $Id$ - * @since 2.5.7 - */ -class DefaultTiledRasterReader implements TiledRasterReader { - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - private RasterDatasetInfo rasterInfo; - - private final ISessionPool sessionPool; - - /** - * Creates an {@link DefaultTiledRasterReader} that uses the given connection to fetch raster - * data for the given {@link RasterDatasetInfo rasterInfo}. - * <p> - * </p> - * - * @param sessionPool - * where to grab sessions from to query the rasters described by {@code rasterInfo} - * @param rasterInfo - * @throws IOException - */ - public DefaultTiledRasterReader(final ISessionPool sessionPool, - final RasterDatasetInfo rasterInfo) throws IOException { - this.sessionPool = sessionPool; - this.rasterInfo = rasterInfo; - } - - /** - * @see org.geotools.arcsde.gce.TiledRasterReader#read - */ - public RenderedImage read(final long rasterId, final int pyramidLevel, final Rectangle tileRange) - throws IOException { - final RenderedImage rasterImage; - - rasterImage = getRasterMatchingTileRange(rasterId, pyramidLevel, tileRange); - - return rasterImage; - } - - private RenderedImage getRasterMatchingTileRange(final long rasterId, int pyramidLevel, - final Rectangle matchingTiles) throws IOException { - - final TileReader tileReader; - tileReader = TileReaderFactory.getInstance(sessionPool, rasterInfo, rasterId, pyramidLevel, - matchingTiles); - - // covers an area of full tiles - final RenderedImage fullTilesRaster; - - /* - * Create the tiled raster covering the full area of the matching tiles - */ - - fullTilesRaster = createTiledRaster(tileReader, matchingTiles, rasterId); - - return fullTilesRaster; - } - - /** - * @param tileReader - * @param matchingTiles - * @param rasterId - * @return - * @throws IOException - */ - private RenderedImage createTiledRaster(final TileReader tileReader, - final Rectangle matchingTiles, final long rasterId) throws IOException { - // Prepare temporary colorModel and sample model, needed to build the final - // ArcSDEPyramidLevel level; - final Dimension tiledImageSize; - final ColorModel colorModel; - final SampleModel sampleModel; - { - final int tiledImageWidth = tileReader.getTilesWide() * tileReader.getTileWidth(); - final int tiledImageHeight = tileReader.getTilesHigh() * tileReader.getTileHeight(); - tiledImageSize = new Dimension(tiledImageWidth, tiledImageHeight); - - final ImageTypeSpecifier fullImageSpec = rasterInfo.getRenderedImageSpec(rasterId); - colorModel = fullImageSpec.getColorModel(); - sampleModel = fullImageSpec.getSampleModel(tiledImageWidth, tiledImageHeight); - } - - final int tileWidth = tileReader.getTileWidth(); - final int tileHeight = tileReader.getTileHeight(); - - // building the final image layout - final ImageLayout imageLayout; - { - // the value for the resulting image.getMinX() and image.getMinY() reflecting its - // position in the whole image at the pyramid level - int minX = (matchingTiles.x * tileWidth); - int minY = (matchingTiles.y * tileHeight); - - int width = tiledImageSize.width; - int height = tiledImageSize.height; - - int tileGridXOffset = minX; - int tileGridYOffset = minY; - - // imageLayout = new ImageLayout(minX, minY, width, height, tileGridXOffset, - // tileGridYOffset, tileWidth, tileHeight, sampleModel, colorModel); - imageLayout = new ImageLayout(0, 0, width, height, 0, 0, tileWidth, tileHeight, - sampleModel, colorModel); - } - - // Finally, build the image input stream - final ImageInputStream raw; - final ImageReader readerInstance; - { - final long[] imageOffsets = new long[] { 0 }; - final Dimension[] imageDimensions = new Dimension[] { tiledImageSize }; - - final ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); -// -// final ImageInputStream tiledImageInputStream; -// tiledImageInputStream = new ArcSDETiledImageInputStream(tileReader); -// raw = new RawImageInputStream(tiledImageInputStream, its, imageOffsets, imageDimensions); -// ImageReaderSpi imageIOSPI = new RawImageReaderSpi(); -// readerInstance = imageIOSPI.createReaderInstance(); - - // final InputStream is = new RasterInputStream(tileReader); - // final ImageInputStream tiledImageInputStream; - // tiledImageInputStream = ImageIO.createImageInputStream(is); - // - // raw = new RawImageInputStream(tiledImageInputStream, its, imageOffsets, - // imageDimensions); - // ImageReaderSpi imageIOSPI = new RawImageReaderSpi(); - // readerInstance = imageIOSPI.createReaderInstance(); - - - ArcSDEImageReader reader = new ArcSDEImageReader((ImageReaderSpi)null, its); - reader.setInput(tileReader, true, true); - - RenderedImage image = reader.readAsRenderedImage(0, null); - //image.getData(); - return image; - } - - // First operator: read the image -// final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, imageLayout); -// -// ParameterBlock pb = new ParameterBlock(); -// pb.add(raw);// Input -// /* -// * image index, always 0 since we're already fetching the required pyramid level -// */ -// pb.add(Integer.valueOf(0)); // Image index -// pb.add(Boolean.FALSE); // Read metadata -// pb.add(Boolean.FALSE);// Read thumbnails -// pb.add(Boolean.FALSE);// Verify input -// pb.add(null);// Listeners -// pb.add(null);// Locale -// final ImageReadParam rParam = new ImageReadParam(); -// pb.add(rParam);// ReadParam -// pb.add(readerInstance);// Reader -// -// RenderedImage image = JAI.create("ImageRead", pb, hints); -// // image.getData(); -// // // translate -// // int minX = (matchingTiles.x * tileWidth); -// // int minY = (matchingTiles.y * tileHeight); -// // pb = new ParameterBlock(); -// // pb.addSource(image); -// // pb.add(Float.valueOf(minX)); -// // pb.add(Float.valueOf(minY)); -// // pb.add(null); -// // -// // image = JAI.create("translate", pb); -// -// return image; - - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/GatherCoverageMetadataCommand.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/GatherCoverageMetadataCommand.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/GatherCoverageMetadataCommand.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,521 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.geom.Point2D; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferByte; -import java.awt.image.DataBufferUShort; -import java.awt.image.IndexColorModel; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.geotools.arcsde.ArcSdeException; -import org.geotools.arcsde.session.Command; -import org.geotools.arcsde.session.ISession; -import org.geotools.arcsde.util.ArcSDEUtils; -import org.geotools.data.DataSourceException; -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.referencing.crs.DefaultEngineeringCRS; -import org.geotools.util.logging.Logging; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -import com.esri.sde.sdk.client.SDEPoint; -import com.esri.sde.sdk.client.SeColumnDefinition; -import com.esri.sde.sdk.client.SeConnection; -import com.esri.sde.sdk.client.SeCoordinateReference; -import com.esri.sde.sdk.client.SeException; -import com.esri.sde.sdk.client.SeExtent; -import com.esri.sde.sdk.client.SeQuery; -import com.esri.sde.sdk.client.SeRaster; -import com.esri.sde.sdk.client.SeRasterAttr; -import com.esri.sde.sdk.client.SeRasterBand; -import com.esri.sde.sdk.client.SeRasterColumn; -import com.esri.sde.sdk.client.SeRow; -import com.esri.sde.sdk.client.SeSqlConstruct; -import com.esri.sde.sdk.client.SeTable; - -/** - * Session command to gather information for an ArcSDE Raster, such as dimensions, spatial extent, - * number of pyramid levels, etc; into a {@link RasterDatasetInfo} - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.8 - * @source $UR$ - */ -public class GatherCoverageMetadataCommand extends Command<RasterDatasetInfo> { - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - private final boolean statisticsMandatory; - - private final String rasterTableName; - - public GatherCoverageMetadataCommand(final String rasterTableName, - final boolean statisticsMandatory) { - this.rasterTableName = rasterTableName; - this.statisticsMandatory = statisticsMandatory; - } - - /** - * @throws IOException - * if an exception occurs accessing the raster metadata - * @throws SeException - * @throws IllegalArgumentException - * if the raster has no CRS, contains no raster attributes, has no pyramids, no - * bands or no statistics - */ - @Override - public RasterDatasetInfo execute(ISession session, SeConnection connection) throws SeException, - IOException { - LOGGER.fine("Gathering raster dataset metadata for " + rasterTableName); - final String[] rasterColumns = getRasterColumns(connection, rasterTableName); - final List<RasterInfo> rastersLayoutInfo = new ArrayList<RasterInfo>(); - { - final List<SeRasterAttr> rasterAttributes; - rasterAttributes = getSeRasterAttr(connection, rasterTableName, rasterColumns); - - if (rasterAttributes.size() == 0) { - throw new IllegalArgumentException("Table " + rasterTableName - + " contains no raster datasets"); - } - - final CoordinateReferenceSystem coverageCrs; - - /* - * by bandId map of colormaps. The dataset may be composed of more than one raster so we - * gather all the colormaps once and held them here by rasterband id - */ - final Map<Long, IndexColorModel> rastersColorMaps; - { - final SeRasterColumn rasterColumn; - final SeRasterBand sampleBand; - final long rasterColumnId; - final int bitsPerSample; - try { - SeRasterAttr ratt = rasterAttributes.get(0); - rasterColumn = new SeRasterColumn(connection, ratt.getRasterColumnId()); - rasterColumnId = rasterColumn.getID().longValue(); - sampleBand = ratt.getBands()[0]; - bitsPerSample = RasterCellType.valueOf(ratt.getPixelType()).getBitsPerSample(); - } catch (SeException e) { - throw new ArcSdeException(e); - } - final SeCoordinateReference seCoordRef = rasterColumn.getCoordRef(); - if (seCoordRef == null) { - throw new IllegalArgumentException(rasterTableName - + " has no coordinate reference system set"); - } - LOGGER.finer("Looking CRS for raster column " + rasterTableName); - coverageCrs = ArcSDEUtils.findCompatibleCRS(seCoordRef); - if (DefaultEngineeringCRS.CARTESIAN_2D == coverageCrs) { - LOGGER.warning("Raster " + rasterTableName - + " has not CRS set, using DefaultEngineeringCRS.CARTESIAN_2D"); - } - if (sampleBand.hasColorMap()) { - rastersColorMaps = loadColorMaps(rasterColumnId, bitsPerSample, connection); - } else { - rastersColorMaps = Collections.emptyMap(); - } - - } - try { - for (SeRasterAttr rAtt : rasterAttributes) { - LOGGER.fine("Gathering raster metadata for " + rasterTableName + " raster " - + rAtt.getRasterId().longValue()); - - if (rAtt.getMaxLevel() == 0) { - throw new IllegalArgumentException( - "Raster cotains no pyramid levels, we don't support non pyramid rasters"); - } - if (rAtt.getNumBands() == 0) { - throw new IllegalArgumentException("Raster " - + rAtt.getRasterId().longValue() + " in " + rasterTableName - + " contains no raster attribtues"); - } - if (this.statisticsMandatory && !rAtt.getBandInfo(1).hasStats()) { - throw new IllegalArgumentException(rasterTableName - + " has no statistics generated (or not all it's rasters have). " - + "Please use sderaster -o stats to create them before use"); - } - - RasterInfo rasterInfo = new RasterInfo(rAtt, coverageCrs); - rastersLayoutInfo.add(rasterInfo); - - final GeneralEnvelope originalEnvelope; - originalEnvelope = calculateOriginalEnvelope(rAtt, coverageCrs); - rasterInfo.setOriginalEnvelope(originalEnvelope); - final List<RasterBandInfo> bands; - bands = setUpBandInfo(connection, rAtt, rastersColorMaps); - rasterInfo.setBands(bands); - if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.finer("Gathered metadata for " + rasterTableName + "#" - + rAtt.getRasterId().longValue() + ":\n" + rasterInfo.toString()); - } - } - } catch (SeException e) { - throw new ArcSdeException("Gathering raster dataset information", e); - } - } - - RasterDatasetInfo rasterInfo = new RasterDatasetInfo(); - rasterInfo.setRasterTable(rasterTableName); - rasterInfo.setRasterColumns(rasterColumns); - rasterInfo.setPyramidInfo(rastersLayoutInfo); - - return rasterInfo; - } - - private String[] getRasterColumns(final SeConnection scon, final String rasterTable) - throws IOException, SeException { - - String[] rasterColumns; - SeTable sTable = new SeTable(scon, rasterTable); - SeColumnDefinition[] cols; - try { - cols = sTable.describe(); - } catch (SeException e) { - throw new ArcSdeException("Exception fetching the list of columns for table " - + rasterTable, e); - } - List<String> fetchColumns = new ArrayList<String>(cols.length / 2); - for (int i = 0; i < cols.length; i++) { - if (cols[i].getType() == SeColumnDefinition.TYPE_RASTER) - fetchColumns.add(cols[i].getName()); - } - if (fetchColumns.size() == 0) { - throw new DataSourceException("Couldn't find any TYPE_RASTER columns in ArcSDE table " - + rasterTable); - } - - rasterColumns = fetchColumns.toArray(new String[fetchColumns.size()]); - return rasterColumns; - } - - private GeneralEnvelope calculateOriginalEnvelope(final SeRasterAttr rasterAttributes, - CoordinateReferenceSystem coverageCrs) throws IOException { - SeExtent sdeExtent; - try { - sdeExtent = rasterAttributes.getExtent(); - } catch (SeException e) { - throw new ArcSdeException("Exception getting the raster extent", e); - } - GeneralEnvelope originalEnvelope = new GeneralEnvelope(coverageCrs); - originalEnvelope.setRange(0, sdeExtent.getMinX(), sdeExtent.getMaxX()); - originalEnvelope.setRange(1, sdeExtent.getMinY(), sdeExtent.getMaxY()); - return originalEnvelope; - } - - private List<SeRasterAttr> getSeRasterAttr(SeConnection scon, String rasterTable, - String[] rasterColumns) throws IOException { - - LOGGER.fine("Gathering raster attributes for " + rasterTable); - SeRasterAttr rasterAttributes; - LinkedList<SeRasterAttr> rasterAttList = new LinkedList<SeRasterAttr>(); - SeQuery query = null; - try { - query = new SeQuery(scon, rasterColumns, new SeSqlConstruct(rasterTable)); - query.prepareQuery(); - query.execute(); - - SeRow row = query.fetch(); - while (row != null) { - rasterAttributes = row.getRaster(0); - rasterAttList.addFirst(rasterAttributes); - row = query.fetch(); - } - } catch (SeException se) { - throw new ArcSdeException("Error fetching raster attributes for " + rasterTable, se); - } finally { - if (query != null) { - try { - query.close(); - } catch (SeException e) { - throw new ArcSdeException(e); - } - } - } - LOGGER.fine("Found " + rasterAttList.size() + " raster attributes for " + rasterTable); - return rasterAttList; - } - - /** - * - * @param band - * @param scon - * @return - * @throws ArcSdeException - */ - private Map<Long, IndexColorModel> loadColorMaps(final long rasterColumnId, - final int bitsPerSample, SeConnection scon) throws IOException { - LOGGER.fine("Reading colormap for raster column " + rasterColumnId); - - final String auxTableName = getAuxTableName(rasterColumnId, scon); - LOGGER.fine("Quering auxiliary table " + auxTableName + " for color map data"); - - Map<Long, IndexColorModel> colorMaps = new HashMap<Long, IndexColorModel>(); - SeQuery query = null; - try { - SeSqlConstruct sqlConstruct = new SeSqlConstruct(); - sqlConstruct.setTables(new String[] { auxTableName }); - String whereClause = "TYPE = 3"; - sqlConstruct.setWhere(whereClause); - - query = new SeQuery(scon, new String[] { "RASTERBAND_ID", "OBJECT" }, sqlConstruct); - query.prepareQuery(); - query.execute(); - - long bandId; - ByteArrayInputStream colorMapIS; - DataBuffer colorMapData; - IndexColorModel colorModel; - - SeRow row = query.fetch(); - while (row != null) { - bandId = ((Number) row.getObject(0)).longValue(); - colorMapIS = row.getBlob(1); - - colorMapData = readColorMap(colorMapIS); - colorModel = RasterUtils.sdeColorMapToJavaColorModel(colorMapData, bitsPerSample); - - colorMaps.put(Long.valueOf(bandId), colorModel); - - row = query.fetch(); - } - } catch (SeException e) { - throw new ArcSdeException("Error fetching colormap data for column " + rasterColumnId - + " from table " + auxTableName, e); - } finally { - if (query != null) { - try { - query.close(); - } catch (SeException e) { - LOGGER.log(Level.INFO, "ignoring exception when closing query to " - + "fetch colormap data", e); - } - } - } - LOGGER.fine("Read color map data for " + colorMaps.size() + " rasters"); - return colorMaps; - } - - private DataBuffer readColorMap(final ByteArrayInputStream colorMapIS) throws IOException { - - final DataInputStream dataIn = new DataInputStream(colorMapIS); - // discard unneeded data - dataIn.readInt(); - - final int colorSpaceType = dataIn.readInt(); - final int numBanks; - if (colorSpaceType == SeRaster.SE_COLORMAP_RGB) { - numBanks = 3; - } else if (colorSpaceType == SeRaster.SE_COLORMAP_RGBA) { - numBanks = 4; - } else { - throw new IllegalStateException("Got unknown colormap type: " + colorSpaceType); - } - LOGGER.finest("Colormap has " + numBanks + " color components"); - - final int buffType = dataIn.readInt(); - final int numElems = dataIn.readInt(); - LOGGER.finest("ColorMap length: " + numElems); - - final DataBuffer buff; - if (buffType == SeRaster.SE_COLORMAP_DATA_BYTE) { - LOGGER.finest("Creating Byte data buffer for " + numBanks + " banks and " + numElems - + " elements per bank"); - buff = new DataBufferByte(numElems, numBanks); - for (int elem = 0; elem < numElems; elem++) { - for (int bank = 0; bank < numBanks; bank++) { - int val = dataIn.readUnsignedByte(); - buff.setElem(bank, elem, val); - } - } - } else if (buffType == SeRaster.SE_COLORMAP_DATA_SHORT) { - LOGGER.finest("Creating Short data buffer for " + numBanks + " banks and " + numElems - + " elements per bank"); - buff = new DataBufferUShort(numElems, numBanks); - for (int elem = 0; elem < numElems; elem++) { - for (int bank = 0; bank < numBanks; bank++) { - int val = dataIn.readUnsignedShort(); - buff.setElem(bank, elem, val); - } - } - } else { - throw new IllegalStateException("Unknown databuffer type from colormap header: " - + buffType + " expected one of TYPE_BYTE, TYPE_SHORT"); - } - - assert dataIn.read() == -1 : "color map data should have been exausted"; - return buff; - } - - private String getAuxTableName(long rasterColumnId, SeConnection scon) throws IOException { - - final String owner; - SeQuery query = null; - try { - final String dbaName = scon.getSdeDbaName(); - String rastersColumnsTable = dbaName + ".SDE_RASTER_COLUMNS"; - - SeSqlConstruct sqlCons = new SeSqlConstruct(rastersColumnsTable); - sqlCons.setWhere("RASTERCOLUMN_ID = " + rasterColumnId); - - try { - query = new SeQuery(scon, new String[] { "OWNER" }, sqlCons); - query.prepareQuery(); - } catch (SeException e) { - // sde 9.3 calls it raster_columns, not sde_raster_columns... - rastersColumnsTable = dbaName + ".RASTER_COLUMNS"; - sqlCons = new SeSqlConstruct(rastersColumnsTable); - sqlCons.setWhere("RASTERCOLUMN_ID = " + rasterColumnId); - query = new SeQuery(scon, new String[] { "OWNER" }, sqlCons); - query.prepareQuery(); - } - query.execute(); - - SeRow row = query.fetch(); - if (row == null) { - throw new IllegalArgumentException("No raster column registered with id " - + rasterColumnId); - } - owner = row.getString(0); - query.close(); - } catch (SeException e) { - throw new ArcSdeException("Error getting auxiliary table for raster column " - + rasterColumnId, e); - } finally { - if (query != null) { - try { - query.close(); - } catch (SeException e) { - LOGGER.log(Level.INFO, "ignoring exception when closing query to " - + "fetch colormap data", e); - } - } - } - - final String auxTableName = owner + ".SDE_AUX_" + rasterColumnId; - - return auxTableName; - } - - private List<RasterBandInfo> setUpBandInfo(SeConnection scon, SeRasterAttr rasterAttributes, - Map<Long, IndexColorModel> rastersColorMaps) throws IOException { - final int numBands; - final SeRasterBand[] seBands; - final RasterCellType cellType; - try { - numBands = rasterAttributes.getNumBands(); - seBands = rasterAttributes.getBands(); - cellType = RasterCellType.valueOf(rasterAttributes.getPixelType()); - } catch (SeException e) { - throw new ArcSdeException(e); - } - - List<RasterBandInfo> detachedBandInfo = new ArrayList<RasterBandInfo>(numBands); - - RasterBandInfo bandInfo; - SeRasterBand band; - for (int bandN = 0; bandN < numBands; bandN++) { - band = seBands[bandN]; - bandInfo = new RasterBandInfo(); - final int bitsPerSample = cellType.getBitsPerSample(); - setBandInfo(numBands, bandInfo, band, scon, bitsPerSample, rastersColorMaps); - detachedBandInfo.add(bandInfo); - } - return detachedBandInfo; - } - - /** - * - * @param numBands - * @param bandInfo - * @param band - * @param scon - * @param bitsPerSample - * only used if the band is colormapped to create the IndexColorModel - * @throws IOException - */ - private void setBandInfo(final int numBands, final RasterBandInfo bandInfo, - final SeRasterBand band, final SeConnection scon, int bitsPerSample, - final Map<Long, IndexColorModel> colorMaps) throws IOException { - - bandInfo.bandId = band.getId().longValue(); - bandInfo.bandNumber = band.getBandNumber(); - bandInfo.bandName = "Band " + bandInfo.bandNumber; - - final boolean hasColorMap = band.hasColorMap(); - if (hasColorMap) { - IndexColorModel colorMap = colorMaps.get(Long.valueOf(bandInfo.bandId)); - LOGGER.finest("Setting band's color map: " + colorMap); - bandInfo.nativeColorMap = colorMap; - bandInfo.colorMap = RasterUtils.ensureNoDataPixelIsAvailable(colorMap); - } else { - bandInfo.nativeColorMap = null; - } - - bandInfo.compressionType = CompressionType.valueOf(band.getCompressionType()); - bandInfo.cellType = RasterCellType.valueOf(band.getPixelType()); - bandInfo.interleaveType = InterleaveType.valueOf(band.getInterleave()); - bandInfo.interpolationType = InterpolationType.valueOf(band.getInterpolation()); - bandInfo.hasStats = band.hasStats(); - if (bandInfo.hasStats) { - try { - bandInfo.statsMin = band.getStatsMin(); - bandInfo.statsMax = band.getStatsMax(); - bandInfo.statsMean = band.getStatsMean(); - bandInfo.statsStdDev = band.getStatsStdDev(); - } catch (SeException e) { - throw new ArcSdeException(e); - } - // double noDataValue = 0; - // bandInfo.noDataValue = noDataValue; - } else { - bandInfo.statsMin = java.lang.Double.NaN; - bandInfo.statsMax = java.lang.Double.NaN; - bandInfo.statsMean = java.lang.Double.NaN; - bandInfo.statsStdDev = java.lang.Double.NaN; - } - if (bandInfo.getColorMap() != null) { - bandInfo.noDataValue = RasterUtils.determineNoDataValue(bandInfo.getColorMap()); - } else { - double statsMin = bandInfo.getStatsMin(); - double statsMax = bandInfo.getStatsMax(); - RasterCellType nativeCellType = bandInfo.getCellType(); - bandInfo.noDataValue = RasterUtils.determineNoDataValue(numBands, statsMin, statsMax, - nativeCellType); - } - SDEPoint tOrigin; - try { - tOrigin = band.getTileOrigin(); - } catch (SeException e) { - throw new ArcSdeException(e); - } - bandInfo.tileOrigin = new Point2D.Double(tOrigin.getX(), tOrigin.getY()); - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterleaveType.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterleaveType.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterleaveType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,64 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.util.NoSuchElementException; - -import com.esri.sde.sdk.client.SeRaster; - -/** - * An enumeration that mirrors the different possible band interleave types in Arcsde (ie, {@code - * SeRaster#SE_RASTER_INTERLEAVE_*}) - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL$ - */ -enum InterleaveType { - INTERLEAVE_BIL, INTERLEAVE_BIL_91, INTERLEAVE_BIP, INTERLEAVE_BIP_91, INTERLEAVE_BSQ, INTERLEAVE_BSQ_91, INTERLEAVE_NONE; - static { - INTERLEAVE_BIL.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIL); - INTERLEAVE_BIL_91.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIL_91); - INTERLEAVE_BIP.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIP); - INTERLEAVE_BIP_91.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIP_91); - INTERLEAVE_BSQ.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BSQ); - INTERLEAVE_BSQ_91.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BSQ_91); - INTERLEAVE_NONE.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_NONE); - } - - private int typeId; - - private void setSdeTypeId(int typeId) { - this.typeId = typeId; - } - - public int getSeRasterInterleaveType() { - return this.typeId; - } - - public static InterleaveType valueOf(final int seRasterInterleaveType) { - for (InterleaveType type : InterleaveType.values()) { - if (type.getSeRasterInterleaveType() == seRasterInterleaveType) { - return type; - } - } - throw new NoSuchElementException("Raster interleave type " + seRasterInterleaveType - + " does not exist"); - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterpolationType.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterpolationType.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterpolationType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,61 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.util.NoSuchElementException; - -import com.esri.sde.sdk.client.SeRaster; - -/** - * An enumeration that mirrors the different possible raster interpolation types in Arcsde (ie, - * {@code SeRaster#SE_INTERPOLATION_*}) - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL$ - */ -enum InterpolationType { - INTERPOLATION_BICUBIC, INTERPOLATION_BILINEAR, INTERPOLATION_NEAREST, INTERPOLATION_NONE; - static { - INTERPOLATION_BICUBIC.setSdeTypeId(SeRaster.SE_INTERPOLATION_BICUBIC); - INTERPOLATION_BILINEAR.setSdeTypeId(SeRaster.SE_INTERPOLATION_BILINEAR); - INTERPOLATION_NEAREST.setSdeTypeId(SeRaster.SE_INTERPOLATION_NEAREST); - INTERPOLATION_NONE.setSdeTypeId(SeRaster.SE_INTERPOLATION_NONE); - } - - private int typeId; - - private void setSdeTypeId(int typeId) { - this.typeId = typeId; - } - - public int getSeInterpolationType() { - return this.typeId; - } - - public static InterpolationType valueOf(final int seInterpolationType) { - for (InterpolationType type : InterpolationType.values()) { - if (type.getSeInterpolationType() == seInterpolationType) { - return type; - } - } - throw new NoSuchElementException("Interpolation type " + seInterpolationType - + " does not exist"); - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/NativeTileReader.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/NativeTileReader.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/NativeTileReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,528 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Dimension; -import java.awt.Rectangle; -import java.io.EOFException; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.geotools.arcsde.ArcSdeException; -import org.geotools.arcsde.session.Command; -import org.geotools.arcsde.session.ISession; -import org.geotools.arcsde.session.ISessionPool; -import org.geotools.arcsde.session.UnavailableConnectionException; -import org.geotools.util.logging.Logging; - -import com.esri.sde.sdk.client.SeConnection; -import com.esri.sde.sdk.client.SeException; -import com.esri.sde.sdk.client.SeQuery; -import com.esri.sde.sdk.client.SeRaster; -import com.esri.sde.sdk.client.SeRasterConstraint; -import com.esri.sde.sdk.client.SeRasterTile; -import com.esri.sde.sdk.client.SeRow; -import com.esri.sde.sdk.client.SeSqlConstruct; -import com.esri.sde.sdk.client.SeStreamOp; - -/** - * Offers an iterator like interface to fetch ArcSDE raster tiles. - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL: - * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org - * /geotools/arcsde/gce/NativeTileReader.java $ - */ -@SuppressWarnings( { "nls" }) -final class NativeTileReader implements TileReader { - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - private final RasterDatasetInfo rasterInfo; - - private final long rasterId; - - private final int pyramidLevel; - - private final Rectangle requestedTiles; - - private final ISessionPool sessionPool; - - private ISession session; - - private TileFetchCommand tileFetchCommand; - - /** - * {@link SeStreamOp} held to be closed at {@link #dispose()} - * - * @see #execute() - */ - private SeQuery preparedQuery; - - private TileInfo nextTile; - - private boolean started; - - private final int bitmaskDataLength; - - private final BitmaskToNoDataConverter noData; - - private final int pixelsPerTile; - - private final int tileDataLength; - - private int bitsPerSample; - - /** - * Command to fetch an {@link SeRasterTile tile} - */ - private static class TileFetchCommand extends Command<TileInfo> { - - private final SeRow row; - - private final int tileDataLength; - - public TileFetchCommand(final SeRow row, final int tileDataLength) { - this.row = row; - this.tileDataLength = tileDataLength; - } - - @Override - public TileInfo execute(ISession session, SeConnection connection) throws SeException, - IOException { - SeRasterTile tile = row.getRasterTile(); - final TileInfo tileInfo; - if (tile == null) { - tileInfo = null; - } else { - byte[] bitMaskData = tile.getBitMaskData(); - int numPixelsRead = tile.getNumPixels(); - byte[] tileData; - if (numPixelsRead == 0) { - tileData = new byte[tileDataLength]; - } else { - tileData = tile.getPixelData(); - } - long bandId = tile.getBandId().longValue(); - int colIndex = tile.getColumnIndex(); - int rowIndex = tile.getRowIndex(); - tileInfo = new TileInfo(bandId, colIndex, rowIndex, numPixelsRead, tileData, - bitMaskData); - } - return tileInfo; - } - } - - /** - * @see DefaultTiledRasterReader#nextRaster() - */ - private static class QueryRasterCommand extends Command<Void> { - - private SeQuery preparedQuery; - - private SeRow row; - - private final SeRasterConstraint rasterConstraint; - - private final String rasterColumn; - - private final String rasterTable; - - private final long rasterId; - - /** - * - * @param rConstraint - * indicates which bands, pyramid level and grid envelope to query - * @param rasterTable - * indicates which raster table to query - * @param rasterColumn - * indicates what raster column in the raster table to query - * @param rasterId - * indicates which raster in the raster catalog to query - */ - public QueryRasterCommand(final SeRasterConstraint rConstraint, final String rasterTable, - final String rasterColumn, final long rasterId) { - this.rasterConstraint = rConstraint; - this.rasterTable = rasterTable; - this.rasterColumn = rasterColumn; - this.rasterId = rasterId; - } - - @Override - public Void execute(ISession session, SeConnection connection) throws SeException, - IOException { - - final SeSqlConstruct sqlConstruct = new SeSqlConstruct(rasterTable); - /* - * Filter by the given raster id - */ - final String rasterIdFilter = rasterColumn + " = " + rasterId; - sqlConstruct.setWhere(rasterIdFilter); - - final String[] rasterColumns = { rasterColumn }; - preparedQuery = new SeQuery(connection, rasterColumns, sqlConstruct); - preparedQuery.prepareQuery(); - preparedQuery.execute(); - - this.row = preparedQuery.fetch(); - if (row == null) { - return null; - } - - preparedQuery.queryRasterTile(rasterConstraint); - - return null; - } - - public SeQuery getPreparedQuery() { - return preparedQuery; - } - - public SeRow getSeRow() { - return row; - } - } - - /** - * Creates a {@link TileReader} that reads tiles out of ArcSDE for the given {@code - * preparedQuery} and {@code SeRow} using the given {@code session}, in the native raster - * format. - * <p> - * As for any object that receives a {@link ISession session}, the same rule applies: this class - * is not responsible of {@link ISession#dispose() disposing} the session, but the calling code - * is. - * </p> - * - * @param preparedQuery - * the query stream to close when done - * @param row - * @param imageDimensions - * the image size, x and y are the offsets, width and height the actual width and - * height, used to ignore incomming pixel data as appropriate to fit the image - * dimensions - * @param bitsPerSample - * @param numberOfBands2 - * @param requestedTiles - */ - NativeTileReader(final ISessionPool sessionPool, final RasterDatasetInfo rasterInfo, - final long rasterId, final int pyramidLevel, final Rectangle requestedTiles, - final BitmaskToNoDataConverter noData) { - this.sessionPool = sessionPool; - this.rasterInfo = rasterInfo; - this.rasterId = rasterId; - this.pyramidLevel = pyramidLevel; - this.requestedTiles = requestedTiles; - this.noData = noData; - - final Dimension tileSize = rasterInfo.getTileDimension(rasterId); - - this.pixelsPerTile = tileSize.width * tileSize.height; - - final RasterCellType nativeCellType = rasterInfo.getNativeCellType(); - this.bitsPerSample = nativeCellType.getBitsPerSample(); - this.tileDataLength = (int) Math - .ceil(((double) pixelsPerTile * (double) bitsPerSample) / 8D); - this.bitmaskDataLength = (int) Math.ceil(pixelsPerTile / 8D); - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getBitsPerSample() - */ - public int getBitsPerSample() { - return bitsPerSample; - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getPixelsPerTile() - */ - public int getPixelsPerTile() { - return pixelsPerTile; - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getNumberOfBands() - */ - public int getNumberOfBands() { - return rasterInfo.getNumBands(); - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getTileWidth() - */ - public int getTileWidth() { - return rasterInfo.getTileWidth(rasterId); - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getTileHeight() - */ - public int getTileHeight() { - return rasterInfo.getTileHeight(rasterId); - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getTilesWide() - */ - public int getTilesWide() { - return requestedTiles.width; - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getTilesHigh() - */ - public int getTilesHigh() { - return requestedTiles.height; - } - - /** - * @see org.geotools.arcsde.gce.TileReader#getBytesPerTile() - */ - public int getBytesPerTile() { - return tileDataLength; - } - - /** - * @see org.geotools.arcsde.gce.TileReader#hasNext() - */ - public boolean hasNext() throws IOException { - if (!started) { - execute(); - try { - nextTile = session.issue(tileFetchCommand); - } catch (IOException e) { - dispose(); - throw e; - } catch (RuntimeException e) { - dispose(); - throw e; - } - started = true; - if (nextTile == null) { - dispose(); - LOGGER.fine("No tiles to fetch at all, releasing connection"); - } - } - return nextTile != null; - } - - /** - * Creates and executes the {@link SeQuery} that's used to fetch the required tiles from the - * specified raster, and stores (as member variables) the {@link SeRow} to fetch the tiles from - * and the {@link SeQuery} to be closed at the TileReader's disposal - * - * @throws IOException - */ - private void execute() throws IOException { - - final int rasterIndex = rasterInfo.getRasterIndex(rasterId); - final int tileWidth = rasterInfo.getTileWidth(rasterId); - final int tileHeight = rasterInfo.getTileHeight(rasterId); - - /* - * Create the raster constraint to query the needed tiles out of the specified raster at the - * given pyramid level - */ - final SeRasterConstraint rConstraint; - try { - final int numberOfBands; - numberOfBands = rasterInfo.getNumBands(); - - int[] bandsToQuery = new int[numberOfBands]; - for (int bandN = 1; bandN <= numberOfBands; bandN++) { - bandsToQuery[bandN - 1] = bandN; - } - - int minTileX = requestedTiles.x; - int minTileY = requestedTiles.y; - int maxTileX = minTileX + requestedTiles.width - 1; - int maxTileY = minTileY + requestedTiles.height - 1; - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine("Requesting tiles [x=" + minTileX + "-" + maxTileX + ", y=" + minTileY - + "-" + maxTileY + "] from tile range [x=0-" - + (rasterInfo.getNumTilesWide(rasterIndex, pyramidLevel) - 1) + ", y=0-" - + (rasterInfo.getNumTilesHigh(rasterIndex, pyramidLevel) - 1) + "]"); - } - // SDEPoint tileOrigin = rAttr.getTileOrigin(); - - if (LOGGER.isLoggable(Level.FINE)) { - Rectangle tiledImageSize = new Rectangle(0, 0, tileWidth * requestedTiles.width, - tileHeight * requestedTiles.height); - - LOGGER.fine("Tiled image size: " + tiledImageSize); - } - - final int interleaveType = SeRaster.SE_RASTER_INTERLEAVE_BIP; - - rConstraint = new SeRasterConstraint(); - rConstraint.setBands(bandsToQuery); - rConstraint.setLevel(pyramidLevel); - rConstraint.setEnvelope(minTileX, minTileY, maxTileX, maxTileY); - rConstraint.setInterleave(interleaveType); - } catch (SeException se) { - throw new ArcSdeException(se); - } - - /* - * Obtain the ISession this tile reader will work with until exhausted - */ - try { - // lets share connections as we're going to do read only operations - final boolean transactional = false; - this.session = sessionPool.getSession(transactional); - // System.err.println("----> Using " + session + " to read raster #" + rasterId - // + " on Thread " + Thread.currentThread().getName() + ". Tile set: " - // + requestedTiles); - if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.finer("Using " + session + " to read raster #" + rasterId + " on Thread " - + Thread.currentThread().getName() + ". Tile set: " + requestedTiles); - } - } catch (UnavailableConnectionException e) { - // really bad luck.. - throw new RuntimeException(e); - } - - final String rasterTable = rasterInfo.getRasterTable(); - final String rasterColumn = rasterInfo.getRasterColumns()[0]; - final QueryRasterCommand queryCommand = new QueryRasterCommand(rConstraint, rasterTable, - rasterColumn, rasterId); - - session.issue(queryCommand); - - final SeRow row = queryCommand.getSeRow(); - - this.tileFetchCommand = new TileFetchCommand(row, tileDataLength); - this.preparedQuery = queryCommand.getPreparedQuery(); - } - - /** - * @see org.geotools.arcsde.gce.TileReader#next() - */ - public TileInfo next() throws IOException { - final TileInfo tile; - final boolean hasNext = hasNext(); - if (hasNext) { - tile = nextTile(); - } else { - throw new IllegalStateException("There're no more tiles to fetch"); - } - - try { - final byte[] bitMaskData = tile.getBitmaskData(); - - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest(" >> Fetching " + tile + " - bitmask: " + bitMaskData.length); - } - - assert bitMaskData.length == 0 ? true : bitmaskDataLength == bitMaskData.length; - - final int numPixels = tile.getNumPixelsRead(); - final byte[] rawTileData = tile.getTileData(); - - final Long bandId = tile.getBandId(); - - if (0 == numPixels) { - if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.finer("tile contains no pixel data, skipping: " + tile); - } - noData.setAll(bandId, rawTileData); - } else if (pixelsPerTile == numPixels) { - - if (bitMaskData.length > 0) { - noData.setNoData(bandId, rawTileData, bitMaskData); - } - - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.finest("returning " + numPixels + " pixels data packaged into " - + tileDataLength + " bytes for tile [" + tile.getColumnIndex() + "," - + tile.getRowIndex() + "]"); - } - } else { - throw new IllegalStateException("Expected pixels per tile == " + pixelsPerTile - + " but got " + numPixels + ": " + tile); - } - - // return new TileInfo(bandId, bitMaskData, numPixels); - return tile; - - } catch (RuntimeException e) { - dispose(); - throw e; - } - } - - private TileInfo nextTile() throws IOException { - if (nextTile == null) { - dispose(); - throw new EOFException("No more tiles to read"); - } - TileInfo curr = nextTile; - - try { - nextTile = session.issue(tileFetchCommand); - } catch (IOException e) { - dispose(); - throw e; - } catch (RuntimeException e) { - dispose(); - throw e; - } - if (nextTile == null) { - dispose(); - LOGGER.finer("There're no more tiles to fetch"); - } - - return curr; - } - - /** - * @see org.geotools.arcsde.gce.TileReader#dispose() - */ - public void dispose() { - if (session != null) { - // System.err.println("TileReader disposing " + session + " on Thread " - // + Thread.currentThread().getName()); - if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.finer("TileReader disposing " + session + " on Thread " - + Thread.currentThread().getName()); - } - try { - session.close(this.preparedQuery); - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Closing tile reader's prepared Query", e); - } - session.dispose(); - session = null; - } - } - - /** - * Disposes as to make sure the {@link ISession session} is returned to the pool even if a - * failing or non careful client left this object hanging around - * - * @see #dispose() - * @see java.lang.Object#finalize() - */ - @Override - protected void finalize() { - dispose(); - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PromotingTileReader.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PromotingTileReader.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PromotingTileReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,206 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_1BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_U; - -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.geotools.util.logging.Logging; - -import com.esri.sde.sdk.client.SeRasterTile; - -/** - * A {@link TileReader} decorator - * - * @author Gabriel Roldan - * @version $Id$ - * @since 2.5.6 - */ -final class PromotingTileReader implements TileReader { - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - private final TileReader nativeReader; - - private final RasterCellType targetType; - - private final SampleDepthPromoter promoter; - - private final BitmaskToNoDataConverter noData; - - public PromotingTileReader(final TileReader nativeTileReader, final RasterCellType sourceType, - final RasterCellType targetType, final BitmaskToNoDataConverter noData) { - - this.nativeReader = nativeTileReader; - this.targetType = targetType; - this.noData = noData; - this.promoter = SampleDepthPromoter.createFor(sourceType, targetType); - LOGGER.fine("Using sample depth promoting tile reader, from " + sourceType + " to " - + targetType); - } - - public int getBitsPerSample() { - return targetType.getBitsPerSample(); - } - - public int getBytesPerTile() { - double pixelsPerTile = getPixelsPerTile(); - double bitsPerSample = getBitsPerSample(); - int bytesPerTile = (int) Math.floor((pixelsPerTile * bitsPerSample) / 8D); - return bytesPerTile; - } - - public int getNumberOfBands() { - return nativeReader.getNumberOfBands(); - } - - public int getPixelsPerTile() { - return nativeReader.getPixelsPerTile(); - } - - public int getTileHeight() { - return nativeReader.getTileHeight(); - } - - public int getTileWidth() { - return nativeReader.getTileWidth(); - } - - public int getTilesHigh() { - return nativeReader.getTilesHigh(); - } - - public int getTilesWide() { - return nativeReader.getTilesWide(); - } - - public boolean hasNext() throws IOException { - return nativeReader.hasNext(); - } - - public TileInfo next() throws IOException { - final TileInfo tileInfo = nativeReader.next(); - final byte[] nativeTileData = tileInfo.getTileData(); - final byte[] tileData = new byte[getBytesPerTile()]; - try { - final byte[] bitmaskData = tileInfo.getBitmaskData(); - final boolean hasNoDataPixels = bitmaskData.length > 0; - final Long bandId = tileInfo.getBandId(); - - final int numPixelsRead = tileInfo.getNumPixelsRead(); - if (numPixelsRead == 0) { - noData.setAll(bandId, tileData); - } else { - final int numSamples = getPixelsPerTile(); - assert numPixelsRead == numSamples; - - for (int sampleN = 0; sampleN < numSamples; sampleN++) { - if (hasNoDataPixels && noData.isNoData(sampleN, bitmaskData)) { - noData.setNoData(bandId, sampleN, tileData); - } else { - promoter.promote(sampleN, nativeTileData, tileData); - } - } - } - - } catch (RuntimeException e) { - dispose(); - throw e; - } - - TileInfo promotedTileInfo = new TileInfo(tileInfo.getBandId(), tileInfo.getColumnIndex(), - tileInfo.getRowIndex(), tileInfo.getNumPixelsRead(), tileData, tileInfo - .getBitmaskData()); - return promotedTileInfo; - } - - /** - * - * @author Gabriel Roldan - */ - private static abstract class SampleDepthPromoter { - - public abstract void promote(int sampleN, byte[] nativeTileData, byte[] tileData); - - public static SampleDepthPromoter createFor(final RasterCellType source, - final RasterCellType target) { - - if (source == TYPE_1BIT && target == RasterCellType.TYPE_8BIT_U) { - return new OneBitToUchar(); - } else if (source == TYPE_8BIT_U && target == TYPE_16BIT_U) { - return new UcharToUshort(); - } else if (source == TYPE_16BIT_S && target == TYPE_32BIT_S) { - return new ShortToInt(); - } - - UnsupportedOperationException exception = new UnsupportedOperationException( - "Promoting from " + source + " to " + target + " not yet implemented"); - LOGGER.log(Level.WARNING, "Can't promote", exception); - throw exception; - } - } - - private static final class UcharToUshort extends SampleDepthPromoter { - - @Override - public void promote(int sampleN, byte[] nativeTileData, byte[] tileData) { - int pixArrayOffset = 2 * sampleN; - tileData[pixArrayOffset] = 0; - tileData[pixArrayOffset + 1] = (byte) ((nativeTileData[sampleN] >>> 0) & 0xFF); - } - } - - private static final class OneBitToUchar extends SampleDepthPromoter { - - @Override - public void promote(int sampleN, byte[] nativeTileData, byte[] tileData) { - int pixArrayOffset = sampleN / 8; - int bit = sampleN % 8; - int _byte = nativeTileData[pixArrayOffset]; - byte ucharvalue = (byte) ((_byte >> (7 - bit)) & 0x01); - tileData[sampleN] = ucharvalue; - } - } - - private static final class ShortToInt extends SampleDepthPromoter { - - @Override - public void promote(int sampleN, byte[] nativeTileData, byte[] tileData) { - int pixArrayOffset = 4 * sampleN; - - tileData[pixArrayOffset] = 0; - tileData[pixArrayOffset + 1] = 0; - tileData[pixArrayOffset + 1] = (byte) ((nativeTileData[sampleN] >>> 8) & 0xFF); - tileData[pixArrayOffset + 1] = (byte) ((nativeTileData[sampleN] >>> 0) & 0xFF); - } - } - - /** - * @see org.geotools.arcsde.gce.TileReader#dispose() - */ - public void dispose() { - this.nativeReader.dispose(); - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PyramidLevelInfo.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PyramidLevelInfo.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PyramidLevelInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,178 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Point2D; - -import org.geotools.geometry.jts.ReferencedEnvelope; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -/** - * Represents one level in an ArcSDE pyramid. Holds information about a given pyramid level, like - * resolution, x/y offsets, number of tiles high/wide, total pixel size and total envelope covered - * by this level. - * - * @author sfarber - * - */ -final class PyramidLevelInfo { - private int pyramidLevel, xTiles, yTiles; - - Point2D extentOffset; - - Point imageOffset; - - private double xRes, yRes; - - private ReferencedEnvelope envelope; - - public Dimension size; - - /** - * - * @param level - * the level index - * @param extent - * the geographical extent the level covers - * @param imgOffset - * the offset of the image at this level - * @param extOffset - * the offset of the image extent at this level - * @param numTilesWide - * @param numTilesHigh - * @param levelSize - * the size of the actual image inside the tiled pixel range - */ - PyramidLevelInfo(int level, ReferencedEnvelope extent, Point imgOffset, Point2D extOffset, - int numTilesWide, int numTilesHigh, Dimension levelSize) { - this.pyramidLevel = level; - this.xRes = extent.getWidth() / levelSize.width; - this.yRes = extent.getHeight() / levelSize.height; - this.envelope = extent; - this.imageOffset = imgOffset; - this.extentOffset = extOffset; - this.xTiles = numTilesWide; - this.yTiles = numTilesHigh; - this.size = levelSize; - } - - /** - * @return Which level in the pyramid this object represents - */ - public int getLevel() { - return pyramidLevel; - } - - /** - * @return The X and Y resolution in units/pixel for pixels at this level - */ - public double getXRes() { - return xRes; - } - - /** - * @return The X and Y resolution in units/pixel for pixels at this level - */ - public double getYRes() { - return yRes; - } - - /** - * @return DOCUMENT ME!!! - */ - public int getXOffset() { - return imageOffset.x; - } - - /** - * @return DOCUMENT ME!!! - */ - public int getYOffset() { - return imageOffset.y; - } - - /** - * @return The total number of tiles covering the width of this level - */ - public int getNumTilesWide() { - return xTiles; - } - - /** - * @return The total number of tiles covering the height of this level - */ - public int getNumTilesHigh() { - return yTiles; - } - - /** - * The envelope covering the image grid range inside fully tiled image at this pyramid level - * - * @return The geographical area covered by the {@link #getImageRange() grid range} of the - * raster at this pyramid level - */ - public ReferencedEnvelope getImageEnvelope() { - final double deltaX = extentOffset.getX(); - final double deltaY = extentOffset.getY(); - double minx = this.envelope.getMinX() - deltaX; - double miny = this.envelope.getMinY() - deltaY; - double maxx = minx + this.envelope.getWidth(); - double maxy = miny + this.envelope.getHeight(); - - CoordinateReferenceSystem crs = this.envelope.getCoordinateReferenceSystem(); - ReferencedEnvelope imageExtent = new ReferencedEnvelope(minx, maxx, miny, maxy, crs); - - return imageExtent; - } - - /** - * @return The total number of pixels in the image at this level as whole tiles - */ - public Dimension getSize() { - return size; - } - - /** - * The rectangle covering the actual raster data inside the tiled space - * - * @return - */ - public Rectangle getImageRange() { - final int offsetX = getXOffset(); - final int offsetY = getYOffset(); - - /* - * get the range of actual data pixels in this pyramid level in pixel space, offset and - * trailing no data pixels to fill up the tile space do not count - */ - final Rectangle levelRange = new Rectangle(offsetX, offsetY, size.width, size.height); - return levelRange; - } - - @Override - public String toString() { - return "[level: " + pyramidLevel + " size: " + size.width + "x" + size.height + " xRes: " - + xRes + " yRes: " + yRes + " xOffset: " + getXOffset() + " yOffset: " - + getYOffset() + " extent: " + envelope.getMinX() + "," + envelope.getMinY() + " " - + envelope.getMaxX() + "," + envelope.getMaxY() + " tilesWide: " + xTiles - + " tilesHigh: " + yTiles + "]"; - } -} \ No newline at end of file Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterBandInfo.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterBandInfo.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterBandInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,175 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.geom.Point2D; -import java.awt.geom.Point2D.Double; -import java.awt.image.IndexColorModel; - -/** - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL$ - */ -final class RasterBandInfo { - - long bandId; - - String bandName; - - int bandNumber; - - /** - * the color map as it is on the database, except that we always create a color map with alpha - * channel regardless of whether the native on has alpha or not, to account for the no-data - * pixel to be a transparent one where appropriate - */ - IndexColorModel nativeColorMap; - - /** - * The color map as it is going to be used by this library. May differ from the native one - * either in the number of elements (when the native color map is not full and it has no - * transparent pixel to be used as no-data value), or in the pixel depth (when the native - * colormap is full, has no no-data pixel, and hence it needs to be promoted to a higher - * transfer type to make room for a no-data index) - */ - IndexColorModel colorMap; - - /** - * The band's no-data value. - */ - Number noDataValue; - - CompressionType compressionType; - - RasterCellType cellType; - - InterleaveType interleaveType; - - InterpolationType interpolationType; - - boolean hasStats; - - Point2D.Double tileOrigin; - - double statsMin; - - double statsMax; - - double statsMean; - - double statsStdDev; - - public RasterBandInfo() { - // do nothing - } - - /** - * @return the ArcSDE identifier for the band - */ - public long getBandId() { - return bandId; - } - - public String getBandName() { - return bandName; - } - - public int getBandNumber() { - return bandNumber; - } - - public boolean isColorMapped() { - return nativeColorMap != null; - } - - public CompressionType getCompressionType() { - return compressionType; - } - - public RasterCellType getCellType() { - return cellType; - } - - public InterleaveType getInterleaveType() { - return interleaveType; - } - - public InterpolationType getInterpolationType() { - return interpolationType; - } - - public boolean isHasStats() { - return hasStats; - } - - public Double getTileOrigin() { - return tileOrigin; - } - - public IndexColorModel getNativeColorMap() { - return nativeColorMap; - } - - public IndexColorModel getColorMap() { - return colorMap; - } - - public double getStatsMin() { - return statsMin; - } - - public double getStatsMax() { - return statsMax; - } - - public double getStatsMean() { - return statsMean; - } - - public double getStatsStdDev() { - return statsStdDev; - } - - public Number getNoDataValue() { - return noDataValue; - } - - @SuppressWarnings("nls") - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getBandName()); - sb.append("[ id:").append(getBandId()); - sb.append(", type:").append(getCellType()); - sb.append(", samples: nodata=").append(getNoDataValue()).append(" min=").append( - getStatsMin()).append(" max=").append(getStatsMax()).append(" mean=").append( - getStatsMean()).append(" stddev=").append(getStatsStdDev()); - /* - * sb.append(", tile origin: ").append((int) getTileOrigin().x).append(",").append( (int) - * getTileOrigin().y); - */ - sb.append(", compression:").append(getCompressionType()); - sb.append(", interpolation:").append(getInterpolationType()); - sb.append(", Color Map: ").append(isColorMapped() ? "YES" : "NO"); - return sb.toString(); - } - -} \ No newline at end of file Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterCellType.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterCellType.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterCellType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,119 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.image.DataBuffer; -import java.util.NoSuchElementException; - -import org.geotools.util.NumberRange; - -import com.esri.sde.sdk.client.SeRaster; - -/** - * An enumeration that mirrors the different possible cell resolutions in Arcsde (ie, {@code - * SeRaster#SE_PIXEL_TYPE_*}) - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL$ - */ -enum RasterCellType { - TYPE_16BIT_S(16, DataBuffer.TYPE_SHORT, true, NumberRange.create(Short.MIN_VALUE, - Short.MAX_VALUE)), // - TYPE_16BIT_U(16, DataBuffer.TYPE_USHORT, false, NumberRange.create((int) 0, (int) 65535)), // - TYPE_1BIT(1, DataBuffer.TYPE_BYTE, false, NumberRange.create((byte) 0, (byte) 1)), // - TYPE_32BIT_REAL(32, DataBuffer.TYPE_FLOAT, true, NumberRange.create(Float.MIN_VALUE, - Float.MAX_VALUE)), // - TYPE_32BIT_S(32, DataBuffer.TYPE_INT, true, NumberRange.create(Integer.MIN_VALUE, - Integer.MAX_VALUE)), // - TYPE_32BIT_U(32, DataBuffer.TYPE_INT, false, NumberRange.create(0L, (long) Math.pow(2, 32) - 1)), // - TYPE_4BIT(4, DataBuffer.TYPE_BYTE, false, NumberRange.create((byte) 0, - (byte) Math.pow(2, 4) - 1)), // - TYPE_64BIT_REAL(64, DataBuffer.TYPE_DOUBLE, true, NumberRange.create(Double.MIN_VALUE, - Double.MAX_VALUE)), // - TYPE_8BIT_S(8, DataBuffer.TYPE_BYTE, true, NumberRange.create(Byte.MIN_VALUE, Byte.MAX_VALUE)), // - TYPE_8BIT_U(8, DataBuffer.TYPE_BYTE, false, NumberRange.create((int) 0, (int) 255)); - static { - // Setting the se_pixel_type in a static initializer to avoid having them linked to the fake - // values in the dummy api - TYPE_16BIT_S.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_16BIT_S); - TYPE_16BIT_U.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_16BIT_U); - TYPE_1BIT.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_1BIT); - TYPE_32BIT_REAL.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_32BIT_REAL); - TYPE_32BIT_S.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_32BIT_S); - TYPE_32BIT_U.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_32BIT_U); - TYPE_4BIT.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_4BIT); - TYPE_64BIT_REAL.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_64BIT_REAL); - TYPE_8BIT_S.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_8BIT_S); - TYPE_8BIT_U.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_8BIT_U); - } - - private int typeId; - - private final int bitsPerSample; - - private final int dataBufferType; - - private final boolean signed; - - private final NumberRange<?> sampleValueRange; - - private RasterCellType(final int bitsPerSample, final int dataBufferType, final boolean signed, - final NumberRange<?> sampleValueRange) { - this.bitsPerSample = bitsPerSample; - this.dataBufferType = dataBufferType; - this.signed = signed; - this.sampleValueRange = sampleValueRange; - } - - private void setSdeTypeId(int typeId) { - this.typeId = typeId; - } - - public int getSeRasterPixelType() { - return this.typeId; - } - - public int getBitsPerSample() { - return bitsPerSample; - } - - public int getDataBufferType() { - return dataBufferType; - } - - public boolean isSigned() { - return signed; - } - - public static RasterCellType valueOf(final int seRasterPixelType) { - for (RasterCellType type : RasterCellType.values()) { - if (type.getSeRasterPixelType() == seRasterPixelType) { - return type; - } - } - throw new NoSuchElementException("Raster pixel type " + seRasterPixelType - + " does not exist"); - } - - public NumberRange<?> getSampleValueRange() { - return sampleValueRange; - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterDatasetInfo.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterDatasetInfo.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterDatasetInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,503 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.image.IndexColorModel; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.imageio.ImageTypeSpecifier; - -import org.geotools.coverage.Category; -import org.geotools.coverage.GridSampleDimension; -import org.geotools.coverage.grid.GeneralGridEnvelope; -import org.geotools.coverage.grid.io.OverviewPolicy; -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.referencing.CRS; -import org.geotools.referencing.operation.builder.GridToEnvelopeMapper; -import org.geotools.referencing.operation.transform.LinearTransform1D; -import org.geotools.resources.i18n.Vocabulary; -import org.geotools.resources.i18n.VocabularyKeys; -import org.geotools.util.NumberRange; -import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.referencing.datum.PixelInCell; -import org.opengis.referencing.operation.MathTransform; -import org.opengis.referencing.operation.NoninvertibleTransformException; -import org.opengis.referencing.operation.TransformException; - -/** - * Wraps metadata information for a raster dataset, whether it is composed of a single raster, or - * it's raster catalog, and provides some conveinent methods to get to the raster metadata of it's - * rasters and pyramid levels. - * <p> - * This is the single entry point to the metadata of a raster dataset. The associated classes - * {@link RasterInfo} and {@link PyramidLevelInfo} are to be considered private to this class. - * </p> - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL: - * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org - * /geotools/arcsde/gce/RasterDatasetInfo.java $ - */ -@SuppressWarnings( { "nls" }) -final class RasterDatasetInfo { - - /** TRasterDatasetInfo the raster table we're pulling images from in this reader * */ - private String rasterTable = null; - - /** - * raster column names on this raster. If there's more than one raster column (is this - * possible?) then we just use the first one. - */ - private String[] rasterColumns; - - /** Array holding information on each level of the pyramid in this raster. * */ - private List<RasterInfo> subRasterInfo; - - /** - * The original (ie, pyramid level zero) envelope for the whole raster dataset - */ - private GeneralEnvelope originalEnvelope; - - private GeneralGridEnvelope originalGridRange; - - private List<GridSampleDimension> gridSampleDimensions; - - private final Map<Integer, ImageTypeSpecifier> renderedImageSpec = new HashMap<Integer, ImageTypeSpecifier>(); - - /** - * @param rasterTable - * the rasterTable to set - */ - void setRasterTable(String rasterTable) { - this.rasterTable = rasterTable; - } - - /** - * @return the raster table name - */ - public String getRasterTable() { - return rasterTable; - } - - /** - * @param rasterColumns - * the rasterColumns to set - */ - void setRasterColumns(String[] rasterColumns) { - this.rasterColumns = rasterColumns; - } - - /** - * @return the raster column names - */ - public String[] getRasterColumns() { - return rasterColumns; - } - - /** - * @param pyramidInfo - * the pyramidInfo to set - */ - void setPyramidInfo(List<RasterInfo> pyramidInfo) { - this.subRasterInfo = pyramidInfo; - } - - public GridSampleDimension[] getGridSampleDimensions() { - if (gridSampleDimensions == null) { - synchronized (this) { - if (gridSampleDimensions == null) { - gridSampleDimensions = buildSampleDimensions(); - } - } - } - return gridSampleDimensions.toArray(new GridSampleDimension[getNumBands()]); - } - - @SuppressWarnings("unchecked") - private List<GridSampleDimension> buildSampleDimensions() { - - final int numBands = getNumBands(); - List<GridSampleDimension> dimensions = new ArrayList<GridSampleDimension>(numBands); - - final Color transparent = new Color(0, 0, 0, 0); - - List<RasterBandInfo> bands = subRasterInfo.get(0).getBands(); - - for (RasterBandInfo band : bands) { - final int bandNumber = band.getBandNumber(); - // use native cell type, in case no-data value has been computed to account for - // sample depth promotion, we want to category to keep being the native range for - // the values category - final RasterCellType targetCellType = getNativeCellType(); - String bandName = band.getBandName(); - - final double statsMin = band.getStatsMin(); - final double statsMax = band.getStatsMax(); - - NumberRange<?> sampleValueRange; - if (Double.isNaN(statsMin) || Double.isNaN(statsMax)) { - sampleValueRange = targetCellType.getSampleValueRange(); - } else { - sampleValueRange = NumberRange.create(statsMin, statsMax); - Class elementClass = targetCellType.getSampleValueRange().getElementClass(); - sampleValueRange = sampleValueRange.castTo(elementClass); - } - - final Color[] colorRange = null; - - final boolean geophysics = isGeoPhysics(); - - Category valuesCat = new Category("values", colorRange, sampleValueRange, - LinearTransform1D.IDENTITY).geophysics(geophysics); - - Category[] categories; - if (geophysics) { - double noDataValue = band.getNoDataValue().doubleValue(); - // same as Category.NODATA but for the actual nodata value instead of hardcoded to - // zero - Category nodataCat = new Category(Vocabulary - .formatInternational(VocabularyKeys.NODATA), transparent, noDataValue); - categories = new Category[] { valuesCat, nodataCat }; - } else { - // do not build a nodata category. A nodata value that doesn't overlap the value - // range couldn't be determined - categories = new Category[] { valuesCat }; - } - /* - * if (band.isHasStats()) { //can't do this, get an exception telling categories - * overlap.. so no real way to express the statistics, uh? Category catMin = new - * Category("Min", null, band.getStatsMin()).geophysics(true); Category catMax = new - * Category("Max", null, band.getStatsMin()).geophysics(true); Category catMean = new - * Category("Mean", null, band.getStatsMin()).geophysics(true); Category catStdDev = new - * Category("StdDev", null, band.getStatsMin()) .geophysics(true); categories = new - * Category[] { valuesCat, nodataCat, catMin, catMax, catMean, catStdDev }; } else { - * categories = new Category[] { valuesCat, nodataCat }; } - */ - - // .geophysics(false) because our sample model always corresponds to the packed view - // (whether it matches the underlying sample depth or we're promoting in order to make - // room for the nodata value). - GridSampleDimension sampleDim = new GridSampleDimension(bandName, categories, null) - .geophysics(false); - - dimensions.add(sampleDim); - } - return dimensions; - } - - private boolean isGeoPhysics() { - if (isColorMapped()) { - return false; - } - return RasterUtils.isGeoPhysics(getNumBands(), getNativeCellType()); - } - - public int getNumBands() { - return subRasterInfo.get(0).getNumBands(); - } - - public int getImageWidth() { - final GeneralGridEnvelope originalGridRange = getOriginalGridRange(); - final int width = originalGridRange.getSpan(0); - return width; - } - - public int getImageHeight() { - final GeneralGridEnvelope originalGridRange = getOriginalGridRange(); - final int height = originalGridRange.getSpan(1); - return height; - } - - /** - * @return the coverageCrs - */ - public CoordinateReferenceSystem getCoverageCrs() { - return subRasterInfo.get(0).getCoordinateReferenceSystem(); - } - - /** - * @return the originalGridRange for the whole raster dataset, based on the first raster in the - * raster dataset - */ - public GeneralGridEnvelope getOriginalGridRange() { - if (originalGridRange == null) { - final MathTransform modelToRaster; - try { - final MathTransform rasterToModel = getRasterToModel(); - modelToRaster = rasterToModel.inverse(); - } catch (NoninvertibleTransformException e) { - throw new IllegalStateException("Can't create transform from model to raster"); - } - - int minx = Integer.MAX_VALUE; - int miny = Integer.MAX_VALUE; - int maxx = Integer.MIN_VALUE; - int maxy = Integer.MIN_VALUE; - - final int rasterCount = getNumRasters(); - for (int rasterN = 0; rasterN < rasterCount; rasterN++) { - final GeneralEnvelope rasterEnvelope = getGridEnvelope(rasterN, 0); - GeneralEnvelope rasterGridRangeInDataSet; - try { - rasterGridRangeInDataSet = CRS.transform(modelToRaster, rasterEnvelope); - } catch (NoninvertibleTransformException e) { - throw new IllegalArgumentException(e); - } catch (TransformException e) { - throw new IllegalArgumentException(e); - } - - minx = Math.min(minx, (int) Math.floor(rasterGridRangeInDataSet.getMinimum(0))); - miny = Math.min(miny, (int) Math.floor(rasterGridRangeInDataSet.getMinimum(1))); - maxx = Math.max(maxx, (int) Math.ceil(rasterGridRangeInDataSet.getMaximum(0))); - maxy = Math.max(maxy, (int) Math.ceil(rasterGridRangeInDataSet.getMaximum(1))); - } - int width = maxx - minx; - int height = maxy - miny; - Rectangle range = new Rectangle(0, 0, width, height); - originalGridRange = new GeneralGridEnvelope(range, 2); - } - return originalGridRange; - } - - public MathTransform getRasterToModel() { - - GeneralEnvelope firstRasterEnvelope = getGridEnvelope(0, 0); - Rectangle firstRasterGridRange = getGridRange(0, 0); - GeneralGridEnvelope gridRange = new GeneralGridEnvelope(firstRasterGridRange, 2); - - // create a raster to model transform, from this tile pixel space to the tile's geographic - // extent - GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(gridRange, firstRasterEnvelope); - geMapper.setPixelAnchor(PixelInCell.CELL_CORNER); - - final MathTransform rasterToModel = geMapper.createTransform(); - return rasterToModel; - } - - /** - * @return the originalEnvelope - */ - public GeneralEnvelope getOriginalEnvelope() { - if (originalEnvelope == null) { - GeneralEnvelope env = null; - for (RasterInfo raster : subRasterInfo) { - GeneralEnvelope rasterEnvelope = raster.getOriginalEnvelope(); - if (env == null) { - env = new GeneralEnvelope(rasterEnvelope); - } else { - env.add(rasterEnvelope); - } - } - originalEnvelope = env; - } - return originalEnvelope; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("ArcSDE Raster: " + getRasterTable()); - sb.append(", Raster columns: ").append(Arrays.asList(getRasterColumns())); - sb.append(", Num bands: ").append(getNumBands()); - sb.append(", Dimension: ").append(getImageWidth()).append("x").append(getImageHeight()); - sb.append(", Pixel type: ").append(getNativeCellType()); - sb.append(", Has Color Map: ").append(isColorMapped()); - for (int rasterIndex = 0; rasterIndex < getNumRasters(); rasterIndex++) { - RasterInfo raster = getRasterInfo(rasterIndex); - sb.append("\n "); - sb.append(raster.toString()); - } - return sb.toString(); - } - - public int getNumRasters() { - return subRasterInfo.size(); - } - - public RasterBandInfo getBand(final int rasterIndex, final int bandIndex) { - RasterInfo rasterInfo = getRasterInfo(rasterIndex); - return rasterInfo.getBand(bandIndex); - } - - public int getNumPyramidLevels(final int rasterIndex) { - RasterInfo rasterInfo = getRasterInfo(rasterIndex); - return rasterInfo.getNumLevels(); - } - - public GeneralEnvelope getGridEnvelope(final int rasterIndex, final int pyramidLevel) { - PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); - return new GeneralEnvelope(level.getImageEnvelope()); - } - - public Rectangle getGridRange(final int rasterIndex, final int pyramidLevel) { - PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); - Rectangle levelRange = level.getImageRange(); - return levelRange; - } - - public int getNumTilesWide(int rasterIndex, int pyramidLevel) { - PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); - return level.getNumTilesWide(); - } - - public int getNumTilesHigh(int rasterIndex, int pyramidLevel) { - PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); - return level.getNumTilesHigh(); - } - - public int getTileWidth(final long rasterId) { - return getTileDimension(rasterId).width; - } - - public int getTileHeight(final long rasterId) { - return getTileDimension(rasterId).height; - } - - public Dimension getTileDimension(final long rasterId) { - final int rasterIndex = getRasterIndex(rasterId); - final RasterInfo rasterInfo = getRasterInfo(rasterIndex); - return rasterInfo.getTileDimension(); - } - - public Dimension getTileDimension(int rasterIndex) { - RasterInfo rasterInfo = getRasterInfo(rasterIndex); - return rasterInfo.getTileDimension(); - } - - private PyramidLevelInfo getLevel(int rasterIndex, int pyramidLevel) { - RasterInfo rasterInfo = getRasterInfo(rasterIndex); - PyramidLevelInfo level = rasterInfo.getPyramidLevel(pyramidLevel); - return level; - } - - private RasterInfo getRasterInfo(int rasterIndex) { - RasterInfo rasterInfo = subRasterInfo.get(rasterIndex); - return rasterInfo; - } - - public ImageTypeSpecifier getRenderedImageSpec(final long rasterId) { - final int rasterIndex = getRasterIndex(rasterId); - return getRenderedImageSpec(rasterIndex); - } - - public ImageTypeSpecifier getRenderedImageSpec(final int rasterIndex) { - if (!this.renderedImageSpec.containsKey(Integer.valueOf(rasterIndex))) { - synchronized (this) { - if (!this.renderedImageSpec.containsKey(Integer.valueOf(rasterIndex))) { - ImageTypeSpecifier imageTypeSpecifier; - imageTypeSpecifier = RasterUtils - .createFullImageTypeSpecifier(this, rasterIndex); - renderedImageSpec.put(Integer.valueOf(rasterIndex), imageTypeSpecifier); - } - } - } - return this.renderedImageSpec.get(Integer.valueOf(rasterIndex)); - } - - public IndexColorModel getColorMap(final int rasterIndex) { - final RasterBandInfo bandOne = getBand(rasterIndex, 0); - return bandOne.getColorMap(); - } - - boolean isColorMapped() { - RasterInfo rasterInfo = getRasterInfo(0); - return rasterInfo.isColorMapped(); - } - - public RasterCellType getNativeCellType() { - RasterInfo rasterInfo = getRasterInfo(0); - return rasterInfo.getNativeCellType(); - } - - public RasterCellType getTargetCellType(final int rasterIndex) { - RasterInfo rasterInfo = getRasterInfo(rasterIndex); - return rasterInfo.getTargetCellType(); - } - - public RasterCellType getTargetCellType(final long rasterId) { - final int rasterIndex = getRasterIndex(rasterId); - return getTargetCellType(rasterIndex); - } - - public Long getRasterId(final int rasterIndex) { - final RasterInfo rasterInfo = getRasterInfo(rasterIndex); - return rasterInfo.getRasterId(); - } - - public int getOptimalPyramidLevel(final int rasterIndex, final OverviewPolicy policy, - final GeneralEnvelope requestedEnvelope, final Rectangle requestedDim) { - - final RasterInfo rasterInfo = getRasterInfo(rasterIndex); - - double[] requestedRes = new double[2]; - double reqSpanX = requestedEnvelope.getSpan(0); - double reqSpanY = requestedEnvelope.getSpan(1); - requestedRes[0] = reqSpanX / requestedDim.getWidth(); - requestedRes[1] = reqSpanY / requestedDim.getHeight(); - - return rasterInfo.getOptimalPyramidLevel(policy, requestedRes); - } - - public int getRasterIndex(Long rasterId) { - int index = -1; - for (RasterInfo p : subRasterInfo) { - index++; - if (rasterId.equals(p.getRasterId())) { - return index; - } - } - throw new IllegalArgumentException("rasterId: " + rasterId); - } - - public double[] getResolution(int rasterN, int pyramidLevel) { - RasterInfo rasterInfo = getRasterInfo(rasterN); - double[] resolution = rasterInfo.getResolution(pyramidLevel); - return resolution; - } - - public Point getTileOffset(final int rasterIndex, final int pyramidLevel) { - RasterInfo rasterInfo = getRasterInfo(rasterIndex); - PyramidLevelInfo level = rasterInfo.getPyramidLevel(pyramidLevel); - return new Point(level.getXOffset(), level.getYOffset()); - } - - public Number getNoDataValue(final int rasterIndex, final int bandIndex) { - RasterBandInfo band = getBand(rasterIndex, bandIndex); - Number noDataValue = band.getNoDataValue(); - return noDataValue; - } - - /** - * @param rasterIndex - * the raster for which bands to return the no data values - * @return the list of no data values, one per band for the raster at index {@code rasterIndex} - */ - public List<Number> getNoDataValues(final int rasterIndex) { - return getRasterInfo(rasterIndex).getNoDataValues(); - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInfo.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInfo.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,405 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Dimension; -import java.awt.Point; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import org.geotools.coverage.grid.io.OverviewPolicy; -import org.geotools.data.DataSourceException; -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.geometry.jts.ReferencedEnvelope; -import org.geotools.referencing.CRS; -import org.opengis.referencing.FactoryException; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -import com.esri.sde.sdk.client.SDEPoint; -import com.esri.sde.sdk.client.SeException; -import com.esri.sde.sdk.client.SeExtent; -import com.esri.sde.sdk.client.SeRasterAttr; - -/** - * A RasterInfo gathers the metadata for a single raster in a raster dataset - * <p> - * Basically, it wraps the SeRasterAttr object and implements some convenience methods for doing - * calculations with it. - * </p> - * - * @author Saul Farber - * @author Gabriel Roldan - */ -final class RasterInfo { - - /** - * Orders pyramid levels by their level index - */ - private static final Comparator<PyramidLevelInfo> levelComparator = new Comparator<PyramidLevelInfo>() { - public int compare(PyramidLevelInfo p0, PyramidLevelInfo p1) { - return (p0.getLevel() - p1.getLevel()); - } - }; - - ArrayList<PyramidLevelInfo> pyramidList; - - private int tileWidth; - - private int tileHeight; - - private GeneralEnvelope originalEnvelope; - - private ArrayList<RasterBandInfo> bands; - - private CoordinateReferenceSystem crs; - - private Long rasterId; - - /** - * Creates an in-memory representation of an ArcSDE Raster Pyramid. Basically it wraps the - * supplide SeRasterAttr object and implements some convenience logic for extracting - * information/ doing calculations with it. - * - * @param rasterAttributes - * the SeRasterAttr object for the raster of interest. - * @param crs - * @throws DataSourceException - */ - public RasterInfo(final SeRasterAttr rasterAttributes, final CoordinateReferenceSystem crs) - throws DataSourceException { - this.crs = crs; - try { - this.rasterId = Long.valueOf(rasterAttributes.getRasterId().longValue()); - // levels goes from 0 to N, maxLevel is the zero-based max index of levels - final int numLevels = rasterAttributes.getMaxLevel() + 1; - - pyramidList = new ArrayList<PyramidLevelInfo>(numLevels); - - tileWidth = rasterAttributes.getTileWidth(); - tileHeight = rasterAttributes.getTileHeight(); - - for (int level = 0; level < numLevels; level++) { - if (level == 1 && rasterAttributes.skipLevelOne()) { - continue; - } - - /* - * this extent corresponds to the actual image size inside the tiled grid. That is, - * to getImageWidth/Height by level. The dimensions of this extent are correct, but - * it needs to be shifted by SeRasterAttr.getExtentOffsetByLevel(level) the same way - * the grid envelope does by SeRasterAttr.getImageOffsetByLevel(level) - */ - final SeExtent slExtent = rasterAttributes.getExtentByLevel(level); - - final int levelWidth = rasterAttributes.getImageWidthByLevel(level); - final int levelHeight = rasterAttributes.getImageHeightByLevel(level); - - Dimension levelImageSize = new Dimension(levelWidth, levelHeight); - - Point imgOffset = new Point(); - // extent offset equals imgOffset * pixel resolution (ie, if resx == 2 and offsetX = - // 10, extent offset x == 20) - Point2D extOffset = new Point2D.Double(); - { - SDEPoint imageOffset = rasterAttributes.getImageOffsetByLevel(level); - int xOffset = (int) (imageOffset == null ? 0 : imageOffset.getX()); - int yOffset = (int) (imageOffset == null ? 0 : imageOffset.getY()); - imgOffset.setLocation(xOffset, yOffset); - - SDEPoint extentOffset = rasterAttributes.getExtentOffsetByLevel(level); - double xOffsetExtent = extentOffset == null ? 0D : extentOffset.getX(); - double yOffsetExtent = extentOffset == null ? 0D : extentOffset.getY(); - extOffset.setLocation(xOffsetExtent, yOffsetExtent); - } - - final int numTilesWide = rasterAttributes.getTilesPerRowByLevel(level); - final int numTileHigh = rasterAttributes.getTilesPerColByLevel(level); - - ReferencedEnvelope levelExtent = new ReferencedEnvelope(slExtent.getMinX(), - slExtent.getMaxX(), slExtent.getMinY(), slExtent.getMaxY(), crs); - - addPyramidLevel(level, levelExtent, imgOffset, extOffset, numTilesWide, - numTileHigh, levelImageSize); - } - - } catch (SeException se) { - throw new DataSourceException(se); - } - } - - public Long getRasterId() { - return rasterId; - } - - public int getTileWidth() { - return tileWidth; - } - - public int getTileHeight() { - return tileHeight; - } - - /** - * Don't use this constructor. It only exists for unit testing purposes. - * - * @param tileWidth - * DON'T USE - * @param tileHeight - * DON'T USE - */ - public RasterInfo(int tileWidth, int tileHeight) { - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - pyramidList = new ArrayList<PyramidLevelInfo>(4); - } - - public Dimension getTileDimension() { - return new Dimension(tileWidth, tileHeight); - } - - public PyramidLevelInfo getPyramidLevel(int level) { - return pyramidList.get(level); - } - - public int getNumLevels() { - return pyramidList.size(); - } - - /** - * @param pyramidLevel - * @return resx, resy, scalefactor - */ - double[] getResolution(final int pyramidLevel) { - final double highestRes = getPyramidLevel(0).getXRes(); - PyramidLevelInfo level = getPyramidLevel(pyramidLevel); - double[] resolution = new double[3]; - resolution[0] = level.getXRes(); - resolution[1] = level.getYRes(); - resolution[2] = level.getXRes() / highestRes; - return resolution; - } - - /** - * <p> - * NOTE: logic stolen and adapted from {@code AbstractGridCoverage2DReader#getOverviewImage()} - * </p> - * - * @param policy - * @return - */ - public int getOptimalPyramidLevel(final OverviewPolicy policy, final double[] requestedRes) { - - int pyramidLevelChoice = 0; - - // sort resolutions from smallest pixels (higher res) to biggest pixels (higher res) - // keeping a reference to the original image choice - final double[] highestRes = getResolution(0); - - // Now search for the best matching resolution. - // Check also for the "perfect match"... unlikely in practice unless someone - // tunes the clients to request exactly the resolution embedded in - // the overviews, something a perf sensitive person might do in fact - - // the requested resolutions - final double reqx = requestedRes[0]; - final double reqy = requestedRes[1]; - - // requested scale factor for least reduced axis - final double requestedScaleFactorX = reqx / highestRes[0]; - final double requestedScaleFactorY = reqy / highestRes[1]; - final int leastReduceAxis = requestedScaleFactorX <= requestedScaleFactorY ? 0 : 1; - final double requestedScaleFactor = leastReduceAxis == 0 ? requestedScaleFactorX - : requestedScaleFactorY; - - final int numLevels = getNumLevels(); - - // no pyramiding or are we looking for a resolution even higher than the native one? - if (0 == numLevels || requestedScaleFactor <= 1) { - pyramidLevelChoice = 0; - } else { - // are we looking for a resolution even lower than the smallest overview? - final double[] min = getResolution(numLevels - 1); - if (requestedScaleFactor >= min[2]) { - pyramidLevelChoice = numLevels - 1; - } else { - // Ok, so we know the overview is between min and max, skip the first - // and search for an overview with a resolution lower than the one requested, - // that one and the one from the previous step will bound the searched resolution - double[] prev = highestRes; - for (int levelN = 1; levelN < numLevels; levelN++) { - final double[] curr = getResolution(levelN); - // perfect match check - if (curr[2] == requestedScaleFactor) { - pyramidLevelChoice = levelN; - } else { - /* - * middle check. The first part of the condition should be sufficient, but - * there are cases where the x resolution is satisfied by the lowest - * resolution, the y by the one before the lowest (so the aspect ratio of - * the request is different than the one of the overviews), and we would end - * up going out of the loop since not even the lowest can "top" the request - * for one axis - */ - if (curr[2] > requestedScaleFactor || levelN == numLevels - 1) { - if (policy == OverviewPolicy.QUALITY) { - pyramidLevelChoice = levelN - 1; - } else if (policy == OverviewPolicy.SPEED) { - return levelN; - } else if (requestedScaleFactor - prev[2] < curr[2] - - requestedScaleFactor) { - pyramidLevelChoice = levelN - 1; - } else { - pyramidLevelChoice = levelN; - } - break; - } - prev = curr; - } - } - } - } - // fallback - return pyramidLevelChoice; - } - - /** - * Don't use this method. It's only public for unit testing purposes. - * - * @param level - * the zero-based level index for the new level - * @param extent - * the geographical extent the level covers, may need to be offsetted by {@code - * extOffset} - * @param imgOffset - * the offset on the X and Y axes of the actual image inside the tile space for this - * level - * @param extOffset - * the offset on the X and Y axes of the actual image inside the tile space for this - * level - * @param numTilesWide - * the number of tiles that make up the level on the X axis - * @param numTilesHigh - * the number of tiles that make up the level on the Y axis - * @param imageSize - * the size of the actual image in pixels - */ - void addPyramidLevel(int level, ReferencedEnvelope extent, Point imgOffset, Point2D extOffset, - int numTilesWide, int numTilesHigh, Dimension imageSize) { - - PyramidLevelInfo pyramidLevel; - pyramidLevel = new PyramidLevelInfo(level, extent, imgOffset, extOffset, numTilesWide, - numTilesHigh, imageSize); - - pyramidList.add(pyramidLevel); - - Collections.sort(pyramidList, levelComparator); - } - - void setOriginalEnvelope(GeneralEnvelope originalEnvelope) { - this.originalEnvelope = originalEnvelope; - } - - public GeneralEnvelope getOriginalEnvelope() { - return originalEnvelope; - } - - void setBands(List<RasterBandInfo> bands) { - this.bands = new ArrayList<RasterBandInfo>(bands); - } - - public List<RasterBandInfo> getBands() { - return new ArrayList<RasterBandInfo>(bands); - } - - public int getNumBands() { - return bands.size(); - } - - public RasterBandInfo getBand(final int index) { - return bands.get(index); - } - - public CoordinateReferenceSystem getCoordinateReferenceSystem() { - return crs; - } - - public RasterCellType getTargetCellType() { -// if (isColorMapped()) { -// // color map is already promoted if needed -// return getNativeCellType(); -// } - List<Number> noDataValues = getNoDataValues(); - RasterCellType nativeCellType = getNativeCellType(); - RasterCellType targetCellType = RasterUtils.determineTargetCellType(nativeCellType, - noDataValues); - return targetCellType; - } - - public boolean isColorMapped() { - return getBand(0).isColorMapped(); - } - - public RasterCellType getNativeCellType() { - return getBand(0).getCellType(); - } - - public List<Number> getNoDataValues() { - final List<Number> noDataValues = new ArrayList<Number>(); - for (RasterBandInfo band : getBands()) { - Number noDataValue = band.getNoDataValue(); - noDataValues.add(noDataValue); - } - return noDataValues; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - sb.append("[Id: ").append(getRasterId()); - String srs = null; - try { - srs = CRS.lookupIdentifier(getCoordinateReferenceSystem(), false); - } catch (FactoryException e) { - e.printStackTrace(); - } - sb.append(", bands: ").append(getNumBands()); - sb.append(", levels: ").append(getNumLevels()); - sb.append(", tile size: ").append(getTileWidth()).append("x").append(getTileHeight()); - sb.append(", crs: ").append(srs == null ? getCoordinateReferenceSystem().toWKT() : srs); - GeneralEnvelope env = getOriginalEnvelope(); - sb.append(", Envelope: ").append(env.getMinimum(0)).append(",").append(env.getMinimum(1)) - .append(" ").append(env.getMaximum(0)).append(",").append(env.getMaximum(1)); - - sb.append("]\n Bands["); - for (RasterBandInfo band : getBands()) { - sb.append("\n\t"); - sb.append(band.toString()); - } - sb.append("\n ]"); - sb.append("\n Pyramid["); - for (int l = 0; l < getNumLevels(); l++) { - sb.append("\n\t").append(getPyramidLevel(l).toString()); - } - sb.append("\n ]"); - return sb.toString(); - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInputStream.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInputStream.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInputStream.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,175 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import javax.imageio.stream.ImageInputStream; - -import org.geotools.arcsde.gce.TileReader.TileInfo; - -/** - * An {@link ImageInputStream} that reads ArcSDE raster tiles in a band interleaved order. - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL: - * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org - * /geotools/arcsde/gce/ArcSDETiledImageInputStream.java $ - */ -final class RasterInputStream extends InputStream { - - private final TileReader tileReader; - - private final int tileDataLength; - - private byte[] currTileData; - - private int currTileDataIndex; - - private final int length; - - private long streamPos; - - ByteArrayInputStream in; - byte[] buf; - - public RasterInputStream(final TileReader tileReader) { - super(); - this.tileReader = tileReader; - final int bytesPerTile = tileReader.getBytesPerTile(); - this.tileDataLength = bytesPerTile; - this.currTileData = new byte[bytesPerTile]; - // force load at the first read invocation - this.currTileDataIndex = tileDataLength; - - final int tilesWide = tileReader.getTilesWide(); - final int tilesHigh = tileReader.getTilesHigh(); - final int numberOfBands = tileReader.getNumberOfBands(); - - length = bytesPerTile * tilesWide * tilesHigh * numberOfBands; - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - while (tileReader.hasNext()) { - TileInfo next = tileReader.next(); - out.write(next.getTileData()); - } - out.flush(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - buf = out.toByteArray(); - this.in = new ByteArrayInputStream(buf); - - if (length != buf.length) { - throw new IllegalStateException(length + " != " + buf.length); - } - } - - @Override - public int read() throws IOException { - return in.read(); - // final byte[] data = getTileData(); - // if (data == null) { - // return -1; - // } - // byte b = data[currTileDataIndex]; - // ++currTileDataIndex; - // return b; - } - - @Override - public int read(byte[] buff, int off, int len) throws IOException { - return in.read(buff, off, len); - // // System.err.println("read " + len + ", pos = " + streamPos + ", length = " + length); - // final byte[] data = getTileData(); - // if (data == null) { - // return -1; - // } - // final int available = data.length - currTileDataIndex; - // final int count = Math.min(available, len); - // System.arraycopy(data, currTileDataIndex, buff, off, count); - // currTileDataIndex += count; - // streamPos += count; - // if (streamPos == length) { - // close(); - // } - // return count; - } - - /** - * Fetches a tile from the {@code tileReader} if necessary and returns the current tile data. - * <p> - * It is needed to fetch a new tile if {@link #currTileDataIndex} indicates all the current tile - * data has been already read. If so, {@code currTileDataIndex} is reset to 0. The {@code read} - * operations are responsible of incrementing {@code currTileDataIndex} depending on how many - * bytes have been consumed from the tile data returned by this method. - * </p> - * - * @return {@code null} if there's no more tiles to fetch, the current tile data otherwise - * @throws IOException - */ - private byte[] getTileData() throws IOException { - if (currTileDataIndex == tileDataLength) { - if (!tileReader.hasNext()) { - return null; - } - - currTileDataIndex = 0; - TileInfo tileInfo = tileReader.next(); - currTileData = tileInfo.getTileData(); - } - return currTileData; - } - - @Override - public void close() throws IOException { - tileReader.dispose(); - } - - @Override - public int available() throws IOException { - return in.available(); - // return tileDataLength - currTileDataIndex; - } - - @Override - public synchronized void mark(int readlimit) { - in.mark(readlimit); - // System.err.println("mark at " + readlimit); - } - - @Override - public boolean markSupported() { - return in.markSupported(); - // return false; - } - - @Override - public long skip(long n) throws IOException { - return in.skip(n); - // return super.skip(n); - } -} \ No newline at end of file Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterQueryInfo.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterQueryInfo.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterQueryInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,216 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Rectangle; -import java.awt.image.RenderedImage; -import java.util.logging.Logger; - -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.util.logging.Logging; - -/** - * Captures information about a query for a single raster in a raster dataset. - * - * @author Gabriel Roldan - * @version $Id$ - * @since 2.5.6 - * @see RasterUtils#findMatchingRasters - * @see RasterUtils#fitRequestToRaster - */ -final class RasterQueryInfo { - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - private GeneralEnvelope requestedEnvelope; - - private Rectangle requestedDim; - - private int pyramidLevel; - - /** - * The two-dimensional range of tile indices whose envelope intersect the requested extent. Will - * have negative width and height if none of the tiles do. - */ - private Rectangle matchingTiles; - - private GeneralEnvelope resultEnvelope; - - private Rectangle resultDimension; - - private Long rasterId; - - private Rectangle mosaicLocation; - - private RenderedImage resultImage; - - private Rectangle tiledImageSize; - - private double[] resolution; - - private int rasterIndex; - - /** - * The full tile range for the matching pyramid level - */ - private Rectangle levelTileRange; - - public RasterQueryInfo() { - setResultDimensionInsideTiledImage(new Rectangle(0, 0, 0, 0)); - setMatchingTiles(new Rectangle(0, 0, 0, 0)); - setResultEnvelope(null); - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder("[Raster query info:"); - s.append("\n\tRaster ID : ").append(getRasterId()); - s.append("\n\tPyramid level : ").append(getPyramidLevel()); - s.append("\n\tResolution : ").append( - getResolution()[0] + "," + getResolution()[1]); - s.append("\n\tRequested envelope : ").append(getRequestedEnvelope()); - s.append("\n\tRequested dimension : ").append(getRequestedDim()); - Rectangle mt = getMatchingTiles(); - Rectangle ltr = getLevelTileRange(); - String matching = "x=" + mt.x + "-" + (mt.x + mt.width - 1) + ", y=" + mt.y + "-" - + (mt.y + mt.height - 1); - String level = "x=" + ltr.x + "-" + (ltr.width - 1) + ", y=" + ltr.y + "-" - + (ltr.height - 1); - s.append("\n\tMatching tiles : ").append(matching).append(" out of ").append(level); - s.append("\n\tTiled image size : ").append(getTiledImageSize()); - s.append("\n\tResult dimension : ").append(getResultDimensionInsideTiledImage()); - s.append("\n\tMosaiced dimension : ").append(getMosaicLocation()); - s.append("\n\tResult envelope : ").append(getResultEnvelope()); - s.append("\n]"); - return s.toString(); - } - - /** - * @return the rasterId (as in SeRaster.getId()) for the raster in the raster dataset this query - * works upon - */ - public Long getRasterId() { - return rasterId; - } - - public GeneralEnvelope getRequestedEnvelope() { - return requestedEnvelope; - } - - public Rectangle getRequestedDim() { - return requestedDim; - } - - public int getPyramidLevel() { - return pyramidLevel; - } - - public Rectangle getMatchingTiles() { - return matchingTiles; - } - - public GeneralEnvelope getResultEnvelope() { - return resultEnvelope; - } - - public Rectangle getResultDimensionInsideTiledImage() { - return resultDimension; - } - - void setRasterId(Long rasterId) { - this.rasterId = rasterId; - } - - void setPyramidLevel(int pyramidLevel) { - this.pyramidLevel = pyramidLevel; - } - - void setRequestedEnvelope(GeneralEnvelope requestedEnvelope) { - this.requestedEnvelope = requestedEnvelope; - } - - void setRequestedDim(Rectangle requestedDim) { - this.requestedDim = requestedDim; - } - - void setResultEnvelope(GeneralEnvelope resultEnvelope) { - this.resultEnvelope = resultEnvelope; - } - - void setMatchingTiles(Rectangle matchingTiles) { - this.matchingTiles = matchingTiles; - } - - void setResultDimensionInsideTiledImage(Rectangle resultDimension) { - this.resultDimension = resultDimension; - } - - void setMosaicLocation(Rectangle rasterMosaicLocation) { - this.mosaicLocation = rasterMosaicLocation; - } - - public Rectangle getMosaicLocation() { - return mosaicLocation; - } - - public void setResultImage(RenderedImage rasterImage) { - this.resultImage = rasterImage; - if (rasterImage.getWidth() != tiledImageSize.width - || rasterImage.getHeight() != tiledImageSize.height) { - LOGGER.warning("Result image and expected dimensions don't match: image=" - + resultImage.getWidth() + "x" + resultImage.getHeight() + ", expected=" - + tiledImageSize.width + "x" + tiledImageSize.height); - } - } - - public RenderedImage getResultImage() { - return resultImage; - } - - void setTiledImageSize(Rectangle tiledImageSize) { - this.tiledImageSize = tiledImageSize; - } - - public Rectangle getTiledImageSize() { - return tiledImageSize; - } - - void setResolution(double[] resolution) { - this.resolution = resolution; - } - - public double[] getResolution() { - return resolution == null ? new double[] { -1, -1 } : resolution; - } - - void setRasterIndex(int rasterN) { - this.rasterIndex = rasterN; - } - - public int getRasterIndex() { - return rasterIndex; - } - - void setLevelTileRange(Rectangle levelTileRange) { - this.levelTileRange = levelTileRange; - } - - public Rectangle getLevelTileRange() { - return levelTileRange; - } -} \ No newline at end of file Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterReaderFactory.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterReaderFactory.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterReaderFactory.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,56 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.io.IOException; - -import org.geotools.arcsde.session.ISessionPool; - -/** - * - * @author Gabriel Roldan - * - * @source $URL: - * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org - * /geotools/arcsde/gce/RasterReaderFactory.java $ - * @version $Id$ - * @since 2.5.7 - */ -public class RasterReaderFactory { - - private final ISessionPool sessionPool; - - public RasterReaderFactory(final ISessionPool connectionPool) { - this.sessionPool = connectionPool; - } - - /** - * Creates a {@link TiledRasterReader} that's able to read one or more raster for the given - * {@link RasterDatasetInfo}, depending on if {@code rasterInfo} represents a single raster or a - * raster catalog. - * - * @param rasterInfo - * @return - * @throws IOException - */ - public TiledRasterReader create(final RasterDatasetInfo rasterInfo) throws IOException { - TiledRasterReader rasterReader = new DefaultTiledRasterReader(sessionPool, rasterInfo); - - return rasterReader; - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterUtils.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterUtils.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterUtils.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,998 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_1BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_REAL; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_4BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_64BIT_REAL; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_U; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Transparency; -import java.awt.color.ColorSpace; -import java.awt.image.BandedSampleModel; -import java.awt.image.ColorModel; -import java.awt.image.ComponentColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.IndexColorModel; -import java.awt.image.SampleModel; -import java.util.ArrayList; -import java.util.List; -import java.util.logging.Logger; - -import javax.imageio.ImageTypeSpecifier; - -import org.geotools.coverage.GridSampleDimension; -import org.geotools.coverage.grid.GeneralGridEnvelope; -import org.geotools.coverage.grid.GridGeometry2D; -import org.geotools.coverage.grid.io.AbstractGridFormat; -import org.geotools.coverage.grid.io.OverviewPolicy; -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.geometry.jts.ReferencedEnvelope; -import org.geotools.referencing.CRS; -import org.geotools.referencing.operation.builder.GridToEnvelopeMapper; -import org.geotools.resources.image.ColorUtilities; -import org.geotools.resources.image.ComponentColorModelJAI; -import org.geotools.util.NumberRange; -import org.geotools.util.logging.Logging; -import org.opengis.geometry.Envelope; -import org.opengis.parameter.GeneralParameterValue; -import org.opengis.parameter.ParameterNotFoundException; -import org.opengis.parameter.ParameterValue; -import org.opengis.referencing.FactoryException; -import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.referencing.datum.PixelInCell; -import org.opengis.referencing.operation.MathTransform; -import org.opengis.referencing.operation.NoninvertibleTransformException; -import org.opengis.referencing.operation.TransformException; - -import com.sun.imageio.plugins.common.BogusColorSpace; - -/** - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL$ - */ -@SuppressWarnings( { "nls" }) -class RasterUtils { - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - private RasterUtils() { - // do nothing - } - - public static ReferencedEnvelope toReferencedEnvelope(GeneralEnvelope envelope) { - double minx = envelope.getMinimum(0); - double maxx = envelope.getMaximum(0); - double miny = envelope.getMinimum(1); - double maxy = envelope.getMaximum(1); - CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem(); - - ReferencedEnvelope refEnv = new ReferencedEnvelope(minx, maxx, miny, maxy, crs); - return refEnv; - } - - public static ReferencedEnvelope toNativeCrs(final GeneralEnvelope requestedEnvelope, - final CoordinateReferenceSystem nativeCRS) throws IllegalArgumentException { - - ReferencedEnvelope reqEnv = toReferencedEnvelope(requestedEnvelope); - - if (!CRS.equalsIgnoreMetadata(nativeCRS, reqEnv.getCoordinateReferenceSystem())) { - // we're being reprojected. We'll need to reproject reqEnv into - // our native coordsys - try { - // ReferencedEnvelope origReqEnv = reqEnv; - reqEnv = reqEnv.transform(nativeCRS, true); - } catch (FactoryException fe) { - // unable to reproject? - throw new IllegalArgumentException("Unable to find a reprojection from requested " - + "coordsys to native coordsys for this request", fe); - } catch (TransformException te) { - throw new IllegalArgumentException("Unable to perform reprojection from requested " - + "coordsys to native coordsys for this request", te); - } - } - return reqEnv; - } - - public static MathTransform createRasterToModel(final Rectangle levelGridRange, - final GeneralEnvelope levelEnvelope) { - // create a raster to model transform, from this tile pixel space to the tile's geographic - // extent - GeneralGridEnvelope gridRange = new GeneralGridEnvelope(levelGridRange, 2); - GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(gridRange, levelEnvelope); - geMapper.setPixelAnchor(PixelInCell.CELL_CORNER); - - final MathTransform rasterToModel = geMapper.createTransform(); - return rasterToModel; - } - - private static Rectangle getResultDimensionForTileRange(final Rectangle tiledImageGridRange, - final Rectangle matchingLevelRange) { - - int minx = Math.max(tiledImageGridRange.x, matchingLevelRange.x); - int miny = Math.max(tiledImageGridRange.y, matchingLevelRange.y); - int maxx = (int) Math.min(tiledImageGridRange.getMaxX(), matchingLevelRange.getMaxX()); - int maxy = (int) Math.min(tiledImageGridRange.getMaxY(), matchingLevelRange.getMaxY()); - - return new Rectangle(minx, miny, maxx - minx, maxy - miny); - } - - /** - * Returns the rectangle specifying the matching tiles for a given pyramid level and rectangle - * specifying the overlapping area to request in the level's pixel space. - * - * @param pixelRange - * @param tilesHigh - * @param tilesWide - * @param tileSize - * @param numTilesHigh - * @param numTilesWide - * - * @param pixelRange - * @param level - * - * @return a rectangle holding the coordinates in tile space that fully covers the requested - * pixel range for the given pyramid level, or a negative area rectangle - */ - private static Rectangle findMatchingTiles(final Dimension tileSize, int numTilesWide, - int numTilesHigh, final Rectangle pixelRange) { - - final int minPixelX = pixelRange.x; - final int minPixelY = pixelRange.y; - - // TODO: WARNING, we're not considering the possible x/y offsets on the level range for the - // given pyramid level here! - - int minTileX = (int) Math.floor(minPixelX / tileSize.getWidth()); - int minTileY = (int) Math.floor(minPixelY / tileSize.getHeight()); - - int numTilesX = (int) Math.ceil(pixelRange.getWidth() / tileSize.getWidth()); - int numTilesY = (int) Math.ceil(pixelRange.getHeight() / tileSize.getHeight()); - - int maxTiledX = (minTileX + numTilesX) * tileSize.width; - int maxTiledY = (minTileY + numTilesY) * tileSize.height; - - if (maxTiledX < pixelRange.getMaxX() && (minTileX + numTilesX) < numTilesWide) { - numTilesX++; - } - - if (maxTiledY < pixelRange.getMaxY() && (minTileY + numTilesY) < numTilesHigh) { - numTilesY++; - } - - Rectangle matchingTiles = new Rectangle(minTileX, minTileY, numTilesX, numTilesY); - return matchingTiles; - } - - private static Rectangle getTargetGridRange(final MathTransform modelToRaster, - final Envelope requestedEnvelope) { - Rectangle levelOverlappingPixels; - int levelMinPixelX; - int levelMaxPixelX; - int levelMinPixelY; - int levelMaxPixelY; - { - // use a model to raster transform to find out which pixel range at the specified level - // better match the requested extent - GeneralEnvelope requestedPixels; - try { - requestedPixels = CRS.transform(modelToRaster, requestedEnvelope); - } catch (NoninvertibleTransformException e) { - throw new IllegalArgumentException(e); - } catch (TransformException e) { - throw new IllegalArgumentException(e); - } - - levelMinPixelX = (int) Math.floor(requestedPixels.getMinimum(0)); - levelMaxPixelX = (int) Math.floor(requestedPixels.getMaximum(0)); - - levelMinPixelY = (int) Math.ceil(requestedPixels.getMinimum(1)); - levelMaxPixelY = (int) Math.ceil(requestedPixels.getMaximum(1)); - - final int width = levelMaxPixelX - levelMinPixelX; - final int height = levelMaxPixelY - levelMinPixelY; - levelOverlappingPixels = new Rectangle(levelMinPixelX, levelMinPixelY, width, height); - } - return levelOverlappingPixels; - } - - /** - * Creates an IndexColorModel out of a DataBuffer obtained from an ArcSDE's raster color map. - * - * @param colorMapData - * @return - */ - public static IndexColorModel sdeColorMapToJavaColorModel(final DataBuffer colorMapData, - final int bitsPerSample) { - if (colorMapData == null) { - throw new NullPointerException("colorMapData"); - } - - if (colorMapData.getNumBanks() < 3 || colorMapData.getNumBanks() > 4) { - throw new IllegalArgumentException("colorMapData shall have 3 or 4 banks: " - + colorMapData.getNumBanks()); - } - - if (bitsPerSample != 8 && bitsPerSample != 16) { - throw new IllegalAccessError("bits per sample shall be either 8 or 16. Got " - + bitsPerSample); - } - - final int numBanks = colorMapData.getNumBanks(); - final int mapSize = colorMapData.getSize(); - - byte[] r = new byte[mapSize]; - byte[] g = new byte[mapSize]; - byte[] b = new byte[mapSize]; - byte[] a = new byte[mapSize]; - - for (int i = 0; i < mapSize; i++) { - r[i] = (byte) (colorMapData.getElem(0, i) & 0xFF); - g[i] = (byte) (colorMapData.getElem(1, i) & 0xFF); - b[i] = (byte) (colorMapData.getElem(2, i) & 0xFF); - a[i] = (byte) ((numBanks == 3 ? 255 : colorMapData.getElem(3, i)) & 0xFF); - } - - IndexColorModel colorModel = new IndexColorModel(bitsPerSample, mapSize, r, g, b, a); - - return colorModel; - } - - public static ImageTypeSpecifier createFullImageTypeSpecifier( - final RasterDatasetInfo rasterInfo, final int rasterIndex) { - - final int numberOfBands = rasterInfo.getNumBands(); - final RasterCellType nativePixelType = rasterInfo.getNativeCellType(); - final RasterCellType pixelType = rasterInfo.getTargetCellType(rasterIndex); - - // Prepare temporary colorModel and sample model, needed to build the final - // ArcSDEPyramidLevel level; - int sampleImageWidth = 1;// rasterInfo.getImageWidth(); - int sampleImageHeight = 1;// rasterInfo.getImageHeight(); - - final ImageTypeSpecifier its; - // treat special cases... - final int bitsPerSample = pixelType.getBitsPerSample(); - final int dataType = pixelType.getDataBufferType(); - final boolean hasColorMap = rasterInfo.isColorMapped(); - - if (hasColorMap) { - // special case, a single band colormapped image - IndexColorModel colorMap = rasterInfo.getColorMap(rasterIndex); - its = createColorMappedImageSpec(colorMap, sampleImageWidth, sampleImageHeight); - - } else if (nativePixelType == TYPE_1BIT && numberOfBands == 1) { - byte noDataValue = rasterInfo.getNoDataValue(rasterIndex, 0).byteValue(); - // special case, a single band 1-bit - its = createOneBitColorMappedImageSpec(sampleImageWidth, sampleImageHeight, noDataValue); - - } else if (nativePixelType == TYPE_4BIT && numberOfBands == 1) { - byte noDataValue = rasterInfo.getNoDataValue(rasterIndex, 0).byteValue(); - // special case, a single band 4-bit - its = createFourBitColorMappedImageSpec(sampleImageWidth, sampleImageHeight, - noDataValue); - } else if (numberOfBands == 1) { - // special case, a single band grayscale image, no matter the pixel depth - its = createGrayscaleImageSpec(sampleImageWidth, sampleImageHeight, dataType, - bitsPerSample); - - } else if (numberOfBands == 3 && pixelType == TYPE_8BIT_U) { - // special case, an optimizable RGB image - its = createRGBImageSpec(sampleImageWidth, sampleImageHeight, dataType); - - } else if (numberOfBands == 4 && pixelType == TYPE_8BIT_U) { - // special case, an optimizable RGBA image - its = createRGBAImageSpec(sampleImageWidth, sampleImageHeight, dataType); - - } else { - /* - * not an special case, go for a more generic sample model, potentially slower than the - * special case ones, but that'll work anyway - */ - - final ColorModel colorModel; - final SampleModel sampleModel; - { - final ColorSpace colorSpace; - colorSpace = new BogusColorSpace(numberOfBands); - int[] numBits = new int[numberOfBands]; - for (int i = 0; i < numberOfBands; i++) { - numBits[i] = bitsPerSample; - } - colorModel = new ComponentColorModelJAI(colorSpace, numBits, false, false, - Transparency.OPAQUE, dataType); - } - { - int[] bankIndices = new int[numberOfBands]; - int[] bandOffsets = new int[numberOfBands]; - // int bandOffset = (tileWidth * tileHeight * pixelType.getBitsPerSample()) / 8; - for (int i = 0; i < numberOfBands; i++) { - bankIndices[i] = i; - bandOffsets[i] = 0;// (i * bandOffset); - } - sampleModel = new BandedSampleModel(dataType, sampleImageWidth, sampleImageHeight, - sampleImageWidth, bankIndices, bandOffsets); - } - its = new ImageTypeSpecifier(colorModel, sampleModel); - } - - return its; - } - - private static ImageTypeSpecifier createFourBitColorMappedImageSpec(int sampleImageWidth, - int sampleImageHeight, byte noDataValue) { - - int maxValue = (int) TYPE_4BIT.getSampleValueRange().getMaximum(); - - int mapSize = noDataValue > maxValue ? noDataValue : maxValue + 1; - - int[] cmap = new int[mapSize]; - ColorUtilities.expand(new Color[] { Color.BLACK, Color.WHITE }, cmap, 0, maxValue); - - for (int i = maxValue; i < mapSize; i++) { - cmap[i] = ColorUtilities.getIntFromColor(0, 0, 0, 0); - } - - int transparentPixel = noDataValue; - IndexColorModel colorModel = new IndexColorModel(8, mapSize, cmap, 0, true, - transparentPixel, DataBuffer.TYPE_BYTE); - - SampleModel sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, - sampleImageHeight); - ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); - return its; - } - - private static ImageTypeSpecifier createOneBitColorMappedImageSpec(int sampleImageWidth, - int sampleImageHeight, byte noDataValue) { - - assert noDataValue == 2; - - final int FALSE = ColorUtilities.getIntFromColor(255, 255, 255, 255); - final int TRUE = ColorUtilities.getIntFromColor(255, 0, 0, 255); - final int NODATA = ColorUtilities.getIntFromColor(0, 255, 0, 255); - - final int mapSize = 3; - int[] cmap = new int[mapSize]; - cmap[0] = FALSE; - cmap[1] = TRUE; - cmap[2] = NODATA; - - int transparentPixel = noDataValue; - IndexColorModel colorModel = new IndexColorModel(8, mapSize, cmap, 0, false, - transparentPixel, DataBuffer.TYPE_BYTE); - - SampleModel sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, - sampleImageHeight); - ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); - return its; - } - - private static ImageTypeSpecifier createRGBAImageSpec(int sampleImageWidth, - int sampleImageHeight, final int dataType) { - - final ImageTypeSpecifier its; - - ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); - boolean hasAlpha = true; - boolean isAlphaPremultiplied = false; - int transparency = Transparency.TRANSLUCENT; - int transferType = dataType; - - int[] nBits = { 8, 8, 8, 8 }; - ColorModel colorModel = new ComponentColorModelJAI(colorSpace, nBits, hasAlpha, - isAlphaPremultiplied, transparency, transferType); - - /* - * Do not use colorModel.createCompatibleSampleModel cause it creates a - * PixelInterleavedSampleModel and we need a BandedSampleModel so it matches how the data - * comes out of ArcSDE - */ - SampleModel sampleModel = new BandedSampleModel(dataType, sampleImageWidth, - sampleImageHeight, 4); - - its = new ImageTypeSpecifier(colorModel, sampleModel); - return its; - } - - private static ImageTypeSpecifier createRGBImageSpec(int sampleImageWidth, - int sampleImageHeight, final int dataType) { - - final ImageTypeSpecifier its; - ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); - boolean hasAlpha = false; - boolean isAlphaPremultiplied = false; - int transparency = Transparency.OPAQUE; - int transferType = dataType; - ColorModel colorModel = new ComponentColorModel(colorSpace, new int[] { 8, 8, 8 }, - hasAlpha, isAlphaPremultiplied, transparency, transferType); - - SampleModel sampleModel = new BandedSampleModel(dataType, sampleImageWidth, - sampleImageHeight, 3); - - its = new ImageTypeSpecifier(colorModel, sampleModel); - return its; - } - - private static ImageTypeSpecifier createGrayscaleImageSpec(int sampleImageWidth, - int sampleImageHeight, final int dataType, int bitsPerPixel) { - final ImageTypeSpecifier its; - ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); - boolean hasAlpha = false; - boolean isAlphaPremultiplied = false; - int transparency = Transparency.OPAQUE; - int transferType = dataType; - int[] nbits = { bitsPerPixel }; - ColorModel colorModel = new ComponentColorModelJAI(colorSpace, nbits, hasAlpha, - isAlphaPremultiplied, transparency, transferType); - - SampleModel sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, - sampleImageHeight); - its = new ImageTypeSpecifier(colorModel, sampleModel); - return its; - } - - private static ImageTypeSpecifier createOneBitImageSpec(final RasterDatasetInfo rasterInfo, - final int numberOfBands, int sampleImageWidth, int sampleImageHeight, - final int bitsPerSample, final int dataType) { - final ColorModel colorModel; - final SampleModel sampleModel; - if (numberOfBands != 1) { - throw new IllegalArgumentException(bitsPerSample - + "-Bit rasters are only supported for one band"); - } - int[] argb = new int[(int) Math.pow(2, bitsPerSample)]; - ColorUtilities.expand(new Color[] { Color.WHITE, Color.BLACK }, argb, 0, argb.length); - GridSampleDimension gridSampleDimension = rasterInfo.getGridSampleDimensions()[0]; - colorModel = gridSampleDimension.getColorModel(0, numberOfBands, dataType); - sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, sampleImageHeight); - - ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); - return its; - } - - private static ImageTypeSpecifier createColorMappedImageSpec(final IndexColorModel colorModel, - int sampleImageWidth, int sampleImageHeight) { - - final SampleModel sampleModel; - final ImageTypeSpecifier its; - LOGGER.fine("Found single-band colormapped raster, using its index color model"); - sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, sampleImageHeight); - its = new ImageTypeSpecifier(colorModel, sampleModel); - return its; - - } - - public static ArcSDEGridCoverage2DReaderJAI.ReadParameters parseReadParams( - final GeneralEnvelope coverageEnvelope, final GeneralParameterValue[] params) - throws IllegalArgumentException { - if (params == null) { - throw new IllegalArgumentException("No GeneralParameterValue given to read operation"); - } - - GeneralEnvelope reqEnvelope = null; - Rectangle 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()); - - CoordinateReferenceSystem nativeCrs = coverageEnvelope - .getCoordinateReferenceSystem(); - CoordinateReferenceSystem requestCrs = reqEnvelope.getCoordinateReferenceSystem(); - if (!CRS.equalsIgnoreMetadata(nativeCrs, requestCrs)) { - LOGGER.info("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().getBounds(); - continue; - } - if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName().toString())) { - overviewPolicy = (OverviewPolicy) param.getValue(); - continue; - } - } - - if (dim == null && reqEnvelope == null) { - throw new ParameterNotFoundException("Parameter is mandatory and shall provide " - + "the extent and dimension to request", AbstractGridFormat.READ_GRIDGEOMETRY2D - .getName().toString()); - } - - if (!reqEnvelope.intersects(coverageEnvelope, true)) { - throw new IllegalArgumentException( - "The requested extend does not overlap the coverage extent: " - + coverageEnvelope); - } - - if (dim.width <= 0 || dim.height <= 0) { - throw new IllegalArgumentException("The requested coverage dimension can't be null: " - + dim); - } - - if (overviewPolicy == null) { - LOGGER.finer("No overview policy requested, defaulting to QUALITY"); - overviewPolicy = OverviewPolicy.QUALITY; - } - LOGGER.fine("Overview policy is " + overviewPolicy); - - LOGGER.info("Reading raster for " + dim.getWidth() + "x" + dim.getHeight() - + " requested dim and " + reqEnvelope.getMinimum(0) + "," - + reqEnvelope.getMaximum(0) + " - " + reqEnvelope.getMinimum(1) - + reqEnvelope.getMaximum(1) + " requested extent"); - - ArcSDEGridCoverage2DReaderJAI.ReadParameters parsedParams = new ArcSDEGridCoverage2DReaderJAI.ReadParameters(); - parsedParams.requestedEnvelope = reqEnvelope; - parsedParams.dim = dim; - parsedParams.overviewPolicy = overviewPolicy; - return parsedParams; - } - - /** - * Given a collection of {@link RasterQueryInfo} instances holding information about how a - * request fits for each individual raster composing a catalog, figure out where their resulting - * images fit into the overall mosaic that's gonna be the result of the request. - * - * @param rasterInfo - * @param resultEnvelope - * @param results - * @return - */ - public static Rectangle setMosaicLocations(final RasterDatasetInfo rasterInfo, - final GeneralEnvelope resultEnvelope, final List<RasterQueryInfo> results) { - final Rectangle mosaicDimension; - final MathTransform modelToRaster; - final MathTransform rasterToModel; - { - /* - * Of all the rasters that match the requested envelope, chose the one with the lowest - * resolution as the base to compute the final mosaic layout, so we avoid JAI upsamples, - * which are buggy and produce repeated patterns over the x axis instead of just scaling - * up the image. - */ - RasterQueryInfo dimensionChoice = findLowestResolution(results); - Long rasterId = dimensionChoice.getRasterId(); - int pyramidLevel = dimensionChoice.getPyramidLevel(); - int rasterIndex = rasterInfo.getRasterIndex(rasterId); - Rectangle levelRange = rasterInfo.getGridRange(rasterIndex, pyramidLevel); - GeneralEnvelope levelEnvelope = rasterInfo.getGridEnvelope(rasterIndex, pyramidLevel); - rasterToModel = createRasterToModel(levelRange, levelEnvelope); - try { - modelToRaster = rasterToModel.inverse(); - } catch (NoninvertibleTransformException e) { - throw new RuntimeException(e); - } - mosaicDimension = getTargetGridRange(modelToRaster, resultEnvelope); - } - - for (RasterQueryInfo rasterResultInfo : results) { - final GeneralEnvelope rasterResultEnvelope = rasterResultInfo.getResultEnvelope(); - - final Rectangle targetRasterGridRange; - targetRasterGridRange = getTargetGridRange(modelToRaster, rasterResultEnvelope); - - rasterResultInfo.setMosaicLocation(targetRasterGridRange); - } - - return mosaicDimension; - } - - private static RasterQueryInfo findLowestResolution(List<RasterQueryInfo> results) { - double[] prev = { Double.MIN_VALUE, Double.MIN_VALUE }; - RasterQueryInfo lowestResQuery = null; - - double[] curr; - for (RasterQueryInfo query : results) { - curr = query.getResolution(); - if (curr[0] > prev[0]) { - prev = curr; - lowestResQuery = query; - } - } - return lowestResQuery; - } - - /** - * Find out the raster ids and their pyramid levels in the raster dataset for the rasters whose - * envelope overlaps the requested one - * - * @param rasterInfo - * @param requestedEnvelope - * @param requestedDim - * @param overviewPolicy - * @return - */ - public static List<RasterQueryInfo> findMatchingRasters(final RasterDatasetInfo rasterInfo, - final GeneralEnvelope requestedEnvelope, final Rectangle requestedDim, - final OverviewPolicy overviewPolicy) { - - final int numRasters = rasterInfo.getNumRasters(); - List<RasterQueryInfo> matchingRasters = new ArrayList<RasterQueryInfo>(numRasters); - - int optimalPyramidLevel; - GeneralEnvelope gridEnvelope; - for (int rasterN = 0; rasterN < numRasters; rasterN++) { - optimalPyramidLevel = rasterInfo.getOptimalPyramidLevel(rasterN, overviewPolicy, - requestedEnvelope, requestedDim); - gridEnvelope = rasterInfo.getGridEnvelope(rasterN, optimalPyramidLevel); - final boolean edgesInclusive = true; - if (requestedEnvelope.intersects(gridEnvelope, edgesInclusive)) { - RasterQueryInfo match = new RasterQueryInfo(); - match.setRequestedEnvelope(requestedEnvelope); - match.setRequestedDim(requestedDim); - - match.setRasterId(rasterInfo.getRasterId(rasterN)); - match.setRasterIndex(rasterN); - match.setPyramidLevel(optimalPyramidLevel); - match.setResolution(rasterInfo.getResolution(rasterN, optimalPyramidLevel)); - matchingRasters.add(match); - } - } - return matchingRasters; - } - - public static void fitRequestToRaster(final GeneralEnvelope requestedEnvelope, - final RasterDatasetInfo rasterInfo, final RasterQueryInfo query) { - - final int rasterIndex = query.getRasterIndex(); - final int pyramidLevel = query.getPyramidLevel(); - final Rectangle rasterGridRange = rasterInfo.getGridRange(rasterIndex, pyramidLevel); - final GeneralEnvelope rasterEnvelope = rasterInfo - .getGridEnvelope(rasterIndex, pyramidLevel); - - double delta = requestedEnvelope.getMinimum(0) - rasterEnvelope.getMinimum(0); - double resX = rasterInfo.getResolution(rasterIndex, pyramidLevel)[0]; - int xMinPixel = (int) Math.floor(delta / resX); - - delta = requestedEnvelope.getMaximum(0) - rasterEnvelope.getMinimum(0); - int xMaxPixel = (int) Math.ceil(delta / resX); - - delta = rasterEnvelope.getMaximum(1) - requestedEnvelope.getMaximum(1); - double resY = rasterInfo.getResolution(rasterIndex, pyramidLevel)[1]; - // Distance in pixels from the top of the whole pyramid image to the top - // of our AOI. - // If we're off the top, this number will be negative. - int yMinPixel = (int) Math.floor(delta / resY); - - delta = rasterEnvelope.getMaximum(1) - requestedEnvelope.getMinimum(1); - int yMaxPixel = (int) Math.ceil(delta / resY); - - xMinPixel = Math.max(xMinPixel, rasterGridRange.x); - yMinPixel = Math.max(yMinPixel, rasterGridRange.y); - xMaxPixel = Math.min(xMaxPixel, rasterGridRange.x + rasterGridRange.width); - yMaxPixel = Math.min(yMaxPixel, rasterGridRange.y + rasterGridRange.height); - - final int widthPixel = xMaxPixel - xMinPixel; - final int heightPixel = yMaxPixel - yMinPixel; - - final double xMinGeo = rasterEnvelope.getMinimum(0) + resX * xMinPixel; - final double yMinGeo = rasterEnvelope.getMaximum(1) - resY * (yMinPixel + heightPixel); - final double widthGeo = resX * widthPixel; - final double heightGeo = resY * heightPixel; - - final Rectangle resultGridRange; - final GeneralEnvelope resultEnvelope; - - resultEnvelope = new GeneralEnvelope(new double[] { xMinGeo, yMinGeo }, new double[] { - xMinGeo + widthGeo, yMinGeo + heightGeo }); - resultEnvelope.setCoordinateReferenceSystem(rasterEnvelope.getCoordinateReferenceSystem()); - - resultGridRange = new Rectangle(xMinPixel, yMinPixel, widthPixel, heightPixel); - - final Rectangle matchingTiles; - final Rectangle levelTileRange; - final Rectangle tiledImageGridRange; - { - final Dimension tileSize = rasterInfo.getTileDimension(rasterIndex); - final int numTilesWide = rasterInfo.getNumTilesWide(rasterIndex, pyramidLevel); - final int numTilesHigh = rasterInfo.getNumTilesHigh(rasterIndex, pyramidLevel); - final Point tileOffset = rasterInfo.getTileOffset(rasterIndex, pyramidLevel); - levelTileRange = new Rectangle(0, 0, numTilesWide, numTilesHigh); - matchingTiles = findMatchingTiles(tileSize, numTilesWide, numTilesHigh, resultGridRange); - - int tiledImageMinX = (matchingTiles.x * tileSize.width); - int tiledImageMinY = (matchingTiles.y * tileSize.height); - - int tiledWidth = (matchingTiles.width * tileSize.width); - int tiledHeight = (matchingTiles.height * tileSize.height); - - tiledImageGridRange = new Rectangle(tiledImageMinX, tiledImageMinY, tiledWidth, - tiledHeight); - } - - /* - * What is the grid range inside the whole level grid range that fits into the matching - * tiles - */ - Rectangle resultDimensionInsideTiledImage; - resultDimensionInsideTiledImage = getResultDimensionForTileRange(tiledImageGridRange, - resultGridRange); - - query.setResultEnvelope(resultEnvelope); - query.setResultDimensionInsideTiledImage(resultDimensionInsideTiledImage); - query.setTiledImageSize(tiledImageGridRange); - query.setLevelTileRange(levelTileRange); - query.setMatchingTiles(matchingTiles); - } - - /** - * Returns a color model based on {@code colorMap} that's guaranteed to have at least one - * transparent pixel whose index can be used as no-data value for colormapped rasters, even if - * the returned IndexColorModel needs to be of a higher sample depth (ie, 16 instead of 8 bit) - * to satisfy that. - * - * @param colorMap - * the raster's native color map the returned one will be based on - * @return the same {@code colorMap} if it has a transparent pixel, another, possibly of a - * higher depth one if not, containing all the colors from {@code colorMap} and a newly - * allocated cell for the transparent pixel if necessary - */ - public static IndexColorModel ensureNoDataPixelIsAvailable(final IndexColorModel colorMap) { - int transparentPixel = colorMap.getTransparentPixel(); - if (transparentPixel > -1) { - return colorMap; - } - - final int transferType = colorMap.getTransferType(); - final int mapSize = colorMap.getMapSize(); - final int maxSize = 65536;// true for either transfer type - - if (mapSize == maxSize) { - LOGGER.fine("There's no room for a new transparent pixel, " - + "returning the original colorMap as is"); - return colorMap; - } - - /* - * The original map size is lower than the maximum allowed by a UShort color map, so expand - * the colormap by one and make that new entry transparent - */ - final int newMapSize = mapSize + 1; - final int[] argb = new int[newMapSize]; - colorMap.getRGBs(argb); - - // set the last entry as transparent - argb[newMapSize - 1] = ColorUtilities.getIntFromColor(0, 0, 0, 0); - - IndexColorModel targetColorModel; - final int significantBits; - final int newTransferType; - - { - if (DataBuffer.TYPE_BYTE == transferType && newMapSize <= 256) { - /* - * REVISIT: check if this needs to be promoted depending on whether I decide to - * treat 1 and 4 bit images as indexed with 1 and 4 significant bits respectively - */ - significantBits = colorMap.getPixelSize(); - newTransferType = DataBuffer.TYPE_BYTE; - } else { - // it's either being promoted or was already 16-bit - significantBits = 16; - newTransferType = DataBuffer.TYPE_USHORT; - } - } - - final int transparentPixelIndex = newMapSize - 1; - final boolean hasalpha = true; - final int startIndex = 0; - - targetColorModel = new IndexColorModel(significantBits, newMapSize, argb, startIndex, - hasalpha, transparentPixelIndex, newTransferType); - - return targetColorModel; - } - - /** - * For a color-mapped raster, the no-data value is set to the - * {@link IndexColorModel#getTransparentPixel() transparent pixel} - * - * @param colorMap - * @return the index in the colorMap that's the transparent pixel as is to be used as no-data - * value - */ - public static Number determineNoDataValue(IndexColorModel colorMap) { - int noDataPixel = colorMap.getTransparentPixel(); - if (-1 == noDataPixel) { - // there were no room for a transparent pixel, find out the closest match - noDataPixel = ColorUtilities.getTransparentPixel(colorMap); - } - return Integer.valueOf(noDataPixel); - } - - /** - * @param numBands - * number of bands in the raster dataset for the band whose nodata value is to be - * determined. Might be useful to treat special cases where some assumptions are made - * depending on the cell type and number of bands - * @param statsMin - * the minimum sample value for the band as reported by the band's statistics, or - * {@code NaN} - * @param statsMax - * the maximum sample value for the band as reported by the band's statistics, or - * {@code NaN} - * @param nativeCellType - * the band's native cell type - * @return - */ - public static Number determineNoDataValue(final int numBands, final double statsMin, - final double statsMax, final RasterCellType nativeCellType) { - - final Number nodata; - - if (nativeCellType == TYPE_32BIT_REAL) { - LOGGER.fine("no data value is Float.NaN"); - return Float.valueOf(Float.NaN); - } else if (nativeCellType == TYPE_64BIT_REAL) { - LOGGER.fine("no data value is Double.NaN"); - return Double.valueOf(Double.NaN); - } else if (nativeCellType == TYPE_1BIT) { - LOGGER.fine("1BIT images no-data value is set to 2," - + " regardless of the raster statistics"); - return Double.valueOf(2); - } else if (nativeCellType == TYPE_4BIT) { - LOGGER.fine("4BIT images no-data value is set to 16," - + " regardless of the raster statistics"); - return Double.valueOf(16); - } else if (!isGeoPhysics(numBands, nativeCellType)) { - LOGGER.fine("3 or 4 band, 8 bit unsigned image, assumed to be " - + "RGB or RGBA respectively and nodata value hardcoded to 255"); - return (Number) nativeCellType.getSampleValueRange().getMaxValue(); - } - - final NumberRange<?> sampleValueRange = nativeCellType.getSampleValueRange(); - - final double minimumSample = sampleValueRange.getMinimum(true); - final double maximumSample = sampleValueRange.getMaximum(true); - - double lower; - double greater; - if (Double.isNaN(statsMin) || Double.isNaN(statsMax)) { - lower = Math.ceil(minimumSample - 1); - greater = Math.floor(maximumSample + 1); - } else { - lower = Math.ceil(statsMin - 1); - greater = Math.floor(statsMax + 1); - } - - final boolean isUnsigned = minimumSample == 0; - - if (sampleValueRange.contains((Number) Double.valueOf(lower))) { - // lower is ok - nodata = lower; - } else if (sampleValueRange.contains((Number) Double.valueOf(greater))) { - // upper is ok - nodata = greater; - } else if (isUnsigned) { - // need to set no-data to the higher value, floor is zero - nodata = greater; - // if (cellType == TYPE_1BIT || cellType == TYPE_4BIT) { - // nodata = greater; - // } else { - // // best guess without promoting. We don't actually want to promote a raster that is - // // non - // // colormapped and either has no statistics or it's range is full to preserve the - // // cases - // // were it may affect badly the visualization (for example, a 3 band 8bit raster - // // promoted to 3 band 16bit is gonna look almost black - // nodata = maximumSample; - // } - } else { - // no-data as the lower value is ok, floor is non zero (the celltype is signed) - nodata = lower; - } - - return nodata; - } - - public static boolean isGeoPhysics(final int numBands, final RasterCellType nativeCellType) { - boolean geophysics = true; - if (nativeCellType == TYPE_8BIT_U && (numBands == 3 || numBands == 4)) { - geophysics = false; - } - return geophysics; - } - - public static RasterCellType determineTargetCellType(final RasterCellType nativeCellType, - final List<Number> noDataValues) { - - if (TYPE_32BIT_REAL == nativeCellType || TYPE_64BIT_REAL == nativeCellType) { - // no data value is NaN, so no need to promote. For other types NaN is not available - for (Number nodata : noDataValues) { - if (!Double.isNaN(nodata.doubleValue())) { - throw new IllegalArgumentException("no data values for float and " - + "double cell types shall be NaN: " + nodata); - } - } - return nativeCellType; - } - - // find a cell type that's deep enough for all the bands in the given raster - double noDataMin = Double.POSITIVE_INFINITY, noDataMax = Double.NEGATIVE_INFINITY; - { - for (Number noData : noDataValues) { - noDataMin = Math.min(noDataMin, noData.doubleValue()); - noDataMax = Math.max(noDataMax, noData.doubleValue()); - } - } - final NumberRange<Double> sampleValueRange; - sampleValueRange = nativeCellType.getSampleValueRange().castTo(Double.class); - - final RasterCellType targetCellType; - - if (sampleValueRange.contains((Number) Double.valueOf(noDataMin)) - && sampleValueRange.contains((Number) Double.valueOf(noDataMax))) { - /* - * The native cell type can hold the no-data values for all bands in the raster - */ - targetCellType = nativeCellType; - } else { - targetCellType = promote(nativeCellType); - } - return targetCellType; - } - - private static RasterCellType promote(final RasterCellType nativeCellType) { - switch (nativeCellType) { - case TYPE_1BIT: - case TYPE_4BIT: - return TYPE_8BIT_U; - case TYPE_8BIT_U: - return TYPE_16BIT_U; - case TYPE_8BIT_S: - return TYPE_16BIT_S; - case TYPE_16BIT_U: - return TYPE_32BIT_U; - case TYPE_16BIT_S: - return TYPE_32BIT_S; - case TYPE_32BIT_S: - case TYPE_32BIT_REAL: - case TYPE_32BIT_U: - return TYPE_64BIT_REAL; - default: - throw new IllegalArgumentException( - "Can't promote a raster of type 64-bit-real, there's " - + "no higher pixel depth than that!"); - } - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReader.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReader.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,150 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.io.IOException; - -/** - * Offers an iterator like interface to read ArcSDE raster tiles into a {@code byte[]} - * - * @author Gabriel Roldan (OpenGeo) - * @since 2.5.4 - * @version $Id$ - * @source $URL: - * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org - * /geotools/arcsde/gce/TileReader.java $ - */ -interface TileReader { - - public class TileInfo { - private final long bandId; - - private final byte[] bitmaskData; - - private final int numPixelsRead; - - private final byte[] tileDta; - - private final int columnIndex; - - private final int rowIndex; - - public TileInfo(long bandId, int colIndex, int rowIndex, int numPixelsRead, - byte[] tileData, byte[] bitMaskData) { - this.bandId = bandId; - this.columnIndex = colIndex; - this.rowIndex = rowIndex; - this.numPixelsRead = numPixelsRead; - this.tileDta = tileData; - this.bitmaskData = bitMaskData; - } - - public Long getBandId() { - return bandId; - } - - public byte[] getBitmaskData() { - return bitmaskData; - } - - public int getNumPixelsRead() { - return numPixelsRead; - } - - public byte[] getTileData() { - return tileDta; - } - - public int getColumnIndex() { - return columnIndex; - } - - public int getRowIndex() { - return rowIndex; - } - } - - /** - * @return number of bits per sample - */ - public abstract int getBitsPerSample(); - - /** - * @return number of samples per tile - */ - public abstract int getPixelsPerTile(); - - /** - * @return numbre of bands being fetched - */ - public abstract int getNumberOfBands(); - - /** - * @return number of pixels per tile over the X axis - */ - public abstract int getTileWidth(); - - /** - * @return number of pixels per tile over the Y axis - */ - public abstract int getTileHeight(); - - /** - * @return number of tiles being fetched over the X axis - */ - public abstract int getTilesWide(); - - /** - * @return number of tiles being fetched over the Y axis - */ - public abstract int getTilesHigh(); - - /** - * @return number of bytes in the raw pixel content of a tile, not taking into account any - * trailing bitmask data. - */ - public abstract int getBytesPerTile(); - - /** - * @return whether there are more tiles to fetch - * @throws IOException - */ - public abstract boolean hasNext() throws IOException; - - /** - * Fetches a tile and fills {@code tileData} with its raw pixel data packaged as bytes according - * to the number of bits per sample - * - * @param tileData - * a possibly {@code null} array where to store the next tile data. If {@code null} a - * new byte[] of length {@link #getBytesPerTile()} will be allocated and filled up - * with the raw tile pixel data. - * @return the bitmask data, or an empty array if the tile is full - * @throws IOException - * @throws {@link IllegalArgumentException} if tileData is not null and its size is less than - * {@link #getBytesPerTile()} - */ - public abstract TileInfo next() throws IOException; - - /** - * Disposes any resource being held by this TileReader, making the TileReader unusable and the - * behaviour of {@link #hasNext()} and {@link #next} unpredictable - */ - public abstract void dispose(); - -} \ No newline at end of file Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReaderFactory.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReaderFactory.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReaderFactory.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,52 +0,0 @@ -package org.geotools.arcsde.gce; - -import java.awt.Rectangle; - -import org.geotools.arcsde.session.ISessionPool; - -public class TileReaderFactory { - - /** - * - * @param preparedQuery - * @param row - * @param nativeType - * @param targetType - * @param noDataValues - * @param numberOfBands - * @param requestedTiles - * @param tileSize - * @return - */ - public static TileReader getInstance(final ISessionPool sessionPool, - final RasterDatasetInfo rasterInfo, final long rasterId, final int pyramidLevel, - final Rectangle requestedTiles) { - - final TileReader tileReader; - - final RasterCellType nativeType = rasterInfo.getNativeCellType(); - final RasterCellType targetType = rasterInfo.getTargetCellType(rasterId); - - final BitmaskToNoDataConverter noData; - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, rasterId); - - if (targetType == nativeType) { - - TileReader nativeTileReader = new NativeTileReader(sessionPool, rasterInfo, rasterId, - pyramidLevel, requestedTiles, noData); - - tileReader = nativeTileReader; - - } else { - // need to promote native to target sample depth - TileReader nativeTileReader; - nativeTileReader = new NativeTileReader(sessionPool, rasterInfo, rasterId, - pyramidLevel, requestedTiles, BitmaskToNoDataConverter.NO_ACTION_CONVERTER); - - TileReader promotingTileReader = new PromotingTileReader(nativeTileReader, nativeType, - targetType, noData); - tileReader = promotingTileReader; - } - return tileReader; - } -} Deleted: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TiledRasterReader.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TiledRasterReader.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TiledRasterReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,93 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-2009, 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.arcsde.gce; - -import java.awt.Rectangle; -import java.awt.image.RenderedImage; -import java.io.IOException; - -/** - * An Iterator like interface to read ArcSDE rasters for a given ArcSDE raster dataset (whether it - * is a single raster or a raster catalog). - * <p> - * Sample usage: <code> - * <pre> - * RasterReaderFactory readerFactory = .... - * RasterDatasetInfo raserInfo = ... - * ArcSDERasterReader reader = readerFactory.create(rasterInfo); - * try{ - * Long nextRasterId; - * while((nextRasterId = reader.nextRaster()) != null){ - * if(amIInterestedInThisRaster(nextRasterId)){ - * int pyramidLevel = ... - * Rectangle tileRange = ... - * RenderedImage raster = reader.read(pyramidLevel, tileRange); - * } - * } - * }finally{ - * reader.dispose(); - * } - * </pre> - * </code> - * </p> - * <p> - * So one has to call {@code nextRaster()} to get the id of the raster immediately available to be - * read through {@link #read()}. This is so because there might be more than one raster on a raster - * dataset and the order they are fetched from the ArcSDE server is non deterministic, and once you - * opened a stream to a raster you can't open another one and then read the former. - * </p> - * - * @author Gabriel Roldan - * @version $Id$ - * @since 2.5.7 - */ -interface TiledRasterReader { - - /** - * Disposes any resource being held by this reader, whether it's a connection to the ArcSDE - * server, opened streams, etc. - */ - // void dispose(); - - /** - * Advances to the next available raster in the raster dataset this reader works upon and - * returns it's {@link SeRasterAttr#getRasterId() raster id}. - * - * @return the ID for the raster ready to be read from the queried raster column in the raster - * dataset, or {@code null} if there are no more rasters to be read. - * @throws IOException - * for any problem occurred retrieving the next {@link SeRasterAttr} in the request - */ - // Long nextRaster() throws IOException; - - /** - * Reads the image subset determined by the given pyramid level and tile range for the currently - * available raster attribute in the requested raster column for the given raster dataset. - * - * @param pyramidLevel - * the pyramid level to read - * @param tileRange - * the range of tiles to read at the given pyramid level. The boundaries of the tile - * range are inclusive and starts at {@code 0,0} for the upper left most tile. - * @return the rendered image determined by the requested pyramid level and tile range - * @throws IOException - * for any exception occurred while reading the image - */ - RenderedImage read(final long rasterId, final int pyramidLevel, final Rectangle tileRange) - throws IOException; - -} \ No newline at end of file Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDEGridCoverage2DReaderJAI.java (from rev 34312, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDEGridCoverage2DReaderJAI.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDEGridCoverage2DReaderJAI.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,812 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.gce; + +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.awt.image.renderable.ParameterBlock; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.imageio.ImageIO; +import javax.imageio.ImageTypeSpecifier; +import javax.media.jai.ImageLayout; +import javax.media.jai.InterpolationNearest; +import javax.media.jai.JAI; +import javax.media.jai.ParameterBlockJAI; +import javax.media.jai.operator.FormatDescriptor; +import javax.media.jai.operator.MosaicDescriptor; + +import org.geotools.arcsde.raster.info.RasterDatasetInfo; +import org.geotools.arcsde.raster.info.RasterQueryInfo; +import org.geotools.arcsde.raster.info.RasterUtils; +import org.geotools.arcsde.raster.io.RasterReaderFactory; +import org.geotools.arcsde.raster.io.TiledRasterReader; +import org.geotools.coverage.CoverageFactoryFinder; +import org.geotools.coverage.GridSampleDimension; +import org.geotools.coverage.TypeMap; +import org.geotools.coverage.grid.GeneralGridEnvelope; +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.data.DataSourceException; +import org.geotools.data.DefaultServiceInfo; +import org.geotools.data.ServiceInfo; +import org.geotools.factory.Hints; +import org.geotools.geometry.GeneralEnvelope; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.referencing.CRS; +import org.geotools.util.logging.Logging; +import org.opengis.coverage.ColorInterpretation; +import org.opengis.coverage.grid.Format; +import org.opengis.coverage.grid.GridCoverageReader; +import org.opengis.geometry.Envelope; +import org.opengis.parameter.GeneralParameterValue; +import org.opengis.parameter.ParameterNotFoundException; +import org.opengis.parameter.ParameterValue; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.TransformException; + +/** + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAI.java $ + */ +@SuppressWarnings( { "deprecation", "nls" }) +public final class ArcSDEGridCoverage2DReaderJAI extends AbstractGridCoverage2DReader { + + private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + /** + * @see LoggingHelper#log(RenderedImage, Long, String) + */ + private static final boolean DEBUG_TO_DISK = Boolean + .getBoolean("org.geotools.arcsde.gce.debug"); + + private final ArcSDERasterFormat parent; + + private final RasterDatasetInfo rasterInfo; + + private DefaultServiceInfo serviceInfo; + + private RasterReaderFactory rasterReaderFactory; + + public ArcSDEGridCoverage2DReaderJAI(final ArcSDERasterFormat parent, + final RasterReaderFactory rasterReaderFactory, final RasterDatasetInfo rasterInfo, + final Hints hints) throws IOException { + // check it's a supported format + { + final int bitsPerSample = rasterInfo.getBand(0, 0).getCellType().getBitsPerSample(); + if (rasterInfo.getNumBands() > 1 && (bitsPerSample == 1 || bitsPerSample == 4)) { + throw new IllegalArgumentException(bitsPerSample + + "-bit rasters with more than one band are not supported"); + } + } + this.parent = parent; + this.rasterReaderFactory = rasterReaderFactory; + this.rasterInfo = rasterInfo; + + super.hints = hints; + super.coverageFactory = CoverageFactoryFinder.getGridCoverageFactory(this.hints); + super.crs = rasterInfo.getCoverageCrs(); + super.originalEnvelope = rasterInfo.getOriginalEnvelope(); + + GeneralGridEnvelope gridRange = rasterInfo.getOriginalGridRange(); + super.originalGridRange = new GridEnvelope2D(gridRange.toRectangle()); + + super.coverageName = rasterInfo.getRasterTable(); + final int numLevels = rasterInfo.getNumPyramidLevels(0); + + // level 0 is not an overview, but the raster itself + super.numOverviews = numLevels - 1; + + // /// + // + // setting the higher resolution avalaible for this coverage + // + // /// + highestRes = super.getResolution(originalEnvelope, (Rectangle) originalGridRange, crs); + // // + // + // get information for the successive images + // + // // + // REVISIT may the different rasters in the raster dataset have different pyramid levels? I + // guess so + if (numOverviews > 0) { + overViewResolutions = new double[numOverviews][2]; + for (int pyramidLevel = 1; pyramidLevel <= numOverviews; pyramidLevel++) { + Rectangle levelGridRange = rasterInfo.getGridRange(0, pyramidLevel); + GeneralEnvelope levelEnvelope = rasterInfo.getGridEnvelope(0, pyramidLevel); + overViewResolutions[pyramidLevel - 1] = super.getResolution(levelEnvelope, + levelGridRange, crs); + } + } else { + overViewResolutions = null; + } + } + + /** + * @see GridCoverageReader#getFormat() + */ + public Format getFormat() { + return parent; + } + + @Override + public ServiceInfo getInfo() { + if (serviceInfo == null) { + serviceInfo = new DefaultServiceInfo(); + serviceInfo.setTitle(rasterInfo.getRasterTable()); + serviceInfo.setDescription(rasterInfo.toString()); + Set<String> keywords = new HashSet<String>(); + keywords.add("ArcSDE"); + serviceInfo.setKeywords(keywords); + } + return serviceInfo; + } + + /** + * @see GridCoverageReader#read(GeneralParameterValue[]) + */ + public GridCoverage2D read(GeneralParameterValue[] params) throws IOException { + + final GeneralEnvelope requestedEnvelope; + final Rectangle requestedDim; + final OverviewPolicy overviewPolicy; + { + final ReadParameters opParams = parseReadParams(getOriginalEnvelope(), params); + overviewPolicy = opParams.overviewPolicy; + requestedEnvelope = opParams.requestedEnvelope; + requestedDim = opParams.dim; + } + + /* + * For each raster in the raster dataset, obtain the tiles, pixel range, and resulting + * envelope + */ + final List<RasterQueryInfo> queries; + queries = findMatchingRasters(requestedEnvelope, requestedDim, overviewPolicy); + if (queries.isEmpty()) { + /* + * none of the rasters match the requested envelope. This may happen by the tiled nature + * of the raster dataset + */ + return createFakeCoverage(requestedEnvelope, requestedDim); + } + + final GeneralEnvelope resultEnvelope = getResultEnvelope(queries); + + final LoggingHelper log = new LoggingHelper(); + log.appendLoggingGeometries(LoggingHelper.REQ_ENV, requestedEnvelope); + log.appendLoggingGeometries(LoggingHelper.RES_ENV, resultEnvelope); + + /* + * Once we collected the matching rasters and their image subsets, find out where in the + * overall resulting mosaic they fit. If the rasters does not share the spatial resolution, + * the QueryInfo.resultDimension and QueryInfo.mosaicLocation width or height won't match + */ + final Rectangle mosaicGeometry; + mosaicGeometry = RasterUtils.setMosaicLocations(rasterInfo, resultEnvelope, queries); + + /* + * Gather the rendered images for each of the rasters that match the requested envelope + */ + final TiledRasterReader rasterReader = rasterReaderFactory.create(rasterInfo); + + try { + readAllTiledRasters(queries, rasterReader, log); + } finally { + // rasterReader.dispose(); + } + + log.log(LoggingHelper.REQ_ENV); + log.log(LoggingHelper.RES_ENV); + log.log(LoggingHelper.MOSAIC_ENV); + log.log(LoggingHelper.MOSAIC_EXPECTED); + + final RenderedImage coverageRaster = createMosaic(queries, mosaicGeometry, log); + assert mosaicGeometry.getWidth() == coverageRaster.getWidth(); + assert mosaicGeometry.getHeight() == coverageRaster.getHeight(); + + /* + * BUILDING COVERAGE + */ + GridSampleDimension[] bands = getSampleDimensions(coverageRaster); + + GridCoverage2D resultCoverage = coverageFactory.create(coverageName, coverageRaster, + resultEnvelope, bands, null, null); + + return resultCoverage; + } + + private GridSampleDimension[] getSampleDimensions(final RenderedImage coverageRaster) + throws IOException { + + GridSampleDimension[] bands = rasterInfo.getGridSampleDimensions(); + + // may the image have been promoted? build the correct band info then + final int imageBands = coverageRaster.getSampleModel().getNumBands(); + if (bands.length == 1 && imageBands > 1) { + LOGGER.fine(coverageName + " was promoted from 1 to " + + coverageRaster.getSampleModel().getNumBands() + + " bands, returning an appropriate set of GridSampleDimension"); + // stolen from super.createCoverage: + final ColorModel cm = coverageRaster.getColorModel(); + bands = new GridSampleDimension[imageBands]; + + // setting bands names. + for (int i = 0; i < imageBands; i++) { + final ColorInterpretation colorInterpretation; + colorInterpretation = TypeMap.getColorInterpretation(cm, i); + if (colorInterpretation == null) { + throw new IOException("Unrecognized sample dimension type"); + } + bands[i] = new GridSampleDimension(colorInterpretation.name()).geophysics(true); + } + } + + return bands; + } + + private void readAllTiledRasters(final List<RasterQueryInfo> queries, + final TiledRasterReader rasterReader, final LoggingHelper log) throws IOException { + + for (RasterQueryInfo queryInfo : queries) { + + final Long rasterId = queryInfo.getRasterId(); + + final RenderedImage rasterImage; + + try { + final int pyramidLevel = queryInfo.getPyramidLevel(); + final Rectangle matchingTiles = queryInfo.getMatchingTiles(); + // final Point imageLocation = queryInfo.getTiledImageSize().getLocation(); + rasterImage = rasterReader.read(rasterId, pyramidLevel, matchingTiles); + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Fetching data for " + queryInfo.toString(), e); + throw e; + } + + queryInfo.setResultImage(rasterImage); + + { + LOGGER.finer(queryInfo.toString()); + log.appendLoggingGeometries(LoggingHelper.MOSAIC_EXPECTED, queryInfo + .getMosaicLocation()); + log + .appendLoggingGeometries(LoggingHelper.MOSAIC_ENV, queryInfo + .getResultEnvelope()); + + final Rectangle tiledImageSize = queryInfo.getTiledImageSize(); + int width = rasterImage.getWidth(); + int height = rasterImage.getHeight(); + if (tiledImageSize.width != width || tiledImageSize.height != height) { + throw new IllegalStateException( + "Read image is not of the expected size. Image=" + width + "x" + height + + ", expected: " + tiledImageSize.width + "x" + + tiledImageSize.height); + } + // if (tiledImageSize.x != rasterImage.getMinX() + // || tiledImageSize.y != rasterImage.getMinY()) { + // throw new IllegalStateException("Read image is not at the expected location " + // + tiledImageSize.x + "," + tiledImageSize.y + ": " + // + rasterImage.getMinX() + "," + rasterImage.getMinY()); + // } + } + } + } + + /** + * Called when the requested envelope do overlap the coverage envelope but none of the rasters + * in the dataset do + * + * @param requestedEnvelope + * @param requestedDim + * @return + */ + private GridCoverage2D createFakeCoverage(GeneralEnvelope requestedEnvelope, + Rectangle requestedDim) { + + ImageTypeSpecifier its = rasterInfo.getRenderedImageSpec(0); + SampleModel sampleModel = its.getSampleModel(requestedDim.width, requestedDim.height); + ColorModel colorModel = its.getColorModel(); + + WritableRaster raster = Raster.createWritableRaster(sampleModel, null); + BufferedImage image = new BufferedImage(colorModel, raster, false, null); + return coverageFactory.create(coverageName, image, requestedEnvelope); + } + + private List<RasterQueryInfo> findMatchingRasters(final GeneralEnvelope requestedEnvelope, + final Rectangle requestedDim, final OverviewPolicy overviewPolicy) { + + final List<RasterQueryInfo> matchingQueries; + matchingQueries = RasterUtils.findMatchingRasters(rasterInfo, requestedEnvelope, + requestedDim, overviewPolicy); + + if (matchingQueries.isEmpty()) { + return matchingQueries; + } + + for (RasterQueryInfo match : matchingQueries) { + RasterUtils.fitRequestToRaster(requestedEnvelope, rasterInfo, match); + } + return matchingQueries; + } + + private GeneralEnvelope getResultEnvelope(final List<RasterQueryInfo> queryInfos) { + + GeneralEnvelope finalEnvelope = null; + + for (RasterQueryInfo rasterQueryInfo : queryInfos) { + // gather resulting envelope + if (finalEnvelope == null) { + finalEnvelope = new GeneralEnvelope(rasterQueryInfo.getResultEnvelope()); + } else { + finalEnvelope.add(rasterQueryInfo.getResultEnvelope()); + } + } + if (finalEnvelope == null) { + throw new IllegalStateException("Restult envelope is null, this shouldn't happen!! " + + "we checked the request overlaps the coverage envelope before!"); + } + return finalEnvelope; + } + + /** + * For each raster: crop->scale->translate->add to mosaic + * + * @param queries + * @param mosaicGeometry + * @return + * @throws IOException + */ + private RenderedImage createMosaic(final List<RasterQueryInfo> queries, + final Rectangle mosaicGeometry, final LoggingHelper log) throws IOException { + + List<RenderedImage> transformed = new ArrayList<RenderedImage>(queries.size()); + + /* + * Do we need to expand to RGB color space and then create a new colormapped image with the + * whole mosaic? + */ + boolean expandThenContractCM = queries.size() > 1 && rasterInfo.isColorMapped(); + if (expandThenContractCM) { + LOGGER.info("Creating mosaic out of " + queries.size() + + " colormapped rasters. The mosaic tiles will be expanded to " + + "\nRGB space and the resulting mosaic reduced to a new IndexColorModel"); + } + + for (RasterQueryInfo query : queries) { + RenderedImage image = query.getResultImage(); + log.log(image, query.getRasterId(), "01_original"); + + image = cropToRequiredDimension(image, query.getTiledImageSize(), query + .getResultDimensionInsideTiledImage()); + log.log(image, query.getRasterId(), "02_crop"); + + final Rectangle mosaicLocation = query.getMosaicLocation(); + // scale + Float scaleX = Float.valueOf((float) (mosaicLocation.getWidth() / image.getWidth())); + Float scaleY = Float.valueOf((float) (mosaicLocation.getHeight() / image.getHeight())); + Float translateX = Float.valueOf(0); + Float translateY = Float.valueOf(0); + // image.getData(); + if (!(Float.valueOf(1.0F).equals(scaleX) && Float.valueOf(1.0F).equals(scaleY))) { + ParameterBlock pb = new ParameterBlock(); + pb.addSource(image); + pb.add(scaleX); + pb.add(scaleY); + pb.add(translateX); + pb.add(translateY); + pb.add(new InterpolationNearest()); + + if (queries.size() > 0) { + try { + LOGGER.info("Forcing loading data for mosaic as per GEOT-"); + // image.getData(); + } catch (RuntimeException e) { + throw new DataSourceException("Error fetching arcsde raster", e); + } + } + + image = JAI.create("scale", pb); + log.log(image, query.getRasterId(), "03_scale"); + + int width = image.getWidth(); + int height = image.getHeight(); + + assert mosaicLocation.width == width; + assert mosaicLocation.height == height; + } + if (image.getMinX() != mosaicLocation.x || image.getMinY() != mosaicLocation.y) { + // translate + ParameterBlock pb = new ParameterBlock(); + pb.addSource(image); + pb.add(Float.valueOf(mosaicLocation.x - image.getMinX())); + pb.add(Float.valueOf(mosaicLocation.y - image.getMinY())); + pb.add(null); + + image = JAI.create("translate", pb); + log.log(image, query.getRasterId(), "04_translate"); + + assert image.getMinX() == mosaicLocation.x : image.getMinX() + " != " + + mosaicLocation.x; + assert image.getMinY() == mosaicLocation.y : image.getMinY() + " != " + + mosaicLocation.y; + assert image.getWidth() == mosaicLocation.width : image.getWidth() + " != " + + mosaicLocation.width; + assert image.getHeight() == mosaicLocation.height : image.getHeight() + " != " + + mosaicLocation.height; + } + if (expandThenContractCM) { + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("Creating color expanded version of tile for raster #" + + query.getRasterId()); + } + + /* + * reformat the image as a 4 band rgba backed by byte data + */ + image = FormatDescriptor.create(image, Integer.valueOf(DataBuffer.TYPE_BYTE), null); + + log.log(image, query.getRasterId(), "04_1_colorExpanded"); + } + + transformed.add(image); + } + + final RenderedImage mosaic; + if (queries.size() == 1) { + /* + * This is besides a very slight perf improvement needed because the JAI mosaic + * operation truncates floating point raster values to 0 and 1. REVISIT: If there's no + * workaround for that we should prevent raster catalogs made of floating point rasters + * and throw an exception as we could not really support that. + */ + mosaic = transformed.get(0); + } else { + /* + * adapted from RasterLayerResponse.java in the imagemosaic module + */ + ParameterBlockJAI mosaicParams = new ParameterBlockJAI("Mosaic"); + + // TODO: set background values to raster's no-data + // mosaicParams.setParameter("backgroundValues",backgroundValues); + + mosaicParams.setParameter("mosaicType", MosaicDescriptor.MOSAIC_TYPE_OVERLAY); + + final ImageLayout layout = new ImageLayout(mosaicGeometry.x, mosaicGeometry.y, + mosaicGeometry.width, mosaicGeometry.height); + layout.setTileWidth(mosaicGeometry.width); + layout.setTileHeight(mosaicGeometry.height); + final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout); + // hints.put(JAI.KEY_SERIALIZE_DEEP_COPY, Boolean.TRUE); + + for (RenderedImage img : transformed) { + mosaicParams.addSource(img); + log.appendLoggingGeometries(LoggingHelper.MOSAIC_RESULT, img); + } + log.log(LoggingHelper.MOSAIC_RESULT); + + LOGGER.fine("Creating mosaic out of " + queries.size() + " raster tiles"); + mosaic = JAI.create("Mosaic", mosaicParams, hints); + + log.log(mosaic, 0L, "05_mosaic_result"); + } + return mosaic; + } + + /** + * Crops the image representing a full tile set to the required dimension and returns it, but + * keeps minx and miny being zero. + * + * @param fullTilesRaster + * @param tiledImageGridRagne + * @param cropTo + * @return + */ + private RenderedImage cropToRequiredDimension(final RenderedImage fullTilesRaster, + final Rectangle tiledImageGridRagne, final Rectangle cropTo) { + + // int minX = fullTilesRaster.getMinX(); + // int minY = fullTilesRaster.getMinY(); + // int width = fullTilesRaster.getWidth(); + // int height = fullTilesRaster.getHeight(); + + int minX = tiledImageGridRagne.x; + int minY = tiledImageGridRagne.y; + int width = tiledImageGridRagne.width; + int height = tiledImageGridRagne.height; + + Rectangle origDim = new Rectangle(minX, minY, width, height); + if (!origDim.contains(cropTo)) { + throw new IllegalArgumentException("Original image (" + origDim + + ") does not contain desired dimension (" + cropTo + ")"); + } else if (origDim.equals(cropTo)) { + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("No need to crop image, full tiled dimension and target one " + + "do match: original: " + width + "x" + height + ", target: " + + cropTo.width + "x" + cropTo.height); + } + return fullTilesRaster; + } + + ParameterBlock cropParams = new ParameterBlock(); + + cropParams.addSource(fullTilesRaster);// Source + cropParams.add(Float.valueOf(cropTo.x - tiledImageGridRagne.x)); // x origin for each band + cropParams.add(Float.valueOf(cropTo.y - tiledImageGridRagne.y)); // y origin for each band + cropParams.add(Float.valueOf(cropTo.width));// width for each band + cropParams.add(Float.valueOf(cropTo.height));// height for each band + + final RenderingHints hints = null; + RenderedImage image = JAI.create("Crop", cropParams, hints); + + assert cropTo.x - tiledImageGridRagne.x == image.getMinX(); + assert cropTo.y - tiledImageGridRagne.y == image.getMinY(); + assert cropTo.width == image.getWidth(); + assert cropTo.height == image.getHeight(); + + // assert cropTo.x == image.getMinX(); + // assert cropTo.y == image.getMinY(); + // assert cropTo.width == image.getWidth(); + // assert cropTo.height == image.getHeight(); + return image; + } + + static class ReadParameters { + GeneralEnvelope requestedEnvelope; + + Rectangle dim; + + OverviewPolicy overviewPolicy; + } + + private static ArcSDEGridCoverage2DReaderJAI.ReadParameters parseReadParams( + final GeneralEnvelope coverageEnvelope, final GeneralParameterValue[] params) + throws IllegalArgumentException { + if (params == null) { + throw new IllegalArgumentException("No GeneralParameterValue given to read operation"); + } + + GeneralEnvelope reqEnvelope = null; + Rectangle 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()); + + CoordinateReferenceSystem nativeCrs = coverageEnvelope + .getCoordinateReferenceSystem(); + CoordinateReferenceSystem requestCrs = reqEnvelope.getCoordinateReferenceSystem(); + if (!CRS.equalsIgnoreMetadata(nativeCrs, requestCrs)) { + LOGGER.info("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().getBounds(); + continue; + } + if (name.equals(AbstractGridFormat.OVERVIEW_POLICY.getName().toString())) { + overviewPolicy = (OverviewPolicy) param.getValue(); + continue; + } + } + + if (dim == null && reqEnvelope == null) { + throw new ParameterNotFoundException("Parameter is mandatory and shall provide " + + "the extent and dimension to request", AbstractGridFormat.READ_GRIDGEOMETRY2D + .getName().toString()); + } + + if (!reqEnvelope.intersects(coverageEnvelope, true)) { + throw new IllegalArgumentException( + "The requested extend does not overlap the coverage extent: " + + coverageEnvelope); + } + + if (dim.width <= 0 || dim.height <= 0) { + throw new IllegalArgumentException("The requested coverage dimension can't be null: " + + dim); + } + + if (overviewPolicy == null) { + LOGGER.finer("No overview policy requested, defaulting to QUALITY"); + overviewPolicy = OverviewPolicy.QUALITY; + } + LOGGER.fine("Overview policy is " + overviewPolicy); + + LOGGER.info("Reading raster for " + dim.getWidth() + "x" + dim.getHeight() + + " requested dim and " + reqEnvelope.getMinimum(0) + "," + + reqEnvelope.getMaximum(0) + " - " + reqEnvelope.getMinimum(1) + + reqEnvelope.getMaximum(1) + " requested extent"); + + ArcSDEGridCoverage2DReaderJAI.ReadParameters parsedParams = new ArcSDEGridCoverage2DReaderJAI.ReadParameters(); + parsedParams.requestedEnvelope = reqEnvelope; + parsedParams.dim = dim; + parsedParams.overviewPolicy = overviewPolicy; + return parsedParams; + } + + private static ReferencedEnvelope toNativeCrs(final GeneralEnvelope requestedEnvelope, + final CoordinateReferenceSystem nativeCRS) throws IllegalArgumentException { + + ReferencedEnvelope reqEnv = toReferencedEnvelope(requestedEnvelope); + + if (!CRS.equalsIgnoreMetadata(nativeCRS, reqEnv.getCoordinateReferenceSystem())) { + // we're being reprojected. We'll need to reproject reqEnv into + // our native coordsys + try { + // ReferencedEnvelope origReqEnv = reqEnv; + reqEnv = reqEnv.transform(nativeCRS, true); + } catch (FactoryException fe) { + // unable to reproject? + throw new IllegalArgumentException("Unable to find a reprojection from requested " + + "coordsys to native coordsys for this request", fe); + } catch (TransformException te) { + throw new IllegalArgumentException("Unable to perform reprojection from requested " + + "coordsys to native coordsys for this request", te); + } + } + return reqEnv; + } + + private static ReferencedEnvelope toReferencedEnvelope(GeneralEnvelope envelope) { + double minx = envelope.getMinimum(0); + double maxx = envelope.getMaximum(0); + double miny = envelope.getMinimum(1); + double maxy = envelope.getMaximum(1); + CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem(); + + ReferencedEnvelope refEnv = new ReferencedEnvelope(minx, maxx, miny, maxy, crs); + return refEnv; + } + + /** + * A simple helper class to guard and easy logging the mosaic geometries in both geographical + * and pixel ranges + */ + private static class LoggingHelper { + + private static final File debugDir = new File(System.getProperty("user.home") + + File.separator + "arcsde_test"); + + static { + if (DEBUG_TO_DISK) { + debugDir.mkdir(); + } + } + + public Level GEOM_LEVEL = Level.FINER; + + public static String REQ_ENV = "Requested envelope"; + + public static String RES_ENV = "Resulting envelope"; + + public static String MOSAIC_ENV = "Resulting mosaiced envelopes"; + + public static String MOSAIC_EXPECTED = "Expected mosaic layout (in pixels)"; + + public static String MOSAIC_RESULT = "Resulting image mosaic layout (in pixels)"; + + private Map<String, StringBuilder> geoms = null; + + LoggingHelper() { + // not much to to + } + + private StringBuilder getGeom(String geomName) { + if (geoms == null) { + geoms = new HashMap<String, StringBuilder>(); + } + StringBuilder sb = geoms.get(geomName); + if (sb == null) { + sb = new StringBuilder("MULTIPOLYGON(\n"); + geoms.put(geomName, sb); + } + return sb; + } + + public void appendLoggingGeometries(String geomName, RenderedImage img) { + if (LOGGER.isLoggable(GEOM_LEVEL)) { + appendLoggingGeometries(geomName, new Rectangle(img.getMinX(), img.getMinY(), img + .getWidth(), img.getHeight())); + } + } + + public void appendLoggingGeometries(String geomName, Rectangle env) { + if (LOGGER.isLoggable(GEOM_LEVEL)) { + appendLoggingGeometries(geomName, new GeneralEnvelope(env)); + } + } + + public void appendLoggingGeometries(String geomName, GeneralEnvelope env) { + if (LOGGER.isLoggable(GEOM_LEVEL)) { + StringBuilder sb = getGeom(geomName); + sb.append(" ((" + env.getMinimum(0) + " " + env.getMinimum(1) + ", " + + env.getMaximum(0) + " " + env.getMinimum(1) + ", " + env.getMaximum(0) + + " " + env.getMaximum(1) + ", " + env.getMinimum(0) + " " + + env.getMaximum(1) + ", " + env.getMinimum(0) + " " + env.getMinimum(1) + + ")),"); + } + } + + public void log(String geomName) { + if (LOGGER.isLoggable(GEOM_LEVEL)) { + StringBuilder sb = getGeom(geomName); + sb.setLength(sb.length() - 1); + sb.append("\n)"); + LOGGER.log(GEOM_LEVEL, geomName + ":\n" + sb.toString()); + } + } + + public void log(RenderedImage image, Long rasterId, String fileName) { + if (DEBUG_TO_DISK) { + LOGGER.warning("BEWARE THE DEBUG FLAG IS TURNED ON! " + + "IF IN PRODUCTION THIS IS A SEVERE MISTAKE!!!"); + // ImageIO.write(FormatDescriptor.create(image, + // Integer.valueOf(DataBuffer.TYPE_BYTE), + // null), "TIFF", new File(debugDir, rasterId.longValue() + fileName + ".tiff")); + + try { + ImageIO.write(image, "TIFF", new File(debugDir, rasterId.longValue() + fileName + + ".tiff")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDERasterFormat.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDERasterFormat.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDERasterFormat.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/gce/ArcSDERasterFormat.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,440 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.gce; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.geotools.arcsde.jndi.SharedSessionPool; +import org.geotools.arcsde.raster.info.GatherCoverageMetadataCommand; +import org.geotools.arcsde.raster.info.RasterDatasetInfo; +import org.geotools.arcsde.raster.io.RasterReaderFactory; +import org.geotools.arcsde.session.ArcSDEConnectionConfig; +import org.geotools.arcsde.session.ISession; +import org.geotools.arcsde.session.ISessionPool; +import org.geotools.arcsde.session.SessionPoolFactory; +import org.geotools.arcsde.session.UnavailableConnectionException; +import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; +import org.geotools.coverage.grid.io.AbstractGridFormat; +import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams; +import org.geotools.data.DataSourceException; +import org.geotools.factory.GeoTools; +import org.geotools.factory.Hints; +import org.geotools.parameter.DefaultParameterDescriptorGroup; +import org.geotools.parameter.ParameterGroup; +import org.geotools.util.logging.Logging; +import org.opengis.coverage.grid.Format; +import org.opengis.coverage.grid.GridCoverageWriter; +import org.opengis.parameter.GeneralParameterDescriptor; + +/** + * An implementation of the ArcSDE Raster Format. Based on the ArcGrid module. + * + * @author Saul Farber (saul.farber) + * @author jeichar + * @author Simone Giannecchini (simboss) + * @author Gabriel Roldan (OpenGeo) + * @source $URL: + * http://svn.geotools.org/geotools/trunk/gt/modules/plugin/arcsde/datastore/src/main/java + * /org/geotools/arcsde/gce/ArcSDERasterFormat.java $ + */ +@SuppressWarnings( { "nls", "deprecation" }) +public final class ArcSDERasterFormat extends AbstractGridFormat implements Format { + + protected static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + /** + * Cache of raster metadata objects, where the keys are the URL's representing the full + * connection properties to a given ArcSDE raster, and the value the + * {@link ArcSDERasterGridCoverage2DReader}'s externalized state, so it is not needed to gather + * the raster properties each time. + */ + private final Map<String, RasterDatasetInfo> rasterInfos = new WeakHashMap<String, RasterDatasetInfo>(); + + private final Map<String, ArcSDEConnectionConfig> connectionConfigs = new WeakHashMap<String, ArcSDEConnectionConfig>(); + + private static final ArcSDERasterFormat instance = new ArcSDERasterFormat(); + + private boolean statisticsMandatory = true; + + /** + * Creates an instance and sets the metadata. + */ + private ArcSDERasterFormat() { + setInfo(); + } + + public static ArcSDERasterFormat getInstance() { + return instance; + } + + /** + * Sets the metadata information. + */ + private void setInfo() { + Map<String, String> info = new HashMap<String, String>(); + + info.put("name", "ArcSDE Raster"); + info.put("description", "ArcSDE Raster Format"); + info.put("vendor", "Geotools"); + info.put("docURL", ""); + info.put("version", GeoTools.getVersion().toString()); + mInfo = info; + + readParameters = new ParameterGroup(new DefaultParameterDescriptorGroup(mInfo, + new GeneralParameterDescriptor[] { READ_GRIDGEOMETRY2D, OVERVIEW_POLICY })); + } + + /** + * @param source + * either a {@link String} or {@link File} instance representing the connection URL + * @see AbstractGridFormat#getReader(Object source) + */ + @Override + public AbstractGridCoverage2DReader getReader(Object source) { + return getReader(source, null); + } + + /** + * @param source + * either a {@link String} or {@link File} instance representing the connection URL + * @see AbstractGridFormat#getReader(Object, Hints) + */ + @Override + public AbstractGridCoverage2DReader getReader(final Object source, final Hints hints) { + try { + if (source == null) { + throw new DataSourceException("No source set to read this coverage."); + } + + // this will be our connection string + final String coverageUrl = parseCoverageUrl(source); + + final ArcSDEConnectionConfig connectionConfig = getConnectionConfig(coverageUrl); + + final ISessionPool sessionPool = setupConnectionPool(connectionConfig); + + final RasterDatasetInfo rasterInfo = getRasterInfo(coverageUrl, sessionPool); + + final RasterReaderFactory rasterReaderFactory = new RasterReaderFactory(sessionPool); + + return new ArcSDEGridCoverage2DReaderJAI(this, rasterReaderFactory, rasterInfo, hints); + } catch (IOException dse) { + LOGGER + .log(Level.SEVERE, "Unable to creata ArcSDERasterReader for " + source + ".", + dse); + throw new RuntimeException(dse); + } + } + + private RasterDatasetInfo getRasterInfo(final String coverageUrl, ISessionPool connectionPool) + throws IOException { + + RasterDatasetInfo rasterInfo = rasterInfos.get(coverageUrl); + if (rasterInfo == null) { + synchronized (rasterInfos) { + rasterInfo = rasterInfos.get(coverageUrl); + if (rasterInfo == null) { + ISession scon; + try { + scon = connectionPool.getSession(false); + } catch (UnavailableConnectionException e) { + throw new RuntimeException(e); + } + try { + final String rasterTable; + { + String sdeUrl = coverageUrl; + if (sdeUrl.indexOf(";") != -1) { + /* + * We're not using any extra param anymore. Yet, be cautious cause a + * client may still be using urls with some old extra param, so just + * strip it + */ + sdeUrl = sdeUrl.substring(0, sdeUrl.indexOf(";")); + } + rasterTable = sdeUrl.substring(sdeUrl.indexOf("#") + 1); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Building ArcSDEGridCoverageReader2D for " + + rasterTable); + } + } + + GatherCoverageMetadataCommand command = new GatherCoverageMetadataCommand( + rasterTable, statisticsMandatory); + rasterInfo = scon.issue(command); + rasterInfos.put(coverageUrl, rasterInfo); + } finally { + scon.dispose(); + } + } + } + } + return rasterInfo; + } + + private ArcSDEConnectionConfig getConnectionConfig(final String coverageUrl) { + ArcSDEConnectionConfig sdeConfig; + sdeConfig = connectionConfigs.get(coverageUrl); + if (sdeConfig == null) { + synchronized (connectionConfigs) { + sdeConfig = connectionConfigs.get(coverageUrl); + if (sdeConfig == null) { + sdeConfig = sdeURLToConnectionConfig(new StringBuffer(coverageUrl)); + connectionConfigs.put(coverageUrl, sdeConfig); + } + } + } + return sdeConfig; + } + + /** + * @see AbstractGridFormat#getWriter(Object) + */ + @Override + public GridCoverageWriter getWriter(Object destination) { + // return new ArcGridWriter(destination); + return null; + } + + /** + * @param source + * either a {@link String} or {@link File} instance representing the connection URL + * @see AbstractGridFormat#accepts(Object input) + */ + @Override + public boolean accepts(Object input) { + StringBuffer url; + if (input instanceof File) { + url = new StringBuffer(((File) input).getPath()); + } else if (input instanceof String) { + url = new StringBuffer((String) input); + } else { + return false; + } + try { + sdeURLToConnectionConfig(url); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * @see Format#getName() + */ + @Override + public String getName() { + return this.mInfo.get("name"); + } + + /** + * @see Format#getDescription() + */ + @Override + public String getDescription() { + return this.mInfo.get("description"); + } + + /** + * @see Format#getVendor() + */ + @Override + public String getVendor() { + return this.mInfo.get("vendor"); + } + + /** + * @see Format#getDocURL() + */ + @Override + public String getDocURL() { + return this.mInfo.get("docURL"); + } + + /** + * @see Format#getVersion() + */ + @Override + public String getVersion() { + return this.mInfo.get("version"); + } + + /** + * Retrieves the default instance for the {@link ArcSDERasterFormat} of the + * {@link GeoToolsWriteParams} to control the writing process. + * + * @return a default instance for the {@link ArcSDERasterFormat} of the + * {@link GeoToolsWriteParams} to control the writing process. + * @see AbstractGridFormat#getDefaultImageIOWriteParameters() + */ + @Override + public GeoToolsWriteParams getDefaultImageIOWriteParameters() { + throw new UnsupportedOperationException("ArcSDE Rasters are read only for now."); + } + + // //////////////// + + /** + * @param input + * either a {@link String} or a {@link File} instance representing the connection URL + * to a given coverage + * @return the connection URL as a string + */ + private String parseCoverageUrl(Object input) { + String coverageUrl; + if (input instanceof String) { + coverageUrl = (String) input; + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("connecting to ArcSDE Raster: " + coverageUrl); + } + } else if (input instanceof File) { + coverageUrl = ((File) input).getPath(); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("connectiong via file-hack to ArcSDE Raster: " + coverageUrl); + } + } else { + throw new IllegalArgumentException("Unsupported input type: " + input.getClass()); + } + return coverageUrl; + } + + /** + * Checks the input provided to this {@link ArcSDERasterGridCoverage2DReader} and sets all the + * other objects and flags accordingly. + * + * @param sdeUrl + * a url representing the connection parameters to an arcsde server instance provied + * to this {@link ArcSDERasterGridCoverage2DReader}. + * @throws IOException + */ + private ISessionPool setupConnectionPool(ArcSDEConnectionConfig sdeConfig) throws IOException { + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Getting ArcSDE connection pool for " + sdeConfig); + } + + ISessionPool sessionPool; + sessionPool = SharedSessionPool.getInstance(sdeConfig, SessionPoolFactory.getInstance()); + + return sessionPool; + } + + /** + * @param sdeUrl + * - A StringBuffer containing a string of form + * 'sde://user:pass@sdehost:[port]/[dbname] + * @return a ConnectionConfig object representing these parameters + */ + public static ArcSDEConnectionConfig sdeURLToConnectionConfig(StringBuffer sdeUrl) { + // annoyingly, geoserver currently stores the user-entered SDE string as + // a File, and passes us the + // File object. The File object strips the 'sde://user...' into a + // 'sde:/user..'. So we need to check + // for both forms of the url. + String sdeHost, sdeUser, sdePass, sdeDBName; + int sdePort; + if (sdeUrl.indexOf("sde:/") == -1) { + throw new IllegalArgumentException( + "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName -- Got " + + sdeUrl); + } + if (sdeUrl.indexOf("sde://") == -1) { + sdeUrl.delete(0, 5); + } else { + sdeUrl.delete(0, 6); + } + + int idx = sdeUrl.indexOf(":"); + if (idx == -1) { + throw new IllegalArgumentException( + "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); + } + sdeUser = sdeUrl.substring(0, idx); + sdeUrl.delete(0, idx); + + idx = sdeUrl.indexOf("@"); + if (idx == -1) { + throw new IllegalArgumentException( + "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); + } + sdePass = sdeUrl.substring(1, idx); + sdeUrl.delete(0, idx); + + idx = sdeUrl.indexOf(":"); + if (idx == -1) { + // there's no "port" specification. Assume 5151; + sdePort = 5151; + + idx = sdeUrl.indexOf("/"); + if (idx == -1) { + throw new IllegalArgumentException( + "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); + } + sdeHost = sdeUrl.substring(1, idx).toString(); + sdeUrl.delete(0, idx); + } else { + sdeHost = sdeUrl.substring(1, idx).toString(); + sdeUrl.delete(0, idx); + + idx = sdeUrl.indexOf("/"); + if (idx == -1) { + throw new IllegalArgumentException( + "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); + } + sdePort = Integer.parseInt(sdeUrl.substring(1, idx).toString()); + sdeUrl.delete(0, idx); + } + + idx = sdeUrl.indexOf("#"); + if (idx == -1) { + throw new IllegalArgumentException( + "ArcSDE Raster URL must be of the form sde://user:pass@sdehost:port/[dbname]#rasterTableName"); + } + sdeDBName = sdeUrl.substring(1, idx).toString(); + sdeUrl.delete(0, idx); + + Map<String, String> params = new HashMap<String, String>(); + params.put(ArcSDEConnectionConfig.SERVER_NAME_PARAM_NAME, sdeHost); + params.put(ArcSDEConnectionConfig.PORT_NUMBER_PARAM_NAME, String.valueOf(sdePort)); + params.put(ArcSDEConnectionConfig.INSTANCE_NAME_PARAM_NAME, sdeDBName); + params.put(ArcSDEConnectionConfig.USER_NAME_PARAM_NAME, sdeUser); + params.put(ArcSDEConnectionConfig.PASSWORD_PARAM_NAME, sdePass); + params.put(ArcSDEConnectionConfig.MIN_CONNECTIONS_PARAM_NAME, "1"); + params.put(ArcSDEConnectionConfig.MAX_CONNECTIONS_PARAM_NAME, "20"); + params.put(ArcSDEConnectionConfig.CONNECTION_TIMEOUT_PARAM_NAME, "-1");// do not wait + + ArcSDEConnectionConfig config = ArcSDEConnectionConfig.fromMap(params); + return config; + } + + /** + * Used by test code to indicate wether to fail when a raster lacks statistics, since we can't + * create statistics with the ArcSDE Java API + * + * @param statisticsMandatory + */ + void setStatisticsMandatory(final boolean statisticsMandatory) { + this.statisticsMandatory = statisticsMandatory; + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/CompressionType.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/CompressionType.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/CompressionType.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/CompressionType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,63 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.util.NoSuchElementException; + +import com.esri.sde.sdk.client.SeRaster; + +/** + * An enumeration that mirrors the different possible raster compression types in Arcsde (ie, + * {@code SeRaster#SE_COMPRESSION_*}) + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/raster/info/CompressionType.java $ + */ +public enum CompressionType { + COMPRESSION_JP2, COMPRESSION_JPEG, COMPRESSION_LZ77, COMPRESSION_NONE; + static { + COMPRESSION_JP2.setSdeTypeId(SeRaster.SE_COMPRESSION_JP2); + COMPRESSION_JPEG.setSdeTypeId(SeRaster.SE_COMPRESSION_JPEG); + COMPRESSION_LZ77.setSdeTypeId(SeRaster.SE_COMPRESSION_LZ77); + COMPRESSION_NONE.setSdeTypeId(SeRaster.SE_COMPRESSION_NONE); + } + + private int typeId; + + private void setSdeTypeId(int typeId) { + this.typeId = typeId; + } + + public int getSeCompressionType() { + return this.typeId; + } + + public static CompressionType valueOf(final int seCompressionType) { + for (CompressionType type : CompressionType.values()) { + if (type.getSeCompressionType() == seCompressionType) { + return type; + } + } + throw new NoSuchElementException("Compression type " + seCompressionType + + " does not exist"); + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/GatherCoverageMetadataCommand.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/GatherCoverageMetadataCommand.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/GatherCoverageMetadataCommand.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/GatherCoverageMetadataCommand.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,521 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.awt.geom.Point2D; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferUShort; +import java.awt.image.IndexColorModel; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.geotools.arcsde.ArcSdeException; +import org.geotools.arcsde.session.Command; +import org.geotools.arcsde.session.ISession; +import org.geotools.arcsde.util.ArcSDEUtils; +import org.geotools.data.DataSourceException; +import org.geotools.geometry.GeneralEnvelope; +import org.geotools.referencing.crs.DefaultEngineeringCRS; +import org.geotools.util.logging.Logging; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +import com.esri.sde.sdk.client.SDEPoint; +import com.esri.sde.sdk.client.SeColumnDefinition; +import com.esri.sde.sdk.client.SeConnection; +import com.esri.sde.sdk.client.SeCoordinateReference; +import com.esri.sde.sdk.client.SeException; +import com.esri.sde.sdk.client.SeExtent; +import com.esri.sde.sdk.client.SeQuery; +import com.esri.sde.sdk.client.SeRaster; +import com.esri.sde.sdk.client.SeRasterAttr; +import com.esri.sde.sdk.client.SeRasterBand; +import com.esri.sde.sdk.client.SeRasterColumn; +import com.esri.sde.sdk.client.SeRow; +import com.esri.sde.sdk.client.SeSqlConstruct; +import com.esri.sde.sdk.client.SeTable; + +/** + * Session command to gather information for an ArcSDE Raster, such as dimensions, spatial extent, + * number of pyramid levels, etc; into a {@link RasterDatasetInfo} + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.8 + * @source $UR$ + */ +public class GatherCoverageMetadataCommand extends Command<RasterDatasetInfo> { + + private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + private final boolean statisticsMandatory; + + private final String rasterTableName; + + public GatherCoverageMetadataCommand(final String rasterTableName, + final boolean statisticsMandatory) { + this.rasterTableName = rasterTableName; + this.statisticsMandatory = statisticsMandatory; + } + + /** + * @throws IOException + * if an exception occurs accessing the raster metadata + * @throws SeException + * @throws IllegalArgumentException + * if the raster has no CRS, contains no raster attributes, has no pyramids, no + * bands or no statistics + */ + @Override + public RasterDatasetInfo execute(ISession session, SeConnection connection) throws SeException, + IOException { + LOGGER.fine("Gathering raster dataset metadata for " + rasterTableName); + final String[] rasterColumns = getRasterColumns(connection, rasterTableName); + final List<RasterInfo> rastersLayoutInfo = new ArrayList<RasterInfo>(); + { + final List<SeRasterAttr> rasterAttributes; + rasterAttributes = getSeRasterAttr(connection, rasterTableName, rasterColumns); + + if (rasterAttributes.size() == 0) { + throw new IllegalArgumentException("Table " + rasterTableName + + " contains no raster datasets"); + } + + final CoordinateReferenceSystem coverageCrs; + + /* + * by bandId map of colormaps. The dataset may be composed of more than one raster so we + * gather all the colormaps once and held them here by rasterband id + */ + final Map<Long, IndexColorModel> rastersColorMaps; + { + final SeRasterColumn rasterColumn; + final SeRasterBand sampleBand; + final long rasterColumnId; + final int bitsPerSample; + try { + SeRasterAttr ratt = rasterAttributes.get(0); + rasterColumn = new SeRasterColumn(connection, ratt.getRasterColumnId()); + rasterColumnId = rasterColumn.getID().longValue(); + sampleBand = ratt.getBands()[0]; + bitsPerSample = RasterCellType.valueOf(ratt.getPixelType()).getBitsPerSample(); + } catch (SeException e) { + throw new ArcSdeException(e); + } + final SeCoordinateReference seCoordRef = rasterColumn.getCoordRef(); + if (seCoordRef == null) { + throw new IllegalArgumentException(rasterTableName + + " has no coordinate reference system set"); + } + LOGGER.finer("Looking CRS for raster column " + rasterTableName); + coverageCrs = ArcSDEUtils.findCompatibleCRS(seCoordRef); + if (DefaultEngineeringCRS.CARTESIAN_2D == coverageCrs) { + LOGGER.warning("Raster " + rasterTableName + + " has not CRS set, using DefaultEngineeringCRS.CARTESIAN_2D"); + } + if (sampleBand.hasColorMap()) { + rastersColorMaps = loadColorMaps(rasterColumnId, bitsPerSample, connection); + } else { + rastersColorMaps = Collections.emptyMap(); + } + + } + try { + for (SeRasterAttr rAtt : rasterAttributes) { + LOGGER.fine("Gathering raster metadata for " + rasterTableName + " raster " + + rAtt.getRasterId().longValue()); + + if (rAtt.getMaxLevel() == 0) { + throw new IllegalArgumentException( + "Raster cotains no pyramid levels, we don't support non pyramid rasters"); + } + if (rAtt.getNumBands() == 0) { + throw new IllegalArgumentException("Raster " + + rAtt.getRasterId().longValue() + " in " + rasterTableName + + " contains no raster attribtues"); + } + if (this.statisticsMandatory && !rAtt.getBandInfo(1).hasStats()) { + throw new IllegalArgumentException(rasterTableName + + " has no statistics generated (or not all it's rasters have). " + + "Please use sderaster -o stats to create them before use"); + } + + RasterInfo rasterInfo = new RasterInfo(rAtt, coverageCrs); + rastersLayoutInfo.add(rasterInfo); + + final GeneralEnvelope originalEnvelope; + originalEnvelope = calculateOriginalEnvelope(rAtt, coverageCrs); + rasterInfo.setOriginalEnvelope(originalEnvelope); + final List<RasterBandInfo> bands; + bands = setUpBandInfo(connection, rAtt, rastersColorMaps); + rasterInfo.setBands(bands); + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("Gathered metadata for " + rasterTableName + "#" + + rAtt.getRasterId().longValue() + ":\n" + rasterInfo.toString()); + } + } + } catch (SeException e) { + throw new ArcSdeException("Gathering raster dataset information", e); + } + } + + RasterDatasetInfo rasterInfo = new RasterDatasetInfo(); + rasterInfo.setRasterTable(rasterTableName); + rasterInfo.setRasterColumns(rasterColumns); + rasterInfo.setPyramidInfo(rastersLayoutInfo); + + return rasterInfo; + } + + private String[] getRasterColumns(final SeConnection scon, final String rasterTable) + throws IOException, SeException { + + String[] rasterColumns; + SeTable sTable = new SeTable(scon, rasterTable); + SeColumnDefinition[] cols; + try { + cols = sTable.describe(); + } catch (SeException e) { + throw new ArcSdeException("Exception fetching the list of columns for table " + + rasterTable, e); + } + List<String> fetchColumns = new ArrayList<String>(cols.length / 2); + for (int i = 0; i < cols.length; i++) { + if (cols[i].getType() == SeColumnDefinition.TYPE_RASTER) + fetchColumns.add(cols[i].getName()); + } + if (fetchColumns.size() == 0) { + throw new DataSourceException("Couldn't find any TYPE_RASTER columns in ArcSDE table " + + rasterTable); + } + + rasterColumns = fetchColumns.toArray(new String[fetchColumns.size()]); + return rasterColumns; + } + + private GeneralEnvelope calculateOriginalEnvelope(final SeRasterAttr rasterAttributes, + CoordinateReferenceSystem coverageCrs) throws IOException { + SeExtent sdeExtent; + try { + sdeExtent = rasterAttributes.getExtent(); + } catch (SeException e) { + throw new ArcSdeException("Exception getting the raster extent", e); + } + GeneralEnvelope originalEnvelope = new GeneralEnvelope(coverageCrs); + originalEnvelope.setRange(0, sdeExtent.getMinX(), sdeExtent.getMaxX()); + originalEnvelope.setRange(1, sdeExtent.getMinY(), sdeExtent.getMaxY()); + return originalEnvelope; + } + + private List<SeRasterAttr> getSeRasterAttr(SeConnection scon, String rasterTable, + String[] rasterColumns) throws IOException { + + LOGGER.fine("Gathering raster attributes for " + rasterTable); + SeRasterAttr rasterAttributes; + LinkedList<SeRasterAttr> rasterAttList = new LinkedList<SeRasterAttr>(); + SeQuery query = null; + try { + query = new SeQuery(scon, rasterColumns, new SeSqlConstruct(rasterTable)); + query.prepareQuery(); + query.execute(); + + SeRow row = query.fetch(); + while (row != null) { + rasterAttributes = row.getRaster(0); + rasterAttList.addFirst(rasterAttributes); + row = query.fetch(); + } + } catch (SeException se) { + throw new ArcSdeException("Error fetching raster attributes for " + rasterTable, se); + } finally { + if (query != null) { + try { + query.close(); + } catch (SeException e) { + throw new ArcSdeException(e); + } + } + } + LOGGER.fine("Found " + rasterAttList.size() + " raster attributes for " + rasterTable); + return rasterAttList; + } + + /** + * + * @param band + * @param scon + * @return + * @throws ArcSdeException + */ + private Map<Long, IndexColorModel> loadColorMaps(final long rasterColumnId, + final int bitsPerSample, SeConnection scon) throws IOException { + LOGGER.fine("Reading colormap for raster column " + rasterColumnId); + + final String auxTableName = getAuxTableName(rasterColumnId, scon); + LOGGER.fine("Quering auxiliary table " + auxTableName + " for color map data"); + + Map<Long, IndexColorModel> colorMaps = new HashMap<Long, IndexColorModel>(); + SeQuery query = null; + try { + SeSqlConstruct sqlConstruct = new SeSqlConstruct(); + sqlConstruct.setTables(new String[] { auxTableName }); + String whereClause = "TYPE = 3"; + sqlConstruct.setWhere(whereClause); + + query = new SeQuery(scon, new String[] { "RASTERBAND_ID", "OBJECT" }, sqlConstruct); + query.prepareQuery(); + query.execute(); + + long bandId; + ByteArrayInputStream colorMapIS; + DataBuffer colorMapData; + IndexColorModel colorModel; + + SeRow row = query.fetch(); + while (row != null) { + bandId = ((Number) row.getObject(0)).longValue(); + colorMapIS = row.getBlob(1); + + colorMapData = readColorMap(colorMapIS); + colorModel = RasterUtils.sdeColorMapToJavaColorModel(colorMapData, bitsPerSample); + + colorMaps.put(Long.valueOf(bandId), colorModel); + + row = query.fetch(); + } + } catch (SeException e) { + throw new ArcSdeException("Error fetching colormap data for column " + rasterColumnId + + " from table " + auxTableName, e); + } finally { + if (query != null) { + try { + query.close(); + } catch (SeException e) { + LOGGER.log(Level.INFO, "ignoring exception when closing query to " + + "fetch colormap data", e); + } + } + } + LOGGER.fine("Read color map data for " + colorMaps.size() + " rasters"); + return colorMaps; + } + + private DataBuffer readColorMap(final ByteArrayInputStream colorMapIS) throws IOException { + + final DataInputStream dataIn = new DataInputStream(colorMapIS); + // discard unneeded data + dataIn.readInt(); + + final int colorSpaceType = dataIn.readInt(); + final int numBanks; + if (colorSpaceType == SeRaster.SE_COLORMAP_RGB) { + numBanks = 3; + } else if (colorSpaceType == SeRaster.SE_COLORMAP_RGBA) { + numBanks = 4; + } else { + throw new IllegalStateException("Got unknown colormap type: " + colorSpaceType); + } + LOGGER.finest("Colormap has " + numBanks + " color components"); + + final int buffType = dataIn.readInt(); + final int numElems = dataIn.readInt(); + LOGGER.finest("ColorMap length: " + numElems); + + final DataBuffer buff; + if (buffType == SeRaster.SE_COLORMAP_DATA_BYTE) { + LOGGER.finest("Creating Byte data buffer for " + numBanks + " banks and " + numElems + + " elements per bank"); + buff = new DataBufferByte(numElems, numBanks); + for (int elem = 0; elem < numElems; elem++) { + for (int bank = 0; bank < numBanks; bank++) { + int val = dataIn.readUnsignedByte(); + buff.setElem(bank, elem, val); + } + } + } else if (buffType == SeRaster.SE_COLORMAP_DATA_SHORT) { + LOGGER.finest("Creating Short data buffer for " + numBanks + " banks and " + numElems + + " elements per bank"); + buff = new DataBufferUShort(numElems, numBanks); + for (int elem = 0; elem < numElems; elem++) { + for (int bank = 0; bank < numBanks; bank++) { + int val = dataIn.readUnsignedShort(); + buff.setElem(bank, elem, val); + } + } + } else { + throw new IllegalStateException("Unknown databuffer type from colormap header: " + + buffType + " expected one of TYPE_BYTE, TYPE_SHORT"); + } + + assert dataIn.read() == -1 : "color map data should have been exausted"; + return buff; + } + + private String getAuxTableName(long rasterColumnId, SeConnection scon) throws IOException { + + final String owner; + SeQuery query = null; + try { + final String dbaName = scon.getSdeDbaName(); + String rastersColumnsTable = dbaName + ".SDE_RASTER_COLUMNS"; + + SeSqlConstruct sqlCons = new SeSqlConstruct(rastersColumnsTable); + sqlCons.setWhere("RASTERCOLUMN_ID = " + rasterColumnId); + + try { + query = new SeQuery(scon, new String[] { "OWNER" }, sqlCons); + query.prepareQuery(); + } catch (SeException e) { + // sde 9.3 calls it raster_columns, not sde_raster_columns... + rastersColumnsTable = dbaName + ".RASTER_COLUMNS"; + sqlCons = new SeSqlConstruct(rastersColumnsTable); + sqlCons.setWhere("RASTERCOLUMN_ID = " + rasterColumnId); + query = new SeQuery(scon, new String[] { "OWNER" }, sqlCons); + query.prepareQuery(); + } + query.execute(); + + SeRow row = query.fetch(); + if (row == null) { + throw new IllegalArgumentException("No raster column registered with id " + + rasterColumnId); + } + owner = row.getString(0); + query.close(); + } catch (SeException e) { + throw new ArcSdeException("Error getting auxiliary table for raster column " + + rasterColumnId, e); + } finally { + if (query != null) { + try { + query.close(); + } catch (SeException e) { + LOGGER.log(Level.INFO, "ignoring exception when closing query to " + + "fetch colormap data", e); + } + } + } + + final String auxTableName = owner + ".SDE_AUX_" + rasterColumnId; + + return auxTableName; + } + + private List<RasterBandInfo> setUpBandInfo(SeConnection scon, SeRasterAttr rasterAttributes, + Map<Long, IndexColorModel> rastersColorMaps) throws IOException { + final int numBands; + final SeRasterBand[] seBands; + final RasterCellType cellType; + try { + numBands = rasterAttributes.getNumBands(); + seBands = rasterAttributes.getBands(); + cellType = RasterCellType.valueOf(rasterAttributes.getPixelType()); + } catch (SeException e) { + throw new ArcSdeException(e); + } + + List<RasterBandInfo> detachedBandInfo = new ArrayList<RasterBandInfo>(numBands); + + RasterBandInfo bandInfo; + SeRasterBand band; + for (int bandN = 0; bandN < numBands; bandN++) { + band = seBands[bandN]; + bandInfo = new RasterBandInfo(); + final int bitsPerSample = cellType.getBitsPerSample(); + setBandInfo(numBands, bandInfo, band, scon, bitsPerSample, rastersColorMaps); + detachedBandInfo.add(bandInfo); + } + return detachedBandInfo; + } + + /** + * + * @param numBands + * @param bandInfo + * @param band + * @param scon + * @param bitsPerSample + * only used if the band is colormapped to create the IndexColorModel + * @throws IOException + */ + private void setBandInfo(final int numBands, final RasterBandInfo bandInfo, + final SeRasterBand band, final SeConnection scon, int bitsPerSample, + final Map<Long, IndexColorModel> colorMaps) throws IOException { + + bandInfo.bandId = band.getId().longValue(); + bandInfo.bandNumber = band.getBandNumber(); + bandInfo.bandName = "Band " + bandInfo.bandNumber; + + final boolean hasColorMap = band.hasColorMap(); + if (hasColorMap) { + IndexColorModel colorMap = colorMaps.get(Long.valueOf(bandInfo.bandId)); + LOGGER.finest("Setting band's color map: " + colorMap); + bandInfo.nativeColorMap = colorMap; + bandInfo.colorMap = RasterUtils.ensureNoDataPixelIsAvailable(colorMap); + } else { + bandInfo.nativeColorMap = null; + } + + bandInfo.compressionType = CompressionType.valueOf(band.getCompressionType()); + bandInfo.cellType = RasterCellType.valueOf(band.getPixelType()); + bandInfo.interleaveType = InterleaveType.valueOf(band.getInterleave()); + bandInfo.interpolationType = InterpolationType.valueOf(band.getInterpolation()); + bandInfo.hasStats = band.hasStats(); + if (bandInfo.hasStats) { + try { + bandInfo.statsMin = band.getStatsMin(); + bandInfo.statsMax = band.getStatsMax(); + bandInfo.statsMean = band.getStatsMean(); + bandInfo.statsStdDev = band.getStatsStdDev(); + } catch (SeException e) { + throw new ArcSdeException(e); + } + // double noDataValue = 0; + // bandInfo.noDataValue = noDataValue; + } else { + bandInfo.statsMin = java.lang.Double.NaN; + bandInfo.statsMax = java.lang.Double.NaN; + bandInfo.statsMean = java.lang.Double.NaN; + bandInfo.statsStdDev = java.lang.Double.NaN; + } + if (bandInfo.getColorMap() != null) { + bandInfo.noDataValue = RasterUtils.determineNoDataValue(bandInfo.getColorMap()); + } else { + double statsMin = bandInfo.getStatsMin(); + double statsMax = bandInfo.getStatsMax(); + RasterCellType nativeCellType = bandInfo.getCellType(); + bandInfo.noDataValue = RasterUtils.determineNoDataValue(numBands, statsMin, statsMax, + nativeCellType); + } + SDEPoint tOrigin; + try { + tOrigin = band.getTileOrigin(); + } catch (SeException e) { + throw new ArcSdeException(e); + } + bandInfo.tileOrigin = new Point2D.Double(tOrigin.getX(), tOrigin.getY()); + } + +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterleaveType.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterleaveType.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterleaveType.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterleaveType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,66 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.util.NoSuchElementException; + +import com.esri.sde.sdk.client.SeRaster; + +/** + * An enumeration that mirrors the different possible band interleave types in Arcsde (ie, {@code + * SeRaster#SE_RASTER_INTERLEAVE_*}) + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/raster/info/InterleaveType.java $ + */ +public enum InterleaveType { + INTERLEAVE_BIL, INTERLEAVE_BIL_91, INTERLEAVE_BIP, INTERLEAVE_BIP_91, INTERLEAVE_BSQ, INTERLEAVE_BSQ_91, INTERLEAVE_NONE; + static { + INTERLEAVE_BIL.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIL); + INTERLEAVE_BIL_91.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIL_91); + INTERLEAVE_BIP.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIP); + INTERLEAVE_BIP_91.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BIP_91); + INTERLEAVE_BSQ.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BSQ); + INTERLEAVE_BSQ_91.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_BSQ_91); + INTERLEAVE_NONE.setSdeTypeId(SeRaster.SE_RASTER_INTERLEAVE_NONE); + } + + private int typeId; + + private void setSdeTypeId(int typeId) { + this.typeId = typeId; + } + + public int getSeRasterInterleaveType() { + return this.typeId; + } + + public static InterleaveType valueOf(final int seRasterInterleaveType) { + for (InterleaveType type : InterleaveType.values()) { + if (type.getSeRasterInterleaveType() == seRasterInterleaveType) { + return type; + } + } + throw new NoSuchElementException("Raster interleave type " + seRasterInterleaveType + + " does not exist"); + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterpolationType.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/InterpolationType.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterpolationType.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/InterpolationType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,63 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.util.NoSuchElementException; + +import com.esri.sde.sdk.client.SeRaster; + +/** + * An enumeration that mirrors the different possible raster interpolation types in Arcsde (ie, + * {@code SeRaster#SE_INTERPOLATION_*}) + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/raster/info/InterpolationType.java $ + */ +public enum InterpolationType { + INTERPOLATION_BICUBIC, INTERPOLATION_BILINEAR, INTERPOLATION_NEAREST, INTERPOLATION_NONE; + static { + INTERPOLATION_BICUBIC.setSdeTypeId(SeRaster.SE_INTERPOLATION_BICUBIC); + INTERPOLATION_BILINEAR.setSdeTypeId(SeRaster.SE_INTERPOLATION_BILINEAR); + INTERPOLATION_NEAREST.setSdeTypeId(SeRaster.SE_INTERPOLATION_NEAREST); + INTERPOLATION_NONE.setSdeTypeId(SeRaster.SE_INTERPOLATION_NONE); + } + + private int typeId; + + private void setSdeTypeId(int typeId) { + this.typeId = typeId; + } + + public int getSeInterpolationType() { + return this.typeId; + } + + public static InterpolationType valueOf(final int seInterpolationType) { + for (InterpolationType type : InterpolationType.values()) { + if (type.getSeInterpolationType() == seInterpolationType) { + return type; + } + } + throw new NoSuchElementException("Interpolation type " + seInterpolationType + + " does not exist"); + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/PyramidLevelInfo.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PyramidLevelInfo.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/PyramidLevelInfo.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/PyramidLevelInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,178 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.geom.Point2D; + +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +/** + * Represents one level in an ArcSDE pyramid. Holds information about a given pyramid level, like + * resolution, x/y offsets, number of tiles high/wide, total pixel size and total envelope covered + * by this level. + * + * @author sfarber + * + */ +final class PyramidLevelInfo { + private int pyramidLevel, xTiles, yTiles; + + Point2D extentOffset; + + Point imageOffset; + + private double xRes, yRes; + + private ReferencedEnvelope envelope; + + public Dimension size; + + /** + * + * @param level + * the level index + * @param extent + * the geographical extent the level covers + * @param imgOffset + * the offset of the image at this level + * @param extOffset + * the offset of the image extent at this level + * @param numTilesWide + * @param numTilesHigh + * @param levelSize + * the size of the actual image inside the tiled pixel range + */ + PyramidLevelInfo(int level, ReferencedEnvelope extent, Point imgOffset, Point2D extOffset, + int numTilesWide, int numTilesHigh, Dimension levelSize) { + this.pyramidLevel = level; + this.xRes = extent.getWidth() / levelSize.width; + this.yRes = extent.getHeight() / levelSize.height; + this.envelope = extent; + this.imageOffset = imgOffset; + this.extentOffset = extOffset; + this.xTiles = numTilesWide; + this.yTiles = numTilesHigh; + this.size = levelSize; + } + + /** + * @return Which level in the pyramid this object represents + */ + public int getLevel() { + return pyramidLevel; + } + + /** + * @return The X and Y resolution in units/pixel for pixels at this level + */ + public double getXRes() { + return xRes; + } + + /** + * @return The X and Y resolution in units/pixel for pixels at this level + */ + public double getYRes() { + return yRes; + } + + /** + * @return DOCUMENT ME!!! + */ + public int getXOffset() { + return imageOffset.x; + } + + /** + * @return DOCUMENT ME!!! + */ + public int getYOffset() { + return imageOffset.y; + } + + /** + * @return The total number of tiles covering the width of this level + */ + public int getNumTilesWide() { + return xTiles; + } + + /** + * @return The total number of tiles covering the height of this level + */ + public int getNumTilesHigh() { + return yTiles; + } + + /** + * The envelope covering the image grid range inside fully tiled image at this pyramid level + * + * @return The geographical area covered by the {@link #getImageRange() grid range} of the + * raster at this pyramid level + */ + public ReferencedEnvelope getImageEnvelope() { + final double deltaX = extentOffset.getX(); + final double deltaY = extentOffset.getY(); + double minx = this.envelope.getMinX() - deltaX; + double miny = this.envelope.getMinY() - deltaY; + double maxx = minx + this.envelope.getWidth(); + double maxy = miny + this.envelope.getHeight(); + + CoordinateReferenceSystem crs = this.envelope.getCoordinateReferenceSystem(); + ReferencedEnvelope imageExtent = new ReferencedEnvelope(minx, maxx, miny, maxy, crs); + + return imageExtent; + } + + /** + * @return The total number of pixels in the image at this level as whole tiles + */ + public Dimension getSize() { + return size; + } + + /** + * The rectangle covering the actual raster data inside the tiled space + * + * @return + */ + public Rectangle getImageRange() { + final int offsetX = getXOffset(); + final int offsetY = getYOffset(); + + /* + * get the range of actual data pixels in this pyramid level in pixel space, offset and + * trailing no data pixels to fill up the tile space do not count + */ + final Rectangle levelRange = new Rectangle(offsetX, offsetY, size.width, size.height); + return levelRange; + } + + @Override + public String toString() { + return "[level: " + pyramidLevel + " size: " + size.width + "x" + size.height + " xRes: " + + xRes + " yRes: " + yRes + " xOffset: " + getXOffset() + " yOffset: " + + getYOffset() + " extent: " + envelope.getMinX() + "," + envelope.getMinY() + " " + + envelope.getMaxX() + "," + envelope.getMaxY() + " tilesWide: " + xTiles + + " tilesHigh: " + yTiles + "]"; + } +} \ No newline at end of file Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterBandInfo.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterBandInfo.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterBandInfo.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterBandInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,186 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.awt.geom.Point2D; +import java.awt.geom.Point2D.Double; +import java.awt.image.IndexColorModel; + +/** + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/raster/info/RasterBandInfo.java $ + */ +public class RasterBandInfo { + + long bandId; + + String bandName; + + int bandNumber; + + /** + * the color map as it is on the database, except that we always create a color map with alpha + * channel regardless of whether the native on has alpha or not, to account for the no-data + * pixel to be a transparent one where appropriate + */ + IndexColorModel nativeColorMap; + + /** + * The color map as it is going to be used by this library. May differ from the native one + * either in the number of elements (when the native color map is not full and it has no + * transparent pixel to be used as no-data value), or in the pixel depth (when the native + * colormap is full, has no no-data pixel, and hence it needs to be promoted to a higher + * transfer type to make room for a no-data index) + */ + IndexColorModel colorMap; + + /** + * The band's no-data value. + */ + Number noDataValue; + + CompressionType compressionType; + + RasterCellType cellType; + + InterleaveType interleaveType; + + InterpolationType interpolationType; + + boolean hasStats; + + Point2D.Double tileOrigin; + + double statsMin; + + double statsMax; + + double statsMean; + + double statsStdDev; + + public RasterBandInfo() { + // do nothing + } + + public RasterBandInfo(long bandId, RasterCellType nativeType, Number noDataValue, + double statsMin, double statsMax) { + this.bandId = bandId; + this.cellType = nativeType; + this.noDataValue = noDataValue; + this.statsMin = statsMin; + this.statsMax = statsMax; + } + + /** + * @return the ArcSDE identifier for the band + */ + public long getBandId() { + return bandId; + } + + public String getBandName() { + return bandName; + } + + public int getBandNumber() { + return bandNumber; + } + + public boolean isColorMapped() { + return nativeColorMap != null; + } + + public CompressionType getCompressionType() { + return compressionType; + } + + public RasterCellType getCellType() { + return cellType; + } + + public InterleaveType getInterleaveType() { + return interleaveType; + } + + public InterpolationType getInterpolationType() { + return interpolationType; + } + + public boolean isHasStats() { + return hasStats; + } + + public Double getTileOrigin() { + return tileOrigin; + } + + public IndexColorModel getNativeColorMap() { + return nativeColorMap; + } + + public IndexColorModel getColorMap() { + return colorMap; + } + + public double getStatsMin() { + return statsMin; + } + + public double getStatsMax() { + return statsMax; + } + + public double getStatsMean() { + return statsMean; + } + + public double getStatsStdDev() { + return statsStdDev; + } + + public Number getNoDataValue() { + return noDataValue; + } + + @SuppressWarnings("nls") + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getBandName()); + sb.append("[ id:").append(getBandId()); + sb.append(", type:").append(getCellType()); + sb.append(", samples: nodata=").append(getNoDataValue()).append(" min=").append( + getStatsMin()).append(" max=").append(getStatsMax()).append(" mean=").append( + getStatsMean()).append(" stddev=").append(getStatsStdDev()); + /* + * sb.append(", tile origin: ").append((int) getTileOrigin().x).append(",").append( (int) + * getTileOrigin().y); + */ + sb.append(", compression:").append(getCompressionType()); + sb.append(", interpolation:").append(getInterpolationType()); + sb.append(", Color Map: ").append(isColorMapped() ? "YES" : "NO"); + return sb.toString(); + } + +} \ No newline at end of file Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterCellType.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterCellType.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterCellType.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterCellType.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,121 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.awt.image.DataBuffer; +import java.util.NoSuchElementException; + +import org.geotools.util.NumberRange; + +import com.esri.sde.sdk.client.SeRaster; + +/** + * An enumeration that mirrors the different possible cell resolutions in Arcsde (ie, {@code + * SeRaster#SE_PIXEL_TYPE_*}) + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/raster/info/RasterCellType.java $ + */ +public enum RasterCellType { + TYPE_16BIT_S(16, DataBuffer.TYPE_SHORT, true, NumberRange.create(Short.MIN_VALUE, + Short.MAX_VALUE)), // + TYPE_16BIT_U(16, DataBuffer.TYPE_USHORT, false, NumberRange.create((int) 0, (int) 65535)), // + TYPE_1BIT(1, DataBuffer.TYPE_BYTE, false, NumberRange.create((byte) 0, (byte) 1)), // + TYPE_32BIT_REAL(32, DataBuffer.TYPE_FLOAT, true, NumberRange.create(Float.MIN_VALUE, + Float.MAX_VALUE)), // + TYPE_32BIT_S(32, DataBuffer.TYPE_INT, true, NumberRange.create(Integer.MIN_VALUE, + Integer.MAX_VALUE)), // + TYPE_32BIT_U(32, DataBuffer.TYPE_INT, false, NumberRange.create(0L, (long) Math.pow(2, 32) - 1)), // + TYPE_4BIT(4, DataBuffer.TYPE_BYTE, false, NumberRange.create((byte) 0, + (byte) Math.pow(2, 4) - 1)), // + TYPE_64BIT_REAL(64, DataBuffer.TYPE_DOUBLE, true, NumberRange.create(Double.MIN_VALUE, + Double.MAX_VALUE)), // + TYPE_8BIT_S(8, DataBuffer.TYPE_BYTE, true, NumberRange.create(Byte.MIN_VALUE, Byte.MAX_VALUE)), // + TYPE_8BIT_U(8, DataBuffer.TYPE_BYTE, false, NumberRange.create((int) 0, (int) 255)); + static { + // Setting the se_pixel_type in a static initializer to avoid having them linked to the fake + // values in the dummy api + TYPE_16BIT_S.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_16BIT_S); + TYPE_16BIT_U.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_16BIT_U); + TYPE_1BIT.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_1BIT); + TYPE_32BIT_REAL.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_32BIT_REAL); + TYPE_32BIT_S.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_32BIT_S); + TYPE_32BIT_U.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_32BIT_U); + TYPE_4BIT.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_4BIT); + TYPE_64BIT_REAL.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_64BIT_REAL); + TYPE_8BIT_S.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_8BIT_S); + TYPE_8BIT_U.setSdeTypeId(SeRaster.SE_PIXEL_TYPE_8BIT_U); + } + + private int typeId; + + private final int bitsPerSample; + + private final int dataBufferType; + + private final boolean signed; + + private final NumberRange<?> sampleValueRange; + + private RasterCellType(final int bitsPerSample, final int dataBufferType, final boolean signed, + final NumberRange<?> sampleValueRange) { + this.bitsPerSample = bitsPerSample; + this.dataBufferType = dataBufferType; + this.signed = signed; + this.sampleValueRange = sampleValueRange; + } + + private void setSdeTypeId(int typeId) { + this.typeId = typeId; + } + + public int getSeRasterPixelType() { + return this.typeId; + } + + public int getBitsPerSample() { + return bitsPerSample; + } + + public int getDataBufferType() { + return dataBufferType; + } + + public boolean isSigned() { + return signed; + } + + public static RasterCellType valueOf(final int seRasterPixelType) { + for (RasterCellType type : RasterCellType.values()) { + if (type.getSeRasterPixelType() == seRasterPixelType) { + return type; + } + } + throw new NoSuchElementException("Raster pixel type " + seRasterPixelType + + " does not exist"); + } + + public NumberRange<?> getSampleValueRange() { + return sampleValueRange; + } + +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterDatasetInfo.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterDatasetInfo.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterDatasetInfo.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterDatasetInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,503 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.image.IndexColorModel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.imageio.ImageTypeSpecifier; + +import org.geotools.coverage.Category; +import org.geotools.coverage.GridSampleDimension; +import org.geotools.coverage.grid.GeneralGridEnvelope; +import org.geotools.coverage.grid.io.OverviewPolicy; +import org.geotools.geometry.GeneralEnvelope; +import org.geotools.referencing.CRS; +import org.geotools.referencing.operation.builder.GridToEnvelopeMapper; +import org.geotools.referencing.operation.transform.LinearTransform1D; +import org.geotools.resources.i18n.Vocabulary; +import org.geotools.resources.i18n.VocabularyKeys; +import org.geotools.util.NumberRange; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.datum.PixelInCell; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.NoninvertibleTransformException; +import org.opengis.referencing.operation.TransformException; + +/** + * Wraps metadata information for a raster dataset, whether it is composed of a single raster, or + * it's raster catalog, and provides some conveinent methods to get to the raster metadata of it's + * rasters and pyramid levels. + * <p> + * This is the single entry point to the metadata of a raster dataset. The associated classes + * {@link RasterInfo} and {@link PyramidLevelInfo} are to be considered private to this class. + * </p> + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/gce/RasterDatasetInfo.java $ + */ +@SuppressWarnings( { "nls" }) +public final class RasterDatasetInfo { + + /** TRasterDatasetInfo the raster table we're pulling images from in this reader * */ + private String rasterTable = null; + + /** + * raster column names on this raster. If there's more than one raster column (is this + * possible?) then we just use the first one. + */ + private String[] rasterColumns; + + /** Array holding information on each level of the pyramid in this raster. * */ + private List<RasterInfo> subRasterInfo; + + /** + * The original (ie, pyramid level zero) envelope for the whole raster dataset + */ + private GeneralEnvelope originalEnvelope; + + private GeneralGridEnvelope originalGridRange; + + private List<GridSampleDimension> gridSampleDimensions; + + private final Map<Integer, ImageTypeSpecifier> renderedImageSpec = new HashMap<Integer, ImageTypeSpecifier>(); + + /** + * @param rasterTable + * the rasterTable to set + */ + void setRasterTable(String rasterTable) { + this.rasterTable = rasterTable; + } + + /** + * @return the raster table name + */ + public String getRasterTable() { + return rasterTable; + } + + /** + * @param rasterColumns + * the rasterColumns to set + */ + void setRasterColumns(String[] rasterColumns) { + this.rasterColumns = rasterColumns; + } + + /** + * @return the raster column names + */ + public String[] getRasterColumns() { + return rasterColumns; + } + + /** + * @param pyramidInfo + * the pyramidInfo to set + */ + public void setPyramidInfo(List<RasterInfo> pyramidInfo) { + this.subRasterInfo = pyramidInfo; + } + + public GridSampleDimension[] getGridSampleDimensions() { + if (gridSampleDimensions == null) { + synchronized (this) { + if (gridSampleDimensions == null) { + gridSampleDimensions = buildSampleDimensions(); + } + } + } + return gridSampleDimensions.toArray(new GridSampleDimension[getNumBands()]); + } + + @SuppressWarnings("unchecked") + private List<GridSampleDimension> buildSampleDimensions() { + + final int numBands = getNumBands(); + List<GridSampleDimension> dimensions = new ArrayList<GridSampleDimension>(numBands); + + final Color transparent = new Color(0, 0, 0, 0); + + List<RasterBandInfo> bands = subRasterInfo.get(0).getBands(); + + for (RasterBandInfo band : bands) { + final int bandNumber = band.getBandNumber(); + // use native cell type, in case no-data value has been computed to account for + // sample depth promotion, we want to category to keep being the native range for + // the values category + final RasterCellType targetCellType = getNativeCellType(); + String bandName = band.getBandName(); + + final double statsMin = band.getStatsMin(); + final double statsMax = band.getStatsMax(); + + NumberRange<?> sampleValueRange; + if (Double.isNaN(statsMin) || Double.isNaN(statsMax)) { + sampleValueRange = targetCellType.getSampleValueRange(); + } else { + sampleValueRange = NumberRange.create(statsMin, statsMax); + Class elementClass = targetCellType.getSampleValueRange().getElementClass(); + sampleValueRange = sampleValueRange.castTo(elementClass); + } + + final Color[] colorRange = null; + + final boolean geophysics = isGeoPhysics(); + + Category valuesCat = new Category("values", colorRange, sampleValueRange, + LinearTransform1D.IDENTITY).geophysics(geophysics); + + Category[] categories; + if (geophysics) { + double noDataValue = band.getNoDataValue().doubleValue(); + // same as Category.NODATA but for the actual nodata value instead of hardcoded to + // zero + Category nodataCat = new Category(Vocabulary + .formatInternational(VocabularyKeys.NODATA), transparent, noDataValue); + categories = new Category[] { valuesCat, nodataCat }; + } else { + // do not build a nodata category. A nodata value that doesn't overlap the value + // range couldn't be determined + categories = new Category[] { valuesCat }; + } + /* + * if (band.isHasStats()) { //can't do this, get an exception telling categories + * overlap.. so no real way to express the statistics, uh? Category catMin = new + * Category("Min", null, band.getStatsMin()).geophysics(true); Category catMax = new + * Category("Max", null, band.getStatsMin()).geophysics(true); Category catMean = new + * Category("Mean", null, band.getStatsMin()).geophysics(true); Category catStdDev = new + * Category("StdDev", null, band.getStatsMin()) .geophysics(true); categories = new + * Category[] { valuesCat, nodataCat, catMin, catMax, catMean, catStdDev }; } else { + * categories = new Category[] { valuesCat, nodataCat }; } + */ + + // .geophysics(false) because our sample model always corresponds to the packed view + // (whether it matches the underlying sample depth or we're promoting in order to make + // room for the nodata value). + GridSampleDimension sampleDim = new GridSampleDimension(bandName, categories, null) + .geophysics(false); + + dimensions.add(sampleDim); + } + return dimensions; + } + + private boolean isGeoPhysics() { + if (isColorMapped()) { + return false; + } + return RasterUtils.isGeoPhysics(getNumBands(), getNativeCellType()); + } + + public int getNumBands() { + return subRasterInfo.get(0).getNumBands(); + } + + public int getImageWidth() { + final GeneralGridEnvelope originalGridRange = getOriginalGridRange(); + final int width = originalGridRange.getSpan(0); + return width; + } + + public int getImageHeight() { + final GeneralGridEnvelope originalGridRange = getOriginalGridRange(); + final int height = originalGridRange.getSpan(1); + return height; + } + + /** + * @return the coverageCrs + */ + public CoordinateReferenceSystem getCoverageCrs() { + return subRasterInfo.get(0).getCoordinateReferenceSystem(); + } + + /** + * @return the originalGridRange for the whole raster dataset, based on the first raster in the + * raster dataset + */ + public GeneralGridEnvelope getOriginalGridRange() { + if (originalGridRange == null) { + final MathTransform modelToRaster; + try { + final MathTransform rasterToModel = getRasterToModel(); + modelToRaster = rasterToModel.inverse(); + } catch (NoninvertibleTransformException e) { + throw new IllegalStateException("Can't create transform from model to raster"); + } + + int minx = Integer.MAX_VALUE; + int miny = Integer.MAX_VALUE; + int maxx = Integer.MIN_VALUE; + int maxy = Integer.MIN_VALUE; + + final int rasterCount = getNumRasters(); + for (int rasterN = 0; rasterN < rasterCount; rasterN++) { + final GeneralEnvelope rasterEnvelope = getGridEnvelope(rasterN, 0); + GeneralEnvelope rasterGridRangeInDataSet; + try { + rasterGridRangeInDataSet = CRS.transform(modelToRaster, rasterEnvelope); + } catch (NoninvertibleTransformException e) { + throw new IllegalArgumentException(e); + } catch (TransformException e) { + throw new IllegalArgumentException(e); + } + + minx = Math.min(minx, (int) Math.floor(rasterGridRangeInDataSet.getMinimum(0))); + miny = Math.min(miny, (int) Math.floor(rasterGridRangeInDataSet.getMinimum(1))); + maxx = Math.max(maxx, (int) Math.ceil(rasterGridRangeInDataSet.getMaximum(0))); + maxy = Math.max(maxy, (int) Math.ceil(rasterGridRangeInDataSet.getMaximum(1))); + } + int width = maxx - minx; + int height = maxy - miny; + Rectangle range = new Rectangle(0, 0, width, height); + originalGridRange = new GeneralGridEnvelope(range, 2); + } + return originalGridRange; + } + + public MathTransform getRasterToModel() { + + GeneralEnvelope firstRasterEnvelope = getGridEnvelope(0, 0); + Rectangle firstRasterGridRange = getGridRange(0, 0); + GeneralGridEnvelope gridRange = new GeneralGridEnvelope(firstRasterGridRange, 2); + + // create a raster to model transform, from this tile pixel space to the tile's geographic + // extent + GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(gridRange, firstRasterEnvelope); + geMapper.setPixelAnchor(PixelInCell.CELL_CORNER); + + final MathTransform rasterToModel = geMapper.createTransform(); + return rasterToModel; + } + + /** + * @return the originalEnvelope + */ + public GeneralEnvelope getOriginalEnvelope() { + if (originalEnvelope == null) { + GeneralEnvelope env = null; + for (RasterInfo raster : subRasterInfo) { + GeneralEnvelope rasterEnvelope = raster.getOriginalEnvelope(); + if (env == null) { + env = new GeneralEnvelope(rasterEnvelope); + } else { + env.add(rasterEnvelope); + } + } + originalEnvelope = env; + } + return originalEnvelope; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ArcSDE Raster: " + getRasterTable()); + sb.append(", Raster columns: ").append(Arrays.asList(getRasterColumns())); + sb.append(", Num bands: ").append(getNumBands()); + sb.append(", Dimension: ").append(getImageWidth()).append("x").append(getImageHeight()); + sb.append(", Pixel type: ").append(getNativeCellType()); + sb.append(", Has Color Map: ").append(isColorMapped()); + for (int rasterIndex = 0; rasterIndex < getNumRasters(); rasterIndex++) { + RasterInfo raster = getRasterInfo(rasterIndex); + sb.append("\n "); + sb.append(raster.toString()); + } + return sb.toString(); + } + + public int getNumRasters() { + return subRasterInfo.size(); + } + + public RasterBandInfo getBand(final int rasterIndex, final int bandIndex) { + RasterInfo rasterInfo = getRasterInfo(rasterIndex); + return rasterInfo.getBand(bandIndex); + } + + public int getNumPyramidLevels(final int rasterIndex) { + RasterInfo rasterInfo = getRasterInfo(rasterIndex); + return rasterInfo.getNumLevels(); + } + + public GeneralEnvelope getGridEnvelope(final int rasterIndex, final int pyramidLevel) { + PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); + return new GeneralEnvelope(level.getImageEnvelope()); + } + + public Rectangle getGridRange(final int rasterIndex, final int pyramidLevel) { + PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); + Rectangle levelRange = level.getImageRange(); + return levelRange; + } + + public int getNumTilesWide(int rasterIndex, int pyramidLevel) { + PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); + return level.getNumTilesWide(); + } + + public int getNumTilesHigh(int rasterIndex, int pyramidLevel) { + PyramidLevelInfo level = getLevel(rasterIndex, pyramidLevel); + return level.getNumTilesHigh(); + } + + public int getTileWidth(final long rasterId) { + return getTileDimension(rasterId).width; + } + + public int getTileHeight(final long rasterId) { + return getTileDimension(rasterId).height; + } + + public Dimension getTileDimension(final long rasterId) { + final int rasterIndex = getRasterIndex(rasterId); + final RasterInfo rasterInfo = getRasterInfo(rasterIndex); + return rasterInfo.getTileDimension(); + } + + public Dimension getTileDimension(int rasterIndex) { + RasterInfo rasterInfo = getRasterInfo(rasterIndex); + return rasterInfo.getTileDimension(); + } + + private PyramidLevelInfo getLevel(int rasterIndex, int pyramidLevel) { + RasterInfo rasterInfo = getRasterInfo(rasterIndex); + PyramidLevelInfo level = rasterInfo.getPyramidLevel(pyramidLevel); + return level; + } + + private RasterInfo getRasterInfo(int rasterIndex) { + RasterInfo rasterInfo = subRasterInfo.get(rasterIndex); + return rasterInfo; + } + + public ImageTypeSpecifier getRenderedImageSpec(final long rasterId) { + final int rasterIndex = getRasterIndex(rasterId); + return getRenderedImageSpec(rasterIndex); + } + + public ImageTypeSpecifier getRenderedImageSpec(final int rasterIndex) { + if (!this.renderedImageSpec.containsKey(Integer.valueOf(rasterIndex))) { + synchronized (this) { + if (!this.renderedImageSpec.containsKey(Integer.valueOf(rasterIndex))) { + ImageTypeSpecifier imageTypeSpecifier; + imageTypeSpecifier = RasterUtils + .createFullImageTypeSpecifier(this, rasterIndex); + renderedImageSpec.put(Integer.valueOf(rasterIndex), imageTypeSpecifier); + } + } + } + return this.renderedImageSpec.get(Integer.valueOf(rasterIndex)); + } + + public IndexColorModel getColorMap(final int rasterIndex) { + final RasterBandInfo bandOne = getBand(rasterIndex, 0); + return bandOne.getColorMap(); + } + + public boolean isColorMapped() { + RasterInfo rasterInfo = getRasterInfo(0); + return rasterInfo.isColorMapped(); + } + + public RasterCellType getNativeCellType() { + RasterInfo rasterInfo = getRasterInfo(0); + return rasterInfo.getNativeCellType(); + } + + public RasterCellType getTargetCellType(final int rasterIndex) { + RasterInfo rasterInfo = getRasterInfo(rasterIndex); + return rasterInfo.getTargetCellType(); + } + + public RasterCellType getTargetCellType(final long rasterId) { + final int rasterIndex = getRasterIndex(rasterId); + return getTargetCellType(rasterIndex); + } + + public Long getRasterId(final int rasterIndex) { + final RasterInfo rasterInfo = getRasterInfo(rasterIndex); + return rasterInfo.getRasterId(); + } + + public int getOptimalPyramidLevel(final int rasterIndex, final OverviewPolicy policy, + final GeneralEnvelope requestedEnvelope, final Rectangle requestedDim) { + + final RasterInfo rasterInfo = getRasterInfo(rasterIndex); + + double[] requestedRes = new double[2]; + double reqSpanX = requestedEnvelope.getSpan(0); + double reqSpanY = requestedEnvelope.getSpan(1); + requestedRes[0] = reqSpanX / requestedDim.getWidth(); + requestedRes[1] = reqSpanY / requestedDim.getHeight(); + + return rasterInfo.getOptimalPyramidLevel(policy, requestedRes); + } + + public int getRasterIndex(Long rasterId) { + int index = -1; + for (RasterInfo p : subRasterInfo) { + index++; + if (rasterId.equals(p.getRasterId())) { + return index; + } + } + throw new IllegalArgumentException("rasterId: " + rasterId); + } + + public double[] getResolution(int rasterN, int pyramidLevel) { + RasterInfo rasterInfo = getRasterInfo(rasterN); + double[] resolution = rasterInfo.getResolution(pyramidLevel); + return resolution; + } + + public Point getTileOffset(final int rasterIndex, final int pyramidLevel) { + RasterInfo rasterInfo = getRasterInfo(rasterIndex); + PyramidLevelInfo level = rasterInfo.getPyramidLevel(pyramidLevel); + return new Point(level.getXOffset(), level.getYOffset()); + } + + public Number getNoDataValue(final int rasterIndex, final int bandIndex) { + RasterBandInfo band = getBand(rasterIndex, bandIndex); + Number noDataValue = band.getNoDataValue(); + return noDataValue; + } + + /** + * @param rasterIndex + * the raster for which bands to return the no data values + * @return the list of no data values, one per band for the raster at index {@code rasterIndex} + */ + public List<Number> getNoDataValues(final int rasterIndex) { + return getRasterInfo(rasterIndex).getNoDataValues(); + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterInfo.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterInfo.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterInfo.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,406 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.geotools.coverage.grid.io.OverviewPolicy; +import org.geotools.data.DataSourceException; +import org.geotools.geometry.GeneralEnvelope; +import org.geotools.geometry.jts.ReferencedEnvelope; +import org.geotools.referencing.CRS; +import org.opengis.referencing.FactoryException; +import org.opengis.referencing.crs.CoordinateReferenceSystem; + +import com.esri.sde.sdk.client.SDEPoint; +import com.esri.sde.sdk.client.SeException; +import com.esri.sde.sdk.client.SeExtent; +import com.esri.sde.sdk.client.SeRasterAttr; + +/** + * A RasterInfo gathers the metadata for a single raster in a raster dataset + * <p> + * Basically, it wraps the SeRasterAttr object and implements some convenience methods for doing + * calculations with it. + * </p> + * + * @author Saul Farber + * @author Gabriel Roldan + */ +public final class RasterInfo { + + /** + * Orders pyramid levels by their level index + */ + private static final Comparator<PyramidLevelInfo> levelComparator = new Comparator<PyramidLevelInfo>() { + public int compare(PyramidLevelInfo p0, PyramidLevelInfo p1) { + return (p0.getLevel() - p1.getLevel()); + } + }; + + ArrayList<PyramidLevelInfo> pyramidList; + + private int tileWidth; + + private int tileHeight; + + private GeneralEnvelope originalEnvelope; + + private ArrayList<RasterBandInfo> bands; + + private CoordinateReferenceSystem crs; + + private Long rasterId; + + /** + * Creates an in-memory representation of an ArcSDE Raster Pyramid. Basically it wraps the + * supplide SeRasterAttr object and implements some convenience logic for extracting + * information/ doing calculations with it. + * + * @param rasterAttributes + * the SeRasterAttr object for the raster of interest. + * @param crs + * @throws DataSourceException + */ + RasterInfo(final SeRasterAttr rasterAttributes, final CoordinateReferenceSystem crs) + throws DataSourceException { + this.crs = crs; + try { + this.rasterId = Long.valueOf(rasterAttributes.getRasterId().longValue()); + // levels goes from 0 to N, maxLevel is the zero-based max index of levels + final int numLevels = rasterAttributes.getMaxLevel() + 1; + + pyramidList = new ArrayList<PyramidLevelInfo>(numLevels); + + tileWidth = rasterAttributes.getTileWidth(); + tileHeight = rasterAttributes.getTileHeight(); + + for (int level = 0; level < numLevels; level++) { + if (level == 1 && rasterAttributes.skipLevelOne()) { + continue; + } + + /* + * this extent corresponds to the actual image size inside the tiled grid. That is, + * to getImageWidth/Height by level. The dimensions of this extent are correct, but + * it needs to be shifted by SeRasterAttr.getExtentOffsetByLevel(level) the same way + * the grid envelope does by SeRasterAttr.getImageOffsetByLevel(level) + */ + final SeExtent slExtent = rasterAttributes.getExtentByLevel(level); + + final int levelWidth = rasterAttributes.getImageWidthByLevel(level); + final int levelHeight = rasterAttributes.getImageHeightByLevel(level); + + Dimension levelImageSize = new Dimension(levelWidth, levelHeight); + + Point imgOffset = new Point(); + // extent offset equals imgOffset * pixel resolution (ie, if resx == 2 and offsetX = + // 10, extent offset x == 20) + Point2D extOffset = new Point2D.Double(); + { + SDEPoint imageOffset = rasterAttributes.getImageOffsetByLevel(level); + int xOffset = (int) (imageOffset == null ? 0 : imageOffset.getX()); + int yOffset = (int) (imageOffset == null ? 0 : imageOffset.getY()); + imgOffset.setLocation(xOffset, yOffset); + + SDEPoint extentOffset = rasterAttributes.getExtentOffsetByLevel(level); + double xOffsetExtent = extentOffset == null ? 0D : extentOffset.getX(); + double yOffsetExtent = extentOffset == null ? 0D : extentOffset.getY(); + extOffset.setLocation(xOffsetExtent, yOffsetExtent); + } + + final int numTilesWide = rasterAttributes.getTilesPerRowByLevel(level); + final int numTileHigh = rasterAttributes.getTilesPerColByLevel(level); + + ReferencedEnvelope levelExtent = new ReferencedEnvelope(slExtent.getMinX(), + slExtent.getMaxX(), slExtent.getMinY(), slExtent.getMaxY(), crs); + + addPyramidLevel(level, levelExtent, imgOffset, extOffset, numTilesWide, + numTileHigh, levelImageSize); + } + + } catch (SeException se) { + throw new DataSourceException(se); + } + } + + public Long getRasterId() { + return rasterId; + } + + public int getTileWidth() { + return tileWidth; + } + + public int getTileHeight() { + return tileHeight; + } + + /** + * Don't use this constructor. It only exists for unit testing purposes. + * + * @param tileWidth + * DON'T USE + * @param tileHeight + * DON'T USE + */ + public RasterInfo(Long rasterId, int tileWidth, int tileHeight) { + this.rasterId = rasterId; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + pyramidList = new ArrayList<PyramidLevelInfo>(4); + } + + public Dimension getTileDimension() { + return new Dimension(tileWidth, tileHeight); + } + + public PyramidLevelInfo getPyramidLevel(int level) { + return pyramidList.get(level); + } + + public int getNumLevels() { + return pyramidList.size(); + } + + /** + * @param pyramidLevel + * @return resx, resy, scalefactor + */ + double[] getResolution(final int pyramidLevel) { + final double highestRes = getPyramidLevel(0).getXRes(); + PyramidLevelInfo level = getPyramidLevel(pyramidLevel); + double[] resolution = new double[3]; + resolution[0] = level.getXRes(); + resolution[1] = level.getYRes(); + resolution[2] = level.getXRes() / highestRes; + return resolution; + } + + /** + * <p> + * NOTE: logic stolen and adapted from {@code AbstractGridCoverage2DReader#getOverviewImage()} + * </p> + * + * @param policy + * @return + */ + public int getOptimalPyramidLevel(final OverviewPolicy policy, final double[] requestedRes) { + + int pyramidLevelChoice = 0; + + // sort resolutions from smallest pixels (higher res) to biggest pixels (higher res) + // keeping a reference to the original image choice + final double[] highestRes = getResolution(0); + + // Now search for the best matching resolution. + // Check also for the "perfect match"... unlikely in practice unless someone + // tunes the clients to request exactly the resolution embedded in + // the overviews, something a perf sensitive person might do in fact + + // the requested resolutions + final double reqx = requestedRes[0]; + final double reqy = requestedRes[1]; + + // requested scale factor for least reduced axis + final double requestedScaleFactorX = reqx / highestRes[0]; + final double requestedScaleFactorY = reqy / highestRes[1]; + final int leastReduceAxis = requestedScaleFactorX <= requestedScaleFactorY ? 0 : 1; + final double requestedScaleFactor = leastReduceAxis == 0 ? requestedScaleFactorX + : requestedScaleFactorY; + + final int numLevels = getNumLevels(); + + // no pyramiding or are we looking for a resolution even higher than the native one? + if (0 == numLevels || requestedScaleFactor <= 1) { + pyramidLevelChoice = 0; + } else { + // are we looking for a resolution even lower than the smallest overview? + final double[] min = getResolution(numLevels - 1); + if (requestedScaleFactor >= min[2]) { + pyramidLevelChoice = numLevels - 1; + } else { + // Ok, so we know the overview is between min and max, skip the first + // and search for an overview with a resolution lower than the one requested, + // that one and the one from the previous step will bound the searched resolution + double[] prev = highestRes; + for (int levelN = 1; levelN < numLevels; levelN++) { + final double[] curr = getResolution(levelN); + // perfect match check + if (curr[2] == requestedScaleFactor) { + pyramidLevelChoice = levelN; + } else { + /* + * middle check. The first part of the condition should be sufficient, but + * there are cases where the x resolution is satisfied by the lowest + * resolution, the y by the one before the lowest (so the aspect ratio of + * the request is different than the one of the overviews), and we would end + * up going out of the loop since not even the lowest can "top" the request + * for one axis + */ + if (curr[2] > requestedScaleFactor || levelN == numLevels - 1) { + if (policy == OverviewPolicy.QUALITY) { + pyramidLevelChoice = levelN - 1; + } else if (policy == OverviewPolicy.SPEED) { + return levelN; + } else if (requestedScaleFactor - prev[2] < curr[2] + - requestedScaleFactor) { + pyramidLevelChoice = levelN - 1; + } else { + pyramidLevelChoice = levelN; + } + break; + } + prev = curr; + } + } + } + } + // fallback + return pyramidLevelChoice; + } + + /** + * Don't use this method. It's only public for unit testing purposes. + * + * @param level + * the zero-based level index for the new level + * @param extent + * the geographical extent the level covers, may need to be offsetted by {@code + * extOffset} + * @param imgOffset + * the offset on the X and Y axes of the actual image inside the tile space for this + * level + * @param extOffset + * the offset on the X and Y axes of the actual image inside the tile space for this + * level + * @param numTilesWide + * the number of tiles that make up the level on the X axis + * @param numTilesHigh + * the number of tiles that make up the level on the Y axis + * @param imageSize + * the size of the actual image in pixels + */ + public void addPyramidLevel(int level, ReferencedEnvelope extent, Point imgOffset, + Point2D extOffset, int numTilesWide, int numTilesHigh, Dimension imageSize) { + + PyramidLevelInfo pyramidLevel; + pyramidLevel = new PyramidLevelInfo(level, extent, imgOffset, extOffset, numTilesWide, + numTilesHigh, imageSize); + + pyramidList.add(pyramidLevel); + + Collections.sort(pyramidList, levelComparator); + } + + void setOriginalEnvelope(GeneralEnvelope originalEnvelope) { + this.originalEnvelope = originalEnvelope; + } + + public GeneralEnvelope getOriginalEnvelope() { + return originalEnvelope; + } + + public void setBands(List<RasterBandInfo> bands) { + this.bands = new ArrayList<RasterBandInfo>(bands); + } + + public List<RasterBandInfo> getBands() { + return new ArrayList<RasterBandInfo>(bands); + } + + public int getNumBands() { + return bands.size(); + } + + public RasterBandInfo getBand(final int index) { + return bands.get(index); + } + + public CoordinateReferenceSystem getCoordinateReferenceSystem() { + return crs; + } + + public RasterCellType getTargetCellType() { + // if (isColorMapped()) { + // // color map is already promoted if needed + // return getNativeCellType(); + // } + List<Number> noDataValues = getNoDataValues(); + RasterCellType nativeCellType = getNativeCellType(); + RasterCellType targetCellType = RasterUtils.determineTargetCellType(nativeCellType, + noDataValues); + return targetCellType; + } + + public boolean isColorMapped() { + return getBand(0).isColorMapped(); + } + + public RasterCellType getNativeCellType() { + return getBand(0).getCellType(); + } + + public List<Number> getNoDataValues() { + final List<Number> noDataValues = new ArrayList<Number>(); + for (RasterBandInfo band : getBands()) { + Number noDataValue = band.getNoDataValue(); + noDataValues.add(noDataValue); + } + return noDataValues; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append("[Id: ").append(getRasterId()); + String srs = null; + try { + srs = CRS.lookupIdentifier(getCoordinateReferenceSystem(), false); + } catch (FactoryException e) { + e.printStackTrace(); + } + sb.append(", bands: ").append(getNumBands()); + sb.append(", levels: ").append(getNumLevels()); + sb.append(", tile size: ").append(getTileWidth()).append("x").append(getTileHeight()); + sb.append(", crs: ").append(srs == null ? getCoordinateReferenceSystem().toWKT() : srs); + GeneralEnvelope env = getOriginalEnvelope(); + sb.append(", Envelope: ").append(env.getMinimum(0)).append(",").append(env.getMinimum(1)) + .append(" ").append(env.getMaximum(0)).append(",").append(env.getMaximum(1)); + + sb.append("]\n Bands["); + for (RasterBandInfo band : getBands()) { + sb.append("\n\t"); + sb.append(band.toString()); + } + sb.append("\n ]"); + sb.append("\n Pyramid["); + for (int l = 0; l < getNumLevels(); l++) { + sb.append("\n\t").append(getPyramidLevel(l).toString()); + } + sb.append("\n ]"); + return sb.toString(); + } + +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterQueryInfo.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterQueryInfo.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterQueryInfo.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterQueryInfo.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,216 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import java.awt.Rectangle; +import java.awt.image.RenderedImage; +import java.util.logging.Logger; + +import org.geotools.geometry.GeneralEnvelope; +import org.geotools.util.logging.Logging; + +/** + * Captures information about a query for a single raster in a raster dataset. + * + * @author Gabriel Roldan + * @version $Id$ + * @since 2.5.6 + * @see RasterUtils#findMatchingRasters + * @see RasterUtils#fitRequestToRaster + */ +public final class RasterQueryInfo { + + private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + private GeneralEnvelope requestedEnvelope; + + private Rectangle requestedDim; + + private int pyramidLevel; + + /** + * The two-dimensional range of tile indices whose envelope intersect the requested extent. Will + * have negative width and height if none of the tiles do. + */ + private Rectangle matchingTiles; + + private GeneralEnvelope resultEnvelope; + + private Rectangle resultDimension; + + private Long rasterId; + + private Rectangle mosaicLocation; + + private RenderedImage resultImage; + + private Rectangle tiledImageSize; + + private double[] resolution; + + private int rasterIndex; + + /** + * The full tile range for the matching pyramid level + */ + private Rectangle levelTileRange; + + public RasterQueryInfo() { + setResultDimensionInsideTiledImage(new Rectangle(0, 0, 0, 0)); + setMatchingTiles(new Rectangle(0, 0, 0, 0)); + setResultEnvelope(null); + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder("[Raster query info:"); + s.append("\n\tRaster ID : ").append(getRasterId()); + s.append("\n\tPyramid level : ").append(getPyramidLevel()); + s.append("\n\tResolution : ").append( + getResolution()[0] + "," + getResolution()[1]); + s.append("\n\tRequested envelope : ").append(getRequestedEnvelope()); + s.append("\n\tRequested dimension : ").append(getRequestedDim()); + Rectangle mt = getMatchingTiles(); + Rectangle ltr = getLevelTileRange(); + String matching = "x=" + mt.x + "-" + (mt.x + mt.width - 1) + ", y=" + mt.y + "-" + + (mt.y + mt.height - 1); + String level = "x=" + ltr.x + "-" + (ltr.width - 1) + ", y=" + ltr.y + "-" + + (ltr.height - 1); + s.append("\n\tMatching tiles : ").append(matching).append(" out of ").append(level); + s.append("\n\tTiled image size : ").append(getTiledImageSize()); + s.append("\n\tResult dimension : ").append(getResultDimensionInsideTiledImage()); + s.append("\n\tMosaiced dimension : ").append(getMosaicLocation()); + s.append("\n\tResult envelope : ").append(getResultEnvelope()); + s.append("\n]"); + return s.toString(); + } + + /** + * @return the rasterId (as in SeRaster.getId()) for the raster in the raster dataset this query + * works upon + */ + public Long getRasterId() { + return rasterId; + } + + public GeneralEnvelope getRequestedEnvelope() { + return requestedEnvelope; + } + + public Rectangle getRequestedDim() { + return requestedDim; + } + + public int getPyramidLevel() { + return pyramidLevel; + } + + public Rectangle getMatchingTiles() { + return matchingTiles; + } + + public GeneralEnvelope getResultEnvelope() { + return resultEnvelope; + } + + public Rectangle getResultDimensionInsideTiledImage() { + return resultDimension; + } + + void setRasterId(Long rasterId) { + this.rasterId = rasterId; + } + + void setPyramidLevel(int pyramidLevel) { + this.pyramidLevel = pyramidLevel; + } + + void setRequestedEnvelope(GeneralEnvelope requestedEnvelope) { + this.requestedEnvelope = requestedEnvelope; + } + + void setRequestedDim(Rectangle requestedDim) { + this.requestedDim = requestedDim; + } + + void setResultEnvelope(GeneralEnvelope resultEnvelope) { + this.resultEnvelope = resultEnvelope; + } + + void setMatchingTiles(Rectangle matchingTiles) { + this.matchingTiles = matchingTiles; + } + + void setResultDimensionInsideTiledImage(Rectangle resultDimension) { + this.resultDimension = resultDimension; + } + + void setMosaicLocation(Rectangle rasterMosaicLocation) { + this.mosaicLocation = rasterMosaicLocation; + } + + public Rectangle getMosaicLocation() { + return mosaicLocation; + } + + public void setResultImage(RenderedImage rasterImage) { + this.resultImage = rasterImage; + if (rasterImage.getWidth() != tiledImageSize.width + || rasterImage.getHeight() != tiledImageSize.height) { + LOGGER.warning("Result image and expected dimensions don't match: image=" + + resultImage.getWidth() + "x" + resultImage.getHeight() + ", expected=" + + tiledImageSize.width + "x" + tiledImageSize.height); + } + } + + public RenderedImage getResultImage() { + return resultImage; + } + + void setTiledImageSize(Rectangle tiledImageSize) { + this.tiledImageSize = tiledImageSize; + } + + public Rectangle getTiledImageSize() { + return tiledImageSize; + } + + void setResolution(double[] resolution) { + this.resolution = resolution; + } + + public double[] getResolution() { + return resolution == null ? new double[] { -1, -1 } : resolution; + } + + void setRasterIndex(int rasterN) { + this.rasterIndex = rasterN; + } + + public int getRasterIndex() { + return rasterIndex; + } + + void setLevelTileRange(Rectangle levelTileRange) { + this.levelTileRange = levelTileRange; + } + + public Rectangle getLevelTileRange() { + return levelTileRange; + } +} \ No newline at end of file Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterUtils.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterUtils.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterUtils.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/info/RasterUtils.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,880 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.info; + +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_16BIT_S; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_16BIT_U; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_1BIT; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_32BIT_REAL; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_32BIT_S; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_32BIT_U; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_4BIT; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_64BIT_REAL; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_8BIT_U; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Transparency; +import java.awt.color.ColorSpace; +import java.awt.image.BandedSampleModel; +import java.awt.image.ColorModel; +import java.awt.image.ComponentColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.IndexColorModel; +import java.awt.image.SampleModel; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Logger; + +import javax.imageio.ImageTypeSpecifier; + +import org.geotools.coverage.GridSampleDimension; +import org.geotools.coverage.grid.GeneralGridEnvelope; +import org.geotools.coverage.grid.io.OverviewPolicy; +import org.geotools.geometry.GeneralEnvelope; +import org.geotools.referencing.CRS; +import org.geotools.referencing.operation.builder.GridToEnvelopeMapper; +import org.geotools.resources.image.ColorUtilities; +import org.geotools.resources.image.ComponentColorModelJAI; +import org.geotools.util.NumberRange; +import org.geotools.util.logging.Logging; +import org.opengis.geometry.Envelope; +import org.opengis.referencing.datum.PixelInCell; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.NoninvertibleTransformException; +import org.opengis.referencing.operation.TransformException; + +import com.sun.imageio.plugins.common.BogusColorSpace; + +/** + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/raster/info/RasterUtils.java $ + */ +@SuppressWarnings( { "nls" }) +public class RasterUtils { + + private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + private RasterUtils() { + // do nothing + } + + public static MathTransform createRasterToModel(final Rectangle levelGridRange, + final GeneralEnvelope levelEnvelope) { + // create a raster to model transform, from this tile pixel space to the tile's geographic + // extent + GeneralGridEnvelope gridRange = new GeneralGridEnvelope(levelGridRange, 2); + GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper(gridRange, levelEnvelope); + geMapper.setPixelAnchor(PixelInCell.CELL_CORNER); + + final MathTransform rasterToModel = geMapper.createTransform(); + return rasterToModel; + } + + private static Rectangle getResultDimensionForTileRange(final Rectangle tiledImageGridRange, + final Rectangle matchingLevelRange) { + + int minx = Math.max(tiledImageGridRange.x, matchingLevelRange.x); + int miny = Math.max(tiledImageGridRange.y, matchingLevelRange.y); + int maxx = (int) Math.min(tiledImageGridRange.getMaxX(), matchingLevelRange.getMaxX()); + int maxy = (int) Math.min(tiledImageGridRange.getMaxY(), matchingLevelRange.getMaxY()); + + return new Rectangle(minx, miny, maxx - minx, maxy - miny); + } + + /** + * Returns the rectangle specifying the matching tiles for a given pyramid level and rectangle + * specifying the overlapping area to request in the level's pixel space. + * + * @param pixelRange + * @param tilesHigh + * @param tilesWide + * @param tileSize + * @param numTilesHigh + * @param numTilesWide + * + * @param pixelRange + * @param level + * + * @return a rectangle holding the coordinates in tile space that fully covers the requested + * pixel range for the given pyramid level, or a negative area rectangle + */ + private static Rectangle findMatchingTiles(final Dimension tileSize, int numTilesWide, + int numTilesHigh, final Rectangle pixelRange) { + + final int minPixelX = pixelRange.x; + final int minPixelY = pixelRange.y; + + // TODO: WARNING, we're not considering the possible x/y offsets on the level range for the + // given pyramid level here! + + int minTileX = (int) Math.floor(minPixelX / tileSize.getWidth()); + int minTileY = (int) Math.floor(minPixelY / tileSize.getHeight()); + + int numTilesX = (int) Math.ceil(pixelRange.getWidth() / tileSize.getWidth()); + int numTilesY = (int) Math.ceil(pixelRange.getHeight() / tileSize.getHeight()); + + int maxTiledX = (minTileX + numTilesX) * tileSize.width; + int maxTiledY = (minTileY + numTilesY) * tileSize.height; + + if (maxTiledX < pixelRange.getMaxX() && (minTileX + numTilesX) < numTilesWide) { + numTilesX++; + } + + if (maxTiledY < pixelRange.getMaxY() && (minTileY + numTilesY) < numTilesHigh) { + numTilesY++; + } + + Rectangle matchingTiles = new Rectangle(minTileX, minTileY, numTilesX, numTilesY); + return matchingTiles; + } + + private static Rectangle getTargetGridRange(final MathTransform modelToRaster, + final Envelope requestedEnvelope) { + Rectangle levelOverlappingPixels; + int levelMinPixelX; + int levelMaxPixelX; + int levelMinPixelY; + int levelMaxPixelY; + { + // use a model to raster transform to find out which pixel range at the specified level + // better match the requested extent + GeneralEnvelope requestedPixels; + try { + requestedPixels = CRS.transform(modelToRaster, requestedEnvelope); + } catch (NoninvertibleTransformException e) { + throw new IllegalArgumentException(e); + } catch (TransformException e) { + throw new IllegalArgumentException(e); + } + + levelMinPixelX = (int) Math.floor(requestedPixels.getMinimum(0)); + levelMaxPixelX = (int) Math.floor(requestedPixels.getMaximum(0)); + + levelMinPixelY = (int) Math.ceil(requestedPixels.getMinimum(1)); + levelMaxPixelY = (int) Math.ceil(requestedPixels.getMaximum(1)); + + final int width = levelMaxPixelX - levelMinPixelX; + final int height = levelMaxPixelY - levelMinPixelY; + levelOverlappingPixels = new Rectangle(levelMinPixelX, levelMinPixelY, width, height); + } + return levelOverlappingPixels; + } + + /** + * Creates an IndexColorModel out of a DataBuffer obtained from an ArcSDE's raster color map. + * + * @param colorMapData + * @return + */ + public static IndexColorModel sdeColorMapToJavaColorModel(final DataBuffer colorMapData, + final int bitsPerSample) { + if (colorMapData == null) { + throw new NullPointerException("colorMapData"); + } + + if (colorMapData.getNumBanks() < 3 || colorMapData.getNumBanks() > 4) { + throw new IllegalArgumentException("colorMapData shall have 3 or 4 banks: " + + colorMapData.getNumBanks()); + } + + if (bitsPerSample != 8 && bitsPerSample != 16) { + throw new IllegalAccessError("bits per sample shall be either 8 or 16. Got " + + bitsPerSample); + } + + final int numBanks = colorMapData.getNumBanks(); + final int mapSize = colorMapData.getSize(); + + byte[] r = new byte[mapSize]; + byte[] g = new byte[mapSize]; + byte[] b = new byte[mapSize]; + byte[] a = new byte[mapSize]; + + for (int i = 0; i < mapSize; i++) { + r[i] = (byte) (colorMapData.getElem(0, i) & 0xFF); + g[i] = (byte) (colorMapData.getElem(1, i) & 0xFF); + b[i] = (byte) (colorMapData.getElem(2, i) & 0xFF); + a[i] = (byte) ((numBanks == 3 ? 255 : colorMapData.getElem(3, i)) & 0xFF); + } + + IndexColorModel colorModel = new IndexColorModel(bitsPerSample, mapSize, r, g, b, a); + + return colorModel; + } + + public static ImageTypeSpecifier createFullImageTypeSpecifier( + final RasterDatasetInfo rasterInfo, final int rasterIndex) { + + final int numberOfBands = rasterInfo.getNumBands(); + final RasterCellType nativePixelType = rasterInfo.getNativeCellType(); + final RasterCellType pixelType = rasterInfo.getTargetCellType(rasterIndex); + + // Prepare temporary colorModel and sample model, needed to build the final + // ArcSDEPyramidLevel level; + int sampleImageWidth = 1;// rasterInfo.getImageWidth(); + int sampleImageHeight = 1;// rasterInfo.getImageHeight(); + + final ImageTypeSpecifier its; + // treat special cases... + final int bitsPerSample = pixelType.getBitsPerSample(); + final int dataType = pixelType.getDataBufferType(); + final boolean hasColorMap = rasterInfo.isColorMapped(); + + if (hasColorMap) { + // special case, a single band colormapped image + IndexColorModel colorMap = rasterInfo.getColorMap(rasterIndex); + its = createColorMappedImageSpec(colorMap, sampleImageWidth, sampleImageHeight); + + } else if (nativePixelType == TYPE_1BIT && numberOfBands == 1) { + byte noDataValue = rasterInfo.getNoDataValue(rasterIndex, 0).byteValue(); + // special case, a single band 1-bit + its = createOneBitColorMappedImageSpec(sampleImageWidth, sampleImageHeight, noDataValue); + + } else if (nativePixelType == TYPE_4BIT && numberOfBands == 1) { + byte noDataValue = rasterInfo.getNoDataValue(rasterIndex, 0).byteValue(); + // special case, a single band 4-bit + its = createFourBitColorMappedImageSpec(sampleImageWidth, sampleImageHeight, + noDataValue); + } else if (numberOfBands == 1) { + // special case, a single band grayscale image, no matter the pixel depth + its = createGrayscaleImageSpec(sampleImageWidth, sampleImageHeight, dataType, + bitsPerSample); + + } else if (numberOfBands == 3 && pixelType == TYPE_8BIT_U) { + // special case, an optimizable RGB image + its = createRGBImageSpec(sampleImageWidth, sampleImageHeight, dataType); + + } else if (numberOfBands == 4 && pixelType == TYPE_8BIT_U) { + // special case, an optimizable RGBA image + its = createRGBAImageSpec(sampleImageWidth, sampleImageHeight, dataType); + + } else { + /* + * not an special case, go for a more generic sample model, potentially slower than the + * special case ones, but that'll work anyway + */ + + final ColorModel colorModel; + final SampleModel sampleModel; + { + final ColorSpace colorSpace; + colorSpace = new BogusColorSpace(numberOfBands); + int[] numBits = new int[numberOfBands]; + for (int i = 0; i < numberOfBands; i++) { + numBits[i] = bitsPerSample; + } + colorModel = new ComponentColorModelJAI(colorSpace, numBits, false, false, + Transparency.OPAQUE, dataType); + } + { + int[] bankIndices = new int[numberOfBands]; + int[] bandOffsets = new int[numberOfBands]; + // int bandOffset = (tileWidth * tileHeight * pixelType.getBitsPerSample()) / 8; + for (int i = 0; i < numberOfBands; i++) { + bankIndices[i] = i; + bandOffsets[i] = 0;// (i * bandOffset); + } + sampleModel = new BandedSampleModel(dataType, sampleImageWidth, sampleImageHeight, + sampleImageWidth, bankIndices, bandOffsets); + } + its = new ImageTypeSpecifier(colorModel, sampleModel); + } + + return its; + } + + private static ImageTypeSpecifier createFourBitColorMappedImageSpec(int sampleImageWidth, + int sampleImageHeight, byte noDataValue) { + + int maxValue = (int) TYPE_4BIT.getSampleValueRange().getMaximum(); + + int mapSize = noDataValue > maxValue ? noDataValue : maxValue + 1; + + int[] cmap = new int[mapSize]; + ColorUtilities.expand(new Color[] { Color.BLACK, Color.WHITE }, cmap, 0, maxValue); + + for (int i = maxValue; i < mapSize; i++) { + cmap[i] = ColorUtilities.getIntFromColor(0, 0, 0, 0); + } + + int transparentPixel = noDataValue; + IndexColorModel colorModel = new IndexColorModel(8, mapSize, cmap, 0, true, + transparentPixel, DataBuffer.TYPE_BYTE); + + SampleModel sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, + sampleImageHeight); + ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); + return its; + } + + private static ImageTypeSpecifier createOneBitColorMappedImageSpec(int sampleImageWidth, + int sampleImageHeight, byte noDataValue) { + + assert noDataValue == 2; + + final int FALSE = ColorUtilities.getIntFromColor(255, 255, 255, 255); + final int TRUE = ColorUtilities.getIntFromColor(255, 0, 0, 255); + final int NODATA = ColorUtilities.getIntFromColor(0, 255, 0, 255); + + final int mapSize = 3; + int[] cmap = new int[mapSize]; + cmap[0] = FALSE; + cmap[1] = TRUE; + cmap[2] = NODATA; + + int transparentPixel = noDataValue; + IndexColorModel colorModel = new IndexColorModel(8, mapSize, cmap, 0, false, + transparentPixel, DataBuffer.TYPE_BYTE); + + SampleModel sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, + sampleImageHeight); + ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); + return its; + } + + private static ImageTypeSpecifier createRGBAImageSpec(int sampleImageWidth, + int sampleImageHeight, final int dataType) { + + final ImageTypeSpecifier its; + + ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); + boolean hasAlpha = true; + boolean isAlphaPremultiplied = false; + int transparency = Transparency.TRANSLUCENT; + int transferType = dataType; + + int[] nBits = { 8, 8, 8, 8 }; + ColorModel colorModel = new ComponentColorModelJAI(colorSpace, nBits, hasAlpha, + isAlphaPremultiplied, transparency, transferType); + + /* + * Do not use colorModel.createCompatibleSampleModel cause it creates a + * PixelInterleavedSampleModel and we need a BandedSampleModel so it matches how the data + * comes out of ArcSDE + */ + SampleModel sampleModel = new BandedSampleModel(dataType, sampleImageWidth, + sampleImageHeight, 4); + + its = new ImageTypeSpecifier(colorModel, sampleModel); + return its; + } + + private static ImageTypeSpecifier createRGBImageSpec(int sampleImageWidth, + int sampleImageHeight, final int dataType) { + + final ImageTypeSpecifier its; + ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); + boolean hasAlpha = false; + boolean isAlphaPremultiplied = false; + int transparency = Transparency.OPAQUE; + int transferType = dataType; + ColorModel colorModel = new ComponentColorModel(colorSpace, new int[] { 8, 8, 8 }, + hasAlpha, isAlphaPremultiplied, transparency, transferType); + + SampleModel sampleModel = new BandedSampleModel(dataType, sampleImageWidth, + sampleImageHeight, 3); + + its = new ImageTypeSpecifier(colorModel, sampleModel); + return its; + } + + private static ImageTypeSpecifier createGrayscaleImageSpec(int sampleImageWidth, + int sampleImageHeight, final int dataType, int bitsPerPixel) { + final ImageTypeSpecifier its; + ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); + boolean hasAlpha = false; + boolean isAlphaPremultiplied = false; + int transparency = Transparency.OPAQUE; + int transferType = dataType; + int[] nbits = { bitsPerPixel }; + ColorModel colorModel = new ComponentColorModelJAI(colorSpace, nbits, hasAlpha, + isAlphaPremultiplied, transparency, transferType); + + SampleModel sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, + sampleImageHeight); + its = new ImageTypeSpecifier(colorModel, sampleModel); + return its; + } + + private static ImageTypeSpecifier createOneBitImageSpec(final RasterDatasetInfo rasterInfo, + final int numberOfBands, int sampleImageWidth, int sampleImageHeight, + final int bitsPerSample, final int dataType) { + final ColorModel colorModel; + final SampleModel sampleModel; + if (numberOfBands != 1) { + throw new IllegalArgumentException(bitsPerSample + + "-Bit rasters are only supported for one band"); + } + int[] argb = new int[(int) Math.pow(2, bitsPerSample)]; + ColorUtilities.expand(new Color[] { Color.WHITE, Color.BLACK }, argb, 0, argb.length); + GridSampleDimension gridSampleDimension = rasterInfo.getGridSampleDimensions()[0]; + colorModel = gridSampleDimension.getColorModel(0, numberOfBands, dataType); + sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, sampleImageHeight); + + ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); + return its; + } + + private static ImageTypeSpecifier createColorMappedImageSpec(final IndexColorModel colorModel, + int sampleImageWidth, int sampleImageHeight) { + + final SampleModel sampleModel; + final ImageTypeSpecifier its; + LOGGER.fine("Found single-band colormapped raster, using its index color model"); + sampleModel = colorModel.createCompatibleSampleModel(sampleImageWidth, sampleImageHeight); + its = new ImageTypeSpecifier(colorModel, sampleModel); + return its; + + } + + /** + * Given a collection of {@link RasterQueryInfo} instances holding information about how a + * request fits for each individual raster composing a catalog, figure out where their resulting + * images fit into the overall mosaic that's gonna be the result of the request. + * + * @param rasterInfo + * @param resultEnvelope + * @param results + * @return + */ + public static Rectangle setMosaicLocations(final RasterDatasetInfo rasterInfo, + final GeneralEnvelope resultEnvelope, final List<RasterQueryInfo> results) { + final Rectangle mosaicDimension; + final MathTransform modelToRaster; + final MathTransform rasterToModel; + { + /* + * Of all the rasters that match the requested envelope, chose the one with the lowest + * resolution as the base to compute the final mosaic layout, so we avoid JAI upsamples, + * which are buggy and produce repeated patterns over the x axis instead of just scaling + * up the image. + */ + RasterQueryInfo dimensionChoice = findLowestResolution(results); + Long rasterId = dimensionChoice.getRasterId(); + int pyramidLevel = dimensionChoice.getPyramidLevel(); + int rasterIndex = rasterInfo.getRasterIndex(rasterId); + Rectangle levelRange = rasterInfo.getGridRange(rasterIndex, pyramidLevel); + GeneralEnvelope levelEnvelope = rasterInfo.getGridEnvelope(rasterIndex, pyramidLevel); + rasterToModel = createRasterToModel(levelRange, levelEnvelope); + try { + modelToRaster = rasterToModel.inverse(); + } catch (NoninvertibleTransformException e) { + throw new RuntimeException(e); + } + mosaicDimension = getTargetGridRange(modelToRaster, resultEnvelope); + } + + for (RasterQueryInfo rasterResultInfo : results) { + final GeneralEnvelope rasterResultEnvelope = rasterResultInfo.getResultEnvelope(); + + final Rectangle targetRasterGridRange; + targetRasterGridRange = getTargetGridRange(modelToRaster, rasterResultEnvelope); + + rasterResultInfo.setMosaicLocation(targetRasterGridRange); + } + + return mosaicDimension; + } + + private static RasterQueryInfo findLowestResolution(List<RasterQueryInfo> results) { + double[] prev = { Double.MIN_VALUE, Double.MIN_VALUE }; + RasterQueryInfo lowestResQuery = null; + + double[] curr; + for (RasterQueryInfo query : results) { + curr = query.getResolution(); + if (curr[0] > prev[0]) { + prev = curr; + lowestResQuery = query; + } + } + return lowestResQuery; + } + + /** + * Find out the raster ids and their pyramid levels in the raster dataset for the rasters whose + * envelope overlaps the requested one + * + * @param rasterInfo + * @param requestedEnvelope + * @param requestedDim + * @param overviewPolicy + * @return + */ + public static List<RasterQueryInfo> findMatchingRasters(final RasterDatasetInfo rasterInfo, + final GeneralEnvelope requestedEnvelope, final Rectangle requestedDim, + final OverviewPolicy overviewPolicy) { + + final int numRasters = rasterInfo.getNumRasters(); + List<RasterQueryInfo> matchingRasters = new ArrayList<RasterQueryInfo>(numRasters); + + int optimalPyramidLevel; + GeneralEnvelope gridEnvelope; + for (int rasterN = 0; rasterN < numRasters; rasterN++) { + optimalPyramidLevel = rasterInfo.getOptimalPyramidLevel(rasterN, overviewPolicy, + requestedEnvelope, requestedDim); + gridEnvelope = rasterInfo.getGridEnvelope(rasterN, optimalPyramidLevel); + final boolean edgesInclusive = true; + if (requestedEnvelope.intersects(gridEnvelope, edgesInclusive)) { + RasterQueryInfo match = new RasterQueryInfo(); + match.setRequestedEnvelope(requestedEnvelope); + match.setRequestedDim(requestedDim); + + match.setRasterId(rasterInfo.getRasterId(rasterN)); + match.setRasterIndex(rasterN); + match.setPyramidLevel(optimalPyramidLevel); + match.setResolution(rasterInfo.getResolution(rasterN, optimalPyramidLevel)); + matchingRasters.add(match); + } + } + return matchingRasters; + } + + public static void fitRequestToRaster(final GeneralEnvelope requestedEnvelope, + final RasterDatasetInfo rasterInfo, final RasterQueryInfo query) { + + final int rasterIndex = query.getRasterIndex(); + final int pyramidLevel = query.getPyramidLevel(); + final Rectangle rasterGridRange = rasterInfo.getGridRange(rasterIndex, pyramidLevel); + final GeneralEnvelope rasterEnvelope = rasterInfo + .getGridEnvelope(rasterIndex, pyramidLevel); + + double delta = requestedEnvelope.getMinimum(0) - rasterEnvelope.getMinimum(0); + double resX = rasterInfo.getResolution(rasterIndex, pyramidLevel)[0]; + int xMinPixel = (int) Math.floor(delta / resX); + + delta = requestedEnvelope.getMaximum(0) - rasterEnvelope.getMinimum(0); + int xMaxPixel = (int) Math.ceil(delta / resX); + + delta = rasterEnvelope.getMaximum(1) - requestedEnvelope.getMaximum(1); + double resY = rasterInfo.getResolution(rasterIndex, pyramidLevel)[1]; + // Distance in pixels from the top of the whole pyramid image to the top + // of our AOI. + // If we're off the top, this number will be negative. + int yMinPixel = (int) Math.floor(delta / resY); + + delta = rasterEnvelope.getMaximum(1) - requestedEnvelope.getMinimum(1); + int yMaxPixel = (int) Math.ceil(delta / resY); + + xMinPixel = Math.max(xMinPixel, rasterGridRange.x); + yMinPixel = Math.max(yMinPixel, rasterGridRange.y); + xMaxPixel = Math.min(xMaxPixel, rasterGridRange.x + rasterGridRange.width); + yMaxPixel = Math.min(yMaxPixel, rasterGridRange.y + rasterGridRange.height); + + final int widthPixel = xMaxPixel - xMinPixel; + final int heightPixel = yMaxPixel - yMinPixel; + + final double xMinGeo = rasterEnvelope.getMinimum(0) + resX * xMinPixel; + final double yMinGeo = rasterEnvelope.getMaximum(1) - resY * (yMinPixel + heightPixel); + final double widthGeo = resX * widthPixel; + final double heightGeo = resY * heightPixel; + + final Rectangle resultGridRange; + final GeneralEnvelope resultEnvelope; + + resultEnvelope = new GeneralEnvelope(new double[] { xMinGeo, yMinGeo }, new double[] { + xMinGeo + widthGeo, yMinGeo + heightGeo }); + resultEnvelope.setCoordinateReferenceSystem(rasterEnvelope.getCoordinateReferenceSystem()); + + resultGridRange = new Rectangle(xMinPixel, yMinPixel, widthPixel, heightPixel); + + final Rectangle matchingTiles; + final Rectangle levelTileRange; + final Rectangle tiledImageGridRange; + { + final Dimension tileSize = rasterInfo.getTileDimension(rasterIndex); + final int numTilesWide = rasterInfo.getNumTilesWide(rasterIndex, pyramidLevel); + final int numTilesHigh = rasterInfo.getNumTilesHigh(rasterIndex, pyramidLevel); + final Point tileOffset = rasterInfo.getTileOffset(rasterIndex, pyramidLevel); + levelTileRange = new Rectangle(0, 0, numTilesWide, numTilesHigh); + matchingTiles = findMatchingTiles(tileSize, numTilesWide, numTilesHigh, resultGridRange); + + int tiledImageMinX = (matchingTiles.x * tileSize.width); + int tiledImageMinY = (matchingTiles.y * tileSize.height); + + int tiledWidth = (matchingTiles.width * tileSize.width); + int tiledHeight = (matchingTiles.height * tileSize.height); + + tiledImageGridRange = new Rectangle(tiledImageMinX, tiledImageMinY, tiledWidth, + tiledHeight); + } + + /* + * What is the grid range inside the whole level grid range that fits into the matching + * tiles + */ + Rectangle resultDimensionInsideTiledImage; + resultDimensionInsideTiledImage = getResultDimensionForTileRange(tiledImageGridRange, + resultGridRange); + + query.setResultEnvelope(resultEnvelope); + query.setResultDimensionInsideTiledImage(resultDimensionInsideTiledImage); + query.setTiledImageSize(tiledImageGridRange); + query.setLevelTileRange(levelTileRange); + query.setMatchingTiles(matchingTiles); + } + + /** + * Returns a color model based on {@code colorMap} that's guaranteed to have at least one + * transparent pixel whose index can be used as no-data value for colormapped rasters, even if + * the returned IndexColorModel needs to be of a higher sample depth (ie, 16 instead of 8 bit) + * to satisfy that. + * + * @param colorMap + * the raster's native color map the returned one will be based on + * @return the same {@code colorMap} if it has a transparent pixel, another, possibly of a + * higher depth one if not, containing all the colors from {@code colorMap} and a newly + * allocated cell for the transparent pixel if necessary + */ + public static IndexColorModel ensureNoDataPixelIsAvailable(final IndexColorModel colorMap) { + int transparentPixel = colorMap.getTransparentPixel(); + if (transparentPixel > -1) { + return colorMap; + } + + final int transferType = colorMap.getTransferType(); + final int mapSize = colorMap.getMapSize(); + final int maxSize = 65536;// true for either transfer type + + if (mapSize == maxSize) { + LOGGER.fine("There's no room for a new transparent pixel, " + + "returning the original colorMap as is"); + return colorMap; + } + + /* + * The original map size is lower than the maximum allowed by a UShort color map, so expand + * the colormap by one and make that new entry transparent + */ + final int newMapSize = mapSize + 1; + final int[] argb = new int[newMapSize]; + colorMap.getRGBs(argb); + + // set the last entry as transparent + argb[newMapSize - 1] = ColorUtilities.getIntFromColor(0, 0, 0, 0); + + IndexColorModel targetColorModel; + final int significantBits; + final int newTransferType; + + { + if (DataBuffer.TYPE_BYTE == transferType && newMapSize <= 256) { + /* + * REVISIT: check if this needs to be promoted depending on whether I decide to + * treat 1 and 4 bit images as indexed with 1 and 4 significant bits respectively + */ + significantBits = colorMap.getPixelSize(); + newTransferType = DataBuffer.TYPE_BYTE; + } else { + // it's either being promoted or was already 16-bit + significantBits = 16; + newTransferType = DataBuffer.TYPE_USHORT; + } + } + + final int transparentPixelIndex = newMapSize - 1; + final boolean hasalpha = true; + final int startIndex = 0; + + targetColorModel = new IndexColorModel(significantBits, newMapSize, argb, startIndex, + hasalpha, transparentPixelIndex, newTransferType); + + return targetColorModel; + } + + /** + * For a color-mapped raster, the no-data value is set to the + * {@link IndexColorModel#getTransparentPixel() transparent pixel} + * + * @param colorMap + * @return the index in the colorMap that's the transparent pixel as is to be used as no-data + * value + */ + public static Number determineNoDataValue(IndexColorModel colorMap) { + int noDataPixel = colorMap.getTransparentPixel(); + if (-1 == noDataPixel) { + // there were no room for a transparent pixel, find out the closest match + noDataPixel = ColorUtilities.getTransparentPixel(colorMap); + } + return Integer.valueOf(noDataPixel); + } + + /** + * @param numBands + * number of bands in the raster dataset for the band whose nodata value is to be + * determined. Might be useful to treat special cases where some assumptions are made + * depending on the cell type and number of bands + * @param statsMin + * the minimum sample value for the band as reported by the band's statistics, or + * {@code NaN} + * @param statsMax + * the maximum sample value for the band as reported by the band's statistics, or + * {@code NaN} + * @param nativeCellType + * the band's native cell type + * @return + */ + public static Number determineNoDataValue(final int numBands, final double statsMin, + final double statsMax, final RasterCellType nativeCellType) { + + final Number nodata; + + if (nativeCellType == TYPE_32BIT_REAL) { + LOGGER.fine("no data value is Float.NaN"); + return Float.valueOf(Float.NaN); + } else if (nativeCellType == TYPE_64BIT_REAL) { + LOGGER.fine("no data value is Double.NaN"); + return Double.valueOf(Double.NaN); + } else if (nativeCellType == TYPE_1BIT) { + LOGGER.fine("1BIT images no-data value is set to 2," + + " regardless of the raster statistics"); + return Double.valueOf(2); + } else if (nativeCellType == TYPE_4BIT) { + LOGGER.fine("4BIT images no-data value is set to 16," + + " regardless of the raster statistics"); + return Double.valueOf(16); + } else if (!isGeoPhysics(numBands, nativeCellType)) { + LOGGER.fine("3 or 4 band, 8 bit unsigned image, assumed to be " + + "RGB or RGBA respectively and nodata value hardcoded to 255"); + return (Number) nativeCellType.getSampleValueRange().getMaxValue(); + } + + final NumberRange<?> sampleValueRange = nativeCellType.getSampleValueRange(); + + final double minimumSample = sampleValueRange.getMinimum(true); + final double maximumSample = sampleValueRange.getMaximum(true); + + double lower; + double greater; + if (Double.isNaN(statsMin) || Double.isNaN(statsMax)) { + lower = Math.ceil(minimumSample - 1); + greater = Math.floor(maximumSample + 1); + } else { + lower = Math.ceil(statsMin - 1); + greater = Math.floor(statsMax + 1); + } + + final boolean isUnsigned = minimumSample == 0; + + if (sampleValueRange.contains((Number) Double.valueOf(lower))) { + // lower is ok + nodata = lower; + } else if (sampleValueRange.contains((Number) Double.valueOf(greater))) { + // upper is ok + nodata = greater; + } else if (isUnsigned) { + // need to set no-data to the higher value, floor is zero + nodata = greater; + // if (cellType == TYPE_1BIT || cellType == TYPE_4BIT) { + // nodata = greater; + // } else { + // // best guess without promoting. We don't actually want to promote a raster that is + // // non + // // colormapped and either has no statistics or it's range is full to preserve the + // // cases + // // were it may affect badly the visualization (for example, a 3 band 8bit raster + // // promoted to 3 band 16bit is gonna look almost black + // nodata = maximumSample; + // } + } else { + // no-data as the lower value is ok, floor is non zero (the celltype is signed) + nodata = lower; + } + + return nodata; + } + + public static boolean isGeoPhysics(final int numBands, final RasterCellType nativeCellType) { + boolean geophysics = true; + if (nativeCellType == TYPE_8BIT_U && (numBands == 3 || numBands == 4)) { + geophysics = false; + } + return geophysics; + } + + public static RasterCellType determineTargetCellType(final RasterCellType nativeCellType, + final List<Number> noDataValues) { + + if (TYPE_32BIT_REAL == nativeCellType || TYPE_64BIT_REAL == nativeCellType) { + // no data value is NaN, so no need to promote. For other types NaN is not available + for (Number nodata : noDataValues) { + if (!Double.isNaN(nodata.doubleValue())) { + throw new IllegalArgumentException("no data values for float and " + + "double cell types shall be NaN: " + nodata); + } + } + return nativeCellType; + } + + // find a cell type that's deep enough for all the bands in the given raster + double noDataMin = Double.POSITIVE_INFINITY, noDataMax = Double.NEGATIVE_INFINITY; + { + for (Number noData : noDataValues) { + noDataMin = Math.min(noDataMin, noData.doubleValue()); + noDataMax = Math.max(noDataMax, noData.doubleValue()); + } + } + final NumberRange<Double> sampleValueRange; + sampleValueRange = nativeCellType.getSampleValueRange().castTo(Double.class); + + final RasterCellType targetCellType; + + if (sampleValueRange.contains((Number) Double.valueOf(noDataMin)) + && sampleValueRange.contains((Number) Double.valueOf(noDataMax))) { + /* + * The native cell type can hold the no-data values for all bands in the raster + */ + targetCellType = nativeCellType; + } else { + targetCellType = promote(nativeCellType); + } + return targetCellType; + } + + private static RasterCellType promote(final RasterCellType nativeCellType) { + switch (nativeCellType) { + case TYPE_1BIT: + case TYPE_4BIT: + return TYPE_8BIT_U; + case TYPE_8BIT_U: + return TYPE_16BIT_U; + case TYPE_8BIT_S: + return TYPE_16BIT_S; + case TYPE_16BIT_U: + return TYPE_32BIT_U; + case TYPE_16BIT_S: + return TYPE_32BIT_S; + case TYPE_32BIT_S: + case TYPE_32BIT_REAL: + case TYPE_32BIT_U: + return TYPE_64BIT_REAL; + default: + throw new IllegalArgumentException( + "Can't promote a raster of type 64-bit-real, there's " + + "no higher pixel depth than that!"); + } + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/BitmaskToNoDataConverter.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/BitmaskToNoDataConverter.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/BitmaskToNoDataConverter.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/BitmaskToNoDataConverter.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,237 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.io; + +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_8BIT_U; + +import java.awt.Dimension; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.geotools.arcsde.raster.info.RasterCellType; +import org.geotools.arcsde.raster.info.RasterDatasetInfo; + +/** + * A helper class to set nodata values directly onto the arcsde tile's returned {@code byte[]} + * + * @author Gabriel Roldan + * @since 2.5.6 + * @version $Id$ + * + */ +class BitmaskToNoDataConverter { + + public static final BitmaskToNoDataConverter NO_ACTION_CONVERTER = new BitmaskToNoDataConverter( + 0, 0, null) { + + @Override + public void setNoData(Long bandId, byte[] tileData, byte[] bitMaskData) { + // no action + } + + @Override + public void setAll(Long bandId, byte[] tileData) { + // no action + } + + @Override + public void setNoData(Long bandId, int sampleN, byte[] tileData) { + // no action + } + + }; + + protected final int pixelsPerTile; + + protected final Map<Long, byte[]> byBandIdNoDataValues; + + protected final int bitsPerSample; + + /** + * + * @param pixelsPerTile + * @param bitsPerSample + * @param byBandIdNoDataValues + */ + private BitmaskToNoDataConverter(final int pixelsPerTile, final int bitsPerSample, + final Map<Long, byte[]> byBandIdNoDataValues) { + + this.pixelsPerTile = pixelsPerTile; + this.bitsPerSample = bitsPerSample; + this.byBandIdNoDataValues = byBandIdNoDataValues; + + } + + /** + * Creates a "nodata setter" for the given raster determined by the raster dataset and the + * raster index inside the dataset + * + * @param rasterInfo + * @param rasterIndex + * @return + */ + public static BitmaskToNoDataConverter getInstance(final RasterDatasetInfo rasterInfo, + final long rasterId) { + + final int rasterIndex = rasterInfo.getRasterIndex(rasterId); + final int numBands = rasterInfo.getNumBands(); + final RasterCellType targetType = rasterInfo.getTargetCellType(rasterIndex); + + Map<Long, byte[]> byBandIdNoDataValues = new HashMap<Long, byte[]>(); + + Dimension tileDimension = rasterInfo.getTileDimension(rasterIndex); + final int samplesPerTile = tileDimension.width * tileDimension.height; + + for (int bandN = 0; bandN < numBands; bandN++) { + long bandId = rasterInfo.getBand(rasterIndex, bandN).getBandId(); + Number noDataValue = rasterInfo.getNoDataValue(rasterIndex, bandN); + byte[] noDataValueBytes = toBytes(noDataValue, targetType); + byBandIdNoDataValues.put(Long.valueOf(bandId), noDataValueBytes); + } + + final int bitsPerSample = targetType.getBitsPerSample(); + BitmaskToNoDataConverter noDataSetter; + if (targetType == TYPE_8BIT_U) { + noDataSetter = new Unsigned8bitConverter(samplesPerTile, bitsPerSample, + byBandIdNoDataValues); + } else { + noDataSetter = new BitmaskToNoDataConverter(samplesPerTile, bitsPerSample, + byBandIdNoDataValues); + } + + return noDataSetter; + } + + static byte[] toBytes(final Number noDataValue, final RasterCellType targetType) { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream writer = new DataOutputStream(out); + + try { + switch (targetType) { + case TYPE_16BIT_S: + writer.writeShort(noDataValue.intValue()); + break; + case TYPE_16BIT_U: + writer.writeShort(noDataValue.intValue()); + break; + case TYPE_32BIT_REAL: + writer.writeFloat(noDataValue.floatValue()); + break; + case TYPE_32BIT_S: + writer.writeInt(noDataValue.intValue()); + break; + case TYPE_32BIT_U: + writer.writeInt(noDataValue.intValue()); + break; + case TYPE_64BIT_REAL: + writer.writeDouble(noDataValue.doubleValue()); + break; + case TYPE_8BIT_S: + writer.writeByte(noDataValue.byteValue()); + break; + case TYPE_8BIT_U: + writer.writeByte(noDataValue.intValue()); + break; + default: + throw new UnsupportedOperationException( + "No no-data converter exists for sample type " + targetType); + } + } catch (IOException e) { + throw new RuntimeException("Can't happen!", e); + } + + byte[] no_data_bytes = out.toByteArray(); + return no_data_bytes; + } + + /** + * Returns whether the sample N in the bitmask byte array is marked as a no-data pixel + */ + public final boolean isNoData(int sampleN, byte[] bitmaskData) { + boolean isNoData = ((bitmaskData[sampleN / 8] >> (7 - (sampleN % 8))) & 0x01) == 0x00; + return isNoData; + } + + /** + * Sets all the samples of {@code tileData} marked as no-data pixel in {@code bitmaskData} to + * the no-data value for band {@code bandId} + */ + public void setNoData(final Long bandId, final byte[] tileData, final byte[] bitmaskData) { + for (int sampleN = 0; sampleN < pixelsPerTile; sampleN++) { + if (isNoData(sampleN, bitmaskData)) { + setNoData(bandId, sampleN, tileData); + } + } + } + + /** + * Sets all the samples in {@code tileData} to the no-data value for the band {@code bandId} + * <p> + * Default implementation is to call {@link #setNoData(Long, int, byte[])} as many times as + * number of samples in a tile. Subclasses may override to optimize. + * </p> + */ + public void setAll(final Long bandId, final byte[] tileData) { + for (int sampleN = 0; sampleN < pixelsPerTile; sampleN++) { + setNoData(bandId, sampleN, tileData); + } + } + + /** + * Sets the sample N for the band {@code bandId} on {@code tileData} to the no-data value + */ + public void setNoData(final Long bandId, final int sampleN, final byte[] tileData) { + byte[] noData = byBandIdNoDataValues.get(bandId); + int pixArrayOffset = (sampleN * bitsPerSample) / 8; + System.arraycopy(noData, 0, tileData, pixArrayOffset, noData.length); + } + + /** + * A subclass that provides some optimization for the case where the target cell type is + * {@link RasterCellType#TYPE_8BIT_U} + */ + static final class Unsigned8bitConverter extends BitmaskToNoDataConverter { + + public Unsigned8bitConverter(final int samplesPerTile, final int bitsPerSample, + final Map<Long, byte[]> byBandIdNoDataValues) { + super(samplesPerTile, bitsPerSample, byBandIdNoDataValues); + } + + /** + * Overrides to use the faster {@link Arrays#fill(byte[], byte)} method rather than calling + * {@link #setNoData(Long, int, byte[])} {@code samplesPerTile} times + */ + @Override + public void setAll(Long bandId, byte[] tileData) { + byte noDataValue = byBandIdNoDataValues.get(bandId)[0]; + Arrays.fill(tileData, noDataValue); + } + + @Override + public void setNoData(Long bandId, int sampleN, byte[] tileData) { + byte noDataValue = byBandIdNoDataValues.get(bandId)[0]; + tileData[sampleN] = noDataValue; + } + } + +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/DefaultTiledRasterReader.java (from rev 34312, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/DefaultTiledRasterReader.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/DefaultTiledRasterReader.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/DefaultTiledRasterReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,221 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.io; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.image.ColorModel; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.io.IOException; +import java.util.logging.Logger; + +import javax.imageio.ImageReader; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.stream.ImageInputStream; +import javax.media.jai.ImageLayout; + +import org.geotools.arcsde.raster.info.RasterDatasetInfo; +import org.geotools.arcsde.raster.jai.ArcSDEImageReader; +import org.geotools.arcsde.session.ISessionPool; +import org.geotools.util.logging.Logging; + +import com.esri.sde.sdk.client.SeQuery; + +/** + * The default implementation for {@link TiledRasterReader}. + * <p> + * This implementation holds a connection and an open {@link SeQuery query} until the reader is + * exhausted or {@link #dispose()} is called. + * </p> + * + * @author Gabriel Roldan + * @version $Id$ + * @since 2.5.7 + */ +class DefaultTiledRasterReader implements TiledRasterReader { + + private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + private RasterDatasetInfo rasterInfo; + + private final ISessionPool sessionPool; + + /** + * Creates an {@link DefaultTiledRasterReader} that uses the given connection to fetch raster + * data for the given {@link RasterDatasetInfo rasterInfo}. + * <p> + * </p> + * + * @param sessionPool + * where to grab sessions from to query the rasters described by {@code rasterInfo} + * @param rasterInfo + * @throws IOException + */ + public DefaultTiledRasterReader(final ISessionPool sessionPool, + final RasterDatasetInfo rasterInfo) throws IOException { + this.sessionPool = sessionPool; + this.rasterInfo = rasterInfo; + } + + /** + * @see org.geotools.arcsde.raster.io.TiledRasterReader#read + */ + public RenderedImage read(final long rasterId, final int pyramidLevel, final Rectangle tileRange) + throws IOException { + final RenderedImage rasterImage; + + rasterImage = getRasterMatchingTileRange(rasterId, pyramidLevel, tileRange); + + return rasterImage; + } + + private RenderedImage getRasterMatchingTileRange(final long rasterId, int pyramidLevel, + final Rectangle matchingTiles) throws IOException { + + final TileReader tileReader; + tileReader = TileReaderFactory.getInstance(sessionPool, rasterInfo, rasterId, pyramidLevel, + matchingTiles); + + // covers an area of full tiles + final RenderedImage fullTilesRaster; + + /* + * Create the tiled raster covering the full area of the matching tiles + */ + + fullTilesRaster = createTiledRaster(tileReader, matchingTiles, rasterId); + + return fullTilesRaster; + } + + /** + * @param tileReader + * @param matchingTiles + * @param rasterId + * @return + * @throws IOException + */ + private RenderedImage createTiledRaster(final TileReader tileReader, + final Rectangle matchingTiles, final long rasterId) throws IOException { + // Prepare temporary colorModel and sample model, needed to build the final + // ArcSDEPyramidLevel level; + final Dimension tiledImageSize; + final ColorModel colorModel; + final SampleModel sampleModel; + { + final int tiledImageWidth = tileReader.getTilesWide() * tileReader.getTileWidth(); + final int tiledImageHeight = tileReader.getTilesHigh() * tileReader.getTileHeight(); + tiledImageSize = new Dimension(tiledImageWidth, tiledImageHeight); + + final ImageTypeSpecifier fullImageSpec = rasterInfo.getRenderedImageSpec(rasterId); + colorModel = fullImageSpec.getColorModel(); + sampleModel = fullImageSpec.getSampleModel(tiledImageWidth, tiledImageHeight); + } + + final int tileWidth = tileReader.getTileWidth(); + final int tileHeight = tileReader.getTileHeight(); + + // building the final image layout + final ImageLayout imageLayout; + { + // the value for the resulting image.getMinX() and image.getMinY() reflecting its + // position in the whole image at the pyramid level + int minX = (matchingTiles.x * tileWidth); + int minY = (matchingTiles.y * tileHeight); + + int width = tiledImageSize.width; + int height = tiledImageSize.height; + + int tileGridXOffset = minX; + int tileGridYOffset = minY; + + // imageLayout = new ImageLayout(minX, minY, width, height, tileGridXOffset, + // tileGridYOffset, tileWidth, tileHeight, sampleModel, colorModel); + imageLayout = new ImageLayout(0, 0, width, height, 0, 0, tileWidth, tileHeight, + sampleModel, colorModel); + } + + // Finally, build the image input stream + final ImageInputStream raw; + final ImageReader readerInstance; + { + final long[] imageOffsets = new long[] { 0 }; + final Dimension[] imageDimensions = new Dimension[] { tiledImageSize }; + + final ImageTypeSpecifier its = new ImageTypeSpecifier(colorModel, sampleModel); + // + // final ImageInputStream tiledImageInputStream; + // tiledImageInputStream = new ArcSDETiledImageInputStream(tileReader); + // raw = new RawImageInputStream(tiledImageInputStream, its, imageOffsets, + // imageDimensions); + // ImageReaderSpi imageIOSPI = new RawImageReaderSpi(); + // readerInstance = imageIOSPI.createReaderInstance(); + + // final InputStream is = new RasterInputStream(tileReader); + // final ImageInputStream tiledImageInputStream; + // tiledImageInputStream = ImageIO.createImageInputStream(is); + // + // raw = new RawImageInputStream(tiledImageInputStream, its, imageOffsets, + // imageDimensions); + // ImageReaderSpi imageIOSPI = new RawImageReaderSpi(); + // readerInstance = imageIOSPI.createReaderInstance(); + + ArcSDEImageReader reader = new ArcSDEImageReader(its); + reader.setInput(tileReader, true, true); + + RenderedImage image = reader.readAsRenderedImage(0, null); + // image.getData(); + return image; + } + + // First operator: read the image + // final RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, imageLayout); + // + // ParameterBlock pb = new ParameterBlock(); + // pb.add(raw);// Input + // /* + // * image index, always 0 since we're already fetching the required pyramid level + // */ + // pb.add(Integer.valueOf(0)); // Image index + // pb.add(Boolean.FALSE); // Read metadata + // pb.add(Boolean.FALSE);// Read thumbnails + // pb.add(Boolean.FALSE);// Verify input + // pb.add(null);// Listeners + // pb.add(null);// Locale + // final ImageReadParam rParam = new ImageReadParam(); + // pb.add(rParam);// ReadParam + // pb.add(readerInstance);// Reader + // + // RenderedImage image = JAI.create("ImageRead", pb, hints); + // // image.getData(); + // // // translate + // // int minX = (matchingTiles.x * tileWidth); + // // int minY = (matchingTiles.y * tileHeight); + // // pb = new ParameterBlock(); + // // pb.addSource(image); + // // pb.add(Float.valueOf(minX)); + // // pb.add(Float.valueOf(minY)); + // // pb.add(null); + // // + // // image = JAI.create("translate", pb); + // + // return image; + + } + +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/NativeTileReader.java (from rev 34302, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/NativeTileReader.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/NativeTileReader.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/NativeTileReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,530 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.io; + +import java.awt.Dimension; +import java.awt.Rectangle; +import java.io.EOFException; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.geotools.arcsde.ArcSdeException; +import org.geotools.arcsde.raster.info.RasterCellType; +import org.geotools.arcsde.raster.info.RasterDatasetInfo; +import org.geotools.arcsde.session.Command; +import org.geotools.arcsde.session.ISession; +import org.geotools.arcsde.session.ISessionPool; +import org.geotools.arcsde.session.UnavailableConnectionException; +import org.geotools.util.logging.Logging; + +import com.esri.sde.sdk.client.SeConnection; +import com.esri.sde.sdk.client.SeException; +import com.esri.sde.sdk.client.SeQuery; +import com.esri.sde.sdk.client.SeRaster; +import com.esri.sde.sdk.client.SeRasterConstraint; +import com.esri.sde.sdk.client.SeRasterTile; +import com.esri.sde.sdk.client.SeRow; +import com.esri.sde.sdk.client.SeSqlConstruct; +import com.esri.sde.sdk.client.SeStreamOp; + +/** + * Offers an iterator like interface to fetch ArcSDE raster tiles. + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/gce/NativeTileReader.java $ + */ +@SuppressWarnings( { "nls" }) +final class NativeTileReader implements TileReader { + + private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + private final RasterDatasetInfo rasterInfo; + + private final long rasterId; + + private final int pyramidLevel; + + private final Rectangle requestedTiles; + + private final ISessionPool sessionPool; + + private ISession session; + + private TileFetchCommand tileFetchCommand; + + /** + * {@link SeStreamOp} held to be closed at {@link #dispose()} + * + * @see #execute() + */ + private SeQuery preparedQuery; + + private TileInfo nextTile; + + private boolean started; + + private final int bitmaskDataLength; + + private final BitmaskToNoDataConverter noData; + + private final int pixelsPerTile; + + private final int tileDataLength; + + private int bitsPerSample; + + /** + * Command to fetch an {@link SeRasterTile tile} + */ + private static class TileFetchCommand extends Command<TileInfo> { + + private final SeRow row; + + private final int tileDataLength; + + public TileFetchCommand(final SeRow row, final int tileDataLength) { + this.row = row; + this.tileDataLength = tileDataLength; + } + + @Override + public TileInfo execute(ISession session, SeConnection connection) throws SeException, + IOException { + SeRasterTile tile = row.getRasterTile(); + final TileInfo tileInfo; + if (tile == null) { + tileInfo = null; + } else { + byte[] bitMaskData = tile.getBitMaskData(); + int numPixelsRead = tile.getNumPixels(); + byte[] tileData; + if (numPixelsRead == 0) { + tileData = new byte[tileDataLength]; + } else { + tileData = tile.getPixelData(); + } + long bandId = tile.getBandId().longValue(); + int colIndex = tile.getColumnIndex(); + int rowIndex = tile.getRowIndex(); + tileInfo = new TileInfo(bandId, colIndex, rowIndex, numPixelsRead, tileData, + bitMaskData); + } + return tileInfo; + } + } + + /** + * @see DefaultTiledRasterReader#nextRaster() + */ + private static class QueryRasterCommand extends Command<Void> { + + private SeQuery preparedQuery; + + private SeRow row; + + private final SeRasterConstraint rasterConstraint; + + private final String rasterColumn; + + private final String rasterTable; + + private final long rasterId; + + /** + * + * @param rConstraint + * indicates which bands, pyramid level and grid envelope to query + * @param rasterTable + * indicates which raster table to query + * @param rasterColumn + * indicates what raster column in the raster table to query + * @param rasterId + * indicates which raster in the raster catalog to query + */ + public QueryRasterCommand(final SeRasterConstraint rConstraint, final String rasterTable, + final String rasterColumn, final long rasterId) { + this.rasterConstraint = rConstraint; + this.rasterTable = rasterTable; + this.rasterColumn = rasterColumn; + this.rasterId = rasterId; + } + + @Override + public Void execute(ISession session, SeConnection connection) throws SeException, + IOException { + + final SeSqlConstruct sqlConstruct = new SeSqlConstruct(rasterTable); + /* + * Filter by the given raster id + */ + final String rasterIdFilter = rasterColumn + " = " + rasterId; + sqlConstruct.setWhere(rasterIdFilter); + + final String[] rasterColumns = { rasterColumn }; + preparedQuery = new SeQuery(connection, rasterColumns, sqlConstruct); + preparedQuery.prepareQuery(); + preparedQuery.execute(); + + this.row = preparedQuery.fetch(); + if (row == null) { + return null; + } + + preparedQuery.queryRasterTile(rasterConstraint); + + return null; + } + + public SeQuery getPreparedQuery() { + return preparedQuery; + } + + public SeRow getSeRow() { + return row; + } + } + + /** + * Creates a {@link TileReader} that reads tiles out of ArcSDE for the given {@code + * preparedQuery} and {@code SeRow} using the given {@code session}, in the native raster + * format. + * <p> + * As for any object that receives a {@link ISession session}, the same rule applies: this class + * is not responsible of {@link ISession#dispose() disposing} the session, but the calling code + * is. + * </p> + * + * @param preparedQuery + * the query stream to close when done + * @param row + * @param imageDimensions + * the image size, x and y are the offsets, width and height the actual width and + * height, used to ignore incomming pixel data as appropriate to fit the image + * dimensions + * @param bitsPerSample + * @param numberOfBands2 + * @param requestedTiles + */ + NativeTileReader(final ISessionPool sessionPool, final RasterDatasetInfo rasterInfo, + final long rasterId, final int pyramidLevel, final Rectangle requestedTiles, + final BitmaskToNoDataConverter noData) { + this.sessionPool = sessionPool; + this.rasterInfo = rasterInfo; + this.rasterId = rasterId; + this.pyramidLevel = pyramidLevel; + this.requestedTiles = requestedTiles; + this.noData = noData; + + final Dimension tileSize = rasterInfo.getTileDimension(rasterId); + + this.pixelsPerTile = tileSize.width * tileSize.height; + + final RasterCellType nativeCellType = rasterInfo.getNativeCellType(); + this.bitsPerSample = nativeCellType.getBitsPerSample(); + this.tileDataLength = (int) Math + .ceil(((double) pixelsPerTile * (double) bitsPerSample) / 8D); + this.bitmaskDataLength = (int) Math.ceil(pixelsPerTile / 8D); + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getBitsPerSample() + */ + public int getBitsPerSample() { + return bitsPerSample; + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getPixelsPerTile() + */ + public int getPixelsPerTile() { + return pixelsPerTile; + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getNumberOfBands() + */ + public int getNumberOfBands() { + return rasterInfo.getNumBands(); + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getTileWidth() + */ + public int getTileWidth() { + return rasterInfo.getTileWidth(rasterId); + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getTileHeight() + */ + public int getTileHeight() { + return rasterInfo.getTileHeight(rasterId); + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getTilesWide() + */ + public int getTilesWide() { + return requestedTiles.width; + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getTilesHigh() + */ + public int getTilesHigh() { + return requestedTiles.height; + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#getBytesPerTile() + */ + public int getBytesPerTile() { + return tileDataLength; + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#hasNext() + */ + public boolean hasNext() throws IOException { + if (!started) { + execute(); + try { + nextTile = session.issue(tileFetchCommand); + } catch (IOException e) { + dispose(); + throw e; + } catch (RuntimeException e) { + dispose(); + throw e; + } + started = true; + if (nextTile == null) { + dispose(); + LOGGER.fine("No tiles to fetch at all, releasing connection"); + } + } + return nextTile != null; + } + + /** + * Creates and executes the {@link SeQuery} that's used to fetch the required tiles from the + * specified raster, and stores (as member variables) the {@link SeRow} to fetch the tiles from + * and the {@link SeQuery} to be closed at the TileReader's disposal + * + * @throws IOException + */ + private void execute() throws IOException { + + final int rasterIndex = rasterInfo.getRasterIndex(rasterId); + final int tileWidth = rasterInfo.getTileWidth(rasterId); + final int tileHeight = rasterInfo.getTileHeight(rasterId); + + /* + * Create the raster constraint to query the needed tiles out of the specified raster at the + * given pyramid level + */ + final SeRasterConstraint rConstraint; + try { + final int numberOfBands; + numberOfBands = rasterInfo.getNumBands(); + + int[] bandsToQuery = new int[numberOfBands]; + for (int bandN = 1; bandN <= numberOfBands; bandN++) { + bandsToQuery[bandN - 1] = bandN; + } + + int minTileX = requestedTiles.x; + int minTileY = requestedTiles.y; + int maxTileX = minTileX + requestedTiles.width - 1; + int maxTileY = minTileY + requestedTiles.height - 1; + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Requesting tiles [x=" + minTileX + "-" + maxTileX + ", y=" + minTileY + + "-" + maxTileY + "] from tile range [x=0-" + + (rasterInfo.getNumTilesWide(rasterIndex, pyramidLevel) - 1) + ", y=0-" + + (rasterInfo.getNumTilesHigh(rasterIndex, pyramidLevel) - 1) + "]"); + } + // SDEPoint tileOrigin = rAttr.getTileOrigin(); + + if (LOGGER.isLoggable(Level.FINE)) { + Rectangle tiledImageSize = new Rectangle(0, 0, tileWidth * requestedTiles.width, + tileHeight * requestedTiles.height); + + LOGGER.fine("Tiled image size: " + tiledImageSize); + } + + final int interleaveType = SeRaster.SE_RASTER_INTERLEAVE_BIP; + + rConstraint = new SeRasterConstraint(); + rConstraint.setBands(bandsToQuery); + rConstraint.setLevel(pyramidLevel); + rConstraint.setEnvelope(minTileX, minTileY, maxTileX, maxTileY); + rConstraint.setInterleave(interleaveType); + } catch (SeException se) { + throw new ArcSdeException(se); + } + + /* + * Obtain the ISession this tile reader will work with until exhausted + */ + try { + // lets share connections as we're going to do read only operations + final boolean transactional = false; + this.session = sessionPool.getSession(transactional); + // System.err.println("----> Using " + session + " to read raster #" + rasterId + // + " on Thread " + Thread.currentThread().getName() + ". Tile set: " + // + requestedTiles); + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("Using " + session + " to read raster #" + rasterId + " on Thread " + + Thread.currentThread().getName() + ". Tile set: " + requestedTiles); + } + } catch (UnavailableConnectionException e) { + // really bad luck.. + throw new RuntimeException(e); + } + + final String rasterTable = rasterInfo.getRasterTable(); + final String rasterColumn = rasterInfo.getRasterColumns()[0]; + final QueryRasterCommand queryCommand = new QueryRasterCommand(rConstraint, rasterTable, + rasterColumn, rasterId); + + session.issue(queryCommand); + + final SeRow row = queryCommand.getSeRow(); + + this.tileFetchCommand = new TileFetchCommand(row, tileDataLength); + this.preparedQuery = queryCommand.getPreparedQuery(); + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#next() + */ + public TileInfo next() throws IOException { + final TileInfo tile; + final boolean hasNext = hasNext(); + if (hasNext) { + tile = nextTile(); + } else { + throw new IllegalStateException("There're no more tiles to fetch"); + } + + try { + final byte[] bitMaskData = tile.getBitmaskData(); + + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.finest(" >> Fetching " + tile + " - bitmask: " + bitMaskData.length); + } + + assert bitMaskData.length == 0 ? true : bitmaskDataLength == bitMaskData.length; + + final int numPixels = tile.getNumPixelsRead(); + final byte[] rawTileData = tile.getTileData(); + + final Long bandId = tile.getBandId(); + + if (0 == numPixels) { + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("tile contains no pixel data, skipping: " + tile); + } + noData.setAll(bandId, rawTileData); + } else if (pixelsPerTile == numPixels) { + + if (bitMaskData.length > 0) { + noData.setNoData(bandId, rawTileData, bitMaskData); + } + + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.finest("returning " + numPixels + " pixels data packaged into " + + tileDataLength + " bytes for tile [" + tile.getColumnIndex() + "," + + tile.getRowIndex() + "]"); + } + } else { + throw new IllegalStateException("Expected pixels per tile == " + pixelsPerTile + + " but got " + numPixels + ": " + tile); + } + + // return new TileInfo(bandId, bitMaskData, numPixels); + return tile; + + } catch (RuntimeException e) { + dispose(); + throw e; + } + } + + private TileInfo nextTile() throws IOException { + if (nextTile == null) { + dispose(); + throw new EOFException("No more tiles to read"); + } + TileInfo curr = nextTile; + + try { + nextTile = session.issue(tileFetchCommand); + } catch (IOException e) { + dispose(); + throw e; + } catch (RuntimeException e) { + dispose(); + throw e; + } + if (nextTile == null) { + dispose(); + LOGGER.finer("There're no more tiles to fetch"); + } + + return curr; + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#dispose() + */ + public void dispose() { + if (session != null) { + // System.err.println("TileReader disposing " + session + " on Thread " + // + Thread.currentThread().getName()); + if (LOGGER.isLoggable(Level.FINER)) { + LOGGER.finer("TileReader disposing " + session + " on Thread " + + Thread.currentThread().getName()); + } + try { + session.close(this.preparedQuery); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Closing tile reader's prepared Query", e); + } + session.dispose(); + session = null; + } + } + + /** + * Disposes as to make sure the {@link ISession session} is returned to the pool even if a + * failing or non careful client left this object hanging around + * + * @see #dispose() + * @see java.lang.Object#finalize() + */ + @Override + protected void finalize() { + dispose(); + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/PromotingTileReader.java (from rev 34312, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/PromotingTileReader.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/PromotingTileReader.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/PromotingTileReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,205 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.io; + +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_16BIT_S; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_16BIT_U; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_1BIT; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_32BIT_S; +import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_8BIT_U; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.geotools.arcsde.raster.info.RasterCellType; +import org.geotools.util.logging.Logging; + +/** + * A {@link TileReader} decorator + * + * @author Gabriel Roldan + * @version $Id$ + * @since 2.5.6 + */ +final class PromotingTileReader implements TileReader { + + private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); + + private final TileReader nativeReader; + + private final RasterCellType targetType; + + private final SampleDepthPromoter promoter; + + private final BitmaskToNoDataConverter noData; + + public PromotingTileReader(final TileReader nativeTileReader, final RasterCellType sourceType, + final RasterCellType targetType, final BitmaskToNoDataConverter noData) { + + this.nativeReader = nativeTileReader; + this.targetType = targetType; + this.noData = noData; + this.promoter = SampleDepthPromoter.createFor(sourceType, targetType); + LOGGER.fine("Using sample depth promoting tile reader, from " + sourceType + " to " + + targetType); + } + + public int getBitsPerSample() { + return targetType.getBitsPerSample(); + } + + public int getBytesPerTile() { + double pixelsPerTile = getPixelsPerTile(); + double bitsPerSample = getBitsPerSample(); + int bytesPerTile = (int) Math.floor((pixelsPerTile * bitsPerSample) / 8D); + return bytesPerTile; + } + + public int getNumberOfBands() { + return nativeReader.getNumberOfBands(); + } + + public int getPixelsPerTile() { + return nativeReader.getPixelsPerTile(); + } + + public int getTileHeight() { + return nativeReader.getTileHeight(); + } + + public int getTileWidth() { + return nativeReader.getTileWidth(); + } + + public int getTilesHigh() { + return nativeReader.getTilesHigh(); + } + + public int getTilesWide() { + return nativeReader.getTilesWide(); + } + + public boolean hasNext() throws IOException { + return nativeReader.hasNext(); + } + + public TileInfo next() throws IOException { + final TileInfo tileInfo = nativeReader.next(); + final byte[] nativeTileData = tileInfo.getTileData(); + final byte[] tileData = new byte[getBytesPerTile()]; + try { + final byte[] bitmaskData = tileInfo.getBitmaskData(); + final boolean hasNoDataPixels = bitmaskData.length > 0; + final Long bandId = tileInfo.getBandId(); + + final int numPixelsRead = tileInfo.getNumPixelsRead(); + if (numPixelsRead == 0) { + noData.setAll(bandId, tileData); + } else { + final int numSamples = getPixelsPerTile(); + assert numPixelsRead == numSamples; + + for (int sampleN = 0; sampleN < numSamples; sampleN++) { + if (hasNoDataPixels && noData.isNoData(sampleN, bitmaskData)) { + noData.setNoData(bandId, sampleN, tileData); + } else { + promoter.promote(sampleN, nativeTileData, tileData); + } + } + } + + } catch (RuntimeException e) { + dispose(); + throw e; + } + + TileInfo promotedTileInfo = new TileInfo(tileInfo.getBandId(), tileInfo.getColumnIndex(), + tileInfo.getRowIndex(), tileInfo.getNumPixelsRead(), tileData, tileInfo + .getBitmaskData()); + return promotedTileInfo; + } + + /** + * + * @author Gabriel Roldan + */ + private static abstract class SampleDepthPromoter { + + public abstract void promote(int sampleN, byte[] nativeTileData, byte[] tileData); + + public static SampleDepthPromoter createFor(final RasterCellType source, + final RasterCellType target) { + + if (source == TYPE_1BIT && target == RasterCellType.TYPE_8BIT_U) { + return new OneBitToUchar(); + } else if (source == TYPE_8BIT_U && target == TYPE_16BIT_U) { + return new UcharToUshort(); + } else if (source == TYPE_16BIT_S && target == TYPE_32BIT_S) { + return new ShortToInt(); + } + + UnsupportedOperationException exception = new UnsupportedOperationException( + "Promoting from " + source + " to " + target + " not yet implemented"); + LOGGER.log(Level.WARNING, "Can't promote", exception); + throw exception; + } + } + + private static final class UcharToUshort extends SampleDepthPromoter { + + @Override + public void promote(int sampleN, byte[] nativeTileData, byte[] tileData) { + int pixArrayOffset = 2 * sampleN; + tileData[pixArrayOffset] = 0; + tileData[pixArrayOffset + 1] = (byte) ((nativeTileData[sampleN] >>> 0) & 0xFF); + } + } + + private static final class OneBitToUchar extends SampleDepthPromoter { + + @Override + public void promote(int sampleN, byte[] nativeTileData, byte[] tileData) { + int pixArrayOffset = sampleN / 8; + int bit = sampleN % 8; + int _byte = nativeTileData[pixArrayOffset]; + byte ucharvalue = (byte) ((_byte >> (7 - bit)) & 0x01); + tileData[sampleN] = ucharvalue; + } + } + + private static final class ShortToInt extends SampleDepthPromoter { + + @Override + public void promote(int sampleN, byte[] nativeTileData, byte[] tileData) { + int pixArrayOffset = 4 * sampleN; + + tileData[pixArrayOffset] = 0; + tileData[pixArrayOffset + 1] = 0; + tileData[pixArrayOffset + 1] = (byte) ((nativeTileData[sampleN] >>> 8) & 0xFF); + tileData[pixArrayOffset + 1] = (byte) ((nativeTileData[sampleN] >>> 0) & 0xFF); + } + } + + /** + * @see org.geotools.arcsde.raster.io.TileReader#dispose() + */ + public void dispose() { + this.nativeReader.dispose(); + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/RasterReaderFactory.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/RasterReaderFactory.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/RasterReaderFactory.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/RasterReaderFactory.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,57 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.io; + +import java.io.IOException; + +import org.geotools.arcsde.raster.info.RasterDatasetInfo; +import org.geotools.arcsde.session.ISessionPool; + +/** + * + * @author Gabriel Roldan + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/gce/RasterReaderFactory.java $ + * @version $Id$ + * @since 2.5.7 + */ +public class RasterReaderFactory { + + private final ISessionPool sessionPool; + + public RasterReaderFactory(final ISessionPool connectionPool) { + this.sessionPool = connectionPool; + } + + /** + * Creates a {@link TiledRasterReader} that's able to read one or more raster for the given + * {@link RasterDatasetInfo}, depending on if {@code rasterInfo} represents a single raster or a + * raster catalog. + * + * @param rasterInfo + * @return + * @throws IOException + */ + public TiledRasterReader create(final RasterDatasetInfo rasterInfo) throws IOException { + TiledRasterReader rasterReader = new DefaultTiledRasterReader(sessionPool, rasterInfo); + + return rasterReader; + } + +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReader.java (from rev 34302, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReader.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReader.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,150 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.io; + +import java.io.IOException; + +/** + * Offers an iterator like interface to read ArcSDE raster tiles into a {@code byte[]} + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/gce/TileReader.java $ + */ +public interface TileReader { + + public class TileInfo { + private final long bandId; + + private final byte[] bitmaskData; + + private final int numPixelsRead; + + private final byte[] tileDta; + + private final int columnIndex; + + private final int rowIndex; + + public TileInfo(long bandId, int colIndex, int rowIndex, int numPixelsRead, + byte[] tileData, byte[] bitMaskData) { + this.bandId = bandId; + this.columnIndex = colIndex; + this.rowIndex = rowIndex; + this.numPixelsRead = numPixelsRead; + this.tileDta = tileData; + this.bitmaskData = bitMaskData; + } + + public Long getBandId() { + return bandId; + } + + public byte[] getBitmaskData() { + return bitmaskData; + } + + public int getNumPixelsRead() { + return numPixelsRead; + } + + public byte[] getTileData() { + return tileDta; + } + + public int getColumnIndex() { + return columnIndex; + } + + public int getRowIndex() { + return rowIndex; + } + } + + /** + * @return number of bits per sample + */ + public abstract int getBitsPerSample(); + + /** + * @return number of samples per tile + */ + public abstract int getPixelsPerTile(); + + /** + * @return numbre of bands being fetched + */ + public abstract int getNumberOfBands(); + + /** + * @return number of pixels per tile over the X axis + */ + public abstract int getTileWidth(); + + /** + * @return number of pixels per tile over the Y axis + */ + public abstract int getTileHeight(); + + /** + * @return number of tiles being fetched over the X axis + */ + public abstract int getTilesWide(); + + /** + * @return number of tiles being fetched over the Y axis + */ + public abstract int getTilesHigh(); + + /** + * @return number of bytes in the raw pixel content of a tile, not taking into account any + * trailing bitmask data. + */ + public abstract int getBytesPerTile(); + + /** + * @return whether there are more tiles to fetch + * @throws IOException + */ + public abstract boolean hasNext() throws IOException; + + /** + * Fetches a tile and fills {@code tileData} with its raw pixel data packaged as bytes according + * to the number of bits per sample + * + * @param tileData + * a possibly {@code null} array where to store the next tile data. If {@code null} a + * new byte[] of length {@link #getBytesPerTile()} will be allocated and filled up + * with the raw tile pixel data. + * @return the bitmask data, or an empty array if the tile is full + * @throws IOException + * @throws {@link IllegalArgumentException} if tileData is not null and its size is less than + * {@link #getBytesPerTile()} + */ + public abstract TileInfo next() throws IOException; + + /** + * Disposes any resource being held by this TileReader, making the TileReader unusable and the + * behaviour of {@link #hasNext()} and {@link #next} unpredictable + */ + public abstract void dispose(); + +} \ No newline at end of file Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReaderFactory.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TileReaderFactory.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReaderFactory.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TileReaderFactory.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,54 @@ +package org.geotools.arcsde.raster.io; + +import java.awt.Rectangle; + +import org.geotools.arcsde.raster.info.RasterCellType; +import org.geotools.arcsde.raster.info.RasterDatasetInfo; +import org.geotools.arcsde.session.ISessionPool; + +public class TileReaderFactory { + + /** + * + * @param preparedQuery + * @param row + * @param nativeType + * @param targetType + * @param noDataValues + * @param numberOfBands + * @param requestedTiles + * @param tileSize + * @return + */ + public static TileReader getInstance(final ISessionPool sessionPool, + final RasterDatasetInfo rasterInfo, final long rasterId, final int pyramidLevel, + final Rectangle requestedTiles) { + + final TileReader tileReader; + + final RasterCellType nativeType = rasterInfo.getNativeCellType(); + final RasterCellType targetType = rasterInfo.getTargetCellType(rasterId); + + final BitmaskToNoDataConverter noData; + noData = BitmaskToNoDataConverter.getInstance(rasterInfo, rasterId); + + if (targetType == nativeType) { + + TileReader nativeTileReader = new NativeTileReader(sessionPool, rasterInfo, rasterId, + pyramidLevel, requestedTiles, noData); + + tileReader = nativeTileReader; + + } else { + // need to promote native to target sample depth + TileReader nativeTileReader; + nativeTileReader = new NativeTileReader(sessionPool, rasterInfo, rasterId, + pyramidLevel, requestedTiles, BitmaskToNoDataConverter.NO_ACTION_CONVERTER); + + TileReader promotingTileReader = new PromotingTileReader(nativeTileReader, nativeType, + targetType, noData); + tileReader = promotingTileReader; + } + return tileReader; + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TiledRasterReader.java (from rev 34277, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/TiledRasterReader.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TiledRasterReader.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/io/TiledRasterReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,93 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.io; + +import java.awt.Rectangle; +import java.awt.image.RenderedImage; +import java.io.IOException; + +/** + * An Iterator like interface to read ArcSDE rasters for a given ArcSDE raster dataset (whether it + * is a single raster or a raster catalog). + * <p> + * Sample usage: <code> + * <pre> + * RasterReaderFactory readerFactory = .... + * RasterDatasetInfo raserInfo = ... + * ArcSDERasterReader reader = readerFactory.create(rasterInfo); + * try{ + * Long nextRasterId; + * while((nextRasterId = reader.nextRaster()) != null){ + * if(amIInterestedInThisRaster(nextRasterId)){ + * int pyramidLevel = ... + * Rectangle tileRange = ... + * RenderedImage raster = reader.read(pyramidLevel, tileRange); + * } + * } + * }finally{ + * reader.dispose(); + * } + * </pre> + * </code> + * </p> + * <p> + * So one has to call {@code nextRaster()} to get the id of the raster immediately available to be + * read through {@link #read()}. This is so because there might be more than one raster on a raster + * dataset and the order they are fetched from the ArcSDE server is non deterministic, and once you + * opened a stream to a raster you can't open another one and then read the former. + * </p> + * + * @author Gabriel Roldan + * @version $Id$ + * @since 2.5.7 + */ +public interface TiledRasterReader { + + /** + * Disposes any resource being held by this reader, whether it's a connection to the ArcSDE + * server, opened streams, etc. + */ + // void dispose(); + + /** + * Advances to the next available raster in the raster dataset this reader works upon and + * returns it's {@link SeRasterAttr#getRasterId() raster id}. + * + * @return the ID for the raster ready to be read from the queried raster column in the raster + * dataset, or {@code null} if there are no more rasters to be read. + * @throws IOException + * for any problem occurred retrieving the next {@link SeRasterAttr} in the request + */ + // Long nextRaster() throws IOException; + + /** + * Reads the image subset determined by the given pyramid level and tile range for the currently + * available raster attribute in the requested raster column for the given raster dataset. + * + * @param pyramidLevel + * the pyramid level to read + * @param tileRange + * the range of tiles to read at the given pyramid level. The boundaries of the tile + * range are inclusive and starts at {@code 0,0} for the upper left most tile. + * @return the rendered image determined by the requested pyramid level and tile range + * @throws IOException + * for any exception occurred while reading the image + */ + RenderedImage read(final long rasterId, final int pyramidLevel, final Rectangle tileRange) + throws IOException; + +} \ No newline at end of file Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDEImageReader.java (from rev 34312, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDEImageReader.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDEImageReader.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDEImageReader.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,187 @@ +package org.geotools.arcsde.raster.jai; + +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.Raster; +import java.awt.image.RenderedImage; +import java.awt.image.SampleModel; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; + +import javax.imageio.ImageReadParam; +import javax.imageio.ImageReader; +import javax.imageio.ImageTypeSpecifier; +import javax.imageio.metadata.IIOMetadata; +import javax.media.jai.PlanarImage; + +import org.geotools.arcsde.raster.io.TileReader; +import org.geotools.arcsde.raster.io.TileReader.TileInfo; + +import com.sun.media.imageioimpl.common.SimpleRenderedImage; + +public class ArcSDEImageReader extends ImageReader { + + private TileReader tileReader; + + private ImageTypeSpecifier typeSpec; + + public ArcSDEImageReader(final ImageTypeSpecifier typeSpec) { + super(null); + this.typeSpec = typeSpec; + } + + @Override + public int getWidth(int imageIndex) throws IOException { + int w = tileReader.getTilesWide() * tileReader.getTileWidth(); + return w; + } + + @Override + public int getHeight(int imageIndex) throws IOException { + int h = tileReader.getTilesHigh() * tileReader.getTileHeight(); + return h; + } + + @Override + public IIOMetadata getImageMetadata(int imageIndex) throws IOException { + return null; + } + + @Override + public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException { + return Collections.singleton(typeSpec).iterator(); + } + + @Override + public int getNumImages(boolean allowSearch) throws IOException { + return 1; + } + + @Override + public IIOMetadata getStreamMetadata() throws IOException { + return null; + } + + @Override + public RenderedImage readAsRenderedImage(int imageIndex, ImageReadParam param) + throws IOException { + // return read(imageIndex, param); + + RenderedImage image = new ArcSDETiledRenderedImage(tileReader, typeSpec); + + // BufferedImage bufferedImage = PlanarImage.wrapRenderedImage(image).getAsBufferedImage(); + // return bufferedImage; + return image; + } + + @Override + public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException { + if (param == null) { + param = getDefaultReadParam(); + } + + RenderedImage rendered = readAsRenderedImage(imageIndex, param); + + BufferedImage bufferedImage = PlanarImage.wrapRenderedImage(rendered).getAsBufferedImage(); + + return bufferedImage; + } + + @Override + public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) { + this.tileReader = (TileReader) input; + } + + @SuppressWarnings("unchecked") + private static class ArcSDETiledRenderedImage extends SimpleRenderedImage { + + private TileReader tileReader; + + public ArcSDETiledRenderedImage(TileReader tileReader, ImageTypeSpecifier typeSpec) { + System.err.println(tileReader); + this.tileReader = tileReader; + super.colorModel = typeSpec.getColorModel(); + super.sampleModel = typeSpec.getSampleModel(); + super.height = tileReader.getTilesHigh() * tileReader.getTileHeight(); + super.width = tileReader.getTilesWide() * tileReader.getTileWidth(); + super.minX = 0; + super.minY = 0; + super.tileGridXOffset = 0; + super.tileGridYOffset = 0; + super.tileHeight = tileReader.getTileHeight(); + super.tileWidth = tileReader.getTileWidth(); + } + + private WritableRaster[][] tileCache = null; + + /** + * @see java.awt.image.RenderedImage#getTile(int, int) + */ + public Raster getTile(final int tileX, final int tileY) { + // System.err.printf("getTile(%d, %d)\n", tileX, tileY); + if (tileCache == null) { + tileCache = new WritableRaster[tileReader.getTilesWide()][tileReader.getTilesHigh()]; + } + + WritableRaster currentTile = tileCache[tileX][tileY]; + if (currentTile == null) { + final int tilesWide = tileReader.getTilesWide(); + final int tilesHigh = tileReader.getTilesHigh(); + + for (int ty = 0; ty < tilesHigh; ty++) { + for (int tx = 0; tx < tilesWide; tx++) { + + currentTile = tileCache[tx][ty]; + + if (currentTile == null) { + int x = tileXToX(tx); + int y = tileYToY(ty); + // System.err.println("fetching tile " + tx + "," + ty); + currentTile = fetchTile(x, y); + tileCache[tx][ty] = currentTile; + } + if (tx == tileX && ty == tileY) { + return currentTile; + } + } + } + } + // currentTile = tileCache[tileX][tileY]; + return currentTile; + } + + private WritableRaster fetchTile(final int xOrigin, final int yOrigin) { + final int numBands = sampleModel.getNumBands(); + + final SampleModel tileSampleModel = super.sampleModel.createCompatibleSampleModel( + tileWidth, tileHeight); + + DataBuffer dataBuffer = sampleModel.createDataBuffer(); + for (int bandN = 0; bandN < numBands; bandN++) { + TileInfo tileInfo; + try { + tileInfo = tileReader.next(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + byte[] rawBandData = tileInfo.getTileData(); + + final int numPixels = tileWidth * tileHeight; + for (int pixelN = 0; pixelN < numPixels; pixelN++) { + int val = rawBandData[2 * pixelN + 1] & 0xFF; + dataBuffer.setElem(bandN, pixelN, val); + } + } + + WritableRaster currentTile; + currentTile = Raster.createWritableRaster(tileSampleModel, dataBuffer, new Point( + xOrigin, yOrigin)); + return currentTile; + } + + } +} Copied: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDETiledImageInputStream.java (from rev 34305, trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/gce/ArcSDETiledImageInputStream.java) =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDETiledImageInputStream.java (rev 0) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/raster/jai/ArcSDETiledImageInputStream.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -0,0 +1,141 @@ +/* + * GeoTools - The Open Source Java GIS Toolkit + * http://geotools.org + * + * (C) 2002-2009, 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.arcsde.raster.jai; + +import java.io.IOException; + +import javax.imageio.stream.ImageInputStream; +import javax.imageio.stream.ImageInputStreamImpl; + +import org.geotools.arcsde.raster.io.TileReader; +import org.geotools.arcsde.raster.io.TileReader.TileInfo; + +/** + * An {@link ImageInputStream} that reads ArcSDE raster tiles in a band interleaved order. + * + * @author Gabriel Roldan (OpenGeo) + * @since 2.5.4 + * @version $Id$ + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/gce/ArcSDETiledImageInputStream.java $ + */ +@Deprecated +final class ArcSDETiledImageInputStream extends ImageInputStreamImpl implements ImageInputStream { + + private final TileReader tileReader; + + private final int tileDataLength; + + private byte[] currTileData; + + private int currTileDataIndex; + + private int currTileIndex = -1; + + private final int length; + + public ArcSDETiledImageInputStream(final TileReader tileReader) { + super(); + this.tileReader = tileReader; + final int bytesPerTile = tileReader.getBytesPerTile(); + this.tileDataLength = bytesPerTile; + this.currTileData = new byte[bytesPerTile]; + // force load at the first read invocation + this.currTileDataIndex = tileDataLength; + + final int tilesWide = tileReader.getTilesWide(); + final int tilesHigh = tileReader.getTilesHigh(); + final int numberOfBands = tileReader.getNumberOfBands(); + + length = bytesPerTile * tilesWide * tilesHigh * numberOfBands; + } + + /** + * Returns the computed lenght of the stream based on the tile dimensions, number of tiles, + * number of bands, and bits per sample + */ + @Override + public long length() { + return length; + } + + @Override + public int read() throws IOException { + final byte[] data = getTileData(); + if (data == null) { + close(); + return -1; + } + byte b = data[currTileDataIndex]; + ++currTileDataIndex; + ++streamPos; + return b; + } + + @Override + public int read(byte[] buff, int off, int len) throws IOException { + final byte[] data = getTileData(); + if (data == null) { + close(); + return -1; + } + final int available = data.length - currTileDataIndex; + final int count = Math.min(available, len); + System.arraycopy(data, currTileDataIndex, buff, off, count); + currTileDataIndex += count; + streamPos += count; + return count; + } + + /** + * Fetches a tile from the {@code tileReader} if necessary and returns the current tile data. + * <p> + * It is needed to fetch a new tile if {@link #currTileDataIndex} indicates all the current tile + * data has been already read. If so, {@code currTileDataIndex} is reset to 0. The {@code read} + * operations are responsible of incrementing {@code currTileDataIndex} depending on how many + * bytes have been consumed from the tile data returned by this method. + * </p> + * + * @return {@code null} if there's no more tiles to fetch, the current tile data otherwise + * @throws IOException + */ + private byte[] getTileData() throws IOException { + if (currTileDataIndex == tileDataLength) { + if (!tileReader.hasNext()) { + return null; + } + + currTileDataIndex = 0; + ++currTileIndex; + TileInfo tile = tileReader.next(); + currTileData = tile.getTileData(); + + if (!tileReader.hasNext()) { + tileReader.dispose(); + } + } + return currTileData; + } + + @Override + public void close() throws IOException { + tileReader.dispose(); + super.close(); + } +} \ No newline at end of file Modified: trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/util/ArcSDEUtils.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/util/ArcSDEUtils.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/main/java/org/geotools/arcsde/util/ArcSDEUtils.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -40,8 +40,10 @@ * implementations * * @author Gabriel Roldan - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/main/java/org + * /geotools/arcsde/util/ArcSDEUtils.java $ * @version $Id$ * @since 2.5.6 * Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEDataStoreFactoryTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEDataStoreFactoryTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEDataStoreFactoryTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -39,7 +39,6 @@ import org.geotools.arcsde.session.UnavailableConnectionException; import org.geotools.data.DataAccessFactory; import org.geotools.data.DataAccessFinder; -import org.geotools.data.DataSourceException; import org.geotools.data.DataStore; import org.geotools.data.DataStoreFactorySpi; import org.geotools.data.DataStoreFinder; @@ -206,7 +205,8 @@ * @throws UnavailableConnectionException */ @Test - public void testCreateDataStoreWithInProcessViews() throws IOException, SeException, UnavailableConnectionException { + public void testCreateDataStoreWithInProcessViews() throws IOException, SeException, + UnavailableConnectionException { ISession session = testData.getConnectionPool().getSession(true); try { InProcessViewSupportTestData.setUp(session, testData); Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactoryTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactoryTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/ArcSDEJNDIDataStoreFactoryTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -51,8 +51,10 @@ /** * @author Gabriel Roldan (OpenGeo) - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/test/java/org + * /geotools/arcsde/ArcSDEJNDIDataStoreFactoryTest.java $ * @version $Id$ * @since 2.5.7 */ Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreNonSpatialTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreNonSpatialTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreNonSpatialTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -67,8 +67,10 @@ * required by the new feature <a href="http://jira.codehaus.org/browse/GEOT-2548">GEOT-2548</a> * * @author Gabriel Roldan, OpenGeo - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/test/java/org + * /geotools/arcsde/data/ArcSDEDataStoreNonSpatialTest.java $ * @version $Id$ * @since 2.5.6 */ Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEDataStoreTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -327,7 +327,8 @@ @SuppressWarnings("unchecked") @Test - public void testCreateNillableShapeSchema() throws IOException, SchemaException, SeException, UnavailableConnectionException { + public void testCreateNillableShapeSchema() throws IOException, SchemaException, SeException, + UnavailableConnectionException { SimpleFeatureType type; final String typeName = "GT_TEST_CREATE"; SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder(); @@ -348,7 +349,7 @@ ds.createSchema(type, hints); testData.deleteTable(typeName); } - + // ///////////////// HELPER FUNCTIONS //////////////////////// /** Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEJavaApiTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEJavaApiTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ArcSDEJavaApiTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1034,8 +1034,7 @@ } // End method createBaseTable @Test - public void testDeleteById() throws IOException, UnavailableConnectionException, - SeException { + public void testDeleteById() throws IOException, UnavailableConnectionException, SeException { final String typeName = testData.getTempTableName(); final SeQuery query = session.createAndExecuteQuery(new String[] { "ROW_ID", "INT32_COL" }, Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ClobTestData.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ClobTestData.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/ClobTestData.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -125,12 +125,14 @@ } } - public SeTable getTempTable(ISession session) throws IOException, UnavailableConnectionException { + public SeTable getTempTable(ISession session) throws IOException, + UnavailableConnectionException { final String tempTableName = getTempTableName(); return session.getTable(tempTableName); } - public SeLayer getTempLayer(ISession session) throws IOException, UnavailableConnectionException { + public SeLayer getTempLayer(ISession session) throws IOException, + UnavailableConnectionException { final String tempTableName = getTempTableName(); return session.getLayer(tempTableName); } Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/FIDReaderTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/FIDReaderTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/FIDReaderTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -38,8 +38,10 @@ /** * @author Gabriel Roldan (TOPP) - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/test/java/org + * /geotools/arcsde/data/FIDReaderTest.java $ * @version $Id$ * @since 2.5.x */ Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/SDEJavaApiJoinTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/SDEJavaApiJoinTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/SDEJavaApiJoinTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -140,8 +140,10 @@ * /arcsde/data/SDEJavaApiJoinTest.java $ @version $Id: SDEJavaApiJoinTest.java 31903 * 2008-11-22 20:44:25Z groldan $ @since 2.3.x * - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/test/java/org + * /geotools/arcsde/data/SDEJavaApiJoinTest.java $ */ public class SDEJavaApiJoinTest { /** package logger */ @@ -161,7 +163,7 @@ * @throws SeException * @throws FactoryException * @throws NoSuchAuthorityCodeException - * @throws UnavailableConnectionException + * @throws UnavailableConnectionException */ @BeforeClass public static void oneTimeSetUp() throws IOException, SeException, Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestFeatureListener.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestFeatureListener.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestFeatureListener.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -27,8 +27,10 @@ * This class copies out some of the details about each event received. * * @author Jody Garnett - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/test/java/org + * /geotools/arcsde/data/TestFeatureListener.java $ */ public class TestFeatureListener implements FeatureListener { List<FeatureEvent> list = new LinkedList<FeatureEvent>(); Modified: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestProgressListener.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestProgressListener.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/data/TestProgressListener.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -27,8 +27,10 @@ * This implementation is good about throwing illegal state exceptions and so forth. * * @author Jody - * - * @source $URL$ + * + * @source $URL: + * http://svn.osgeo.org/geotools/trunk/modules/plugin/arcsde/datastore/src/test/java/org + * /geotools/arcsde/data/TestProgressListener.java $ */ public class TestProgressListener implements ProgressListener { int progressCount; Deleted: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAILegacyOnlineTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAILegacyOnlineTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAILegacyOnlineTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,546 +0,0 @@ -/** - * - */ -package org.geotools.arcsde.gce; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.awt.Rectangle; -import java.awt.image.ColorModel; -import java.awt.image.IndexColorModel; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.renderable.ParameterBlock; -import java.io.File; -import java.io.IOException; -import java.util.logging.Logger; - -import javax.imageio.ImageIO; -import javax.media.jai.JAI; -import javax.media.jai.PlanarImage; - -import org.geotools.arcsde.ArcSDERasterFormatFactory; -import org.geotools.arcsde.session.ArcSDEConnectionConfig; -import org.geotools.coverage.grid.GridCoverage2D; -import org.geotools.coverage.grid.GridEnvelope2D; -import org.geotools.coverage.grid.GridGeometry2D; -import org.geotools.coverage.grid.ViewType; -import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; -import org.geotools.coverage.grid.io.AbstractGridFormat; -import org.geotools.coverage.grid.io.OverviewPolicy; -import org.geotools.gce.geotiff.GeoTiffWriter; -import org.geotools.geometry.Envelope2D; -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.parameter.Parameter; -import org.geotools.util.logging.Logging; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; -import org.opengis.coverage.grid.GridEnvelope; -import org.opengis.geometry.Envelope; -import org.opengis.parameter.GeneralParameterValue; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -import com.vividsolutions.jts.util.Stopwatch; - -/** - * Tests over legacy data that should not be deleted - * - */ -@SuppressWarnings( { "deprecation", "nls" }) -public class ArcSDEGridCoverage2DReaderJAILegacyOnlineTest { - - private static final String RASTER_TEST_DEBUG_TO_DISK = "raster.test.debugToDisk"; - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - /** - * Whether to write the fetched rasters to disk or not - */ - private static boolean DEBUG; - - static RasterTestData rasterTestData; - - private static String tableName; - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - rasterTestData = new RasterTestData(); - rasterTestData.setUp(); - DEBUG = Boolean - .valueOf(rasterTestData.getRasterTestDataProperty(RASTER_TEST_DEBUG_TO_DISK)); - rasterTestData.setOverrideExistingTestTables(false); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - rasterTestData.tearDown(); - } - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - // nothing to do - } - - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - // try { - // LOGGER.info("tearDown: deleting " + tableName); - // // wait I may delete an actual business table, comment out until this suite is fully - // // based on fake data rasterTestData.deleteTable(tableName); - // } catch (Exception e) { - // LOGGER.log(Level.INFO, "Error deleting test table " + tableName, e); - // } - } - - /** - * Test method for {@link org.geotools.arcsde.gce.ArcSDEGridCoverage2DReaderJAI#getInfo()}. - */ - @Test - @Ignore - public void testGetInfo() { - fail("Not yet implemented"); - } - - @Test - public void testIMG_USGSQUAD_SGBASE() throws Exception { - tableName = "SDE.RASTER.IMG_USGSQUAD_SGBASE"; - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final int count = 0; - long time = 0; - // warm up - _testIMG_USGSQUAD_SGBASE(reader); - for (int i = 0; i < count; i++) { - time += _testIMG_USGSQUAD_SGBASE(reader); - } - System.err.println(count + " reads in " + time + "ms"); - } - - private long _testIMG_USGSQUAD_SGBASE(AbstractGridCoverage2DReader reader) throws Exception { - - // http://localhost:8080/geoserver/wms?WIDTH=256&LAYERS=sde%3AIMG_USGSQUAD_SGBASE&STYLES=&SRS=EPSG%3A26986&HEIGHT=256&FORMAT=image%2Fjpeg&TILED=true&TILESORIGIN=169118.35%2C874964.388&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&BBOX=239038.74625,916916.62575,253022.8255,930900.705 - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - - final int reqWidth = 256; - final int reqHeight = 256; - - GeneralEnvelope reqEnvelope = new GeneralEnvelope(originalEnvelope); - reqEnvelope.setEnvelope(239038.74625, 916916.62575, 253022.8255, 930900.705); - - Stopwatch sw = new Stopwatch(); - sw.start(); - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - RenderedImage image = coverage.getRenderedImage(); - writeToDisk(image, "testRead_" + tableName); - sw.stop(); - return sw.getTime(); - } - - @Test - public void testIMG_USGSQUAD_SGBASE2() throws Exception { - tableName = "SDE.RASTER.IMG_USGSQUAD_SGBASE"; - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - // http://localhost:8080/geoserver/wms?WIDTH=256&LAYERS=sde%3AIMG_USGSQUAD_SGBASE&STYLES=&SRS=EPSG%3A26986&HEIGHT=256&FORMAT=image%2Fjpeg&TILED=true&TILESORIGIN=169118.35%2C874964.388&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&BBOX=239038.74625,916916.62575,253022.8255,930900.705 - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0) / 30; - final int reqHeight = originalGridRange.getSpan(1) / 30; - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, originalEnvelope); - assertNotNull("read coverage returned null", coverage); - - RenderedImage image = coverage.getRenderedImage(); - writeToDisk(image, "testRead_" + tableName); - } - - @Test - public void testCOLOROQ_TEST() throws Exception { - tableName = "SDE.RASTER.COLOROQ_TEST"; - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - System.out.println(reader.getInfo().getDescription()); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = 225; - final int reqHeight = 512; - - GeneralEnvelope reqEnvelope = originalEnvelope; - - assertTrue(originalEnvelope.intersects(reqEnvelope, true)); - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - writeToDisk(coverage, "testRead_" + tableName); - - RenderedImage image = coverage.view(ViewType.RENDERED).getRenderedImage(); - // writeToDisk(image, tableName); - } - - @Test - public void testReadIMG_USGSQUADM_Buggy() throws Exception { - // http://localhost:8080/geoserver/wms?HEIGHT=500&WIDTH=1200&LAYERS=sde:IMG_USGSQUADM&STYLES=&SRS=EPSG%3A26986&FORMAT=image%2Fjpeg&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&BBOX=253178.45971681,872419.13604732,253521.78247071,872562.18719478 - tableName = "SDE.RASTER.IMG_USGSQUADM"; - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope requestEnvelope = new GeneralEnvelope(reader.getOriginalEnvelope()); - requestEnvelope.setEnvelope(253178.45971681, 872419.13604732, 253521.78247071, - 872562.18719478); - - final int reqWidth = 1200; - final int reqHeight = 500; - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, requestEnvelope); - assertNotNull("read coverage returned null", coverage); - - RenderedImage image = coverage.view(ViewType.PHOTOGRAPHIC).getRenderedImage(); - writeToDisk(image, "testRead_" + tableName); - } - - @Test - public void testReadIMG_USGSQUADM() throws Exception { - tableName = "SDE.RASTER.IMG_USGSQUADM"; - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0) / 200; - final int reqHeight = originalGridRange.getSpan(1) / 200; - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, originalEnvelope); - assertNotNull("read coverage returned null", coverage); - - RenderedImage image = coverage.view(ViewType.PHOTOGRAPHIC).getRenderedImage(); - writeToDisk(image, "testRead_" + tableName); - } - - @Test - public void testReadIMGCOQ_2005() throws Exception { - tableName = "SDE.RASTER.IMG_COQ2005_CLIP_BOS"; - - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0) / 2; - final int reqHeight = originalGridRange.getSpan(1) / 2; - - Envelope reqEnvelope = originalEnvelope; - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - GridGeometry2D gg = coverage.getGridGeometry(); - Envelope2D envelope2D = gg.getEnvelope2D(); - GridEnvelope gridRange = gg.getGridRange(); - - System.out.println("requested size: " + reqWidth + "x" + reqHeight); - System.out.println("result size : " + gridRange.getSpan(0) + "x" + gridRange.getSpan(1)); - - System.out.println("requested envelope: " + reqEnvelope); - - System.out.println("result envelope : " + envelope2D); - - // RenderedImage image = coverage.getRenderedImage(); - // writeToDisk(coverage, "testRead_" + tableName); - - RenderedImage image = coverage.view(ViewType.RENDERED).getRenderedImage(); - writeToDisk(image, tableName); - - writeBand(image, new int[] { 0 }, "red"); - writeBand(image, new int[] { 1 }, "green"); - writeBand(image, new int[] { 2 }, "blue"); - writeBand(image, new int[] { 3 }, "alpha"); - - writeBand(image, new int[] { 0, 1, 2 }, "rgb"); - } - - @Test - public void testIMG_WIND_SPD30M_ADAMS() throws Exception { - tableName = "SDE.RASTER.IMG_WIND_SPD30M_ADAMS"; - - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0); - final int reqHeight = originalGridRange.getSpan(1); - - GeneralEnvelope reqEnvelope = originalEnvelope; - reqEnvelope.setCoordinateReferenceSystem(originalEnvelope.getCoordinateReferenceSystem()); - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - GridGeometry2D gg = coverage.getGridGeometry(); - Envelope2D envelope2D = gg.getEnvelope2D(); - GridEnvelope gridRange = gg.getGridRange(); - - System.out.println("requested size: " + reqWidth + "x" + reqHeight); - System.out.println("result size : " + gridRange.getSpan(0) + "x" + gridRange.getSpan(1)); - - System.out.println("requested envelope: " + reqEnvelope); - - System.out.println("result envelope : " + envelope2D); - - // writeToDisk(coverage, "testRead_" + tableName); - } - - @Test - public void testReadNOAA_13006_1() throws Exception { - tableName = "SDE.RASTER.NOAA_13006_1"; - - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = 100;// 800;// originalGridRange.getSpan(0) / 8; - final int reqHeight = 75;// 595;// originalGridRange.getSpan(1) / 8; - - GeneralEnvelope reqEnvelope = new GeneralEnvelope(new double[] { 274059, 837434 }, - new double[] { 355782, 898216 }); - reqEnvelope.setCoordinateReferenceSystem(originalEnvelope.getCoordinateReferenceSystem()); - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - GridGeometry2D gg = coverage.getGridGeometry(); - Envelope2D envelope2D = gg.getEnvelope2D(); - GridEnvelope gridRange = gg.getGridRange(); - - System.out.println("requested size: " + reqWidth + "x" + reqHeight); - System.out.println("result size : " + gridRange.getSpan(0) + "x" + gridRange.getSpan(1)); - - System.out.println("requested envelope: " + reqEnvelope); - - System.out.println("result envelope : " + envelope2D); - - writeToDisk(coverage, "testRead_" + tableName); - - RenderedImage image = coverage.getRenderedImage(); - ColorModel colorModel = image.getColorModel(); - SampleModel sampleModel = image.getSampleModel(); - - assertTrue(colorModel instanceof IndexColorModel); - IndexColorModel cm = ((IndexColorModel) colorModel); - - int numComponents = cm.getMapSize(); - byte[] r = new byte[numComponents]; - byte[] g = new byte[numComponents]; - byte[] b = new byte[numComponents]; - byte[] a = new byte[numComponents]; - cm.getReds(r); - cm.getGreens(g); - cm.getBlues(b); - cm.getAlphas(a); - for (int i = 0; i < numComponents; i++) { - System.out.print(i + " = "); - System.out.print((int) r[i] & 0xFF); - System.out.print(','); - System.out.print((int) g[i] & 0xFF); - System.out.print(','); - System.out.print((int) b[i] & 0xFF); - System.out.print(','); - System.out.print((int) a[i] & 0xFF); - System.out.print('\n'); - } - } - - private void writeBand(RenderedImage image, int[] bands, String channel) throws Exception { - ParameterBlock pb = new ParameterBlock(); - pb.addSource(image); - pb.add(bands); - PlanarImage alpha = JAI.create("bandSelect", pb); - writeToDisk(alpha, tableName + "_" + channel); - } - - @Test - public void testReadIMGCOQ_2001() throws Exception { - tableName = "SDE.RASTER.IMG_COQ2001_CLIP_BOS_1"; - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0) / 4; - final int reqHeight = originalGridRange.getSpan(1) / 4; - - Envelope reqEnvelope = originalEnvelope; - // GeneralEnvelope reqEnvelope = new GeneralEnvelope(originalEnvelope - // .getCoordinateReferenceSystem()); - // - // final double reqMinx = 235901.26048201; - // final double reqMiny = 901552.0880242661; - // final double reqMaxx = 236781.26048201; - // final double reqMaxy = 902253.0880242661; - // - // reqEnvelope.setEnvelope(reqMinx, reqMiny, reqMaxx, reqMaxy); - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - GridGeometry2D gg = coverage.getGridGeometry(); - Envelope2D envelope2D = gg.getEnvelope2D(); - GridEnvelope gridRange = gg.getGridRange(); - - System.out.println("requested size: " + reqWidth + "x" + reqHeight); - System.out.println("result size : " + gridRange.getSpan(0) + "x" + gridRange.getSpan(1)); - - System.out.println("requested envelope: " + reqEnvelope); - - System.out.println("result envelope : " + envelope2D); - - // RenderedImage image = coverage.getRenderedImage(); - // writeToDisk(coverage, "testRead_" + tableName); - } - - private void writeToDisk(GridCoverage2D coverage, String fileName) throws Exception { - Object destination; - { - String file = System.getProperty("user.home"); - file += File.separator + "arcsde_test" + File.separator + fileName + ".tiff"; - File path = new File(file); - path.getParentFile().mkdirs(); - destination = path; - } - GeoTiffWriter writer = new GeoTiffWriter(destination); - - System.out.println("\n --- Writing to " + destination); - try { - long t = System.currentTimeMillis(); - writer.write(coverage, null); - System.out.println(" - wrote in " + t + "ms" + destination); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - private void writeToDisk(final RenderedImage image, String fileName) throws Exception { - if (!DEBUG) { - LOGGER.fine("DEBUG == false, not writing image to disk"); - return; - } - String file = System.getProperty("user.home"); - file += File.separator + "arcsde_test" + File.separator + fileName + ".tiff"; - File path = new File(file); - path.getParentFile().mkdirs(); - - System.out.println("\n --- Writing to " + file); - try { - long t = System.currentTimeMillis(); - ImageIO.write(image, "TIFF", path); - t = System.currentTimeMillis() - t; - System.out.println(" - wrote in " + t + "ms" + file); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - private GridCoverage2D readCoverage(final AbstractGridCoverage2DReader reader, - final int reqWidth, final int reqHeight, final Envelope reqEnv) throws Exception { - - GeneralParameterValue[] requestParams = new Parameter[2]; - final CoordinateReferenceSystem crs = reader.getCrs(); - - GridGeometry2D gg2d; - gg2d = new GridGeometry2D(new GridEnvelope2D(new Rectangle(reqWidth, reqHeight)), reqEnv); - - requestParams[0] = new Parameter<GridGeometry2D>(AbstractGridFormat.READ_GRIDGEOMETRY2D, - gg2d); - requestParams[1] = new Parameter<OverviewPolicy>(AbstractGridFormat.OVERVIEW_POLICY, - OverviewPolicy.QUALITY); - - final GridCoverage2D coverage; - coverage = (GridCoverage2D) reader.read(requestParams); - - return coverage; - } - - private AbstractGridCoverage2DReader getReader() throws IOException { - final ArcSDEConnectionConfig config = rasterTestData.getConnectionPool().getConfig(); - - final String rgbUrl = "sde://" + config.getUserName() + ":" + config.getPassword() + "@" - + config.getServerName() + ":" + config.getPortNumber() + "/" - + config.getDatabaseName() + "#" + tableName; - - final ArcSDERasterFormat format = new ArcSDERasterFormatFactory().createFormat(); - - AbstractGridCoverage2DReader reader = format.getReader(rgbUrl); - return reader; - } - - @Test - public void testReadRUGGED_RD() throws Exception { - tableName = "SDE.RASTER.RUGGED_RD"; - - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0) / 8; - final int reqHeight = originalGridRange.getSpan(1) / 8; - - Envelope reqEnvelope = originalEnvelope; - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - GridGeometry2D gg = coverage.getGridGeometry(); - Envelope2D envelope2D = gg.getEnvelope2D(); - GridEnvelope gridRange = gg.getGridRange(); - - System.out.println("requested size: " + reqWidth + "x" + reqHeight); - System.out.println("result size : " + gridRange.getSpan(0) + "x" + gridRange.getSpan(1)); - - System.out.println("requested envelope: " + reqEnvelope); - - System.out.println("result envelope : " + envelope2D); - - // RenderedImage image = coverage.getRenderedImage(); - // writeToDisk(coverage, "testRead_" + tableName); - - RenderedImage image = coverage.view(ViewType.RENDERED).getRenderedImage(); - // writeToDisk(image, tableName); - - // writeBand(image, new int[] { 0 }, "band1"); - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAIOnlineTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAIOnlineTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/ArcSDEGridCoverage2DReaderJAIOnlineTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,719 +0,0 @@ -/** - * - */ -package org.geotools.arcsde.gce; - -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_1BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_REAL; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_4BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_64BIT_REAL; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_U; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.awt.Color; -import java.awt.Rectangle; -import java.awt.color.ColorSpace; -import java.awt.image.ColorModel; -import java.awt.image.DataBuffer; -import java.awt.image.IndexColorModel; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.io.File; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.imageio.ImageIO; -import javax.media.jai.operator.FormatDescriptor; - -import org.geotools.arcsde.ArcSDERasterFormatFactory; -import org.geotools.arcsde.session.ArcSDEConnectionConfig; -import org.geotools.coverage.grid.GridCoverage2D; -import org.geotools.coverage.grid.GridEnvelope2D; -import org.geotools.coverage.grid.GridGeometry2D; -import org.geotools.coverage.grid.ViewType; -import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; -import org.geotools.coverage.grid.io.AbstractGridFormat; -import org.geotools.coverage.grid.io.OverviewPolicy; -import org.geotools.gce.geotiff.GeoTiffWriter; -import org.geotools.geometry.Envelope2D; -import org.geotools.geometry.GeneralEnvelope; -import org.geotools.geometry.jts.ReferencedEnvelope; -import org.geotools.parameter.Parameter; -import org.geotools.resources.image.ColorUtilities; -import org.geotools.util.logging.Logging; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; -import org.opengis.coverage.grid.GridEnvelope; -import org.opengis.geometry.Envelope; -import org.opengis.parameter.GeneralParameterValue; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -import com.esri.sde.sdk.client.SeRaster; - -/** - * @author groldan - * - */ -@SuppressWarnings( { "deprecation", "nls" }) -public class ArcSDEGridCoverage2DReaderJAIOnlineTest { - - private static final String RASTER_TEST_DEBUG_TO_DISK = "raster.test.debugToDisk"; - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - /** - * Whether to write the fetched rasters to disk or not - */ - private static boolean DEBUG; - - static RasterTestData rasterTestData; - - private static String tableName; - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - // rasterTestData = new RasterTestData(); - // rasterTestData.setUp(); - // DEBUG = Boolean - // .valueOf(rasterTestData.getRasterTestDataProperty(RASTER_TEST_DEBUG_TO_DISK)); - // rasterTestData.setOverrideExistingTestTables(false); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - // rasterTestData.tearDown(); - } - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - rasterTestData = new RasterTestData(); - rasterTestData.setUp(); - DEBUG = Boolean - .valueOf(rasterTestData.getRasterTestDataProperty(RASTER_TEST_DEBUG_TO_DISK)); - rasterTestData.setOverrideExistingTestTables(false); - } - - /** - * @throws java.lang.Exception - */ - @After - public void tearDown() throws Exception { - try { - LOGGER.info("tearDown: deleting " + tableName); - rasterTestData.deleteTable(tableName); - rasterTestData.tearDown(); - } catch (Exception e) { - LOGGER.log(Level.INFO, "Error deleting test table " + tableName, e); - } - } - - @Test - public void testRead_01bit_1Band() throws Exception { - testReadFullLevel0(TYPE_1BIT, 1); - } - - @Test - public void testRead_01bit_MoreThanOneBandIsUnsupported() throws Exception { - try { - testReadFullLevel0(TYPE_1BIT, 2); - fail("Expected IAE"); - } catch (IllegalArgumentException e) { - assertTrue(true); - } - } - - /* - * 4bit rasters are not supported by now, need to check exactly what color model/sample model - * combination makes JAI happy, or to unpack the incoming samples into full bytes - */ - @Test - @Ignore - public void testRead_04bit_1Band() throws Exception { - testReadFullLevel0(TYPE_4BIT, 1); - } - - @Test - public void testRead_04bit_MoreThanOneBandIsUnsupported() throws Exception { - try { - testReadFullLevel0(TYPE_4BIT, 2); - fail("Expected IAE"); - } catch (IllegalArgumentException e) { - assertTrue(true); - } - } - - @Test - public void testRead_08bit_U_1Band() throws Exception { - testReadFullLevel0(TYPE_8BIT_U, 1, TYPE_16BIT_U); - } - - @Test - public void testRead_08bit_U_4Band() throws Exception { - GridCoverage2D coverage = testReadFullLevel0(TYPE_8BIT_U, 4); - - final RenderedImage image = coverage.view(ViewType.GEOPHYSICS).getRenderedImage(); - assertEquals(DataBuffer.TYPE_BYTE, image.getSampleModel().getTransferType()); - - ColorModel colorModel = image.getColorModel(); - - assertEquals(ColorSpace.TYPE_RGB, colorModel.getColorSpace().getType()); - assertEquals(4, colorModel.getNumComponents()); - assertTrue(colorModel.hasAlpha()); - } - - @Test - public void testRead_08bit_U_3Band() throws Exception { - GridCoverage2D coverage = testReadFullLevel0(TYPE_8BIT_U, 3); - - final RenderedImage image = coverage.view(ViewType.GEOPHYSICS).getRenderedImage(); - assertEquals(DataBuffer.TYPE_BYTE, image.getSampleModel().getTransferType()); - - ColorModel colorModel = image.getColorModel(); - - assertEquals(ColorSpace.TYPE_RGB, colorModel.getColorSpace().getType()); - assertEquals(3, colorModel.getNumComponents()); - assertFalse(colorModel.hasAlpha()); - } - - @Test - public void testRead_08bit_U_7Band() throws Exception { - testReadFullLevel0(TYPE_8BIT_U, 7); - } - - @Test - public void testRead_08bitU_ColorMapped() throws Exception { - tableName = rasterTestData.loadRGBColorMappedRaster(); - testReadFullLevel0(TYPE_8BIT_U, 1, TYPE_8BIT_U, "testRead_8bitU_RGBColorMappedRaster"); - } - - @Test - public void testRead_08bit_S_1Band() throws Exception { - testReadFullLevel0(TYPE_8BIT_S, 1); - } - - @Test - public void testRead_08bit_S_7Band() throws Exception { - testReadFullLevel0(TYPE_8BIT_S, 7); - } - - @Test - public void testRead_16bit_S_1Band() throws Exception { - testReadFullLevel0(TYPE_16BIT_S, 1); - } - - @Test - public void testRead_16bit_S_7Band() throws Exception { - testReadFullLevel0(TYPE_16BIT_S, 7); - } - - @Test - public void testRead_16bit_U_1Band() throws Exception { - testReadFullLevel0(TYPE_16BIT_U, 1); - } - - @Test - public void testRead_16bit_U_7Band() throws Exception { - testReadFullLevel0(TYPE_16BIT_U, 7); - } - - @Test - public void testRead_16bit_U_ColorMapped() throws Exception { - int[] ARGB = new int[65536]; - ColorUtilities.expand(new Color[] { Color.BLACK, Color.WHITE }, ARGB, 0, ARGB.length); - IndexColorModel colorModel = ColorUtilities.getIndexColorModel(ARGB); - tableName = rasterTestData.getRasterTableName(TYPE_16BIT_U, 1, true); - rasterTestData.loadTestRaster(tableName, 1, TYPE_16BIT_U, colorModel); - testReadFullLevel0(TYPE_16BIT_U, 1, TYPE_16BIT_U, "testRead_16bit_U_ColorMapped"); - } - - @Test - public void testRead_32bit_REAL_1Band() throws Exception { - GridCoverage2D coverage = testReadFullLevel0(TYPE_32BIT_REAL, 1); - - RenderedImage geophysics = coverage.geophysics(true).getRenderedImage(); - RenderedImage rendered = coverage.geophysics(false).getRenderedImage(); - - ColorModel gpCm = geophysics.getColorModel(); - SampleModel gpSm = geophysics.getSampleModel(); - - ColorModel rCm = rendered.getColorModel(); - SampleModel rSm = geophysics.getSampleModel(); - - System.out.println("Geophysics: \t" + gpCm + "\n\t" + gpSm); - System.out.println("Rendered : \t" + rCm + "\n\t" + rSm); - - System.out.println(Float.NaN); - Float valueOf = Float.valueOf("NaN"); - System.out.println(valueOf); - System.out.println(valueOf.floatValue()); - } - - @Test - public void testRead_32bit_REAL_7Band() throws Exception { - testReadFullLevel0(TYPE_32BIT_REAL, 7); - } - - @Test - public void testRead_32bit_U_1Band() throws Exception { - testReadFullLevel0(TYPE_32BIT_U, 1); - } - - @Test - public void testRead_32bit_U_7Band() throws Exception { - testReadFullLevel0(TYPE_32BIT_U, 7); - } - - @Test - public void testRead_32bit_S_1Band() throws Exception { - testReadFullLevel0(TYPE_32BIT_S, 1); - } - - @Test - public void testRead_32bit_S_7Band() throws Exception { - testReadFullLevel0(TYPE_32BIT_S, 7); - } - - @Test - public void testRead_64bit_REAL_1Band() throws Exception { - testReadFullLevel0(TYPE_64BIT_REAL, 1); - } - - @Test - public void testRead_64bit_REAL_7Band() throws Exception { - testReadFullLevel0(TYPE_64BIT_REAL, 7); - } - - @Test - public void testReadSampleRGB() throws Exception { - tableName = rasterTestData.loadRGBRaster(); - testReadFullLevel0(TYPE_8BIT_U, 3, TYPE_8BIT_U, "sampleRGB"); - } - - @Test - public void testReadRasterCatalogFull() throws Exception { - tableName = rasterTestData.loadRasterCatalog(); - GridCoverage2D coverage = testReadFullLevel0(TYPE_8BIT_U, 3, TYPE_8BIT_U, "RasterCatalog"); - - GridGeometry2D gridGeometry = coverage.getGridGeometry(); - Envelope2D envelope2D = gridGeometry.getEnvelope2D(); - GridEnvelope2D gridRange2D = gridGeometry.getGridRange2D(); - - // assertEquals(0, envelope2D.getMinX(), 1); - // assertEquals(0, envelope2D.getMinY(), 1); - // assertEquals(512, envelope2D.getMaxX(), 1); - // assertEquals(512, envelope2D.getMaxY(), 1); - // - // assertEquals(512, gridRange2D.width); - // assertEquals(512, gridRange2D.height); - } - - @Test - public void testReadRasterCatalogSubset() throws Exception { - tableName = rasterTestData.loadRasterCatalog(); - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0) / 2; - final int reqHeight = originalGridRange.getSpan(1) / 2; - - GeneralEnvelope reqEnvelope = new GeneralEnvelope(originalEnvelope - .getCoordinateReferenceSystem()); - double deltaX = originalEnvelope.getSpan(0) / 6; - double deltaY = originalEnvelope.getSpan(1) / 6; - - double minx = originalEnvelope.getMinimum(0) + deltaX; - double miny = originalEnvelope.getMinimum(1) + deltaY; - double maxx = originalEnvelope.getMaximum(0) - deltaX; - double maxy = originalEnvelope.getMaximum(1) - deltaY; - reqEnvelope.setEnvelope(minx, miny, maxx, maxy); - - assertTrue(originalEnvelope.intersects(reqEnvelope, true)); - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - writeToDisk(coverage, "testReadRasterCatalogSubset"); - } - - @Test - public void testReadRasterCatalog2() throws Exception { - tableName = rasterTestData.loadRasterCatalog(); - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + tableName, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int reqWidth = originalGridRange.getSpan(0) / 10; - final int reqHeight = originalGridRange.getSpan(1) / 10; - - GeneralEnvelope reqEnvelope = new GeneralEnvelope(originalEnvelope - .getCoordinateReferenceSystem()); - double minx = originalEnvelope.getMinimum(0); - double miny = originalEnvelope.getMinimum(1); - double maxx = minx + originalEnvelope.getSpan(0);// / 2; - double maxy = miny + originalEnvelope.getSpan(1);// / 2; - reqEnvelope.setEnvelope(minx, miny, maxx, maxy); - - assertTrue(originalEnvelope.intersects(reqEnvelope, true)); - - final GridCoverage2D coverage = readCoverage(reader, reqWidth, reqHeight, reqEnvelope); - assertNotNull("read coverage returned null", coverage); - - writeToDisk(coverage, "testReadRasterCatalog2"); - } - - private GridCoverage2D testReadFullLevel0(final RasterCellType cellType, final int numBands) - throws Exception { - return testReadFullLevel0(cellType, numBands, cellType); - } - - private GridCoverage2D testReadFullLevel0(final RasterCellType cellType, final int numBands, - final RasterCellType resultingCellType) throws Exception { - - tableName = rasterTestData.getRasterTableName(cellType, numBands, false); - rasterTestData.loadTestRaster(tableName, numBands, cellType, null); - return testReadFullLevel0(cellType, numBands, resultingCellType, tableName + "_" + numBands - + "-Band"); - } - - private GridCoverage2D testReadFullLevel0(final RasterCellType cellType, final int numBands, - final RasterCellType resultingCellType, final String fileNamePostFix) throws Exception { - - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull("Couldn't obtain a reader for " + fileNamePostFix, reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - - final int origWidth = originalGridRange.getSpan(0); - final int origHeight = originalGridRange.getSpan(1); - - final GridCoverage2D coverage = readCoverage(reader, origWidth, origHeight, - originalEnvelope); - assertNotNull("read coverage returned null", coverage); - - assertEquals(numBands, coverage.getNumSampleDimensions()); - // for (int i = 0; i < numBands; i++) { - // NumberRange<?> range = cellType.getSampleValueRange(); - // GridSampleDimension sampleDimension = coverage.getSampleDimension(i); - // assertNotNull("Sample dimension #" + i, sampleDimension); - // assertEquals(range, sampleDimension.getRange()); - // } - - assertNotNull(coverage.getEnvelope()); - GeneralEnvelope envelope = (GeneralEnvelope) coverage.getEnvelope(); - assertTrue(originalEnvelope.intersects(envelope, true)); - - GridGeometry2D gridGeometry = coverage.getGridGeometry(); - - // ///////////////////////////////////////////////////////////assertEquals(originalGridRange, - // gridGeometry.getGridRange()); - - final RenderedImage geophysics = coverage.view(ViewType.NATIVE).getRenderedImage(); - assertNotNull(geophysics); - - final String fileName = "testReadFullLevel0_" + fileNamePostFix; - - if (!(geophysics.getColorModel() instanceof IndexColorModel)) { - // not sure why, but the geotiff writer goes OOM if it's an indexed image - writeToDisk(coverage, fileName); - } - writeToDisk(geophysics, fileName); - - // ////assertEquals(cellType.getDataBufferType(), image.getSampleModel().getDataType()); - final int[] sampleSize = geophysics.getSampleModel().getSampleSize(); - final ColorModel colorModel = geophysics.getColorModel(); - - if (colorModel instanceof IndexColorModel) { - switch (cellType) { - case TYPE_1BIT: - assertEquals("1-bit image should have been promoted to 8-bit", 8, sampleSize[0]); - break; - case TYPE_8BIT_U: - assertEquals("8-bit indexed image should have been " - + "promoted to 16bit to account for no-data values", 16, sampleSize[0]); - break; - case TYPE_16BIT_U: - assertEquals(16, sampleSize[0]); - break; - default: - throw new IllegalArgumentException(cellType.toString()); - } - } else { - for (int band = 0; band < numBands; band++) { - assertEquals(resultingCellType.getBitsPerSample(), sampleSize[band]); - } - } - - return coverage; - } - - @Test - public void tesReadOverlapsSampleRGBIamge() throws Exception { - tableName = rasterTestData.getRasterTableName(RasterCellType.TYPE_8BIT_U, 3); - rasterTestData.loadRGBRaster(); - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull(reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - - final CoordinateReferenceSystem originalCrs = originalEnvelope - .getCoordinateReferenceSystem(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - final int requestedWidth = originalGridRange.getSpan(0); - final int requestedHeight = originalGridRange.getSpan(1); - - final GeneralEnvelope requestedEnvelope; - final GridCoverage2D coverage; - { - final double minx = originalEnvelope.getMinimum(0); - final double miny = originalEnvelope.getMinimum(1); - - double shiftX = originalEnvelope.getSpan(0) / 2; - double shiftY = originalEnvelope.getSpan(1) / 2; - - double x1 = minx - shiftX; - double x2 = minx + shiftX; - double y1 = miny + shiftY; - double y2 = miny + 2 * shiftY; - - requestedEnvelope = new GeneralEnvelope(new ReferencedEnvelope(x1, x2, y1, y2, - originalCrs)); - coverage = readCoverage(reader, requestedWidth, requestedHeight, requestedEnvelope); - } - assertNotNull(coverage); - assertNotNull(coverage.getRenderedImage()); - CoordinateReferenceSystem crs = coverage.getCoordinateReferenceSystem(); - assertNotNull(crs); - - final String fileName = "tesReadOverlapsSampleRGBIamge"; - - final RenderedImage image = coverage.view(ViewType.GEOPHYSICS).getRenderedImage(); - assertNotNull(image); - writeToDisk(coverage, fileName); - - assertSame(originalCrs, crs); - - final Envelope returnedEnvelope = coverage.getEnvelope(); - - // these ones should equal to the tile dimension in the arcsde raster - int tileWidth = image.getTileWidth(); - int tileHeight = image.getTileHeight(); - assertTrue(tileWidth > 0); - assertTrue(tileHeight > 0); - - int fullWidth = originalGridRange.getSpan(0); - int fullHeight = originalGridRange.getSpan(1); - - GeneralEnvelope expectedEnvelope = new GeneralEnvelope(originalCrs); - expectedEnvelope.setRange(0, originalEnvelope.getMinimum(0), originalEnvelope.getMinimum(0) - + (originalEnvelope.getSpan(0) / 2)); - - expectedEnvelope.setRange(1, originalEnvelope.getMinimum(1), originalEnvelope.getMinimum(1) - + (originalEnvelope.getSpan(1) / 2)); - - LOGGER.info("\nRequested width : " + requestedWidth + "\nReturned width :" - + image.getWidth() + "\nRequested height:" + requestedHeight - + "\nReturned height :" + image.getHeight()); - - LOGGER.info("\nOriginal envelope : " + originalEnvelope + "\n requested envelope :" - + requestedEnvelope + "\n expected envelope :" + expectedEnvelope - + "\n returned envelope :" + returnedEnvelope); - - assertEquals(501, image.getWidth()); - assertEquals(501, image.getHeight()); - // assertEquals(expectedEnvelope, returnedEnvelope); - } - - @Test - public void tesReadOverlaps() throws Exception { - tableName = rasterTestData.getRasterTableName(RasterCellType.TYPE_8BIT_U, 1); - rasterTestData.loadTestRaster(tableName, 1, 100, 100, TYPE_8BIT_U, null, true, false, - SeRaster.SE_INTERPOLATION_NEAREST, null); - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull(reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - - final CoordinateReferenceSystem originalCrs = originalEnvelope - .getCoordinateReferenceSystem(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - final int requestedWidth = originalGridRange.getSpan(0); - final int requestedHeight = originalGridRange.getSpan(1); - - final GeneralEnvelope requestedEnvelope; - requestedEnvelope = new GeneralEnvelope(new ReferencedEnvelope(-100, 100, -100, 100, - originalCrs)); - - final GridCoverage2D coverage; - coverage = readCoverage(reader, requestedWidth, requestedHeight, requestedEnvelope); - - assertNotNull(coverage); - assertNotNull(coverage.getRenderedImage()); - - final String fileName = "tesReadOverlaps_Level0_8BitU_1-Band"; - - final RenderedImage image = coverage.view(ViewType.GEOPHYSICS).getRenderedImage(); - assertNotNull(image); - writeToDisk(coverage, fileName); - - final Envelope returnedEnvelope = coverage.getEnvelope(); - - // these ones should equal to the tile dimension in the arcsde raster - int tileWidth = image.getTileWidth(); - int tileHeight = image.getTileHeight(); - assertTrue(tileWidth > 0); - assertTrue(tileHeight > 0); - - int fullWidth = originalGridRange.getSpan(0); - int fullHeight = originalGridRange.getSpan(1); - - GeneralEnvelope expectedEnvelope = new GeneralEnvelope(originalCrs); - expectedEnvelope.setRange(0, 0, 100); - expectedEnvelope.setRange(1, 0, 100); - - LOGGER.info("\nRequested width : " + requestedWidth + "\nReturned width :" - + image.getWidth() + "\nRequested height:" + requestedHeight - + "\nReturned height :" + image.getHeight()); - - LOGGER.info("\nOriginal envelope : " + originalEnvelope + "\n requested envelope :" - + requestedEnvelope + "\n expected envelope :" + expectedEnvelope - + "\n returned envelope :" + returnedEnvelope); - - assertEquals(50, image.getWidth()); - assertEquals(50, image.getHeight()); - // assertEquals(expectedEnvelope, returnedEnvelope); - } - - @Test - public void tesReadNotOverlaps() throws Exception { - tableName = rasterTestData.getRasterTableName(RasterCellType.TYPE_8BIT_U, 1); - rasterTestData.loadTestRaster(tableName, 1, 100, 100, TYPE_8BIT_U, null, true, false, - SeRaster.SE_INTERPOLATION_NEAREST, null); - final AbstractGridCoverage2DReader reader = getReader(); - assertNotNull(reader); - - final GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope(); - - final CoordinateReferenceSystem originalCrs = originalEnvelope - .getCoordinateReferenceSystem(); - final GridEnvelope originalGridRange = reader.getOriginalGridRange(); - final int requestedWidth = originalGridRange.getSpan(0); - final int requestedHeight = originalGridRange.getSpan(1); - - final GeneralEnvelope nonOverlappingEnvelope; - nonOverlappingEnvelope = new GeneralEnvelope(new ReferencedEnvelope(300, 500, 300, 500, - originalCrs)); - - final GridCoverage2D coverage; - try { - coverage = readCoverage(reader, requestedWidth, requestedHeight, nonOverlappingEnvelope); - fail("Expected IAE, envelopes does not overlap"); - } catch (IllegalArgumentException e) { - assertTrue(true); - } - } - - private void writeToDisk(GridCoverage2D coverage, String fileName) throws Exception { - Object destination; - { - String file = System.getProperty("user.home"); - file += File.separator + "arcsde_test" + File.separator + fileName + ".tiff"; - File path = new File(file); - path.getParentFile().mkdirs(); - destination = path; - } - GeoTiffWriter writer = new GeoTiffWriter(destination); - - System.out.println("\n --- Writing to " + destination); - try { - long t = System.currentTimeMillis(); - writer.write(coverage, null); - System.out.println(" - wrote in " + t + "ms" + destination); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - private void writeToDisk(final RenderedImage image, String fileName) throws Exception { - if (!DEBUG) { - LOGGER.fine("DEBUG == false, not writing image to disk"); - return; - } - String file = System.getProperty("user.home"); - file += File.separator + "arcsde_test" + File.separator + fileName; - File geophysics = new File(file + "_geophysics.tiff"); - File rendered = new File(file + "_rendered.tiff"); - geophysics.getParentFile().mkdirs(); - System.out.println("\n --- Writing to " + file); - - try { - ImageIO.write(image, "TIFF", geophysics); - - ImageIO.write(FormatDescriptor.create(image, Integer.valueOf(DataBuffer.TYPE_BYTE), - null), "TIFF", rendered); - } catch (Exception e) { - e.printStackTrace(); - throw e; - } - } - - private GridCoverage2D readCoverage(final AbstractGridCoverage2DReader reader, - final int reqWidth, final int reqHeight, final Envelope reqEnv) throws Exception { - - GeneralParameterValue[] requestParams = new Parameter[2]; - final CoordinateReferenceSystem crs = reader.getCrs(); - - GridGeometry2D gg2d; - gg2d = new GridGeometry2D(new GridEnvelope2D(new Rectangle(reqWidth, reqHeight)), reqEnv); - - requestParams[0] = new Parameter<GridGeometry2D>(AbstractGridFormat.READ_GRIDGEOMETRY2D, - gg2d); - requestParams[1] = new Parameter<OverviewPolicy>(AbstractGridFormat.OVERVIEW_POLICY, - OverviewPolicy.SPEED); - - final GridCoverage2D coverage; - coverage = (GridCoverage2D) reader.read(requestParams); - - return coverage; - } - - private AbstractGridCoverage2DReader getReader() throws IOException { - final ArcSDEConnectionConfig config = rasterTestData.getConnectionPool().getConfig(); - - final String rgbUrl = "sde://" + config.getUserName() + ":" + config.getPassword() + "@" - + config.getServerName() + ":" + config.getPortNumber() + "/" - + config.getDatabaseName() + "#" + tableName; - - final ArcSDERasterFormat format = new ArcSDERasterFormatFactory().createFormat(); - // we can't create statistics here so tell ArcSDERasterFormat not to fail - format.setStatisticsMandatory(false); - - AbstractGridCoverage2DReader reader = format.getReader(rgbUrl); - return reader; - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/BitmaskToNoDataConverterTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/BitmaskToNoDataConverterTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/BitmaskToNoDataConverterTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,271 +0,0 @@ -package org.geotools.arcsde.gce; - -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_1BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_4BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_U; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.awt.Dimension; -import java.awt.Point; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.geotools.arcsde.gce.BitmaskToNoDataConverter.Unsigned8bitConverter; -import org.geotools.geometry.jts.ReferencedEnvelope; -import org.junit.Test; - -public class BitmaskToNoDataConverterTest { - - @Test - public void testGetInstance8BitU() throws IOException { - RasterDatasetInfo rasterInfo; - BitmaskToNoDataConverter noData; - - int noDataValue; - double statsMin; - double statsMax; - - noDataValue = 0; - statsMin = 1; - statsMax = 255; - rasterInfo = createRasterInfo(TYPE_8BIT_U, noDataValue, statsMin, statsMax); - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, 0); - assertNotNull(noData); - - noDataValue = 255; - statsMin = 0; - statsMax = 254; - rasterInfo = createRasterInfo(TYPE_8BIT_U, noDataValue, statsMin, statsMax); - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, 0); - assertNotNull(noData); - assertTrue(noData instanceof BitmaskToNoDataConverter.Unsigned8bitConverter); - - final int samplesPerTile = rasterInfo.getTileDimension(0).width - * rasterInfo.getTileDimension(0).height; - - /* - * bulk set, a whole no-data tile - */ - byte[] tileData = new byte[samplesPerTile]; - noData.setAll(1L, tileData); - DataInputStream in = new DataInputStream(new ByteArrayInputStream(tileData)); - for (int sampleN = 0; sampleN < samplesPerTile; sampleN++) { - assertEquals(255, in.readByte() & 0xFF); - } - - /* - * bitmask data states the first 8 and last 8 samples are no-data - */ - byte[] bitmaskData = new byte[(int) Math.ceil(samplesPerTile / 8D)]; - Arrays.fill(bitmaskData, (byte) 0xFF); - // set the first 8 and last 8 samples to no-data - bitmaskData[0] = 0x00; - bitmaskData[bitmaskData.length - 1] = 0x00; - - final byte dataValue = 5; - byte[] expected = new byte[samplesPerTile]; - Arrays.fill(expected, dataValue); - Arrays.fill(expected, 0, 8, (byte) noDataValue); - Arrays.fill(expected, expected.length - 8, expected.length, (byte) noDataValue); - - tileData = new byte[samplesPerTile]; - Arrays.fill(tileData, dataValue); - noData.setNoData(1L, tileData, bitmaskData); - assertTrue("Arrays differ, expected:" + Arrays.toString(expected) + ", actual:" - + Arrays.toString(tileData), Arrays.equals(expected, tileData)); - - /* - * set individual sample - */ - tileData = new byte[samplesPerTile]; - noData.setNoData(1L, 0, tileData); - assertEquals(noDataValue, tileData[0] & 0xFF); - noData.setNoData(1L, 5, tileData); - assertEquals(noDataValue, tileData[5] & 0xFF); - noData.setNoData(1L, tileData.length - 1, tileData); - assertEquals(noDataValue, tileData[tileData.length - 1] & 0xFF); - } - - @Test - public void testGetInstance1Bit() { - RasterDatasetInfo rasterInfo; - BitmaskToNoDataConverter noData; - - int noDataValue; - double statsMin; - double statsMax; - - noDataValue = 0; - statsMin = 0; - statsMax = 1; - rasterInfo = createRasterInfo(TYPE_1BIT, noDataValue, statsMin, statsMax); - try { - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, 0); - fail("Expected UOE, noDataValue == 0 is non valid"); - } catch (UnsupportedOperationException e) { - assertTrue(true); - } - - noDataValue = 2; - statsMin = 0; - statsMax = 1; - rasterInfo = createRasterInfo(TYPE_1BIT, noDataValue, statsMin, statsMax); - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, 0); - assertNotNull(noData); - // make sure promotion from 1 to 8 bit is being taking place here and hence we got a 8-bit-u - // no-data setter - assertTrue(noData instanceof Unsigned8bitConverter); - } - - @Test - public void testGetInstance4Bit() { - RasterDatasetInfo rasterInfo; - BitmaskToNoDataConverter noData; - - int noDataValue; - double statsMin; - double statsMax; - - statsMin = TYPE_4BIT.getSampleValueRange().getMinimum(); - statsMax = TYPE_4BIT.getSampleValueRange().getMaximum(); - noDataValue = 0; - - rasterInfo = createRasterInfo(TYPE_4BIT, noDataValue, statsMin, statsMax); - try { - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, 0); - fail("Expected UOE, noDataValue == 0 is non valid"); - } catch (UnsupportedOperationException e) { - assertTrue(true); - } - - noDataValue = (int) (statsMax + 1); - - rasterInfo = createRasterInfo(TYPE_4BIT, noDataValue, statsMin, statsMax); - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, 0); - assertNotNull(noData); - // make sure promotion from 4 to 8 bit is being taking place here and hence we got a 8-bit-u - // no-data setter - assertTrue(noData instanceof Unsigned8bitConverter); - } - - @Test - public void testGetInstance16BitU() throws IOException { - RasterDatasetInfo rasterInfo; - BitmaskToNoDataConverter noData; - - final int noDataValue = (int) TYPE_16BIT_U.getSampleValueRange().getMaximum(); - double statsMin; - double statsMax; - - statsMin = TYPE_16BIT_U.getSampleValueRange().getMinimum(); - statsMax = TYPE_16BIT_U.getSampleValueRange().getMaximum() - 1; - rasterInfo = createRasterInfo(TYPE_16BIT_U, noDataValue, statsMin, statsMax); - noData = BitmaskToNoDataConverter.getInstance(rasterInfo, 0); - assertNotNull(noData); - - final int samplesPerTile = rasterInfo.getTileDimension(0).width - * rasterInfo.getTileDimension(0).height; - - final int dataValue = 5; - - byte[] tileData = new byte[TYPE_16BIT_U.getBitsPerSample() * samplesPerTile]; - noData.setAll(1L, tileData); - DataInputStream in = new DataInputStream(new ByteArrayInputStream(tileData)); - for (int sampleN = 0; sampleN < samplesPerTile; sampleN++) { - assertEquals(noDataValue, in.readUnsignedShort()); - } - - byte[] bitmaskData = new byte[(int) Math.ceil(samplesPerTile / 8D)]; - Arrays.fill(bitmaskData, (byte) 0xFF); - // set the first 8 and last 8 samples to no-data - bitmaskData[0] = 0x00; - bitmaskData[bitmaskData.length - 1] = 0x00; - - byte[] expected; - { - ByteArrayOutputStream actualOut = new ByteArrayOutputStream(); - DataOutputStream actualWriter = new DataOutputStream(actualOut); - - ByteArrayOutputStream expectedOut = new ByteArrayOutputStream(); - DataOutputStream expectedWriter = new DataOutputStream(expectedOut); - for (int i = 0; i < samplesPerTile; i++) { - actualWriter.writeShort(dataValue); - - if (i < 8 || i >= samplesPerTile - 8) { - expectedWriter.writeShort(noDataValue); - } else { - expectedWriter.writeShort(dataValue); - } - } - tileData = actualOut.toByteArray(); - expected = expectedOut.toByteArray(); - } - - noData.setNoData(1L, tileData, bitmaskData); - assertTrue("Arrays differ, expected:" + Arrays.toString(expected) + ", actual:" - + Arrays.toString(tileData), Arrays.equals(expected, tileData)); - - /* - * set individual sample - */ - final int bitsPerSample = TYPE_16BIT_U.getBitsPerSample(); - tileData = new byte[bitsPerSample * samplesPerTile]; - - in = new DataInputStream(new ByteArrayInputStream(tileData)); - noData.setNoData(1L, 0, tileData); - assertEquals(noDataValue, in.readUnsignedShort()); - - in = new DataInputStream(new ByteArrayInputStream(tileData, 5 * (bitsPerSample / 8), 2)); - noData.setNoData(1L, 5, tileData); - assertEquals(noDataValue, in.readUnsignedShort()); - - in = new DataInputStream(new ByteArrayInputStream(tileData, (samplesPerTile - 1) - * (bitsPerSample / 8), 2)); - noData.setNoData(1L, samplesPerTile - 1, tileData); - assertEquals(noDataValue, in.readUnsignedShort()); - } - - private RasterDatasetInfo createRasterInfo(RasterCellType nativeType, Number noDataValue, - double statsMin, double statsMax) { - - RasterDatasetInfo datasetInfo = new RasterDatasetInfo(); - - List<RasterInfo> datasetRasters = new ArrayList<RasterInfo>(); - // fake a 3x8 pixel raster so it's a matrix of 24 elements and it matches a full bitmask - // array (no unused bitmask bits) - RasterInfo rasterInfo = new RasterInfo(3, 8); - datasetRasters.add(rasterInfo); - - rasterInfo.addPyramidLevel(0, new ReferencedEnvelope(), new Point(), new Point(), 10, 10, - new Dimension(100, 100)); - List<RasterBandInfo> bands = new ArrayList<RasterBandInfo>(); - RasterBandInfo bandInfo = new RasterBandInfo(); - bands.add(bandInfo); - - bandInfo.bandId = 1L; - // the native type - bandInfo.cellType = nativeType; - // the target type will be determined based on the native type bounds and the band's - // statistics - bandInfo.noDataValue = noDataValue; - bandInfo.statsMin = statsMin; - bandInfo.statsMax = statsMax; - - rasterInfo.setBands(bands); - - datasetInfo.setPyramidInfo(datasetRasters); - - return datasetInfo; - } - -} Deleted: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterInfoTest.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterInfoTest.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterInfoTest.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,192 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-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.arcsde.gce; - -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.geom.Point2D; -import java.awt.geom.Point2D.Double; -import java.io.IOException; - -import org.geotools.arcsde.session.Command; -import org.geotools.arcsde.session.ISession; -import org.geotools.arcsde.session.ISessionPool; -import org.geotools.geometry.jts.ReferencedEnvelope; -import org.geotools.referencing.CRS; -import org.geotools.referencing.crs.DefaultEngineeringCRS; -import org.junit.Test; -import org.opengis.referencing.FactoryException; -import org.opengis.referencing.crs.CoordinateReferenceSystem; - -import com.esri.sde.sdk.client.SeConnection; -import com.esri.sde.sdk.client.SeCoordinateReference; -import com.esri.sde.sdk.client.SeException; -import com.esri.sde.sdk.client.SeObjectId; -import com.esri.sde.sdk.client.SeQuery; -import com.esri.sde.sdk.client.SeRaster; -import com.esri.sde.sdk.client.SeRasterAttr; -import com.esri.sde.sdk.client.SeRasterColumn; -import com.esri.sde.sdk.client.SeRow; -import com.esri.sde.sdk.client.SeSqlConstruct; - -/** - * Tests the functionality of the ArcSDE raster-display package to read rasters from an ArcSDE - * database - * - * @author Saul Farber, (based on ArcSDEPoolTest by Gabriel Roldan) - * @source $URL: - * http://svn.geotools.org/geotools/trunk/gt/modules/plugin/arcsde/datastore/src/test/java - * /org/geotools/arcsde/gce/ArcSDEPyramidTest.java $ - * @version $Id$ - */ -public class RasterInfoTest { - - /** - * test case for {@link RasterInfo#pickOptimalRasterLevel(ReferencedEnvelope, Rectangle)} - */ - @Test - public void testPickOptimalPyramidLevel() { - - } - - /** - * Creates a pyramid equivalent to the one created by ArcSDE: <code> - * <pre> - * ArcSDEPyramid[Nº levels: 10, tile size: 63x63 - * Levels: - * [0 size: 1013x1021 xRes: 2.0000000000000004 yRes: 2.0 xOffset: 0 yOffset: 0 extent: ReferencedEnvelope[0.0 : 2026.0000000000002, 0.0 : 2042.0] tilesWide: 17 tilesHigh: 17] - * [1 size: 507x511 xRes: 3.996055226824458 yRes: 3.996086105675147 xOffset: 0 yOffset: 0 extent: ReferencedEnvelope[0.0 : 2026.0000000000002, 0.0 : 2042.0] tilesWide: 9 tilesHigh: 9] - * [2 size: 254x256 xRes: 7.976377952755906 yRes: 7.9765625 xOffset: 0 yOffset: 0 extent: ReferencedEnvelope[2.0019762845849804 : 2028.0019762845852, -2.0019607843137237 : 2039.9980392156863] tilesWide: 5 tilesHigh: 5] - * [3 size: 127x128 xRes: 15.889701534343782 yRes: 15.890563725490196 xOffset: 0 yOffset: 0 extent: ReferencedEnvelope[6.005928853754941 : 2023.9980237154152, 2.0019607843137237 : 2035.9941176470588] tilesWide: 3 tilesHigh: 3] - * [4 size: 64x64 xRes: 31.53112648221344 yRes: 31.530882352941177 xOffset: 0 yOffset: 0 extent: ReferencedEnvelope[14.013833992094863 : 2032.005928853755, 10.009803921568619 : 2027.986274509804] tilesWide: 2 tilesHigh: 2] - * [5 size: 32x32 xRes: 62.06126482213439 yRes: 62.06078431372549 xOffset: 0 yOffset: 0 extent: ReferencedEnvelope[30.029644268774707 : 2015.9901185770752, 26.025490196078408 : 2011.9705882352941] tilesWide: 1 tilesHigh: 1] - * [6 size: 16x16 xRes: 120.11857707509883 yRes: 120.11764705882354 xOffset: 0 yOffset: 0 extent: ReferencedEnvelope[62.06126482213439 : 1983.9584980237157, 58.05686274509799 : 1979.9392156862746] tilesWide: 1 tilesHigh: 1] - * ] - * </pre> - * </code> - * - * @return - */ - private RasterInfo createPyramid() { - - RasterInfo pyramid = new RasterInfo(1013, 1021); - - final CoordinateReferenceSystem crs = DefaultEngineeringCRS.CARTESIAN_2D; - - final Point imageOffset = new Point(0, 0); - final Point2D extentOffset = new Double(0, 0); - - pyramid.addPyramidLevel(0, new ReferencedEnvelope(0, 2026, 0, 2042, crs), imageOffset, - extentOffset, 17, 17, new Dimension(1013, 1021)); - pyramid.addPyramidLevel(1, new ReferencedEnvelope(0, 2026.0000000000002, 0, 2042, crs), - imageOffset, extentOffset, 9, 9, new Dimension(507, 511)); - - pyramid.addPyramidLevel(2, new ReferencedEnvelope(2.0019762845849804, 2028.0019762845852, - -2.0019607843137237, 2039.9980392156863, crs), imageOffset, extentOffset, 5, 5, - new Dimension(254, 256)); - - pyramid.addPyramidLevel(3, new ReferencedEnvelope(6.005928853754941, 2023.9980237154152, - 2.0019607843137237, 2035.9941176470588, crs), imageOffset, extentOffset, 3, 3, - new Dimension(127, 128)); - - pyramid.addPyramidLevel(4, new ReferencedEnvelope(4.013833992094863, 2032.005928853755, - 10.009803921568619, 2027.986274509804, crs), imageOffset, extentOffset, 2, 2, - new Dimension(64, 64)); - - pyramid.addPyramidLevel(5, new ReferencedEnvelope(30.029644268774707, 2015.9901185770752, - 26.025490196078408, 2011.9705882352941, crs), imageOffset, extentOffset, 1, 1, - new Dimension(32, 23)); - - pyramid.addPyramidLevel(6, new ReferencedEnvelope(62.06126482213439, 1983.9584980237157, - 58.05686274509799, 1979.9392156862746, crs), imageOffset, extentOffset, 1, 1, - new Dimension(16, 16)); - return pyramid; - } - - @Test - public void testArcSDEPyramidHypothetical() throws Exception { - RasterInfo pyramid = createPyramid(); - System.out.println(pyramid); - - RasterTestData testData = new RasterTestData(); - testData.setUp(); - String tableName = testData.getRasterTableName(RasterCellType.TYPE_8BIT_U, 1, false); - testData.loadTestRaster(tableName, 1, 1013, 1021, RasterCellType.TYPE_8BIT_U, null, true, - false, SeRaster.SE_INTERPOLATION_NEAREST, 9); - - ISessionPool pool = testData.getConnectionPool(); - ISession conn = pool.getSession(); - - final SeQuery q = conn.createAndExecuteQuery(new String[] { "RASTER" }, new SeSqlConstruct( - tableName)); - - try { - pyramid = conn.issue(new Command<RasterInfo>() { - @Override - public RasterInfo execute(ISession session, SeConnection connection) - throws SeException, IOException { - SeRow r = q.fetch(); - SeRasterAttr rAttr = r.getRaster(0); - - SeObjectId rasterColumnId = rAttr.getRasterColumnId(); - SeRasterColumn rasterColumn = new SeRasterColumn(connection, rasterColumnId); - SeCoordinateReference coordRef = rasterColumn.getCoordRef(); - String coordRefWKT = coordRef.getCoordSysDescription(); - CoordinateReferenceSystem crs; - try { - crs = CRS.parseWKT(coordRefWKT); - } catch (FactoryException e) { - throw new RuntimeException(e); - } - - RasterInfo pyramid = new RasterInfo(rAttr, crs); - return pyramid; - } - }); - } finally { - conn.dispose(); - } - - System.out.println(pyramid); - - /* - * ArcSDEPyramid pyramid = new ArcSDEPyramid(10, 10); pyramid.addPyramidLevel(0, new - * SeExtent(0, 0, 100, 100), null, null, 10, 10, new Dimension( 100, 100)); - * pyramid.addPyramidLevel(1, new SeExtent(0, 0, 100, 100), null, null, 5, 5, new Dimension( - * 50, 50)); - * - * RasterQueryInfo ret = pyramid.fitExtentToRasterPixelGrid(new ReferencedEnvelope(0, 10, 0, - * 10, null), 0); assertTrue(ret.envelope.equals(new ReferencedEnvelope(0, 10, 0, 10, - * null))); assertTrue(ret.image.width == 10 && ret.image.height == 10); - * - * ret = pyramid.fitExtentToRasterPixelGrid(new ReferencedEnvelope(0, 9, 0, 9, null), 0); - * assertTrue(ret.envelope.intersects((BoundingBox) new ReferencedEnvelope(0, 9, 0, 9, - * null))); assertTrue(ret.image.width == 9 && ret.image.height == 9); - * - * ret = pyramid.fitExtentToRasterPixelGrid(new ReferencedEnvelope(15, 300, 15, 300, null), - * 1); assertTrue(ret.envelope.equals(new ReferencedEnvelope(14, 300, 14, 300, null))); - * assertTrue(ret.image.width == 143 && ret.image.height == 143); - * - * ret = pyramid.fitExtentToRasterPixelGrid( new ReferencedEnvelope(-100, 200, -100, 200, - * null), 1); assertTrue(ret.envelope.equals(new ReferencedEnvelope(-100, 200, -100, 200, - * null))); assertTrue(ret.image.width == 150 && ret.image.height == 150); - */ - } - -} \ No newline at end of file Deleted: trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterTestData.java =================================================================== --- trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterTestData.java 2009-11-03 15:07:00 UTC (rev 34312) +++ trunk/modules/plugin/arcsde/datastore/src/test/java/org/geotools/arcsde/gce/RasterTestData.java 2009-11-03 16:03:24 UTC (rev 34313) @@ -1,1581 +0,0 @@ -/* - * GeoTools - The Open Source Java GIS Toolkit - * http://geotools.org - * - * (C) 2002-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.arcsde.gce; - -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_16BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_1BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_REAL; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_32BIT_U; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_4BIT; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_64BIT_REAL; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_S; -import static org.geotools.arcsde.gce.RasterCellType.TYPE_8BIT_U; - -import java.awt.Rectangle; -import java.awt.Transparency; -import java.awt.color.ColorSpace; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.ComponentColorModel; -import java.awt.image.ComponentSampleModel; -import java.awt.image.DataBuffer; -import java.awt.image.DataBufferByte; -import java.awt.image.DataBufferShort; -import java.awt.image.DataBufferUShort; -import java.awt.image.IndexColorModel; -import java.awt.image.Raster; -import java.awt.image.RenderedImage; -import java.awt.image.SampleModel; -import java.awt.image.WritableRaster; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.logging.Logger; - -import javax.imageio.ImageIO; - -import org.geotools.arcsde.ArcSdeException; -import org.geotools.arcsde.data.TestData; -import org.geotools.arcsde.gce.producer.ArcSDERasterFloatProducerImpl; -import org.geotools.arcsde.gce.producer.ArcSDERasterOneBitPerBandProducerImpl; -import org.geotools.arcsde.gce.producer.ArcSDERasterOneBytePerBandProducerImpl; -import org.geotools.arcsde.gce.producer.ArcSDERasterProducer; -import org.geotools.arcsde.session.ArcSDEConnectionConfig; -import org.geotools.arcsde.session.Command; -import org.geotools.arcsde.session.ISession; -import org.geotools.arcsde.session.ISessionPool; -import org.geotools.arcsde.session.UnavailableConnectionException; -import org.geotools.util.logging.Logging; - -import com.esri.sde.sdk.client.SDEPoint; -import com.esri.sde.sdk.client.SeColumnDefinition; -import com.esri.sde.sdk.client.SeConnection; -import com.esri.sde.sdk.client.SeCoordinateReference; -import com.esri.sde.sdk.client.SeException; -import com.esri.sde.sdk.client.SeExtent; -import com.esri.sde.sdk.client.SeInsert; -import com.esri.sde.sdk.client.SeQuery; -import com.esri.sde.sdk.client.SeRaster; -import com.esri.sde.sdk.client.SeRasterAttr; -import com.esri.sde.sdk.client.SeRasterBand; -import com.esri.sde.sdk.client.SeRasterColumn; -import com.esri.sde.sdk.client.SeRasterConstraint; -import com.esri.sde.sdk.client.SeRasterConsumer; -import com.esri.sde.sdk.client.SeRasterProducer; -import com.esri.sde.sdk.client.SeRasterRenderedImage; -import com.esri.sde.sdk.client.SeRegistration; -import com.esri.sde.sdk.client.SeRow; -import com.esri.sde.sdk.client.SeSqlConstruct; -import com.esri.sde.sdk.client.SeTable; -import com.esri.sde.sdk.pe.PeFactory; -import com.esri.sde.sdk.pe.PePCSDefs; -import com.esri.sde.sdk.pe.PeProjectedCS; - -@SuppressWarnings( { "nls", "deprecation" }) -public class RasterTestData { - - private TestData testData; - - private static final Logger LOGGER = Logging.getLogger("org.geotools.arcsde.gce"); - - private boolean overrideExistingTables; - - public void setUp() throws IOException { - // load a raster dataset into SDE - testData = new TestData(); - testData.setUp(); - overrideExistingTables = true; - } - - public void tearDown() throws Exception { - // destroy all sample tables; - // for (RasterTableName table : RasterTableName.values()) { - // String tableName = getRasterTableName(table); - // testData.deleteTable(tableName); - // } - testData.tearDown(false, true); - } - - public void deleteTable(final String tableName) throws Exception { - testData.deleteTable(tableName, false); - } - - public ISessionPool getConnectionPool() throws IOException { - return testData.getConnectionPool(); - } - - public String getRasterTableName(final RasterCellType cellType, final int numBands, - final boolean colorMapped) throws IOException { - String testTableName = getRasterTableName(cellType, numBands); - if (colorMapped) { - testTableName += "_CM"; - } - return testTableName; - } - - public String getRasterTableName(final RasterCellType cellType, final int numBands) - throws IOException { - String typeString = cellType.toString(); - typeString = typeString.substring("TYPE_".length()); - String testTableName = testData.getTempTableName() + "_" + cellType + "_" + numBands - + "_BAND"; - return testTableName; - } - - public String createCoverageUrl(final RasterCellType cellType, final int numBands) - throws IOException { - final String rasterTableName = getRasterTableName(cellType, numBands); - return createCoverageUrl(rasterTableName); - } - - public String createCoverageUrl(final RasterCellType cellType, final int numBands, - final boolean colorMapped) throws IOException { - final String rasterTableName = getRasterTableName(cellType, numBands, colorMapped); - return createCoverageUrl(rasterTableName); - } - - public String createCoverageUrl(final String rasterTableName) throws IOException { - final ArcSDEConnectionConfig config = getConnectionPool().getConfig(); - String url = "sde://" + config.getUserName() + ":" + config.getPassword() + "@" - + config.getServerName() + ":" + config.getPortNumber() + "/" - + config.getDatabaseName() + "#" + rasterTableName; - return url; - } - - public String getRasterTestDataProperty(String propName) { - return testData.getConProps().get(propName); - } - - // public void load1bitRaster() throws Exception { - // final String tableName = getRasterTableName(RasterTableName.ONEBIT); - // final int numberOfBands = 1; - // final int pixelType = SeRaster.SE_PIXEL_TYPE_1BIT; - // final boolean pyramiding = true; - // final boolean skipLevelOne = false; - // final int interpolationType = SeRaster.SE_INTERPOLATION_NEAREST; - // final IndexColorModel colorModel = null; - // loadTestRaster(tableName, numberOfBands, pixelType, colorModel, pyramiding, skipLevelOne, - // interpolationType); - // } - - /** - * Loads the 1bit raster test data into the table given in - * {@link RasterTestData#get1bitRasterTableName()} - * - * @throws Exception - */ - public String load1bitRaster() throws Exception { - ISession session = getConnectionPool().getSession(); - final String tableName = getRasterTableName(TYPE_1BIT, 1); - - // clean out the table if it's currently in-place - testData.deleteTable(tableName); - // build the base business table. We'll add the raster data to it in a bit - createRasterBusinessTempTable(tableName, session); - session.dispose(); - - SeExtent imgExtent = new SeExtent(231000, 898000, 231000 + 500, 898000 + 500); - SeCoordinateReference crs = getSeCRSFromPeProjectedCSId(PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M); - String rasterFilename = testData.getConProps().get("sampledata.onebitraster"); - ArcSDERasterProducer producer = new ArcSDERasterOneBitPerBandProducerImpl(); - - importRasterImage(tableName, crs, rasterFilename, SeRaster.SE_PIXEL_TYPE_1BIT, imgExtent, - producer); - - return tableName; - } - - /** - * Loads the 1bit raster test data into the table given in - * {@link RasterTestData#get1bitRasterTableName()} - * - * @throws Exception - */ - public String loadRGBRaster() throws Exception { - final String tableName = getRasterTableName(TYPE_8BIT_U, 3); - - // clean out the table if it's currently in-place - testData.deleteTable(tableName); - // build the base business table. We'll add the raster data to it in a bit - ISession session = getConnectionPool().getSession(); - try { - createRasterBusinessTempTable(tableName, session); - } finally { - session.dispose(); - } - - SeExtent imgExtent = new SeExtent(231000, 898000, 231000 + 501, 898000 + 501); - SeCoordinateReference crs = getSeCRSFromPeProjectedCSId(PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M); - String rasterFilename = testData.getConProps().get("sampledata.rgbraster"); - ArcSDERasterProducer prod = new ArcSDERasterOneBytePerBandProducerImpl(); - - importRasterImage(tableName, crs, rasterFilename, SeRaster.SE_PIXEL_TYPE_8BIT_U, imgExtent, - prod); - - return tableName; - } - - public String loadRGBColorMappedRaster() throws Exception { - final String tableName = getRasterTableName(TYPE_8BIT_U, 1, true); - - // clean out the table if it's currently in-place - testData.deleteTable(tableName); - - // build the base business table. We'll add the raster data to it in a bit - // Note that this DOESN'T LOAD THE COLORMAP RIGHT NOW. - ISession session = getConnectionPool().getSession(); - try { - createRasterBusinessTempTable(tableName, session); - } finally { - session.dispose(); - } - - SeExtent imgExtent = new SeExtent(231000, 898000, 231000 + 500, 898000 + 500); - SeCoordinateReference crs = getSeCRSFromPeProjectedCSId(PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M); - String rasterFilename = testData.getConProps().get("sampledata.rgbraster-colormapped"); - ArcSDERasterProducer prod = new ArcSDERasterOneBytePerBandProducerImpl(); - - importRasterImage(tableName, crs, rasterFilename, SeRaster.SE_PIXEL_TYPE_8BIT_U, imgExtent, - prod); - return tableName; - } - - public String loadOneByteGrayScaleRaster() throws Exception { - final String tableName = getRasterTableName(TYPE_8BIT_U, 1); - - // clean out the table if it's currently in-place - testData.deleteTable(tableName); - // build the base business table. We'll add the raster data to it in a bit - // Note that this DOESN'T LOAD THE COLORMAP RIGHT NOW. - ISession session = getConnectionPool().getSession(); - try { - createRasterBusinessTempTable(tableName, session); - } finally { - session.dispose(); - } - - SeExtent imgExtent = new SeExtent(231000, 898000, 231000 + 500, 898000 + 500); - SeCoordinateReference crs = getSeCRSFromPeProjectedCSId(PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M); - String rasterFilename = testData.getConProps().get("sampledata.onebyteonebandraster"); - ArcSDERasterProducer prod = new ArcSDERasterOneBytePerBandProducerImpl(); - - importRasterImage(tableName, crs, rasterFilename, SeRaster.SE_PIXEL_TYPE_8BIT_U, imgExtent, - prod); - - return tableName; - } - - public String loadFloatRaster() throws Exception { - final String tableName = getRasterTableName(TYPE_32BIT_REAL, 1); - - // clean out the table if it's currently in-place - testData.deleteTable(tableName); - // build the base business table. We'll add the raster data to it in a bit - // Note that this DOESN'T LOAD THE COLORMAP RIGHT NOW. - ISession session = getConnectionPool().getSession(); - try { - createRasterBusinessTempTable(tableName, session); - } finally { - session.dispose(); - } - - SeExtent imgExtent = new SeExtent(245900, 899600, 246300, 900000); - SeCoordinateReference crs = getSeCRSFromPeProjectedCSId(PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M); - String rasterFilename = testData.getConProps().get("sampledata.floatraster"); - ArcSDERasterProducer prod = new ArcSDERasterFloatProducerImpl(); - - importRasterImage(tableName, crs, rasterFilename, SeRaster.SE_PIXEL_TYPE_32BIT_REAL, - imgExtent, prod); - - return tableName; - } - - public SeCoordinateReference getSeCRSFromPeProjectedCSId(int PeProjectedCSId) { - SeCoordinateReference crs; - try { - PeProjectedCS pcs = (PeProjectedCS) PeFactory.factory(PeProjectedCSId); - crs = new SeCoordinateReference(); - crs.setCoordSysByDescription(pcs.toString()); - } catch (Throwable t) { - throw new RuntimeException(t); - } - return crs; - } - - public void createRasterBusinessTempTable(final String tableName, final ISession conn) - throws Exception { - conn.issue(new Command<Void>() { - - @Override - public Void execute(ISession session, SeConnection connection) throws SeException, - IOException { - SeColumnDefinition[] colDefs = new SeColumnDefinition[1]; - SeTable table = new SeTable(connection, tableName); - - // first column to be SDE managed feature id - colDefs[0] = new SeColumnDefinition("ROW_ID", SeColumnDefinition.TYPE_INTEGER, 10, - 0, false); - table.create(colDefs, testData.getConfigKeyword()); - - /* - * Register the column to be used as feature id and managed by sde - */ - SeRegistration reg = new SeRegistration(connection, table.getName()); - LOGGER.fine("setting rowIdColumnName to ROW_ID in table " + reg.getTableName()); - reg.setRowIdColumnName("ROW_ID"); - final int rowIdColumnType = SeRegistration.SE_REGISTRATION_ROW_ID_COLUMN_TYPE_SDE; - reg.setRowIdColumnType(rowIdColumnType); - reg.alter(); - return null; - } - }); - } - - public void importRasterImage(final String tableName, SeCoordinateReference crs, - final String rasterFilename, final int sePixelType, SeExtent extent, - ArcSDERasterProducer prod) throws Exception { - importRasterImage(tableName, crs, rasterFilename, sePixelType, extent, prod, null); - } - - public void importRasterImage(final String tableName, final SeCoordinateReference crs, - final String rasterFilename, final int sePixelType, final SeExtent extent, - final ArcSDERasterProducer prod, final IndexColorModel colorModel) throws Exception { - - Command<Void> importRasterCmd = new Command<Void>() { - @Override - public Void execute(ISession session, SeConnection connection) throws SeException, - IOException { - createRasterColumn(tableName, crs, session); - - IndexColorModel indexCM = colorModel; - - // now start loading the actual raster data - BufferedImage sampleImage = ImageIO.read(org.geotools.test.TestData.getResource( - null, rasterFilename)); - { - ColorModel imgColorModel = sampleImage.getColorModel(); - if (imgColorModel instanceof IndexColorModel) { - indexCM = (IndexColorModel) imgColorModel; - } - } - int imageWidth = sampleImage.getWidth(), imageHeight = sampleImage.getHeight(); - - SeRasterAttr attr = new SeRasterAttr(true); - attr.setImageSize(imageWidth, imageHeight, sampleImage.getSampleModel() - .getNumBands()); - int tileWidth = imageWidth >> 3;// 128; - int tileHeight = imageHeight >> 3;// 128; - attr.setTileSize(tileWidth, tileHeight); - attr.setPixelType(sePixelType); - attr.setCompressionType(SeRaster.SE_COMPRESSION_NONE); - - int maxLevels = (imageWidth / (2 * tileWidth)) - 1; - attr.setPyramidInfo(maxLevels, false, SeRaster.SE_INTERPOLATION_BILINEAR); - attr.setMaskMode(false); - attr.setImportMode(false); - - attr.setExtent(extent); - - // attr.setImageOrigin(); - - prod.setSeRasterAttr(attr); - prod.setSourceImage(sampleImage); - attr.setRasterProducer(prod); - - try { - SeInsert insert = new SeInsert(connection); - insert.intoTable(tableName, new String[] { "RASTER" }); - // no buffered writes on raster loads - insert.setWriteMode(false); - SeRow row = insert.getRowToSet(); - row.setRaster(0, attr); - - insert.execute(); - insert.close(); - } catch (SeException se) { - se.printStackTrace(); - throw se; - } - - // if there's a colormap to insert, let's add that too - if (indexCM != null) { - attr = getRasterAttributes(tableName, new Rectangle(0, 0, 0, 0), 0, - new int[] { 1 }); - final int numEntries = indexCM.getMapSize(); - // number of colors, including alpha, if any - final int numBanks = indexCM.getNumComponents(); - final int colorMapType = numBanks == 3 ? SeRaster.SE_COLORMAP_RGB - : SeRaster.SE_COLORMAP_RGBA; - DataBufferByte dataBuffer = new DataBufferByte(numEntries, numBanks); - for (int elem = 0; elem < numEntries; elem++) { - dataBuffer.setElem(0, elem, indexCM.getRed(elem)); - dataBuffer.setElem(1, elem, indexCM.getGreen(elem)); - dataBuffer.setElem(2, elem, indexCM.getBlue(elem)); - if (numBanks == 4) { - dataBuffer.setElem(3, elem, indexCM.getAlpha(elem)); - } - } - SeRaster raster; - try { - raster = attr.getRasterInfo(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - SeRasterBand[] bands = raster.getBands(); - SeRasterBand band = bands[0]; - band.setColorMap(colorMapType, dataBuffer); - band.alter(); - } - return null; - } - }; - final ISession session = getConnectionPool().getSession(); - try { - session.issue(importRasterCmd); - } finally { - session.dispose(); - } - } - - public void loadUshortRaster() throws Exception { - final RasterCellType pixelType = TYPE_16BIT_U; - final int numberOfBands = 1; - final String tableName = getRasterTableName(pixelType, numberOfBands); - final boolean pyramiding = true; - final boolean skipLevelOne = false; - final int interpolationType = SeRaster.SE_INTERPOLATION_NEAREST; - final IndexColorModel colorModel = null; - final int imageWidth = 256; - final int imageHeight = 256; - - try { - loadTestRaster(tableName, numberOfBands, imageWidth, imageHeight, pixelType, - colorModel, pyramiding, skipLevelOne, interpolationType, null); - } catch (SeException e) { - throw new ArcSdeException(e); - } - } - - public String loadShortRaster() throws Exception { - final int numberOfBands = 1; - final RasterCellType pixelType = TYPE_16BIT_S; - final String tableName = getRasterTableName(pixelType, numberOfBands); - final boolean pyramiding = true; - final boolean skipLevelOne = false; - final int interpolationType = SeRaster.SE_INTERPOLATION_NEAREST; - final IndexColorModel colorModel = null; - final int imageWidth = 256; - final int imageHeight = 256; - - try { - loadTestRaster(tableName, numberOfBands, imageWidth, imageHeight, pixelType, - colorModel, pyramiding, skipLevelOne, interpolationType, null); - } catch (SeException e) { - throw new ArcSdeException(e); - } - return tableName; - } - - /** - * Creates a 4 band raster, with 8 bit unsigned pixel type and no colormap, and pyramid - * - * @throws Exception - * @see {@link #loadTestRaster(String, int, int, IndexColorModel, boolean, boolean, int) - - */ - public String loadRGBARaster() throws Exception { - final int numberOfBands = 4; - final RasterCellType pixelType = TYPE_8BIT_U; - final String tableName = getRasterTableName(pixelType, numberOfBands); - final boolean pyramiding = true; - final boolean skipLevelOne = false; - final int interpolationType = SeRaster.SE_INTERPOLATION_NEAREST; - final IndexColorModel colorModel = null; - final int imageWidth = 256; - final int imageHeight = 256; - loadTestRaster(tableName, numberOfBands, imageWidth, imageHeight, pixelType, colorModel, - pyramiding, skipLevelOne, interpolationType, null); - return tableName; - } - - public String load8bitUnsignedColorMappedRaster() throws Exception { - final int numberOfBands = 1; - final RasterCellType pixelType = TYPE_8BIT_U; - final String tableName = getRasterTableName(pixelType, numberOfBands); - final boolean pyramiding = true; - final boolean skipLevelOne = false; - final int interpolationType = SeRaster.SE_INTERPOLATION_NEAREST; - - int cmBits = 8; - int cmSize = 1; - byte[] cmR = { 0x00 }; - byte[] cmG = { 0x00 }; - byte[] cmB = { (byte) 0xFF }; - final IndexColorModel colorModel = new IndexColorModel(cmBits, cmSize, cmR, cmG, cmB); - final int imageWidth = 256; - final int imageHeight = 256; - - loadTestRaster(tableName, numberOfBands, imageWidth, imageHeight, pixelType, colorModel, - pyramiding, skipLevelOne, interpolationType, null); - - return tableName; - } - - /** - * Creates a a raster in the database with NO pyramiding, some default settings and the provided - * parameters. - * <p> - * Default settings - * <ul> - * <li>CRS: PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M - * <li>Extent: minx=0, miny=0, maxx=512, maxy=512 - * <li>Width: 256 - * <li>Height: 256 - * <li>Tile size: 64x64 (being less than the recommended minimum of 128, but ok for our testing - * purposes) - * <li>Compression: none - * <li>Pyramid: none - * </ul> - * </p> - * - * @param tableName - * the name of the table to create - * @param numberOfBands - * the number of bands of the raster - * @param pixelType - * the pixel (cell) depth of the raster bands (one of the {@code - * SeRaster#SE_PIXEL_TYPE_*} constants) - * @param colorModel - * the color model to apply to the raster, may be {@code null}. A non null value adds - * as precondition that {@code numberOfBands == 1} - * @throws Exception - */ - public void loadTestRaster(final String tableName, final int numberOfBands, - final RasterCellType pixelType, final IndexColorModel colorModel) throws Exception { - final boolean pyramiding = true; - final boolean skipLevelOne = false; - final int interpolationType = SeRaster.SE_INTERPOLATION_BILINEAR; - final int imageWidth = 256; - final int imageHeight = 256; - - loadTestRaster(tableName, numberOfBands, imageWidth, imageHeight, pixelType, colorModel, - pyramiding, skipLevelOne, interpolationType, null); - } - - /** - * Creates a simple raster catalog, extent and grid range coincide for easier assertions. - * <p> - * The catalog rasters are not inserted in left-right, top-bottom direction though, as it may - * happen in real life. - * </p> - * <p> - * The grid extent layout is as follows (invert Y for pixel layout): - * - * <pre> - * 0,512 256,512 512,512 - * _____________________ - * | | | - * | | | - * | 3 | 1 | - * | | | - * |__________|__________| 512,256 - * 0,256 |256,256 | - * | | - * | 2 | - * | | - * |__________| - * 0,0 256,0 512,0 - * </pre> - * - * </p> - * - * @return - * @throws Exception - */ - public String loadRasterCatalog() throws Exception { - final int numberOfBands = 3; - final RasterCellType pixelType = TYPE_8BIT_U; - final String tableName = getRasterTableName(pixelType, numberOfBands) + "_CAT"; - - { - // clean out the table if it's currently in-place - testData.deleteTable(tableName); - // build the base business table. We'll add the raster data to it in a bit - // Note that this DOESN'T LOAD THE COLORMAP RIGHT NOW. - ISession session = getConnectionPool().getSession(); - try { - createRasterBusinessTempTable(tableName, session); - } finally { - session.dispose(); - } - } - - final SeCoordinateReference crs = getSeCRSFromPeProjectedCSId(PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M); - - final ISession session = getConnectionPool().getSession(); - try { - createRasterColumn(tableName, crs, session); - - int imageWidth; - int imageHeight; - SeExtent extent; - int maxLevels; - - maxLevels = 2; - extent = new SeExtent(0, 0, 256, 256); - imageWidth = 228; - imageHeight = 228; - addRasterToCatalog(session, tableName, crs, numberOfBands, imageWidth, imageHeight, - pixelType, maxLevels, extent); - - maxLevels = 3; - extent = new SeExtent(512, 512, 768, 768); - imageWidth = 456; - imageHeight = 456; - addRasterToCatalog(session, tableName, crs, numberOfBands, imageWidth, imageHeight, - pixelType, maxLevels, extent); - - maxLevels = 3; - extent = new SeExtent(512, 0, 768, 256); - imageWidth = 532; - imageHeight = 532; - addRasterToCatalog(session, tableName, crs, numberOfBands, imageWidth, imageHeight, - pixelType, maxLevels, extent); - - maxLevels = 3; - extent = new SeExtent(0, 512, 256, 768); - imageWidth = 1024; - imageHeight = 1024; - addRasterToCatalog(session, tableName, crs, numberOfBands, imageWidth, imageHeight, - pixelType, maxLevels, extent); - - maxLevels = 3; - extent = new SeExtent(256, 256, 512, 512); - imageWidth = 99; - imageHeight = 99; - addRasterToCatalog(session, tableName, crs, numberOfBands, imageWidth, imageHeight, - pixelType, maxLevels, extent); - } finally { - session.dispose(); - } - - return tableName; - } - - private void addRasterToCatalog(ISession session, String tableName, SeCoordinateReference crs, - int numberOfBands, int imageWidth, int imageHeight, RasterCellType pixelType, - int maxLevels, SeExtent extent) throws IOException { - - IndexColorModel colorModel = null; - boolean skipLevelOne = false; - int interpolationType = SeRaster.SE_INTERPOLATION_BICUBIC; - - addRasterAttribute(tableName, numberOfBands, imageWidth, imageHeight, pixelType, - colorModel, maxLevels, skipLevelOne, interpolationType, extent, session); - - } - - /** - * Creates a a raster in the database with some default settings and the provided parameters. - * <p> - * Default settings - * <ul> - * <li>CRS: PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M - * <li>Extent: minx=0, miny=0, maxx=512, maxy=512 - * <li>Width: 256 - * <li>Height: 256 - * <li>Tile size: 64x64 (being less than the recommended minimum of 128, but ok for our testing - * purposes) - * <li>Compression: none - * </ul> - * </p> - * - * @param tableName - * the name of the table to create - * @param numberOfBands - * the number of bands of the raster - * @param pixelType - * the pixel (cell) depth of the raster bands (one of the {@code - * SeRaster#SE_PIXEL_TYPE_*} constants) - * @param colorModel - * the color model to apply to the raster, may be {@code null}. A non null value adds - * as precondition that {@code numberOfBands == 1} - * @param pyramiding - * whether to create tiles or not for the raster. If {@code true} and {@code - * skipLevelOne == true} a pyramid with three levels will be created, avoiding to - * create the pyramid tiles for level 0 (same dimension as the source raster). If - * {@code skipLevelOne == false}, even for level 0 the pyramid tiles will be created, - * that is, four levels. - * @param skipLevelOne - * only relevant if {@code pyramiding == true}, {@code true} indicates not to create - * pyramid tiles for the first level, since its equal in dimension than the source - * raster - * @param interpolationType - * only relevant if {@code pyramiding == true}, indicates which interpolation method - * to use in building the pyramid tiles. Shall be one of - * {@link SeRaster#SE_INTERPOLATION_BICUBIC}, - * {@link SeRaster#SE_INTERPOLATION_BILINEAR}, - * {@link SeRaster#SE_INTERPOLATION_NEAREST}. Otherwise - * {@link SeRaster#SE_INTERPOLATION_NONE} - * @throws Exception - */ - public void loadTestRaster(final String tableName, final int numberOfBands, - final int imageWidth, final int imageHeight, final RasterCellType pixelType, - final IndexColorModel colorModel, final boolean pyramiding, final boolean skipLevelOne, - final int interpolationType, final Integer forceNumLevels) throws Exception { - - if (colorModel != null && numberOfBands > 1) { - throw new IllegalArgumentException( - "Indexed rasters shall contain a single band. numberOfBands = " + numberOfBands); - } - { - // clean out the table if it's currently in-place - testData.deleteTable(tableName); - // build the base business table. We'll add the raster data to it in a bit - // Note that this DOESN'T LOAD THE COLORMAP RIGHT NOW. - ISession session = getConnectionPool().getSession(); - try { - createRasterBusinessTempTable(tableName, session); - } finally { - session.dispose(); - } - } - - final SeExtent extent = new SeExtent(0, 0, 2 * imageWidth, 2 * imageHeight); - final SeCoordinateReference crs = getSeCRSFromPeProjectedCSId(PePCSDefs.PE_PCS_NAD_1983_HARN_MA_M); - - final ISession session = getConnectionPool().getSession(); - try { - createRasterColumn(tableName, crs, session); - - int maxLevels; - if (pyramiding) { - int tileWidth = imageWidth >> 4; - if (forceNumLevels == null) { - maxLevels = (imageWidth / (4 * tileWidth)) - 1; - } else { - maxLevels = forceNumLevels; - } - } else { - maxLevels = 0; - } - addRasterAttribute(tableName, numberOfBands, imageWidth, imageHeight, pixelType, - colorModel, maxLevels, skipLevelOne, interpolationType, extent, session); - - } finally { - session.dispose(); - } - } - - private void addRasterAttribute(final String tableName, final int numberOfBands, - final int imageWidth, final int imageHeight, final RasterCellType pixelType, - final IndexColorModel colorModel, final int maxPyramidLevel, - final boolean skipLevelOne, final int interpolationType, final SeExtent extent, - final ISession session) throws IOException { - - // attr.setImageOrigin(); - - final SeRasterProducer prod = new SeRasterProducer() { - public void addConsumer(SeRasterConsumer consumer) { - } - - public boolean isConsumer(SeRasterConsumer consumer) { - return false; - } - - public void removeConsumer(SeRasterConsumer consumer) { - } - - /** - * Note that due to some synchronization problems inherent in the SDE api code, the - * startProduction() method MUST return before consumer.setScanLines() or - * consumer.setRasterTiles() is called. Hence the thread implementation. - */ - public void startProduction(final SeRasterConsumer consumer) { - if (!(consumer instanceof SeRasterRenderedImage)) { - throw new IllegalArgumentException( - "You must set SeRasterAttr.setImportMode(false) to " - + "load data using this SeProducer implementation."); - } - - Thread runme = new Thread() { - @Override - public void run() { - try { - PixelSampler sampler = PixelSampler.getSampler(pixelType); - // for each band... - for (int bandN = 0; bandN < numberOfBands; bandN++) { - final byte[] imgBandData; - imgBandData = sampler.getImgBandData(imageWidth, imageHeight, - bandN, numberOfBands); - consumer.setScanLines(imageHeight, imgBandData, null); - consumer.rasterComplete(SeRasterConsumer.SINGLEFRAMEDONE); - } - consumer.rasterComplete(SeRasterConsumer.STATICIMAGEDONE); - } catch (Exception se) { - se.printStackTrace(); - consumer.rasterComplete(SeRasterConsumer.IMAGEERROR); - } - } - }; - runme.start(); - } - }; - - session.issue(new Command<Void>() { - @Override - public Void execute(ISession session, SeConnection connection) throws SeException, - IOException { - { - SeRasterAttr attr = new SeRasterAttr(true); - attr.setImageSize(imageWidth, imageHeight, numberOfBands); - int tileWidth = imageWidth >> 4; - int tileHeight = imageHeight >> 4; - attr.setTileSize(tileWidth, tileHeight); // this is lower than the recommended - // minimum - // of 128,128 but - // it's ok for our testing purposes - attr.setPixelType(pixelType.getSeRasterPixelType()); - attr.setCompressionType(SeRaster.SE_COMPRESSION_NONE); - - attr.setPyramidInfo(maxPyramidLevel, skipLevelOne, interpolationType); - - attr.setInterleave(true, SeRaster.SE_RASTER_INTERLEAVE_BIP); - attr.setMaskMode(false); - attr.setImportMode(false); - - attr.setExtent(extent); - attr.setRasterProducer(prod); - - SeInsert insert = new SeInsert(connection); - insert.intoTable(tableName, new String[] { "RASTER" }); - // no buffered writes on raster loads - insert.setWriteMode(false); - SeRow row = insert.getRowToSet(); - row.setRaster(0, attr); - // import the data - insert.execute(); - insert.close(); - } - // if there's a colormap to insert, let's add that too - if (colorModel != null) { - try { - SeQuery query = new SeQuery(connection, new String[] { "RASTER" }, - new SeSqlConstruct(tableName)); - query.prepareQuery(); - query.execute(); - SeRow row = query.fetch(); - SeRasterAttr attr = row.getRaster(0); - SeRaster raster; - try { - raster = attr.getRasterInfo(); - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - SeRasterBand band1 = raster.getBands()[0]; - final int numEntries = colorModel.getMapSize(); - // number of colors, including alpha, if any - final int numBanks = colorModel.getNumComponents(); - final int colorMapType = numBanks == 3 ? SeRaster.SE_COLORMAP_RGB - : SeRaster.SE_COLORMAP_RGBA; - final int dataType = pixelType.getDataBufferType(); - DataBuffer dataBuffer; - if (DataBuffer.TYPE_BYTE == dataType) { - dataBuffer = new DataBufferByte(numEntries, numBanks); - } else if (DataBuffer.TYPE_USHORT == dataType) { - /* - * beware we're using DataBufferShort instead of DataBufferUShort as - * that's what the esri api expects - */ - dataBuffer = new DataBufferShort(numEntries, numBanks); - } else { - throw new IllegalArgumentException("data type: " + pixelType); - } - for (int elem = 0; elem < numEntries; elem++) { - dataBuffer.setElem(0, colorModel.getRed(elem)); - dataBuffer.setElem(1, colorModel.getGreen(elem)); - dataBuffer.setElem(2, colorModel.getBlue(elem)); - if (numBanks == 4) { - dataBuffer.setElem(3, colorModel.getAlpha(elem)); - } - } - band1.setColorMap(colorMapType, dataBuffer); - band1.alter(); - } catch (SeException e) { - e.printStackTrace(); - throw new ArcSdeException(e); - } - } - return null; - } - }); - } - - private void createRasterColumn(final String tableName, final SeCoordinateReference crs, - final ISession session) throws IOException { - // much of this code is from - // http://edndoc.esri.com/arcsde/9.2/concepts/rasters/dataloading/dataloading.htm - session.issue(new Command<Void>() { - - @Override - public Void execute(ISession session, SeConnection connection) throws SeException, - IOException { - SeRasterColumn rasCol = new SeRasterColumn(connection); - rasCol.setTableName(tableName); - rasCol.setDescription("Sample geotools ArcSDE raster test-suite data."); - rasCol.setRasterColumnName("RASTER"); - rasCol.setCoordRef(crs); - rasCol.setConfigurationKeyword(testData.getConfigKeyword()); - - rasCol.create(); - return null; - } - }); - } - - /** - * convenience method to test if two images are identical in their RGB pixel values - * - * @param image - * @param fileName - * @return - * @throws IOException - */ - public static boolean imageEquals(RenderedImage image, String fileName) throws IOException { - InputStream in = org.geotools.test.TestData.url(null, fileName).openStream(); - BufferedImage expected = ImageIO.read(in); - - return imageEquals(image, expected); - } - - public static boolean imageEquals(RenderedImage image1, RenderedImage image2) { - return imageEquals(image1, image2, true); - } - - /** - * convenience method to test if two images are identical in their RGB pixel values - * - * @param image1 - * @param image2 - * @return - */ - public static boolean imageEquals(RenderedImage image1, RenderedImage image2, - boolean ignoreAlpha) { - - final int h = image1.getHeight(); - final int w = image2.getWidth(); - - int skipBand = -1; - if (ignoreAlpha) { - skipBand = 3; - } - - for (int b = 0; b < image1.getData().getNumBands(); b++) { - if (b == skipBand) - continue; - int[] img1data = image1.getData().getSamples(0, 0, image1.getWidth(), - image1.getHeight(), b, new int[image1.getHeight() * image1.getWidth()]); - int[] img2data = image2.getData().getSamples(0, 0, image1.getWidth(), - image1.getHeight(), b, new int[image1.getHeight() * image1.getWidth()]); - - if (!Arrays.equals(img1data, img2data)) { - // try to figure out which pixel (exactly) was different - for (int i = 0; i < img1data.length; i++) { - if (img1data[i] != img2data[i]) { - final int x = i % image1.getWidth(); - final int y = i / image1.getHeight(); - System.out.println("pixel " + i + " (possibly " + x + "," + y - + ") differs: " + img1data[i] + " != " + img2data[i]); - return false; - } - } - } - - /* - * for (int xpos = 0; xpos < image1.getWidth(); xpos++) { System.out.println("checking - * column " + xpos); int[] img1data = image1.getData().getSamples(xpos, 0, 1, - * image1.getHeight(), b, new int[image1.getHeight()]); int[] img2data = - * image2.getData().getSamples(xpos, 0, 1, image1.getHeight(), b, new - * int[image1.getHeight()]); if (!Arrays.equals(img1data, img2data)) { - * System.out.println("pixels in column " + xpos + " are different"); return false; } } - */ - - } - return true; - } - - public SeRasterAttr getRasterAttributes(final String rasterName, final Rectangle tiles, - final int level, final int[] bands) throws IOException { - - ISession conn; - try { - conn = getConnectionPool().getSession(); - } catch (UnavailableConnectionException e) { - throw new RuntimeException(e); - } - - try { - final SeQuery query = conn.createAndExecuteQuery(new String[] { conn.getRasterColumn( - rasterName).getName() }, new SeSqlConstruct(rasterName)); - - return conn.issue(new Command<SeRasterAttr>() { - - @Override - public SeRasterAttr execute(ISession session, SeConnection connection) - throws SeException, IOException { - final SeRow r = query.fetch(); - - // Now build a SeRasterConstraint object which queries the db for - // the right tiles/bands/pyramid level - SeRasterConstraint rConstraint = new SeRasterConstraint(); - rConstraint.setEnvelope((int) tiles.getMinX(), (int) tiles.getMinY(), - (int) tiles.getMaxX(), (int) tiles.getMaxY()); - rConstraint.setLevel(level); - rConstraint.setBands(bands); - - // Finally, execute the raster query aganist the already-opened - // SeQuery object which already has an SeRow fetched against it. - - query.queryRasterTile(rConstraint); - final SeRasterAttr rattr = r.getRaster(0); - - query.close(); - - return rattr; - } - }); - } finally { - conn.dispose(); - } - } - - public static abstract class PixelSampler { - - private static Map<RasterCellType, PixelSampler> byPixelTypeSamplers = new HashMap<RasterCellType, PixelSampler>(); - static { - byPixelTypeSamplers.put(TYPE_1BIT, new SamplerType1Bit()); - byPixelTypeSamplers.put(TYPE_4BIT, new SamplerType4Bit()); - byPixelTypeSamplers.put(TYPE_8BIT_U, new SamplerType8BitUnsigned()); - byPixelTypeSamplers.put(TYPE_8BIT_S, new SamplerType8BitSigned()); - byPixelTypeSamplers.put(TYPE_16BIT_U, new SamplerType16BitUnsigned()); - byPixelTypeSamplers.put(TYPE_16BIT_S, new SamplerType16BitSigned()); - byPixelTypeSamplers.put(TYPE_32BIT_U, new SamplerType32BitUnsigned()); - byPixelTypeSamplers.put(TYPE_32BIT_S, new SamplerType32BitSigned()); - byPixelTypeSamplers.put(TYPE_32BIT_REAL, new SamplerType32BitReal()); - byPixelTypeSamplers.put(TYPE_64BIT_REAL, new SamplerType64BitReal()); - &nb | ||||||||||||||||