Product definition
June 25, 2026 · View on GitHub
- Table of Contents
- Product definition
- Product configuration
- Shared attributes
- Product level attributes
- Views
- Layers
- BackgroundLayer
- MapLayer
- LocationLayer
- IsobandLayer
- IsolineLayer
- IsolabelLayer
- SymbolLayer
- ArrowLayer
- NumberLayer
- NullLayer
- CloudCeilingLayer
- StreamLayer
- RasterLayer
- LegendLayer
- TimeLayer
- TagLayer
- WKTLayer
- HovmoellerLayer
- GraticuleLayer
- CircleLayer
- TranslationLayer
- WindRoseLayer
- OSMLayer
- PostGISLayer
- IceMapLayer
- FinnishRoadObservationLayer
- PresentWeatherObservationLayer
- MetarLayer
- Structure definitions
- Sampling structure
- Heatmap structure
- Isoband structure
- Intersection structure
- Isoline structure
- Isofilter structure
- LegendLabels structure
- LegendSymbols structure
- Text structure
- Map structure
- MapStyles structure
- MapFeature structure
- Positions structure
- Label structure
- LabelConfig structure
- Location structure
- Observation structure
- Station structure
- WindRose structure
- Connector structure
- Filters structure
- Product configuration
- Using dynamically created grids
- JSON references and includes
- Symbol substitutions via URI
- Dali querystring parameters
- WMS querystring parameters
- WMS GetMap and GetCapabilities configuration
- Configuration
- Examples
Product definition
Product configuration
There can be thousands of different products defined for the Dali plugin. Each product has its own configuration file, which is used in order to define values for the attributes needed when constructing the final image. The table below shows an example of a product configuration file and the image that is returned when this product is requested.
| Product configuration file | Produced image |
|---|---|
| f |
The product configuration files are written in Java Script Object Notation (JSON). In order to create your own product configuration files you just need to know what attributes are used on different configuration levels, i.e., what attributes can be given on the product level, on the view level and on the layer level. These attributes are described flater in this chapter.
The attribute description for each attribute consists of the attribute name, the attribute type, the default value and a short description of the attribute usage. In the attribute description the attribute type can be defined in the following ways:
| Type | Description |
|---|---|
| (string) | A string with no default value. Not giving a value to the string may be considered as an error if the title for the SVG is not defined, or if no default symbol name is given for a legend. |
| string | A string. Usually this variable needs to be defined, for example, qid to give an ID to isobands. The string may also have a default value, for example, the default separator for the LegendLabels is an "en dash" (Unicode 2013). |
| Structure | When the type is a structure name then the attribute value is defined as a structure, or the value is a reference to a such structure. For example "projection" -attribute is defined to be the type of Projection structure, which means that it contains a list of attributes defined for the Projection structure. |
| [Structure] | An array of structures. The type of the structure is defined inside the brackets. For example an attribute which type is [Layer] contains an array of Layer structures. |
| {string:string} | A map from names to values. |
| {string:string:string} | A map from principal names to a map of name-value pairs. |
Shared attributes
There are some upper level attributes that can be used also on the lower level in the hierarchy. For example, some attributes defined on the product level are visible also on the view and layer levels. These levels can directly use these upper level attribute values or override the values with their own values. These attributes can be overridden also by the request parameters. For example, the "language" attribute can be overridden by using the "language" parameter in the request.
The table below contains a list of attributes that are shared from the top level to the lower levels.
Shared attributes
| Name | Type | Default value | Description |
|---|---|---|---|
| language | (string) | config:language | Language code. The code does not need to follow ISO 639-1 or other similar standards, it is merely a code name. |
| customer | (string) | config:customer | The customer name. The customer setting defines the subdirectory in the Dali configuration directory from which the JSON, CSS and SVG includes are taken, unless an absolute path is given for a JSON include. |
| producer | (string) | config:model | The producer name to be used when querying data from QEngine. |
| tz | (string) | UTC | The timezone used for parsing time (but not origintime) |
| time | (string) | The time in any string format recognized by the MacGyver time parser (ISO-format, SQL, timestamps, epoch times, offsets from current time). | |
| time_offset | (int) | The time offset in minutes to be added to the time. The principal time is usually set at the top most level (Product), and a time_offset is then used to modify it for different views of data. | |
| interval_start | (int) | Time in minutes backward from time+time_offset. Used for selecting an interval of observations. | |
| interval_end | (int) | Time in minutes forward from time+time_offset. Used for selecting an interval of observations. | |
| origintime | (string) | The querydata origintime in any string format recognized by the MacGyver time parser (ISO-format, SQL, timestamps, epoch times, offsets from current time). | |
| projection | Projection | The Projection structure. | |
| xmargin | int | 0 | X-margin in pixels for clipping out features. |
| ymargin | int | 0 | Y-margin in pixels for clipping out features. |
| clip | bool | false | Whether each layer should be placed into a rectangular clipPath. |
Time intervals
Normally the "valid time" for the data is time + time_offset. If a time interval is defined, it is producer dependent how the settings are interpreted. Most layers ignore the settings, and the settings are ignored for all forecasts. For lightning data the interval is true, all flashes in the interval are picked. For other observations only the latest one in the time interval will be selected. For this reason interval_end is usually not needed for observations either.
Clipping and margins
Normally any point strictly outside the layer rectangle will be omitted. However, if the image will be tiled, this may produce images where symbols or text appear only in one tile, and the rest of the symbol is cut off from the adjacent tile. The layer effective bounding box may be expanded using the setting "margin", which sets both the x-margin and y-margin, or directly by setting "xmargin" and/or "ymargin".
For observations any point inside the expanded area will be included. Typically one would expand the layer by half the size of any symbol needed in the image, be it a symbol, a number or whatever. The marging has an effect also on geometries. For example if one wishes to draw isolines with a line a few pixels wide, the margings should be expanded accordingly.
For tiled images expanding the area has no discernible effect, since the clipping will be automatic since the rendered image is still the expected size and does not include the margins. However, if one has multiple views in a single image, one should set "clip" to true to make sure none of the layers leak outside their respective areas. Using a clipPath slows rendering down a little, hence it is not on by default.
Output formatting
Most output types (svg, png, pdf, ps, geojson, topojson, kml) are rendered
through the CTPP/SVG pipeline. Three output types bypass this pipeline entirely: geotiff
returns raw float data via GDAL, mvt returns protobuf-encoded Mapbox Vector Tiles, and
datatile returns RGBA-encoded float data as standard PNG (see DataTile output).
PNG output formatting can be tuned using the following settings inside a top level "png" tag:
| Name | Type | Default value | Description |
|---|---|---|---|
| quality | (double) | 10 | The PNG compression level. 10=good, 20=poor |
| errorfactor | (double) | 2.0 | Tuning parameter for color reduction. Must be greater than 1.0 |
| maxcolors | (int) | 0 | Desired maximum number of colors in the palette. Zero implies no maximum, and palette fitting will be fully adaptive |
| truecolor | (bool) | false | Set to avoid color reduction completely |
WebP output uses the same color reduction settings from the "png" tag, and adds its own compression speed and animation controls in a top level "webp" tag:
| Name | Type | Default value | Description |
|---|---|---|---|
| level | (int) | 1 | The WebP lossless compression preset level, 0...9. 0 = fastest encoding with the largest file, 9 = slowest encoding with the smallest file. The default 1 is roughly 2.5 times faster to encode than the libwebp default settings at the cost of a slightly larger file. |
| frames | (int) | - | Setting this enables animated WebP output with the given number of frames, 1...1000. See WebP time animation below. |
| frame_duration | (int) | 100 | The display duration of each animation frame in milliseconds. |
| loop | (int) | 0 | The animation loop count. Zero loops forever. |
| accumulate | (bool) | false | If true, the animated elements accumulate over the loop instead of each frame showing only its own time bucket. |
WebP time animation
When "frames" is set and the output type is webp, layers supporting time animation
divide the layer time interval (see Time intervals) into the given
number of frames, and each rendered element is assigned to a frame based on its actual
observation time. The frames are then encoded into a single looping animated WebP image.
All other layers (backgrounds, maps etc.) render identically into every frame.
Currently the symbol-layer observation rendering supports time animation, the main use
case being lightning data: with for example "interval_start": 30, "interval_end": 30
and "frames": 6, each frame replays ten minutes of flash strokes in stroke time order.
Setting "accumulate": true makes the strokes pile up during the loop instead, which
shows where lightning activity has moved during the interval. A typical flash layer
interval is 5, 15, 30 or 60 minutes to match the available radar data time period.
The SVG content is generated only once; each frame is rasterized from the same SVG with a different visibility selection, so rendering cost grows only with rasterization and encoding, not with data queries. Note that this product-level setting is separate from the WMS GetMap "animation" block, which animates over consecutive valid times.
Product level attributes
The product level is the highest structural level used in the product configuration file. The product attributes are used in order to define product level properties for the current product. On the other hand, the product attributes define the substructures related to the current product.
The table below contains a list of attributes that can be defined for the product.
Product
| Name | Type | Default value | Description |
|---|---|---|---|
| svg_tmpl | (string) | config:templates | The CTPP2 template to be used for generating the SVG image. The default template is selected automatically based on the selected image format. |
| qid | (string) | - | An identifier for the product. Usually this can be left empty. |
| width | (int) | projection.xsize | The width of the SVG image. If the product contains only one view with no transformations, the default value will be taken from the Product projection xsize variable. |
| height | (int) | projection.ysize | The height of the SVG image. If the product contains only one view with no transformations, the default value will be taken from the Product projection ysize variable. |
| title | (string) | - | The title of the generated SVG image. |
| attributes | Attributes | - | The SVG image attributes for the product level <g>...</g> group. |
| defs | Defs | - | The SVG image header definitions such as styles, symbols, paths not to be drawn directly etc. This information is defined as the Defs structure, which is described in the next section. |
| views | [View] | - | An array of the View structures used in the SVG image. Usually a product has only one View structure which shares the projection defined on the product level. |
Views
A product can contain several views that are merged together into single image / product. The product level attribute "views" are used in order to define an array of the View structures for the product. Each view must have its own View structure in this array. The View structure is used in order to define the view level attributes and the lower level structures (i.e. layers).
The table below contains a list of attributes that can be defined in the View structure.
View
| Name | Type | Default value | Description |
|---|---|---|---|
| qid | (string) | - | An identifier for the view. Normally something simple such as "view1" or "v1". |
| attributes | Attributes | - | The SVG attributes for the View level |
| layers | [Layer] | - | An array of the Layer structures needed in order to construct the view. |
Layers
A view can contain several layers that are merged together into single view. The view level attribute "layers" are used in order to define an array of the Layer structures for the view. Each layer must have its own Layer structure in this array. The Layer structure is used in order to define layer level properties. The most important attribute on the layer level is the "layer_type" attribute, which defines the type of the current layer. At the same time it defines indirectly which layer module the Dali plugin should use to create an image for the current layer. On the other hand, the layer type tells us which other attributes we should define for the current layer. In other words, if the layer type is for example "isoband" then we should define on this level also all isoband layer related attributes. All the layers share some common attributes. In addition, each layer type has its own attributes. The table below contains a list of attributes that are common in all layers.
Layer
| Name | Type | Default Value | Description | |
|---|---|---|---|---|
| qid | (string) | - | An identified for the layer. Normally something simple such as "layer1" or "t2mlayer" or just "l1". | |
| css | (string) | - | An external CSS file to be included into the style section. E.g., "isobands/temperature.css". | |
| attributes | Attributes | - | The SVG attributes for the Layer level top SVG tag. Often the tag is <g>, but may be any other tag too for example when layer_type=tag is used. | |
| layer_type | (string) | "tag" | The type of the layer. | |
| Value | Layer | |||
| background | BackgroundLayer | |||
| map | MapLayer | |||
| location | LocationLayer | |||
| isoband | IsobandLayer | |||
| isoline | IsolineLayer | |||
| isolabel | IsolabelLayer | |||
| symbol | SymbolLayer | |||
| arrow | ArrowLayer | |||
| number | NumberLayer | |||
| null | NumllLayer | |||
| cloudceiling | CloudCeilingLayer | |||
| stream | StreamLayer | |||
| legend | LegendLayer | |||
| time | TileLayer | |||
| tag | TagLayer | |||
| translation | TranslationLayer | |||
| windrose | WindRoseLayer | |||
| metar | MetarLayer | |||
| osm | OSMLayer | |||
| postgis | PostGISLayer | |||
| icemap | IceMApLayer | |||
| minresolution | (double) | - | Minimum resolution of the projection for the layer to be generated at all. | |
| maxresolution | (double) | - | Maximum resolution of the projection for the layer to be generated at all. | |
Notice that the term "resolution" used above might be a little bit misleading in this case, since it refers to the number of kilometers represented by one pixel. Hence the higher the resolution the worse the accuracy of the image actually is. The actual condition to be satisfied is minresolution <= resolution < maxresolution, where one or both of the limits may be missing.
BackgroundLayer
The background layer is used to create a background for the current view. So it is usually the lowest layer in the view.
The table below shows a simple example on the usage of the background layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The background layer has no attributes than the generic Layer or Properties attributes. The layer exists merely to obtain the "xsize" and "ysize" attribute values for a rectangle element so that the background can be filled with a value.
MapLayer
The table below shows a simple example on the usage of the map layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the map layer in addition to the common layer attributes.
MapLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| map | Map | - | The Map structure |
| precision | double | 1.0 | Precision of SVG coordinates |
| styles | [MapStyles] | - | Optional regional styling based on forecast values |
LocationLayer
The location layer renders point symbols at geographic positions retrieved from the geonames database. Typical use: city dots on a weather map so the viewer can locate the weather data. Location names are context for the weather information — weather symbols always take visual priority.
Symbols are selected by population and geonames feature code using the AttributeSelection mechanism. An optional label block renders the location name as an SVG text element next to the symbol using one of several cartographic label-placement algorithms.
Layer ordering and weather priority. Because location labels are context rather than primary data, place the location layer before weather layers in the views.layers array so that weather symbols render on top. The label halo (white stroke) keeps text legible even when a weather symbol overlaps it.
The table below shows a simple example of the location layer with greedy label placement.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below lists the attributes that can be defined for the location layer in addition to the common layer attributes.
LocationLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| keyword | string | - | The geonames keyword identifying the set of locations to render. |
| mindistance | double | 30 | Minimum pixel distance between symbol anchor points. Locations closer than this to an already-placed symbol are skipped (in geonames priority order). |
| countries | (string) or string | - | One or more ISO 3166-1 alpha-2 country codes to restrict the result set. |
| symbol | (string) | - | Default symbol IRI used when symbols is absent. |
| symbols | [AttributeSelection] or {string:[AttributeSelection]} | - | Symbol selection by population, or a map from geonames feature codes (e.g. PPLC, ADM2) to per-code selections. |
| label | LabelConfig | - | Optional text label placement. Omitting this key disables labels entirely (backward-compatible). |
| pan_invariant | bool | false | When true, the candidate set used for placement is computed from a margin-extended version of the request bbox so cities just outside the visible area still influence visible cities' placement decisions. The result: label-direction decisions are stable when the request bbox is shifted (panning) at the same zoom + projection. Required for tile rendering and for WMS viewer software that pans by issuing successive overlapping GetMap requests. Default false for backward compatibility. |
| placement_margin | double | - | Optional explicit buffer in image pixels for pan_invariant mode. When unset the margin is auto-computed from mindistance, free_space_radius, label width, and a safety buffer (≈ 250 px for typical settings). Increase for tighter invariance at the visible edges; decrease if the auto value is too generous and you'd rather save the projection cost. |
SVG symbols requirements. All <symbol> definitions referenced from the location layer must (1) place their geometry around the origin (e.g. cx="0" cy="0" for a circle, or x="-N" y="-N" for a rect) and (2) declare overflow="visible". The plugin renders <use x="..." y="..."/> at each location's projected anchor; symbols with non-origin geometry shift off-anchor, and symbols without overflow="visible" get clipped by the implicit symbol viewport. See the test products under test/dali/customers/test/products/location_labels_*.json for the required pattern.
Render order. Within the location layer, labels are emitted first (halo + fill) and markers last, so the marker stays visually clean even when the label halo would otherwise bleed onto it. Labels and markers for the same candidate render in reverse-priority order (highest-priority last) so on overlap the more important label stays on top. A marker is suppressed when its label is dropped — an isolated marker on a dense map is unreadable.
LabelConfig settings
The label object configures how location names are placed as SVG text next to their symbols. All label rendering happens inside the same SVG group as the symbols, so labels appear above symbols within the group but below layers placed after the location layer in the product.
The placement algorithms come from the cartographic literature. For most weather map use cases greedy (k=8) gives the best quality-to-cost ratio. Use simulated-annealing when label density is high and overlap quality matters more than rendering time.
LabelConfig — common settings
| Name | Type | Default | Description |
|---|---|---|---|
| algorithm | string | "none" | Placement algorithm. One of none, fixed, greedy, priority-greedy, simulated-annealing. |
| candidates | int | 8 | Number of candidate positions tried per label: 4, 8, or 16. Positions are tried in priority order. The order applies right beats above on ties: NE > SE > NW > SW > E > W > N > S. (Imhof 1975 published this exact priority but with NW > SE; we follow the modern dynamic-visualisation convention used in arxiv:1209.5765 where the right-versus-left preference dominates.) |
| offset | double | 5.0 | Pixel gap between the marker rectangle and the label bounding box edge. |
| symbol_size | double | - | Alias that sets both symbol_width and symbol_height to the same value. Convenient for symmetric markers (dots, circles). Not stored as a separate field; explicit width/height parsed afterwards override it. |
| symbol_width | double | 8.0 | Marker bounding-box width in pixels. The marker is assumed centered on the anchor with overflow=visible. The placement keeps labels off this rectangle; it acts as a hard obstacle in greedy and as an energy term in simulated-annealing. Set to 0 (together with symbol_height) to disable the obstacle entirely. |
| symbol_height | double | 8.0 | Marker bounding-box height in pixels. See symbol_width. |
| symbol_margin | double | 0.0 | Extra margin added on every side of the marker rectangle before obstacle testing. |
| bbox_padding | double | 0.0 | Pixels of padding applied to each label bbox during overlap testing only (visual position is unchanged). Absorbs sub-pixel font-metric drift between platforms (e.g. Rocky10 vs RHEL9 fontconfig). Applied symmetrically on all four edges. |
| bbox_quantum | int | 0 | If > 1, measured label width and height are rounded up to the next multiple of this many pixels before placement. Same purpose as bbox_padding: stabilises placement against tiny font-measurement variation. 0 disables quantisation. |
| font_family | string | "sans-serif" | SVG font-family attribute. |
| font_size | double | 11 | Default font size in pixels. |
| font_weight | string | "normal" | Default font weight ("normal" or "bold"). |
| fill | string | "#333333" | Default label text colour. |
| stroke | string | "white" | Halo colour rendered as a thick stroke behind the text. Set to "" to disable the halo. |
| stroke_width | double | 2.0 | Halo stroke width in pixels. |
| stroke_opacity | double | 0.75 | Halo stroke opacity (0 = transparent, 1 = opaque). |
| max_labels | int | 200 | Hard cap on the number of labels processed. Applied before placement; the highest-priority (geonames sort order) locations are kept. |
| free_space_weight | double | 0.0 | When > 0, each candidate's positions are reordered (greedy) or its position-penalty is reduced (SA) to prefer directions pointing into local empty space. Coastal cities push their labels into the sea instead of covering inland features. The "occupied" set includes both other markers and labels already placed earlier in the run. See algorithms documentation. |
| free_space_radius | double | 0.0 | When free_space_weight > 0, neighbours beyond this image-pixel distance are ignored when computing each candidate's free direction. 0.0 means use all candidates regardless of distance. A value of (e.g. 120) keeps the calculation local to each city's cluster. |
| priority_bucket_ratio | double | 1.0 | Used only by priority-greedy. When > 1, populations falling within the same log-bucket compare as equal and the within-bucket tiebreak prefers shorter labels (smaller label_w). Eliminates strict-population sensitivity (e.g. 100,000 vs 100,010 census difference flipping placement). 1.5 ≈ 10 % buckets, 2.0 = power-of-two buckets, 100 = decade buckets. |
| classes | array | [] | Population-based style overrides; see table below. First matching class wins. |
classes array entries (population-range style overrides):
| Name | Type | Default | Description |
|---|---|---|---|
| lolimit | double | - | Inclusive lower population bound for this class to apply. |
| hilimit | double | - | Exclusive upper population bound for this class to apply. |
| font_size | double | 0 | Font size override (0 = inherit LabelConfig default). |
| font_weight | string | "" | Font weight override ("" = inherit). |
| fill | string | "" | Text colour override ("" = inherit). |
| symbol_size | double | - | Per-class shorthand to override symbol_width and symbol_height together (matches the rendered marker for this population class). Useful when small cities use a small dot and large cities use a larger dot. |
| symbol_width | double | 0 | Per-class marker bounding-box width override (0 = inherit). Combined with the per-class symbol selection in LocationLayer.symbols so each population tier's marker has the right obstacle. |
| symbol_height | double | 0 | Per-class marker bounding-box height override (0 = inherit). |
Algorithm comparison gallery — same dataset (Finnish municipalities), same projection, different placement settings. See labeling_algorithms.md for the underlying theory.
| fixed NE position for everyone, no conflict avoidance. Drops labels that would cover another marker. ![]() |
greedy Imhof preference order, first non-overlap wins. Geonames priority order. ![]() |
priority-greedy Same as greedy but candidates pre-sorted by population descending. ![]() |
| simulated-annealing Global label-overlap minimisation. Best results for dense urban areas. ![]() |
greedy + free-space biasfree_space_weight: 1.5, free_space_radius: 120Coastal cities push labels into the sea. ![]() |
SA + free-space bias Same bias on top of SA's overlap optimisation. ![]() |
priority-greedy + bucketingpriority_bucket_ratio: 2.0Close-population cities tied; shorter label wins. ![]() |
greedy + pan-invariantpan_invariant: trueCentred on Jyväskylä. ![]() |
same, bbox shifted +0.5° Cities visible in both panned views land at identical offsets from their markers (verified per-city). ![]() |
Algorithm-specific settings:
fixed — Places every label at a single predetermined position relative to its symbol. No label-vs-label conflict detection (labels may overlap each other), but a label that would cover another marker is dropped — and that candidate's marker is dropped with it. Processes candidates in priority order so the highest-priority kept entry always wins.
| Name | Type | Default | Description |
|---|---|---|---|
| fixed_position | string | "ne" | One of n, ne, e, se, s, sw, w, nw. Follows Imhof preference naming. |
greedy — For each location in geonames priority order (highest-priority city first), tries up to candidates positions and places the label at the first position that does not overlap any already-placed label and fits within the map bounds. Dropped labels have no text rendered. This is the recommended algorithm for most weather map use cases.
priority-greedy — Identical to greedy but the input is re-sorted by population descending before placement. Use this when you need to guarantee that the largest cities get labels regardless of their geonames autocomplete rank.
simulated-annealing — Global near-optimum placement based on Christensen, Marks & Shieber (1995). Minimises total label-overlap area across all labels simultaneously. Slower than greedy (configurable number of iterations) but produces noticeably better results for dense urban areas. The RNG seed is fixed, so output is deterministic and the HTTP response cache works correctly.
LabelConfig — simulated-annealing sub-object
| Name | Type | Default | Description |
|---|---|---|---|
| iterations | int | 2000 | Number of SA steps. 2000 is sufficient for maps with up to ~150 labels. |
| initial_temperature | double | 1.0 | Starting temperature T₀. Higher values allow more uphill moves early in the search. |
| cooling_rate | double | 0.99 | Geometric cooling factor applied each iteration: T ← T × cooling_rate. |
| overlap_weight | double | 1.0 | Energy weight for label–label overlap area (in pixels²). |
| position_weight | double | 0.2 | Energy weight for position preference (index in Imhof order). Keeps labels near NE. |
Example — simulated annealing with population-scaled labels:
"label": {
"algorithm": "simulated-annealing",
"candidates": 8,
"offset": 4,
"font_family": "sans-serif",
"font_size": 10,
"fill": "#222222",
"stroke": "white",
"stroke_width": 2.5,
"stroke_opacity": 0.75,
"max_labels": 150,
"simulated_annealing": {
"iterations": 2000,
"initial_temperature": 1.0,
"cooling_rate": 0.99,
"overlap_weight": 1.0,
"position_weight": 0.2
},
"classes": [
{ "lolimit": 500000, "font_size": 13, "font_weight": "bold", "fill": "#111111" },
{ "lolimit": 50000, "font_size": 11 }
]
}
IsobandLayer
The isoband layer can be used to add filled contours (= isobands) to the view. Isobands can be used for presenting several kinds of data such as temperatures, rain amounts, cloudiness, ice cover, flash heatmap etc.
The tables below show simple examples on the usage of the isoband layer.
| Product configuration file | Produced image layer |
|---|---|
| /td> |
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the isoband layer in addition to the common layer attributes.
IsobandLayer
| Name | Type | Default Value | Description | |
|---|---|---|---|---|
| parameter | (string) | - | The parameter name for the isobands. | |
| level | double | linear | The querydata level value. By default the first level is used. | |
| interpolation | (string) | "linear" | The interpolation method. | |
| Value | Description | |||
| linear | Values are interpolated linearly. | |||
| midpoint|nearest|discrete | Nearest point value is used. | |||
| logarithmic | Logarithmic interpolation. Useful mostly for logarithmic parameters such as precipitation rate, which may have sharp peaks near strong showers. | |||
| isobands | [Isoband] | - | An array if Isoband structures./td> | |
| autoqid | (string) | - | Pattern for generating a qid for each isoband, for example "temperature_{}_{}"| | |
| autoclass | (string) | - | Pattern for generating a class for each isoband, for example "Temperature_{}_{}"| | |
| precision | (double) | 1.0 | Precision of SVG coordinates./td> | |
| unit_conversion | (string) | - | Name of desired unit conversion defined in the configuration file. | |
| multiplier | (double) | - | A multiplier for valid data values for unit conversion purposes. | |
| offset | (double) | 0.0 | An offset for valid data values for unit conversion purposes. | |
| smoother | Smoother | - | Smoother settings for the 2D grid data. | |
| sampling | Sampling | - | Sampling settings for the 2D grid data. | |
| extrapolation | int | 0 | How many grid cells to extrapolate data into regions with unknown values | |
| minarea | double | - | Mimimum area for polygons, including holes | |
| areaunits | (string) | km^2 | Units for minarea setting. km^2 or px^2 | |
| isofilter | Isofilter | - | Lowpass filter for isolines/isobands. Preferable to smoother settings when data resolution is high. | |
| heatmap | Heatmap | - | Heatmap settings. | |
| inside | Map | - | Intersect isoband with the map | |
| outsider | Map | - | Substract map from isoband. | |
| intersect | [Intersection] or Intersection | - | Alternate isoband(s) with which to intersect. | |
| closed_range | bool | true | True if the last isoband is a closed interval. For example for percentages one would want range 90 <= x <= 100 instead of 90 <= x < 100. | |
| validate | bool | false | True if the geometries are to be validated (slow, for debugging purposes) | |
| strict | bool | false | In strict mode invalid geometries cause an error. Extra information will be dumped to the journal and attached to the thrown exception | |
| desliver | bool | false | True if sliver polygons are to be removed | |
| subdivide | int | 0 | Bilinear cell subdivision for smoother isoband boundaries. 0 (default) keeps the classic linear marching-squares straight segment between each pair of cell edge intersections. N in [2..10] splits every interior level-curve segment into N chords with N-1 samples on the true bilinear level curve. N=1 is accepted as a no-op. Most visible when individual grid cells are large relative to screen pixels and have high corner-value contrast (e.g. a single high radar pixel surrounded by zeros turns from a sharp diamond into a rounded four-lobed blob). Forwarded to the contour engine and further to trax. | |
| subdivide_min_cell_pixels | double | 2.0 | Automatic sub-pixel gate on subdivide. When >0, the effective subdivision count is forced to 0 whenever the projected data cells are smaller than this many output pixels across their shorter dimension; interior bilinear samples would not be visible at that scale. Estimated from an approximate median of a 5x5 uniform sub-lattice of the projected coordinate matrix. Set to 0 to disable the gate entirely and always honour the subdivide value. | |
In automatic qid and class generation "." will be replaced by ",", since "." is reserved for JSON paths. If both the lower limit and upper limit are undefined, "nan" will be used to fill the pattern. If only the lower limit is missing, "-inf" will be used. If only the upper limit is missing, "inf" will be used.
IsolineLayer
The isoline layer can be used to add isolines to the view. Isolines can be used to present several kinds of data like pressure variations, altitude variations, etc.
The table below shows a simple example on the usage of the isoline layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the isoline layer in addition to the common layer attributes.
IsolineLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| parameter | (string) | - | The parameter name for the isolines. |
| level | (double) | - | The querydata level value. By default the first level is used. |
| interpolation | linear | (string) | linear or logarithmic |
| isolines | [Isoline] | - | An array of Isoline structures or set of parameters which define how array of Isoline structures are generated (see. isolines-attribute below) |
| autoqid | (string) | - | Pattern for generating a qid for each isoline, for example "temperature_{}" |
| autoclass | (string) | - | Pattern for generating a class for each isoline, for example "Temperature_{}" |
| precision | (double) | 1.0 | Precision of SVG coordinates. |
| unit_conversion | (string) | - | Name of desired unit conversion. Unit conversions are listed in the configuration file. |
| multiplier | (double) | 1.0 | A multiplier for valid data values for unit conversion purposes. |
| offset | (double) | 0.0 | An offset for valid data values for unit conversion purposes. |
| smoother | Smoother | - | Smoother settings for the 2D grid data. |
| sampling | Sampling | - | Sampling settings for the 2D grid data. |
| extrapolation | int | 0 | How many grid cells to extrapolate data into regions with unknown values. |
| minarea | double | - | Minimum area for closed linestrings in km^2. |
| areaunits | (string) | km^2 | Units for minarea setting. km^2 or px^2 |
| inside | Map | - | Intersect isoband with a map |
| outside | Map | - | Substract map from isoband |
| intersect | [Intersect] or Intersect | - | Alternate isoband(s) with which to intersect. |
| validate | bool | false | True if the geometries are to be validated (slow, for debugging purposes) |
| strict | bool | false | In strict mode invalid geometries cause an error. Extra information will be dumped to the journal and attached to the thrown exception |
| desliver | bool | false | True if sliver polygons are to be removed |
| subdivide | int | 0 | Bilinear cell subdivision for smoother isolines. 0 = off (classic linear marching squares). 2..10 inserts N-1 samples on the true bilinear level curve between consecutive cell edge intersections. See the isoband layer entry for details. |
| subdivide_min_cell_pixels | double | 2.0 | Automatic sub-pixel gate on subdivide. Effective subdivision is forced to 0 when projected data cells are smaller than this many output pixels. Set to 0 to disable. |
In automatic qid/class generation "." will be replaced by ",", since "." is reserved for JSON path definitions.
isolines-attribute
Isolines are defined by using isolines-attribute. There are two alternative ways to define the attribute
- Define explicitly array of Isoline structures
Example:
"isolines":
{
[
{
"qid": "pressure_950",
"value": 950,
"attributes": {}
},
{
"qid": "pressure_960",
"value": 960,
"attributes": {}
},
{
"qid": "pressure_970",
"value": 970,
"attributes": {}
},
{
"qid": "pressure_980",
"value": 980,
"attributes": {}
},
{
"qid": "pressure_990",
"value": 990,
"attributes": {}
},
{
"qid": "pressure_1000",
"value": 1000,
"attributes": {}
},
{
"qid": "pressure_1010",
"value": 1010,
"attributes": {}
},
{
"qid": "pressure_1020",
"value": 1020,
"attributes": {}
},
{
"qid": "pressure_1030",
"value": 1030,
"attributes": {}
},
{
"qid": "pressure_1040",
"value": 1040,
"attributes": {}
},
{
"qid": "pressure_1050",
"value": 1050,
"attributes": {}
},
]
}
- Define set of parameters which are used to generate Isoline structures. The following parameters must be defined:
| Name | Type | Description | Note | Example |
|---|---|---|---|---|
| qidprefix | string | Prefix of isoline layer id. | optional, default value is 'isoline_' | qidprefix="pressure_isoline_" |
| startvalue | int/double | Value of first Isoline. | startvalue=900 | |
| endvalue | int/double | Value of last Isoline | endvalue=1100 | |
| interval | int/double | The interval between two successive Isolines. | interval=10 | |
| except | int/double or array of ints/doubles | The interval is not shown if it is divisible by except value(s) (modulo-operator) | optional | except=5 or except=[3,5] |
Example:
{
"qid": "l4",
"layer_type": "isoline",
"isolines":
{
"qidprefix": "isoline_main_",
"startvalue": 900,
"endvalue": 1100,
"interval": 5
},
"css": "isolines/pressure.css",
"parameter": "Pressure",
"attributes":
{
"fill": "none",
"stroke": "#333",
"stroke-width": "1.5px"
}
},
{
"qid": "l5",
"layer_type": "isoline",
"isolines":
{
"qidprefix": "isoline_sub_",
"startvalue": 900,
"endvalue": 1100,
"interval": 2,
"except": 5
},
"css": "isolines/pressure.css",
"parameter": "Pressure",
"attributes":
{
"fill": "none",
"stroke": "red",
"stroke-dasharray": "10,5",
"stroke-width": "1.0px"
}
}
TFP metaparameter (isoband and isoline layers)
Both IsobandLayer and IsolineLayer accept "parameter": "TFP" as a metaparameter
that causes the layer to compute Hewson's Thermal Front Parameter on the fly from an
underlying scalar field before contouring. This is intended as a diagnostic
display — a clean isoband plot of TFP is one of the standard forecaster tools for
spotting frontal zones, moisture boundaries, jet-stream edges, and so on. It makes no
binary "is there a front here" decisions.
The underlying scalar field is configured in a "tfp" block on the layer:
| Name | Type | Default | Description |
|---|---|---|---|
| field | string | "PotentialTemperature" | The underlying scalar field to compute TFP on. Any scalar is admissible — θ, θw (PseudoAdiabaticPotentialTemperature), θe, humidity, wind speed, potential vorticity — and the choice determines what kind of feature the TFP highlights. |
| smoothing_passes | int | 3 | Number of separable 1-2-1 binomial smoother passes applied to the underlying field before TFP. TFP is a second derivative and strongly amplifies grid-scale noise; without smoothing the result is unreadable on typical NWP grids. |
| min_gradient | double | 0.0 | If non-zero, mask TFP to missing wherever ` |
TFP has units of (units of field) / m². For potential temperature that's K/m²; Hewson
scales by (100 km)² so "1 unit" in his paper equals 10⁻¹⁰ K/m². The supplied test
CSS (isobands/tfp.css) uses that scale.
Which underlying field to use depends on what you want to see:
| Field | Typical level | Highlights |
|---|---|---|
PotentialTemperature (θ) | 850 hPa | Classical thermal fronts. |
PseudoAdiabaticPotentialTemperature (θw) | 850 hPa | Moisture-aware fronts; Hewson's preferred choice. |
Humidity / specific humidity | 850 hPa | Dry lines, moisture boundaries. |
WindSpeedMS | 300 hPa | Jet-stream edges, jet streaks. |
| Potential vorticity | upper levels | Dynamical tropopause, PV-stream features. |
If you want objective front detection (not just a diagnostic display), the most
complete open implementation is Spensberger's dynlib (University of Bergen; Fortran
with Python bindings, includes Hewson-style routines tuned against ERA5 climatologies).
Schemm, Sprenger & Wernli 2018 is a simpler first-derivative alternative based on
|∇θe|. Neither is wrapped into this plugin; they would be the natural starting points
if automatic detection ever becomes a requirement.
Example (TFP of θ at 850 hPa):
{
"layer_type": "isoband",
"parameter": "TFP",
"level": 850,
"tfp":
{
"field": "PotentialTemperature",
"smoothing_passes": 3
},
"css": "isobands/tfp.css",
"isobands": "json:isobands/tfp"
}
IsolabelLayer
The isolabel layer is derived from the isoline layer, and thus inherits all its settings. The settings below are then available for positioning isovalues on the isolines. It is also possible to refer to the isolines that would be generated by an isoband layer, or to list to generate isovalues explicitly without referring to another layer.
IsolabelLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| label | Label | - | Label settings including the orientation |
| angles | [double] | [0,-45,45,180] | Orientations in which local maxima are searched for suitable label positions. |
| upright | bool | false | Optionally turn labels upright if their angle is outside -90...90 |
| max_angle | double | 60 | Allow label angles only in range -60...60 |
| min_isoline_length | double | 150 | Minimum length of the isoline in pixels for placing any labels |
| min_distance_other | double | 20 | Minimum distance to labels on isolines with another value |
| min_distance_same | double | 50 | Minimum distance to labels on isolines with the same value |
| min_distance_self | double | 200 | Minimum distance to labels on the same isoline segment |
| min_distance_edge | double | 10 | Minimum distance from the label centre to the image edge |
| max_distance_edge | double | 9999 | Maximum distance from the label centre to the image edge |
| max_curvature | double | 90 | Maximum integrated turn (degrees) along the polyline within ±stencil_size of the candidate |
| stencil_size | int | 5 | Search N adjacent points when looking for extrema and calculating the local curvature. |
| isovalues | [double] | - | Array of isovalue to be used. |
| isobands | [Isoband] | - | Array of isoband structures. All potential values will be extracted and inserted to the isovalues array. |
| isolines | [Isoline] | - | Array of isoline structures. All values will be extracted and inserted to the isovalues array. |
| textattributes | [Attributes] | - | Attributes to be assigned separately for each label |
The isovalues parameter may also be a JSON object with start, stop and step (default=1) doubles.
The placement algorithm — multi-angle local-extremum scan, divisibility-weighted MST selection, data-probed orientation — is documented in isolabel_algorithms.md, with a gallery of rendered tests showing each tunable in action.
SymbolLayer
The symbol layer can be used to add different kinds of symbols to the view. The symbols are added at the given locations and the type of the symbol depends on the value of the field defined by the "parameter" attribute. The actual symbol figures are stored in the file systems s SVG images.
The table below shows a simple example on the usage of the symbol layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the symbol layer in addition to the common layer attributes.
SymbolLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| parameter | (string) | - | The parameter for the symbols. |
| unit_conversion | (string) | - | Name of desired unit conversion. Unit conversions are listed in the configuration file. |
| multiplier | (double) | 1.0 | A multiplier for valid data values for unit conversion purposes. |
| offset | (double) | 0.0 | An offset for valid data for unit conversion purposes. |
| level | (double) | - | The querydata level value. By default the first level is used. |
| positions | Positions | - | The positions for the symbols. |
| minvalues | int | 0 | Minimum required number of valid values (or return error response) |
| maxdistance | double | 5 | Maximum distance for a station to be accepted close enough to the position. |
| dx | (int) | - | Common positional adjustment for all symbols. |
| dy | (int) | - | Common positional adjustment for all symbols. |
| scale | (double) | - | Scale factor for the symbols. |
| symbol | (string) | - | The default symbol. This is mostly useful for marking all the positions with a single symbol. |
| symbols | [AttributeSelection] | - | The symbols for different data values. |
| mindistance | int | - | Minimum distance in pixels between symbols. |
| priority | string or integer array | - | Priority order of symbols. |
| rendering_order | string | "normal" | Rendering order of the symbol is normal or reverse with respect to priority |
Note that assigning a proper scale for symbols with CSS or SVG attributes alone is difficult. Using the scale-attribute eases the scaling of the symbols.
Observation symbols (in particular lightning data) can be replayed in observation time order as an animated WebP image by setting "frames" in the product level "webp" tag, see WebP time animation.
mindistance and priority
Allowed priority values
| Value | Description |
|---|---|
| min | Symbols with smallest value are drawn first. |
| max | Symbols with biggest value are drawn first. |
| extrema | Symbols with biggest mean deviation drawn first. |
| none | Symbols are treated equally, there is no priority order. |
| [83,82,81] | Symbols with values 83,82,81 are drawn first in the order given, the rest have no priority order. |
Symbols are drawn on the map starting from the highest priority. If there already is a symbol nearby on the map (mindistance parameter), the symbol is not shown. Regarding mindistance and priority parameters the same logic is applied in Arrow-, Number- and CloudCeilingLayers.
ArrowLayer
The arrow layer is usually used for showing wind directions on the map. Technically it can be used for showing all kinds of directions.
The table below shows a simple example on the usage of the arrow layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the arrow layer in addition to the common layer attributes.
ArrowLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| direction | (string) | - | The parameter name for the arrow direction. |
| speed | (string) | - | The parameter name for speed, if one is needed. |
| u | (string) | - | The parameter name for the U-component of the speed vector. |
| v | (string) | - | The parameter name for the V-component of the speed vector. |
| fixeddirection | (double) | - | Fixed direction value for testing purposes. |
| fixedspeed | (double) | - | Fixed speed value for testing purposes. |
| level | (double) | - | The querydata level value. By default the first level is used. |
| symbol | (string) | - | The default symbol for the arrows. May be overridden in the arrows-definitions. The special value "windbarb" has not yet been implemented. |
| scale | (double) | - | Scale factor for the symbols |
| unit_conversion | (string) | - | Name of desired unit conversion. Unit conversions are listed in the configuration file. |
| multiplier | (double) | - | Multiplier for the speed parameter |
| offset | (double) | - | Offset for the speed parameter |
| minrotationspeed | (double) | - | Minimum required speed for the arrow to be rotated at all |
| dx | (int) | - | Common positional adjustment for all arrows. |
| dy | (int) | - | Common positional adjustment for all arrows. |
| southflop | boolean | false | Set to true if the arrow symbol should be flipped horizontally in the southern hemisphere (used for wind barbs). |
| northflop | boolean | false | Set to true if the arrow symbol should be flipped horizontally in the northern hemisphere. |
| flip | boolean | false | Set to true if the arrow symbol should always be flipped vertically. |
| positions | Positions | - | Arrow position generation definitions. |
| minvalues | int | 0 | Minimum required number of valid values (or return error response) |
| maxdistance | double | 5 | Maximum distance for a station to be accepted close enough to the position. |
| arrows | [AttributeSelection] | - | SVG attribute selection for speed dependent arrows. |
| mindistance | int | - | Minimum distance in pixels between arrows. |
| rendering_order | string | "normal" | Rendering order of the symbol is normal or reverse with respect to priority |
| priority | string or integer array | - | Priority order of arrows. |
Note: A direction parameter is sufficient to draw arrows. An additional speed component may be defined to style the arrows depending on the speed. Both U- and V-components must be specified if used, but these cannot be used simultaneously with the direction and speed parameters.
NumberLayer
The number layers is used to add numerical values for temperatures, pressures, rain amounts, etc. in the view.
The table below shows a simple example on the usage of the number layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the number layer in addition to the common layer attributes.
NumberLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| parameter | (string) | - | The parameter name for the numbers. |
| level | (double) | - | The querydata level value. By default the first level is used. |
| positions | Positions | - | The positions for the numbers. |
| minvalues | int | 0 | Minimum required number of valid values (or return error response) |
| maxdistance | double | 5 | Maximum distance for a station to be accepted close enough to the position. |
| label | Label | - | Label definitions. |
| numbers | [AttributeSelection] | - | SVG attribute selection based on the value of the label. |
| unit_conversion | (string) | - | Name of desired unit conversion. Unit conversions are listed in the configuration file. |
| multiplier | (double) | 1.0 | A multiplier for valid data values for unit conversion purposes. |
| offset | (double) | 0.0 | An offset for valid data for unit conversion purposes. |
| mindistance | int | - | Minimum distance in pixels between numbers. |
| rendering_order | string | "normal" | Rendering order of the symbol is normal or reverse with respect to priority |
| priority | string or integer array | - | Priority order of numbers. |
NullLayer
Null layers are merely placeholders for definitions which will be inserted into the block if a suitable STYLES option is used and the qid settings match. Sample use cases for null layers include
- enabling numbers over isobands
- enabling symbols over the underlying image
- enabling arrows over isobands
- enabling isolabels over isobands
CloudCeilingLayer
The cloud_ceiling layer is used for cloud ceiling observations (ie. the height of the base of the lowest clouds when the cloud amount is more than falf of the sky: broken, overcast or obscured)
The table below shows a simple example on the usage of the cloud_ceiling layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The cloudceiling layer is inherited from number layer so the same attributes are available. Label object has two new attributes 'padding_char' and 'padding_length' to format numbers.
StreamLayer
The streamline layer is used for visualizing directional parameters such as wind direction, wave direction, ice drift direction etc.
The table below contains a list of attributes that can be defined for the streamline layer in addition to the common layer attributes.
StreamLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| parameter | (string) | - | The parameter name for the direction. |
| u | (string) | - | Alternative U-component parameter for the direction. |
| v | (string) | - | Alternative V-component parameter for the direction. |
| min_length | (int) | 5 | Minimum generated stream line length in pixels. |
| max_length | (int) | 2048 | Maximum generated stream line length in pixels. |
| line_length | (int) | 32 | Length of a stream line segment in pixels. |
| xstep | (int) | 20 | Streamline start point step size in x-direction. |
| ystep | (int) | 20 | Streamline start point step size in y-direction. |
| precision | (double) | 1.0 | Precision of the generated coordinates. |
RasterLayer
The core concept of a raster layer is to render an image from a set of grid-based values, where each grid cell corresponds to a pixel in the resulting raster image. The color of each pixel is determined by mapping its associated value to a position within a predefined color map. This color map is essentially a list of value-color pairs, allowing for the identification of lower and upper bounds for each input value, along with their associated colors. By default, the final color is computed via linear interpolation between these bounds on a per-channel basis (A, R, G, B), resulting in a smooth color gradient. This technique eliminates hard boundaries between value regions, yielding a more continuous and visually intuitive representation.
The value-to-color mapping is handled by so-called painter elements, which can vary in type. The current implementation supports the following painter types.
-
Painter: "default"
The "default" painter, utilizes a full value-to-color list to perform interpolation across multiple defined intervals. The relationships between values and the colors are defined in the colormap file. It is also possible to use colors without interpolation (smooth_colors=false), which makes them look like the vector based isobands. -
Painter: "range" The "range" painter functions almost similarly as the "default" painter. The difference is that the value ranges are part of the layer definition. Each range contains the minimum and maximum values of the dataset, along with their corresponding colors. In addition a range definition can contain colors that should be used above or below the given range. Usually this is the fastest way to define simple color ranges. For example, clouds can be painted so that only the opacity of the white color is changed according to the cloud coverage percent.
-
Painter: "stream" The "stream" painter has the same idea as the Stream Layer, but the stream lines are drawn by using raster points. The direction of the stream lines are painted by using the variation of the alfa channel. Notice that the "stream" painter is capable for painting animation steps, which means that it can paint stream lines differently in each loop steps. This creates a moving effect when multiple images are put into the same animation file (WebP -file). It is also possible to make stream lines move faster in certain areas and this way simulate different stream speeds in different areas.
-
Painter: "border" The "border" painter can be used for painting different kinds of border lines (like isolines). The current painter paints each border by using two colors: inside color and the outside color. This improves their visibility. For example, if the inside color is white and the outside color is black, then the border should be visible almost everywhere, because the background color cannot be dark and light at the same time. Notice that the "border" painter is also used as an additional painter for each layer. This means that in spite of that we have selected the "master painter" to be "default","range" or "stream", we can use the "border" painter with the same parameter. For example, if we use the "range" painter to paint clouds, we might want to make cloud borders more visible by using "data_borders" definition with the current painter. This definition contains the same attributes that would be given normally to the "border" painter.
-
Painter: "shadow" The "shadow" painter can be used for painting shadows for the actual data. For example, we can paint shadows for clouds or raining areas. The basic idea is that we pick data ranges from the actual and paint this data with party transparent colors. In addition we shift these areas so that they are painter a little bit different location than the original data. This creates quite nice shadow effects. Notice that the "shadow" painter is also used as an additional painter for each layer. This means that in spite of that we have selected the "master painter" to be "default","range" or "stream", we can use the "shadow" painter with the same parameter. For example, if we use the "range" painter to paint clouds, we might want to create some shadows for these cloud by using "data_shadows" definition with the current painter. This definition contains the same attributes that would be given normally to the "shadow" painter.
Product configuration file
{
"title": "Temperature",
"abstract": "Temperature",
"source": "grid",
"producer": "ec",
"projection": {},
"animation" :
{
"enabled" : false, // Enables/disables the animation
"timesteps" : 24, // Number of animation timesteps
"timestep_interval" : 90, // Frame interval time (= milliseconds) between timesteps
"data_timestep" : 20, // Timestep length (= minutes)in in the actual data
"loopsteps" : 1, // Number of animation steps inside the same timestep
"loopstep_interval" : 70 // Frame interval time (=milliseconds) between loopsteps
},
"views":
[
{
"layers":
[
{
// ************************************************************
// Temperature
// ************************************************************
// This demonstrates the usage of the "default" data painter,
// which paints data values according to color definitions
// specified in the "colormap" file.
// ************************************************************
"layer_type" : "raster",
"qid" : "temperature-1",
"compression" : 1,
"visible" : true,
"parameter" : "temperature",
"interpolation" : "linear",
"data_painter" : "default",
"data_opacity_land" : 1.0,
"data_opacity_sea" : 1.0,
"colormap" : "temperature",
"smooth_colors" : true,
"land_position" : "bottom",
"land_color" : "FFD0D0D0",
"sea_position" : "top",
"sea_color" : "FFCCE0F8",
"land_border_position" : "none",
"land_border_color" : "80000000",
"sea_border_color" : "80FFFFFF",
"land_shading_position" : "top",
"land_shading_light" : 128,
"land_shading_shadow" : 384,
"sea_shading_position" : "top",
"sea_shading_light" : 128,
"sea_shading_shadow" : 384
}
,
{
// ************************************************************
// High clouds
// ************************************************************
// This demonstrates the usage of the "range" data painter,
// which is almost the same as the "default" data painter. The
// main diffence is that it does not have colormaps, but it
// defines similar color ranges in the "ranges" array. In
// addition it can define colors that are outside of the given
// range (color_low,color_high). This is a fast way to define
// colors for data that does not require many color ranges.
//
// Notice that in this example we use interpolation method
// "transfer" that tries to calculate cloud transitions
// with a different algorithm (i.e. it does not use linear
// interpolation. The same algorithm can be used with rain.
// ************************************************************
"layer_type" : "raster",
"qid" : "high-clouds-1",
"visible" : true,
"compression" : 1,
"parameter" : "NH-PRCNT:ECGMTA:1008:6:0:1",
"interpolation" : "transfer",
"data_painter" : "range",
"data_opacity_land" : 1.0,
"data_opacity_sea" : 1.0,
"ranges" : [
{
"value_min" : 10,
"value_max" : 100,
"color_min" : "20FFFFFF",
"color_max" : "FFFFFFFF",
"color_low" : "00000000",
"color_high" : "00000000"
}
]
,
"data_shadows" : [
{
"value_min" : 20,
"value_max" : 100,
"color_min" : "30000000",
"color_max" : "30000000",
"dx" : 5,
"dy" : 5
}
]
,
"land_position" : "none",
"land_color" : "FF404040",
"sea_position" : "none",
"sea_color" : "FFCCE0F8",
"land_border_position" : "top",
"land_border_color" : "20000000",
"sea_border_color" : "20FFFFFF",
"land_shading_position" : "none",
"land_shading_light" : 256,
"land_shading_shadow" : 384,
"sea_shading_position" : "none",
"sea_shading_light" : 128,
"sea_shading_shadow" : 384
}
]
}
]
}
The table below contains a list of attributes that can be defined for the raster layer in addition to the common layer attributes.
RasterLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| parameter | (string) | - | The parameter name for the direction. |
| compression | (int) | 1 | Compression rate (1 = fast,low compression, 9 = slow, high compression). |
| interpolation | (string) | linear | Interpolation method when fetching grid pixels (linear / nearest / transfer). |
| data_painter | (string) | default | Painter element (default / range / stream / border / shadow / null). |
| data_opacity_land | (double) | 1.0 | Opacity of the painted parameter over the land (0 .. 1). |
| data_opacity_sea | (double) | 1.0 | Opacity of the painted parameter over the sea (0 .. 1). |
The raster layer can paint land and sea colors above or below of the painted parameter. This allows you to create some nice effects. For example, if you want to make sea areas darker than the land areas the you can paint sea areas above the parameter layer by using colors with some transparency (for example 20000000).
| Name | Type | Default value | Description |
|---|---|---|---|
| land_position | (string) | none | Land layer position compared to the painted parameter (none,bottom,top). |
| land_color | (ARGB) | 00000000 | Land layer color. |
| sea_position | (string) | none | Sea layer position compared to the painted parameter (none,bottom,top). |
| sea_color | (ARGB) | 00000000 | Sea layer color. |
| land_border_position | (string) | none | Land border position compared to the painted parameter (none,bottom,top). |
| land_border_color | (ARGB) | 00000000 | Land border color. |
| sea_border_color | (ARGB) | 00000000 | Sea border color. |
The raster layer can paint topographical shadings above or below the painted parameter.
| Name | Type | Default value | Description |
|---|---|---|---|
| land_shading_position | (string) | none | Land shading layer position compared to the painted parameter (none,bottom,top). |
| land_shading_light | (int) | 0 | Multiplier for light areas (0..1000). |
| land_shading_shadow | (int) | 0 | Multiplier for shadow areas (0..1000). |
| sea_shading_position | (string) | none | Sea shading layer position compared to the painted parameter (none,bottom,top). |
| sea_shading_light | (int) | 0 | Multiplier for light areas (0..1000). Bigger value inceases the lightness. |
| sea_shading_shadow | (int) | 0 | Multiplier for shadow areas (0..1000). Bigger value inceases the darkness. |
The raster layer can create borders for the actual parameter. Technically this uses the "border" painter functionality, but in this case is more like an addition to the current painter.
| Name | Type | Default value | Description |
|---|---|---|---|
| data_borders | (struct) | This is a structure that contain same attributes as the "border" painter uses. |
The raster layer can create shadows for the actual parameter. Technically this uses the "shadow" painter functionality, but in this case is more like an addition to the current painter.
| Name | Type | Default value | Description |
|---|---|---|---|
| data_shadows | (struct) | This is a structure that contain same attributes as the "shadow" painter uses. |
Attributes for the painter "default".
| Name | Type | Default value | Description |
|---|---|---|---|
| colormap | (string) | Name of the colormap for "default" painter. This refers to a colormap file. | |
| smooth_colors | (bool) | true | Should the painter use "smooth colors" i.e. linarly interpolate colors. |
Attributes for the painter "range".
| Name | Type | Default value | Description |
|---|---|---|---|
| ranges | (array) | Array of range structures. |
Attributes for the "range" structure.
| Name | Type | Default value | Description |
|---|---|---|---|
| value_min | (float) | The minimum value of the value range. | |
| value_max | (float) | The maximum value of the value range. | |
| color_min | (ARGB) | 00000000 | The color used with the minimum value (min_value). |
| color_max | (ARGB) | 00000000 | The color used with the maximum value (max_value). |
| color_low | (ARGB) | 00000000 | The color used with values that are smaller than the minimum value (min_value). |
| color_high | (ARGB) | 00000000 | The color used with values that are bigger than the maximum value (max_value). |
Attributes for the painter "stream".
| Name | Type | Default value | Description |
|---|---|---|---|
| stream_color | (RGB) | 000000 | The color used for stream lines. |
| trace_length_min | (int) | 10 | Minimum trace length of the stream line. |
| trace_length_max | (int) | 128 | Maximum trace length of the steram line. |
| trace_step_x | (int) | 10 | Stream line trace start point step size in x-direction. |
| trace_step_y | (int) | 10 | Stream line trace start point step size in y-direction. |
| line_length | (int) | 16 | Length of the line segment. |
Stream lines can use colors for indicating the speed of the stream. In this case the 'parameter' attribute is replaced with 'speed' and 'direction' attributes. In addition, the 'colormap' attribute is used for selecting stream color according to the speed value.
| Name | Type | Default value | Description |
|---|---|---|---|
| direction | (string) | - | The parameter name for the direction. |
| speed | (string) | - | The parameter name for the speed. |
| colormap | (string) | Name of the colormap for "default" painter. This refers to a colormap file. | |
| smooth_colors | (bool) | true | Should the painter use "smooth colors" i.e. linarly interpolate colors. |
If the data is given as 'parameter' attribute (without speed information) then the painter needs line colors.
| Name | Type | Default value | Description |
|---|---|---|---|
| line_color_land | (ARGB) | FF000000 | The stream line color on the land area. |
| line_color_sea | (ARGB) | FF000000 | The stream line color on the sea area. |
Attributes for the "border" painter.
| Name | Type | Default value | Description |
|---|---|---|---|
| borders | (array) | The array of "border" structures. |
Attributes for the "border" structure.
| Name | Type | Default value | Description |
|---|---|---|---|
| inside_value_min | (float) | The values bigger than this are inside the given border. | |
| steps | (int) | The number of steps (we can paint multiple borders by using steps). | |
| step_increase | (float) | After each step the "inside_value_min" is increased with this number. | |
| inside_color | (ARGB) | 00000000 | The border color inside the curren area. |
| outside_color | (ARGB) | 00000000 | The border color outside the current area. |
Attributes for the "shadow" painter.
| Name | Type | Default value | Description |
|---|---|---|---|
| shadows | (array) | The array of "shadow" structures. |
Attributes for the "shadow" structure.
| Name | Type | Default value | Description |
|---|---|---|---|
| value_min | (float) | The minimum value of the value range. | |
| value_max | (float) | The maximum value of the value range. | |
| color_min | (ARGB) | 00000000 | The color used with the minimum value (min_value). |
| color_max | (ARGB) | 40000000 | The color used with the maximum value (max_value). |
| dx | (int) | 10 | The number of pixels that the shadow painting is shifted in x-direction . |
| dy | (int) | 10 | The number of pixels that the shadow painting is shifted in y-direction . |
LegendLayer
The legend layer can be used to define legends for the colors or symbols used in the product.
The table below shows a simple example on the usage of the legend layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the legend layer in addition to the common layer attributes.
LegendLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| x | int | 10 | X-coordinate of the first legend symbol. |
| y | int | 10 | Y-coordinate of the first legend symbol. |
| dx | int | 0 | X-coordinate offset to the next legend symbol. |
| dy | int | 20 | Y-coordinate offset to the next legend symbol. |
| symbols | LegendSymbols | - | Symbols to be used in the legend. |
| labels | LegendLabels | - | Label definitions for the legend. |
| isobands | [Isoband] | - | An array if Isobands structures from which to generate the legend |
TimeLayer
The time layer can be used in order to add the date and time information into the view.
The table below shows a simple example on the usage of the time layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the time layer in addition to the common layer attributes.
TimeLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| timezone | (string) | UTC | The time zone for the timestamp. |
| prefix | (string) | - | Prefix for the full string to be printed. |
| suffix | (string) | - | Suffix for the full string to be printed. |
| timestamp | (string) or [string] | validtime | Type of the timestamp. Valid values are "validtime", "origintime", "starttime", "endtime" and "wallclock". "now" is an alias for "wallclock". A duration can be shown instead by selecting one of "time_offset", "interval_start", "interval_end", "leadtime", "leadhour", an ISO time duration string or a signed integer with a time indicator suffix |
| format | (string) or [string] | "%Y-%m-%d %H:%M" | Format of the timestamp. See Boost time formatting flags. |
| x | (int) | - | X-position of the timestamp. If the value is negative then the position is counted from the right edge. |
| y | (int) | - | Y-position of the timestamp. If the value is negative then the position is counted from the bottom edge. |
Both timestamp and format may be arrays, in which case their lengths should match. The formatted strings will be concatenated to the prefix and the suffix will be appended to the result. This feature can be used to print strings such as origintime + leadtime, starttime - endtime etc.
Times and time durations are formatted according to Boost.Date_time time formatter flag specifications.
TagLayer
The tag layer can be used to add different kinds of SVG elements such as lines, rectangles, circles, text, etc. in the view.
The table below shows a simple example on the usage of the tag layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
| Produced SVG file | |
|
The table below contains a list of attributes that can be defined for the tag layer in addition to the common layer attributes.
TagLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| tag | (string) | - | The SVG tag to generate, for example "text" or "g". |
| cdata | (string) | - | The CDATA section for the tag. This should not be used if the tag is stand-alone. |
WKTLayer
The WKT layer can be used insert an arbitrary geometry into the image in the Well Known Text format.
The table below shows a simple example on the usage of the WKT layer.
{
"title": "WKT data",
"projection":
{
"xsize": 500,
"ysize": 500,
"crs": "EPSG:3035",
"bboxcrs": "WGS84",
"cx": 25,
"cy": 60,
"resolution": 10
},
"views": [
{
"qid": "v1",
"layers": [
{
"qid": "mymap",
"layer_type": "map",
"map":
{
"lines": true,
"schema": "natural_earth",
"minarea": 50,
"table": "admin_0_countries"
},
"attributes":
{
"id": "europe",
"stroke": "#333",
"stroke-width": "0.5px",
"fill": "none"
}
},
{
"layer_type": "wkt",
"qid": "test1",
// non segmented box around Finland
"wkt": "POLYGON((30 60, 30 70, 20 70, 20 60, 30 60))",
"attributes":
{
"fill": "none",
"stroke": "black",
"stroke-width": 1
}
},
{
"layer_type": "wkt",
"qid": "test2",
// a bit bigger box with segmentation to 100 km resolution
"wkt": "POLYGON((40 50, 40 75, 10 75, 10 50, 40 50))",
"resolution": 100,
"attributes":
{
"fill": "none",
"stroke": "red",
"stroke-width": 1
}
},
{
"layer_type": "wkt",
"qid": "test3",
// biggest box with segmentation to 20 pixel resolution
"wkt": "POLYGON((50 40, 50 80, 0 80, 0 40, 50 40))",
"relativeresolution": 20,
"attributes":
{
"fill": "none",
"stroke": "green",
"stroke-width": 1
}
}
]
}
]
}
The table below contains a list of attributes that can be defined for the WKT layer in addition to the common layer attributes.
WKTLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| wkt | (string) | - | The Well Known Text for the geometry. |
| precision | (double) | 1.0 | Precision of printed SVG coordinates. |
| resolution | (double) | - | The segmentation resolution in kilometers. |
| relativeresolution | (double) | - | The segmentation resolution in pixels. |
By default the WKT is not segmented into smaller linesegments. However, if the CRS of the image is not geographic, long straight lines in the WKT will not curve as expected unless the WKT is segmented into multiple parts in a resolution suitable for the output image. In the example above the black WKT is not segmented at all, the red one is segmented to 100 km resolution, and the green one to 20 pixel resolution.
WeatherFrontsLayer
The fronts layer renders cold, warm, occluded, and stationary fronts as SVG paths decorated
with meteorological symbols (triangles and semi-circles) at even arc-length intervals. The
symbols are rendered using a <textPath> element referencing a weather-font that maps
specific characters to the standard front glyphs.
Two data sources are supported via the source field:
source | Description |
|---|---|
"synthetic" | Curves defined inline in the product JSON (default). Useful for testing and for manually drawn analyses. |
"grid" | Fronts detected on the fly from querydata using Hewson's Thermal Front Parameter (TFP) method. |
Front types
| Type | JSON string | Default line class | Default glyph characters |
|---|---|---|---|
| Cold front | "cold" | coldfront | C / c |
| Warm front | "warm" | warmfront | W / w |
| Occluded | "occluded" | occludedfront | O / o |
| Stationary | "stationary" | stationaryfront | S / s |
| Trough | "trough" | trough | T / t |
| Ridge | "ridge" | ridge | R / r |
Symbols are rendered as inline SVG shapes (filled triangles and semicircles) — no external font is required. Uppercase glyph characters produce symbols on the left side of the directed path; lowercase on the right side. The layer places alternating symbols at even arc-length intervals using the spacing parameters below.
| Glyph char | Shape |
|---|---|
C | Filled triangle pointing left of path (cold front active side) |
c | Filled triangle pointing right of path (cold front passive side) |
W | Filled semicircle bulging left of path (warm front active side) |
w | Filled semicircle bulging right of path (warm front passive side) |
Compound glyphs ("CW", "Cw", etc.) place multiple shapes at the same position, used for
occluded and stationary fronts.
WeatherFrontsLayer settings
| Name | Type | Default | Description |
|---|---|---|---|
| front_source | string | "synthetic" | Data source. Currently only "synthetic" is implemented; the field is retained as an extensibility point for future sources such as WOML. (Named front_source rather than source because the latter is reserved for the paraminfo data source.) |
| fronts | array | [] | Front curve definitions (used when front_source is "synthetic"). See below. |
| styles | object | – | Per-type style overrides. Keys are front type strings (e.g. "cold"). See below. |
For automatic front diagnosis from gridded data, use the TFP metaparameter with
IsobandLayer or IsolineLayer (documented below). That produces Hewson's Thermal
Front Parameter field directly as an isoband/isoline plot, which is how TFP is used in
operational forecasting — as a diagnostic aid for a trained eye, not as an automatic
detector.
fronts array (synthetic source)
Each element in the fronts array defines one front curve:
| Name | Type | Default | Description |
|---|---|---|---|
| type | string | "cold" | Front type: "cold", "warm", "occluded", "stationary", "trough", or "ridge". |
| side | string | "left" | Which side the symbols face: "left" or "right". |
| points | array | – | Array of [longitude, latitude] pairs (WGS84) defining the front path. Required. |
styles object
The styles object maps front type names to style overrides. Any omitted field keeps its
default value.
| Name | Type | Default | Description |
|---|---|---|---|
| line_css | string | (type-specific) | CSS class name applied to the <use> stroke path. |
| glyph_css | string | (type-specific) | CSS class name applied to the <text> glyph group. |
| glyph1 | string | (type-specific) | Character rendered at odd-numbered symbol positions. |
| glyph2 | string | (type-specific) | Character rendered at even-numbered symbol positions. |
| font_size | double | 30.0 | Glyph font size in pixels. |
| spacing | double | 60.0 | Arc-length spacing between glyph centres in pixels. |
Example 1 – synthetic fronts (cold, warm, occluded, stationary)
The product JSON that produced the image above:
{
"title": "Weather fronts – synthetic example",
"projection": {
"xsize": 700, "ysize": 500,
"crs": "EPSG:3857", "bboxcrs": "WGS84",
"x1": -2, "y1": 47, "x2": 42, "y2": 67
},
"views": [{
"qid": "v1",
"layers": [
{ "layer_type": "background",
"attributes": { "fill": "rgb(190,230,255)" } },
{ "qid": "land", "layer_type": "map",
"map": { "schema": "natural_earth", "table": "admin_0_countries", "minarea": 50 },
"attributes": { "fill": "rgb(220,220,210)", "stroke": "none" } },
{ "qid": "borders", "layer_type": "map",
"map": { "lines": true, "schema": "natural_earth",
"table": "admin_0_countries", "minarea": 50 },
"attributes": { "fill": "none", "stroke": "#555", "stroke-width": "0.5px" } },
{
"qid": "wf",
"layer_type": "fronts",
"css": "fronts/fronts.css",
"front_source": "synthetic",
"fronts": [
{ "type": "cold", "side": "left",
"points": [[6,56.5],[8.5,55.5],[11,54.5],[14.5,54],[18,54.5],[21.5,55.5],[24.5,57],[27,59]] },
{ "type": "warm", "side": "right",
"points": [[6,56.5],[5.5,58.5],[6,60.5],[7.5,62.5],[9,64],[11,65]] },
{ "type": "occluded", "side": "left",
"points": [[6,56.5],[5,54.5],[4.5,52],[5,50],[7,48.5]] },
{ "type": "stationary", "side": "left",
"points": [[27,59],[30,59.5],[33,60.5],[36,62],[38,63.5]] }
]
}
]
}]
}
The CSS file (fronts/fronts.css) defines the stroke colours for the front lines and the
fill colour for the symbol shapes:
.coldfront { fill: none; stroke: #0044cc; stroke-width: 2.5; }
.warmfront { fill: none; stroke: #cc2200; stroke-width: 2.5; }
.occludedfront { fill: none; stroke: #880088; stroke-width: 2.5; }
.stationaryfront { fill: none; stroke: #444444; stroke-width: 2.0; }
.trough { fill: none; stroke: #996600; stroke-width: 1.5; stroke-dasharray: 6 3; }
.ridge { fill: none; stroke: #006622; stroke-width: 1.5; stroke-dasharray: 6 3; }
.coldfrontglyph { fill: #0044cc; stroke: none; }
.warmfrontglyph { fill: #cc2200; stroke: none; }
.occludedfrontglyph { fill: #880088; stroke: none; }
.stationaryfrontglyph { fill: #444444; stroke: none; }
.troughglyph { display: none; }
.ridgeglyph { display: none; }
HovmoellerLayer
A Hovmöller layer renders a two-dimensional time–space cross-section (or vertical cross-section) as a grid of coloured rectangles inside an ordinary SVG product. Unlike all other layers the coordinate axes are not a geographic map projection. Five slice geometries are supported:
direction | X axis (columns) | Y axis (rows) | Fixed coordinate(s) |
|---|---|---|---|
time_lon | Longitude, evenly spaced | Time steps | Latitude, pressure level |
time_lat | Latitude, evenly spaced | Time steps | Longitude, pressure level |
time_level | Pressure levels | Time steps | Latitude + longitude |
lon_level | Longitude, evenly spaced | Pressure levels | Latitude + valid time |
lat_level | Latitude, evenly spaced | Pressure levels | Longitude + valid time |
The three time_* directions (true Hovmöller diagrams) iterate all time steps available in
the querydata; the time query-string parameter is ignored for those. The two *_level
directions are vertical cross-sections (not strictly Hovmöller diagrams) that render the single
time step selected by the standard time= request parameter.
Each cell is coloured by matching the interpolated value against the first matching Isoband
definition, using the same isoband JSON and CSS files as IsobandLayer.
The canvas pixel dimensions are taken from the product-level projection.xsize /
projection.ysize. No geographic crs is required in the projection block.
HovmoellerLayer settings
| Name | Type | Default | Description |
|---|---|---|---|
| parameter | string | – | Parameter name, e.g. "GeopHeight" or "Temperature". Required. |
| level | double | – | Pressure level in hPa for time_lon and time_lat. Inherited from the shared Properties block so it can also be set via the level= query-string parameter. |
| direction | string | time_lon | Slice geometry: time_lon, time_lat, time_level, lon_level, or lat_level. |
| latitude | double | 60.0 | Fixed latitude for time_lon, time_level, lon_level. |
| longitude | double | 25.0 | Fixed longitude for time_lat, time_level, lat_level. |
| lon_min | double | 5.0 | Western longitude bound for time_lon and lon_level. |
| lon_max | double | 35.0 | Eastern longitude bound for time_lon and lon_level. |
| lat_min | double | 55.0 | Southern latitude bound for time_lat and lat_level. |
| lat_max | double | 70.0 | Northern latitude bound for time_lat and lat_level. |
| nx | integer | 50 | Number of evenly-spaced sample points along the space axis. Ignored for time_level (the number of levels in the data is used). |
| time_ascending | boolean | true | true = oldest time at the top (classic Hovmöller convention, time increases downward). false = newest time at the top. Has no effect on *_level directions. |
| level_ascending | boolean | false | false = surface (high hPa) on the left / top (meteorological convention). true = surface on the right / bottom. Applies to all *_level directions. |
| isobands | string | – | Path to the isoband colour definitions, e.g. "json:isobands/geoph500.json". |
| css | string | – | Path to the CSS stylesheet for the isoband classes. |
Notes on level ordering and sparse data
For all *_level directions the levels are drawn with surface (high hPa) on the left / top by
default (level_ascending=false). The data is commonly stored in ascending pressure order
(300, 500 … 1000 hPa); the layer reverses this automatically so 1000 hPa appears first.
With only six pressure levels (as in the test data) the cells are wide and the diagram is more schematic than meteorologically detailed, but it clearly shows the vertical structure. Finer resolution requires model-level or high-density pressure-level querydata.
Example 1 – time × longitude (classic Hovmöller)
Shows how 500 hPa geopotential height ridges and troughs propagate eastward along 60°N over 10 days.
{
"title": "Hovmoeller GeopHeight 500 hPa",
"producer": "ecmwf_skandinavia_painepinta",
"projection": { "xsize": 700, "ysize": 500 },
"views": [{
"layers": [{
"layer_type": "hovmoeller",
"parameter": "GeopHeight",
"level": 500,
"direction": "time_lon",
"latitude": 60.0,
"lon_min": 5.0,
"lon_max": 35.0,
"nx": 53,
"isobands": "json:isobands/geoph500.json",
"css": "isobands/geoph500.css"
}]
}]
}
Request: GET /dali?customer=…&product=hovmoeller_geoph500

Time increases downward (oldest at top). X axis: 5°E–35°E along 60°N. Colour scale: blue = trough / low GPH, red = ridge / high GPH.
Example 2 – time × latitude
Shows the meridional propagation of the 500 hPa ridge/trough pattern along 20°E.
{
"title": "Hovmoeller GeopHeight 500 hPa – time vs latitude",
"producer": "ecmwf_skandinavia_painepinta",
"projection": { "xsize": 700, "ysize": 500 },
"views": [{
"layers": [{
"layer_type": "hovmoeller",
"parameter": "GeopHeight",
"level": 500,
"direction": "time_lat",
"longitude": 20.0,
"lat_min": 52.0,
"lat_max": 70.0,
"nx": 57,
"isobands": "json:isobands/geoph500.json",
"css": "isobands/geoph500.css"
}]
}]
}
Request: GET /dali?customer=…&product=hovmoeller_geoph500_time_lat

Time increases downward (oldest at top). X axis: 52°N–70°N along 20°E.
Example 3 – time × pressure level
Shows the vertical thermal evolution at a fixed location (60°N, 20°E) over the forecast period. All levels in the querydata are used automatically.
{
"title": "Hovmoeller Temperature – time vs pressure level",
"producer": "ecmwf_skandinavia_painepinta",
"projection": { "xsize": 700, "ysize": 500 },
"views": [{
"layers": [{
"layer_type": "hovmoeller",
"parameter": "Temperature",
"direction": "time_level",
"latitude": 60.0,
"longitude": 20.0,
"isobands": "json:isobands/temperature_levels.json",
"css": "isobands/temperature_levels.css"
}]
}]
}
Request: GET /dali?customer=…&product=hovmoeller_temperature_time_level

Time increases downward (oldest at top). X axis: pressure levels from surface (1000 hPa, left) to upper troposphere (300 hPa, right). Colour scale: red = warm (surface, low levels), blue = cold (upper troposphere).
GraticuleLayer
The graticule layer is used to draw a latitude-longitude grid.
A sample configuration from the tests (graticule_num_cross):
{
"title": "Graticule demo",
"producer": "kap",
"language": "fi",
"projection":
{
"crs": "data",
"xsize": 500,
"ysize": 500
},
"views":
[
{
"qid": "v1",
"layers":
[
{
"qid": "l1",
"layer_type": "map",
"map":
{
"schema": "natural_earth",
"table": "admin_0_countries",
"minarea": 100
},
"attributes":
{
"id": "europe_country_lines",
"fill": "none",
"stroke": "#222",
"stroke-width": "1px"
}
},
{
"qid": "l2",
"layer_type": "graticule",
"attributes":
{
"font-family": "Roboto",
"font-size": 10,
"text-anchor": "middle"
},
"graticules":
[
{
"layout": "grid",
"step": 1,
"except": 10,
"attributes":
{
"fill": "none",
"stroke": "#888",
"stroke-width": "0.2px"
}
},
{
"layout": "grid",
"step": 10,
"attributes":
{
"fill": "none",
"stroke": "#000",
"stroke-width": "0.5px"
},
"labels":
{
"layout": "cross",
"dy": 5,
"attributes":
{
"fill": "black"
},
"textattributes":
{
"filter": "url(#rectbackground?border=black&background=white&borderwidth=1)"
}
}
}
]
}
]
}
]
}
The generated image is:

GraticuleLayer settings
| Name | Type | Default value | Description |
|---|---|---|---|
| graticules | [Graticule] | - | Vector of graticule definitions |
| mask | (string) | - | Optional mask to be used (for example "url(#alphadilation)" |
| mask_id | (string) | - | If empty, qid+mask will be used |
| precision | (double) | 1.0 | Coordinate output precision |
Graticule settings
| Name | Type | Default value | Description |
|---|---|---|---|
| layout | (string) | "grid" | Normal "grid" or "ticks" at the image edges |
| step | (integer) | 10 | Desired multiples in degrees |
| except | ([int)]) | - | Undesired multiples in degrees |
| length | int | 5 | Tick length in pixels when layout=ticks |
| attributes | (Attributes) | - | Presentation attributes for the lines |
| labels | (GraticuleLabels) | - | Optional label definitions for the lines |
GraticuleLabels
| Name | Type | Default value | Description |
|---|---|---|---|
| layout | (string) | "none" | Label layout algorithm: none |
| step | int | - | Desired multiples in degrees, by default inherited from Graticule settings |
| orientation | (string) | "horizontal" | Label orientation: horizontal |
| degree_sign | (bool) | true | If true, a degree sign will be appended to the number |
| minus_sign | (bool) | true | If false, N/S/W/E will be appended at the end |
| dx | (int) | 0 | X-offset when applicable in the selected layout |
| dy | (int) | 0 | Y-offset when applicable in the selected layout |
| attributes | (Attributes) | - | Presentation attributes for the group of labels |
| textattributes | (Attributes) | - | Presentation attributes for individual labels |
CircleLayer
The circle layer is typically used to draw circles of specific radius around airports to indicate a specific area of interest.
A sample configuration from the plugin tests:
{
"title": "Circles",
"projection":
{
"xsize": 500,
"ysize": 500,
"crs": "EPSG:3035",
"bboxcrs": "WGS84",
"cx": 25,
"cy": 60,
"resolution": 1
},
"views": [
{
"qid": "v1",
"layers": [
{
"qid": "mymap",
"layer_type": "map",
"map":
{
"lines": true,
"schema": "natural_earth",
"minarea": 50,
"table": "admin_0_countries"
},
"attributes":
{
"id": "europe",
"stroke": "#333",
"stroke-width": "0.5px",
"fill": "none"
}
},
{
"layer_type": "circle",
"qid": "c",
"places":
[
"Helsinki",
"Tampere",
"Turku"
],
"circles":
[
{
"radius": 20,
"attributes":
{
"stroke": "black",
"stroke-width": 1.5
}
},
{
"radius": 50,
"attributes":
{
"stroke": "black",
"stroke-width": 0.5
}
}
],
"attributes":
{
"fill": "none"
}
}
]
}
]
}
The image generated from the configuration file:
CircleLayer settings
| Name | Type | Default value | Description |
|---|---|---|---|
| places | [string] | - | List of location names |
| geoids | [int] | - | List of geonames.org IDs |
| keyword | (string) | - | Keyword for the database list of location names |
| features | (string) | - | List of feature names such as SYNOP,PPL etc (applies to places search only) |
| lines | (bool) | true | Treat the circles as polylines or as polygons |
| circles | Circle/[Circle] | - | One or several circle definitions |
| labels | (CircleLabels) | - | Optional circle radius labels |
Circle settings
| Name | Type | Default value | Description |
|---|---|---|---|
| radius | (double) | - | Circle radius in kilometers |
| attributes | (Attributes) | - | Presentation attributes for the circle |
CircleLabels settings
| Name | Type | Default value | Description |
|---|---|---|---|
| layout | [string] | - | Positions for labels (top, left, right, bottom, east, west, north, south) |
| attributes | (Attributes) | - | Common attributes for all labels |
| textattributes | (Attributes) | - | Attributes for individual labels |
| dx | (int) | 0 | Coordinate adjustment |
| dy | (int) | 0 | Coordinate adjustment |
| prefix | (string) | - | Prefix for the label, for example "R=" |
| suffix | (string) | - | Suffix for the label, for example " km" |
TranslationLayer
The translation layer can be used to create products that support multiple languages. When a product is requested we can use "language" parameter to define which language to use. In this case the "language" parameter overrides the value of the "language" attribute in the product file.
The table below shows a simple example on the usage of the translation layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
![]() |
The table below contains a list of attributes that can be defined for the translation layer in addition to the common layer attributes.
TranslationLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| tag | string | "text" | The SVG tag to be used. |
| translations | {string:string} | - | The text to be selected for the active language. The language code is the key, the value is the translation. |
WindRoseLayer
The windrose layer can be used to add "wind roses" into the view. A wind rose shows the average wind speed for different directions over the given time interval.
The table below shows a simple example on the usage of the windrose layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the windrose layer in addition to the common layer attributes.
WindRoseLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| timezone | string | "UTC" | The time zone used for the time settings. |
| starttimeoffset | int | 0 | Defines the (backward) time period with respect to the valid time of the actual time set for the layer. |
| endtimeoffset | int | 24 | Defines the (backward) time period with respect to the valid time of the actual time set for the layer. |
| windrose | WindRose | - | Wind rose appearance definitions. |
| stations | [Station] | - | The stations to use for the observations. |
| observations | [Observation] | - | The observations to be used. |
OSMLayer
The OSM layer renders OpenStreetMap data loaded into PostGIS via osm2pgsql. It supports polygon features (landuse, water, buildings), linear features (roads, coastlines, administrative boundaries) and place-name labels. Each layer response carries an HTTP ETag derived from the OSM data age, enabling efficient HTTP caching. The OGC API – Tiles endpoint also supports Mapbox Vector Tile (MVT/PBF) output via the addMVTLayer mechanism.
Data setup and scale guidance
OSM data is not bundled; it must be downloaded and imported separately. The appropriate data source depends heavily on the target map scale:
| Scale (km/px) | Recommended layer | Notes |
|---|---|---|
| > 5 km/px (continental/global) | MapLayer with Natural Earth | Natural Earth is already part of the GIS engine; no extra import needed. Better generalised than OSM for small-scale overviews. |
| 1–5 km/px (global overview) | Natural Earth or simplified OSM planet | A simplified OSM planet extract (e.g. osmdata.xyz land polygons, ~400 MB) is usable here but offers no significant advantage over Natural Earth. Full planet.osm.pbf (~80 GB uncompressed) is needed for global coverage below ~5 km/px. |
| 0.1–1 km/px (regional) | Regional Geofabrik extract | E.g. scandinavia-latest.osm.pbf (~350 MB). OSM PostGIS starts paying off in detail and feature richness below ~500 m/px. |
| < 0.1 km/px (city/local) | Full local OSM extract | Geofabrik city or sub-region extract; can include buildings, POIs, fine road network. |
Note: A simplified OSM planet extract is NOT a good substitute for Natural Earth at global scales (> 5 km/px). Its generalisation is inconsistent and file sizes are large for the quality achieved. Use Natural Earth (via
MapLayer) for global/continental backgrounds and reserve OSM PostGIS for regional or local detail layers.
Import command using osm2pgsql:
osm2pgsql -d smartmet -U smartmet \
--schema osm_scandinavia \
--middle-schema osm_scandinavia \
--output flex --style osm2pgsql-flex-config.lua \
scandinavia-latest.osm.pbf
osm2pgsql creates the standard tables planet_osm_polygon, planet_osm_line, planet_osm_point and planet_osm_roads inside the configured schema.
ETag / caching
Configure a timestamp_file path. After each osm2pgsql import, touch that file:
touch /var/smartmet/osm/scandinavia.timestamp
The layer uses the file's modification time as the data version. All tile responses for the same viewport return HTTP 304 Not Modified until the file is touched again.
Multi-resolution strategy
Use MapLayer (Natural Earth) for global context and switch to OSMLayer for regional/local detail:
| Resolution range | Layer type | Data source | Purpose |
|---|---|---|---|
| > 5 km/px | map (Natural Earth) | Built-in GIS engine | Global/continental background |
| 0.5–5 km/px | osm (simplified extract) | osmdata.xyz land polygons or similar | Coarse regional overview |
| < 0.5 km/px | osm (full regional extract) | Geofabrik regional .osm.pbf | Detailed local map |
CSS styling and themes
The css field of each feature set specifies the stylesheet file to load. The attributes.class field selects which CSS class from that stylesheet applies to the SVG elements rendered for that feature set. This means one CSS theme file can style all feature types:
{ "css": "osm/osm-bright", "attributes": { "class": "water" } }
See the OSM style themes section below for ready-made CSS files.
Sample configuration
The example below combines Natural Earth (global background) with an OSM PostGIS layer (regional detail). Both feature sets use the osm-bright theme from osm/osm-bright.css.
{
"layer_type": "group",
"layers": [
{
"comment": "Global background from Natural Earth — no OSM import needed",
"layer_type": "map",
"minresolution": 2.0,
"map": { "schema": "natural_earth", "table": "admin_0_countries", "minarea": 50 },
"attributes": { "fill": "rgb(220,220,210)", "stroke": "none" }
},
{
"comment": "Regional OSM detail — requires osm_scandinavia PostGIS schema",
"layer_type": "osm",
"maxresolution": 2.0,
"timestamp_file": "/var/smartmet/osm/scandinavia.timestamp",
"feature_sets": [
{
"pgname": "postgis",
"schema": "osm_scandinavia",
"table": "planet_osm_polygon",
"where": "natural = 'water'",
"mvt_layer_name": "water",
"css": "osm/osm-bright",
"attributes": { "class": "water" }
},
{
"pgname": "postgis",
"schema": "osm_scandinavia",
"table": "planet_osm_polygon",
"where": "landuse IN ('forest','wood')",
"mvt_layer_name": "landuse_forest",
"css": "osm/osm-bright",
"attributes": { "class": "forest" }
},
{
"pgname": "postgis",
"schema": "osm_scandinavia",
"table": "planet_osm_line",
"where": "highway IN ('motorway','trunk','primary','secondary')",
"lines": true,
"mvt_layer_name": "roads_major",
"css": "osm/osm-bright",
"attributes": { "class": "primary" }
},
{
"pgname": "postgis",
"schema": "osm_scandinavia",
"table": "planet_osm_point",
"where": "place IN ('city','town','village')",
"fieldnames": ["name", "place"],
"labels": true,
"mvt_layer_name": "place_labels",
"css": "osm/osm-bright",
"attributes": { "class": "place_label" }
}
]
}
]
}
Note: A rendered sample image will be added once OSM data has been imported. See the data setup instructions above.
OSMLayer supports two backends. The backend attribute selects which one is used; the required companion attributes differ between them.
PostGIS backend ("backend": "postgis", default): renders from osm2pgsql-imported PostGIS tables via the GIS engine. Requires feature_sets.
PMTiles backend ("backend": "pmtiles"): renders from a memory-mapped .pmtiles file managed by the OSM engine (engines/osm). Requires the OSM engine to be configured and loaded. For OGC Tiles / MVT requests the raw pre-encoded tile bytes are passed through directly — zero decoding or re-encoding. For WMS/SVG requests the tile is decoded via OGR. Requires source (the OSM engine source name).
The table below lists OSMLayer-specific attributes (in addition to the common layer attributes).
OSMLayer
| Name | Type | Default | Description |
|---|---|---|---|
| backend | string | "postgis" | Data backend: "postgis" (via GIS engine) or "pmtiles" (via OSM engine mmap). |
| source | string | - | Required for pmtiles backend. Source name as defined in the OSM engine configuration (e.g. "scandinavia"). |
| timestamp_file | string | - | PostGIS backend only. Path to a file whose modification time represents the OSM data age. Touch after each osm2pgsql import to invalidate HTTP ETags. For the pmtiles backend the ETag is derived from the .pmtiles file's own modification time automatically. |
| precision | double | 1.0 | Coordinate precision for SVG path output (PostGIS backend only). |
| feature_sets | [FeatureSet] | - | Required for postgis backend. Array of feature set definitions (see below). |
OSMLayer FeatureSet
| Name | Type | Default | Description |
|---|---|---|---|
| pgname | string | - | Required. PostGIS connection name as defined in the GIS engine configuration. |
| schema | string | - | Required. Database schema (e.g. osm_scandinavia). |
| table | string | - | Required. Table within the schema (e.g. planet_osm_polygon, planet_osm_line, planet_osm_point). |
| where | string | - | Optional SQL WHERE clause to filter rows (e.g. "natural = 'water'"). |
| fieldnames | [string] | [] | OSM tag columns to fetch and include as MVT feature attributes (e.g. ["name","highway"]). "name" is added automatically when labels is true. |
| lines | boolean | false | true → apply line clipping (for road/river geometries); false → apply polygon clipping. |
| labels | boolean | false | Render the name attribute as SVG <text> labels. Simple pixel-distance collision avoidance skips overlapping labels. In MVT output the name attribute is always included as a feature property for client-side label rendering. |
| minarea | double | - | Minimum polygon area (m²) filter passed to the GIS engine. |
| mindistance | double | - | Minimum distance simplification filter passed to the GIS engine. |
| mvt_layer_name | string | table name | Name of the layer in MVT output. Defaults to the table value. |
| css | string | - | CSS stylesheet file to load for this feature set. Combine with attributes.class to select which class from the file is applied to SVG elements. |
| attributes | Attributes | - | SVG attributes for the <g> group containing this feature set. Use "class" here to name the CSS class from the stylesheet; other attributes (e.g. "opacity") override or supplement the CSS. |
OSM style themes
Several ready-made CSS theme files are provided under layers/osm/. Each file defines CSS classes for common OSM feature types. Reference the same file in every feature set and set attributes.class to the appropriate feature class name.
Available themes (see file headers for full attribution):
| File | Based on | Character |
|---|---|---|
osm/osm-bright.css | OpenMapTiles OSM Bright | Colorful, classic cartographic style with coloured road hierarchy |
osm/positron.css | CartoDB Positron | Very light, minimal greyscale — designed as a neutral data backdrop |
osm/dark-matter.css | CartoDB Dark Matter | Dark/night style — high contrast for coloured overlays on dark background |
osm/osm-liberty.css | OSM Liberty | Free OSM Bright alternative; slightly cooler tones, same class names |
Note: These CSS files define the visual style but cannot be tested until OSM data has been imported into PostGIS. Class names are identical across all themes so switching themes is a one-line change (edit the
cssfield).
CSS class reference
All theme files define the following CSS classes:
| Class | OSM source | Geometry |
|---|---|---|
water | natural=water, waterway=* | Polygon |
forest | landuse=forest, landuse=wood, natural=wood | Polygon |
scrub | natural=scrub, natural=heath | Polygon |
grass | landuse=grass, landuse=meadow, natural=grassland | Polygon |
beach | natural=beach, natural=sand | Polygon |
glacier | natural=glacier | Polygon |
residential | landuse=residential | Polygon |
commercial | landuse=commercial, landuse=retail | Polygon |
industrial | landuse=industrial | Polygon |
park | leisure=park, leisure=recreation_ground, leisure=nature_reserve | Polygon |
hospital | amenity=hospital | Polygon |
school | amenity=school, amenity=university, amenity=college | Polygon |
airport | aeroway=aerodrome | Polygon |
buildings | building=* | Polygon |
motorway | highway=motorway | Line |
trunk | highway=trunk | Line |
primary | highway=primary | Line |
secondary | highway=secondary | Line |
tertiary | highway=tertiary | Line |
road | highway IN (residential, unclassified, living_street) | Line |
service_road | highway IN (service, track) | Line |
footway | highway IN (footway, cycleway, path) | Line |
railway | railway=rail | Line |
railway_transit | railway IN (subway, light_rail, tram) | Line |
coastline | natural=coastline | Line |
admin_country | boundary=administrative AND admin_level=2 | Line |
admin_state | boundary=administrative AND admin_level IN (3,4) | Line |
place_label | place IN (city, town, village, suburb) | Point / label |
road_label | Road labels | Line / label |
PostGISLayer
The PostGIS layer can be used to create image layers based on data queried from the PostGIS database.
The table below shows a simple example on the usage of the PostGIS layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the PostGIS layer in addition to the common layer attributes.
PostGISLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| lines | boolean | false | Should the data be handled as if it would be stroked or filled. This will alter how the data will be clipped to the view - polygons will either be preserved for filling or cut into polylines for stroking. |
| pgname | (string) | - | Identifier of PostGIS database. The GIS-engine configuration file defines connection info (host,database,username,password) for pgname. |
| schema | (string) | - | The database schema used in the database query. |
| table | (string) | - | The database table used in the database query. |
| geometry_column | (string) | - | The name of the geometry column in the specified schema.table |
| time_column | string | - | The name of the time column in the specified schema.table. |
| time_truncate | string | - | |
| filters | Filters | - | |
| precision | (double) | 1.0 | Precision of printed SVG coordinates. |
IceMapLayer
IceMapLayer inherits database-related functionality such as PostGISLayer definitions and filters from the PostGISLayer.
The table below shows a simple example on the usage of the PostGIS layer.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for the ice map layer in addition to the common layer attributes.
IceMapLayer
| Name | Type | Default Value | Description | |
|---|---|---|---|---|
| attribute_columns | (string) | - | Comma-separated list of columns in specified schema.table. | |
| firstname_column | string | - | Column name in specified schema.table. Primary name of the location, e.g. 'Helsinki'. This is used by 'named_location'-layer. | |
| secondname_column | (string) | - | Column name in specified schema.table.Secondary name of the location, e.g. 'Helsingfors'. This is used by 'named_location'-layer. | |
| symbol | A symbol. Position of symbol is fetched from database | The first parameter for PostGreSQL DATE_TRUNC-function. Supported values are 'minute', 'hour', 'day', 'week' ,'month', 'year'. If time_truncate is defined it is applied to requested datetime. | ||
| layer_subtype | (string) | "geometry" | Specifies the actual layer type. Full list of supported types is listed in the table below. If layer_subtype is missing geometry type is assumed. | |
| Name | Description | |||
| symbol | A symbol. Position of symbol is fetched from database. | |||
| named_location | Location with name. Position of location is fetched from database. symbol-property can be defined to mark the location on map. firstname_column, secondname_column can be defined to show location name on map. | |||
| coordinate_grid | Coordinate grid. Grid is drawn on requested geometry. If result set contains no geometry, grid is drawn on whole. | |||
| degree_of_pressure | Degree of pressure for ice. | |||
| mean_temperature | Ellipse type of label, where we have background ellipse and text written on . | |||
| label | Rectangle type of label, where we have a background rectangle and text written on it. Attributes for background rectangle and foreground text is defined in filters' 'attribute' and 'text_attribute' sections. '*'-character is a wildcard-character: The beginning of the name can be anything, as long as it ends with 'label'-string. | |||
| temperature_isotherm_label | Same as *label except that position (fetched from database) of label is modified a bit. | |||
| symbol | string | - | Name of file containing the symbol-definition in SVG-format. This is needed only if value of layer_subtype-property is 'symbol'. The requested geometry determines the position of the symbol. | |
| pattern | string | 0.0 | Name of the file containing the pattern definition in SVG-format. The pattern is applied on the requested geometry, for example diagonal hatching on polygonal area. | |
| precision | (double) | 1.0 | Precision of printed SVG coordinates. | |
FinnishRoadObservationLayer
FinnishRoadObservationLayer can be used to show weather conditions at road weather stations all over Finland.
The table below shows a simple example on the usage of FinnishRoadObservationLayer layer. Note! synop-font must be used to show the weather symbols correctly.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for finnish road observation layer.
FinnishRoadObservationLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| positions | Positions | - | The positions for the symbols. |
| label | Label | - | Label definitions. |
| maxdistance | double | 5 | Maximum distance for a station to be accepted close enough to the position. |
| mindistance | int | - | Minimum distance in pixels between symbols. |
| missing | int | 106 | Synop-font symbol for missing observations. Zero disables showing missing values. |
Algorithm to deduce weather condition symbol
Step 1) Get the following parameters at requested timestep:
- Mean air temperature (ILMA) of the observations from previous 24 hours
- Precipitation type (SADE) of the nearest observation, maximum 20 minutes away
- Precipitation form (RST) of the nearest observation, maximum 20 minutes away
Step 2) Get symbol for each station by using the following function, where 'r' is precipitation type and 'rform' is precipitation form:
if (r == 1)
{
if (rform == 9) return 51; // drizzle, not freezing, intermittent, slight
if (rform == 10) return 52; // rain, not freezing, intermittent, slight
if (rform == 11) return 53; // intermittent fall of snowflakes, slight
if (rform == 18) return 213; // drizzle, freezing, slight
if (rform == 19) return 223; // rain, freezing, slight
}
if (r == 2)
{
if (rform == 9) return 209; // drizzle, not freezing, intermittent, moderate
if (rform == 10) return 219; // rain, not freezing, intermittent, moderate
if (rform == 11) return 229; // intermittent fall of snowflakes, moderate
if (rform == 18) return 214; // drizzle, freezing, moderate or heavy
if (rform == 19) return 224; // rain, freezing, moderate or heavy
}
if (r == 3)
{
if (rform == 9) return 212; // drizzle, not freezing, continous, heavy
if (rform == 10) return 222; // rain, not freezing, continous, heavy
if (rform == 11) return 232; // continous flall of snowflakes, heavy
if (rform == 18) return 214; // drizzle, freezing, moderate or heavy
if (rform == 19) return 224; // rain, freezing, moderate or heavy
}
if (rform == 13) return 225; // rain or drizzle and snow, slight
if (rform == 14) return 244; // shower(s) of snow pellets or snow hail
if (rform == 15) return 233; // ice needles (with or without fog)
if (rform == 16) return 234; // snow grains (with or without fog)
if (rform == 17) return 236; // ice pellets (sleet)
// r 4,5,6 are redundant but re-check in case there is no match in the the cases above
if (r == 4) return 53; // intermittent fall of snowflakes, slight
if (r == 5) return 229; // intermittent fall of snowflakes, moderate
if (r == 6) return 232; // continous flall of snowflakes, heavy
Step 3) Get priority for each station by using the following function, where symbol is the symbol of the station and t2m is mean temperature of the station:
if(t2m <= 0)
{
// WINTER: t2m < 0
switch (symbol)
{
case 106:
return 0;
break;
case 53:
case 229:
return 1;
break;
case 232:
case 233:
case 234:
case 236:
case 244:
return 2;
break;
case 225:
return 5;
break;
case 51:
case 213:
return 6;
break;
case 209:
return 7;
break;
case 52:
case 212:
case 214:
case 223:
return 8;
break;
case 219:
return 9;
break;
case 222:
case 224:
return 10;
break;
}
}
else if(t2m < 10)
{
// SPRING OR AUTUMN: 0 > t2m < 10
switch (symbol)
{
case 106:
return 0;
break;
case 51:
case 52:
case 53:
case 213:
case 223:
case 225:
return 1; // slight
break;
case 209:
case 219:
case 229:
case 233:
case 234:
case 236:
case 244:
return 2; // moderate
break;
case 212:
case 214:
case 222:
case 224:
case 232:
return 4; // heavy
break;
}
}
else
{
// SUMMER: t2m >= 10
switch (symbol)
{
case 51:
case 52:
case 106:
return 0;
break;
case 209:
case 219:
return 1;
break;
case 212:
case 222:
return 2;
break;
case 213:
return 3;
break;
case 214:
return 4;
break;
case 225:
return 5;
break;
case 53:
case 223:
return 6;
break;
case 224:
case 229:
return 7;
break;
case 232:
return 8;
break;
case 233:
case 234:
case 236:
case 244:
return 9;
break;
}
}
Step 4) Show stations on the map starting from the highest priority. If there already is a station nearby on the map (mindistance parameter), don't show the station.
PresentWeatherObservationLayer
PresentWeatherObservationLayer can be used to show present weather conditions at Automatic Weather Stations (AWS).
The table below shows a simple example on the usage of PresentWeatherObservationLayer layer. Note! synop-font must be used to show the weather symbols correctly.
| Product configuration file | Produced image layer |
|---|---|
| ![]() |
The table below contains a list of attributes that can be defined for present weather observation layer.
PresentWeatherObservationLayer
| Name | Type | Default value | Description |
|---|---|---|---|
| positions | Positions | - | The positions for the symbols. |
| label | Label | - | Label definitions. |
| maxdistance | double | 5 | Maximum distance for a station to be accepted close enough to the position. |
| mindistance | int | - | Minimum distance in pixels between symbols. |
| missing | int | 106 | Synop-font symbol for missing observations. Zero disables showing missing values. |
Algorithm to deduce present weather symbol
Step 1) Get WaWa code (ww_aws) for the desired timestep
Step 2) Get symbol for each station by using the following get_symbol(float wawa-code) function:
float convert_possible_wawa_2_ww(float theValue)
{
static const float wwCodeArray[100] =
{0, 1, 2, 3, 4, 5, 0, 0, 0, 0,
10, 0,13, 0, 0, 0, 0, 0,18, 0,
28,21,20,21,22,24,29, 0, 0, 0,
42,41,43,45,47,48, 0, 0, 0, 0,
61,63,65,61,65,71,75,66,67, 0,
50,51,53,55,56,57,57,58,59, 0,
60,61,63,65,66,67,67,68,69, 0,
70,71,73,75,79,79,79,77,78, 0,
80,80,81,81,82,85,86,86, 0,89,
92,17,93,96,17,97,99, 0, 0, 8};
if(theValue >= 100 && theValue <= 199)
return wwCodeArray[static_cast(theValue-100)];
return theValue;
}
int get_symbol(float wawa)
{
float value = convert_possible_wawa_2_ww(wawa);
if(value != kFloatMissing && value > 3)
{
if(value == 99)
return 48; // synop fontin viimeinen arvo 255 osuu synop arvolle 98, onneksi myös 99 löytyy fontista, mutta sen sijainti on vain 48
else
return (157 + value);
}
return 106; // Missing symbol 'Z'
}
Step 3) Get priority for each station by using the following function:
int get_symbol_priority(int symbol)
{
switch (symbol)
{
case 106:
return 0;
break;
case 170:
case 171:
case 172:
case 173:
return 1;
break;
case 162:
case 163:
case 164:
case 165:
case 167:
case 168:
case 169:
case 174:
case 177:
case 178:
case 179:
case 180:
case 181:
case 182:
case 183:
case 184:
case 185:
case 186:
case 198:
case 207:
case 208:
case 213:
case 215:
case 217:
case 218:
case 227:
case 235:
case 242:
case 244:
case 246:
case 248:
case 249:
case 250:
case 251:
return 5;
break;
case 187:
case 188:
case 189:
case 193:
case 195:
case 197:
case 199:
case 200:
case 209:
case 210:
case 219:
case 220:
case 225:
case 228:
case 229:
case 233:
case 234:
case 236:
case 237:
case 240:
return 7;
break;
case 161:
case 175:
case 211:
case 216:
case 221:
case 223:
case 230:
case 231:
case 238:
case 252:
case 253:
return 8;
break;
case 201:
case 202:
case 203:
case 204:
case 205:
case 206:
case 212:
case 239:
case 241:
case 243:
case 245:
case 247:
return 9;
break;
case 166:
case 176:
case 190:
case 191:
case 192:
case 194:
case 196:
case 214:
case 222:
case 224:
case 226:
case 232:
case 254:
case 255:
case 48:
return 10;
break;
}
return 0;
}
MetarLayer
MetarLayer renders METAR aviation weather observations as standard meteorological station plots, one plot per airport. It queries raw TAC METAR (or SPECI) messages from the AVI engine and decodes them directly, so no pre-processing pipeline is required.
Each station plot can include:
- Wind barb — direction and speed in knots with standard meteorological barb notation
- Temperature / dew point — displayed in METAR convention (e.g.
03/M02) - Visibility — in metres, capped at 9999
- Present weather — raw TAC tokens (e.g.
-RA,TSRA,+SN) - Sky condition — cloud layers as amount+base (e.g.
BKN025) orCAVOK/SKC - QNH pressure — in hPa
- Wind gust — in knots when reported
- Aviation status box — colour-coded LIFR / IFR / MVFR / VFR category
Requires: The AVI engine (
engines/avi) must be configured and loaded. MetarLayer is compiled out whenWITHOUT_AVIis defined.
Aviation flight rule categories
The status box colour is derived from ceiling and visibility:
| Category | Colour | Ceiling | Visibility |
|---|---|---|---|
| LIFR (Low IFR) | magenta | < 500 ft | < 1600 m |
| IFR | red | 500–999 ft | 1600–4999 m |
| MVFR (Marginal VFR) | blue | 1000–2999 ft | 5000–7999 m |
| VFR | green | ≥ 3000 ft | ≥ 8000 m |
Sample configuration
{
"qid": "metar_obs",
"layer_type": "metar",
"message_type": "METAR",
"exclude_specis": true,
"font_size": 10,
"plot_size": 60,
"mindistance": 50,
"show_temperature": true,
"show_dewpoint": true,
"show_visibility": true,
"show_wind": true,
"show_pressure": true,
"show_gust": true,
"show_weather": true,
"show_sky_info": true,
"show_status": true,
"attributes": { "id": "metar_layer" }
}
To restrict the plot to specific airports or countries:
{
"layer_type": "metar",
"icaos": ["EFHK", "ESSA", "EKCH"],
"countries": ["FI", "SE", "NO", "DK"]
}
MetarLayer
| Name | Type | Default | Description |
|---|---|---|---|
| message_type | string | "METAR" | AVI message type to query: "METAR", "SPECI", "AWSMETAR", etc. |
| message_format | string | "TAC" | Message format. Currently only "TAC" (raw METAR text) is supported. |
| exclude_specis | boolean | true | Skip SPECI (special) reports when message_type is "METAR". |
| font_size | int | 10 | Base font size in pixels for all text elements in the station plot. |
| plot_size | int | 0 | Bounding-box side length in pixels. 0$ = \text{auto} (6 \times $font_size). |
| show_temperature | boolean | true | Show temperature in the upper-left quadrant of the station plot. |
| show_dewpoint | boolean | true | Show dew point below the temperature. |
| show_visibility | boolean | true | Show visibility in metres in the lower-left quadrant. |
| show_wind | boolean | true | Draw a wind barb at the station centre. |
| show_pressure | boolean | true | Show QNH pressure (hPa) in the upper-right quadrant. |
| show_gust | boolean | true | Show wind gust speed (kt) below the pressure when reported. |
| show_weather | boolean | true | Show present-weather TAC tokens (e.g. -RA, TSRA) below the station symbol. |
| show_sky_info | boolean | true | Show sky-condition string (CAVOK, SKC, or cloud layer list) in the lower-right quadrant. |
| show_status | boolean | true | Draw a colour-coded aviation status box (LIFR/IFR/MVFR/VFR) at the station centre. |
| color | string | - | Override all element colours with a single SVG colour value. When omitted each element uses its default colour. |
| mindistance | int | 0 | Minimum pixel distance between rendered station plots. Plots closer than this threshold are suppressed (most-recent observation wins). |
| icaos | [string] | - | Restrict rendering to the listed ICAO station codes. All stations are shown when omitted. |
| countries | [string] | - | Restrict rendering to stations in the listed ISO 3166-1 alpha-2 country codes (e.g. "FI", "SE"). All countries are shown when omitted. |
Structure definitions
The product, view and layer definitions contain several attributes of type structure. This section defines attributes used in these structures.
Attribute structure
The Attributes structure is used to define attributes for the current Scalable Vector Graphics (SVG) element. This structure has quite many elements such as Product, View, Layer, etc. In this way we can define the drawing colors or the line widths that are used when the related SVG element is created.
The Attribute definitions are just plain mappings from strings to strings, i.e.. the attribute names and values are directly copied into the related XML element in the SVG file.
Using an attribute name or a value that is not in the the SVG specification may result in errors in the SVG file created.
The Attributes structure can contain one or several attribute definitions. For example:
"attributes":
{
"stroke": "black",
"stroke-width": "0.1"
}
If the attribute value is a number, it can be given without the quotation marks. For example:
"attributes":
{
"stroke": "black",
"stroke-width": 0.1
}
However, it should be noted that the number will then also be printed as a number into the SVG properties. In this particular case it would be wiser to use the string form, since the number 0.1 is not exactly an IEEE-754 number, and we actually get something a bit different in the output. Note that the integers in general are safe to be used without quotes.
AttributeSelection structure
Sometimes there is a need to use different SVG attributes and symbols according to field values that are used for the image drawing. For example, if there is sunny weather in location X and rainy weather in location Y, we probably want to use different weather symbols and SVG attributes for these locations in our weather map.
The AttributeSelection structure encapsulates an Attributes structure, a symbol and its usage condition in the same structure. The usage condition defines when the symbol and the attributes can be used. There is usually one AttributeSelection structure available for each condition. For example, if we have a data field that can have five different values then we probably have the AttributeSelection structure for each of these values.
The table below contains a list of attributes that can be defined in the AttributeSelection structure.
AttributeSelection
| Name | Type | Default value | Description |
|---|---|---|---|
| value | (double) | - | The value to be matched. |
| lolimit | (double) | - | The lower limit to be matched. |
| hilimit | (double) | - | The upper limit to be matched. |
| symbol | (string) | - | The symbol name to be associated with the match. |
| attributes | Attributes | - | The SVG attributes associated with the match. |
EXAMPLE: Selecting a precipitation symbol according to a value:
[
{
"value": 0,
"symbol": "drizzle"
},
{
"value": 1,
"symbol": "rain"
},
...
{
"value": 6,
"symbol": "hail"
}
]
EXAMPLE: Selecting a weather symbols and attributes:
[
{
"value": 1,
"symbol": "sunny",
"attributes":
{
"class": "Sunny"
}
},
{
"value": 2,
"symbol": "partly_cloudy",
"attributes":
{
"class": "PartlyCloudy"
}
},
...
]
EXAMPLE: Selecting attributes according to a value range:
[
{
"lolimit": 3,
"hilimit": 5,
"attributes":
{
"class": "ModerateWindArrow_3_5"
}
},
{
"lolimit": 5,
"hilimit": 7,
"attributes":
{
"class": "ModerateWindArrow_5_7"
}
},
{
"lolimit": 7,
"hilimit": 10,
"attributes":
{
"class": "FreshWindArrow_7_10"
}
},
...
]
Projection structure
The Projection structure can be used to define a projection for a product, views or layers. The "projection" attribute is one of the shared attributes that is inherited from top to down in the product hierarchy. So, if the "projection" attribute is defined on the product level then all the views and the layers are using it, unless they do not override the value of this attribute.
The table below contains a list of attributes that can be defined in the Projection structure.
Projection
| Name | Type | Default value | Description |
|---|---|---|---|
| crs | (string) | - | The projection description as understood by GDAL, or "data" implying the projection of the selected producer. |
| bboxcrs | (string) | - | The projection description for defining the bounding box coordinates only. If not defined, the bounding box coordinates are expected to be in the same projection as the image. |
| xsize | (int) | - | Width of the projection in pixels. |
| ysize | (int) | - | Height of the projection in pixels. |
| x1 | (double) | - | Bottom left X-coordinate of the bounding box. |
| y1 | (double) | - | Bottom left Y-coordinate of the bounding box. |
| x2 | (double) | - | Top right X-coordinate of the bounding box. |
| y2 | (double) | - | Top right Y-coordinate of the bounding box. |
| cx | (double) | - | Center coordinate of the bounding box. |
| cy | (double) | - | Center coordinate of the bounding box. |
| resolution | (double) | - | The resolution of the image in kilometers. A resolution of 2.5 means one pixel is approximately 2.5 kilometers in the projection used. The description is not valid if the projection is geographic (latlon) instead of geometric. |
The bounding box of the projection can be defined in the following ways:
1. Bounding box with corners A bounding box is defined using the corner points and it should consist of "x1", "y1", "x2", "y2" and either "xsize" or "ysize". If both "xsize" and "ysize" are given, the aspect of the image may become distorted.
2. Bounding box with a center point A bounding box is defined using a center coordinate and it should consist of "cx", "cy", "resolution" and both "xsize" and "ysize".
3. Bounding box from data If "crs" = "data" is given and the a bounding box coordinates are not given, the bounding of the data will be used. The size still has to be defined separately.
CRS definitions
OGR/GDAL supports numerous ways to define a spatial reference. Currently OGRSpatialReference::SetFromUserInput supports the following methods:
- Well Known Text definition - passed on to importFromWkt().
- "EPSG:n" - number passed on to importFromEPSG().
- "EPSGA:n" - number passed on to importFromEPSGA().
- "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
- "urn:ogc:def:crs:EPSG::n" - ogc urns
- PROJ.4 definitions - passed on to importFromProj4().
- Filename - file read for WKT, XML or PROJ.4 definition.
- Well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83, WGS84 or WGS72.
- WKT (directly or in a file) in ESRI format should be prefixed with ESRI:: to trigger an automatic morphFromESRI().
- "IGNF:xxx" - "+init=IGNF:xxx" passed on to importFromProj4().
Defs structure
The Defs structure is used to define header information such as styles, symbols, paths not to be drawn directly, etc. for the generated SVG image.
The table below contains a list of attributes used in this structure.
Defs
| Name | Type | Default value | Description |
|---|---|---|---|
| qid | (string) | - | An identifier for the definitions section. Usually this can be left empty, unless the query string is to be used, or some times when path data is generated into the definitions section to avoid conflicting names with the paths in the body section. |
| styles | {string:string:string} | - | A map from CSS class names such as ".Temperature" to a map of attribute name-value pairs. This is mostly useful when testing different styles before exporting the settings to a CSS file, or if the style is so particular to the current product that it is not desirable to put it into a shared external file. |
| layers | [Layer] | - | An array of Layer structures which generate content to the header only. Many layers change their behavior when defined in the definitions section. For example, normally the IsobandLayer module would generate both path data into the definitions section and a respective use-tag into the body section. In the definitions section no use-tag will be generated. |
Smoother structure
Two independent smoothers are available; only one runs per layer.
Savitzky-Golay (size/degree) is good at preserving local minima and maxima if at least degree 2 is used. Larger sizes with degree 2 tend to smooth data well while preserving extrema, higher degrees preserve original data better.
Trax grid smoother (method) is selected by giving a method. It supersedes size/degree when both are present. The methods are:
box— separable box blur (normalized convolution). Withpasses>= 3 it approximates a Gaussian. Cheap and smooth, but it attenuates extrema like any low-pass filter. Cost is O(N) per pass independent of radius (running sums), so it is the cheapest way to smooth at any scale.median— per-window median. Removes spikes of either sign, keeps step edges sharp, and never invents a value that was not in the data (no overshoot). Preserves the value of features broader than the window. Uses an exact 2D window, so unlikebox/morphologyits cost grows with radius.morphology— grayscale opening/closing with a box element. Opening removes bright features smaller than the element while preserving the value of larger ones; closing does the same for dark features;openclosedoes both. Preserves the magnitude of broad extrema. Unlikeboxand Savitzky-Golay it never averages or interpolates: it takes the window minimum (erode) or maximum (dilate), so the output is the exact value of some input cell, and the structuring element is a flat, axis-aligned square (separable min/max along x then y, chosen because it runs in O(N) per pass independent of radius). The result is therefore terraced into square-cornered plateaus, which the contourer traces as visibly blocky, axis-aligned boundaries — a circular element would round them but would not be separable. Usemorphologywhen you specifically want to delete small bright and/or dark speckles while preserving the exact magnitude of the larger features (e.g. cleaning isolated spurious pixels without flattening real peaks); preferboxormedianwhen smooth-looking contours matter more than exact extremum values.
The radius/passes are in grid cells (index space), not projected distance.
Method gallery — the same temperature field and projection, smoothed by each method at a matched scale. The point of a smoother comparison is to judge how each method treats features of a given size, so the parameters here are deliberately tuned to remove roughly the same spatial scale rather than to a fixed radius value: at equal radius the rank/morphological filters remove much more than the polynomial Savitzky-Golay fit, which would make the comparison misleading. The Savitzky-Golay size 3 window (7×7) sets the reference scale, and box/median/morphology are dialed to match it (box radius 1, passes 3; median radius 2; morphology radius 2). At this matched scale the differences are about character, not amount:
Savitzky-Golaykeeps the most fine structure and the strongest extrema (a local polynomial fit), at the highest cost.boxremoves the same scale of detail but, being a linear low-pass, attenuates the cold pockets the most.mediangives sharp, overshoot-free edges — every output value existed in the input — and preserves the value of features broader than the window.morphology(openclose) keeps the magnitude of the broad extrema but leaves visibly blocky, axis-aligned boundaries from its flat box structuring element.

Cost matters as much as character. The gallery above is tuned to equal smoothing quality, which flatters Savitzky-Golay — but it is the slowest filter, while box and morphology are O(N) per pass independent of radius and median sits in between (its cost grows with radius). On a busy server the right question is usually not "which preserves extrema best?" but "which is the cheapest filter that smooths well enough?" — and for plain speckle removal that is almost always box. The gallery below repeats the comparison at each method's minimal setting (Savitzky-Golay reduced to a 5×5 window, the others to a single 3×3-class pass): a single cheap box pass already removes the grid speckle nearly as well as the much more expensive Savitzky-Golay fit. Reach for Savitzky-Golay only when you genuinely need its exact extremum-height preservation and can afford the CPU; otherwise prefer box (general smoothing), median (spike/edge preservation) or morphology (speckle removal with magnitude preservation).

box also smooths correctly inside a missing-data footprint: the smoothing stays within the valid region and the missing-data boundary is preserved (the preserve_missing default).

The table below contains a list of attributes used in this structure.
Smoother
| Name | Type | Default value | Description |
|---|---|---|---|
| size | int | - | Savitzky-Golay: implies 2*N+1 adjacent points are used in the weighted mean. |
| degree | int | - | Savitzky-Golay: degree of the polynomial to fit to the data. |
| method | string | - | Trax smoother: box, median or morphology. Enables the Trax smoother path. |
| radius | int | - | Trax smoother: window/element half-width in grid cells (2*radius+1 wide). |
| passes | int | 3 | Trax smoother: number of repeats (box: passes>=3 approximates a Gaussian). |
| boundary | string | normalized | Trax smoother: edge handling — normalized, replicate or reflect. |
| morphology | string | openclose | Morphology only: open, close or openclose. |
| preserve_missing | bool | true | Trax smoother: keep input NaN cells missing instead of filling from neighbours. |
Sampling structure
Data to be contoured may be interpolated to another resolution. When used for temperature and topographic data is available, this may be used to provide higher apparent resolution by applying a lapse rate correction to the temperature. The desired resolution may be absolute or relative to the image resolution.
| Name | Type | Default value | Description |
|---|---|---|---|
| resolution | double | - | Desired resolution in kilometers per pixel |
| relativeresolution | double | - | Desired relative resolution with respect to the resolution of the image resolution. |
| minresolution | double | - | Minimum resolution for applying sampling |
| maxresolution | double | - | Maximum resolution for applying sampling |
Heatmap structure
Heatmap settings for "flash" producer's data. Currently parameter's (e.g. peak power) values are not used when building heatmap (they could be used for weighting). Heatmap library (https://github.com/lucasb-eyer/heatmap) supports use of custom kernels for stamp generation. Currently three kernels are implemented.
The table below contains a list of attributes used in this structure.
Heatmap
| Name | Type | Default value | Description |
|---|---|---|---|
| resolution | int | - | Heatmap resolution. |
| radius | int | - | Stamp radius. |
| kernel | (string) | exp | Stamp generation: linear, sqrt or exp (standard deviation). |
| deviation | (double) | 10.0 | Deviation for exp. |
Isoband structure
The table below contains a list of attributes used in the Isoband structure.
Isoband
| Name | Type | Default value | Description |
|---|---|---|---|
| qid | string | - | The identifier for the isoband. |
| lolimit | (double) | - | The low limit value for the isoband. If omitted, -infinity is implied. |
| hilimit | (double) | - | The high limit value for the isoband. If omitted, +infinity is implied. |
| value | (double) | - | Set both low and high limits for the isoband. Usually used for discrete data with nearest neighbour interpolation. |
| attributes | Attributes | - | The SVG attributes for the isoband. Usually only a class is given. |
| label | (Text) | - | Translatable label for the isoband. |
If both lolimit and hilimit are omitted, the missing values isoband will be generated.
Intersection structure
The table below contains a list of attributes used in the Intersection structure.
Intersection
| Name | Type | Default value | Description |
|---|---|---|---|
| lolimit | (double) | - | The low limit value for the intersecting isoband. If omitted, -infinity is implied. |
| hilimit | (double) | - | The high limit value for the intersecting isoband. If omitted, +infinity is implied. |
| level | (double) | - | The level from which to pick the intersecting isoband. |
| producer | (string) | - | The model from which to take the isoband, if not the same model as in the isoband itself. |
| parameter | string | - | The parameter whose isoband is used to intersect the actual isoband. |
| smoother | (Smoother) | - | How to smoothen the intersecting isoband. |
| unit_conversion | (string) | - | Name of desired unit conversion. Unit conversions are listed in the configuration file. |
| multiplier | (double) | 1.0 | A multiplier for valid data values for unit conversion purposes. |
| offset | (double) | 0.0 | An offset for valid data for unit conversion purposes. |
Note: Intersections work also for observation data. Instead of logical geometry operations the values are merely compared to the limits. For example, one may choose to render observed wind speed only when temperature is below zero.
Isoline structure
The table below contains a list of attributes used in the Isoline structure.
Isoline
| Name | Type | Default value | Description |
|---|---|---|---|
| qid | string | - | The identifier for the isoline. |
| value | double | - | The isoline value. The default is NaN, which means calculating the line between missing and valid data. |
| attributes | Attributes | - | The SVG attributes for the isoline. Often omitted completely, and the IsolineLayer |
| level attributes are used instead commonly for all isolines. |
Isofilter structure
Isolines and isobands can be smoothened by postprocessing the calculated polygons with a low pass filter.
Isofilter
| Name | Type | Default value | Description |
|---|---|---|---|
| type | string | none | Smoother type: none, average, linear, gaussian, tukey, taubin. Gaussian filtering seems to work best for plain smoothing; taubin additionally preserves feature sizes (see below). |
| radius | double | 0 | Filtering distance along the isoline in pixels. Zero disables filtering. Depending on the roughness of the data good values tend to be in the range 10-30 pixels. |
| iterations | integer | 1 | Number of passes. Zero disables filtering. Using 2-3 passes tends to remove small details better than simply increasing the radius. |
| lambda | double | 0.5 | Taubin shrinking-pass factor, in the open interval (0,1). Only used when type=taubin. |
| mu | double | -0.53 | Taubin inflating-pass factor. Must be negative with magnitude greater than lambda. Only used when type=taubin. |
| validate | bool or object | true | Adaptive validity backoff (enabled by default; set to false to disable). With wide radii the smoother can pull a narrow isoband across itself, producing a self-intersecting polygon that is invalid for clipping. The whole set of geometries is re-smoothed at a halved radius until every polygon is valid; this keeps the shared edges between adjacent isobands coherent (gap-free), unlike repairing a single band. Only active when a smoothing filter is configured (type/radius set), so unsmoothed geometry is never validity-checked. See below. |
Note that zooming into an image reduces the amount of smoothing since the set radius now covers a smaller area of the original data, and hence original details can be seen better.
The plain moving-average smoothers (average, linear, gaussian, tukey) always shrink: they pull every vertex towards the local average, so feature sizes collapse and a narrow isoband can fold onto itself. The taubin type alternates a shrinking pass (factor lambda) with an inflating pass (factor mu), so the overall shape and feature sizes (areas) are preserved while small details are still removed. This keeps isoband areas truer and reduces — but does not eliminate — smoothing-induced self-intersections, so it combines well with the validate setting.
The validate setting may be given as a boolean (true to enable with defaults) or as an object for finer control:
Isofilter validate
| Name | Type | Default | Description |
|---|---|---|---|
| enabled | bool | true | Whether the backoff is active. The backoff is enabled by default; pass "validate": false (or {"enabled": false}) to turn it off. |
| tries | int | 4 | Maximum number of radius halvings before giving up. After exhausting the budget the geometry is left unsmoothed (which is always valid) rather than emitted invalid. Allowed range 1–10. |
| bisect | bool | true | After halving finds a valid radius, take one bisection step back towards the previous (larger, invalid) radius to retain as much smoothing as possible while staying valid. |
| debug | bool | false | Log a line whenever a backoff fires, reporting the initial and final smoothing radius. |
Validation only re-smooths when an actual radius/type is set, and for isolines it is effectively a no-op (a self-crossing line is still OGC-valid). It is most useful for isobands rendered with a wide gaussian radius.
LegendLabels structure
The table below contains a list of attributes used in the LegendLabels structure.
LegendLabels
| Name | Type | Default Value | Description | |
|---|---|---|---|---|
| type | (string) | "range" | Legend label type. | |
| Value | Description | |||
| none | No labels will be generated. | |||
| lolimit | The lower limit of the isoband will be used, unless the lolimit is missing. | |||
| hilimit | The upper limit of the isoband will be used, unless the hilimit is missing. | |||
| range | A range of values will be used. If lolimit is missing, "< hilimit" is used. If hilimit is missing, "> lolimit" is used. If both limits are missing, the text "MISSING" is returned. Otherwise a label of the form "lolimit...hilimit" is used where the string separating the limits is configurable separately. | |||
| format | (string) | - | printf-type format for the labels. Normally not needed, but if decimals are involved it may be best to define a format such as "%.1f" since not all numbers (such as 0.1) can be represented exactly as IEEE-754 numbers. | |
| dx | int | 30 | X-coordinate offset from the respective legend symbol. | |
| dy | int | 30 | Y-coordinate offset from the respective legend symbol. Depending on the type of the legend, one may often have to define this value to be the height of the respective symbol plus possibly half of the used font size. | |
| separator | (string) | "\u2013" (endash) | The string separating the isoband lolimit and hilimit when type=range is used. | |
| conversions | {string:string} | - | Conversions from automatically generated labels to alternate strings. Typically one may wish to change the values at the bottom and at the top of the legend to strings such as "< lolimit" or "> hilimit". The value may also be a map from language codes to translations. | |
| attributes | Attributes | - | The SVG attributes for the labels. | |
LegendSymbols structure
The table below contains a list of attributes used in the LegendSymbols structure.
LegendSymbols
| Name | Type | Default value | Description |
|---|---|---|---|
| css | (string) | - | External CSS file to be included for the symbols. |
| symbol | (string) | - | The default symbol for the legend. |
| start | (string) | - | The optional symbol to be used for the start of the legend. |
| end | (string) | - | The optional symbol to be used for the end of the legend. |
| missing | (string) | - | The optional symbol to be used for missing values. If not defined, no symbol will be generated for the isoband nor the respective label, meaning no translation will be necessary for the "MISSING" label. |
| attributes | Attributes | - | The extra SVG attributes for the symbol beyond the ones defined in the isoband. |
Text structure
The text structure is used to define translatable strings with optional styling attributes. The structure may for example be used to define fixed labels for isobands.
Text
| Name | Type | Default value | Description |
|---|---|---|---|
| (any string) | string | - | Translation for specific language |
| default | (string) | - | The default translation |
| attributes | Attributes | - | The extra SVG attributes for the translation |
Map structure
The table below contains a list of attributes used in the Map structure.
Map
| Name | Type | Default value | Description |
|---|---|---|---|
| schema | string | "" | The database schema. |
| table | string | "" | The database table. |
| where | string | "" | The optional where clause for the PostGIS query. For example "cntryname='Finland'" |
| lines | boolean | false | Should the data be handled as if it would be stroked or filled. This will alter how the data will be clipped to the view - polygons will either be preserved for filling or cut into polylines for stroking. |
| minarea | double | - | Minimum area for polygons in square kilometers. For Finland useful values are around 10-100. |
| mindistance | double | - | Feature generalization tolerance in kilometers. For Finland useful values are around 1-4. |
| amalgamation_length | double | - | Maximum gap length the polygon amalgamator may bridge to merge nearby polygons (e.g. archipelago islands). In CRS coordinate units — for EPSG:4326 that means decimal degrees, not metres or kilometres. At 60° N, 0.05° ≈ 3 km. See Map amalgamation and simplification. |
| amalgamation_area | double | - | After amalgamation, drop polygons whose area is below this threshold. In CRS coordinate units squared — for EPSG:4326 at 60° N, 0.0005°² ≈ 1.5 km². |
| amalgamation_mainland_area | double | - | Optional speedup: polygons whose individual geographic area (km²) is at or above this threshold bypass the amalgamation triangulation entirely and are emitted unchanged. The intent is "the mainland" — a few huge polygons that would otherwise dominate the CDT input vertex count without benefiting from amalgamation. Typical archipelago value: 1000 km². The amalgamated island outlines may slightly overlap the mainland coastline — visually they abut at the coastline anyway. |
| amalgamation_mainland_amalgamate | boolean | false | When true, mainland polygons identified by amalgamation_mainland_area are individually run through the constrained Delaunay triangulation instead of being emitted unchanged. The single-polygon CDT pass closes concavities — small bays and inlets whose gap triangles all have edges shorter than amalgamation_length — producing a visibly more solid coastline that recognisably matches what residents would expect. Pays the per-mainland CDT cost (≈ 220 ms extra on the gshhg-h Northern-Baltic test) but is still much cheaper than putting the mainland into the global cluster CDT, since there is no clustering or neighbour search. Has no effect unless amalgamation_mainland_area is also set. |
| simplifier | string | - | Polygon/line simplification algorithm. Only visvalingam_whyatt (area-based vertex removal) is supported; none disables simplification. |
| tolerance | double | - | Simplification tolerance in pixels. Converted to a CRS coordinate-unit² area threshold using the projection box before being applied (Visvalingam-Whyatt removes vertices whose triangle-with-neighbours area is below this). Typical useful values are 1–3 px. |
Map amalgamation and simplification
The amalgamation_* and simplifier/tolerance options thin the polygons returned by PostGIS before they are clipped, projected, and rendered. They reduce SVG path length on dense coastlines, archipelagos, and high-resolution country borders, and let a single map dataset render cleanly across multiple zoom levels.
The pipeline applied by the GIS engine, in order:
- Amalgamator (
amalgamation_length/amalgamation_area): merges nearby polygons by triangulating the gaps between them with a constrained Delaunay triangulation. Gap triangles whose edges are all shorter thanamalgamation_lengthare accepted as part of the merged outline; the shapetoolsamalgamatetool used the same idea.amalgamation_areafilters polygons inside this step; setting onlyamalgamation_areafilters small polygons without merging. - minarea: drops individual polygons whose area is below this km² threshold. Still useful after amalgamation to remove the small islands the amalgamator could not merge into a neighbour.
- mindistance: runs GEOS
SimplifyPreserveTopologywith a kilometre tolerance. Independent of the new pixel-based simplifier and may be combined with it. - Simplifier (
simplifier+tolerance): Visvalingam-Whyatt vertex removal applied per polygon. The underlyingFmi::GeometrySimplifiercan also preserve shared boundaries when an upstream step has produced a topology with vertices shared between polygons (e.g. the contour engine's isoband/isoline output, or the output of the amalgamator above), but for typical PostGIS map data — where each feature is a separate polygon with its own copy of every vertex — the engine asks the simplifier to operate without cross-feature anchoring so that simplification is actually performed.
Amalgamation runs first so that the subsequent minarea and the new simplifier see the merged outline rather than each input island in isolation, and so that amalgamation_area does not pre-empt the more familiar minarea filter on un-merged polygons.
tolerance is specified in output pixels and converted to a CRS coordinate-unit² area threshold once per request using the active projection. Typical values are around 1–3 pixels: small enough that the silhouette is preserved at the chosen resolution, large enough that ~50–75 % of redundant vertices are removed.
Why Visvalingam-Whyatt only. Earlier versions also supported Douglas-Peucker (
"douglas_peucker") but it was removed in favour of VW. DP keeps the points furthest from the chord between consecutive anchors, which on a smoothly-curving coastline tends to keep the tips of peninsulas while straight-cutting the bays between them — visibly spiky at any non-trivial tolerance. Worse, on closed rings without topology preservation DP needs synthetic anchors, which the implementation finds via an O(n²) all-pairs distance search per ring; on a single ~10 000-vertex coastline ring this alone takes 30+ seconds. VW is O(n log n), tracks the overall shape character better on natural features, and works equally well for the rare polygonal/man-made cases.
Choosing an algorithm
The four operations attack different problems and can be combined freely. A short comparison:
| Operation | What it does | When to reach for it |
|---|---|---|
minarea | Drops whole polygons below a km² threshold (despeckle). Vertex count of surviving polygons is unchanged. | Removing slivers and tiny islands from a coastline at a given zoom. Cheap and predictable; keep it on by default for high-resolution country tables. |
mindistance (GEOS DP, in km) | Per-geometry Douglas-Peucker via GEOS SimplifyPreserveTopology. Each input polygon is simplified independently; tolerance is absolute (km). | A simple "thin the geometry" knob in absolute distance. Tolerance interpretation does not depend on the rendering resolution, which is good for cache reuse but bad for multi-zoom rendering. |
amalgamation_length / _area | Merges nearby polygons by triangulating the gaps between them and accepting gap triangles with edges shorter than amalgamation_length; then optionally drops merged outlines below amalgamation_area. Topology change. With amalgamation_mainland_area (and the recommended amalgamation_mainland_amalgamate), large mainland polygons go through their own per-polygon CDT so their bays close up too, producing a more solid coastline. | Archipelagos and any dataset with hundreds of small islands close together (Stockholm archipelago, Saimaa lakes, fjord coastlines). Equivalent to the old shapetools amalgamate tool. |
simplifier=visvalingam_whyatt | Area-based vertex removal: repeatedly removes the vertex whose triangle with its neighbours has the smallest area. Tolerance is in pixels². | The recommended default for any vertex thinning. Preserves overall shape character on natural features (coastlines, rivers, lake outlines); equally fine for polygonal/man-made features. |
Rules of thumb:
- For an archipelago or a coastline with thousands of small islands, run the amalgamator first; the simplifier alone will leave you with thousands of separate triangles instead of fewer, recognisable outlines.
minareaafter amalgamation is still useful to trim the small islands that were too far from a neighbour to be merged.- For amalgamator parameters (
amalgamation_length,amalgamation_area) the units are CRS coordinate units (degrees inEPSG:4326, metres in projected CRS), not km. At 60° N, 1° lat ≈ 111 km and 1° lon ≈ 56 km — soamalgamation_length=0.05≈ 3 km andamalgamation_area=0.0005≈ 1.5 km².
The seven examples below are drawn from the regression suite (test/dali/customers/test/products/map_*.json) and double as reference outputs. All seven render the same Northern-Baltic view (lon 18°–27°, lat 59°–65°, 900×600 px) of the high-resolution gshhg.gshhs_h_l1 table (GSHHG-h L1 land polygons, ≈ 200 m vertex spacing, ~14 000 polygons in the bbox prefilter). They are directly comparable so that the visual effect of each option is easy to read. The four amalgamation rows (map_amalgamate, map_amalgamate_wide, map_amalgamate_extreme, map_amalgamate_pathological) form a deliberate calibration sequence — amalgamation_length 0.01 → 0.02 → 0.05 → 0.1 — showing how the silhouette degrades as the gap distance grows past a sensible value for the rendering pixel scale, all the way to "the algorithm still produces valid topology but the output is nonsensical."
| Description | Map JSON snippet |
|---|---|
map_baseline.json — no simplification. Reference image showing the full source resolution (≈ 45 000 vertices in the rendered output, every individual skerry preserved). | { "schema": "gshhg", "table": "gshhs_h_l1", "where": "the_geom && ST_MakeEnvelope(17, 58, 28, 66, 4326)" } |
map_simplifier_vw.json — Visvalingam-Whyatt @ 5 px + minarea=2 km² (~89 % reduction, ≈ 5 200 remaining). Blockier than the baseline but preserves the overall shape character; the recommended default. | { ..., "simplifier": "visvalingam_whyatt", "tolerance": 5.0, "minarea": 2 } |
map_amalgamate.json — amalgamation, amalgamation_length=0.01 (≈ 0.6 km gap), amalgamation_mainland_area=1000 km² with amalgamation_mainland_amalgamate=true, minarea=2 km². Nearby skerries merge into landmasses that survive minarea; mainland polygons are individually triangulated so their own bays close up below the gap-triangle threshold. The archipelago character is preserved (rather than dissolved as in the simplifier-only cases) and the coastline reads as visibly more solid than a coastline that keeps every concavity. | { ..., "amalgamation_length": 0.01, "amalgamation_mainland_area": 1000, "amalgamation_mainland_amalgamate": true, "minarea": 2 } |
map_amalgamate_wide.json — same as above but amalgamation_length doubled to 0.02 (≈ 1.2 km gap). Demonstrates how the visual silhouette degrades as the gap distance grows: the Finnish coast still reads cleanly, the Stockholm and Åland archipelagos dissolve further into bigger blobs, and individual skerry clusters in the southern bbox start to merge into single landmasses. Useful as a calibration reference for how aggressive amalgamation_length should be at a given pixel scale. | { ..., "amalgamation_length": 0.02, "amalgamation_mainland_area": 1000, "amalgamation_mainland_amalgamate": true, "minarea": 2 } |
map_amalgamate_extreme.json — same as above but amalgamation_length raised to 0.05 (≈ 3 km gap), 5× the recommended default. Demonstrates what "too aggressive" looks like: the Stockholm archipelago has dissolved into the Swedish mainland, Åland is a single solid blob no longer recognisable as an archipelago, the Estonian and Latvian coasts have lost most of their detail, and southern Finnish bays have filled in. The output is no longer a faithful representation of the source dataset. Use as a "do not exceed" reference. | { ..., "amalgamation_length": 0.05, "amalgamation_mainland_area": 1000, "amalgamation_mainland_amalgamate": true, "minarea": 2 } |
map_amalgamate_pathological.json — amalgamation_length=0.1 (≈ 6 km gap), 10× the recommended default. Demonstrates outright algorithm breakdown at a gap distance much larger than the source data's natural island spacing: mainland coastlines reduce to cartoonish silhouettes and the gap-triangle pass starts producing scattered "ghost" white spots (narrow interior holes inside the merged region that survive the boundary walk) in the Bothnian Sea and Gulf of Finland. Useful only to show that the algorithm degrades gracefully rather than failing — it still produces valid topology, just nonsensical output. | { ..., "amalgamation_length": 0.1, "amalgamation_mainland_area": 1000, "amalgamation_mainland_amalgamate": true, "minarea": 2 } |
map_amalgamate_simplified.json — same amalgamation + minarea followed by Visvalingam-Whyatt @ 1.5 px. The recommended pipeline for archipelago / dense-coastline rendering. | { ..., "amalgamation_length": 0.01, "amalgamation_mainland_area": 1000, "amalgamation_mainland_amalgamate": true, "minarea": 2, "simplifier": "visvalingam_whyatt", "tolerance": 1.5 } |
MapStyles structure
Individual map features can be styled based on a forecast value assigned for the area. For example, one can colour regions based on the forecast warning level for the area.
The table below contains a list of attributes used in the MapStyles structure.
MapStyles
| Name | Type | Default value | Description |
|---|---|---|---|
| field | string | "" | The database field used to identify the region (region number or name). |
| parameter | string | "" | The forecast parameter name. |
| features | [MapFeature] | - | Optional mapping from field names to station numbers. If omitted, database field must be a region number |
| attributes | AttributeSelection | - | Mapping from forecast value to presentation attributes |
Note: Optional FeatureMapping attributes override common MapLayer attributes, and attributes based on the actual data value override both.
MapFeature structure
Feature mappings are used to make database shape names to region numbers to be picked from forecast data. Feature mappings are not needed if the database contains region numbers directly. However, feature mappings can also be used to style individual regions.
The table below contains a list of attributes used in the MapFeature structure.
MapFeature
| Name | Type | Default value | Description |
|---|---|---|---|
| value | string | - | The value of the database field to be mapped to a region number, typically the name of the region |
| number | int | - | The region number to be picked from the forecast. |
| attributes | [Attributes] | - | Region specific presentation attributes |
Note: Optional FeatureMapping attributes override common MapLayer attributes, and attributes based on the actual data value override both.
Positions structure
The Positions structure can be used to define a list of positions which are needed in quite many layers. For example, in the NumberLayer this structure defines positions for the printed number, in the SymbolLayer it defines positions for the symbols and in the ArrowLayer it defines positions for the arrows.
The table below contains a list of attributes used in this structure.
Positionsuint
| Name | Type | Default Value | Description | |
|---|---|---|---|---|
| layout | (string) | grid | Layout type. If can have one of the following values: | |
| Value | Description | |||
| grid | This layout places symbols in a fixed step pixel grid. Sometimes not the best choice for arrow-layers, experts prefer graticule-layouts. | |||
| data | This layout places symbols at the coordinates defined by the used querydata or the stations themselves if observations are used. | |||
| keyword | This layout places symbols at the coordinates of the locations associated with the given database keyword. | |||
| station | This layout places symbols at the stations listed by the user. | |||
| latlon | This layout places symbols at the locations listed by the user. | |||
| graticule | This layout places symbols along the edges of a NxN graticule grid with a fixed step size in degrees. | |||
| graticulefill | This layout places symbols along the edges of a NxN graticule grid plus inside the graticule cell while satisfying the set minimum distance condition. This layout is recommended for arrow-layers. | |||
| xmargin | uint | 0 | Extra margin for coordinate generation | |
| ymargin | uint | 0 | Extra margin for coordinate generation | |
| x | uint | 5 | Grid layout: X-coordinate of the first number | |
| y | uint | 5 | Grid layout: Y-coordinate of the first number | |
| dx | uint | 20 | Grid layout: X-coordinate offset to the next number on the same row | |
| uint | - | Other layouts: X-coordinate adjustment for the selected position | ||
| dy | 20 | Grid layout: Y-coordinate offset to the next number on the next row | ||
| uint | - | Other layouts: Y-coordinate adjustment for the selected position | ||
| ddx | uint | 0 | Grid layout: X-coordinate adjustment for the next row in grid layout | |
| size | uint | 10 | Graticule/GraticuleFill layout: size of the graticule cell in degrees | |
| step | uint>/td> | 1 | Graticule layout: step size for the graticule in degrees | |
| mindistance | double | 50 | GraticuleFill layout: minimum distance between parallel and meridian symbols in pixels | |
| keyword | string | - | Keyword layout: the database keyword | |
| stations | XXXXX | - | Station layout: the user listed stations | |
| locations | XXXXX | - | LatLon layout: the user listed locations | |
| direction | string | - | Direction parameter. Specify a direction parameter or both "u" and "v" attributes | |
| u | string | - | U-component of the direction parameter | |
| v | string | - | V-component of the direction parameter | |
| directionoffset | int | 0 | Extra displacement in the direction of the specified parameter | |
| rotate | int | 0 | Extra rotational displacement | |
| outside | Map | - | Only coordinates outside this shape will generate output | |
| inside | Map | - | Only coordinates inside this shape will generate output | |
| intersect | [Intersection] or Intersection | - | Isobands in which the positions must be | |
Note: The direction is positive for wind. For currents one may use "rotate"=90 or a negative "directionoffset". If an extra rotation is desired, it is recommended to use a negative offset.
If the layout is not "data" and the producer refers to observations, the generated candidate locations will be snapped to the nearest stations if the "maxdistance" condition set for the layer is satisfied. This way one may for example place candidate points into a grid and get an evenly distributed sample of the full set of stations.
The margins can be used to generate point locations outside the image area. This is useful for example if the symbols to be placed at the locations can be expected to overflow to adjacent WMS tiles. xmargin and ymargin can be set simultaneously using the "margin" setting.
Label structure
The table below contains a list of attributes used in the Label structure.
Label
| Name | Type | Default value | Description |
|---|---|---|---|
| dx | int | 0 | X-coordinate offset of the number from the actual coordinate. |
| dy | int | 0 | Y-coordinate offset of the number from the actual coordinate. |
| unit_conversion | (string) | - | Name of desired unit conversion. Unit conversions are listed in the configuration file. |
| multiplier | double | 1 | Multiplier for the number for unit conversion purposes. |
| offset | double | 0 | Offset for the number for unit conversion purposes. |
| missing | string | "-" | Label for missing values. No text is output if the value is empty. |
| precision | int | 0 | Number of decimals. |
| multiple | double | 0 | If nonzero, round value to multiple of set value |
| rounding | string | "tonearest" | Rounding mode: tonearest, towardzero, upward or downward. |
| locale | string | - | Locale for printing the number, for example fi_FI. |
| prefix | string | "" | Prefix for the number. |
| suffix | string | "" | Suffix for the number, usually for units. |
| plusprefix | string | "" | Prefix replacing the sign for non-negative numbers. |
| minusprefix | string | "" | Prefix replacing the sign for negative numbers. |
| orientation | string | "horizontal" | Horizontal or auto. Currently used only by IsolabelLayer |
| padding_char | string | - | Padding character for numbers. |
| padding_length | int | - | Padding length for numbers. Padding is done if length of number is shorter than padding_length. |
Note: signed prefixes are needed in aviation charts where negative temperatures are typically shown without a sign. In the exceptional case where positive numbers are actually needed, they are displayed using a plus sign or a "PS"-prefix.
LabelConfig structure
The LabelConfig structure is used by LocationLayer to place location name labels next to point symbols. See LabelConfig settings for the complete reference including algorithm-specific parameters and the classes population-override array.
The key fields are summarised here for quick reference:
LabelConfig (summary)
| Name | Type | Default | Description |
|---|---|---|---|
| algorithm | string | "none" | none | fixed | greedy | priority-greedy | simulated-annealing |
| candidates | int | 8 | Candidate positions per label: 4, 8, or 16 (Imhof preference order). |
| offset | double | 5.0 | Pixel gap between symbol anchor and label. |
| font_family | string | "sans-serif" | SVG font family. |
| font_size | double | 11 | Base font size in pixels. |
| font_weight | string | "normal" | "normal" or "bold". |
| fill | string | "#333333" | Label text colour. |
| stroke | string | "white" | Halo colour (empty string = no halo). |
| stroke_width | double | 2.0 | Halo width in pixels. |
| stroke_opacity | double | 0.75 | Halo opacity. |
| max_labels | int | 200 | Hard cap on labels processed. |
| fixed_position | string | "ne" | Fixed-algorithm position: n ne e se s sw w nw. |
| simulated_annealing | object | - | SA sub-object: iterations, initial_temperature, cooling_rate, overlap_weight, position_weight. |
| classes | array | [] | Population-range style overrides (lolimit, hilimit, font_size, font_weight, fill). |
Location structure
The table below contains a list of attributes used in the Location structure.
Location
| Name | Type | Default value | Description |
|---|---|---|---|
| longitude | double | - | The longitude of the coordinate. |
| latitude | double | - | The latitude of the coordinate. |
| dx | int | - | X-coordinate adjustment for the location. |
| dy | int | - | Y-coordinate adjustment for the location. |
Observation structure
The table below contains a list of attributes used in the Observation structure.
Observation
| Name | Type | Default value | Description |
|---|---|---|---|
| parameter | string | - | The parameter name of the observation. |
| label | Label | - | How to render the observed value. |
| attributes | Attributes | - | SVG rendering attributes for the generated text. |
Station structure
The table below contains a list of attributes used in the Station structure.
Station
| Name | Type | Default value | Description |
|---|---|---|---|
| fmisid | (int) | - | fmisid of the station. |
| wmo | (int) | - | wmo-number of the station. |
| lpnn | (int) | - | LPNN-number of the station (Finland only) |
| geoid | (int) | - | geonames database identifier for the station |
| longitude | (double) | - | The longitude for the location of the wind rose. Default is to place the wind rose at the station coordinates. |
| latitude | (double) | - | The latitude for the location of the wind rose. Default is to place the wind rose at the station coordinates. |
| symbol | (string) | - | An optional symbol to be placed at the station. |
| attributes | Attributes | - | The SVG rendering attributes for the symbol |
| title | Title | - | The title for the wind rose. |
| dx | (int) | - | Station specific positional adjustment for the symbol attached to the station. |
| dy | (int) | - | Station specific positional adjustment for the symbol attached to the station. |
Note that both longitude and latitude have to be specified in order to alter the location where a number or something else will be displayed instead of the station coordinates.
WindRose structure
The table below contains a list of attributes used in the WindRose structure.
WindRose
| Name | Type | Default value | Description |
|---|---|---|---|
| minpercentage | int | 0 | Required percentage of observations for a sector to be shown. This can be used to prevent very small sectors which are irrelevant. |
| radius | int | 20 | The radius of the wind rose. |
| sectors | int | 8 | The number of wind direction sectors in the wind rose |
| symbol | (string) | - | The symbol for the wind rose, usually either a circle or omitted. |
| attributes | Attributes | - | The SVG attributes for the symbol. |
| connector | (Connector) | - | How to connect the wind rose to the location of the station. |
| parameter | (string) | - | The wind direction parameter name. |
| limits | AttributeSelection | - | SVG styling based on the average wind speed in the sector. |
Connector structure
The table below contains a list of attributes used in the Connector structure.
Connector
| Name | Type | Default value | Description |
|---|---|---|---|
| startoffset | int | 0 | Start offset for the connecting line. Usually zero, unless a symbol is placed at the station. |
| endoffset | int | 0 | End offset for the connecting line. Usually matches the radius of the wind rose. |
| attributes | Attributes | - | The SVG rendering attributes for the connecting line. |
Filters structure
The table below contains a list of attributes used in the Filter structure.
Filters
| Name | Type | Default value | Description |
|---|---|---|---|
| where | (string) | WHERE condition to be appended to the database query | |
| attributes | Attributes | SVG-attributes for geometry | |
| text_attributes | Attributes | SVG-attributes for text |
Using dynamically created grids
Introduction
A layer usually contains the "parameter" attribute definition, which refers to the grid data that is needed in order to draw the current layer. Typically the value of the "parameter" attribute is a parameter name that points to fixed grid data, which means that this data is pre-calculated. In this case the biggest drawback is that if we want to get grids that are calculated differently then we need a pre-calculation process that calculates these grids and stores them in such way that they can be accessed.
Luckily now there is a way to caculate new grids on the fly and use their data as they were fixed grids. The basic idea is that the "parameter" attibute can contain a function that defines how the new grid should be calculated. This feature is available only for producers that use grid-engine.
There are two different ways to create new grids:
- We can create new grids that uses data from other grids that have the same timestamp.
- We can create grids that uses data from multiple timesteps. For example, calculating max temperature values for a day.
Grids with the same timestamp
Here are some examples that calculate a new grid from other grids that have the same timestamp:
The new grid contains maximum values from 3 ensemble grids (1,3,5):
"parameter" : "MAX{Temperature:MEPS:1093:6:0:3:1;Temperature:MEPS:1093:6:0:3:3;Temperature:MEPS:1093:6:0:3:5}"
The new grid contains average values counted from 14 ensemble grids (1-14):
"parameter" : "AVG{Temperature:MEPS:1093:6:0:3:1-14}"
The new grid contains probability percentage that the values in 14 ensemble grids (1-14) are below zero (= in range -1000 .. 0):
"parameter": "IN_PRCNT{-1000;0;Temperature:MEPS:1093:6:0:3:1-14}"
Grids calculated over multiple timesteps
The basic idea is that, we can have multiple timesteps before and after the requested time. The timestep size (in minutes) can be used in order to calculate timestamp for the requested data. The requested data is expressed in timestep index range (not actual time lengths). The index number 0 refers to the requested time, the first timestep before the requested time is refered with the index number -1, and the first timestep after the requested time is refered with the index number 1. If functions require additional parameters (like value range), these parameters are added after the timestep range and timestep size defintions.
Here are some examples:
The new grid contains maximum values from the previous 24 timesteps.Timestep size = 60 minutes, range of grid indexes = [-23 .. 0] counted from the requested time.
"parameter" : "/MAX{Temperature;-23;0;60}"
The new grid contains probability percentage that the temperature drops below the zero (= is in range -1000..0) during the following 48 hours.
"parameter": "/IN_PRCNT{Temperature;0;47;60;-1000;0}"
Functions
Dynamic grid caculation uses C++ or LUA functions for creating new grids. C++ functions are faster than LUA functions and that's why we try to implement most common functions with C++. At least the following functions are currently available for producing new grids:
AVG => Calculate average/mean values.
MAX => Calculate maximum values.
MIN => Calculate minimum values.
MUL => Multiplies grid values with a multiplyer. This can be used also for dividing grid values (i.e. multiplier = 1/divider).
SUM => Add 1-N grids together. If there are additional numbers in the function then these numbers are added to each grid value.
IN_PRCNT => Returns the probability percent that the value in the given grids is inside the given value range.
OUT_PRCNT => Returns the probability percent that the value in the given grids is outside the given value range.
JSON references and includes
Product JSON files support four mechanisms for composing and reusing configuration fragments.
json: value references
Any JSON string value that starts with json: causes the corresponding file to be loaded and
the string replaced with the parsed JSON content. Paths are resolved relative to the customer
layers/ directory (e.g. <root>/customers/<customer>/layers/), or if the path starts with
/, relative to the Dali root directory.
{
"isobands": "json:isobands/temperature.json",
"label": "json:numbers/integers.json"
}
When a json: value appears as an element of an array, the loaded JSON replaces that element
(useful for including complete layer definitions):
"layers": [
"json:paths/legendmask.json",
{ "layer_type": "map", "..." }
]
ref: cross-references
Any string value of the form ref:<dotted.path> is replaced with the value found by following
the dotted path in the current JSON document. The top-level refs object is a conventional
place to collect reusable fragments:
{
"refs": {
"finland": {
"schema": "natural_earth",
"table": "admin_0_countries",
"where": "iso_a2 IN ('FI','AX')"
},
"myprojection": "json:maps/finlandprojection.json"
},
"projection": "ref:refs.myprojection",
"views": [{
"layers": [{
"layer_type": "isoband",
"inside": "ref:refs.finland"
}]
}]
}
JSON Pointer ($ref) dereferencing
Standard JSON Pointer references of the form {"$ref": "#/path/to/value"} are resolved after
json: and ref: substitution. This follows the JSON Reference / JSON Schema convention.
Processing pipeline
The product JSON goes through the following stages, which can be inspected individually
by adding stage=<n> to the request:
| Stage | stage= value | Operation |
|---|---|---|
| Raw JSON | 1 | File loaded as-is, no substitution |
| Reference substitution | 2 | Query-string json: and ref: replacements applied |
| Include expansion | 3 | json: and ref: values resolved from files |
| JSON Pointer dereferencing | 4 | $ref pointers resolved |
| Variable expansion | (default) | Query-string variable overrides applied |
Symbol substitutions via URI
SVG resources referenced via url(#name) — markers, fill patterns, filters, and gradients —
can be parameterised by appending a query string to the fragment identifier:
url(#<symbol-name>?<param1>=<value1>&<param2>=<value2>)
The plugin loads the SVG resource file, substitutes every $(variableName=defaultValue)
placeholder with the supplied value (or the default if the parameter is absent), and injects
the result into the SVG <defs> section.
Example — coloured spearhead marker:
In the JSON attributes:
"marker-end": "url(#spearhead?fill=#606060)"
In the marker SVG file (markers/spearhead.svg):
<marker id="spearhead" markerWidth="9" markerHeight="9"
viewBox="0 0 10 10" orient="auto" refX="1" refY="5">
<path d="M 0 0 L 10 5 L 0 10 z" fill="$(fill=black)"/>
</marker>
The placeholder $(fill=black) is replaced with #606060. If fill is not supplied in the
URL, the default value black is used.
Multiple parameters are separated by &:
"filter": "url(#rectbackground?border=black&background=white&borderwidth=1)"
Supported SVG attributes for parameterised references:
| Attribute | Resource type |
|---|---|
filter | SVG filter element |
marker | SVG marker element |
marker-start | SVG marker element |
marker-mid | SVG marker element |
marker-end | SVG marker element |
fill | SVG pattern element |
linearGradient | SVG linearGradient element |
radialGradient | SVG radialGradient element |
Each resource is included in the <defs> block only once per unique name, regardless of how
many times it is referenced. If the same symbol name is used with different parameters in the
same product, only the first reference wins — use distinct names in that case.
File search order for SVG resources:
<root>/customers/<customer>/<subdir>/<name>.svg<root>/<name>.svg<root>/resources/layers/<subdir>/<name>.svg<root>/resources/<subdir>/<name>.svg<root>/resources/<name>.svg
The <subdir> is markers/, patterns/, filters/, or gradients/ depending on the
attribute type. An absolute path (starting with /) skips the customer directory and
searches from the Dali root directly.
Filters are a special case: after steps 1–2 they are searched only in
<root>/resources/filters/<name>.svg (they do not fall back through the
resources/layers/ and resources/ locations used by the other resource types).
Dali querystring parameters
The Dali endpoint (default URL /dali) accepts the following query parameters.
Required parameter:
| Parameter | Type | Description |
|---|---|---|
product | string | Path to the product JSON file relative to customers/<customer>/products/. Example: product=temperature loads .../products/temperature.json. |
Content and data selection:
| Parameter | Type | Default | Description |
|---|---|---|---|
customer | string | config default | Customer name; selects the sub-tree under <root>/customers/. |
type | string | svg | Output format: svg, png, pdf, ps, xml, geojson, topojson, kml, geotiff, mvt, datatile. |
producer | string | config default | Data producer (querydata model name). |
time | string | latest | Valid time in any format accepted by the MacGyver time parser (ISO 8601, SQL, epoch, offset from now). |
time_offset | int | 0 | Minutes added to time. |
origintime | string | latest | Querydata origin time (model run time). |
interval_start | int | – | Start of observation time interval in minutes before time + time_offset. |
interval_end | int | – | End of observation time interval in minutes after time + time_offset. |
timestep | int | – | Timestep in minutes. |
level | double | – | Pressure level in hPa. |
elevation | double | – | Grid-data level value; takes precedence over level when both are given. |
elevation_unit | string | – | Unit of the elevation value. |
levelId / levelid | int | – | Level type ID. |
forecastNumber | int | – | Ensemble member number. |
forecastType | int | – | Forecast type. |
geometryId | int | – | Grid geometry ID. |
source | string | config default | Primary forecast source override. |
language | string | config default | Language code, e.g. fi, en, sv. |
tz | string | UTC | Timezone used when parsing time. |
Layout and clipping:
| Parameter | Type | Default | Description |
|---|---|---|---|
width | int | 1000 | Image width in pixels (20–10000). |
height | int | 1000 | Image height in pixels (20–10000). |
margin | int | – | Sets both xmargin and ymargin. |
xmargin | int | 0 | X-margin for symbol clipping (pixels). |
ymargin | int | 0 | Y-margin for symbol clipping (pixels). |
clip | bool | false | Wrap each layer in a <clipPath>. |
precision | double | – | Number of decimals used when writing coordinates into the SVG output. |
Overriding JSON structure fields:
| Parameter | Type | Description |
|---|---|---|
title | string | Override the product title field. |
projection | JSON | Override the top-level projection block. |
views | JSON | Override the top-level views array. |
defs | JSON | Override the top-level defs block. |
attributes | JSON | Override the top-level attributes block. |
animation | JSON | Override the animation block. |
png | JSON | Override the png output options block. |
svg_tmpl | string | Select a CTPP2 template by name (see Template selection). |
Product modification via querystring
Any field inside the product JSON can be overridden at request time by using dotted
qid.fieldname parameter names. A qid must first be assigned to the layer or object to
be modified.
Example: override the weather parameter and producer of a layer whose qid is "temp_layer":
GET /dali?product=temperature&temp_layer.parameter=DailyMeanTemperature&temp_layer.producer=daily00
This is equivalent to editing the JSON as:
{ "qid": "temp_layer", "parameter": "DailyMeanTemperature", "producer": "daily00", "..." }
Dotted parameters work at any nesting depth as long as each intermediate object has a qid.
Template selection
Products are rendered through a CTPP2 template. The template is selected in the following order of precedence:
- The
svg_tmplquery-string parameter. - The
svg_tmplfield in the product JSON. - The entry in the
templatesconfig block matching the requestedtype(e.g.geojson,topojson,kml). - The
templates.defaultconfig entry. - Hard-coded fallback:
"svg".
Template files are .c2t files stored in the directory configured by templatedir (default
/usr/share/smartmet/wms).
Example templates block in the plugin configuration:
templates:
{
default = "svg";
geojson = "geojson";
topojson = "topojson";
kml = "kml";
}
DataTile output
The datatile output type produces a standard PNG where pixel RGBA values encode raw
float data rather than visual colours. This is the standard technique used by web
weather visualisation platforms to deliver gridded meteorological fields to browser-side
JavaScript that renders particle animations, interpolated overlays, and similar effects.
Unlike GeoTIFF output (which carries full float32 precision but requires GDAL on the client), datatile PNGs can be loaded by any browser as a normal image and decoded in a few lines of JavaScript.
Requesting datatile output
Via the Dali endpoint, set type=datatile in the query string or in the product JSON:
GET /dali?customer=grid&type=datatile&product=datatile_temperature&time=200808050800
Via WMS GetMap, use the MIME type application/x-datatile+png as the FORMAT:
GET /wms?service=wms&request=GetMap&version=1.3.0&layers=grid:datatile_temperature&styles=&crs=EPSG:4326&bbox=34,-12,74,40&width=64&height=64&format=application/x-datatile%2Bpng&time=200808050800
Via WMTS and OGC API Tiles, use datatile as the format extension or f= parameter.
Encoding schemes
The encoding scheme is described in PNG tEXt metadata chunks embedded in the
output, so clients can self-discover the scale and offset.
Single-band encoding (one scalar parameter such as temperature or precipitation):
| Channel | Meaning |
|---|---|
| R | High byte of 16-bit quantised value |
| G | Low byte of 16-bit quantised value |
| B | 0 (reserved) |
| A | 255 = valid, 0 = missing/nodata |
PNG tEXt chunks: datatile:bands=1, datatile:min, datatile:max,
datatile:encoding=uint16.
Client-side decode:
value = (R * 256 + G) / 65535.0 * (max - min) + min;
Dual-band encoding (two parameters, e.g. wind U + V or direction + speed):
| Channel | Meaning |
|---|---|
| R | High byte of 16-bit quantised band 1 |
| G | Low byte of 16-bit quantised band 1 |
| B | High byte of 16-bit quantised band 2 |
| A | Low byte of 16-bit quantised band 2 |
Valid values are quantised to [1, 65535]; a pixel with all four bytes zero indicates missing data.
PNG tEXt chunks: datatile:bands=2, datatile:min1, datatile:max1,
datatile:min2, datatile:max2, datatile:encoding=uint16.
Client-side decode:
value1 = (R * 256 + G - 1) / 65534.0 * (max1 - min1) + min1;
value2 = (B * 256 + A - 1) / 65534.0 * (max2 - min2) + min2;
Supported layer types
Any layer that supports GeoTIFF output also supports datatile output. The first datatile-capable layer found in the product is used:
| Layer type | Bands | Notes |
|---|---|---|
isoband | 1 | Scalar parameter |
isoline | 1 | Scalar parameter |
raster | 1 | Scalar parameter |
number | 1 | Scalar parameter |
symbol | 1 | Scalar parameter |
arrow | 1 or 2 | Single-param (speed or direction only) yields 1 band; combined direction+speed or U+V yields dual-band |
For arrow layers in U+V mode, the raw U and V components are encoded directly
(not converted to direction and speed), since client-side particle systems need
the original vector components.
Pipeline
The datatile output bypasses the SVG/CTPP rendering pipeline entirely, following the same architecture as GeoTIFF and MVT outputs. The data flow is:
Request → Product::generateDataTile()
→ Layer::generateDataTile()
→ Grid engine query (same as GeoTIFF path)
→ Float values quantised to RGBA pixels
→ PNG encoded with tEXt metadata
→ Cached and returned
GetCapabilities
The Dali endpoint exposes a JSON catalog of the products available on the server.
Unlike the WMS GetCapabilities response this is not an OGC document — it is a
simple JSON structure intended for UIs, dashboards, and scripts that want to
enumerate Dali products and their time dimensions.
GET /dali?request=GetCapabilities
GET /dali?request=GetCapabilities&customer=ely
The optional customer=<name> parameter restricts the listing to a single
customer. When omitted, every customer directory under root/customers/ is
listed in alphabetical order, and products within a customer are listed in
alphabetical path order so that the output is deterministic across runs.
Each product entry contains the fields that are visible in the top-level
product JSON (no json: reference expansion is performed) plus a time
dimension resolved via the Querydata engine when the product declares a
producer:
| Field | Description |
|---|---|
name | Product name relative to the customer's products/ directory, without the .json extension. |
path | Array of path segments [customer, …, leaf], useful for building tree UIs. |
title | Product title if present. |
abstract | Product abstract if present. |
producer | Producer declared at the top of the product JSON. |
source | Product source if present. |
image | Output dimensions (width, height) and format (defaults to svg). |
projection | Selected projection fields: crs, bboxcrs, xsize, ysize, x1, y1, x2, y2, cx, cy, resolution. |
time | default (latest valid time), origintime (latest origin time if any), and values (either start/end/PTnH-style interval for uniform steps, or a comma-separated list). |
url_template | URL template clients can expand, e.g. /dali?customer=ely&product=temperatureoverlay&time={time}. |
Response example (abbreviated):
{
"service" : "Dali",
"version" : "1.0.0",
"customers" : [
{
"name" : "ely",
"products" : [
{
"name" : "temperatureoverlay",
"path" : ["ely", "temperatureoverlay"],
"title" : "Temperature Overlay",
"url_template" : "/dali?customer=ely&product=temperatureoverlay&time={time}"
}
]
}
]
}
A per-product parse or lookup failure is reported inline as
{"name": "...", "error": "parse_error"} (or "unreadable" / "exception")
so a single bad product JSON does not break the whole catalog.
Debugging parameters
The following parameters are useful during development and are silently ignored in production
or stripped by the allowed-parameter filter if not in the allowed_keys set.
| Parameter | Description |
|---|---|
printjson=1 | Print the fully expanded product JSON to the server console. |
printhash=1 | Print the CTPP2 CDT object to the server console. |
printparams=1 | Print the grid parameter list used by the product. |
timer=1 | Print timing information per product generation stage. |
stage=1–4 | Return the intermediate JSON at the given pipeline stage instead of rendering. |
debug=1 | Include exception/backtrace details in error responses instead of a terse message. |
quiet=1 | Suppress server-side logging of the error when a request fails. |
WMS querystring parameters
The WMS endpoint (default URL /wms) implements OGC WMS 1.3.0. The set of accepted
versions is configured by the supported_versions setting (see the wms group in the
plugin configuration); 1.3.0 is the standard version. When an older
version such as 1.1.1 is enabled, the axis-order rules and the SRS parameter name of
that version apply.
Parameter names are matched case-insensitively, so REQUEST, request, and Request are
equivalent.
GetCapabilities
GET /wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities
Returns the capabilities document listing all available layers, CRS, and formats.
| Parameter | Description |
|---|---|
SERVICE=WMS | Must be WMS. |
VERSION | WMS version. |
REQUEST=GetCapabilities | Request type. |
FORMAT | text/xml (default) or application/json. |
LANGUAGE | Language code for titles/abstracts; defaults to the configured default language. |
NAMESPACE | Restrict the listing to layers in the given namespace. |
LAYOUT | Layer hierarchy in the response: flat (default), recursive, or recursivetimes. Overrides the configured default. |
STARTTIME / ENDTIME | Limit the advertised time dimension to the given range (ISO 8601). |
DIM_REFERENCE_TIME | Advertise the time dimension for the given model run (origin time). |
ENABLEINTERVALS | 1 / 0: force the multi-interval time dimension on or off, overriding the configured default. |
SHOW_HIDDEN | 1: include layers flagged hidden in the response. |
GetMap
Required parameters:
| Parameter | Description |
|---|---|
SERVICE=WMS | Must be WMS. |
VERSION | WMS version, e.g. 1.3.0. Must be one of the configured supported_versions. |
REQUEST=GetMap | Request type. |
LAYERS | Comma-separated list of layer names. |
STYLES | Comma-separated list of styles (may be empty: STYLES=). |
CRS (WMS 1.3) or SRS (WMS 1.1) | Coordinate reference system identifier, e.g. EPSG:4326. |
BBOX | Bounding box: minx,miny,maxx,maxy. Axis order follows the CRS definition in WMS 1.3. |
WIDTH | Image width in pixels. |
HEIGHT | Image height in pixels. |
FORMAT | Output MIME type; must be one of the server's configured formats. Common values: image/png, image/webp, image/svg+xml, application/pdf, application/geo+json, application/topo+json, image/tiff (GeoTIFF), application/vnd.mapbox-vector-tile (MVT), application/x-datatile+png (DataTile). |
Optional parameters:
| Parameter | Description |
|---|---|
TRANSPARENT=TRUE|FALSE | Whether the background is transparent (default FALSE). |
BGCOLOR=0xRRGGBB | Background colour (used only when TRANSPARENT=FALSE). |
EXCEPTIONS | Exception reporting format: XML (default), JSON, INIMAGE, or BLANK. |
TIME | Time selection. Accepts current, a single ISO 8601 time, a comma-separated list (t1,t2,…), an interval min/max/resolution, or a comma-separated list of such intervals. |
ELEVATION | Elevation value (forwarded to the layer as the level parameter). |
DIM_REFERENCE_TIME / ORIGINTIME | Select the model run (origin time). DIM_REFERENCE_TIME is the standard WMS dimension name; ORIGINTIME is an equivalent alias. Both are forwarded to Dali as origintime. An invalid reference time for a grid producer is rejected. |
DIM_INTERVAL_START | Extension: start of observation time interval in minutes before TIME (integer). |
DIM_INTERVAL_END | Extension: end of observation time interval in minutes after TIME (integer). |
GetFeatureInfo
GET /wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetFeatureInfo&LAYERS=<name>&QUERY_LAYERS=<name>
&CRS=EPSG:4326&BBOX=...&WIDTH=...&HEIGHT=...&STYLES=&I=<col>&J=<row>
GetFeatureInfo reuses the full GetMap parameter set (LAYERS, STYLES, CRS/SRS,
BBOX, WIDTH, HEIGHT, TIME, …) to reconstruct the map, plus:
| Parameter | Description |
|---|---|
QUERY_LAYERS | Comma-separated list of layers to query (subset of LAYERS). Required. |
I / J | Pixel column / row of the query point (WMS 1.3 names). Required. |
INFO_FORMAT | Response MIME type; defaults to application/json. |
GetLegendGraphic
GET /wms?SERVICE=WMS&VERSION=1.3.0&SLD_VERSION=1.1.0&REQUEST=GetLegendGraphic&LAYER=<name>&FORMAT=image/png
Returns a legend image for the named layer.
Required parameters:
| Parameter | Description |
|---|---|
VERSION | WMS version. |
SLD_VERSION | SLD specification version, e.g. 1.1.0. |
LAYER | Layer name (singular, unlike GetMap's LAYERS). |
FORMAT | Output MIME type, e.g. image/png. |
Optional parameters:
| Parameter | Description |
|---|---|
STYLE | Style name; defaults to default. |
WIDTH / HEIGHT | Legend size in pixels; default 500 × 500. |
TIME | Time used to resolve a time-dependent legend; defaults to current. |
WMS GetMap and GetCapabilities configuration
A product definition may at the top level include the following settings which directly affect WMS GetCapabilities and GetMap requests:
WMS settings
| Name | Type | Default value | Description |
|---|---|---|---|
| hidden | (bool) | false | If true the WMS will not appear in GetCapabilities responses but GetMap requests continue to work. |
| title | Text | - | Obligatory title for the WMS layer |
| name | string | - | Name of the WMS layer. By default the name is deduced from the path of the configuration file. |
| abstract | (Text) | - | Abstract for the layer. |
| keywords | (list of strings) | - | Keywords for the layer. |
| opaque | (int) | 0 | Is the WMS layer opaque. |
| queryable | (int) | 0 | Does the layer support GetFeatureInfo requests. |
| cascaded | (int) | 0 | How many times the layer has been cascaded. |
| no_subsets | (int) | 0 | Can only the full bounding box be requested. |
| fixed_width | (int) | 0 | Nonzero implies the size cannot be changed. |
| fixed_height | (int) | 0 | Nonzero implies the size cannot be changed |
| capabilities_start | (duration) | - | Limit start time of GetCapabilities |
| capabilities_end | (duration) | - | Limit end time of GetCapabilities |
The GetCapabilities list of available times occasionally needs to be limited. For example, one might have weather observations for more than one hunder years, and one would not want to generate a full many or time slider for all available times. Typically the observation engine provides metadata for observation producers which are more useful for example for time series requests than WMS requests.
The time duration can be
- an integer, which are understood to be minutes
- a string with an ISO-8601 time duration such as "P10D", "P1Y", "PT24H" etc
- an integer string not starting with P, and ending with a time suffix such as "100d"
Using ISO-8601 is recommended, the other syntaxes are supported mostly for backward compatibility as the same time duration parser is used in several other places.
WMS layer variants
In meteorology it is common to have multiple weather models with the same parameters available. For example for temperature the WMS layer settings would typically differ only in the name, title and abstract, and the product settings in the name of the producer and occasionally the name of the weather parameter (road temperature, ground temperature).
In such cases one use the same JSON file for the product settings, and define how the different variants are change in the settings. Below is an example how one might define some variants. It is also possible to change individual settings deeper in the JSON structure as long as a qid has been set. Substitutions then work as if the setting has been changed via the qid using a querystring. For example if a IsobandLayer layer has a qid with value temp_layer, one can change the parameter used by the layer by using temp_layer.parameter=SomeTemperature in the querystring, or by using "temp_layer.parameter": "SomeTemperature" in the variant settings.
Sample variant configuration
{
"parameter": "Temperature", // default
"producer": "pal", // default
// all variants to be generated
"variants":
[
{
"name": "fmi:pal:temperature",
"title": "FMI Forecast Temperature",
"abstract": "Terrain corrected temperature using atmospheric lapse rate and terrain elevation produced by FMI."
},
{
"name": "fmi:pal:dailymeantemperature",
"parameter": "DailyMeanTemperature",
"producer": "daily00",
"title": "FMI Forecast Mean Temperature",
"abstract": "Daily mean temperature produced by FMI."
},
{
"name": "fmi:roadmodel:roadtemperature",
"parameter": "RoadTemperature",
"producer": "roadmodel_skandinavia_pinta",
"title":
{
"en": "Roadmodel Troad",
"fi": "Tienpintalämpötila"
},
"abstract":
{
"en": "Road temperature produced by roadmodel",
"fi": "Tiesäämallin laskema tienpintalämpötila"
}
}
],
// Common settings
"projection": {},
"styles":
[
...
],
"views":
[
...
]
}
Configuration
In order to use the Dali plugin you need to edit two configuration files. These files are the main configuration file of the SmartMet Server environment and the Dali plugin specific configuration file.
Main configuration file
The main configuration file is named as "smartmet.conf". The main purpose of this configuration file is to define which plugins and engines need to be loaded when the server is started. In addition, it defines which configuration files these plugins and engines are using.
In order to use the Dali plugin you need to do the following steps:
- Check that there is the "dali" entry listed in the "plugins" attribute. If not, you should add it.
plugins = ["dali"]
- Check that the "engines" attribute contains all required engines such as AuthEngine, Contour, GeoEngine, GIS, ObsEngine, QEngine". Notice that the "geoengine" entry must be before the "obsengine" entry because of some linking reasons such as the ObsEngine uses the GeoEngine's functions.
engines = ["authengine","contour","geoengine","gis","obsengine","qengine"]
- Check that there is a dedicated configuration entry for the Dali plugin. In this entry you can disable or enable the usage of the Dali plugin. In addition, we can define the name and path of the configuration file for the Dali plugin.
dali:
{
disabled = false;
configfile = "/etc/smartmet/plugins/dali.conf"
}
Plugin configuration file
The plugin configuration file uses libconfig++ syntax. The path to this file is set in
smartmet.conf under the dali.configfile key.
Required settings
| Setting | Description |
|---|---|
root | Root directory for Dali product files. Customer-specific content lives under <root>/customers/<customer>/. |
wms.root | Root directory for WMS product files. Separate from root to allow different access controls. |
templates | Group mapping output type names to CTPP2 template base names (without .c2t). A default entry is required. |
regular_attributes | Array of allowed SVG regular attribute names (see the SVG specification appendix). |
presentation_attributes | Array of allowed SVG presentation attribute names. |
Optional settings
| Setting | Default | Description |
|---|---|---|
url | /dali | URL path of the native Dali endpoint. |
model | – | Default querydata producer name. |
language | – | Default language code. |
languages | – | Comma-separated list of supported language codes. |
customer | – | Default customer name. |
primaryForecastSource | – | Default primary forecast source (querydata or grid). |
template | svg | Default CTPP2 template base name (deprecated in favour of templates.default). |
templatedir | /usr/share/smartmet/wms | Directory containing CTPP2 .c2t template files. |
css_cache_size | 1000 | Maximum number of cached CSS stylesheets. |
max_image_size | – | Maximum allowed image area in pixels (width × height). |
wms.url | /wms | URL path of the WMS endpoint. |
wms.max_layers | 10 | Maximum number of WMS layers per GetMap request (DDoS protection). |
wms.quiet | false | If true, suppress WMS error stack traces in the log. |
wmts.url | /wmts | URL path of the WMTS endpoint. |
wmts.tile_width | 1024 | Default WMTS tile width. |
wmts.tile_height | 1024 | Default WMTS tile height. |
tiles.url | /tiles | URL path of the native tile endpoint. |
authenticate | false | Enable API-key authentication (requires the authentication engine). |
observation_disabled | false | Disable the observation engine (for deployments without ObsEngine). |
gridengine_disabled | false | Disable the grid engine (for deployments without GridEngine). |
heatmap.max_points | – | Maximum number of points in a heatmap layer. |
cache group
Controls the image cache used for binary formats (PNG, WebP, PDF).
| Setting | Default | Description |
|---|---|---|
cache.directory | – | Filesystem cache directory path. |
cache.memory_bytes | – | Maximum memory cache size in bytes (also accepts strings like "100M"). |
cache.filesystem_bytes | – | Maximum filesystem cache size in bytes. |
templates group
Maps product type strings to CTPP2 template names. The default entry is used when no
specific entry matches.
templates:
{
default = "svg";
geojson = "geojson";
topojson = "topojson";
kml = "kml";
}
precision group
Overrides the default SVG coordinate precision (number of decimal places) per layer type.
The key default sets the fallback.
precision:
{
default = 0.3;
geojson = 5.0;
topojson = 5.0;
}
unit_conversion group
Defines named unit conversions that can be referenced in product JSON. Each conversion
applies output = input * multiplier + offset.
unit_conversion:
{
ms_to_knots: { multiplier = 1.94384449244; }
celsius_to_fahrenheit:{ multiplier = 1.8; offset = 32.0; }
celsius_to_kelvin: { offset = 273.15; }
}
wms group
The wms group contains a get_capabilities sub-group for configuring the
GetCapabilities response (service metadata, contact information, supported CRS list,
supported output formats, INSPIRE extended capabilities, etc.) and a get_legend_graphic
sub-group for legend layout and symbol translations. See the test configuration file for
a fully annotated example.
Sample configuration file
Below is a minimal but complete plugin configuration file:
// Dali plugin configuration
url = "/dali";
model = "pal_skandinavia";
language = "en";
languages = "en,fi,sv";
customer = "default";
root = "/var/smartmet/dali";
templatedir = "/usr/share/smartmet/wms";
templates:
{
default = "svg";
geojson = "geojson";
topojson = "topojson";
kml = "kml";
}
precision:
{
default = 0.3;
geojson = 5.0;
topojson = 5.0;
}
cache:
{
directory = "/var/cache/smartmet/dali";
memory_bytes = "100M";
filesystem_bytes = "500M";
}
max_image_size = 20971520; // 20 M pixels
unit_conversion:
{
ms_to_knots: { multiplier = 1.94384449244; }
celsius_to_fahrenheit: { multiplier = 1.8; offset = 32.0; }
celsius_to_kelvin: { offset = 273.15; }
}
// WMS endpoint settings
wms:
{
url = "/wms";
root = "/var/smartmet/wms";
max_layers = 10;
quiet = false;
get_capabilities:
{
version = "1.3.0";
disable_updates = false;
expiration_time = 60;
service:
{
title = "SmartMet Web Map Service";
};
capability:
{
request:
{
getcapabilities: { format = ["text/xml"]; dcptype: ({ http: { get: { online_resource = "https://example.com/wms"; }; }; }); };
getmap: { format = ["image/png", "image/svg+xml"]; dcptype: ({ http: { get: { online_resource = "https://example.com/wms"; }; }; }); };
};
exception = ["XML"];
master_layer: { title = "SmartMet WMS"; queryable = 0; opaque = 1; };
};
};
};
// SVG attribute white-lists (abbreviated; full lists in the standard configuration)
regular_attributes = ["id", "class", "width", "height", "viewBox", "transform", /* ... */];
presentation_attributes = ["fill", "stroke", "stroke-width", "opacity", "font-size", /* ... */];
Examples
The following pages document the test suite outputs, demonstrating the rendering capabilities of each API endpoint. Map-projection examples are kept in a single separate document since map rendering is a supporting capability shared across all APIs rather than their primary purpose.
| Document | Contents |
|---|---|
| Map Projections | All PROJ-supported projections tested via the /dali map product |
| Dali Examples | All /dali endpoint test cases: output formats, isobands, isolabels, symbols, wind, METAR, Hovmoeller, fire weather, world projections, and more |
| WMS Examples | /wms GetMap, GetCapabilities, GetFeatureInfo, and GetLegendGraphic test cases |
| WMTS Examples | /wmts GetCapabilities, GetTile (isoband and temperature numbers) |
| OGC Tiles Examples | /tiles GetCollections and GetTile test cases |












/td>














