Skip to content

Commit

Permalink
Merge pull request #4203 from rldhont/fix-permalink-extent-precision
Browse files Browse the repository at this point in the history
JS: Enhancing the way Map is managed
  • Loading branch information
rldhont authored Feb 14, 2024
2 parents b83e16f + 2b43af0 commit 26286b6
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 28 deletions.
11 changes: 10 additions & 1 deletion assets/src/modules/Lizmap.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,16 @@ export default class Lizmap {
}

/**
* @param {Array} bounds - Left, bottom, right, top
* The view extent - an array with left, bottom, right, top
* @type {Array<number>}
*/
get extent() {
return this.map.getView().calculateExtent();
}

/**
* Setting the view extent
* @param {Array<number>} bounds - Left, bottom, right, top
*/
set extent(bounds) {
this.map.getView().fit(bounds, {nearest: true});
Expand Down
22 changes: 16 additions & 6 deletions assets/src/modules/Permalink.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class Permalink {
this._ignoreHashChange = false;
// Store the build or received hash
this._hash = '';
this._extent4326 = [0, 0, 0, 0, 0];

// Change `checked`, `style` states based on URL fragment
if (window.location.hash) {
Expand Down Expand Up @@ -147,21 +148,29 @@ export default class Permalink {
}

_runPermalink(setExtent = true) {
this._hash = ''+window.location.hash;
const items = mainLizmap.state.layersAndGroupsCollection.layers.concat(mainLizmap.state.layersAndGroupsCollection.groups);

if (this._hash === ''+window.location.hash) {
return;
}
if (window.location.hash === "") {
this._hash = '';
return;
}

this._hash = ''+window.location.hash;

const items = mainLizmap.state.layersAndGroupsCollection.layers.concat(mainLizmap.state.layersAndGroupsCollection.groups);

const [extent4326, itemsInURL, stylesInURL, opacitiesInURL] = window.location.hash.substring(1).split('|').map(part => part.split(','));

if (setExtent && extent4326.length === 4) {
if (setExtent
&& extent4326.length === 4
&& this._extent4326.filter((v, i) => {return parseFloat(extent4326[i]).toPrecision(6) != v}).length != 0) {
const mapExtent = transformExtent(
extent4326.map(coord => parseFloat(coord)),
'EPSG:4326',
lizMap.map.projection.projCode
);
this._extent4326 = extent4326.map(coord => parseFloat(coord).toPrecision(6));
mainLizmap.extent = mapExtent;
}

Expand Down Expand Up @@ -225,15 +234,16 @@ export default class Permalink {
let hash = '';

// BBOX
let bbox = lizMap.map.getExtent().toArray();
let bbox = mainLizmap.extent;
if (lizMap.map.projection.projCode !== 'EPSG:4326') {
bbox = transformExtent(
bbox,
lizMap.map.projection.projCode,
'EPSG:4326'
);
}
hash = bbox.join();
this._extent4326 = bbox.map(x => x.toFixed(6));
hash = this._extent4326.join();

// Item's visibility, style and opacity
// Only write layer's properties when visible
Expand Down
37 changes: 35 additions & 2 deletions assets/src/modules/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,10 @@ export default class map extends olMap {
);
});

this.on('moveend', this.refreshOL2View);
this.on('moveend', () => {
this.refreshOL2View();
this._dispatchMapStateChanged();
});

// Init view
this.syncNewOLwithOL2View();
Expand Down Expand Up @@ -584,7 +587,14 @@ export default class map extends olMap {
}

mainLizmap.state.rootMapGroup.addListener(
evt => this.getLayerOrGroupByName(evt.name).setVisible(evt.visibility),
evt => {
const olLayerOrGroup = this.getLayerOrGroupByName(evt.name);
if (olLayerOrGroup) {
olLayerOrGroup.setVisible(evt.visibility);
} else {
console.log('`'+evt.name+'` is not an OpenLayers layer or group!');
}
},
['layer.visibility.changed', 'group.visibility.changed']
);

Expand Down Expand Up @@ -648,6 +658,29 @@ export default class map extends olMap {
if (startupFeatures) {
this.setHighlightFeatures(startupFeatures, "geojson");
}

mainLizmap.state.map.addListener(
evt => {
const view = this.getView();
const updateCenter = ('center' in evt && view.getCenter().filter((v, i) => {return evt['center'][i] != v}).length != 0);
const updateResolution = ('resolution' in evt && evt['resolution'] !== view.getResolution());
const updateExtent = ('extent' in evt && view.calculateExtent().filter((v, i) => {return evt['extent'][i] != v}).length != 0);
if (updateCenter && updateResolution) {
view.animate({
center: evt['center'],
resolution: evt['resolution'],
duration: 50
});
} else if (updateCenter) {
view.setCenter(evt['center']);
} else if (updateResolution) {
view.setResolution(evt['resolution']);
} else if (updateExtent) {
view.fit(evt['extent'], {nearest: true});
}
},
['map.state.changed']
);
}

get hasEmptyBaseLayer() {
Expand Down
43 changes: 42 additions & 1 deletion assets/src/modules/state/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,29 @@ const mapStateProperties = {
pointScaleDenominator: {type: 'number'},
};

/**
* Map state ready.
* @event MapState#map.state.ready
* @type {object}
* @property {string} type - map.state.ready
* @property {boolean} ready - true
*/

/**
* Map state changed
* @event MapState#map.state.changed
* @type {object}
* @property {string} type - map.state.changed
* @property {string} [projection] - the map projection code if it changed
* @property {number[]} [center] - the map center if it changed
* @property {number[]} [size] - the map size if it changed
* @property {number[]} [extent] - the map extent (calculate by the map view) if it changed
* @property {number} [resolution] - the map resolution if it changed
* @property {number} [scaleDenominator] - the map scale denominator if it changed
* @property {number} [pointResolution] - the map resolution (calculate from the center) if it changed
* @property {number} [pointScaleDenominator] - the map scale denominator (calculate from the center) if it changed
*/

/**
* Class representing the map state
* @class
Expand All @@ -35,6 +58,7 @@ export class MapState extends EventDispatcher {
*/
constructor(startupFeatures) {
super();
this._ready = false;
// default values
this._projection = 'EPSG:3857';
this._center = [0, 0];
Expand All @@ -47,7 +71,6 @@ export class MapState extends EventDispatcher {
this._startupFeatures = startupFeatures;
}


/**
* Update the map state
* @param {object} evt - the map state changed object
Expand All @@ -59,6 +82,8 @@ export class MapState extends EventDispatcher {
* @param {number} [evt.scaleDenominator] - the map scale denominator
* @param {number} [evt.pointResolution] - the map resolution (calculate from the center)
* @param {number} [evt.pointScaleDenominator] - the map scale denominator (calculate from the center)
* @fires MapState#map.state.ready
* @fires MapState#map.state.changed
*/
update(evt) {
let updatedProperties = {};
Expand Down Expand Up @@ -114,6 +139,14 @@ export class MapState extends EventDispatcher {
}
// Dispatch event only if something have changed
if (Object.getOwnPropertyNames(updatedProperties).length != 0) {
const neededProperties = ['center', 'size', 'extent', 'resolution'];
if (!this._ready && Object.getOwnPropertyNames(updatedProperties).filter(v => neededProperties.includes(v)).length == 4) {
this._ready = true;
this.dispatch({
type: 'map.state.ready',
ready: true,
});
}
this.dispatch(
Object.assign({
type: 'map.state.changed'
Expand All @@ -122,6 +155,14 @@ export class MapState extends EventDispatcher {
}
}

/**
* Map is ready
* @type {boolean}
*/
get isReady() {
return this._ready;
}

/**
* Map projection code
* @type {string}
Expand Down
8 changes: 8 additions & 0 deletions assets/src/utils/EventDispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,14 @@ export default class EventDispatcher {
// interpretation of what an "id" is
writable: false
});
// Add the immutable target property
Object.defineProperty(event, "target", {
value: this,
enumerable: false,
// This could go either way, depending on your
// interpretation of what an "id" is
writable: false
});
// Add it to the stack
this._stackEventId.unshift(event['__eventid__']);
// Limit the stack to 10 events
Expand Down
38 changes: 24 additions & 14 deletions tests/end2end/playwright/permalink.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ test.describe('Permalink', () => {
// Close subdock
await page.getByRole('button', { name: 'Close' }).click();

// The check hash url before changing it
const old_url = new URL(page.url());
await expect(old_url.hash).not.toHaveLength(0);
// The decoded hash is
// #3.0635044037670305,43.401957103265374,4.567657653648659,43.92018105321636
// |layer_legend_single_symbol,tramway_lines,Group%20as%20layer
// |d%C3%A9faut,categorized,
// |1,1,1,1
await expect(old_url.hash).toContain('|layer_legend_single_symbol,tramway_lines,Group%20as%20layer|')
await expect(old_url.hash).toContain('|d%C3%A9faut,categorized,|')
await expect(old_url.hash).toContain('|0.6,1,1')

// Test permalink with empty string styles
const bbox = '3.0635044037670305,43.401957103265374,4.567657653648659,43.92018105321636'
const layers = 'layer_legend_single_symbol,layer_legend_categorized,tramway_lines,Group as layer'
Expand All @@ -317,20 +329,6 @@ test.describe('Permalink', () => {
// Reload to force applying hash with empty string styles
await page.reload({ waitUntil: 'networkidle' });

// waiting for hash updated
await page.waitForTimeout(1000);
// The url has changed
const checked_url = new URL(page.url());
await expect(checked_url.hash).not.toHaveLength(0);
// The decoded hash is
// #3.0635044037670305,43.401957103265374,4.567657653648659,43.92018105321636
// |layer_legend_single_symbol,layer_legend_categorized,tramway_lines,Group%20as%20layer
// |d%C3%A9faut,d%C3%A9faut,a_single,
// |1,1,1,1
await expect(checked_url.hash).toContain('|layer_legend_single_symbol,layer_legend_categorized,tramway_lines,Group%20as%20layer|')
await expect(checked_url.hash).toContain('|d%C3%A9faut,d%C3%A9faut,a_single,|')
await expect(checked_url.hash).toContain('|1,1,1,1')

// No error
await expect(page.locator('p.error-msg')).toHaveCount(0);
await expect(page.locator('#switcher lizmap-treeview ul li')).not.toHaveCount(0);
Expand All @@ -355,6 +353,18 @@ test.describe('Permalink', () => {

// Close subdock
await page.getByRole('button', { name: 'Close' }).click();

// The url has changed
const checked_url = new URL(page.url());
await expect(checked_url.hash).not.toHaveLength(0);
// The decoded hash is
// #3.0635044037670305,43.401957103265374,4.567657653648659,43.92018105321636
// |layer_legend_single_symbol,layer_legend_categorized,tramway_lines,Group%20as%20layer
// |d%C3%A9faut,d%C3%A9faut,a_single,
// |1,1,1,1
await expect(checked_url.hash).toContain('|layer_legend_single_symbol,layer_legend_categorized,tramway_lines,Group%20as%20layer|')
await expect(checked_url.hash).toContain('|d%C3%A9faut,d%C3%A9faut,a_single,|')
await expect(checked_url.hash).toContain('|1,1,1,1')
});

test('Build permalink and change hash', async ({ page }) => {
Expand Down
8 changes: 4 additions & 4 deletions tests/end2end/playwright/theme.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ test.describe('Theme', () => {
const url = new URL(page.url());
await expect(url.hash).not.toHaveLength(1);
// The decoded hash is
// #3.7308717840938743,43.54038574169922,4.017985172062126,43.679557362551954
// #3.730872,43.540386,4.017985,43.679557
// |group1,Les%20quartiers
// |,style1
// |1,1
await expect(url.hash).toMatch(/#3.730871\d+,43.540385\d+,4.0179851\d+,43.679557\d+\|/)
await expect(url.hash).toMatch(/#3.7308\d+,43.5403\d+,4.0179\d+,43.6795\d+\|/)
await expect(url.hash).toContain('|group1,Les%20quartiers|,style1|1,1')
});

Expand All @@ -54,11 +54,11 @@ test.describe('Theme', () => {
const url = new URL(page.url());
await expect(url.hash).not.toHaveLength(0);
// The decoded hash is
// #3.7308717840938743,43.54038574169922,4.017985172062126,43.679557362551954
// #3.730872,43.540386,4.017985,43.679557
// |Les%20quartiers|style2|1
// |style2
// |1
await expect(url.hash).toMatch(/#3.730871\d+,43.540385\d+,4.0179851\d+,43.679557\d+\|/)
await expect(url.hash).toMatch(/#3.7308\d+,43.5403\d+,4.0179\d+,43.6795\d+\|/)
await expect(url.hash).toContain('|Les%20quartiers|style2|1')
});
});

0 comments on commit 26286b6

Please sign in to comment.