/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2011, Open Source Geospatial Foundation (OSGeo) * * This file is hereby placed into the Public Domain. This means anyone is * free to do whatever they wish with this file. Use it well and enjoy! */ //docs start prelim package org.geotools.tutorial.coverage; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.coverage.grid.io.GridFormatFinder; import org.geotools.coverage.processing.CoverageProcessor; import org.geotools.coverage.processing.Operations; import org.geotools.factory.Hints; import org.geotools.gce.geotiff.GeoTiffFormat; import org.geotools.geometry.Envelope2D; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.resources.Arguments; import org.opengis.geometry.Envelope; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.crs.CoordinateReferenceSystem; import java.io.File; import java.io.IOException; /** * Simple tiling of a coverage based simply on the number vertical/horizontal tiles desired and * subdividing the geographic envelope. Uses coverage processing operations. * */ public class ImageTiler { private final int NUM_HORIZONTAL_TILES = 16; private final int NUM_VERTICAL_TILES = 8; private Integer numberOfHorizontalTiles = NUM_HORIZONTAL_TILES; private Integer numberOfVerticalTiles = NUM_VERTICAL_TILES; private Double tileScale; private File inputFile; private File outputDirectory; private String getFileExtension(File file) { String name = file.getName(); try { return name.substring(name.lastIndexOf(".") + 1); } catch (Exception e) { return ""; } } public Integer getNumberOfHorizontalTiles() { return numberOfHorizontalTiles; } public void setNumberOfHorizontalTiles(Integer numberOfHorizontalTiles) { this.numberOfHorizontalTiles = numberOfHorizontalTiles; } public Integer getNumberOfVerticalTiles() { return numberOfVerticalTiles; } public void setNumberOfVerticalTiles(Integer numberOfVerticalTiles) { this.numberOfVerticalTiles = numberOfVerticalTiles; } public File getInputFile() { return inputFile; } public void setInputFile(File inputFile) { this.inputFile = inputFile; } public File getOutputDirectory() { return outputDirectory; } public void setOutputDirectory(File outputDirectory) { this.outputDirectory = outputDirectory; } public Double getTileScale() { return tileScale; } public void setTileScale(Double tileScale) { this.tileScale = tileScale; } //docs end prelim /** * Argument parsing and initial setup. * * @param args Program arguments * @throws Exception */ //docs start main public static void main(String[] args) throws Exception { //GeoTools provides utility classes to parse command line arguments Arguments processedArgs = new Arguments(args); ImageTiler tiler = new ImageTiler(); try { tiler.setInputFile(new File(processedArgs.getRequiredString("-f"))); tiler.setOutputDirectory(new File(processedArgs.getRequiredString("-o"))); tiler.setNumberOfHorizontalTiles(processedArgs.getOptionalInteger("-htc")); tiler.setNumberOfVerticalTiles(processedArgs.getOptionalInteger("-vtc")); tiler.setTileScale(processedArgs.getOptionalDouble("-scale")); } catch (IllegalArgumentException e) { System.out.println(e.getMessage()); printUsage(); System.exit(1); } tiler.tile(); } private static void printUsage() { System.out.println("Usage: -f inputFile -o outputDirectory [-tw tileWidth<default:256> " + "-th tileHeight<default:256> "); System.out.println("-htc horizontalTileCount<default:16> -vtc verticalTileCount<default:8>"); } //docs end main /** * Crop the coverage to the given envelope * @param gridCoverage coverage to crp * @param envelope envelope to crop it to * @return the cropped coverage */ //docs start cropping private GridCoverage2D cropCoverage(GridCoverage2D gridCoverage, Envelope envelope) { CoverageProcessor processor = CoverageProcessor.getInstance(); //An example of manually creating the operation and parameters we want final ParameterValueGroup param = processor.getOperation("CoverageCrop").getParameters(); param.parameter("Source").setValue(gridCoverage); param.parameter("Envelope").setValue(envelope); return (GridCoverage2D) processor.doOperation(param); } //docs end cropping /** * Create the target tile envelope. * @param coverageMinX minimum x of our coverage * @param coverageMinY minimum y of our coverage * @param geographicTileWidth our target tile envelope width * @param geographicTileHeight our target tile envelope height * @param targetCRS the target tile CRS * @param horizontalIndex horizontal index of the tile envelope * @param verticalIndex vertical index of the tile envelope * @return tile envelope */ //docs start make envelope private Envelope getTileEnvelope(double coverageMinX, double coverageMinY, double geographicTileWidth, double geographicTileHeight, CoordinateReferenceSystem targetCRS, int horizontalIndex, int verticalIndex) { double envelopeStartX = (horizontalIndex * geographicTileWidth) + coverageMinX; double envelopeEndX = envelopeStartX + geographicTileWidth; double envelopeStartY = (verticalIndex * geographicTileHeight) + coverageMinY; double envelopeEndY = envelopeStartY + geographicTileHeight; return new ReferencedEnvelope( envelopeStartX, envelopeEndX, envelopeStartY, envelopeEndY, targetCRS); } //docs end make envelope //docs start load coverage private void tile() throws IOException { AbstractGridFormat format = GridFormatFinder.findFormat(this.getInputFile()); String fileExtension = this.getFileExtension(this.getInputFile()); //working around a bug/quirk in geotiff loading via format.getReader which doesn't set this //correctly Hints hints = null; if (format instanceof GeoTiffFormat) { hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,Boolean.TRUE); } GridCoverage2DReader gridReader = format.getReader( this.getInputFile(), hints); GridCoverage2D gridCoverage = gridReader.read(null); //docs end load coverage //docs start envelope Envelope2D coverageEnvelope = gridCoverage.getEnvelope2D(); double coverageMinX = coverageEnvelope.getBounds().getMinX(); double coverageMaxX = coverageEnvelope.getBounds().getMaxX(); double coverageMinY = coverageEnvelope.getBounds().getMinY(); double coverageMaxY = coverageEnvelope.getBounds().getMaxY(); int htc = this.getNumberOfHorizontalTiles() != null ? this.getNumberOfHorizontalTiles() : NUM_HORIZONTAL_TILES; int vtc = this.getNumberOfVerticalTiles() != null ? this.getNumberOfVerticalTiles() : NUM_VERTICAL_TILES; double geographicTileWidth = (coverageMaxX - coverageMinX) / (double)htc; double geographicTileHeight = (coverageMaxY - coverageMinY) / (double)vtc; CoordinateReferenceSystem targetCRS = gridCoverage.getCoordinateReferenceSystem(); //make sure to create our output directory if it doesn't already exist File tileDirectory = this.getOutputDirectory(); if (!tileDirectory.exists()) { tileDirectory.mkdirs(); } //iterate over our tile counts for (int i = 0; i < htc; i++) { for (int j = 0; j < vtc; j++) { System.out.println("Processing tile at indices i: " + i + " and j: " + j); //create the envelope of the tile Envelope envelope = getTileEnvelope(coverageMinX, coverageMinY, geographicTileWidth, geographicTileHeight, targetCRS, i, j); GridCoverage2D finalCoverage = cropCoverage(gridCoverage, envelope); if (this.getTileScale() != null) { finalCoverage = scaleCoverage(finalCoverage); } //use the AbstractGridFormat's writer to write out the tile File tileFile = new File(tileDirectory, i + "_" + j + "." + fileExtension); format.getWriter(tileFile).write(finalCoverage, null); } } } //docs end envelope //docs start scale /** * Scale the coverage based on the set tileScale * * As an alternative to using parameters to do the operations, we can use the * Operations class to do them in a slightly more type safe way. * * @param coverage the coverage to scale * @return the scaled coverage */ private GridCoverage2D scaleCoverage(GridCoverage2D coverage) { Operations ops = new Operations(null); coverage = (GridCoverage2D) ops.scale( coverage, this.getTileScale(), this.getTileScale(), 0, 0); return coverage; } } //docs end scale