Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance issues when clearing the chart several times (ref: hotline and almostOver layers) #233

Closed
mttucl opened this issue Feb 22, 2023 · 11 comments

Comments

@mttucl
Copy link

mttucl commented Feb 22, 2023

Hi @Raruto,

I have a list of tracks and the option to add more tracks from geojsons or strings. What would be the proper way to clear a showing track and load a new one?

I made a JSFiddle example of my attempt but the issue is the more I switch between tracks, the slower the map becomes.

In your example, you add the layer and then addData instead of load the url or a string like in my case. I'm not sure if this is the issue but for my use, it is more convenient to load the json which will take care of the chart, and the layer e.g. hotline, and start/end markers.

I appreciate your help with this.

@Raruto
Copy link
Owner

Raruto commented Feb 22, 2023

Hi @mttucl

the issue is the more I switch between tracks, the slower the map becomes.

will take care of the chart, and the layer e.g. hotline, and start/end markers.

As you say, the problem could be related precisely to the fact that not all other support layers / markers are correctly removed from the map.

Keep in mind that for a long time my average use case of one layer per map (and per page!) was more than enough, that's why your case it's not much tested..

Waiting for improvement pull requests
👋 Raruto

@mttucl
Copy link
Author

mttucl commented Feb 24, 2023

The issue seems to be with almostOver. No issue if you turn it off in the above JSFiddle example. It might have to do with almostOver plugin but mainly with _initAlmostOverHandler by registering below events every time it is called with load.

map
  .on('almost:move', (e) => this._onMouseMoveLayer(e))
  .on('almost:out',  (e) => this._onMouseOut(e));

Instead, it should handle any update with map.almostOver.removeLayer(layer) and map.almostOver.addLayer(layer).

I also see a performance issue with hotline but not a severe as almostOver. Turning both off gives the best performance which is a shame because I like them.

Sorry, I'm not very good at this and I don't have a proper fix.

My hack is with L.Control.Elevation.include by adding reload and _updateMapIntegrations that is similar to load and _initMapIntegrations but with calling below instead of _initAlmostOverHandler

this.on('eledata_clear', () => {
  map.almostOver.removeLayer(layer);
});
_updateAlmostOverHandler: function(map, layer) {
  if (L.GeometryUtil && map.almostOver && map.almostOver.enabled()) {
    map.almostOver.addLayer(layer);                                  
  }
}

I still see a performance issue with the above but it is much better than before.

@Raruto
Copy link
Owner

Raruto commented Feb 24, 2023

Hi @mttucl this is exactly one of those cases where it's worth writing some automated test (as you see from the /spec folder there is nothing meaningful about it..)

Since it's something intrinsic to this library I suggest you directly edit the code within the /src folder (during your tests), so then it becomes easy for you to propose a pull request.

As for the performance issue you notice some options (eg. the almostOver layer) this happens because these integrations need to add dummy layer (ie. opacity = 0) to the map in order to work properly, that's why at the moment they may not all be removed.

Here too, to facilitate the writing of the tests, it should be enough for you to check the value returned by the following property from time to time:

Object.keys(map._layers).length

Just to be clear, I know that without a starting point it can seem complicated or challenging:

  • I will not write these tests for you..
  • feel free to ask if you need additional information about the current code

Have a good debugging sessions
👋 Raruto

@mttucl
Copy link
Author

mttucl commented Feb 24, 2023

Hi @Raruto,

I will be happy to contribute to this project. Fair warning, I'm a novice in using GitHub and have limited experience in Javascript. Is the test supposed to be in /spec folder to run Object.keys(map._layers).length when editing the code within the /src folder? What is it supposed to do? Just to show if the dummy layers are cleared every time new data is loaded? How is it automated? I looked up automated tests and I see mentions of GitHub Actions which I have no idea about but seems intersting.

In any case, I will keep debugging as I'm enjoying it and I would love to improve it.

@Raruto
Copy link
Owner

Raruto commented Feb 24, 2023

Hi @mttucl,

