Skip to content

Conversation

@metzm
Copy link
Contributor

@metzm metzm commented Nov 14, 2025

JSON output has been added to g.proj with PR 5419 . However, this converts GRASS native projection information to JSON which is not supported outside GRASS and even in GRASS, becoming rather useless with the changes introduced since PROJ 6.

PROJJSON is a standard representation for coordinate reference systems (CRS) in JSON format, used in various geospatial applications, including GeoParquet for storing CRS information.

The Schema for the PROJJSON format is defined at: https://proj.org/en/latest/schemas/v0.7/projjson.schema.json

From https://proj.org/en/stable/specifications/projjson.html

PROJJSON is a JSON encoding of WKT2:2019 / ISO-19162:2019: Geographic information - Well-known text representation of coordinate reference systems, which itself implements the model of ISO19111. Apart from the difference of encodings, the semantics is intended to be exactly the same as WKT2:2019, and PROJJSON can be morphed losslessly from/into WKT2:2019.

PROJJSON is aimed at encoding definitions of coordinate reference systems (and their composing objects: datums, datum ensembles, coordinate systems, conversion) and coordinate operations.

This PR replaces the JSON output of g.proj with the PROJJSON standard.

@metzm metzm added this to the 8.5.0 milestone Nov 14, 2025
@metzm metzm requested a review from wenzeslaus November 14, 2025 18:43
@metzm metzm added C Related code is in C module general labels Nov 14, 2025
@echoix
Copy link
Member

echoix commented Nov 14, 2025

Very nice to see a standards-based approach improving inter compatibility

@github-actions github-actions bot added Python Related code is in Python tests Related to Test Suite libraries labels Nov 15, 2025
nilason
nilason previously approved these changes Nov 15, 2025
Copy link
Contributor

@nilason nilason left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great improvement!

@echoix
Copy link
Member

echoix commented Nov 17, 2025

I think after merging #6579, it would be a good idea to merge main into this PR and check one last time, since it also plays into the tests of the same subject.
Making sure there isn't any semantic conflicts with these two

Copy link
Contributor

@petrasovaa petrasovaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! We were originally thinking this would be separate format though, to keep it consistent with the rest of the JSON implementations in other tools. So I would argue we should add option format=projjson, especially since that is the official name of that format. If the version doesn't allow to print it, it would stop with some helpful error message. Currently with this PR the format=json gives you completely different schema depending on you proj version, which doesn't seem right.

@metzm
Copy link
Contributor Author

metzm commented Nov 17, 2025

Currently with this PR the format=json gives you completely different schema depending on you proj version, which doesn't seem right.

PROJJSON has been available since PROJ 6, as well as WKT2. That means your test test_wkt_output would also fail with an old PROJ version < 6 because this test expects WKT2.

Considering that 1) all currently maintained linux distros have at least PROJ 6 and 2) the minimum required PROJ version for GRASS 8.5 is likely to be bumped to PROJ 8 there is practically no chance that an unexpected output is generated by g.proj -p format=json or g.proj -p format=wkt.

@metzm
Copy link
Contributor Author

metzm commented Nov 17, 2025

json output has been added to g.proj relatively recently with #5419 and it was IMHO an arbitrary decision to wrap the traditional GRASS projection info into json instead of using a standard json format also recognized by other software. This PR tries to correct this.

@metzm
Copy link
Contributor Author

metzm commented Nov 17, 2025

For above reasons, I would argue to replace the json format option with projjson and remove code for the current json output. Or do not introduce projjson, keep json but provide only PROJJSON output.

The reason is that WKT2 (PROJJSON is the json version of WKT) is now the preferred format for CRS definitions (works also correctly in the absence of authority name and code) and the traditional GRASS format for CRS definitons should be retired because superseded by PROJ 6+ CRS definition and coordinate transformation management.

@petrasovaa
Copy link
Contributor

petrasovaa commented Nov 17, 2025

In that case I would keep only "projjson" option, since that is the name of the format. The documentation then needs to be updated.

In case of XY projects, it prints:

{
    "name": "xy_location_unprojected"
}

which is technically not projjson format.

@metzm
Copy link
Contributor Author

metzm commented Nov 17, 2025

In that case I would keep only "projjson" option, since that is the name of the format. The documentation then needs to be updated.

OK

In case of XY projects, it prints:

{
    "name": "xy_location_unprojected"
}

