package org.geoserver.wcs2_0.eo.response; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.DimensionInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.config.GeoServer; import org.geoserver.ows.URLMangler.URLType; import org.geoserver.ows.util.ResponseUtils; import org.geoserver.wcs.WCSInfo; import org.geoserver.wcs2_0.GetCoverage; import org.geoserver.wcs2_0.eo.WCSEOMetadata; import org.geoserver.wcs2_0.response.WCS20CoverageMetadataProvider; import org.geoserver.wcs2_0.response.WCSDimensionsHelper; import org.geoserver.wcs2_0.util.NCNameResourceCodec; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.geometry.GeneralEnvelope; import org.geotools.referencing.CRS; import org.geotools.referencing.CRS.AxisOrder; import org.geotools.util.logging.Logging; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.xml.sax.Attributes; import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.NamespaceSupport; /** * Encodes the minimal set of EO metadata for a given coverage: * * <pre> * <wcseo:EOMetadata> * <eop:EarthObservation gml:id="someEOCoverage1_metadata"> * <om:phenomenonTime> * <gml:TimePeriod gml:id="someEOCoverage1_tp"> * <gml:beginPosition>2008-03-13T10:00:06.000</gml:beginPosition> * <gml:endPosition>2008-03-13T10:20:26.000</gml:endPosition> * </gml:TimePeriod> * </om:phenomenonTime> * <om:resultTime> * <gml:TimeInstant gml:id="someEOCoverage1_archivingdate"> * <gml:timePosition>2001-08-22T11:02:47.999</gml:timePosition> * </gml:TimeInstant> * </om:resultTime> * <om:procedure/> * <om:observedProperty/> * <om:featureOfInterest> * <eop:Footprint gml:id="someEOCoverage1_fp"> * <eop:multiExtentOf> * <gml:MultiSurface gml:id="someEOCoverage1_ms" srsName="EPSG:4326"> * <gml:surfaceMembers> * <gml:Polygon gml:id="someEOCoverage1_fppoly"> * <gml:exterior> * <gml:LinearRing> * <gml:posList>43.516667 2.1025 43.381667 2.861667 42.862778 2.65 42.996389 1.896944 43.516667 2.1025</gml:posList> * </gml:LinearRing> * </gml:exterior> * </gml:Polygon> * </gml:surfaceMembers> * </gml:MultiSurface> * </eop:multiExtentOf> * <eop:centerOf> * <gml:Point gml:id="someEOCoverage1_p" srsName="EPSG:4326"> * <gml:pos>43.190833 2.374167</gml:pos> * </gml:Point> * </eop:centerOf> * </eop:Footprint> * </om:featureOfInterest> * <om:result/> * <eop:metaDataProperty> * <eop:EarthObservationMetaData> * <eop:identifier>someEOCoverage1</eop:identifier> * <eop:acquisitionType>NOMINAL</eop:acquisitionType> * <eop:status>ARCHIVED</eop:status> * </eop:EarthObservationMetaData> * </eop:metaDataProperty> * </eop:EarthObservation> * </wcseo:EOMetadata> * </pre> * * @author Andrea Aime - GeoSolutions */ public class WCSEOCoverageMetadataProvider implements WCS20CoverageMetadataProvider { static final Logger LOGGER = Logging.getLogger(WCSEOCoverageMetadataProvider.class); private GeoServer gs; public WCSEOCoverageMetadataProvider(GeoServer gs) { this.gs = gs; } private boolean isEarthObservationEnabled() { WCSInfo wcs = gs.getService(WCSInfo.class); Boolean enabled = wcs.getMetadata().get(WCSEOMetadata.ENABLED.key, Boolean.class); return Boolean.TRUE.equals(enabled); } @Override public String[] getSchemaLocations(String schemaBaseURL) { if(!isEarthObservationEnabled()) { return new String[0]; } String schemaLocation = ResponseUtils.buildURL(schemaBaseURL, "schemas/wcseo/1.0/wcsEOCoverage.xsd", null, URLType.RESOURCE); return new String[] { WCSEOMetadata.NAMESPACE, schemaLocation }; } @Override public void registerNamespaces(NamespaceSupport namespaces) { if(!isEarthObservationEnabled()) { return; } namespaces.declarePrefix("wcseo", WCSEOMetadata.NAMESPACE); namespaces.declarePrefix("eop", "http://www.opengis.net/eop/2.0"); namespaces.declarePrefix("gml", "http://www.opengis.net/gml/3.2"); namespaces.declarePrefix("om", "http://www.opengis.net/om/2.0"); } @Override public void encode(Translator tx, Object context) throws IOException { if (!(context instanceof CoverageInfo) || !isEarthObservationEnabled()) { return; } CoverageInfo ci = (CoverageInfo) context; DimensionInfo time = ci.getMetadata().get(ResourceInfo.TIME, DimensionInfo.class); if (time == null) { LOGGER.log(Level.FINE, "We received a coverage info that has no " + "associated time, cannot add EO metadata to it: "+ ci.prefixedName()); return; } GridCoverage2DReader reader = (GridCoverage2DReader) ci.getGridCoverageReader(null, null); String coverageId = NCNameResourceCodec.encode(ci); WCSDimensionsHelper dimensionHelper = new WCSDimensionsHelper(time, reader, coverageId); tx.start("wcseo:EOMetadata"); tx.start("eop:EarthObservation", atts("gml:id", coverageId + "_metadata")); // phenomenon time tx.start("om:phenomenonTime"); tx.start("gml:TimePeriod", atts("gml:id", coverageId + "_tp")); element(tx, "gml:beginPosition", dimensionHelper.getBeginTime(), null); element(tx, "gml:endPosition", dimensionHelper.getEndTime(), null); tx.end("gml:TimePeriod"); tx.end("om:phenomenonTime"); // resultTime tx.start("om:resultTime"); tx.start("gml:TimeInstant", atts("gml:id", coverageId + "_rt")); element(tx, "gml:timePosition", dimensionHelper.getEndTime(), null); tx.end("gml:TimeInstant"); tx.end("om:resultTime"); // some empty elements... element(tx, "om:procedure", null, null); element(tx, "om:observedProperty", null, null); // the footprint GeneralEnvelope ge = reader.getOriginalEnvelope(); CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem(); String srsName = getSRSName(crs); final boolean axisSwap = CRS.getAxisOrder(crs).equals(AxisOrder.EAST_NORTH); double minx = ge.getLowerCorner().getOrdinate(axisSwap ? 1 : 0); double miny = ge.getLowerCorner().getOrdinate(axisSwap ? 0 : 1); double maxx = ge.getUpperCorner().getOrdinate(axisSwap ? 1 : 0); double maxy = ge.getUpperCorner().getOrdinate(axisSwap ? 0 : 1); tx.start("om:featureOfInterest"); tx.start("eop:Footprint", atts("gml:id", coverageId + "_fp")); tx.start("eop:multiExtentOf"); tx.start("gml:MultiSurface", atts("gml:id", coverageId + "_ms", "srsName", srsName)); tx.start("gml:surfaceMembers"); tx.start("gml:Polygon", atts("gml:id", coverageId + "_msp")); tx.start("gml:exterior"); tx.start("gml:LinearRing"); String posList = posList(minx, miny, minx, maxy, maxx, maxy, maxx, miny, minx, miny); element(tx, "gml:posList", posList, null); tx.end("gml:LinearRing"); tx.end("gml:exterior"); tx.end("gml:Polygon"); tx.end("gml:surfaceMembers"); tx.end("gml:MultiSurface"); tx.end("eop:multiExtentOf"); double midx = (minx + maxx) / 2; double midy = (miny + maxy) / 2; tx.start("eop:centerOf"); tx.start("gml:Point", atts("gml:id", coverageId + "_co", "srsName", srsName)); element(tx, "gml:pos", midx + " " + midy, null); tx.end("gml:Point"); tx.end("eop:centerOf"); tx.end("eop:Footprint"); tx.end("om:featureOfInterest"); // fixed metadata properties (at least for the moment) tx.start("eop:metaDataProperty"); tx.start("eop:EarthObservationMetaData"); element(tx, "eop:identifier", coverageId, null); element(tx, "eop:acquisitionType", "NOMINAL", null); element(tx, "eop:status", "ARCHIVED", null); tx.end("eop:EarthObservationMetaData"); tx.end("eop:metaDataProperty"); tx.end("eop:EarthObservation"); tx.end("wcseo:EOMetadata"); } private String posList(double... ordinates) { StringBuilder sb = new StringBuilder(); for (double ord : ordinates) { sb.append(ord).append(" "); } return sb.substring(0, sb.length() - 1); } private String getSRSName(CoordinateReferenceSystem crs) { Integer EPSGCode = null; try { EPSGCode = CRS.lookupEpsgCode(crs, false); } catch (FactoryException e) { throw new IllegalStateException("Unable to lookup epsg code for this CRS:" + crs, e); } if (EPSGCode == null) { throw new IllegalStateException("Unable to lookup epsg code for this CRS:" + crs); } return GetCoverage.SRS_STARTER + EPSGCode; } private void element(Translator tx, String element, String content, AttributesImpl attributes) { tx.start(element, attributes); if(content != null) { tx.chars(content); } tx.end(element); } Attributes atts(String... atts) { AttributesImpl attributes = new AttributesImpl(); for (int i = 0; i < atts.length; i += 2) { attributes.addAttribute(null, atts[i], atts[i], null, atts[i + 1]); } return attributes; } }