Skip to content

GSIP 173

Andrea Aime edited this page Dec 3, 2018 · 7 revisions

GSIP 173 - GetLegendGraphic JSON output format

Overview

Note: this is a new version of the abandoned GSIP 81, updated to be more strictly a legend, use the GeoServer facilities made available in the meantime, and generate a more compact output.

GeoServer should be able to generate a JSON representation of a legend that can be used client side to lay out a nice looking legend.

Proposed By

Andrea Aime - Ian Turton

Assigned to Release

This proposal is for GeoServer 2.15-beta.

Motivation

A server side generated legend graphic might not fit properly in the client user interface, is not dynamic, and may be layed out in a unsuitable way. A JSON description of the legend would allow clients to achieve deeper, seamless integration with the rest of the user interface/user experience.

Proposal

The overall JSON structure would look as follows:

{
  "Legend": [
    {
      "title": "Default",
      "symbolyzers": [
        {
          "Polygon": {
            "stroke": "#c0c0c0",
            "stroke-dasharray": [
              2,
              2
            ],
            "stroke-dashoffset": 10
          }
        }
      ]
    }
}

The basic JSON structure is a “Legend” object containing an array of Rule objects (see examples section at the end of the document).

Each Rule object has a title (to be shown in the legend), a filter, a z-order (derived from the current FeatureTypeStyle order, used to determine where the rule stacks up), and an array of symbolizers. The rule does not have a min/max scale denominator, as those should have been filtered during the GetLegendGraphic request, indicating the current scale.

Each Symbolizer has a type (“Line”, “Point”, “Polygon”, “Text”, “Raster”) and contains the visual properties of the corresponding SLD visual object, using as JSON property name the same name as the corresponding CSSProperty. Properties that have no visual value, such as the symbolizer geometry or most vendor options (e.g., label priority, label placement), are to be omitted. In case of doubt, the GeoCSS property names will be used.

Filters and dynamic properties

Filters, as well as any property depending on feature attributes, will be encoded as a CQL expression, and written as a string between square brackets, e.g.:

      "filter": "[total_usd_percapita <= 1000000]"
      "rotation": "[myAngleAttribute]"

Point symbology and external graphic

The point symbology can be complex to represent in JSON.

In general, all point symbolizer, graphic strokes, fill strokes will have a “url” property where they can retrieve the symbol in question, along with a “size” and a “rotation” property for eventual re-scaling.

The “url” is going to be always backed by the existing KML IconService, which is going to be moved to the WMS module for re-use. The classes in that module also provide facilities for extracting the necessary properties from the style and build the URL.

In addition to that, simple external graphic will also have a “external-graphic-url” property pointing to the external graphic, and a “external-graphic-type” reporting the type of referenced image, just like in SLD.

Finally, for marks a “mark” property with the mark name will be provided, along with fill and stroke related properties to allow rebuilding the simplest symbols on the client side too, e.g.:

{
  "type": "Point",
  "mark": "square",
  "fill": "0xFF0000",
  "stroke": "0x0000",
  "stroke-width": 1,
  "url": "http://cloudsdi.geo-solutions.it:80/geoserver/kml/icon/point?0.0.0=",
  "Size": 32
}

{
  "type": "Point",
  "external-graphic-url": "http://myserver/icon.png",
  "external-graphic-type": "image/png",
  "url": "http://cloudsdi.geo-solutions.it:80/geoserver/kml/icon/point?0.0.0=",
  "size": 32
}

Examples

Example GetLegendGraphic call:

http://localhost:8080/geoserver/sf/wms?service=WMS&version=1.1.0&request=GetLegendGraphic&layer=sf:sfdem&styles=&bbox=589980.0,4913700.0,609000.0,4928010.0&width=512&height=385&srs=EPSG:26713&format=application/json&outputFormat=application/json

Raster colormap example output:

{
  "Legend": [
    {
      "title": "This is the rule title",
      "symbolyzers": [
        {
          "type": "Raster",
          "colormap": {
            "entries": [
              {
                "label": "nodata",
                "opacity": "0.0",
                "quantity": "-500",
                "color": "#000000"
              },
              {
                "label": "values",
                "opacity": null,
                "quantity": "0",
                "color": "#AAFFAA"
              },
              {
                "label": null,
                "opacity": null,
                "quantity": "1000",
                "color": "#00FF00"
              },
              {
                "label": "values",
                "opacity": null,
                "quantity": "1200",
                "color": "#FFFF00"
              },
              {
                "label": "values",
                "opacity": null,
                "quantity": "1400",
                "color": "#FF7F00"
              },
              {
                "label": "values",
                "opacity": null,
                "quantity": "1600",
                "color": "#BF7F3F"
              },
              {
                "label": "values",
                "opacity": null,
                "quantity": "2000",
                "color": "#000000"
              }
            ]
          }
        }
      ]
    }
  ]
}

Categorized vector data example

{
  "Legend": [
    {
      "title": "Default",
      "symbolyzers": [
        {
          "Polygon": {
            "stroke": "#c0c0c0",
            "stroke-dasharray": [
              2,
              2
            ],
            "stroke-dashoffset": 10
          }
        }
      ]
    },
    {
      "title": "low",
      "filter": "[total_usd_percapita <= 1000000]",
      "symbolyzers": [
        {
          "Polygon": {
            "fill-opacity": 0.5,
            "fill": "#00ff00"
          }
        }
      ]
    },
    {
      "title": "mid",
      "filter": "[total_usd_percapita > 1000000 AND total_usd_percapita <= 10000000]",
      "symbolyzers": [
        {
          "Polygon": {
            "fill-opacity": 0.5,
            "fill": "#ff0000"
          }
        }
      ]
    },
    {
      "title": "high",
      "filter": "[total_usd_percapita > 1000000]",
      "symbolyzers": [
        {
          "Polygon": {
            "fill-opacity": 0.5,
            "fill": "#0000ff"
          }
        }
      ]
    }
  ]
}

State

  • Under Discussion
  • In Progress
  • Completed
  • Rejected
  • Deferred

Motivation

Proposal

Backwards Compatibility

Feedback

Voting

Project Steering Committee:

  • Alessio Fabiani:
  • Andrea Aime:
  • Ben Caradoc-Davies:
  • Brad Hards:
  • Ian Turton:
  • Jody Garnett:
  • Jukka Rahkonen:
  • Kevin Smith:
  • Simone Giannecchini:

Links

Clone this wiki locally