@@ -14,6 +14,7 @@ use actix_web::{web, FromRequest, HttpRequest, HttpResponse};
1414use geoengine_datatypes:: primitives:: {
1515 AxisAlignedRectangle , BandSelection , RasterQueryRectangle , SpatialPartition2D ,
1616} ;
17+ use geoengine_datatypes:: raster:: GeoTransform ;
1718use geoengine_datatypes:: { primitives:: SpatialResolution , spatial_reference:: SpatialReference } ;
1819use geoengine_operators:: call_on_generic_raster_processor_gdal_types;
1920use geoengine_operators:: engine:: { CanonicOperatorName , ExecutionContext , WorkflowOperatorPath } ;
@@ -176,6 +177,7 @@ async fn wcs_capabilities_handler<C: ApplicationContext>(
176177 ( "session_token" = [ ] )
177178 )
178179) ]
180+ #[ allow( clippy:: too_many_lines) ]
179181async fn wcs_describe_coverage_handler < C : ApplicationContext > (
180182 workflow : web:: Path < WorkflowId > ,
181183 request : web:: Query < DescribeCoverage > ,
@@ -219,28 +221,54 @@ async fn wcs_describe_coverage_handler<C: ApplicationContext>(
219221 let spatial_reference: Option < SpatialReference > = result_descriptor. spatial_reference . into ( ) ;
220222 let spatial_reference = spatial_reference. ok_or ( error:: Error :: MissingSpatialReference ) ?;
221223
222- // TODO: give tighter bounds if possible
223- let area_of_use: SpatialPartition2D = spatial_reference. area_of_use_projected ( ) ?;
224-
225- let ( bbox_ll_0, bbox_ll_1, bbox_ur_0, bbox_ur_1) =
226- match spatial_reference_specification ( & spatial_reference. proj_string ( ) ?) ?
227- . axis_order
228- . ok_or ( Error :: AxisOrderingNotKnownForSrs {
229- srs_string : spatial_reference. srs_string ( ) ,
230- } ) ? {
231- AxisOrder :: EastNorth => (
232- area_of_use. lower_left ( ) . x ,
233- area_of_use. lower_left ( ) . y ,
234- area_of_use. upper_right ( ) . x ,
235- area_of_use. upper_right ( ) . y ,
236- ) ,
237- AxisOrder :: NorthEast => (
238- area_of_use. lower_left ( ) . y ,
239- area_of_use. lower_left ( ) . x ,
240- area_of_use. upper_right ( ) . y ,
241- area_of_use. upper_right ( ) . x ,
242- ) ,
243- } ;
224+ let resolution = result_descriptor
225+ . resolution
226+ . unwrap_or ( SpatialResolution :: zero_point_one ( ) ) ;
227+
228+ // TODO: swap depending on axis order?
229+ let pixel_size_x = resolution. x ;
230+ let pixel_size_y = -resolution. y ;
231+
232+ let bbox = if let Some ( bbox) = result_descriptor. bbox {
233+ bbox
234+ } else {
235+ spatial_reference. area_of_use_projected ( ) ?
236+ } ;
237+
238+ let axis_order = spatial_reference_specification ( & spatial_reference. proj_string ( ) ?) ?
239+ . axis_order
240+ . ok_or ( Error :: AxisOrderingNotKnownForSrs {
241+ srs_string : spatial_reference. srs_string ( ) ,
242+ } ) ?;
243+ let ( bbox_ll_0, bbox_ll_1, bbox_ur_0, bbox_ur_1) = match axis_order {
244+ AxisOrder :: EastNorth => (
245+ bbox. lower_left ( ) . x ,
246+ bbox. lower_left ( ) . y ,
247+ bbox. upper_right ( ) . x ,
248+ bbox. upper_right ( ) . y ,
249+ ) ,
250+ AxisOrder :: NorthEast => (
251+ bbox. lower_left ( ) . y ,
252+ bbox. lower_left ( ) . x ,
253+ bbox. upper_right ( ) . y ,
254+ bbox. upper_right ( ) . x ,
255+ ) ,
256+ } ;
257+
258+ // TODO: swap depending on axis order?
259+ // let raster_size_x = (bbox.size_x() / pixel_size_x.abs()).ceil() as usize;
260+ // let raster_size_y = (bbox.size_y() / pixel_size_y.abs()).ceil() as usize;
261+
262+ let geo_transform = GeoTransform :: new ( bbox. upper_left ( ) , pixel_size_x, pixel_size_y) ;
263+
264+ let [ raster_size_x, raster_size_y] =
265+ * ( geo_transform. lower_right_pixel_idx ( & bbox) + [ 1 , 1 ] ) . inner ( ) ;
266+
267+ // TODO: swap order of GridOffsets ix axis is swapped? Currently, GDAL seems to take the rotation as pixel size. WCS spec says
268+ // "When the GridType is “urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs”, there shall be four values in this GridOffsets. The center two of these offsets will be zero when the GridCRS is not rotated or skewed in the GridBaseCRS."
269+ // though.
270+
271+ // TODO: output all available bands
244272
245273 let mock = format ! (
246274 r#"<?xml version="1.0" encoding="UTF-8"?>
@@ -259,15 +287,29 @@ async fn wcs_describe_coverage_handler<C: ApplicationContext>(
259287 <ows:LowerCorner>{bbox_ll_0} {bbox_ll_1}</ows:LowerCorner>
260288 <ows:UpperCorner>{bbox_ur_0} {bbox_ur_1}</ows:UpperCorner>
261289 </ows:BoundingBox>
290+ <ows:BoundingBox crs="urn:ogc:def:crs:OGC:1.3:CRS:imageCRS" dimensions="2">
291+ <ows:LowerCorner>0 0</ows:LowerCorner>
292+ <ows:UpperCorner>{raster_size_y} {raster_size_x}</ows:UpperCorner>
293+ </ows:BoundingBox>
262294 <wcs:GridCRS>
263295 <wcs:GridBaseCRS>urn:ogc:def:crs:{srs_authority}::{srs_code}</wcs:GridBaseCRS>
264296 <wcs:GridType>urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs</wcs:GridType>
265297 <wcs:GridOrigin>{origin_x} {origin_y}</wcs:GridOrigin>
266- <wcs:GridOffsets>0 0.0 0.0 -0 </wcs:GridOffsets>
298+ <wcs:GridOffsets>{pixel_size_x} 0.0 0.0 {pixel_size_y} </wcs:GridOffsets>
267299 <wcs:GridCS>urn:ogc:def:cs:OGC:0.0:Grid2dSquareCS</wcs:GridCS>
268300 </wcs:GridCRS>
269301 </wcs:SpatialDomain>
270302 </wcs:Domain>
303+ <wcs:Range>
304+ <wcs:Field>
305+ <wcs:Identifier>contents</wcs:Identifier>
306+ <wcs:Axis identifier="Bands">
307+ <wcs:AvailableKeys>
308+ <wcs:Key>{band_name}</wcs:Key>
309+ </wcs:AvailableKeys>
310+ </wcs:Axis>
311+ </wcs:Field>
312+ </wcs:Range>
271313 <wcs:SupportedCRS>{srs_authority}:{srs_code}</wcs:SupportedCRS>
272314 <wcs:SupportedFormat>image/tiff</wcs:SupportedFormat>
273315 </wcs:CoverageDescription>
@@ -276,12 +318,9 @@ async fn wcs_describe_coverage_handler<C: ApplicationContext>(
276318 workflow_id = identifiers,
277319 srs_authority = spatial_reference. authority( ) ,
278320 srs_code = spatial_reference. code( ) ,
279- origin_x = area_of_use. upper_left( ) . x,
280- origin_y = area_of_use. upper_left( ) . y,
281- bbox_ll_0 = bbox_ll_0,
282- bbox_ll_1 = bbox_ll_1,
283- bbox_ur_0 = bbox_ur_0,
284- bbox_ur_1 = bbox_ur_1,
321+ origin_x = bbox. upper_left( ) . x,
322+ origin_y = bbox. upper_left( ) . y,
323+ band_name = result_descriptor. bands[ 0 ] . name,
285324 ) ;
286325
287326 Ok ( HttpResponse :: Ok ( ) . content_type ( mime:: TEXT_XML ) . body ( mock) )
@@ -422,8 +461,18 @@ async fn wcs_get_coverage_handler<C: ApplicationContext>(
422461 }
423462 } ;
424463
464+ // snap bbox to grid
465+ let geo_transform = GeoTransform :: new (
466+ request_partition. upper_left ( ) ,
467+ spatial_resolution. x ,
468+ -spatial_resolution. y ,
469+ ) ;
470+ let idx = geo_transform. lower_right_pixel_idx ( & request_partition) + [ 1 , 1 ] ;
471+ let lower_right = geo_transform. grid_idx_to_pixel_upper_left_coordinate_2d ( idx) ;
472+ let snapped_partition = SpatialPartition2D :: new ( request_partition. upper_left ( ) , lower_right) ?;
473+
425474 let query_rect = RasterQueryRectangle {
426- spatial_bounds : request_partition ,
475+ spatial_bounds : snapped_partition ,
427476 time_interval : request. time . unwrap_or_else ( default_time_from_config) . into ( ) ,
428477 spatial_resolution,
429478 attributes : BandSelection :: first ( ) , // TODO: support multi bands in API and set the selection here
0 commit comments