OpenLayers Widget¶
The OpenLayers Map Widget wraps OpenLayers version 4.6.5 to create dynamic maps and represent geographical data.
Configuration¶
The following table illustrates the configuration properties available (required properties are marked in bold).
Property | JSON Key | Default | Description |
---|---|---|---|
Center | center.x, center.y | X and Y coordinates to center the map on | |
Zoom | zoom | Zoom level (0 is zoomed out) | |
Data Source | dataSource | Name of the Data Source providing data for the overlays | |
Layers | layers | One OSM layer | Array of layers that will be added to the map (more details below). |
Overlay Groups | overlayGroups | Groups of overlays that will be displayed over the map, attached to a given position (more details below) | |
Controls | controls | Array of controls that will be added to the map (more details below) | |
DataSource Mapping | dataSourceMapping | Mapping between overlay properties and Data Source fields (more details below) | |
Specific Events | specificEvents | Array of parameters (defined in the Parameters section) and widget events that can trigger their change (more details below) | |
Projection | projection | EPSG:3857 |
Projection |
Layers¶
Layers property defines the layers that will be added to the OpenLayers map. Every layer must have a source, except VectorTile layers. Note that although OpenLayers provides many optional properties to configure layers, the current implementation of the OpenLayers Map Widget does not support all of them. If no layer type nor source are specified, a single Tile layer with an OSM (Open Street Map) source will be added to the map.
The following table lists which sources are valid for each type of layer:
Type | Source | Configuration Required | Notes |
---|---|---|---|
Image | ImageArcGISRest | No | |
ImageCanvas | Yes | ||
ImageMapGuide | No | ||
ImageStatic | Yes | ||
ImageWMS | Yes | ||
Raster | Yes | ||
Tile | BingMaps | Yes | |
CartoDB | Yes | ||
OSM | No | ||
Stamen | Yes | ||
TileArcGISRest | No | ||
TileDebug | Yes | ||
TileJSON | Yes | ||
TileUTFGrid | Yes | ||
TileWMS | Yes | ||
WMTS | Yes | ||
XYZ | No | ||
Zoomify | Yes | ||
Heatmap | Cluster | Yes | Requires Weight property |
Vector | No | Requires Weight property | |
Vector | Cluster | Yes | Can use Style function property |
Vector | No | Can use Style function property | |
VectorTile | VectorTile | No |
The configuration of sources is described in the documentation of OpenLayers API. Note that the Configuration property can contain JavaScript code (see examples).
Overlay Groups¶
Overlays are elements that will be displayed over the map and attached to a given position. Each of them can have its own style, positioning and content. They can be interactive, that is, you can click on them and use Parameters to store the Name of the selected overlays (see Specific Events).
Every overlay must be part of a group, even if there is only one group on the map, and each group of overlays can have one overlay selected at a time. Let’s say you want to place some red circles on cities and change them to blue when you click on them: if you want to have only one blue city at a time, you can have all the overlays in one group; if you want to have any number of blue cities at a time, you must add every city in a different group.
Note that overlays within the same group have their own template and style. The concept of group is only related to selection.
Each group has the following properties (required properties are marked in bold):
Property | JSON Key | Description |
---|---|---|
Name | name | Unique identifier for the group |
Overlay Initially Selected | initiallySelected | Name of the overlay that will be assigned the CSS Class On Selection when the page is loaded. If not provided, all overlays will have the regular CSS Class until one is clicked. To use this field, you must provide names for the overlays. |
Overlays | overlays | Array of overlays |
Each overlay has the following properties (required properties are marked in bold):
Property | JSON Key | Default | Description |
---|---|---|---|
Name | name | Unique identifier for the overlay | |
CSS Class | cssClass | CSS class name (defined beforehand in the Styles section of the dashboard) | |
CSS Class On Selection | cssClassSelected | Optional CSS class name (defined beforehand in the Styles section of the dashboard) to apply when the overlay is selected | |
Position | position | X and Y coordinates where the overlay will be attached | |
Positioning | positioning | top-left |
Where the overlay is placed with respect to Position |
Template | template | HTML template for overlay content |
Note that Name property is not required, but it will be generated automatically if not provided, therefore if you want it to be stored in a Parameter you will not recognize which overlay the name belongs to if you do not provide it.
Instead of configuring the overlays in the Overlay Groups property, it is possible to provide them via a Data Source (see Data Source Mapping).
Controls¶
Controls are graphical elements on a fixed position over the map. They can provide user input or information.
Control | Description |
---|---|
Attribution | Informational button showing the attributions for the map layer sources |
MousePosition | Shows the coordinates of the mouse cursor |
OverviewMap | Shows an overview of the main map |
ScaleLine | Shows a scale line with rough Y-axis distances |
Zoom | Buttons for zoom-in and zoom-out |
ZoomSlider | Slider for zooming in or out |
ZoomToExtent | Button for zooming to an extent (default is zooming to zoom 0) |
More information on controls can be found in the OpenLayers API documentation, e.g. CSS classes to modify control styles.
Data Source Mapping¶
As an alternative to Overlay Groups property, a Data Source can also be used to provide overlays for the map. In this case, a mapping must be provided for the Widget to correctly read the dataset. The dataset structure differs a bit from the Data Sources for the other Widgets. It recalls the same structure of the Overlay Groups property, i.e., an array of groups, each one having a name, the optional name of an overlay pre-selected at loading and an array of overlays with their own properties (coordinates, CSS class, HTML template, etc.).
The Data Source must return a result like this:
[{
"groupID": "g1", //unique group name (will be assigned randomly if missing)
"selectedOverlay": "ov1", //optional
"overlays": [{ //list of overlays
"css": "my-css-class",
"cssSelected": "my-css-class-sel",
"id": "ov1",
"content": "<div>OV 1</div>",
"coordinates": [11, 46], //alternatively you can have separate X and Y fields
"positioning": "top-left"
},
{
"css": "my-css-class",
"cssSelected": "my-css-class-sel",
"id": "ov2",
"content": "<div>OV 2</div>",
"coordinates": [10, 45],
"positioning": "bottom-right"
}
]
}]
You must use the Data Source Mapping property set to specify which dataset fields contain each piece of data necessary for configuring the overlays, that is, how to interpret the keys of the objects returned by the Data Source. The following table illustrates how to configure Data Source Mapping properties (required properties are marked in bold):
Property | JSON Key | Description |
---|---|---|
Identifier Field | identifierField | Name of the field containing a unique identifier for the group. If it is not specified and the datasource provides more that one group, each group will be assigned a random ID. |
Overlay Initially Selected Field | initiallySelectedField | Name of the field containing the ID of the pre-selected overlay. If not provided, all overlays will have the regular CSS class until one is clicked. |
Overlay List Field | overlayListField | Name of the field containing the list of overlays |
CSS Class Field | cssClassField | Name of the field containing the CSS class for the overlay (must be defined beforehand in the Styles section) |
CSS Class On Selection Field | cssClassOnSelectionField | Name of the field containing the CSS class for the overlay after its selection (must be defined beforehand in the Styles section) |
Overlay Identifier Field | overlayIdField | Name of the field containing a unique identifier for the overlay. If it is not specified, a random ID will be assigned. |
Position Field | positionField | Name of the field containing an array with the coordinates ([x_coord, y_coord]) for the overlay. Use xField and yField if coordinates are in two separate fields. |
X Coordinate Field | xField | Name of the field containing X coordinate for the overlay |
Y Coordinate Field | yField | Name of the field containing Y coordinate for the overlay |
Positioning Field | positioningField | Name of the field containing the overlay positioning |
Template Field | templateField | Name of the field containing the HTML template for the overlay |
A widget that uses the Data Source in the example above would need the following Data Source Mapping:
"dataSourceMapping": {
"identifierField": "groupID",
"initiallySelectedField": "selectedOverlay",
"overlayListField": "overlays",
"cssClassField": "css",
"cssClassOnSelectionField": "cssSelected",
"overlayIdField": "id",
"positionField": "coordinates",
"positioningField": "positioning",
"templateField": "content"
}
Note that if the Data Source provides more than one group, each object in the array must have the same keys (e.g. in every object, the group ID can be found in a field named “groupID”).
Specific Events¶
Events generated exclusively by the OpenLayers Widget (see Parameter-based Interaction). They produce a value that will be stored in the given Parameter. Note that Parameters must be defined beforehand in the Parameters section of the dashboard.
The subproperty Section can be used to specify the name of the map portion that triggers the event. If the event is triggered by the map itself, you can leave this option empty.
Event | Value | Section |
---|---|---|
clickOnOverlay |
Name of the overlay clicked | Name of an overlay group (e.g. “g1”) |
clickOnWMSLayer |
Feature Info | Name of a WMS layer (e.g. “topp:states”), obtained with getGetFeatureInfoUrl |
Examples¶
Many examples of maps can be found on the OpenLayers website, although not all of them are reproducible with Cyclotron.
Although Configuration property has the structure of a JSON object in the editor, it is a JavaScript string that is evaluated at dashboard loading, therefore it can contain JavaScript code (e.g. object instantiation).
OSM layer and WMS layer with zoom control¶
{
"center": {
"x": "11.123251",
"y": "46.044685"
},
"controls": [{
"control": "Zoom"
}],
"layers": [{
"source": {
"name": "OSM"
},
"type": "Tile"
}, {
"source": {
"configuration": "{\r\n \"url\": \"http://my.geoserver.com/wms\",\r\n \"params\": {\r\n \"FORMAT\": \"image/png\",\r\n \"VERSION\": \"1.1.1\",\r\n \"STYLES\": \"\",\r\n \"LAYERS\": \"topp:states\"}\r\n}",
"name": "ImageWMS"
},
"type": "Image"
}],
"widget": "openLayersMap",
"zoom": 8
}
Default OSM layer and some overlays¶
This example uses air quality data stations located in Trentino to illustrate how to configure selectable overlays. Each overlay is positioned over a station and is represented as a circle whose color is assigned randomly among five options. As an overlay is clicked on, it becomes the currently selected one and the circle enlarges. The ID of the currently selected overlay is held by the Parameter “currentStation”. When you select an overlay, “currentStation” is updated and such update triggers the refresh of the Data Source, which in turn changes the color and template of the overlays.
The following dashboard components are required:
- Parameter:
{
"name": "currentStation",
"defaultValue": "7"
}
- CSS Style:
.station {
opacity: .8;
border-radius: 50%;
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
font-size: 12px;
}
.station.sel {
width: 70px !important;
height: 70px !important;
border: 2px solid #ddd;
padding: 20px 20px;
margin: auto;
}
.station.level1 {
background-color: #7BBB6D;
}
.station.level2 {
background-color: #BBCE55;
}
.station.level3 {
background-color: #EDC12F;
}
.station.level4 {
background-color: #F09227;
}
.station.level5 {
background-color: #E64770;
}
JSON Data Source
- Name:
air-quality-stations
- URL:
https://am-test.smartcommunitylab.it/dss/services/ariadb/Stations
- Post-Processor:
e = function(dataSet){ res = []; var stations = dataSet.Entries.Entry; //array of objects with keys: id, name, X, Y _.each(stations, (station) => { var level = Math.floor(Math.random() * (6 - 1)) + 1; station.css = 'station level' + level; station.cssSelected = 'sel'; station.template = '<div>' + level + '</div>'; }); stationGroup = {}; stationGroup.currentlySelected = '7'; stationGroup.stationOverlays = stations; res.push(stationGroup); return res; }
- Subscription to Parameters:
currentStation
- Name:
OpenLayers Map Widget:
{
"center": {
"x": "11.123251",
"y": "46.044685"
},
"dataSource": "air-quality-stations",
"dataSourceMapping": {
"cssClassField": "css",
"cssClassOnSelectionField": "cssSelected",
"initiallySelectedField": "currentlySelected",
"overlayIdField": "id",
"overlayListField": "stationOverlays",
"templateField": "template",
"xField": "X",
"yField": "Y"
},
"specificEvents": [{
"event": "clickOnOverlay",
"paramName": "currentStation"
}],
"widget": "openLayersMap",
"zoom": 11
}
Parametric ImageWMS layer¶
In this example, the OpenLayers Widget uses the value generated by a Time Slider Widget as TIME parameter for the WMS layer.
The following dashboard components are required:
- Parameter:
{
"name": "sliderValue",
"defaultValue": "2017-09-10"
}
- Slider Widget:
{
"maxValue": "2018-03-30",
"minValue": "2017-09-10",
"momentFormat": "YYYY-MM-DD",
"pips": {
"density": 2,
"mode": "count",
"stepped": true,
"values": "4"
},
"specificEvents": [{
"event": "dateTimeChange",
"paramName": "sliderValue"
}],
"tooltips": true,
"widget": "slider"
}
- OpenLayers Map Widget:
{
"center": {
"x": "11.123251",
"y": "46.044685"
},
"layers": [{
"source": {
"name": "OSM"
},
"type": "Tile"
}, {
"source": {
"configuration": "{\n \"url\": \"http://maps.dedagroup.it/geoserver/mobility/wms\",\n \"params\": {\n \"FORMAT\": \"image/png\",\n \"VERSION\": \"1.1.1\",\n \"LAYERS\": \"mobility:trento_archi1day_total_week\",\n \"TIME\": \"#{sliderValue}T00:00:00.000Z\"\n }\n}",
"name": "ImageWMS"
},
"type": "Image"
}],
"parameterSubscription": ["sliderValue"],
"widget": "openLayersMap",
"zoom": 11
}
The WMS layer configuration is repeated below for easier reading:
{
"url": "http://maps.dedagroup.it/geoserver/mobility/wms",
"params": {
"FORMAT": "image/png",
"VERSION": "1.1.1",
"LAYERS": "mobility:trento_archi1day_total_week",
"TIME": "#{sliderValue}T00:00:00.000Z"
}
}
Vector layer with GeoJSON features¶
This example reproduces GeoJSON example.
In order to add GeoJSON features to the OpenLayers widget, you can either pass it an array of Feature or the URL of a GeoJSON file. In the first case, features can be added as a script in the Scripts section of the dashboard:
var geoJsonFeatures = {
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "EPSG:3857"
}
},
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [0, 0]
}
}, {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [[4e6, -2e6], [8e6, 2e6]]
}
}, {
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [[4e6, 2e6], [8e6, -2e6]]
}
}, {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6]]]
}
}, {
"type": "Feature",
"geometry": {
"type": "MultiLineString",
"coordinates": [
[[-1e6, -7.5e5], [-1e6, 7.5e5]],
[[1e6, -7.5e5], [1e6, 7.5e5]],
[[-7.5e5, -1e6], [7.5e5, -1e6]],
[[-7.5e5, 1e6], [7.5e5, 1e6]]
]
}
}, {
"type": "Feature",
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[[[-5e6, 6e6], [-5e6, 8e6], [-3e6, 8e6], [-3e6, 6e6]]],
[[[-2e6, 6e6], [-2e6, 8e6], [0, 8e6], [0, 6e6]]],
[[[1e6, 6e6], [1e6, 8e6], [3e6, 8e6], [3e6, 6e6]]]
]
}
}, {
"type": "Feature",
"geometry": {
"type": "GeometryCollection",
"geometries": [{
"type": "LineString",
"coordinates": [[-5e6, -5e6], [0, -5e6]]
}, {
"type": "Point",
"coordinates": [4e6, -5e6]
}, {
"type": "Polygon",
"coordinates": [[[1e6, -6e6], [2e6, -4e6], [3e6, -6e6]]]
}]
}
}]
}
Then you can add to the map widget a vector layer with the following source:
Name: Vector
Configuration:
{ "features": (new ol.format.GeoJSON()).readFeatures(geoJsonFeatures).concat(new ol.Feature(new ol.geom.Circle([5e6, 7e6], 1e6))) }
- Notice the use of geoJsonFeatures variable that you created in the script
- Here concat() function is used to add a circle, which is a feature type not supported natively by GeoJSON
As an alternative to adding features as a Script, you can give a URL in the source configuration of the Vector layer. The Configuration property would be:
{
"format": new ol.format.GeoJSON(),
"url": "https://path/to/your/geojsonfile.json"
}
If you want to give the features a proper style, you can use the Style Function property to provide a StyleFunction:
e = function(feature) {
var image = new ol.style.Circle({
radius: 5,
fill: null,
stroke: new ol.style.Stroke({color: 'red', width: 1})
});
var styles = {
'Point': new ol.style.Style({
image: image
}),
'LineString': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
}),
'MultiLineString': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
}),
'MultiPoint': new ol.style.Style({
image: image
}),
'MultiPolygon': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'yellow',
width: 1
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 0, 0.1)'
})
}),
'Polygon': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
lineDash: [4],
width: 3
}),
fill: new ol.style.Fill({
color: 'rgba(0, 0, 255, 0.1)'
})
}),
'GeometryCollection': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'magenta',
width: 2
}),
fill: new ol.style.Fill({
color: 'magenta'
}),
image: new ol.style.Circle({
radius: 10,
fill: null,
stroke: new ol.style.Stroke({
color: 'magenta'
})
})
}),
'Circle': new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'red',
width: 2
}),
fill: new ol.style.Fill({
color: 'rgba(255,0,0,0.2)'
})
})
};
return styles[feature.getGeometry().getType()];
}
Notes¶
You might notice that some layers displaying images fetched via HTTP requests (e.g. ImageWMS sources) might refresh only at page reload or when the map is zoomed in or out. This happens because the browser is caching previous results and does not automatically perform a new request if the URL is unchanged. One way to avoid the use of cached content is to make every URL unique by adding a parameter that changes every time you need a refresh.
In this example:
"layers": [{
"source": {
"configuration": "{
\"url\": \"http://my.geoserver.com/wms\",
\"params\": {
\"FORMAT\": \"image/png\",
\"LAYERS\": \"my:layer\",
\"requestVersion\": \"#{reqVersion}\"
}
}",
"name": "ImageWMS"
},
"type": "Image"
}],
"parameterSubscription": ["reqVersion"]
the map widget subscribes to the Parameter reqVersion and its WMS layer adds it to the parameters that will be appended to the WMS server URL. The server will ignore it, but whenever reqVersion is updated, the browser will find that the URL has changed and request new content.