turf icon indicating copy to clipboard operation
turf copied to clipboard

pointToLineDistance strange bahavior

Open alqird opened this issue 6 years ago • 6 comments

Hi, I've just got strange results from function pointToLineDistance when compared to nearestPointOnLine and to what should be expected. Short explanation code:

let lineCoords = [
	[ 10.964832305908203, 41.004681939880314 ],
	[ 10.977363586425781, 40.99096148527727 ],
	[ 10.983200073242188, 40.97075154073346 ],
	[ 11.02834701538086, 40.98372150040732 ],
	[ 11.02508544921875, 41.00716631272605 ],
	[ 10.994186401367188, 41.01947819666632 ],
	[ 10.964832305908203, 41.004681939880314 ]
];

let line = turf.lineString(lineCoords);

let x0 = 11.02834;
let x1 = 11.02835;
let dx = 0.000001;
for (let i = 0, x = x0; x <= x1; i++, x = x0 + i * dx) {
	let p = turf.point([ x, 41.0 ]);
	let d = turf.pointToLineDistance(p, line, {units : 'meters'});
	let pm = turf.nearestPointOnLine(line, p, {units : 'meters'});
	console.log(turf.getCoord(p), d, pm.properties.dist);
}

The result is: [ 11.02834, 41 ] 188.40111660891222 188.42219043739743 [ 11.028341, 41 ] 188.4845779466462 188.50565183748427 [ 11.028342, 41 ] 188.5680392846129 188.58911323788854 [ 11.028343, 41 ] 188.65150062254605 188.67257463823046 [ 11.028344, 41 ] 188.73496196071352 188.75603603901936 [ 11.028345, 41 ] 188.8184232988473 188.83949743959744 [ 11.028346, 41 ] 188.9018846372153 188.92295884065962 [ 11.028347, 41 ] 188.9853459755496 189.00642024172396 [ 11.028348, 41 ] 842.5784253401666 189.08988164279026 [ 11.028349, 41 ] 842.6056956528126 189.17334304432177 [ 11.02835, 41 ] 842.6329734397548 189.2568044457904

There is a threshold longitude for which pointToLineDistance becomes invalid. Any suggestions? Thanks

alqird avatar Dec 06 '17 15:12 alqird

🤔 ... strange

Maybe the input is being mutated by the TurfJS modules, lets try to add this as a test case.

👍

DenisCarriere avatar Dec 06 '17 18:12 DenisCarriere

Have had a quick look into this, it's nothing to do with mutation as even without the loop the result is incorrect, see https://runkit.com/rowanwins/5a84b60f0a657c00122a6e07

There is something funky going on, for some reason it doesn't seem to be detecting it's nearest segment, from what I can tell in this instance it's returning the distance to the nearest line vertice...

rowanwins avatar Feb 14 '18 22:02 rowanwins

FYI have made good headway on refactoring this module to use the algorithm here, it's source code is way simpler and is producing the correct result.

initial benchmark

\\ new implementation
issue-1156 x 750,288 ops/sec ±1.15% (85 runs sampled)

\\ old implementation
issue-1156 x 200,858 ops/sec ±2.38% (82 runs sampled)

The only issue is that the new implementation doesn't provide a distinction between mercator and WGS, I need to dig into the original source code a bit more to see what that was impacting, I might be able to adapt what I've got.

Stay tuned

rowanwins avatar Feb 15 '18 06:02 rowanwins

This issue described above has been observed in pointToLineDistance involving a single point and linestrings made of only two points per line. I discovered it when checking distance to a group of parallel lines, observing the error to be introduced at random distances from the point.

I have not seen the issue in bypassing pointToLineDistance in the following way:

// start with a point, a line, and the units per the input to pointToLineDistance
var line = // a geoJSON FeatureSet of parallel lines
var point = // an array of [lon, lat]
var unitString = 'meters' or 'feet'

// the workaround for lines that are straight...
// extract coordinates from the line
var linePoints = turf.coordAll(line);

// make a triangle of three segments of unit length using 
// our 'point' and the start and end of the line
  // segment from point to line head
var segA = turf.length(turf.lineString([point,linePoints[0]]),{units: unitString});
  // segment from point to line tail
var segB = turf.length(turf.lineString([point,linePoints.pop()]),{units: unitString});
  // segment from line head to line tail
var segC = turf.length(feature,{units: unitString});

// determine the angle between the segA and segC
var angle = Math.acos((Math.pow(segB,2)+Math.pow(segC,2)-Math.pow(segA,2))/(2*segB*segC));

// use that angle to create a right triangle, the base of 
// which is the distance from the point to the line
distance = Math.sin(angle)*segB;

bbattista avatar Jun 29 '19 23:06 bbattista

I am experiencing the same behaviour. It only happens for specific configurations and for now I solved the problem by computing the distance to line AB and the reverse line BA and taking the minimum distance as I found so far that using either AB or BA gives the correct result. When the computation fails, it returns the distance to the first point of the line. This suggests that something goes wrong with the computation or comparison of the first dot product.

pt1 = turf.point([5.4288,50.9022])
{type: "Feature", properties: {…}, geometry: {…}}
pt2 = turf.point([5.4292,50.9056])
{type: "Feature", properties: {…}, geometry: {…}}
P = turf.point([5.4287,50.9046])
{type: "Feature", properties: {…}, geometry: {…}}
line1 = turf.lineString([pt1.geometry.coordinates,pt2.geometry.coordinates],{name:'line'})
{type: "Feature", properties: {…}, geometry: {…}}
turf.pointToLineDistance(P,line1,{units:'kilometres'})
0.26696030500490275
line2 = turf.lineString([pt2.geometry.coordinates,pt1.geometry.coordinates],{name:'line'})
{type: "Feature", properties: {…}, geometry: {…}}
turf.pointToLineDistance(P,line2,{units:'kilometres'})
0.02673710899765041

RoelHelsen avatar Mar 18 '20 13:03 RoelHelsen

Hi all. Is there any news about this issue?

finvernizzi avatar Sep 19 '20 05:09 finvernizzi