1.. _rfc-61:
2
3=======================================================================================
4RFC 61 : Support for measured geometries
5=======================================================================================
6
7Author: Ari Jolma
8
9Contact: ari.jolma at gmail.com
10
11Status: Adopted
12
13Implementation in version: 2.1
14
15Summary
16-------
17
18This RFC defines how to implement measured geometries (geometries, where
19the points have M coordinate, i.e., they are XYM or XYZM).
20
21Rationale
22---------
23
24An M coordinate, which is also known as "measure", is an additional
25value that can be stored for each point of a geometry (IBM Technical
26Note,
27`https://www-304.ibm.com/support/docview.wss?uid=swg21054384 <https://www-304.ibm.com/support/docview.wss?uid=swg21054384>`__).
28
29M coordinate is in the OGC simple feature model and it is used in many
30vector data formats.
31
32Changes
33-------
34
35Changes are required into the C++ API and the C API needs to be
36enhanced. Several drivers need to be changed to take advantage of this
37enhancement but also due to the changes in the C++ API.
38
39Common API
40~~~~~~~~~~
41
42New OGRwkbGeometryType values are needed. SFSQL 1.2 and ISO SQL/MM Part
433 will be used, i.e., 2D type + 2000 for M and 2D type + 3000 for ZM.
44(Also types such as Tin, PolyhedralSurface and Triangle types can be
45added for completeness, even if unimplemented currently). wkbCurve and
46wkbSurface have been moved from #define to the OGRwkbGeometryType
47enumerations, and their Z/M/ZM variants have been added as well (per
48#6401)
49
50On a more general note, there could (should?) be a path to using a clean
51set of values and have legacy support as an exception.
52
53Abstract types are defined and not part of the enum.
54
55::
56
57   // additions to enum OGRwkbGeometryType
58       wkbCurve = 13,          /**< Curve (abstract type). ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
59       wkbSurface = 14,        /**< Surface (abstract type). ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
60       wkbPolyhedralSurface = 15,/**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
61       wkbTIN = 16,              /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
62       wkbTriangle = 17,         /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
63
64       wkbCurveZ = 1013,           /**< wkbCurve with Z component. ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
65       wkbSurfaceZ = 1014,         /**< wkbSurface with Z component. ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
66       wkbPolyhedralSurfaceZ = 1015,  /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
67       wkbTINZ = 1016,                /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
68       wkbTriangleZ = 1017,           /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
69
70       wkbPointM = 2001,              /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
71       wkbLineStringM = 2002,         /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
72       wkbPolygonM = 2003,            /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
73       wkbMultiPointM = 2004,         /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
74       wkbMultiLineStringM = 2005,    /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
75       wkbMultiPolygonM = 2006,       /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
76       wkbGeometryCollectionM = 2007, /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
77       wkbCircularStringM = 2008,     /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
78       wkbCompoundCurveM = 2009,      /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
79       wkbCurvePolygonM = 2010,       /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
80       wkbMultiCurveM = 2011,         /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
81       wkbMultiSurfaceM = 2012,       /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
82       wkbCurveM = 2013,              /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
83       wkbSurfaceM = 2014,            /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
84       wkbPolyhedralSurfaceM = 2015,  /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
85       wkbTINM = 2016,                /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
86       wkbTriangleM = 2017,           /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
87
88       wkbPointZM = 3001,              /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
89       wkbLineStringZM = 3002,         /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
90       wkbPolygonZM = 3003,            /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
91       wkbMultiPointZM = 3004,         /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
92       wkbMultiLineStringZM = 3005,    /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
93       wkbMultiPolygonZM = 3006,       /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
94       wkbGeometryCollectionZM = 3007, /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
95       wkbCircularStringZM = 3008,     /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
96       wkbCompoundCurveZM = 3009,      /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
97       wkbCurvePolygonZM = 3010,       /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
98       wkbMultiCurveZM = 3011,         /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
99       wkbMultiSurfaceZM = 3012,       /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
100       wkbCurveZM = 3013,              /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
101       wkbSurfaceZM = 3014,            /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
102       wkbPolyhedralSurfaceZM = 3015,  /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
103       wkbTINZM = 3016,                /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
104       wkbTriangleZM = 3017,           /**< ISO SQL/MM Part 3. GDAL &gt;= 2.1 */
105
106   // add tests for M
107   #define wkbHasM(x)     OGR_GT_HasM(x)
108   #define wkbSetM(x)     OGR_GT_SetM(x)
109
110   OGRwkbGeometryType CPL_DLL OGR_GT_SetM( OGRwkbGeometryType eType );
111   int                CPL_DLL OGR_GT_HasM( OGRwkbGeometryType eType );
112
113
114C++ API
115~~~~~~~
116
117The property int nCoordinateDimension in class OGRGeometry will be
118replaced by int flags. It may have the following flags:
119
120::
121
122   #define OGR_G_NOT_EMPTY_POINT 0x1
123   #define OGR_G_3D 0x2
124   #define OGR_G_MEASURED 0x4
125   #define OGR_G_IGNORE_MEASURED 0x8
126
127The "ignore" flag is needed internally for backwards compatibility. The
128flag OGR_G_NOT_EMPTY_POINT is used only to denote the emptiness of an
129OGRPoint object.
130
131Currently a hack to set nCoordDimension negative is used to denote an
132empty point.
133
134The removal of nCoordinateDimension may imply changes to drivers etc.
135which get or set it.
136
137The tests are
138
139::
140
141   Is3D = flags & OGR_G_3D
142   IsMeasured = flags & OGR_G_MEASURED
143
144The setters and getters are implemented with \|= and &=.
145
146When any of these flags is set or unset, the corresponding data becomes
147invalid and may be discarded.
148
149Keep the following methods with original semantics, i.e., coordinate
150dimension is 2 or 3, but deprecate. There is some discrepancy in
151documentation. Their documentation says that they may return zero for
152empty points while in ogrpoint.cpp it says that negative nCoordDimension
153values are used for empty points and the getCoordinateDimension method
154of point returns absolute value of nCoordDimension - thus not zero. A
155fix to the doc is probably enough.
156
157::
158
159   int getCoordinateDimension();
160   void setCoordinateDimension(int nDimension);
161   void flattenTo2D()
162
163It is proposed to possibly add a new method to replace
164getCoordinateDimension. set3D and setMeasured would replace
165setCoordinateDimension and flattenTo2D. See below.
166
167class OGRGeometry:
168
169::
170
171   //Possibly add methods (SF Common Architecture):
172   int Dimension(); // -1 for empty geometries (to denote undefined), 0 for points, 1 for curves, 2 for surfaces, max of components for collections
173   char *GeometryType(); // calls OGRToOGCGeomType (which needs to be enhanced)
174
175   //Add methods (SF Common Architecture) see above for implementation:
176   int CoordinateDimension(); // 2 if not 3D and not measured, 3 if 3D or measured, 4 if 3D and measured
177   OGRBoolean Is3D() const;
178   OGRBoolean IsMeasured() const;
179
180   //Add methods (non-standard; note the use of one method instead of second unset* method):
181   virtual void set3D(OGRBoolean bIs3D);
182   virtual void setMeasured(OGRBoolean bIsMeasured);
183
184   //Add now or later methods:
185   virtual OGRGeometry *LocateAlong(double mValue);
186   virtual OGRGeometry *LocateBetween(double mStart, double mEnd);
187
188   //Remove b3D from importPreambleFromWkb: it is not used, the flags are managed within the method.
189
190int CoordinateDimension() should have the new semantics. The method name
191in simple features documents actually is without prefix get.
192
193Whether set3D and setMeasured should affect the children geometries in a
194collection is an issue. Currently doc for setCoordinateDimension says
195"Setting the dimension of a geometry collection will affect the children
196geometries.", thus we have already committed to maintaining dimensions
197of children in collections. It is proposed that set3D and setMeasured
198either add or strip Z or M values to or from the geometry (including
199possible children). In general the strategy should be to follow the
200existing strategy regarding Z (i.e., to strip or add).
201
202Add property double m to class OGRPoint. Add constructor, getters, and
203setters for it.
204
205Add property double \*padfM to class OGRSimpleCurve. Add constructor,
206getters, and setters for it. New setters with postfix M are needed for
207XYM data since the object may be upgraded to XYZ from XY in setters. Add
208also methods RemoveM() and AddM() with similar semantics as Make3D and
209Make2D.
210
211Override methods set3D and setMeasured in those classes where
212setCoordinateDimension is overridden.
213
214Change the semantics of methods whose name begins with \_ and have a
215parameter "int b3D". The parameter will be "int coordinates", i.e., a
216flags like int, which tells about Z and M.
217
218C API
219~~~~~
220
221ogr_core.h:
222
223::
224
225   OGRwkbGeometryType CPL_DLL OGR_GT_SetM( OGRwkbGeometryType eType );
226   int                CPL_DLL OGR_GT_HasM( OGRwkbGeometryType eType );
227
228The current behavior is that calling SetPoint on a geometry with
229coordinate dimension 2 upgrades the coordinate dimension 3. To keep 2D
230points 2D SetPoint_2D must be used. Thus we need separate functions for
231M and ZM geometries. The proposal is to use postfixes M and ZM, i.e.,
232SetPointM, SetPointZM. Similarly for AddPoint.
233
234Currently there is no SetPoints_2D function. The doc at pabyZ param at
235SetPoints comments that "defaults to NULL for 2D objects" but that does
236not seem to be the case. See #6344. If that is fixed as written there,
237then only SetPointsZM is needed.
238
239GetPoint and GetPoints do not have a 2D version, so only \*ZM version is
240needed.
241
242ogr_api.h:
243
244::
245
246   void   CPL_DLL OGR_G_Is3D( OGRGeometryH );
247   void   CPL_DLL OGR_G_IsMeasured( OGRGeometryH );
248
249   void   CPL_DLL OGR_G_Set3D( OGRGeometryH, int );
250   void   CPL_DLL OGR_G_SetMeasured( OGRGeometryH, int );
251
252   double CPL_DLL OGR_G_GetM( OGRGeometryH, int );
253
254ogr_p.h (This is public header, so new functions are needed)
255
256::
257
258   const char CPL_DLL * OGRWktReadPointsM( const char * pszInput,
259                                          OGRRawPoint **ppaoPoints,
260                                          double **ppadfZ,
261                                           double **ppadfM,
262                                          int * pnMaxPoints,
263                                          int * pnReadPoints );
264   void CPL_DLL OGRMakeWktCoordinateM( char *, double, double, double, double, int ); // int = flags OGR_G_3D OGR_G_MEASURED
265   // Change the semantics of OGRReadWKBGeometryType: b3D is not used and the returned eGeometryType may may any valid type
266
267pggeometry.h is internal, so we can change the function prototype
268
269::
270
271   void OGRCreateFromMultiPatchPart(OGRMultiPolygon *poMP,
272                                    OGRPolygon*& poLastPoly,
273                                    int nPartType,
274                                    int nPartPoints,
275                                    double* padfX,
276                                    double* padfY,
277                                    double* padfZ,
278                                    double* padfM);
279
280Use of padfM requires changes to openfilegdb driver.
281
282GEOS, filters, and other issues
283~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284
285When a geometry with measures is sent to GEOS or used as a filter the M
286coordinate is ignored.
287
288LocateAlong and LocateBetween are the only standard methods, which use M
289but there could be others, which for example get the extent of M. Such
290are not intended to be added now but they can be added later.
291
292SWIG bindings (Python / Java / C# / Perl) changes
293-------------------------------------------------
294
295The new C API functions need to be exposed through swig. Further changes
296depend on whether the language bindings are aware of coordinates. At
297least Python and Perl are.
298
299The new geometry types will be included into the i files.
300
301Some new setters and getters are needed for M. Is3D, IsMeasured, Set3D
302and SetMeasured methods should be added. Also OGR_GT_HasM.
303
304Drivers
305-------
306
307Drivers that are probably affected by the C++ changes are at least
308(these use the CoordinateDimension API) pg, mssqlspatial, sqlite, db2,
309mysql, gml, pgdump, geojson, libkml, gpkg, wasp, gpx, filegdb, vfk, bna,
310dxf.
311
312The now deprecated CoordinateDimension API is proposed to be replaced
313with calls to \*3D and \*Measured.
314
315Once the support for M coordinates is in place the driver will advertise
316the support.
317
318Within the work of this RFC the support is built into memory, shape and
319pg drivers. Support for other drivers are left for further work.
320
321Utilities
322---------
323
324There is a minimum requirement and new possibilities.
325
326ogrinfo: report measured geom type, report measures
327
328ogr2ogr: support measured geom types
329
330ogrlineref: seems to deal specifically with measures, needs more thought
331
332gdal_rasterize: measure could be used for the burn-in value
333
334gdal_contour: measure could be used as the "elevation" value
335
336gdal_grid: measure could be used as the "Z" value
337
338Documentation
339-------------
340
341All new methods/functions are documented.
342
343Test Suite
344----------
345
346At least the initial tests will be done with Perl unit tests
347(swi/perl/t/measures-\*.t). Later autotest suite will be extended.
348Existing tests should not fail.
349
350Compatibility Issues
351--------------------
352
353Many drivers (actually datasets and layers) which support measures need
354to have the support added. Support should be advertised using
355
356::
357
358   #define ODsCMeasuredGeometries   "MeasuredGeometries"
359   #define OLCMeasuredGeometries    "MeasuredGeometries"
360
361The entry point for a creating a layer is CreateLayer method in
362GDALDataset. If the dataset does not support measured geometries it will
363strip the measured flag from the geometry type it gets as a parameter.
364This is in line with current behavior non linear geometry types and
365datasets not supporting them.
366
367ICreateLayer, which all drivers that have create layer capability
368implement, have geometry type as an argument. The method should call
369CPLError() with CPLE_NotSupported and return NULL if the driver does not
370support measures. Similarly for ICreateFeature and ISetFeature.
371
372The user-oriented API functions (CreateLayer, CreateFeature, and
373SetFeature) should (silently) strip out the measures before continuing
374to the I\* methods in drivers that do not support measures. This (side
375effect) may not be what is wanted in some usage scenarios but it would
376follow the pattern of what is already done with nonlinear geometries.
377This should be documented.
378
379An alternative would be to store M value(s) (or WKT or WKB) as attribute
380(scalar or vector, depending on the geometry type).
381
382Needs a decision.
383
384Some incompatibilities will necessarily be introduced. For example when
385the current XYM-as-XYZ hack in shape will be replaced by proper XYM.
386
387Related tickets
388---------------
389
390`https://trac.osgeo.org/gdal/ticket/6063 <https://trac.osgeo.org/gdal/ticket/6063>`__
391`https://trac.osgeo.org/gdal/ticket/6331 <https://trac.osgeo.org/gdal/ticket/6331>`__
392
393Implementation
394--------------
395
396The implementation will be done by Ari Jolma.
397
398The proposed implementation will be in
399`https://github.com/ajolma/GDAL-XYZM <https://github.com/ajolma/GDAL-XYZM>`__
400
401Voting history
402--------------
403
404+1 from Even, Tamas, Jukka and Daniel
405