Skip to content

Commit

Permalink
[GEOS-7148] WCS 2.0.1 can send coverage in wrong format
Browse files Browse the repository at this point in the history
  • Loading branch information
aaime committed Oct 2, 2015
1 parent eb19c32 commit 46807ea
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 39 deletions.
9 changes: 2 additions & 7 deletions src/wcs2_0/src/main/java/applicationContext.xml
Expand Up @@ -157,13 +157,8 @@
<constructor-arg ref="catalog" index="1" />
</bean>

<bean id="arcGridWcsMimeMapper" class="org.geoserver.wcs2_0.response.ReaderClassMimeMapper">
<constructor-arg index="0" value="org.geotools.gce.arcgrid.ArcGridReader"/>
<constructor-arg index="1" value="text/plain" />
</bean>

<bean id="gtopo30WcsMimeMapper" class="org.geoserver.wcs2_0.response.ReaderClassMimeMapper">
<constructor-arg index="0" value="org.geotools.gce.gtopo30.GTopo30Reader"/>
<bean id="arcGridWcsMimeMapper" class="org.geoserver.wcs2_0.response.FormatNameMimeMapper">
<constructor-arg index="0" value="ArcGrid"/>
<constructor-arg index="1" value="text/plain" />
</bean>

Expand Down
Expand Up @@ -46,7 +46,7 @@ public class DefaultWebCoverageService20 implements WebCoverageService20 {

protected Logger LOGGER = Logging.getLogger(DefaultWebCoverageService20.class);

private MIMETypeMapper mimemapper;
private MIMETypeMapper mimeMapper;

private Catalog catalog;

Expand All @@ -68,7 +68,7 @@ public DefaultWebCoverageService20(GeoServer geoServer, CoverageResponseDelegate
this.catalog = geoServer.getCatalog();
this.responseFactory = responseFactory;
this.envelopeAxesMapper=envelopeDimensionsMapper;
this.mimemapper=mimemappe;
this.mimeMapper=mimemappe;
this.wcsDescribeCoverageExtensions = GeoServerExtensions
.extensions(WCS20DescribeCoverageExtension.class);
this.availableDescribeCovExtensions = wcsDescribeCoverageExtensions != null
Expand Down Expand Up @@ -121,7 +121,7 @@ public WCS20DescribeCoverageTransformer describeCoverage(DescribeCoverageType re

WCSInfo wcs = getServiceInfo();

WCS20DescribeCoverageTransformer describeTransformer = new WCS20DescribeCoverageTransformer(wcs, catalog, responseFactory,envelopeAxesMapper,mimemapper);
WCS20DescribeCoverageTransformer describeTransformer = new WCS20DescribeCoverageTransformer(wcs, catalog, responseFactory,envelopeAxesMapper,mimeMapper);
describeTransformer.setEncoding(Charset.forName(wcs.getGeoServer().getSettings().getCharset()));
return describeTransformer;
}
Expand All @@ -135,7 +135,7 @@ public GridCoverage getCoverage(GetCoverageType request) {
throw new OWS20Exception("Required parameter coverageId missing", WCS20Exception.WCS20ExceptionCode.EmptyCoverageIdList, "coverageId");
}

return new GetCoverage(getServiceInfo(), catalog,envelopeAxesMapper).run(request);
return new GetCoverage(getServiceInfo(), catalog, envelopeAxesMapper, mimeMapper).run(request);
}

@Override
Expand Down
16 changes: 15 additions & 1 deletion src/wcs2_0/src/main/java/org/geoserver/wcs2_0/GetCoverage.java
Expand Up @@ -40,6 +40,7 @@
import org.geoserver.wcs2_0.exception.WCS20Exception.WCS20ExceptionCode;
import org.geoserver.wcs2_0.response.DimensionBean;
import org.geoserver.wcs2_0.response.GranuleStackImpl;
import org.geoserver.wcs2_0.response.MIMETypeMapper;
import org.geoserver.wcs2_0.response.WCSDimensionsSubsetHelper;
import org.geoserver.wcs2_0.util.EnvelopeAxesLabelsMapper;
import org.geoserver.wcs2_0.util.NCNameResourceCodec;
Expand Down Expand Up @@ -153,17 +154,20 @@ public class GetCoverage {
/** Factory used to create new coverages */
private GridCoverageFactory gridCoverageFactory;

private MIMETypeMapper mimeMapper;

public final static String SRS_STARTER="http://www.opengis.net/def/crs/EPSG/0/";

/** Hints to indicate that a scale has been pre-applied, reporting the scaling factors */
public static Hints.Key PRE_APPLIED_SCALE = new Hints.Key(Double[].class);

private static final double EPS = 1e-6;

public GetCoverage(WCSInfo serviceInfo, Catalog catalog, EnvelopeAxesLabelsMapper envelopeDimensionsMapper) {
public GetCoverage(WCSInfo serviceInfo, Catalog catalog, EnvelopeAxesLabelsMapper envelopeDimensionsMapper, MIMETypeMapper mimeMapper) {
this.wcs = serviceInfo;
this.catalog = catalog;
this.envelopeDimensionsMapper=envelopeDimensionsMapper;
this.mimeMapper = mimeMapper;

// building the needed URI CRS Factories
Hints hints = GeoTools.getDefaultHints();
Expand Down Expand Up @@ -207,6 +211,16 @@ public GridCoverage run(GetCoverageType request) {
if(LOGGER.isLoggable(Level.FINE)){
LOGGER.fine("Executing GetCoverage request on coverage :"+linfo.toString());
}

// prepare the default format
if(request.getFormat() == null) {
try {
String nativeFormat = mimeMapper.mapNativeFormat(cinfo);
request.setFormat(nativeFormat);
} catch(Exception e) {
LOGGER.log(Level.WARNING, "Could not compute the native type of the coverage, defaulting to image/tiff", e);
}
}

// === k, now start the execution
GridCoverage coverage = null;
Expand Down
Expand Up @@ -18,21 +18,21 @@
* @author Andrea Aime - GeoSolutions
*
*/
public class ReaderClassMimeMapper implements CoverageMimeTypeMapper {
public class FormatNameMimeMapper implements CoverageMimeTypeMapper {

private String mime;
private String formatName;

private Class<? extends GridCoverageReader> readerClass;
private String mime;

public ReaderClassMimeMapper(Class<? extends GridCoverageReader> readerClass, String mime) {
this.readerClass = readerClass;
public FormatNameMimeMapper(String formatName, String mime) {
this.formatName = formatName;
this.mime = mime;
}

@Override
public String getMimeType(CoverageInfo ci) throws IOException {
GridCoverageReader reader = ci.getGridCoverageReader(null, null);
if (reader != null && readerClass.isAssignableFrom(reader.getClass())) {
if (formatName.equals(reader.getFormat().getName())) {
return mime;
}

Expand Down
Expand Up @@ -60,8 +60,13 @@ public String getMimeType(CoverageInfo cInfo) throws IOException {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Found reader for format: " + reader.getFormatName());
}
return reader.getOriginatingProvider().getMIMETypes()[0];

String mime = reader.getOriginatingProvider().getMIMETypes()[0];
// the native format rules says "the range set values can be obtained unaltered",
// so we cannot allow lossy compressions (which would alter the range set values)
String lcMime = mime.toLowerCase();
if(lcMime.contains("jpeg") || lcMime.contains("mrsid") || lcMime.contains("ecw")) {
return null;
}
}
} catch (Exception e) {
if (LOGGER.isLoggable(Level.WARNING)) {
Expand Down
Expand Up @@ -23,6 +23,7 @@
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.wcs.responses.CoverageResponseDelegate;
import org.geoserver.wcs.responses.CoverageResponseDelegateFinder;
import org.geoserver.wcs.responses.GeoTIFFCoverageResponseDelegate;
import org.geotools.util.SoftValueHashMap;
import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging;
Expand All @@ -42,6 +43,8 @@
public class MIMETypeMapper implements ApplicationContextAware {

private static final String NO_MIME_TYPE = "NoMimeType";

public static final String DEFAULT_FORMAT = GeoTIFFCoverageResponseDelegate.GEOTIFF_CONTENT_TYPE;

private Logger LOGGER = Logging.getLogger(MIMETypeMapper.class);

Expand All @@ -67,8 +70,9 @@ private MIMETypeMapper(CoverageResponseDelegateFinder finder, Catalog catalog) {
}

/**
* Returns a mime types or null for the provided {@link CoverageInfo} using the
* {@link CoverageInfo#getNativeFormat()} as its key.
* Returns a mime types for the provided {@link CoverageInfo} using the
* {@link CoverageInfo#getNativeFormat()} as its key. In case none was found,
* the DEFAULT_FORMAT format is returned.
*
* @param cInfo the {@link CoverageInfo} to find a mime type for
* @return a mime types or null for the provided {@link CoverageInfo} using the
Expand All @@ -82,7 +86,7 @@ public String mapNativeFormat(final CoverageInfo cInfo) throws IOException {
String mime = mimeTypeCache.get(cInfo.getId());
if(mime != null) {
if(NO_MIME_TYPE.equals(mime)) {
return null;
return DEFAULT_FORMAT;
} else {
return mime;
}
Expand All @@ -95,6 +99,7 @@ public String mapNativeFormat(final CoverageInfo cInfo) throws IOException {
}
}

// the native format must be encodable
if (mime != null && outputMimeTypes.contains(mime)) {
mimeTypeCache.put(cInfo.getId(), mime);
if (LOGGER.isLoggable(Level.FINE)) {
Expand All @@ -105,7 +110,7 @@ public String mapNativeFormat(final CoverageInfo cInfo) throws IOException {
// we either don't have a clue about the mime, or we don't have an encoder,
// save the response as null
mimeTypeCache.put(cInfo.getId(), NO_MIME_TYPE);
return null;
return DEFAULT_FORMAT;
}
}

Expand Down
Expand Up @@ -306,10 +306,6 @@ private void handleServiceParameters(CoverageInfo ci) throws IOException {
element("wcs:CoverageSubtype", "RectifiedGridCoverage");

String mapNativeFormat = mimemapper.mapNativeFormat(ci);
if(mapNativeFormat==null){
// fall back on GeoTiff when a native format cannot be determined
mapNativeFormat = GeoTIFFCoverageResponseDelegate.GEOTIFF_CONTENT_TYPE;
}
element("wcs:nativeFormat",mapNativeFormat);
end("wcs:ServiceParameters");
}
Expand Down
Expand Up @@ -77,6 +77,7 @@ public void testCustomUnit() throws Exception {
assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription/gmlcov:rangeType/swe:DataRecord/swe:field)", dom);
assertXpathEvaluatesTo("rain", "//wcs:CoverageDescription/gmlcov:rangeType//swe:DataRecord/swe:field/@name", dom);
assertXpathEvaluatesTo("mm", "//wcs:CoverageDescription/gmlcov:rangeType/swe:DataRecord/swe:field/swe:Quantity/swe:uom/@code", dom);
assertXpathEvaluatesTo("text/plain", "//wcs:CoverageDescriptions//wcs:CoverageDescription[1]//wcs:ServiceParameters//wcs:nativeFormat", dom);
}

@Test
Expand Down Expand Up @@ -218,7 +219,7 @@ public void testNativeFormatArcGrid() throws Exception {
assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription)", dom);
assertXpathEvaluatesTo("sf__rain", "//wcs:CoverageDescriptions/wcs:CoverageDescription[1]/@gml:id", dom);
assertXpathEvaluatesTo("1", "count(//wcs:CoverageDescription[1]//gmlcov:rangeType//swe:DataRecord//swe:field)", dom);
assertXpathEvaluatesTo("image/tiff", "//wcs:CoverageDescriptions/wcs:CoverageDescription[1]//wcs:ServiceParameters//wcs:nativeFormat", dom);
assertXpathEvaluatesTo("text/plain", "//wcs:CoverageDescriptions/wcs:CoverageDescription[1]//wcs:ServiceParameters//wcs:nativeFormat", dom);
}

@Test
Expand Down
Expand Up @@ -4,6 +4,18 @@

import java.util.Map;

import javax.xml.namespace.QName;

import org.eclipse.emf.common.util.EList;
import org.geoserver.data.test.MockData;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.wcs2_0.WCS20Const;
import org.geotools.wcs.v2_0.RangeSubset;
import org.geotools.wcs.v2_0.Scaling;
import org.junit.Test;

import com.mockrunner.mock.web.MockHttpServletResponse;

import net.opengis.wcs20.GetCoverageType;
import net.opengis.wcs20.InterpolationType;
import net.opengis.wcs20.RangeItemType;
Expand All @@ -17,16 +29,15 @@
import net.opengis.wcs20.TargetAxisExtentType;
import net.opengis.wcs20.TargetAxisSizeType;

import org.eclipse.emf.common.util.EList;
import org.geoserver.wcs2_0.WCS20Const;
import org.geotools.wcs.v2_0.RangeSubset;
import org.geotools.wcs.v2_0.Scaling;
import org.junit.Test;
import org.w3c.dom.Document;

import com.mockrunner.mock.web.MockHttpServletResponse;

public class GetCoverageKvpTest extends WCSKVPTestSupport {

private static final QName RAIN = new QName(MockData.SF_URI, "rain", MockData.SF_PREFIX);

@Override
protected void onSetUp(SystemTestData testData) throws Exception {
super.onSetUp(testData);
testData.addRasterLayer(RAIN, "rain.zip", "asc", getCatalog());
}

@Test
public void testParseBasic() throws Exception {
Expand All @@ -38,11 +49,20 @@ public void testParseBasic() throws Exception {
@Test
public void testGetCoverageNoWs() throws Exception {
MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" +
"&coverageId=BlueMarble&&Format=image/tiff");
"&coverageId=BlueMarble&Format=image/tiff");

assertEquals("image/tiff", response.getContentType());
}

@Test
public void testGetCoverageNativeFormat() throws Exception {
MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" +
"&coverageId=sf__rain");

// we got back an ArcGrid response
assertEquals("text/plain", response.getContentType());
}

@Test
public void testNotExistent() throws Exception {
MockHttpServletResponse response = getAsServletResponse("wcs?request=GetCoverage&service=WCS&version=2.0.1" +
Expand Down

0 comments on commit 46807ea

Please sign in to comment.