Skip to content

Commit 1e77910

Browse files
committed
make wcs handler more compatible with gdal
1 parent db8351b commit 1e77910

File tree

5 files changed

+187
-35
lines changed

5 files changed

+187
-35
lines changed

services/src/api/handlers/wcs.rs

Lines changed: 79 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use actix_web::{web, FromRequest, HttpRequest, HttpResponse};
1414
use geoengine_datatypes::primitives::{
1515
AxisAlignedRectangle, BandSelection, RasterQueryRectangle, SpatialPartition2D,
1616
};
17+
use geoengine_datatypes::raster::GeoTransform;
1718
use geoengine_datatypes::{primitives::SpatialResolution, spatial_reference::SpatialReference};
1819
use geoengine_operators::call_on_generic_raster_processor_gdal_types;
1920
use 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)]
179181
async 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

services/src/api/ogc/wcs/request.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,21 @@ pub struct GetCoverage {
7777
#[serde(deserialize_with = "parse_wcs_bbox")]
7878
#[param(value_type = String, example = "-90,-180,90,180,urn:ogc:def:crs:EPSG::4326")]
7979
pub boundingbox: WcsBoundingbox, // TODO: optional?
80-
#[serde(alias = "GRIDBASECRS", alias = "CRS", alias = "crs")]
80+
#[serde(
81+
alias = "GRIDBASECRS",
82+
alias = "GridBaseCRS",
83+
alias = "CRS",
84+
alias = "crs"
85+
)]
8186
#[serde(deserialize_with = "parse_wcs_crs")]
8287
#[param(example = "urn:ogc:def:crs:EPSG::4326", value_type = String)]
8388
pub gridbasecrs: SpatialReference,
8489
#[serde(default)]
85-
#[serde(alias = "GRIDORIGIN")]
90+
#[serde(alias = "GRIDORIGIN", alias = "GridOrigin")]
8691
#[serde(deserialize_with = "parse_grid_origin_option")]
8792
#[param(value_type = String, example="90,-180")]
8893
pub gridorigin: Option<GridOrigin>,
89-
#[serde(alias = "GRIDOFFSETS")]
94+
#[serde(alias = "GRIDOFFSETS", alias = "GridOffsets")]
9095
#[serde(default)]
9196
#[serde(deserialize_with = "parse_grid_offset_option")]
9297
#[param(value_type = String, example="-0.1,0.1")]

test_data/api_calls/wcs.http

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
###
2+
3+
# @name anonymousSession
4+
POST http://localhost:3030/api/anonymous
5+
Content-Type: application/json
6+
7+
###
8+
9+
# @name workflow
10+
POST http://localhost:3030/api/workflow
11+
Authorization: Bearer {{anonymousSession.response.body.$.id}}
12+
Content-Type: application/json
13+
14+
{
15+
"type": "Raster",
16+
"operator": {
17+
"type": "GdalSource",
18+
"params": {
19+
"data": "ndvi"
20+
}
21+
}
22+
}
23+
24+
###
25+
26+
27+
# @name workflow
28+
POST http://localhost:3030/api/workflow
29+
Authorization: Bearer {{anonymousSession.response.body.$.id}}
30+
Content-Type: application/json
31+
32+
{
33+
"type": "Raster",
34+
"operator": {
35+
"type": "GdalSource",
36+
"params": {
37+
"data": "ndvi_3857"
38+
}
39+
}
40+
}
41+
42+
###
43+
44+
GET http://localhost:4200/api/wcs/{{workflow.response.body.$.id}}?SERVICE=WCS&REQUEST=DescribeCoverage&VERSION=1.1.1&IDENTIFIERS={{workflow.response.body.$.id}}&FORMAT=text/xml&crs=urn:ogc:def:crs:EPSG::4326
45+
Authorization: Bearer {{anonymousSession.response.body.$.id}}

