How to best tie into a webapp's mapbox-gl update code? - javascript

Background
I'm writing a browser extension which paints over the map of komoot.com/plan.
Currently I do this by placing a canvas on top of the existing canvas.
This works well but it is static and does not yet react to when the user moves the map around or zooms into it or the website focusses a particular location on the map.
Question
How do I best tie into this event loop of map updates?
Approaches considered
I could mimic / reimplement how komoot processes user input, but this sounds fragile and unreliable and messy. I would do this by adding listeners for mouse button events and cursor movement, etc.
The page's URL contains the lat and long coordinates together with the zoom level, e.g., https://www.komoot.com/plan/#49.9535480,5.3956956,11.345z. It changes after the map has changed. I assume there's a way to be notified of changes in the URL. If so I could then dynamically update my canvas.
This would still require some level of imitation of the page's internals. However, considerably less so than option 1.
Doing so I could only update my canvas after the animation is finished. Not a deal breaker but ideally I'd want to update it frame by frame together with the map itself for a more pleasing user experience.
Additional Details
Komoot seems to be using mapbox-gl
It's a Manifest 2 Content Script extension
This is my first browser extension ever
I'm writing this in Scala.js using this excellent template
Don't let this keep you from posting javascript solutions or pointing me to javascript documentation!
Screenshot

You don't seem to have mentioned the obvious way: use the move event:
map.on('move',e => {...
// get center with map.getCenter()
});
But it's not really clear exactly what you're trying to do, so hard to advise more specifically.

Related

MapBox event when all Tiles are loaded

I'm using Mapbox GL JS API to manipulate a Mapbox map. Right before I upload my result (which is a canvas.toDataURL) to the server through HTTP I need to resize my map (bigger resolution) and then use fitbounds to get back to the original points. After fit bounds fires, it takes the map a while to load all new tiles in. Only after this can I actually perform the upload. Right now though, I don't know if there's an event that's capable of telling me if all tiles are loaded.
I've tried all possible load functions and events in the API. There's a few issues on the GITHUB project but they're now at least a year old and there's been no update. Halfway through 2015 they started talking about adding an Idle event, but I can't seem to find any new documentation of it anywhere.
Has anyone found a way to make the code wait for the map to load? Or has any information regarding an update on this feature?
I doubt it matters much, but I'm working in an angular.js app.
We just added a Map#areTilesLoaded check which sounds like what you're looking for. That should go out in the next release (v0.37.0). In the meantime, the following should work.
map.on('sourcedata', (e)=> {
if (map.loaded()) {
// all tiles are loaded
// turn off sourcedata listener if its no longer needed
map.off('sourcedata');
}
});

How do I prevent a dynamicMapLayer from refreshing on every zoom or pan of the map?

I'm using the leaflet L.esri.dynamicMapLayer to display a large amount of polylines on the map, and the export request to ArcGIS Server to draw them can take a while. If the user quickly makes several zooms or pans, I can end up with a bunch of pending export requests, which also blocks other requests to ArcGIS Server. All those export requests except the last are useless.
For other client side layers, I'm already controlling the refresh by making sure the user has stopped zooming or panning for at least 2 second before refreshing the layers myself. How can I do the same for the dynamicMapLayer, can I pause or stop the automatic refresh and decide myself when I want the export request to be made?
Note that we cannot use tiles for better performance, because of other reasons the layer must stay dynamic.
How do I prevent a dynamicMapLayer from refreshing on every zoom or pan of the map?
You can not. It is designed that way.
Unless the esri folks redesign that to make a subclass of L.GridLayer instead of L.ImageOverlay, there's hardly any way around it.
I'm already controlling the refresh by making sure the user has stopped zooming or panning for at least 2 second before refreshing the layers myself. How can I do the same for the dynamicMapLayer?
With a horrible, ugly hack. Overwrite the private L.Esri.DynamicMapLayer._update method so that it becomes a decorator over the previous method, e.g. something like:
(function() {
var previousProto = L.Esri.DynamicMapLayer.prototype;
L.Esri.DynamicMapLayer.include({
_update: function(){
throttle(previousProto._update, 2000);
}
});
})();
It's ugly, it's against most good coding practices (overwriting private methods, eeeew), and it might break.
Note that we cannot use tiles for better performance, because of other reasons the layer must stay dynamic.
I disagree. "tiles" doesn't mean "static". You can easily apply cache-busting, or use a time dimension, or send all data to the client and let it slice it into vector tiles for quick rendering, or use something fancier like Carto(DB)'s Torque.
The fact that your Esri tools don't allow you to readily create different sets of tiles, or do not allow for tiled access of changing resources, or do not allow for triggering client-side data invalidations, doesn't mean that it cannot be accomplished.

Hiding crucial data from an SVG

I have a SVG generated map for the game I am developing. I have no problems with the game being open-source and it uses open web technologies such as HTML and SVG. No problems there.
But at the same time I want the players not to be able to see or reverse engineer a map of the whole world (to retain true exploration). For now I generate map using a seed that is secret and not version controlled. So even though the algorithm is known curious players can use open-sourced code to generate "game-like worlds" but not that exact one. This solves the "global" problem.
But since SVG is rendered on a page as a single Voronoi diagram all the data (I don't mind the coordinates of points) would be extractable. Data like resources, land types, biomes, climate etc. could be fetched from SVG to gain an upper hand in finding good locations for settlements.
Any idea how to prevent that? Players have limited vision so I thought about either:
not rendering the whole Voronoi diagram at all (just the visible part), but that could be potentially tricky to do (maybe, haven't looked into it yet),
inserting the resource/land tile data into SVG graph only to visible locations
I can see the benefits of both approaches and if done correctly it could even boost the performance (not rendering the whole thing/rendering with less data) and lead to bigger worlds without impacting performance.
Any other ideas/programming/architectural approaches to help with the issue?
(I am using Vue.js, d3.js, svg-pan-zoom and Laravel backend just in case it helps.)
The ideas that you gave are perfect, but for implementing them, you need to make hard work, and spend much time.
I have a suggestion. Is will work for most of the users. Maybe some users will "hack" it. But I believe it will work for 95% of the times.
You can create a very big rectangle, from the top left point 0,0 until the right bottom point. The rectangle will be white, and it will be over all other shapes.
This way if someone will download the SVG, we will see nothing. Just a big white rectangle.
In you game HTML, you can add a CSS selector, to hide this rectangle.
If you following this method, most of the users (who don't have a photo editing software) will not be able to see the map.
Users who knows how to inspect elements in HTML may see the map. But I believe that most of them who will see a white box, will not believe that there is something behind.
I think that this is a simple temporary approach that you can do, before doing other more defensive ways.

Can I trap rendering events with google map Api

I try to draw a very large dataset on google map (2500+ rectangles). The rendering of the rectangles take more than 5 secs. The whole page just stuck for the 5 secs, so I am thinking about adding a loading indicator or progress bar during the rendering.
To do this, I need to trap events of rending (start,finish rendering).
I checked the google maps Api documentation, did not find anything useful. Just what to know whether there is some work around or something I miss in the api doc that can help me to trap rendering events.
As of Google Maps v3.14 the answer is no. There's no such event to listen for in the API. If you dug through the code long enough you might be able to find a hack, but given that you're in control of the rectangles you're drawing and you have a count of them, why not iterate the progress bar as you add them? Individually they will render very quickly so whether you iterate the progress before or after each is added to the map should make no difference to the user, despite the fact that it feels like the wrong order to the developer.
This gives an overview of all the events in GMapsV3 http://gmaps-samples-v3.googlecode.com/svn/trunk/map_events/map_events.html
Check if the events you need are there.

Limiting zoom/viewable area with Google Earth API

I know this is not nearly as straightforward as it is with the Maps API v3, but what would be the best way to effectively limit the zoom or viewable area (if the latter is even possible) with the Earth API? We are trying to prevent people from inadvertently zooming out too far when navigating the small focus area of our map.
Untested, but my first attempt would probably be to listen for the viewchanged event, and check the current view to see if it matches your criteria. If not, you could then flyTo something within your view, and hopefully the effect would be somewhat smooth (but I'm not too sure it would be totally smooth).
See http://code.google.com/apis/ajax/playground/#viewchange_event_(and_viewchangebegin,_viewchangeend) for an example of checking for the viewchanged event.

Categories