Skip to content

Commit

Permalink
Issue 105: Enhance SVM to write GeoTIFF files
Browse files Browse the repository at this point in the history
  • Loading branch information
gwlucastrig committed Jan 10, 2024
1 parent f5ad940 commit 883a15f
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 54 deletions.
43 changes: 27 additions & 16 deletions gis/src/main/java/org/tinfour/gis/geotiff/GeoTiffTableBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.zip.Deflater;
import org.apache.commons.imaging.ImageWriteException;
import org.apache.commons.imaging.PixelDensity;
import org.apache.commons.imaging.common.RationalNumber;
import org.apache.commons.imaging.common.mylzw.MyLzwCompressor;
import org.apache.commons.imaging.formats.tiff.TiffElement;
import org.apache.commons.imaging.formats.tiff.TiffImageData;
import static org.apache.commons.imaging.formats.tiff.constants.GdalLibraryTagConstants.EXIF_TAG_GDAL_NO_DATA;
import org.apache.commons.imaging.formats.tiff.constants.GeoTiffTagConstants;
import static org.apache.commons.imaging.formats.tiff.constants.GeoTiffTagConstants.EXIF_TAG_MODEL_PIXEL_SCALE_TAG;
import static org.apache.commons.imaging.formats.tiff.constants.GeoTiffTagConstants.EXIF_TAG_MODEL_TIEPOINT_TAG;
import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_DEFLATE_ADOBE;
import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_LZW;
import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_UNCOMPRESSED;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
import static org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants.PLANAR_CONFIGURATION_VALUE_CHUNKY;
Expand Down Expand Up @@ -222,7 +222,6 @@ public void setPixelToModelTransform(AffineTransform pixel2Model) {
}
}


