0001: /*
0002: * Geotools2 - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation;
0009: * version 2.1 of the License.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: *
0016: */
0017: package org.geotools.gce.gtopo30;
0018:
0019: import java.awt.RenderingHints;
0020: import java.awt.geom.AffineTransform;
0021: import java.awt.image.DataBuffer;
0022: import java.awt.image.RenderedImage;
0023: import java.awt.image.renderable.ParameterBlock;
0024: import java.io.BufferedOutputStream;
0025: import java.io.BufferedWriter;
0026: import java.io.File;
0027: import java.io.FileNotFoundException;
0028: import java.io.FileOutputStream;
0029: import java.io.FileWriter;
0030: import java.io.IOException;
0031: import java.io.PrintWriter;
0032: import java.io.UnsupportedEncodingException;
0033: import java.net.URL;
0034: import java.net.URLDecoder;
0035: import java.util.Iterator;
0036: import java.util.List;
0037: import java.util.logging.Level;
0038: import java.util.logging.Logger;
0039: import java.util.zip.ZipEntry;
0040: import java.util.zip.ZipOutputStream;
0041:
0042: import javax.imageio.ImageIO;
0043: import javax.imageio.ImageWriteParam;
0044: import javax.imageio.ImageWriter;
0045: import javax.imageio.stream.ImageOutputStream;
0046: import javax.media.jai.Histogram;
0047: import javax.media.jai.ImageLayout;
0048: import javax.media.jai.InterpolationBilinear;
0049: import javax.media.jai.JAI;
0050: import javax.media.jai.ParameterBlockJAI;
0051: import javax.media.jai.PlanarImage;
0052: import javax.media.jai.RenderedOp;
0053:
0054: import org.geotools.coverage.Category;
0055: import org.geotools.coverage.GridSampleDimension;
0056: import org.geotools.coverage.grid.GeneralGridRange;
0057: import org.geotools.coverage.grid.GridCoverage2D;
0058: import org.geotools.coverage.grid.GridGeometry2D;
0059: import org.geotools.coverage.grid.io.AbstractGridCoverageWriter;
0060: import org.geotools.coverage.grid.io.AbstractGridFormat;
0061: import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams;
0062: import org.geotools.coverage.processing.operation.Resample;
0063: import org.geotools.coverage.processing.operation.SelectSampleDimension;
0064: import org.geotools.data.DataSourceException;
0065: import org.geotools.factory.Hints;
0066: import org.geotools.parameter.Parameter;
0067: import org.geotools.referencing.operation.matrix.XAffineTransform;
0068: import org.geotools.resources.CRSUtilities;
0069: import org.geotools.resources.image.CoverageUtilities;
0070: import org.geotools.util.NumberRange;
0071: import org.opengis.coverage.grid.Format;
0072: import org.opengis.coverage.grid.GridCoverage;
0073: import org.opengis.coverage.grid.GridCoverageWriter;
0074: import org.opengis.parameter.GeneralParameterValue;
0075: import org.opengis.parameter.ParameterValueGroup;
0076: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0077: import org.opengis.referencing.operation.TransformException;
0078:
0079: import com.sun.media.jai.operator.ImageWriteDescriptor;
0080:
0081: /**
0082: * Class used for encoding a {@link GridCoverage2D} into a GTOPO30 file.
0083: *
0084: *
0085: * @author Simone Giannecchini
0086: * @author jeichar
0087: * @author mkraemer
0088: * @source $URL:
0089: * http://svn.geotools.org/geotools/trunk/gt/plugin/gtopo30/src/org/geotools/gce/gtopo30/GTopo30Writer.java $
0090: */
0091: final public class GTopo30Writer extends AbstractGridCoverageWriter
0092: implements GridCoverageWriter {
0093:
0094: /** Logger. */
0095: private final static Logger LOGGER = org.geotools.util.logging.Logging
0096: .getLogger("org.geotools.gce.gtopo30");
0097:
0098: static {
0099: // register new JAI operation
0100: NoDataReplacerOpImage.register(JAI.getDefaultInstance());
0101: }
0102:
0103: /** Cached factory for a {@link SelectSampleDimension} operation. */
0104: private final static SelectSampleDimension sdFactory = new SelectSampleDimension();
0105:
0106: /** Cached factory for {@link Resample} operation. */
0107: private final static Resample resampleFactory = new Resample();
0108:
0109: /**
0110: * Standard width for the GIF image.
0111: */
0112: private final static int GIF_WIDTH = 640;
0113:
0114: /** Standard height for the GIF image. */
0115: private final static int GIF_HEIGHT = 480;
0116:
0117: /**
0118: * Creates a {@link GTopo30Writer}.
0119: *
0120: * @param dest
0121: * The destination object can be a {@link File} (a directory
0122: * actually), an {@link URL} to a directory, or a String
0123: * representing a directory or an {@link URL} to a directory.
0124: * @throws DataSourceException
0125: */
0126: public GTopo30Writer(final Object dest) throws DataSourceException {
0127: this (dest, null);
0128: }
0129:
0130: /**
0131: * Creates a {@link GTopo30Writer}.
0132: *
0133: * @param dest
0134: * The destination object can be a File (a directory actually),
0135: * an URL to a directory, or a String representing a directory or
0136: * an URL to a directory.
0137: * @throws DataSourceException
0138: */
0139: public GTopo30Writer(final Object dest, final Hints hints)
0140: throws DataSourceException {
0141:
0142: // /////////////////////////////////////////////////////////////////////
0143: //
0144: // Initial checks
0145: //
0146: // /////////////////////////////////////////////////////////////////////
0147: if (dest == null) {
0148: throw new NullPointerException(
0149: "The provided destination is null.");
0150: }
0151:
0152: destination = dest;
0153: final File temp;
0154: final URL url;
0155:
0156: // we only accept a directory as a path
0157: if (dest instanceof String) {
0158: temp = new File((String) dest);
0159:
0160: // if it exists and it is not a directory that 's not good
0161: if ((temp.exists() && !temp.isDirectory())
0162: || !temp.exists()) {
0163: destination = null; // we cannot write
0164: } else if (!temp.exists()) {
0165: // well let's create it!
0166: if (!temp.mkdir()) {
0167: destination = null;
0168: } else {
0169: destination = temp.getAbsolutePath();
0170: }
0171: }
0172: } else if (dest instanceof File) {
0173: temp = (File) dest;
0174:
0175: if (temp.exists() && !temp.isDirectory()) {
0176: this .destination = null;
0177: } else if (!temp.exists()) {
0178: // let's create it
0179: if (temp.mkdir()) {
0180: destination = temp.getAbsolutePath();
0181: } else {
0182: destination = null;
0183: }
0184: }
0185: } else if (dest instanceof URL) {
0186: url = (URL) dest;
0187:
0188: if (url.getProtocol().compareToIgnoreCase("file") != 0) {
0189: destination = null;
0190: }
0191:
0192: try {
0193: temp = new File(URLDecoder.decode(url.getFile(),
0194: "UTF-8)"));
0195: } catch (UnsupportedEncodingException e) {
0196: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
0197: final DataSourceException ex = new DataSourceException(
0198: e);
0199: throw ex;
0200: }
0201:
0202: if (temp.exists() && !temp.isDirectory()) {
0203: destination = null;
0204: } else if (!temp.exists()) {
0205: // let's create it
0206: if (temp.mkdir()) {
0207: destination = temp.getAbsolutePath();
0208: } else {
0209: destination = null;
0210: }
0211: }
0212: } else if (dest instanceof ImageOutputStream) {
0213: this .destination = dest;
0214: } else {
0215: throw new IllegalArgumentException(
0216: "The provided destination is of an incorrect type.");
0217:
0218: }
0219:
0220: // /////////////////////////////////////////////////////////////////////
0221: //
0222: // managing hints
0223: //
0224: // /////////////////////////////////////////////////////////////////////
0225: if (super .hints == null) {
0226: this .hints = new Hints(Hints.LENIENT_DATUM_SHIFT,
0227: Boolean.TRUE);
0228: }
0229: if (hints != null) {
0230:
0231: this .hints.add(hints);
0232: }
0233: }
0234:
0235: /**
0236: * @see org.opengis.coverage.grid.GridCoverageWriter#getFormat()
0237: */
0238: public Format getFormat() {
0239: return new GTopo30Format();
0240: }
0241:
0242: /**
0243: * @see org.opengis.coverage.grid.GridCoverageWriter#write(org.opengis.coverage.grid.GridCoverage,
0244: * org.opengis.parameter.GeneralParameterValue[])
0245: */
0246: public void write(final GridCoverage coverage,
0247: final GeneralParameterValue[] params)
0248: throws java.lang.IllegalArgumentException,
0249: java.io.IOException {
0250:
0251: // /////////////////////////////////////////////////////////////////////
0252: //
0253: // Checking input params
0254: //
0255: // /////////////////////////////////////////////////////////////////////
0256: if (coverage == null)
0257: throw new NullPointerException(
0258: "The provided source coverage is null");
0259: // the source GridCoverage2D
0260: GridCoverage2D gc2D = (GridCoverage2D) coverage;
0261:
0262: // /////////////////////////////////////////////////////////////////////
0263: //
0264: // Checking writing params
0265: //
0266: // /////////////////////////////////////////////////////////////////////
0267: GeoToolsWriteParams gtParams = null;
0268: if (params != null) {
0269:
0270: if (params != null) {
0271: Parameter param;
0272: final int length = params.length;
0273: for (int i = 0; i < length; i++) {
0274: param = (Parameter) params[i];
0275: if (param
0276: .getDescriptor()
0277: .getName()
0278: .getCode()
0279: .equals(
0280: AbstractGridFormat.GEOTOOLS_WRITE_PARAMS
0281: .getName().toString())) {
0282: gtParams = (GeoToolsWriteParams) param
0283: .getValue();
0284: }
0285: }
0286: }
0287: }
0288: if (gtParams == null)
0289: gtParams = new GTopo30WriteParams();
0290: // compression
0291: final boolean compressed = gtParams.getCompressionMode() == ImageWriteParam.MODE_EXPLICIT;
0292: // write band
0293: int[] writeBands = gtParams.getSourceBands();
0294: int writeBand = CoverageUtilities.getVisibleBand(gc2D
0295: .getRenderedImage());
0296: if ((writeBands == null || writeBands.length == 0 || writeBands.length > 1)
0297: && (writeBand < 0 || writeBand > gc2D
0298: .getNumSampleDimensions()))
0299: throw new IllegalArgumentException(
0300: "You need to supply a valid index for deciding which band to write.");
0301: if (!((writeBands == null || writeBands.length == 0 || writeBands.length > 1)))
0302: writeBand = writeBands[0];
0303:
0304: // destination file name
0305: String fileName = gc2D.getName().toString();
0306:
0307: // destination XXX HAS to be a dir
0308: if (compressed) {
0309: destination = new ZipOutputStream(new BufferedOutputStream(
0310: new FileOutputStream(new File((File) destination,
0311: new StringBuffer(fileName).append(".zip")
0312: .toString()))));
0313: }
0314:
0315: // /////////////////////////////////////////////////////////////////////
0316: //
0317: // STEP 1
0318: //
0319: // We might need to do a band select in order to cope with the GTOPO30
0320: // limitation.
0321: //
0322: // /////////////////////////////////////////////////////////////////////
0323: final ParameterValueGroup pvg = sdFactory.getParameters();
0324: pvg.parameter("Source").setValue(gc2D);
0325: pvg.parameter("SampleDimensions").setValue(
0326: new int[] { writeBand });
0327: pvg.parameter("VisibleSampleDimension").setValue(writeBand);
0328: gc2D = (GridCoverage2D) sdFactory.doOperation(pvg, hints);
0329:
0330: // /////////////////////////////////////////////////////////////////////
0331: //
0332: // STEP 2
0333: //
0334: // We might need to reformat the selected band for this coverage in
0335: // order to cope with the GTOPO30 limitation.
0336: //
0337: // /////////////////////////////////////////////////////////////////////
0338: final PlanarImage reFormattedData2Short = reFormatCoverageImage(
0339: gc2D, DataBuffer.TYPE_SHORT);
0340:
0341: // /////////////////////////////////////////////////////////////////////
0342: //
0343: // STEP 2
0344: //
0345: // Start with writing things out.
0346: //
0347: // /////////////////////////////////////////////////////////////////////
0348: // //
0349: //
0350: // write DEM
0351: //
0352: // //
0353: this .writeDEM(reFormattedData2Short, fileName, destination);
0354:
0355: // //
0356: //
0357: // write statistics
0358: //
0359: // //
0360: this .writeStats(reFormattedData2Short, fileName, destination,
0361: gc2D);
0362: // we won't use this image anymore let's release the resources.
0363:
0364: // //
0365: //
0366: // write world file
0367: //
0368: // //
0369: this .writeWorldFile(gc2D, fileName, destination);
0370:
0371: // //
0372: //
0373: // write projection
0374: //
0375: // //
0376: this .writePRJ(gc2D, fileName, destination);
0377:
0378: // //
0379: //
0380: // write HDR
0381: //
0382: // //
0383: this .writeHDR(gc2D, fileName, destination);
0384:
0385: // //
0386: //
0387: // write gif
0388: //
0389: // //
0390: this .writeGIF(gc2D, fileName, destination);
0391:
0392: // //
0393: //
0394: // write src
0395: //
0396: // //
0397: this .writeSRC(gc2D, fileName, destination);
0398:
0399: if (compressed) {
0400:
0401: ((ZipOutputStream) destination).close();
0402: }
0403: }
0404:
0405: /**
0406: * Reformats the input single banded planar image to having
0407: * {@link DataBuffer#TYPE_SHORT} sample type as requested by the GTOPO30
0408: * format.
0409: *
0410: * @param gc2D
0411: * source {@link GridCoverage2D} from which to take the input
0412: * {@link PlanarImage}.
0413: *
0414: * @return the reformated {@link PlanarImage}.
0415: */
0416: private PlanarImage reFormatCoverageImage(
0417: final GridCoverage2D gc2D, int writeBand) {
0418: // internal image
0419: PlanarImage image = (PlanarImage) gc2D.getRenderedImage();
0420: // sample dimension type
0421: final int origDataType = image.getSampleModel().getDataType();
0422: // short?
0423: if (DataBuffer.TYPE_SHORT == origDataType) {
0424: return image;
0425: }
0426:
0427: final GridSampleDimension visibleSD = ((GridSampleDimension) gc2D
0428: .getSampleDimension(0)).geophysics(true);
0429:
0430: // getting categories
0431: final List oldCategories = visibleSD.getCategories();
0432:
0433: // removing old nodata category
0434: // candidate
0435: Category candidate = null;
0436: NumberRange candidateRange = null;
0437: final Iterator it = oldCategories.iterator();
0438:
0439: while (it.hasNext()) {
0440: candidate = (Category) it.next();
0441:
0442: // removing candidate for NaN
0443: if (candidate.getName().toString().equalsIgnoreCase(
0444: "no data")) {
0445: candidateRange = candidate.getRange();
0446:
0447: break;
0448: }
0449: }
0450:
0451: // new no data category
0452: final double oldNoData = candidateRange.getMinimum();
0453: final ParameterBlockJAI pbjM = new ParameterBlockJAI(
0454: "org.geotools.gce.gtopo30.NoDataReplacer");
0455: pbjM.addSource(image);
0456: pbjM.setParameter("oldNoData", oldNoData);
0457: image = JAI.create("org.geotools.gce.gtopo30.NoDataReplacer",
0458: pbjM, hints);
0459: return image;
0460: }
0461:
0462: /**
0463: * Writing down the header file for the gtopo30 format:
0464: *
0465: * @param coverage
0466: * The GridCoverage to write
0467: * @param file
0468: * The destination object (can be a File or ZipOutputStream)
0469: *
0470: * @throws IOException
0471: * If the file could not be written
0472: */
0473: private void writeHDR(final GridCoverage2D gc, final String name,
0474: final Object dest) throws IOException {
0475:
0476: // final GeneralEnvelope envelope = (GeneralEnvelope) gc.getEnvelope();
0477: final double noData = -9999.0;
0478:
0479: try {
0480: // checking the directions of the axes.
0481: // we need to understand how the axes of this gridcoverage are
0482: // specified
0483: final CoordinateReferenceSystem crs = CRSUtilities
0484: .getCRS2D(gc.getCoordinateReferenceSystem());
0485: /*
0486: * Note from Martin: I suggest to replace all the above lines by the commented code below.
0487: * The change need to be tested in order to make sure that I didn't made a mistake in the
0488: * mathematic. Note that 'lonFirst' totally vanish.
0489: */
0490: final AffineTransform gridToWorld = (AffineTransform) gc
0491: .getGridGeometry().getGridToCoordinateSystem();
0492: boolean lonFirst = (XAffineTransform.getSwapXY(gridToWorld) != -1);
0493:
0494: final double geospatialDx = Math
0495: .abs((lonFirst) ? gridToWorld.getScaleX()
0496: : gridToWorld.getShearY());
0497: final double geospatialDy = Math
0498: .abs((lonFirst) ? gridToWorld.getScaleY()
0499: : gridToWorld.getShearX());
0500:
0501: // getting corner coordinates of the left upper corner
0502: final double xUpperLeft = lonFirst ? gridToWorld
0503: .getTranslateX() : gridToWorld.getTranslateY();
0504: final double yUpperLeft = lonFirst ? gridToWorld
0505: .getTranslateY() : gridToWorld.getTranslateX();
0506: /*
0507: final AffineTransform worldToGrid = (AffineTransform) gc
0508: .getGridGeometry().getGridToCoordinateSystem().inverse();
0509:
0510: final double geospatialDx = 1 / XAffineTransform.getScaleX0(worldToGrid);
0511: final double geospatialDy = 1 / XAffineTransform.getScaleY0(worldToGrid);
0512:
0513: // getting corner coordinates of the left upper corner
0514: final double xUpperLeft = -worldToGrid.getTranslateX() * geospatialDx;
0515: final double yUpperLeft = -worldToGrid.getTranslateY() * geospatialDy;
0516: */
0517:
0518: // calculating the physical resolution over x and y.
0519: final int geometryWidth = gc.getGridGeometry()
0520: .getGridRange().getLength(0);
0521: final int geometryHeight = gc.getGridGeometry()
0522: .getGridRange().getLength(1);
0523:
0524: if (dest instanceof File) {
0525:
0526: final PrintWriter out = new PrintWriter(
0527: new BufferedOutputStream(
0528: new FileOutputStream(new File(
0529: (File) dest, new StringBuffer(
0530: name).append(".HDR")
0531: .toString()))));
0532:
0533: // output header and assign header fields
0534: out.print("BYTEORDER");
0535: out.print(" ");
0536: out.println("M");
0537:
0538: out.print("LAYOUT");
0539: out.print(" ");
0540: out.println("BIL");
0541:
0542: out.print("NROWS");
0543: out.print(" ");
0544: out.println(geometryHeight);
0545:
0546: out.print("NCOLS");
0547: out.print(" ");
0548: out.println(geometryWidth);
0549:
0550: out.print("NBANDS");
0551: out.print(" ");
0552: out.println("1");
0553:
0554: out.print("NBITS");
0555: out.print(" ");
0556: out.println("16");
0557:
0558: out.print("BANDROWBYTES");
0559: out.print(" ");
0560: out.println(geometryWidth * 2);
0561:
0562: out.print("TOTALROWBYTES");
0563: out.print(" ");
0564: out.println(geometryWidth * 2);
0565:
0566: out.print("BANDGAPBYTES");
0567: out.print(" ");
0568: out.println(0);
0569:
0570: out.print("NODATA");
0571: out.print(" ");
0572: out.println((int) noData);
0573:
0574: out.print("ULXMAP");
0575: out.print(" ");
0576: out.println(xUpperLeft);
0577:
0578: out.print("ULYMAP");
0579: out.print(" ");
0580: out.println(yUpperLeft);
0581:
0582: out.print("XDIM");
0583: out.print(" ");
0584: out.println(geospatialDx);
0585:
0586: out.print("YDIM");
0587: out.print(" ");
0588: out.println(geospatialDy);
0589: out.flush();
0590: out.close();
0591: } else {
0592: final ZipOutputStream outZ = (ZipOutputStream) dest;
0593: final ZipEntry e = new ZipEntry(gc.getName().toString()
0594: + ".HDR");
0595: outZ.putNextEntry(e);
0596:
0597: // writing world file
0598: outZ.write("BYTEORDER".getBytes());
0599: outZ.write(" ".getBytes());
0600: outZ.write("M".getBytes());
0601: outZ.write("\n".getBytes());
0602:
0603: outZ.write("LAYoutZ".getBytes());
0604: outZ.write(" ".getBytes());
0605: outZ.write("BIL".getBytes());
0606: outZ.write("\n".getBytes());
0607:
0608: outZ.write("NROWS".getBytes());
0609: outZ.write(" ".getBytes());
0610: outZ.write(Integer.toString(geometryHeight).getBytes());
0611: outZ.write("\n".getBytes());
0612:
0613: outZ.write("NCOLS".getBytes());
0614: outZ.write(" ".getBytes());
0615: outZ.write(Integer.toString(geometryWidth).getBytes());
0616: outZ.write("\n".getBytes());
0617:
0618: outZ.write("NBANDS".getBytes());
0619: outZ.write(" ".getBytes());
0620: outZ.write("1".getBytes());
0621: outZ.write("\n".getBytes());
0622:
0623: outZ.write("NBITS".getBytes());
0624: outZ.write(" ".getBytes());
0625: outZ.write("16".getBytes());
0626: outZ.write("\n".getBytes());
0627:
0628: outZ.write("BANDROWBYTES".getBytes());
0629: outZ.write(" ".getBytes());
0630: outZ.write(Integer.toString(geometryWidth * 2)
0631: .getBytes());
0632: outZ.write("\n".getBytes());
0633:
0634: outZ.write("TOTALROWBYTES".getBytes());
0635: outZ.write(" ".getBytes());
0636: outZ.write(Integer.toString(geometryWidth * 2)
0637: .getBytes());
0638: outZ.write("\n".getBytes());
0639:
0640: outZ.write("BANDGAPBYTES".getBytes());
0641: outZ.write(" ".getBytes());
0642: outZ.write("0".getBytes());
0643: outZ.write("\n".getBytes());
0644:
0645: outZ.write("NODATA".getBytes());
0646: outZ.write(" ".getBytes());
0647: outZ.write(Integer.toString((int) noData).getBytes());
0648: outZ.write("\n".getBytes());
0649:
0650: outZ.write("ULXMAP".getBytes());
0651: outZ.write(" ".getBytes());
0652: outZ.write(Double.toString(
0653: xUpperLeft + (geospatialDx / 2)).getBytes());
0654: outZ.write("\n".getBytes());
0655:
0656: outZ.write("ULYMAP".getBytes());
0657: outZ.write(" ".getBytes());
0658: outZ.write(Double.toString(
0659: yUpperLeft - (geospatialDy / 2)).getBytes());
0660: outZ.write("\n".getBytes());
0661:
0662: outZ.write("XDIM".getBytes());
0663: outZ.write(" ".getBytes());
0664: outZ.write(Double.toString(geospatialDx).getBytes());
0665: outZ.write("\n".getBytes());
0666:
0667: outZ.write("YDIM".getBytes());
0668: outZ.write(" ".getBytes());
0669: outZ.write(Double.toString(geospatialDy).toString()
0670: .getBytes());
0671: outZ.write("\n".getBytes());
0672:
0673: outZ.closeEntry();
0674:
0675: ((ZipOutputStream) dest).closeEntry();
0676: }
0677: } catch (TransformException e) {
0678: final IOException ioe = new IOException(
0679: "Unable to write world file");
0680: ioe.initCause(e);
0681: throw ioe;
0682: }
0683: }
0684:
0685: /**
0686: * Writes the source file (.SRC). The default byte order is BIG_ENDIAN.
0687: *
0688: * @param gc
0689: * The GridCoverage to write
0690: * @param file
0691: * The destination object (can be a File or ZipOutputStream)
0692: * @param dest
0693: *
0694: * @throws FileNotFoundException
0695: * If the destination file could not be found
0696: * @throws IOException
0697: * If the file could not be written
0698: */
0699: private void writeSRC(GridCoverage2D gc, final String name,
0700: Object dest) throws FileNotFoundException, IOException {
0701:
0702: // /////////////////////////////////////////////////////////////////////
0703: // TODO @task @todo
0704: // Here I am making the assumption that the non geophysiscs view is 8
0705: // bit but it can also be 16. I should do something more general like a
0706: // clamp plus a format but for the moment this is enough.
0707: //
0708: // We need also to get the one visible band
0709: //
0710: // /////////////////////////////////////////////////////////////////////
0711: gc = gc.geophysics(false);
0712: ImageOutputStream out = null;
0713:
0714: if (dest instanceof File) {
0715: out = ImageIO.createImageOutputStream(new File((File) dest,
0716: new StringBuffer(name).append(".SRC").toString()));
0717: } else {
0718: final ZipOutputStream outZ = (ZipOutputStream) dest;
0719: final ZipEntry e = new ZipEntry(gc.getName().toString()
0720: + ".SRC");
0721: outZ.putNextEntry(e);
0722:
0723: out = ImageIO.createImageOutputStream(outZ);
0724: }
0725:
0726: // setting byte order
0727: out.setByteOrder(java.nio.ByteOrder.BIG_ENDIAN);
0728:
0729: // /////////////////////////////////////////////////////////////////////
0730: //
0731: // Prepare to write
0732: //
0733: // /////////////////////////////////////////////////////////////////////
0734: RenderedImage image = gc.getRenderedImage();
0735: image = untileImage(image);
0736:
0737: final ParameterBlockJAI pbj = new ParameterBlockJAI(
0738: "imagewrite");
0739: pbj.addSource(image);
0740: pbj.setParameter("Format", "raw");
0741: pbj.setParameter("Output", out);
0742: final RenderedOp wOp = JAI.create("ImageWrite", pbj);
0743:
0744: // /////////////////////////////////////////////////////////////////////
0745: //
0746: // Dispose things
0747: //
0748: // /////////////////////////////////////////////////////////////////////
0749: final Object o = wOp
0750: .getProperty(ImageWriteDescriptor.PROPERTY_NAME_IMAGE_WRITER);
0751: if (o instanceof ImageWriter)
0752: ((ImageWriter) o).dispose();
0753:
0754: if (!(dest instanceof File)) {
0755: ((ZipOutputStream) dest).closeEntry();
0756: }
0757: out.flush();
0758: out.close();
0759:
0760: }
0761:
0762: /**
0763: * Writing a gif file as an overview for this GTopo30.
0764: *
0765: * @param gc
0766: * The GridCoverage to write
0767: * @param file
0768: * The destination object (can be a File or ZipOutputStream)
0769: * @param dest
0770: *
0771: * @throws IOException
0772: * If the file could not be written
0773: */
0774: private void writeGIF(final GridCoverage2D gc, final String name,
0775: Object dest) throws IOException {
0776: ImageOutputStream out = null;
0777:
0778: if (dest instanceof File) {
0779: // writing gif image
0780: out = ImageIO.createImageOutputStream(new File((File) dest,
0781: new StringBuffer(name).append(".GIF").toString()));
0782: } else {
0783: final ZipOutputStream outZ = (ZipOutputStream) dest;
0784: final ZipEntry e = new ZipEntry(gc.getName().toString()
0785: + ".GIF");
0786: outZ.putNextEntry(e);
0787:
0788: out = ImageIO.createImageOutputStream(outZ);
0789: }
0790:
0791: // rescaling to a smaller resolution in order to save space on storage
0792: final GridCoverage2D gc1 = rescaleCoverage(gc);
0793:
0794: // get the non-geophysiscs view
0795: final GridCoverage2D gc2 = gc1.geophysics(false);
0796:
0797: // get the underlying image
0798: final RenderedImage image = gc2.getRenderedImage();
0799:
0800: // write it down as a gif
0801: final ParameterBlockJAI pbj = new ParameterBlockJAI(
0802: "ImageWrite");
0803: pbj.addSource(image);
0804: pbj.setParameter("Output", out);
0805: pbj.setParameter("Format", "gif");
0806: JAI.create("ImageWrite", pbj, new RenderingHints(
0807: JAI.KEY_TILE_CACHE, null));
0808:
0809: if (dest instanceof File) {
0810: out.close();
0811: } else {
0812: out.flush();
0813: ((ZipOutputStream) dest).closeEntry();
0814: }
0815:
0816: // disposing the old unused coverages
0817: gc1.dispose();
0818: }
0819:
0820: /**
0821: * Purpose of this method is rescaling the original coverage in order to
0822: * create an overview for the shaded relief gif image to be put inside the
0823: * gtopo30 set of files.
0824: *
0825: * @param gc
0826: * the original {@link GridCoverage2D}.
0827: *
0828: * @return the rescaled {@link GridCoverage2D}.
0829: */
0830: private GridCoverage2D rescaleCoverage(GridCoverage2D gc) {
0831: final RenderedImage image = gc.getRenderedImage();
0832: final int width = image.getWidth();
0833: final int height = image.getHeight();
0834:
0835: if ((height < GIF_HEIGHT) && (width < GIF_WIDTH)) {
0836: return gc;
0837: }
0838:
0839: // new grid range
0840: final GeneralGridRange newGridrange = new GeneralGridRange(
0841: new int[] { 0, 0 }, new int[] { GIF_WIDTH, GIF_HEIGHT });
0842:
0843: final GridGeometry2D newGridGeometry = new GridGeometry2D(
0844: newGridrange, gc.getEnvelope());
0845:
0846: // resample this coverage
0847: final ParameterValueGroup pvg = resampleFactory.getParameters();
0848: pvg.parameter("Source").setValue(gc);
0849: pvg.parameter("GridGeometry").setValue(newGridGeometry);
0850: pvg.parameter("InterpolationType").setValue(
0851: new InterpolationBilinear());
0852: return (GridCoverage2D) resampleFactory.doOperation(pvg, hints);
0853:
0854: }
0855:
0856: /**
0857: * Write a projection file (.PRJ) using wkt
0858: *
0859: * @param gc
0860: * The GridCoverage to write
0861: * @param file
0862: * The destination object (can be a File or ZipOutputStream)
0863: * @param dest
0864: *
0865: * @throws IOException
0866: * If the file could not be written
0867: */
0868: private void writePRJ(final GridCoverage2D gc, String name,
0869: Object dest) throws IOException {
0870: if (dest instanceof File) {
0871: // create the file
0872: final BufferedWriter fileWriter = new BufferedWriter(
0873: new FileWriter(new File((File) dest,
0874: new StringBuffer(name).append(".PRJ")
0875: .toString())));
0876:
0877: // write information on crs
0878: fileWriter.write(gc.getCoordinateReferenceSystem().toWKT());
0879: fileWriter.close();
0880: } else {
0881: final ZipOutputStream out = (ZipOutputStream) dest;
0882: final ZipEntry e = new ZipEntry(new StringBuffer(gc
0883: .getName().toString()).append(".PRJ").toString());
0884: out.putNextEntry(e);
0885: out.write(gc.getCoordinateReferenceSystem().toWKT()
0886: .getBytes());
0887: out.closeEntry();
0888: }
0889: }
0890:
0891: /**
0892: * Writes the stats file (.STX).
0893: *
0894: * @param image
0895: * The GridCoverage to write
0896: * @param file
0897: * The destination object (can be a File or ZipOutputStream)
0898: * @param gc
0899: * DOCUMENT ME!
0900: *
0901: * @throws IOException
0902: * If the file could not be written
0903: */
0904: private void writeStats(final PlanarImage image, String name,
0905: Object dest, final GridCoverage2D gc) throws IOException {
0906: ParameterBlock pb = new ParameterBlock();
0907: // /////////////////////////////////////////////////////////////////////
0908: //
0909: // we need to evaluate stats first using jai
0910: //
0911: // /////////////////////////////////////////////////////////////////////
0912: final int visibleBand = CoverageUtilities.getVisibleBand(image);
0913: final GridSampleDimension sd = (GridSampleDimension) gc
0914: .getSampleDimension(visibleBand);
0915: final double[] Max = new double[] { sd.getMaximumValue() };
0916: final double[] Min = new double[] { -9999.0 };
0917:
0918: // histogram
0919: pb.addSource(image);
0920: pb.add(null); // no roi
0921: pb.add(1);
0922: pb.add(1);
0923: pb.add(new int[] { (int) (Max[0] - Min[0] + 1) });
0924: pb.add(Min);
0925: pb.add(Max);
0926: pb.add(1);
0927:
0928: // /////////////////////////////////////////////////////////////////////
0929: //
0930: // Create the histogram
0931: //
0932: // /////////////////////////////////////////////////////////////////////
0933: final PlanarImage histogramImage = JAI.create("histogram", pb,
0934: new RenderingHints(JAI.KEY_TILE_CACHE, null));
0935: final Histogram hist = (Histogram) histogramImage
0936: .getProperty("histogram");
0937: pb.removeParameters();
0938: pb.removeSources();
0939:
0940: // /////////////////////////////////////////////////////////////////////
0941: //
0942: // Write things
0943: //
0944: // /////////////////////////////////////////////////////////////////////
0945: if (dest instanceof File) {
0946: // files destinations
0947: // write statistics
0948: if (dest instanceof File) {
0949: dest = new File((File) dest, new StringBuffer(name)
0950: .append(".STX").toString());
0951: }
0952: // writing world file
0953: final PrintWriter p = new PrintWriter(new FileOutputStream(
0954: ((File) dest)));
0955: p.print(1);
0956: p.print(" ");
0957: p.print((int) Min[0]);
0958: p.print(" ");
0959: p.print((int) Max[0]);
0960: p.print(" ");
0961: p.print(hist.getMean()[0]);
0962: p.print(" ");
0963: p.print(hist.getStandardDeviation()[0]);
0964: p.close();
0965: } else {
0966: final ZipOutputStream outZ = (ZipOutputStream) dest;
0967: final ZipEntry e = new ZipEntry(name + ".STX");
0968: outZ.putNextEntry(e);
0969:
0970: // writing world file
0971: outZ.write("1".getBytes());
0972: outZ.write(" ".getBytes());
0973: outZ.write(new Integer((int) Min[0]).toString().getBytes());
0974: outZ.write(" ".getBytes());
0975: outZ.write(new Integer((int) Max[0]).toString().getBytes());
0976: outZ.write(" ".getBytes());
0977: outZ.write(new Double(hist.getMean()[0]).toString()
0978: .getBytes());
0979: outZ.write(" ".getBytes());
0980: outZ.write(new Double(hist.getStandardDeviation()[0])
0981: .toString().getBytes());
0982: ((ZipOutputStream) dest).closeEntry();
0983: }
0984:
0985: histogramImage.dispose();
0986: }
0987:
0988: /**
0989: * Writes the world file (.DMW)
0990: *
0991: * @param gc
0992: * The GridCoverage to write
0993: * @param worldFile
0994: * The destination world file (can be a file or a
0995: * ZipOutputStream)
0996: *
0997: * @throws IOException
0998: * if the file could not be written
0999: */
1000: private void writeWorldFile(final GridCoverage2D gc, String name,
1001: Object dest) throws IOException {
1002: // final RenderedImage image = (PlanarImage) gc.getRenderedImage();
1003:
1004: /**
1005: * It is worth to point out that here I build the values using the axes
1006: * order specified in the CRs which can be either LAT,LON or LON,LAT.
1007: * This is important since we ned to know how to assocaite the
1008: * underlying raster dimensions with the envelope which is in CRS
1009: * values.
1010: */
1011:
1012: try {
1013: // /////////////////////////////////////////////////////////////////////
1014: //
1015: // trying to understand the direction of the first axis in order to
1016: // understand how to associate the value to the crs.
1017: //
1018: // /////////////////////////////////////////////////////////////////////
1019: final CoordinateReferenceSystem crs = CRSUtilities
1020: .getCRS2D(gc.getCoordinateReferenceSystem());
1021: final AffineTransform gridToWorld = (AffineTransform) gc
1022: .getGridGeometry().getGridToCoordinateSystem();
1023: boolean lonFirst = (XAffineTransform.getSwapXY(gridToWorld) != -1);
1024:
1025: // /////////////////////////////////////////////////////////////////////
1026: //
1027: // associate value to crs
1028: //
1029: // /////////////////////////////////////////////////////////////////////
1030: final double xPixelSize = (lonFirst) ? gridToWorld
1031: .getScaleX() : gridToWorld.getShearY();
1032: final double rotation1 = (lonFirst) ? gridToWorld
1033: .getShearX() : gridToWorld.getScaleY();
1034: final double rotation2 = (lonFirst) ? gridToWorld
1035: .getShearY() : gridToWorld.getScaleX();
1036: final double yPixelSize = (lonFirst) ? gridToWorld
1037: .getScaleY() : gridToWorld.getShearX();
1038: final double xLoc = lonFirst ? gridToWorld.getTranslateX()
1039: : gridToWorld.getTranslateY();
1040: final double yLoc = lonFirst ? gridToWorld.getTranslateY()
1041: : gridToWorld.getTranslateX();
1042: if (dest instanceof File) {
1043:
1044: dest = new File((File) dest, new StringBuffer(name)
1045: .append(".DMW").toString());
1046: // writing world file
1047: final PrintWriter out = new PrintWriter(
1048: new FileOutputStream((File) dest));
1049: out.println(xPixelSize);
1050: out.println(rotation1);
1051: out.println(rotation2);
1052: out.println(yPixelSize);
1053: out.println(xLoc);
1054: out.println(yLoc);
1055: out.close();
1056: } else {
1057: final ZipOutputStream outZ = (ZipOutputStream) dest;
1058: final ZipEntry e = new ZipEntry(gc.getName().toString()
1059: + ".DMW");
1060: outZ.putNextEntry(e);
1061:
1062: // writing world file
1063: outZ.write(Double.toString(xPixelSize).getBytes());
1064: outZ.write("\n".getBytes());
1065: outZ.write(Double.toString(rotation1).toString()
1066: .getBytes());
1067: outZ.write("\n".getBytes());
1068: outZ.write(Double.toString(rotation2).toString()
1069: .getBytes());
1070: outZ.write("\n".getBytes());
1071: outZ.write(Double.toString(xPixelSize).toString()
1072: .getBytes());
1073: outZ.write("\n".getBytes());
1074: outZ.write(Double.toString(yPixelSize).toString()
1075: .getBytes());
1076: outZ.write("\n".getBytes());
1077: outZ.write(Double.toString(xLoc).toString().getBytes());
1078: outZ.write("\n".getBytes());
1079: outZ.write(Double.toString(yLoc).toString().getBytes());
1080: outZ.write("\n".getBytes());
1081: ((ZipOutputStream) dest).closeEntry();
1082: }
1083: } catch (TransformException e) {
1084: final IOException ioe = new IOException(
1085: "Unable to write world file");
1086: ioe.initCause(e);
1087: throw ioe;
1088: }
1089: }
1090:
1091: /**
1092: * Writes the digital elevation model file (.DEM). The default byte order is
1093: * BIG_ENDIAN.
1094: *
1095: * @param image
1096: * The GridCoverage object to write
1097: * @param name
1098: * DOCUMENT ME!
1099: * @param dest
1100: * The destination object (can be a File or a ZipOutputStream)
1101: *
1102: * @throws FileNotFoundException
1103: * If the destination file could not be found
1104: * @throws IOException
1105: * If the file could not be written
1106: */
1107: private void writeDEM(PlanarImage image, final String name,
1108: Object dest) throws FileNotFoundException, IOException {
1109: ImageOutputStream out;
1110:
1111: if (dest instanceof File) {
1112: // write statistics
1113: if (dest instanceof File) {
1114: dest = new File((File) dest, new StringBuffer(name)
1115: .append(".DEM").toString());
1116: }
1117: out = ImageIO.createImageOutputStream((File) dest);
1118: } else {
1119: final ZipOutputStream outZ = (ZipOutputStream) dest;
1120: final ZipEntry e = new ZipEntry(name + ".DEM");
1121: outZ.putNextEntry(e);
1122: out = ImageIO.createImageOutputStream(outZ);
1123: }
1124:
1125: out.setByteOrder(java.nio.ByteOrder.BIG_ENDIAN);
1126:
1127: // untile the image in case it is tiled
1128: // otherwise we could add tiles which are unexistant in the original
1129: // data
1130: // generating failures when reading back the data again.
1131: image = untileImage(image);
1132:
1133: // requesting an imageio writer
1134: // setting tile parameters in order to tile the image on the disk
1135: final ParameterBlockJAI pbjW = new ParameterBlockJAI(
1136: "ImageWrite");
1137: pbjW.addSource(image);
1138: pbjW.setParameter("Format", "raw");
1139: pbjW.setParameter("Output", out);
1140: JAI.create("ImageWrite", pbjW);
1141:
1142: if (dest instanceof File) {
1143: out.flush();
1144: out.close();
1145: } else {
1146: ((ZipOutputStream) dest).closeEntry();
1147: }
1148: }
1149:
1150: /**
1151: * This method has the objective of untiling the final image to write on
1152: * disk since we do not want to have tiles added to the file on disk causing
1153: * failures when reading it back into memory.
1154: *
1155: * @param image
1156: * Image to untile.
1157: *
1158: * @return Untiled image.
1159: */
1160: private PlanarImage untileImage(RenderedImage image) {
1161: final ParameterBlockJAI pbj = new ParameterBlockJAI("format");
1162: pbj.addSource(image);
1163: pbj.setParameter("dataType", image.getSampleModel()
1164: .getTransferType());
1165:
1166: final ImageLayout layout = new ImageLayout(image);
1167: layout.unsetTileLayout();
1168: layout.setTileGridXOffset(0);
1169: layout.setTileGridYOffset(0);
1170: layout.setTileHeight(image.getHeight());
1171: layout.setTileWidth(image.getWidth());
1172: layout.setValid(ImageLayout.TILE_GRID_X_OFFSET_MASK
1173: | ImageLayout.TILE_GRID_Y_OFFSET_MASK
1174: | ImageLayout.TILE_HEIGHT_MASK
1175: | ImageLayout.TILE_WIDTH_MASK);
1176:
1177: final RenderingHints hints = new RenderingHints(
1178: JAI.KEY_IMAGE_LAYOUT, layout);
1179: // avoid caching this image
1180:
1181: return JAI.create("format", pbj, hints);
1182: }
1183:
1184: }
|