I will be happy to contribute to this project. Fair warning, I'm a novice in using GitHub and have limited experience in Javascript.

No problem, I've always kept these repositories active just to learn new things. But keep in mind my real use case is really much more mundane than anything I've published over the years, so I'm happy if someone else tries to lighten some of my work.

Is the test supposed to be in /spec folder to run Object.keys(map._layers).length when editing the code within the /src folder? What is it supposed to do? Just to show if the dummy layers are cleared every time new data is loaded? How is it automated? I looked up automated tests and I see mentions of GitHub Actions which I have no idea about but seems interesting.

Frontend testing is a tricky business:

  • has high maintenance costs
  • in order to design a fronted test you to learn / experiment various related projects
  • frontend tests tends to be much slower to run
  • sometimes if a test fails it's not so easy to figure out what to do
  • ...

To give you an idea of the available ones, I report below some images from the article: Restructuring Frontend Testing Pyramid: alternative to Unit/Integration/E2E approach

NB

  • at bottom line = you can write many tests because they are cheap
  • at top = you can write few tests because the cost is high

Frontend testing pyramid (in short)

image

Frontend testing pyramid (in a real world)

image

Anyway, my advice is to take inspiration from what others (much more expert than me) have done.
The leaflet repository is always a good source because it's more probable that you will find more information on the net.

As you can see within this repository this subject it has never been digged that much, except for:

  • some "obvious" manual tests (ref: /examples folder)
  • some "simple" unit tests (ref: /spec folder)

I would just like to point out, even if it doesn't solve the problem of this issue, that in the next period it was my intention to introduce some more specific jsdoc comments (ref: Types without Typescript)

In a local branch I was working on better documenting the available types for the options and the various event listeners, but even here the issue is to try to find the right compromise on code readability...

The limit is your imagination
👋 Raruto

@Raruto
Copy link
Owner

Raruto commented Feb 24, 2023

