Product definition

June 25, 2026 · View on GitHub

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

{ // *** Product
  "title": "MyRedMap",
  "projection":
   {
     "xsize": 300,
      "ysize": 550,
      "crs": "EPSG:2393",
      "bboxcrs": "EPSG:4326",
      "cy": 64.8,
      "cx": 25.4,
      "resolution": 2.5
   },
   "views": [
       {  // *** View 1
       "layers": [
        { // **** Layer 1 : Light blue background
           "layer_type": "background",
           "attributes":
           {
               "stroke": "none",
               "fill": "rgb(190,230,255)"
           }
        },
        { // ***  Layer 2 : Red map with black borders
           "layer_type": "map",
           "map":
          {
            "schema": "natural_earth",
            "table": "europe_country_wgs84"
           },
           "attributes":
           {
            "class": "Europe",
            "stroke": "black",
            "stroke-width": "0.5",
            "fill": "rgb(255,0,0)"
           }
        }
       ]
     }
    ]
}
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:

TypeDescription
(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.
stringA 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).
StructureWhen 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 
NameTypeDefault valueDescription
language(string)config:languageLanguage code. The code does not need to follow ISO 639-1 or other similar standards, it is merely a code name.
customer(string)config:customerThe 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:modelThe producer name to be used when querying data from QEngine.
tz(string)UTCThe 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).
projectionProjectionThe Projection structure.
xmarginint0X-margin in pixels for clipping out features.
ymarginint0Y-margin in pixels for clipping out features.
clipboolfalseWhether 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:

NameTypeDefault valueDescription
quality(double)10The PNG compression level. 10=good, 20=poor
errorfactor(double)2.0Tuning parameter for color reduction. Must be greater than 1.0
maxcolors(int)0Desired maximum number of colors in the palette. Zero implies no maximum, and palette fitting will be fully adaptive
truecolor(bool)falseSet 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:

NameTypeDefault valueDescription
level(int)1The 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)100The display duration of each animation frame in milliseconds.
loop(int)0The animation loop count. Zero loops forever.
accumulate(bool)falseIf 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 
NameTypeDefault valueDescription
svg_tmpl(string)config:templatesThe 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.xsizeThe 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.ysizeThe 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.
attributesAttributes-The SVG image attributes for the product level <g>...</g> group.
defsDefs-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
NameTypeDefault valueDescription
qid(string)-An identifier for the view. Normally something simple such as "view1" or "v1".
attributesAttributes-The SVG attributes for the View level ... group.
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".
attributesAttributes-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.
ValueLayer
backgroundBackgroundLayer
mapMapLayer
locationLocationLayer
isobandIsobandLayer
isolineIsolineLayer
isolabelIsolabelLayer
symbolSymbolLayer
arrowArrowLayer
numberNumberLayer
nullNumllLayer
cloudceilingCloudCeilingLayer
streamStreamLayer
legendLegendLayer
timeTileLayer
tagTagLayer
translationTranslationLayer
windroseWindRoseLayer
metarMetarLayer
osmOSMLayer
postgisPostGISLayer
icemapIceMApLayer
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

{ // *** Product
  "title": "MyLightBlueBackground",
  "projection":
   {
     "xsize": 300,
      "ysize": 550,
      "crs": "EPSG:2393",
      "bboxcrs": "EPSG:4326",
      "cy": 64.8,
      "cx": 25.4,
      "resolution": 2.5
   },
   "views": [
       {  // *** View 1
       "layers": [
        { // **** Layer 1 : Light blue background
           "layer_type": "background",
           "attributes":
           {
               "stroke": "none",
               "fill": "rgb(190,230,255)"
           }
        }
       ]
     }
    ]
}

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

{ // *** Product
   "title": "MyMap",
   "projection":
   {
      "xsize": 300,
      "ysize": 550,
      "crs": "EPSG:2393",
      "bboxcrs": "EPSG:4326",
      "cy": 64.8,
      "cx": 25.4,
      "resolution": 2.5
   },
   "views": [
    {
     "layers": [
    {
       "layer_type": "map",
       "map":
       {
        "schema": "natural_earth",
        "table": "europe_country_wgs84"
       },
       "attributes":
       {
        "class": "Europe"
       }
    } ]
  }]
 }

The table below contains a list of attributes that can be defined for the map layer in addition to the common layer attributes.

MapLayer
NameTypeDefault valueDescription
mapMap-The Map structure
precisiondouble1.0Precision 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

{ // *** Product
  "title": "MyCities",
  "projection": {
    "xsize": 300, "ysize": 550, "crs": "EPSG:2393",
    "bboxcrs": "EPSG:4326", "cy": 64.8, "cx": 25.4,
    "resolution": 2.5
  },
  "views": [ { "layers": [
    { // *** Layer 1: Map background
      "layer_type": "map",
      "map": {
        "schema": "natural_earth",
        "table": "europe_country_wgs84"
      },
      "attributes": { "class": "Europe" }
    },
    { // *** Layer 2: Cities with labels
      "qid": "cities",
      "layer_type": "location",
      "keyword": "ely_cities",
      "css": "maps/map.css",
      "mindistance": 20,
      "symbols": {
        "default": [
          { "hilimit": 1000000, "symbol": "city" },
          { "lolimit": 1000000, "symbol": "bigcity" }
        ],
        "PPLC": [ { "symbol": "capital" } ]
      },
      "label": {
        "algorithm": "greedy",
        "candidates": 8,
        "offset": 4,
        "font_family": "sans-serif",
        "font_size": 10,
        "fill": "#333333",
        "stroke": "white",
        "stroke_width": 2,
        "stroke_opacity": 0.8,
        "classes": [
          { "lolimit": 500000,
            "font_size": 13, "font_weight": "bold" }
        ]
      }
    }
  ] } ]
}

The table below lists the attributes that can be defined for the location layer in addition to the common layer attributes.

LocationLayer
NameTypeDefault valueDescription
keywordstring-The geonames keyword identifying the set of locations to render.
mindistancedouble30Minimum 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.
labelLabelConfig-Optional text label placement. Omitting this key disables labels entirely (backward-compatible).
pan_invariantboolfalseWhen 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_margindouble-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
NameTypeDefaultDescription
algorithmstring"none"Placement algorithm. One of none, fixed, greedy, priority-greedy, simulated-annealing.
candidatesint8Number 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.)
offsetdouble5.0Pixel gap between the marker rectangle and the label bounding box edge.
symbol_sizedouble-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_widthdouble8.0Marker 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_heightdouble8.0Marker bounding-box height in pixels. See symbol_width.
symbol_margindouble0.0Extra margin added on every side of the marker rectangle before obstacle testing.
bbox_paddingdouble0.0Pixels 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_quantumint0If > 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_familystring"sans-serif"SVG font-family attribute.
font_sizedouble11Default font size in pixels.
font_weightstring"normal"Default font weight ("normal" or "bold").
fillstring"#333333"Default label text colour.
strokestring"white"Halo colour rendered as a thick stroke behind the text. Set to "" to disable the halo.
stroke_widthdouble2.0Halo stroke width in pixels.
stroke_opacitydouble0.75Halo stroke opacity (0 = transparent, 1 = opaque).
max_labelsint200Hard cap on the number of labels processed. Applied before placement; the highest-priority (geonames sort order) locations are kept.
free_space_weightdouble0.0When > 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_radiusdouble0.0When 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  3×mindistance~3 \times \text{mindistance} (e.g. 120) keeps the calculation local to each city's cluster.
priority_bucket_ratiodouble1.0Used 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.
classesarray[]Population-based style overrides; see table below. First matching class wins.

classes array entries (population-range style overrides):

NameTypeDefaultDescription
lolimitdouble-Inclusive lower population bound for this class to apply.
hilimitdouble-Exclusive upper population bound for this class to apply.
font_sizedouble0Font size override (0 = inherit LabelConfig default).
font_weightstring""Font weight override ("" = inherit).
fillstring""Text colour override ("" = inherit).
symbol_sizedouble-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_widthdouble0Per-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_heightdouble0Per-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 bias
free_space_weight: 1.5, free_space_radius: 120
Coastal cities push labels into the sea.

SA + free-space bias
Same bias on top of SA's overlap optimisation.
priority-greedy + bucketing
priority_bucket_ratio: 2.0
Close-population cities tied; shorter label wins.

greedy + pan-invariant
pan_invariant: true
Centred 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.

NameTypeDefaultDescription
fixed_positionstring"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
NameTypeDefaultDescription
iterationsint2000Number of SA steps. 2000 is sufficient for maps with up to ~150 labels.
initial_temperaturedouble1.0Starting temperature T₀. Higher values allow more uphill moves early in the search.
cooling_ratedouble0.99Geometric cooling factor applied each iteration: T ← T × cooling_rate.
overlap_weightdouble1.0Energy weight for label–label overlap area (in pixels²).
position_weightdouble0.2Energy 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

{ // *** Product
  "title": "MyCloudiness",
  "projection":
   {
     "xsize": 300,
      "ysize": 550,
      "crs": "EPSG:2393",
      "bboxcrs": "EPSG:4326",
      "cy": 64.8,
      "cx": 25.4,
      "resolution": 2.5
   },
   "views": [
       {  // *** View 1
       "layers": [
        { // ***  Layer 1 : Cloudiness
           "layer_type": "isoband",
           "isobands": "json:isobands/cloudiness.json",
           "css": "isobands/cloudiness.css",
           "parameter": "TotalCloudCover",
           "multiplier": 0.0898,
           "offset" : -0.49,
           "attributes":
           {
            "shape-rendering": "crispEdges"
           }
        }
       ]
     }
    ]
}
/td>
Product configuration file Produced image layer

{ // *** Product
    "title" : "Lightning heatmap for 15 minute period",
    "abstract" :  "Lightning heatmap for 15 minute period",
    "interval_start": 15,
    "interval_end": 15,
    "projection":
    {
        "crs": "EPSG:4326",
        "cx" : 25,
        "cy" : 65,
        "resolution" : 5,
        "xsize": 500,
        "ysize": 500
    },
    "producer": "flash",
    "views": [{
        "layers": [
            {
                "qid": "l",
                "layer_type": "isoband",
                "parameter": "peak_current",
                "isobands": "json:isobands/heatmap.json",
                "css": "isobands/heatmap.css",
                "attributes": {
                    "shape-rendering": "crispEdges"
                },
                "heatmap" : {
                    "resolution" : 10,
                    "radius" : 10,
                    "kernel" : "exp",
                    "deviation" : 10.0
                }
            },
            {
                "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": "0.3px"
                }
            }
        ]
    }]
}

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.
leveldoublelinearThe querydata level value. By default the first level is used.
interpolation(string)"linear"The interpolation method.
ValueDescription
linearValues are interpolated linearly.
midpoint|nearest|discreteNearest point value is used.
logarithmicLogarithmic 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.0Precision 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.0An offset for valid data values for unit conversion purposes.
smootherSmoother-Smoother settings for the 2D grid data.
samplingSampling-Sampling settings for the 2D grid data.
extrapolationint0How many grid cells to extrapolate data into regions with unknown values
minareadouble-Mimimum area for polygons, including holes
areaunits(string)km^2Units for minarea setting. km^2 or px^2
isofilterIsofilter-Lowpass filter for isolines/isobands. Preferable to smoother settings when data resolution is high.
heatmapHeatmap-Heatmap settings.
insideMap-Intersect isoband with the map
outsiderMap-Substract map from isoband.
intersect[Intersection] or Intersection-Alternate isoband(s) with which to intersect.
closed_rangebooltrueTrue if the last isoband is a closed interval. For example for percentages one would want range 90 <= x <= 100 instead of 90 <= x < 100.
validateboolfalseTrue if the geometries are to be validated (slow, for debugging purposes)
strictboolfalseIn strict mode invalid geometries cause an error. Extra information will be dumped to the journal and attached to the thrown exception
desliverboolfalseTrue if sliver polygons are to be removed
subdivideint0Bilinear 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_pixelsdouble2.0Automatic 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

{ // *** Product
  "title": "MyPressure",
  "projection":
   {
     "xsize": 300,
      "ysize": 550,
      "crs": "EPSG:2393",
      "bboxcrs": "EPSG:4326",
      "cy": 64.8,
      "cx": 25.4,
      "resolution": 2.5
   },
   "views": [
       {  // *** View 1
       "layers": [
      { // *** Layer 1 : Map
         "layer_type": "map",
         "map":
         {
          "schema": "natural_earth",
          "table": "europe_country_wgs84"
         },
         "attributes":
         {
          "class": "Europe"
         }
      },
      { // *** Layer 2 : Isoline
           "layer_type": "isoline",
           "isolines": "json:isolines/pressure.json",
           "css": "isolines/pressure.css",
           "parameter": "Pressure",
           "attributes" :
           {
            "class" : "Pressure"
           }
        }
       ]
     }
    ]
}
}

The table below contains a list of attributes that can be defined for the isoline layer in addition to the common layer attributes.

IsolineLayer
NameTypeDefault valueDescription
parameter(string)-The parameter name for the isolines.
level(double)-The querydata level value. By default the first level is used.
interpolationlinear(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.0Precision of SVG coordinates.
unit_conversion(string)-Name of desired unit conversion. Unit conversions are listed in the configuration file.
multiplier(double)1.0A multiplier for valid data values for unit conversion purposes.
offset(double)0.0An offset for valid data values for unit conversion purposes.
smootherSmoother-Smoother settings for the 2D grid data.
samplingSampling-Sampling settings for the 2D grid data.
extrapolationint0How many grid cells to extrapolate data into regions with unknown values.
minareadouble-Minimum area for closed linestrings in km^2.
areaunits(string)km^2Units for minarea setting. km^2 or px^2
insideMap-Intersect isoband with a map
outsideMap-Substract map from isoband
intersect[Intersect] or Intersect-Alternate isoband(s) with which to intersect.
validateboolfalseTrue if the geometries are to be validated (slow, for debugging purposes)
strictboolfalseIn strict mode invalid geometries cause an error. Extra information will be dumped to the journal and attached to the thrown exception
desliverboolfalseTrue if sliver polygons are to be removed
subdivideint0Bilinear 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_pixelsdouble2.0Automatic 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

  1. 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": {}
    },
]
}
  1. Define set of parameters which are used to generate Isoline structures. The following parameters must be defined:
NameTypeDescriptionNoteExample
qidprefixstringPrefix of isoline layer id.optional, default value is 'isoline_'qidprefix="pressure_isoline_"
startvalueint/doubleValue of first Isoline.startvalue=900
endvalueint/doubleValue of last Isolineendvalue=1100
intervalint/doubleThe interval between two successive Isolines.interval=10
exceptint/double or array of ints/doublesThe interval is not shown if it is divisible by except value(s) (modulo-operator)optionalexcept=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:

NameTypeDefaultDescription
fieldstring"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_passesint3Number 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_gradientdouble0.0If 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:

FieldTypical levelHighlights
PotentialTemperature (θ)850 hPaClassical thermal fronts.
PseudoAdiabaticPotentialTemperature (θw)850 hPaMoisture-aware fronts; Hewson's preferred choice.
Humidity / specific humidity850 hPaDry lines, moisture boundaries.
WindSpeedMS300 hPaJet-stream edges, jet streaks.
Potential vorticityupper levelsDynamical 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
NameTypeDefault valueDescription
labelLabel-Label settings including the orientation
angles[double][0,-45,45,180]Orientations in which local maxima are searched for suitable label positions.
uprightboolfalseOptionally turn labels upright if their angle is outside -90...90
max_angledouble60Allow label angles only in range -60...60
min_isoline_lengthdouble150Minimum length of the isoline in pixels for placing any labels
min_distance_otherdouble20Minimum distance to labels on isolines with another value
min_distance_samedouble50Minimum distance to labels on isolines with the same value
min_distance_selfdouble200Minimum distance to labels on the same isoline segment
min_distance_edgedouble10Minimum distance from the label centre to the image edge
max_distance_edgedouble9999Maximum distance from the label centre to the image edge
max_curvaturedouble90Maximum integrated turn (degrees) along the polyline within ±stencil_size of the candidate
stencil_sizeint5Search 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

{ // *** Product
    "title": "MyWeatherSymbols",
    "refs":
  {
      "myprojection": "json:maps/finlandprojection.json",
      "finland":  {
       "schema": "natural_earth",
       "table": "finland_country_wgs84",
       "minarea": 100,
       "mindistance": 1
      }
    },
    "projection": "ref:refs.myprojection",
    "views": [
   {
    "attributes":  { "id": "view1" },
       "layers": [
    {
         "layer_type": "map",
         "map": {
          "schema": "natural_earth",
           "table": "finland_country_wgs84",
          "minarea": 80,
           "mindistance": 5
         },
         "attributes": {
           "id": "europe_country_lines",
           "fill": "none",
           "stroke": "#666",
           "stroke-width": "0.5pt"
         }
       },
      {
        "layer_type": "symbol",
        "css": "symbols/weather.css",
        "symbols": "json:symbols/weather.json",
        "parameter": "weathersymbol",
        "scale": 1.25,
        "positions": {
         "layout": "latlon",
         "locations": [
             { "longitude": 24.93417, "latitude": 60.17556 },
             { "longitude": 28.27838, "latitude": 61.02292 },
             { "longitude": 21.85943, "latitude": 61.57477 },
             { "longitude": 25.60742, "latitude": 62.20806 },
             { "longitude": 27.89568, "latitude": 62.94718 },
             { "longitude": 23.13066, "latitude": 63.83847 },
             { "longitude": 27.40756, "latitude": 64.13355 },
             { "longitude": 24.56371, "latitude": 65.73641 },
             { "longitude": 28.15806, "latitude": 67.29250 },
             { "longitude": 24.15138, "latitude": 67.60517 },
             { "longitude": 27.02881, "latitude": 68.90596 }
         ],
     "dx": -20,
     "dy": -20
    }
  }]

}]

}

The table below contains a list of attributes that can be defined for the symbol layer in addition to the common layer attributes.

SymbolLayer 
NameTypeDefault valueDescription
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.0A multiplier for valid data values for unit conversion purposes.
offset(double)0.0An offset for valid data for unit conversion purposes.
level(double)-The querydata level value. By default the first level is used.
positionsPositions-The positions for the symbols.
minvaluesint0Minimum required number of valid values (or return error response)
maxdistancedouble5Maximum 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.
mindistanceint-Minimum distance in pixels between symbols.
prioritystring or integer array-Priority order of symbols.
rendering_orderstring"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
ValueDescription
minSymbols with smallest value are drawn first.
maxSymbols with biggest value are drawn first.
extremaSymbols with biggest mean deviation drawn first.
noneSymbols 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

{ // *** Product
  "title": "MyWindDirections",
  "projection":
   {
     "xsize": 300,
      "ysize": 550,
      "crs": "EPSG:2393",
      "bboxcrs": "EPSG:4326",
      "cy": 64.8,
      "cx": 25.4,
      "resolution": 2.5
   },
   "views": [
  {  // *** View 1
      "layers": [
    { // *** Layer 1 : Map
      "layer_type": "map",
      "map":
      {
        "schema": "natural_earth",
        "table": "europe_country_wgs84"
       },
       "attributes":
       {
        "class": "Europe"
       }
    },
{ // *** Layer 2 :Wind arrows
  "layer_type": "arrow",
  "css": "arrows/windarrow.css",
  "direction": "WindDirection",
  "speed": "WindSpeedMS",
  "attributes":
  {
    "id": "wind_arrows",
    "class": "WindArrow",
    "mask": "url(#windlegendmask)"
   },
   "arrows": "json:arrows/windarrow.json",
   "positions":
   {
    "x": 5,
    "y": 5,
    "dx": 30,
    "dy": 30,
    "ddx": 15
   }
} ]

} ] }

The table below contains a list of attributes that can be defined for the arrow layer in addition to the common layer attributes.

ArrowLayer 
NameTypeDefault valueDescription
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.
southflopbooleanfalseSet to true if the arrow symbol should be flipped horizontally in the southern hemisphere (used for wind barbs).
northflopbooleanfalseSet to true if the arrow symbol should be flipped horizontally in the northern hemisphere.
flipbooleanfalseSet to true if the arrow symbol should always be flipped vertically.
positionsPositions-Arrow position generation definitions.
minvaluesint0Minimum required number of valid values (or return error response)
maxdistancedouble5Maximum distance for a station to be accepted close enough to the position.
arrows[AttributeSelection]-SVG attribute selection for speed dependent arrows.
mindistanceint-Minimum distance in pixels between arrows.
rendering_orderstring"normal"Rendering order of the symbol is normal or reverse with respect to priority
prioritystring 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

{ // *** Product
   "title": "Temperature Overlay",
   "refs":
  {
    "myprojection": "json:maps/finlandprojection.json"
   },
   "projection": "ref:refs.myprojection",
   "views": [
     {  // *** View 1
     "layers": [
    { // *** Layer 1 : Map
      "layer_type": "map",
      "map":
      {
        "schema": "natural_earth",
        "table": "europe_country_wgs84"
       },
       "attributes":
       {
        "class": "Europe"
       }
    },
      { // *** Layer 2 : Numbers
       "layer_type": "number",
       "css": "numbers/numbers.css",
       "parameter": "Temperature",
       "attributes":
      {
         "class": "Number",
        "mask" : "url(#legendmask)"
        },
        "label": "json:numbers/integers.json",
        "positions":
      {
        "x": 20,
        "y": 18,
        "dx": 30,
        "dy": 30,
        "ddx": 15
       }
        } ]
  } ]
}

The table below contains a list of attributes that can be defined for the number layer in addition to the common layer attributes.

NumberLayer 
NameTypeDefault valueDescription
parameter(string)-The parameter name for the numbers.
level(double)-The querydata level value. By default the first level is used.
positionsPositions-The positions for the numbers.
minvaluesint0Minimum required number of valid values (or return error response)
maxdistancedouble5Maximum distance for a station to be accepted close enough to the position.
labelLabel-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.0A multiplier for valid data values for unit conversion purposes.
offset(double)0.0An offset for valid data for unit conversion purposes.
mindistanceint-Minimum distance in pixels between numbers.
rendering_orderstring"normal"Rendering order of the symbol is normal or reverse with respect to priority
prioritystring 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

{
    "title": "Cloud Ceiling",
    "abstract": "Cloud ceiling from FMI's AWS-stations",
    "producer": "observations_fmi",
    "interval_start": 15,
    "interval_end": 0,
    "language": "fi",
    "projection": {},
    "views": [{
	"qid": "v1",
	"attributes": {
	    "id": "view1"
	},
	"layers": [
	    {
		"qid": "finland",
		"layer_type": "map",
		"map":
		{
		    "schema": "natural_earth",
		    "table": "admin_0_countries",
		    "where": "iso_a2 IN ('FI','AX')"
		},
		"attributes": {
		    "id": "finland_country",
		    "fill": "rgb(255, 255, 204)"
		}
	    },
            {
                "qid": "finland-roads",
                "layer_type": "map",
                "map": {
                    "schema": "esri",
                    "table": "europe_roads_eureffin",
                    "where": "cntryname='Finland'",
                    "lines": true
                },
                "attributes": {
                    "class": "Road"
                }
            },
            {
		"qid": "borders",
		"layer_type": "map",
		"css": "maps/map.css",
		"map": {
                    "lines": true,
                    "schema": "esri",
                    "table": "europe_country_wgs84",
                    "mindistance": 2.5,
                    "minarea": 10
		},
		"attributes": {
                    "class": "Border",
                    "id": "BorderMap"
		}
            },
	    {
                "qid": "cities",
                "layer_type": "location",
                "keyword": "ely_cities",
                "css": "maps/map.css",
                "symbols": {
                    "default": [
                        {
                            "symbol": "city"
                        }
                    ]
                }
            },
	    {
		"qid": "cloud_ceiling_observations",
		"layer_type": "cloud_ceiling",
		"keyword": "synop_fi",
		"priority": "min",
		"mindistance": 35,
		"attributes": {
		    "fill": "rgba(0,0,255,1.00)", 
		    "font-family": "Verdana", 
		    "font-size": "16px", 
		    "text-anchor": "middle", 
		    "stroke": "#000000", 
		    "stroke-width": 0.5
		},		
		"label": {		    
		    "padding_char": "0",
		    "padding_length": 3,
		    "missing": "-",
		    "multiple": 100,
		    "dx": 6,
		    "dy": -10,
		    "rounding": "towardzero"
		}
	    }
        ]
    }]
}

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 
NameTypeDefault valueDescription
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)5Minimum generated stream line length in pixels.
max_length(int)2048Maximum generated stream line length in pixels.
line_length(int)32Length of a stream line segment in pixels.
xstep(int)20Streamline start point step size in x-direction.
ystep(int)20Streamline start point step size in y-direction.
precision(double)1.0Precision 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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 
NameTypeDefault valueDescription
parameter(string)-The parameter name for the direction.
compression(int)1Compression rate (1 = fast,low compression, 9 = slow, high compression).
interpolation(string)linearInterpolation method when fetching grid pixels (linear / nearest / transfer).
data_painter(string)defaultPainter element (default / range / stream / border / shadow / null).
data_opacity_land(double)1.0Opacity of the painted parameter over the land (0 .. 1).
data_opacity_sea(double)1.0Opacity 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).

NameTypeDefault valueDescription
land_position(string)noneLand layer position compared to the painted parameter (none,bottom,top).
land_color(ARGB)00000000Land layer color.
sea_position(string)noneSea layer position compared to the painted parameter (none,bottom,top).
sea_color(ARGB)00000000Sea layer color.
land_border_position(string)noneLand border position compared to the painted parameter (none,bottom,top).
land_border_color(ARGB)00000000Land border color.
sea_border_color(ARGB)00000000Sea border color.

The raster layer can paint topographical shadings above or below the painted parameter.

NameTypeDefault valueDescription
land_shading_position(string)noneLand shading layer position compared to the painted parameter (none,bottom,top).
land_shading_light(int)0Multiplier for light areas (0..1000).
land_shading_shadow(int)0Multiplier for shadow areas (0..1000).
sea_shading_position(string)noneSea shading layer position compared to the painted parameter (none,bottom,top).
sea_shading_light(int)0Multiplier for light areas (0..1000). Bigger value inceases the lightness.
sea_shading_shadow(int)0Multiplier 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.

NameTypeDefault valueDescription
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.

NameTypeDefault valueDescription
data_shadows(struct)This is a structure that contain same attributes as the "shadow" painter uses.

Attributes for the painter "default".

NameTypeDefault valueDescription
colormap(string)Name of the colormap for "default" painter. This refers to a colormap file.
smooth_colors(bool)trueShould the painter use "smooth colors" i.e. linarly interpolate colors.

Attributes for the painter "range".

NameTypeDefault valueDescription
ranges(array)Array of range structures.

Attributes for the "range" structure.

NameTypeDefault valueDescription
value_min(float)The minimum value of the value range.
value_max(float)The maximum value of the value range.
color_min(ARGB)00000000The color used with the minimum value (min_value).
color_max(ARGB)00000000The color used with the maximum value (max_value).
color_low(ARGB)00000000The color used with values that are smaller than the minimum value (min_value).
color_high(ARGB)00000000The color used with values that are bigger than the maximum value (max_value).

Attributes for the painter "stream".

NameTypeDefault valueDescription
stream_color(RGB)000000The color used for stream lines.
trace_length_min(int)10Minimum trace length of the stream line.
trace_length_max(int)128Maximum trace length of the steram line.
trace_step_x(int)10Stream line trace start point step size in x-direction.
trace_step_y(int)10Stream line trace start point step size in y-direction.
line_length(int)16Length 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.

NameTypeDefault valueDescription
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)trueShould 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.

NameTypeDefault valueDescription
line_color_land(ARGB)FF000000The stream line color on the land area.
line_color_sea(ARGB)FF000000The stream line color on the sea area.

Attributes for the "border" painter.

NameTypeDefault valueDescription
borders(array)The array of "border" structures.

Attributes for the "border" structure.

NameTypeDefault valueDescription
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)00000000The border color inside the curren area.
outside_color(ARGB)00000000The border color outside the current area.

Attributes for the "shadow" painter.

NameTypeDefault valueDescription
shadows(array)The array of "shadow" structures.

