From e71318b0575c93b7ec02a9efb9825972aeb01ebe Mon Sep 17 00:00:00 2001 From: maltaesousa Date: Wed, 7 Jan 2026 17:58:04 +0100 Subject: [PATCH 1/7] fix printing circles --- src/VectorEncoder.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/VectorEncoder.ts b/src/VectorEncoder.ts index bf4f2d5..efed0d9 100644 --- a/src/VectorEncoder.ts +++ b/src/VectorEncoder.ts @@ -167,9 +167,6 @@ export default class VectorEncoder { if (styleFunction) { styleData = styleFunction(feature, resolution) as null | Style | Style[]; } - if (feature.getGeometry().getType() === 'Circle') { - feature = this.featureCircleAsPolygon(feature as Feature); - } const origGeojsonFeature = this.geojsonFormat.writeFeatureObject(feature); let styles = styleData !== null && !Array.isArray(styleData) ? [styleData] : (styleData as Style[]); @@ -186,6 +183,13 @@ export default class VectorEncoder { // FIXME: the return of the function is very complicate and would require // handling more cases than we actually do let geometry: any = style.getGeometry(); + // Fallback to the feature geometry if style doesn't give one. + if (geometry === null) { + geometry = feature.getGeometry(); + } + if (geometry.getType() === "Circle") { + geometry = this.featureCircleAsPolygon(feature as Feature).getGeometry(); + } let geojsonFeature; // In some cases, the geometries are objects, in other cases they're functions. // we need to ensure we're handling functions, wether they return an object or not. @@ -200,7 +204,6 @@ export default class VectorEncoder { geojsonFeatures.push(geojsonFeature); } else { geojsonFeature = origGeojsonFeature; - geometry = feature.getGeometry(); // no need to encode features with no geometry if (!geometry) { return; From 374ef162a03b827e6c5fa78e526b84255b39c9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Malta=20e=20Sousa?= Date: Thu, 8 Jan 2026 10:42:49 +0100 Subject: [PATCH 2/7] Update src/VectorEncoder.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Stéphane Brunner --- src/VectorEncoder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VectorEncoder.ts b/src/VectorEncoder.ts index efed0d9..3100589 100644 --- a/src/VectorEncoder.ts +++ b/src/VectorEncoder.ts @@ -188,7 +188,7 @@ export default class VectorEncoder { geometry = feature.getGeometry(); } if (geometry.getType() === "Circle") { - geometry = this.featureCircleAsPolygon(feature as Feature).getGeometry(); + geometry = fromCircle((feature as Feature).getGeometry(), Constants.CIRCLE_TO_POLYGON_SIDES); } let geojsonFeature; // In some cases, the geometries are objects, in other cases they're functions. From 1b0e5efa59f7a631e8d6e48d1b381caafa158eb2 Mon Sep 17 00:00:00 2001 From: maltaesousa Date: Wed, 21 Jan 2026 14:40:23 +0100 Subject: [PATCH 3/7] Cleaner changes and add test --- CHANGES.md | 10 +++++++++- package-lock.json | 4 ++-- package.json | 2 +- src/VectorEncoder.ts | 11 ----------- test.js | 45 +++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a455d93..443cc8f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,4 +1,12 @@ -# @geoblocks/mapfisprint changes +# @geoblocks/mapfishprint changes + +## 0.2.23 + +- Resolve a bug where a style with its own geometry as a Circle was transformed into a GeometryCollection resulting in an error + +Breaking changes: + +- VectorEncoder.featureCircleAsPolygon() was removed. ## 0.2.20 diff --git a/package-lock.json b/package-lock.json index e91bfb0..2111c7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@geoblocks/mapfishprint", - "version": "0.2.22", + "version": "0.2.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@geoblocks/mapfishprint", - "version": "0.2.22", + "version": "0.2.23", "license": "BSD-3-Clause", "devDependencies": { "@geoblocks/print": "0.7.9", diff --git a/package.json b/package.json index 9a66067..55e3bbf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@geoblocks/mapfishprint", - "version": "0.2.22", + "version": "0.2.23", "publishConfig": { "access": "public" }, diff --git a/src/VectorEncoder.ts b/src/VectorEncoder.ts index 3100589..fc3e645 100644 --- a/src/VectorEncoder.ts +++ b/src/VectorEncoder.ts @@ -601,15 +601,4 @@ export default class VectorEncoder { symbolizers.push(symbolizer); } } - - /** - * Converts a circle feature to a N sides polygon feature. - * Sides are defined in Constants.CIRCLE_TO_POLYGON_SIDES. - */ - protected featureCircleAsPolygon(feature: Feature) { - return new Feature({ - ...feature.getProperties(), - geometry: fromCircle(feature.getGeometry(), Constants.CIRCLE_TO_POLYGON_SIDES), - }); - } } diff --git a/test.js b/test.js index 4126fbe..6310c69 100644 --- a/test.js +++ b/test.js @@ -1,7 +1,7 @@ import test from 'node:test'; import assert from 'node:assert'; import Map from 'ol/Map.js'; -import {MFPEncoder, BaseCustomizer} from './lib/index.js'; +import {MFPEncoder, MFPVectorEncoder, BaseCustomizer} from './lib/index.js'; import TileLayer from 'ol/layer/Tile.js'; import OSM from 'ol/source/OSM.js'; import {View} from 'ol'; @@ -324,3 +324,46 @@ test('Vector features', async (t) => { }, }); }); + +test('MFPVectorEncoder can encode a circle with a circel in its style.geometry', async (t) => { + const geomStyleFn = () => { + return new Style({ + geometry: fCircle.getGeometry(), + fill, + stroke + }); + }; + fCircle.setStyle(geomStyleFn); + const vectorLayer = new VectorLayer({ + source: new VectorSource({ + features: [fCircle], + }), + }); + const customizer = new BaseCustomizer(); + const resolution = 1.0583354500042335; + const encodedSpecialLayer = new MFPVectorEncoder(vectorLayer.getLayerState(), customizer).encodeVectorLayer(resolution); + + assert.deepEqual(encodedSpecialLayer.geoJson.features[0], { + type: 'Feature', + geometry: { + type: 'Polygon', + coordinates: [ + [ + [796622, 5836960], + [796619.0710678119, 5836967.071067812], + [796612, 5836970], + [796604.9289321881, 5836967.071067812], + [796602, 5836960], + [796604.9289321881, 5836952.928932188], + [796612, 5836950], + [796619.0710678119, 5836952.928932188], + [796622, 5836960], + ], + ], + }, + properties: { + name: 'A circle', + _mfp_style: '1', + }, + }); +}); \ No newline at end of file From 5e27635752d793682e4a8e78979e1dfaeba92527 Mon Sep 17 00:00:00 2001 From: maltaesousa Date: Wed, 21 Jan 2026 15:11:37 +0100 Subject: [PATCH 4/7] Convert circle feature geometry before asigning it to original geometry --- src/VectorEncoder.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/VectorEncoder.ts b/src/VectorEncoder.ts index fc3e645..5bfe210 100644 --- a/src/VectorEncoder.ts +++ b/src/VectorEncoder.ts @@ -167,6 +167,11 @@ export default class VectorEncoder { if (styleFunction) { styleData = styleFunction(feature, resolution) as null | Style | Style[]; } + + const featureGeometry = feature.getGeometry(); + if (featureGeometry.getType() === "Circle") { + feature.setGeometry(fromCircle(featureGeometry as Circle, Constants.CIRCLE_TO_POLYGON_SIDES)); + } const origGeojsonFeature = this.geojsonFormat.writeFeatureObject(feature); let styles = styleData !== null && !Array.isArray(styleData) ? [styleData] : (styleData as Style[]); @@ -185,18 +190,21 @@ export default class VectorEncoder { let geometry: any = style.getGeometry(); // Fallback to the feature geometry if style doesn't give one. if (geometry === null) { - geometry = feature.getGeometry(); - } - if (geometry.getType() === "Circle") { - geometry = fromCircle((feature as Feature).getGeometry(), Constants.CIRCLE_TO_POLYGON_SIDES); + geometry = featureGeometry; } - let geojsonFeature; // In some cases, the geometries are objects, in other cases they're functions. // we need to ensure we're handling functions, wether they return an object or not. if (typeof geometry === 'function') { geometry = geometry(feature); } - if (geometry && typeof geometry === 'object') { + // no need to encode features with no geometry + if (!geometry) return; + + if (geometry.getType() === "Circle") { + geometry = fromCircle(geometry as Circle, Constants.CIRCLE_TO_POLYGON_SIDES); + } + let geojsonFeature; + if (typeof geometry === 'object') { // note that (typeof null === 'object') const styledFeature = feature.clone(); styledFeature.setGeometry(geometry); @@ -204,10 +212,6 @@ export default class VectorEncoder { geojsonFeatures.push(geojsonFeature); } else { geojsonFeature = origGeojsonFeature; - // no need to encode features with no geometry - if (!geometry) { - return; - } if (!this.customizer_.geometryFilter(geometry)) { return; } From 9692de2c587ee95787ae4a6b870c0d8d9c7ad5cc Mon Sep 17 00:00:00 2001 From: maltaesousa Date: Wed, 21 Jan 2026 15:13:07 +0100 Subject: [PATCH 5/7] Typo --- test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.js b/test.js index 6310c69..a89a6b9 100644 --- a/test.js +++ b/test.js @@ -325,7 +325,7 @@ test('Vector features', async (t) => { }); }); -test('MFPVectorEncoder can encode a circle with a circel in its style.geometry', async (t) => { +test('MFPVectorEncoder can encode a circle with a circle in its style.geometry', async (t) => { const geomStyleFn = () => { return new Style({ geometry: fCircle.getGeometry(), From d5bea323a65b5d1a579cc41cc09eacf3d4d54563 Mon Sep 17 00:00:00 2001 From: maltaesousa Date: Mon, 9 Feb 2026 15:00:10 +0100 Subject: [PATCH 6/7] v1.0.0 --- CHANGES.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 443cc8f..36e1164 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # @geoblocks/mapfishprint changes -## 0.2.23 +## 1.0.0 - Resolve a bug where a style with its own geometry as a Circle was transformed into a GeometryCollection resulting in an error diff --git a/package-lock.json b/package-lock.json index 2111c7c..de9db91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@geoblocks/mapfishprint", - "version": "0.2.23", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@geoblocks/mapfishprint", - "version": "0.2.23", + "version": "1.0.0", "license": "BSD-3-Clause", "devDependencies": { "@geoblocks/print": "0.7.9", diff --git a/package.json b/package.json index 55e3bbf..9f05a05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@geoblocks/mapfishprint", - "version": "0.2.23", + "version": "1.0.0", "publishConfig": { "access": "public" }, From 44b4e11d150db029c4aba091c781a91a435c1490 Mon Sep 17 00:00:00 2001 From: Guillaume Beraudo Date: Mon, 9 Feb 2026 15:44:36 +0100 Subject: [PATCH 7/7] Clone and 0.3 --- CHANGES.md | 2 +- package.json | 2 +- src/VectorEncoder.ts | 7 +++++-- test.js | 8 +++++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 36e1164..a1374fb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # @geoblocks/mapfishprint changes -## 1.0.0 +## 0.3.0 - Resolve a bug where a style with its own geometry as a Circle was transformed into a GeometryCollection resulting in an error diff --git a/package.json b/package.json index 9f05a05..a881ef1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@geoblocks/mapfishprint", - "version": "1.0.0", + "version": "0.3.0", "publishConfig": { "access": "public" }, diff --git a/src/VectorEncoder.ts b/src/VectorEncoder.ts index 5bfe210..b2996bf 100644 --- a/src/VectorEncoder.ts +++ b/src/VectorEncoder.ts @@ -169,7 +169,10 @@ export default class VectorEncoder { } const featureGeometry = feature.getGeometry(); - if (featureGeometry.getType() === "Circle") { + if (featureGeometry.getType() === 'Circle') { + const featureId = feature.getId(); + feature = feature.clone(); + feature.setId(featureId); feature.setGeometry(fromCircle(featureGeometry as Circle, Constants.CIRCLE_TO_POLYGON_SIDES)); } const origGeojsonFeature = this.geojsonFormat.writeFeatureObject(feature); @@ -200,7 +203,7 @@ export default class VectorEncoder { // no need to encode features with no geometry if (!geometry) return; - if (geometry.getType() === "Circle") { + if (geometry.getType() === 'Circle') { geometry = fromCircle(geometry as Circle, Constants.CIRCLE_TO_POLYGON_SIDES); } let geojsonFeature; diff --git a/test.js b/test.js index a89a6b9..1d9f0cd 100644 --- a/test.js +++ b/test.js @@ -330,7 +330,7 @@ test('MFPVectorEncoder can encode a circle with a circle in its style.geometry', return new Style({ geometry: fCircle.getGeometry(), fill, - stroke + stroke, }); }; fCircle.setStyle(geomStyleFn); @@ -341,7 +341,9 @@ test('MFPVectorEncoder can encode a circle with a circle in its style.geometry', }); const customizer = new BaseCustomizer(); const resolution = 1.0583354500042335; - const encodedSpecialLayer = new MFPVectorEncoder(vectorLayer.getLayerState(), customizer).encodeVectorLayer(resolution); + const encodedSpecialLayer = new MFPVectorEncoder(vectorLayer.getLayerState(), customizer).encodeVectorLayer( + resolution, + ); assert.deepEqual(encodedSpecialLayer.geoJson.features[0], { type: 'Feature', @@ -366,4 +368,4 @@ test('MFPVectorEncoder can encode a circle with a circle in its style.geometry', _mfp_style: '1', }, }); -}); \ No newline at end of file +});