/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, availible at the root * application directory. */ package org.geoserver.wms.worldwind; import java.awt.Rectangle; import java.awt.image.DataBuffer; import java.awt.image.RenderedImage; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.imageio.ImageWriter; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.stream.ImageOutputStream; import javax.media.jai.Interpolation; import javax.media.jai.RenderedOp; import javax.media.jai.TiledImage; import javax.media.jai.operator.FormatDescriptor; import org.geoserver.data.util.CoverageUtils; import org.geoserver.platform.ServiceException; import org.geoserver.wms.GetMapRequest; import org.geoserver.wms.MapLayerInfo; import org.geoserver.wms.MapProducerCapabilities; import org.geoserver.wms.WMS; import org.geoserver.wms.WMSMapContent; import org.geoserver.wms.map.RenderedImageMapResponse; import org.geoserver.wms.worldwind.util.BilWCSUtils; import org.geotools.coverage.CoverageFactoryFinder; import org.geotools.coverage.grid.GeneralGridEnvelope; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.coverage.grid.GridCoverageFactory; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.geometry.GeneralEnvelope; import org.geotools.referencing.CRS; import org.geotools.util.logging.Logging; import org.opengis.coverage.grid.GridCoverage; import org.opengis.coverage.grid.GridCoverageReader; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.vfny.geoserver.wcs.WcsException; import com.sun.media.imageioimpl.plugins.raw.RawImageWriterSpi; /** * Map producer for producing Raw bil images out of an elevation model. * Modelled after the GeoTIFFMapResponse, relying on Geotools and the * RawImageWriterSpi * @author Tishampati Dhar * @since 2.0.x * */ public final class BilMapResponse extends RenderedImageMapResponse { /** A logger for this class. */ private static final Logger LOGGER = Logging.getLogger(BilMapResponse.class); /** the only MIME type this map producer supports */ static final String MIME_TYPE = "image/bil"; private static final String[] OUTPUT_FORMATS = {MIME_TYPE,"application/bil", "application/bil8","application/bil16", "application/bil32" }; private WMS wmsConfig; /** GridCoverageFactory. - Where do we use this again ?*/ private final static GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null); /** Raw Image Writer **/ private final static ImageWriterSpi writerSPI = new RawImageWriterSpi(); /** * Constructor for a {@link BilMapResponse}. * * @param wms * that is asking us to encode the image. */ public BilMapResponse(final WMS wms) { super(OUTPUT_FORMATS,wms); this.wmsConfig = wms; } @Override public void formatImageOutputStream(RenderedImage image, OutputStream outStream, WMSMapContent mapContent) throws ServiceException, IOException { //TODO: Write reprojected terrain tile // TODO Get request tile size GetMapRequest request = mapContent.getRequest(); String bilEncoding = (String) request.getFormat(); int height = request.getHeight(); int width = request.getWidth(); if ((height>512)||(width>512)){ throw new ServiceException("Cannot get WMS bil" + " tiles bigger than 512x512, try WCS"); } List<MapLayerInfo> reqlayers = request.getLayers(); //Can't fetch bil for more than 1 layer if (reqlayers.size() > 1) { throw new ServiceException("Cannot combine layers into BIL output"); } MapLayerInfo mapLayerInfo = reqlayers.get(0); /* final ParameterValueGroup writerParams = format.getWriteParameters(); writerParams.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()) .setValue(wp); */ GridCoverageReader coverageReader = mapLayerInfo.getCoverageReader(); /* * Try to use a gridcoverage style render */ GridCoverage2D subCov = null; try { subCov = getFinalCoverage(request, mapLayerInfo, (AbstractGridCoverage2DReader)coverageReader); } catch (IndexOutOfBoundsException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (FactoryException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (TransformException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(subCov!=null) { /* BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out)); writer.write(subCov.toString()); writer.flush(); writer.close(); */ image = subCov.getRenderedImage(); if(image!=null) { int dtype = image.getData().getDataBuffer().getDataType(); /* Throw exception if required to perform conversion */ /* if((bilEncoding.equals("application/bil32"))&&(dtype!=DataBuffer.TYPE_FLOAT)) { throw new ServiceException("Cannot fetch BIL float data,"+ "Wrong underlying data type"); } if((bilEncoding.equals("application/bil16"))&&(dtype!=DataBuffer.TYPE_SHORT)) { throw new ServiceException("Cannot fetch BIL int data,"+ "Wrong underlying data type"); } if((bilEncoding.equals("application/bil8"))&&(dtype!=DataBuffer.TYPE_BYTE)) { throw new ServiceException("Cannot fetch BIL byte data,"+ "Wrong underlying data type"); } */ /* * Perform format conversion * Operator is not created if no conversion is necessary */ RenderedOp formcov = null; if((bilEncoding.equals("application/bil32"))&&(dtype!=DataBuffer.TYPE_FLOAT)) { formcov = FormatDescriptor.create(image,DataBuffer.TYPE_FLOAT ,null); } if((bilEncoding.equals("application/bil16"))&&(dtype!=DataBuffer.TYPE_SHORT)) { formcov = FormatDescriptor.create(image,DataBuffer.TYPE_SHORT ,null); } if((bilEncoding.equals("application/bil8"))&&(dtype!=DataBuffer.TYPE_BYTE)) { formcov = FormatDescriptor.create(image,DataBuffer.TYPE_BYTE ,null); } TiledImage tiled = null; if (formcov!= null) tiled = new TiledImage(formcov,width,height); else tiled = new TiledImage(image,width,height); final ImageOutputStream imageOutStream = ImageIO.createImageOutputStream(outStream); final ImageWriter writer = writerSPI.createWriterInstance(); writer.setOutput(imageOutStream); writer.write(tiled); imageOutStream.flush(); imageOutStream.close(); } else { throw new ServiceException("Cannot render to BIL"); } } else { throw new ServiceException("You requested a bil of size:"+ height+"x"+width+",but you can't have it!!"); } } /** * getFinalCoverage - message the RenderedImage into Bil * * @param request CoverageRequest * @param meta CoverageInfo * @param parameters * @param coverage GridCoverage * @return GridCoverage2D * @throws WcsException * @throws IOException * @throws IndexOutOfBoundsException * @throws FactoryException * @throws TransformException */ private static GridCoverage2D getFinalCoverage(GetMapRequest request, MapLayerInfo meta, AbstractGridCoverage2DReader coverageReader /*GridCoverage coverage*/) throws WcsException, IOException, IndexOutOfBoundsException, FactoryException, TransformException { // This is the final Response CRS final String responseCRS = request.getSRS(); // - first check if the responseCRS is present on the Coverage // ResponseCRSs list /* if (!meta.getSRS().contains(responseCRS)) { throw new WmsException("This Layer does not support the requested Response-CRS."); } */ // - then create the Coordinate Reference System final CoordinateReferenceSystem targetCRS = CRS.decode(responseCRS); // This is the CRS of the requested Envelope final String requestCRS = request.getSRS(); // - first check if the requestCRS is present on the Coverage // RequestCRSs list /* if (!meta.getSRS().contains(requestCRS)) { throw new WmsException("This Layer does not support the requested CRS."); } */ // - then create the Coordinate Reference System final CoordinateReferenceSystem sourceCRS = CRS.decode(requestCRS); // This is the CRS of the Coverage Envelope final CoordinateReferenceSystem cvCRS = ((GeneralEnvelope) coverageReader .getOriginalEnvelope()).getCoordinateReferenceSystem(); final MathTransform GCCRSTodeviceCRSTransformdeviceCRSToGCCRSTransform = CRS .findMathTransform(cvCRS, sourceCRS, true); final MathTransform GCCRSTodeviceCRSTransform = CRS.findMathTransform(cvCRS, targetCRS, true); final MathTransform deviceCRSToGCCRSTransform = GCCRSTodeviceCRSTransformdeviceCRSToGCCRSTransform .inverse(); com.vividsolutions.jts.geom.Envelope envelope = request.getBbox(); GeneralEnvelope destinationEnvelope; final boolean lonFirst = sourceCRS.getCoordinateSystem().getAxis(0).getDirection().absolute() .equals(AxisDirection.EAST); // the envelope we are provided with is lon,lat always if (!lonFirst) { destinationEnvelope = new GeneralEnvelope(new double[] { envelope.getMinY(), envelope.getMinX() }, new double[] { envelope.getMaxY(), envelope.getMaxX() }); } else { destinationEnvelope = new GeneralEnvelope(new double[] { envelope.getMinX(), envelope.getMinY() }, new double[] { envelope.getMaxX(), envelope.getMaxY() }); } destinationEnvelope.setCoordinateReferenceSystem(sourceCRS); // this is the destination envelope in the coverage crs final GeneralEnvelope destinationEnvelopeInSourceCRS = (!deviceCRSToGCCRSTransform .isIdentity()) ? CRS.transform(deviceCRSToGCCRSTransform, destinationEnvelope) : new GeneralEnvelope(destinationEnvelope); destinationEnvelopeInSourceCRS.setCoordinateReferenceSystem(cvCRS); /** * Reading Coverage on Requested Envelope */ Rectangle destinationSize = null; /* if ((request.getGridLow() != null) && (request.getGridHigh() != null)) { final int[] lowers = new int[] { request.getGridLow()[0].intValue(), request.getGridLow()[1].intValue() }; final int[] highers = new int[] { request.getGridHigh()[0].intValue(), request.getGridHigh()[1].intValue() }; destinationSize = new Rectangle(lowers[0], lowers[1], highers[0], highers[1]); } else { //destinationSize = coverageReader.getOriginalGridRange().toRectangle(); throw new WmsException("Neither Grid Size nor Grid Resolution have been specified."); } */ destinationSize = new Rectangle(0,0,request.getHeight(),request.getWidth()); /** * Checking for supported Interpolation Methods */ /* Interpolation interpolation = Interpolation.getInstance(Interpolation.INTERP_NEAREST); final String interpolationType = request.getInterpolation(); if (interpolationType != null) { boolean interpolationSupported = false; Iterator internal = meta.getInterpolationMethods().iterator(); while (internal.hasNext()) { if (interpolationType.equalsIgnoreCase((String) internal.next())) { interpolationSupported = true; } } if (!interpolationSupported) { throw new WcsException( "The requested Interpolation method is not supported by this Coverage."); } else { if (interpolationType.equalsIgnoreCase("bilinear")) { interpolation = Interpolation.getInstance(Interpolation.INTERP_BILINEAR); } else if (interpolationType.equalsIgnoreCase("bicubic")) { interpolation = Interpolation.getInstance(Interpolation.INTERP_BICUBIC); } } } */ Interpolation interpolation = Interpolation.getInstance(Interpolation.INTERP_BILINEAR); Map<Object,Object> parameters = new HashMap<Object,Object>(); // ///////////////////////////////////////////////////////// // // Reading the coverage // // ///////////////////////////////////////////////////////// parameters.put(AbstractGridFormat.READ_GRIDGEOMETRY2D.getName().toString(), new GridGeometry2D(new GeneralGridEnvelope(destinationSize), destinationEnvelopeInSourceCRS)); final GridCoverage coverage = coverageReader.read(CoverageUtils.getParameters( coverageReader.getFormat().getReadParameters(), parameters, true)); if ((coverage == null) || !(coverage instanceof GridCoverage2D)) { throw new IOException("The requested coverage could not be found."); } /** * Band Select */ /* Coverage bandSelectedCoverage = null; bandSelectedCoverage = WCSUtils.bandSelect(request.getParameters(), coverage); */ /** * Crop */ final GridCoverage2D croppedGridCoverage = BilWCSUtils.crop(coverage, (GeneralEnvelope) coverage.getEnvelope(), cvCRS, destinationEnvelopeInSourceCRS, Boolean.TRUE); /** * Scale/Resampling (if necessary) */ //GridCoverage2D subCoverage = null; GridCoverage2D subCoverage = croppedGridCoverage; final GeneralGridEnvelope newGridrange = new GeneralGridEnvelope(destinationSize); /*if (!newGridrange.equals(croppedGridCoverage.getGridGeometry() .getGridRange())) {*/ subCoverage = BilWCSUtils.scale(croppedGridCoverage, newGridrange, croppedGridCoverage, cvCRS, destinationEnvelopeInSourceCRS); //} /** * Reproject */ subCoverage = BilWCSUtils.reproject(subCoverage, sourceCRS, targetCRS, interpolation); return subCoverage; } /** * This is not really an image map */ @Override public MapProducerCapabilities getCapabilities(String outputFormat) { // FIXME become more capable return new MapProducerCapabilities(false, false, false, false, null); } }