which is technically not projjson format.

In this case nothing should be printed, conforming to proj_as_projjson() and proj_as_wkt().

@wenzeslaus
Copy link
Member

Behavior with no CRS aka XY

There are multiple ways how to handle no projection or print "nothing".

JSON null literal

JSON has a null value. A null literal gets translated to None in Python with the json package:

>>> import json
>>> json.loads("null")
>>> json.loads("null") is None
True

Other empty values

There are other "empty" values which translate to False when converted to boolean; empty dictionary and empty string are the most relevant here:

>>> json.loads("{}")
{}
>>> json.loads('""')
''
>>> bool(json.loads("{}"))
False
>>> bool(json.loads('""'))
False

No text output

An empty string is not a valid JSON. An attempt to load an empty string ends with a traceback. When calling json.loads, you need to know if the input is valid JSON or get ready for tracebacks.

>>> json.loads("")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

So, literally printing nothing would either end with traceback in case case of XY or it would require users to put additional checking code to figure out the if it is XY or not.

Current behavior with the custom JSON

With EPSG, this works as expected:

export PYTHONPATH="$(grass --config python-path)"
python
gs.create_project("/tmp/project_epsg", crs="EPSG:3358")
gs.setup.init("/tmp/project_epsg")
print(tools.g_proj(flags="p", format="json").text)
{
    "name": "NAD83(HARN) \/ North Carolina",
    "datum": "nad83harn",
    "ellps": "grs80",
    "proj": "lcc",
    "lat_0": "33.75",
    "lon_0": "-79",
    "lat_1": "36.1666666666667",
    "lat_2": "34.3333333333333",
    "x_0": "609601.22",
    "y_0": "0",
    "no_defs": "defined",
    "srid": "EPSG:3358",
    "unit": "meter",
    "units": "meters",
    "meters": "1"
}

With XY project:

gs.create_project("/tmp/project_xy")
gs.setup.init("/tmp/project_xy")
print(tools.g_proj(flags="p", format="json").text)
{
    "name": "xy_location_unprojected"
}

I'm not excited about the output, but it is a valid JSON. The check for XY vs others is possible and explicit, although not ideal:

>>> tools.g_proj(flags="p", format="json")["name"] == "xy_location_unprojected"
True

Special xy_location_unprojected value as a name

The reason for the xy_location_unprojected special value is legacy from the -g flag, also to format="shell":

>>> tools.g_proj(flags="g").keyval["name"] == "xy_location_unprojected"
True
>>> tools.g_proj(flags="p", format="shell").keyval["name"] == "xy_location_unprojected"
True

Final considerations

  • If we say the current format="json" is wrong, we should also remove format="shell" (and change the reason why -g is deprecated).
  • We need a way to signal "no projection" (XY) with g.proj, ideally with one call.
    • g.region can do it, but g.proj is the number one tool to deal with this.
    • g.proj output should always be a valid JSON or the tool itself should give an error (rather than outputting invalid JSON).
    • Signaling "no projection" as an error does not seem good to me because it is a valid state from GRASS perspective.
    • If PROJJSON has some sort of unprojected XY, non-georeferenced plain, Cartesian CRS, it should be used.
    • Literal null is a way to represent "no data available" so it corresponds fairly well to "no projection information available".
  • As an alternative, PROJJSON could be nested inside the JSON output, e.g., under key projjson. No PROJJSON available, would result in "projjson": null.

@metzm
Copy link
Contributor Author

metzm commented Nov 18, 2025

Considering that PROJJSON is supposed to be compatible with WKT, PROJJSON and WKT output for a XY GRASS project, no CRS set, should be similar. For WKT it is

XY location (unprojected)

the POJJSON equivalent would be

{
    "name": "XY location (unprojected)"
}

This gives valid (PROJ)JSON output, equivalent to WKT output and the information that a CRS is not defined for the given input to g.proj is provided in the output with both WKT and PROJJSON.

@github-actions github-actions bot added the HTML Related code is in HTML label Nov 18, 2025
@github-actions github-actions bot added docs markdown Related to markdown, markdown files labels Nov 18, 2025
@metzm metzm requested a review from petrasovaa November 19, 2025 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C Related code is in C docs general HTML Related code is in HTML libraries markdown Related to markdown, markdown files module Python Related code is in Python tests Related to Test Suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants