0

I am using an implementation of the Ramer Douglas Peucker algorithm to reduce the amount of points that I have for a map route. For example, if I have more than 500 points, I want to run the algorithm with a tolerance that will reduce the point count to less than 500 while being as close to it as possible. What I have tried so far, which is incredibly inefficient is the following:

simp = coordSimplify(data.tableData, 0)
while (simp.length > 400) {
    i += 0.0001;
    simp = coordSimplify(data.tableData, i);
}

But I realise that this will slow the whole process down hugely.

How can I make this whole process more efficient? I was thinking some sort of binary chop algorithm, but then I'm not sure how I'd calculate the top and bottom bounds each time.

TIA

Luke B
  • 288
  • 2
  • 5
  • 18
  • Have you looked at people's implementations on github? – epascarello Feb 12 '21 at 18:10
  • A little bit here and there - why? – Luke B Feb 13 '21 at 07:24
  • `github` is one of the very best programming resources ever invented. I once found a complete(!) "trouble ticket system" there which a company had worked on for more than a year ... finished and perfected it ... then gave it away on github! You should look *very thoroughly* for "prior art" that is similar to what you have in mind, because *"somebody* out there has already done it" and they probably put it on github or sourceforge. ***Actum Ne Agas:*** **Do Not Do A Thing Already Done.™** – Mike Robinson Feb 14 '21 at 17:39
  • 1
    I know how to use Github. – Luke B Feb 15 '21 at 17:05

1 Answers1

1

Suggest trying something along the following lines, which is effectively a binary search seeking the epsilon value (using the terminology at https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) that falls within the target point length range of simpTargetLo and simpTargetHi.

(Note that I have not tested this, as I don't have access to coordSimplify(), so there might be a few syntax errors, but the logic should be sound.)

// Set the acceptable result.
let simpTargetLo = 490;
let simpTargetHi = 510;

// Set the initial epsilon range which needs to be outside the estimated solution.
let epsilonLo = 0;
let epsilonHi = 1;
let epsilonMid;

// Calculate the initial low and high simp values.
let simpLo = coordSimplify(data.tableData, epsilonLo);
let simpHi = coordSimplify(data.tableData, epsilonHi);
let simpMid;

// Ensure that the initial simp values fall outside the target range.
if ( !( simpLo.length <= simpTargetLo && simpTargetHi <= simpHi.length ) ) {
  throw new Error( `Initial epsilon need expanding.\n  epsilonLo ${epsilonLo} returns ${simpLo.length}\n  epsilonHi ${epsilonHi} returns ${simpHi.length}` );
}

// Finally, let's ensure we don't get into an infinite loop in the event that
// their is no solution or the solution oscillates outside the target range.
let iterations = 0;
let maxIterations = 100;

do {
  
  // Calculate the simp at the midpoint of the low and high epsilon.
  epsilonMid = ( epsilonLo + epsilonHi ) / 2;
  simpMid = coordSimplify(data.tableData, epsilonMid );
  
  // Narrow the epsilon low and high range if the simp result is still outside
  // both the target low and high.
  if ( simpMid.length < simpTargetLo ) {
    epsilonLo = epsilonMid;
  } else if ( simpTargetHi < simpMid.length ) {
    epsilonHi = epsilonMid;
  } else {
    // Otherwise, we have a solution!
    break;
  }
  
  iterations++;
  
while( iterations < maxIterations );

if ( iterations < maxIterations ) {
  console.log( `epsilon ${epsilonMid} returns ${simpMid.length}` );
} else {
  console.log( `Unable to find solution.` );
}

Note that this method of narrowing to a solution depends on the proper choice of initial epsilonLo and epsilonHi, in addition to assuming that coordSimplify() is fairly continuous in nature...

Trentium
  • 3,419
  • 2
  • 12
  • 19