A Guide for Developers Traveling Through OpenLayers - 16. Displaying Images on the Map Using WMS GetImage
A Guide for Developers Traveling Through OpenLayers - 16. Displaying Images on the Map Using WMS GetImage
This chapter covers how to display images on the map via WFS.
While WFS in the previous chapter received spatial data as GeoJSON and displayed it directly as objects, WMS receives an image that GeoServer has rendered from objects and displays that.
In other words, you can think of it as receiving a Tile Map directly from GeoServer and rendering it.
To display WMS, a total of 4 objects are needed. Since WMS receives and displays images, we need ImageWMS to hold the image, and ImageLayer to render it onto the map using that image. The remaining View and Map objects are also required.
We'll explain how to implement each of these 4 elements in order, ultimately creating a map that uses WMS.
We call the WMS image via the data we built through GeoServer.
Among the WMS operations, we use GetImage, which provides attribute information. The request method for GetImage is as follows.
TXT
GET https://example.com/geoserver/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&layers=test:building&exceptions=application%2Fjson&WIDTH=256&HEIGHT=256&CRS=EPSG%3A3857&STYLES=&BBOX=14168061.814827133%2C4367306.048101831%2C14168367.562940273%2C4367611.796214972
| Parameter | Example | Require | Description |
|---|---|---|---|
| service | WMS (fixed) | Y | Service name |
| version | 1.3.0 (fixed), 1.1.1, 1.1.0, 1.0.0 | Y | Version |
| request | GetMap (fixed) | Y | Request name |
| layers | repo_name:layer_name | Y | Layer name (separate multiple with commas) |
| styles | style1 | Style name to apply (if empty, applies the default style set in GeoServer; multiple separated by commas) | |
| srs(or crs) | EPSG:4326 | Reference coordinate system (if empty, uses the layer's default CRS) | |
| bbox | Y | Image extent coordinates | |
| width | 256 | Y | Image width |
| height | 256 | Y | Image height |
| format | image/png | Y | Request name |
| transparent | false (default) | Whether the background is transparent | |
| bgcolor | FFFFFF (default) | Background color in RRGGBB format | |
| exceptions | application/vnd.ogc.se_xml (default) | Exception response format | |
| time | 2022-03-14T22:30.27.520+09:00 | Time for time-series data (yyyy-MM-ddThh:mm:ss.SSSZ) | |
| sld | https://example.com/sld.xml | Path to the XML file | |
| sld_body | SLD XML |
However, compared to WFS, the WMS URL requires more parameters, making it somewhat more complex. In WFS, we entered the URL directly into VectorSource, but with WMS's source object, ImageWMS, if you provide a few required values, it will build and call the URL automatically.
For now, just understand that WMS is called directly in this manner, and move on.
OpenLayers' ImageWMS object generates and calls a WMS URL from the given values.
TYPESCRIPT
import { ImageWMS } from 'ol/source'; const source = new ImageWMS({ url: 'https://example.com/geoserver/wms', params: { layers: 'test:building', exceptions: 'application/json' }, serverType: 'geoserver' });
The complete information for ImageWMS can be found in the official documentation.
The details of the configuration are as follows.
| Name | Type | Default | Description |
|---|---|---|---|
| attributions | ol/source/Source-AttributionLike | undefined | Attribution text (bottom right of the map) | |
| crossOrigin | null | string | undefined | The image's CORS attribute (reference) | |
| hidpi | boolean | true | When requesting WMS from a remote server, uses the Map object's pixelRatio value |
| serverType | ol/source/WMSServerType | string | undefined | Type of the WMS server (mapserver, geoserver, qgis, etc.) Only needed when hidpi is true | |
| imageLoadFunction | ol/Image-LoadFunction | undefined | true | Method to load the WMS URL image Used when overriding the WMS call method |
| boolean | true | A deprecated property; using interpolate is recommended | |
| interpolate | boolean | true | Whether to use interpolated values during resampling |
| params | object | WMS request parameters. At least one LAYERS must be provided STYLES defaults to an empty value '' VERSION defaults to 1.3.0 WIDTH, HEIGHT, BOX, CRS(SRS) are set dynamically | |
| projection | ol/proj-ProjectionLike | undefined | Projection object. Defaults to the projection declared on the Map's View object | |
| ratio | number | 1.5 | Size of the viewport used when requesting images. 1 is the same as the map viewport, and 2 means twice the map viewport's width and height Must be 1 or greater |
| resolutions | Array<number> | undefined | Resolutions. If specific resolutions are set, calls only occur at those resolutions | |
| url | string | WMS URL |
url and params.layers must both be provided to perform a valid WMS call. If you provide just these two values, ImageWMS will calculate and fill in the remaining parameters needed for WMS on its own.
OpenLayers' TileLayer object renders the map using the image called via ImageWMS. This map consists of an image, similar to a basemap.
TYPESCRIPT
import ImageLayer from 'ol/layer/Image'; const layer = new TileLayer({ source: source, minZoom: 15, properties: { name: 'wms' }, zIndex: 5 });
| Name | Type | Default | Description |
|---|---|---|---|
| className | string | ol-layer | Class name |
| opacity | number | 1 | Opacity (0 ~ 1) |
| visible | boolean | true | Whether visible |
| extent | ol/extent-Extent | undefined | The layer's rendering extent. Data outside this extent isn't shown | |
| zIndex | number | undefined | Priority (higher shows on top) | |
| minResolution | number | undefined | Minimum display resolution | |
| maxResolution | number | undefined | Maximum display resolution | |
| minZoom | number | undefined | Minimum display zoom level | |
| maxZoom | number | undefined | Maximum display zoom level | |
| map | ol/PluggableMap-PluggableMap | undefined | Uses the corresponding layer as an overlay in the specified Map object | |
| source | (ol/source/Image-ImageSource | ol/source/VectorTile-VectorTile) | undefined | The layer's source | |
| properties | object | undefined | Arbitrary properties. Can be manipulated with get(), set() |
The complete information for ImageLayer can be found at ol/layer/Image-ImageLayer.
We create a View object to declare the map's viewing information.
TYPESCRIPT
import View from 'ol/View'; import proj4 from 'proj4'; const view = new View({ projection: 'EPSG:3857', center: proj4('EPSG:4326', 'EPSG:3857', [ 127.28923267492068, 36.48024986578043 ]), zoom: 17 });
| Name | Type | Default | Description |
|---|---|---|---|
| center | ol/coordinate-Coordinate | undefined | Center of the map | |
| constrainRotation | boolean | number | true | Whether to constrain rotation. If a number, indicates the number of allowed rotation steps (0 means 90, 180, 270, 360) |
| enableRotation | boolean | true | Whether rotation is enabled |
| extent | ol/extent-Extent | undefined | The map's viewing extent. Cannot go beyond the specified extent | |
| constrainOnlyCenter | boolean | false | If true, the extent constraint is applied only to the View's center, not the entire extent |
| smoothExtentConstraint | boolean | true | Whether the View may slightly go outside the extent range |
| maxResolution | number | undefined | Maximum viewing resolution. Cannot zoom in beyond the specified resolution. | |
| minResolution | number | undefined | Minimum viewing resolution. Cannot zoom out beyond the specified resolution. | |
| maxZoom | number | 28 | Maximum viewing zoom level. Cannot zoom in beyond the specified zoom level. |
| minZoom | number | 0 | Minimum viewing zoom level. Cannot zoom out beyond the specified zoom level. |
| multiWorld | boolean | false | Whether to allow multiple worlds |
| constrainResolution | boolean | false | Whether to allow only integer zoom levels |
| smoothResolutionConstraint | boolean | true | Whether to use loose zoom-in/zoom-out rules |
| showFullExtent | boolean | false | Whether to show the full configured extent |
| projection | ol/proj-ProjectionLike | EPSG:3857 | Coordinate system |
| resolution | number | undefined | Initial resolution | |
| resolutions | Array<number> | undefined | List of available resolutions (descending order). Ignores the max/minResolution, max/minZoom, zoomFactor options | |
| rotation | number | 0 | Default rotation value |
| zoom | number | undefined | Default zoom level | |
| zoomFactor | number | 2 | Zoom factor |
| padding | Array<number> | [ 0, 0, 0, 0 ] | Padding |
WMS can also style the elements drawn in the image.
While WFS could describe style objects directly in the code, WMS is fundamentally handled on the backend side, like GeoServer, so the server itself describes the style directly.
It's usually described in XML form, which is called SLD.
For GeoServer, you can manage this in the [Styles] menu. If you haven't made any changes, several ready-to-use SLDs are loaded by default.
When adding a layer, you can configure the style during the [Publish] step, and what you set there is the SLD used for WMS.
You can also register multiple styles, in which case you can call the desired style by the name specified in STYLES. The default value of STYLES is an empty '', and in that case the specified default style is used to render the image.
XML
<?xml version="1.0" encoding="UTF-8"?> <StyledLayerDescriptor version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <!-- a Named Layer is the basic building block of an SLD document --> <NamedLayer> <Name>default_polygon</Name> <UserStyle> <!-- Styles can have names, titles and abstracts --> <Title>Default Polygon</Title> <Abstract>A sample style that draws a polygon</Abstract> <!-- FeatureTypeStyles describe how to render different features --> <!-- A FeatureTypeStyle for rendering polygons --> <FeatureTypeStyle> <Rule> <Name>rule1</Name> <Title>Gray Polygon with Black Outline</Title> <Abstract>A polygon with a gray fill and a 1 pixel black outline</Abstract> <PolygonSymbolizer> <Fill> <CssParameter name="fill">#ED143D</CssParameter> <CssParameter name="opacity">0.6</CssParameter> </Fill> <Stroke> <CssParameter name="stroke">#ED143d</CssParameter> <CssParameter name="stroke-width">2</CssParameter> </Stroke> </PolygonSymbolizer> <TextSymbolizer> <Geometry> <ogc:Function name="centroid"> <ogc:PropertyName>SHAPE</ogc:PropertyName> </ogc:Function> </Geometry> <Label> <ogc:PropertyName>buld_nm</ogc:PropertyName> </Label> <Font> <CssParameter name="font-family">sans-serif</CssParameter> <CssParameter name="font-size">16</CssParameter> </Font> <LabelPlacement> <PointPlacement> <AnchorPoint> <AnchorPointX>0.5</AnchorPointX> <AnchorPointY>0.5</AnchorPointY> </AnchorPoint> <Displacement> <DisplacementX>0</DisplacementX> <DisplacementY>0</DisplacementY> </Displacement> </PointPlacement> </LabelPlacement> <Halo> <Radius> <ogc:Literal>2</ogc:Literal> </Radius> <Fill> <CssParameter name="fill">#000000</CssParameter> </Fill> </Halo> <Fill> <CssParameter name="fill">#FFFFFF</CssParameter> </Fill> </TextSymbolizer> </Rule> </FeatureTypeStyle> </UserStyle> </NamedLayer> </StyledLayerDescriptor>
The SLD above is actually the SLD used in the project's WMS request. If you've read the section on style description in WFS, this will be easier to understand. Due to the nature of XML it looks complex, but once you break it down, there isn't much to it.
Likewise, the way it's described differs slightly by data type, such as Point and Polygon.
There's also a way to use the design you want without necessarily describing the SLD in GeoServer, which is possible by using the sld_body WMS request parameter.
If you enter the SLD directly into sld_body, that SLD is applied with priority. The parameter's content is the SLD itself.
We create a Map object that combines all the information to build the map.
TYPESCRIPT
import Map from 'ol/Map'; import { ImageWMS } from 'ol/source'; import ImageLayer from 'ol/layer/Image'; import View from 'ol/View'; import proj4 from 'proj4'; // WMS source object const source = new ImageWMS({ url: 'https://example.com/geoserver/wms', params: { layers: 'test:building', exceptions: 'application/json' }, serverType: 'geoserver' }); // WMS layer object const layer = new ImageLayer({ source: source, minZoom: 15, properties: { name: 'wms' }, zIndex: 5 }); // View object const view = new View({ projection: 'EPSG:3857', center: proj4('EPSG:4326', 'EPSG:3857', [ 127.28923267492068, 36.48024986578043 ]), zoom: 17 }); // Map object const map = new Map({ layers: [ vworldBaseLayer, vworldHybridLayer, wfsLayer ], target: 'map', view: view });
| Name | Type | Default | Description |
|---|---|---|---|
| controls | ol/Collection-Collection<ol/control/Control-Control> | Array<ol/control/Control-Control> | undefined | ol/control/defaults | Map control object |
| pixelRatio | number | window.devicePixelRatio | Device pixel ratio |
| interactions | ol/Collection-Collection<ol/interaction/Interaction-Interaction> | Array<ol/interaction/Interaction-Interaction> | undefined | ||
| keyboardEventTarget | HTMLElement | Document | string | undefined | Target element for keyboard events | |
| layers | Array<ol/layer/Base-BaseLayer> | ol/Collection-Collection<ol/layer/Base-BaseLayer> | ol/layer/Group-LayerGroup | undefined | List of layers. Later entries in the array have higher priority | |
| maxTilesLoading | number | 16 | Maximum number of tiles that can load simultaneously |
| moveTolerance | number | 1 | Minimum pixels the mouse must move to be recognized as a map move event |
| overlays | ol/Collection-Collection<ol/Overlay-Overlay> | Array<ol/Overlay-Overlay> | undefined | Map overlay object | |
| target | HTMLElement | string | undefined | DOM or DOM ID where the map will be displayed | |
| view | ol/View-View | Promise<ol/View-View> | undefined | The map's view object |
We assign the objects declared so far to the Map object. The declared map is displayed in the DOM specified by target.
target: map means displaying the map in the DOM with the ID map. You can also assign an HTMLElement directly, not just an ID.
You can confirm that the image called via WMS is displayed on the map.
OpenLayers provides two ways to call WMS: the Image method, which fetches the entire image of the current extent, and the Tile method, which splits it into multiple grids and fetches those.
This chapter describes the Image method, which calls the entire image of the current extent.
If you'd like to call WMS in a tiled form, you can use TileWMS and TileLayer. The usage is the same. Based on the code described in this chapter, simply changing ImageWMS to TileWMS works without any issues. The same goes for TileLayer.
The difference between the two methods is illustrated in the diagram above. The basemap also uses the TileWMS method.
-
Image method
- Since the WMS call happens only once, the number of requests can be reduced.
- The size of a single response is relatively large, and it is slower.
-
Tile method
- There are far more WMS call requests compared to the Image method.
- Since it calls multiple smaller images, it is relatively faster.
Check the differences and adopt whichever method is more suitable for your service.
The basemap is also essentially a kind of TileWMS.
You can check an example implementing this at OpenLayers6 Sandbox - WMS.
You can confirm that OpenLayers renders the map by calling spatial data through GeoServer.
