Line data Source code
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 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "ogr_core.h"
30 : #include "ogr_api.h"
31 : #include "ogr_spatialref.h"
32 : #include "ogr_geomcoordinateprecision.h"
33 :
34 : #include <algorithm>
35 : #include <cmath>
36 :
37 : /************************************************************************/
38 : /* OGRGeomCoordinatePrecisionCreate() */
39 : /************************************************************************/
40 :
41 : /** Creates a new instance of OGRGeomCoordinatePrecision.
42 : *
43 : * The default X,Y,Z,M resolutions are set to OGR_GEOM_COORD_PRECISION_UNKNOWN.
44 : *
45 : * @since GDAL 3.9
46 : */
47 21 : OGRGeomCoordinatePrecisionH OGRGeomCoordinatePrecisionCreate(void)
48 : {
49 : static_assert(OGR_GEOM_COORD_PRECISION_UNKNOWN ==
50 : OGRGeomCoordinatePrecision::UNKNOWN);
51 :
52 21 : return new OGRGeomCoordinatePrecision();
53 : }
54 :
55 : /************************************************************************/
56 : /* OGRGeomCoordinatePrecisionDestroy() */
57 : /************************************************************************/
58 :
59 : /** Destroy a OGRGeomCoordinatePrecision.
60 : *
61 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance or nullptr
62 : * @since GDAL 3.9
63 : */
64 21 : void OGRGeomCoordinatePrecisionDestroy(
65 : OGRGeomCoordinatePrecisionH hGeomCoordPrec)
66 : {
67 21 : delete hGeomCoordPrec;
68 21 : }
69 :
70 : /************************************************************************/
71 : /* OGRGeomCoordinatePrecisionGetXYResolution() */
72 : /************************************************************************/
73 :
74 : /** Get the X/Y resolution of a OGRGeomCoordinatePrecision
75 : *
76 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
77 : * @return the the X/Y resolution of a OGRGeomCoordinatePrecision or
78 : * OGR_GEOM_COORD_PRECISION_UNKNOWN
79 : * @since GDAL 3.9
80 : */
81 47 : double OGRGeomCoordinatePrecisionGetXYResolution(
82 : OGRGeomCoordinatePrecisionH hGeomCoordPrec)
83 : {
84 47 : VALIDATE_POINTER1(hGeomCoordPrec,
85 : "OGRGeomCoordinatePrecisionGetXYResolution", 0);
86 47 : return hGeomCoordPrec->dfXYResolution;
87 : }
88 :
89 : /************************************************************************/
90 : /* OGRGeomCoordinatePrecisionGetZResolution() */
91 : /************************************************************************/
92 :
93 : /** Get the Z resolution of a OGRGeomCoordinatePrecision
94 : *
95 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
96 : * @return the the Z resolution of a OGRGeomCoordinatePrecision or
97 : * OGR_GEOM_COORD_PRECISION_UNKNOWN
98 : * @since GDAL 3.9
99 : */
100 44 : double OGRGeomCoordinatePrecisionGetZResolution(
101 : OGRGeomCoordinatePrecisionH hGeomCoordPrec)
102 : {
103 44 : VALIDATE_POINTER1(hGeomCoordPrec,
104 : "OGRGeomCoordinatePrecisionGetZResolution", 0);
105 44 : return hGeomCoordPrec->dfZResolution;
106 : }
107 :
108 : /************************************************************************/
109 : /* OGRGeomCoordinatePrecisionGetMResolution() */
110 : /************************************************************************/
111 :
112 : /** Get the M resolution of a OGRGeomCoordinatePrecision
113 : *
114 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
115 : * @return the the M resolution of a OGRGeomCoordinatePrecision or
116 : * OGR_GEOM_COORD_PRECISION_UNKNOWN
117 : * @since GDAL 3.9
118 : */
119 29 : double OGRGeomCoordinatePrecisionGetMResolution(
120 : OGRGeomCoordinatePrecisionH hGeomCoordPrec)
121 : {
122 29 : VALIDATE_POINTER1(hGeomCoordPrec,
123 : "OGRGeomCoordinatePrecisionGetMResolution", 0);
124 29 : return hGeomCoordPrec->dfMResolution;
125 : }
126 :
127 : /************************************************************************/
128 : /* OGRGeomCoordinatePrecisionGetFormats() */
129 : /************************************************************************/
130 :
131 : /** Get the list of format names for coordinate precision format specific
132 : * options.
133 : *
134 : * An example of a supported value for pszFormatName is
135 : * "FileGeodatabase" for layers of the OpenFileGDB driver.
136 : *
137 : * The returned values may be used for the pszFormatName argument of
138 : * OGRGeomCoordinatePrecisionGetFormatSpecificOptions().
139 : *
140 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
141 : * @return a null-terminated list to free with CSLDestroy(), or nullptr.
142 : * @since GDAL 3.9
143 : */
144 : char **
145 6 : OGRGeomCoordinatePrecisionGetFormats(OGRGeomCoordinatePrecisionH hGeomCoordPrec)
146 : {
147 6 : VALIDATE_POINTER1(hGeomCoordPrec, "OGRGeomCoordinatePrecisionGetFormats",
148 : nullptr);
149 12 : CPLStringList aosFormats;
150 11 : for (const auto &kv : hGeomCoordPrec->oFormatSpecificOptions)
151 : {
152 5 : aosFormats.AddString(kv.first.c_str());
153 : }
154 6 : return aosFormats.StealList();
155 : }
156 :
157 : /************************************************************************/
158 : /* OGRGeomCoordinatePrecisionGetFormatSpecificOptions() */
159 : /************************************************************************/
160 :
161 : /** Get format specific coordinate precision options.
162 : *
163 : * An example of a supported value for pszFormatName is
164 : * "FileGeodatabase" for layers of the OpenFileGDB driver.
165 : *
166 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
167 : * @param pszFormatName A format name (one of those returned by
168 : * OGRGeomCoordinatePrecisionGetFormats())
169 : * @return a null-terminated list, or nullptr. The list must *not* be freed,
170 : * and is owned by hGeomCoordPrec
171 : * @since GDAL 3.9
172 : */
173 7 : CSLConstList OGRGeomCoordinatePrecisionGetFormatSpecificOptions(
174 : OGRGeomCoordinatePrecisionH hGeomCoordPrec, const char *pszFormatName)
175 : {
176 7 : VALIDATE_POINTER1(hGeomCoordPrec,
177 : "OGRGeomCoordinatePrecisionGetFormatSpecificOptions",
178 : nullptr);
179 : const auto oIter =
180 7 : hGeomCoordPrec->oFormatSpecificOptions.find(pszFormatName);
181 7 : if (oIter == hGeomCoordPrec->oFormatSpecificOptions.end())
182 : {
183 1 : return nullptr;
184 : }
185 6 : return oIter->second.List();
186 : }
187 :
188 : /************************************************************************/
189 : /* OGRGeomCoordinatePrecisionSetFormatSpecificOptions() */
190 : /************************************************************************/
191 :
192 : /** Set format specific coordinate precision options.
193 : *
194 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
195 : * @param pszFormatName A format name (must not be null)
196 : * @param papszOptions null-terminated list of options.
197 : * @since GDAL 3.9
198 : */
199 1 : void OGRGeomCoordinatePrecisionSetFormatSpecificOptions(
200 : OGRGeomCoordinatePrecisionH hGeomCoordPrec, const char *pszFormatName,
201 : CSLConstList papszOptions)
202 : {
203 1 : VALIDATE_POINTER0(hGeomCoordPrec,
204 : "OGRGeomCoordinatePrecisionSetFormatSpecificOptions");
205 1 : hGeomCoordPrec->oFormatSpecificOptions[pszFormatName] = papszOptions;
206 : }
207 :
208 : /************************************************************************/
209 : /* OGRGeomCoordinatePrecisionSet() */
210 : /************************************************************************/
211 :
212 : /**
213 : * \brief Set the resolution of the geometry coordinate components.
214 : *
215 : * For the X, Y and Z ordinates, the precision should be expressed in the units
216 : * of the CRS of the geometry. So typically degrees for geographic CRS, or
217 : * meters/feet/US-feet for projected CRS.
218 : * Users might use OGRGeomCoordinatePrecisionSetFromMeter() for an even more
219 : * convenient interface.
220 : *
221 : * For a projected CRS with meters as linear unit, 1e-3 corresponds to a
222 : * millimetric precision.
223 : * For a geographic CRS in 8.9e-9 corresponds to a millimetric precision
224 : * (for a Earth CRS)
225 : *
226 : * Resolution should be stricty positive, or set to
227 : * OGR_GEOM_COORD_PRECISION_UNKNOWN when unknown.
228 : *
229 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
230 : * @param dfXYResolution Resolution for for X and Y coordinates.
231 : * @param dfZResolution Resolution for for Z coordinates.
232 : * @param dfMResolution Resolution for for M coordinates.
233 : * @since GDAL 3.9
234 : */
235 :
236 21 : void OGRGeomCoordinatePrecisionSet(OGRGeomCoordinatePrecisionH hGeomCoordPrec,
237 : double dfXYResolution, double dfZResolution,
238 : double dfMResolution)
239 : {
240 21 : VALIDATE_POINTER0(hGeomCoordPrec, "OGRGeomCoordinatePrecisionSet");
241 21 : hGeomCoordPrec->dfXYResolution = dfXYResolution;
242 21 : hGeomCoordPrec->dfZResolution = dfZResolution;
243 21 : hGeomCoordPrec->dfMResolution = dfMResolution;
244 : }
245 :
246 : /************************************************************************/
247 : /* OGRGeomCoordinatePrecisionSetFromMeter() */
248 : /************************************************************************/
249 :
250 : /**
251 : * \brief Set the resolution of the geometry coordinate components.
252 : *
253 : * For the X, Y and Z ordinates, the precision should be expressed in meter,
254 : * e.g 1e-3 for millimetric precision.
255 : *
256 : * Resolution should be stricty positive, or set to
257 : * OGR_GEOM_COORD_PRECISION_UNKNOWN when unknown.
258 : *
259 : * @param hGeomCoordPrec OGRGeomCoordinatePrecision instance (must not be null)
260 : * @param hSRS Spatial reference system, used for metric to SRS unit conversion
261 : * (must not be null)
262 : * @param dfXYMeterResolution Resolution for for X and Y coordinates, in meter.
263 : * @param dfZMeterResolution Resolution for for Z coordinates, in meter.
264 : * @param dfMResolution Resolution for for M coordinates.
265 : * @since GDAL 3.9
266 : */
267 3 : void OGRGeomCoordinatePrecisionSetFromMeter(
268 : OGRGeomCoordinatePrecisionH hGeomCoordPrec, OGRSpatialReferenceH hSRS,
269 : double dfXYMeterResolution, double dfZMeterResolution, double dfMResolution)
270 : {
271 3 : VALIDATE_POINTER0(hGeomCoordPrec, "OGRGeomCoordinatePrecisionSet");
272 3 : VALIDATE_POINTER0(hSRS, "OGRGeomCoordinatePrecisionSet");
273 3 : return hGeomCoordPrec->SetFromMeter(OGRSpatialReference::FromHandle(hSRS),
274 : dfXYMeterResolution, dfZMeterResolution,
275 3 : dfMResolution);
276 : }
277 :
278 : /************************************************************************/
279 : /* GetConversionFactors() */
280 : /************************************************************************/
281 :
282 1738 : static void GetConversionFactors(const OGRSpatialReference *poSRS,
283 : double &dfXYFactor, double &dfZFactor)
284 : {
285 1738 : dfXYFactor = 1;
286 1738 : dfZFactor = 1;
287 :
288 1738 : if (poSRS)
289 : {
290 1129 : if (poSRS->IsGeographic())
291 : {
292 733 : dfXYFactor = poSRS->GetSemiMajor(nullptr) * M_PI / 180;
293 : }
294 : else
295 : {
296 396 : dfXYFactor = poSRS->GetLinearUnits(nullptr);
297 : }
298 :
299 1129 : if (poSRS->GetAxesCount() == 3)
300 : {
301 40 : poSRS->GetAxis(nullptr, 2, nullptr, &dfZFactor);
302 : }
303 : }
304 1738 : }
305 :
306 : /************************************************************************/
307 : /* OGRGeomCoordinatePrecision::SetFromMeter() */
308 : /************************************************************************/
309 :
310 : /**
311 : * \brief Set the resolution of the geometry coordinate components.
312 : *
313 : * For the X, Y and Z coordinates, the precision should be expressed in meter,
314 : * e.g 1e-3 for millimetric precision.
315 : *
316 : * Resolution should be stricty positive, or set to
317 : * OGRGeomCoordinatePrecision::UNKNOWN when unknown.
318 : *
319 : * @param poSRS Spatial reference system, used for metric to SRS unit conversion
320 : * (must not be null)
321 : * @param dfXYMeterResolution Resolution for for X and Y coordinates, in meter.
322 : * @param dfZMeterResolution Resolution for for Z coordinates, in meter.
323 : * @param dfMResolutionIn Resolution for for M coordinates.
324 : * @since GDAL 3.9
325 : */
326 12 : void OGRGeomCoordinatePrecision::SetFromMeter(const OGRSpatialReference *poSRS,
327 : double dfXYMeterResolution,
328 : double dfZMeterResolution,
329 : double dfMResolutionIn)
330 : {
331 12 : double dfXYFactor = 1;
332 12 : double dfZFactor = 1;
333 12 : GetConversionFactors(poSRS, dfXYFactor, dfZFactor);
334 :
335 12 : dfXYResolution = dfXYMeterResolution / dfXYFactor;
336 12 : dfZResolution = dfZMeterResolution / dfZFactor;
337 12 : dfMResolution = dfMResolutionIn;
338 12 : }
339 :
340 : /************************************************************************/
341 : /* OGRGeomCoordinatePrecision::ConvertToOtherSRS() */
342 : /************************************************************************/
343 :
344 : /**
345 : * \brief Return equivalent coordinate precision setting taking into account
346 : * a change of SRS.
347 : *
348 : * @param poSRSSrc Spatial reference system of the current instance
349 : * (if null, meter unit is assumed)
350 : * @param poSRSDst Spatial reference system of the returned instance
351 : * (if null, meter unit is assumed)
352 : * @return a new OGRGeomCoordinatePrecision instance, with a poSRSDst SRS.
353 : * @since GDAL 3.9
354 : */
355 863 : OGRGeomCoordinatePrecision OGRGeomCoordinatePrecision::ConvertToOtherSRS(
356 : const OGRSpatialReference *poSRSSrc,
357 : const OGRSpatialReference *poSRSDst) const
358 : {
359 863 : double dfXYFactorSrc = 1;
360 863 : double dfZFactorSrc = 1;
361 863 : GetConversionFactors(poSRSSrc, dfXYFactorSrc, dfZFactorSrc);
362 :
363 863 : double dfXYFactorDst = 1;
364 863 : double dfZFactorDst = 1;
365 863 : GetConversionFactors(poSRSDst, dfXYFactorDst, dfZFactorDst);
366 :
367 863 : OGRGeomCoordinatePrecision oNewPrec;
368 863 : oNewPrec.dfXYResolution = dfXYResolution * dfXYFactorSrc / dfXYFactorDst;
369 863 : oNewPrec.dfZResolution = dfZResolution * dfZFactorSrc / dfZFactorDst;
370 863 : oNewPrec.dfMResolution = dfMResolution;
371 :
372 : // Only preserve source forma specific options if no reprojection is
373 : // involved
374 1289 : if ((!poSRSSrc && !poSRSDst) ||
375 426 : (poSRSSrc && poSRSDst && poSRSSrc->IsSame(poSRSDst)))
376 : {
377 464 : oNewPrec.oFormatSpecificOptions = oFormatSpecificOptions;
378 : }
379 :
380 1726 : return oNewPrec;
381 : }
382 :
383 : /************************************************************************/
384 : /* OGRGeomCoordinatePrecision::ResolutionToPrecision() */
385 : /************************************************************************/
386 :
387 : /**
388 : * \brief Return the number of decimal digits after the decimal point to
389 : * get the specified resolution.
390 : *
391 : * @since GDAL 3.9
392 : */
393 :
394 : /* static */
395 57 : int OGRGeomCoordinatePrecision::ResolutionToPrecision(double dfResolution)
396 : {
397 : return static_cast<int>(
398 57 : std::ceil(std::log10(1. / std::min(1.0, dfResolution))));
399 : }
|