Skip to content

Commit fde3d58

Browse files
authored
Add option in proj CLI to use a CRS (OSGeo#3825)
To easily apply proj on the projection of a projected CRS, this PR adds the option `-C`. It follows the same logic as in function proj_factors, that is to compute the projection from the CRS to the geographic underlying CRS. Example of usage: ``` echo -110.16666 31 | proj -C EPSG:26948 -V #Transverse Mercator # Cyl, Sph&Ell # approx # +proj=tmerc +lat_0=31 +lon_0=-110.166666666667 +k=0.9999 +x_0=213360 +y_0=0 # +ellps=GRS80 #Final Earth figure: ellipsoid # Major axis (a): 6378137.000 # 1/flattening: 298.257222 # squared eccentricity: 0.006694380023 Longitude: 110d9'59.976"W [ -110.16666 ] Latitude: 31dN [ 31 ] Easting (x): 213360.64 Northing (y): 0.00 Meridian scale (h) : 0.99990000 ( -0.01 % error ) Parallel scale (k) : 0.99990000 ( -0.01 % error ) Areal scale (s): 0.99980001 ( -0.02 % error ) Angular distortion (w): 0.000 Meridian/Parallel angle: 90.00000 Convergence : 0d0'0.012" [ 0.00000343 ] Max-min (Tissot axis a-b) scale error: 0.99990 0.99990 ```
1 parent 9052f4c commit fde3d58

File tree

4 files changed

+139
-17
lines changed

4 files changed

+139
-17
lines changed

docs/source/apps/proj.rst

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ invproj
1818

1919
Synopsis
2020
********
21-
**proj** [**-beEfiIlmorsStTvVwW**] [args]] [*+opt[=arg]* ...] file ...
21+
**proj** [**-beEfiIlmorsStTvVwW**] [args]] ([*+opt[=arg]* ...] | {crs}) file ...
2222

23-
**invproj** [**-beEfiIlmorsStTvVwW**] [args]] [*+opt[=arg]* ...] file ...
23+
**invproj** [**-beEfiIlmorsStTvVwW**] [args]] ([*+opt[=arg]* ...] | {crs}) file ...
2424

2525

2626
Description
@@ -172,6 +172,13 @@ also used for supporting files like datum shift files.
172172
Usage of *+opt* varies with projection and for a complete description
173173
consult the :ref:`projection pages <projections>`.
174174

175+
.. versionadded:: 9.3.0
176+
177+
*{crs}* is one of the possibilities accepted by :c::func:`proj_create()`, provided it
178+
expresses a projected CRS, like a WKT string, an object code (like "EPSG:32632")
179+
a PROJJSON string, etc.
180+
The projection computed will be those of the map projection implied by
181+
the transformation from the base geographic CRS of the projected CRS to the projected CRS.
175182

176183
One or more files (processed in left to right order) specify the source of
177184
data to be converted. A ``-`` will specify the location of processing standard
@@ -205,6 +212,41 @@ data will appear as three lines of::
205212

206213
460770.43 5011865.86
207214

215+
This other example
216+
217+
.. code-block:: console
218+
219+
proj EPSG:6421 -V <<EOF
220+
-120 35.8
221+
EOF
222+
223+
Will perform the projection of the coordinates in "NAD83(2011) / California zone 4"
224+
(`EPSG:6421`) into its geographic system, "NAD83(2011)", showing the expanded annotated listing.
225+
The output will appear as:
226+
227+
.. code-block:: console
228+
229+
#Lambert Conformal Conic
230+
# Conic, Sph&Ell
231+
# lat_1= and lat_2= or lat_0, k_0=
232+
# +proj=lcc +lat_0=35.3333333333333 +lon_0=-119 +lat_1=37.25 +lat_2=36
233+
# +x_0=2000000 +y_0=500000 +ellps=GRS80
234+
#Final Earth figure: ellipsoid
235+
# Major axis (a): 6378137.000
236+
# 1/flattening: 298.257222
237+
# squared eccentricity: 0.006694380023
238+
Longitude: 120dW [ -120 ]
239+
Latitude: 35d48'N [ 35.8 ]
240+
Easting (x): 1909606.87
241+
Northing (y): 552253.58
242+
Meridian scale (h) : 1.00004382 ( 0.004382 % error )
243+
Parallel scale (k) : 1.00004382 ( 0.004382 % error )
244+
Areal scale (s): 1.00008765 ( 0.008765 % error )
245+
Angular distortion (w): 0.000
246+
Meridian/Parallel angle: 90.00000
247+
Convergence : -0d35'47.714" [ -0.59658715 ]
248+
Max-min (Tissot axis a-b) scale error: 1.00004 1.00004
249+
208250
.. only:: man
209251

210252
Other programs

src/apps/proj.cpp

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* <<<< Cartographic projection filter program >>>> */
22
#include "proj.h"
33
#include "emess.h"
4+
#include "proj_experimental.h"
45
#include "proj_internal.h"
56
#include "utils.h"
67
#include <ctype.h>
@@ -9,6 +10,8 @@
910
#include <stdlib.h>
1011
#include <string.h>
1112

13+
#include <proj/crs.hpp>
14+
1215
#include <vector>
1316

1417
#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__WIN32__)
@@ -22,7 +25,9 @@
2225
#define MAX_LINE 1000
2326
#define PJ_INVERSE(P) (P->inv ? 1 : 0)
2427

25-
static PJ *Proj;
28+
static PJ *Proj = nullptr;
29+
static PJ *ProjForFactors = nullptr;
30+
static bool swapAxisCrs = false;
2631
static union {
2732
PJ_XY (*fwd)(PJ_LP, PJ *);
2833
PJ_LP (*inv)(PJ_XY, PJ *);
@@ -115,16 +120,16 @@ static void process(FILE *fid) {
115120
data.uv.v *= fscale;
116121
}
117122
if (dofactors && !inverse) {
118-
facs = proj_factors(Proj, coord);
119-
facs_bad = proj_errno(Proj);
123+
facs = proj_factors(ProjForFactors, coord);
124+
facs_bad = proj_errno(ProjForFactors);
120125
}
121126

122127
const auto xy = (*proj.fwd)(data.lp, Proj);
123128
data.xy = xy;
124129

125130
if (dofactors && inverse) {
126-
facs = proj_factors(Proj, coord);
127-
facs_bad = proj_errno(Proj);
131+
facs = proj_factors(ProjForFactors, coord);
132+
facs_bad = proj_errno(ProjForFactors);
128133
}
129134

130135
if (postscale && data.uv.u != HUGE_VAL) {
@@ -281,8 +286,8 @@ static void vprocess(FILE *fid) {
281286
if (!*s && (s > line))
282287
--s; /* assumed we gobbled \n */
283288
coord.lp = dat_ll;
284-
facs = proj_factors(Proj, coord);
285-
if (proj_errno(Proj)) {
289+
facs = proj_factors(ProjForFactors, coord);
290+
if (proj_errno(ProjForFactors)) {
286291
emess(-1, "failed to compute factors\n\n");
287292
continue;
288293
}
@@ -298,10 +303,12 @@ static void vprocess(FILE *fid) {
298303
(void)fputs(proj_rtodms2(pline, sizeof(pline), dat_ll.phi, 'N', 'S'),
299304
stdout);
300305
(void)printf(" [ %.11g ]\n", dat_ll.phi * RAD_TO_DEG);
301-
(void)fputs("Easting (x): ", stdout);
306+
(void)fputs(swapAxisCrs ? "Northing (y): " : "Easting (x): ",
307+
stdout);
302308
(void)printf(oform, dat_xy.x);
303309
putchar('\n');
304-
(void)fputs("Northing (y): ", stdout);
310+
(void)fputs(swapAxisCrs ? "Easting (x): " : "Northing (y): ",
311+
stdout);
305312
(void)printf(oform, dat_xy.y);
306313
putchar('\n');
307314
(void)printf("Meridian scale (h) : %.8f ( %.4g %% error )\n",
@@ -507,8 +514,6 @@ int main(int argc, char **argv) {
507514
} else /* assumed to be input file name(s) */
508515
eargv[eargc++] = *argv;
509516
}
510-
if (eargc == 0) /* if no specific files force sysin */
511-
eargv[eargc++] = const_cast<char *>("-");
512517

513518
if (oform) {
514519
if (!validate_form_string_for_numbers(oform)) {
@@ -525,14 +530,80 @@ int main(int argc, char **argv) {
525530
}
526531
proj_context_use_proj4_init_rules(nullptr, true);
527532

533+
if (argvVector.empty() && eargc >= 1) {
534+
// Consider the next arg as a CRS, not a file.
535+
std::string ocrs = eargv[0];
536+
eargv++;
537+
eargc--;
538+
// logic copied from proj_factors function
539+
if (PJ *P = proj_create(nullptr, ocrs.c_str())) {
540+
const auto type = proj_get_type(P);
541+
if (type == PJ_TYPE_PROJECTED_CRS) {
542+
try {
543+
using namespace osgeo::proj;
544+
auto crs = dynamic_cast<const crs::ProjectedCRS *>(
545+
P->iso_obj.get());
546+
auto dir =
547+
crs->coordinateSystem()->axisList()[0]->direction();
548+
swapAxisCrs = dir == cs::AxisDirection::NORTH ||
549+
dir == cs::AxisDirection::SOUTH;
550+
} catch (...) {
551+
}
552+
auto ctx = P->ctx;
553+
auto geodetic_crs = proj_get_source_crs(ctx, P);
554+
assert(geodetic_crs);
555+
auto datum = proj_crs_get_datum(ctx, geodetic_crs);
556+
auto datum_ensemble =
557+
proj_crs_get_datum_ensemble(ctx, geodetic_crs);
558+
auto cs = proj_create_ellipsoidal_2D_cs(
559+
ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, "Radian", 1.0);
560+
auto geogCRSNormalized = proj_create_geographic_crs_from_datum(
561+
ctx, "unnamed crs", datum ? datum : datum_ensemble, cs);
562+
proj_destroy(datum);
563+
proj_destroy(datum_ensemble);
564+
proj_destroy(cs);
565+
Proj = proj_create_crs_to_crs_from_pj(ctx, geogCRSNormalized, P,
566+
nullptr, nullptr);
567+
568+
auto conversion = proj_crs_get_coordoperation(ctx, P);
569+
auto projCS = proj_create_cartesian_2D_cs(
570+
ctx, PJ_CART2D_EASTING_NORTHING, "metre", 1.0);
571+
auto projCRSNormalized = proj_create_projected_crs(
572+
ctx, nullptr, geodetic_crs, conversion, projCS);
573+
assert(projCRSNormalized);
574+
proj_destroy(geodetic_crs);
575+
proj_destroy(conversion);
576+
proj_destroy(projCS);
577+
ProjForFactors = proj_create_crs_to_crs_from_pj(
578+
ctx, geogCRSNormalized, projCRSNormalized, nullptr,
579+
nullptr);
580+
581+
proj_destroy(geogCRSNormalized);
582+
proj_destroy(projCRSNormalized);
583+
} else {
584+
emess(3, "CRS must be projected");
585+
}
586+
proj_destroy(P);
587+
} else {
588+
emess(3, "CRS is not parseable");
589+
}
590+
}
591+
if (eargc == 0) /* if no specific files force sysin */
592+
eargv[eargc++] = const_cast<char *>("-");
593+
528594
// proj historically ignores any datum shift specifier, like nadgrids,
529595
// towgs84, etc
530596
argvVector.push_back(const_cast<char *>("break_cs2cs_recursion"));
531597

532-
if (!(Proj = proj_create_argv(nullptr, static_cast<int>(argvVector.size()),
533-
argvVector.data())))
534-
emess(3, "projection initialization failure\ncause: %s",
535-
proj_errno_string(proj_context_errno(nullptr)));
598+
if (!Proj) {
599+
if (!(Proj =
600+
proj_create_argv(nullptr, static_cast<int>(argvVector.size()),
601+
argvVector.data())))
602+
emess(3, "projection initialization failure\ncause: %s",
603+
proj_errno_string(proj_context_errno(nullptr)));
604+
605+
ProjForFactors = Proj;
606+
}
536607

537608
if (!proj_angular_input(Proj, PJ_FWD)) {
538609
emess(3, "can't initialize operations that take non-angular input "
@@ -616,6 +687,8 @@ int main(int argc, char **argv) {
616687
emess_dat.File_name = nullptr;
617688
}
618689

690+
if (ProjForFactors && ProjForFactors != Proj)
691+
proj_destroy(ProjForFactors);
619692
if (Proj)
620693
proj_destroy(Proj);
621694

test/cli/testproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ echo "doing tests into file ${OUT}, please wait"
3535
$EXE +ellps=WGS84 +proj=ob_tran +o_proj=latlon +o_lon_p=0.0 +o_lat_p=90.0 +lon_0=360.0 +to_meter=0.0174532925199433 +no_defs -E -f '%.3f' >${OUT} <<EOF
3636
2 49
3737
EOF
38+
#
39+
echo "Test CRS option"
40+
#
41+
$EXE EPSG:32620 -S >>${OUT} <<EOF
42+
-63 0
43+
EOF
3844

3945
#
4046
# do 'diff' with distribution results

test/cli/testproj_out.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
2 49 2.000 49.000
2+
500000.00 0.00 <0.9996 0.9996 0.9992 0 0.9996 0.9996>

0 commit comments

Comments
 (0)