Skip to content

Commit

Permalink
Merge pull request #4077 from rldhont/fix-digitzing-invalid-saved-data
Browse files Browse the repository at this point in the history
[Bugfix] JS: catch invalid data saved for Digitizing
  • Loading branch information
rldhont authored Feb 1, 2024
2 parents 47ea812 + c28f2b9 commit 6a668a7
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 21 deletions.
77 changes: 57 additions & 20 deletions assets/src/modules/Digitizing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -576,7 +577,7 @@ export default class Digitizing {
}
});
}

this._drawSource.removeFeature(features[0]);

// Stop erasing mode when no features left
Expand All @@ -585,7 +586,7 @@ export default class Digitizing {
}

this.saveFeatureDrawn();

mainEventDispatcher.dispatch('digitizing.erase');
}
};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
}
}
Expand Down
100 changes: 99 additions & 1 deletion tests/end2end/playwright/draw.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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;
});
});

0 comments on commit 6a668a7

Please sign in to comment.