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
Related
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.
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]};
I'm using the ESRI JavaScript API v3.8. (I know 3.11 is out - can't upgrade yet.)
What I'm trying to do is to create a geometric buffer of a size provided by the user from an arbitrary line (or point) selected by the user. Some of the relevant code is shown below:
var params = new esri.tasks.BufferParameters();
params.distances = [values.distance]; //the input distance
params.geometries = [gr.geometry]; //the input geometry
params.unit = esri.tasks.GeometryService.UNIT_FOOT;
params.outSpatialReference = mapView.map.spatialReference; //always 3857
params.bufferSpatialReference = gr.geometry.spatialReference; //always 3857
esri.config.defaults.io.corsEnabledServers.push('mydomain.com');
esri.config.defaults.io.proxyUrl = 'https://serverdomain.com/proxy';
var gsvc = new esri.tasks.GeometryService('https://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer');
gsvc.simplify(params.geometries, function(geometries){
params.geometries = geometries;
gsvc.buffer(params, function(geometries){
//add output geometry to the map and perform spatial query with it
}, function(err){
//handle error
});
}, function(err){
//handle error
});
The problem is that, if I use an input distance of 500 (feet), then measure the distance from the center line of the input geometry on self._queryGeometry, using ESRI's measurement tool, the actual width of the polygon created is something like 370 feet on either side of the center line.
I've managed to get this to work more accurately using the Illinois State Plane spatial reference, as my test objects are in Illinois, but the logic needs to work everywhere.
When I try various incarnations of doing a geodesic buffer, the input distance unit seems to get ignored and, using an input distance value of 500, I get a buffer that spans the entire world! Either that or the results are exactly the same, depending on how things are set up.
I believe I need to do a geodesic buffer, but I have absolutely no idea how to go about that in such a way that the geometry service will actually pay attention to the units I'm sending in.
Any ideas would be greatly appreciated. Let me know if I've left anything out.
Sounds like there might be a spatial reference issue somewhere. You can try re-projecting geometry into 3857 if that's what the map is and I would inspect the geometry being returned from the buffer and simplify to make sure it looks like what your expecting. I have had issues with the geometry service area's and length's returning slightly incorrect geometries and in my case it ended up being an issue with an incorrect spatial reference. Also, I know you said you can't upgrade, but 3.13 is out and can do geometry options locally without the need for a proxy or any network requests, if possible, it would be worth trying out.
If I have stored in my DB several latlng points, and I want to compare those points with my actual latlng position( giving me the distance between each latlng points with my actual latlng), how would that be posible with google maps API? or it's something that it would be easier using my database?
Computing the distance between two points on a sphere requires the use of the haversine formula, which requires a pretty solid understanding of trigonometry.
The easier way would be to leverage the Google Maps API which has the handy function computeDistanceBetween in the google.maps.geometry.spherical namespace.
Here's some sample code for using computeDistanceBetween:
var home = ['London', new google.maps.LatLng(51.5, -0.1167)];
var pts = [
['Prague', new google.maps.LatLng(50.08, 14.43)],
['Paris', new google.maps.LatLng(48.856614, 2.3522219000000177)],
['Berlin', new google.maps.LatLng(52.5200065999, 13.404953999999975)]
];
// provide a shortcut for the distance function
var dist = google.maps.geometry.spherical.computeDistanceBetween;
pts.forEach(function(pt){
var d = dist(home[1], pt[1])/1000;
// d is now the distance (in km) between the home coordinate and the point
});
See working example here: http://jsfiddle.net/aJTK2/5/
If you intend to use your database for this sort of work, you might want to think about using PostGIS for this. With PostGIS installed:
CREATE EXTENSION postgis;
SELECT ST_Distance(
ST_GeographyFromText('POINT(115.764286 -31.746416)'),
ST_GeographyFromText('POINT(151.036606 -33.906896)')
);
Produces:
st_distance
------------------
3295294.42439749
(1 row)
Compared to Google Maps output, which thinks it's about 3700 km (walking, not crow-flies).
So that seems about right, distance wise.
Note that this is spheroid distance, i.e over the earth's surface, not point-to-point through it.
Watch out for the co-ordinate order in PostGIS vs Google Maps.
To learn more about PostGIS:
Introduction to PostGIS
Introduction to PostGIS - Geography
PostGIS.net
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.