org.mapfish.print.map.style.json.MapfishJsonStyleParserPlugin Maven / Gradle / Ivy
package org.mapfish.print.map.style.json;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.json.JSONException;
import org.json.JSONObject;
import org.mapfish.print.Constants;
import org.mapfish.print.ExceptionUtils;
import org.mapfish.print.attribute.map.MapfishMapContext;
import org.mapfish.print.config.Configuration;
import org.mapfish.print.map.style.ParserPluginUtils;
import org.mapfish.print.map.style.StyleParserPlugin;
import org.mapfish.print.wrapper.json.PJsonObject;
import org.springframework.http.client.ClientHttpRequestFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static org.mapfish.print.map.style.json.MapfishJsonStyleVersion1.DEFAULT_GEOM_ATT_NAME;
/**
* Supports a JSON based style format.
*
* This style parser support two versions of JSON formatting. Both versions use the same parameter names for configuring
* the values of the various properties of the style but the layout differs between the two and version 2 is more flexible
* and powerful than version 1.
*
* Mapfish JSON Style Version 1
*
* Version 1 is compatible with mapfish print <= v2 and is based on the OpenLayers v2 styling. The layout is as follows:
*
*
* {
* "version" : "1",
* "styleProperty":"_gx_style",
* "1": {
* "fillColor":"#FF0000",
* "fillOpacity":0,
* "rotation" : "30",
*
* "externalGraphic" : "mark.png"
* "graphicName": "circle",
* "graphicOpacity": 0.4,
* "pointRadius": 5,
*
* "strokeColor":"#FFA829",
* "strokeOpacity":1,
* "strokeWidth":5,
* "strokeLinecap":"round",
* "strokeDashstyle":"dot",
*
* "fontColor":"#000000",
* "fontFamily": "sans-serif",
* "fontSize": "12px",
* "fontStyle": "normal",
* "fontWeight": "bold",
* "haloColor": "#123456",
* "haloOpacity": "0.7",
* "haloRadius": "3.0",
* "label": "${name}",
* "labelAlign": "cm",
* "labelRotation": "45",
* "labelXOffset": "-25.0",
* "labelYOffset": "-35.0"
* }
* }
*
*
* Mapfish JSON Style Version 2
*
* Version 2 uses the same property names as version 1 but has a different structure. The layout is as follows:
*
*
* {
* "version" : "2",
* // shared values can be declared here (at top level)
* // and used in form ${constName} later in json
* "val1" : "#FFA829",
* // default values for properties can be defined here
* " strokeDashstyle" : "dot"
* "[population > 300]" : {
* // default values for current rule can be defined here
* // they will override default values defined at
* // higher level
* "rotation" : "30",
*
* //min and max scale denominator are optional
* "maxScale" : 1000000,
* "minScale" : 100000,
* "symbolizers" : [{
* // values defined in symbolizer will override defaults
* "type" : "point",
* "fillColor":"#FF0000",
* "fillOpacity":0,
* "rotation" : "30",
* "externalGraphic" : "mark.png",
*
* "graphicName": "circle",
* "graphicOpacity": 0.4,
* "pointRadius": 5,
*
* "strokeColor":"${val1}",
* "strokeOpacity":1,
* "strokeWidth":5,
* "strokeLinecap":"round",
* "strokeDashstyle":"dot"
* },{
* "type" : "line",
* "strokeColor":"${val1}",
* "strokeOpacity":1,
* "strokeWidth":5,
* "strokeLinecap":"round",
* "strokeDashstyle":"dot"
* },{
* "type" : "polygon",
* "fillColor":"#FF0000",
* "fillOpacity":0,
*
* "strokeColor":"${val1}",
* "strokeOpacity":1,
* "strokeWidth":5,
* "strokeLinecap":"round",
* "strokeDashstyle":"dot"
* },{
* "type" : "text",
* "fontColor":"#000000",
* "fontFamily": "sans-serif",
* "fontSize": "12px",
* "fontStyle": "normal",
* "fontWeight": "bold",
* "haloColor": "#123456",
* "haloOpacity": "0.7",
* "haloRadius": "3.0",
* "label": "[name]",
* "fillColor":"#FF0000",
* "fillOpacity":0,
* "labelAlign": "cm",
* "labelRotation": "45",
* "labelXOffset": "-25.0",
* "labelYOffset": "-35.0"
* }
* ]}
* }
*
*
* As illustrated above the style consists of:
*
*
* - The version number (2) (required)
* -
* Common values which can be referenced in symbolizer property values.(optional)
*
Values can be referenced in the value of a property with the pattern: ${valName}
* Value names can only contain numbers, characters, _ or -
*
* Values do not have to be the full property they will be interpolated. For example:
* The value is ${val}
*
*
* -
* Defaults property definitions(optional):
*
* In order to reduce duplication and keep the style definitions small, default values can be specified. The
* default values in the root (style level) will be used in all symbolizers if the value is not defined. The
* style level default will apply to all symbolizers defined in the system.
*
*
* The only difference between a value and a default is that the default has a well known name, therefore defaults
* can also be used as values.
*
*
* -
* All the styling rules (At least one is required)
*
* A styling rule has a key which is the filter which selects the features that the rule will be used to draw and the
* rule definition object.
*
* The filter is either *
or an
*
* ECQL Expression) surrounded by square brackets. For example: [att < 23].
*
*
* WARNING: At the moment DWITHIN and BEYOND spatial functions take a unit parameter. However it
* is ignored by geotools and the distance is always in the crs of the geometry projection.
*
* The rule definition is as follows:
*
* -
* Default property values (optional):
*
* Each rule can also have defaults. If the style and the rule have a default for the same property
* the rule will override the style default. All defaults can be (of course) overridden by a value
* in a symbolizer.
*
*
* -
* minScale (optional)
*
* The minimum scale that the rule should evaluate to true
*
*
* -
* maxScale (optional)
*
* The maximum scale that the rule should evaluate to true
*
*
* -
* An array of symbolizers. (at least one required).
*
* A symbolizer must have a type property (point, line, polygon, text) which indicates the type of
* symbolizer and it has the attributes for that type of symbolizer. All values have defaults
* so it is possible to define a symbolizer as with only the type property. The only exception is
* that the "text" symbolizer needs a label property.
*
*
*
*
*
*
* Configuration Elements
* The items in the list below are the properties that can be set on the different symbolizers. In brackets list the symbolizers
* the values can apply to.
*
* Most properties can be static values or ECQL expressions. If the property has [ ]
around the property value
* then it will be interpreted as an ECQL expression. Otherwise it is assumed to be static text. If you need static text
* that start and ends with [ ]
then you will have to enter: ['propertyValue']
(where propertyValue
* start and ends with [ ]
.
*
*
* The items below with (ECQL) can have ECQL expressions.
*
*
* - fillColor(ECQL) - (polygon, point, text) The color used to fill the point graphic, polygon or text.
* - fillOpacity(ECQL) - (polygon, point, text) The opacity used when fill the point graphic, polygon or text.
* - rotation(ECQL) - (point) The rotation of the point graphic
* -
* externalGraphic - (point) one of the two options for declaring the point graphic to use. This can
* be a URL to the icon to use or, if just a string it will be assumed to refer to a file in the
* configuration directory (or subdirectory). Only files in the configuration directory (or subdirectory) will be allowed.
*
* -
* graphicName(ECQL) - (point) one of the two options for declaring the point graphic to use. This is the
* default and will be a square if not specified. The option are any of the Geotools Marks.
*
Geotools has by default 3 types of marks:
*
* - WellKnownMarks: cross, star, triangle, arrow, X, hatch, square
* - ShapeMarks: shape://vertline, shape://horline, shape://slash, shape://backslash, shape://dot, shape://plus,
* shape://times, shape://oarrow, shape://carrow, shape://coarrow, shape://ccarrow
* - TTFMarkFactory: ttf://fontName#code (where fontName is a TrueType font and the code is the code number of the
* character to render for the point.
*
*
* - graphicOpacity(ECQL) - (point) the opacity to use when drawing the point graphic
* - pointRadius(ECQL) - (point) the size at which to draw the point graphic
* -
* strokeColor(ECQL) - (line, point, polygon) the color to use when drawing a line or the outline of a
* polygon or point graphic
*
* - strokeOpacity(ECQL) - (line, point, polygon) the opacity to use when drawing the line/stroke
* - strokeWidth(ECQL) - (line, point, polygon) the widh of the line/stroke
* -
* strokeLinecap(ECQL) - (line, point, polygon) the style used when drawing the end of a line.
*
* Options: butt (sharp square edge), round (rounded edge), and square (slightly elongated square edge). Default is butt
*
*
* -
* strokeDashstyle - (line, point, polygon) A string describing how to draw the line or an array of
* floats describing the line lengths and space lengths:
*
* - dot - translates to dash array: [0.1, 2 * strokeWidth]
* - dash - translates to dash array: [2 * strokeWidth, 2 * strokeWidth]
* - dashdot - translates to dash array: [3 * strokeWidth, 2 * strokeWidth, 0.1, 2 * strokeWidth]
* - longdash - translates to dash array: [4 * strokeWidth, 2 * strokeWidth]
* - longdashdot - translates to dash array: [5 * strokeWidth, 2 * strokeWidth, 0.1, 2 * strokeWidth]
* - {string containing spaces to delimit array elements} - Example: [1 2 3 1 2]
*
*
* - fontColor(ECQL) - (text) the color of the text drawn
* - fontFamily(ECQL) - (text) the font of the text drawn
* - fontSize(ECQL) - (text) the font size of the text drawn
* - fontStyle(ECQL) - (text) the font style of the text drawn
* - fontWeight(ECQL) - (text) the font weight of the text drawn
* - haloColor(ECQL) - (text) the color of the halo around the text
* - haloOpacity(ECQL) - (text) the opacity of the halo around the text
* - haloRadius(ECQL) - (text) the radius of the halo around the text
* -
* label(ECQL) - (text) the expression used to create the label e. See the section on labelling for more
* details
*
* -
* labelAlign - (Point Placement) the indicator of how to align the text with respect to the geometry.
* This property must have 2 characters, the x-align and the y-align.
*
* X-Align options:
*
*
* - l - align to the left of the geometric center
* - c - align on the center of the geometric center
* - r - align to the right of the geometric center
*
*
* Y-Align options:
*
*
* - b - align to the bottom of the geometric center
* - m - align on the middle of the geometric center
* - t - align to the top of the geometric center
*
*
*
* - labelRotation(ECQL) - (Point Placement) the rotation of the label
* - labelXOffset(ECQL) - (Point Placement) the amount to offset the label along the x axis. negative number
* offset to the left
* - labelYOffset(ECQL) - (Point Placement) the amount to offset the label along the y axis. negative number
* offset to the top of the printing
* - labelAnchorPointX(ECQL) - (Point Placement) The point along the x axis that the label is started at
* anchored). Offset and rotation is relative to this point. Only one of labelAnchorPointX/Y or labelAlign will be respected,
* since they are both ways of defining the anchor Point
* - labelAnchorPointY(ECQL) - (Point Placement) The point along the y axis that the label is started at
* (anchored). Offset and rotation is relative to this point. Only one of labelAnchorPointX/Y or labelAlign will be respected,
* since they are both ways of defining the anchor Point
* - labelPerpendicularOffset(ECQL) - (Line Placement) If this property is defined it will be assumed that the
* geometry is a line and this property defines how far from the center of the line the label should be drawn.
*
*
* Labelling:
*
* Labelling in this style format is done by defining a text symbolizer ("type":"text"). All text sybmolizers consist of:
*
*
* - Label Property
* - Halo Properties
* - Font/weight/style/color/opacity
* - Placement Properties
*
*
* Label Property
*
* The label property defines what label will be drawn for a given feature. The value is either a string which will
* be the static label for all features that the symbolizer will be drawn on or a string surrounded by [] which
* indicates that it is an ECQL Expression. Examples:
*
*
* - Static label
* - [attributeName]
* - ['Static Label Again']
* - [5]
* - 5
* - env('java.home')
* - centroid(geomAtt)
*
*
* Halo Properties
*
* A halo is a space around the drawn label text that is color (using the halo properties). A label with a halo is like
* the drawn label text with a buffer around the label text drawn using the halo properties. This allows the label to
* be clearly visible regardless of the background. For example if the text is black and the halo is with, then the text will
* always be readable thanks to the white buffer around the label text.
*
* Font/weight/style/color/opacity
*
* The Font/weight/style/color/opacity properties define how the label text is drawn. They are for the most part equivalent to
* the similarly named css and SLD properties.
*
* Placement Properties
*
* An important part of defining a text symbolizer is defining where the text/label will be drawn. The placement properties
* are used for this purpose. There are two types of placements, Point and Line placement and only one type of placement
* can be used. The type of placement is determined by inspecting the properties in the text symbolizer and if the
* labelPerpendicularOffset property is defined then a line placement will be created for the text symbolizer.
*
*
* It is important to realize that since only one type of placement can be used, an error will be reported if
* labelPerpendicularOffset is defined in the text symbolizer along with any of the point placement properties.
*
* Point Placement
*
* Point placement defines an anchor point which is the point to draw the text relative to. For example an
* anchor point of 0.5, 0.5 ("labelAnchorPointX" : "0.5", "labelAnchorPointY" : "0.5") would position the start of the label
* at the center of the geometry.
*
*
* After anchor point, comes displacement displacement defines the distance
* from the anchor point to start the label. The combination of the two values determines the final location of the
* label.
*
* Lastly, there is a label rotation which defines the orientation of the label.
*
* There are two ways to define the anchor point, either the labelAnchorPointX/Y properties are set or the
* labelAlign property is set. If both are defined then the labelAlign will be ignored.
*
*
* ECQL references:
*
* -
* http://docs.geoserver.org/stable/en/user/filter/ecql_reference.html#ecql-expr
* -
* http://udig.refractions.net/files/docs/latest/user/Constraint%20Query%20Language.html
* -
* http://docs.geoserver.org/stable/en/user/filter/function_reference.html#filter-function-reference
* -
* http://docs.geotools.org/stable/userguide/library/cql/ecql.html
* -
* http://docs.geoserver.org/latest/en/user/tutorials/cql/cql_tutorial.html
*
*/
public final class MapfishJsonStyleParserPlugin implements StyleParserPlugin {
enum Versions {
ONE("1") {
@Override
Style parseStyle(final PJsonObject json,
final StyleBuilder styleBuilder,
final Configuration configuration) {
return new MapfishJsonStyleVersion1(json, styleBuilder, configuration, DEFAULT_GEOM_ATT_NAME).parseStyle();
}
}, TWO("2") {
@Override
Style parseStyle(final PJsonObject json,
final StyleBuilder styleBuilder,
final Configuration configuration) {
return new MapfishJsonStyleVersion2(json, styleBuilder, configuration).parseStyle();
}
};
private final String versionNumber;
Versions(final String versionNumber) {
this.versionNumber = versionNumber;
}
abstract Style parseStyle(PJsonObject json, StyleBuilder styleBuilder, Configuration configuration);
}
static final String JSON_VERSION = "version";
private StyleBuilder sldStyleBuilder = new StyleBuilder();
@Override
public Optional