diff --git a/deegree-core/deegree-core-layer/src/main/java/org/deegree/layer/config/ConfigUtils.java b/deegree-core/deegree-core-layer/src/main/java/org/deegree/layer/config/ConfigUtils.java index bb40b97b82..5ab3c8acb4 100644 --- a/deegree-core/deegree-core-layer/src/main/java/org/deegree/layer/config/ConfigUtils.java +++ b/deegree-core/deegree-core-layer/src/main/java/org/deegree/layer/config/ConfigUtils.java @@ -38,11 +38,13 @@ import static org.slf4j.LoggerFactory.getLogger; import java.io.File; +import java.math.BigInteger; import java.net.URL; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.deegree.commons.utils.Pair; import org.deegree.layer.dims.Dimension; @@ -219,6 +221,7 @@ public static MapOptions parseLayerOptions( LayerOptionsType cfg ) { int maxFeats = -1; int rad = -1; boolean opaque = false; + Integer decimalPlaces = null; try { alias = Antialias.valueOf( cfg.getAntiAliasing() ); } catch ( Throwable e ) { @@ -240,6 +243,9 @@ public static MapOptions parseLayerOptions( LayerOptionsType cfg ) { if ( cfg.getFeatureInfo() != null ) { if ( cfg.getFeatureInfo().isEnabled() ) { rad = Math.max( 0, cfg.getFeatureInfo().getPixelRadius().intValue() ); + decimalPlaces = Optional.ofNullable(cfg.getFeatureInfo().getDecimalPlaces()) + .map(BigInteger::intValue) + .orElse(null); } else { rad = 0; } @@ -254,7 +260,9 @@ public static MapOptions parseLayerOptions( LayerOptionsType cfg ) { interpolation( interpol ). antialias( alias ). maxFeatures( maxFeats ). - featureInfoRadius( rad ).build(); + featureInfoRadius( rad ). + featureInfoDecimalPlaces( decimalPlaces ). + build(); } public static Map> parseDimensions( String layerName, List dimensions ) { diff --git a/deegree-core/deegree-core-layer/src/main/resources/META-INF/schemas/layers/base/base.xsd b/deegree-core/deegree-core-layer/src/main/resources/META-INF/schemas/layers/base/base.xsd index af450006c5..0638415eb2 100644 --- a/deegree-core/deegree-core-layer/src/main/resources/META-INF/schemas/layers/base/base.xsd +++ b/deegree-core/deegree-core-layer/src/main/resources/META-INF/schemas/layers/base/base.xsd @@ -101,6 +101,7 @@ + diff --git a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/MapOptions.java b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/MapOptions.java index 0584b8ea13..8bc4236267 100644 --- a/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/MapOptions.java +++ b/deegree-core/deegree-core-rendering-2d/src/main/java/org/deegree/rendering/r2d/context/MapOptions.java @@ -54,28 +54,47 @@ public class MapOptions { private boolean opaque; + private Integer featureInfoDecimalPlaces; + /** * Instantiates {@link MapOptions} with default values (quality = null, interpol = null, antialias = null, - * maxFeatures = -1, featureInfoRadius = -1) + * maxFeatures = -1, featureInfoRadius = -1, opaque = false, featureInfoDecimalPlaces = null) * + * @deprecated Use {@link MapOptions.Builder} instead. */ public MapOptions() { - this( null, null, null, -1, -1 ); + this( null, null, null, -1, -1, false, null ); } + /** + * Instantiates {@link MapOptions} + * + * @deprecated Use {@link MapOptions.Builder} instead. + */ public MapOptions( Quality quality, Interpolation interpol, Antialias antialias, int maxFeatures, int featureInfoRadius ) { - this( quality, interpol, antialias, maxFeatures, featureInfoRadius, false ); + this( quality, interpol, antialias, maxFeatures, featureInfoRadius, false, null ); } + /** + * Instantiates {@link MapOptions} + * + * @deprecated Use {@link MapOptions.Builder} instead. + */ public MapOptions( Quality quality, Interpolation interpol, Antialias antialias, int maxFeatures, int featureInfoRadius, boolean opaque ) { + this( quality, interpol, antialias, maxFeatures, featureInfoRadius, opaque, null ); + } + + private MapOptions( Quality quality, Interpolation interpol, Antialias antialias, int maxFeatures, + int featureInfoRadius, boolean opaque, Integer featureInfoDecimalPlaces ) { this.quality = quality; this.interpol = interpol; this.antialias = antialias; this.maxFeatures = maxFeatures; this.featureInfoRadius = featureInfoRadius; this.opaque = opaque; + this.featureInfoDecimalPlaces = featureInfoDecimalPlaces; } /** @@ -168,6 +187,23 @@ public void setOpaque( boolean opaque ) { this.opaque = opaque; } + /** + * @return featureInfoDecimalPlaces, a non null positive value defines the requested number of + * digits after the decimal point to be used for numeric values, if this feature is available + */ + public Integer getFeatureInfoDecimalPlaces() { + return featureInfoDecimalPlaces; + } + + /** + * @param featureInfoDecimalPlaces + * the featureInfoDecimalPlaces to set, a non null positive value defines the requested + * number of digits after the decimal point to be used for numeric values, if this feature is available + */ + public void setFeatureInfoDecimalPlaces( Integer featureInfoDecimalPlaces ) { + this.featureInfoDecimalPlaces = featureInfoDecimalPlaces; + } + /** * Quality * @@ -297,6 +333,10 @@ public static class Builder { private int featureInfoRadius = -1; + private boolean opaque; + + private Integer featureInfoDecimalPlaces; + /** * @param quality * the quality to set @@ -342,8 +382,30 @@ public Builder featureInfoRadius( int featureInfoRadius ) { return this; } + /** + * @param opaque + * set if layer is opaque + */ + public Builder opaque( boolean opaque ) { + this.opaque = opaque; + return this; + } + + /** + * @param featureInfoDecimalPlaces + * the featureInfoDecimalPlaces to set, a non null positive value defines the requested + * number of digits after the decimal point to be used for numeric values, if this feature is + * available + */ + public Builder featureInfoDecimalPlaces( Integer featureInfoDecimalPlaces ) { + this.featureInfoDecimalPlaces = featureInfoDecimalPlaces; + return this; + } + + public MapOptions build() { - return new MapOptions( quality, interpolation, antialias, maxFeatures, featureInfoRadius ); + return new MapOptions( quality, interpolation, antialias, maxFeatures, featureInfoRadius, opaque, + featureInfoDecimalPlaces ); } } diff --git a/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageFeatureInfoHandler.java b/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageFeatureInfoHandler.java index 585200da96..05d68ffa6f 100644 --- a/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageFeatureInfoHandler.java +++ b/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageFeatureInfoHandler.java @@ -46,6 +46,7 @@ Occam Labs UG (haftungsbeschränkt) import static org.slf4j.LoggerFactory.getLogger; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -101,13 +102,17 @@ class CoverageFeatureInfoHandler { private CoverageDimensionHandler dimensionHandler; + private Integer decimalPlaces; + CoverageFeatureInfoHandler( AbstractRaster raster, Envelope bbox, FeatureType featureType, - InterpolationType interpol, CoverageDimensionHandler dimensionHandler ) { + InterpolationType interpol, CoverageDimensionHandler dimensionHandler, + Integer decimalPlaces ) { this.raster = raster; this.bbox = bbox; this.featureType = featureType; this.interpol = interpol; this.dimensionHandler = dimensionHandler; + this.decimalPlaces = decimalPlaces; } FeatureCollection handleFeatureInfoPoint( int x, int y, int width, int height ) { @@ -144,17 +149,13 @@ FeatureCollection handleFeatureInfoPoint( int x, int y, int width, int height ) switch ( dataType ) { case SHORT: case USHORT: { - PrimitiveValue val = new PrimitiveValue( new BigDecimal( 0xffff & data.getShortSample( x, y, 0 ) ), - new PrimitiveType( BaseType.DECIMAL ) ); - props.add( new GenericProperty( featureType.getPropertyDeclarations().get( 0 ), val ) ); + addValueToProps( props, new BigDecimal( 0xffff & data.getShortSample( x, y, 0 ) ) ); break; } case BYTE: { // TODO unknown why this always yields 0 values for eg. satellite images/RGB/ARGB for ( int i = 0; i < data.getBands(); ++i ) { - PrimitiveValue val = new PrimitiveValue( new BigDecimal( 0xff & data.getByteSample( x, y, i ) ), - new PrimitiveType( BaseType.DECIMAL ) ); - props.add( new GenericProperty( featureType.getPropertyDeclarations().get( 0 ), val ) ); + addValueToProps( props, new BigDecimal( 0xff & data.getByteSample( x, y, i ) ) ); } break; } @@ -163,9 +164,7 @@ FeatureCollection handleFeatureInfoPoint( int x, int y, int width, int height ) case UNDEFINED: LOG.warn( "The raster is of type '{}', this is handled as float currently.", dataType ); case FLOAT: - props.add( new GenericProperty( featureType.getPropertyDeclarations().get( 0 ), - new PrimitiveValue( new BigDecimal( data.getFloatSample( x, y, 0 ) ), - new PrimitiveType( BaseType.DECIMAL ) ) ) ); + addValueToProps( props, new BigDecimal( data.getFloatSample( x, y, 0 ) ) ); break; } Feature f = new GenericFeature( featureType, null, props, null ); @@ -193,17 +192,13 @@ FeatureCollection handleFeatureInfo() { switch ( dataType ) { case SHORT: case USHORT: { - PrimitiveValue val = new PrimitiveValue( new BigDecimal( 0xffff & data.getShortSample( 0, 0, 0 ) ), - new PrimitiveType( BaseType.DECIMAL ) ); - props.add( new GenericProperty( findValueProperty(), null, val, createAttributeList() ) ); + addValueToProps( props, new BigDecimal( 0xffff & data.getShortSample( 0, 0, 0 ) ) ); break; } case BYTE: { // TODO unknown why this always yields 0 values for eg. satellite images/RGB/ARGB for ( int i = 0; i < data.getBands(); ++i ) { - PrimitiveValue val = new PrimitiveValue( new BigDecimal( 0xff & data.getByteSample( 0, 0, i ) ), - new PrimitiveType( BaseType.DECIMAL ) ); - props.add( new GenericProperty( findValueProperty(), null, val, createAttributeList() ) ); + addValueToProps( props, new BigDecimal( 0xff & data.getByteSample( 0, 0, i ) ) ); } break; } @@ -212,10 +207,7 @@ FeatureCollection handleFeatureInfo() { case UNDEFINED: LOG.warn( "The raster is of type '{}', this is handled as float currently.", dataType ); case FLOAT: - props.add( new GenericProperty( findValueProperty(), null, - new PrimitiveValue( new BigDecimal( data.getFloatSample( 0, 0, 0 ) ), - new PrimitiveType( BaseType.DECIMAL ) ), - createAttributeList() ) ); + addValueToProps( props, new BigDecimal( data.getFloatSample( 0, 0, 0 ) ) ); break; } Feature f = new GenericFeature( featureType, null, props, null ); @@ -257,4 +249,12 @@ private String createUom( Dimension dimension ) { return dimension.getUnits(); } + private void addValueToProps( List props, BigDecimal result ) { + PrimitiveValue val = new PrimitiveValue( roundValue( result ), new PrimitiveType( BaseType.DECIMAL ) ); + props.add( new GenericProperty( findValueProperty(), val ) ); + } + + private BigDecimal roundValue( BigDecimal value ) { + return decimalPlaces != null ? value.setScale( decimalPlaces, RoundingMode.HALF_UP ) : value; + } } \ No newline at end of file diff --git a/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayer.java b/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayer.java index a1b38b4a36..ecde734435 100644 --- a/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayer.java +++ b/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayer.java @@ -40,6 +40,7 @@ import static org.slf4j.LoggerFactory.getLogger; import java.util.List; +import java.util.Optional; import org.deegree.commons.ows.exception.OWSException; import org.deegree.coverage.rangeset.RangeSet; @@ -50,6 +51,7 @@ import org.deegree.layer.AbstractLayer; import org.deegree.layer.LayerQuery; import org.deegree.layer.metadata.LayerMetadata; +import org.deegree.rendering.r2d.context.MapOptions; import org.deegree.rendering.r2d.context.MapOptions.Interpolation; import org.deegree.style.StyleRef; import org.deegree.style.se.unevaluated.Style; @@ -161,7 +163,7 @@ public CoverageLayerData infoQuery( LayerQuery query, List headers ) return new CoverageLayerData( raster, bbox, query.getWidth(), query.getHeight(), InterpolationType.NEAREST_NEIGHBOR, filter, style, getMetadata().getFeatureTypes().get( 0 ), dimensionHandler, featureInfoMode, - query.getX(), query.getY() ); + query.getX(), query.getY(), getFeatureInfoDecimalPlaces() ); } catch ( OWSException e ) { throw e; @@ -172,4 +174,10 @@ public CoverageLayerData infoQuery( LayerQuery query, List headers ) return null; } + private Integer getFeatureInfoDecimalPlaces() { + return Optional.of( getMetadata() ) // + .map( LayerMetadata::getMapOptions ) // + .map( MapOptions::getFeatureInfoDecimalPlaces ) // + .orElse( null ); + } } diff --git a/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayerData.java b/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayerData.java index 4fa98b35a0..56c406760c 100644 --- a/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayerData.java +++ b/deegree-layers/deegree-layers-coverage/src/main/java/org/deegree/layer/persistence/coverage/CoverageLayerData.java @@ -97,16 +97,18 @@ public class CoverageLayerData implements LayerData { private final int infoPosY; + private final Integer featureInfoDecimalPlaces; + public CoverageLayerData( AbstractRaster raster, Envelope bbox, int width, int height, InterpolationType interpol, RangeSet filter, Style style, FeatureType featureType, CoverageFeatureInfoMode featureInfoMode ) { - this( raster, bbox, width, height, interpol, filter, style, featureType, null, featureInfoMode, -1, -1 ); + this( raster, bbox, width, height, interpol, filter, style, featureType, null, featureInfoMode, -1, -1, null ); } public CoverageLayerData( AbstractRaster raster, Envelope bbox, int width, int height, InterpolationType interpol, RangeSet filter, Style style, FeatureType featureType, CoverageDimensionHandler dimensionHandler, CoverageFeatureInfoMode featureInfoMode, - int infoPosX, int infoPosY ) { + int infoPosX, int infoPosY, Integer featureInfoDecimalPlaces ) { this.raster = raster; this.bbox = bbox; this.width = width; @@ -119,6 +121,7 @@ public CoverageLayerData( AbstractRaster raster, Envelope bbox, int width, int h this.featureInfoMode = featureInfoMode; this.infoPosX = infoPosX; this.infoPosY = infoPosY; + this.featureInfoDecimalPlaces = featureInfoDecimalPlaces; } @Override @@ -176,7 +179,7 @@ public void render( RenderContext context ) { @Override public FeatureCollection info() { CoverageFeatureInfoHandler handler = new CoverageFeatureInfoHandler( raster, bbox, featureType, interpol, - dimensionHandler ); + dimensionHandler, featureInfoDecimalPlaces ); if ( featureInfoMode == CoverageFeatureInfoMode.POINT ) { return handler.handleFeatureInfoPoint( infoPosX, infoPosY, width, height ); } else { diff --git a/deegree-services/deegree-services-wms/src/main/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMerger.java b/deegree-services/deegree-services-wms/src/main/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMerger.java index ff71a8e621..c113a19100 100644 --- a/deegree-services/deegree-services-wms/src/main/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMerger.java +++ b/deegree-services/deegree-services-wms/src/main/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMerger.java @@ -156,7 +156,7 @@ private boolean checkIfLargerCascadedValue( int cascaded, LayerMetadata metadata private void adjustMapOptions( LayerMetadata themeMetadata, int queryable, boolean opaque, int cascaded ) { if ( themeMetadata.getMapOptions() == null ) { - themeMetadata.setMapOptions( new MapOptions() ); + themeMetadata.setMapOptions( new MapOptions.Builder().build() ); } if ( queryable == QUERYABLE_DISABLED_MASK ) { themeMetadata.getMapOptions().setFeatureInfoRadius( 0 ); diff --git a/deegree-services/deegree-services-wms/src/test/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMergerTest.java b/deegree-services/deegree-services-wms/src/test/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMergerTest.java index f33605ee6f..ce9ba97829 100644 --- a/deegree-services/deegree-services-wms/src/test/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMergerTest.java +++ b/deegree-services/deegree-services-wms/src/test/java/org/deegree/services/wms/controller/capabilities/theme/LayerMetadataMergerTest.java @@ -165,13 +165,13 @@ public void mergeEmptyThemeSingleSubthemeWithLayer() { @Test public void mergeThemeWithTwoSubthemesWithTwoLayersWithOpaqueAndWithoutOpaque() { - final MapOptions mapOptionsWithOpaque = new MapOptions( null, null, null, -1, -1, true ); + final MapOptions mapOptionsWithOpaque = new MapOptions.Builder().opaque( true ).build(); final Layer layerWithOpaque = createLayer( "LayerWithOpaque", -180.0, -90.0, 180.0, 90, mapOptionsWithOpaque ); final List layersWithOpaque = Collections.singletonList( layerWithOpaque ); final Theme subThemeWithOpaque = createTheme( "SubthemeWithOpaque", -180.0, -90.0, 180.0, 90, layersWithOpaque, null, null ); - final MapOptions mapOptionsWithoutOpaque = new MapOptions( null, null, null, -1, -1, false ); + final MapOptions mapOptionsWithoutOpaque = new MapOptions.Builder().opaque( false ).build(); final Layer layerWithoutOpaque = createLayer( "LayerWithoutOpaque", -180.0, -90.0, 180.0, 90, mapOptionsWithoutOpaque ); final List layersWithoutOpaque = Collections.singletonList( layerWithoutOpaque ); diff --git a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/layers.adoc b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/layers.adoc index 005b25bdb8..5bf9be43d3 100644 --- a/deegree-services/deegree-webservices-handbook/src/main/asciidoc/layers.adoc +++ b/deegree-services/deegree-webservices-handbook/src/main/asciidoc/layers.adoc @@ -307,6 +307,11 @@ disabled (default is true) |FeatureInfo |0..1 |None |attribute _pixelRadius_: Number of pixels to consider when doing GetFeatureInfo, default is 1 + +|FeatureInfo |0..1 |None |attribute _decimalPlaces_: Desired number of +digits after the decimal point when returning numeric values in +GetFeatureInfo, default is unbounded. Currently limited to +GetFeatureInfo for coverage (raster) data. |=== Here is an example snippet: