Maps — Getting started

🟢 Beginner

map getting-started

This guide walks through the minimum configuration needed to get a working map in OnSphere, then progressively adds the four supported layer types.

git checkout origin/osp-web-configuration .
git checkout origin/osp-maps-configuration .
git checkout origin/osp-mongo-configuration .
git checkout origin/example-remote-connector-basic .

Add the map module to your configuration

Every map layer needs three binding files:

File

Purpose

layer.ospp

Declares the layer type, geometry type, and data sources.

layer.maps

Binds the layer to an osp-maps module instance.

layer.web

Exposes the layer to an osp-web module instance.

layer.maps and layer.web follow the same minimal pattern for all layer types:

root/layers/<layer-name>/layer.maps
{
    "moduleId": "modules.maps.maps-1"
}
root/layers/<layer-name>/layer.web
{
    "moduleId": "modules.web.web-1"
}

The moduleId values must match the module instance identifiers declared in your stack.

Choose your basemap: Mapbox or MapLibre

The Map widget renders one basemap library per widget instance. Choose based on your connectivity and token requirements:

Mapbox

MapLibre + Protomaps

Connectivity

Online — requires internet access to Mapbox tile servers

Offline-capable — tiles served from a local PMTiles archive

API token

Required

Not required

Satellite view

Available via Mapbox Studio styles

Not available

Key config field

mapStyle

protomapSource

Mapbox — set mapStyle in mapsWidgetSettings with light and dark style URLs:

{
  "type": "Maps",
  "mapsWidgetSettings": {
    "mapStyle": {
      "dark": "mapbox://styles/mapbox/dark-v11",
      "light": "mapbox://styles/mapbox/light-v11"
    }
  }
}

See Get an API token for instructions on obtaining a Mapbox API token and placing it in module.web.

MapLibre — set library and protomapSource with the resource path to your PMTiles archive:

{
  "type": "Maps",
  "mapsWidgetSettings": {
    "library": "MapLibre",
    "protomapSource": "/osp/resources/tiles/world.pmtiles"
  }
}

See MapLibre and Protomaps for instructions on generating a PMTiles archive and loading it as a resource.

Note

The rest of this guide applies to both basemaps. The basemap choice only affects mapStyle or protomapSource in Dashboard view.

Add a simple point from geo.maps

Layer type: STANDARD | Source type: STATIC

Static points inherit their geographic coordinates from a geo.maps file placed in the configuration tree. Every value nested below that file is linked to that location until another geo.maps is found deeper in the tree.

1. Create geo.maps

Declare one GeoJSON Point feature per location. Coordinates follow the order [longitude, latitude, altitude?].

root/site/bulle/geo.maps
{
    "geoJsonString": {
        "type": "Feature",
        "geometry": {
          "type": "Point",
          "coordinates": [7.0567, 46.6195, 450]
        },
        "properties": {
          "name": "Bulle city",
          "label": {"name": "Bulle","text-color": "#F0FFFF", "text-anchor": "bottom"}
        }
      },
      
    "moduleId": "modules.maps.maps-1"
}

The name property populates the click popup. The optional label property adds a text label anchored above or below the point. moduleId binds the file to an osp-maps instance.

2. Create layer.ospp

root/layers/points/layer.ospp
{
    "name": "Example points layer",
    "description": "",
    "layerType": "STANDARD",
    "valueType": "POINT",
    "mapSources": [
        {
            "type": "STATIC",
            "linkedValues": [
                "root.site.bulle.value.variable",
                "root.site.lausanne.value.variable"
            ]
        }
    ]
}

layerType: STANDARD renders points coloured by their linked value. mapSources[].type: STATIC reads geometry from geo.maps. linkedValues lists the value ids that should appear on the map.

3. Register the layer in Dashboard view

