/** * Rectify elevation raster. For best performance each elevation raster must have correct * parameters and values set. The <code>rectify()</code> operation validates that correct * Elevation min and max values are set or calculated. All values that beyond min/max and voids, * must be marked with "Missing Signal" (aka "nodata" value). * * @param raster A DataRaster to rectify * @throws IllegalArgumentException if <code>raster</code> is <code>null</code> */ public static void rectify(ByteBufferRaster raster) throws IllegalArgumentException { if (null == raster) { String msg = Logging.getMessage("nullValue.RasterIsNull"); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } int width = raster.getWidth(); int height = raster.getHeight(); if (width == 0 || height == 0) { // nothing to do return; } double[] minmax = raster.getExtremes(); if (null == minmax) { // nothing to do return; } Double minValue = minmax[0]; Double maxValue = minmax[1]; Double missingDataSignal = AVListImpl.getDoubleValue(raster, AVKey.MISSING_DATA_SIGNAL, null); // check if the minimum value is one of the well known NODATA values if (ElevationsUtil.isKnownMissingSignal(minValue) || (missingDataSignal != null && missingDataSignal.equals(minValue))) { missingDataSignal = minValue; raster.setTransparentValue(missingDataSignal); minmax = raster.getExtremes(); if (null != minmax) { minValue = minmax[0]; maxValue = minmax[1]; } } BufferWrapper bufferWrapper = raster.getBuffer(); // Allocate a buffer to hold one row of scalar values. double[] array = new double[width]; boolean needsConversion = false; double conversionValue = 1d; if (raster.hasKey(AVKey.ELEVATION_UNIT)) { String unit = raster.getStringValue(AVKey.ELEVATION_UNIT); if (AVKey.UNIT_METER.equalsIgnoreCase(unit)) { needsConversion = false; } else if (AVKey.UNIT_FOOT.equalsIgnoreCase(unit)) { needsConversion = true; conversionValue = WWMath.convertFeetToMeters(1); minValue = WWMath.convertFeetToMeters(minValue); maxValue = WWMath.convertFeetToMeters(maxValue); raster.setValue(AVKey.ELEVATION_UNIT, AVKey.UNIT_METER); } else { needsConversion = false; String msg = Logging.getMessage("generic.UnrecognizedElevationUnit", unit); Logging.logger().warning(msg); } } boolean rasterHasVoids = false; for (int j = 0; j < height; j++) { bufferWrapper.getDouble(j * width, array, 0, width); boolean commitChanges = false; for (int i = 0; i < width; i++) { double value = array[i]; if (null != missingDataSignal && value == missingDataSignal) { rasterHasVoids = true; } else { if (needsConversion) { value *= conversionValue; commitChanges = true; array[i] = value; } if (value < minValue || value > maxValue) { rasterHasVoids = true; if (null != missingDataSignal) { array[i] = missingDataSignal; commitChanges = true; } } } } if (commitChanges) bufferWrapper.putDouble(j * width, array, 0, width); } if (rasterHasVoids) { if (missingDataSignal != null) raster.setValue(AVKey.MISSING_DATA_SIGNAL, missingDataSignal); } else { raster.removeKey(AVKey.MISSING_DATA_SIGNAL); } raster.setValue(AVKey.ELEVATION_MIN, minValue); raster.setValue(AVKey.ELEVATION_MAX, maxValue); }
private void writeGeographicElevationGeoKeys(ArrayList<TiffIFDEntry> ifds, AVList params) throws IOException { long offset = this.theChannel.position(); if (isElevation(params) && isGeographic(params)) { int epsg = GeoTiff.GCS.WGS_84; if (params.hasKey(AVKey.PROJECTION_EPSG_CODE)) epsg = (Integer) params.getValue(AVKey.PROJECTION_EPSG_CODE); int elevUnits = GeoTiff.Unit.Linear.Meter; if (params.hasKey(AVKey.ELEVATION_UNIT)) { if (AVKey.UNIT_FOOT.equals(params.getValue(AVKey.ELEVATION_UNIT))) elevUnits = GeoTiff.Unit.Linear.Foot; } int rasterType = GeoTiff.RasterType.RasterPixelIsArea; if (params.hasKey(AVKey.RASTER_PIXEL) && AVKey.RASTER_PIXEL_IS_POINT.equals(params.getValue(AVKey.RASTER_PIXEL))) rasterType = GeoTiff.RasterType.RasterPixelIsPoint; short[] values = new short[] { // GeoKeyDirectory header GeoTiff.GeoKeyHeader.KeyDirectoryVersion, GeoTiff.GeoKeyHeader.KeyRevision, GeoTiff.GeoKeyHeader.MinorRevision, 0, // IMPORTANT!! we will update count below, after the array initialization // end of header - // geo keys array /* key 1 */ GeoTiff.GeoKey.ModelType, 0, 1, GeoTiff.ModelType.Geographic, /* key 2 */ // TODO: Replace GeoTiff.RasterType.RasterPixelIsPoint GeoTiff.GeoKey.RasterType, 0, 1, (short) (0xFFFF & rasterType), /* key 3 */ GeoTiff.GeoKey.GeographicType, 0, 1, (short) (0xFFFF & epsg), /* key 4 */ GeoTiff.GeoKey.GeogAngularUnits, 0, 1, GeoTiff.Unit.Angular.Angular_Degree, /* key 5 */ GeoTiff.GeoKey.VerticalCSType, 0, 1, GeoTiff.VCS.WGS_84_ellipsoid, /* key 6 */ GeoTiff.GeoKey.VerticalUnits, 0, 1, (short) (0xFFFF & elevUnits), }; // IMPORTANT!! update count - number of geokeys values[3] = (short) (values.length / 4); byte[] bytes = this.getBytes(values); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(GeoTiff.Tag.GEO_KEY_DIRECTORY, Tiff.Type.SHORT, values.length, offset)); } }