Attributes for the "shadow" structure.

NameTypeDefault valueDescription
value_min(float)The minimum value of the value range.
value_max(float)The maximum value of the value range.
color_min(ARGB)00000000The color used with the minimum value (min_value).
color_max(ARGB)40000000The color used with the maximum value (max_value).
dx(int)10The number of pixels that the shadow painting is shifted in x-direction .
dy(int)10The 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

{ // *** Product
   "title": "MyLegendLayer",
   "refs": {
       "myprojection": {
        "xsize": 300, "ysize": 550, "crs": "EPSG:2393",
      "bboxcrs": "EPSG:4326",  "cy": 64.8, "cx": 25.4,
      "resolution": 2.5
       }
   },
      "defs": {
    "styles": {
       ".Label": { "font": "Arial", "font-size": 9 },
       ".Units": { "font": "Arial", "font-size": 11 }
    },
    "layers": [  {
      "tag": "symbol",
      "attributes": { "id": "rect" },
      "layers": [ {
        "tag": "rect",
        "attributes": { "width": "14", "height": "12"}
       } ]
     },
     {
        "tag": "symbol",
        "attributes": { "id": "uptriangle" },
        "layers": [ {
        "tag": "path",
        "attributes": {"d": "M0 12,7 0,14 12Z"}
       } ]
     } ]
   },
   "projection": "ref:refs.myprojection",
   "views": [ { // *** View 1
     "layers": [ {  // *** Layer 1 : Wind speed labels
       "layer_type": "legend",
       "x": 10, "y": 10, "dx": 0, "dy": 12,
       "isobands": "json:isobands/wind.json",
          "symbols": {
        "css": "isobands/wind.css",
        "symbol": "rect",
        "start": "uptriangle",
        "attributes": {"stroke": "black", "stroke-width": "0.5"}
       },
       "labels":  {
        "type": "lolimit",
        "dx": 18,
        "dy": 14,
        "conversions": { "32": "> 32" }
       },
       "attributes": {
         "transform": "translate(-7 0)",
        "id": "windlegend",
         "class": "Label"
       },
       "layers": [ {
         "tag": "text",
            "cdata": "m/s",
         "attributes": { "x": 28, "y": 12, "class": "Units" }
      } ]
    } ]
     } ]
}

The table below contains a list of attributes that can be defined for the legend layer in addition to the common layer attributes.

LegendLayer 
NameTypeDefault valueDescription
xint10X-coordinate of the first legend symbol.
yint10Y-coordinate of the first legend symbol.
dxint0X-coordinate offset to the next legend symbol.
dyint20Y-coordinate offset to the next legend symbol.
symbolsLegendSymbols-Symbols to be used in the legend.
labelsLegendLabels-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

{ // *** Product
   "title": "MyTimeLayer",
   "projection" :
  {
    "crs": "EPSG:3067",
    "bboxcrs": "WGS84",
    "xsize": 320,
    "ysize": 384,
    "x1": 11.2,
    "x2": 31.5,
    "y1": 53.2,
    "y2": 66.5
   },
  "views": [
  { // *** View 1
       "layers": [
    { // *** Layer 1
       "layer_type" : "time",
         "timestamp" : "origintime",
       "timezone" : "UTC",
       "format" : "%d.%m.%Y/%H:%M:%S",
        "x" : 140,
         "y" : 120,
       "attributes": {
         "text-anchor": "middle",
          "font-size": 18
       }
     } ]
   } ]
}

The table below contains a list of attributes that can be defined for the time layer in addition to the common layer attributes.

TimeLayer 
NameTypeDefault valueDescription
timezone(string)UTCThe 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]validtimeType 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

{ // *** Product
   "title": "MyTagDemo",
   "width": 500,
   "height": 500,
   "views": [
    { // *** View 1
     "attributes":
     {
      "id": "view1"
     },
     "layers": [
     { // *** Layer 1: Yellow box with black borders
         "layer_type" : "tag",
         "tag" : "rect",
         "attributes" :
         {
        "x" : 150,
        "y" : 150,
        "width" : 200,
        "height" : 200,
        "fill" :  "yellow",
        "stroke" : "black"
         }
     },
     { // *** Layer 2 : Hello world text
         "layer_type" : "tag",
         "tag" : "text",
         "cdata" : "Hello world",
         "attributes" :
         {
        "x" : 250,
        "y" : 250,
        "font-size" :  18,
        "text-anchor" : "middle"
         }
     } ]
    } ]
}
Produced SVG file

<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">

<title>MyTagDemo</title>

<defs>    <style type="text/css"<>![CDATA[ ]]></style> </defs>

<g> <g id="view1">

<rect fill="yellow" height="200" style="stroke:black" width="200" x="150" y="150"/> <text font-size="18" style="text-anchor:middle" x="250" y="250">Hello world</text>

</g>

</g>

</svg>

The table below contains a list of attributes that can be defined for the tag layer in addition to the common layer attributes.

TagLayer 
NameTypeDefault valueDescription
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 
NameTypeDefault valueDescription
wkt(string)-The Well Known Text for the geometry.
precision(double)1.0Precision 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:

sourceDescription
"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
TypeJSON stringDefault line classDefault glyph characters
Cold front"cold"coldfrontC / c
Warm front"warm"warmfrontW / w
Occluded"occluded"occludedfrontO / o
Stationary"stationary"stationaryfrontS / s
Trough"trough"troughT / t
Ridge"ridge"ridgeR / 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 charShape
CFilled triangle pointing left of path (cold front active side)
cFilled triangle pointing right of path (cold front passive side)
WFilled semicircle bulging left of path (warm front active side)
wFilled 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
NameTypeDefaultDescription
front_sourcestring"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.)
frontsarray[]Front curve definitions (used when front_source is "synthetic"). See below.
stylesobjectPer-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:

NameTypeDefaultDescription
typestring"cold"Front type: "cold", "warm", "occluded", "stationary", "trough", or "ridge".
sidestring"left"Which side the symbols face: "left" or "right".
pointsarrayArray 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.

NameTypeDefaultDescription
line_cssstring(type-specific)CSS class name applied to the <use> stroke path.
glyph_cssstring(type-specific)CSS class name applied to the <text> glyph group.
glyph1string(type-specific)Character rendered at odd-numbered symbol positions.
glyph2string(type-specific)Character rendered at even-numbered symbol positions.
font_sizedouble30.0Glyph font size in pixels.
spacingdouble60.0Arc-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:

directionX axis (columns)Y axis (rows)Fixed coordinate(s)
time_lonLongitude, evenly spacedTime stepsLatitude, pressure level
time_latLatitude, evenly spacedTime stepsLongitude, pressure level
time_levelPressure levelsTime stepsLatitude + longitude
lon_levelLongitude, evenly spacedPressure levelsLatitude + valid time
lat_levelLatitude, evenly spacedPressure levelsLongitude + 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
NameTypeDefaultDescription
parameterstringParameter name, e.g. "GeopHeight" or "Temperature". Required.
leveldoublePressure 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.
directionstringtime_lonSlice geometry: time_lon, time_lat, time_level, lon_level, or lat_level.
latitudedouble60.0Fixed latitude for time_lon, time_level, lon_level.
longitudedouble25.0Fixed longitude for time_lat, time_level, lat_level.
lon_mindouble5.0Western longitude bound for time_lon and lon_level.
lon_maxdouble35.0Eastern longitude bound for time_lon and lon_level.
lat_mindouble55.0Southern latitude bound for time_lat and lat_level.
lat_maxdouble70.0Northern latitude bound for time_lat and lat_level.
nxinteger50Number of evenly-spaced sample points along the space axis. Ignored for time_level (the number of levels in the data is used).
time_ascendingbooleantruetrue = 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_ascendingbooleanfalsefalse = surface (high hPa) on the left / top (meteorological convention). true = surface on the right / bottom. Applies to all *_level directions.
isobandsstringPath to the isoband colour definitions, e.g. "json:isobands/geoph500.json".
cssstringPath 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

Hovmöller time × longitude – 500 hPa geopotential height

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

Hovmöller time × latitude – 500 hPa geopotential height

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

Hovmöller time × pressure level – temperature at 60°N 20°E

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
NameTypeDefault valueDescription
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.0Coordinate output precision
Graticule settings
NameTypeDefault valueDescription
layout(string)"grid"Normal "grid" or "ticks" at the image edges
step(integer)10Desired multiples in degrees
except([int)])-Undesired multiples in degrees
lengthint5Tick length in pixels when layout=ticks
attributes(Attributes)-Presentation attributes for the lines
labels(GraticuleLabels)-Optional label definitions for the lines
GraticuleLabels
NameTypeDefault valueDescription
layout(string)"none"Label layout algorithm: none
stepint-Desired multiples in degrees, by default inherited from Graticule settings
orientation(string)"horizontal"Label orientation: horizontal
degree_sign(bool)trueIf true, a degree sign will be appended to the number
minus_sign(bool)trueIf false, N/S/W/E will be appended at the end
dx(int)0X-offset when applicable in the selected layout
dy(int)0Y-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
NameTypeDefault valueDescription
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)trueTreat the circles as polylines or as polygons
circlesCircle/[Circle]-One or several circle definitions
labels(CircleLabels)-Optional circle radius labels
Circle settings
NameTypeDefault valueDescription
radius(double)-Circle radius in kilometers
attributes(Attributes)-Presentation attributes for the circle
CircleLabels settings
NameTypeDefault valueDescription
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)0Coordinate adjustment
dy(int)0Coordinate 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