root/dashboard.view
{
    "configuration": [
      {
        "id": "Lo-_-jGf",
        "type": "Maps",
        "title": "",
        "mapsWidgetSettings": {
          "layerOptions": [
            {
              "layerId": "root.layers.points",
              "activatedByDefault": true
            }
          ],
          "mapStyle": {
            "dark": "mapbox://styles/mapbox/dark-v11",
            "light": "mapbox://styles/mapbox/light-v11"
          },
          "centerPosition": [
            7.153656,
            46.80603
          ],
          "zoomLevel": 9
        }
      }
    ],
    "layout": {
      "lg": [
        {
          "w": 12,
          "h": 6,
          "x": 0,
          "y": 0,
          "i": "Lo-_-jGf"
        }
      ]
    },
    "breakpoints": {
      "lg": 1200,
      "md": 996,
      "sm": 768,
      "xs": 480,
      "xxs": 0
    },
    "cols": {
      "lg": 12,
      "md": 10,
      "sm": 6,
      "xs": 4,
      "xxs": 2
    },
    "rowHeight": 150
  }
  

layerOptions[].layerId must match the OnSphere id of the layer folder (for example root.layers.points). Set activatedByDefault: true to show the layer when the dashboard opens.

See also

Display standard points on map — full walkthrough including value and script configuration.

Add alarm-linked points from geo.maps

Layer type: SEVERITY | Source type: STATIC

Note

Requires osp-alarms.

Use this approach when you want to colour a geographic point by the highest alarm severity at that location. The geometry still comes from geo.maps; the severity and count values come from alarm aggregations already declared in the configuration tree.

Replace layer.ospp with a SEVERITY layer that references the alarm aggregation values for each site:

root/layers/alarms/layer.ospp
{
    "name": "Alarm layer",
    "description": "",
    "layerType": "SEVERITY",
    "valueType": "POINT",
    "mapSources": [
        {
            "type": "STATIC",
            "severityConfigurations": [
                {
                    "severity": "root.site.bulle.alarms.highest_severity",
                    "count": "root.site.bulle.alarms.count",
                    "dashboard": "root.alarmGenerator"
                },
                {
                    "severity": "root.site.lausanne.alarms.highest_severity",
                    "count": "root.site.lausanne.alarms.count",
                    "dashboard": "root.alarmGenerator"
                }
            ]
        }
    ]
}

layerType: SEVERITY colours each point by alarm severity. severityConfigurations maps each site’s highest-severity and count value ids to a display entry. The optional dashboard field navigates to another dashboard when the user clicks the feature.

The geo.maps file and the layer.maps / layer.web binding files are identical to the previous step.

See also

Display alarm points on map — full walkthrough including alarm aggregation setup.

Add a live alarm-backed layer

Layer type: SEVERITY | Source type: ALARMS

Note

Requires osp-alarms.

Use this approach when alarms carry their own geographic coordinates (for example in additionalData.geometry). No geo.maps file is needed — osp-maps streams live alarms and extracts the geometry from the configured field path on each alarm document.

1. Declare an alarm view

The view controls which alarm fields osp-alarms projects. It must expose the geometry field and, for SEVERITY layers, also highestSeverity and count.

root/alarms/views/live_map/view.ospp
{
    "name": "Live map alarms",
    "description": "Alarm view for the offline live alarm map example",
    "liveColumns": [
        {
            "name": "Serial",
            "field": "serial"
        },
        {
            "name": "Summary",
            "field": "summary"
        },
        {
            "name": "Source",
            "field": "source"
        },
        {
            "name": "Location",
            "field": "location"
        },
        {
            "name": "Severity",
            "field": "severity"
        },
        {
            "name": "Highest severity",
            "field": "highestSeverity"
        },
        {
            "name": "Count",
            "field": "count"
        },
        {
            "name": "Marker type",
            "field": "additionalData.markerType"
        },
        {
            "name": "Geometry",
            "field": "additionalData.geometry"
        },
        {
            "name": "Tags",
            "field": "tags"
        },
        {
            "name": "Last occurrence",
            "field": "lastTimestamp"
        }
    ],
    "historyColumns": [
        {
            "name": "Serial",
            "field": "serial"
        },
        {
            "name": "Summary",
            "field": "summary"
        },
        {
            "name": "Source",
            "field": "source"
        },
        {
            "name": "Location",
            "field": "location"
        },
        {
            "name": "Severity",
            "field": "severity"
        },
        {
            "name": "Count",
            "field": "count"
        },
        {
            "name": "Marker type",
            "field": "additionalData.markerType"
        },
        {
            "name": "Geometry",
            "field": "additionalData.geometry"
        },
        {
            "name": "Operation time",
            "field": "operationTime"
        }
    ]
}

