Coverage Report

Created: 2025-06-09 07:07

/src/gdal/ogr/ogrgeomcoordinateprecision.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  Implements OGRGeomCoordinatePrecision.
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "ogr_core.h"
14
#include "ogr_api.h"
15
#include "ogr_spatialref.h"
16
#include "ogr_geomcoordinateprecision.h"
17
18
#include <algorithm>
19
#include <cmath>
20
21
/************************************************************************/
22
/*                    OGRGeomCoordinatePrecisionCreate()                */
23
/************************************************************************/
24
25
/** Creates a new instance of OGRGeomCoordinatePrecision.
26
 *
27
 * The default X,Y,Z,M resolutions are set to OGR_GEOM_COORD_PRECISION_UNKNOWN.
28
 *
29
 * @since GDAL 3.9
30
 */
31
OGRGeomCoordinatePrecisionH OGRGeomCoordinatePrecisionCreate(void)
32
0
{
33
0
    static_assert(OGR_GEOM_COORD_PRECISION_UNKNOWN ==
34
0
                  OGRGeomCoordinatePrecision::UNKNOWN);
35
36
0
    return new OGRGeomCoordinatePrecision();
37
0
}
38
39
/************************************************************************/
40
/*                    OGRGeomCoordinatePrecisionDestroy()               */
41
/************************************************************************/
42
43
/** Destroy a OGRGeomCoordinatePrecision.
44
 *
45
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance or nullptr
46
 * @since GDAL 3.9
47
 */
48
void OGRGeomCoordinatePrecisionDestroy(
49
    OGRGeomCoordinatePrecisionH hGeomCoordPrec)
50
0
{
51
0
    delete hGeomCoordPrec;
52
0
}
53
54
/************************************************************************/
55
/*                 OGRGeomCoordinatePrecisionGetXYResolution()          */
56
/************************************************************************/
57
58
/** Get the X/Y resolution of a OGRGeomCoordinatePrecision
59
 *
60
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
61
 * @return the the X/Y resolution of a OGRGeomCoordinatePrecision or
62
 * OGR_GEOM_COORD_PRECISION_UNKNOWN
63
 * @since GDAL 3.9
64
 */
65
double OGRGeomCoordinatePrecisionGetXYResolution(
66
    OGRGeomCoordinatePrecisionH hGeomCoordPrec)
67
0
{
68
0
    VALIDATE_POINTER1(hGeomCoordPrec,
69
0
                      "OGRGeomCoordinatePrecisionGetXYResolution", 0);
70
0
    return hGeomCoordPrec->dfXYResolution;
71
0
}
72
73
/************************************************************************/
74
/*                 OGRGeomCoordinatePrecisionGetZResolution()           */
75
/************************************************************************/
76
77
/** Get the Z resolution of a OGRGeomCoordinatePrecision
78
 *
79
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
80
 * @return the the Z resolution of a OGRGeomCoordinatePrecision or
81
 * OGR_GEOM_COORD_PRECISION_UNKNOWN
82
 * @since GDAL 3.9
83
 */
84
double OGRGeomCoordinatePrecisionGetZResolution(
85
    OGRGeomCoordinatePrecisionH hGeomCoordPrec)
86
0
{
87
0
    VALIDATE_POINTER1(hGeomCoordPrec,
88
0
                      "OGRGeomCoordinatePrecisionGetZResolution", 0);
89
0
    return hGeomCoordPrec->dfZResolution;
90
0
}
91
92
/************************************************************************/
93
/*                 OGRGeomCoordinatePrecisionGetMResolution()           */
94
/************************************************************************/
95
96
/** Get the M resolution of a OGRGeomCoordinatePrecision
97
 *
98
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
99
 * @return the the M resolution of a OGRGeomCoordinatePrecision or
100
 * OGR_GEOM_COORD_PRECISION_UNKNOWN
101
 * @since GDAL 3.9
102
 */
103
double OGRGeomCoordinatePrecisionGetMResolution(
104
    OGRGeomCoordinatePrecisionH hGeomCoordPrec)
