Skip to content

Commit a200e1f

Browse files
committed
tinshift: support TIN GeoPackage files
Such files are equivalent to already supported TIN JSON files, but scale better for arbitrarily large triangulations, in particular for network-based access. The format is defined in source/specifications/tin_gpkg.rst and the https://github.com/OSGeo/PROJ-data/blob/master/grid_tools/tin_json_to_gpkg_tin.py Python script may be used o convert an existing TIN JSON into a TIN GeoPackage.
1 parent c6a3dd1 commit a200e1f

24 files changed

+1709
-71
lines changed
22 KB
Binary file not shown.

data/tests/tinshift_empty_file.gpkg

Whitespace-only changes.
21 KB
Binary file not shown.
21 KB
Binary file not shown.
24 KB
Binary file not shown.
24 KB
Binary file not shown.

docs/source/operations/transformations/tinshift.rst

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ Triangulated Irregular Network based transformation
2020

2121

2222
The ``tinshift`` transformation takes one mandatory
23-
argument, ``file``, that points to a JSON file, which contains the
23+
argument, ``file``, that points to a :ref:`TIN JSON <tin_json>` or
24+
:ref:`TIN GeoPackage <tin_gpkg>` file, which contains the
2425
triangulation and associated metadata. Input and output coordinates must be
2526
geographic or projected coordinates.
26-
Depending on the content of the JSON file, horizontal, vertical or both
27+
Depending on the content of the TIN file, horizontal, vertical or both
2728
components of the coordinates may be transformed.
2829

2930
The transformation is invertible, with the same computational complexity than
@@ -37,7 +38,7 @@ Required
3738

3839
.. option:: +file=<filename>
3940

40-
Filename to the JSON file for the TIN.
41+
Filename or URL to the JSON or GeoPackage file for the TIN.
4142

4243

4344
Example
@@ -116,7 +117,14 @@ the inverse transformation), otherwise which triangle will be selected is
116117
unspecified. Besides that, the triangulation does not need to have particular
117118
properties (like being a Delaunay triangulation)
118119

119-
JSON File format
120-
++++++++++++++++
120+
JSON TIN File format
121+
++++++++++++++++++++
121122

122123
See :ref:`tin_json`.
124+
125+
GeoPackage TIN File format
126+
++++++++++++++++++++++++++
127+
128+
.. versionadded:: 9.8.0
129+
130+
See :ref:`tin_gpkg`.

docs/source/specifications/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ for the sake of broader interoperability.
1212

