[Git][debian-gis-team/proj][upstream] New upstream version 9.8.1
Bas Couwenberg (@sebastic)
gitlab at salsa.debian.org
Fri Apr 10 14:36:39 BST 2026
Bas Couwenberg pushed to branch upstream at Debian GIS Project / proj
Commits:
86566592 by Bas Couwenberg at 2026-04-10T15:27:39+02:00
New upstream version 9.8.1
- - - - -
15 changed files:
- CITATION.cff
- NEWS.md
- man/man1/cct.1
- man/man1/cs2cs.1
- man/man1/geod.1
- man/man1/gie.1
- man/man1/proj.1
- man/man1/projinfo.1
- man/man1/projsync.1
- src/apps/gie.cpp
- src/release.cpp
- test/CMakeLists.txt
- test/gie/deformation.gie
- + test/gie/epsg_grid.gie
- + test/gie/epsg_no_grid.gie
Changes:
=====================================
CITATION.cff
=====================================
@@ -3,7 +3,7 @@ message: Please cite this software using these metadata or in the CITATION file.
type: software
title: PROJ
version: 9.8.1
-date-released: 2026-04-08
+date-released: 2026-04-10
doi: 10.5281/zenodo.5884394
abstract: PROJ is a generic coordinate transformation software that transforms
geospatial coordinates from one coordinate reference system (CRS) to another.
=====================================
NEWS.md
=====================================
@@ -39,6 +39,9 @@ longer available in PROJ 9.8.1.
Helps for example for **EPSG:5705** (Baltic 1977 height) to **EPSG:5706** (Caspian depth)
by using intermediate operation from Baltic 1977 height to Caspian *height*
+* gie: various fixes around crs_src/crs_dst support and bootstrap
+ test/gie/epsg_grid.gie and test/gie/epsg_no_grid.gie (#4740)
+
## 9.8.0
### Updates
=====================================
man/man1/cct.1
=====================================
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "CCT" "1" "08 Apr 2026" "9.8" "PROJ"
+.TH "CCT" "1" "10 avril 2026" "9.8" "PROJ"
.SH NAME
cct \- Coordinate Conversion and Transformation
.SH SYNOPSIS
=====================================
man/man1/cs2cs.1
=====================================
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "CS2CS" "1" "08 Apr 2026" "9.8" "PROJ"
+.TH "CS2CS" "1" "10 avril 2026" "9.8" "PROJ"
.SH NAME
cs2cs \- Cartographic coordinate system filter
.SH SYNOPSIS
=====================================
man/man1/geod.1
=====================================
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "GEOD" "1" "08 Apr 2026" "9.8" "PROJ"
+.TH "GEOD" "1" "10 avril 2026" "9.8" "PROJ"
.SH NAME
geod \- Geodesic computations
.SH SYNOPSIS
=====================================
man/man1/gie.1
=====================================
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "GIE" "1" "08 Apr 2026" "9.8" "PROJ"
+.TH "GIE" "1" "10 avril 2026" "9.8" "PROJ"
.SH NAME
gie \- The Geospatial Integrity Investigation Environment
.SH SYNOPSIS
=====================================
man/man1/proj.1
=====================================
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "PROJ" "1" "08 Apr 2026" "9.8" "PROJ"
+.TH "PROJ" "1" "10 avril 2026" "9.8" "PROJ"
.SH NAME
proj \- Cartographic projection filter
.SH SYNOPSIS
=====================================
man/man1/projinfo.1
=====================================
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "PROJINFO" "1" "08 Apr 2026" "9.8" "PROJ"
+.TH "PROJINFO" "1" "10 avril 2026" "9.8" "PROJ"
.SH NAME
projinfo \- Geodetic object and coordinate operation queries
.SH SYNOPSIS
=====================================
man/man1/projsync.1
=====================================
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
-.TH "PROJSYNC" "1" "08 Apr 2026" "9.8" "PROJ"
+.TH "PROJSYNC" "1" "10 avril 2026" "9.8" "PROJ"
.SH NAME
projsync \- Downloading tool of resource files
.SH SYNOPSIS
=====================================
src/apps/gie.cpp
=====================================
@@ -104,6 +104,8 @@ Thomas Knudsen, thokn at sdfe.dk, 2017-10-01/2017-10-08
***********************************************************************/
+#define FROM_PROJ_CPP
+
#include <ctype.h>
#include <errno.h>
#include <math.h>
@@ -116,9 +118,13 @@ Thomas Knudsen, thokn at sdfe.dk, 2017-10-01/2017-10-08
#include "proj.h"
#include "proj_internal.h"
#include "proj_strtod.h"
+
+#include <algorithm>
#include <cmath> /* for isnan */
#include <math.h>
+#include <proj/internal/internal.hpp>
+
#include "optargpm.h"
/* Package for flexible format I/O - ffio */
@@ -192,6 +198,7 @@ typedef struct {
char crs_dst[MAX_OPERATION + 1];
char crs_src[MAX_OPERATION + 1];
PJ *P;
+ bool crs_dst_is_lat_lon_or_y_x;
PJ_COORD a, b, e;
PJ_DIRECTION dir;
int verbosity;
@@ -561,6 +568,21 @@ static int require_grid(const char *args) {
const char *grid_filename = column(args, 1);
grid_info = proj_grid_info(grid_filename);
if (strlen(grid_info.filename) == 0) {
+
+ if (proj_context_is_network_enabled(nullptr)) {
+ auto dbContext = NS_PROJ::io::DatabaseContext::create();
+ std::string fullFilename, packageName, url;
+ bool directDownload, openLicense, gridAvailable = false;
+ if (dbContext->lookForGridInfo(
+ grid_filename,
+ /* considerKnownGridsAsAvailable = */ true, fullFilename,
+ packageName, url, directDownload, openLicense,
+ gridAvailable) &&
+ gridAvailable) {
+ return 0;
+ }
+ }
+
if (T.verbosity > 1) {
fprintf(T.fout, "Test skipped because of missing grid %s\n",
grid_filename);
@@ -644,6 +666,56 @@ static int operation(const char *args) {
return 0;
}
+static bool isLatOrNorthingFirst(const PJ *crs) {
+ auto crsType = proj_get_type(crs);
+
+ if (crsType == PJ_TYPE_COMPOUND_CRS) {
+ auto horiz_crs = proj_crs_get_sub_crs(nullptr, crs, 0);
+ assert(horiz_crs);
+ bool ret = isLatOrNorthingFirst(horiz_crs);
+ proj_destroy(horiz_crs);
+ return ret;
+ }
+
+ if (crsType != PJ_TYPE_GEOGRAPHIC_2D_CRS &&
+ crsType != PJ_TYPE_GEOGRAPHIC_3D_CRS &&
+ crsType != PJ_TYPE_GEOCENTRIC_CRS && crsType != PJ_TYPE_PROJECTED_CRS) {
+ fprintf(stderr, "Bug in gie.cpp:%d: unsupported CRS type %d!\n",
+ __LINE__, crsType);
+ return false;
+ }
+
+ auto cs = proj_crs_get_coordinate_system(nullptr, crs);
+ assert(cs);
+
+ const char *axisName = "";
+ const char *unitName = "";
+ proj_cs_get_axis_info(nullptr, cs, 0,
+ &axisName, // name,
+ nullptr, // abbrev
+ nullptr, // direction
+ nullptr, // unit conv factor
+ &unitName, // unit name
+ nullptr, // unit authority
+ nullptr // unit code
+ );
+ const bool isLatOrNorthingFirst =
+ NS_PROJ::internal::ci_find(std::string(axisName), "latitude") !=
+ std::string::npos ||
+ NS_PROJ::internal::ci_find(std::string(axisName), "northing") !=
+ std::string::npos;
+ proj_destroy(cs);
+
+ if (strcmp(unitName, "degree") != 0 && strcmp(unitName, "metre") != 0) {
+ // We should add normalization to this unit in expect()
+ fprintf(stderr, "Bug in gie.cpp:%d: unsupported unit %s!\n", __LINE__,
+ unitName);
+ return false;
+ }
+
+ return isLatOrNorthingFirst;
+}
+
static int crs_to_crs_operation() {
T.op_id++;
T.operation_lineno = F->lineno;
@@ -671,6 +743,14 @@ static int crs_to_crs_operation() {
proj_errno_reset(nullptr);
proj_context_use_proj4_init_rules(nullptr, T.use_proj4_init_rules);
+ T.crs_dst_is_lat_lon_or_y_x = false;
+ PJ *pj_dst = proj_create(nullptr, T.crs_dst);
+ if (!pj_dst) {
+ fprintf(stderr, "Cannot instantiate crs_dst = %s\n", T.crs_dst);
+ }
+ T.crs_dst_is_lat_lon_or_y_x = isLatOrNorthingFirst(pj_dst);
+ proj_destroy(pj_dst);
+
T.P = proj_create_crs_to_crs(nullptr, T.crs_src, T.crs_dst, nullptr);
strcpy(T.crs_src, "");
@@ -925,18 +1005,6 @@ static int expect_failure_with_errno_message(int expected, int got) {
return 1;
}
-/* For test purposes, we want to call a transformation of the same */
-/* dimensionality as the number of dimensions given in accept */
-static PJ_COORD expect_trans_n_dim(const PJ_COORD &ci) {
- if (4 == T.dimensions_given_at_last_accept)
- return proj_trans(T.P, T.dir, ci);
-
- if (3 == T.dimensions_given_at_last_accept)
- return pj_approx_3D_trans(T.P, T.dir, ci);
-
- return pj_approx_2D_trans(T.P, T.dir, ci);
-}
-
/*****************************************************************************/
static int expect(const char *args) {
/*****************************************************************************
@@ -989,7 +1057,7 @@ static int expect(const char *args) {
/* Try to carry out the operation - and expect failure */
ci =
proj_angular_input(T.P, T.dir) ? torad_coord(T.P, T.dir, T.a) : T.a;
- co = expect_trans_n_dim(ci);
+ co = proj_trans(T.P, T.dir, ci);
if (expect_failure_with_errno) {
if (proj_errno(T.P) == expect_failure_with_errno)
@@ -1043,7 +1111,7 @@ static int expect(const char *args) {
/* do the transformation, but mask off dimensions not given in expect-ation
*/
- co = expect_trans_n_dim(ci);
+ co = proj_trans(T.P, T.dir, ci);
if (T.dimensions_given < 4)
co.v[3] = 0;
if (T.dimensions_given < 3)
@@ -1056,23 +1124,68 @@ static int expect(const char *args) {
co.v[1], co.v[2], co.v[3]);
#if 0
- /* We need to handle unusual axis orders - that'll be an item for version 5.1 */
+ /* We need to handle unusual axis orders when axisswap explicitly present.
+ * Otherwise when using dst_crs code below will take care of that.
+ */
if (T.P->axisswap) {
ce = proj_trans (T.P->axisswap, T.dir, ce);
co = proj_trans (T.P->axisswap, T.dir, co);
}
#endif
+
if (std::isnan(co.v[0]) && std::isnan(ce.v[0])) {
d = 0.0;
- } else if (proj_angular_output(T.P, T.dir)) {
+ } else if (proj_angular_output(T.P, T.dir)) { // angular = radians...
+ d = proj_lpz_dist(T.P, ce, co);
+ } else if (proj_degree_output(T.P, T.dir)) {
+ co.v[0] = proj_torad(co.v[0]);
+ co.v[1] = proj_torad(co.v[1]);
+
+ ce.v[0] = proj_torad(ce.v[0]);
+ ce.v[1] = proj_torad(ce.v[1]);
+
+ if (T.crs_dst_is_lat_lon_or_y_x) {
+ std::swap(co.v[0], co.v[1]);
+ std::swap(ce.v[0], ce.v[1]);
+ }
+
d = proj_lpz_dist(T.P, ce, co);
} else {
- d = proj_xyz_dist(co, ce);
+
+ if (T.crs_dst_is_lat_lon_or_y_x) {
+ std::swap(co.v[0], co.v[1]);
+ std::swap(ce.v[0], ce.v[1]);
+ }
+
+ d = proj_xyz_dist(ce, co);
}
// Test written like that to handle NaN
if (!(d <= T.tolerance))
return expect_message(d, args);
+
+ // Somewhat arbitrary temporal threshold but should be fine for all intended
+ // purposes.
+ constexpr double TEMPORAL_THRESHOLD_IN_YEAR = 1e-4;
+ if (T.dimensions_given == 4 &&
+ std::fabs(ce.v[3] - co.v[3]) > TEMPORAL_THRESHOLD_IN_YEAR) {
+ another_failure();
+
+ if (T.verbosity < 0)
+ return 1;
+ if (0 == T.op_ko && T.verbosity < 2)
+ banner(T.operation);
+ fprintf(T.fout, "%s", T.op_ko ? " -----\n" : delim);
+ fprintf(T.fout, " FAILURE in %s(%d):\n",
+ opt_strip_path(T.curr_file), (int)F->lineno);
+ fprintf(T.fout, " expected: %s\n", args);
+ fprintf(T.fout, " got: %.12f %.12f %.9f %.9f\n",
+ T.b.v[0], T.b.v[1], T.b.v[2], T.b.v[3]);
+ fprintf(T.fout, " deviation: %.4f year, %.4f maximum allowed\n",
+ std::fabs(ce.v[3] - co.v[3]), TEMPORAL_THRESHOLD_IN_YEAR);
+ return 1;
+ }
+
succs++;
another_success();
=====================================
src/release.cpp
=====================================
@@ -8,6 +8,6 @@
char const pj_release[] = "Rel. " STR(PROJ_VERSION_MAJOR) "." STR(
PROJ_VERSION_MINOR) "." STR(PROJ_VERSION_PATCH) ", "
- "April 8th, 2026";
+ "April 10th, 2026";
const char *pj_get_release() { return pj_release; }
=====================================
test/CMakeLists.txt
=====================================
@@ -79,6 +79,7 @@ proj_add_gie_test("guyou" "gie/guyou.gie")
proj_add_gie_test("peirce_q" "gie/peirce_q.gie")
proj_add_gie_test("tinshift" "gie/tinshift.gie")
proj_add_gie_test("spilhaus" "gie/spilhaus.gie")
+proj_add_gie_test("epsg_no_grid" "gie/epsg_no_grid.gie")
if(TIFF_ENABLED)
proj_add_gie_test("Deformation" "gie/deformation.gie")
@@ -91,6 +92,7 @@ endif()
if(TIFF_ENABLED AND CURL_ENABLED AND RUN_NETWORK_DEPENDENT_TESTS)
proj_add_gie_network_dependent_test("nkg" "gie/nkg.gie")
+proj_add_gie_network_dependent_test("epsg_grid" "gie/epsg_grid.gie")
endif()
# GIGS tests. Uncommented tests are expected to fail due to issues with
=====================================
test/gie/deformation.gie
=====================================
@@ -152,7 +152,7 @@ expect -147.0 64.0 0.0 2011.0
roundtrip 100
accept -147.0 64.0 0.0 2011.0
-expect -147.0 64.0 0.0 2020.0
+expect -147.0 64.0 0.0 2011.0
roundtrip 100
-------------------------------------------------------------------------------
=====================================
test/gie/epsg_grid.gie
=====================================
@@ -0,0 +1,32 @@
+### This file must contain only (crs_src, crs_dst) tuples where the best transformation
+### DOES involve the use a grid/json/etc. resource file
+### Otherwise use epsg_no_grid.gie
+
+<gie-strict>
+
+###################################
+#### Generic / worldwide scope ####
+###################################
+
+# Test EGM2008 grid
+crs_src EPSG:4979 # WGS 84 geographic 3D
+crs_dst EPSG:9518 # WGS 84 + EGM2008
+tolerance 0.1 mm
+require_grid us_nga_egm08_25.tif
+
+accept 55.0 12.0 10.0
+expect 55.0 12.0 -27.8257
+
+################
+#### FRANCE ####
+################
+
+crs_src EPSG:9785 # RGF93 v2b + NGF-IGN69 height
+crs_dst EPSG:9781 # RGF93 v2b geographic 3D
+tolerance 0.1 mm
+require_grid fr_ign_RAF20.tif
+
+accept 43.6109 3.8761 0.0
+expect 43.6109 3.8761 49.6904
+
+</gie-strict>
=====================================
test/gie/epsg_no_grid.gie
=====================================
@@ -0,0 +1,61 @@
+### This file must contain only (crs_src, crs_dst) tuples where the best transformation
+### does NOT involve the use a grid/json/etc. resource file
+### Otherwise use epsg_grid.gie
+
+<gie-strict>
+
+###################################
+#### Generic / worldwide scope ####
+###################################
+
+# ETRS89 -> ETRS89/UTM32
+crs_src EPSG:4258
+crs_dst EPSG:25832
+tolerance 0.1 mm
+accept 55.0 12.0
+expect 691875.6321 6098907.8250
+
+# WGS 84 geographic 3D -> WGS 84 geocentric
+crs_src EPSG:4979
+crs_dst EPSG:4978
+tolerance 0.1 mm
+accept 55.0 12.0 10.0
+expect 3586475.2672 762328.8513 5201391.7147
+
+# WGS 84 geocentric -> WGS 84 geographic 3D
+crs_src EPSG:4979
+crs_dst EPSG:4978
+tolerance 0.1 mm
+expect 3586475.2672 762328.8513 5201391.7147
+accept 55.0 12.0 10.0
+
+###################
+#### AUSTRALIA ####
+###################
+
+# GDA2020 -> ITRF2014, using EPSG:8049 "ITRF2014 to GDA2020 (1)" 15-parameter Helmert with 2020.0 central epoch
+crs_src EPSG:7843 # GDA2020
+crs_dst EPSG:7912 # ITRF2014
+tolerance 0.1 mm
+accept -33.8623 151.2077 0.0 2020.0
+expect -33.8623 151.2077 0.0 2020.0
+
+crs_src EPSG:7843 # GDA2020
+crs_dst EPSG:7912 # ITRF2014
+tolerance 0.1 mm
+accept -33.8623 151.2077 0.0 2026.0
+expect -33.862297056 151.207701181 -0.00101 2026.0
+
+###################
+###### FINLAND ####
+###################
+
+# Test northing, easting output (actually more a gie test itself, than a EPSG one)
+crs_src EPSG:4123 # Finland KKJ
+crs_dst EPSG:2393 # Finland YKJ Northing, Easting
+
+tolerance 0.1 mm
+accept 60.1699 24.9384
+expect 6674944.7742 3385559.8151
+
+</gie-strict>
View it on GitLab: https://salsa.debian.org/debian-gis-team/proj/-/commit/86566592ed1279c03caa04da8dfd7284bfb39ce2
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/proj/-/commit/86566592ed1279c03caa04da8dfd7284bfb39ce2
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260410/d92fd79d/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list