/**
* Stores the GeoKey content and model-pixel coordinate transform data to
* the TIFF output directory instance
Expand Down Expand Up @@ -343,9 +342,9 @@ TiffOutputField encodeASCII(TagInfo tInfo, String string)
}

/**
* Encode a block of floating-point values as a tile. This method
* is supports the storage of 32-bit float-point values. At present,
* it only supports the TIFF tile format (with square tiles). It may
* Encode a block of floating-point values as a tile. This method
* is supports the storage of 32-bit float-point values. At present,
* it only supports the TIFF tile format (with square tiles). It may
* be enhanced in the future.
* <p>
* To prepare for the Deflater compression,
Expand Down Expand Up @@ -425,17 +424,28 @@ public byte[] encodeBlock(float[] f, int nRows, int scanSize) {
}
}

// apply the Deflate
Deflater deflater = new Deflater(6);
deflater.setInput(b, 0, b.length);
deflater.finish();
byte[] deflaterResult = new byte[b.length + 1024];
int dN = deflater.deflate(deflaterResult, 0, deflaterResult.length, Deflater.FULL_FLUSH);
if (dN <= 0) {
// deflate failed
try {
final int LZW_MINIMUM_CODE_SIZE = 8;
MyLzwCompressor compressor = new MyLzwCompressor(
LZW_MINIMUM_CODE_SIZE, ByteOrder.BIG_ENDIAN, true);
final byte[] compressed = compressor.compress(b);
return compressed;
} catch (IOException iex) {
return null;
}
return Arrays.copyOf(deflaterResult, dN);

//
// // apply the Deflate
// Deflater deflater = new Deflater(6);
// deflater.setInput(b, 0, b.length);
// deflater.finish();
// byte[] deflaterResult = new byte[b.length + 1024];
// int dN = deflater.deflate(deflaterResult, 0, deflaterResult.length, Deflater.FULL_FLUSH);
// if (dN <= 0) {
// // deflate failed
// return null;
// }
// return Arrays.copyOf(deflaterResult, dN);
}

/**
Expand All @@ -458,7 +468,8 @@ public void writeTiles(File geoTiffFile, int width, int height, int tileSize, by
short planarConfiguration;

if (compressionEnabled) {
compression = TIFF_COMPRESSION_DEFLATE_ADOBE;
//compression = TIFF_COMPRESSION_DEFLATE_ADOBE;
compression = TIFF_COMPRESSION_LZW;
} else {
compression = TIFF_COMPRESSION_UNCOMPRESSED;
}
Expand Down
35 changes: 25 additions & 10 deletions svm/src/main/java/org/tinfour/svm/SvmRasterGeoTiff.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,34 @@ void buildAndWriteRaster(
// here we use the value -131072 because it results in a bit-format
// such that the low-order 3 bytes are all zero.
boolean useDepthModel = false;
float noDataValue = -131072; // raw bytes are 0xc8000000
String gdalNoDataString = "-131072";


if (properties.isBathymetryModelSpecified()) {
SvmBathymetryModel bathyModel = properties.getBathymetryModel();
if (bathyModel.isDepth()) {
useDepthModel = true;
noDataValue = 1;
} else {
useDepthModel = false;
noDataValue = -131072;
}
}

float noDataValue = -1000000f; // raw bytes are 0xc8000000
String gdalNoDataString = "-1000000";
if (properties.isGeoTiffNoDataCodeSpecified()) {
noDataValue = properties.getGeoTiffNoDataCode();
if (noDataValue == ((int) noDataValue)) {
gdalNoDataString = String.format("%d", (int) noDataValue);
} else {
gdalNoDataString = String.format("%f", noDataValue);
}
}

boolean dataCompressionEnabled = properties.isGeoTiffDataCompressionEnabled();

ps.println("");
ps.println("Processing GeoTiff data");
ps.format("Processing GeoTiff data%n");
ps.format(" GDAL no-data code: %s%n", gdalNoDataString);
ps.format(" Data Compression: %s%n", Boolean.toString(dataCompressionEnabled));

boolean status = writeAuxiliaryFiles(ps, data, geoTiffFile, "SvmRasterTemplate.aux.xml");
if (!status) {
Expand Down Expand Up @@ -184,7 +197,7 @@ void buildAndWriteRaster(
IIncrementalTinNavigator navigator = tin.getNavigator();
NaturalNeighborInterpolator nni = new NaturalNeighborInterpolator(tin);
GeoTiffTableBuilder gkTab = new GeoTiffTableBuilder();
gkTab.setCompressionEnabled(true);
gkTab.setCompressionEnabled(dataCompressionEnabled);
for (int iTileRow = 0; iTileRow < nRowsOfTiles; iTileRow++) {
int row0 = iTileRow * tileSize;
int row1 = row0 + tileSize;
Expand Down Expand Up @@ -255,11 +268,13 @@ void buildAndWriteRaster(
int projectionCode = properties.getGeoTiffProjectionCode();
int unitOfMeasureCode = 9001; // meters
SvmUnitSpecification unitOfDistance = properties.getUnitOfDistance();
if(approxEquals(unitOfDistance.getScaleFactor(), 3.28)){
unitOfMeasureCode=9002; // feet;
}else if(approxEquals(unitOfDistance.getScaleFactor(),1.093)){
unitOfMeasureCode=9012;
String uLab = unitOfDistance.getLabel().toLowerCase();
if ("ft".equalsIgnoreCase(uLab)) {
unitOfMeasureCode = 9002; // feet;
} else if (uLab.startsWith("y")) {
unitOfMeasureCode = 9012;
}

try {
gkTab.addGeoKey(GeoKey.GTModelTypeGeoKey, GtModelType.ProjectedCoordinateSystem.getCode());
gkTab.addGeoKey(GeoKey.GTRasterTypeGeoKey, 1);
Expand Down
101 changes: 75 additions & 26 deletions svm/src/main/java/org/tinfour/svm/properties/SvmProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public class SvmProperties {
private final static String gridImageFileName = "rasterImageFileName";
private final static String rasterGeoTiffFileName = "rasterGeoTiffFileName";
private final static String rasterGeoTiffProjectionCode = "rasterGeoTiffProjectionCode";
private final static String rasterGeoTiffNoDataCode = "rasterGeoTiffNoDataCode";
private final static String rasterGeoTiffDataCompression = "rasterGeoTiffDataCompression";

private final static String supplementalConstraintsFileKey = "supplementalConstraints";

Expand Down Expand Up @@ -139,8 +141,8 @@ static public int indexArg(String[] args, String target, boolean valueRequired)
// for the target option is missing). So we need to check.
String s = args[i + 1];
if (s.charAt(0) == '-' && s.length() > 1 && !Character.isDigit(s.charAt(1))) {
throw new IllegalArgumentException(
"Missing value for option " + target);
throw new IllegalArgumentException(
"Missing value for option " + target);
}
}
return i;
Expand Down Expand Up @@ -189,33 +191,34 @@ static public SvmProperties load(String[] args) throws IOException {
/**
* Checks to see if the properties object contains an explicit setting
* for Locale and, if so, puts it into effect.
*
* @throws IOException in the event of an ill-formatted or unknown setting.
*/
private void checkForLocaleSettings() throws IOException {
String q = properties.getProperty("Locale");
if(q==null){
String q = properties.getProperty("Locale");
if (q == null) {
q = properties.getProperty("locale");
}

if(q!=null){
if (q != null) {
// a specific Locale was provided. See if it works
// in this code, we check for an unchecked
// Note that we use an UNCHECKED exception here. When this code was
// developed under Java 8, it was unclear what Java would do in the
// future for handling ill-formed locales.
try{
try {
Locale locale = Locale.forLanguageTag(q);
if(locale == null){
throw new IOException("Unrecognized specification for Locale, \""+q+"\"");
if (locale == null) {
throw new IOException("Unrecognized specification for Locale, \"" + q + "\"");
}
Locale.setDefault(locale);
explicitLocale = locale;
String qf = String.format("%f", 3.14);
if(qf.indexOf(',')>0){
if (qf.indexOf(',') > 0) {
usesCommaForDecimal = true;
}
}catch(RuntimeException rex){
throw new IOException("Invalid specification for Locale, \""+q+"\"");
} catch (RuntimeException rex) {
throw new IOException("Invalid specification for Locale, \"" + q + "\"");
}
}
}
Expand Down Expand Up @@ -247,25 +250,27 @@ public void load(File file) throws IOException {
unitOfVolume = extractUnit("Volume", unitOfVolumeKey, getUnitOfVolume());
}


/**
* Gets the explicit value for the locale, if it was set.
*
* @return if explicitly set, a valid instance; otherwise, a null
*/
public Locale getExplicitLocale(){
public Locale getExplicitLocale() {
return explicitLocale;
}

/**
* Indicates whether the Locale uses the comma character for the
* decimal part of floating-point values.
* This method is used for formatting numeric strings in SVM. Many non-English
* This method is used for formatting numeric strings in SVM. Many non-English
* speaking conventions use the comma rather than the period.
* @return true if the Locale uses periods for decimals; otherwise, false.
*
* @return true if the Locale uses periods for decimals; otherwise, false.
*/
public boolean doesLocaleUseCommaForDecimal(){
public boolean doesLocaleUseCommaForDecimal() {
return usesCommaForDecimal;
}

/**
* Get a list of the SVM file specifications for input samples. The
* SvmFileSpecification may include metadata such as DBF file field name and
Expand Down Expand Up @@ -734,18 +739,16 @@ public File getGridImageFile() {
return extractOutputFile(outputFolderKey, properties.getProperty(gridImageFileName));
}


/**
* Gets the path to a file for writing an output GeoTIFF file containing
* the interpolated grid of water bottom elevations.
*
* @return a valid File instance or a null if not specified.
*/
public File getGeoTiffFile(){
public File getGeoTiffFile() {
return extractOutputFile(outputFolderKey, properties.getProperty(rasterGeoTiffFileName));
}



/**
* Get a reference to a supplemental shapefile providing
* constraints for SVM processing.
Expand Down Expand Up @@ -850,9 +853,9 @@ public File getContourLineShapefile() {
return null;
}


/**
* Indicates whether the optional shoreline append operation is enabled.
*
* @return true if the append operation is enabled; otherwise, false.
*/
public boolean isContourShapefileShorelineEnabled() {
Expand All @@ -862,6 +865,7 @@ public boolean isContourShapefileShorelineEnabled() {
}
return false;
}

/**
* Get the dimensions for the contour graph image file.
*
Expand Down Expand Up @@ -988,27 +992,72 @@ public boolean isBathymetryModelSpecified() {
/**
* Indicates whether the source properties includes a specification
* for the GeoTiff Projection code (usually an EPSG projection code).
*
* @return true if a projection code is set; otherwise, false
*/
public boolean isGeoTiffProjectionCodeSpecified(){
public boolean isGeoTiffProjectionCodeSpecified() {
String s = properties.getProperty(SvmProperties.rasterGeoTiffProjectionCode);
return s != null && !s.isBlank();
}

/**
* Gets the specified GeoTIFF projection code, if any.
*
* @return if available, a valid integer; otherwise, a zero.
*/
public int getGeoTiffProjectionCode(){
public int getGeoTiffProjectionCode() {
String s = properties.getProperty(SvmProperties.rasterGeoTiffProjectionCode);
if(s==null || s.isBlank()){
if (s == null || s.isBlank()) {
return 0;
}
try{
try {
return Integer.parseInt(s.trim());
}catch(NumberFormatException nfe){
} catch (NumberFormatException nfe) {
return 0;
}
}

/**
* Gets a string to be used for the GDAL No-Data element.
*
* @return if specified, a valid non-blank string; otherwise, a null.
*/
public float getGeoTiffNoDataCode() {
String s = properties.getProperty(SvmProperties.rasterGeoTiffNoDataCode);
if (s == null || s.isBlank()) {
return -1000000f;
}
try {
return Float.parseFloat(s.trim());
} catch (NumberFormatException nfe) {
return -1000000f;
}
}

/**
* Indicates whether the properties file specifies a no-data code.
*
* @return true if a no-data code is specified; otherwise, false.
*/
public boolean isGeoTiffNoDataCodeSpecified() {
String s = properties.getProperty(SvmProperties.rasterGeoTiffNoDataCode);
return s != null && !s.isBlank();
}

/**
* Indicates whether data compression is enabled for GeoTIFF data.
*
* @return true if data compression is enabled; otherwise, false.
*/
public boolean isGeoTiffDataCompressionEnabled() {
String s = properties.getProperty(SvmProperties.rasterGeoTiffDataCompression);
if (s != null && !s.isBlank()) {
try {
return Boolean.parseBoolean(s.trim());
} catch (NumberFormatException nfe) {
// no action required
}
}
return false;
}
}
Loading

0 comments on commit 883a15f

Please sign in to comment.