Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions general/g.proj/g.proj.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h2>DESCRIPTION</h2>

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

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

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

<p>PROJJSON format is a JSON version of the WKT format, see the <a
href="https://proj.org/en/stable/specifications/projjson.html">PROJJSON
specification</a>


<p>The maximum size of input WKT or PROJ CRS descriptions is
limited to 8000 bytes.

Expand Down
80 changes: 23 additions & 57 deletions general/g.proj/g.proj.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,31 @@ is limited to:

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

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

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

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

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

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

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

PROJJSON format is a JSON version of the WKT format, see the [PROJJSON
specification](https://proj.org/en/stable/specifications/projjson.html)

The maximum size of input WKT or PROJ CRS descriptions is limited to
8000 bytes.

Expand Down Expand Up @@ -134,19 +141,12 @@ Print the CRS information for the current project in WKT format:
g.proj -p format=wkt
```

Print the CRS information for the current project in PROJ.4 format:
Print the CRS information for the current project in PROJ.4 format (deprecated):

```sh
g.proj -p format=proj4
```

List the possible datum transformation parameters for the current
project:

```sh
g.proj -t datumtrans=-1
```

### Create projection (PRJ) file

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

### Using g.proj JSON output with pandas

Using the CRS information for the current project in JSON format with pandas:

```python
import grass.script as gs
import pandas as pd

# Run g.proj to get CRS information in JSON format.
proj_data = gs.parse_command("g.proj", flags="p", format="json")

df = pd.DataFrame.from_dict(proj_data, orient='index')
print(df)
```

```sh
0
name Lambert Conformal Conic
proj lcc
datum nad83
a 6378137.0
es 0.006694380022900787
lat_1 36.16666666666666
lat_2 34.33333333333334
lat_0 33.75
lon_0 -79
x_0 609601.22
y_0 0
no_defs defined
unit Meter
units Meters
meters 1
```

## REFERENCES

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

Further reading:
### Further reading

- [ASPRS Grids and
Datum](https://www.asprs.org/asprs-publications/grids-and-datums)
Expand Down
15 changes: 8 additions & 7 deletions general/g.proj/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,13 @@ int main(int argc, char *argv[])
location->description = _("Name of new project (location) to create");

format = G_define_standard_option(G_OPT_F_FORMAT);
format->options = "plain,shell,json,wkt,proj4";
format->descriptions = _("plain;Human readable text output;"
"shell;shell script style text output;"
"json;JSON (JavaScript Object Notation);"
"wkt;Well-known text output;"
"proj4;PROJ.4 style text output;");
format->options = "plain,shell,wkt,projjson,proj4";
format->descriptions =
_("plain;Human readable text output;"
"shell;shell script style text output;"
"wkt;Well-known text output;"
"projjson;JSON (JavaScript Object Notation) version of WKT;"
"proj4;PROJ.4 style text output;");
format->guisection = _("Print");

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

/* Initialisation & Validation */

if (strcmp(format->answer, "json") == 0) {
if (strcmp(format->answer, "projjson") == 0) {
outputFormat = JSON;
}
else if (strcmp(format->answer, "shell") == 0) {
Expand Down
104 changes: 76 additions & 28 deletions general/g.proj/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,32 @@
static int check_xy(enum OutputFormat);
static void print_json(G_JSON_Value *);

#if PROJ_VERSION_MAJOR >= 6
static void print_projjson(void);
#endif

/* print projection information gathered from one of the possible inputs
* in GRASS format */
void print_projinfo(enum OutputFormat format)
{
int i;
G_JSON_Value *value = NULL;
G_JSON_Object *object = NULL;

if (check_xy(format))
return;

if (format == PLAIN)
if (format == JSON) {
#if PROJ_VERSION_MAJOR >= 6
print_projjson();

return;
#else
G_fatal_error(_("JSON output is not available."));
#endif
}
if (format == PLAIN) {
fprintf(
stdout,
"-PROJ_INFO-------------------------------------------------\n");
else if (format == JSON) {
value = G_json_value_init_object();
if (value == NULL) {
G_fatal_error(
_("Failed to initialize JSON object. Out of memory?"));
}
object = G_json_object(value);
}

for (i = 0; i < projinfo->nitems; i++) {
Expand All @@ -67,17 +71,13 @@ void print_projinfo(enum OutputFormat format)
fprintf(stdout, "%-11s: %s\n", projinfo->key[i],
projinfo->value[i]);
break;
case JSON:
G_json_object_set_string(object, projinfo->key[i],
projinfo->value[i]);
break;
case PROJ4:
case WKT:
case JSON:
break;
}
}

/* TODO: use projsrid instead */
if (projsrid) {
switch (format) {
case PLAIN:
Expand All @@ -88,11 +88,9 @@ void print_projinfo(enum OutputFormat format)
case SHELL:
fprintf(stdout, "%s=%s\n", "srid", projsrid);
break;
case JSON:
G_json_object_set_string(object, "srid", projsrid);
break;
case PROJ4:
case WKT:
case JSON:
break;
}
}
Expand All @@ -111,21 +109,14 @@ void print_projinfo(enum OutputFormat format)
fprintf(stdout, "%s=%s\n", projunits->key[i],
projunits->value[i]);
break;
case JSON:
G_json_object_set_string(object, projunits->key[i],
projunits->value[i]);
break;
case PROJ4:
case WKT:
case JSON:
break;
}
}
}

if (format == JSON) {
print_json(value);
}

return;
}

Expand Down Expand Up @@ -361,7 +352,8 @@ static int check_xy(enum OutputFormat format)
}
object = G_json_object(value);

G_json_object_set_string(object, "name", "xy_location_unprojected");
G_json_object_set_string(object, "name",
"XY location (unprojected)");

print_json(value);
break;
Expand All @@ -375,7 +367,6 @@ static int check_xy(enum OutputFormat format)
return 0;
}

/* TODO: use proj_as_projjson() from proj */
void print_json(G_JSON_Value *value)
{
char *serialized_string = G_json_serialize_to_string_pretty(value);
Expand All @@ -387,3 +378,60 @@ void print_json(G_JSON_Value *value)
G_json_free_serialized_string(serialized_string);
G_json_value_free(value);
}

#if PROJ_VERSION_MAJOR >= 6
void print_projjson(void)
{
/* PROJ6+: create a PJ object from wkt or srid,
* then get PROJJSON using PROJ API */
const char *projstr = NULL;
PJ *obj = NULL;

if (check_xy(PLAIN))
return;

if (projwkt) {
obj = proj_create_from_wkt(NULL, projwkt, NULL, NULL, NULL);
}
if (!obj && projsrid) {
obj = proj_create(NULL, projsrid);
}
if (!obj && projepsg) {
int epsg_num;
char *buf = NULL;

epsg_num = atoi(G_find_key_value("epsg", projepsg));
if (epsg_num) {
G_asprintf(&buf, "EPSG:%d", epsg_num);
obj = proj_create(NULL, buf);
G_free(buf);
}
}
if (!obj) {
char *outwkt;

outwkt = GPJ_grass_to_wkt(projinfo, projunits, 0, 0);
/* datum info might be incomplete or incorrect */
if (outwkt) {
obj = proj_create_from_wkt(NULL, projwkt, NULL, NULL, NULL);
G_free(outwkt);
}
}
if (obj) {
projstr = proj_as_projjson(NULL, obj, NULL);

if (projstr)
projstr = G_store(projstr);
proj_destroy(obj);
}

if (projstr) {
fprintf(stdout, "%s\n", projstr);
G_free((char *)projstr);
}
else
G_warning(_("Unable to convert to PROJJSON"));

return;
}
#endif
9 changes: 6 additions & 3 deletions general/g.proj/testsuite/test_g_proj.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,16 @@ def test_shell_output(self):

self.assertEqual(result_flag, result_format)

def test_proj_info_output_json(self):
def test_projjson_output(self):
"""Test if g.proj returns consistent projection info in JSON format."""
# proj has its own PROJJSON format, use this?
module = SimpleModule("g.proj", flags="p", format="json")
module = SimpleModule("g.proj", flags="p", format="projjson")
self.assertModule(module)
result = json.loads(module.outputs.stdout)
self.assert_keys_in_grass_output(result)
self.assertEqual("ProjectedCRS", result["type"])
# the base GEOGCRS must be "NAD83(HARN)" with corresponding EPSG code
self.assertEqual("EPSG", result["base_crs"]["id"]["authority"])
self.assertEqual(4152, result["base_crs"]["id"]["code"])


if __name__ == "__main__":
Expand Down
Loading
Loading