{ // *** Product
   "title": "MyTranslationDemo ",
   "language": "en",
   "projection":
   {
    "crs": "data",
    "xsize": 500,
    "ysize": 500
   },
   "attributes":
   {
    "filter": "url(#shadow)"
   },
   "views": [
    {
     "qid": "v1",
     "attributes":
     {
      "id": "view1"
     },
     "layers": [
    {
       "layer_type": "translation",
       "translations":
       {
        "en": "Hello World!",
        "fi": "Moro maailma!"
       },
       "attributes":
       {
        "x": "250",
        "y": "250",
        "font-family": "Verdana",
        "font-weight": "bold",
        "font-size": "55",
        "fill": "yellow",
        "stroke": "black",
        "text-anchor": "middle",
        "filter": "url(#shadow)"
       }
    } ]
    } ]
}

The table below contains a list of attributes that can be defined for the translation layer in addition to the common layer attributes.

TranslationLayer 
NameTypeDefault valueDescription
tagstring"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

{
   "projection": ...,
   "defs": ...,
   "views": [
  {
     "layers": [
    {
       "layer_type": "windrose",
       "timezone": "UTC",   "starttimeoffset": -12, "endtimeoffset": 12,
       "css": "wind/windrose.css",
       "windrose": {
        "minpercentage": 3, "radius": 30, "sectors": 8,
        "symbol": "windrose",
        "attributes": { "class": "WindRose" },
        "connector": {
           "startoffset": 2, "endoffset": 30,
           "attributes": { class": "RoseConnector" }
        },
        "parameter": "max_t(WindSpeed)",
        "limits": [
           { "hilimit": 8,"attributes": { "class": "LightWind" } },
           { "lolimit": 8, "hilimit": 14, "attributes":
             { "class": "ModerateWind" } },
           { "lolimit": 14, "attributes":  { "class": "HeavyWind" } } ]
       },
       "observations": [
      {
        "parameter": "mean_t(T)",
         "label": { "dx": 25,   "dy": 16, "suffix": " °C" },
         "attributes": { class": "RoseTemperature"}
      },
      {
         "parameter": "mean_t(ws_10min)",
         "label": { "dx": 25,  "dy": 3, "suffix": " m/s" },
         "attributes":  { "class": "RoseMeanWind"}
      },
      {
         "parameter": "max_t(ws_10min)",
         "label": { "dx": 25,   "dy": -10, "suffix": " m/s" },
         "attributes": { "class": "RoseMaxWind"}
      } ],
          "stations": [
      {
         "fmisid": 100908, "longitude": 23,  "latitude": 61,
         "symbol": "station",
         "attributes": { "class": "StationMarker"},
         "title": {
          "text": "Utö, "dx": -10,  "dy": -26,
          "attributes": { "class": "StationName" }
         }
      },
      {
           "fmisid": 101673, "longitude": 25.3,  "latitude": 63.8,
         "symbol": "station",
         "attributes": { "class": "StationMarker" },
         "title": {
          "text": "Ulkokalla", "dx": -20, "dy": -26,
          "attributes": { "class": "StationName" }
         }
      } ]
    } ]
  } ]
}

The table below contains a list of attributes that can be defined for the windrose layer in addition to the common layer attributes.

WindRoseLayer 
NameTypeDefault valueDescription
timezonestring"UTC"The time zone used for the time settings.
starttimeoffsetint0Defines the (backward) time period with respect to the valid time of the actual time set for the layer.
endtimeoffsetint24Defines the (backward) time period with respect to the valid time of the actual time set for the layer.
windroseWindRose-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 layerNotes
> 5 km/px (continental/global)MapLayer with Natural EarthNatural 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 planetA 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 extractE.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 extractGeofabrik 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 rangeLayer typeData sourcePurpose
> 5 km/pxmap (Natural Earth)Built-in GIS engineGlobal/continental background
0.5–5 km/pxosm (simplified extract)osmdata.xyz land polygons or similarCoarse regional overview
< 0.5 km/pxosm (full regional extract)Geofabrik regional .osm.pbfDetailed 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
NameTypeDefaultDescription
backendstring"postgis"Data backend: "postgis" (via GIS engine) or "pmtiles" (via OSM engine mmap).
sourcestring-Required for pmtiles backend. Source name as defined in the OSM engine configuration (e.g. "scandinavia").
timestamp_filestring-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.
precisiondouble1.0Coordinate precision for SVG path output (PostGIS backend only).
feature_sets[FeatureSet]-Required for postgis backend. Array of feature set definitions (see below).
OSMLayer FeatureSet
NameTypeDefaultDescription
pgnamestring-Required. PostGIS connection name as defined in the GIS engine configuration.
schemastring-Required. Database schema (e.g. osm_scandinavia).
tablestring-Required. Table within the schema (e.g. planet_osm_polygon, planet_osm_line, planet_osm_point).
wherestring-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.
linesbooleanfalsetrue → apply line clipping (for road/river geometries); false → apply polygon clipping.
labelsbooleanfalseRender 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.
minareadouble-Minimum polygon area (m²) filter passed to the GIS engine.
mindistancedouble-Minimum distance simplification filter passed to the GIS engine.
mvt_layer_namestringtable nameName of the layer in MVT output. Defaults to the table value.
cssstring-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.
attributesAttributes-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):

FileBased onCharacter
osm/osm-bright.cssOpenMapTiles OSM BrightColorful, classic cartographic style with coloured road hierarchy
osm/positron.cssCartoDB PositronVery light, minimal greyscale — designed as a neutral data backdrop
osm/dark-matter.cssCartoDB Dark MatterDark/night style — high contrast for coloured overlays on dark background
osm/osm-liberty.cssOSM LibertyFree 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 css field).

CSS class reference

All theme files define the following CSS classes:

ClassOSM sourceGeometry
waternatural=water, waterway=*Polygon
forestlanduse=forest, landuse=wood, natural=woodPolygon
scrubnatural=scrub, natural=heathPolygon
grasslanduse=grass, landuse=meadow, natural=grasslandPolygon
beachnatural=beach, natural=sandPolygon
glaciernatural=glacierPolygon
residentiallanduse=residentialPolygon
commerciallanduse=commercial, landuse=retailPolygon
industriallanduse=industrialPolygon
parkleisure=park, leisure=recreation_ground, leisure=nature_reservePolygon
hospitalamenity=hospitalPolygon
schoolamenity=school, amenity=university, amenity=collegePolygon
airportaeroway=aerodromePolygon
buildingsbuilding=*Polygon
motorwayhighway=motorwayLine
trunkhighway=trunkLine
primaryhighway=primaryLine
secondaryhighway=secondaryLine
tertiaryhighway=tertiaryLine
roadhighway IN (residential, unclassified, living_street)Line
service_roadhighway IN (service, track)Line
footwayhighway IN (footway, cycleway, path)Line
railwayrailway=railLine
railway_transitrailway IN (subway, light_rail, tram)Line
coastlinenatural=coastlineLine
admin_countryboundary=administrative AND admin_level=2Line
admin_stateboundary=administrative AND admin_level IN (3,4)Line
place_labelplace IN (city, town, village, suburb)Point / label
road_labelRoad labelsLine / 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

{ // *** Product
   "title": "Ice map",
   "projection":
   {
    "crs": "EPSG:2393",  // YKJ
    "bboxcrs": "WGS84",
    "xsize": 800, "ysize": 800, "resolution": 1.75,
    "cx": 19,
    "cy": 60
   },
   "views": [
  { // *** View 1
     "attributes": { "id": "view1"},
     "layers": [
    { // *** Layer 1: Ice map
       "layer_type": "postgis",
       "lines": false,
       "pgname": "icemap",
       "schema": "icemap",
       "table": "seaice",
       "time_column": "publicationdate",
       "time_truncate": "day",
       "geometry_column": "geom",
       "css": "ice/icemap.css",
       "filters": "json:ice/icemap.json",
       "attributes":  { "id": "iceareas"  }
    },
    { // *** Layer 2 : Country borders
       "layer_type": "map",
       "enable": "png",
       "map":
       {
        "lines": true,
        "schema": "natural_earth",
        "table": "europe_country_wgs84",
        "minarea": 20,
        "mindistance": 5
       },
       "attributes":
       {
        "id": "europe_country_line",
        "class": "Border"
       }
    } ]
  } ]
}

The table below contains a list of attributes that can be defined for the PostGIS layer in addition to the common layer attributes.

PostGISLayer 
NameTypeDefault valueDescription
linesbooleanfalseShould 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_columnstring-The name of the time column in the specified schema.table.
time_truncatestring-
filtersFilters-
precision(double)1.0Precision 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

{ // *** Product
   "title": "Ice map",
   "projection":
   {
    "crs": "EPSG:2393",  // YKJ
    "bboxcrs": "WGS84",
    "xsize": 800, "ysize": 800, "resolution": 1.75,
    "cx": 19,
    "cy": 60
   },
   "views": [
  {
     "attributes": { "id": "view1"},
     "layers": [
    {
       "layer_type": "postgis",
       "pgname": "icemap",
       "schema": "icemap",
       "table": "seaice",
       "time_column": "publicationdate",
       "time_truncate": "day",
       "geometry_column": "geom",
       "css": "ice/icemap.css",
       "filters": "json:ice/icemap.json",
       "attributes":
       {
        "id": "iceareas"
       }
      },
      {
         "qid": "l3-",
         "enable": "png",
         "layer_type": "map",
         "map":
         {
          "lines": true,
          "schema": "natural_earth",
          "table": "europe_country_wgs84",
          "minarea": 20,
          "mindistance": 5
         },
         "attributes":
         {
          "id": "europe_country_line",
          "class": "Border"
         }
      }
     ]
  }
 ]
}

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_columnstring-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.
symbolA symbol. Position of symbol is fetched from databaseThe 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.
NameDescription
symbolA symbol. Position of symbol is fetched from database.
named_locationLocation 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_gridCoordinate grid. Grid is drawn on requested geometry. If result set contains no geometry, grid is drawn on whole.
degree_of_pressureDegree of pressure for ice.
mean_temperatureEllipse type of label, where we have background ellipse and text written on .
labelRectangle 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_labelSame as *label except that position (fetched from database) of label is modified a bit.
symbolstring-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.
patternstring0.0Name 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.0Precision 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

