I am publishing my answer for those who visit this page hoping to find an answer to the problem you have formulated!
So, I invite everyone to fly with me in a plane with the engine I have assembled with the name: "Free and Magic geoSQL", in order to admire the beautiful mountain scenery of the Cordillera mountain system and from a height of flight to see their centerlines :-)! We have a difficult and interesting task ahead of us, but we can do it even without SFCGAL
library and ST_ApproximateMedialAxis
and ST_StraightSkeleton
functions, because we are pilots and navigators of high class :-), I strongly hope so! I command: "Fasten your seatbelts", "From the screw", "Fly"! and of course I start the engine, which works so far for only one polygon:
WITH
tbla AS (SELECT (ST_Dump(geom)).geom FROM <name_polygon_table>),
tblb AS (SELECT (ST_DumpPoints(geom)).geom FROM tbla),
tblc AS (SELECT ((ST_Dump(ST_VoronoiPolygons(ST_Collect(geom)))).geom) geom FROM tblb),
tbld AS (SELECT (ST_Dump(ST_Intersection(a.geom, b.geom))).geom FROM tblc a JOIN tbla b ON ST_Intersects(a.geom, b.geom)),
tble AS (SELECT line1, line2, CASE WHEN line1>line2 THEN line1 WHEN line1<=line2 THEN line2 END FROM (SELECT ST_MakeLine((ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),1)),
(ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),2))) AS line1, ST_MakeLine((ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),2)), (ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),3))) AS line2 FROM tbla) AS foo),
tblf AS (SELECT (ST_Buffer(line2, ST_Length(line2), 'endcap=flat join=round')) geom FROM tble),
tblg AS (SELECT DISTINCT (ST_Dump(ST_Difference(a.geom, b.geom))).geom FROM tblf a JOIN tbla b ON ST_Intersects(a.geom, b.geom)),
tblh AS (SELECT (ST_Dump(ST_Buffer(geom, -0.000001))).geom geom FROM tblg),
tbli AS (SELECT (ST_Buffer(geom, 0.000002)) geom FROM tblh a JOIN tble b ON ST_Intersects(a.geom, b.line2)),
tblj AS (SELECT ST_ExteriorRing(ST_Union(a.geom)) geom FROM tbld a JOIN tbli b ON ST_Intersects(a.geom, b.geom)),
tblk AS (SELECT ST_MakeLine(pt1, pt2) geom FROM (SELECT ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) pt1, ST_PointN(geom, generate_series(2, ST_NPoints(geom))) pt2 FROM tblj) AS geom),
tbll AS (SELECT (ST_Dump(ST_Intersection(ST_Buffer(a.geom, -0.0001), b.geom))).geom FROM tbla a JOIN tblk b ON ST_Contains(a.geom, b.geom))
SELECT ST_Union(geom) geom FROM tbll
(If necessary, adjust it to the size of your polygons (function ST_Buffer
), I played with data in EPSG:4326).
From now on, everyone will be able to see and create the centerline of the polygon.
Original solutions.
The script is called: ST_LongestAxisPolygonsFromVoronoiDiagrams
.
(!Not to use for simple shapes like rectangle!)
"I want to thank all those who contribute to the creation and development of promising geoinstruments by sharing their ideas and codes..."
First editing:
CREATE OR REPLACE FUNCTION ST_LongestAxisPolygonsFromVoronoiDiagrams(
geom GEOMETRY
)
RETURNS GEOMETRY AS
$BODY$
WITH
tbla AS (SELECT (ST_Dump(geom)).geom),
tblb AS (SELECT (ST_DumpPoints(geom)).geom FROM tbla),
tblc AS (SELECT ((ST_Dump(ST_VoronoiPolygons(ST_Collect(geom)))).geom) geom FROM tblb),
tbld AS (SELECT (ST_Dump(ST_Intersection(a.geom, b.geom))).geom FROM tblc a JOIN tbla b ON ST_Intersects(a.geom, b.geom)),
tble AS (SELECT line1, line2, CASE WHEN line1>line2 THEN line1 WHEN line1<=line2 THEN line2 END FROM (SELECT ST_MakeLine((ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),1)),
(ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),2))) AS line1, ST_MakeLine((ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),2)), (ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),3))) AS line2 FROM tbla) AS foo),
tblf AS (SELECT (ST_Buffer(line2, ST_Length(line2), 'endcap=flat join=round')) geom FROM tble),
tblg AS (SELECT DISTINCT (ST_Dump(ST_Difference(a.geom, b.geom))).geom FROM tblf a JOIN tbla b ON ST_Intersects(a.geom, b.geom)),
tblh AS (SELECT (ST_Dump(ST_Buffer(geom, -0.000001))).geom geom FROM tblg),
tbli AS (SELECT (ST_Buffer(geom, 0.000002)) geom FROM tblh a JOIN tble b ON ST_Intersects(a.geom, b.line2)),
tblj AS (SELECT ST_ExteriorRing(ST_Union(a.geom)) geom FROM tbld a JOIN tbli b ON ST_Intersects(a.geom, b.geom)),
tblk AS (SELECT ST_MakeLine(pt1, pt2) geom FROM (SELECT ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) pt1, ST_PointN(geom, generate_series(2, ST_NPoints(geom))) pt2 FROM tblj) AS geom),
tbll AS (SELECT (ST_Dump(ST_Intersection(ST_Buffer(a.geom, -0.0001), b.geom))).geom FROM tbla a JOIN tblk b ON ST_Contains(a.geom, b.geom))
SELECT ST_Union(geom) geom FROM tbll
$BODY$
LANGUAGE SQL;
RUN
SELECT ST_LongestAxisPolygonsFromVoronoiDiagrams(geom) geom FROM <source_table>
If necessary, make the line smoother, add the function generate_series...
Second edition 13.03.2020
CREATE OR REPLACE FUNCTION ST_LongestAxisPolygonsFromVoronoiDiagrams_gs(
geom GEOMETRY,
n integer)
RETURNS GEOMETRY AS
$BODY$
WITH
tbla AS (SELECT (ST_Dump(geom)).geom),
tblb AS (WITH btbl AS (SELECT (ST_ExteriorRing(((ST_Dump(geom)).geom))) geom),
intervals AS (SELECT generate_series (0, n) as steps)
SELECT steps AS stp, ST_LineInterpolatePoint(geom, steps/(SELECT count(steps)::float-1 FROM intervals)) geom FROM btbl, intervals GROUP BY intervals.steps, geom),
tblc AS (SELECT ((ST_Dump(ST_VoronoiPolygons(ST_Collect(geom)))).geom) geom FROM tblb),
tbld AS (SELECT (ST_Dump(ST_Intersection(a.geom, b.geom))).geom FROM tblc a JOIN tbla b ON ST_Intersects(a.geom, b.geom)),
tble AS (SELECT line1, line2 FROM (SELECT ST_MakeLine((ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),1)),
(ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),2))) AS line1, ST_MakeLine((ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),2)), (ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),3))) AS line2 FROM tbla) AS foo),
tblf AS (SELECT (ST_Buffer(line2, ST_Length(line2), 'endcap=flat join=round')) geom FROM tble),
tblg AS (SELECT DISTINCT (ST_Dump(ST_Difference(a.geom, b.geom))).geom FROM tblf a JOIN tbla b ON ST_Intersects(a.geom, b.geom)),
tblh AS (SELECT (ST_Dump(ST_Buffer(geom, -0.000001))).geom geom FROM tblg),
tbli AS (SELECT (ST_Buffer(geom, 0.000002)) geom FROM tblh a JOIN tble b ON ST_Intersects(a.geom, b.line2)),
tblj AS (SELECT ST_ExteriorRing(ST_Union(a.geom)) geom FROM tbld a JOIN tbli b ON ST_Intersects(a.geom, b.geom)),
tblk AS (SELECT ST_MakeLine(pt1, pt2) geom FROM (SELECT ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) pt1, ST_PointN(geom, generate_series(2, ST_NPoints(geom))) pt2 FROM tblj) AS geom),
tbll AS (SELECT (ST_Dump(ST_Intersection(ST_Buffer(a.geom, -0.0001), b.geom))).geom FROM tbla a JOIN tblk b ON ST_Contains(a.geom, b.geom))
SELECT ST_MakeLine(ST_Centroid(geom)) geom FROM tbll
$BODY$
LANGUAGE SQL;
RUN
SELECT ST_LongestAxisPolygonsFromVoronoiDiagrams_gs(geom, 300) geom FROM <source_table>
Third edition 30.03.2020
As an option, for 2 or more polygons run the following script:
SELECT DISTINCT b.geom
FROM <source_table> AS a
JOIN LATERAL (SELECT ST_LongestAxisPolygonsFromVoronoiDiagrams_gs(geom) AS geom
FROM <source_table>) AS b ON true;
Fourth edition 13.05.2021
CREATE OR REPLACE FUNCTION ST_CentraLAxisLongestLineFromPolygons_VD(
geom GEOMETRY,
n integer)
RETURNS GEOMETRY AS
$BODY$
WITH
tbla AS (SELECT (ST_Dump(geom)).geom geom),
tblb AS (SELECT ST_Buffer(ST_Boundary(ST_OrientedEnvelope(geom)),0.00001) geom FROM tbla),
tblc AS (SELECT (ST_Dump(ST_Difference(ST_Boundary(a.geom), b.geom))).geom geom FROM tbla a JOIN tblb b ON true),
tbld AS (SELECT (ST_Dump(ST_Difference(ST_Buffer(ST_OrientedEnvelope(a.geom), -0.00001), b.geom))).geom geom FROM tbla a JOIN tbla b ON true),
tble AS (SELECT ST_Buffer(geom, 0.00001) geom FROM (SELECT ST_MakeLine((ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),1)),
(ST_PointN(ST_Boundary(ST_OrientedEnvelope(geom)),4))) AS geom FROM tbla) AS foo),
tblf AS (SELECT generate_series (0, n) as steps),
tblg AS (SELECT steps AS stp, ST_LineInterpolatePoint(geom, steps/(SELECT count(steps)::float-1 FROM tblf)) geom FROM tblc, tblf GROUP BY tblf.steps, geom),
tblh AS (SELECT ((ST_Dump(ST_VoronoiPolygons(ST_Collect(geom)))).geom) geom FROM tblg),
tbli AS (SELECT (a.geom) geom FROM tbld a JOIN tble b ON ST_Intersects(a.geom, ST_Buffer(b.geom, 0.00001))),
tblj AS (SELECT ST_ExteriorRing(ST_Union(a.geom)) geom FROM tblh a JOIN tbli b ON ST_Intersects(a.geom, b.geom))
SELECT ST_Intersection(a.geom, b.geom) geom FROM tblj a JOIN tbla b ON ST_Intersects(a.geom, b.geom);
$BODY$
LANGUAGE SQL;
Run
SELECT ST_CentraLAxisLongestLineFromPolygons_VD(geom, 100) AS geom FROM <source_table>
ST_StraightSkeleton
)...but that can be a massive pain...