Skip to content

Commit 5e9b545

Browse files
authored
Merge pull request #7 from geographika/wcs
Add WCS tutorial
2 parents 590faba + 3dfaa34 commit 5e9b545

6 files changed

Lines changed: 275 additions & 73 deletions

File tree

Lines changed: 158 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,183 @@
1-
# Web Coverage Services
1+
# Web Coverage Services (WCS)
22

3-
TODO
3+
## Overview
44

5-
https://mapserver.org/ogc/wcs_server.html
5+
This tutorial demonstrates the [WCS Server](https://mapserver.org/ogc/wcs_server.html) capabilities of MapServer.
6+
We'll be using [WCS 2.0](https://mapserver.org/ogc/wcs_server.html#wcs-2-0) for this tutorial, and will serve a
7+
Cloud-Optimized GeoTIFF (COG) from the Estonian Land Board as the source dataset. The dataset is a Digital Terrain Model (DTM) with a 1 m resolution.
68

7-
https://mapserver.org/ogc/wcs_format.html
9+
<div class="map">
10+
<iframe src="https://mapserver.github.io/getting-started-with-mapserver-demo/wcs.html"></iframe>
11+
</div>
812

9-
We'll be using [WCS 2.0](https://mapserver.org/ogc/wcs_server.html#wcs-2-0) for this tutorial.
13+
## WCS Requests
1014

11-
# GetCapabilities
12-
http://localhost:7000/?map=/etc/mapserver/wcs.map&SERVICE=WCS&REQUEST=GetCapabilities
15+
Some sample MapServer requests for testing the WCS service are listed below. You can test these in your browser.
1316

14-
# DescribeCoverage 2.0
15-
http://localhost:7000/?map=/etc/mapserver/wcs.map&SERVICE=WCS&VERSION=2.0.1&REQUEST=DescribeCoverage&COVERAGEID=dtm
17+
- [GetCapabilities](http://localhost:7000/?map=/etc/mapserver/wcs.map&SERVICE=WCS&REQUEST=GetCapabilities&VERSION=2.0.1)
18+
- [DescribeCoverage 2.0](http://localhost:7000/?map=/etc/mapserver/wcs.map&SERVICE=WCS&VERSION=2.0.1&REQUEST=DescribeCoverage&COVERAGEID=dtm)
19+
- [GetCoverage 2.0 image/tiff full](http://localhost:7000/?map=/etc/mapserver/wcs.map&SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCoverage&COVERAGEID=dtm&FORMAT=image/tiff)
1620

17-
# GetCoverage 2.0 image/tiff full
18-
http://localhost:7000/?map=/etc/mapserver/wcs.map&SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCoverage&COVERAGEID=dtm&FORMAT=image/tiff
21+
You can also connect to the MapServer Docker container and use `mapserv` to test the requests from the command line.
1922

2023
```bash
21-
gdalinfo /etc/mapserver/data/raster/54752_dtm_1m.tif
24+
docker exec -it mapserver bash
25+
mapserv -nh "QUERY_STRING=map=/etc/mapserver/wcs.map&SERVICE=WCS&REQUEST=GetCapabilities&VERSION=2.0.1"
2226
```
2327

24-
NoData Value=-9999
25-
Size is 5002, 5002
28+
## Source Dataset
2629

27-
curl "http://localhost:7000/?map=/etc/mapserver/wcs.map&SERVICE=WCS&REQUEST=GetCapabilities"
30+
Let's get some information about the source dataset using the GDAL CLI command[gdal raster info](https://gdal.org/en/latest/programs/gdal_raster_info.html)
31+
(the modern equivalent of `gdalinfo`).
2832

29-
!!! tip
33+
```bash
34+
gdal raster info /etc/mapserver/data/raster/54752_dtm_1m.tif
35+
```
3036

31-
The `COVERAGEID` will be the `LAYER` `NAME`
37+
The truncated output is shown below.
3238

39+
```
40+
Driver: GTiff/GeoTIFF
41+
Files: /etc/mapserver/data/raster/54752_dtm_1m.tif
42+
/etc/mapserver/data/raster/54752_dtm_1m.tif.aux.xml
43+
Size is 5000, 5000
44+
Coordinate System is:
45+
PROJCRS["Estonian Coordinate System of 1997",
46+
...
47+
ID["EPSG",3301]]
48+
Origin = (655000.000000000000000,6475000.000000000000000)
49+
Pixel Size = (1.000000000000000,-1.000000000000000)
50+
...
51+
Image Structure Metadata:
52+
LAYOUT=COG
53+
...
54+
Center ( 657500.000, 6472500.000) ( 26d41'30.35"E, 58d21'52.44"N)
55+
Band 1 Block=512x512 Type=Float32, ColorInterp=Gray
56+
Min=30.680 Max=83.205
57+
Minimum=30.680, Maximum=83.205, Mean=60.461, StdDev=9.347
58+
NoData Value=-9999
59+
Overviews: 2500x2500, 1250x1250, 625x625, 312x312
60+
Metadata:
61+
STATISTICS_MAXIMUM=83.205001831055
62+
...
63+
```
3364

65+
From the output we can see that the dataset is in the [EPSG:3301](https://spatialreference.org/ref/epsg/3301/) coordinate reference system, with an origin at (655000, 6475000)
66+
and a pixel size of 1 × 1 (with a negative Y resolution, as is typical for north-up rasters). The `LAYOUT=COG` indicates that the file is structured as a Cloud-Optimized GeoTIFF (COG).
67+
68+
## Configuring a Mapfile for WCS
69+
70+
The Mapfile for the WCS service is similar to a WMS Mapfile, but with some differences. The `LAYER` type is set to `RASTER`, and the `METADATA` section contains
71+
keywords prefixed with `wcs_` to specify the WCS parameters.
72+
73+
```scala
74+
WEB
75+
METADATA
76+
"wcs_enable_request" "*"
77+
"wcs_srs" "EPSG:4326 EPSG:3857"
78+
"wcs_title" "Example WCS Mapfile"
79+
"wcs_description" "Test description"
80+
"wcs_onlineresource" "http://localhost:7000/"
81+
END
82+
END
3483
```
35-
msWCSGetCoverage20(): WCS server error. Raster size out of range, width and height of resulting coverage must be no more than MAXSIZE=4096.
84+
85+
If the Mapfile is used for multiple services such as WMS and WCS, a single metadata item can be specified using the `ows_` prefix, for example `ows_title`.
86+
87+
[LAYER METADATA](https://mapserver.org/ogc/wcs_server.html#layer-object-metadata) can also be used to specify additional information about the coverage,
88+
but is not required for this tutorial.
89+
90+
The [OUTPUTFORMAT](https://mapserver.org/mapfile/outputformat.html) defines the properties of the output format.
91+
In this case we are defining a custom output format for GeoTIFFs with a `FLOAT32` data type to match the source raster
92+
and ensure that the full precision of the source raster is preserved in WCS responses.
93+
94+
```scala
95+
OUTPUTFORMAT
96+
NAME "GEOTIFF"
97+
DRIVER "GDAL/GTiff"
98+
MIMETYPE "image/tiff"
99+
IMAGEMODE FLOAT32
100+
EXTENSION "tif"
101+
END
36102
```
37103

38-
Set the [MAXSIZE](https://mapserver.org/mapfile/map.html#mapfile-map-maxsize) directive on the `MAP` to a larger value. By default this is set to 4096.
104+
We can use the full power of GDAL to define custom output formats. For example, we could define a COG output format
105+
by switching to the [COG Driver](https://gdal.org/en/latest/drivers/raster/cog.htm), and add statistics to the output file by adding the `STATISTICS=YES` format option:
106+
107+
```scala
108+
OUTPUTFORMAT
109+
NAME "GEOTIFF_COG"
110+
DRIVER "GDAL/COG"
111+
MIMETYPE "image/tiff"
112+
IMAGEMODE FLOAT32
113+
EXTENSION "tif"
114+
FORMATOPTION "STATISTICS=YES"
115+
END
116+
```
117+
118+
## Requesting a WCS in OpenLayers
39119

40-
[WCS and NULL Values](https://github.com/geographika/wcs-test)
120+
Typically WCS requests are made from client applications such as QGIS, ArcGIS Pro, or custom JS code in web applications to download the raw raster data,
121+
rather than to display it as a map image. However, for the purposes of this tutorial we will be using OpenLayers to make requests to the WCS and display the results.
122+
WCS is not natively supported in OpenLayers, but we can use the [ImageWMS](https://openlayers.org/en/latest/apidoc/module-ol_source_ImageWMS.html) source as a workaround
123+
by overriding the request parameters to call WCS, and display the results as an image layer on the map.
124+
125+
!!! tip
41126

127+
The `COVERAGEID` corresponds to the MapServer `LAYER` `NAME`
42128

129+
## Code
130+
131+
??? JavaScript "wcs.js"
132+
133+
```js
134+
--8<-- "wcs.js"
135+
```
136+
137+
??? Mapfile "wcs.map"
138+
139+
``` scala title="wcs.map"
140+
--8<-- "wcs.map"
141+
```
142+
143+
## Exercises
144+
145+
1. From the command line, test the WCS 2.0.1 protocol by making a `GetCoverage` request and saving the output as a GeoTIFF using the configured `OUTPUTFORMAT` (MapServer format name, not a MIME type).
146+
Then use `gdal raster info` to check the output file.
147+
148+
```
149+
mapserv -nh "QUERY_STRING=map=/etc/mapserver/wcs.map&SERVICE=WCS&VERSION=2.0.1&REQUEST=GetCoverage&COVERAGEID=dtm&FORMAT=GEOTIFF&SUBSETTINGCRS=http://www.opengis.net/def/crs/EPSG/0/4326&SUBSET=x(26.6507,26.7362)&SUBSET=y(58.3414,58.3879)&SCALESIZE=x(400),y(400)" \
150+
> output.tif
151+
gdal raster info output.tif
43152
```
44-
mapserv -nh "QUERY_STRING=map=test.map&SERVICE=WCS&VERSION=2.0.0&REQUEST=GetCoverage&CoverageId=test&FORMAT=GEOTIFF_INT16&BBOX=-69.955,3.420,-69.701,3.5896&CRS=EPSG:4326&WIDTH=500&HEIGHT=500" > output2.tif
45-
gdalinfo output.tif
153+
154+
2. Add the COG output format to the Mapfile and make a `GetCoverage` request to download a COG-formatted output. Check the output file with `gdal raster info` to see the difference in metadata.
155+
156+
3. Update the JavaScript code to test the WCS 1.0.0 protocol. This requires different parameters to be passed in the requests,
157+
for example `COVERAGEID` becomes `COVERAGE`, and the CRS parameters are different. You can also remove the entire `imageLoadFunction` as WCS 1.0.0
158+
more closely matches the WMS protocol, using `BBOX`,`WIDTH`, and `HEIGHT` parameters to specify the area and size of the output image.
159+
160+
```js
161+
params: {
162+
SERVICE: 'WCS',
163+
VERSION: '1.0.0',
164+
REQUEST: 'GetCoverage',
165+
FORMAT: 'image/png',
166+
COVERAGE: 'dtm',
167+
CRS: 'EPSG:3857',
168+
RESPONSE_CRS: 'EPSG:3857',
169+
},
170+
```
171+
172+
## Possible Errors
173+
46174
```
175+
msWCSGetCoverage20(): WCS server error. Raster size out of range, width and height of resulting coverage must be no more than MAXSIZE=4096.
176+
```
177+
178+
Set the [MAXSIZE](https://mapserver.org/mapfile/map.html#mapfile-map-maxsize) directive in the `MAP` to a larger value. By default, this is set to 4096.
179+
180+
## Further Reading
47181

48-
<!--
49-
https://openlayers.org/workshop/en/cog/
50-
-->
182+
- [MapServer WCS Use Cases](https://mapserver.org/ogc/wcs_format.html)
183+
- [WCS and NULL Values](https://github.com/geographika/wcs-test)

workshop/content/mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ nav:
2525
- Outputs:
2626
- WMS: outputs/wms.md
2727
- WFS: outputs/wfs.md
28+
- WCS: outputs/wcs.md
2829
- Tiles: outputs/tiles.md
2930
- Vector Tiles: outputs/vector-tiles.md
3031
- OGC API - Features: outputs/ogcapi-features.md
@@ -35,6 +36,7 @@ nav:
3536
- Clusters: advanced/clusters.md
3637
- SLD: advanced/sld.md
3738
- STAC: advanced/stac.md
39+
# - WCS and non-EPSG Projections: advanced/wcs-projections.md
3840
# - Apache: advanced/apache.md
3941
# - MapScript: advanced/mapscript.md
4042
- Summary: summary.md

workshop/exercises/app/index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ <h2>Inputs</h2>
2222
<li><a href="raster.html">Raster</a></li>
2323
<li><a href="stars.html">Stars (Vector)</a></li>
2424
<li><a href="postgis.html">PostGIS (Databases)</a></li>
25-
</ul>
25+
</ul>
2626
<h2>Outputs</h2>
2727
<ul>
2828
<li><a href="wfs.html">WFS</a></li>
29+
<li><a href="wcs.html">WCS</a></li>
2930
<li><a href="tiles.html">Tiles Mode</a></li>
3031
<li><a href="vector-tiles.html">Vector Tiles</a></li>
3132
<li><a href="ogcapi-features.html">OGC API - Features</a></li>
@@ -36,10 +37,12 @@ <h2>Advanced</h2>
3637
<li><a href="gdalg.html">GDAL Pipelines</a></li>
3738
<li><a href="railways.html">Vector Symbols (Railways)</a></li>
3839
<li><a href="clusters.html">Clusters</a></li>
40+
<!-- <li><a href="direction.html">Direction</a></li> -->
3941
<li><a href="landuse.html">Landuse</a></li>
4042
<li><a href="contours.html">Contours</a></li>
4143
<li><a href="sld.html">SLD - Styled Layer Descriptor</a></li>
4244
<li><a href="stac.html">STAC</a></li>
45+
<!-- <li><a href="wcs-projections.html">STAC</a></li> -->
4346
</ul>
4447
<h2>Miscellaneous</h2>
4548
<ul>

workshop/exercises/app/js/wcs.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import '../css/style.css';
2+
import Map from 'ol/Map';
3+
import View from 'ol/View';
4+
import ImageLayer from 'ol/layer/Image';
5+
import ImageWMS from 'ol/source/ImageWMS';
6+
7+
const mapserverUrl = import.meta.env.VITE_MAPSERVER_BASE_URL;
8+
const mapfilesPath = import.meta.env.VITE_MAPFILES_PATH;
9+
const url = mapserverUrl + mapfilesPath + 'wcs.map';
10+
11+
const wcsSource = new ImageWMS({
12+
url,
13+
params: {
14+
SERVICE: 'WCS',
15+
VERSION: '2.0.1',
16+
REQUEST: 'GetCoverage',
17+
FORMAT: 'image/png',
18+
COVERAGEID: 'dtm',
19+
SUBSETTINGCRS: 'http://www.opengis.net/def/crs/EPSG/0/3857',
20+
OUTPUTCRS: 'http://www.opengis.net/def/crs/EPSG/0/3857',
21+
},
22+
projection: 'EPSG:3857',
23+
imageLoadFunction: (image, src) => {
24+
const srcUrl = new URL(src);
25+
const params = srcUrl.searchParams;
26+
27+
// Get the ImageWMS params
28+
const bbox = params.get('BBOX').split(',');
29+
const width = params.get('WIDTH');
30+
const height = params.get('HEIGHT');
31+
32+
// Replace with WCS 2.0.1 equivalents
33+
params.append('SUBSET', `x(${bbox[0]},${bbox[2]})`);
34+
params.append('SUBSET', `y(${bbox[1]},${bbox[3]})`);
35+
params.set('SCALESIZE', `x(${width}),y(${height})`);
36+
37+
// Remove the WMS params
38+
params.delete('BBOX');
39+
params.delete('WIDTH');
40+
params.delete('HEIGHT');
41+
params.delete('CRS');
42+
43+
image.getImage().src = srcUrl.toString();
44+
},
45+
ratio: 1,
46+
});
47+
48+
const map = new Map({
49+
target: 'map',
50+
layers: [
51+
new ImageLayer({
52+
source: wcsSource
53+
}),
54+
],
55+
view: new View({
56+
projection: 'EPSG:3857',
57+
center: [2975862, 8046369],
58+
zoom: 14,
59+
}),
60+
});
61+
62+
// apply CSS filters to enhance the contrast in the DTM
63+
map.once('rendercomplete', () => {
64+
document.querySelector('.ol-layer canvas').style.filter =
65+
'brightness(2.2) contrast(2) sepia(1) hue-rotate(90deg) saturate(3)';
66+
});

workshop/exercises/app/wcs.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/x-icon" href="https://openlayers.org/favicon.ico" />
6+
<link rel="stylesheet" href="node_modules/ol/ol.css">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8+
<title>WCS</title>
9+
</head>
10+
<body>
11+
<div id="map"></div>
12+
<script type="module" src="./js/wcs.js"></script>
13+
</body>
14+
</html>

0 commit comments

Comments
 (0)