High Performance Coordinate System - Three.js - javascript

Conceptual Question
I am building a flight simulator in Three.js. I intend to rip CSV data for Latitude, Longitude, and Elevation from Google Earth and transfer it into arcGIS to create a Digital Elevation Model (DEM). I then want to create the terrain based on the DEM. I already have a splat map texture shader I wrote and things are looking good.
However, I will need to add models and more specifically text and landing zones for the towns. This will require accurate XYZ coordinates.
I figure this is an interesting problem. I have seen one question before on stackoverflow similar to this but it was not quite to the same depth I'm looking for.
1) How to create coordinate system that maps actual XYZ, Latitude, Longitude, Elevation data to a PlaneBufferGeometry?
My assumption is that if I take a hypothetical 100,000 x 100,000 map sample then I will need to create a Plane that has matching vert count and then maps 1:1.
new THREE.PlaneBufferGeometry( 100000, 100000, 100000, 100000 );
Then the trickier part of mapping lat long coordinates to this. Perhaps just a multiplier like * 100 or so per lat, long degrees?
2) How to create the most efficient data structure for this. It will contain a lot of data.
I am thinking the most efficient data structure would be an array with Z integers.
let vertArray = new Array(10000000000);
for (i = 0; i < 9999999999; i++) {
vertArray[i] = planeBufferGeometry.vertices[i].z;
}
Each 100,000 in the array would represent a Y coordinate, while each i value in said sections would be an X coordinate. The value of the Z coordinate would be stored in the array itself.
So hypothetically if I wanted to get X: 3, Y: 4, Z: ? it would be...
const xCoord = 3,
yCoord = 4,
index = (yCoord * 100000) + xCoord,
zCoord = vertArray[index];
This is the smallest overhead approach I can think of... defining Array length ahead of time, keeping the array one dimensional, filling with only integers. Any better ideas? Perhaps creating an array would be unneeded and I could create an equation that pulls vert data directly from the rendered mesh?
3) Are there ways to decrease the impact of large data stored in browser memory?
I have yet to implement this but the idea of a 10 Million Length array in the browser is quite a lot in my mind. I would prefer being able to load the entire thing rather than doing some sort of AJAX call when the helicopter gets near the edge of a sub-plane. Think "zones" in MMORPG's.

Related

Check if list of latitude, longitude are withing range

I'm having a problem
I would like to ask what the most efficient way is to check if latitude and longitude coordinates are inside a range (for example 100 meters) from a list of latitudes and longitude points.
For example I have this list of coordinates:
[[48.34483,51.16.24517],[48.484,16.2585],[48.361,51.87739419],[6.38477205,51.87745015],[48.3645,51.16.73167],[6.38391099,51.87755068],[48.3575,16.725],[6.38380232,51.87720004],[6.38376297,51.87708017],[6.38375183,51.87704018],[6.38373055,51.8769829]]
I would like somehow that all points that are in a specific range (100m for example),
to be somehow grouped.
Is there any way how I can indicate that for example from the above list:
[48.484,16.2585],[48.361,51.87739419] and [48.3575,16.725]
are in a radius of 100m ( distance between these points is less then 100m) and they should be groped
Sounds like a great question for a GIS professional; you could perhaps post on gis.stackexchange.com. Are you using a mapping technology where you already have access to an API? The functionality that you're looking for are referred to as geometric operations. I'd start by looking into geometry functions available in an API which calculate the distance between points. You could find the geometric center of all of the points, then request the geometry API to create a buffer around that point. Next, query if each point falls within that buffer.
Found a post which might help with finding the center of the points here:
How do I find the center of a number of geographic points?
Also found a post on stackexchange which sounds very similar to yours, only the post is in reference to ArcGIS and the Point Distance (Analysis) tool:
https://gis.stackexchange.com/q/91571/81346
Ideally you'd use a geospatial db for this, to avoid performance issues when dealing with increasing numbers of points. MySQL, Postgres etc all support geospatial functions.
But as you've tagged your question with javascript, I'll post a JS solution. There's an npm package called haversine - with it, you should be able to loop through each point and return the other points that are within 100m. Something like:
// bring in haversine from npm
var haversine = require("haversine");
// define the full list of points
var data = [
[48.34483,51.1624517],
[48.484,16.2585],
[48.361,51.87739419],
[6.38477205,51.87745015],
[48.3645,51.1673167],
[6.38391099,51.87755068],
[48.3575,16.725],
[6.38380232,51.87720004],
[6.38376297,51.87708017],
[6.38375183,51.87704018],
[6.38373055,51.8769829]
];
var points = data.map(point => new Object({latitude: point[0], longitude: point[1]}));
// var to store results in
var results = [];
// loop through the points
points.forEach((pair) => {
var nearby = points;
// filter the full list to those within 100m of pair
nearby.filter(point => haversine(pair, point, {unit: 'mile'}) <= 100);
results.push({
'point': pair,
'nearby': nearby
});
});
console.log(results);
Note: I corrected some of the points in your list, which had double decimals so weren't valid

