ng Leaflet Directive issue with ngRepeat + map.invalidateSize() - javascript

I have a page that generates a series of maps based off of an async call. When the maps first load, they are missing tiles, as seen in the below image.
I can resolve this by calling map.invalidateSize() to redraw the tiles in each. Currently I create 8 on load and the rest are paginated via an Angular directive. Toggling to any other 8 or switching back and forth will "fix" the first 8.
Since these first 8 are the product of an ngRepeat, they all have the same ID. I could, in theory, add the index or some other string modification to the ids of each of the 8 maps and create a loop of the below code to run map.invalidateSize() on each, but this seems like a roundabout and overall poor solution to the issue. Does anyone have a better suggestion?
leafletData.getMap('leafletMap-trips').then(function(map) {
setTimeout(function () { map.invalidateSize() });
});

As a matter of fact, nope. The leaflet API only allow size invalidation per-map, so you will pretty much need to do as you explained.

Related

How to call a JS function or Python method when a glyph is completely rendered?

I am using bokeh as a Server Application. When I make a selection in a plot I do some actions in python and I update some sources (CDS). This changes are reflected in the plot. Is there a way to check when the glyphs are completely rendered (after the update)? I want to call a JavaScript function when all is completely loaded? With that function I want to call other python method to update the CDS again.
If I do not wait for this profiles to be rendered probably the application breaks, and that´s what I want to avoid. Actually I did some tests in the past and I had to create a huge CDS instead of several smaller CDS to make it work properly.
My Use Case. Why do I want to make this?
I have many tabs in my layout, they can be 10 for example. And each tab has some plots (3-6 plots). If I update the entire ColumnDataSource at the same time, it will take a while. Then I want to make it more fluent, so I would like to update only the data of the current visible tab, it will render faster and the user would receive an immediate response. I can disable the rest of the tabs temporarily to prevent malfunctions. At this moment I would need to call the JS or python method in order to update the content of the rest of the tabs.
Here a drawing of what I want to achieve in order to speed up the process:
About the data
Basically I have two DataFrames, one to build the cloud of points (around 5000 row and 130 columns) and I extract from the selected points another DataFrame to know which lines I should draw (360 columns and 5 to 15 rows), making some filters and selections. The algorythm I have used is in the answer of a question I have written time ago. With this amount of data the algorythm takes 6 or 7 seconds to finish.
Any other idea of how to improve the performance or how to split or the computing?
To improve the rendering speed you could try the webgl JavaScript API. This Bokeh documentation page Speeding up with WebGL explains how to do it. webgl supports circles, lines and most of the markers. Application:
p = Plot(output_backend="webgl") # for the glyph API
p = figure(output_backend="webgl") # for the plotting API
Please be aware that users report issues with webgl like plot stuttering, etc... but it may work in your case depend on which type of glyphs your plot contains.
Also make sure your data passed to the plot doesn't include NaN's as it is known to slow down Bokeh performance.
To my knowledge there is no attribute that indicates that rendering is completed or is still ongoing but you may think about some other alternatives to speed things up like combination of Bokeh with Datashader (pre-rendering large datasets into a fixed-size raster image) or Dask (speed up data reading from multiple sources like multiple csv files)
For example you could have one standard Bokeh plot where you make a
selection and let the other plots being generated as Datashader images
and embed them in Bokeh plots.
This example shows how to combine Bokeh + Datashader which significantly improves performance especially when over-plotting takes place. Please note that each time a single point is added to the plot entire canvas area will be re-drawn in the browser. This is how browsers work. Datashader can provide a single image so updating the plot is much quicker while you can still use Toolbar tools like zoom, pan etc....
Also the Python code implementation details counts. Using e.g. gridplot to link many plots can slow down performance so it is better to add them one by one to the document root, etc...
Time ago I made a trick to check if my design would work if I could trigger some function if the plots were rendered:
First I updated the current tab. This worked very well and fast.
Then I set a timeout to update the data of the rest of the tabs. But, in the meantime this second algorythm was being executed I could not work with the plots of the current tab because they were frozen.
So, the approach of triggering a function when everything is rendered is not a good idea, because even with such a callback the app would not work as I was expecting.

