public void createRenderables() { this.gridElements = new ArrayList<GridElement>(); double gridStep = this.size / 10; Position p1, p2; ArrayList<Position> positions = new ArrayList<Position>(); // South-North lines for (int i = 1; i <= 9; i++) { double easting = this.SWEasting + gridStep * i; positions.clear(); p1 = computePosition(this.UTMZone, this.hemisphere, easting, SWNorthing); p2 = computePosition(this.UTMZone, this.hemisphere, easting, SWNorthing + this.size); if (this.isTruncated) { computeTruncatedSegment(p1, p2, this.UTMZoneSector, positions); } else { positions.add(p1); positions.add(p2); } if (positions.size() > 0) { p1 = positions.get(0); p2 = positions.get(1); Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); Sector lineSector = Sector.boundingSector(p1, p2); GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_EASTING); ge.setValue(easting); this.gridElements.add(ge); } } // West-East lines for (int i = 1; i <= 9; i++) { double northing = this.SWNorthing + gridStep * i; positions.clear(); p1 = computePosition(this.UTMZone, this.hemisphere, SWEasting, northing); p2 = computePosition(this.UTMZone, this.hemisphere, SWEasting + this.size, northing); if (this.isTruncated) { computeTruncatedSegment(p1, p2, this.UTMZoneSector, positions); } else { positions.add(p1); positions.add(p2); } if (positions.size() > 0) { p1 = positions.get(0); p2 = positions.get(1); Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); Sector lineSector = Sector.boundingSector(p1, p2); GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_NORTHING); ge.setValue(northing); this.gridElements.add(ge); } } }
protected void makeTessellatedLocations( Globe globe, int subdivisions, List<LatLon> locations, List<LatLon> tessellatedLocations) { ArrayList<Vec4> points = new ArrayList<Vec4>(); for (LatLon ll : locations) { points.add(globe.computePointFromLocation(ll)); } //noinspection StringEquality if (WWMath.computeWindingOrderOfLocations(locations) != AVKey.COUNTER_CLOCKWISE) Collections.reverse(locations); Vec4 centerPoint = Vec4.computeAveragePoint(points); Vec4 surfaceNormal = globe.computeSurfaceNormalAtPoint(centerPoint); int numPoints = points.size(); float[] coords = new float[3 * numPoints]; for (int i = 0; i < numPoints; i++) { points.get(i).toFloatArray(coords, 3 * i, 3); } GeometryBuilder gb = new GeometryBuilder(); GeometryBuilder.IndexedTriangleArray tessellatedPoints = gb.tessellatePolygon(0, numPoints, coords, surfaceNormal); for (int i = 0; i < subdivisions; i++) { gb.subdivideIndexedTriangleArray(tessellatedPoints); } for (int i = 0; i < tessellatedPoints.getVertexCount(); i++) { Vec4 v = Vec4.fromFloatArray(tessellatedPoints.getVertices(), 3 * i, 3); tessellatedLocations.add(globe.computePositionFromPoint(v)); } }
public Position getReferencePosition() { ArrayList<LatLon> locations = new ArrayList<LatLon>(this.layers.size()); for (Layer l : this.layers) { locations.add(l.getCenter()); } return this.computeReferencePosition(locations, this.getAltitudes()); }
protected Iterable<? extends LatLon> computeDrawLocations( DrawContext dc, SurfaceTileDrawContext sdc) { ArrayList<LatLon> drawList = new ArrayList<LatLon>(); double safeDistanceDegreesSquared = Math.pow(this.computeSafeRadius(dc, sdc).degrees, 2); for (LatLon location : this.getLocations()) { if (this.computeLocationDistanceDegreesSquared(sdc.getSector(), location) <= safeDistanceDegreesSquared) drawList.add(location); } return drawList; }
protected static ArrayList<Position> computeElevations(ArrayList<Position> locations) { Sector sector = Sector.boundingSector(locations); HighResolutionTerrain hrt = new HighResolutionTerrain(new Earth(), sector, null, 1.0); ArrayList<Position> computedPositions = new ArrayList<Position>(); for (LatLon latLon : locations) { Double elevation = hrt.getElevation(latLon); computedPositions.add(new Position(latLon, Math.round(elevation * 10000.0) / 10000.0)); } return computedPositions; }
public void doActionOnButton3() { // Sector sector = Sector.fromDegrees( 44d, 46d, -123.3d, -123.2d ); ArrayList<LatLon> latlons = new ArrayList<LatLon>(); latlons.add(LatLon.fromDegrees(45.50d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.51d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.52d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.53d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.54d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.55d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.56d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.57d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.58d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.59d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.60d, -123.3d)); ElevationModel model = this.wwd.getModel().getGlobe().getElevationModel(); StringBuffer sb = new StringBuffer(); for (LatLon ll : latlons) { double e = model.getElevation(ll.getLatitude(), ll.getLongitude()); sb.append("\n").append(e); } Logging.logger().info(sb.toString()); }
private void writeGeographicImageGeoKeys(ArrayList<TiffIFDEntry> ifds, AVList params) throws IOException { long offset = this.theChannel.position(); if (isImage(params) && isGeographic(params)) { int epsg = GeoTiff.GCS.WGS_84; if (params.hasKey(AVKey.PROJECTION_EPSG_CODE)) epsg = (Integer) params.getValue(AVKey.PROJECTION_EPSG_CODE); 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 */ GeoTiff.GeoKey.RasterType, 0, 1, (short) (0xFFFF & GeoTiff.RasterType.RasterPixelIsArea), /* key 3 */ GeoTiff.GeoKey.GeographicType, 0, 1, (short) (0xFFFF & epsg), /* key 4 */ GeoTiff.GeoKey.GeogAngularUnits, 0, 1, GeoTiff.Unit.Angular.Angular_Degree }; // 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)); } }
@Override public Iterable<? extends LatLon> getLocations(Globe globe) { if (this.boundaries.getContourCount() == 0) return null; ArrayList<LatLon> combinedBoundaries = new ArrayList<LatLon>(); for (int i = 0; i < this.boundaries.getContourCount(); i++) { for (LatLon location : this.boundaries.getContour(i)) { combinedBoundaries.add(location); } } return combinedBoundaries; }
protected static ArrayList<Position> readReferencePositions(String filePath) throws FileNotFoundException { ArrayList<Position> positions = new ArrayList<Position>(); Scanner scanner = new Scanner(new File(filePath)); while (scanner.hasNextDouble()) { double lat = scanner.nextDouble(); double lon = scanner.nextDouble(); double elevation = scanner.nextDouble(); positions.add(Position.fromDegrees(lat, lon, elevation)); } return positions; }
private Iterable<GeographicText> makeIterable(DrawContext dc) { // get dispay dist for this service for use in label annealing double maxDisplayDistance = this.getPlaceNameService().getMaxDisplayDistance(); ArrayList<GeographicText> list = new ArrayList<GeographicText>(); for (int i = 0; i < this.numEntries; i++) { CharSequence str = getText(i); Position pos = getPosition(i); GeographicText text = new UserFacingText(str, pos); text.setFont(this.placeNameService.getFont()); text.setColor(this.placeNameService.getColor()); text.setBackgroundColor(this.placeNameService.getBackgroundColor()); text.setVisible(isNameVisible(dc, this.placeNameService, pos)); text.setPriority(maxDisplayDistance); list.add(text); } return list; }
protected Extent computeExtent(Globe globe, double verticalExaggeration) { List<Layer> cakeLayers = this.getLayers(); if (cakeLayers == null || cakeLayers.isEmpty()) { return null; } else if (cakeLayers.size() == 1) { return cakeLayers.get(0).computeExtent(globe, verticalExaggeration); } else { ArrayList<Box> extents = new ArrayList<Box>(); for (Layer layer : cakeLayers) { extents.add(layer.computeExtent(globe, verticalExaggeration)); } return Box.union(extents); } }
public List<NavigationTile> navTilesVisible( DrawContext dc, double minDistSquared, double maxDistSquared) { ArrayList<NavigationTile> navList = new ArrayList<NavigationTile>(); if (this.isNavSectorVisible(dc, minDistSquared, maxDistSquared)) { if (this.level > 0 && !this.hasSubTiles()) this.buildSubNavTiles(); if (this.hasSubTiles()) { for (NavigationTile nav : subNavTiles) { navList.addAll(nav.navTilesVisible(dc, minDistSquared, maxDistSquared)); } } else // at bottom level navigation tile { navList.add(this); } } return navList; }
@Override protected void doMoveTo(Position oldReferencePosition, Position newReferencePosition) { if (this.boundaries.getContourCount() == 0) return; for (int i = 0; i < this.boundaries.getContourCount(); i++) { ArrayList<LatLon> newLocations = new ArrayList<LatLon>(); for (LatLon ll : this.boundaries.getContour(i)) { Angle heading = LatLon.greatCircleAzimuth(oldReferencePosition, ll); Angle pathLength = LatLon.greatCircleDistance(oldReferencePosition, ll); newLocations.add(LatLon.greatCircleEndPosition(newReferencePosition, heading, pathLength)); } this.boundaries.setContour(i, newLocations); } // We've changed the multi-polygon's list of boundaries; flag the shape as changed. this.onShapeChanged(); }
protected ArrayList<SquareZone> createSquaresGrid( int UTMZone, String hemisphere, Sector UTMZoneSector, double minEasting, double maxEasting, double minNorthing, double maxNorthing) { ArrayList<SquareZone> squares = new ArrayList<SquareZone>(); double startEasting = Math.floor(minEasting / ONEHT) * ONEHT; double startNorthing = Math.floor(minNorthing / ONEHT) * ONEHT; int cols = (int) Math.ceil((maxEasting - startEasting) / ONEHT); int rows = (int) Math.ceil((maxNorthing - startNorthing) / ONEHT); SquareZone[][] squaresArray = new SquareZone[rows][cols]; int col = 0; for (double easting = startEasting; easting < maxEasting; easting += ONEHT) { int row = 0; for (double northing = startNorthing; northing < maxNorthing; northing += ONEHT) { SquareZone sz = new SquareZone(UTMZone, hemisphere, UTMZoneSector, easting, northing, ONEHT); if (sz.boundingSector != null && !sz.isOutsideGridZone()) { squares.add(sz); squaresArray[row][col] = sz; } row++; } col++; } // Keep track of neighbors for (col = 0; col < cols; col++) { for (int row = 0; row < rows; row++) { SquareZone sz = squaresArray[row][col]; if (sz != null) { sz.setNorthNeighbor(row + 1 < rows ? squaresArray[row + 1][col] : null); sz.setEastNeighbor(col + 1 < cols ? squaresArray[row][col + 1] : null); } } } return squares; }
protected void addRectangularArrayShapes(RenderableLayer layer) { ArrayList<AnalyticSurface.GridPointAttributes> pointAttrs = new ArrayList<AnalyticSurface.GridPointAttributes>(); for (double value : this.arrayValues) { Color color = this.colorForValue(value, 0.5); // color for value at 50% brightness pointAttrs.add(AnalyticSurface.createGridPointAttributes(value, color)); } AnalyticSurfaceAttributes attrs = new AnalyticSurfaceAttributes(); attrs.setDrawOutline(false); attrs.setDrawShadow(false); AnalyticSurface surface = new AnalyticSurface(); surface.setSurfaceAttributes(attrs); surface.setSector(this.arraySector); surface.setDimensions(this.arrayWidth, this.arrayHeight); surface.setValues(pointAttrs); surface.setAltitudeMode(WorldWind.CLAMP_TO_GROUND); layer.addRenderable(surface); }
@Override protected void doRestoreState(RestorableSupport rs, RestorableSupport.StateObject context) { super.doRestoreState(rs, context); RestorableSupport.StateObject so = rs.getStateObject(context, "layers"); if (so == null) return; RestorableSupport.StateObject[] lsos = rs.getAllStateObjects(so, "layer"); if (lsos == null || lsos.length == 0) return; ArrayList<Layer> layerList = new ArrayList<Layer>(lsos.length); for (RestorableSupport.StateObject lso : lsos) { if (lso != null) { Layer layer = new Layer(); layer.doRestoreState(rs, lso); layerList.add(layer); } } this.setLayers(layerList); }
protected static ArrayList<Position> generateReferenceLocations( Sector sector, int numLats, int numLons) { ArrayList<Position> locations = new ArrayList<Position>(); double dLat = (sector.getMaxLatitude().degrees - sector.getMinLatitude().degrees) / (numLats - 1); double dLon = (sector.getMaxLongitude().degrees - sector.getMinLongitude().degrees) / (numLons - 1); for (int j = 0; j < numLats; j++) { double lat = sector.getMinLatitude().degrees + j * dLat; for (int i = 0; i < numLons; i++) { double lon = sector.getMinLongitude().degrees + i * dLon; // Specify angles to five decimal places. locations.add( Position.fromDegrees( Math.round(lat * 100000.0) / 100000.0, Math.round(lon * 100000.0) / 100000.0, 0)); } } return locations; }
public void doActionOnButton4() { ArrayList<LatLon> locations = new ArrayList<LatLon>(); locations.add(LatLon.fromDegrees(45.50d, -123.3d)); locations.add(LatLon.fromDegrees(45.52d, -123.3d)); locations.add(LatLon.fromDegrees(45.54d, -123.3d)); locations.add(LatLon.fromDegrees(45.56d, -123.3d)); locations.add(LatLon.fromDegrees(45.58d, -123.3d)); locations.add(LatLon.fromDegrees(45.60d, -123.3d)); locations.add(LatLon.fromDegrees(40.50d, -120.1d)); locations.add(LatLon.fromDegrees(40.52d, -120.2d)); locations.add(LatLon.fromDegrees(40.54d, -120.3d)); locations.add(LatLon.fromDegrees(40.56d, -120.4d)); locations.add(LatLon.fromDegrees(40.58d, -120.5d)); locations.add(LatLon.fromDegrees(40.60d, -120.6d)); // Now, let's find WMSBasicElevationModel WMSBasicElevationModel wmsbem = null; ElevationModel model = this.wwd.getModel().getGlobe().getElevationModel(); if (model instanceof CompoundElevationModel) { CompoundElevationModel cbem = (CompoundElevationModel) model; for (ElevationModel em : cbem.getElevationModels()) { // you can have additional checks if you know specific model name, etc. if (em instanceof WMSBasicElevationModel) { wmsbem = (WMSBasicElevationModel) em; break; } } } else if (model instanceof WMSBasicElevationModel) { wmsbem = (WMSBasicElevationModel) model; } if (null != wmsbem) { ElevationsRetriever retriever = new ElevationsRetriever(wmsbem, locations, 10000, 30000, new NotifyWhenReady()); retriever.start(); } else { String message = Logging.getMessage( "ElevationModel.ExceptionRequestingElevations", "No instance of WMSBasicElevationModel was found"); Logging.logger().severe(message); } }
public void doActionOnButton2() { ArrayList<LatLon> latlons = new ArrayList<LatLon>(); latlons.add(LatLon.fromDegrees(45.50d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.51d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.52d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.53d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.54d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.55d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.56d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.57d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.58d, -123.3d)); // latlons.add( LatLon.fromDegrees( 45.59d, -123.3d ) ); latlons.add(LatLon.fromDegrees(45.60d, -123.3d)); Sector sector = Sector.fromDegrees(44d, 46d, -123d, -121d); // Sector sector = Sector.boundingSector( latlons ); double[] elevations = new double[latlons.size()]; // request resolution of DTED2 (1degree / 3600 ) double targetResolution = Angle.fromDegrees(1d).radians / 3600; double resolutionAchieved = this.wwd .getModel() .getGlobe() .getElevationModel() .getElevations(sector, latlons, targetResolution, elevations); StringBuffer sb = new StringBuffer(); for (double e : elevations) { sb.append("\n").append(e); } sb.append("\nresolutionAchieved = ").append(resolutionAchieved); sb.append(", requested resolution = ").append(targetResolution); Logging.logger().info(sb.toString()); }
public void createRenderables() { this.gridElements = new ArrayList<GridElement>(); ArrayList<Position> positions = new ArrayList<Position>(); Position p1, p2; Object polyline; Sector lineSector; // left segment positions.clear(); if (this.isTruncated) { computeTruncatedSegment(sw, nw, this.UTMZoneSector, positions); } else { positions.add(sw); positions.add(nw); } if (positions.size() > 0) { p1 = positions.get(0); p2 = positions.get(1); polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); lineSector = Sector.boundingSector(p1, p2); GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_WEST); ge.setValue(this.SWEasting); this.gridElements.add(ge); } // right segment positions.clear(); if (this.isTruncated) { computeTruncatedSegment(se, ne, this.UTMZoneSector, positions); } else { positions.add(se); positions.add(ne); } if (positions.size() > 0) { p1 = positions.get(0); p2 = positions.get(1); polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); lineSector = Sector.boundingSector(p1, p2); GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_EAST); ge.setValue(this.SWEasting + this.size); this.gridElements.add(ge); } // bottom segment positions.clear(); if (this.isTruncated) { computeTruncatedSegment(sw, se, this.UTMZoneSector, positions); } else { positions.add(sw); positions.add(se); } if (positions.size() > 0) { p1 = positions.get(0); p2 = positions.get(1); polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); lineSector = Sector.boundingSector(p1, p2); GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_SOUTH); ge.setValue(this.SWNorthing); this.gridElements.add(ge); } // top segment positions.clear(); if (this.isTruncated) { computeTruncatedSegment(nw, ne, this.UTMZoneSector, positions); } else { positions.add(nw); positions.add(ne); } if (positions.size() > 0) { p1 = positions.get(0); p2 = positions.get(1); polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); lineSector = Sector.boundingSector(p1, p2); GridElement ge = new GridElement(lineSector, polyline, GridElement.TYPE_LINE_NORTH); ge.setValue(this.SWNorthing + this.size); this.gridElements.add(ge); } // Label if (this.name != null) { // Only add a label to squares above some dimension if (this.boundingSector.getDeltaLon().degrees * Math.cos(this.centroid.getLatitude().radians) > .2 && this.boundingSector.getDeltaLat().degrees > .2) { LatLon labelPos = null; if (this.UTMZone != 0) // Not at poles { labelPos = this.centroid; } else if (this.isPositionInside(new Position(this.squareCenter, 0))) { labelPos = this.squareCenter; } else if (this.squareCenter.getLatitude().degrees <= this.UTMZoneSector.getMaxLatitude().degrees && this.squareCenter.getLatitude().degrees >= this.UTMZoneSector.getMinLatitude().degrees) { labelPos = this.centroid; } if (labelPos != null) { GeographicText text = new UserFacingText(this.name, new Position(labelPos, 0)); text.setPriority(this.size * 10); this.gridElements.add( new GridElement(this.boundingSector, text, GridElement.TYPE_GRIDZONE_LABEL)); } } } }
public void writeRaster(BufferWrapperRaster raster) throws IOException, IllegalArgumentException { if (raster == null) { String msg = Logging.getMessage("nullValue.RasterIsNull"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (0 == raster.getWidth() || 0 == raster.getHeight()) { String msg = Logging.getMessage("generic.InvalidImageSize", raster.getWidth(), raster.getHeight()); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } this.validateParameters(raster, raster.getWidth(), raster.getHeight()); int bitsPerSample, samplesPerPixel, sampleFormat, photometric, numBands; if (AVKey.ELEVATION.equals(raster.getValue(AVKey.PIXEL_FORMAT))) { if (AVKey.FLOAT32.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.IEEEFLOAT; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.ELEVATIONS_FLOAT32; } else if (AVKey.INT16.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.SIGNED; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.ELEVATIONS_INT16; } else { String msg = Logging.getMessage("GeotiffWriter.UnsupportedType", raster.getValue(AVKey.DATA_TYPE)); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } else if (AVKey.IMAGE.equals(raster.getValue(AVKey.PIXEL_FORMAT))) { if (AVKey.INT8.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.UNSIGNED; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.MONOCHROME_UINT8; } else if (AVKey.INT16.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 1; samplesPerPixel = Tiff.SamplesPerPixel.MONOCHROME; sampleFormat = Tiff.SampleFormat.UNSIGNED; photometric = Tiff.Photometric.Grayscale_BlackIsZero; bitsPerSample = Tiff.BitsPerSample.MONOCHROME_UINT16; } else if (AVKey.INT32.equals(raster.getValue(AVKey.DATA_TYPE))) { numBands = 3; // TODO check ALPHA / Transparency samplesPerPixel = Tiff.SamplesPerPixel.RGB; sampleFormat = Tiff.SampleFormat.UNSIGNED; photometric = Tiff.Photometric.Color_RGB; bitsPerSample = Tiff.BitsPerSample.RGB; } else { String msg = Logging.getMessage("GeotiffWriter.UnsupportedType", raster.getValue(AVKey.DATA_TYPE)); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } else { String msg = Logging.getMessage("GeotiffWriter.UnsupportedType", raster.getValue(AVKey.PIXEL_FORMAT)); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } int bytesPerSample = numBands * bitsPerSample / Byte.SIZE; this.writeTiffHeader(); // write the image data... int numRows = raster.getHeight(); int numCols = raster.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; BufferWrapper srcBuffer = raster.getBuffer(); ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * bytesPerSample); switch (bitsPerSample) { // case Tiff.BitsPerSample.MONOCHROME_BYTE: case Tiff.BitsPerSample.MONOCHROME_UINT8: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols * numBands; x++) { dataBuff.put(srcBuffer.getByte(x + y * numCols)); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; // case Tiff.BitsPerSample.MONOCHROME_UINT16: case Tiff.BitsPerSample.ELEVATIONS_INT16: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols * numBands; x++) { dataBuff.putShort(srcBuffer.getShort(x + y * numCols)); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; case Tiff.BitsPerSample.ELEVATIONS_FLOAT32: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols * numBands; x++) { dataBuff.putFloat(srcBuffer.getFloat(x + y * numCols)); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; case Tiff.BitsPerSample.RGB: { for (int y = 0; y < numRows; y++) { stripOffsets[y] = (int) this.theChannel.position(); stripCounts[y] = numCols * bytesPerSample; dataBuff.clear(); for (int x = 0; x < numCols; x++) { int color = srcBuffer.getInt(x + y * numCols); byte red = (byte) (0xFF & (color >> 16)); byte green = (byte) (0xFF & (color >> 8)); byte blue = (byte) (0xFF & color); // dataBuff.put(0xFF & (color >> 24)); // alpha dataBuff.put(red).put(green).put(blue); } dataBuff.flip(); this.theChannel.write(dataBuff); } } break; } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); long offset = this.theChannel.position(); if (Tiff.BitsPerSample.RGB == bitsPerSample) { short[] bps = new short[numBands]; for (int i = 0; i < numBands; i++) { bps[i] = Tiff.BitsPerSample.MONOCHROME_BYTE; } this.theChannel.write(ByteBuffer.wrap(this.getBytes(bps))); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, numBands, offset)); } else ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, 1, bitsPerSample)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add(new TiffIFDEntry(Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, photometric)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, samplesPerPixel)); ifds.add(new TiffIFDEntry(Tiff.Tag.ORIENTATION, Tiff.Type.SHORT, 1, Tiff.Orientation.DEFAULT)); ifds.add( new TiffIFDEntry( Tiff.Tag.PLANAR_CONFIGURATION, Tiff.Type.SHORT, 1, Tiff.PlanarConfiguration.CHUNKY)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLE_FORMAT, Tiff.Type.SHORT, 1, sampleFormat)); offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the // ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, raster); this.writeIFDs(ifds); }
/** Create the graticule grid elements */ private void createUTMRenderables() { this.gridElements = new ArrayList<GridElement>(); ArrayList<Position> positions = new ArrayList<Position>(); // Generate meridians and zone labels int lon = -180; int zoneNumber = 1; int maxLat; for (int i = 0; i < 60; i++) { Angle longitude = Angle.fromDegrees(lon); // Meridian positions.clear(); positions.add(new Position(Angle.fromDegrees(-80), longitude, 10e3)); positions.add(new Position(Angle.fromDegrees(-60), longitude, 10e3)); positions.add(new Position(Angle.fromDegrees(-30), longitude, 10e3)); positions.add(new Position(Angle.ZERO, longitude, 10e3)); positions.add(new Position(Angle.fromDegrees(30), longitude, 10e3)); if (lon < 6 || lon > 36) { // 'regular' UTM meridians maxLat = 84; positions.add(new Position(Angle.fromDegrees(60), longitude, 10e3)); positions.add(new Position(Angle.fromDegrees(maxLat), longitude, 10e3)); } else { // Exceptions: shorter meridians around and north-east of Norway if (lon == 6) { maxLat = 56; positions.add(new Position(Angle.fromDegrees(maxLat), longitude, 10e3)); } else { maxLat = 72; positions.add(new Position(Angle.fromDegrees(60), longitude, 10e3)); positions.add(new Position(Angle.fromDegrees(maxLat), longitude, 10e3)); } } Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); Sector sector = Sector.fromDegrees(-80, maxLat, lon, lon); this.gridElements.add(new GridElement(sector, polyline, GridElement.TYPE_LINE)); // Zone label GeographicText text = new UserFacingText(zoneNumber + "", Position.fromDegrees(0, lon + 3, 0)); sector = Sector.fromDegrees(-90, 90, lon + 3, lon + 3); this.gridElements.add(new GridElement(sector, text, GridElement.TYPE_LONGITUDE_LABEL)); // Increase longitude and zone number lon += 6; zoneNumber++; } // Generate special meridian segments for exceptions around and north-east of Norway for (int i = 0; i < 5; i++) { positions.clear(); lon = specialMeridians[i][0]; positions.add( new Position(Angle.fromDegrees(specialMeridians[i][1]), Angle.fromDegrees(lon), 10e3)); positions.add( new Position(Angle.fromDegrees(specialMeridians[i][2]), Angle.fromDegrees(lon), 10e3)); Object polyline = createLineRenderable(positions, Polyline.GREAT_CIRCLE); Sector sector = Sector.fromDegrees(specialMeridians[i][1], specialMeridians[i][2], lon, lon); this.gridElements.add(new GridElement(sector, polyline, GridElement.TYPE_LINE)); } // Generate parallels - no exceptions int lat = -80; for (int i = 0; i < 21; i++) { Angle latitude = Angle.fromDegrees(lat); for (int j = 0; j < 4; j++) { // Each prallel is divided into four 90 degrees segments positions.clear(); lon = -180 + j * 90; positions.add(new Position(latitude, Angle.fromDegrees(lon), 10e3)); positions.add(new Position(latitude, Angle.fromDegrees(lon + 30), 10e3)); positions.add(new Position(latitude, Angle.fromDegrees(lon + 60), 10e3)); positions.add(new Position(latitude, Angle.fromDegrees(lon + 90), 10e3)); Object polyline = createLineRenderable(positions, Polyline.LINEAR); Sector sector = Sector.fromDegrees(lat, lat, lon, lon + 90); this.gridElements.add(new GridElement(sector, polyline, GridElement.TYPE_LINE)); } // Latitude band label if (i < 20) { GeographicText text = new UserFacingText(latBands.charAt(i) + "", Position.fromDegrees(lat + 4, 0, 0)); Sector sector = Sector.fromDegrees(lat + 4, lat + 4, -180, 180); this.gridElements.add(new GridElement(sector, text, GridElement.TYPE_LATITUDE_LABEL)); } // Increase latitude lat += lat < 72 ? 8 : 12; } }
private void writeColorImage(BufferedImage image, AVList params) throws IOException { int numBands = image.getRaster().getNumBands(); long offset; this.writeTiffHeader(); // write the image data... int numRows = image.getHeight(); int numCols = image.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * numBands); Raster rast = image.getRaster(); for (int i = 0; i < numRows; i++) { stripOffsets[i] = (int) this.theChannel.position(); stripCounts[i] = numCols * numBands; int[] rowData = rast.getPixels(0, i, image.getWidth(), 1, (int[]) null); dataBuff.clear(); for (int j = 0; j < numCols * numBands; j++) { putUnsignedByte(dataBuff, rowData[j]); } dataBuff.flip(); this.theChannel.write(dataBuff); } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); ifds.add( new TiffIFDEntry( Tiff.Tag.PLANAR_CONFIGURATION, Tiff.Type.SHORT, 1, Tiff.PlanarConfiguration.CHUNKY)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, numBands)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add( new TiffIFDEntry( Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, Tiff.Photometric.Color_RGB)); ifds.add(new TiffIFDEntry(Tiff.Tag.ORIENTATION, Tiff.Type.SHORT, 1, Tiff.Orientation.DEFAULT)); offset = this.theChannel.position(); short[] bps = new short[numBands]; for (int i = 0; i < numBands; i++) { bps[i] = Tiff.BitsPerSample.MONOCHROME_BYTE; } this.theChannel.write(ByteBuffer.wrap(this.getBytes(bps))); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, numBands, offset)); offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, params); this.writeIFDs(ifds); }
private void writeGrayscaleImage(BufferedImage image, AVList params) throws IOException { int type = image.getType(); int bitsPerSample = (BufferedImage.TYPE_USHORT_GRAY == type) ? Tiff.BitsPerSample.MONOCHROME_UINT16 : Tiff.BitsPerSample.MONOCHROME_UINT8; int numBands = image.getSampleModel().getNumBands(); // well, numBands for GrayScale images must be 1 int bytesPerSample = numBands * bitsPerSample / Byte.SIZE; this.writeTiffHeader(); // write the image data... int numRows = image.getHeight(); int numCols = image.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * bytesPerSample); Raster rast = image.getRaster(); for (int i = 0; i < numRows; i++) { stripOffsets[i] = (int) this.theChannel.position(); stripCounts[i] = numCols * bytesPerSample; int[] rowData = rast.getPixels(0, i, image.getWidth(), 1, (int[]) null); dataBuff.clear(); if (BufferedImage.TYPE_USHORT_GRAY == type) { for (int j = 0; j < numCols * numBands; j++) { this.putUnsignedShort(dataBuff, rowData[j]); } } else if (BufferedImage.TYPE_BYTE_GRAY == type) { for (int j = 0; j < numCols * numBands; j++) { this.putUnsignedByte(dataBuff, rowData[j]); } } dataBuff.flip(); this.theChannel.write(dataBuff); } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, 1, bitsPerSample)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add( new TiffIFDEntry( Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, Tiff.Photometric.Grayscale_BlackIsZero)); ifds.add( new TiffIFDEntry(Tiff.Tag.SAMPLE_FORMAT, Tiff.Type.SHORT, 1, Tiff.SampleFormat.UNSIGNED)); long offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, numBands)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the // ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, params); this.writeIFDs(ifds); }
private void appendGeoTiff(ArrayList<TiffIFDEntry> ifds, AVList params) throws IOException, IllegalArgumentException { if (null == params || 0 == params.getEntries().size()) { String reason = Logging.getMessage("nullValue.AVListIsNull"); Logging.logger().finest(Logging.getMessage("GeotiffWriter.GeoKeysMissing", reason)); return; } long offset = this.theChannel.position(); if (params.hasKey(AVKey.DISPLAY_NAME)) { String value = params.getStringValue(AVKey.DISPLAY_NAME); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.trim().getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add(new TiffIFDEntry(Tiff.Tag.DOCUMENT_NAME, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.DESCRIPTION)) { String value = params.getStringValue(AVKey.DESCRIPTION); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.trim().getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(Tiff.Tag.IMAGE_DESCRIPTION, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.VERSION)) { String value = params.getStringValue(AVKey.VERSION); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.trim().getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(Tiff.Tag.SOFTWARE_VERSION, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.DATE_TIME)) { String value = params.getStringValue(AVKey.DATE_TIME); if (null != value && 0 < value.trim().length()) { offset = this.theChannel.position(); byte[] bytes = value.getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add(new TiffIFDEntry(Tiff.Tag.DATE_TIME, Tiff.Type.ASCII, bytes.length, offset)); } } if (params.hasKey(AVKey.SECTOR)) { if (params.hasKey(AVKey.PIXEL_WIDTH) && params.hasKey(AVKey.PIXEL_HEIGHT)) { offset = this.theChannel.position(); double[] values = new double[] { (Double) params.getValue(AVKey.PIXEL_WIDTH), (Double) params.getValue(AVKey.PIXEL_HEIGHT), isElevation(params) ? 1d : 0d }; byte[] bytes = this.getBytes(values); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry( GeoTiff.Tag.MODEL_PIXELSCALE, Tiff.Type.DOUBLE, values.length, offset)); } if (params.hasKey(AVKey.WIDTH) && params.hasKey(AVKey.HEIGHT)) { offset = this.theChannel.position(); double w = (Integer) params.getValue(AVKey.WIDTH); double h = (Integer) params.getValue(AVKey.HEIGHT); Sector sec = (Sector) params.getValue(AVKey.SECTOR); double[] values = new double[] { // i , j, k=0, x, y, z=0 0d, 0d, 0d, sec.getMinLongitude().degrees, sec.getMaxLatitude().degrees, 0d, w - 1, 0d, 0d, sec.getMaxLongitude().degrees, sec.getMaxLatitude().degrees, 0d, w - 1, h - 1, 0d, sec.getMaxLongitude().degrees, sec.getMinLatitude().degrees, 0d, 0d, h - 1, 0d, sec.getMinLongitude().degrees, sec.getMinLatitude().degrees, 0d, }; byte[] bytes = this.getBytes(values); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add( new TiffIFDEntry(GeoTiff.Tag.MODEL_TIEPOINT, Tiff.Type.DOUBLE, values.length, offset)); } // Tiff.Tag.MODEL_TRANSFORMATION excludes Tiff.Tag.MODEL_TIEPOINT & Tiff.Tag.MODEL_PIXELSCALE if (params.hasKey(AVKey.MISSING_DATA_SIGNAL) || params.hasKey(AVKey.MISSING_DATA_REPLACEMENT)) { offset = this.theChannel.position(); Object nodata = params.hasKey(AVKey.MISSING_DATA_SIGNAL) ? params.getValue(AVKey.MISSING_DATA_SIGNAL) : params.getValue(AVKey.MISSING_DATA_REPLACEMENT); String value = "" + nodata + "\0"; byte[] bytes = value.getBytes(); this.theChannel.write(ByteBuffer.wrap(bytes)); ifds.add(new TiffIFDEntry(GeoTiff.Tag.GDAL_NODATA, Tiff.Type.ASCII, bytes.length, offset)); } if (params.hasKey(AVKey.COORDINATE_SYSTEM)) { String cs = params.getStringValue(AVKey.COORDINATE_SYSTEM); if (AVKey.COORDINATE_SYSTEM_GEOGRAPHIC.equals(cs)) { if (isElevation(params)) this.writeGeographicElevationGeoKeys(ifds, params); else this.writeGeographicImageGeoKeys(ifds, params); } else if (AVKey.COORDINATE_SYSTEM_PROJECTED.equals(cs)) { String msg = Logging.getMessage("GeotiffWriter.FeatureNotImplementedd", cs); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); // TODO extract PCS (Projection Coordinate System) } else { String msg = Logging.getMessage("GeotiffWriter.UnknownCoordinateSystem", cs); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } } }
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)); } }