A Guide for Developers Traveling Through OpenLayers - 18. Attaching a Popup to WFS
A Guide for Developers Traveling Through OpenLayers - 18. Attaching a Popup to WFS
When you click a marker or object displayed on the map, a popup shows detailed information about that object. In this chapter, we'll display a popup on a WFS map to show a marker's detailed information.
We use the WFS map covered in Chapter 17 as-is. Since the Select object is included, we can visually show the user that interaction is possible.
OpenLayers can display any HTML tag on top of the map through the Overlay object. Although OpenLayers handles the behavior, it isn't an object rendered onto the canvas; it outputs an actual DOM element.
By default, Overlay follows the position clicked on the map. In other words, if you display an Overlay at a specific position on the map and then move the map, the position isn't fixed — it follows the marker. Handling this kind of behavior directly in the DOM would require quite cumbersome handling. Thanks to this characteristic, it's a very good fit for popups.
Let's create the Overlay object directly.
| Parameter | Type | Default | Description |
|---|---|---|---|
| id | number | string | undefined | Overlay ID Used when calling the ol/Map-Map#getOverlayById method | |
| element | HTMLElement | undefined | The target Element for the overlay | |
| offset | Array<number> | [ 0, 0 ] | Offset (px) for the overlay's display [ x, y ], where x is horizontal and y is vertical. Larger values move it right and down, respectively. |
| position | ol/events/condition-Condition | undefined | Display position of the overlay | |
| positioning | ol/OverlayPositioning | top-left | Placement anchor of the overlay Values such as bottom-left, top-right, etc. |
| stopEvent | boolean | true | Whether to stop event propagation If true, placed in a DOM with the class ol-overlay container-stopevent If false, placed in a DOM with the class specified in the className property |
| insertFirst | boolean | true | Whether to insert the overlay first or append it to the element |
| autoPan | ol/Overlay-PanIntoViewOptions | boolean | false | When setPosition is called on the overlay, automatically pans the map so the overlay is fully visible |
| autoPanAnimation | ol/Overlay-PanOptions | Animation settings for the pan triggered by autoPan. Ignored if the autoPan setting is not an object but a boolean | |
| autoPanMargin | number | 20 | Margin between the overlay and the map edge during the pan triggered by autoPan Ignored if the autoPan setting is not an object but a boolean |
| autoPanOptions | ol/Overlay-PanIntoViewOptions | undefined | Options for autoPan Takes precedence over autoPanAnimation and autoPanMargin | |
| className | string | ol-overlay-container ol-selectable | CSS class value |
The way to create one is as follows.
HTML
<div id="map-popup"></div>
Enter the DOM element to use for the popup. Use the id or class attribute appropriately to identify the DOM. The position of the tag within the HTML code doesn't matter much.
TYPESCRIPT
import { Overlay } from 'ol'; const popup = document.getElementById('map-popup') as HTMLElement | null; const overlay = new Overlay({ id: 'popup', element: popup || undefined, positioning: 'center-center', autoPan: { animation: { duration: 250 } } });
Once you assign the tag and configure it appropriately, you can create the Overlay object.
The complete information for Overlay can be found in the official documentation.
Let's apply the created Overlay to the Map.
TYPESCRIPT
import { Map } from 'ol'; const map = new Map({ // ... overlays: [ overlay ] // ... });
You can apply it as shown above. You can register Overlays in the form of an array.
TYPESCRIPT
// Add an overlay map.addOverlay(); // Return an overlay by ID map.getOverlayById(); // Return the list of overlays map.getOverlays(); // Remove an overlay map.removeOverlay();
The related methods are as above. They can be called on the created Map object.
Now that we've registered the overlay, we can display it and show the desired data through appropriate event handling.
TSX
map.on('singleclick', (e) => { // If there's an object at that pixel if (map.hasFeatureAtPixel(e.pixel)) { map.forEachFeatureAtPixel(e.pixel, feature => { // If the object's ID starts with buld_sejong if (feature.getId()?.toString().startsWith('buld_sejong')) { const geom = feature.getGeometry(); // If the spatial information is valid if (geom) { const [ minX, minY, maxX, maxY ] = geom.getExtent(); setPopupState(( <ul> <li>{feature.getId() || ''}</li> <li>{feature.get('buld_nm') || <span>No name</span>}</li> <li>{feature.get('bul_man_no')}</li> </ul> )); overlay.setPosition([ (maxX + minX) / 2, (maxY + minY) / 2 ]); } } }); } // If there isn't one else { overlay.setPosition(undefined); } });
We make appropriate use of the click event as shown above.
- On click, check whether a Feature exists at that pixel using the hasFeatureAtPixel method.
- If not, hide the overlay with overlay.setPosition(undefined).
- Retrieve all Features located at that pixel using the forEachFeatureAtPixel method.
- Among them, check the data of the Feature we need.
- In this text, the target is Features whose ID starts with buld_sejong.
- Display the desired data in the DOM.
- In this text, state-based data management is used.
- Call feature.getGeometry() to retrieve the geometry information and calculate the overlay's position.
- In this text, the center value of the Feature's extent is used.
- Display the overlay at the desired position in the form overlay.setPosition([ x, y ]).
The logic proceeds as described above. Of course this is just one example of usage, so you can describe whatever behavior you want in the event to perform various actions.
As a side note, when hovering the mouse pointer over a Feature, you can set the mouse cursor shape to pointer to visually indicate to the user that interaction is possible.
TYPESCRIPT
map.on('pointermove', (e) => map.getViewport().style.cursor = map.hasFeatureAtPixel(e.pixel) ? 'pointer' : '');
Through the pointermove event, if there's even one Feature at the current pixel, the cursor CSS is changed to pointer.
You can check an example implementing this at OpenLayers6 Sandbox - WFS Popup.
