/
OlStyler.js
252 lines (226 loc) · 9.04 KB
/
OlStyler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
import { getRules } from './Utils';
import categorizeSymbolizers from './categorizeSymbolizers';
import { processExternalGraphicSymbolizers } from './imageCache';
import { defaultPointStyle } from './styles/static';
import getPointStyle from './styles/pointStyle';
import getLineStyle from './styles/lineStyle';
import getPolygonStyle from './styles/polygonStyle';
import getTextStyle from './styles/textStyle';
import getLinePointStyle from './styles/linePointStyle';
import getPolygonPointStyle from './styles/polygonPointStyle';
const defaultStyles = [defaultPointStyle];
/**
* @private
* Convert symbolizers together with the feature to OL style objects and append them to the OL styles array.
* @example appendStyles(styles, point[j], feature, getPointStyle);
* @param {Array<ol/style>} styles Array of OL styles.
* @param {Array<object>} symbolizers Array of feature symbolizers.
* @param {ol/feature} feature OpenLayers feature.
* @param {Function} styleFunction Function for getting the OL style object. Signature (symbolizer, feature) => OL style.
* @param {Function} getProperty A property getter: (feature, propertyName) => property value.
*/
function appendStyles(
styles,
symbolizers,
feature,
styleFunction,
getProperty
) {
(symbolizers || []).forEach(symbolizer => {
const olStyle = styleFunction(symbolizer, feature, getProperty);
if (olStyle) {
styles.push(olStyle);
}
});
}
/**
* Create openlayers style
* @example OlStyler(getGeometryStyles(rules), geojson.geometry.type);
* @param {object} categorizedSymbolizers Symbolizers categorized by type, e.g. .pointSymbolizers = [array of point symbolizer objects].
* @param {object|Feature} feature {@link http://geojson.org|geojson}
* or {@link https://openlayers.org/en/latest/apidoc/module-ol_Feature-Feature.html|ol/Feature} Changed in 0.0.04 & 0.0.5!
* @param {Function} getProperty A property getter: (feature, propertyName) => property value.
* @param {object} [options] Optional options object.
* @param {boolean} [options.strictGeometryMatch] Default false. When true, only apply symbolizers to the corresponding geometry type.
* E.g. point symbolizers will not be applied to lines and polygons. Default false (according to SLD spec).
* @param {boolean} [options.useFallbackStyles] Default true. When true, provides default OL styles as fallback for unknown geometry types.
* @return ol.style.Style or array of it
*/
export default function OlStyler(
categorizedSymbolizers,
feature,
getProperty,
options = {}
) {
const {
polygonSymbolizers,
lineSymbolizers,
pointSymbolizers,
textSymbolizers,
} = categorizedSymbolizers;
const defaultOptions = {
strictGeometryMatch: false,
useFallbackStyles: true,
};
const styleOptions = { ...defaultOptions, ...options };
const geometry = feature.getGeometry
? feature.getGeometry()
: feature.geometry;
const geometryType = geometry.getType ? geometry.getType() : geometry.type;
let styles = [];
switch (geometryType) {
case 'Point':
case 'MultiPoint':
appendStyles(
styles,
pointSymbolizers,
feature,
getPointStyle,
getProperty
);
appendStyles(styles, textSymbolizers, feature, getTextStyle, getProperty);
break;
case 'LineString':
case 'MultiLineString':
appendStyles(styles, lineSymbolizers, feature, getLineStyle, getProperty);
if (!styleOptions.strictGeometryMatch) {
appendStyles(
styles,
pointSymbolizers,
feature,
getLinePointStyle,
getProperty
);
}
appendStyles(styles, textSymbolizers, feature, getTextStyle, getProperty);
break;
case 'Polygon':
case 'MultiPolygon':
appendStyles(
styles,
polygonSymbolizers,
feature,
getPolygonStyle,
getProperty
);
if (!styleOptions.strictGeometryMatch) {
appendStyles(
styles,
lineSymbolizers,
feature,
getLineStyle,
getProperty
);
}
appendStyles(
styles,
pointSymbolizers,
feature,
getPolygonPointStyle,
getProperty
);
appendStyles(styles, textSymbolizers, feature, getTextStyle, getProperty);
break;
default:
if (styleOptions.useFallbackStyles) {
styles = defaultStyles;
}
}
// Set z-index of styles explicitly to fix a bug where GraphicStroke is always rendered above a line symbolizer.
styles.forEach((style, index) => style.setZIndex(index));
return styles;
}
/**
* @private
* Extract feature id from an OpenLayers Feature.
* @param {Feature} feature {@link https://openlayers.org/en/latest/apidoc/module-ol_Feature-Feature.html|ol/Feature}
* @returns {string} Feature id.
*/
function getOlFeatureId(feature) {
return feature.getId();
}
/**
* @private
* Extract a property value from an OpenLayers Feature.
* @param {Feature} feature {@link https://openlayers.org/en/latest/apidoc/module-ol_Feature-Feature.html|ol/Feature}
* @param {string} propertyName The name of the feature property to read.
* @returns {object} Property value.
*/
function getOlFeatureProperty(feature, propertyName) {
return feature.get(propertyName);
}
/**
* Create an OpenLayers style function from a FeatureTypeStyle object extracted from an SLD document.
*
* **Important!** When using externalGraphics for point styling, make sure to call .changed() on the layer
* inside options.imageLoadedCallback to immediately see the loaded image. If you do not do this, the
* image icon will only become visible the next time OpenLayers draws the layer (after pan or zoom).
* @param {FeatureTypeStyle} featureTypeStyle Feature Type Style object.
* @param {object} options Options
* @param {function} options.convertResolution An optional function to convert the resolution in map units/pixel to resolution in meters/pixel.
* When not given, the map resolution is used as-is.
* @param {function} options.imageLoadedCallback Optional callback that will be called with the url of an externalGraphic when
* an image has been loaded (successfully or not). Call .changed() inside the callback on the layer to see the loaded image.
* @param {function} options.getProperty Optional custom property getter: (feature, propertyName) => property value.
* @returns {Function} A function that can be set as style function on an OpenLayers vector style layer.
* @example
* myOlVectorLayer.setStyle(SLDReader.createOlStyleFunction(featureTypeStyle, {
* imageLoadedCallback: () => { myOlVectorLayer.changed(); }
* }));
*/
export function createOlStyleFunction(featureTypeStyle, options = {}) {
const imageLoadedCallback = options.imageLoadedCallback || (() => {});
// Keep track of whether a callback has been registered per image url.
const callbackRef = {};
return (feature, mapResolution) => {
// Determine resolution in meters/pixel.
const resolution =
typeof options.convertResolution === 'function'
? options.convertResolution(mapResolution)
: mapResolution;
const getProperty =
typeof options.getProperty === 'function'
? options.getProperty
: getOlFeatureProperty;
// Determine applicable style rules for the feature, taking feature properties and current resolution into account.
const rules = getRules(featureTypeStyle, feature, resolution, {
getProperty,
getFeatureId: getOlFeatureId,
});
// Start loading images for external graphic symbolizers and when loaded:
// * update symbolizers to use the cached image.
// * call imageLoadedCallback with the image url.
processExternalGraphicSymbolizers(
rules,
featureTypeStyle,
imageLoadedCallback,
callbackRef
);
// Convert style rules to style rule lookup categorized by geometry type.
const categorizedSymbolizers = categorizeSymbolizers(rules);
// Determine style rule array.
const olStyles = OlStyler(categorizedSymbolizers, feature, getProperty);
return olStyles;
};
}
/**
* Create an array of OpenLayers style instances for features with the chosen geometry type from a style rule.
* Since this function creates a static OpenLayers style and not a style function,
* usage of this function is only suitable for simple symbolizers that do not depend on feature properties
* and do not contain external graphics. External graphic marks will be shown as a grey circle instead.
* @param {StyleRule} styleRule Feature Type Style Rule object.
* @param {string} geometryType One of 'Point', 'LineString' or 'Polygon'
* @returns {Array<ol.Style>} An array of OpenLayers style instances.
* @example
* myOlVectorLayer.setStyle(SLDReader.createOlStyle(featureTypeStyle.rules[0], 'Point');
*/
export function createOlStyle(styleRule, geometryType) {
const categorizedSymbolizers = categorizeSymbolizers([styleRule]);
const olStyles = OlStyler(
categorizedSymbolizers,
{ geometry: { type: geometryType } },
() => null,
{ strictGeometryMatch: true, useFallbackStyles: false }
);
return olStyles.filter(style => style !== null);
}