105
0
{
106
0
    VALIDATE_POINTER1(hGeomCoordPrec,
107
0
                      "OGRGeomCoordinatePrecisionGetMResolution", 0);
108
0
    return hGeomCoordPrec->dfMResolution;
109
0
}
110
111
/************************************************************************/
112
/*                  OGRGeomCoordinatePrecisionGetFormats()              */
113
/************************************************************************/
114
115
/** Get the list of format names for coordinate precision format specific
116
 * options.
117
 *
118
 * An example of a supported value for pszFormatName is
119
 * "FileGeodatabase" for layers of the OpenFileGDB driver.
120
 *
121
 * The returned values may be used for the pszFormatName argument of
122
 * OGRGeomCoordinatePrecisionGetFormatSpecificOptions().
123
 *
124
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
125
 * @return a null-terminated list to free with CSLDestroy(), or nullptr.
126
 * @since GDAL 3.9
127
 */
128
char **
129
OGRGeomCoordinatePrecisionGetFormats(OGRGeomCoordinatePrecisionH hGeomCoordPrec)
130
0
{
131
0
    VALIDATE_POINTER1(hGeomCoordPrec, "OGRGeomCoordinatePrecisionGetFormats",
132
0
                      nullptr);
133
0
    CPLStringList aosFormats;
134
0
    for (const auto &kv : hGeomCoordPrec->oFormatSpecificOptions)
135
0
    {
136
0
        aosFormats.AddString(kv.first.c_str());
137
0
    }
138
0
    return aosFormats.StealList();
139
0
}
140
141
/************************************************************************/
142
/*           OGRGeomCoordinatePrecisionGetFormatSpecificOptions()       */
143
/************************************************************************/
144
145
/** Get format specific coordinate precision options.
146
 *
147
 * An example of a supported value for pszFormatName is
148
 * "FileGeodatabase" for layers of the OpenFileGDB driver.
149
 *
150
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
151
 * @param pszFormatName A format name (one of those returned by
152
 * OGRGeomCoordinatePrecisionGetFormats())
153
 * @return a null-terminated list, or nullptr. The list must *not* be freed,
154
 * and is owned by hGeomCoordPrec
155
 * @since GDAL 3.9
156
 */
157
CSLConstList OGRGeomCoordinatePrecisionGetFormatSpecificOptions(
158
    OGRGeomCoordinatePrecisionH hGeomCoordPrec, const char *pszFormatName)
159
0
{
160
0
    VALIDATE_POINTER1(hGeomCoordPrec,
161
0
                      "OGRGeomCoordinatePrecisionGetFormatSpecificOptions",
162
0
                      nullptr);
163
0
    const auto oIter =
164
0
        hGeomCoordPrec->oFormatSpecificOptions.find(pszFormatName);
165
0
    if (oIter == hGeomCoordPrec->oFormatSpecificOptions.end())
166
0
    {
167
0
        return nullptr;
168
0
    }
169
0
    return oIter->second.List();
170
0
}
171
172
/************************************************************************/
173
/*          OGRGeomCoordinatePrecisionSetFormatSpecificOptions()        */
174
/************************************************************************/
175
176
/** Set format specific coordinate precision options.
177
 *
178
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
179
 * @param pszFormatName A format name (must not be null)
180
 * @param papszOptions null-terminated list of options.
181
 * @since GDAL 3.9
182
 */
183
void OGRGeomCoordinatePrecisionSetFormatSpecificOptions(
184
    OGRGeomCoordinatePrecisionH hGeomCoordPrec, const char *pszFormatName,
185
    CSLConstList papszOptions)
186
0
{
187
0
    VALIDATE_POINTER0(hGeomCoordPrec,
188
0
                      "OGRGeomCoordinatePrecisionSetFormatSpecificOptions");
189
0
    hGeomCoordPrec->oFormatSpecificOptions[pszFormatName] = papszOptions;
190
0
}
191
192
/************************************************************************/
193
/*                      OGRGeomCoordinatePrecisionSet()                 */
194
/************************************************************************/
195
196
/**
197
 * \brief Set the resolution of the geometry coordinate components.
198
 *
199
 * For the X, Y and Z ordinates, the precision should be expressed in the units
200
 * of the CRS of the geometry. So typically degrees for geographic CRS, or
201
 * meters/feet/US-feet for projected CRS.
202
 * Users might use OGRGeomCoordinatePrecisionSetFromMeter() for an even more
203
 * convenient interface.
204
 *
205
 * For a projected CRS with meters as linear unit, 1e-3 corresponds to a
206
 * millimetric precision.
207
 * For a geographic CRS in 8.9e-9 corresponds to a millimetric precision
208
 * (for a Earth CRS)
209
 *
210
 * Resolution should be stricty positive, or set to
211
 * OGR_GEOM_COORD_PRECISION_UNKNOWN when unknown.
212
 *
213
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
214
 * @param dfXYResolution Resolution for for X and Y coordinates.
215
 * @param dfZResolution Resolution for for Z coordinates.
216
 * @param dfMResolution Resolution for for M coordinates.
217
 * @since GDAL 3.9
218
 */