Compute Voronoï diagram on a large dataset of very close points

I have a large dataset of geographical points (around 22 000 points, but I could be more in the future) and I need to compute their Voronoï diagram. I first project my points from (lat,lng) to (x,y) (using latLngToLayerPoint() from Leaflet) and then compute the diagram based on a Javascript implementation of Fortune's algorithm . I recover each cells of the diagrams or more precisely va and vb, being respectively :
"A Voronoi.Vertex object with an x and a y property defining the start
point (relative to the Voronoi site on the left) of this Voronoi.Edge
object."
and
"A Voronoi.Vertex object with an x and a y property defining the end
point (relative to Voronoi site on the left) of this Voronoi.Edge
object."
(cf. Documentation)
Finally, I project back these points to display the diagram using leaflet. I know that, in order to compute the diagram each point needs to be unique, so I get rid of duplicates before computing the diagram. But the thing is, I end up with a pretty bad result (non-noded intersections, complex polygons):
Close-up
I have holes in the diagram and I'm not sure why. The points are house Address so some of them, even if they are not equals, are really (really) close. And I wonder if the issue doesn't come from the projection (if (lat1,lng1) and (lat2,lng2) are almost equals, will (x1,y1) and (x2,y2) be equals ?). I strongly suspect that is where the issue come from, but I don't know how to workaround (establish a threshold ?)
Edit : I precise that I delete the duplicates after the projection, so it's not about the precision of the projection but more about what happen if two points are one-pixel apart ?
So I found the solution to my problem, I post it in case of anyone need to compute a Voronoï diagram on a map using Leaflet and Turf and is having troubles implementing the Fortune's algorithm (until turf-voronoi works).
Other sources of how to compute a Voronoï diagram on map can be found (but using d3) (I think d3 also use this Javascript implementation of Fortune's algorithm)
The problem was not caused by the size of the dataset or the proximity of the points, but by how I recovered the cells.
So you first need to project your point from (lat,lng) to (x,y)(using latLngToLayerPoint()), compute the diagram : voronoi.compute(sites,bbox), where the sites are your points looking like this [ {x: 200, y: 200}, {x: 50, y: 250}, {x: 400, y: 100} /* , ... */ ] (note that your sites needs to be unique) and if you want the frame of the screen for your current zoom to be your bbox juste use :
var xl = 0,
xr = $(document).width(),
yt = 0,
yb = $(document).height();
Once you computed the diagram, just recover the cells (be carfull, if you want the right polygons you need the edges to be counterclockwise ordered (or clockwise ordered, but you them to be ordered), thankfully the algorithm provides the half edges of a given Voronoï.Vertex counterclockwise ordered). To recover the vertex of each cell you can use either getStartpoint() or getEndpoint() without forgetting to project them back from (x,y) to (lat,lng) (using layerPointToLatLng())
diagram.cells.forEach(function (c) {
var edges=[];
var size = c.halfedges.length;
for (var i = 0; i < size; i++) {
var pt = c.halfedges[i].getEndpoint();
edges.push(map.layerPointToLatLng(L.point(pt.x,pt.y)));
};
voronoi_cells.push(L.polygon(edges));
});
Finally, you have to use a FeatureCollection to display the diagram :
I highly recomment you don't implement a Voronoi tesselation algorithm by yourself, and use https://github.com/Turfjs/turf-voronoi instead.

Efficiently sorting large arrays of people by their proximity to the user

So I'm trying to create a list of the 15 closest people in an array of varying sizes. The array will almost always be ~100 objects in size, but for the sake of testing, I'm trying to make it work with 10,000 (there may be need for the project to be scaled up to these numbers further down the line).
Currently, the method in place is to loop through the array of people, and calculate their proximity to the user based on both the person in question's and the user's latitude and longitude (the former of which is stored in the array). This is done using the haversine formulae and works well (though it does take ~500 milliseconds).
The problem however is that when run on a mobile device (a Samsung Galaxy S5 for the sake of this example), performance really suffers. The time taken for the S5 to sort through 10,000 records in order of how close they are to a pre-determined latitude and longitude is a staggering 1,500-1,600 milliseconds, an unacceptable delay for an app that will be doing many things either side of this process.
So my question is, am I missing some fundamentally more efficient means of sorting this list? Is there an alternative formulae available that is more efficient? Could I simply calculate the combined difference in Latitude and Longitude in .000001s and sort based on that?
Notes:
The user's location is variable, so proximities cannot be stored
I am aware that I'm asking a mobile CPU to perform 100,000,000 calculations in a short space of time and so this may be unavoidable
The method of sorting is the native JavaScript sort method, below is a simplified version of what I am doing to test these timings:
patientArray.sort(function(a, b)
{
return GetDistanceToPoint(a["Lat"], a["Lng"]) - GetDistanceToPoint(b["Lat"], b["Lng"]);
});
// Function to get the User's distance to a point
function GetDistanceToPoint(Latitude, Longitude)
{
// Check if the User's current Latitude and Longitude are available
if(currentLat && currentLng)
{
// Convert degrees to a radius
function degreeToRadius(degree)
{
return degree * (Math.PI/180)
}
// Variable to store radius of the Earth in Km
var earthRadius = 6371;
// Calculate the distance between the two points
var dLat = degreeToRadius(Latitude-currentLat);
var dLon = degreeToRadius(Longitude-currentLng);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(degreeToRadius(currentLat)) * Math.cos(degreeToRadius(Latitude)) * Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = earthRadius * c;
return d;
}
return "-1";
}
It all has to be tested but here are some ideas that I would try.
For heavy use of trigonometric functions you can use lookup tables. This is always a good idea. For example precompute 360 (or more) values of sin() and for every sin(radians) in your code use sinTable[degrees].
(I say 360 values as an example because with that your index is an angle in degrees but any value will do and it all depends on what precision you need - it can have thousands of values if needed.)
Avoid unnecessary calculations. May seem obvious but people often write something like x/(2*Math.PI) instead of x*A where A (a better name of course) is computed once as 1/(2*Math.PI).
Memoize every value that you can, if it makes sense.
If your data have some specific qualities, like for example never spanning half of the planet, then you can try to cheat a little bit and use coordinates on a flat plane - then you only have to compute square roots (which could also be precomputed to use lookup tables).
Those are the first things that crossed my mind. Hope it helps.
UPDATE:
You made an edit so I know a little bit more now. Here are my tips:
Don't convert degrees to radians. Keep degrees and use them as indexes in lookup tables of precomputed values of trigonometric functions. If you need more precision then multiply the degrees by 10 or something and use a scale from 0 to 3600 instead of 0 to 360. Find a good size/precision compromise that works for you.
You can eliminate all sin() and cos() calls that way and if you're lucky you can eliminate atan2(). I wouldn't worry so much about sqrt() but you can eliminate it too if you know what the values are typically going to be. If values of functions like sqrt() or atan2() are not known up fron then you can fall back to real functions for values that are out of range of your lookup tables.
Avoid to many function calls. Instead of an anonymous function that you pass to patientArray.sort(), that calls GetDistanceToPoint(), that calls degreeToRadius() - you need only one function that can be passed directly as an argument to .sort() and that function doesn't need to return d - it can return just c (in your example).
You don't need to multiply everything by earthRadius if you only use that value for sorting.
Another quick ideas: using typed arrays (for lookup tables), and asm.js and SIMD.js for additional optimization if possible.
Those are the first things that come to mind. I'd love to hear how much faster your code can get. Good luck.
UPDATE 2:
Another idea - instead of (or in addition to) optimizing the GetDistanceToPoint() you can also make sure that it isn't called more than once for every object.
Instead of:
patientArray.sort(function(a, b)
{
return GetDistanceToPoint(a["Lat"], a["Lng"]) - GetDistanceToPoint(b["Lat"], b["Lng"]);
});
You can try doing something like:
patientArray.forEach(function (element) {
element.distance = GetDistanceToPoint(element["Lat"], element["Lng"]);
});
or maybe for loop will be faster:
for (var i = 0; i < patientArray.length; i++) {
var element = patientArray[i];
element.distance = GetDistanceToPoint(element["Lat"], element["Lng"]);
}
to store the value for the entire patientArray array.
And then in the sort function:
patientArray.sort(function(a, b)
{
return a.distance - b.distance;
});
It may hopefully save you a lot of calls to GetDistanceToPoint.

How to store multi connected waypoints

I am trying to create a basic 2D road system on a grid. Currently I have a list of staight lines but they are not connected to each other.
The part I am stuck on is how I construct the data and store my data so I know which waypoint points to which ever waypoint (more than one way point can connect to any other way point).
So currently if you image i have this as my data:
var point = [];
point[0] = {'x':2,'y':6};
point[1] = {'x':2,'y':8};
point[2] = {'x':6,'y':9};
point[3] = {'x':7,'y':2};
Now suppose point 0 connects to points 2 and 3. And point 1 connects to point 3.
What would be the best way to store the information that these points are linked, also allowing me then to look up and obtain properties of the object relating to a connected waypoint (which would mainly be useful for pathfinding in the future).
For example I may need to find a waypoint at a given x or y position. Or i may want to obtain relevant waypoint data that are connected to for example point 1, such as their x and y position and what ever waypoints may connect to them too.
The road network can be represented by an adjacency list. Basically, each point will be given a list (which could be implemented by an array) containing the indices which can be reached from it. In your example, this can be expressed as follows.
var point = [];
point[0] = {'x':2,'y':6, 'neighbors':[2,3]};
point[1] = {'x':2,'y':8, 'neighbors':[3]};
point[2] = {'x':6,'y':9, 'neighbors':[0]};
point[3] = {'x':7,'y':2, 'neighbors':[0,1]};

Group array of lat/lng coordinates together on map

I'm trying to compare an array of lat/lng coordinates for a map to see if any "cluster" or group together. I want to remove the ones that are too close together so if there are 4-5 stacking on top of each other on a map, it wil only show 1 until you zoom in a bit more, and then it will recalculate all of them again.
I've tried comparing the array to itself, but it doesn't seem to give consistant results. Has anyone attempted something like this before?
JSON Example:
[
{
Latitude = "44.033843";
Longitude = "-79.48865499999999";
},
{
Latitude = "44.033843";
Longitude = "-79.48865499999999";
}]
Iterate the nodes and for a zoom level only display those that are beyond a set distance from each other. The haversine formula is simple enough to implement: example in JS here.
http://www.movable-type.co.uk/scripts/latlong.html
For the efficiency aspect, you probably don't want to calculate the entire list against the temporary list on every iteration so as a first level declutter a simple rounding works (every degree being ~60 miles from each other) ... start with rounding to the nearest 5 degrees, then 1, then 10ths, 100ths, etc as you zoom in. Ordering these lists and pulling unique array values first - then calculating distances from the resultant list.
There are certainly many other algorithms to do it - but at some point you have to calculate distances.
EDIT: this assumes you're happy to fudge things a little bit, and instead of worrying about the actual distance between points on the globe, you look at the "Manhattan" distance of their lat/long coordinates. It depends on how precise you need to be, and whether you have points near the Earth's poles. But for most practical purposes this assumption should be fine.
Suppose your desired precision is one decimal place. Then I would just iterate through the array, building up a has where the keys are the rounded coordinates and the values are arrays of lat/long pairs which round to the corresponding key.
hash = Hash.new
latLongArray.each { |point|
key = [point.lat.round(1), point.long.round(1)]
hash[key] = Array(hash[key]) + [point.lat, point.long]
}
This way you have them clustered, and you can in fact just put markers at the coordinates given by the keys themselves.

Categories