Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dynamic attribute based point icons #364

Merged
merged 3 commits into from
Aug 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions data/styles/point_dynamic_icon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Style } from 'geostyler-style';

const pointDynamicIconPoint: Style = {
name: 'OL Style',
rules: [
{
name: 'OL Style Rule 0',
symbolizers: [{
kind: 'Icon',
image: '{{path}}',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why double brackets? we are trying to use this with GeoServer styles and they use $ brackets - ${path}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The double brackets are only used within the parsing context. This is basically just a hint for the parser to not just write a static url, but to use the styling-format dependent way of providing dymanic icons. In the case of openlayers styles, the parser creates a style function instead of a style object.

So at the end, the double brackets should not be part of the parsed style

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I get the reason for the placeholders, I'm curious about the choice to use double brackets vs the ${value} convention.

Maybe our use case is not very common, but this might make more sense if I explain... we want to take existing SLDs (from GeoServer) and use geostyler to conver them into OpenLayers styles in order to keep a consistent style between WMS rendered map images and client side renderings via WFS. So we convert from SLD -> geostyler style -> OL style.

When we do this we end up with placeholders using $ bracket notation that aren't understood as placeholders in the OL conversion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, now I understand. The main problem here is that the geostyler-sld-parser does not yet support handling dynamic icon urls. So currently, the href attribute of <OnlineResource> will always be interpreted as a static url (see here), and thereby the parser passes the ${value} notation to the other styles.

As soon as the geostyler-sld-parser supports this feature, it should replace the SLD specific notation (${value}) with the geostyler-style specific notation ({{value}}), so other parsers who support this feature will be able to interpret it.

}]
}
]
};

export default pointDynamicIconPoint;
19 changes: 19 additions & 0 deletions src/OlStyleParser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import OlStyleParser, { OlParserStyleFct } from './OlStyleParser';

import point_simplepoint from '../data/styles/point_simplepoint';
import point_icon from '../data/styles/point_icon';
import point_dynamic_icon from '../data/styles/point_dynamic_icon';
import point_simplesquare from '../data/styles/point_simplesquare';
import point_simplestar from '../data/styles/point_simplestar';
import point_simpletriangle from '../data/styles/point_simpletriangle';
Expand Down Expand Up @@ -491,6 +492,24 @@ describe('OlStyleParser implements StyleParser', () => {
expect(olIcon).toBeDefined();
});
});
it('can write an OpenLayers IconSymbolizer with feature attribute based src', () => {
expect.assertions(5);
return styleParser.writeStyle(point_dynamic_icon)
.then((olStyle: OlStyle) => {
expect(olStyle).toBeDefined();
const dummyFeat = new OlFeature({
path: 'image.jpg'
});

const styles = olStyle(dummyFeat, 1);
expect(styles).toBeDefined();
expect(styles).toHaveLength(1);

const olIcon: OlStyleIcon = styles[0].getImage() as OlStyleIcon;
expect(olIcon).toBeDefined();
expect(olIcon.getSrc()).toEqual(dummyFeat.get('path'));
});
});
it('can write a OpenLayers RegularShape square', () => {
expect.assertions(8);
return styleParser.writeStyle(point_simplesquare)
Expand Down
53 changes: 42 additions & 11 deletions src/OlStyleParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,10 @@ export class OlStyleParser implements StyleParser {
const hasTextSymbolizer = rules[0].symbolizers.some((symbolizer: Symbolizer) => {
return symbolizer.kind === 'Text';
});
if (!hasFilter && !hasScaleDenominator && !hasTextSymbolizer) {
const hasDynamicIconSymbolizer = rules[0].symbolizers.some((symbolizer: Symbolizer) => {
return symbolizer.kind === 'Icon' && symbolizer.image?.includes('{{');
});
if (!hasFilter && !hasScaleDenominator && !hasTextSymbolizer && !hasDynamicIconSymbolizer) {
if (nrSymbolizers === 1) {
return this.geoStylerStyleToOlStyle(geoStylerStyle);
} else {
Expand Down Expand Up @@ -910,16 +913,44 @@ export class OlStyleParser implements StyleParser {
* @return {object} The OL Style object
*/
getOlIconSymbolizerFromIconSymbolizer(symbolizer: IconSymbolizer): any {
return new this.OlStyleConstructor({
image: new this.OlStyleIconConstructor({
src: symbolizer.image,
crossOrigin: 'anonymous',
opacity: symbolizer.opacity,
scale: symbolizer.size || 1,
// Rotation in openlayers is radians while we use degree
rotation: symbolizer.rotate ? symbolizer.rotate * Math.PI / 180 : undefined
})
});
const baseProps = {
src: symbolizer.image,
crossOrigin: 'anonymous',
opacity: symbolizer.opacity,
scale: symbolizer.size || 1,
// Rotation in openlayers is radians while we use degree
rotation: symbolizer.rotate ? symbolizer.rotate * Math.PI / 180 : undefined
};
// check if IconSymbolizer.image contains a placeholder
const prefix = '\\{\\{';
const suffix = '\\}\\}';
const regExp = new RegExp(prefix + '.*?' + suffix, 'g');
const regExpRes = symbolizer.image ? symbolizer.image.match(regExp) : null;
if (regExpRes) {
// if it contains a placeholder
// return olStyleFunction
const olPointStyledIconFn = (feature: any) => {
let src: string | undefined = OlStyleUtil.resolveAttributeTemplate(feature, symbolizer.image as string, '');
// src can't be blank, would trigger ol errors
if(!src) {
src = symbolizer.image;
}
const image = new this.OlStyleIconConstructor({
...baseProps,
src // order is important
});
return new this.OlStyleConstructor({
image
});
};
return olPointStyledIconFn;
} else {
return new this.OlStyleConstructor({
image: new this.OlStyleIconConstructor({
...baseProps
})
});
}
}

/**
Expand Down