The additionalData.geometry entry in liveColumns is the field the layer will read as the GeoJSON geometry for each alarm.

2. Declare alarm filters

Each layer must declare at least one alarm filter id. The first declared filter id is used by default when the widget does not specify one.

root/alarms/filters/critical_only/filter.ospp
{
    "name": "Critical only",
    "description": "Display alarms with a highest severity of major or above"
}
root/alarms/filters/critical_only/filter.alarms
{
    "moduleId": "modules.alarms.alarms-1",
    "query": {
        "highestSeverity": {
            "$gte": 500
        }
    }
}

3. Create layer.ospp

root/layers/alarm_severity/layer.ospp
{
    "name": "Live alarm severity",
    "description": "Live alarm severity points streamed directly from the alarms module",
    "layerType": "SEVERITY",
    "valueType": "POINT",
    "clusteringType": "NONE",
    "mapSources": [
        {
            "type": "ALARMS",
            "geometry": "additionalData.geometry",
            "view": "root.alarms.views.live_map",
            "filter": [
                "root.alarms.filters.all",
                "root.alarms.filters.critical_only"
            ]
        }
    ]
}

geometry is the dot-path to the GeoJSON object inside each alarm document. view is fixed at configuration time — the widget can only switch between the declared filter ids at runtime, not the view.

See also

Display offline live alarm layers on map — full walkthrough including alarm creation from the map.

Add a collection-backed layer

Layer type: STANDARD | Source type: COLLECTIONS

Note

Requires osp-collections.

Use this approach when map items are records in a collection schema. Each document must expose a GeoJSON object at a configured field path. osp-maps watches the collection and pushes geometry updates to the widget automatically.

1. Add a GeoJSON field to the collection schema

The schema must define a field that holds a valid GeoJSON object. In this example the GeoJSON is stored at map.geometry.

root/collections/dynamic_map_items/schema.ospp
{
    "schema": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string"
            },
            "category": {
                "type": "string",
                "enum": [
                    "fire",
                    "maintenance"
                ]
            },
            "visibleOnMap": {
                "type": "boolean"
            },
            "map": {
                "type": "object",
                "properties": {
                    "geometry": {
                        "type": "object",
                        "properties": {
                            "type": {
                                "type": "string",
                                "enum": [
                                    "Point"
                                ]
                            },
                            "coordinates": {
                                "type": "array",
                                "minItems": 2,
                                "maxItems": 2,
                                "items": {
                                    "type": "number"
                                }
                            }
                        },
                        "required": [
                            "type",
                            "coordinates"
                        ]
                    }
                },
                "required": [
                    "geometry"
                ]
            }
        },
        "required": [
            "name",
            "category",
            "visibleOnMap",
            "map"
        ]
    },
    "filters": [
        {
            "id": "visible_only",
            "name": "Visible points only"
        }
    ]
}

The filters array declares filter ids that the layer and the widget can reference. The actual MongoDB query for each filter id is implemented in schema.collections.

2. Create layer.ospp

root/layers/collection_standard/layer.ospp
{
    "name": "Dynamic collection points",
    "description": "Points streamed from the collections module",
    "layerType": "STANDARD",
    "valueType": "POINT",
    "clusteringType": "NONE",
    "mapSources": [
        {
            "type": "COLLECTIONS",
            "collectionSource": "root.collections.dynamic_map_items",
            "geometry": "map.geometry",
            "filter": [
                "visible_only"
            ]
        }
    ]
}

collectionSource is the OnSphere id of the collection schema folder. geometry is the dot-path to the GeoJSON object in each document. filter lists the schema filter ids available to this layer.

See also

Display collection-backed layers on map — full walkthrough including filter reuse with a collection table.

What’s next