PS just to understand the concept of "manual tests":

  • every demo file uploaded in the /examples folder could be considered a "manual test" because you have to open all of them in your browser to personally check if something is wrong (if I'm not mistaken the leaflet repo has a /debug folder with similar tests)
  • instead with an "automatic test" (e.g. if you run npm run test from the terminal) you will be able to run several tests at the same time and you will always be sure if something has failed (there is no discretion on the result, because in this case uvu tells you if something has gone wrong crooked)

@Raruto Raruto changed the title How to load new data dynamically Performance issues when clearing the chart several times (ref: hotline and almostOver layers) Feb 24, 2023
@Raruto
Copy link
Owner

Raruto commented Feb 24, 2023

Since it's something intrinsic to this library I suggest you directly edit the code within the /src folder (during your tests), so then it becomes easy for you to propose a pull request.

Is the test supposed to be in /spec folder to run Object.keys(map._layers).length when editing the code within the /src folder?

I meant that for your local debugging I don't recommend going the way of overriding functions which I showed you in here: #231 (comment)

In this case, If you don't want to go crazy, it's much easier to locally edit the code of this library (which you can found in the /src folder)

@mttucl
Copy link
Author

mttucl commented Feb 25, 2023

Hi @Raruto,

Thanks for the explanation and pointers.

I'm looking through the code and noticed you have a featureGroup for hotline that you use to clear its layers but not for almostOver. Would it make sense to do the same for almostOver. For updating layers, we can still use _initAlmostOverHandler but with a logic to skip below if already initialized.

map
   .on('almost:move', (e) => this._onMouseMoveLayer(e))
   .on('almost:out',  (e) => this._onMouseOut(e));

For hotline, can we add trkseg in the same hotline featureGroup or a new group that we can clear. This is the only dummy layer I see with opacity = 0 option. Not sure if there are any others.

@Raruto
Copy link
Owner

Raruto commented Feb 27, 2023

@mttucl giving us a quick look, I think you just need to do the reverse of what is written here (ie. I don't think there is a need to create a "grouping" layer).

map.almostOver.addLayer(layer);

Ref: makinacorpus/Leaflet.AlmostOver/src/leaflet.almostover.js#L81-L97

BTW, at the moment what I think doesn't really matter, the important thing (however you intend to edit the code) is that the number of levels currently loaded in the map will always prove us what is right or wrong:

// expected: Object.keys(map._layers).length == 7

controlElevation.load(...);

// expected: Object.keys(map._layers).length == 19

controlElevation.clear()

// excpected: Object.keys(map._layers).length) == 7 

/* test accepted ? */

Just to make it clear, if you start taking your jsfiddle and edit it one step at a time in order to load it as a new file in the /examples folder you are already on the right track to understand how to write a test for this case (then knowing how to code / transform it in an automated test is just a next step).

The closest thing I've done in the past is: examples/leaflet-elevation_multiple-maps.html (but as you can see it has never derived in an automated test ..)

👋 Raruto

Raruto added a commit that referenced this issue Apr 19, 2023
@Raruto
Copy link
Owner

Raruto commented Apr 19, 2023

Hi @mttucl,

here 88cfa11 you can see a starting point on how start coding some "end to end" tests.

You can try the code of that pull as follows:

npm i
npm run test

For the first times, it could be better if you use playwright through the graphical interface:

npm i
npx playwright test --ui

For more info:

While hoping someone else joins forces..

👋 Raruto

Raruto added a commit that referenced this issue Apr 23, 2023
Raruto added a commit that referenced this issue Apr 23, 2023
* add `edgeScale` option

* prevent multiple control instantiations

* add `leaflet-elevation_edge-scale.html`

* update examples

* update versions

* workaround when "rotate: true"

* update default option

* Update leaflet-elevation_edge-scale.html

* update dev dependencies

* setup some `e2e` tests

Start to handle: #233 (comment)

* Update leaflet-elevation.spec.ts

* fix warning

* add test case for: #241

* comments

* sample tests integration: `uvu` + `playwright`

* remove extra parameters

* remove `e2e` folder

* delete `spec` folder

* add test case for: #241

* make use of abortcontroller to ensure killing of http server

* add default `timeout` to uvu tests

* Create leaflet-elevation_edge-scale.spec.js

* update tests

* update how develop instructions

* Update leaflet-elevation_multiple-maps.spec.js

* Update leaflet-elevation_multiple-maps.html

* add clear button example

Related to: #233

* add test file

* Update leaflet-elevation_clear-button.spec.js
@Raruto
Copy link
Owner

Raruto commented Apr 23, 2023

And here it is a new dedicated example from v2.3.0: examples/leaflet-elevation_clear-button.html

Now, someone else improve the related test file..

/**
* examples/leaflet-elevation_clear-button.html
*/
import * as assert from 'uvu/assert';
import { suite } from '../test/setup/http_server.js';
const test = suite('examples/leaflet-elevation_clear-button.html');
test('eledata_clear', async ({ page }) => {
let gpx;
// Load "demo.geojson"
gpx = await page.evaluate(() => new Promise(resolve => {
controlElevation.once('eledata_loaded', (gpx) => resolve(gpx));
load_track(0);
}));
assert.is(gpx.name, 'demo.geojson');
assert.not.type(gpx.layer, 'undefined');
assert.type(gpx.track_info.distance, 'number');
// Load "waypoints.geojson"
gpx = await page.evaluate(() => new Promise(resolve => {
controlElevation.once('eledata_loaded', (gpx) => resolve(gpx));
load_track(1);
}));
assert.is(gpx.name, 'waypoints.geojson');
assert.not.type(gpx.layer, 'undefined');
assert.type(gpx.track_info.distance, 'number');
// Clear all
const { ctrl, layers} = await page.evaluate(() => new Promise(resolve => {
controlElevation.once('eledata_clear', () => resolve({
ctrl: controlElevation,
layers: controlElevation._layers.getLayers()
}));
load_track(-1);
}));
assert.is(layers.length, 0);
assert.type(ctrl.track_info.name, 'undefined');
assert.type(ctrl.track_info.distance, 'undefined');
});
test.run();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants