Skip to content

GSIP 173

Andrea Aime edited this page Jan 10, 2019 · 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

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 (also 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. For other properties, in case of doubt, the GeoCSS property names will be used. Properties that have no visual value, such as the symbolizer geometry or most vendor options (e.g., label priority, label placement), are not going to be included.

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"
          }
        }
      ]
    }
  ]
}

Style pre-processing and code re-use

The legend is generated from the style by means of filtering and pre-processing the style. The pre-processing is present in the BufferedImageLegendGraphicBuilder, and should possibly be extracted in a separate class (might be a base class or an external worker class, which ultimately returns a list of Rule objects).

State

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

Motivation

Proposal

Backwards Compatibility

Feedback

Voting

Project Steering Committee:

  • Alessio Fabiani: +1
  • Andrea Aime: +1
  • Brad Hards: +0
  • Ian Turton: +1
  • Jody Garnett: +1
  • Jukka Rahkonen:
  • Kevin Smith:
  • Simone Giannecchini: +0

Links

Clone this wiki locally