5

The code can be viewed at

http://jsfiddle.net/qsr5bs6v/

Following are the lines to add a polyline

L.polyline([[31.233, 121.465], [31.233499, 121.500634], [31.190172, 121.588107]], {
    color: '#000',
    smoothFactor: 10.0
}).addTo(map)

As can be seen, there is an angle in the joint point of every two lines belonging to the polyline, like this, which is not so attractive:

enter image description here

I was wondering whether there is an easy way to make the angle into a rounded curve in Mapbox..

(I saw this post about smoothing a polyline Smooth polyline with minimal deformation . In that post, I saw CHAIKIN'S ALGORITHMS is suggested but the drawback of that algorithm is that the smoothed curve doesn't pass directly through the control points... )

Community
  • 1
  • 1
Hanfei Sun
  • 45,281
  • 39
  • 129
  • 237
  • 2
    I removed your curve and graphics tag because they won't help much, added the Leaflet tag. Mapbox is an extended version of Leaflet with a bunch of extras. So you might want your adjust your search endeavours also – iH8 Mar 16 '15 at 11:05
  • In Leaflet (and so Mapbox) a polyline is basically a Path with some extras: a first thing to have a look at I'd say is the (`lineJoin` option)[http://leafletjs.com/reference.html#path-linejoin]. – MarcoL Mar 16 '15 at 11:58

3 Answers3

8

You can use turf-bezier to create an interpolated bezier line out of any LineString geometry.

tmcw
  • 11,536
  • 3
  • 36
  • 45
  • 6
    the link is no longer valid and `turf-bezier` module has been renamed to [@turf/bezier-spline](https://www.npmjs.com/package/@turf/bezier-spline) – Vadim Gremyachev Nov 28 '18 at 15:36
8

In my case, linejoin option was unnoticeable and bezier curves alter path too much. Inspired by this solution, I've created custom points-To-Path method for Leaflet to smooth path corners in L.polyline. I am sure this can be easily adapted to Mapbox.

Note: this method was tested only with polylines and does not expect closed path.

example: https://jsfiddle.net/v51amucr/

Result:

example smooth polyline

function roundPathCorners(rings, radius) {
  function moveTowardsFractional(movingPoint, targetPoint, fraction) {
    return {
      x: movingPoint.x + (targetPoint.x - movingPoint.x) * fraction,
      y: movingPoint.y + (targetPoint.y - movingPoint.y) * fraction
    };
  }

  function pointForCommand(cmd) {
    return {
      x: parseFloat(cmd[cmd.length - 2]),
      y: parseFloat(cmd[cmd.length - 1])
    };
  }

  var resultCommands = [];
  if (+radius) {
  // negative numbers create artifacts
    radius = Math.abs(radius);
  } else {
    radius = 0.15;
  }

  for (i = 0, len = rings.length; i < len; i++) {
    commands = rings[i];
    // start point    
    resultCommands.push(["M", commands[0].x, commands[0].y]);

    for (var cmdIndex = 1; cmdIndex < commands.length; cmdIndex++) {
      var prevCmd = resultCommands[resultCommands.length - 1];
      var curCmd = commands[cmdIndex];
      var nextCmd = commands[cmdIndex + 1];

      if (nextCmd && prevCmd) {
        // Calc the points we're dealing with
        var prevPoint = pointForCommand(prevCmd); // convert to Object
        var curPoint = curCmd;
        var nextPoint = nextCmd;

        // The start and end of the cuve are just our point moved towards the previous and next points, respectivly
        var curveStart, curveEnd;

        curveStart = moveTowardsFractional(
          curPoint,
          prevCmd.origPoint || prevPoint,
          radius
        );
        curveEnd = moveTowardsFractional(
          curPoint,
          nextCmd.origPoint || nextPoint,
          radius
        );

        // Adjust the current command and add it
        curCmd = Object.values(curveStart);

        curCmd.origPoint = curPoint;
        curCmd.unshift("L");
        resultCommands.push(curCmd);

        // The curve control points are halfway between the start/end of the curve and
        // calculate curve, if radius is different than 0
        if (radius) {
          var startControl = moveTowardsFractional(curveStart, curPoint, 0.5);
          var endControl = moveTowardsFractional(curPoint, curveEnd, 0.5);
          // Create the curve
          var curveCmd = [
            "C",
            startControl.x,
            startControl.y,
            endControl.x,
            endControl.y,
            curveEnd.x,
            curveEnd.y
          ];
          // Save the original point for fractional calculations
          curveCmd.origPoint = curPoint;
          resultCommands.push(curveCmd);
        }
      } else {
        // Pass through commands that don't qualify
        var el = Object.values(curCmd);
        el.unshift("L");
        resultCommands.push(el);
      }
    }
  }

  return (
    resultCommands.reduce(function(str, c) {
      return str + c.join(" ") + " ";
    }, "") || "M0 0"
  );
};
Lynx
  • 271
  • 4
  • 7
0

You should get a string geometry that can be converted to an array of coordinates

function decode(str) {
var flag = true;
setTimeout(() => { flag = false; return []; }, 3000);
var index = 0,
  lat = 0,
  lng = 0,
  coordinates = [],
  shift = 0,
  result = 0,
  byte = null,
  latitude_change,
  longitude_change,
  factor = Math.pow(10, 6);
while (flag && index < str.length) {
  byte = null;
  shift = 0;
  result = 0;
  do {
    byte = str.charCodeAt(index++) - 63;
    result |= (byte & 0x1f) << shift;
    shift += 5;
  } while (flag && byte >= 0x20);
  latitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1));
  shift = result = 0;
  do {
    byte = str.charCodeAt(index++) - 63;
    result |= (byte & 0x1f) << shift;
    shift += 5;
  } while (flag && byte >= 0x20);
  longitude_change = ((result & 1) ? ~(result >> 1) : (result >> 1));
  lat += latitude_change;
  lng += longitude_change;
  coordinates.push([lat / factor, lng / factor]);
}
return coordinates;

}