{
    "title": "Road observations",
    "producer": "road",
    "timestep": 15,
    "language": "fi",
    "projection":
    {
    },
    "views": [
        {
	    "qid": "v1",
	    "attributes":
	    {
		"id": "view1"
	    },
            "layers": [
		{
		    "qid": "finland",
		    "layer_type": "map",
		    "map":
		    {
			"schema": "natural_earth",
			"table": "admin_0_countries",
			"where": "iso_a2 IN ('FI','AX')"
		    },
		    "attributes":
		    {
			"id": "finland_country",
			"fill": "rgb(255, 255, 204)"
		    }
		},
                {
                    "qid": "finland-roads",
                    "layer_type": "map",
                    "map": {
                        "schema": "natural_earth",
                        "table": "europe_roads_eureffin",
                        "where": "cntryname='Finland'",
                        "lines": true
                    },
                    "attributes": {
			"fill": "none",
			"stroke": "rgb(150,150,150)",
			"stroke-width": "0.6px"
                    }
                },
		{
		    "tag": "g",
		    "layers": [
			{
			    "qid": "l4",
			    "layer_type": "map",
			    "map":
			    {
				"schema": "natural_earth",
				"table": "admin_0_countries",
				"minarea": 100
			    },
			    "attributes":
			    {
				"id": "europe_country_lines",
				"fill": "none",
				"stroke": "#222",
				"stroke-width": "0.3px"
			    }
			},
			{
			    "qid": "road_weather_stations_observations",
			    "layer_type": "finnish_road_observation",
			    "keyword": "road_weather_stations_master",
			    "mindistance": 20,
			    "attributes":
			    {
				"font-family": "synop",
				"font-style": "normal",
				"font-weight": "normal",
				"font-size": 48
			    }
			}
		    ]
		}
            ]
        }
    ]
}

The table below contains a list of attributes that can be defined for finnish road observation layer.

FinnishRoadObservationLayer
NameTypeDefault valueDescription
positionsPositions-The positions for the symbols.
labelLabel-Label definitions.
maxdistancedouble5Maximum distance for a station to be accepted close enough to the position.
mindistanceint-Minimum distance in pixels between symbols.
missingint106Synop-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:

  1. Mean air temperature (ILMA) of the observations from previous 24 hours
  2. Precipitation type (SADE) of the nearest observation, maximum 20 minutes away
  3. 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

{
    "title": "Present weather observations",
    "producer": "opendata",
    "timestep": 10,
    "language": "fi",
    "projection":
    {
    },
    "views": [
        {
	    "qid": "v1",
	    "attributes":
	    {
		"id": "view1"
	    },
            "layers": [
		{
		    "qid": "finland",
		    "layer_type": "map",
		    "map":
		    {
			"schema": "natural_earth",
			"table": "admin_0_countries",
			"where": "iso_a2 IN ('FI','AX')"
		    },
		    "attributes":
		    {
			"id": "finland_country",
			"fill": "rgb(255, 255, 204)"
		    }
		},
                {
                    "qid": "finland-roads",
                    "layer_type": "map",
                    "map": {
                        "schema": "natural_earth",
                        "table": "europe_roads_eureffin",
                        "where": "cntryname='Finland'",
                        "lines": true
                    },
                    "attributes": {
			"fill": "none",
			"stroke": "rgb(150,150,150)",
			"stroke-width": "0.6px"
                    }
                },
		{
		    "tag": "g",
		    "layers": [
			{
			    "qid": "l4",
			    "layer_type": "map",
			    "map":
			    {
				"schema": "natural_earth",
				"table": "admin_0_countries",
				"minarea": 100
			    },
			    "attributes":
			    {
				"id": "europe_country_lines",
				"fill": "none",
				"stroke": "#222",
				"stroke-width": "0.3px"
			    }
			},
			{
			    "qid": "wawa_observations",
			    "layer_type": "present_weather_observation",
			    "keyword": "synop_fi",
			    "paramater": "WW_AWS",
			    "mindistance": 20,
			    "attributes":
			    {
				"font-family": "synop",
				"font-style": "normal",
				"font-weight": "normal",
				"font-size": 48
			    }
			}
		    ]
		}
            ]
        }
    ]
}

The table below contains a list of attributes that can be defined for present weather observation layer.

PresentWeatherObservationLayer
NameTypeDefault valueDescription
positionsPositions-The positions for the symbols.
labelLabel-Label definitions.
maxdistancedouble5Maximum distance for a station to be accepted close enough to the position.
mindistanceint-Minimum distance in pixels between symbols.
missingint106Synop-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) or CAVOK/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 when WITHOUT_AVI is defined.

Aviation flight rule categories

The status box colour is derived from ceiling and visibility:

CategoryColourCeilingVisibility
LIFR (Low IFR)magenta< 500 ft< 1600 m
IFRred500–999 ft1600–4999 m
MVFR (Marginal VFR)blue1000–2999 ft5000–7999 m
VFRgreen≥ 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
NameTypeDefaultDescription
message_typestring"METAR"AVI message type to query: "METAR", "SPECI", "AWSMETAR", etc.
message_formatstring"TAC"Message format. Currently only "TAC" (raw METAR text) is supported.
exclude_specisbooleantrueSkip SPECI (special) reports when message_type is "METAR".
font_sizeint10Base font size in pixels for all text elements in the station plot.
plot_sizeint0Bounding-box side length in pixels. 0$ = \text{auto} (6 \times $font_size).
show_temperaturebooleantrueShow temperature in the upper-left quadrant of the station plot.
show_dewpointbooleantrueShow dew point below the temperature.
show_visibilitybooleantrueShow visibility in metres in the lower-left quadrant.
show_windbooleantrueDraw a wind barb at the station centre.
show_pressurebooleantrueShow QNH pressure (hPa) in the upper-right quadrant.
show_gustbooleantrueShow wind gust speed (kt) below the pressure when reported.
show_weatherbooleantrueShow present-weather TAC tokens (e.g. -RA, TSRA) below the station symbol.
show_sky_infobooleantrueShow sky-condition string (CAVOK, SKC, or cloud layer list) in the lower-right quadrant.
show_statusbooleantrueDraw a colour-coded aviation status box (LIFR/IFR/MVFR/VFR) at the station centre.
colorstring-Override all element colours with a single SVG colour value. When omitted each element uses its default colour.
mindistanceint0Minimum 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 
NameTypeDefault valueDescription
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.
attributesAttributes-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
NameTypeDefault valueDescription
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:

  1. Well Known Text definition - passed on to importFromWkt().
  2. "EPSG:n" - number passed on to importFromEPSG().
  3. "EPSGA:n" - number passed on to importFromEPSGA().
  4. "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
  5. "urn:ogc:def:crs:EPSG::n" - ogc urns
  6. PROJ.4 definitions - passed on to importFromProj4().
  7. Filename - file read for WKT, XML or PROJ.4 definition.
  8. Well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83, WGS84 or WGS72.
  9. WKT (directly or in a file) in ESRI format should be prefixed with ESRI:: to trigger an automatic morphFromESRI().
  10. "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
NameTypeDefault valueDescription
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). With passes >= 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 unlike box/morphology its 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; openclose does both. Preserves the magnitude of broad extrema. Unlike box and 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. Use morphology when 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); prefer box or median when 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-Golay keeps the most fine structure and the strongest extrema (a local polynomial fit), at the highest cost.
  • box removes the same scale of detail but, being a linear low-pass, attenuates the cold pockets the most.
  • median gives 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
NameTypeDefault valueDescription
sizeint-Savitzky-Golay: implies 2*N+1 adjacent points are used in the weighted mean.
degreeint-Savitzky-Golay: degree of the polynomial to fit to the data.
methodstring-Trax smoother: box, median or morphology. Enables the Trax smoother path.
radiusint-Trax smoother: window/element half-width in grid cells (2*radius+1 wide).
passesint3Trax smoother: number of repeats (box: passes>=3 approximates a Gaussian).
boundarystringnormalizedTrax smoother: edge handling — normalized, replicate or reflect.
morphologystringopencloseMorphology only: open, close or openclose.
preserve_missingbooltrueTrax 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.

NameTypeDefault valueDescription
resolutiondouble-Desired resolution in kilometers per pixel
relativeresolutiondouble-Desired relative resolution with respect to the resolution of the image resolution.
minresolutiondouble-Minimum resolution for applying sampling
maxresolutiondouble-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
NameTypeDefault valueDescription
resolutionint-Heatmap resolution.
radiusint-Stamp radius.
kernel(string)expStamp generation: linear, sqrt or exp (standard deviation).
deviation(double)10.0Deviation for exp.

Isoband structure

The table below contains a list of attributes used in the Isoband structure.

