From 2eb924c5238436bec8c22bbb758d1305a60a0299 Mon Sep 17 00:00:00 2001 From: Andrew Olsen Date: Thu, 5 Dec 2024 11:45:05 +1300 Subject: [PATCH 1/3] fix: set up test structure for QGIS 3.28 Tests should read both pre-3.28 QML - from data/qmls_old and, post-3.28 QML - stored in data/qmls When writing, the output should be 3.28 compatible. This only sets up the new text structure - the test data and conversion logic is not yet updated to be 3.28 compatible. --- data/qmls/line_simple.qml | 2 +- data/qmls/no_symbolizer.qml | 2 +- data/qmls/point_external_graphic.qml | 2 +- data/qmls/point_label.qml | 2 +- data/qmls/point_multiple_symbols.qml | 2 +- data/qmls/point_ranges.qml | 2 +- data/qmls/point_rules.qml | 2 +- data/qmls/point_simple.qml | 2 +- data/qmls/polygon_simple.qml | 2 +- data/qmls/polygon_simple_nostyle.qml | 2 +- data/qmls/text_text_buffer.qml | 2 +- data/qmls_old/line_simple.qml | 23 +++ data/qmls_old/no_symbolizer.qml | 4 + data/qmls_old/point_categories.qml | 163 ++++++++++++++++++ data/qmls_old/point_external_graphic.qml | 20 +++ data/qmls_old/point_label.qml | 14 ++ data/qmls_old/point_multiple_symbols.qml | 38 +++++ data/qmls_old/point_ranges.qml | 201 +++++++++++++++++++++++ data/qmls_old/point_rules.qml | 57 +++++++ data/qmls_old/point_simple.qml | 25 +++ data/qmls_old/polygon_simple.qml | 23 +++ data/qmls_old/polygon_simple_nostyle.qml | 23 +++ data/qmls_old/text_text_buffer.qml | 15 ++ src/QGISStyleParser.spec.ts | 176 ++++++++++---------- src/QGISStyleParser.ts | 10 +- 25 files changed, 717 insertions(+), 97 deletions(-) create mode 100644 data/qmls_old/line_simple.qml create mode 100644 data/qmls_old/no_symbolizer.qml create mode 100644 data/qmls_old/point_categories.qml create mode 100644 data/qmls_old/point_external_graphic.qml create mode 100644 data/qmls_old/point_label.qml create mode 100644 data/qmls_old/point_multiple_symbols.qml create mode 100644 data/qmls_old/point_ranges.qml create mode 100644 data/qmls_old/point_rules.qml create mode 100644 data/qmls_old/point_simple.qml create mode 100644 data/qmls_old/polygon_simple.qml create mode 100644 data/qmls_old/polygon_simple_nostyle.qml create mode 100644 data/qmls_old/text_text_buffer.qml diff --git a/data/qmls/line_simple.qml b/data/qmls/line_simple.qml index 3f6eff71..b2b4000d 100644 --- a/data/qmls/line_simple.qml +++ b/data/qmls/line_simple.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/no_symbolizer.qml b/data/qmls/no_symbolizer.qml index 554c0244..e354ec88 100644 --- a/data/qmls/no_symbolizer.qml +++ b/data/qmls/no_symbolizer.qml @@ -1,4 +1,4 @@ - + diff --git a/data/qmls/point_external_graphic.qml b/data/qmls/point_external_graphic.qml index b5f29d82..5febb8f7 100644 --- a/data/qmls/point_external_graphic.qml +++ b/data/qmls/point_external_graphic.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/point_label.qml b/data/qmls/point_label.qml index b2518c97..247e751e 100644 --- a/data/qmls/point_label.qml +++ b/data/qmls/point_label.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/point_multiple_symbols.qml b/data/qmls/point_multiple_symbols.qml index def659e7..c025578f 100644 --- a/data/qmls/point_multiple_symbols.qml +++ b/data/qmls/point_multiple_symbols.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/point_ranges.qml b/data/qmls/point_ranges.qml index f0b7bfcc..c89d1001 100644 --- a/data/qmls/point_ranges.qml +++ b/data/qmls/point_ranges.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/point_rules.qml b/data/qmls/point_rules.qml index 015197ee..6b2e7983 100644 --- a/data/qmls/point_rules.qml +++ b/data/qmls/point_rules.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/point_simple.qml b/data/qmls/point_simple.qml index cdf96047..2b9c499a 100644 --- a/data/qmls/point_simple.qml +++ b/data/qmls/point_simple.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/polygon_simple.qml b/data/qmls/polygon_simple.qml index 1522635f..60f705d9 100644 --- a/data/qmls/polygon_simple.qml +++ b/data/qmls/polygon_simple.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/polygon_simple_nostyle.qml b/data/qmls/polygon_simple_nostyle.qml index 1af90a75..3584a069 100644 --- a/data/qmls/polygon_simple_nostyle.qml +++ b/data/qmls/polygon_simple_nostyle.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls/text_text_buffer.qml b/data/qmls/text_text_buffer.qml index f1e57608..bf5840ee 100644 --- a/data/qmls/text_text_buffer.qml +++ b/data/qmls/text_text_buffer.qml @@ -1,5 +1,5 @@ - + diff --git a/data/qmls_old/line_simple.qml b/data/qmls_old/line_simple.qml new file mode 100644 index 00000000..3f6eff71 --- /dev/null +++ b/data/qmls_old/line_simple.qml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/no_symbolizer.qml b/data/qmls_old/no_symbolizer.qml new file mode 100644 index 00000000..554c0244 --- /dev/null +++ b/data/qmls_old/no_symbolizer.qml @@ -0,0 +1,4 @@ + + + + diff --git a/data/qmls_old/point_categories.qml b/data/qmls_old/point_categories.qml new file mode 100644 index 00000000..d07ad43e --- /dev/null +++ b/data/qmls_old/point_categories.qml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/point_external_graphic.qml b/data/qmls_old/point_external_graphic.qml new file mode 100644 index 00000000..b5f29d82 --- /dev/null +++ b/data/qmls_old/point_external_graphic.qml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/point_label.qml b/data/qmls_old/point_label.qml new file mode 100644 index 00000000..b2518c97 --- /dev/null +++ b/data/qmls_old/point_label.qml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/data/qmls_old/point_multiple_symbols.qml b/data/qmls_old/point_multiple_symbols.qml new file mode 100644 index 00000000..def659e7 --- /dev/null +++ b/data/qmls_old/point_multiple_symbols.qml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/point_ranges.qml b/data/qmls_old/point_ranges.qml new file mode 100644 index 00000000..f0b7bfcc --- /dev/null +++ b/data/qmls_old/point_ranges.qml @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/point_rules.qml b/data/qmls_old/point_rules.qml new file mode 100644 index 00000000..015197ee --- /dev/null +++ b/data/qmls_old/point_rules.qml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/point_simple.qml b/data/qmls_old/point_simple.qml new file mode 100644 index 00000000..cdf96047 --- /dev/null +++ b/data/qmls_old/point_simple.qml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/polygon_simple.qml b/data/qmls_old/polygon_simple.qml new file mode 100644 index 00000000..1522635f --- /dev/null +++ b/data/qmls_old/polygon_simple.qml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/polygon_simple_nostyle.qml b/data/qmls_old/polygon_simple_nostyle.qml new file mode 100644 index 00000000..1af90a75 --- /dev/null +++ b/data/qmls_old/polygon_simple_nostyle.qml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/qmls_old/text_text_buffer.qml b/data/qmls_old/text_text_buffer.qml new file mode 100644 index 00000000..f1e57608 --- /dev/null +++ b/data/qmls_old/text_text_buffer.qml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/QGISStyleParser.spec.ts b/src/QGISStyleParser.spec.ts index 475fb4e5..db76f9ef 100644 --- a/src/QGISStyleParser.spec.ts +++ b/src/QGISStyleParser.spec.ts @@ -24,90 +24,98 @@ describe('QMLStyleParser implements StyleParser', () => { styleParser = new QGISStyleParser(); }); - describe('#readStyle', () => { - it('is defined', () => { - expect(styleParser.readStyle).toBeDefined(); - }); - describe('PointSymbolizer', () => { - it('can read a simple QML PointSymbol', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/point_simple.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(point_simple); - }); - it('can read a QML PointSymbolizer with an external graphic', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/point_external_graphic.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(point_external_graphic); - }); - it('can read a QML PointSymbolizer with multiple symbols', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/point_multiple_symbols.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(point_multiple_symbols); - }); - }); - describe('TextSymbolizer', () => { - it('can read some basics of the QML Labeling for Points', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/point_label.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(point_label); - }); - }); - describe('LineSymbolizer', () => { - it('can read a simple QML LineSymbol', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/line_simple.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(line_simple); - }); - }); - describe('FillSymbolizer', () => { - it('can read a simple QML FillSymbol', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/polygon_simple.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(polygon_simple); - }); - }); - describe('FillSymbolizer with no style', () => { - it('can read a simple QML FillSymbol with no style', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/polygon_simple_nostyle.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(polygon_simple_nostyle); - }); - }); - describe('Filter Parsing', () => { - it('can read a rule based QML PointSymbolizer', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/point_rules.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(point_rules); - }); - it('can read a category based QML PointSymbolizer', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/point_categories.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(point_categories); - }); - it('can read a range based QML PointSymbolizer', async () => { - expect.assertions(2); - const qml = fs.readFileSync('./data/qmls/point_ranges.qml', 'utf8'); - const { output: geoStylerStyle } = await styleParser.readStyle(qml); - expect(geoStylerStyle).toBeDefined(); - expect(geoStylerStyle).toEqual(point_ranges); + const QML_FOLDERS = [ + ['>=3.28.0', 'qmls'], + ['<3.28.0', 'qmls_old'] + ]; + + QML_FOLDERS.forEach(qmlVersionFolder => { + const [qmlVersion, qmlFolder] = qmlVersionFolder; + describe(`#readStyle ${qmlVersion}`, () => { + it('is defined', () => { + expect(styleParser.readStyle).toBeDefined(); + }); + describe('PointSymbolizer', () => { + it('can read a simple QML PointSymbol', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/point_simple.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(point_simple); + }); + it('can read a QML PointSymbolizer with an external graphic', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/point_external_graphic.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(point_external_graphic); + }); + it('can read a QML PointSymbolizer with multiple symbols', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/point_multiple_symbols.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(point_multiple_symbols); + }); + }); + describe('TextSymbolizer', () => { + it('can read some basics of the QML Labeling for Points', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/point_label.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(point_label); + }); + }); + describe('LineSymbolizer', () => { + it('can read a simple QML LineSymbol', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/line_simple.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(line_simple); + }); + }); + describe('FillSymbolizer', () => { + it('can read a simple QML FillSymbol', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/polygon_simple.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(polygon_simple); + }); + }); + describe('FillSymbolizer with no style', () => { + it('can read a simple QML FillSymbol with no style', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/polygon_simple_nostyle.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(polygon_simple_nostyle); + }); + }); + describe('Filter Parsing', () => { + it('can read a rule based QML PointSymbolizer', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/point_rules.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(point_rules); + }); + it('can read a category based QML PointSymbolizer', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/point_categories.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(point_categories); + }); + it('can read a range based QML PointSymbolizer', async () => { + expect.assertions(2); + const qml = fs.readFileSync(`./data/${qmlFolder}/point_ranges.qml`, 'utf8'); + const { output: geoStylerStyle } = await styleParser.readStyle(qml); + expect(geoStylerStyle).toBeDefined(); + expect(geoStylerStyle).toEqual(point_ranges); + }); }); }); }); diff --git a/src/QGISStyleParser.ts b/src/QGISStyleParser.ts index 09922c06..c03d1893 100644 --- a/src/QGISStyleParser.ts +++ b/src/QGISStyleParser.ts @@ -24,6 +24,8 @@ import { Builder } from 'xml2js'; +const OUTPUT_VERSION = '3.28.0-Firenze'; + const get = (obj: any, path: any, defaultValue = undefined) => { const travel = (regexp: RegExp) => String.prototype.split @@ -962,7 +964,9 @@ export class QGISStyleParser implements StyleParser { if (rules.length > 0 || symbols.length > 0) { return { qgis: { - $: {}, + $: { + version: OUTPUT_VERSION + }, 'renderer-v2': [{ $: { type @@ -982,7 +986,9 @@ export class QGISStyleParser implements StyleParser { } else { return { qgis: { - $: {}, + $: { + version: OUTPUT_VERSION + }, 'renderer-v2': [{ $: { type: 'nullSymbol' From 354d853b38f0e95f1ae6b30324dc95415955db7c Mon Sep 17 00:00:00 2001 From: Andrew Olsen Date: Fri, 6 Dec 2024 12:54:03 +1300 Subject: [PATCH 2/3] fix: update QGISStyleParser to output 3.28 style QML and to read both 3.28 style QML, and earlier style QML Update test data in qmls/ to be 3.28 style QML --- data/qmls/line_simple.qml | 20 ++- data/qmls/point_categories.qml | 192 +++++++++++---------- data/qmls/point_external_graphic.qml | 14 +- data/qmls/point_multiple_symbols.qml | 48 +++--- data/qmls/point_ranges.qml | 238 ++++++++++++++------------- data/qmls/point_rules.qml | 72 ++++---- data/qmls/point_simple.qml | 24 +-- data/qmls/polygon_simple.qml | 20 ++- data/qmls/polygon_simple_nostyle.qml | 20 ++- src/QGISStyleParser.ts | 61 +++++-- 10 files changed, 393 insertions(+), 316 deletions(-) diff --git a/data/qmls/line_simple.qml b/data/qmls/line_simple.qml index b2b4000d..caddeead 100644 --- a/data/qmls/line_simple.qml +++ b/data/qmls/line_simple.qml @@ -7,15 +7,17 @@ - - - - - - - - - + diff --git a/data/qmls/point_categories.qml b/data/qmls/point_categories.qml index d07ad43e..e2d7a519 100644 --- a/data/qmls/point_categories.qml +++ b/data/qmls/point_categories.qml @@ -1,5 +1,5 @@ - + @@ -10,24 +10,26 @@ - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - +