Re: Coordinate transformations with pipelines
Antonio Valanzano <[email protected]> Thu, 26 Feb 2026 11:28:21 +0100
| Newsgroups | gmane.comp.gis.postgis |
|---|---|
| Message-ID | <CANV1FQayqwn37+wP18yjpFiZ_77ivQ+MetA+bfZX_w_YLitHBw@mail.gmail.com> |
--0000000000002f1097064bb79516
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Dear Florian,
thanks for your answer.
I have already used projinfo and I have discovered that there 3
possible transformations as you can see from the following output
---------------------------------------------------------------------------=
------------------------
C:\>projinfo -s EPSG:23033 -t EPSG:25833 -o proj --spatial-test intersects
Candidate operations found: 3
-------------------------------------
Operation No. 1:
unknown id, Inverse of UTM zone 33N + ED50 to ETRS89 (1) + UTM zone 33N, 1
m, Norway - offshore north of 65=C2=B0N. Also Svalbard.
PROJ string:
+proj=3Dpipeline
+step +inv +proj=3Dutm +zone=3D33 +ellps=3Dintl
+step +proj=3Dpush +v_3
+step +proj=3Dcart +ellps=3Dintl
+step +proj=3Dhelmert +x=3D-116.641 +y=3D-56.931 +z=3D-110.559
+rx=3D0.892507816631186
+ry=3D0.920766095087038 +rz=3D-0.916640798962096 +s=3D-3.52
+convention=3Dposition_vector
+step +inv +proj=3Dcart +ellps=3DGRS80
+step +proj=3Dpop +v_3
+step +proj=3Dutm +zone=3D33 +ellps=3DGRS80
-------------------------------------
Operation No. 2:
unknown id, Inverse of UTM zone 33N + ED50 to ETRS89 (4) + UTM zone 33N, 1
m, Denmark - onshore.
PROJ string:
+proj=3Dpipeline
+step +inv +proj=3Dutm +zone=3D33 +ellps=3Dintl
+step +proj=3Dpush +v_3
+step +proj=3Dcart +ellps=3Dintl
+step +proj=3Dhelmert +x=3D-81.1 +y=3D-89.4 +z=3D-115.8 +rx=3D0.485 +ry=
=3D0.024
+rz=3D0.413
+s=3D-0.54 +convention=3Dposition_vector
+step +inv +proj=3Dcart +ellps=3DGRS80
+step +proj=3Dpop +v_3
+step +proj=3Dutm +zone=3D33 +ellps=3DGRS80
-------------------------------------
Operation No. 3:
unknown id, Inverse of UTM zone 33N + Ballpark geographic offset from ED50
to ETRS89 + UTM zone 33N, unknown accuracy, World, has ballpark
transformation
PROJ string:
+proj=3Dpipeline
+step +inv +proj=3Dutm +zone=3D33 +ellps=3Dintl
+step +proj=3Dutm +zone=3D33 +ellps=3DGRS80
C:\>
---------------------------------------------------------------------------=
-------------------------
PostGIS in my case has used the last one (the less accurate transformation
- "ballpark transformation")
This is the default behaviour in this case as you can see also looking at
the output of cs2cs
C:\>cs2cs EPSG:23033 EPSG:25833
541000 4516000
540998.14 4515921.57 0.00
The result is coincident with the output of my sql script
540998.1383329581 4515921.565374701
My question was about the criteria used by the Proj.6 lib for choosing
between several possible transformations and also to know the
transformation pipeline code used.
Searching on the net I have found that the same results (the approximate
ones) can be obtained using the function ST_TransformPipeline () a variant
of ST_Transform and specifying the EPSG code the transformation
-- I have first transformed the cartografic coordinates 541000 4516000 fro=
m
EPSG:23033 to geographic coordinates with EPSG:4230
WITH coord_4258 AS
(
SELECT
ST_X(ST_TransformPipeline('SRID=3D4230;POINT(15.48596772786105
40.79326538046647)'::geometry,'urn:ogc:def:coordinateOperation:EPSG::1611')=
)
as x_4258,
ST_Y(ST_TransformPipeline('SRID=3D4230;POINT(15.48596772786105
40.79326538046647)'::geometry,'urn:ogc:def:coordinateOperation:EPSG::1611')=
)
as y_4258
)
SELECT
ST_AsEWKT(ST_Transform(ST_Point(x_4258,y_4258, 4258), 25833))
FROM coord_4258;
---------------
"st_asewkt"
"SRID=3D25833;POINT(540998.1383329581 4515921.565374701)"
.
In order to obtain better results I have used a specific EPSG
transformation code (1588)
WITH coord_4258 AS
(
SELECT
ST_X(ST_TransformPipeline('SRID=3D4230;POINT(15.48596772786105
40.79326538046647)'::geometry,'urn:ogc:def:coordinateOperation:EPSG::1588')=
)
as x_4258,
ST_Y(ST_TransformPipeline('SRID=3D4230;POINT(15.48596772786105
40.79326538046647)'::geometry,'urn:ogc:def:coordinateOperation:EPSG::1588')=
)
as y_4258
)
SELECT
ST_AsEWKT(ST_Transform(ST_Point(x_4258,y_4258, 4258), 25833))
FROM coord_4258;
----------------
"st_asewkt"
"SRID=3D25833;POINT(540931.3414974756 4515810.597825925)"
The most accurate results are those obtained using a NTv2 grid file
540932.206 4515807.111
and the results of the transformation with EPSG:1588 are better than those
of EPSG:1611 (used da PostGIS).
The questions are now:
1. why did Proj.6 lib choose the transformation with EPSG code 1611 rather
than the one with EPSG code 1588 ?
2. why there is no evidence in the output of ST_Transform or cs2cs of the
transformation code chosen for doing the transformation ?
I hope this long mail will help clarify my initial question.
Antonio
Il giorno gio 26 feb 2026 alle ore 10:29 Florian Nadler <
[email protected]> ha scritto:
> Try projinfo (https://proj.org/en/stable/apps/projinfo.html#examples)
> from the commandline of your PostGIS host:
>
> projinfo -s EPSG:23033 -t EPSG:25833
>
> Transformation can done via commandline as follows:
>
> cs2cs EPSG:23033 EPSG:25833 <<EOF
> 541000 4516000
> EOF
>
> Florian
>
> Am 24.02.2026 um 08:18 schrieb Antonio Valanzano:
> > I am usign PostGIS 3.5 with PostgreSQL 17.0
> >
> > Here are the details for my installation:
> >
> > "postgis_full_version"
> > "POSTGIS=3D""3.5.3 3.5.3"" [EXTENSION] PGSQL=3D""170""
> > GEOS=3D""3.13.1-CAPI-1.19.2"" PROJ=3D""8.2.1 NETWORK_ENABLED=3DOFF
> > URL_ENDPOINT=3Dhttps://cdn.proj.org
> >
> USER_WRITABLE_DIRECTORY=3DC:\Windows\ServiceProfiles\NetworkService\AppDa=
ta\Local/proj
>
> >
> > DATABASE_PATH=3DC:\Program
> > Files\PostgreSQL\17\share\contrib\postgis-3.5\proj\proj.db"" (compiled
> > against PROJ 8.2.1)
> > LIBXML=3D""2.12.5"" LIBJSON=3D""0.12"" LIBPROTOBUF=3D""1.2.1"" WAGYU=3D=
""0.5.0
> > (Internal)"" (core procs from ""3.5.2 3.5.2"" need upgrade)"
> >
> >
> > I have done the following coordinate transformation
> > from ED50 / UTM zone 33N (espg:23033)
> > to ETRS89 / UTM zone 33N (epsg:25833
> >
> > SELECT
> > ST_X(ST_Transform(ST_Point(541000,4516000,23033),25833)) as X,
> > ST_Y(ST_Transform(ST_Point(541000,4516000,23033),25833)) as Y;
> >
> > -- result
> > "x" "y"
> > 540998.1383329581 4515921.565374701
> >
> > I have read that the best way to utilize PROJ.6 capabilities is by
> > allowing the library to freely choose the best possible transformation
> > paths
> > between the origin and the destination CRSes.
> >
> > Does someone know how to find which specific transformation pipeline
> > has been chosen by the PROJ.6 library in this specific transformation ?
> >
> > Antonio
> >
> >
> --
> CYBERTEC PostgreSQL International GmbH
> R=C3=B6merstra=C3=9Fe 19, A-2752 W=C3=B6llersdorf
> Web: https://www.cybertec-postgresql.com
>
>
--0000000000002f1097064bb79516
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<div dir=3D"ltr"><div dir=3D"ltr"><div>Dear Florian,</div><div>thanks for y=
our answer.</div><div><br></div><div>I have already used projinfo and I hav=
e discovered that there 3 possible=C2=A0transformations as you can see from=
the following output</div><div><br></div><div>----------------------------=
-----------------------------------------------------------------------</di=
v><div>C:\>projinfo -s EPSG:23033 -t EPSG:25833 -o proj --spatial-test i=
ntersects<br>Candidate operations found: 3<br>-----------------------------=
--------<br>Operation No. 1:<br><br>unknown id, Inverse of UTM zone 33N + E=
D50 to ETRS89 (1) + UTM zone 33N, 1 m, Norway - offshore north of 65=C2=B0N=
. Also Svalbard.<br><br>PROJ string:<br>+proj=3Dpipeline<br>=C2=A0 +step +i=
nv +proj=3Dutm +zone=3D33 +ellps=3Dintl<br>=C2=A0 +step +proj=3Dpush +v_3<b=
r>=C2=A0 +step +proj=3Dcart +ellps=3Dintl<br>=C2=A0 +step +proj=3Dhelmert +=
x=3D-116.641 +y=3D-56.931 +z=3D-110.559 +rx=3D0.892507816631186<br>=C2=A0 =
=C2=A0 =C2=A0 =C2=A0 +ry=3D0.920766095087038 +rz=3D-0.916640798962096 +s=3D=
-3.52<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 +convention=3Dposition_vector<br>=C2=
=A0 +step +inv +proj=3Dcart +ellps=3DGRS80<br>=C2=A0 +step +proj=3Dpop +v_3=
<br>=C2=A0 +step +proj=3Dutm +zone=3D33 +ellps=3DGRS80<br><br>-------------=
------------------------<br>Operation No. 2:<br><br>unknown id, Inverse of =
UTM zone 33N + ED50 to ETRS89 (4) + UTM zone 33N, 1 m, Denmark - onshore.<b=
r><br>PROJ string:<br>+proj=3Dpipeline<br>=C2=A0 +step +inv +proj=3Dutm +zo=
ne=3D33 +ellps=3Dintl<br>=C2=A0 +step +proj=3Dpush +v_3<br>=C2=A0 +step +pr=
oj=3Dcart +ellps=3Dintl<br>=C2=A0 +step +proj=3Dhelmert +x=3D-81.1 +y=3D-89=
.4 +z=3D-115.8 +rx=3D0.485 +ry=3D0.024 +rz=3D0.413<br>=C2=A0 =C2=A0 =C2=A0 =
=C2=A0 +s=3D-0.54 +convention=3Dposition_vector<br>=C2=A0 +step +inv +proj=
=3Dcart +ellps=3DGRS80<br>=C2=A0 +step +proj=3Dpop +v_3<br>=C2=A0 +step +pr=
oj=3Dutm +zone=3D33 +ellps=3DGRS80<br><br>---------------------------------=
----<br>Operation No. 3:<br><br>unknown id, Inverse of UTM zone 33N + Ballp=
ark geographic offset from ED50 to ETRS89 + UTM zone 33N, unknown accuracy,=
World, has ballpark transformation<br><br>PROJ string:<br>+proj=3Dpipeline=
<br>=C2=A0 +step +inv +proj=3Dutm +zone=3D33 +ellps=3Dintl<br>=C2=A0 +step =
+proj=3Dutm +zone=3D33 +ellps=3DGRS80<br><br>C:\>=C2=A0</div><div>
---------------------------------------------------------------------------=
-------------------------</div><div>PostGIS in my case has used the last on=
e (the less accurate transformation - "ballpark transformation")=
=C2=A0</div><div>This is the default behaviour in this case as you can see =
also looking at the output of cs2cs</div><div><br></div><div>C:\>cs2cs E=
PSG:23033 EPSG:25833<br>541000 4516000<br>540998.14 =C2=A0 =C2=A0 =C2=A0 45=
15921.57 0.00=C2=A0</div><div><br></div><div>The result is coincident with =
the output of my sql script=C2=A0</div><div>
540998.1383329581 4515921.565374701=C2=A0</div><div><br></div><div>My quest=
ion was about the criteria used by the Proj.6 lib for choosing between seve=
ral possible transformations and also to know the transformation pipeline c=
ode used.</div><div><br></div><div>Searching on the net I have found that t=
he same results (the approximate ones) can be obtained using the function S=
T_TransformPipeline ()=C2=A0 a variant of ST_Transform and specifying the E=
PSG code the transformation=C2=A0</div><div><br></div><div>-- I have first =
transformed the cartografic coordinates=C2=A0
541000 4516000<b>=C2=A0</b>from EPSG:23033 to geographic coordinates with E=
PSG:4230</div><div><br></div><div>WITH coord_4258 AS<br>(<br>=C2=A0 SELECT<=
br>=C2=A0 =C2=A0 =C2=A0ST_X(ST_TransformPipeline('SRID=3D4230;POINT(15.=
48596772786105 40.79326538046647)'::geometry,'urn:ogc:def:coordinat=
eOperation:EPSG::1611')) as x_4258,<br>=C2=A0 =C2=A0 =C2=A0ST_Y(ST_Tran=
sformPipeline('SRID=3D4230;POINT(15.48596772786105 40.79326538046647)&#=
39;::geometry,'urn:ogc:def:coordinateOperation:EPSG::1611')) as y_4=
258<br>=C2=A0 )<br>SELECT<br>=C2=A0 ST_AsEWKT(ST_Transform(ST_Point(x_4258,=
y_4258, 4258), 25833))<br>FROM coord_4258;<br>---------------<br>"st_a=
sewkt"<br>"SRID=3D25833;POINT(540998.1383329581 4515921.565374701=
)"</div><div>.</div><div><br></div><div>In order to obtain better resu=
lts I have used a specific EPSG transformation code (1588)</div><div><br></=
div><div>WITH coord_4258 AS<br>(<br>=C2=A0 SELECT<br>=C2=A0 =C2=A0 =C2=A0ST=
_X(ST_TransformPipeline('SRID=3D4230;POINT(15.48596772786105 40.7932653=
8046647)'::geometry,'urn:ogc:def:coordinateOperation:EPSG::1588'=
;)) as x_4258,<br>=C2=A0 =C2=A0 =C2=A0ST_Y(ST_TransformPipeline('SRID=
=3D4230;POINT(15.48596772786105 40.79326538046647)'::geometry,'urn:=
ogc:def:coordinateOperation:EPSG::1588')) as y_4258<br>=C2=A0 )<br>SELE=
CT<br>=C2=A0 ST_AsEWKT(ST_Transform(ST_Point(x_4258,y_4258, 4258), 25833))<=
br>FROM coord_4258;<br>----------------<br>"st_asewkt"<br>"S=
RID=3D25833;POINT(540931.3414974756 4515810.597825925)"</div><div><br>=
</div><div>The most accurate results are those obtained using a NTv2 grid f=
ile</div><div>540932.206 =C2=A04515807.111</div><div><br></div><div>and the=
results of the transformation with EPSG:1588 are better than those of EPSG=
:1611 (used da PostGIS).</div><div><br></div><div>The questions are now:</d=
iv><div><br></div><div>1. why did Proj.6 lib=C2=A0 c<span class=3D"gmail-T2=
86Pc">hoose </span>
the transformation with EPSG code 1611 rather than the one with EPSG code 1=
588 ?</div><div>2. why there is no evidence in the output of ST_Transform o=
r cs2cs of the transformation code chosen for doing the transformation ?</d=
iv><div><br></div><div>I hope this long mail will help clarify my initial q=
uestion.</div><div><br></div><div>Antonio</div><div><br></div><div><br></di=
v><div><br></div><div><br></div></div><br><div class=3D"gmail_quote gmail_q=
uote_container"><div dir=3D"ltr" class=3D"gmail_attr">Il giorno gio 26 feb =
2026 alle ore 10:29 Florian Nadler <<a href=3D"mailto:florian.nadler@cyb=
ertec.at">[email protected]</a>> ha scritto:<br></div><blockquo=
te class=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px =
solid rgb(204,204,204);padding-left:1ex">Try projinfo (<a href=3D"https://p=
roj.org/en/stable/apps/projinfo.html#examples" rel=3D"noreferrer" target=3D=
"_blank">https://proj.org/en/stable/apps/projinfo.html#examples</a>) <br>
from the commandline of your PostGIS host:<br>
<br>
=C2=A0=C2=A0projinfo -s EPSG:23033 -t EPSG:25833<br>
<br>
Transformation can done via=C2=A0 commandline as follows:<br>
<br>
cs2cs EPSG:23033 EPSG:25833 <<EOF<br>
541000 4516000<br>
EOF<br>
<br>
Florian<br>
<br>
Am 24.02.2026 um 08:18 schrieb Antonio Valanzano:<br>
> I am usign PostGIS 3.5 with PostgreSQL 17.0<br>
><br>
> Here are the details for my installation:<br>
><br>
> "postgis_full_version"<br>
> "POSTGIS=3D""3.5.3 3.5.3"" [EXTENSION] PGSQL=
=3D""170"" <br>
> GEOS=3D""3.13.1-CAPI-1.19.2"" PROJ=3D""8=
.2.1 NETWORK_ENABLED=3DOFF<br>
> URL_ENDPOINT=3D<a href=3D"https://cdn.proj.org" rel=3D"noreferrer" tar=
get=3D"_blank">https://cdn.proj.org</a> <br>
> USER_WRITABLE_DIRECTORY=3DC:\Windows\ServiceProfiles\NetworkService\Ap=
pData\Local/proj <br>
><br>
> DATABASE_PATH=3DC:\Program <br>
> Files\PostgreSQL\17\share\contrib\postgis-3.5\proj\proj.db""=
(compiled <br>
> against PROJ 8.2.1)<br>
> LIBXML=3D""2.12.5"" LIBJSON=3D""0.12&quo=
t;" LIBPROTOBUF=3D""1.2.1"" WAGYU=3D""0.=
5.0 <br>
> (Internal)"" (core procs from ""3.5.2 3.5.2"&=
quot; need upgrade)"<br>
><br>
><br>
> I have done the following coordinate transformation<br>
> from=C2=A0 ED50 / UTM zone 33N =C2=A0(espg:23033)<br>
> to=C2=A0 ETRS89 / UTM zone 33N =C2=A0(epsg:25833<br>
><br>
> SELECT<br>
> =C2=A0 ST_X(ST_Transform(ST_Point(541000,4516000,23033),25833)) as X,<=
br>
> =C2=A0 ST_Y(ST_Transform(ST_Point(541000,4516000,23033),25833)) as Y;<=
br>
><br>
> -- result<br>
> "x" "y"<br>
> 540998.1383329581 4515921.565374701<br>
><br>
> I have read that the best way to utilize PROJ.6 capabilities is by <br=
>
> allowing the library to freely choose the best possible transformation=
<br>
> paths<br>
> between the origin and the destination CRSes.<br>
><br>
> Does someone know how to find which specific transformation pipeline <=
br>
> has been chosen by the PROJ.6 library in this specific transformation =
?<br>
><br>
> Antonio<br>
><br>
><br>
-- <br>
CYBERTEC PostgreSQL International GmbH<br>
R=C3=B6merstra=C3=9Fe 19, A-2752 W=C3=B6llersdorf<br>
Web: <a href=3D"https://www.cybertec-postgresql.com" rel=3D"noreferrer" tar=
get=3D"_blank">https://www.cybertec-postgresql.com</a><br>
<br>
</blockquote></div></div>
--0000000000002f1097064bb79516--