@@ -38,6 +38,8 @@ import OlStyleCircle from 'ol/style/Circle';
3838import OlStyleFill from 'ol/style/Fill' ;
3939import OlStyleIcon , { Options as OlStyleIconOptions } from 'ol/style/Icon' ;
4040import OlStyleRegularshape from 'ol/style/RegularShape' ;
41+ import OlLineString from 'ol/geom/LineString' ;
42+ import OlMultiLineString from 'ol/geom/MultiLineString' ;
4143import { METERS_PER_UNIT } from 'ol/proj/Units' ;
4244
4345import OlStyleUtil , { DEGREES_TO_RADIANS } from './Util/OlStyleUtil' ;
@@ -53,6 +55,7 @@ import {
5355 LINE_WELLKNOWNNAMES ,
5456 NOFILL_WELLKNOWNNAMES
5557} from './Util/OlSvgUtil' ;
58+ import OlGraphicStrokeUtil from './Util/OlGraphicStrokeUtil' ;
5659
5760export interface OlParserStyleFct {
5861 ( feature ?: any , resolution ?: number ) : any ;
@@ -167,6 +170,9 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
167170 OlStyleCircleConstructor = OlStyleCircle ;
168171 OlStyleIconConstructor = OlStyleIcon ;
169172 OlStyleRegularshapeConstructor = OlStyleRegularshape ;
173+ OlLineStringContructor = OlLineString ;
174+ OlMultiLineStringConstructor = OlMultiLineString ;
175+ OlPointConstructor = OlGeomPoint ;
170176
171177 constructor ( ol ?: any ) {
172178 if ( ol ) {
@@ -178,6 +184,9 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
178184 this . OlStyleCircleConstructor = ol . style . Circle ;
179185 this . OlStyleIconConstructor = ol . style . Icon ;
180186 this . OlStyleRegularshapeConstructor = ol . style . RegularShape ;
187+ this . OlLineStringContructor = ol . geom . LineString ;
188+ this . OlMultiLineStringConstructor = ol . geom . MultiLineString ;
189+ this . OlPointConstructor = ol . geom . Point ;
181190 }
182191 }
183192
@@ -749,6 +758,7 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
749758 const hasMaxScale = geoStylerStyle ?. rules ?. [ 0 ] ?. scaleDenominator ?. max !== undefined ? true : false ;
750759 const hasScaleDenominator = hasMinScale || hasMaxScale ? true : false ;
751760 const hasFunctions = OlStyleUtil . containsGeoStylerFunctions ( geoStylerStyle ) ;
761+ const hasGraphicStroke = OlGraphicStrokeUtil . containsGraphicStroke ( geoStylerStyle ) ;
752762
753763 const nrSymbolizers = geoStylerStyle . rules [ 0 ] . symbolizers . length ;
754764 const hasTextSymbolizer = rules [ 0 ] . symbolizers . some ( ( symbolizer : Symbolizer ) => {
@@ -757,7 +767,14 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
757767 const hasDynamicIconSymbolizer = rules [ 0 ] . symbolizers . some ( ( symbolizer : Symbolizer ) => {
758768 return symbolizer . kind === 'Icon' && typeof ( symbolizer . image ) === 'string' && symbolizer . image . includes ( '{{' ) ;
759769 } ) ;
760- if ( ! hasFilter && ! hasScaleDenominator && ! hasTextSymbolizer && ! hasDynamicIconSymbolizer && ! hasFunctions ) {
770+ if (
771+ ! hasFilter
772+ && ! hasScaleDenominator
773+ && ! hasTextSymbolizer
774+ && ! hasDynamicIconSymbolizer
775+ && ! hasFunctions
776+ && ! hasGraphicStroke
777+ ) {
761778 if ( nrSymbolizers === 1 ) {
762779 return this . geoStylerStyleToOlStyle ( geoStylerStyle ) ;
763780 } else {
@@ -848,7 +865,9 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
848865 if ( isWithinScale && matchesFilter ) {
849866 rule . symbolizers . forEach ( ( symb : Symbolizer ) => {
850867 if ( symb . visibility === false ) {
868+ // TODO why pushing null instead of just skipping/returning?
851869 styles . push ( null ) ;
870+ return ;
852871 }
853872
854873 if ( isGeoStylerBooleanFunction ( symb . visibility ) ) {
@@ -858,16 +877,25 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
858877 }
859878 }
860879
861- const olSymbolizer : any = this . getOlSymbolizerFromSymbolizer ( symb , feature ) ;
862- // either an OlStyle or an ol.StyleFunction. OpenLayers only accepts an array
880+ const olSymbolizer : any = this . getOlSymbolizerFromSymbolizer ( symb , feature , resolution ) ;
881+ // either an OlStyle, OlStyle[] or an ol.StyleFunction. OpenLayers only accepts an array
863882 // of OlStyles, not ol.StyleFunctions.
864883 // So we have to check it and in case of an ol.StyleFunction call that function
865884 // and add the returned style to const styles.
866- if ( typeof olSymbolizer !== 'function' ) {
867- styles . push ( olSymbolizer ) ;
868- } else {
885+ if ( typeof olSymbolizer === 'function' ) {
869886 const styleFromFct : any = olSymbolizer ( feature , resolution ) ;
870887 styles . push ( styleFromFct ) ;
888+ } else if ( Array . isArray ( olSymbolizer ) ) {
889+ olSymbolizer . forEach ( ( s ) => {
890+ if ( typeof s === 'function' ) {
891+ const styleFromFct : any = s ( feature , resolution ) ;
892+ styles . push ( styleFromFct ) ;
893+ } else {
894+ styles . push ( s ) ;
895+ }
896+ } ) ;
897+ } else {
898+ styles . push ( olSymbolizer ) ;
871899 }
872900 } ) ;
873901 }
@@ -995,7 +1023,7 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
9951023 * @param symbolizer A GeoStyler-Style Symbolizer.
9961024 * @return The OpenLayers Style object or a StyleFunction
9971025 */
998- getOlSymbolizerFromSymbolizer ( symbolizer : Symbolizer , feature ?: OlFeature ) : OlStyle {
1026+ getOlSymbolizerFromSymbolizer ( symbolizer : Symbolizer , feature ?: OlFeature , resolution ?: number ) : OlStyle {
9991027 let olSymbolizer : any ;
10001028 symbolizer = structuredClone ( symbolizer ) ;
10011029
@@ -1010,7 +1038,7 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
10101038 olSymbolizer = this . getOlTextSymbolizerFromTextSymbolizer ( symbolizer , feature ) ;
10111039 break ;
10121040 case 'Line' :
1013- olSymbolizer = this . getOlLineSymbolizerFromLineSymbolizer ( symbolizer , feature ) ;
1041+ olSymbolizer = this . getOlLineSymbolizerFromLineSymbolizer ( symbolizer , feature , resolution ) ;
10141042 break ;
10151043 case 'Fill' :
10161044 olSymbolizer = this . getOlPolygonSymbolizerFromFillSymbolizer ( symbolizer , feature ) ;
@@ -1195,7 +1223,15 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
11951223 * @param symbolizer A GeoStyler-Style LineSymbolizer.
11961224 * @return The OL Style object
11971225 */
1198- getOlLineSymbolizerFromLineSymbolizer ( symbolizer : LineSymbolizer , feat ?: OlFeature ) : OlStyle | OlStyleStroke {
1226+ getOlLineSymbolizerFromLineSymbolizer (
1227+ symbolizer : LineSymbolizer , feat ?: OlFeature , resolution ?: number
1228+ ) : OlStyle | OlStyleStroke | OlStyle [ ] {
1229+ // graphicStroke will always be evaluated in a function, so we
1230+ // can assume that the feature is available.
1231+ if ( symbolizer . graphicStroke ) {
1232+ // If graphicStroke is set, we ignore unrelated stroke properties
1233+ return this . getOlGraphicStrokeFromGraphicStroke ( symbolizer , feat ! , resolution ! ) ;
1234+ }
11991235 for ( const key of Object . keys ( symbolizer ) ) {
12001236 if ( isGeoStylerFunction ( symbolizer [ key as keyof LineSymbolizer ] ) ) {
12011237 ( symbolizer as any ) [ key ] = OlStyleUtil . evaluateFunction ( ( symbolizer as any ) [ key ] , feat ) ;
@@ -1218,6 +1254,106 @@ export class OlStyleParser implements StyleParser<OlStyleLike> {
12181254 } ) ;
12191255 }
12201256
1257+ getOlGraphicStrokeFromGraphicStroke (
1258+ symbolizer : LineSymbolizer , feat : OlFeature , resolution : number
1259+ ) {
1260+ const geom = feat . getGeometry ( ) ;
1261+ if ( ! geom || ! ( geom instanceof this . OlLineStringContructor || geom instanceof this . OlMultiLineStringConstructor ) ) {
1262+ throw new Error (
1263+ 'GraphicStroke can only be applied to (Multi-)LineString geometries'
1264+ ) ;
1265+ }
1266+
1267+ const graphicStroke = symbolizer . graphicStroke ! ;
1268+ const symbolSize = this . getSymbolSizeFromGraphicStroke ( graphicStroke , feat ) ;
1269+ if ( symbolSize <= 0 ) {
1270+ console . warn ( 'Symbol size must be greater than zero for graphic stroke. No graphic will be drawn.' ) ;
1271+ return [ ] ;
1272+ }
1273+ const symbolRotation = graphicStroke . rotate ;
1274+ const evaluatedSymbolRotation = isGeoStylerFunction ( symbolRotation )
1275+ ? OlStyleUtil . evaluateNumberFunction ( symbolRotation , feat )
1276+ : symbolRotation ?? 0 ;
1277+ // We currently do not support expressions for dasharrays
1278+ const dashArray = symbolizer . dasharray as number [ ] | undefined ;
1279+ const dashOffset = symbolizer . dashOffset || 0 ;
1280+ const evaluatedDashOffset = isGeoStylerFunction ( dashOffset )
1281+ ? OlStyleUtil . evaluateNumberFunction ( dashOffset , feat )
1282+ : dashOffset ;
1283+
1284+ const symbolizerGenerator = ( modifiedGraphicStroke : any ) => {
1285+ return this . getOlSymbolizerFromSymbolizer ( modifiedGraphicStroke , feat , resolution ) ;
1286+ } ;
1287+
1288+ if ( geom instanceof this . OlLineStringContructor ) {
1289+ return OlGraphicStrokeUtil . processLineStringGraphicStroke (
1290+ geom ,
1291+ symbolSize ,
1292+ resolution ,
1293+ dashArray ,
1294+ evaluatedDashOffset ,
1295+ evaluatedSymbolRotation ,
1296+ graphicStroke ,
1297+ symbolizerGenerator ,
1298+ this . OlPointConstructor
1299+ ) ;
1300+ } else {
1301+ // For every line in the MultiLineString, we start the pattern at the
1302+ // beginning of the line. This means that the pattern can be
1303+ // discontinuous at vertices where the lines connect.
1304+ return geom . getLineStrings ( ) . flatMap ( line =>
1305+ OlGraphicStrokeUtil . processLineStringGraphicStroke (
1306+ line ,
1307+ symbolSize ,
1308+ resolution ,
1309+ dashArray ,
1310+ evaluatedDashOffset ,
1311+ evaluatedSymbolRotation ,
1312+ graphicStroke ,
1313+ symbolizerGenerator ,
1314+ this . OlPointConstructor
1315+ )
1316+ ) ;
1317+ }
1318+ }
1319+
1320+ /**
1321+ * Get the size of a symbol from graphicStroke.
1322+ * @param graphicStroke The graphicStroke from the LineSymbolizer.
1323+ * @param feat The feature.
1324+ * @returns The size of the symbol in pixels.
1325+ */
1326+ getSymbolSizeFromGraphicStroke (
1327+ graphicStroke : LineSymbolizer [ 'graphicStroke' ] , feat : OlFeature
1328+ ) {
1329+ let size = 0 ;
1330+ if ( graphicStroke ! . kind === 'Mark' ) {
1331+ const radius = graphicStroke ! . radius ;
1332+ if ( isGeoStylerFunction ( radius ) ) {
1333+ size = OlStyleUtil . evaluateNumberFunction ( radius , feat ) * 2 ;
1334+ } else {
1335+ size = ( radius ?? 0 ) * 2 ;
1336+ }
1337+ if ( size <= 0 ) {
1338+ return size ;
1339+ }
1340+ const strokeWidth = graphicStroke ! . strokeWidth ;
1341+ if ( isGeoStylerFunction ( strokeWidth ) ) {
1342+ size += OlStyleUtil . evaluateNumberFunction ( strokeWidth , feat ) ;
1343+ } else {
1344+ size += strokeWidth ?? 0 ;
1345+ }
1346+ } else if ( graphicStroke ! . kind === 'Icon' ) {
1347+ const iconSize = graphicStroke ! . size ;
1348+ if ( isGeoStylerFunction ( iconSize ) ) {
1349+ size = OlStyleUtil . evaluateNumberFunction ( iconSize , feat ) ;
1350+ } else {
1351+ size = iconSize ?? 0 ;
1352+ }
1353+ }
1354+ return size ;
1355+ }
1356+
12211357 /**
12221358 * Get the OL Style object from an GeoStyler-Style FillSymbolizer.
12231359 *
0 commit comments