OpenLayers KML Incremental Updates

I am trying to determine if it is feasible for OpenLayers v5 to handle incremental updates to KML files. The KML files I am working with are large but are dynamic and receive updates every so often. Instead of refreshing the entire KML file it would be preferable from a performance perspective to just load the updates. We were thinking of passing the updates using the field. Any help would be much appreciated!
If you included tags in angle brackets, they were likely lost from your question.
I'm guessing you were thinking of using the KML NetworkLinkControl and Update tags? If so, then OpenLayers would need to support NetworkLink, NetworkLinkControl, Update, and other associated KML tags. I don't see any reference to NetworkLInkControl in the OpenLayers documentation, so you're probably out of luck on that option, but I might not be looking in the right place.
Another way to do this, since OpenLayers does appear to support NetworkLink KMLs, might be to split up your data KML into a number of areas, combine them via a series of NetworkLinks, and only update the ones on your server which have changes.

Dynamically reload leaflet tiles

I would like to load specific leaflet tiles, based on changes to the user's activity. So, say, a user opens the map and sees particular tiles. Then, after a few minutes, some tiles are reloaded (because they have changed) and displayed. Perhaps the original was orange, the new one is blue.
This question and answer give a suggestion that could work for refreshing all the tiles, but that would be rather inefficient.
Is there a way to force the reload of specific individual tiles?
You'll have to implement a plugin for that, subclassing L.TileLayer and overwriting its createTile() method, so that you either have references to the tiles, or adding some kind of timeout or logic to each (loading/loaded) tile.
There's no generic way of doing this as of now, you'll have to code it.
I know I'm way to late, but I have a solution to this. You can manipulate the url using a parameter that you update only for the tiles that should reload. The rest will keep the cached tile image.
L.TileLayer.CustomLayer = L.TileLayer.extend({
getTileUrl: (coords) => {
var test = some_function_deciding_test_based_on_tile(coords);
return `http://example.com/tile_${coords.x}_${coords.y}.png?test=${test}`;
}
});
In this case if some_function_deciding_test_based_on_tile(coords) returns the same value as it did the previous time, the cached tile will be used. Otherwise a new tile will be fetched.

How to load 1000+ Google Earth markers effectively?

I have an array that contains over 1000+ markers for Google Earth.
At the moment I am looping through the array with a for loop which is causing the Earth to freeze until they have all loaded successfully.
Can anyone recommend the best way to handle this so it loads a lot quicker for visitors?
Ideally I was thinking of loading only those markers in view/bounds but haven't seen any documentation to support this idea.
Any help would be greatly appreciated!
Thanks
I would advise you to use HTML5 WebWorkers to instantiate the markers asynchronously and then just use whatever method they have for show()/hide(), iterating through your objects.
It will only work in latest browsers, that implement WebWorkers, but i don't think there is another efficient way
One possibility is to instead do this from the server using KML Updates:
https://developers.google.com/kml/documentation/updates
Each update would load in 100 markers, say, and display them and then a second later it reloads and pulls the next 100 markers.

What is the Proper Way to Destroy a Map Instance?

I recently developed an html5 mobile application. The application was a single page where navigation hash change events replaced the entire DOM. One section of the application was a Google Map using API v3. Before the map div is removed from the DOM, I want to remove any event handlers/listeners and free up as much memory as possible as the user may not return to that section again.
What is the best way to destroy a map instance?
I'm adding a second answer on this question, because I don't want to remove the back and forth we had via follow-up comments on my previous answer.
But I recently came across some information that directly addresses your question and so I wanted to share. I don't know if you are aware of this, but during the Google Maps API Office Hours May 9 2012 Video, Chris Broadfoot and Luke Mahe from Google discussed this very question from stackoverflow. If you set the video playback to 12:50, that is the section where they discuss your question.
Essentially, they admit that it is a bug, but also add that they don't really support use cases that involve creating/destroying successive map instances. They strongly recommend creating a single instance of the map and reusing it in any scenario of this kind. They also talk about setting the map to null, and explicitly removing event listeners. You expressed concerns about the event listeners, I thought just setting the map to null would suffice, but it looks like your concerns are valid, because they mention event listeners specifically. They also recommended completely removing the DIV that holds the map as well.
At any rate, just wanted to pass this along and make sure it is included in the stackoverflow discussion and hope it helps you and others-
The official answer is you don't. Map instances in a single page application should be reused and not destroyed then recreated.
For some single page applications, this may mean re-architecting the solution such that once a map is created it may be hidden or disconnected from the DOM, but it is never destroyed/recreated.
Since apparently you cannot really destroy map instances, a way to reduce this problem if
you need to show several maps at once on a website
the number of maps may change with user interaction
the maps need to be hidden and re-shown together with other components (ie they do not appear in a fixed position in the DOM)
is keeping a pool of map instances.
The pool keeps tracks of instances being used, and when it is requested a new instance, it checks if any of the available map instances is free: if it is, it will return an existing one, if it is not, it will create a new map instance and return it, adding it to the pool. This way you will only have a maximum number of instances equal to the maximum number of maps you ever show simultaneously on screen.
I'm using this code (it requires jQuery):
var mapInstancesPool = {
pool: [],
used: 0,
getInstance: function(options){
if(mapInstancesPool.used >= mapInstancesPool.pool.length){
mapInstancesPool.used++;
mapInstancesPool.pool.push (mapInstancesPool.createNewInstance(options));
} else {
mapInstancesPool.used++;
}
return mapInstancesPool.pool[mapInstancesPool.used-1];
},
reset: function(){
mapInstancesPool.used = 0;
},
createNewInstance: function(options){
var div = $("<div></div>").addClass("myDivClassHereForStyling");
var map = new google.maps.Map(div[0], options);
return {
map: map,
div: div
}
}
}
You pass it the starting map options (as per the second argument of google.maps.Map's constructor), and it returns both the map instance (on which you can call functions pertaining to google.maps.Map), and the container , which you can style using the class "myDivClassHereForStyling", and you can dinamically append to the DOM.
If you need to reset the system, you can use mapInstancesPool.reset(). It will reset the counter to 0, while keeping all existing instances in the pool for reuse.
In my application I needed to remove all maps at once and create a new set of maps, so there's no function to recycle a specific map instance: your mileage may vary.
To remove the maps from the screen, I use jQuery's detach, which doesn't destroy the map's container .
By using this system, and using
google.maps.event.clearInstanceListeners(window);
google.maps.event.clearInstanceListeners(document);
and running
google.maps.event.clearInstanceListeners(divReference[0]);
divReference.detach()
(where divReference is the div's jQuery object returned from the Instance Pool)
on every div I'm removing, I managed to keep Chrome's memory usage more or less stable, as opposed to it increasing every time I delete maps and add new ones.
I would have suggested removing the content of the map div and using delete on the variable holding the reference to the map, and probably explicitly deleteing any event listeners.
There is an acknowledged bug, though, and this may not work.
As google doesnt provide gunload() for api v3 better use iframe in html and assign map.html as a source to this iframe. after use make src as null. That will definitely free the memory consumed by map.
When you remove the div, that removes the display panel and the map will disappear. To remove the map instance, just make sure that your reference to the map is set to null and that any references to other parts of the map are set to null. At that point, JavaScript garbage collection will take care of cleaning up, as described in: How does garbage collection work in JavaScript?.
I guess you're talking about addEventListener. When you remove the DOM elements, some browsers leak these events and doesn't remove them. This is why jQuery does several things when removing an element:
It removes the events when it can using removeEventListener. That means it's keeping an array with the event listeners it added on this element.
It deletes the attributes about events (onclick, onblur, etc) using delete on the DOM element when addEventListener is not available (still, it has an array where it stores the events added).
It sets the element to null to avoid IE 6/7/8 memory leaks.
It then removes the element.

Categories

Resources