219
220
void OGRGeomCoordinatePrecisionSet(OGRGeomCoordinatePrecisionH hGeomCoordPrec,
221
                                   double dfXYResolution, double dfZResolution,
222
                                   double dfMResolution)
223
0
{
224
0
    VALIDATE_POINTER0(hGeomCoordPrec, "OGRGeomCoordinatePrecisionSet");
225
0
    hGeomCoordPrec->dfXYResolution = dfXYResolution;
226
0
    hGeomCoordPrec->dfZResolution = dfZResolution;
227
0
    hGeomCoordPrec->dfMResolution = dfMResolution;
228
0
}
229
230
/************************************************************************/
231
/*                  OGRGeomCoordinatePrecisionSetFromMeter()            */
232
/************************************************************************/
233
234
/**
235
 * \brief Set the resolution of the geometry coordinate components.
236
 *
237
 * For the X, Y and Z ordinates, the precision should be expressed in meter,
238
 * e.g 1e-3 for millimetric precision.
239
 *
240
 * Resolution should be stricty positive, or set to
241
 * OGR_GEOM_COORD_PRECISION_UNKNOWN when unknown.
242
 *
243
 * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
244
 * @param hSRS Spatial reference system, used for metric to SRS unit conversion
245
 *             (must not be null)
246
 * @param dfXYMeterResolution Resolution for for X and Y coordinates, in meter.
247
 * @param dfZMeterResolution Resolution for for Z coordinates, in meter.
248
 * @param dfMResolution Resolution for for M coordinates.
249
 * @since GDAL 3.9
250
 */
251
void OGRGeomCoordinatePrecisionSetFromMeter(
252
    OGRGeomCoordinatePrecisionH hGeomCoordPrec, OGRSpatialReferenceH hSRS,
253
    double dfXYMeterResolution, double dfZMeterResolution, double dfMResolution)
254
0
{
255
0
    VALIDATE_POINTER0(hGeomCoordPrec, "OGRGeomCoordinatePrecisionSet");
256
0
    VALIDATE_POINTER0(hSRS, "OGRGeomCoordinatePrecisionSet");
257
0
    return hGeomCoordPrec->SetFromMeter(OGRSpatialReference::FromHandle(hSRS),
258
0
                                        dfXYMeterResolution, dfZMeterResolution,
259
0
                                        dfMResolution);
260
0
}
261
262
/************************************************************************/
263
/*                           GetConversionFactors()                     */
264
/************************************************************************/
265
266
static void GetConversionFactors(const OGRSpatialReference *poSRS,
267
                                 double &dfXYFactor, double &dfZFactor)
268
0
{
269
0
    dfXYFactor = 1;
270
0
    dfZFactor = 1;
271
272
0
    if (poSRS)
273
0
    {
274
0
        if (poSRS->IsGeographic())
275
0
        {
276
0
            dfXYFactor = poSRS->GetSemiMajor(nullptr) * M_PI / 180;
277
0
        }
278
0
        else
279
0
        {
280
0
            dfXYFactor = poSRS->GetLinearUnits(nullptr);
281
0
        }
282
283
0
        if (poSRS->GetAxesCount() == 3)
284
0
        {
285
0
            poSRS->GetAxis(nullptr, 2, nullptr, &dfZFactor);
286
0
        }
287
0
    }
288
0
}
289
290
/************************************************************************/
291
/*                OGRGeomCoordinatePrecision::SetFromMeter()            */
292
/************************************************************************/
293
294
/**
295
 * \brief Set the resolution of the geometry coordinate components.
296
 *
297
 * For the X, Y and Z coordinates, the precision should be expressed in meter,
298
 * e.g 1e-3 for millimetric precision.
299
 *
300
 * Resolution should be stricty positive, or set to
301
 * OGRGeomCoordinatePrecision::UNKNOWN when unknown.
302
 *
303
 * @param poSRS Spatial reference system, used for metric to SRS unit conversion
304
 *              (must not be null)
305
 * @param dfXYMeterResolution Resolution for for X and Y coordinates, in meter.
306
 * @param dfZMeterResolution Resolution for for Z coordinates, in meter.
307
 * @param dfMResolutionIn Resolution for for M coordinates.
308
 * @since GDAL 3.9
309
 */
