Dynamically reload leaflet tiles - javascript

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.

Related

Model aggregating in viewer - coordinate issue

I am dynamically aggregating models in the Viewer (coming from multiple BIM files). Basically, I initialize the viewer, and then LoadDocument and LoadModel for each model that user chooses to view, dynamically.
These are mostly NVC files (what I used for testing), which are sharing the coordinate system. Models getting dynamically loaded and getting 'stitched' correctly.
However, coordinate system is getting screwed up when the second model gets loaded - not always but in some cases (in v6.3 - haven't checked previous versions). When this happens, I can notice the followings:
When I click 'home' icon in the viewer, it doesn't show the full aggregated model but only shows the first loaded model in the center and rest go outside screen.
When I click an element to select, it selects something else (with an offset), and some cases selects nothing (meaning, clicked coordinates has been interpreted incorrectly).
I have tried multiple combinations and this problem seems to happen when first loaded model is not occupying full size of the aggregated model (say, one floor of the building).
I figured out a solution though. After loading the models (every time when user choose some), if I 'hide' all models and 'show' them again, it fixes the problems with click and AGGREGATED_SELECTION event.
This is not a nice user experience because I want to be able to load the models in the background while user is moving around the previously aggregated model-set. If I hide/show after loading the new one, it will be annoying to user.
Is there a way to 'reset' the coordinate system of the viewer (to the same effect what happens when hide/show the models)?
I would appreciate some help to get around this issue.
Thanks
Bandu
ok, so there is a massive offset, causing a precision issue. Which is why you are seeing lots of 'jittering' of the camera.
To fix this... we need to correct the massive offset, by returning all the geometry closer to origin manually. (or fixing the original navisworks file).
First, let's roughly figure out the offset value...
Hold down ALT-key and click anywhere on an object. This sets a green dot, the pivot point. Then use viewer.navigation.getPivotPoint() to get the x,y,z value. (details: https://github.com/wallabyway/markupExt/issues/2).
You should see an xyz value like this...
Z.Vector3 {x: 1296285.515098644, y: 14995636.431742325, z: 364.26238179027337}
Now, adjust set the global offset using this value, to correctly move all the models closer to 0,0,0. like this...
line 70:
var modelOptions = {
sharedPropertyDbPath: doc.getPropertyDbPath(),
globalOffset: {x:1296285.515098644, y: 14995636.431742325, z:0}
};
viewer.loadModel(svfUrl, modelOptions, onLoadModelSuccess);
`
This reverses the effect of this big offset. The result is the geometry stops flickering and the camera movement stops 'jittering' due to precision issues.
Let me know if that works for you.
Michael
ps. you can pull the 'exact' global-offset out of the small side-file AECModelData.json, see blog: forge.autodesk.com/blog/add-revit-levels-and-2d-minimap-your-3d

fabricjs set image as background

I'm looking for a way for placing an object permanently to the back of the canvas.
I see that the various
canvas.sendBackwards(myObject)
canvas.sendToBack(myObject)
will send the object to the back of the canvas but if I add a new element and then send it backwards it will go beneath the other image and I need to avoid this. I cannot use canvas.setBackgroundImage because I'm creating a custom image class and setting it as backgroundImage will make me loose some functionality. I would like to set something like a z-index on the newly created image. For example, while initializing a new image I can set lockMovementX (and many others) to false or true, isn't there nothing like this for z-index of every canvas element, or do I have to push my background element to the back every time there's a change on the canvas?
I've run into the same situation - I want an image as a background layer yet don't want to apply it as a background image due to restricted functionality.
I did write a function for my application that re-stacks layers every-time there is new layer added. I set the name attribute to this layer so I could quickly identify what layer it is that needs to be sent to the back.
While this may seen inefficient, I've never had any performance issues related to this function. But also in my application I typically only have about 5 to 20 layers - typically on the lower end of that range.

Timing issue with image load?

I have a draggable image class that displays an image as expected when it is the only object on the stage. But when I add other graphics objects the image is not visible until I mouseover where the image is located.
It seems like the stage needs an extra update when there are more graphics objects on the display list. Calling tick() or setting my update flag manually doesn’t solve the issue (presumably this happens too quickly).
I can hack around it by putting a counter in the tick function like
if(update || ticks < 10). Clearly, this is not really a solution; the tick code shouldn’t have to check the counter every tick, forever.
Does anybody know the correct way?
You can see it here: http://eduk8r.org/bridge/. You’ll have to mouseover the image the first time, though. (The image is located to the left hand side of the bridge).
The relevant code is in controller.js and DraggableImage.js.
UPDATE: I had thought that reloading the page was sufficient to display the image but that only seems to happen locally.

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.

Capture website screenshot using javascript

I've seen similar questions asked and the answers were not quite what I'm after. Since this question is slightly different, I'm asking again - Hopefully you'll agree this isn't a duplicate.
What I want to do: Generate an image showing the contents of my own website as seen by the user (actually, each specific user).
Why I want to do it: I've got some code that identifies places on the page where the user's mouse hovers for a significant length of time (ppl tend to move the mouse to areas of interest). I also record click locations. These are recorded as X/Y co-ords. relative to the top-left of the page
NB: This is only done for users who are doing usability testing.
I'd ideally like to be able to capture a screenshot and then use something server-side to overlay the mouse data on the image (hotspots, mouse path, etc.)
The problem I have is that page content is very dynamic (not so much during display but during server-side generation) - depending on the type of user, assigned roles, etc... whole boxes can be missing - and the rest of the layout readjusts accordingly - consequently there's no single "right" screenshot for a page.
Option 1 (which feels a little nasty): would be to walk the DOM and serialize it and send that back to the server. I'd then open up the appropriate browser and de-serialize the DOM. This should work but sounds difficult to automate. I suspect there'd also be some issues around relative URLs, etc.
Option 2: Once the page has finished loading, capture an image of the client area (I'd ideally like to capture the whole length of the page but suspect this will be even harder). Most pages don't require scrolling so this shouldn't be a major issue - something to improve for version 2. I'd then upload this image to the server via AJAX.
NB: I don't want to see anything outside the contents of my own page (chrome, address bar, anything)
I'd prefer to be able to do this without installing anything on the end-user pc (hence javascript). If the only possibility is a client-side app, we can do that but it will mean more hassle when getting random users to usability test (currently, we just email friends/family/guinea pigs a different URL)
One alternative solution would be to "record" the positions and dimensions of the main structural elements on the page:
(using jQuery)
var pageStructure = {};
$("#header, #navigation, #sidebar, #article, #ad, #footer").each(function() {
var elem = $(this);
var offset = elem.offset();
var width = elem.outerWidth();
var height = elem.outerHeight();
pageStructure[this.id] = [offset.left, offset.top, width, height];
});
Then you send the serialized pageStructure along with the mouse-data, and based on that data you can reconstruct the layout of the given page.
One thing we always talk about where I work is the value of ownership vs the cost required to make something from scratch. With the group I have, we could build just about anything...however, at a per-hour rate in the $100 range, it would need to be a pretty marketable tool or replace a very expensive product for it to be worth our time. So, when it comes to things like this that are already done, I'd consider looking elsewhere first. Think of what you could do with all that extra time....
A simple, quick google search found this: http://www.trymyui.com/ It's likely not perfect, but it points to the fact that solutions like this are out there and already working/tested. Or, you could download a script such as this heatmap Obviously, you'd need to add a bit to allow you to re-create what was on the screen while the map was created.
Good Luck.
IMO, it's not worth reinventing the wheel. Just buy an existing solution like ClickTale.
http://www.clicktale.com/

Categories

Resources