Fitting Bezier Polygon to Points

A colleague asked if I could implement the fitting of a 2D linestring to a set of points using a bezier approximation returning a polygon.

The original implementation is Smooth a 2D polyline through a set of points by interpolation (Catmull-Rom) or approximation (Chaikin)..

First off, we do a Chaikin smoothing (method 1) of a set of points using 3 iterations.

WITH data AS (
SELECT 1 as method,
       3 as iterations,
	   geometry::STGeomFromText('MULTIPOINT((3 0.5),(2 1),(3 2),(3 3),(4 2.5),(4.8 3.7),(3 5.5),(6 8),(7 9.5),(8.3 5.1),(6.5 4.2),(9 3),(9 2),(8 2),(7 3),(3 0.5))',0) as geom
)
SELECT 'Chaikin' as Method, f.bGeom as geom
  FROM (SELECT cast(method as varchar(2)) as Method, 
               [dbo].[STBezier](d.geom,0.5,d.iterations,d.method) as bGeom
          FROM data as d
		) as f
UNION ALL
SELECT cast(p.uid as varchar(7)) as method, p.point.STBuffer(0.2) as geom
  FROM data as d
       cross apply
	   [dbo].[STDumpPoints](d.geom) as p
GO

This looks like the following. Note that the polygon does not pass through the individual points.

Chaikin (3) Fitted Polygon

To make the polygon exterior ring pass through the supplied points, we can move to a Catmull-Rom curve fitting (method 2). In the following we use a minimal amount of iterations (2) which will result in a less smooth curve fitting.

WITH data AS (
SELECT 2 as method,
       2 as iterations,
	   geometry::STGeomFromText('MULTIPOINT((3 0.5),(2 1),(3 2),(3 3),(4 2.5),(4.8 3.7),(3 5.5),(6 8),(7 9.5),(8.3 5.1),(6.5 4.2),(9 3),(9 2),(8 2),(7 3),(3 0.5))',0) as geom
)
SELECT 'Catmull-Rom' as Method, f.bGeom as geom
  FROM (SELECT cast(method as varchar(2)) as Method, 
               [dbo].[STBezier](d.geom,0.5,d.iterations,d.method) as bGeom
          FROM data as d
		) as f
UNION ALL
SELECT cast(p.uid as varchar(7)) as method, p.point.STBuffer(0.2) as geom
  FROM data as d
       cross apply
	   [dbo].[STDumpPoints](d.geom) as p
GO

This looks like the following:

Catmull-Rom (2) Fitted Polygon

Upping the number of iterations to, say 5, results in smoother exterior ring.

WITH data AS (
SELECT 2 as method,
       5 as iterations,
	   geometry::STGeomFromText('MULTIPOINT((3 0.5),(2 1),(3 2),(3 3),(4 2.5),(4.8 3.7),(3 5.5),(6 8),(7 9.5),(8.3 5.1),(6.5 4.2),(9 3),(9 2),(8 2),(7 3),(3 0.5))',0) as geom
)
SELECT 'Catmull-Rom' as Method, f.bGeom as geom
  FROM (SELECT cast(method as varchar(2)) as Method, 
               [dbo].[STBezier](d.geom,0.5,d.iterations,d.method) as bGeom
          FROM data as d
		) as f
UNION ALL
SELECT cast(p.uid as varchar(7)) as method, p.point.STBuffer(0.2) as geom
  FROM data as d
       cross apply
	   [dbo].[STDumpPoints](d.geom) as p
GO

Catmull-Rom (5) Fitted Polygon

The following example, uses data in an actual projected space, SRID 27700 (OSGB36 / British National Grid — United Kingdom Ordnance Survey). In particular we will look at the role of, or need for, a closing point when processing the data using a Chaikin smoothing:

First, smoothing using a closing point.

WITH data AS (
  SELECT geometry::STGeomFromText(
  'MULTIPOINT(
  (534239.6309 258830.8997),
  (534232.6309 258836.8997),
  (534224.6309 258830.8997),
  (534232.6309 258826.8997),
  (534239.6309 258830.8997))',27700) as geom
)
SELECT 'Chaikin' as method, f.bGeom as geom
  FROM (SELECT [dbo].[STBezier](d.geom,0.5,3,1) as bGeom
          FROM data as d
        ) as f
UNION ALL
SELECT cast(p.uid as varchar) as method, p.point.STBuffer(0.5) as geom
  FROM data as d
       cross apply
	   [dbo].[STDumpPoints](d.geom) as p
GO

This looks like this:

Chaikin (Closing Point) Fitted Polygon

Now, without the last point.

WITH data AS (
  SELECT geometry::STGeomFromText(
  'MULTIPOINT(
  (534239.6309 258830.8997),
  (534232.6309 258836.8997),
  (534224.6309 258830.8997),
  (534232.6309 258826.8997),
  (534239.6309 258830.8997))',27700) as geom
)
SELECT 'Chaikin' as method, f.bGeom as geom
  FROM (SELECT [dbo].[STBezier](d.geom,0.5,3,1) as bGeom
          FROM data as d
        ) as f
UNION ALL
SELECT cast(p.uid as varchar) as method, p.point.STBuffer(0.5) as geom
  FROM data as d
       cross apply
	   [dbo].[STDumpPoints](d.geom) as p
GO

Which looks like this.

Chaikin (No Closing Point) Fitted Polygon

I hope this is of interest to someone out there.