/* 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.vfny.geoserver.wcs.responses; import java.awt.geom.AffineTransform; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.TreeSet; import java.util.logging.Logger; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CoverageDimensionInfo; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.MetadataLinkInfo; import org.geoserver.config.GeoServer; import org.geoserver.config.ServiceInfo; import org.geoserver.wcs.WCSInfo; import org.geotools.factory.Hints; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.ReferencingFactoryFinder; import org.opengis.coverage.grid.GridGeometry; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CRSFactory; import org.opengis.referencing.datum.DatumFactory; import org.opengis.referencing.operation.CoordinateOperationFactory; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.vfny.geoserver.Request; import org.vfny.geoserver.Response; import org.vfny.geoserver.wcs.WcsException; import org.vfny.geoserver.wcs.requests.DescribeRequest; import org.vfny.geoserver.wcs.requests.WCSRequest; /** * DOCUMENT ME! * * @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $ (last * modification) * @author $Author: Simone Giannecchini (simboss1@gmail.com) $ (last * modification) */ public class DescribeResponse implements Response { private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.responses"); private static final String CURR_VER = "\"1.0.0\""; private static final String WCS_URL = "http://www.opengis.net/wcs"; private static final String WCS_NAMESPACE = new StringBuffer("\n xmlns=\"").append(WCS_URL) .append("\"") .toString(); private static final String XLINK_URL = "\"http://www.w3.org/1999/xlink\""; private static final String XLINK_NAMESPACE = new StringBuffer("\n xmlns:xlink=").append(XLINK_URL) .toString(); private static final String OGC_URL = "\"http://www.opengis.net/ogc\""; private static final String OGC_NAMESPACE = new StringBuffer("\n xmlns:ogc=").append(OGC_URL) .toString(); private static final String GML_URL = "\"http://www.opengis.net/gml\""; private static final String GML_NAMESPACE = new StringBuffer("\n xmlns:gml=").append(GML_URL) .toString(); private static final String SCHEMA_URI = "\"http://www.w3.org/2001/XMLSchema-instance\""; private static final String XSI_NAMESPACE = new StringBuffer("\n xmlns:xsi=").append(SCHEMA_URI) .toString(); /** Fixed return footer information */ private static final String FOOTER = "\n</CoverageDescription>"; /** * * @uml.property name="request" * @uml.associationEnd multiplicity="(0 1)" */ private DescribeRequest request; /** Main XML class for interpretation and response. */ private String xmlResponse = new String(); /** * The default datum factory. * * @uml.property name="datumFactory" * @uml.associationEnd multiplicity="(1 1)" */ protected final DatumFactory datumFactory = ReferencingFactoryFinder.getDatumFactory(null); /** * The service configuration bean this response works upon */ private WCSInfo wcs; public DescribeResponse(final WCSInfo wcs) { this.wcs = wcs; } /** * Returns any extra headers that this service might want to set in the HTTP response object. * @see org.vfny.geoserver.Response#getResponseHeaders() */ public HashMap getResponseHeaders() { return null; } /** * The default coordinate reference system factory. */ // protected final static CRSFactory crsFactory = // FactoryFinder.getCRSFactory(new // Hints(Hints.CRS_AUTHORITY_FACTORY,EPSGCRSAuthorityFactory.class)); protected final static CRSFactory crsFactory = ReferencingFactoryFinder.getCRSFactory(new Hints( Hints.CRS_AUTHORITY_FACTORY, CRSAuthorityFactory.class)); /** * The default math transform factory. * * @uml.property name="mtFactory" * @uml.associationEnd multiplicity="(1 1)" */ protected final MathTransformFactory mtFactory = ReferencingFactoryFinder .getMathTransformFactory(null); /** * The default transformations factory. */ protected final static CoordinateOperationFactory opFactory = ReferencingFactoryFinder .getCoordinateOperationFactory(new Hints(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE)); public void execute(Request req) throws WcsException { WCSRequest request = (WCSRequest) req; if (!(request instanceof DescribeRequest)) { throw new WcsException(new StringBuffer( "illegal request type, expected DescribeRequest, got ").append(request) .toString()); } DescribeRequest wcsRequest = (DescribeRequest) request; this.request = wcsRequest; LOGGER.finer("processing describe request" + wcsRequest); String outputFormat = wcsRequest.getOutputFormat(); if (!outputFormat.equalsIgnoreCase("XMLSCHEMA")) { throw new WcsException(new StringBuffer("output format: ").append(outputFormat) .append(" not ") .append("supported by geoserver") .toString()); } // generates response, using general function xmlResponse = generateCoverages(wcsRequest); if (!request.getWCS().getGeoServer().getGlobal().isVerbose()) { xmlResponse = xmlResponse.replaceAll(">\n[ \\t\\n]*", ">"); xmlResponse = xmlResponse.replaceAll("\n[ \\t\\n]*", " "); } } public String getContentType(GeoServer gs) { return "text/xml; charset=" + gs.getGlobal().getCharset(); } public String getContentEncoding() { return null; } public String getContentDisposition() { return null; } public void writeTo(OutputStream out) throws WcsException { try { final Charset encoding = Charset.forName(wcs.getGeoServer().getGlobal().getCharset()); Writer writer = new OutputStreamWriter(out, encoding); writer.write(xmlResponse); writer.flush(); } catch (IOException ex) { throw new WcsException(ex, "", getClass().getName()); } } private final String generateCoverages(DescribeRequest wcsRequest) throws WcsException { List requestedTypes = wcsRequest.getCoverages(); // Initialize return information and intermediate return objects StringBuffer tempResponse = new StringBuffer(); // ComplexType table = new ComplexType(); if (requestedTypes.size() == 0) { // if there are no specific requested types then get all. Catalog catalog = wcsRequest.getWCS().getGeoServer().getCatalog(); requestedTypes = new ArrayList(); for (CoverageInfo ci : catalog.getCoverages()) { requestedTypes.add(ci.getName()); } } tempResponse.append("<?xml version=\"1.0\" encoding=\"") .append(wcs.getGeoServer().getGlobal().getCharset()).append("\"?>") .append("\n<CoverageDescription version=").append(CURR_VER).append(" ") .toString(); tempResponse.append(WCS_NAMESPACE); tempResponse.append(XLINK_NAMESPACE); tempResponse.append(OGC_NAMESPACE); tempResponse.append(GML_NAMESPACE); tempResponse.append(XSI_NAMESPACE); /*tempResponse.append(" xsi:schemaLocation=\"").append(WCS_URL).append( " ").append(request.getSchemaBaseUrl()).append( "wcs/1.0.0/describeCoverage.xsd\">\n\n");*/ tempResponse.append(" xsi:schemaLocation=\"").append(WCS_URL).append(" ") .append("http://schemas.opengis.net/wcs/1.0.0/") .append("describeCoverage.xsd\">\n\n"); tempResponse.append(generateSpecifiedCoverages(requestedTypes, wcsRequest.getWCS())); tempResponse.append(FOOTER); return tempResponse.toString(); } private String generateSpecifiedCoverages(List requestedTypes, WCSInfo gs) throws WcsException { String tempResponse = new String(); String curCoverageName = new String(); final int length = requestedTypes.size(); CoverageInfo meta; Catalog catalog = gs.getGeoServer().getCatalog(); for (int i = 0; i < length; i++) { curCoverageName = requestedTypes.get(i).toString(); meta = catalog.getCoverageByName(curCoverageName); if (meta == null) { throw new WcsException(new StringBuffer("Coverage ").append(curCoverageName) .append(" does ") .append("not exist on this server") .toString()); } try { tempResponse = tempResponse + printElement(meta); } catch(Exception e) { throw new WcsException(e); } } tempResponse = tempResponse + "\n\n"; return tempResponse; } private static String printElement(CoverageInfo cv) throws Exception { StringBuffer tempResponse = new StringBuffer(); tempResponse.append("\n <CoverageOffering>"); if(cv.getMetadataLinks() != null) { for (MetadataLinkInfo link : cv.getMetadataLinks()) { tempResponse.append("\n <metadataLink about=\"").append(link.getAbout()) .append("\" metadataType=\"").append(link.getMetadataType()) .append("\"/>"); } } String tmp = cv.getDescription(); if ((tmp != null) && (tmp != "")) { tempResponse.append("\n <description>").append(tmp).append("</description>"); } tmp = cv.getName(); if ((tmp != null) && (tmp != "")) { tempResponse.append("\n <name>").append(tmp).append("</name>"); } tmp = cv.getTitle(); if ((tmp != null) && (tmp != "")) { tempResponse.append("\n <label>").append(tmp).append("</label>"); } final ReferencedEnvelope envelope = cv.getLatLonBoundingBox(); tempResponse.append("\n <lonLatEnvelope" + " srsName=\"WGS84(DD)\"") /*urn:ogc:def:crs:OGC:1.3:CRS84*/ .append(">"); tempResponse.append("\n <gml:pos>").append(envelope.getLowerCorner().getOrdinate(0)) .append(" ").append(envelope.getLowerCorner().getOrdinate(1)) .append("</gml:pos>"); tempResponse.append("\n <gml:pos>").append(envelope.getUpperCorner().getOrdinate(0)) .append(" ").append(envelope.getUpperCorner().getOrdinate(1)) .append("</gml:pos>"); /*tempResponse.append("\n <gml:timePosition></gml:timePosition>"); tempResponse.append("\n <gml:timePosition></gml:timePosition>");*/ tempResponse.append("\n </lonLatEnvelope>"); if ((cv.getKeywords() != null) && (cv.getKeywords().size() > 0)) { tempResponse.append("\n <keywords>"); for (int i = 0; i < cv.getKeywords().size(); i++) tempResponse.append("\n <keyword>" + cv.getKeywords().get(i) + "</keyword>"); tempResponse.append("\n </keywords>"); } // TODO we need to signal somehow that something went wrong ReferencedEnvelope cvEnvelope = cv.boundingBox(); tempResponse.append("\n <domainSet>"); tempResponse.append("\n <spatialDomain>"); // Envelope String userDefinedCrsIdentifier = cv.getSRS(); tempResponse.append("\n <gml:Envelope") .append((((userDefinedCrsIdentifier != null) && (userDefinedCrsIdentifier != "")) ? new StringBuffer(" srsName=\"").append(userDefinedCrsIdentifier).append("\"").toString() : "")) .append(">"); tempResponse.append("\n <gml:pos>") .append((cvEnvelope != null) ? new StringBuffer(Double.toString(cvEnvelope.getLowerCorner().getOrdinate(0))).append( " ").append(cvEnvelope.getLowerCorner().getOrdinate(1)).toString() : "") .append("</gml:pos>"); tempResponse.append("\n <gml:pos>") .append((cvEnvelope != null) ? new StringBuffer(Double.toString(cvEnvelope.getUpperCorner().getOrdinate(0))).append( " ").append(cvEnvelope.getUpperCorner().getOrdinate(1)).toString() : "") .append("</gml:pos>"); tempResponse.append("\n </gml:Envelope>"); // Grid GridGeometry grid = cv.getGrid(); MathTransform gridToCRS = grid.getGridToCRS(); final int gridDimension = gridToCRS != null ? gridToCRS.getSourceDimensions() : 0; // RectifiedGrid tempResponse.append("\n <gml:RectifiedGrid").append( (gridToCRS != null) ? new StringBuffer(" dimension=\"").append( gridDimension).append("\"").toString() : "") .append(">"); String lowers = ""; String upers = ""; for (int r = 0; r < gridDimension; r++) { lowers += (grid.getGridRange().getLow(r) + " "); upers += (grid.getGridRange().getHigh(r) + " "); } tempResponse.append("\n <gml:limits>"); tempResponse.append("\n <gml:GridEnvelope>"); tempResponse.append("\n <gml:low>" + ((cvEnvelope != null) ? lowers : "") + "</gml:low>"); tempResponse.append("\n <gml:high>" + ((cvEnvelope != null) ? upers : "") + "</gml:high>"); tempResponse.append("\n </gml:GridEnvelope>"); tempResponse.append("\n </gml:limits>"); for (CoverageDimensionInfo dimension : cv.getDimensions()) { tempResponse.append("\n <gml:axisName>" + dimension.getName() + "</gml:axisName>"); } tempResponse.append("\n <gml:origin>"); tempResponse.append("\n <gml:pos>" + (gridToCRS != null ? ((AffineTransform) gridToCRS).getTranslateX() + " " + ((AffineTransform) gridToCRS).getTranslateY() : ((cvEnvelope != null) ? (cvEnvelope.getLowerCorner() .getOrdinate(0) + " " + cvEnvelope.getUpperCorner().getOrdinate(1)) : "")) + "</gml:pos>"); tempResponse.append("\n </gml:origin>"); tempResponse.append("\n <gml:offsetVector>" + (gridToCRS != null ? ((AffineTransform) gridToCRS).getScaleX() + " " + ((AffineTransform) gridToCRS).getShearX() : ((cvEnvelope != null) ? ((cvEnvelope.getUpperCorner().getOrdinate(0) - cvEnvelope.getLowerCorner().getOrdinate(0)) / (grid.getGridRange().getHigh(0) - grid.getGridRange().getLow(0))) : 0.0) + " 0.0") + "</gml:offsetVector>"); tempResponse.append("\n <gml:offsetVector>" + (gridToCRS != null ? ((AffineTransform) gridToCRS).getShearY() + " " + ((AffineTransform) gridToCRS).getScaleY() : "0.0 " + ((cvEnvelope != null) ? ((cvEnvelope.getLowerCorner().getOrdinate(1) - cvEnvelope.getUpperCorner().getOrdinate(1)) / (grid.getGridRange().getHigh(1) - grid.getGridRange().getLow(1))) : -0.0)) + "</gml:offsetVector>"); tempResponse.append("\n </gml:RectifiedGrid>"); tempResponse.append("\n </spatialDomain>"); tempResponse.append("\n </domainSet>"); // rangeSet TreeSet nodataValues = new TreeSet(); try { if (cv.getDimensions().size() > 0) { int numSampleDimensions = cv.getDimensions().size(); tempResponse.append("\n <rangeSet>"); tempResponse.append("\n <RangeSet>"); //tempResponse.append("\n <!-- WARNING: Mandatory metadata '..._rangeset_name' was missing in this context. --> "); tempResponse.append("\n <name>" + cv.getName() + "</name>"); tempResponse.append("\n <label>" + cv.getTitle() + "</label>"); tempResponse.append("\n <axisDescription>"); tempResponse.append("\n <AxisDescription>"); tempResponse.append("\n <name>Band</name>"); tempResponse.append("\n <label>Band</label>"); tempResponse.append("\n <values>"); if (numSampleDimensions == 1) { tempResponse.append("\n <singleValue>").append("1") .append("</singleValue>"); } else { tempResponse.append("\n <interval>"); tempResponse.append("\n <min>1</min>"); tempResponse.append("\n <max>" + numSampleDimensions + "</max>"); tempResponse.append("\n </interval>"); } tempResponse.append("\n </values>"); tempResponse.append("\n </AxisDescription>"); tempResponse.append("\n </axisDescription>"); for(CoverageDimensionInfo dim : cv.getDimensions()) for(Double nullValue : dim.getNullValues()) if (!nodataValues.contains(nullValue)) { nodataValues.add(nullValue); } tempResponse.append("\n <nullValues>"); if (nodataValues.size() > 0) { if (nodataValues.size() == 1) { tempResponse.append("\n <singleValue>" + (Double) nodataValues.first() + "</singleValue>"); } else { tempResponse.append("\n <interval>"); tempResponse.append("\n <min>" + (Double) nodataValues.first() + "</min>"); tempResponse.append("\n <max>" + (Double) nodataValues.last() + "</max>"); tempResponse.append("\n <interval>"); } } else { tempResponse.append("\n <singleValue>0</singleValue>"); } tempResponse.append("\n </nullValues>"); tempResponse.append("\n </RangeSet>"); tempResponse.append("\n </rangeSet>"); } } catch (Exception e) { // TODO Handle this exceptions ... e.printStackTrace(); } if (((cv.getRequestSRS() != null) && (cv.getRequestSRS().size() > 0)) || ((cv.getResponseSRS() != null) && (cv.getResponseSRS().size() > 0))) { tempResponse.append("\n <supportedCRSs>"); if ((cv.getResponseSRS() != null) && (cv.getResponseSRS().size() > 0) && (cv.getRequestSRS() != null) && (cv.getRequestSRS().size() > 0)) { tempResponse.append("\n <requestResponseCRSs>"); ArrayList CRSs = new ArrayList(); for (int i = 0; i < cv.getRequestSRS().size(); i++) if (!CRSs.contains(cv.getRequestSRS().get(i))) { CRSs.add(cv.getRequestSRS().get(i)); } for (int i = 0; i < cv.getResponseSRS().size(); i++) if (!CRSs.contains(cv.getResponseSRS().get(i))) { CRSs.add(cv.getResponseSRS().get(i)); } for (int i = 0; i < CRSs.size(); i++) tempResponse.append(CRSs.get(i) + " "); tempResponse.append("\n </requestResponseCRSs>"); } else { if ((cv.getRequestSRS() != null) && (cv.getRequestSRS().size() > 0)) { for (int i = 0; i < cv.getRequestSRS().size(); i++) tempResponse.append("\n <requestCRSs>" + cv.getRequestSRS().get(i) + "</requestCRSs>"); } if ((cv.getResponseSRS() != null) && (cv.getResponseSRS().size() > 0)) { for (int i = 0; i < cv.getResponseSRS().size(); i++) tempResponse.append("\n <responseCRSs>" + cv.getResponseSRS().get(i) + "</responseCRSs>"); } } tempResponse.append("\n </supportedCRSs>"); } final String nativeFormat = (((cv.getNativeFormat() != null) && cv.getNativeFormat().equalsIgnoreCase("GEOTIFF")) ? "GeoTIFF" : cv.getNativeFormat()); String supportedFormat = ""; if (((cv.getSupportedFormats() != null) && (cv.getSupportedFormats().size() > 0))) { tempResponse.append("\n <supportedFormats" + (((nativeFormat != null) && (nativeFormat != "")) ? (" nativeFormat=\"" + nativeFormat + "\"") : "") + ">"); for (int i = 0; i < cv.getSupportedFormats().size(); i++) { supportedFormat = (String) cv.getSupportedFormats().get(i); supportedFormat = (supportedFormat.equalsIgnoreCase("GEOTIFF") ? "GeoTIFF" : supportedFormat); tempResponse.append("\n <formats>" + supportedFormat + "</formats>"); } tempResponse.append("\n </supportedFormats>"); } if (((cv.getInterpolationMethods() != null) && (cv.getInterpolationMethods().size() > 0))) { tempResponse.append("\n <supportedInterpolations" + (((cv.getDefaultInterpolationMethod() != null) && (cv.getDefaultInterpolationMethod() != "")) ? (" default=\"" + cv.getDefaultInterpolationMethod() + "\"") : "") + ">"); for (int i = 0; i < cv.getInterpolationMethods().size(); i++) tempResponse.append("\n <interpolationMethod>" + cv.getInterpolationMethods().get(i) + "</interpolationMethod>"); tempResponse.append("\n </supportedInterpolations>"); } tempResponse.append("\n </CoverageOffering>"); return tempResponse.toString(); } /* * (non-Javadoc) * * @see org.vfny.geoserver.responses.Response#abort() */ public void abort(ServiceInfo gs) { // nothing to undo } }