Isoband
NameTypeDefault valueDescription
qidstring-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.
attributesAttributes-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
NameTypeDefault valueDescription
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.
parameterstring-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.0A multiplier for valid data values for unit conversion purposes.
offset(double)0.0An 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
NameTypeDefault valueDescription
qidstring-The identifier for the isoline.
valuedouble-The isoline value. The default is NaN, which means calculating the line between missing and valid data.
attributesAttributes-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
NameTypeDefault valueDescription
typestringnoneSmoother type: none, average, linear, gaussian, tukey, taubin. Gaussian filtering seems to work best for plain smoothing; taubin additionally preserves feature sizes (see below).
radiusdouble0Filtering 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.
iterationsinteger1Number of passes. Zero disables filtering. Using 2-3 passes tends to remove small details better than simply increasing the radius.
lambdadouble0.5Taubin shrinking-pass factor, in the open interval (0,1). Only used when type=taubin.
mudouble-0.53Taubin inflating-pass factor. Must be negative with magnitude greater than lambda. Only used when type=taubin.
validatebool or objecttrueAdaptive 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
NameTypeDefaultDescription
enabledbooltrueWhether the backoff is active. The backoff is enabled by default; pass "validate": false (or {"enabled": false}) to turn it off.
triesint4Maximum 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.
bisectbooltrueAfter 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.
debugboolfalseLog 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.
ValueDescription
noneNo labels will be generated.
lolimitThe lower limit of the isoband will be used, unless the lolimit is missing.
hilimitThe upper limit of the isoband will be used, unless the hilimit is missing.
rangeA 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.
dxint30X-coordinate offset from the respective legend symbol.
dyint30Y-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.
attributesAttributes-The SVG attributes for the labels.

LegendSymbols structure

The table below contains a list of attributes used in the LegendSymbols structure.

LegendSymbols
NameTypeDefault valueDescription
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.
attributesAttributes-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
NameTypeDefault valueDescription
(any string)string-Translation for specific language
default(string)-The default translation
attributesAttributes-The extra SVG attributes for the translation

Map structure

The table below contains a list of attributes used in the Map structure.

Map
NameTypeDefault valueDescription
schemastring""The database schema.
tablestring""The database table.
wherestring""The optional where clause for the PostGIS query. For example "cntryname='Finland'"
linesbooleanfalseShould 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.
minareadouble-Minimum area for polygons in square kilometers. For Finland useful values are around 10-100.
mindistancedouble-Feature generalization tolerance in kilometers. For Finland useful values are around 1-4.
amalgamation_lengthdouble-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_areadouble-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_areadouble-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_amalgamatebooleanfalseWhen 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.
simplifierstring-Polygon/line simplification algorithm. Only visvalingam_whyatt (area-based vertex removal) is supported; none disables simplification.
tolerancedouble-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:

  1. 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 than amalgamation_length are accepted as part of the merged outline; the shapetools amalgamate tool used the same idea. amalgamation_area filters polygons inside this step; setting only amalgamation_area filters small polygons without merging.
  2. 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.
  3. mindistance: runs GEOS SimplifyPreserveTopology with a kilometre tolerance. Independent of the new pixel-based simplifier and may be combined with it.
  4. Simplifier (simplifier + tolerance): Visvalingam-Whyatt vertex removal applied per polygon. The underlying Fmi::GeometrySimplifier can 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:

OperationWhat it doesWhen to reach for it
minareaDrops 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 / _areaMerges 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_whyattArea-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.
  • minarea after 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 in EPSG:4326, metres in projected CRS), not km. At 60° N, 1° lat ≈ 111 km and 1° lon ≈ 56 km — so amalgamation_length=0.05 ≈ 3 km and amalgamation_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."

DescriptionMap 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.jsonamalgamation_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
NameTypeDefault valueDescription
fieldstring""The database field used to identify the region (region number or name).
parameterstring""The forecast parameter name.
features[MapFeature]-Optional mapping from field names to station numbers. If omitted, database field must be a region number
attributesAttributeSelection-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
NameTypeDefault valueDescription
valuestring-The value of the database field to be mapped to a region number, typically the name of the region
numberint-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.

Positions
uint
Name Type Default Value Description
layout(string)gridLayout type. If can have one of the following values:
ValueDescription
gridThis layout places symbols in a fixed step pixel grid. Sometimes not the best choice for arrow-layers, experts prefer graticule-layouts.
dataThis layout places symbols at the coordinates defined by the used querydata or the stations themselves if observations are used.
keywordThis layout places symbols at the coordinates of the locations associated with the given database keyword.
stationThis layout places symbols at the stations listed by the user.
latlonThis layout places symbols at the locations listed by the user.
graticuleThis layout places symbols along the edges of a NxN graticule grid with a fixed step size in degrees.
graticulefillThis 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.
xmarginuint0Extra margin for coordinate generation
ymarginuint0Extra margin for coordinate generation
xuint5Grid layout: X-coordinate of the first number
yuint5Grid layout: Y-coordinate of the first number
dxuint20Grid layout: X-coordinate offset to the next number on the same row
uint-Other layouts: X-coordinate adjustment for the selected position
dy20Grid layout: Y-coordinate offset to the next number on the next row
uint-Other layouts: Y-coordinate adjustment for the selected position
ddxuint0Grid layout: X-coordinate adjustment for the next row in grid layout
sizeuint10Graticule/GraticuleFill layout: size of the graticule cell in degrees
stepuint>/td>1Graticule layout: step size for the graticule in degrees
mindistancedouble50GraticuleFill layout: minimum distance between parallel and meridian symbols in pixels
keywordstring-Keyword layout: the database keyword
stationsXXXXX-Station layout: the user listed stations
locationsXXXXX-LatLon layout: the user listed locations
directionstring-Direction parameter. Specify a direction parameter or both "u" and "v" attributes
ustring-U-component of the direction parameter
vstring-V-component of the direction parameter
directionoffsetint0Extra displacement in the direction of the specified parameter
rotateint0Extra rotational displacement
outsideMap-Only coordinates outside this shape will generate output
insideMap-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
NameTypeDefault valueDescription
dxint0X-coordinate offset of the number from the actual coordinate.
dyint0Y-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.
multiplierdouble1Multiplier for the number for unit conversion purposes.
offsetdouble0Offset for the number for unit conversion purposes.
missingstring"-"Label for missing values. No text is output if the value is empty.
precisionint0Number of decimals.
multipledouble0If nonzero, round value to multiple of set value
roundingstring"tonearest"Rounding mode: tonearest, towardzero, upward or downward.
localestring-Locale for printing the number, for example fi_FI.
prefixstring""Prefix for the number.
suffixstring""Suffix for the number, usually for units.
plusprefixstring""Prefix replacing the sign for non-negative numbers.
minusprefixstring""Prefix replacing the sign for negative numbers.
orientationstring"horizontal"Horizontal or auto. Currently used only by IsolabelLayer
padding_charstring-Padding character for numbers.
padding_lengthint-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)
NameTypeDefaultDescription
algorithmstring"none"none | fixed | greedy | priority-greedy | simulated-annealing
candidatesint8Candidate positions per label: 4, 8, or 16 (Imhof preference order).
offsetdouble5.0Pixel gap between symbol anchor and label.
font_familystring"sans-serif"SVG font family.
font_sizedouble11Base font size in pixels.
font_weightstring"normal""normal" or "bold".
fillstring"#333333"Label text colour.
strokestring"white"Halo colour (empty string = no halo).
stroke_widthdouble2.0Halo width in pixels.
stroke_opacitydouble0.75Halo opacity.
max_labelsint200Hard cap on labels processed.
fixed_positionstring"ne"Fixed-algorithm position: n ne e se s sw w nw.
simulated_annealingobject-SA sub-object: iterations, initial_temperature, cooling_rate, overlap_weight, position_weight.
classesarray[]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
NameTypeDefault valueDescription
longitudedouble-The longitude of the coordinate.
latitudedouble-The latitude of the coordinate.
dxint-X-coordinate adjustment for the location.
dyint-Y-coordinate adjustment for the location.

Observation structure

The table below contains a list of attributes used in the Observation structure.

Observation
NameTypeDefault valueDescription
parameterstring-The parameter name of the observation.
labelLabel-How to render the observed value.
attributesAttributes-SVG rendering attributes for the generated text.

Station structure

The table below contains a list of attributes used in the Station structure.

Station
NameTypeDefault valueDescription
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.
attributesAttributes-The SVG rendering attributes for the symbol
titleTitle-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
NameTypeDefault valueDescription
minpercentageint0Required percentage of observations for a sector to be shown. This can be used to prevent very small sectors which are irrelevant.
radiusint20The radius of the wind rose.
sectorsint8The number of wind direction sectors in the wind rose
symbol(string)-The symbol for the wind rose, usually either a circle or omitted.
attributesAttributes-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.
limitsAttributeSelection-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
NameTypeDefault valueDescription
startoffsetint0Start offset for the connecting line. Usually zero, unless a symbol is placed at the station.
endoffsetint0End offset for the connecting line. Usually matches the radius of the wind rose.
attributesAttributes-The SVG rendering attributes for the connecting line.

Filters structure

The table below contains a list of attributes used in the Filter structure.

Filters
NameTypeDefault valueDescription
where(string)WHERE condition to be appended to the database query
attributesAttributesSVG-attributes for geometry
text_attributesAttributesSVG-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:

  1. We can create new grids that uses data from other grids that have the same timestamp.
  2. 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:

Stagestage= valueOperation
Raw JSON1File loaded as-is, no substitution
Reference substitution2Query-string json: and ref: replacements applied
Include expansion3json: and ref: values resolved from files
JSON Pointer dereferencing4$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:

AttributeResource type
filterSVG filter element
markerSVG marker element
marker-startSVG marker element
marker-midSVG marker element
marker-endSVG marker element
fillSVG pattern element
linearGradientSVG linearGradient element
radialGradientSVG 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:

  1. <root>/customers/<customer>/<subdir>/<name>.svg
  2. <root>/<name>.svg
  3. <root>/resources/layers/<subdir>/<name>.svg
  4. <root>/resources/<subdir>/<name>.svg
  5. <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:

ParameterTypeDescription
productstringPath to the product JSON file relative to customers/<customer>/products/. Example: product=temperature loads .../products/temperature.json.

Content and data selection:

ParameterTypeDefaultDescription
customerstringconfig defaultCustomer name; selects the sub-tree under <root>/customers/.
typestringsvgOutput format: svg, png, pdf, ps, xml, geojson, topojson, kml, geotiff, mvt, datatile.
producerstringconfig defaultData producer (querydata model name).
timestringlatestValid time in any format accepted by the MacGyver time parser (ISO 8601, SQL, epoch, offset from now).
time_offsetint0Minutes added to time.
origintimestringlatestQuerydata origin time (model run time).
interval_startintStart of observation time interval in minutes before time + time_offset.
interval_endintEnd of observation time interval in minutes after time + time_offset.
timestepintTimestep in minutes.
leveldoublePressure level in hPa.
elevationdoubleGrid-data level value; takes precedence over level when both are given.
elevation_unitstringUnit of the elevation value.
levelId / levelidintLevel type ID.
forecastNumberintEnsemble member number.
forecastTypeintForecast type.
geometryIdintGrid geometry ID.
sourcestringconfig defaultPrimary forecast source override.
languagestringconfig defaultLanguage code, e.g. fi, en, sv.
tzstringUTCTimezone used when parsing time.

Layout and clipping:

ParameterTypeDefaultDescription
widthint1000Image width in pixels (20–10000).
heightint1000Image height in pixels (20–10000).
marginintSets both xmargin and ymargin.
xmarginint0X-margin for symbol clipping (pixels).
ymarginint0Y-margin for symbol clipping (pixels).
clipboolfalseWrap each layer in a <clipPath>.
precisiondoubleNumber of decimals used when writing coordinates into the SVG output.

Overriding JSON structure fields:

ParameterTypeDescription
titlestringOverride the product title field.
projectionJSONOverride the top-level projection block.
viewsJSONOverride the top-level views array.
defsJSONOverride the top-level defs block.
attributesJSONOverride the top-level attributes block.
animationJSONOverride the animation block.
pngJSONOverride the png output options block.
svg_tmplstringSelect 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:

  1. The svg_tmpl query-string parameter.
  2. The svg_tmpl field in the product JSON.
  3. The entry in the templates config block matching the requested type (e.g. geojson, topojson, kml).
  4. The templates.default config entry.
  5. 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):

ChannelMeaning
RHigh byte of 16-bit quantised value
GLow byte of 16-bit quantised value
B0 (reserved)
A255 = 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):

ChannelMeaning
RHigh byte of 16-bit quantised band 1
GLow byte of 16-bit quantised band 1
BHigh byte of 16-bit quantised band 2
ALow 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 typeBandsNotes
isoband1Scalar parameter
isoline1Scalar parameter
raster1Scalar parameter
number1Scalar parameter
symbol1Scalar parameter
arrow1 or 2Single-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:

FieldDescription
nameProduct name relative to the customer's products/ directory, without the .json extension.
pathArray of path segments [customer, …, leaf], useful for building tree UIs.
titleProduct title if present.
abstractProduct abstract if present.
producerProducer declared at the top of the product JSON.
sourceProduct source if present.
imageOutput dimensions (width, height) and format (defaults to svg).
projectionSelected projection fields: crs, bboxcrs, xsize, ysize, x1, y1, x2, y2, cx, cy, resolution.
timedefault (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_templateURL 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.

ParameterDescription
printjson=1Print the fully expanded product JSON to the server console.
printhash=1Print the CTPP2 CDT object to the server console.
printparams=1Print the grid parameter list used by the product.
timer=1Print timing information per product generation stage.
stage=14Return the intermediate JSON at the given pipeline stage instead of rendering.
debug=1Include exception/backtrace details in error responses instead of a terse message.
quiet=1Suppress 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.

ParameterDescription
SERVICE=WMSMust be WMS.
VERSIONWMS version.
REQUEST=GetCapabilitiesRequest type.
FORMATtext/xml (default) or application/json.
LANGUAGELanguage code for titles/abstracts; defaults to the configured default language.
NAMESPACERestrict the listing to layers in the given namespace.
LAYOUTLayer hierarchy in the response: flat (default), recursive, or recursivetimes. Overrides the configured default.
STARTTIME / ENDTIMELimit the advertised time dimension to the given range (ISO 8601).
DIM_REFERENCE_TIMEAdvertise the time dimension for the given model run (origin time).
ENABLEINTERVALS1 / 0: force the multi-interval time dimension on or off, overriding the configured default.
SHOW_HIDDEN1: include layers flagged hidden in the response.

GetMap

Required parameters:

ParameterDescription
SERVICE=WMSMust be WMS.
VERSIONWMS version, e.g. 1.3.0. Must be one of the configured supported_versions.
REQUEST=GetMapRequest type.
LAYERSComma-separated list of layer names.
STYLESComma-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.
BBOXBounding box: minx,miny,maxx,maxy. Axis order follows the CRS definition in WMS 1.3.
WIDTHImage width in pixels.
HEIGHTImage height in pixels.
FORMATOutput 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:

ParameterDescription
TRANSPARENT=TRUE|FALSEWhether the background is transparent (default FALSE).
BGCOLOR=0xRRGGBBBackground colour (used only when TRANSPARENT=FALSE).
EXCEPTIONSException reporting format: XML (default), JSON, INIMAGE, or BLANK.
TIMETime 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.
ELEVATIONElevation value (forwarded to the layer as the level parameter).
DIM_REFERENCE_TIME / ORIGINTIMESelect 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_STARTExtension: start of observation time interval in minutes before TIME (integer).
DIM_INTERVAL_ENDExtension: 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:

ParameterDescription
QUERY_LAYERSComma-separated list of layers to query (subset of LAYERS). Required.
I / JPixel column / row of the query point (WMS 1.3 names). Required.
INFO_FORMATResponse 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:

ParameterDescription
VERSIONWMS version.
SLD_VERSIONSLD specification version, e.g. 1.1.0.
LAYERLayer name (singular, unlike GetMap's LAYERS).
FORMATOutput MIME type, e.g. image/png.

Optional parameters:

ParameterDescription
STYLEStyle name; defaults to default.
WIDTH / HEIGHTLegend size in pixels; default 500 × 500.
TIMETime 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
NameTypeDefault valueDescription
hidden(bool)falseIf true the WMS will not appear in GetCapabilities responses but GetMap requests continue to work.
titleText-Obligatory title for the WMS layer
namestring-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)0Is the WMS layer opaque.
queryable(int)0Does the layer support GetFeatureInfo requests.
cascaded(int)0How many times the layer has been cascaded.
no_subsets(int)0Can only the full bounding box be requested.
fixed_width(int)0Nonzero implies the size cannot be changed.
fixed_height(int)0Nonzero 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:

  1. Check that there is the "dali" entry listed in the "plugins" attribute. If not, you should add it.

	plugins = ["dali"]
  1. 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"]
  1. 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

SettingDescription
rootRoot directory for Dali product files. Customer-specific content lives under <root>/customers/<customer>/.
wms.rootRoot directory for WMS product files. Separate from root to allow different access controls.
templatesGroup mapping output type names to CTPP2 template base names (without .c2t). A default entry is required.
regular_attributesArray of allowed SVG regular attribute names (see the SVG specification appendix).
presentation_attributesArray of allowed SVG presentation attribute names.

Optional settings

SettingDefaultDescription
url/daliURL path of the native Dali endpoint.
modelDefault querydata producer name.
languageDefault language code.
languagesComma-separated list of supported language codes.
customerDefault customer name.
primaryForecastSourceDefault primary forecast source (querydata or grid).
templatesvgDefault CTPP2 template base name (deprecated in favour of templates.default).
templatedir/usr/share/smartmet/wmsDirectory containing CTPP2 .c2t template files.
css_cache_size1000Maximum number of cached CSS stylesheets.
max_image_sizeMaximum allowed image area in pixels (width × height).
wms.url/wmsURL path of the WMS endpoint.
wms.max_layers10Maximum number of WMS layers per GetMap request (DDoS protection).
wms.quietfalseIf true, suppress WMS error stack traces in the log.
wmts.url/wmtsURL path of the WMTS endpoint.
wmts.tile_width1024Default WMTS tile width.
wmts.tile_height1024Default WMTS tile height.
tiles.url/tilesURL path of the native tile endpoint.
authenticatefalseEnable API-key authentication (requires the authentication engine).
observation_disabledfalseDisable the observation engine (for deployments without ObsEngine).
gridengine_disabledfalseDisable the grid engine (for deployments without GridEngine).
heatmap.max_pointsMaximum number of points in a heatmap layer.

cache group

Controls the image cache used for binary formats (PNG, WebP, PDF).

SettingDefaultDescription
cache.directoryFilesystem cache directory path.
cache.memory_bytesMaximum memory cache size in bytes (also accepts strings like "100M").
cache.filesystem_bytesMaximum 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.

DocumentContents
Map ProjectionsAll PROJ-supported projections tested via the /dali map product
Dali ExamplesAll /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