test_data/api_calls/wms.http

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
###
3+
4+
# @name anonymousSession
5+
POST http://localhost:3030/api/anonymous
6+
Content-Type: application/json
7+
8+
###
9+
10+
# @name workflow
11+
POST http://localhost:3030/api/workflow
12+
Authorization: Bearer {{anonymousSession.response.body.$.id}}
13+
Content-Type: application/json
14+
15+
{
16+
"type": "Raster",
17+
"operator": {
18+
"type": "GdalSource",
19+
"params": {
20+
"data": "ndvi"
21+
}
22+
}
23+
}
24+
25+
###
26+
27+
# @name workflow
28+
POST http://localhost:3030/api/workflow
29+
Authorization: Bearer {{anonymousSession.response.body.$.id}}
30+
Content-Type: application/json
31+
32+
{
33+
"type": "Raster",
34+
"operator": {
35+
"type": "GdalSource",
36+
"params": {
37+
"data": "ndvi_3857"
38+
}
39+
}
40+
}
41+
42+
###
43+
44+
# {{workflow.response.body.$.id}}
45+
46+
# http://localhost:4200/api/wms/890d41ec-7e4c-5000-8ab8-7394a758a86f?REQUEST=GetMap&SERVICE=WMS&VERSION=1.3.0&FORMAT=image/png&STYLES=custom:{"type":"singleBand","band":0,"bandColorizer":{"type":"linearGradient","breakpoints":[{"value":-1,"color":[0,0,0,255]},{"value":1,"color":[255,255,255,255]}],"noDataColor":[0,0,0,0],"overColor":[246,250,254,255],"underColor":[247,251,255,255]}}&TRANSPARENT=true&layers=890d41ec-7e4c-5000-8ab8-7394a758a86f&time=2000-01-01T00:00:00.000Z&EXCEPTIONS=application/json&WIDTH=256&HEIGHT=256&CRS=EPSG:32632&BBOX=353060.0,5644460.0,394020.0,5603500.0
47+
48+
GET http://localhost:4200/api/wms/890d41ec-7e4c-5000-8ab8-7394a758a86f?REQUEST=GetMap&SERVICE=WMS&VERSION=1.3.0&FORMAT=image%2Fpng&STYLES=custom%3A%7B%22type%22%3A%22singleBand%22%2C%22band%22%3A0%2C%22bandColorizer%22%3A%7B%22type%22%3A%22linearGradient%22%2C%22breakpoints%22%3A%5B%7B%22value%22%3A-1%2C%22color%22%3A%5B0%2C0%2C0%2C255%5D%7D%2C%7B%22value%22%3A1%2C%22color%22%3A%5B255%2C255%2C255%2C255%5D%7D%5D%2C%22noDataColor%22%3A%5B0%2C0%2C0%2C0%5D%2C%22overColor%22%3A%5B246%2C250%2C254%2C255%5D%2C%22underColor%22%3A%5B247%2C251%2C255%2C255%5D%7D%7D&TRANSPARENT=true&layers=890d41ec-7e4c-5000-8ab8-7394a758a86f&time=2022-01-01T00%3A00%3A00.000Z&EXCEPTIONS=application%2Fjson&WIDTH=256&HEIGHT=256&CRS=EPSG:32632&BBOX=353060.0%2C5603500.0%2C394020.0%2C5644460.0
49+
Authorization: Bearer {{anonymousSession.response.body.$.id}}

test_data/dataset_defs/ndvi (3587).json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@
2222
"end": "2014-07-01T00:00:00.000Z"
2323
},
2424
"bbox": {
25-
"upperLeftCoordinate": [-20026376.39, 20048966.1],
26-
"lowerRightCoordinate": [20026376.39, -20048966.1]
25+
"upperLeftCoordinate": [
26+
-20037508.3427892439067364, 19971868.8804085627198219
27+
],
28+
"lowerRightCoordinate": [
29+
20027452.8429077081382275, -19966571.3752283006906509
30+
]
2731
},
2832
"resolution": {
2933
"x": 14052.95025804873876,

0 commit comments

Comments
 (0)