diff --git a/assets/src/modules/Digitizing.js b/assets/src/modules/Digitizing.js index b6197015f9..9054a27e3c 100644 --- a/assets/src/modules/Digitizing.js +++ b/assets/src/modules/Digitizing.js @@ -4,6 +4,7 @@ import Utils from '../modules/Utils.js'; import GeoJSON from 'ol/format/GeoJSON.js'; import GPX from 'ol/format/GPX.js'; import KML from 'ol/format/KML.js'; +import WKT from 'ol/format/WKT.js'; import { Draw, Modify, Select } from 'ol/interaction.js'; import { createBox } from 'ol/interaction/Draw.js'; @@ -576,7 +577,7 @@ export default class Digitizing { } }); } - + this._drawSource.removeFeature(features[0]); // Stop erasing mode when no features left @@ -585,7 +586,7 @@ export default class Digitizing { } this.saveFeatureDrawn(); - + mainEventDispatcher.dispatch('digitizing.erase'); } }; @@ -979,7 +980,7 @@ export default class Digitizing { const savedFeatures = []; for(const feature of this.featureDrawn){ const geomType = feature.getGeometry().getType(); - + if( geomType === 'Circle'){ savedFeatures.push({ type: geomType, @@ -1008,29 +1009,65 @@ export default class Digitizing { loadFeatureDrawnToMap() { // get saved data without context for draw const oldSavedGeomJSON = this._context === 'draw' ? localStorage.getItem(this._repoAndProjectString + '_drawLayer') : null; + + // Clear old saved data without context for draw from localStorage + if (oldSavedGeomJSON !== null) { + localStorage.removeItem(this._repoAndProjectString + '_drawLayer'); + localStorage.setItem(this._repoAndProjectString + '_' + this._context + '_drawLayer', oldSavedGeomJSON); + } + + // keep saved data without context for draw or get saved data with context const savedGeomJSON = oldSavedGeomJSON !== null ? oldSavedGeomJSON : localStorage.getItem(this._repoAndProjectString + '_' + this._context + '_drawLayer'); if (savedGeomJSON) { - const savedFeatures = JSON.parse(savedGeomJSON); - const loadedFeatures = []; - for(const feature of savedFeatures){ - let loadedGeom; - if(feature.type === 'Point'){ - loadedGeom = new Point(feature.coords); - } else if(feature.type === 'LineString'){ - loadedGeom = new LineString(feature.coords); - } else if(feature.type === 'Polygon'){ - loadedGeom = new Polygon(feature.coords); - } else if(feature.type === 'Circle'){ - loadedGeom = new CircleGeom(feature.center, feature.radius); - } + let loadedFeatures = []; + // the saved data could be an invalid JSON + try { + const savedFeatures = JSON.parse(savedGeomJSON); + + // convert saved data to features + for(const feature of savedFeatures){ + let loadedGeom; + if(feature.type === 'Point'){ + loadedGeom = new Point(feature.coords); + } else if(feature.type === 'LineString'){ + loadedGeom = new LineString(feature.coords); + } else if(feature.type === 'Polygon'){ + loadedGeom = new Polygon(feature.coords); + } else if(feature.type === 'Circle'){ + loadedGeom = new CircleGeom(feature.center, feature.radius); + } - if(loadedGeom){ - const loadedFeature = new Feature(loadedGeom); - loadedFeature.set('color', feature.color); - loadedFeatures.push(loadedFeature); + if(loadedGeom){ + const loadedFeature = new Feature(loadedGeom); + loadedFeature.set('color', feature.color); + loadedFeatures.push(loadedFeature); + } + } + } catch(json_error) { + // the saved data is an invalid JSON + console.log('`'+savedGeomJSON+'` is not a JSON!'); + // the saved data could be a WKT from previous lizmap version + try { + const formatWKT = new WKT(); + loadedFeatures = formatWKT.readFeatures(savedGeomJSON); + console.log(loadedFeatures.length+' features read from WKT!'); + // set color + for(const loadedFeature of loadedFeatures){ + loadedFeature.set('color', this._drawColor); + } + // No features read from localStorage so remove the data + if (loadedFeatures.length == 0) { + localStorage.removeItem(this._repoAndProjectString + '_' + this._context + '_drawLayer'); + } + } catch(wkt_error) { + console.log('`'+savedGeomJSON+'` is not a WKT!'); + console.error(json_error); + console.error(wkt_error); } } + + // Draw features this._drawSource.addFeatures(loadedFeatures); } } diff --git a/tests/end2end/playwright/draw.spec.ts b/tests/end2end/playwright/draw.spec.ts index b3b8183db6..81a1abb7b7 100644 --- a/tests/end2end/playwright/draw.spec.ts +++ b/tests/end2end/playwright/draw.spec.ts @@ -164,10 +164,12 @@ test.describe('Draw', () => { bubbles: true, cancelable: true, }); - + input.dispatchEvent(event); }); + expect(await page.evaluate(() => lizMap.mainLizmap.digitizing.featureDrawn)).toHaveLength(2); + // Delete polygon await page.locator('.digitizing-erase').click(); @@ -182,10 +184,106 @@ test.describe('Draw', () => { await page.waitForTimeout(300); + expect(await page.evaluate(() => lizMap.mainLizmap.digitizing.featureDrawn)).toHaveLength(1); + + await page.locator('.digitizing-save').click(); + + // Get the JSON has been stored + const json_stored = await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer')); + + // Clear local storage + await page.evaluate(() => localStorage.removeItem('testsrepository_draw_draw_drawLayer')); + expect(await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer'))).toBeNull; + + // Check the JSON + await expect(json_stored).toEqual('[{"type":"Polygon","color":"#000000","coords":[[[764321.0416656,6290805.935670358],[767628.3399468632,6290805.935670358],[767628.3399468632,6295105.423436],[764321.0416656,6295105.423436],[764321.0416656,6290805.935670358],[764321.0416656,6290805.935670358]]]}]'); + + // Hide all elements but #map, #newOlMap and their children + await page.$eval("*", el => el.style.visibility = 'hidden'); + await page.$eval("#newOlMap, #newOlMap *", el => el.style.visibility = 'visible'); + + expect(await page.locator('#newOlMap').screenshot()).toMatchSnapshot('draw-edition.png'); + }); + + test('From local storage', async ({ page }) => { + const the_json = '[{"type":"Polygon","color":"#000000","coords":[[[764321.0416656,6290805.935670358],[767628.3399468632,6290805.935670358],[767628.3399468632,6295105.423436],[764321.0416656,6295105.423436],[764321.0416656,6290805.935670358],[764321.0416656,6290805.935670358]]]}]'; + await page.evaluate(token => localStorage.setItem('testsrepository_draw_draw_drawLayer', token), the_json); + const json_stored = await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer')); + await expect(json_stored).toEqual(the_json); + + // Reload + await page.reload({ waitUntil: 'networkidle' }); + // Display + await page.locator('#button-draw').click(); + + // Clear local storage + await page.evaluate(() => localStorage.removeItem('testsrepository_draw_draw_drawLayer')); + expect(await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer'))).toBeNull; + + // Check the geometry has been drawn + expect(await page.evaluate(() => lizMap.mainLizmap.digitizing.featureDrawn)).toHaveLength(1); + // Hide all elements but #map, #newOlMap and their children await page.$eval("*", el => el.style.visibility = 'hidden'); await page.$eval("#newOlMap, #newOlMap *", el => el.style.visibility = 'visible'); + // Check rendering expect(await page.locator('#newOlMap').screenshot()).toMatchSnapshot('draw-edition.png'); }); + + test('WKT found in local storage', async ({ page }) => { + // Save WKT to the old local storage + const wkt = 'POINT(770737.2003016905 6279832.319974077)'; + await page.evaluate(token => localStorage.setItem('testsrepository_draw_drawLayer', token), wkt); + const old_stored = await page.evaluate(() => localStorage.getItem('testsrepository_draw_drawLayer')); + await expect(old_stored).toEqual(wkt); + + // Reload + await page.reload({ waitUntil: 'networkidle' }); + + // No error + await expect(page.locator('p.error-msg')).toHaveCount(0); + await expect(page.locator('#switcher lizmap-treeview ul li')).not.toHaveCount(0); + + // The WKT has been drawn + const drawn = await page.evaluate(() => lizMap.mainLizmap.digitizing.featureDrawn[0].getGeometry().getCoordinates()) + await expect(drawn).toEqual([770737.2003016905, 6279832.319974077]); + + // The WKT has been moved to the new storage + const new_stored = await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer')); + await expect(new_stored).toEqual(wkt); + expect(await page.evaluate(() => localStorage.getItem('testsrepository_draw_drawLayer'))).toBeNull; + + // Save to local storage + await page.locator('#button-draw').click(); + await page.locator('.digitizing-save').click(); + + // Ths JSON has been stored + const json_stored = await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer')); + await expect(json_stored).toEqual('[{"type":"Point","color":"#ff0000","coords":[770737.2003016905,6279832.319974077]}]'); + + // Clear local storage + await page.evaluate(() => localStorage.removeItem('testsrepository_draw_draw_drawLayer')); + expect(await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer'))).toBeNull; + }); + + test('Not well formed data in local storage', async ({ page }) => { + // Save not well formed data in local storage + const bad_wkt = 'foobar POINT(770737.2003016905 6279832.319974077)'; + await page.evaluate(token => localStorage.setItem('testsrepository_draw_drawLayer', token), bad_wkt); + const old_stored = await page.evaluate(() => localStorage.getItem('testsrepository_draw_drawLayer')); + await expect(old_stored).toEqual(bad_wkt); + + // Reload + await page.reload({ waitUntil: 'networkidle' }); + + // No error + await expect(page.locator('p.error-msg')).toHaveCount(0); + await expect(page.locator('#switcher lizmap-treeview ul li')).not.toHaveCount(0); + + // Not well formed data has been removed + expect(await page.evaluate(() => localStorage.getItem('testsrepository_draw_drawLayer'))).toBeNull; + expect(await page.evaluate(() => localStorage.getItem('testsrepository_draw_draw_drawLayer'))).toBeNull; + expect(await page.evaluate(() => lizMap.mainLizmap.digitizing.featureDrawn)).toBeNull; + }); });