package com.revolsys.gdal; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.BandedSampleModel; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.DataBufferInt; import java.awt.image.DataBufferShort; import java.awt.image.Raster; import java.awt.image.SampleModel; import java.awt.image.WritableRaster; import java.io.File; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Map; import org.gdal.gdal.Band; import org.gdal.gdal.ColorTable; import org.gdal.gdal.Dataset; import org.gdal.gdal.Driver; import org.gdal.gdal.gdal; import org.gdal.gdalconst.gdalconstConstants; import org.gdal.ogr.ogr; import org.gdal.osr.SpatialReference; import org.gdal.osr.osr; import com.revolsys.gdal.raster.GdalImageFactory; import com.revolsys.geometry.cs.CoordinateSystem; import com.revolsys.geometry.cs.epsg.EpsgCoordinateSystems; import com.revolsys.geometry.cs.esri.EsriCoordinateSystems; import com.revolsys.geometry.cs.esri.EsriCsWktWriter; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.io.FileUtil; import com.revolsys.io.IoFactoryRegistry; import com.revolsys.logging.Logs; import com.revolsys.record.io.format.json.Json; import com.revolsys.spring.resource.FileSystemResource; import com.revolsys.spring.resource.Resource; import com.revolsys.util.OS; import com.revolsys.util.Property; public class Gdal { private static boolean available = false; static { try { gdal.SetConfigOption("CPL_TMPDIR", System.getProperty("java.io.tmpdir")); osr.UseExceptions(); ogr.UseExceptions(); String defaultDriverPath = null; if (OS.isMac()) { defaultDriverPath = "/usr/local/lib/gdalplugins"; } setGdalProperty("GDAL_DRIVER_PATH", defaultDriverPath); setGdalProperty("GDAL_DATA", null); gdal.SetConfigOption("GDAL_PAM", "Yes"); gdal.AllRegister(); ogr.RegisterAll(); available = true; } catch (final UnsatisfiedLinkError e) { } catch (final Throwable e) { Logs.debug(Gdal.class, e); } } private static void addGeoreferencedImageFactory( final GdalImageFactory georeferencedImageFactory) { if (georeferencedImageFactory.isAvailable()) { IoFactoryRegistry.addFactory(georeferencedImageFactory); } } private static void addGeoreferencedImageFactory(final String driverName, final String formatName, final String fileExtension, final String mimeType) { final GdalImageFactory readerSpi = new GdalImageFactory(driverName, formatName, fileExtension, mimeType); addGeoreferencedImageFactory(readerSpi); } public static Dataset closeDataSet(final Dataset dataSet) { if (dataSet != null) { try { dataSet.delete(); } catch (final Throwable e) { } } return null; } public static BufferedImage getBufferedImage(final Dataset dataset) { return getBufferedImage(dataset, -1); } /** * <p> * Convert the overview raster from {@link Dataset} to a * {@link BufferedImage} . The result image will be the dimensions of the * overview raster. * </p> * * @param dataset * The image dataset. * @param overviewIndex * The index of the overview raster data. Use -1 for the whole * image. * @return The buffered image */ public static BufferedImage getBufferedImage(final Dataset dataset, final int overviewIndex) { return getBufferedImage(dataset, overviewIndex, 0, 0, -1, -1, -1, -1); } /** * <p> * Convert the overview raster from {@link Dataset} to a * {@link BufferedImage} . The raster will be clipped to the * sourceOffsetX,sourceOffsetY -> sourceWidth, sourceHeight rectangle. The * clip rectangle will be adjusted to fit inside the bounds of the source * image. The result image will be the dimensions of sourceWidth, * sourceHeight. * </p> * * @param dataset * The image dataset. * @param overviewIndex * The index of the overview raster data. Use -1 for the whole * image. * @param sourceOffsetX * The x location of the clip rectangle. * @param sourceOffsetY * The y location of the clip rectangle. * @param sourceWidth * The width of the clip rectangle. Use -1 to auto calculate. * @param sourceHeight * The height of the clip rectangle. Use -1 to auto calculate. * @return The buffered image. */ public static BufferedImage getBufferedImage(final Dataset dataset, final int overviewIndex, final int sourceOffsetX, final int sourceOffsetY, final int sourceWidth, final int sourceHeight) { return getBufferedImage(dataset, overviewIndex, sourceOffsetX, sourceOffsetY, sourceWidth, sourceHeight, -1, -1); } /** * <p> * Convert the overview raster from {@link Dataset} to a * {@link BufferedImage} . The raster will be clipped to the * sourceOffsetX,sourceOffsetY -> sourceWidth, sourceHeight rectangle. The * clip rectangle will be adjusted to fit inside the bounds of the source * image. The result image will scaled to the dimensions of targetWidth, * targetHeight. * </p> * * @param dataset * The image dataset. * @param overviewIndex * The index of the overview raster data. Use -1 for the whole * image. * @param sourceOffsetX * The x location of the clip rectangle. * @param sourceOffsetY * The y location of the clip rectangle. * @param sourceWidth * The width of the clip rectangle. Use -1 to auto calculate. * @param sourceHeight * The height of the clip rectangle. Use -1 to auto calculate. * @param targetWidth * The width of the result image. Use -1 to auto calculate. * @param targetHeight * The height of the result image. Use -1 to auto calculate. * @return The buffered image. */ public static BufferedImage getBufferedImage(final Dataset dataset, final int overviewIndex, int sourceOffsetX, int sourceOffsetY, int sourceWidth, int sourceHeight, int targetWidth, int targetHeight) { synchronized (dataset) { final int bandCount = dataset.getRasterCount(); final ByteBuffer[] bandData = new ByteBuffer[bandCount]; final int[] banks = new int[bandCount]; final int[] offsets = new int[bandCount]; int pixels = 0; int bandDataType = 0; int rasterColorInterpretation = -1; ColorTable rasterColorTable = null; for (int bandIndex = 0; bandIndex < bandCount; bandIndex++) { final Band band = dataset.GetRasterBand(bandIndex + 1); try { Band overviewBand; if (overviewIndex == -1) { overviewBand = band; } else { overviewBand = band.GetOverview(overviewIndex); } try { if (rasterColorTable == null) { rasterColorTable = band.GetRasterColorTable(); rasterColorInterpretation = band.GetRasterColorInterpretation(); bandDataType = band.getDataType(); final int overviewWidth = overviewBand.getXSize(); final int overviewHeight = overviewBand.getYSize(); if (sourceOffsetX < 0) { sourceOffsetX = 0; } if (sourceOffsetY < 0) { sourceOffsetY = 0; } if (sourceOffsetX >= overviewWidth || sourceOffsetY >= overviewHeight) { return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); } if (sourceWidth < 0) { sourceWidth = overviewWidth; } if (sourceOffsetX + sourceWidth > overviewWidth) { sourceWidth = overviewWidth - sourceOffsetX; } if (targetWidth < 0) { targetWidth = sourceWidth; } if (sourceHeight < 0) { sourceHeight = overviewHeight; } if (sourceOffsetY + sourceHeight > overviewHeight) { sourceHeight = overviewHeight - sourceOffsetY; } if (targetHeight < 0) { targetHeight = sourceHeight; } pixels = targetWidth * targetHeight; } if (pixels > 0 && sourceHeight > 0 && sourceWidth > 0) { final int bufferSize = pixels * gdal.GetDataTypeSize(bandDataType) / 8; final ByteBuffer data = ByteBuffer.allocateDirect(bufferSize); data.order(ByteOrder.nativeOrder()); final int result = overviewBand.ReadRaster_Direct(sourceOffsetX, sourceOffsetY, sourceWidth, sourceHeight, targetWidth, targetHeight, bandDataType, data); if (result == gdalconstConstants.CE_None) { bandData[bandIndex] = data; } else { throw new RuntimeException("Error converting image"); } } else { return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); } banks[bandIndex] = bandIndex; offsets[bandIndex] = 0; } finally { overviewBand.delete(); } } finally { band.delete(); } } DataBuffer imageBuffer = null; SampleModel sampleModel = null; int dataType = 0; int dataBufferType = 0; if (bandDataType == gdalconstConstants.GDT_Byte) { final byte[][] bytes = new byte[bandCount][]; for (int bandIndex = 0; bandIndex < bandCount; bandIndex++) { bytes[bandIndex] = new byte[pixels]; bandData[bandIndex].get(bytes[bandIndex]); } imageBuffer = new DataBufferByte(bytes, pixels); dataBufferType = DataBuffer.TYPE_BYTE; sampleModel = new BandedSampleModel(dataBufferType, targetWidth, targetHeight, targetWidth, banks, offsets); dataType = rasterColorInterpretation == gdalconstConstants.GCI_PaletteIndex ? BufferedImage.TYPE_BYTE_INDEXED : BufferedImage.TYPE_BYTE_GRAY; } else if (bandDataType == gdalconstConstants.GDT_Int16) { final short[][] shorts = new short[bandCount][]; for (int bandIndex = 0; bandIndex < bandCount; bandIndex++) { shorts[bandIndex] = new short[pixels]; bandData[bandIndex].asShortBuffer().get(shorts[bandIndex]); } imageBuffer = new DataBufferShort(shorts, pixels); dataBufferType = DataBuffer.TYPE_USHORT; sampleModel = new BandedSampleModel(dataBufferType, targetWidth, targetHeight, targetWidth, banks, offsets); dataType = BufferedImage.TYPE_USHORT_GRAY; } else if (bandDataType == gdalconstConstants.GDT_Int32) { final int[][] ints = new int[bandCount][]; for (int bandIndex = 0; bandIndex < bandCount; bandIndex++) { ints[bandIndex] = new int[pixels]; bandData[bandIndex].asIntBuffer().get(ints[bandIndex]); } imageBuffer = new DataBufferInt(ints, pixels); dataBufferType = DataBuffer.TYPE_INT; sampleModel = new BandedSampleModel(dataBufferType, targetWidth, targetHeight, targetWidth, banks, offsets); dataType = BufferedImage.TYPE_CUSTOM; } final WritableRaster raster = Raster.createWritableRaster(sampleModel, imageBuffer, null); BufferedImage image = null; ColorModel colorModel = null; if (rasterColorInterpretation == gdalconstConstants.GCI_PaletteIndex) { dataType = BufferedImage.TYPE_BYTE_INDEXED; colorModel = rasterColorTable.getIndexColorModel(gdal.GetDataTypeSize(bandDataType)); image = new BufferedImage(colorModel, raster, false, null); } else { ColorSpace colorSpace = null; if (bandCount > 2) { colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); colorModel = new ComponentColorModel(colorSpace, false, false, Transparency.OPAQUE, dataBufferType); image = new BufferedImage(colorModel, raster, true, null); } else { image = new BufferedImage(targetWidth, targetHeight, dataType); image.setData(raster); } } return image; } } public static BufferedImage getBufferedImage(final File file) { final Dataset dataset = getDataset(file); return getBufferedImage(dataset); } public static BufferedImage getBufferedImage(final String fileName) { final File file = FileUtil.getFile(fileName); return getBufferedImage(file); } public static CoordinateSystem getCoordinateSystem(final SpatialReference spatialReference) { if (spatialReference == null) { return null; } else { final String wkt = spatialReference.ExportToWkt(); final CoordinateSystem coordinateSystem = EsriCoordinateSystems.getCoordinateSystem(wkt); return EpsgCoordinateSystems.getCoordinateSystem(coordinateSystem); } } public static Dataset getDataset(final File file) { final int mode = gdalconstConstants.GA_ReadOnly; return getDataset(file, mode); } public static Dataset getDataset(final File file, final int mode) { if (isAvailable()) { final String path = file.getAbsolutePath(); if (file.exists()) { final Dataset dataset = gdal.Open(path, mode); if (dataset == null) { throw new GdalException(); } else { final Resource resource = new FileSystemResource(file); setProjectionFromPrjFile(dataset, resource); final long modifiedTime = loadSettings(dataset, resource); // loadAuxXmlFile(modifiedTime); return dataset; } } else { throw new IllegalArgumentException("File no found: " + path); } } else { throw new IllegalStateException("GDAL is not available"); } } public static Dataset getDataset(final String fileName) { final File file = FileUtil.getFile(fileName); return getDataset(file); } public static Dataset getDataset(final String name, final int mode) { if (Property.hasValue(name)) { final File file = new File(name); return getDataset(file, mode); } else { throw new IllegalArgumentException("File name must not be null or empty"); } } public static SpatialReference getSpatialReference(CoordinateSystem coordinateSystem) { if (coordinateSystem == null) { return null; } else { final int srid = coordinateSystem.getCoordinateSystemId(); if (srid <= 0) { coordinateSystem = EsriCoordinateSystems.getCoordinateSystem(coordinateSystem); final String wkt = EsriCsWktWriter.toWkt(coordinateSystem); final SpatialReference spatialReference = new SpatialReference(wkt); return spatialReference; } else { return getSpatialReference(srid); } } } public static SpatialReference getSpatialReference(final GeometryFactory geometryFactory) { if (geometryFactory == null) { return null; } else { final int srid = geometryFactory.getCoordinateSystemId(); if (srid <= 0) { final GeometryFactory coordinateSystem = geometryFactory; return getSpatialReference(coordinateSystem); } else { return getSpatialReference(srid); } } } public static SpatialReference getSpatialReference(final int srid) { final SpatialReference spatialReference = new SpatialReference(""); spatialReference.ImportFromEPSG(srid); return spatialReference; } public static String getSpatialReferenceWkt(final int srid) { final SpatialReference spatialReference = getSpatialReference(srid); return spatialReference.ExportToWkt(); } public static String getVersion() { if (Gdal.isAvailable()) { return gdal.VersionInfo(); } else { return "0.0.0"; } } public static void init() { } public static void ioFactoryInit() { addGeoreferencedImageFactory("ECW", "ECW", "ecw", "image/ecw"); addGeoreferencedImageFactory("JP2ECW", "JPEG 2000", "jp2", "image/jp2"); } public static boolean isAvailable() { return available; } /** * Returns <code>true</code> if a driver for the specific format is * available. <code>false</code> otherwise.<BR> * It is worth to point out that a successful loading of the native library * is not sufficient to grant the support for a specific format. We should * also check if the proper driver is available. * * @return <code>true</code> if a driver for the specific format is * available. <code>false</code> otherwise.<BR> */ public static boolean isDriverAvailable(final String driverName) { if (isAvailable()) { try { final Driver driver = gdal.GetDriverByName(driverName); if (driver == null) { return false; } else { return true; } } catch (final UnsatisfiedLinkError e) { Logs.debug(Gdal.class, "Error loading driver: " + driverName, e); return false; } } else { return false; } } public static long loadSettings(final Dataset dataset, final Resource resource) { final Resource settingsFile = resource.newResourceAddExtension("rgobject"); if (settingsFile.exists()) { try { final Map<String, Object> settings = Json.toMap(settingsFile); final String boundingBoxWkt = (String)settings.get("boundingBox"); if (Property.hasValue(boundingBoxWkt)) { final BoundingBox boundingBox = BoundingBox.newBoundingBox(boundingBoxWkt); if (!boundingBox.isEmpty()) { setSpatialReference(dataset, boundingBox.getCoordinateSystem()); final double x = boundingBox.getMinX(); final double width = boundingBox.getWidth(); final int imageWidth = dataset.getRasterXSize(); final double y = boundingBox.getMaxY(); final double height = boundingBox.getHeight(); final int imageHeight = dataset.getRasterYSize(); final double[] transform = new double[] { x, width / imageWidth, 0, y, 0, -height / imageHeight }; dataset.SetGeoTransform(transform); } } return settingsFile.getLastModified(); } catch (final Throwable e) { Logs.error(Gdal.class, "Unable to load:" + settingsFile, e); return -1; } } else { return -1; } } private static void setGdalProperty(final String name, final String defaultValue) { String value = System.getProperty(name); if (!Property.hasValue(value)) { value = System.getenv(name); if (!Property.hasValue(value)) { value = defaultValue; } } if (Property.hasValue(value)) { gdal.SetConfigOption(name, value); } } public static void setProjectionFromPrjFile(final Dataset dataset, final Resource resource) { final GeometryFactory geometryFactory = EsriCoordinateSystems.getGeometryFactory(resource); if (geometryFactory != null) { final CoordinateSystem coordinateSystem = geometryFactory.getCoordinateSystem(); setSpatialReference(dataset, coordinateSystem); } } public static void setSpatialReference(final Dataset dataset, final CoordinateSystem coordinateSystem) { final SpatialReference spatialReference = getSpatialReference(coordinateSystem); if (spatialReference != null) { dataset.SetProjection(spatialReference.ExportToWkt()); } } }