1313
projjson
1414
geodetictiffgrids
15+
tin_gpkg
1516
tin_json
1617

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
.. _tin_gpkg:
2+
3+
================================================================================
4+
Triangulated irregular network (TIN) GeoPackage format
5+
================================================================================
6+
7+
Introduction
8+
------------
9+
10+
The TIN GeoPackage format defines the format for a triangulation model stored in a
11+
`GeoPackage <https://geopackage.org>`__ file, that defines a horizontal and/or
12+
vertical transformation between two CRS.
13+
14+
That file format may be used by the :ref:`tinshift <tinshift>` operation since
15+
PROJ 9.8
16+
17+
Note: this format has similar capabilities than the :ref:`TIN JSON <tin_json>` file format,
18+
but can handle arbitrarily large triangulations.
19+
20+
The `tin_json_to_gpkg_tin.py <https://github.com/OSGeo/PROJ-data/blob/master/grid_tools/tin_json_to_gpkg_tin.py>`__
21+
Python script can be used to convert a TIN JSON file into a TIN GeoPackage file.
22+
23+
Specification
24+
-------------
25+
26+
The file MUST be a GeoPackage v1.2, v1.3 or v1.4 file.
27+
28+
``vertices`` table
29+
++++++++++++++++++
30+
31+
The file MUST contain a ``vertices`` GeoPackage table, of geometry type ``POINT``,
32+
registered in the ``gpkg_contents`` table with ``data_type`` set to ``features``.
33+
34+
The ``x_min``, ``y_min``, ``x_max`` and ``y_max`` columns record in the
35+
record of ``gpkg_contents`` MUST contain the bounding box of all vertices.
36+
37+
The CRS referenced by the table MUST be the source CRS of the transformation.
38+
39+
The ``vertices`` table MUST have at least the following columns:
40+
41+
- ``fid``: the integer primary key column.
42+
43+
- ``geom``: the GeoPackage POINT geometry column. There are constraints on the
44+
format of the GeoPackage BLOB. Both the GeoPackage header and the WKB MUST use Intel
45+
byte order. The GeoPackage header MUST NOT have an envelope.
46+
The point encodes the coordinates in the source CRS (equivalent of the
47+
``source_x`` and ``source_y`` columns in the TIN JSON format. Such colums
48+
may also be added to the ``vertices`` table, but they are not used by the
49+
PROJ implementation)
50+
51+
If the TIN defines a horizontal shift, the following columns MUST be present:
52+
53+
- ``target_x``: target X coordinate, in a column of type REAL, if the TIN defines a horizontal shift.
54+
55+
- ``target_y``: target Y coordinate, in a column of type REAL, if the TIN defines a horizontal shift.
56+
57+
If the TIN defines a vertical shift, the following columns MUST be present:
58+
59+
- ``source_z`` and ``target_z``: source and target Z coordinate, in columns of type REAL
60+
61+
- (or) ``offset_z``: value of ``target_z`` - ``source_z``, in a column of type REAL
62+
63+
Additional columns may be present, but are not used by PROJ.
64+
65+
The ``vertices`` table may have a RTree spatial index, but this is not used
66+
by PROJ.
67+
68+
69+
``triangles_def`` table
70+
+++++++++++++++++++++++
71+
72+
The file MUST contain a ``triangles_def`` GeoPackage table.
73+
74+
Its ``data_type`` registered in the ``gpkg_contents`` may be ``attributes``,
75+
or ``features``. This is not used by PROJ.
76+
77+
The ``triangles_def`` table MUST have at least the following columns:
78+
79+
- ``fid``: the integer primary key column.
80+
81+
- ``idx_vertex1``: foreign key to the ``fid`` column of ``vertices``, for the 1st vertex of the triangle.
82+
83+
- ``idx_vertex2``: foreign key to the ``fid`` column of ``vertices``, for the 2nd vertex of the triangle.
84+
85+
- ``idx_vertex3``: foreign key to the ``fid`` column of ``vertices``, for the 3rd vertex of the triangle.
86+
87+
Additional columns may be present, but are not used by PROJ.
88+
89+
90+
``gpkg_metadata`` and ``gpkg_metadata_reference`` tables
91+
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
92+
93+
The file MUST contain the ``gpkg_metadata`` and ``gpkg_metadata_reference`` tables,
94+
as defined by the `GeoPackage metadata <https://www.geopackage.org/spec/#extension_metadata>`__
95+
extension.
96+
97+
The ``gpkg_metadata`` table MUST contain the following record:
98+
99+
- ``id`` = 1
100+
101+
- ``md_scope`` = ``dataset``
102+
103+
- ``md_uri`` = ``https://proj.org``
104+
105+
- ``mime_type`` = ``application/json``
106+
107+
- ``metadata``: serialized JSON content of the metadata described below.
108+
109+
The ``gpkg_metadata_reference`` table MUST contain the following record:
110+
111+
- ``reference_scope`` = ``geopackage``
112+
113+
- ``table_name`` = NULL
114+
115+
- ``column_name`` = NULL
116+
117+
- ``timestamp``: any ISO-8601 valid value
118+
119+
- ``row_id_value`` = NULL
120+
121+
- ``md_file_id`` = 1
122+
123+
- ``md_parent_id`` = NULL
124+
125+
126+
JSON content in the ``gpkg_metadata`` table
127+
+++++++++++++++++++++++++++++++++++++++++++
128+
129+
Required members
130+
~~~~~~~~~~~~~~~~
131+
132+
The content of the record of id 1 in the ``gpkg_metadata`` table MUST be a
133+
serialized JSON object, with the following required members:
134+
135+
- ``file_type`` = ``triangulation_file``
136+
137+
- ``format_version`` = ``1.0`` or ``1.1``
138+
139+
- ``transformed_components``: an array which may contain one or two strings:
140+
``horizontal`` when horizontal components of the coordinates are transformed
141+
and/or ``vertical`` when the vertical component is transformed.
142+
143+
Optional members used by PROJ
144+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
145+
146+
- ``fallback_strategy``
147+
148+
String identifying how to treat points that do not fall into any of the
149+
specified triangles. When this item is set, ``format_version`` must be set to >= 1.1.
150+
Possible values are ``none``, ``nearest_side`` and ``nearest_centroid``. The
151+
default is ``none`` and signifies, that points that fall outside the
152+
specified triangles are not transformed. This is also the behavior for
153+
``format_version`` before 1.1. If ``fallback_strategy`` is set to
154+
``nearest_side``, then points that do not fall into any triangle are
155+
transformed according to the triangle closest to them by euclidean distance.
156+
If ``fallback_strategy`` is set to ``nearest_centroid``, then points that do
157+
not fall into any triangle are transformed according to the triangle with the
158+
closest centroid (intersection of its medians).
159+
160+
Conditional members
161+
~~~~~~~~~~~~~~~~~~~
162+
163+
When ``transformed_components`` contains the ``horizontal`` string, the JSON
164+
metadata MUST contain the additional members:
165+
166+
- ``min_shift_x``: minimum value of ``target_x`` - ``source_x`` over all vertices of the ``vertices`` table.
167+
168+
- ``max_shift_x``: maximum value of ``target_x`` - ``source_x`` over all vertices of the ``vertices`` table.
169+
170+
- ``min_shift_y``: minimum value of ``target_y`` - ``source_y`` over all vertices of the ``vertices`` table.
171+
172+
- ``max_shift_y``: maximum value of ``target_y`` - ``source_y`` over all vertices of the ``vertices`` table.
173+
174+
Note: those fields are used when performing inverse transformations
175+
176+
177+
If the above described ``fallback_strategy`` field is set to ``nearest_side`` or ``nearest_centroid``,
178+
a ``num_vertices`` member MUST be set with the number of vertices of the ``vertices`` table.
179+
180+
Note: this is used to compute the initial search radius in the inverse transformation for those modes.
181+
182+
Other optional members
183+
~~~~~~~~~~~~~~~~~~~~~~
184+
185+
All other members defined in the :ref:`TIN JSON <tin_json>` file format (except ``vertices``, ``vertices_columns``,
186+
``triangles`` and ``triangles_columns`` which are redundant with the GeoPackage ``vertices`` and ``triangles_def``
187+
tables) may be set.
188+
189+
``rtree_triangles_geom`` RTree virtual table
190+
++++++++++++++++++++++++++++++++++++++++++++
191+
192+
A RTree virtual table MUST be created as following:
193+
194+
.. code-block:: sql
195+
196+
CREATE VIRTUAL TABLE rtree_triangles_geom USING rtree(id, minx, maxx, miny, maxy)
197+
198+
199+
It MUST be filled with as many records as there are triangles in the ``triangles_def``
200+
table, with the ``id`` column of ``rtree_triangles_geom`` containing the
201+
corresponding value of the ``fid`` column of ``triangles_def``, and the
202+
``minx``, ``maxx``, ``miny``, ``maxy`` columns containing the bounding box of
203+
each triangle.
204+
205+
Optional ``triangles`` spatial view
206+
+++++++++++++++++++++++++++++++++++
207+
208+
The file MAY contain an optional ``triangles`` spatial view, extending the
209+
``triangles_def`` table with the GeoPackage geometry blob of each triangle.
210+
211+
Such spatial view can be used to display the triangles in a GIS software that
212+
supports GeoPackage (and spatial views), such as QGIS.
213+
214+
The `tin_json_to_gpkg_tin.py <https://github.com/OSGeo/PROJ-data/blob/master/grid_tools/tin_json_to_gpkg_tin.py>`__
215+
script creates such view with:
216+
217+
.. code-block:: python
218+
219+
srs_id_i32le = as_i32le_hex(srs_id)
220+
wkb_polygon_i32le = as_i32le_hex(3)
221+
number_rings_i32le = as_i32le_hex(1)
222+
number_vertices_i32le = as_i32le_hex(4)
223+
triangle_gpkg_prefix = f"47500001{srs_id_i32le}01{wkb_polygon_i32le}{number_rings_i32le}{number_vertices_i32le}"
224+
225+
# other_fields is typically ",v1.target_x, v1.target_y, v2.target_x, v2.target_y, v3.target_x, v3.target_y" for a horizontal transformation
226+
227+
# 14 = GPKG_header_size_without_envelope (8) + WKB point header (5) + base_one_index (1)
228+
ds.ExecuteSQL(
229+
f"CREATE VIEW triangles AS SELECT triangles_def.fid AS OGC_FID {other_fields}, CAST(X'{triangle_gpkg_prefix}' || substr(v1.geom, 14) || substr(v2.geom, 14) || substr(v3.geom, 14) || substr(v1.geom, 14) AS BLOB) AS geom FROM triangles_def LEFT JOIN vertices v1 ON idx_vertex1 = v1.fid LEFT JOIN vertices v2 ON idx_vertex2 = v2.fid LEFT JOIN vertices v3 ON idx_vertex3 = v3.fid"
230+
)
231+
ds.ExecuteSQL(
232+
f"INSERT INTO gpkg_contents (table_name, identifier, data_type, srs_id, min_x, min_y, max_x, max_y) VALUES ('triangles', 'triangles', 'features', {srs_id}, {min_x}, {min_y}, {max_x}, {max_y})"
233+
)
234+
ds.ExecuteSQL(
235+
f"INSERT INTO gpkg_geometry_columns (table_name, column_name, geometry_type_name, srs_id, z, m) values ('triangles', 'geom', 'POLYGON', {srs_id}, 0, 0)"
236+
)
237+
238+
239+
.. spelling:word-list::
240+
241+
RTree

docs/source/specifications/tin_json.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ file.
1010
That file format may be used by the :ref:`tinshift <tinshift>` operation since
1111
PROJ 7.2
1212

13+
Note: the :ref:`TIN GeoPackage <tin_gpkg>` file format can also be used since
14+
PROJ 9.8. It can handle arbitrarily large triangulations.
15+
1316
Below a minimal example, from the KKJ to ETRS89 transformation, with just a
1417
single triangle:
1518

0 commit comments

Comments
 (0)