Skip to content

Commit 7ad4664

Browse files
authored
g.proj: use PROJJSON as json output format (#6614)
* g.proj: use PROJJSON as json output format * change to format=projjson * adjust tests
1 parent 6eb02f6 commit 7ad4664

File tree

9 files changed

+171
-125
lines changed

9 files changed

+171
-125
lines changed

general/g.proj/g.proj.html

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ <h2>DESCRIPTION</h2>
2020

2121
<p>When compiled with OGR, functionality is increased and allows output of
2222
the CRS information in the Well-Known Text (WKT) format popularised
23-
by proprietary GIS. In addition, if one of the parameters <em>georef</em>,
23+
by PROJ and GDAL. In addition, if one of the parameters <em>georef</em>,
2424
<em>wkt</em>, <em>proj4</em> or <em>epsg</em> is specified, rather than being
2525
read from the current project, the CRS information is imported from
2626
an external source as follows:
@@ -110,12 +110,18 @@ <h2>NOTES</h2>
110110

111111
<p>Output is simply based on the input CRS information. g.proj does
112112
<strong>not</strong> attempt to verify that the co-ordinate system thus
113-
described matches an existing system in use in the world. In particular,
114-
this means there are no EPSG Authority codes in the WKT output.
113+
described matches a pre-defined existing system in use in the world. In
114+
particular, this means there may be no authority names and codes in the
115+
WKT output.
115116

116117
<p>WKT format shows the false eastings and northings in the projected unit
117118
(e.g. meters, feet) but in PROJ format it should always be given in meters.
118119

120+
<p>PROJJSON format is a JSON version of the WKT format, see the <a
121+
href="https://proj.org/en/stable/specifications/projjson.html">PROJJSON
122+
specification</a>
123+
124+
119125
<p>The maximum size of input WKT or PROJ CRS descriptions is
120126
limited to 8000 bytes.
121127

general/g.proj/g.proj.md

Lines changed: 25 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,31 @@ is limited to:
1717

1818
When compiled with OGR, functionality is increased and allows output of
1919
the CRS information in the Well-Known Text (WKT) format popularised by
20-
proprietary GIS. In addition, if one of the parameters *georef*, *wkt*,
20+
PROJ and GDAL. In addition, if one of the parameters *georef*, *wkt*,
2121
*proj4* or *epsg* is specified, rather than being read from the current
2222
project, the CRS information is imported from an external source as
2323
follows:
2424

25-
- With **georef**=*filename* g.proj attempts to invoke GDAL and OGR in turn
26-
to read a georeferenced file *filename*. The CRS information will be read
27-
from this file. If the file is not georeferenced or cannot be read,
28-
XY (unprojected) will be used.
25+
georef=*filename*
26+
*g.proj* attempts to invoke GDAL and OGR in turn to read a georeferenced
27+
file *filename*. The CRS information will be read from this file. If the
28+
file is not georeferenced or cannot be read, XY (unprojected) will be
29+
used.
2930

30-
- When using **wkt**=*filename*, the file *filename* should contain a CRS
31-
description in WKT format with or without line-breaks (e.g. a '.prj' file).
32-
If **-** is given for the filename, the WKT description will be read from
33-
stdin rather than a file.
31+
wkt=*filename* or **-**
32+
The file *filename* should contain a CRS description in WKT format with
33+
or without line-breaks (e.g. a '.prj' file). If **-** is given for the
34+
filename, the WKT description will be read from stdin rather than a
35+
file.
3436

35-
- **proj4**=*description* should be a CRS description in [PROJ](https://proj.org/)
37+
proj4=*description* or **-**
38+
*description* should be a CRS description in [PROJ](https://proj.org/)
3639
format, enclosed in quotation marks if there are any spaces. If **-** is
3740
given for *description*, the PROJ description will be read from stdin
3841
rather than as a directly-supplied command-line parameter.
3942

40-
- **epsg**=*number* should correspond to the index number of a valid co-ordinate
43+
epsg=*number*
44+
*number* should correspond to the index number of a valid co-ordinate
4145
system in the [EPSG database](https://epsg.org/search/by-name). EPSG
4246
code support is based upon a local copy of the GDAL CSV co-ordinate
4347
system and datum information files, stored in the directory
@@ -95,14 +99,17 @@ co-ordinate system. This can be useful to change the datum information
9599
for an existing project.
96100

97101
Output is simply based on the input CRS information. g.proj does **not**
98-
attempt to verify that the co-ordinate system thus described matches an
99-
existing system in use in the world. In particular, this means there are
100-
no EPSG Authority codes in the WKT output.
102+
attempt to verify that the co-ordinate system thus described matches a
103+
pre-defined existing system in use in the world. In particular, this
104+
means there may be no authority names and codes in the WKT output.
101105

102106
WKT format shows the false eastings and northings in the projected unit
103107
(e.g. meters, feet) but in PROJ format it should always be given in
104108
meters.
105109

110+
PROJJSON format is a JSON version of the WKT format, see the [PROJJSON
111+
specification](https://proj.org/en/stable/specifications/projjson.html)
112+
106113
The maximum size of input WKT or PROJ CRS descriptions is limited to
107114
8000 bytes.
108115

@@ -116,10 +123,10 @@ Print the CRS information for the current project:
116123
g.proj -p
117124
```
118125

119-
Print the CRS information for the current project in JSON format:
126+
Print the CRS information for the current project in PROJJSON format:
120127

121128
```sh
122-
g.proj -p format=json
129+
g.proj -p format=projjson
123130
```
124131

125132
Print the CRS information for the current project in shell format:
@@ -134,19 +141,12 @@ Print the CRS information for the current project in WKT format:
134141
g.proj -p format=wkt
135142
```
136143

137-
Print the CRS information for the current project in PROJ.4 format:
144+
Print the CRS information for the current project in PROJ.4 format (deprecated):
138145

139146
```sh
140147
g.proj -p format=proj4
141148
```
142149

143-
List the possible datum transformation parameters for the current
144-
project:
145-
146-
```sh
147-
g.proj -t datumtrans=-1
148-
```
149-
150150
### Create projection (PRJ) file
151151

152152
Create a '.prj' file in ESRI format corresponding to the current
@@ -240,47 +240,13 @@ Reproject external vector map to current GRASS project using the OGR
240240
ogr2ogr -t_srs "`g.proj -wf`" polbnda_italy_GB_ovest.shp polbnda_italy_LL.shp
241241
```
242242

243-
### Using g.proj JSON output with pandas
244-
245-
Using the CRS information for the current project in JSON format with pandas:
246-
247-
```python
248-
import grass.script as gs
249-
import pandas as pd
250-
251-
# Run g.proj to get CRS information in JSON format.
252-
proj_data = gs.parse_command("g.proj", flags="p", format="json")
253-
254-
df = pd.DataFrame.from_dict(proj_data, orient='index')
255-
print(df)
256-
```
257-
258-
```sh
259-
0
260-
name Lambert Conformal Conic
261-
proj lcc
262-
datum nad83
263-
a 6378137.0
264-
es 0.006694380022900787
265-
lat_1 36.16666666666666
266-
lat_2 34.33333333333334
267-
lat_0 33.75
268-
lon_0 -79
269-
x_0 609601.22
270-
y_0 0
271-
no_defs defined
272-
unit Meter
273-
units Meters
274-
meters 1
275-
```
276-
277243
## REFERENCES
278244

279245
[PROJ](https://proj.org): Projection/datum support library
280246
[GDAL raster library and toolset](https://gdal.org)
281247
[OGR vector library and toolset](https://gdal.org/)
282248

283-
Further reading:
249+
### Further reading
284250

285251
- [ASPRS Grids and
286252
Datum](https://www.asprs.org/asprs-publications/grids-and-datums)

general/g.proj/main.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,13 @@ int main(int argc, char *argv[])
235235
location->description = _("Name of new project (location) to create");
236236

237237
format = G_define_standard_option(G_OPT_F_FORMAT);
238-
format->options = "plain,shell,json,wkt,proj4";
239-
format->descriptions = _("plain;Human readable text output;"
240-
"shell;shell script style text output;"
241-
"json;JSON (JavaScript Object Notation);"
242-
"wkt;Well-known text output;"
243-
"proj4;PROJ.4 style text output;");
238+
format->options = "plain,shell,wkt,projjson,proj4";
239+
format->descriptions =
240+
_("plain;Human readable text output;"
241+
"shell;shell script style text output;"
242+
"wkt;Well-known text output;"
243+
"projjson;JSON (JavaScript Object Notation) version of WKT;"
244+
"proj4;PROJ.4 style text output;");
244245
format->guisection = _("Print");
245246

246247
G_option_exclusive(printinfo, datuminfo, create, NULL);
@@ -250,7 +251,7 @@ int main(int argc, char *argv[])
250251

251252
/* Initialisation & Validation */
252253

253-
if (strcmp(format->answer, "json") == 0) {
254+
if (strcmp(format->answer, "projjson") == 0) {
254255
outputFormat = JSON;
255256
}
256257
else if (strcmp(format->answer, "shell") == 0) {

general/g.proj/output.c

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,32 @@
3232
static int check_xy(enum OutputFormat);
3333
static void print_json(G_JSON_Value *);
3434

35+
#if PROJ_VERSION_MAJOR >= 6
36+
static void print_projjson(void);
37+
#endif
38+
3539
/* print projection information gathered from one of the possible inputs
3640
* in GRASS format */
3741
void print_projinfo(enum OutputFormat format)
3842
{
3943
int i;
40-
G_JSON_Value *value = NULL;
41-
G_JSON_Object *object = NULL;
4244

4345
if (check_xy(format))
4446
return;
4547

46-
if (format == PLAIN)
48+
if (format == JSON) {
49+
#if PROJ_VERSION_MAJOR >= 6
50+
print_projjson();
51+
52+
return;
53+
#else
54+
G_fatal_error(_("JSON output is not available."));
55+
#endif
56+
}
57+
if (format == PLAIN) {
4758
fprintf(
4859
stdout,
4960
"-PROJ_INFO-------------------------------------------------\n");
50-
else if (format == JSON) {
51-
value = G_json_value_init_object();
52-
if (value == NULL) {
53-
G_fatal_error(
54-
_("Failed to initialize JSON object. Out of memory?"));
55-
}
56-
object = G_json_object(value);
5761
}
5862

5963
for (i = 0; i < projinfo->nitems; i++) {
@@ -67,17 +71,13 @@ void print_projinfo(enum OutputFormat format)
6771
fprintf(stdout, "%-11s: %s\n", projinfo->key[i],
6872
projinfo->value[i]);
6973
break;
70-
case JSON:
71-
G_json_object_set_string(object, projinfo->key[i],
72-
projinfo->value[i]);
73-
break;
7474
case PROJ4:
7575
case WKT:
76+
case JSON:
7677
break;
7778
}
7879
}
7980

80-
/* TODO: use projsrid instead */
8181
if (projsrid) {
8282
switch (format) {
8383
case PLAIN:
@@ -88,11 +88,9 @@ void print_projinfo(enum OutputFormat format)
8888
case SHELL:
8989
fprintf(stdout, "%s=%s\n", "srid", projsrid);
9090
break;
91-
case JSON:
92-
G_json_object_set_string(object, "srid", projsrid);
93-
break;
9491
case PROJ4:
9592
case WKT:
93+
case JSON:
9694
break;
9795
}
9896
}
@@ -111,21 +109,14 @@ void print_projinfo(enum OutputFormat format)
111109
fprintf(stdout, "%s=%s\n", projunits->key[i],
112110
projunits->value[i]);
113111
break;
114-
case JSON:
115-
G_json_object_set_string(object, projunits->key[i],
116-
projunits->value[i]);
117-
break;
118112
case PROJ4:
119113
case WKT:
114+
case JSON:
120115
break;
121116
}
122117
}
123118
}
124119

125-
if (format == JSON) {
126-
print_json(value);
127-
}
128-
129120
return;
130121
}
131122

@@ -361,7 +352,8 @@ static int check_xy(enum OutputFormat format)
361352
}
362353
object = G_json_object(value);
363354

364-
G_json_object_set_string(object, "name", "xy_location_unprojected");
355+
G_json_object_set_string(object, "name",
356+
"XY location (unprojected)");
365357

366358
print_json(value);
367359
break;
@@ -375,7 +367,6 @@ static int check_xy(enum OutputFormat format)
375367
return 0;
376368
}
377369

378-
/* TODO: use proj_as_projjson() from proj */
379370
void print_json(G_JSON_Value *value)
380371
{
381372
char *serialized_string = G_json_serialize_to_string_pretty(value);
@@ -387,3 +378,60 @@ void print_json(G_JSON_Value *value)
387378
G_json_free_serialized_string(serialized_string);
388379
G_json_value_free(value);
389380
}
381+
382+
#if PROJ_VERSION_MAJOR >= 6
383+
void print_projjson(void)
384+
{
385+
/* PROJ6+: create a PJ object from wkt or srid,
386+
* then get PROJJSON using PROJ API */
387+
const char *projstr = NULL;
388+
PJ *obj = NULL;
389+
390+
if (check_xy(PLAIN))
391+
return;
392+
393+
if (projwkt) {
394+
obj = proj_create_from_wkt(NULL, projwkt, NULL, NULL, NULL);
395+
}
396+
if (!obj && projsrid) {
397+
obj = proj_create(NULL, projsrid);
398+
}
399+
if (!obj && projepsg) {
400+
int epsg_num;
401+
char *buf = NULL;
402+
403+
epsg_num = atoi(G_find_key_value("epsg", projepsg));
404+
if (epsg_num) {
405+
G_asprintf(&buf, "EPSG:%d", epsg_num);
406+
obj = proj_create(NULL, buf);
407+
G_free(buf);
408+
}
409+
}
410+
if (!obj) {
411+
char *outwkt;
412+
413+
outwkt = GPJ_grass_to_wkt(projinfo, projunits, 0, 0);
414+
/* datum info might be incomplete or incorrect */
415+
if (outwkt) {
416+
obj = proj_create_from_wkt(NULL, projwkt, NULL, NULL, NULL);
417+
G_free(outwkt);
418+
}
419+
}
420+
if (obj) {
421+
projstr = proj_as_projjson(NULL, obj, NULL);
422+
423+
if (projstr)
424+
projstr = G_store(projstr);
425+
proj_destroy(obj);
426+
}
427+
428+
if (projstr) {
429+
fprintf(stdout, "%s\n", projstr);
430+
G_free((char *)projstr);
431+
}
432+
else
433+
G_warning(_("Unable to convert to PROJJSON"));
434+
435+
return;
436+
}
437+
#endif

0 commit comments

Comments
 (0)