src.gov.nasa.worldwind.util.gdal.GDALUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.util.gdal;
import gov.nasa.worldwind.Configuration;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.data.*;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.formats.tiff.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.util.*;
import org.gdal.gdal.*;
import org.gdal.gdalconst.*;
import org.gdal.ogr.ogr;
import org.gdal.osr.*;
import java.awt.*;
import java.awt.color.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.awt.image.*;
import java.io.*;
import java.lang.reflect.*;
import java.nio.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
/**
* @author Lado Garakanidze
* @version $Id: GDALUtils.java 1837 2014-02-05 18:25:20Z tgaskins $
*/
public class GDALUtils
{
public static long ALPHA_MASK = 0xFFFFFFFFL;
protected static byte ALPHA_TRANSPARENT = (byte) 0x00;
protected static byte ALPHA_OPAQUE = (byte) 0xFF;
protected static final String JAVA_LIBRARY_PATH = "java.library.path";
protected static final String GDAL_DRIVER_PATH = "GDAL_DRIVER_PATH";
protected static final String OGR_DRIVER_PATH = "OGR_DRIVER_PATH";
protected static final String GDAL_DATA_PATH = "GDAL_DATA";
protected static final AtomicBoolean gdalIsAvailable = new AtomicBoolean(false);
// This is an OLD default libname request by WW build of GDAL
protected static final String gdalalljni = Configuration.isMacOS()
? "gdalalljni" : (is32bitArchitecture() ? "gdalalljni32" : "gdalalljni64");
protected static final CopyOnWriteArraySet loadedLibraries = new CopyOnWriteArraySet();
protected static final CopyOnWriteArraySet failedLibraries = new CopyOnWriteArraySet();
static
{
// Allow the app or user to prevent library loader replacement.
if (System.getProperty("gov.nasa.worldwind.prevent.gdal.loader.replacement") == null)
replaceLibraryLoader(); // This must be the first line of initialization
initialize();
}
private static class GDALLibraryLoader implements gdal.LibraryLoader
{
public void load(String libName) throws UnsatisfiedLinkError
{
if (WWUtil.isEmpty(libName))
{
String message = Logging.getMessage("nullValue.LibraryIsNull");
Logging.logger().severe(message);
throw new java.lang.UnsatisfiedLinkError(message);
}
// check if the library is already loaded
if (loadedLibraries.contains(libName))
return;
String message;
// check if the library is already know (from previous attempts) to fail to load
if ( !failedLibraries.contains(libName) )
{
try
{
NativeLibraryLoader.loadLibrary(libName);
loadedLibraries.add(libName);
Logging.logger().info( Logging.getMessage("generic.LibraryLoadedOK", libName ));
return; // GOOD! Leaving now
}
catch (Throwable t)
{
String reason = WWUtil.extractExceptionReason(t);
message = Logging.getMessage("generic.LibraryNotLoaded", libName, reason);
Logging.logger().finest(message);
failedLibraries.add(libName);
}
}
else
{
String reason = Logging.getMessage("generic.LibraryNotFound", libName );
message = Logging.getMessage("generic.LibraryNotLoaded", libName, reason);
}
throw new UnsatisfiedLinkError(message);
}
}
protected static void replaceLibraryLoader()
{
try
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class gdalClass = cl.loadClass("org.gdal.gdal.gdal");
boolean isKnownBuild = false;
Method[] methods = gdalClass.getDeclaredMethods();
for (Method m : methods)
{
if ("setLibraryLoader".equals(m.getName()))
{
gdal.setLibraryLoader(new GDALLibraryLoader());
// Logging.logger().finest(Logging.getMessage("gdal.LibraryLoaderReplacedOK"));
isKnownBuild = true;
break;
}
}
if (!isKnownBuild)
{
String message = Logging.getMessage("gdal.UnknownBuild", gdal.VersionInfo());
Logging.logger().finest(message);
}
}
catch (ClassNotFoundException cnf)
{
Logging.logger().finest(cnf.getMessage());
}
catch (Throwable t)
{
Logging.logger().finest(t.getMessage());
}
}
protected static boolean is32bitArchitecture()
{
String arch = System.getProperty("sun.arch.data.model");
if( !WWUtil.isEmpty(arch) )
return ("32".equals(arch));
// GNU JAVA does not return "sun.arch.data.model"
return "x86".equals(System.getProperty("os.arch"));
}
protected static boolean gdalPreLoadNativeLibrary(boolean allowLogErrors)
{
try
{
NativeLibraryLoader.loadLibrary(gdalalljni);
loadedLibraries.add(gdalalljni);
Logging.logger().info( Logging.getMessage("generic.LibraryLoadedOK", gdalalljni ));
return true;
}
catch (Throwable t)
{
if( allowLogErrors )
Logging.logger().finest(WWUtil.extractExceptionReason(t));
}
return false;
}
protected static void initialize()
{
try
{
boolean runningAsJavaWebStart = (null != System.getProperty("javawebstart.version", null));
// attempt to load library from default locations
// (current path OR by specifying java.library.path from the command line)
boolean gdalNativeLibraryLoaded = gdalPreLoadNativeLibrary(false);
if (!gdalNativeLibraryLoaded && !runningAsJavaWebStart)
{
// if we are here, library is not in any default place, so we will search in sub-folders
String[] folders = findGdalFolders();
String newJavaLibraryPath = buildPathString(folders, true);
if (newJavaLibraryPath != null)
{
alterJavaLibraryPath(newJavaLibraryPath);
// gdalNativeLibraryLoaded = gdalLoadNativeLibrary(true);
}
}
if ( /* gdalNativeLibraryLoaded && */ gdalJNI.isAvailable() && gdalconstJNI.isAvailable())
{
if (!runningAsJavaWebStart)
{
// No need, because we are build one dynamic library that contains ALL drivers
// and dependant libraries
// gdal.SetConfigOption(GDAL_DRIVER_PATH, pathToLibs);
// gdal.SetConfigOption(OGR_DRIVER_PATH, pathToLibs);
String dataFolder = findGdalDataFolder();
if (null != dataFolder)
{
String msg = Logging.getMessage("gdal.SharedDataFolderFound", dataFolder);
Logging.logger().finest(msg);
gdal.SetConfigOption(GDAL_DATA_PATH, dataFolder);
}
}
gdal.AllRegister();
ogr.RegisterAll();
/**
* "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string. ie. "1170"
* "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a string. "20020416"
* "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "1.1.7"
* "--version": Returns full version , ie. "GDAL 1.1.7, released 2002/04/16"
*/
String msg = Logging.getMessage("generic.LibraryLoadedOK", "GDAL v" + gdal.VersionInfo("RELEASE_NAME"));
Logging.logger().info(msg);
listAllRegisteredDrivers();
gdalIsAvailable.set(true);
}
else
{
String reason = Logging.getMessage("generic.LibraryNotFound", "GDAL" );
String msg = Logging.getMessage("generic.LibraryNotLoaded", "GDAL", reason );
Logging.logger().warning(msg);
}
}
catch (Throwable t)
{
Logging.logger().log(Level.FINEST, t.getMessage(), t);
}
}
protected static String getCurrentDirectory()
{
String cwd = System.getProperty("user.dir");
if (null == cwd || cwd.length() == 0)
{
String message = Logging.getMessage("generic.UsersHomeDirectoryNotKnown");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
return cwd;
}
protected static String[] findGdalFolders()
{
try
{
String cwd = getCurrentDirectory();
FileTree fileTree = new FileTree(new File(cwd));
fileTree.setMode(FileTree.FILES_AND_DIRECTORIES);
GDALLibraryFinder filter = new GDALLibraryFinder(/*gdalalljni*/);
fileTree.asList(filter);
return filter.getFolders();
}
catch (Throwable t)
{
Logging.logger().severe(t.getMessage());
}
return null;
}
protected static String findGdalDataFolder()
{
try
{
String cwd = getCurrentDirectory();
FileTree fileTree = new FileTree(new File(cwd));
fileTree.setMode(FileTree.FILES_AND_DIRECTORIES);
GDALDataFinder filter = new GDALDataFinder();
fileTree.asList(filter);
String[] folders = filter.getFolders();
if (null != folders && folders.length > 0)
{
if (folders.length > 1)
{
String msg = Logging.getMessage("gdal.MultipleDataFoldersFound", buildPathString(folders, false));
Logging.logger().warning(msg);
}
return folders[0];
}
}
catch (Throwable t)
{
Logging.logger().severe(t.getMessage());
}
String message = Logging.getMessage("gdal.SharedDataFolderNotFound");
Logging.logger().severe(message);
// throw new WWRuntimeException( message );
return null;
}
protected static String buildPathString(String[] folders, boolean addDefaultValues)
{
String del = System.getProperty("path.separator");
StringBuffer path = new StringBuffer();
path.append("lib-external/gdal").append(del);
if (null != folders && folders.length > 0)
{
for (String folder : folders)
{
path.append(folder).append(del);
}
}
if (addDefaultValues)
{
path.append(".").append(del); // append current directory
path.append(System.getProperty("user.dir")).append(del);
path.append(System.getProperty(JAVA_LIBRARY_PATH));
}
return path.toString();
}
protected static void listAllRegisteredDrivers()
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < gdal.GetDriverCount(); i++)
{
Driver drv = gdal.GetDriver(i);
String msg = Logging.getMessage("gdal.DriverDetails", drv.getShortName(), drv.getLongName(),
drv.GetDescription());
sb.append(msg).append("\n");
}
Logging.logger().finest(sb.toString());
}
/** @return returns an error string, if no errors returns null */
public static String getErrorMessage()
{
try
{
if (gdalIsAvailable.get())
{
int errno = gdal.GetLastErrorNo();
if (errno != gdalconst.CE_None)
{
return Logging.getMessage("gdal.InternalError", errno, gdal.GetLastErrorMsg());
}
}
}
catch (Throwable t)
{
return t.getMessage();
}
return null;
}
/**
* Opens image or elevation file, returns a DataSet object
*
* @param source the location of the local file, expressed as either a String path, a File, or a file URL.
* @param isSilentMode specifies a silent mode of reading file (usually needed for canRead() and readMetadata())
*
* @return returns a Dataset object
*
* @throws FileNotFoundException if file not found
* @throws IllegalArgumentException if file is null
* @throws SecurityException if file could not be read
* @throws WWRuntimeException if GDAL library was not initialized
*/
public static Dataset open(Object source, boolean isSilentMode)
throws FileNotFoundException, IllegalArgumentException, SecurityException, WWRuntimeException
{
if (!gdalIsAvailable.get())
{
if (isSilentMode)
{
return null;
}
String message = Logging.getMessage("gdal.GDALNotAvailable");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
File file = WWIO.getFileForLocalAddress(source);
if (null == file)
{
if (isSilentMode)
{
return null;
}
String message = Logging.getMessage("generic.UnrecognizedSourceType", source.getClass().getName());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (!file.exists())
{
if (isSilentMode)
{
return null;
}
String message = Logging.getMessage("generic.FileNotFound", file.getAbsolutePath());
Logging.logger().severe(message);
throw new FileNotFoundException(message);
}
if (!file.canRead())
{
if (isSilentMode)
{
return null;
}
String message = Logging.getMessage("generic.FileNoReadPermission", file.getAbsolutePath());
Logging.logger().severe(message);
throw new SecurityException(message);
}
Dataset ds = null;
try
{
gdal.PushErrorHandler("CPLQuietErrorHandler");
ds = gdal.Open(file.getAbsolutePath(), gdalconst.GA_ReadOnly);
}
finally
{
gdal.PopErrorHandler();
}
if (ds == null)
{
if (isSilentMode)
{
return null;
}
String message = Logging.getMessage("generic.CannotOpenFile", GDALUtils.getErrorMessage());
Logging.logger().fine(message);
throw new WWRuntimeException(message);
}
return ds;
}
/**
* Opens image or elevation file, returns a DataSet object
*
* @param source the location of the local file, expressed as either a String path, a File, or a file URL.
*
* @return returns a Dataset object
*
* @throws FileNotFoundException if file not found
* @throws IllegalArgumentException if file is null
* @throws SecurityException if file could not be read
* @throws WWRuntimeException if GDAL library was not initialized
*/
public static Dataset open(Object source)
throws FileNotFoundException, IllegalArgumentException, SecurityException, WWRuntimeException
{
return open(source, false);
}
/**
* Checks if a data raster can is readable
*
* @param source the location of the local file, expressed as either a String path, a File, or a file URL.
*
* @return true, if source is readable
*/
public static boolean canOpen(Object source)
{
if (!gdalIsAvailable.get())
{
return false;
}
File file = (null != source) ? WWIO.getFileForLocalAddress(source) : null;
if (null == file)
{
return false;
}
Dataset ds = null;
boolean canOpen = false;
try
{
gdal.PushErrorHandler("CPLQuietErrorHandler");
if (file.exists() && file.canRead())
{
ds = gdal.Open(file.getAbsolutePath(), gdalconst.GA_ReadOnly);
canOpen = !(ds == null);
}
}
catch (Throwable t)
{
// this is a quiet mode, no need to log
}
finally
{
if (null != ds)
{
ds.delete();
}
gdal.PopErrorHandler();
}
return canOpen;
}
/**
* Opens image or elevation file, returns as a BufferedImage (even for elevations)
*
* @param ds GDAL's Dataset object
* @param params AVList of parameters
*
* @return DataRaster returns as a BufferedImage (even for elevations)
*
* @throws IllegalArgumentException if file is null
* @throws SecurityException if file could not be read
* @throws WWRuntimeException if GDAL library was not initialized
*/
protected static DataRaster composeImageDataRaster(Dataset ds, AVList params)
throws IllegalArgumentException, SecurityException, WWRuntimeException
{
if (!gdalIsAvailable.get())
{
String message = Logging.getMessage("gdal.GDALNotAvailable");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
if (null == ds)
{
String message = Logging.getMessage("nullValue.DataSetIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
BufferedImage img = null;
int width = ds.getRasterXSize();
int height = ds.getRasterYSize();
int bandCount = ds.getRasterCount();
if( bandCount < 1 )
{
String message = Logging.getMessage("generic.UnexpectedBandCount", bandCount );
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Double[] dbls = new Double[16];
ByteBuffer[] bands = new ByteBuffer[bandCount];
int[] bandsOrder = new int[bandCount];
int[] offsets = new int[bandCount];
int imgSize = width * height;
int bandDataType = 0, buf_size = 0;
double maxValue = -Double.MAX_VALUE;
for (int bandIdx = 0; bandIdx < bandCount; bandIdx++)
{
/* Bands are not 0-base indexed, so we must add 1 */
Band imageBand = ds.GetRasterBand(bandIdx + 1);
if (null == imageBand)
{
String message = Logging.getMessage("nullValue.RasterBandIsNull`");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
bandDataType = imageBand.getDataType();
buf_size = imgSize * (gdal.GetDataTypeSize(bandDataType) / 8);
ByteBuffer data = ByteBuffer.allocateDirect(buf_size);
data.order(ByteOrder.nativeOrder());
int colorInt = imageBand.GetRasterColorInterpretation();
if (params.hasKey(AVKey.RASTER_BAND_MAX_PIXEL_VALUE))
{
maxValue = (Double) params.getValue(AVKey.RASTER_BAND_MAX_PIXEL_VALUE);
}
else if ((bandDataType == gdalconstConstants.GDT_UInt16 || bandDataType == gdalconstConstants.GDT_UInt32)
&& colorInt != gdalconst.GCI_AlphaBand && colorInt != gdalconst.GCI_Undefined)
{
imageBand.GetMaximum(dbls);
if (dbls[0] == null)
{
double[] minmax = new double[2];
imageBand.ComputeRasterMinMax(minmax);
maxValue = (minmax[1] > maxValue) ? minmax[1] : maxValue;
}
else
{
maxValue = (dbls[0] > maxValue) ? dbls[0] : maxValue;
}
}
int returnVal = imageBand.ReadRaster_Direct(0, 0, imageBand.getXSize(),
imageBand.getYSize(), width, height, bandDataType, data);
if (returnVal != gdalconstConstants.CE_None)
{
throw new WWRuntimeException(GDALUtils.getErrorMessage());
}
int destBandIdx = bandIdx;
if (colorInt == gdalconst.GCI_RedBand)
{
destBandIdx = 0;
}
else if (colorInt == gdalconst.GCI_GreenBand)
{
destBandIdx = 1;
}
else if (colorInt == gdalconst.GCI_BlueBand)
{
destBandIdx = 2;
}
bands[destBandIdx] = data;
bandsOrder[destBandIdx] = destBandIdx;
offsets[destBandIdx] = 0;
}
int bitsPerColor = gdal.GetDataTypeSize(bandDataType);
int actualBitsPerColor = bitsPerColor;
if (params.hasKey(AVKey.RASTER_BAND_ACTUAL_BITS_PER_PIXEL))
{
actualBitsPerColor = (Integer) params.getValue(AVKey.RASTER_BAND_ACTUAL_BITS_PER_PIXEL);
}
else if (maxValue > 0d)
{
actualBitsPerColor = (int) Math.ceil(Math.log(maxValue) / Math.log(2d));
}
else
{
actualBitsPerColor = bitsPerColor;
}
int[] reqBandOrder = bandsOrder;
try
{
reqBandOrder = extractBandOrder(ds, params);
if (null == reqBandOrder || 0 == reqBandOrder.length)
{
reqBandOrder = bandsOrder;
}
else
{
offsets = new int[reqBandOrder.length];
bandsOrder = new int[reqBandOrder.length];
for (int i = 0; i < reqBandOrder.length; i++)
{
bandsOrder[i] = i;
offsets[i] = 0;
}
}
}
catch (Exception e)
{
reqBandOrder = bandsOrder;
Logging.logger().severe(e.getMessage());
}
DataBuffer imgBuffer = null;
int bufferType = 0;
// A typical sample RGB:
// bitsPerSample is 24=3x8, bitsPerColor { 8,8,8 }, SignificantBitsPerColor {8,8,8}, byteOffsets {2, 1, 0}
// A typical sample RGBA:
// bitsPerSample is 32=4x8, bitsPerColor { 8,8,8,8 }, SignificantBitsPerColor {8,8,8,8}, byteOffsets { 3, 2, 1, 0}
// A typical Aerial Photo Image RGB
// (16 bits per each color, significant bits per color vary from 9bits, 10bits, 11bits, and 12bits
// bitsPerSample is 48=3x16, bitsPerColor { 16,16,16 }, SignificantBitsPerColor { 11,11,11 }, byteOffsets { 4, 2, 0}
// A typical Aerial Photo Image RGBA
// (16 bits per each color, significant bits per color vary from 9bits, 10bits, 11bits, and 12bits
// bitsPerSample is 64=4x16, bitsPerColor { 16,16,16,16 }, SignificantBitsPerColor { 12,12,12,12 }, byteOffsets { 6, 4, 2, 0 }
int reqBandCount = reqBandOrder.length;
boolean hasAlpha = (reqBandCount == 2) || (reqBandCount == 4);
IntBuffer imageMask = null;
if (hasAlpha && params.hasKey(AVKey.GDAL_MASK_DATASET))
{
imageMask = extractImageMask(params);
}
if (bandDataType == gdalconstConstants.GDT_Byte)
{
byte[][] int8 = new byte[reqBandCount][];
for (int i = 0; i < reqBandCount; i++)
{
int srcBandIndex = reqBandOrder[i];
int8[i] = new byte[imgSize];
bands[srcBandIndex].get(int8[i]);
}
if (hasAlpha && null != imageMask)
{
applyImageMask(int8[reqBandCount - 1], imageMask);
}
imgBuffer = new DataBufferByte(int8, imgSize);
bufferType = DataBuffer.TYPE_BYTE;
}
else if (bandDataType == gdalconstConstants.GDT_Int16)
{
short[][] int16 = new short[reqBandCount][];
for (int i = 0; i < reqBandCount; i++)
{
int srcBandIndex = reqBandOrder[i];
int16[i] = new short[imgSize];
bands[srcBandIndex].asShortBuffer().get(int16[i]);
}
if (hasAlpha && null != imageMask)
{
applyImageMask(int16[reqBandCount - 1], imageMask);
}
imgBuffer = new DataBufferShort(int16, imgSize);
bufferType = DataBuffer.TYPE_SHORT;
}
else if (bandDataType == gdalconstConstants.GDT_Int32 || bandDataType == gdalconstConstants.GDT_UInt32)
{
int[][] uint32 = new int[reqBandCount][];
for (int i = 0; i < reqBandCount; i++)
{
int srcBandIndex = reqBandOrder[i];
uint32[i] = new int[imgSize];
bands[srcBandIndex].asIntBuffer().get(uint32[i]);
}
if (hasAlpha && null != imageMask)
{
applyImageMask(uint32[reqBandCount - 1], imageMask);
}
imgBuffer = new DataBufferInt(uint32, imgSize);
bufferType = DataBuffer.TYPE_INT;
}
else if (bandDataType == gdalconstConstants.GDT_UInt16)
{
short[][] uint16 = new short[reqBandCount][];
for (int i = 0; i < reqBandCount; i++)
{
int srcBandIndex = reqBandOrder[i];
uint16[i] = new short[imgSize];
bands[srcBandIndex].asShortBuffer().get(uint16[i]);
}
if (hasAlpha && null != imageMask)
{
applyImageMask(uint16[reqBandCount - 1], imageMask);
}
imgBuffer = new DataBufferUShort(uint16, imgSize);
bufferType = DataBuffer.TYPE_USHORT;
}
else
{
String message = Logging.getMessage("generic.UnrecognizedDataType", bandDataType);
Logging.logger().severe(message);
}
SampleModel sm = new BandedSampleModel(bufferType, width, height, width, bandsOrder, offsets);
WritableRaster raster = Raster.createWritableRaster(sm, imgBuffer, null);
ColorModel cm;
Band band1 = ds.GetRasterBand(1);
if (band1.GetRasterColorInterpretation() == gdalconstConstants.GCI_PaletteIndex)
{
cm = band1.GetRasterColorTable().getIndexColorModel(gdal.GetDataTypeSize(bandDataType));
img = new BufferedImage(cm, raster, false, null);
}
else
{
// Determine the color space.
int transparency = hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
int baseColorSpace = (reqBandCount > 2) ? ColorSpace.CS_sRGB : ColorSpace.CS_GRAY;
ColorSpace cs = ColorSpace.getInstance(baseColorSpace);
int[] nBits = new int[reqBandCount];
for (int i = 0; i < reqBandCount; i++)
{
nBits[i] = actualBitsPerColor;
}
cm = new ComponentColorModel(cs, nBits, hasAlpha, false, transparency, bufferType);
img = new BufferedImage(cm, raster, false, null);
}
if( null != img )
{
if( AVListImpl.getBooleanValue(params, AVKey.BLACK_GAPS_DETECTION, false) )
{
// remove voids
img = detectVoidsAndMakeThemTransparent(img);
}
}
return BufferedImageRaster.wrap(img, params);
}
/**
* Attempts to detect if there are any black|white|gray voids (also called black skirts) i n the image raster caused
* by inaccurate clipping. The algorithm checks each corner of the image and if it detects black|white|gray pixel,
* uses a scanline version of flood fill algorithm to make the area transparent. See more
* http://en.wikipedia.org/wiki/Flood_fill#Scanline_fill
*
* @param sourceImage a source image raster
*
* @return BufferedImage with voids (if detected) filled with a transparent pixel values
*/
protected static BufferedImage detectVoidsAndMakeThemTransparent(BufferedImage sourceImage)
{
BufferedImage dest;
if (sourceImage == null)
{
String message = Logging.getMessage("nullValue.ImageIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int width = sourceImage.getWidth();
int height = sourceImage.getHeight();
if (width <= 3 || height <= 3)
{
// raster size is too small for the algorithm, just return the source raster
return sourceImage;
}
try
{
// first run (creates a copy and flips vertically)
dest = verticalFlip(sourceImage);
scanFill(dest);
// second run
dest = verticalFlip(dest);
scanFill(dest);
}
catch (Throwable t)
{
Logging.logger().log(java.util.logging.Level.SEVERE, t.getMessage(), t);
dest = sourceImage;
}
return dest;
}
protected static void scanFill(BufferedImage sourceImage)
{
if (null == sourceImage || sourceImage.getWidth() <= 3 || sourceImage.getHeight() <= 3)
return;
ArrayList voids = new ArrayList();
voids.add(0); // a=r=g=b=0
voids.add(0xFF << 24); // a=255, r=g=b=0
voids.add(0xFFFFFFFF); // a=255, r=g=b=255
voids.add(0x00FFFFFF); // a=0, r=g=b=255
int NODATA_TRANSPARENT = 0x00000000;
int width = sourceImage.getWidth();
int height = sourceImage.getHeight();
int[] scanline1 = new int[width + 2];
int[] scanline2 = new int[width + 2];
sourceImage.getRGB(0, 0, width, 1, scanline2, 1, width);
// check the first pixel (in NW corner)
int nw = 0x00FFFFFF & scanline2[1]; // ignore alpha value
if (nw == 0x00808080) // r=g=b=128 (80H)
{
voids.add((0xFF << 24) | nw); // alpha=255(FFH), r=g=b=128 (80H)
voids.add(0x00FFFFFF & nw); // alpha=0, r=g=b=128 (80H)
}
// check the last pixel (in NE corner)
int ne = 0x00FFFFFF & scanline2[width]; // ignore alpha value
if (ne == 0x00808080) // r=g=b=128 (80H)
{
voids.add((0xFF << 24) | ne); // alpha=255(FFH), r=g=b=128 (80H)
voids.add(0x00FFFFFF & ne); // alpha=0, r=g=b=128 (80H)
}
int numVoids = voids.size();
int[] nodata = new int[numVoids];
for (int i = 0; i < numVoids; i++)
{
nodata[i] = voids.get(i);
}
scanline2[0] = scanline2[width + 1] = NODATA_TRANSPARENT;
Arrays.fill(scanline1, NODATA_TRANSPARENT);
int pixel;
for (int h = 0; h < height; h++)
{
int[] scanline0 = scanline1.clone();
scanline1 = scanline2.clone();
if (h + 1 < height)
{
sourceImage.getRGB(0, h + 1, width, 1, scanline2, 1, width);
scanline2[0] = scanline2[width + 1] = NODATA_TRANSPARENT;
}
else
Arrays.fill(scanline2, NODATA_TRANSPARENT);
for (int i = 1; i <= width; i++)
{
pixel = scanline1[i];
for (int v = 0; v < numVoids; v++)
{
if (pixel == nodata[v] &&
(scanline0[i - 1] == NODATA_TRANSPARENT || scanline0[i] == NODATA_TRANSPARENT
|| scanline0[i + 1] == NODATA_TRANSPARENT || scanline1[i - 1] == NODATA_TRANSPARENT
|| scanline1[i + 1] == NODATA_TRANSPARENT || scanline2[i - 1] == NODATA_TRANSPARENT
|| scanline2[i] == NODATA_TRANSPARENT || scanline2[i + 1] == NODATA_TRANSPARENT))
{
scanline1[i] = NODATA_TRANSPARENT;
break;
}
}
}
sourceImage.setRGB(0, h, width, 1, scanline1, 1, width);
}
}
/**
* Flips image raster vertically
*
* @param img A source raster as a BufferedImage
*
* @return A vertically flipped image raster as a BufferedImage
*/
protected static BufferedImage verticalFlip(BufferedImage img)
{
if (null == img)
return null;
int w = img.getWidth();
int h = img.getHeight();
// BufferedImage flipImg = new BufferedImage(w, h, img.getColorModel().getTransparency() );
BufferedImage flipImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = flipImg.createGraphics();
java.awt.Composite prevComposite = g2d.getComposite();
g2d.setComposite(java.awt.AlphaComposite.Src);
g2d.drawImage(img, 0, 0, w, h, 0, h, w, 0, null);
g2d.setComposite(prevComposite);
g2d.dispose();
return flipImg;
}
protected static void applyImageMask(byte[] alphaBand, IntBuffer maskBand)
{
if (null == alphaBand || null == maskBand || alphaBand.length != maskBand.capacity())
{
return;
}
int size = alphaBand.length;
maskBand.rewind();
for (int i = 0; i < size; i++)
{
long pixel = ALPHA_MASK & maskBand.get();
if (pixel == ALPHA_MASK)
{
alphaBand[i] = ALPHA_TRANSPARENT;
}
}
maskBand.rewind();
}
protected static void applyImageMask(short[] alphaBand, IntBuffer maskBand)
{
if (null == alphaBand || null == maskBand || alphaBand.length != maskBand.capacity())
{
return;
}
int size = alphaBand.length;
maskBand.rewind();
for (int i = 0; i < size; i++)
{
long pixel = ALPHA_MASK & maskBand.get();
if (pixel == ALPHA_MASK)
{
alphaBand[i] = ALPHA_TRANSPARENT;
}
}
maskBand.rewind();
}
protected static void applyImageMask(int[] alphaBand, IntBuffer maskBand)
{
if (null == alphaBand || null == maskBand || alphaBand.length != maskBand.capacity())
{
return;
}
int size = alphaBand.length;
maskBand.rewind();
for (int i = 0; i < size; i++)
{
long pixel = ALPHA_MASK & maskBand.get();
if (pixel == ALPHA_MASK)
{
alphaBand[i] = ALPHA_TRANSPARENT;
}
}
maskBand.rewind();
}
protected static IntBuffer extractImageMask(AVList params)
{
if (null == params || !params.hasKey(AVKey.GDAL_MASK_DATASET))
{
return null;
}
try
{
Object o = params.getValue(AVKey.GDAL_MASK_DATASET);
if (o instanceof Dataset)
{
Dataset maskDS = (Dataset) o;
Band maskBand = maskDS.GetRasterBand(1);
if (null == maskBand)
{
String message = Logging.getMessage("nullValue.RasterBandIsNull");
Logging.logger().severe(message);
return null;
}
int width = maskDS.getRasterXSize();
int height = maskDS.getRasterYSize();
int maskBandDataType = maskBand.getDataType();
int maskDataSize = width * height * (gdal.GetDataTypeSize(maskBandDataType) / 8);
ByteBuffer maskData = ByteBuffer.allocateDirect(maskDataSize);
maskData.order(ByteOrder.nativeOrder());
int returnVal = maskBand.ReadRaster_Direct(0, 0, maskBand.getXSize(),
maskBand.getYSize(), width, height, maskBandDataType, maskData);
if (returnVal != gdalconstConstants.CE_None)
{
throw new WWRuntimeException(GDALUtils.getErrorMessage());
}
return maskData.asIntBuffer();
}
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, e.getMessage(), e);
}
return null;
}
/**
* Calculates geo-transform matrix for a north-up raster
*
* @param sector Geographic area, a Sector
* @param width none-zero width of a raster
* @param height none-zero height of a raster
*
* @return an array of 6 doubles that contain a geo-transform matrix
*
* @throws IllegalArgumentException if sector is null, or raster size is zero
*/
public static double[] calcGetGeoTransform(Sector sector, int width, int height) throws IllegalArgumentException
{
if (null == sector)
{
String message = Logging.getMessage("nullValue.SectorIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (0 == width)
{
String message = Logging.getMessage("generic.InvalidWidth", width);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (0 == height)
{
String message = Logging.getMessage("generic.InvalidHeight", height);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// * geotransform[1] : width of pixel
// * geotransform[4] : rotational coefficient, zero for north up images.
// * geotransform[2] : rotational coefficient, zero for north up images.
// * geotransform[5] : height of pixel (but negative)
// * geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x offset to center of top left pixel.
// * geotransform[3] + 0.5 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left pixel.
double[] gx = new double[6];
gx[GDAL.GT_0_ORIGIN_LON] = sector.getMinLongitude().degrees;
gx[GDAL.GT_1_PIXEL_WIDTH] = Math.abs(sector.getDeltaLonDegrees() / (double) width);
gx[GDAL.GT_2_ROTATION_X] = 0d;
gx[GDAL.GT_3_ORIGIN_LAT] = sector.getMaxLatitude().degrees;
gx[GDAL.GT_4_ROTATION_Y] = 0d;
gx[GDAL.GT_5_PIXEL_HEIGHT] = -Math.abs(sector.getDeltaLatDegrees() / (double) height);
// correct for center of pixel vs. top left of pixel
// GeoTransform[0] -= 0.5 * GeoTransform[1];
// GeoTransform[0] -= 0.5 * GeoTransform[2];
// GeoTransform[3] -= 0.5 * GeoTransform[4];
// GeoTransform[3] -= 0.5 * GeoTransform[5];
return gx;
}
public static SpatialReference createGeographicSRS() throws WWRuntimeException
{
if (!gdalIsAvailable.get())
{
String message = Logging.getMessage("gdal.GDALNotAvailable");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
SpatialReference srs = new SpatialReference();
srs.ImportFromProj4("+proj=latlong +datum=WGS84 +no_defs");
return srs;
}
protected static LatLon getLatLonForRasterPoint(double[] gt, int x, int y, CoordinateTransformation ct)
{
if (!gdalIsAvailable.get())
{
String message = Logging.getMessage("gdal.GDALNotAvailable");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
java.awt.geom.Point2D geoPoint = GDAL.getGeoPointForRasterPoint(gt, x, y);
if (null == geoPoint)
{
return null;
}
double[] latlon = ct.TransformPoint(geoPoint.getX(), geoPoint.getY());
return LatLon.fromDegrees(latlon[1] /* latitude */, latlon[0] /* longitude */);
}
public static AVList extractRasterParameters(Dataset ds) throws IllegalArgumentException, WWRuntimeException
{
return extractRasterParameters(ds, null, false);
}
/**
* Extracts raster parameters to an AVList
*
* @param ds A GDAL dataset
* @param params AVList to hold retrieved metadata, if null, a new instance will be created and returned
* as a return value
* @param quickReadingMode if quick reading mode is enabled GDAL will not spend much time on heavy calculations,
* like for example calculating Min/Max for entire elevation raster
*
* @return AVList with retrieved metadata
*
* @throws IllegalArgumentException when the passed dataset is null pr emtpy, or any of the dimension is 0
* @throws gov.nasa.worldwind.exception.WWRuntimeException
* if GDAL is not available, or a dataset contains no bands
*
* The extractRasterParameters() sets next key/value pairs:
*
* AVKey.WIDTH - the maximum width of the image
*
* AVKey.HEIGHT - the maximum height of the image
*
* AVKey.COORDINATE_SYSTEM - one of the next values: AVKey.COORDINATE_SYSTEM_SCREEN
* AVKey.COORDINATE_SYSTEM_GEOGRAPHIC AVKey.COORDINATE_SYSTEM_PROJECTED
*
* AVKey.SECTOR - in case of Geographic CS, contains a regular Geographic Sector
* defined by lat/lon coordinates of corners in case of Projected CS, contains a
* bounding box of the area
*
* AVKey.COORDINATE_SYSTEM_NAME
*
*
* AVKey.PIXEL_WIDTH (Double) pixel size, UTM images usually specify 1 (1 meter);
* if missing and Geographic Coordinate System is specified will be calculated as
* LongitudeDelta/WIDTH
*
* AVKey.PIXEL_HEIGHT (Double) pixel size, UTM images usually specify 1 (1 meter);
* if missing and Geographic Coordinate System is specified will be calculated as
* LatitudeDelta/HEIGHT
*
* AVKey.ORIGIN (LatLon) specifies coordinate of the image's origin (one of the
* corners, or center) If missing, upper left corner will be set as origin
*
* AVKey.DATE_TIME (0 terminated String, length == 20) if missing, current date &
* time will be used
*
* AVKey.PIXEL_FORMAT required (valid values: AVKey.ELEVATION | AVKey.IMAGE }
* specifies weather it is a digital elevation model or image
*
* AVKey.IMAGE_COLOR_FORMAT required if AVKey.PIXEL_FORMAT is AVKey.IMAGE (valid
* values: AVKey.COLOR and AVKey.MONOCHROME)
*
* AVKey.DATA_TYPE required ( valid values: AVKey.INT16, and AVKey.FLOAT32 )
*
* AVKey.VERSION optional, if missing a default will be used "NASA World Wind"
*
* AVKey.DISPLAY_NAME, (String) optional, specifies a name of the document/image
*
* AVKey.DESCRIPTION (String) optional, for any kind of descriptions
*
* AVKey.MISSING_DATA_SIGNAL optional, set the AVKey.MISSING_DATA_SIGNAL ONLY if
* you know for sure that the specified value actually represents void (NODATA)
* areas. Elevation data usually has "-32767" (like DTED), or "-32768" like SRTM,
* but some has "0" (mostly images) and "-9999" like NED. Note! Setting "-9999" is
* very ambiguos because -9999 for elevation is valid value;
*
* AVKey.MISSING_DATA_REPLACEMENT (String type forced by spec) Most images have
* "NODATA" as "0", elevations have as "-9999", or "-32768" (sometimes "-32767")
*
* AVKey.COORDINATE_SYSTEM required, valid values AVKey.COORDINATE_SYSTEM_GEOGRAPHIC
* or AVKey.COORDINATE_SYSTEM_PROJECTED
*
* AVKey.COORDINATE_SYSTEM_NAME Optional, A name of the Coordinates System as a
* String
*
* AVKey.PROJECTION_EPSG_CODE Required; Integer; EPSG code or Projection Code If CS
* is Geodetic and EPSG code is not specified, a default WGS84 (4326) will be used
*
* AVKey.PROJECTION_DATUM Optional, AVKey.PROJECTION_DESC Optional,
* AVKey.PROJECTION_NAME Optional, AVKey.PROJECTION_UNITS Optional,
*
* AVKey.ELEVATION_UNIT Required, if AVKey.PIXEL_FORMAT = AVKey.ELEVATION, value:
* AVKey.UNIT_FOOT or AVKey.UNIT_METER (default, if not specified)
*
* AVKey.RASTER_PIXEL, optional, values: AVKey.RASTER_PIXEL_IS_AREA or
* AVKey.RASTER_PIXEL_IS_POINT if not specified, default for images is
* RASTER_PIXEL_IS_AREA, and AVKey.RASTER_PIXEL_IS_POINT for elevations
*/
public static AVList extractRasterParameters(Dataset ds, AVList params, boolean quickReadingMode)
throws IllegalArgumentException, WWRuntimeException
{
if (null == params)
{
params = new AVListImpl();
}
if (!gdalIsAvailable.get())
{
String message = Logging.getMessage("gdal.GDALNotAvailable");
Logging.logger().finest(message);
throw new WWRuntimeException(message);
}
if (null == ds)
{
String message = Logging.getMessage("nullValue.DataSetIsNull");
Logging.logger().finest(message);
throw new IllegalArgumentException(message);
}
int width = ds.getRasterXSize();
if (0 >= width)
{
String message = Logging.getMessage("generic.InvalidWidth", width);
Logging.logger().finest(message);
throw new IllegalArgumentException(message);
}
params.setValue(AVKey.WIDTH, width);
int height = ds.getRasterYSize();
if (0 >= height)
{
String message = Logging.getMessage("generic.InvalidHeight", height);
Logging.logger().finest(message);
throw new IllegalArgumentException(message);
}
params.setValue(AVKey.HEIGHT, height);
int bandCount = ds.getRasterCount();
if (0 >= bandCount)
{
String message = Logging.getMessage("generic.UnexpectedBandCount", bandCount);
Logging.logger().finest(message);
throw new WWRuntimeException(message);
}
params.setValue(AVKey.NUM_BANDS, bandCount);
Band band = ds.GetRasterBand(1);
if (null != band)
{
if (band.GetOverviewCount() > 0)
{
params.setValue(AVKey.RASTER_HAS_OVERVIEWS, Boolean.TRUE);
}
int dataType = band.getDataType();
if (dataType == gdalconst.GDT_Int16 || dataType == gdalconst.GDT_CInt16)
{
params.setValue(AVKey.PIXEL_FORMAT, AVKey.ELEVATION);
params.setValue(AVKey.DATA_TYPE, AVKey.INT16);
}
else if (dataType == gdalconst.GDT_Int32 || dataType == gdalconst.GDT_CInt32)
{
params.setValue(AVKey.PIXEL_FORMAT, AVKey.ELEVATION);
params.setValue(AVKey.DATA_TYPE, AVKey.INT32);
}
else if (dataType == gdalconst.GDT_Float32 || dataType == gdalconst.GDT_CFloat32)
{
params.setValue(AVKey.PIXEL_FORMAT, AVKey.ELEVATION);
params.setValue(AVKey.DATA_TYPE, AVKey.FLOAT32);
}
else if (dataType == gdalconst.GDT_Byte)
{
// if has only one band => one byte index of the palette, 216 marks voids
params.setValue(AVKey.IMAGE_COLOR_FORMAT, AVKey.COLOR);
params.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE);
params.setValue(AVKey.DATA_TYPE, AVKey.INT8);
}
else if (dataType == gdalconst.GDT_UInt16)
{
params.setValue(AVKey.IMAGE_COLOR_FORMAT,
((bandCount >= 3) ? AVKey.COLOR : AVKey.GRAYSCALE));
params.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE);
params.setValue(AVKey.DATA_TYPE, AVKey.INT16);
}
else if (dataType == gdalconst.GDT_UInt32)
{
params.setValue(AVKey.IMAGE_COLOR_FORMAT,
((bandCount >= 3) ? AVKey.COLOR : AVKey.GRAYSCALE));
params.setValue(AVKey.PIXEL_FORMAT, AVKey.IMAGE);
params.setValue(AVKey.DATA_TYPE, AVKey.INT32);
}
else
{
String msg = Logging.getMessage("generic.UnrecognizedDataType", dataType);
Logging.logger().severe(msg);
throw new WWRuntimeException(msg);
}
if( "GTiff".equalsIgnoreCase(ds.GetDriver().getShortName())
&& params.hasKey(AVKey.FILE)
&& AVKey.ELEVATION.equals(params.getValue(AVKey.PIXEL_FORMAT))
&& !params.hasKey(AVKey.ELEVATION_UNIT) )
{
GeotiffReader reader = null;
try
{
File src = (File)params.getValue(AVKey.FILE);
AVList tiffParams = new AVListImpl();
reader = new GeotiffReader(src);
reader.copyMetadataTo(tiffParams);
WWUtil.copyValues( tiffParams, params, new String[] { AVKey.ELEVATION_UNIT,
AVKey.ELEVATION_MIN, AVKey.ELEVATION_MAX, AVKey.MISSING_DATA_SIGNAL }, false );
}
catch (Throwable t)
{
Logging.logger().finest(WWUtil.extractExceptionReason(t));
}
finally
{
if( null != reader )
reader.dispose();
}
}
extractMinMaxSampleValues(ds, band, params );
if( AVKey.ELEVATION.equals(params.getValue(AVKey.PIXEL_FORMAT))
&& ( !params.hasKey(AVKey.ELEVATION_MIN)
|| !params.hasKey(AVKey.ELEVATION_MAX)
|| !params.hasKey(AVKey.MISSING_DATA_SIGNAL)
)
// skip this heavy calculation if the file is opened in Quick Reading Node (when checking canRead())
&& !quickReadingMode
)
{
double[] minmax = new double[2];
band.ComputeRasterMinMax(minmax);
if ( ElevationsUtil.isKnownMissingSignal(minmax[0]))
{
params.setValue(AVKey.MISSING_DATA_SIGNAL, minmax[0]);
if( setNoDataValue( band, minmax[0]) )
{
band.ComputeRasterMinMax(minmax);
params.setValue(AVKey.ELEVATION_MIN, minmax[0]);
params.setValue(AVKey.ELEVATION_MAX, minmax[1]);
}
}
else
{
params.setValue(AVKey.ELEVATION_MIN, minmax[0]);
params.setValue(AVKey.ELEVATION_MAX, minmax[1]);
}
}
}
String proj_wkt = null;
if (params.hasKey(AVKey.SPATIAL_REFERENCE_WKT))
{
proj_wkt = params.getStringValue(AVKey.SPATIAL_REFERENCE_WKT);
}
if (WWUtil.isEmpty(proj_wkt))
{
proj_wkt = ds.GetProjectionRef();
}
if (WWUtil.isEmpty(proj_wkt))
{
proj_wkt = ds.GetProjection();
}
SpatialReference srs = null;
if (!WWUtil.isEmpty(proj_wkt))
{
params.setValue(AVKey.SPATIAL_REFERENCE_WKT, proj_wkt);
srs = new SpatialReference(proj_wkt);
}
double[] gt = new double[6];
ds.GetGeoTransform(gt);
if (gt[GDAL.GT_5_PIXEL_HEIGHT] > 0)
{
gt[GDAL.GT_5_PIXEL_HEIGHT] = -gt[GDAL.GT_5_PIXEL_HEIGHT];
}
// calculate geo-coordinates in image's native CS and Projection (these are NOT lat/lon coordinates)
java.awt.geom.Point2D[] corners = GDAL.computeCornersFromGeotransform(gt, width, height);
double minX = GDAL.getMinX(corners);
double minY = GDAL.getMinY(corners);
double maxX = GDAL.getMaxX(corners);
double maxY = GDAL.getMaxY(corners);
double rotX = gt[GDAL.GT_2_ROTATION_X];
double rotY = gt[GDAL.GT_4_ROTATION_Y];
double pixelWidth = gt[GDAL.GT_1_PIXEL_WIDTH];
double pixelHeight = gt[GDAL.GT_5_PIXEL_HEIGHT];
params.setValue(AVKey.PIXEL_WIDTH, pixelWidth);
params.setValue(AVKey.PIXEL_HEIGHT, pixelHeight);
if (minX == 0d && pixelWidth == 1d && rotX == 0d && maxY == 0d && rotY == 0d && pixelHeight == 1d)
{
params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_SCREEN);
}
else if (Angle.isValidLongitude(minX) && Angle.isValidLatitude(maxY)
&& Angle.isValidLongitude(maxX) && Angle.isValidLatitude(minY))
{
if (null == srs)
{
srs = createGeographicSRS();
}
else if (srs.IsGeographic() == 0)
{
String msg = Logging.getMessage("generic.UnexpectedCoordinateSystem", srs.ExportToWkt());
Logging.logger().warning(msg);
srs = createGeographicSRS();
}
}
if (null != srs)
{
Sector sector = null;
if (!params.hasKey(AVKey.SPATIAL_REFERENCE_WKT))
{
params.setValue(AVKey.SPATIAL_REFERENCE_WKT, srs.ExportToWkt());
}
if (srs.IsLocal() == 1)
{
params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_UNKNOWN);
String msg = Logging.getMessage("generic.UnknownCoordinateSystem", proj_wkt);
Logging.logger().severe(msg);
return params;
// throw new WWRuntimeException(msg);
}
// save area in image's native CS and Projection
GDAL.Area area = new GDAL.Area(srs, ds);
if (null != area)
{
params.setValue(AVKey.GDAL_AREA, area);
sector = area.getSector();
if (null != sector)
{
params.setValue(AVKey.SECTOR, sector);
LatLon origin = new LatLon(sector.getMaxLatitude(), sector.getMinLongitude());
params.setValue(AVKey.ORIGIN, origin);
}
}
if (srs.IsGeographic() == 1)
{
params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_GEOGRAPHIC);
// no need to extract anything, all parameters were extracted above
}
else if (srs.IsProjected() == 1)
{
params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_PROJECTED);
// ----8><----------------------------------------------------------------------------------------
// Example of a non-typical GDAL projection string
//
// PROJCS
// [
// "NAD83 / Massachusetts Mainland",
// GEOGCS
// [
// "NAD83",
// DATUM
// [
// "North_American_Datum_1983",
// SPHEROID [ "GRS 1980", 6378137, 298.2572221010002, AUTHORITY[ "EPSG", "7019" ]],
// AUTHORITY [ "EPSG", "6269" ]
// ],
// PRIMEM [ "Greenwich", 0 ],
// UNIT [ "degree", 0.0174532925199433 ],
// AUTHORITY [ "EPSG", "4269" ]
// ],
// PROJECTION [ "Lambert_Conformal_Conic_2SP" ],
// PARAMETER [ "standard_parallel_1",42.68333333333333 ],
// PARAMETER["standard_parallel_2",41.71666666666667],
// PARAMETER["latitude_of_origin",41],
// PARAMETER["central_meridian",-71.5],
// PARAMETER["false_easting",200000],
// PARAMETER["false_northing",750000],
// UNIT [ "metre", 1, AUTHORITY [ "EPSG", "9001" ]],
// AUTHORITY [ "EPSG", "26986" ]
// ]
// ----8><----------------------------------------------------------------------------------------
// String projcs = srs.GetAttrValue("PROJCS");
// String geocs = srs.GetAttrValue("PROJCS|GEOGCS");
// String projcs_unit = srs.GetAttrValue("PROJCS|GEOGCS|UNIT");
String projection = srs.GetAttrValue("PROJCS|PROJECTION");
String unit = srs.GetAttrValue("PROJCS|UNIT");
if (null != unit)
{
unit = unit.toLowerCase();
if ("meter".equals(unit) || "meters".equals(unit) || "metre".equals(unit) || "metres".equals(unit))
{
params.setValue(AVKey.PROJECTION_UNITS, AVKey.UNIT_METER);
}
else if ("foot".equals(unit) || "feet".equals(unit))
{
params.setValue(AVKey.PROJECTION_UNITS, AVKey.UNIT_FOOT);
}
else
{
Logging.logger().warning(Logging.getMessage("generic.UnknownProjectionUnits", unit));
}
}
if (null != projection && 0 < projection.length())
{
params.setValue(AVKey.PROJECTION_NAME, projection);
}
}
else if (srs.IsLocal() == 1)
{
params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_SCREEN);
}
else
{
params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_UNKNOWN);
String msg = Logging.getMessage("generic.UnknownCoordinateSystem", proj_wkt);
Logging.logger().severe(msg);
// throw new WWRuntimeException(msg);
}
}
if (!params.hasKey(AVKey.COORDINATE_SYSTEM))
{
params.setValue(AVKey.COORDINATE_SYSTEM, AVKey.COORDINATE_SYSTEM_UNKNOWN);
}
return params;
}
protected static Double convertStringToDouble(String s)
{
return ( s == null ) ? null : WWUtil.convertStringToDouble(s);
}
protected static void extractMinMaxSampleValues(Dataset ds, Band band, AVList params)
{
if( null != ds && null != params && AVKey.ELEVATION.equals(params.getValue(AVKey.PIXEL_FORMAT)))
{
band = (null != band ) ? band : ds.GetRasterBand(1);
Double[] dbls = new Double[16];
Double minValue = convertStringToDouble(ds.GetMetadataItem("TIFFTAG_MINSAMPLEVALUE"));
Double maxValue = convertStringToDouble(ds.GetMetadataItem("TIFFTAG_MAXSAMPLEVALUE"));
// TODO garakl This feature is not working for GeoTiff files
// String type = band.GetUnitType();
if( minValue == null || maxValue == null )
{
band.GetMinimum(dbls);
minValue = (null != dbls[0] ) ? dbls[0] : minValue;
band.GetMaximum(dbls);
maxValue = (null != dbls[0] ) ? dbls[0] : maxValue;
}
band.GetNoDataValue(dbls);
Double missingSignal = (null != dbls[0])
? dbls[0] : convertStringToDouble(ds.GetMetadataItem("TIFFTAG_GDAL_NODATA"));
if( ElevationsUtil.isKnownMissingSignal(minValue) )
{
if( missingSignal == null )
missingSignal = minValue;
minValue = null;
}
if( null != minValue )
params.setValue(AVKey.ELEVATION_MIN, minValue);
if( null != maxValue )
params.setValue(AVKey.ELEVATION_MAX, maxValue);
if( null != missingSignal )
params.setValue(AVKey.MISSING_DATA_SIGNAL, missingSignal );
}
}
protected static boolean setNoDataValue(Band band, Double nodata)
{
if( null != band && null != nodata )
{
try
{
gdal.PushErrorHandler("CPLQuietErrorHandler");
return gdalconst.CE_None == band.SetNoDataValue( nodata );
}
finally
{
gdal.PopErrorHandler();
}
}
return false;
}
public static DataRaster composeDataRaster(Dataset ds, AVList params)
throws IllegalArgumentException, WWRuntimeException
{
if (!gdalIsAvailable.get())
{
String message = Logging.getMessage("gdal.GDALNotAvailable");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
params = extractRasterParameters(ds, params, false);
String pixelFormat = params.getStringValue(AVKey.PIXEL_FORMAT);
if (AVKey.ELEVATION.equals(pixelFormat))
{
return composeNonImageDataRaster(ds, params);
}
else if (AVKey.IMAGE.equals(pixelFormat))
{
return composeImageDataRaster(ds, params);
}
else
{
String message = Logging.getMessage("generic.UnexpectedRasterType", pixelFormat);
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
}
public static int[] extractBandOrder(Dataset ds, AVList params)
throws IllegalArgumentException, WWRuntimeException
{
if (!gdalIsAvailable.get())
{
String message = Logging.getMessage("gdal.GDALNotAvailable");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
if (null == ds)
{
String message = Logging.getMessage("nullValue.DataSetIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (null == params)
{
String message = Logging.getMessage("nullValue.ParamsIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int[] bandsOrder = null;
if (params.hasKey(AVKey.BANDS_ORDER))
{
int bandsCount = ds.getRasterCount();
Object o = params.getValue(AVKey.BANDS_ORDER);
if (null != o && o instanceof Integer[])
{
Integer[] order = (Integer[]) o;
bandsOrder = new int[order.length];
for (int i = 0; i < order.length; i++)
{
bandsOrder[i] = order[i];
}
}
else if (null != o && o instanceof int[])
{
bandsOrder = (int[]) o;
}
if (null == bandsOrder)
{
String message = Logging.getMessage("nullValue.BandOrderIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (0 == bandsOrder.length)
{
String message = Logging.getMessage("generic.BandOrderIsEmpty");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
for (int i = 0; i < bandsOrder.length; i++)
{
if (bandsOrder[i] < 0 || bandsOrder[i] >= bandsCount)
{
String message = Logging.getMessage("generic.InvalidBandOrder", bandsOrder[i], bandsCount);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
}
}
return bandsOrder;
}
/**
* The "composeDataRaster" method creates a ByteBufferRaster from an elevation (or non-image) Dataset.
*
* @param ds The GDAL dataset with data raster (expected only elevation raster); f or imagery rasters use
* composeImageDataRaster() method
* @param params , The AVList with properties (usually used to force projection info or sector)
*
* @return ByteBufferRaster as DataRaster
*
* @throws IllegalArgumentException if raster parameters (height, width, sector, etc) are invalid
* @throws WWRuntimeException when invalid raster detected (like attempt to use the method for imagery
* raster)
*/
protected static DataRaster composeNonImageDataRaster(Dataset ds, AVList params)
throws IllegalArgumentException, WWRuntimeException
{
String pixelFormat = params.getStringValue(AVKey.PIXEL_FORMAT);
if (!AVKey.ELEVATION.equals(pixelFormat))
{
String message = Logging.getMessage("generic.UnexpectedRasterType", pixelFormat);
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
Object o = params.getValue(AVKey.SECTOR);
if (null == o || !(o instanceof Sector))
{
String message = Logging.getMessage("generic.MissingRequiredParameter", AVKey.SECTOR);
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
Sector sector = (Sector) o;
int bandCount = ds.getRasterCount();
// we expect here one band (elevation rasters have -32767 or -32768 in void places) data raster
if (bandCount != 1)
{
String message = Logging.getMessage("generic.UnexpectedBandCount", bandCount);
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
ByteOrder byteOrder = ByteOrder.nativeOrder();
if (params.hasKey(AVKey.BYTE_ORDER))
{
byteOrder = AVKey.LITTLE_ENDIAN.equals(params.getStringValue(AVKey.BYTE_ORDER))
? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
}
else
{
params.setValue(AVKey.BYTE_ORDER,
(byteOrder == ByteOrder.BIG_ENDIAN) ? AVKey.BIG_ENDIAN : AVKey.LITTLE_ENDIAN);
}
int width = ds.getRasterXSize();
int height = ds.getRasterYSize();
Band band = ds.GetRasterBand(1);
if (null == band)
{
String message = Logging.getMessage("nullValue.RasterBandIsNull");
Logging.logger().severe(message);
throw new WWRuntimeException(message);
}
int dataType = band.getDataType();
int dataTypeSize = gdal.GetDataTypeSize(dataType);
int bufferSize = width * height * (dataTypeSize / 8);
ByteBuffer data = null;
try
{
data = ByteBuffer.allocateDirect(bufferSize);
}
catch (Throwable t)
{
String message = Logging.getMessage("generic.MemoryAllocationError", bufferSize);
Logging.logger().log(Level.SEVERE, message, t);
throw new WWRuntimeException(message);
}
data.order(byteOrder);
int returnVal = band.ReadRaster_Direct(0, 0, band.getXSize(), band.getYSize(),
width, height, band.getDataType(), data);
if (returnVal != gdalconstConstants.CE_None)
{
throw new WWRuntimeException(GDALUtils.getErrorMessage());
}
ByteBufferRaster raster = new ByteBufferRaster(width, height, sector, data, params);
ElevationsUtil.rectify( raster );
return raster;
}
protected static void alterJavaLibraryPath(String newJavaLibraryPath)
throws IllegalAccessException, NoSuchFieldException
{
System.setProperty(JAVA_LIBRARY_PATH, newJavaLibraryPath);
newClassLoader = ClassLoader.class;
fieldSysPaths = newClassLoader.getDeclaredField("sys_paths");
if (null != fieldSysPaths)
{
fieldSysPaths_accessible = fieldSysPaths.isAccessible();
if (!fieldSysPaths_accessible)
{
fieldSysPaths.setAccessible(true);
}
originalClassLoader = fieldSysPaths.get(newClassLoader);
// Reset it to null so that whenever "System.loadLibrary" is called,
// it will be reconstructed with the changed value.
fieldSysPaths.set(newClassLoader, null);
}
}
protected static void restoreJavaLibraryPath()
{
try
{
//Revert back the changes.
if (null != originalClassLoader && null != fieldSysPaths)
{
fieldSysPaths.set(newClassLoader, originalClassLoader);
fieldSysPaths.setAccessible(fieldSysPaths_accessible);
}
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE, e.getMessage(), e);
}
}
private static Class newClassLoader = null;
private static Object originalClassLoader = null;
private static Field fieldSysPaths = null;
private static boolean fieldSysPaths_accessible = false;
}