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.
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:
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
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:
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.
I hope this is of interest to someone out there.