Skip to content

Commit

Permalink
core: fix linestring interpolation
Browse files Browse the repository at this point in the history
The previous code sometimes failed when two linestring points share the same coordinates.
I decided on replacing the previous algorithm by a tried and tested approach instead of working out its kinks.
  • Loading branch information
multun committed Dec 5, 2023
1 parent e62a553 commit e851720
Showing 1 changed file with 33 additions and 20 deletions.
53 changes: 33 additions & 20 deletions core/osrd-geom/src/main/java/fr/sncf/osrd/geom/LineString.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,31 +147,44 @@ public static LineString concatenate(List<LineString> lineStringList) {
private Point interpolate(double distance) {
assert distance >= 0.;
assert distance <= cumulativeLengths[cumulativeLengths.length - 1];
var interval = Arrays.binarySearch(cumulativeLengths, distance);

// binarySearch returns a negative position if it doesn't find the element
if (interval < 0)
interval = - interval - 1;
// if we're at the first point
if (distance == 0.)
return new Point(bufferX[0], bufferY[0]);

var x1 = bufferX[interval];
var y1 = bufferY[interval];
var x2 = bufferX[interval + 1];
var y2 = bufferY[interval + 1];
var intervalIndex = Arrays.binarySearch(cumulativeLengths, distance);

double ratio = distance / cumulativeLengths[0];
// if we're exactly on any other point
if (intervalIndex >= 0)
return new Point(bufferX[intervalIndex + 1], bufferY[intervalIndex + 1]);

if (cumulativeLengths[0] == 0) {
// Avoids NaNs
assert distance == 0;
ratio = 0;
}
// if we're in-between points
intervalIndex = - intervalIndex - 1;

if (interval > 0) {
ratio = (distance - cumulativeLengths[interval - 1])
/ (cumulativeLengths[interval] - cumulativeLengths[interval - 1]);
}
// A -- P ---- B
double startToA = intervalIndex > 0 ? cumulativeLengths[intervalIndex - 1] : 0;
double startToB = cumulativeLengths[intervalIndex];
double ab = startToB - startToA;
double ap = distance - startToA;
assert !Double.isNaN(ap);
double ratio = ap / ab;

var aX = bufferX[intervalIndex];
var aY = bufferY[intervalIndex];

// if ratio is undefined, A and B are the same point
if (Double.isNaN(ratio))
return new Point(aX, aY);

// clamp the linear interpolation ratio
if (ratio < 0.)
ratio = 0.;
if (ratio > 1.)
ratio = 1.;

return new Point(x1 + ratio * (x2 - x1), y1 + ratio * (y2 - y1));
var bX = bufferX[intervalIndex + 1];
var bY = bufferY[intervalIndex + 1];
return new Point(aX + ratio * (bX - aX), aY + ratio * (bY - aY));
}

/**
Expand All @@ -186,7 +199,7 @@ public Point interpolateNormalized(double distance) {
}

/**
* A troncate a LineString from begin to end
* Truncate a LineString from the provided begin and end offsets
* begin and end are distance on the LineString
* begin and end are between 0.0 and 1.0
*/
Expand Down

0 comments on commit e851720

Please sign in to comment.