310
void OGRGeomCoordinatePrecision::SetFromMeter(const OGRSpatialReference *poSRS,
311
                                              double dfXYMeterResolution,
312
                                              double dfZMeterResolution,
313
                                              double dfMResolutionIn)
314
0
{
315
0
    double dfXYFactor = 1;
316
0
    double dfZFactor = 1;
317
0
    GetConversionFactors(poSRS, dfXYFactor, dfZFactor);
318
319
0
    dfXYResolution = dfXYMeterResolution / dfXYFactor;
320
0
    dfZResolution = dfZMeterResolution / dfZFactor;
321
0
    dfMResolution = dfMResolutionIn;
322
0
}
323
324
/************************************************************************/
325
/*             OGRGeomCoordinatePrecision::ConvertToOtherSRS()          */
326
/************************************************************************/
327
328
/**
329
 * \brief Return equivalent coordinate precision setting taking into account
330
 * a change of SRS.
331
 *
332
 * @param poSRSSrc Spatial reference system of the current instance
333
 *                 (if null, meter unit is assumed)
334
 * @param poSRSDst Spatial reference system of the returned instance
335
 *                 (if null, meter unit is assumed)
336
 * @return a new OGRGeomCoordinatePrecision instance, with a poSRSDst SRS.
337
 * @since GDAL 3.9
338
 */
339
OGRGeomCoordinatePrecision OGRGeomCoordinatePrecision::ConvertToOtherSRS(
340
    const OGRSpatialReference *poSRSSrc,
341
    const OGRSpatialReference *poSRSDst) const
342
0
{
343
0
    double dfXYFactorSrc = 1;
344
0
    double dfZFactorSrc = 1;
345
0
    GetConversionFactors(poSRSSrc, dfXYFactorSrc, dfZFactorSrc);
346
347
0
    double dfXYFactorDst = 1;
348
0
    double dfZFactorDst = 1;
349
0
    GetConversionFactors(poSRSDst, dfXYFactorDst, dfZFactorDst);
350
351
0
    OGRGeomCoordinatePrecision oNewPrec;
352
0
    oNewPrec.dfXYResolution = dfXYResolution * dfXYFactorSrc / dfXYFactorDst;
353
0
    oNewPrec.dfZResolution = dfZResolution * dfZFactorSrc / dfZFactorDst;
354
0
    oNewPrec.dfMResolution = dfMResolution;
355
356
    // Only preserve source forma specific options if no reprojection is
357
    // involved
358
0
    if ((!poSRSSrc && !poSRSDst) ||
359
0
        (poSRSSrc && poSRSDst && poSRSSrc->IsSame(poSRSDst)))
360
0
    {
361
0
        oNewPrec.oFormatSpecificOptions = oFormatSpecificOptions;
362
0
    }
363
364
0
    return oNewPrec;
365
0
}
366
367
/************************************************************************/
368
/*           OGRGeomCoordinatePrecision::ResolutionToPrecision()        */
369
/************************************************************************/
370
371
/**
372
 * \brief Return the number of decimal digits after the decimal point to
373
 * get the specified resolution.
374
 *
375
 * @since GDAL 3.9
376
 */
377
378
/* static */
379
int OGRGeomCoordinatePrecision::ResolutionToPrecision(double dfResolution)
380
0
{
381
0
    return static_cast<int>(
382
0
        std::ceil(std::log10(1. / std::min(1.0, dfResolution))));
383
0
}