Mapbox GL JS line animation - javascript

I am working with ~150,000 polylines that span North America. Across all the lines I'd like to draw/animate from the start to the end of each line. I started down the road of adding vertices to an array and updating a Mapbox source like this example. This was not feasible given the size of the entire dataset. In geojson form it's about 800mb and manipulating this in the browser seemed unreasonable.
As an alternate approach, I've broken each polyline into equally sized segments of 10km in length. I added an attribute on each segment which represents its percent distance from the start. I created an interval below that triggers a setFilter event, filtering segments based in the percent from start.
currentLinPos=0;
lineInterval=setInterval(function(){
currentLinPos+=.01;
if(currentLinPos>0.9999){
currentLinPos=0
}
map.setFilter('lines',['<','linePrc',currentLinPos])
}, 250);
Even when working with a small subset of the data, performance choppy and lags. I expected a better result from Mapbox GL but perhaps I need to rethink my data structure or approach entirely. Or would using something like DECK.GL be far superior here?

Related

Performant GL Triangles Mapbox GL JS

I am working on trying to create a basic, grid-based, but performant weather-arrow visualization system.
EDIT 2:
Up-to-date version here: ( Mapbox Tracker ) of the system using the workflow which is described below
Usage Instructions:
- Click on Wind icon (on the left)
- Wait for triangles to occupy screen
- Pan time-slider (at the bottom)
As you will observe (especially on larger resolutions or when panning time slider quickly) there is quite a performance hit when drawing the triangles.
I would greatly appreciate any advice on where to start with either using something in the current API which would help, or any ideas on how to tap into the current graphics pipeline with some type of custom buffer where I would only need to rotate, scale, change color of triangles already populated in screen space.
I feel as though my specific use-case would greatly benefit from something like this, I really just don't know how to approach it.
I have a naive implementation running using this workflow:
Create a geojson FeatureCollection source
Create a fill layer
Using Data Driven property: fill-color
Data function:
Get map bounds
Project sw & ne into screen points (map.project(LatLng))
Divide height and width into portions
Loop through width and height portions
Lookup data
Access data rotation property
Create vertices based on center point + size
Rotate vertices
Create Point objects for vertices
Unproject Point Object and wrap map.unproject(Point).wrap()
Create Feature Object, assign Data driven Color
Assign unprojected LatLng as Coordinates to Polygon geometry
Add to Feature Array for Collection
Call setData on layer
So while this works, I'm looking for advice for a more performance friendly approach.
What I'm thinking here is whether I can somehow create a custom layer, one where I only need to draw to screen co-ordinates to represent the data relative to its LatLng point. So that I can draw colored, scaled, rotated triangles in screen space, and then have them update to relevant data from the new relative LatLng position.
E.g. Update some type of Mesh on screen instead of having to: unproject, then update feature collection source using map.getSource('arrows').setData(d), requestAnimationFrame(function) etc.
I've done similar in three.js in other projects but I would much rather use something that is more mapbox native. Does this sound feasible? Am I going to see a decent performance boost if so?
I've not dealt with raw gl calls before etc so I might need a pointer or two in the right direction if its going to need to get as low level as that.
EDIT:
Previous Implementation using gmaps / three.js : volvooceanrace
(wait for button on left to go from grey to black) click on top button which shows a 'wind' label when hovered over, slide red time bar underneath to change data.
Added screenshot of current working implementation
Mapbox GL Arrows
Not sure what was available in 2016, but a reasonable approach these days might be to use symbol layers, and the icon-rotate data-driven property to rotate each icon based on the property of its data point.

Enclose multiple separate sets of points using polygons which stack next to each other

I am working on a data set of coordinate points(many dots in area) either (x,y) or (lat,lon) which fall into multiple categories. What I am trying to do is get polygons of areas from those points which are called concave or non-convex as far as I know, but also those polygons have to be next to each other with no gaps between them.
These are the initial points(example)
This is the approximate result I am aiming for
Real life example would be European geopolitical map, if you had all of the addresses of all countries and wanted to get area of each country as a polygon and end up with a map.
I have come across many questions related to getting polygons from set of points, but were unable to use it in my scenario. If you need any more information please let me know. Thank you for your help.
You could use a Voronoi tesselation of the input space. Instead of having point you have sets of points though. Basically, you take each point in space, and look at the closest point to it. It then gets the same "class" as that point. For smoother outputs you could look at a k majority out of N nearest points. It would mean working with a bitmap image rather than 2D coordinates, but you'd get something workable. You could then use simpler image manipulation tricks (edge detection, binary set operations etc to get just the edges, and then perhaps superimpose those on the image).
As an alternative, you could run a convex hull algorithm on each data set, and then try to fix the overlap areas.

How do I use collision detection to fix overlapping images with d3.js?

I'm trying to display a large number of images on a d3 display using T-SNE. The x and y coordinates are pre-calculated, the location on the svg area is adjusted using using translate/zoom.
At the moment they all display using the precalculated coordinates.
and they remain in place as zooming/panning.
I'm looking to use collision detection (like this example) to adjust the images locations slightly so that they don't overlap, but as much as possible maintain the rough global structure.
Here's my attempt so far
https://gist.github.com/GerHarte/329af8ee5ffd8a1f87c5
With this it loads as in the image above, but as soon as I pan or zoom, all the points expand out hugely to a completely different location on the canvas and look like this, they don't seem to overlap, but they're extremely far apart.
Is there something wrong in my code or is there a better way to approach this?
Update:
I followed Lar's answer here, with the slight addition of setting the raw data points to where Lar's code settles since the points are translated when zooming or panning. The results look great (see below), but for a larger number of points (5000+) it seems to crash before converging on a final result.
Are there any suggestions to improve the efficiency with this approach? Going to try the Multi-Foci Forced Directed approach.

Cesium JS best way to get an array of points for an arc

What is the neatest (code design) and most per-formant way of getting an array of points for an arc (polyline), for the purpose of animating using Cesium's timer/clock.
Variable inputs include (start/end location), height (highest point) from earth's surface and number of points for drawing.
I'm currently using a polyline collection, so the answer should describe how to generate the points for existing polylines or convert to a different approach.
I would also need the arc (color) to fadeIn or fadeOut to opacity 0.
Multiple arcs may be added or removed from the collection per second. Each arc will have different start and end points
The start and end location should have height 0 (touching the earth).
(For Cesium version b26)
Just to be sure I understand your question, you have a bunch of polylines on a map and you want to get a bunch of data points along the line for use in animating the something along the path. I'll also assume you want geodesic lines/arcs for the polylines rather than the straight lines that are normally drawn on Mercator maps as geodesic lines actually follow the spatially accurate path of the polyline i.e. the same path a plane would take. If this is the case then take a look at this blog post: http://alastaira.wordpress.com/2011/06/27/geodesics-on-bing-maps-v7/ This post describes how to calculate data points along the geodesic path of a polyline.
CesiumJS includes several spline functions that can be used. One of the easier ones to use that an accomplish what you want with just three points is the Catmull-Rom Spline:
http://cesiumjs.org/Cesium/Build/Documentation/CatmullRomSpline.html
You will need to produce a middle point. To do this you can take a mean of the lat/lon coordinates and add a large height. Because of the spline used and the low number of points, it does end up looking a little egg shaped. The benefit to this is that you can ask the spline object for an arbitrary number of points, so the arc can be as smooth as you want. Just don't forget to add the first and last points to the array returned by the spline as those are omitted.
There are other types of splines, but I found the Catmull-Rom spline the easiest to use. You can search the CesiumJS documentation for some of the other included splines.
I've been looking into the same thing (minus the time aspect) and I found Cesium.EllipsoidGeodesic(start, end, ellipsoid), which allows you to get points at fractions of the path. It seems to me that you can choose the fraction based on the distance and calculate regular points using the result.
https://cesiumjs.org/Cesium/Build/Documentation/EllipsoidGeodesic.html
I haven't tried it yet, but it's on my list of things to do.

How to draw/load part of a page that is currently looked at?

I am currently working on a map generator application based on javascript, and I have wrote more than 400 lines of code, that creates a hexagonal map, adds coordinates to tiles, adds textures on tiles like grass, ocean and elements like castles, units etc.
I have added quite a few useful functions to this offline map editor, like zoom in and zoom out, turning grid on/off, dragging the map, and a few others, and I'm currently studying on how to add save and load functionality to this offline game map editor.
It sort of looks like a paint application, except that instated of drawing pixels, you use it to draw a map with hex tiles. You simply click on Generate a new map and you give your desired map size (e.g 64 tiles width by 64 tiles height) and the map is drawn for you, the tiles are simple divs that have the relative background image as texture. Tiles are drawn one by one using a simple for loop. But as the code grows in size so does my worries.
Because the map I create on my own map editor will be used on an online multiplayer game, it will be huge! for example to support at least 20000 users on the upcoming game there should be at least 20000 tiles, only for the users to occupy, not to mention the territory they will own, mountains, jungles, barbarian tribes, and so on..
I have made the calculations and found out that a 512 by 512 (about 262000 tiles) map will sufficiently answer the needs of that many users. However, the map will be huge. so I decided to test and see how much load time does it take to make such a map using the codes I have created with the least process possible and I found out that it takes nearly a minute or more, which is not acceptable, from a gamers perspective.
A zoom in for example in such a huge map will mean looping through every 262000 tile to change their size. although the process takes less time than drawing/loading the map from scratch, but it is still slow.
I was thinking with a map that huge which won't even fit in a browser's window, why should I draw the entire map? why not instead load the part which the user is currently looking at. Loading/drawing only the part that is needed, this way reducing load time and increasing performance. But this is proving to be a real challenge, and there are very limited resources online about implementing such a functionality. Where to start? How to approach the problem and respective solution?
I would start out by separating your concerns a little more. You're able to view WxH number of pixels, and the top left of the user's screen sits at (x,y) coordinates.
Loading the entire map, as you have pointed out, is crazy. But by knowing how large the game world is, and by knowing the user's coordinates in that world, you can easily select the subset of items that are in view.
Keep in mind that at a zoomed out resolution you shouldn't be using the full-sized images. Loading 262000 images (for just the map!) is going to be too heavy and probably crash. You should have different images for different zoom levels. This is a much bigger question and you should buy a book and do more research on google. But at least the thinking about the "where the user is" vs "where the items in the world are" is a place that I would start at.
Hope that helps.

Categories