Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Mapinfo Image Warper
4 : * Purpose: Implementation of one or more GDALTrasformerFunc types, including
5 : * the GenImgProj (general image reprojector) transformer.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2002, i3 - information integration and imaging
10 : * Fort Collin, CO
11 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
12 : * Copyright (c) 2021, CLS
13 : *
14 : * Permission is hereby granted, free of charge, to any person obtaining a
15 : * copy of this software and associated documentation files (the "Software"),
16 : * to deal in the Software without restriction, including without limitation
17 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 : * and/or sell copies of the Software, and to permit persons to whom the
19 : * Software is furnished to do so, subject to the following conditions:
20 : *
21 : * The above copyright notice and this permission notice shall be included
22 : * in all copies or substantial portions of the Software.
23 : *
24 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 : * DEALINGS IN THE SOFTWARE.
31 : ****************************************************************************/
32 :
33 : #include "cpl_port.h"
34 : #include "gdal_alg.h"
35 : #include "gdal_alg_priv.h"
36 :
37 : #include <climits>
38 : #include <cmath>
39 : #include <cstddef>
40 : #include <cstdlib>
41 : #include <cstring>
42 :
43 : #include <algorithm>
44 : #include <limits>
45 : #include <utility>
46 :
47 : #include "cpl_conv.h"
48 : #include "cpl_error.h"
49 : #include "cpl_list.h"
50 : #include "cpl_minixml.h"
51 : #include "cpl_multiproc.h"
52 : #include "cpl_string.h"
53 : #include "cpl_vsi.h"
54 : #include "gdal.h"
55 : #include "gdal_priv.h"
56 : #include "ogr_core.h"
57 : #include "ogr_spatialref.h"
58 : #include "ogr_srs_api.h"
59 :
60 : CPL_C_START
61 : void *GDALDeserializeGCPTransformer(CPLXMLNode *psTree);
62 : void *GDALDeserializeTPSTransformer(CPLXMLNode *psTree);
63 : void *GDALDeserializeGeoLocTransformer(CPLXMLNode *psTree);
64 : void *GDALDeserializeRPCTransformer(CPLXMLNode *psTree);
65 : CPL_C_END
66 :
67 : static CPLXMLNode *GDALSerializeReprojectionTransformer(void *pTransformArg);
68 : static void *GDALDeserializeReprojectionTransformer(CPLXMLNode *psTree);
69 :
70 : static CPLXMLNode *GDALSerializeGenImgProjTransformer(void *pTransformArg);
71 : static void *GDALDeserializeGenImgProjTransformer(CPLXMLNode *psTree);
72 :
73 : static void *GDALCreateApproxTransformer2(GDALTransformerFunc pfnRawTransformer,
74 : void *pRawTransformerArg,
75 : double dfMaxErrorForward,
76 : double dfMaxErrorReverse);
77 :
78 : /************************************************************************/
79 : /* GDALIsTransformer() */
80 : /************************************************************************/
81 :
82 9980 : bool GDALIsTransformer(void *hTransformerArg, const char *pszClassName)
83 : {
84 9980 : if (!hTransformerArg)
85 0 : return false;
86 : // All transformers should have a GDALTransformerInfo member as their first members
87 9980 : GDALTransformerInfo *psInfo =
88 : static_cast<GDALTransformerInfo *>(hTransformerArg);
89 9980 : return memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
90 19066 : strlen(GDAL_GTI2_SIGNATURE)) == 0 &&
91 19066 : strcmp(psInfo->pszClassName, pszClassName) == 0;
92 : }
93 :
94 : /************************************************************************/
95 : /* GDALTransformFunc */
96 : /* */
97 : /* Documentation for GDALTransformFunc typedef. */
98 : /************************************************************************/
99 :
100 : /*!
101 :
102 : \typedef typedef int (*GDALTransformerFunc)( void *pTransformerArg, int
103 : bDstToSrc, int nPointCount, double *x, double *y, double *z, int *panSuccess );
104 :
105 : Generic signature for spatial point transformers.
106 :
107 : This function signature is used for a variety of functions that accept
108 : passed in functions used to transform point locations between two coordinate
109 : spaces.
110 :
111 : The GDALCreateGenImgProjTransformer(), GDALCreateReprojectionTransformerEx(),
112 : GDALCreateGCPTransformer() and GDALCreateApproxTransformer() functions can
113 : be used to prepare argument data for some built-in transformers. As well,
114 : applications can implement their own transformers to the following signature.
115 :
116 : \code
117 : typedef int
118 : (*GDALTransformerFunc)( void *pTransformerArg,
119 : int bDstToSrc, int nPointCount,
120 : double *x, double *y, double *z, int *panSuccess );
121 : \endcode
122 :
123 : @param pTransformerArg application supplied callback data used by the
124 : transformer.
125 :
126 : @param bDstToSrc if TRUE the transformation will be from the destination
127 : coordinate space to the source coordinate system, otherwise the transformation
128 : will be from the source coordinate system to the destination coordinate system.
129 :
130 : @param nPointCount number of points in the x, y and z arrays.
131 :
132 : @param x input X coordinates. Results returned in same array.
133 :
134 : @param y input Y coordinates. Results returned in same array.
135 :
136 : @param z input Z coordinates. Results returned in same array.
137 :
138 : @param panSuccess array of ints in which success (TRUE) or failure (FALSE)
139 : flags are returned for the translation of each point.
140 :
141 : @return TRUE if the overall transformation succeeds (though some individual
142 : points may have failed) or FALSE if the overall transformation fails.
143 :
144 : */
145 :
146 : /************************************************************************/
147 : /* GDALSuggestedWarpOutput() */
148 : /************************************************************************/
149 :
150 : /**
151 : * Suggest output file size.
152 : *
153 : * This function is used to suggest the size, and georeferenced extents
154 : * appropriate given the indicated transformation and input file. It walks
155 : * the edges of the input file (approximately 20 sample points along each
156 : * edge) transforming into output coordinates in order to get an extents box.
157 : *
158 : * Then a resolution is computed with the intent that the length of the
159 : * distance from the top left corner of the output imagery to the bottom right
160 : * corner would represent the same number of pixels as in the source image.
161 : * Note that if the image is somewhat rotated the diagonal taken isn't of the
162 : * whole output bounding rectangle, but instead of the locations where the
163 : * top/left and bottom/right corners transform. The output pixel size is
164 : * always square. This is intended to approximately preserve the resolution
165 : * of the input data in the output file.
166 : *
167 : * The values returned in padfGeoTransformOut, pnPixels and pnLines are
168 : * the suggested number of pixels and lines for the output file, and the
169 : * geotransform relating those pixels to the output georeferenced coordinates.
170 : *
171 : * The trickiest part of using the function is ensuring that the
172 : * transformer created is from source file pixel/line coordinates to
173 : * output file georeferenced coordinates. This can be accomplished with
174 : * GDALCreateGenImgProjTransformer() by passing a NULL for the hDstDS.
175 : *
176 : * @param hSrcDS the input image (it is assumed the whole input image is
177 : * being transformed).
178 : * @param pfnTransformer the transformer function.
179 : * @param pTransformArg the callback data for the transformer function.
180 : * @param padfGeoTransformOut the array of six doubles in which the suggested
181 : * geotransform is returned.
182 : * @param pnPixels int in which the suggest pixel width of output is returned.
183 : * @param pnLines int in which the suggest pixel height of output is returned.
184 : *
185 : * @return CE_None if successful or CE_Failure otherwise.
186 : */
187 :
188 35 : CPLErr CPL_STDCALL GDALSuggestedWarpOutput(GDALDatasetH hSrcDS,
189 : GDALTransformerFunc pfnTransformer,
190 : void *pTransformArg,
191 : double *padfGeoTransformOut,
192 : int *pnPixels, int *pnLines)
193 :
194 : {
195 35 : VALIDATE_POINTER1(hSrcDS, "GDALSuggestedWarpOutput", CE_Failure);
196 :
197 35 : double adfExtent[4] = {};
198 :
199 35 : return GDALSuggestedWarpOutput2(hSrcDS, pfnTransformer, pTransformArg,
200 : padfGeoTransformOut, pnPixels, pnLines,
201 35 : adfExtent, 0);
202 : }
203 :
204 470 : static int GDALSuggestedWarpOutput2_MustAdjustForRightBorder(
205 : GDALTransformerFunc pfnTransformer, void *pTransformArg, double *padfExtent,
206 : CPL_UNUSED int nPixels, int nLines, double dfPixelSizeX,
207 : double dfPixelSizeY)
208 : {
209 470 : double adfX[21] = {};
210 470 : double adfY[21] = {};
211 :
212 470 : const double dfMaxXOut = padfExtent[2];
213 470 : const double dfMaxYOut = padfExtent[3];
214 :
215 : // Take 20 steps.
216 470 : int nSamplePoints = 0;
217 10340 : for (double dfRatio = 0.0; dfRatio <= 1.01; dfRatio += 0.05)
218 : {
219 : // Ensure we end exactly at the end.
220 9870 : if (dfRatio > 0.99)
221 470 : dfRatio = 1.0;
222 :
223 : // Along right.
224 9870 : adfX[nSamplePoints] = dfMaxXOut;
225 9870 : adfY[nSamplePoints] = dfMaxYOut - dfPixelSizeY * dfRatio * nLines;
226 9870 : nSamplePoints++;
227 : }
228 470 : double adfZ[21] = {};
229 :
230 470 : int abSuccess[21] = {};
231 :
232 470 : bool bErr = false;
233 470 : if (!pfnTransformer(pTransformArg, TRUE, nSamplePoints, adfX, adfY, adfZ,
234 : abSuccess))
235 : {
236 0 : bErr = true;
237 : }
238 :
239 470 : if (!bErr && !pfnTransformer(pTransformArg, FALSE, nSamplePoints, adfX,
240 : adfY, adfZ, abSuccess))
241 : {
242 0 : bErr = true;
243 : }
244 :
245 470 : nSamplePoints = 0;
246 470 : int nBadCount = 0;
247 10340 : for (double dfRatio = 0.0; !bErr && dfRatio <= 1.01; dfRatio += 0.05)
248 : {
249 9870 : const double expected_x = dfMaxXOut;
250 9870 : const double expected_y = dfMaxYOut - dfPixelSizeY * dfRatio * nLines;
251 9870 : if (fabs(adfX[nSamplePoints] - expected_x) > dfPixelSizeX ||
252 5640 : fabs(adfY[nSamplePoints] - expected_y) > dfPixelSizeY)
253 4230 : nBadCount++;
254 9870 : nSamplePoints++;
255 : }
256 :
257 470 : return nBadCount == nSamplePoints;
258 : }
259 :
260 369 : static int GDALSuggestedWarpOutput2_MustAdjustForBottomBorder(
261 : GDALTransformerFunc pfnTransformer, void *pTransformArg, double *padfExtent,
262 : int nPixels, CPL_UNUSED int nLines, double dfPixelSizeX,
263 : double dfPixelSizeY)
264 : {
265 369 : double adfX[21] = {};
266 369 : double adfY[21] = {};
267 :
268 369 : const double dfMinXOut = padfExtent[0];
269 369 : const double dfMinYOut = padfExtent[1];
270 :
271 : // Take 20 steps.
272 369 : int nSamplePoints = 0;
273 8118 : for (double dfRatio = 0.0; dfRatio <= 1.01; dfRatio += 0.05)
274 : {
275 : // Ensure we end exactly at the end.
276 7749 : if (dfRatio > 0.99)
277 369 : dfRatio = 1.0;
278 :
279 : // Along right.
280 7749 : adfX[nSamplePoints] = dfMinXOut + dfPixelSizeX * dfRatio * nPixels;
281 7749 : adfY[nSamplePoints] = dfMinYOut;
282 7749 : nSamplePoints++;
283 : }
284 369 : double adfZ[21] = {};
285 :
286 369 : int abSuccess[21] = {};
287 :
288 369 : bool bErr = false;
289 369 : if (!pfnTransformer(pTransformArg, TRUE, nSamplePoints, adfX, adfY, adfZ,
290 : abSuccess))
291 : {
292 0 : bErr = true;
293 : }
294 :
295 369 : if (!bErr && !pfnTransformer(pTransformArg, FALSE, nSamplePoints, adfX,
296 : adfY, adfZ, abSuccess))
297 : {
298 0 : bErr = true;
299 : }
300 :
301 369 : nSamplePoints = 0;
302 369 : int nBadCount = 0;
303 8118 : for (double dfRatio = 0.0; !bErr && dfRatio <= 1.01; dfRatio += 0.05)
304 : {
305 7749 : const double expected_x = dfMinXOut + dfPixelSizeX * dfRatio * nPixels;
306 7749 : const double expected_y = dfMinYOut;
307 7749 : if (fabs(adfX[nSamplePoints] - expected_x) > dfPixelSizeX ||
308 5574 : fabs(adfY[nSamplePoints] - expected_y) > dfPixelSizeY)
309 2175 : nBadCount++;
310 7749 : nSamplePoints++;
311 : }
312 :
313 369 : return nBadCount == nSamplePoints;
314 : }
315 :
316 : /************************************************************************/
317 : /* GDALSuggestedWarpOutput2() */
318 : /************************************************************************/
319 :
320 : /**
321 : * Suggest output file size.
322 : *
323 : * This function is used to suggest the size, and georeferenced extents
324 : * appropriate given the indicated transformation and input file. It walks
325 : * the edges of the input file (approximately 20 sample points along each
326 : * edge) transforming into output coordinates in order to get an extents box.
327 : *
328 : * Then a resolution is computed with the intent that the length of the
329 : * distance from the top left corner of the output imagery to the bottom right
330 : * corner would represent the same number of pixels as in the source image.
331 : * Note that if the image is somewhat rotated the diagonal taken isn't of the
332 : * whole output bounding rectangle, but instead of the locations where the
333 : * top/left and bottom/right corners transform. The output pixel size is
334 : * always square. This is intended to approximately preserve the resolution
335 : * of the input data in the output file.
336 : *
337 : * The values returned in padfGeoTransformOut, pnPixels and pnLines are
338 : * the suggested number of pixels and lines for the output file, and the
339 : * geotransform relating those pixels to the output georeferenced coordinates.
340 : *
341 : * The trickiest part of using the function is ensuring that the
342 : * transformer created is from source file pixel/line coordinates to
343 : * output file georeferenced coordinates. This can be accomplished with
344 : * GDALCreateGenImgProjTransformer() by passing a NULL for the hDstDS.
345 : *
346 : * @param hSrcDS the input image (it is assumed the whole input image is
347 : * being transformed).
348 : * @param pfnTransformer the transformer function.
349 : * @param pTransformArg the callback data for the transformer function.
350 : * @param padfGeoTransformOut the array of six doubles in which the suggested
351 : * geotransform is returned.
352 : * @param pnPixels int in which the suggest pixel width of output is returned.
353 : * @param pnLines int in which the suggest pixel height of output is returned.
354 : * @param padfExtent Four entry array to return extents as (xmin, ymin, xmax,
355 : * ymax).
356 : * @param nOptions Options flags. Zero or GDAL_SWO_ROUND_UP_SIZE to ask *pnPixels
357 : * and *pnLines to be rounded up instead of being rounded to the closes integer, or
358 : * GDAL_SWO_FORCE_SQUARE_PIXEL to indicate that the generated pixel size is a square.
359 : *
360 : * @return CE_None if successful or CE_Failure otherwise.
361 : */
362 :
363 848 : CPLErr CPL_STDCALL GDALSuggestedWarpOutput2(GDALDatasetH hSrcDS,
364 : GDALTransformerFunc pfnTransformer,
365 : void *pTransformArg,
366 : double *padfGeoTransformOut,
367 : int *pnPixels, int *pnLines,
368 : double *padfExtent, int nOptions)
369 : {
370 848 : VALIDATE_POINTER1(hSrcDS, "GDALSuggestedWarpOutput2", CE_Failure);
371 :
372 : const bool bIsGDALGenImgProjTransform{
373 1696 : pTransformArg &&
374 848 : GDALIsTransformer(pTransformArg, GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME)};
375 :
376 : /* -------------------------------------------------------------------- */
377 : /* Setup sample points all around the edge of the input raster. */
378 : /* -------------------------------------------------------------------- */
379 848 : if (bIsGDALGenImgProjTransform)
380 : {
381 : // In case CHECK_WITH_INVERT_PROJ has been modified.
382 848 : GDALRefreshGenImgProjTransformer(pTransformArg);
383 : }
384 0 : else if (GDALIsTransformer(pTransformArg,
385 : GDAL_APPROX_TRANSFORMER_CLASS_NAME))
386 : {
387 : // In case CHECK_WITH_INVERT_PROJ has been modified.
388 0 : GDALRefreshApproxTransformer(pTransformArg);
389 : }
390 :
391 848 : const int nInXSize = GDALGetRasterXSize(hSrcDS);
392 848 : const int nInYSize = GDALGetRasterYSize(hSrcDS);
393 :
394 : /* ------------------------------------------------------------- */
395 : /* Special case for warping on the same (or null) CRS. */
396 : /* ------------------------------------------------------------- */
397 848 : if ((!nOptions || (nOptions & GDAL_SWO_FORCE_SQUARE_PIXEL) == 0) &&
398 847 : pTransformArg && bIsGDALGenImgProjTransform)
399 : {
400 847 : const GDALGenImgProjTransformInfo *psInfo =
401 : static_cast<const GDALGenImgProjTransformInfo *>(pTransformArg);
402 :
403 847 : if (!psInfo->pSrcTransformer &&
404 774 : !psInfo->bHasCustomTransformationPipeline &&
405 770 : !psInfo->pDstTransformer && psInfo->adfSrcGeoTransform[2] == 0 &&
406 770 : psInfo->adfSrcGeoTransform[4] == 0 &&
407 770 : psInfo->adfDstGeoTransform[0] == 0 &&
408 760 : psInfo->adfDstGeoTransform[1] == 1 &&
409 760 : psInfo->adfDstGeoTransform[2] == 0 &&
410 760 : psInfo->adfDstGeoTransform[3] == 0 &&
411 760 : psInfo->adfDstGeoTransform[4] == 0 &&
412 760 : psInfo->adfDstGeoTransform[5] == 1)
413 : {
414 760 : const OGRSpatialReference *poSourceCRS = nullptr;
415 760 : const OGRSpatialReference *poTargetCRS = nullptr;
416 :
417 760 : if (psInfo->pReprojectArg)
418 : {
419 555 : const GDALReprojectionTransformInfo *psRTI =
420 : static_cast<const GDALReprojectionTransformInfo *>(
421 : psInfo->pReprojectArg);
422 555 : poSourceCRS = psRTI->poForwardTransform->GetSourceCS();
423 555 : poTargetCRS = psRTI->poForwardTransform->GetTargetCS();
424 : }
425 :
426 1315 : if ((!poSourceCRS && !poTargetCRS) ||
427 555 : (poSourceCRS && poTargetCRS &&
428 555 : poSourceCRS->IsSame(poTargetCRS)))
429 : {
430 :
431 545 : const bool bNorthUp{psInfo->adfSrcGeoTransform[5] < 0.0};
432 :
433 545 : memcpy(padfGeoTransformOut, psInfo->adfSrcGeoTransform,
434 : sizeof(double) * 6);
435 :
436 545 : if (!bNorthUp)
437 : {
438 26 : padfGeoTransformOut[3] = padfGeoTransformOut[3] +
439 26 : nInYSize * padfGeoTransformOut[5];
440 26 : padfGeoTransformOut[5] = -padfGeoTransformOut[5];
441 : }
442 :
443 545 : *pnPixels = nInXSize;
444 545 : *pnLines = nInYSize;
445 :
446 : // Calculate extent from hSrcDS
447 545 : if (padfExtent)
448 : {
449 545 : padfExtent[0] = psInfo->adfSrcGeoTransform[0];
450 545 : padfExtent[1] = psInfo->adfSrcGeoTransform[3] +
451 545 : nInYSize * psInfo->adfSrcGeoTransform[5];
452 545 : padfExtent[2] = psInfo->adfSrcGeoTransform[0] +
453 545 : nInXSize * psInfo->adfSrcGeoTransform[1];
454 545 : padfExtent[3] = psInfo->adfSrcGeoTransform[3];
455 545 : if (!bNorthUp)
456 : {
457 26 : std::swap(padfExtent[1], padfExtent[3]);
458 : }
459 : }
460 545 : return CE_None;
461 : }
462 : }
463 : }
464 :
465 303 : const int N_PIXELSTEP = 50;
466 : int nSteps = static_cast<int>(
467 303 : static_cast<double>(std::min(nInYSize, nInXSize)) / N_PIXELSTEP + 0.5);
468 303 : if (nSteps < 20)
469 282 : nSteps = 20;
470 21 : else if (nSteps > 100)
471 6 : nSteps = 100;
472 :
473 : // TODO(rouault): How is this goto retry supposed to work? Added in r20537.
474 : // Does redoing the same malloc multiple times work? If it is needed, can
475 : // it be converted to a tigher while loop around the MALLOC3s and free? Is
476 : // the point to try with the full requested steps. Then, if there is not
477 : // enough memory, back off and try with just 20 steps?
478 303 : retry:
479 303 : int nStepsPlusOne = nSteps + 1;
480 303 : int nSampleMax = nStepsPlusOne * nStepsPlusOne;
481 :
482 303 : double dfStep = 1.0 / nSteps;
483 303 : double *padfY = nullptr;
484 303 : double *padfZ = nullptr;
485 303 : double *padfYRevert = nullptr;
486 303 : double *padfZRevert = nullptr;
487 :
488 : int *pabSuccess = static_cast<int *>(
489 303 : VSI_MALLOC3_VERBOSE(sizeof(int), nStepsPlusOne, nStepsPlusOne));
490 : double *padfX = static_cast<double *>(
491 303 : VSI_MALLOC3_VERBOSE(sizeof(double) * 3, nStepsPlusOne, nStepsPlusOne));
492 : double *padfXRevert = static_cast<double *>(
493 303 : VSI_MALLOC3_VERBOSE(sizeof(double) * 3, nStepsPlusOne, nStepsPlusOne));
494 303 : if (pabSuccess == nullptr || padfX == nullptr || padfXRevert == nullptr)
495 : {
496 0 : CPLFree(padfX);
497 0 : CPLFree(padfXRevert);
498 0 : CPLFree(pabSuccess);
499 0 : if (nSteps > 20)
500 : {
501 0 : nSteps = 20;
502 0 : goto retry;
503 : }
504 0 : return CE_Failure;
505 : }
506 :
507 303 : padfY = padfX + nSampleMax;
508 303 : padfZ = padfX + nSampleMax * 2;
509 303 : padfYRevert = padfXRevert + nSampleMax;
510 303 : padfZRevert = padfXRevert + nSampleMax * 2;
511 :
512 : // Take N_STEPS steps.
513 7249 : for (int iStep = 0; iStep <= nSteps; iStep++)
514 : {
515 6946 : double dfRatio = (iStep == nSteps) ? 1.0 : iStep * dfStep;
516 6946 : int iStep2 = iStep;
517 :
518 : // Along top.
519 6946 : padfX[iStep2] = dfRatio * nInXSize;
520 6946 : padfY[iStep2] = 0.0;
521 6946 : padfZ[iStep2] = 0.0;
522 :
523 : // Along bottom.
524 6946 : iStep2 += nStepsPlusOne;
525 6946 : padfX[iStep2] = dfRatio * nInXSize;
526 6946 : padfY[iStep2] = nInYSize;
527 6946 : padfZ[iStep2] = 0.0;
528 :
529 : // Along left.
530 6946 : iStep2 += nStepsPlusOne;
531 6946 : padfX[iStep2] = 0.0;
532 6946 : padfY[iStep2] = dfRatio * nInYSize;
533 6946 : padfZ[iStep2] = 0.0;
534 :
535 : // Along right.
536 6946 : iStep2 += nStepsPlusOne;
537 6946 : padfX[iStep2] = nInXSize;
538 6946 : padfY[iStep2] = dfRatio * nInYSize;
539 6946 : padfZ[iStep2] = 0.0;
540 : }
541 :
542 303 : int nSamplePoints = 4 * nStepsPlusOne;
543 :
544 303 : memset(pabSuccess, 1, sizeof(int) * nSampleMax);
545 :
546 : /* -------------------------------------------------------------------- */
547 : /* Transform them to the output coordinate system. */
548 : /* -------------------------------------------------------------------- */
549 303 : if (!pfnTransformer(pTransformArg, FALSE, nSamplePoints, padfX, padfY,
550 : padfZ, pabSuccess))
551 : {
552 0 : CPLError(CE_Failure, CPLE_AppDefined,
553 : "GDALSuggestedWarpOutput() failed because the passed "
554 : "transformer failed.");
555 0 : CPLFree(padfX);
556 0 : CPLFree(padfXRevert);
557 0 : CPLFree(pabSuccess);
558 0 : return CE_Failure;
559 : }
560 :
561 303 : constexpr int SIGN_FINAL_UNINIT = -2;
562 303 : constexpr int SIGN_FINAL_INVALID = 0;
563 303 : int iSignDiscontinuity = SIGN_FINAL_UNINIT;
564 303 : int nFailedCount = 0;
565 303 : const int iSignArray[2] = {-1, 1};
566 28087 : for (int i = 0; i < nSamplePoints; i++)
567 : {
568 27784 : if (pabSuccess[i])
569 : {
570 : // Fix for https://trac.osgeo.org/gdal/ticket/7243
571 : // where echo "-2050000.000 2050000.000" |
572 : // gdaltransform -s_srs EPSG:3411 -t_srs EPSG:4326
573 : // gives "-180 63.691332898492"
574 : // but we would rather like 180
575 24820 : if (iSignDiscontinuity == 1 || iSignDiscontinuity == -1)
576 : {
577 9279 : if (!((iSignDiscontinuity * padfX[i] > 0 &&
578 9230 : iSignDiscontinuity * padfX[i] <= 180.0) ||
579 50 : (fabs(padfX[i] - iSignDiscontinuity * -180.0) < 1e-8)))
580 : {
581 27 : iSignDiscontinuity = SIGN_FINAL_INVALID;
582 : }
583 : }
584 15541 : else if (iSignDiscontinuity == SIGN_FINAL_UNINIT)
585 : {
586 699 : for (const auto &iSign : iSignArray)
587 : {
588 524 : if ((iSign * padfX[i] > 0 && iSign * padfX[i] <= 180.0) ||
589 397 : (fabs(padfX[i] - iSign * -180.0) < 1e-8))
590 : {
591 127 : iSignDiscontinuity = iSign;
592 127 : break;
593 : }
594 : }
595 302 : if (iSignDiscontinuity == SIGN_FINAL_UNINIT)
596 : {
597 175 : iSignDiscontinuity = SIGN_FINAL_INVALID;
598 : }
599 : }
600 : }
601 : else
602 : {
603 2964 : nFailedCount++;
604 : }
605 : }
606 :
607 303 : if (iSignDiscontinuity == 1 || iSignDiscontinuity == -1)
608 : {
609 9828 : for (int i = 0; i < nSamplePoints; i++)
610 : {
611 9728 : if (pabSuccess[i])
612 : {
613 9111 : if (fabs(padfX[i] - iSignDiscontinuity * -180.0) < 1e-8)
614 : {
615 2 : double axTemp[2] = {iSignDiscontinuity * -180.0,
616 2 : iSignDiscontinuity * 180.0};
617 2 : double ayTemp[2] = {padfY[i], padfY[i]};
618 2 : double azTemp[2] = {padfZ[i], padfZ[i]};
619 2 : int abSuccess[2] = {FALSE, FALSE};
620 2 : if (pfnTransformer(pTransformArg, TRUE, 2, axTemp, ayTemp,
621 2 : azTemp, abSuccess) &&
622 4 : fabs(axTemp[0] - axTemp[1]) < 1e-8 &&
623 2 : fabs(ayTemp[0] - ayTemp[1]) < 1e-8)
624 : {
625 2 : padfX[i] = iSignDiscontinuity * 180.0;
626 : }
627 : }
628 : }
629 : }
630 : }
631 :
632 : /* -------------------------------------------------------------------- */
633 : /* Check if the computed target coordinates are revertable. */
634 : /* If not, try the detailed grid sampling. */
635 : /* -------------------------------------------------------------------- */
636 303 : if (nFailedCount)
637 : {
638 49 : CPLDebug("WARP", "At least one point failed after direct transform");
639 : }
640 : else
641 : {
642 254 : memcpy(padfXRevert, padfX, nSamplePoints * sizeof(double));
643 254 : memcpy(padfYRevert, padfY, nSamplePoints * sizeof(double));
644 254 : memcpy(padfZRevert, padfZ, nSamplePoints * sizeof(double));
645 254 : if (pfnTransformer(pTransformArg, TRUE, nSamplePoints, padfXRevert,
646 254 : padfYRevert, padfZRevert, pabSuccess))
647 : {
648 22053 : for (int i = 0; nFailedCount == 0 && i < nSamplePoints; i++)
649 : {
650 21815 : if (!pabSuccess[i])
651 : {
652 16 : nFailedCount++;
653 16 : break;
654 : }
655 :
656 21799 : double dfRatio = (i % nStepsPlusOne) * dfStep;
657 21799 : if (dfRatio > 0.99)
658 950 : dfRatio = 1.0;
659 :
660 21799 : double dfExpectedX = 0.0;
661 21799 : double dfExpectedY = 0.0;
662 21799 : if (i < nStepsPlusOne)
663 : {
664 5581 : dfExpectedX = dfRatio * nInXSize;
665 : }
666 16218 : else if (i < 2 * nStepsPlusOne)
667 : {
668 5534 : dfExpectedX = dfRatio * nInXSize;
669 5534 : dfExpectedY = nInYSize;
670 : }
671 10684 : else if (i < 3 * nStepsPlusOne)
672 : {
673 5404 : dfExpectedY = dfRatio * nInYSize;
674 : }
675 : else
676 : {
677 5280 : dfExpectedX = nInXSize;
678 5280 : dfExpectedY = dfRatio * nInYSize;
679 : }
680 :
681 21799 : if (fabs(padfXRevert[i] - dfExpectedX) >
682 21799 : nInXSize / static_cast<double>(nSteps) ||
683 21790 : fabs(padfYRevert[i] - dfExpectedY) >
684 21790 : nInYSize / static_cast<double>(nSteps))
685 9 : nFailedCount++;
686 : }
687 254 : if (nFailedCount != 0)
688 25 : CPLDebug("WARP",
689 : "At least one point failed after revert transform");
690 : }
691 : else
692 : {
693 0 : nFailedCount = 1;
694 : }
695 : }
696 :
697 : /* -------------------------------------------------------------------- */
698 : /* If any of the edge points failed to transform, we need to */
699 : /* build a fairly detailed internal grid of points instead to */
700 : /* help identify the area that is transformable. */
701 : /* -------------------------------------------------------------------- */
702 303 : if (nFailedCount)
703 : {
704 74 : nSamplePoints = 0;
705 :
706 : // Take N_STEPS steps.
707 1782 : for (int iStep = 0; iStep <= nSteps; iStep++)
708 : {
709 1708 : double dfRatio = (iStep == nSteps) ? 1.0 : iStep * dfStep;
710 :
711 51102 : for (int iStep2 = 0; iStep2 <= nSteps; iStep2++)
712 : {
713 49394 : const double dfRatio2 =
714 49394 : iStep2 == nSteps ? 1.0 : iStep2 * dfStep;
715 :
716 : // From top to bottom, from left to right.
717 49394 : padfX[nSamplePoints] = dfRatio2 * nInXSize;
718 49394 : padfY[nSamplePoints] = dfRatio * nInYSize;
719 49394 : padfZ[nSamplePoints] = 0.0;
720 49394 : nSamplePoints++;
721 : }
722 : }
723 :
724 74 : CPLAssert(nSamplePoints == nSampleMax);
725 :
726 74 : if (!pfnTransformer(pTransformArg, FALSE, nSamplePoints, padfX, padfY,
727 : padfZ, pabSuccess))
728 : {
729 0 : CPLError(CE_Failure, CPLE_AppDefined,
730 : "GDALSuggestedWarpOutput() failed because the passed"
731 : "transformer failed.");
732 :
733 0 : CPLFree(padfX);
734 0 : CPLFree(padfXRevert);
735 0 : CPLFree(pabSuccess);
736 :
737 0 : return CE_Failure;
738 : }
739 : }
740 :
741 : /* -------------------------------------------------------------------- */
742 : /* Collect the bounds, ignoring any failed points. */
743 : /* -------------------------------------------------------------------- */
744 303 : double dfMinXOut = 0.0;
745 303 : double dfMinYOut = 0.0;
746 303 : double dfMaxXOut = 0.0;
747 303 : double dfMaxYOut = 0.0;
748 303 : bool bGotInitialPoint = false;
749 :
750 303 : nFailedCount = 0;
751 70649 : for (int i = 0; i < nSamplePoints; i++)
752 : {
753 70346 : int x_i = 0;
754 70346 : int y_i = 0;
755 :
756 70346 : if (nSamplePoints == nSampleMax)
757 : {
758 49394 : x_i = i % nStepsPlusOne;
759 49394 : y_i = i / nStepsPlusOne;
760 : }
761 : else
762 : {
763 20952 : if (i < 2 * nStepsPlusOne)
764 : {
765 10476 : x_i = i % nStepsPlusOne;
766 10476 : y_i = (i < nStepsPlusOne) ? 0 : nSteps;
767 : }
768 : }
769 :
770 70346 : if (x_i > 0 && (pabSuccess[i - 1] || pabSuccess[i]))
771 : {
772 46545 : double x_out_before = padfX[i - 1];
773 46545 : double x_out_after = padfX[i];
774 46545 : int nIter = 0;
775 46545 : double x_in_before =
776 46545 : static_cast<double>(x_i - 1) * nInXSize / nSteps;
777 46545 : double x_in_after = static_cast<double>(x_i) * nInXSize / nSteps;
778 46545 : int invalid_before = !(pabSuccess[i - 1]);
779 46545 : int invalid_after = !(pabSuccess[i]);
780 :
781 : // Detect discontinuity in target coordinates when the target x
782 : // coordinates change sign. This may be a false positive when the
783 : // target tx is around 0 Dichotomic search to reduce the interval
784 : // to near the discontinuity and get a better out extent.
785 61917 : while ((invalid_before || invalid_after ||
786 130430 : x_out_before * x_out_after < 0.0) &&
787 : nIter < 16)
788 : {
789 21968 : double x = (x_in_before + x_in_after) / 2.0;
790 21968 : double y = static_cast<double>(y_i) * nInYSize / nSteps;
791 21968 : double z = 0.0;
792 21968 : int bSuccess = TRUE;
793 21968 : if (pfnTransformer(pTransformArg, FALSE, 1, &x, &y, &z,
794 43936 : &bSuccess) &&
795 21968 : bSuccess)
796 : {
797 16495 : if (bGotInitialPoint)
798 : {
799 16477 : dfMinXOut = std::min(dfMinXOut, x);
800 16477 : dfMinYOut = std::min(dfMinYOut, y);
801 16477 : dfMaxXOut = std::max(dfMaxXOut, x);
802 16477 : dfMaxYOut = std::max(dfMaxYOut, y);
803 : }
804 : else
805 : {
806 18 : bGotInitialPoint = true;
807 18 : dfMinXOut = x;
808 18 : dfMaxXOut = x;
809 18 : dfMinYOut = y;
810 18 : dfMaxYOut = y;
811 : }
812 :
813 16495 : if (invalid_before || x_out_before * x < 0)
814 : {
815 8738 : invalid_after = FALSE;
816 8738 : x_in_after = (x_in_before + x_in_after) / 2.0;
817 8738 : x_out_after = x;
818 : }
819 : else
820 : {
821 7757 : invalid_before = FALSE;
822 7757 : x_out_before = x;
823 7757 : x_in_before = (x_in_before + x_in_after) / 2.0;
824 : }
825 : }
826 : else
827 : {
828 5473 : if (invalid_before)
829 : {
830 2727 : x_in_before = (x_in_before + x_in_after) / 2.0;
831 : }
832 2746 : else if (invalid_after)
833 : {
834 2746 : x_in_after = (x_in_before + x_in_after) / 2.0;
835 : }
836 : else
837 : {
838 0 : break;
839 : }
840 : }
841 21968 : nIter++;
842 : }
843 : }
844 :
845 70346 : if (!pabSuccess[i])
846 : {
847 12036 : nFailedCount++;
848 12036 : continue;
849 : }
850 :
851 58310 : if (bGotInitialPoint)
852 : {
853 58026 : dfMinXOut = std::min(dfMinXOut, padfX[i]);
854 58026 : dfMinYOut = std::min(dfMinYOut, padfY[i]);
855 58026 : dfMaxXOut = std::max(dfMaxXOut, padfX[i]);
856 58026 : dfMaxYOut = std::max(dfMaxYOut, padfY[i]);
857 : }
858 : else
859 : {
860 284 : bGotInitialPoint = true;
861 284 : dfMinXOut = padfX[i];
862 284 : dfMaxXOut = padfX[i];
863 284 : dfMinYOut = padfY[i];
864 284 : dfMaxYOut = padfY[i];
865 : }
866 : }
867 :
868 303 : if (nFailedCount > nSamplePoints - 10)
869 : {
870 4 : CPLError(CE_Failure, CPLE_AppDefined,
871 : "Too many points (%d out of %d) failed to transform, "
872 : "unable to compute output bounds.",
873 : nFailedCount, nSamplePoints);
874 :
875 4 : CPLFree(padfX);
876 4 : CPLFree(padfXRevert);
877 4 : CPLFree(pabSuccess);
878 :
879 4 : return CE_Failure;
880 : }
881 :
882 299 : if (nFailedCount)
883 45 : CPLDebug("GDAL",
884 : "GDALSuggestedWarpOutput(): %d out of %d points failed to "
885 : "transform.",
886 : nFailedCount, nSamplePoints);
887 :
888 299 : bool bIsGeographicCoords = false;
889 299 : if (bIsGDALGenImgProjTransform)
890 : {
891 299 : const GDALGenImgProjTransformInfo *pGIPTI =
892 : static_cast<const GDALGenImgProjTransformInfo *>(pTransformArg);
893 299 : if (pGIPTI->pSrcTransformer == GDALGeoLocTransform &&
894 28 : pGIPTI->pDstTransformer == nullptr &&
895 28 : pGIPTI->adfDstGeoTransform[0] == 0 &&
896 26 : pGIPTI->adfDstGeoTransform[1] == 1 &&
897 26 : pGIPTI->adfDstGeoTransform[2] == 0 &&
898 26 : pGIPTI->adfDstGeoTransform[3] == 0 &&
899 26 : pGIPTI->adfDstGeoTransform[4] == 0 &&
900 26 : pGIPTI->adfDstGeoTransform[5] == 1)
901 : {
902 : /* --------------------------------------------------------------------
903 : */
904 : /* Special case for geolocation array, to quickly find the
905 : * bounds. */
906 : /* --------------------------------------------------------------------
907 : */
908 26 : const GDALGeoLocTransformInfo *pGLTI =
909 : static_cast<const GDALGeoLocTransformInfo *>(
910 : pGIPTI->pSrcTransformArg);
911 :
912 26 : if (pGIPTI->pReproject == nullptr)
913 : {
914 : const char *pszGLSRS =
915 26 : CSLFetchNameValue(pGLTI->papszGeolocationInfo, "SRS");
916 26 : if (pszGLSRS == nullptr)
917 : {
918 4 : bIsGeographicCoords = true;
919 : }
920 : else
921 : {
922 44 : OGRSpatialReference oSRS;
923 44 : if (oSRS.SetFromUserInput(pszGLSRS) == OGRERR_NONE &&
924 22 : oSRS.IsGeographic())
925 : {
926 20 : bIsGeographicCoords = true;
927 : }
928 : }
929 : }
930 :
931 208 : for (const auto &xy :
932 26 : {std::pair<double, double>(pGLTI->dfMinX, pGLTI->dfYAtMinX),
933 26 : std::pair<double, double>(pGLTI->dfXAtMinY, pGLTI->dfMinY),
934 26 : std::pair<double, double>(pGLTI->dfMaxX, pGLTI->dfYAtMaxX),
935 130 : std::pair<double, double>(pGLTI->dfXAtMaxY, pGLTI->dfMaxY)})
936 : {
937 104 : double x = xy.first;
938 104 : double y = xy.second;
939 104 : if (pGLTI->bSwapXY)
940 : {
941 4 : std::swap(x, y);
942 : }
943 104 : double xOut = std::numeric_limits<double>::quiet_NaN();
944 104 : double yOut = std::numeric_limits<double>::quiet_NaN();
945 104 : if (pGIPTI->pReproject == nullptr ||
946 0 : pGIPTI->pReproject(pGIPTI->pReprojectArg, false, 1, &x, &y,
947 : nullptr, nullptr))
948 : {
949 104 : xOut = x;
950 104 : yOut = y;
951 : }
952 104 : dfMinXOut = std::min(dfMinXOut, xOut);
953 104 : dfMinYOut = std::min(dfMinYOut, yOut);
954 104 : dfMaxXOut = std::max(dfMaxXOut, xOut);
955 104 : dfMaxYOut = std::max(dfMaxYOut, yOut);
956 26 : }
957 : }
958 273 : else if (pGIPTI->pSrcTransformer == nullptr &&
959 230 : pGIPTI->pDstTransformer == nullptr &&
960 230 : pGIPTI->pReproject == GDALReprojectionTransform &&
961 219 : pGIPTI->adfDstGeoTransform[0] == 0 &&
962 219 : pGIPTI->adfDstGeoTransform[1] == 1 &&
963 219 : pGIPTI->adfDstGeoTransform[2] == 0 &&
964 219 : pGIPTI->adfDstGeoTransform[3] == 0 &&
965 219 : pGIPTI->adfDstGeoTransform[4] == 0 &&
966 219 : pGIPTI->adfDstGeoTransform[5] == 1)
967 : {
968 : /* ------------------------------------------------------------- */
969 : /* Special case for warping using source geotransform and */
970 : /* reprojection to deal with the poles. */
971 : /* ------------------------------------------------------------- */
972 219 : const GDALReprojectionTransformInfo *psRTI =
973 : static_cast<const GDALReprojectionTransformInfo *>(
974 : pGIPTI->pReprojectArg);
975 : const OGRSpatialReference *poSourceCRS =
976 219 : psRTI->poForwardTransform->GetSourceCS();
977 : const OGRSpatialReference *poTargetCRS =
978 219 : psRTI->poForwardTransform->GetTargetCS();
979 437 : if (poTargetCRS != nullptr &&
980 218 : psRTI->poReverseTransform != nullptr &&
981 218 : poTargetCRS->IsGeographic() &&
982 84 : fabs(poTargetCRS->GetAngularUnits() -
983 521 : CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-9 &&
984 84 : (!poSourceCRS || !poSourceCRS->IsGeographic()))
985 : {
986 78 : bIsGeographicCoords = true;
987 :
988 78 : std::unique_ptr<CPLConfigOptionSetter> poSetter;
989 78 : if (pGIPTI->bCheckWithInvertPROJ)
990 : {
991 : // CHECK_WITH_INVERT_PROJ=YES prevent reliable
992 : // transformation of poles.
993 4 : poSetter = std::make_unique<CPLConfigOptionSetter>(
994 4 : "CHECK_WITH_INVERT_PROJ", "NO", false);
995 4 : GDALRefreshGenImgProjTransformer(pTransformArg);
996 : // GDALRefreshGenImgProjTransformer() has invalidated psRTI
997 4 : psRTI = static_cast<const GDALReprojectionTransformInfo *>(
998 : pGIPTI->pReprojectArg);
999 : }
1000 :
1001 234 : for (const auto &sign : iSignArray)
1002 : {
1003 156 : double X = 0.0;
1004 156 : const double Yinit = 90.0 * sign;
1005 156 : double Y = Yinit;
1006 156 : if (psRTI->poReverseTransform->Transform(1, &X, &Y))
1007 : {
1008 102 : const auto invGT = pGIPTI->adfSrcInvGeoTransform;
1009 102 : const double x = invGT[0] + X * invGT[1] + Y * invGT[2];
1010 102 : const double y = invGT[3] + X * invGT[4] + Y * invGT[5];
1011 102 : constexpr double EPSILON = 1e-5;
1012 102 : if (x >= -EPSILON && x <= nInXSize + EPSILON &&
1013 26 : y >= -EPSILON && y <= nInYSize + EPSILON)
1014 : {
1015 6 : if (psRTI->poForwardTransform->Transform(1, &X,
1016 12 : &Y) &&
1017 6 : fabs(Y - Yinit) <= 1e-6)
1018 : {
1019 6 : bool bMinXMaxXSet = false;
1020 6 : if (poSourceCRS)
1021 : {
1022 : const char *pszProjection =
1023 6 : poSourceCRS->GetAttrValue("PROJECTION");
1024 6 : if (pszProjection &&
1025 6 : EQUAL(pszProjection,
1026 : SRS_PT_ORTHOGRAPHIC))
1027 : {
1028 : const double dfLon0 =
1029 4 : poSourceCRS->GetNormProjParm(
1030 : SRS_PP_CENTRAL_MERIDIAN, 0.0);
1031 4 : dfMinXOut = dfLon0 - 90;
1032 4 : dfMaxXOut = dfLon0 + 90;
1033 4 : bMinXMaxXSet = true;
1034 : }
1035 : }
1036 6 : if (!bMinXMaxXSet)
1037 : {
1038 2 : dfMinXOut = -180;
1039 2 : dfMaxXOut = 180;
1040 : }
1041 6 : if (sign < 0)
1042 2 : dfMinYOut = Yinit;
1043 : else
1044 4 : dfMaxYOut = Yinit;
1045 : }
1046 : }
1047 : }
1048 : }
1049 :
1050 78 : if (poSetter)
1051 : {
1052 4 : poSetter.reset();
1053 4 : GDALRefreshGenImgProjTransformer(pTransformArg);
1054 4 : pGIPTI = static_cast<const GDALGenImgProjTransformInfo *>(
1055 : pTransformArg);
1056 4 : psRTI = static_cast<const GDALReprojectionTransformInfo *>(
1057 : pGIPTI->pReprojectArg);
1058 4 : poSourceCRS = psRTI->poForwardTransform->GetSourceCS();
1059 4 : poTargetCRS = psRTI->poForwardTransform->GetTargetCS();
1060 : }
1061 : }
1062 :
1063 : // Use TransformBounds() to handle more particular cases
1064 219 : if (poSourceCRS != nullptr && poTargetCRS != nullptr &&
1065 218 : pGIPTI->adfSrcGeoTransform[1] != 0 &&
1066 218 : pGIPTI->adfSrcGeoTransform[2] == 0 &&
1067 218 : pGIPTI->adfSrcGeoTransform[4] == 0 &&
1068 218 : pGIPTI->adfSrcGeoTransform[5] != 0)
1069 : {
1070 218 : const double dfULX = pGIPTI->adfSrcGeoTransform[0];
1071 218 : const double dfULY = pGIPTI->adfSrcGeoTransform[3];
1072 218 : const double dfLRX =
1073 218 : dfULX + pGIPTI->adfSrcGeoTransform[1] * nInXSize;
1074 218 : const double dfLRY =
1075 218 : dfULY + pGIPTI->adfSrcGeoTransform[5] * nInYSize;
1076 218 : const double dfMinSrcX = std::min(dfULX, dfLRX);
1077 218 : const double dfMinSrcY = std::min(dfULY, dfLRY);
1078 218 : const double dfMaxSrcX = std::max(dfULX, dfLRX);
1079 218 : const double dfMaxSrcY = std::max(dfULY, dfLRY);
1080 218 : double dfTmpMinXOut = std::numeric_limits<double>::max();
1081 218 : double dfTmpMinYOut = std::numeric_limits<double>::max();
1082 218 : double dfTmpMaxXOut = std::numeric_limits<double>::min();
1083 218 : double dfTmpMaxYOut = std::numeric_limits<double>::min();
1084 436 : if (psRTI->poForwardTransform->TransformBounds(
1085 : dfMinSrcX, dfMinSrcY, dfMaxSrcX, dfMaxSrcY,
1086 : &dfTmpMinXOut, &dfTmpMinYOut, &dfTmpMaxXOut,
1087 : &dfTmpMaxYOut,
1088 218 : 2)) // minimum number of points as we already have a
1089 : // logic above to sample
1090 : {
1091 207 : dfMinXOut = std::min(dfMinXOut, dfTmpMinXOut);
1092 207 : dfMinYOut = std::min(dfMinYOut, dfTmpMinYOut);
1093 207 : dfMaxXOut = std::max(dfMaxXOut, dfTmpMaxXOut);
1094 207 : dfMaxYOut = std::max(dfMaxYOut, dfTmpMaxYOut);
1095 : }
1096 : }
1097 : }
1098 : }
1099 :
1100 : /* -------------------------------------------------------------------- */
1101 : /* Compute the distance in "georeferenced" units from the top */
1102 : /* corner of the transformed input image to the bottom left */
1103 : /* corner of the transformed input. Use this distance to */
1104 : /* compute an approximate pixel size in the output */
1105 : /* georeferenced coordinates. */
1106 : /* -------------------------------------------------------------------- */
1107 299 : double dfDiagonalDist = 0.0;
1108 299 : double dfDeltaX = 0.0;
1109 299 : double dfDeltaY = 0.0;
1110 :
1111 299 : if (pabSuccess[0] && pabSuccess[nSamplePoints - 1])
1112 : {
1113 257 : dfDeltaX = padfX[nSamplePoints - 1] - padfX[0];
1114 257 : dfDeltaY = padfY[nSamplePoints - 1] - padfY[0];
1115 : // In some cases this can result in 0 values. See #5980
1116 : // Fallback to safer method in that case.
1117 : }
1118 299 : if (dfDeltaX == 0.0 || dfDeltaY == 0.0)
1119 : {
1120 47 : dfDeltaX = dfMaxXOut - dfMinXOut;
1121 47 : dfDeltaY = dfMaxYOut - dfMinYOut;
1122 : }
1123 :
1124 299 : dfDiagonalDist = sqrt(dfDeltaX * dfDeltaX + dfDeltaY * dfDeltaY);
1125 :
1126 : /* -------------------------------------------------------------------- */
1127 : /* Compute a pixel size from this. */
1128 : /* -------------------------------------------------------------------- */
1129 : const double dfPixelSize =
1130 299 : dfDiagonalDist / sqrt(static_cast<double>(nInXSize) * nInXSize +
1131 299 : static_cast<double>(nInYSize) * nInYSize);
1132 :
1133 299 : const double dfPixels = (dfMaxXOut - dfMinXOut) / dfPixelSize;
1134 299 : const double dfLines = (dfMaxYOut - dfMinYOut) / dfPixelSize;
1135 :
1136 299 : const int knIntMaxMinusOne = std::numeric_limits<int>::max() - 1;
1137 299 : if (dfPixels > knIntMaxMinusOne || dfLines > knIntMaxMinusOne)
1138 : {
1139 0 : CPLError(CE_Failure, CPLE_AppDefined,
1140 : "Computed dimensions are too big : %.0f x %.0f",
1141 : dfPixels + 0.5, dfLines + 0.5);
1142 :
1143 0 : CPLFree(padfX);
1144 0 : CPLFree(padfXRevert);
1145 0 : CPLFree(pabSuccess);
1146 :
1147 0 : return CE_Failure;
1148 : }
1149 :
1150 299 : if ((nOptions & GDAL_SWO_ROUND_UP_SIZE) != 0)
1151 : {
1152 8 : constexpr double EPS = 1e-5;
1153 8 : *pnPixels = static_cast<int>(std::ceil(dfPixels - EPS));
1154 8 : *pnLines = static_cast<int>(std::ceil(dfLines - EPS));
1155 : }
1156 : else
1157 : {
1158 291 : *pnPixels = static_cast<int>(dfPixels + 0.5);
1159 291 : *pnLines = static_cast<int>(dfLines + 0.5);
1160 : }
1161 :
1162 299 : double dfPixelSizeX = dfPixelSize;
1163 299 : double dfPixelSizeY = dfPixelSize;
1164 :
1165 299 : const double adfRatioArray[] = {0.000, 0.001, 0.010, 0.100, 1.000};
1166 :
1167 : /* -------------------------------------------------------------------- */
1168 : /* Check that the right border is not completely out of source */
1169 : /* image. If so, adjust the x pixel size a bit in the hope it will */
1170 : /* fit. */
1171 : /* -------------------------------------------------------------------- */
1172 478 : for (const auto &dfRatio : adfRatioArray)
1173 : {
1174 470 : const double dfTryPixelSizeX =
1175 470 : dfPixelSizeX - dfPixelSizeX * dfRatio / *pnPixels;
1176 470 : double adfExtent[4] = {dfMinXOut, dfMaxYOut - (*pnLines) * dfPixelSizeY,
1177 470 : dfMinXOut + (*pnPixels) * dfTryPixelSizeX,
1178 470 : dfMaxYOut};
1179 470 : if (!GDALSuggestedWarpOutput2_MustAdjustForRightBorder(
1180 : pfnTransformer, pTransformArg, adfExtent, *pnPixels, *pnLines,
1181 : dfTryPixelSizeX, dfPixelSizeY))
1182 : {
1183 291 : dfPixelSizeX = dfTryPixelSizeX;
1184 291 : break;
1185 : }
1186 : }
1187 :
1188 : /* -------------------------------------------------------------------- */
1189 : /* Check that the bottom border is not completely out of source */
1190 : /* image. If so, adjust the y pixel size a bit in the hope it will */
1191 : /* fit. */
1192 : /* -------------------------------------------------------------------- */
1193 379 : for (const auto &dfRatio : adfRatioArray)
1194 : {
1195 369 : const double dfTryPixelSizeY =
1196 369 : dfPixelSizeY - dfPixelSizeY * dfRatio / *pnLines;
1197 : double adfExtent[4] = {
1198 369 : dfMinXOut, dfMaxYOut - (*pnLines) * dfTryPixelSizeY,
1199 369 : dfMinXOut + (*pnPixels) * dfPixelSizeX, dfMaxYOut};
1200 369 : if (!GDALSuggestedWarpOutput2_MustAdjustForBottomBorder(
1201 : pfnTransformer, pTransformArg, adfExtent, *pnPixels, *pnLines,
1202 : dfPixelSizeX, dfTryPixelSizeY))
1203 : {
1204 289 : dfPixelSizeY = dfTryPixelSizeY;
1205 289 : break;
1206 : }
1207 : }
1208 :
1209 : /* -------------------------------------------------------------------- */
1210 : /* Recompute some bounds so that all return values are consistent */
1211 : /* -------------------------------------------------------------------- */
1212 299 : double dfMaxXOutNew = dfMinXOut + (*pnPixels) * dfPixelSizeX;
1213 299 : if (bIsGeographicCoords &&
1214 102 : ((dfMaxXOut <= 180 && dfMaxXOutNew > 180) || dfMaxXOut == 180))
1215 : {
1216 3 : dfMaxXOut = 180;
1217 3 : dfPixelSizeX = (dfMaxXOut - dfMinXOut) / *pnPixels;
1218 : }
1219 : else
1220 : {
1221 296 : dfMaxXOut = dfMaxXOutNew;
1222 : }
1223 :
1224 299 : double dfMinYOutNew = dfMaxYOut - (*pnLines) * dfPixelSizeY;
1225 299 : if (bIsGeographicCoords && dfMinYOut >= -90 && dfMinYOutNew < -90)
1226 : {
1227 0 : dfMinYOut = -90;
1228 0 : dfPixelSizeY = (dfMaxYOut - dfMinYOut) / *pnLines;
1229 : }
1230 : else
1231 : {
1232 299 : dfMinYOut = dfMinYOutNew;
1233 : }
1234 :
1235 : /* -------------------------------------------------------------------- */
1236 : /* Return raw extents. */
1237 : /* -------------------------------------------------------------------- */
1238 299 : padfExtent[0] = dfMinXOut;
1239 299 : padfExtent[1] = dfMinYOut;
1240 299 : padfExtent[2] = dfMaxXOut;
1241 299 : padfExtent[3] = dfMaxYOut;
1242 :
1243 : /* -------------------------------------------------------------------- */
1244 : /* Set the output geotransform. */
1245 : /* -------------------------------------------------------------------- */
1246 299 : padfGeoTransformOut[0] = dfMinXOut;
1247 299 : padfGeoTransformOut[1] = dfPixelSizeX;
1248 299 : padfGeoTransformOut[2] = 0.0;
1249 299 : padfGeoTransformOut[3] = dfMaxYOut;
1250 299 : padfGeoTransformOut[4] = 0.0;
1251 299 : padfGeoTransformOut[5] = -dfPixelSizeY;
1252 :
1253 299 : CPLFree(padfX);
1254 299 : CPLFree(padfXRevert);
1255 299 : CPLFree(pabSuccess);
1256 :
1257 299 : return CE_None;
1258 : }
1259 :
1260 : /************************************************************************/
1261 : /* GetCurrentCheckWithInvertPROJ() */
1262 : /************************************************************************/
1263 :
1264 2519 : static bool GetCurrentCheckWithInvertPROJ()
1265 : {
1266 2519 : return CPLTestBool(CPLGetConfigOption("CHECK_WITH_INVERT_PROJ", "NO"));
1267 : }
1268 :
1269 : /************************************************************************/
1270 : /* GDALCreateGenImgProjTransformerInternal() */
1271 : /************************************************************************/
1272 :
1273 : static void *GDALCreateSimilarGenImgProjTransformer(void *hTransformArg,
1274 : double dfRatioX,
1275 : double dfRatioY);
1276 :
1277 1557 : static GDALGenImgProjTransformInfo *GDALCreateGenImgProjTransformerInternal()
1278 : {
1279 : /* -------------------------------------------------------------------- */
1280 : /* Initialize the transform info. */
1281 : /* -------------------------------------------------------------------- */
1282 : GDALGenImgProjTransformInfo *psInfo =
1283 : static_cast<GDALGenImgProjTransformInfo *>(
1284 1557 : CPLCalloc(sizeof(GDALGenImgProjTransformInfo), 1));
1285 :
1286 1557 : memcpy(psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
1287 : strlen(GDAL_GTI2_SIGNATURE));
1288 1557 : psInfo->sTI.pszClassName = GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME;
1289 1557 : psInfo->sTI.pfnTransform = GDALGenImgProjTransform;
1290 1557 : psInfo->sTI.pfnCleanup = GDALDestroyGenImgProjTransformer;
1291 1557 : psInfo->sTI.pfnSerialize = GDALSerializeGenImgProjTransformer;
1292 1557 : psInfo->sTI.pfnCreateSimilar = GDALCreateSimilarGenImgProjTransformer;
1293 :
1294 1557 : psInfo->bCheckWithInvertPROJ = GetCurrentCheckWithInvertPROJ();
1295 1557 : psInfo->bHasCustomTransformationPipeline = false;
1296 :
1297 1557 : return psInfo;
1298 : }
1299 :
1300 : /************************************************************************/
1301 : /* GDALCreateSimilarGenImgProjTransformer() */
1302 : /************************************************************************/
1303 :
1304 34 : static void *GDALCreateSimilarGenImgProjTransformer(void *hTransformArg,
1305 : double dfRatioX,
1306 : double dfRatioY)
1307 : {
1308 34 : VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarGenImgProjTransformer",
1309 : nullptr);
1310 :
1311 34 : GDALGenImgProjTransformInfo *psInfo =
1312 : static_cast<GDALGenImgProjTransformInfo *>(hTransformArg);
1313 :
1314 : GDALGenImgProjTransformInfo *psClonedInfo =
1315 34 : GDALCreateGenImgProjTransformerInternal();
1316 :
1317 34 : memcpy(psClonedInfo, psInfo, sizeof(GDALGenImgProjTransformInfo));
1318 :
1319 34 : psClonedInfo->bCheckWithInvertPROJ = GetCurrentCheckWithInvertPROJ();
1320 :
1321 34 : if (psClonedInfo->pSrcTransformArg)
1322 7 : psClonedInfo->pSrcTransformArg = GDALCreateSimilarTransformer(
1323 : psInfo->pSrcTransformArg, dfRatioX, dfRatioY);
1324 27 : else if (dfRatioX != 1.0 || dfRatioY != 1.0)
1325 : {
1326 14 : if (psClonedInfo->adfSrcGeoTransform[2] == 0.0 &&
1327 14 : psClonedInfo->adfSrcGeoTransform[4] == 0.0)
1328 : {
1329 14 : psClonedInfo->adfSrcGeoTransform[1] *= dfRatioX;
1330 14 : psClonedInfo->adfSrcGeoTransform[5] *= dfRatioY;
1331 : }
1332 : else
1333 : {
1334 : // If the x and y ratios are not equal, then we cannot really
1335 : // compute a geotransform.
1336 0 : psClonedInfo->adfSrcGeoTransform[1] *= dfRatioX;
1337 0 : psClonedInfo->adfSrcGeoTransform[2] *= dfRatioX;
1338 0 : psClonedInfo->adfSrcGeoTransform[4] *= dfRatioX;
1339 0 : psClonedInfo->adfSrcGeoTransform[5] *= dfRatioX;
1340 : }
1341 14 : if (!GDALInvGeoTransform(psClonedInfo->adfSrcGeoTransform,
1342 14 : psClonedInfo->adfSrcInvGeoTransform))
1343 : {
1344 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
1345 0 : GDALDestroyGenImgProjTransformer(psClonedInfo);
1346 0 : return nullptr;
1347 : }
1348 : }
1349 :
1350 34 : if (psClonedInfo->pReprojectArg)
1351 10 : psClonedInfo->pReprojectArg =
1352 10 : GDALCloneTransformer(psInfo->pReprojectArg);
1353 :
1354 34 : if (psClonedInfo->pDstTransformArg)
1355 0 : psClonedInfo->pDstTransformArg =
1356 0 : GDALCloneTransformer(psInfo->pDstTransformArg);
1357 :
1358 34 : return psClonedInfo;
1359 : }
1360 :
1361 : /************************************************************************/
1362 : /* GDALCreateGenImgProjTransformer() */
1363 : /************************************************************************/
1364 :
1365 : /**
1366 : * Create image to image transformer.
1367 : *
1368 : * This function creates a transformation object that maps from pixel/line
1369 : * coordinates on one image to pixel/line coordinates on another image. The
1370 : * images may potentially be georeferenced in different coordinate systems,
1371 : * and may used GCPs to map between their pixel/line coordinates and
1372 : * georeferenced coordinates (as opposed to the default assumption that their
1373 : * geotransform should be used).
1374 : *
1375 : * This transformer potentially performs three concatenated transformations.
1376 : *
1377 : * The first stage is from source image pixel/line coordinates to source
1378 : * image georeferenced coordinates, and may be done using the geotransform,
1379 : * or if not defined using a polynomial model derived from GCPs. If GCPs
1380 : * are used this stage is accomplished using GDALGCPTransform().
1381 : *
1382 : * The second stage is to change projections from the source coordinate system
1383 : * to the destination coordinate system, assuming they differ. This is
1384 : * accomplished internally using GDALReprojectionTransform().
1385 : *
1386 : * The third stage is converting from destination image georeferenced
1387 : * coordinates to destination image coordinates. This is done using the
1388 : * destination image geotransform, or if not available, using a polynomial
1389 : * model derived from GCPs. If GCPs are used this stage is accomplished using
1390 : * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the
1391 : * transformation is created.
1392 : *
1393 : * @param hSrcDS source dataset, or NULL.
1394 : * @param pszSrcWKT the coordinate system for the source dataset. If NULL,
1395 : * it will be read from the dataset itself.
1396 : * @param hDstDS destination dataset (or NULL).
1397 : * @param pszDstWKT the coordinate system for the destination dataset. If
1398 : * NULL, and hDstDS not NULL, it will be read from the destination dataset.
1399 : * @param bGCPUseOK TRUE if GCPs should be used if the geotransform is not
1400 : * available on the source dataset (not destination).
1401 : * @param dfGCPErrorThreshold ignored/deprecated.
1402 : * @param nOrder the maximum order to use for GCP derived polynomials if
1403 : * possible. Use 0 to autoselect, or -1 for thin plate splines.
1404 : *
1405 : * @return handle suitable for use GDALGenImgProjTransform(), and to be
1406 : * deallocated with GDALDestroyGenImgProjTransformer().
1407 : */
1408 :
1409 90 : void *GDALCreateGenImgProjTransformer(GDALDatasetH hSrcDS,
1410 : const char *pszSrcWKT,
1411 : GDALDatasetH hDstDS,
1412 : const char *pszDstWKT, int bGCPUseOK,
1413 : CPL_UNUSED double dfGCPErrorThreshold,
1414 : int nOrder)
1415 : {
1416 90 : char **papszOptions = nullptr;
1417 :
1418 90 : if (pszSrcWKT != nullptr)
1419 31 : papszOptions = CSLSetNameValue(papszOptions, "SRC_SRS", pszSrcWKT);
1420 90 : if (pszDstWKT != nullptr)
1421 31 : papszOptions = CSLSetNameValue(papszOptions, "DST_SRS", pszDstWKT);
1422 90 : if (!bGCPUseOK)
1423 0 : papszOptions = CSLSetNameValue(papszOptions, "GCPS_OK", "FALSE");
1424 90 : if (nOrder != 0)
1425 0 : papszOptions = CSLSetNameValue(papszOptions, "MAX_GCP_ORDER",
1426 0 : CPLString().Printf("%d", nOrder));
1427 :
1428 90 : void *pRet = GDALCreateGenImgProjTransformer2(hSrcDS, hDstDS, papszOptions);
1429 90 : CSLDestroy(papszOptions);
1430 :
1431 90 : return pRet;
1432 : }
1433 :
1434 : /************************************************************************/
1435 : /* InsertCenterLong() */
1436 : /* */
1437 : /* Insert a CENTER_LONG Extension entry on a GEOGCS to indicate */
1438 : /* the center longitude of the dataset for wrapping purposes. */
1439 : /************************************************************************/
1440 :
1441 687 : static void InsertCenterLong(GDALDatasetH hDS, OGRSpatialReference *poSRS,
1442 : CPLStringList &aosOptions)
1443 :
1444 : {
1445 687 : if (!poSRS->IsGeographic())
1446 166 : return;
1447 :
1448 522 : if (poSRS->GetExtension(nullptr, "CENTER_LONG"))
1449 0 : return;
1450 :
1451 : /* -------------------------------------------------------------------- */
1452 : /* For now we only do this if we have a geotransform since */
1453 : /* other forms require a bunch of extra work. */
1454 : /* -------------------------------------------------------------------- */
1455 522 : double adfGeoTransform[6] = {};
1456 :
1457 522 : if (GDALGetGeoTransform(hDS, adfGeoTransform) != CE_None)
1458 0 : return;
1459 :
1460 : /* -------------------------------------------------------------------- */
1461 : /* Compute min/max longitude based on testing the four corners. */
1462 : /* -------------------------------------------------------------------- */
1463 522 : const int nXSize = GDALGetRasterXSize(hDS);
1464 522 : const int nYSize = GDALGetRasterYSize(hDS);
1465 :
1466 : const double dfMinLong =
1467 1044 : std::min(std::min(adfGeoTransform[0] + 0 * adfGeoTransform[1] +
1468 522 : 0 * adfGeoTransform[2],
1469 1044 : adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
1470 522 : 0 * adfGeoTransform[2]),
1471 1044 : std::min(adfGeoTransform[0] + 0 * adfGeoTransform[1] +
1472 522 : nYSize * adfGeoTransform[2],
1473 1044 : adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
1474 522 : nYSize * adfGeoTransform[2]));
1475 : const double dfMaxLong =
1476 1044 : std::max(std::max(adfGeoTransform[0] + 0 * adfGeoTransform[1] +
1477 522 : 0 * adfGeoTransform[2],
1478 1044 : adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
1479 522 : 0 * adfGeoTransform[2]),
1480 1044 : std::max(adfGeoTransform[0] + 0 * adfGeoTransform[1] +
1481 522 : nYSize * adfGeoTransform[2],
1482 1044 : adfGeoTransform[0] + nXSize * adfGeoTransform[1] +
1483 522 : nYSize * adfGeoTransform[2]));
1484 :
1485 522 : if (dfMaxLong - dfMinLong > 360.0)
1486 1 : return;
1487 :
1488 : /* -------------------------------------------------------------------- */
1489 : /* Insert center long. */
1490 : /* -------------------------------------------------------------------- */
1491 521 : const double dfCenterLong = (dfMaxLong + dfMinLong) / 2.0;
1492 521 : aosOptions.SetNameValue("CENTER_LONG", CPLSPrintf("%g", dfCenterLong));
1493 : }
1494 :
1495 : /************************************************************************/
1496 : /* GDALComputeAreaOfInterest() */
1497 : /************************************************************************/
1498 :
1499 951 : bool GDALComputeAreaOfInterest(OGRSpatialReference *poSRS, double adfGT[6],
1500 : int nXSize, int nYSize,
1501 : double &dfWestLongitudeDeg,
1502 : double &dfSouthLatitudeDeg,
1503 : double &dfEastLongitudeDeg,
1504 : double &dfNorthLatitudeDeg)
1505 : {
1506 951 : bool ret = false;
1507 :
1508 951 : if (!poSRS)
1509 0 : return false;
1510 :
1511 951 : OGRSpatialReference oSrcSRSHoriz(*poSRS);
1512 951 : if (oSrcSRSHoriz.IsCompound())
1513 : {
1514 17 : oSrcSRSHoriz.StripVertical();
1515 : }
1516 :
1517 951 : OGRSpatialReference *poGeog = oSrcSRSHoriz.CloneGeogCS();
1518 951 : if (poGeog)
1519 : {
1520 951 : poGeog->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1521 951 : poGeog->SetAngularUnits(SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV));
1522 :
1523 951 : auto poCT = OGRCreateCoordinateTransformation(&oSrcSRSHoriz, poGeog);
1524 951 : if (poCT)
1525 : {
1526 951 : poCT->SetEmitErrors(false);
1527 :
1528 : double x[4], y[4];
1529 951 : x[0] = adfGT[0];
1530 951 : y[0] = adfGT[3];
1531 951 : x[1] = adfGT[0] + nXSize * adfGT[1];
1532 951 : y[1] = adfGT[3];
1533 951 : x[2] = adfGT[0];
1534 951 : y[2] = adfGT[3] + nYSize * adfGT[5];
1535 951 : x[3] = x[1];
1536 951 : y[3] = y[2];
1537 951 : int validity[4] = {false, false, false, false};
1538 951 : poCT->Transform(4, x, y, nullptr, validity);
1539 951 : dfWestLongitudeDeg = std::numeric_limits<double>::max();
1540 951 : dfSouthLatitudeDeg = std::numeric_limits<double>::max();
1541 951 : dfEastLongitudeDeg = -std::numeric_limits<double>::max();
1542 951 : dfNorthLatitudeDeg = -std::numeric_limits<double>::max();
1543 4755 : for (int i = 0; i < 4; i++)
1544 : {
1545 3804 : if (validity[i])
1546 : {
1547 3792 : ret = true;
1548 3792 : dfWestLongitudeDeg = std::min(dfWestLongitudeDeg, x[i]);
1549 3792 : dfSouthLatitudeDeg = std::min(dfSouthLatitudeDeg, y[i]);
1550 3792 : dfEastLongitudeDeg = std::max(dfEastLongitudeDeg, x[i]);
1551 3792 : dfNorthLatitudeDeg = std::max(dfNorthLatitudeDeg, y[i]);
1552 : }
1553 : }
1554 951 : if (validity[0] && validity[1] && x[0] > x[1])
1555 : {
1556 3 : dfWestLongitudeDeg = x[0];
1557 3 : dfEastLongitudeDeg = x[1];
1558 : }
1559 951 : if (ret && std::fabs(dfWestLongitudeDeg) <= 180 &&
1560 947 : std::fabs(dfEastLongitudeDeg) <= 180 &&
1561 944 : std::fabs(dfSouthLatitudeDeg) <= 90 &&
1562 940 : std::fabs(dfNorthLatitudeDeg) <= 90)
1563 : {
1564 940 : CPLDebug("GDAL", "Computing area of interest: %g, %g, %g, %g",
1565 : dfWestLongitudeDeg, dfSouthLatitudeDeg,
1566 : dfEastLongitudeDeg, dfNorthLatitudeDeg);
1567 : }
1568 : else
1569 : {
1570 11 : CPLDebug("GDAL", "Could not compute area of interest");
1571 11 : dfWestLongitudeDeg = 0;
1572 11 : dfSouthLatitudeDeg = 0;
1573 11 : dfEastLongitudeDeg = 0;
1574 11 : dfNorthLatitudeDeg = 0;
1575 : }
1576 951 : OGRCoordinateTransformation::DestroyCT(poCT);
1577 : }
1578 :
1579 951 : delete poGeog;
1580 : }
1581 :
1582 951 : return ret;
1583 : }
1584 :
1585 3 : bool GDALComputeAreaOfInterest(OGRSpatialReference *poSRS, double dfX1,
1586 : double dfY1, double dfX2, double dfY2,
1587 : double &dfWestLongitudeDeg,
1588 : double &dfSouthLatitudeDeg,
1589 : double &dfEastLongitudeDeg,
1590 : double &dfNorthLatitudeDeg)
1591 : {
1592 3 : bool ret = false;
1593 :
1594 3 : if (!poSRS)
1595 0 : return false;
1596 :
1597 3 : OGRSpatialReference oSrcSRSHoriz(*poSRS);
1598 3 : if (oSrcSRSHoriz.IsCompound())
1599 : {
1600 0 : oSrcSRSHoriz.StripVertical();
1601 : }
1602 :
1603 3 : OGRSpatialReference *poGeog = oSrcSRSHoriz.CloneGeogCS();
1604 3 : if (poGeog)
1605 : {
1606 3 : poGeog->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1607 :
1608 3 : auto poCT = OGRCreateCoordinateTransformation(&oSrcSRSHoriz, poGeog);
1609 3 : if (poCT)
1610 : {
1611 : double x[4], y[4];
1612 3 : x[0] = dfX1;
1613 3 : y[0] = dfY1;
1614 3 : x[1] = dfX2;
1615 3 : y[1] = dfY1;
1616 3 : x[2] = dfX1;
1617 3 : y[2] = dfY2;
1618 3 : x[3] = dfX2;
1619 3 : y[3] = dfY2;
1620 3 : int validity[4] = {false, false, false, false};
1621 3 : poCT->Transform(4, x, y, nullptr, validity);
1622 3 : dfWestLongitudeDeg = std::numeric_limits<double>::max();
1623 3 : dfSouthLatitudeDeg = std::numeric_limits<double>::max();
1624 3 : dfEastLongitudeDeg = -std::numeric_limits<double>::max();
1625 3 : dfNorthLatitudeDeg = -std::numeric_limits<double>::max();
1626 15 : for (int i = 0; i < 4; i++)
1627 : {
1628 12 : if (validity[i])
1629 : {
1630 12 : ret = true;
1631 12 : dfWestLongitudeDeg = std::min(dfWestLongitudeDeg, x[i]);
1632 12 : dfSouthLatitudeDeg = std::min(dfSouthLatitudeDeg, y[i]);
1633 12 : dfEastLongitudeDeg = std::max(dfEastLongitudeDeg, x[i]);
1634 12 : dfNorthLatitudeDeg = std::max(dfNorthLatitudeDeg, y[i]);
1635 : }
1636 : }
1637 3 : if (validity[0] && validity[1] && (dfX1 - dfX2) * (x[0] - x[1]) < 0)
1638 : {
1639 0 : dfWestLongitudeDeg = x[0];
1640 0 : dfEastLongitudeDeg = x[1];
1641 : }
1642 3 : if (ret)
1643 : {
1644 3 : CPLDebug("GDAL", "Computing area of interest: %g, %g, %g, %g",
1645 : dfWestLongitudeDeg, dfSouthLatitudeDeg,
1646 : dfEastLongitudeDeg, dfNorthLatitudeDeg);
1647 : }
1648 : else
1649 : {
1650 0 : CPLDebug("GDAL", "Could not compute area of interest");
1651 0 : dfWestLongitudeDeg = 0;
1652 0 : dfSouthLatitudeDeg = 0;
1653 0 : dfEastLongitudeDeg = 0;
1654 0 : dfNorthLatitudeDeg = 0;
1655 : }
1656 3 : delete poCT;
1657 : }
1658 :
1659 3 : delete poGeog;
1660 : }
1661 :
1662 3 : return ret;
1663 : }
1664 :
1665 : /************************************************************************/
1666 : /* GDALGCPAntimeridianUnwrap() */
1667 : /************************************************************************/
1668 :
1669 : /* Deal with discontinuties of dfGCPX longitudes around the anti-meridian.
1670 : * Cf https://github.com/OSGeo/gdal/issues/8371
1671 : */
1672 40 : static void GDALGCPAntimeridianUnwrap(int nGCPCount, GDAL_GCP *pasGCPList,
1673 : const OGRSpatialReference &oSRS,
1674 : CSLConstList papszOptions)
1675 : {
1676 : const char *pszGCPAntimeridianUnwrap =
1677 40 : CSLFetchNameValueDef(papszOptions, "GCP_ANTIMERIDIAN_UNWRAP", "AUTO");
1678 119 : const bool bForced = EQUAL(pszGCPAntimeridianUnwrap, "YES") ||
1679 39 : EQUAL(pszGCPAntimeridianUnwrap, "ON") ||
1680 118 : EQUAL(pszGCPAntimeridianUnwrap, "TRUE") ||
1681 39 : EQUAL(pszGCPAntimeridianUnwrap, "1");
1682 46 : if (bForced || (!oSRS.IsEmpty() && oSRS.IsGeographic() &&
1683 6 : fabs(oSRS.GetAngularUnits(nullptr) -
1684 6 : CPLAtof(SRS_UA_DEGREE_CONV)) < 1e-8 &&
1685 6 : EQUAL(pszGCPAntimeridianUnwrap, "AUTO")))
1686 : {
1687 6 : if (!bForced)
1688 : {
1689 : // Proceed to unwrapping only if the longitudes are within
1690 : // [-180, -170] or [170, 180]
1691 425 : for (int i = 0; i < nGCPCount; ++i)
1692 : {
1693 423 : const double dfLongAbs = fabs(pasGCPList[i].dfGCPX);
1694 423 : if (dfLongAbs > 180 || dfLongAbs < 170)
1695 : {
1696 3 : return;
1697 : }
1698 : }
1699 : }
1700 :
1701 3 : bool bDone = false;
1702 633 : for (int i = 0; i < nGCPCount; ++i)
1703 : {
1704 630 : if (pasGCPList[i].dfGCPX < 0)
1705 : {
1706 48 : if (!bDone)
1707 : {
1708 3 : bDone = true;
1709 3 : CPLDebug("WARP", "GCP longitude unwrapping");
1710 : }
1711 48 : pasGCPList[i].dfGCPX += 360;
1712 : }
1713 : }
1714 : }
1715 : }
1716 :
1717 : /************************************************************************/
1718 : /* GDALCreateGenImgProjTransformer2() */
1719 : /************************************************************************/
1720 :
1721 : /* clang-format off */
1722 : /**
1723 : * Create image to image transformer.
1724 : *
1725 : * This function creates a transformation object that maps from pixel/line
1726 : * coordinates on one image to pixel/line coordinates on another image. The
1727 : * images may potentially be georeferenced in different coordinate systems,
1728 : * and may used GCPs to map between their pixel/line coordinates and
1729 : * georeferenced coordinates (as opposed to the default assumption that their
1730 : * geotransform should be used).
1731 : *
1732 : * This transformer potentially performs three concatenated transformations.
1733 : *
1734 : * The first stage is from source image pixel/line coordinates to source
1735 : * image georeferenced coordinates, and may be done using the geotransform,
1736 : * or if not defined using a polynomial model derived from GCPs. If GCPs
1737 : * are used this stage is accomplished using GDALGCPTransform().
1738 : *
1739 : * The second stage is to change projections from the source coordinate system
1740 : * to the destination coordinate system, assuming they differ. This is
1741 : * accomplished internally using GDALReprojectionTransform().
1742 : *
1743 : * The third stage is converting from destination image georeferenced
1744 : * coordinates to destination image coordinates. This is done using the
1745 : * destination image geotransform, or if not available, using a polynomial
1746 : * model derived from GCPs. If GCPs are used this stage is accomplished using
1747 : * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the
1748 : * transformation is created.
1749 : *
1750 : * Supported Options (specified with the -to switch of gdalwarp for example):
1751 : * <ul>
1752 : * <li> SRC_SRS: WKT SRS, or any string recognized by
1753 : * OGRSpatialReference::SetFromUserInput(), to be used as an override for
1754 : * hSrcDS.</li>
1755 : * <li> DST_SRS: WKT SRS, or any string recognized by
1756 : * OGRSpatialReference::SetFromUserInput(), to be used as an override for
1757 : * hDstDS.
1758 : * </li>
1759 : * <li> COORDINATE_OPERATION: (GDAL >= 3.0) Coordinate operation, as
1760 : * a PROJ or WKT string, used as an override over the normally computed
1761 : * pipeline. The pipeline must take into account the axis order of the source
1762 : * and target SRS. <li> COORDINATE_EPOCH: (GDAL >= 3.0) Coordinate epoch,
1763 : * expressed as a decimal year. Useful for time-dependant coordinate operations.
1764 : * </li>
1765 : * <li> SRC_COORDINATE_EPOCH: (GDAL >= 3.4) Coordinate epoch of source CRS,
1766 : * expressed as a decimal year. Useful for time-dependant coordinate operations.
1767 : * </li>
1768 : * <li> DST_COORDINATE_EPOCH: (GDAL >= 3.4) Coordinate epoch of target CRS,
1769 : * expressed as a decimal year. Useful for time-dependant coordinate operations.
1770 : * </li>
1771 : * <li> GCPS_OK: If false, GCPs will not be used, default is TRUE.
1772 : * </li>
1773 : * <li> REFINE_MINIMUM_GCPS: The minimum amount of GCPs that should be available
1774 : * after the refinement.
1775 : * </li>
1776 : * <li> REFINE_TOLERANCE: The tolerance that specifies when a GCP will be
1777 : * eliminated.
1778 : * </li>
1779 : * <li> MAX_GCP_ORDER: the maximum order to use for GCP derived polynomials if
1780 : * possible. The default is to autoselect based on the number of GCPs.
1781 : * A value of -1 triggers use of Thin Plate Spline instead of polynomials.
1782 : * </li>
1783 : * <li>GCP_ANTIMERIDIAN_UNWRAP=AUTO/YES/NO. (GDAL >= 3.8) Whether to
1784 : * "unwrap" longitudes of ground control points that span the antimeridian.
1785 : * For datasets with GCPs in longitude/latitude coordinate space spanning the
1786 : * antimeridian, longitudes will have a discontinuity on +/- 180 deg, and
1787 : * will result in a subset of the GCPs with longitude in the [-180,-170] range
1788 : * and another subset in [170, 180]. By default (AUTO), that situation will be
1789 : * detected and longitudes in [-180,-170] will be shifted to [180, 190] to get
1790 : * a continuous set. This option can be set to YES to force that behavior
1791 : * (useful if no SRS information is available), or to NO to disable it.
1792 : * </li>
1793 : * <li> SRC_METHOD: may have a value which is one of GEOTRANSFORM,
1794 : * GCP_POLYNOMIAL, GCP_TPS, GEOLOC_ARRAY, RPC to force only one geolocation
1795 : * method to be considered on the source dataset. Will be used for pixel/line
1796 : * to georef transformation on the source dataset. NO_GEOTRANSFORM can be
1797 : * used to specify the identity geotransform (ungeoreference image)
1798 : * </li>
1799 : * <li> DST_METHOD: may have a value which is one of GEOTRANSFORM,
1800 : * GCP_POLYNOMIAL, GCP_TPS, GEOLOC_ARRAY (added in 3.5), RPC to force only one
1801 : * geolocation method to be considered on the target dataset. Will be used for
1802 : * pixel/line to georef transformation on the destination dataset.
1803 : * NO_GEOTRANSFORM can be used to specify the identity geotransform
1804 : * (ungeoreference image)
1805 : * </li>
1806 : * <li> RPC_HEIGHT: A fixed height to be used with RPC
1807 : * calculations.
1808 : * </li>
1809 : * <li> RPC_DEM: The name of a DEM file to be used with RPC
1810 : * calculations. See GDALCreateRPCTransformerV2() for more details.
1811 : * </li>
1812 : * <li> Other RPC related options. See GDALCreateRPCTransformerV2()
1813 : * </li>
1814 : * <li>
1815 : * INSERT_CENTER_LONG: May be set to FALSE to disable setting up a CENTER_LONG
1816 : * value on the coordinate system to rewrap things around the center of the
1817 : * image.
1818 : * </li>
1819 : * <li> SRC_APPROX_ERROR_IN_SRS_UNIT=err_threshold_in_SRS_units. (GDAL
1820 : * >= 2.2) Use an approximate transformer for the source transformer. Must be
1821 : * defined together with SRC_APPROX_ERROR_IN_PIXEL to be taken into account.
1822 : * </li>
1823 : * <li> SRC_APPROX_ERROR_IN_PIXEL=err_threshold_in_pixel. (GDAL >= 2.2) Use
1824 : * an approximate transformer for the source transformer.. Must be defined
1825 : * together with SRC_APPROX_ERROR_IN_SRS_UNIT to be taken into account.
1826 : * </li>
1827 : * <li>
1828 : * DST_APPROX_ERROR_IN_SRS_UNIT=err_threshold_in_SRS_units. (GDAL >= 2.2) Use
1829 : * an approximate transformer for the destination transformer. Must be defined
1830 : * together with DST_APPROX_ERROR_IN_PIXEL to be taken into account.
1831 : * </li>
1832 : * <li>
1833 : * DST_APPROX_ERROR_IN_PIXEL=err_threshold_in_pixel. (GDAL >= 2.2) Use an
1834 : * approximate transformer for the destination transformer. Must be defined
1835 : * together with DST_APPROX_ERROR_IN_SRS_UNIT to be taken into account.
1836 : * </li>
1837 : * <li>
1838 : * REPROJECTION_APPROX_ERROR_IN_SRC_SRS_UNIT=err_threshold_in_src_SRS_units.
1839 : * (GDAL >= 2.2) Use an approximate transformer for the coordinate
1840 : * reprojection. Must be used together with
1841 : * REPROJECTION_APPROX_ERROR_IN_DST_SRS_UNIT to be taken into account.
1842 : * </li>
1843 : * <li>
1844 : * REPROJECTION_APPROX_ERROR_IN_DST_SRS_UNIT=err_threshold_in_dst_SRS_units.
1845 : * (GDAL >= 2.2) Use an approximate transformer for the coordinate
1846 : * reprojection. Must be used together with
1847 : * REPROJECTION_APPROX_ERROR_IN_SRC_SRS_UNIT to be taken into account.
1848 : * </li>
1849 : * <li>
1850 : * AREA_OF_INTEREST=west_lon_deg,south_lat_deg,east_lon_deg,north_lat_deg. (GDAL
1851 : * >= 3.0) Area of interest, used to compute the best coordinate operation
1852 : * between the source and target SRS. If not specified, the bounding box of the
1853 : * source raster will be used.
1854 : * </li>
1855 : * <li> GEOLOC_BACKMAP_OVERSAMPLE_FACTOR=[0.1,2]. (GDAL >= 3.5) Oversample
1856 : * factor used to derive the size of the "backmap" used for geolocation array
1857 : * transformers. Default value is 1.3.
1858 : * </li>
1859 : * <li> GEOLOC_USE_TEMP_DATASETS=YES/NO.
1860 : * (GDAL >= 3.5) Whether temporary GeoTIFF datasets should be used to store
1861 : * the backmap. The default is NO, that is to use in-memory arrays, unless the
1862 : * number of pixels of the geolocation array is greater than 16 megapixels.
1863 : * </li>
1864 : * <li>
1865 : * GEOLOC_ARRAY/SRC_GEOLOC_ARRAY=filename. (GDAL >= 3.5.2) Name of a GDAL
1866 : * dataset containing a geolocation array and associated metadata. This is an
1867 : * alternative to having geolocation information described in the GEOLOCATION
1868 : * metadata domain of the source dataset. The dataset specified may have a
1869 : * GEOLOCATION metadata domain containing appropriate metadata, however default
1870 : * values are assigned for all omitted items. X_BAND defaults to 1 and Y_BAND to
1871 : * 2, however the dataset must contain exactly 2 bands. PIXEL_OFFSET and
1872 : * LINE_OFFSET default to 0. PIXEL_STEP and LINE_STEP default to the ratio of
1873 : * the width/height of the source dataset divided by the with/height of the
1874 : * geolocation array. SRS defaults to the geolocation array dataset's spatial
1875 : * reference system if set, otherwise WGS84 is used.
1876 : * GEOREFERENCING_CONVENTION is selected from the main metadata domain if it
1877 : * is omitted from the GEOLOCATION domain, and if not available
1878 : * TOP_LEFT_CORNER is assigned as a default.
1879 : * If GEOLOC_ARRAY is set SRC_METHOD
1880 : * defaults to GEOLOC_ARRAY.
1881 : * </li>
1882 : * <li>DST_GEOLOC_ARRAY=filename. (GDAL >= 3.5.2) Name of a
1883 : * GDAL dataset that contains at least 2 bands with the X and Y geolocation
1884 : * bands. This is an alternative to having geolocation information described in
1885 : * the GEOLOCATION metadata domain of the destination dataset. See
1886 : * SRC_GEOLOC_ARRAY description for details, assumptions, and defaults. If this
1887 : * option is set, DST_METHOD=GEOLOC_ARRAY will be assumed if not set.
1888 : * </li>
1889 : * </ul>
1890 : *
1891 : * The use case for the *_APPROX_ERROR_* options is when defining an approximate
1892 : * transformer on top of the GenImgProjTransformer globally is not practical.
1893 : * Such a use case is when the source dataset has RPC with a RPC DEM. In such
1894 : * case we don't want to use the approximate transformer on the RPC
1895 : * transformation, as the RPC DEM generally involves non-linearities that the
1896 : * approximate transformer will not detect. In such case, we must a
1897 : * non-approximated GenImgProjTransformer, but it might be worthwhile to use
1898 : * approximate sub- transformers, for example on coordinate reprojection. For
1899 : * example if warping from a source dataset with RPC to a destination dataset
1900 : * with a UTM projection, since the inverse UTM transformation is rather costly.
1901 : * In which case, one can use the REPROJECTION_APPROX_ERROR_IN_SRC_SRS_UNIT and
1902 : * REPROJECTION_APPROX_ERROR_IN_DST_SRS_UNIT options.
1903 : *
1904 : * @param hSrcDS source dataset, or NULL.
1905 : * @param hDstDS destination dataset (or NULL).
1906 : * @param papszOptions NULL-terminated list of string options (or NULL).
1907 : *
1908 : * @return handle suitable for use GDALGenImgProjTransform(), and to be
1909 : * deallocated with GDALDestroyGenImgProjTransformer() or NULL on failure.
1910 : */
1911 : /* clang-format on */
1912 :
1913 1316 : void *GDALCreateGenImgProjTransformer2(GDALDatasetH hSrcDS, GDALDatasetH hDstDS,
1914 : char **papszOptions)
1915 :
1916 : {
1917 1316 : CSLConstList papszMD = nullptr;
1918 : GDALRPCInfoV2 sRPCInfo;
1919 1316 : const char *pszMethod = CSLFetchNameValue(papszOptions, "SRC_METHOD");
1920 1316 : if (pszMethod == nullptr)
1921 1305 : pszMethod = CSLFetchNameValue(papszOptions, "METHOD");
1922 1316 : const char *pszSrcSRS = CSLFetchNameValue(papszOptions, "SRC_SRS");
1923 1316 : const char *pszDstSRS = CSLFetchNameValue(papszOptions, "DST_SRS");
1924 :
1925 1316 : const char *pszValue = CSLFetchNameValue(papszOptions, "MAX_GCP_ORDER");
1926 1316 : const int nOrder = pszValue ? atoi(pszValue) : 0;
1927 :
1928 1316 : pszValue = CSLFetchNameValue(papszOptions, "GCPS_OK");
1929 1316 : const bool bGCPUseOK = pszValue ? CPLTestBool(pszValue) : true;
1930 :
1931 1316 : pszValue = CSLFetchNameValue(papszOptions, "REFINE_MINIMUM_GCPS");
1932 1316 : const int nMinimumGcps = pszValue ? atoi(pszValue) : -1;
1933 :
1934 1316 : pszValue = CSLFetchNameValue(papszOptions, "REFINE_TOLERANCE");
1935 1316 : const bool bRefine = pszValue != nullptr;
1936 1316 : const double dfTolerance = pszValue ? CPLAtof(pszValue) : 0.0;
1937 :
1938 1316 : double dfWestLongitudeDeg = 0.0;
1939 1316 : double dfSouthLatitudeDeg = 0.0;
1940 1316 : double dfEastLongitudeDeg = 0.0;
1941 1316 : double dfNorthLatitudeDeg = 0.0;
1942 1316 : bool bHasAreaOfInterest = false;
1943 1316 : pszValue = CSLFetchNameValue(papszOptions, "AREA_OF_INTEREST");
1944 1316 : if (pszValue)
1945 : {
1946 0 : char **papszTokens = CSLTokenizeString2(pszValue, ", ", 0);
1947 0 : if (CSLCount(papszTokens) == 4)
1948 : {
1949 0 : dfWestLongitudeDeg = CPLAtof(papszTokens[0]);
1950 0 : dfSouthLatitudeDeg = CPLAtof(papszTokens[1]);
1951 0 : dfEastLongitudeDeg = CPLAtof(papszTokens[2]);
1952 0 : dfNorthLatitudeDeg = CPLAtof(papszTokens[3]);
1953 0 : bHasAreaOfInterest = true;
1954 : }
1955 0 : CSLDestroy(papszTokens);
1956 : }
1957 :
1958 1316 : const char *pszCO = CSLFetchNameValue(papszOptions, "COORDINATE_OPERATION");
1959 :
1960 2632 : OGRSpatialReference oSrcSRS;
1961 1316 : if (pszSrcSRS)
1962 : {
1963 115 : oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1964 228 : if (pszSrcSRS[0] != '\0' &&
1965 113 : oSrcSRS.SetFromUserInput(pszSrcSRS) != OGRERR_NONE)
1966 : {
1967 0 : CPLError(CE_Failure, CPLE_AppDefined,
1968 : "Failed to import coordinate system `%s'.", pszSrcSRS);
1969 0 : return nullptr;
1970 : }
1971 : }
1972 :
1973 2632 : OGRSpatialReference oDstSRS;
1974 1316 : if (pszDstSRS)
1975 : {
1976 895 : oDstSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1977 1788 : if (pszDstSRS[0] != '\0' &&
1978 893 : oDstSRS.SetFromUserInput(pszDstSRS) != OGRERR_NONE)
1979 : {
1980 0 : CPLError(CE_Failure, CPLE_AppDefined,
1981 : "Failed to import coordinate system `%s'.", pszDstSRS);
1982 0 : return nullptr;
1983 : }
1984 : }
1985 :
1986 : const char *pszSrcGeolocArray =
1987 1316 : CSLFetchNameValueDef(papszOptions, "SRC_GEOLOC_ARRAY",
1988 : CSLFetchNameValue(papszOptions, "GEOLOC_ARRAY"));
1989 1316 : if (pszMethod == nullptr && pszSrcGeolocArray != nullptr)
1990 7 : pszMethod = "GEOLOC_ARRAY";
1991 :
1992 : /* -------------------------------------------------------------------- */
1993 : /* Initialize the transform info. */
1994 : /* -------------------------------------------------------------------- */
1995 : GDALGenImgProjTransformInfo *psInfo =
1996 1316 : GDALCreateGenImgProjTransformerInternal();
1997 :
1998 1316 : bool bCanUseSrcGeoTransform = false;
1999 :
2000 : /* -------------------------------------------------------------------- */
2001 : /* Get forward and inverse geotransform for the source image. */
2002 : /* -------------------------------------------------------------------- */
2003 1316 : if (hSrcDS == nullptr ||
2004 73 : (pszMethod != nullptr && EQUAL(pszMethod, "NO_GEOTRANSFORM")))
2005 : {
2006 86 : psInfo->adfSrcGeoTransform[0] = 0.0;
2007 86 : psInfo->adfSrcGeoTransform[1] = 1.0;
2008 86 : psInfo->adfSrcGeoTransform[2] = 0.0;
2009 86 : psInfo->adfSrcGeoTransform[3] = 0.0;
2010 86 : psInfo->adfSrcGeoTransform[4] = 0.0;
2011 86 : psInfo->adfSrcGeoTransform[5] = 1.0;
2012 86 : memcpy(psInfo->adfSrcInvGeoTransform, psInfo->adfSrcGeoTransform,
2013 : sizeof(double) * 6);
2014 : }
2015 2398 : else if ((pszMethod == nullptr || EQUAL(pszMethod, "GEOTRANSFORM")) &&
2016 1168 : GDALGetGeoTransform(hSrcDS, psInfo->adfSrcGeoTransform) == CE_None)
2017 : {
2018 1098 : if (!GDALInvGeoTransform(psInfo->adfSrcGeoTransform,
2019 1098 : psInfo->adfSrcInvGeoTransform))
2020 : {
2021 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
2022 0 : GDALDestroyGenImgProjTransformer(psInfo);
2023 0 : return nullptr;
2024 : }
2025 1098 : if (pszSrcSRS == nullptr)
2026 : {
2027 1028 : auto hSRS = GDALGetSpatialRef(hSrcDS);
2028 1028 : if (hSRS)
2029 870 : oSrcSRS = *(OGRSpatialReference::FromHandle(hSRS));
2030 : }
2031 1098 : if (!bHasAreaOfInterest && pszCO == nullptr && !oSrcSRS.IsEmpty())
2032 : {
2033 935 : GDALComputeAreaOfInterest(&oSrcSRS, psInfo->adfSrcGeoTransform,
2034 : GDALGetRasterXSize(hSrcDS),
2035 : GDALGetRasterYSize(hSrcDS),
2036 : dfWestLongitudeDeg, dfSouthLatitudeDeg,
2037 : dfEastLongitudeDeg, dfNorthLatitudeDeg);
2038 : }
2039 1098 : bCanUseSrcGeoTransform = true;
2040 : }
2041 132 : else if (bGCPUseOK &&
2042 62 : (pszMethod == nullptr || EQUAL(pszMethod, "GCP_POLYNOMIAL")) &&
2043 264 : GDALGetGCPCount(hSrcDS) > 0 && nOrder >= 0)
2044 : {
2045 29 : if (pszSrcSRS == nullptr)
2046 : {
2047 28 : auto hSRS = GDALGetGCPSpatialRef(hSrcDS);
2048 28 : if (hSRS)
2049 28 : oSrcSRS = *(OGRSpatialReference::FromHandle(hSRS));
2050 : }
2051 :
2052 29 : const auto nGCPCount = GDALGetGCPCount(hSrcDS);
2053 29 : auto pasGCPList = GDALDuplicateGCPs(nGCPCount, GDALGetGCPs(hSrcDS));
2054 29 : GDALGCPAntimeridianUnwrap(nGCPCount, pasGCPList, oSrcSRS, papszOptions);
2055 :
2056 29 : if (bRefine)
2057 : {
2058 0 : psInfo->pSrcTransformArg = GDALCreateGCPRefineTransformer(
2059 : nGCPCount, pasGCPList, nOrder, FALSE, dfTolerance,
2060 : nMinimumGcps);
2061 : }
2062 : else
2063 : {
2064 29 : psInfo->pSrcTransformArg =
2065 29 : GDALCreateGCPTransformer(nGCPCount, pasGCPList, nOrder, FALSE);
2066 : }
2067 :
2068 29 : GDALDeinitGCPs(nGCPCount, pasGCPList);
2069 29 : CPLFree(pasGCPList);
2070 :
2071 29 : if (psInfo->pSrcTransformArg == nullptr)
2072 : {
2073 0 : GDALDestroyGenImgProjTransformer(psInfo);
2074 0 : return nullptr;
2075 : }
2076 29 : psInfo->pSrcTransformer = GDALGCPTransform;
2077 : }
2078 :
2079 114 : else if (bGCPUseOK && GDALGetGCPCount(hSrcDS) > 0 && nOrder <= 0 &&
2080 11 : (pszMethod == nullptr || EQUAL(pszMethod, "GCP_TPS")))
2081 : {
2082 11 : if (pszSrcSRS == nullptr)
2083 : {
2084 11 : auto hSRS = GDALGetGCPSpatialRef(hSrcDS);
2085 11 : if (hSRS)
2086 10 : oSrcSRS = *(OGRSpatialReference::FromHandle(hSRS));
2087 : }
2088 :
2089 11 : const auto nGCPCount = GDALGetGCPCount(hSrcDS);
2090 11 : auto pasGCPList = GDALDuplicateGCPs(nGCPCount, GDALGetGCPs(hSrcDS));
2091 11 : GDALGCPAntimeridianUnwrap(nGCPCount, pasGCPList, oSrcSRS, papszOptions);
2092 :
2093 11 : psInfo->pSrcTransformArg = GDALCreateTPSTransformerInt(
2094 : nGCPCount, pasGCPList, FALSE, papszOptions);
2095 :
2096 11 : GDALDeinitGCPs(nGCPCount, pasGCPList);
2097 11 : CPLFree(pasGCPList);
2098 :
2099 11 : if (psInfo->pSrcTransformArg == nullptr)
2100 : {
2101 2 : GDALDestroyGenImgProjTransformer(psInfo);
2102 2 : return nullptr;
2103 : }
2104 9 : psInfo->pSrcTransformer = GDALTPSTransform;
2105 : }
2106 :
2107 48 : else if ((pszMethod == nullptr || EQUAL(pszMethod, "RPC")) &&
2108 183 : (papszMD = GDALGetMetadata(hSrcDS, "RPC")) != nullptr &&
2109 43 : GDALExtractRPCInfoV2(papszMD, &sRPCInfo))
2110 : {
2111 43 : psInfo->pSrcTransformArg =
2112 43 : GDALCreateRPCTransformerV2(&sRPCInfo, FALSE, 0, papszOptions);
2113 43 : if (psInfo->pSrcTransformArg == nullptr)
2114 : {
2115 1 : GDALDestroyGenImgProjTransformer(psInfo);
2116 1 : return nullptr;
2117 : }
2118 42 : psInfo->pSrcTransformer = GDALRPCTransform;
2119 42 : if (pszSrcSRS == nullptr)
2120 : {
2121 42 : oSrcSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
2122 42 : oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2123 : }
2124 : }
2125 :
2126 98 : else if ((pszMethod == nullptr || EQUAL(pszMethod, "GEOLOC_ARRAY")) &&
2127 49 : ((papszMD = GDALGetMetadata(hSrcDS, "GEOLOCATION")) != nullptr ||
2128 : pszSrcGeolocArray != nullptr))
2129 : {
2130 44 : CPLStringList aosGeolocMD; // keep in this scope
2131 44 : if (pszSrcGeolocArray != nullptr)
2132 : {
2133 7 : if (papszMD != nullptr)
2134 : {
2135 0 : CPLError(
2136 : CE_Warning, CPLE_AppDefined,
2137 : "Both GEOLOCATION metadata domain on the source dataset "
2138 : "and [SRC_]GEOLOC_ARRAY transformer option are set. "
2139 : "Only using the later.");
2140 : }
2141 : aosGeolocMD =
2142 14 : GDALCreateGeolocationMetadata(hSrcDS, pszSrcGeolocArray,
2143 7 : /* bIsSource= */ true);
2144 7 : if (aosGeolocMD.empty())
2145 : {
2146 2 : GDALDestroyGenImgProjTransformer(psInfo);
2147 2 : return nullptr;
2148 : }
2149 5 : papszMD = aosGeolocMD.List();
2150 : }
2151 :
2152 42 : psInfo->pSrcTransformArg = GDALCreateGeoLocTransformerEx(
2153 : hSrcDS, papszMD, FALSE, nullptr, papszOptions);
2154 42 : if (psInfo->pSrcTransformArg == nullptr)
2155 : {
2156 1 : GDALDestroyGenImgProjTransformer(psInfo);
2157 1 : return nullptr;
2158 : }
2159 41 : psInfo->pSrcTransformer = GDALGeoLocTransform;
2160 41 : if (pszSrcSRS == nullptr)
2161 : {
2162 41 : pszSrcSRS = CSLFetchNameValue(papszMD, "SRS");
2163 41 : if (pszSrcSRS)
2164 : {
2165 39 : oSrcSRS.SetFromUserInput(pszSrcSRS);
2166 39 : oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2167 : }
2168 : }
2169 : }
2170 :
2171 5 : else if (pszMethod != nullptr)
2172 : {
2173 0 : CPLError(CE_Failure, CPLE_AppDefined,
2174 : "Unable to compute a %s based transformation between "
2175 : "pixel/line and georeferenced coordinates for %s.",
2176 : pszMethod, GDALGetDescription(hSrcDS));
2177 :
2178 0 : GDALDestroyGenImgProjTransformer(psInfo);
2179 0 : return nullptr;
2180 : }
2181 :
2182 : else
2183 : {
2184 5 : CPLError(CE_Failure, CPLE_AppDefined,
2185 : "Unable to compute a transformation between pixel/line "
2186 : "and georeferenced coordinates for %s. "
2187 : "There is no affine transformation and no GCPs. "
2188 : "Specify transformation option SRC_METHOD=NO_GEOTRANSFORM "
2189 : "to bypass this check.",
2190 : GDALGetDescription(hSrcDS));
2191 :
2192 5 : GDALDestroyGenImgProjTransformer(psInfo);
2193 5 : return nullptr;
2194 : }
2195 :
2196 : /* -------------------------------------------------------------------- */
2197 : /* Handle optional source approximation transformer. */
2198 : /* -------------------------------------------------------------------- */
2199 1305 : if (psInfo->pSrcTransformer)
2200 : {
2201 : const char *pszSrcApproxErrorFwd =
2202 121 : CSLFetchNameValue(papszOptions, "SRC_APPROX_ERROR_IN_SRS_UNIT");
2203 : const char *pszSrcApproxErrorReverse =
2204 121 : CSLFetchNameValue(papszOptions, "SRC_APPROX_ERROR_IN_PIXEL");
2205 121 : if (pszSrcApproxErrorFwd && pszSrcApproxErrorReverse)
2206 : {
2207 1 : void *pArg = GDALCreateApproxTransformer2(
2208 : psInfo->pSrcTransformer, psInfo->pSrcTransformArg,
2209 : CPLAtof(pszSrcApproxErrorFwd),
2210 : CPLAtof(pszSrcApproxErrorReverse));
2211 1 : if (pArg == nullptr)
2212 : {
2213 0 : GDALDestroyGenImgProjTransformer(psInfo);
2214 0 : return nullptr;
2215 : }
2216 1 : psInfo->pSrcTransformArg = pArg;
2217 1 : psInfo->pSrcTransformer = GDALApproxTransform;
2218 1 : GDALApproxTransformerOwnsSubtransformer(psInfo->pSrcTransformArg,
2219 : TRUE);
2220 : }
2221 : }
2222 :
2223 : /* -------------------------------------------------------------------- */
2224 : /* Get forward and inverse geotransform for destination image. */
2225 : /* If we have no destination use a unit transform. */
2226 : /* -------------------------------------------------------------------- */
2227 1305 : const char *pszDstMethod = CSLFetchNameValue(papszOptions, "DST_METHOD");
2228 : const char *pszDstGeolocArray =
2229 1305 : CSLFetchNameValue(papszOptions, "DST_GEOLOC_ARRAY");
2230 1305 : if (pszDstMethod == nullptr && pszDstGeolocArray != nullptr)
2231 2 : pszDstMethod = "GEOLOC_ARRAY";
2232 :
2233 1305 : if (!hDstDS ||
2234 4 : (pszDstMethod != nullptr && EQUAL(pszDstMethod, "NO_GEOTRANSFORM")))
2235 : {
2236 968 : psInfo->adfDstGeoTransform[0] = 0.0;
2237 968 : psInfo->adfDstGeoTransform[1] = 1.0;
2238 968 : psInfo->adfDstGeoTransform[2] = 0.0;
2239 968 : psInfo->adfDstGeoTransform[3] = 0.0;
2240 968 : psInfo->adfDstGeoTransform[4] = 0.0;
2241 968 : psInfo->adfDstGeoTransform[5] = 1.0;
2242 968 : memcpy(psInfo->adfDstInvGeoTransform, psInfo->adfDstGeoTransform,
2243 : sizeof(double) * 6);
2244 : }
2245 672 : else if ((pszDstMethod == nullptr || EQUAL(pszDstMethod, "GEOTRANSFORM")) &&
2246 335 : GDALGetGeoTransform(hDstDS, psInfo->adfDstGeoTransform) == CE_None)
2247 : {
2248 328 : if (pszDstSRS == nullptr)
2249 : {
2250 246 : auto hSRS = GDALGetSpatialRef(hDstDS);
2251 246 : if (hSRS)
2252 166 : oDstSRS = *(OGRSpatialReference::FromHandle(hSRS));
2253 : }
2254 328 : if (!GDALInvGeoTransform(psInfo->adfDstGeoTransform,
2255 328 : psInfo->adfDstInvGeoTransform))
2256 : {
2257 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
2258 0 : GDALDestroyGenImgProjTransformer(psInfo);
2259 0 : return nullptr;
2260 : }
2261 : }
2262 9 : else if (bGCPUseOK &&
2263 2 : (pszDstMethod == nullptr ||
2264 2 : EQUAL(pszDstMethod, "GCP_POLYNOMIAL")) &&
2265 18 : GDALGetGCPCount(hDstDS) > 0 && nOrder >= 0)
2266 : {
2267 0 : if (pszDstSRS == nullptr)
2268 : {
2269 0 : auto hSRS = GDALGetGCPSpatialRef(hDstDS);
2270 0 : if (hSRS)
2271 0 : oDstSRS = *(OGRSpatialReference::FromHandle(hSRS));
2272 : }
2273 :
2274 0 : const auto nGCPCount = GDALGetGCPCount(hDstDS);
2275 0 : auto pasGCPList = GDALDuplicateGCPs(nGCPCount, GDALGetGCPs(hDstDS));
2276 0 : GDALGCPAntimeridianUnwrap(nGCPCount, pasGCPList, oDstSRS, papszOptions);
2277 :
2278 0 : if (bRefine)
2279 : {
2280 0 : psInfo->pDstTransformArg = GDALCreateGCPRefineTransformer(
2281 : nGCPCount, pasGCPList, nOrder, FALSE, dfTolerance,
2282 : nMinimumGcps);
2283 : }
2284 : else
2285 : {
2286 0 : psInfo->pDstTransformArg =
2287 0 : GDALCreateGCPTransformer(nGCPCount, pasGCPList, nOrder, FALSE);
2288 : }
2289 :
2290 0 : GDALDeinitGCPs(nGCPCount, pasGCPList);
2291 0 : CPLFree(pasGCPList);
2292 :
2293 0 : if (psInfo->pDstTransformArg == nullptr)
2294 : {
2295 0 : GDALDestroyGenImgProjTransformer(psInfo);
2296 0 : return nullptr;
2297 : }
2298 0 : psInfo->pDstTransformer = GDALGCPTransform;
2299 : }
2300 9 : else if (bGCPUseOK && GDALGetGCPCount(hDstDS) > 0 && nOrder <= 0 &&
2301 0 : (pszDstMethod == nullptr || EQUAL(pszDstMethod, "GCP_TPS")))
2302 : {
2303 0 : if (pszDstSRS == nullptr)
2304 : {
2305 0 : auto hSRS = GDALGetGCPSpatialRef(hDstDS);
2306 0 : if (hSRS)
2307 0 : oDstSRS = *(OGRSpatialReference::FromHandle(hSRS));
2308 : }
2309 :
2310 0 : const auto nGCPCount = GDALGetGCPCount(hDstDS);
2311 0 : auto pasGCPList = GDALDuplicateGCPs(nGCPCount, GDALGetGCPs(hDstDS));
2312 0 : GDALGCPAntimeridianUnwrap(nGCPCount, pasGCPList, oDstSRS, papszOptions);
2313 :
2314 0 : psInfo->pDstTransformArg = GDALCreateTPSTransformerInt(
2315 : nGCPCount, pasGCPList, FALSE, papszOptions);
2316 :
2317 0 : GDALDeinitGCPs(nGCPCount, pasGCPList);
2318 0 : CPLFree(pasGCPList);
2319 :
2320 0 : if (psInfo->pDstTransformArg == nullptr)
2321 : {
2322 0 : GDALDestroyGenImgProjTransformer(psInfo);
2323 0 : return nullptr;
2324 : }
2325 0 : psInfo->pDstTransformer = GDALTPSTransform;
2326 : }
2327 2 : else if ((pszDstMethod == nullptr || EQUAL(pszDstMethod, "RPC")) &&
2328 13 : (papszMD = GDALGetMetadata(hDstDS, "RPC")) != nullptr &&
2329 2 : GDALExtractRPCInfoV2(papszMD, &sRPCInfo))
2330 : {
2331 2 : psInfo->pDstTransformArg =
2332 2 : GDALCreateRPCTransformerV2(&sRPCInfo, FALSE, 0, papszOptions);
2333 2 : if (psInfo->pDstTransformArg == nullptr)
2334 : {
2335 0 : GDALDestroyGenImgProjTransformer(psInfo);
2336 0 : return nullptr;
2337 : }
2338 2 : psInfo->pDstTransformer = GDALRPCTransform;
2339 2 : if (pszDstSRS == nullptr)
2340 : {
2341 2 : oDstSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
2342 2 : oDstSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2343 : }
2344 : }
2345 14 : else if ((pszDstMethod == nullptr || EQUAL(pszDstMethod, "GEOLOC_ARRAY")) &&
2346 7 : ((papszMD = GDALGetMetadata(hDstDS, "GEOLOCATION")) != nullptr ||
2347 : pszDstGeolocArray != nullptr))
2348 : {
2349 7 : CPLStringList aosGeolocMD; // keep in this scope
2350 7 : if (pszDstGeolocArray != nullptr)
2351 : {
2352 2 : if (papszMD != nullptr)
2353 : {
2354 0 : CPLError(
2355 : CE_Warning, CPLE_AppDefined,
2356 : "Both GEOLOCATION metadata domain on the target dataset "
2357 : "and DST_GEOLOC_ARRAY transformer option are set. "
2358 : "Only using the later.");
2359 : }
2360 : aosGeolocMD =
2361 4 : GDALCreateGeolocationMetadata(hDstDS, pszDstGeolocArray,
2362 2 : /* bIsSource= */ false);
2363 2 : if (aosGeolocMD.empty())
2364 : {
2365 1 : GDALDestroyGenImgProjTransformer(psInfo);
2366 1 : return nullptr;
2367 : }
2368 1 : papszMD = aosGeolocMD.List();
2369 : }
2370 :
2371 6 : psInfo->pDstTransformArg = GDALCreateGeoLocTransformerEx(
2372 : hDstDS, papszMD, FALSE, nullptr, papszOptions);
2373 6 : if (psInfo->pDstTransformArg == nullptr)
2374 : {
2375 1 : GDALDestroyGenImgProjTransformer(psInfo);
2376 1 : return nullptr;
2377 : }
2378 5 : psInfo->pDstTransformer = GDALGeoLocTransform;
2379 5 : if (pszDstSRS == nullptr)
2380 : {
2381 5 : pszDstSRS = CSLFetchNameValue(papszMD, "SRS");
2382 5 : if (pszDstSRS)
2383 : {
2384 5 : oDstSRS.SetFromUserInput(pszDstSRS);
2385 5 : oDstSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2386 : }
2387 : }
2388 : }
2389 : else
2390 : {
2391 0 : CPLError(CE_Failure, CPLE_AppDefined,
2392 : "Unable to compute a transformation between pixel/line "
2393 : "and georeferenced coordinates for %s. "
2394 : "There is no affine transformation and no GCPs. "
2395 : "Specify transformation option DST_METHOD=NO_GEOTRANSFORM "
2396 : "to bypass this check.",
2397 : GDALGetDescription(hDstDS));
2398 :
2399 0 : GDALDestroyGenImgProjTransformer(psInfo);
2400 0 : return nullptr;
2401 : }
2402 :
2403 : /* -------------------------------------------------------------------- */
2404 : /* Handle optional destination approximation transformer. */
2405 : /* -------------------------------------------------------------------- */
2406 1303 : if (psInfo->pDstTransformer)
2407 : {
2408 : const char *pszDstApproxErrorFwd =
2409 7 : CSLFetchNameValue(papszOptions, "DST_APPROX_ERROR_IN_PIXEL");
2410 : const char *pszDstApproxErrorReverse =
2411 7 : CSLFetchNameValue(papszOptions, "DST_APPROX_ERROR_IN_SRS_UNIT");
2412 7 : if (pszDstApproxErrorFwd && pszDstApproxErrorReverse)
2413 : {
2414 0 : void *pArg = GDALCreateApproxTransformer2(
2415 : psInfo->pDstTransformer, psInfo->pDstTransformArg,
2416 : CPLAtof(pszDstApproxErrorFwd),
2417 : CPLAtof(pszDstApproxErrorReverse));
2418 0 : if (pArg == nullptr)
2419 : {
2420 0 : GDALDestroyGenImgProjTransformer(psInfo);
2421 0 : return nullptr;
2422 : }
2423 0 : psInfo->pDstTransformArg = pArg;
2424 0 : psInfo->pDstTransformer = GDALApproxTransform;
2425 0 : GDALApproxTransformerOwnsSubtransformer(psInfo->pDstTransformArg,
2426 : TRUE);
2427 : }
2428 : }
2429 :
2430 : /* -------------------------------------------------------------------- */
2431 : /* Setup reprojection. */
2432 : /* -------------------------------------------------------------------- */
2433 :
2434 1303 : if (CPLFetchBool(papszOptions, "STRIP_VERT_CS", false))
2435 : {
2436 0 : if (oSrcSRS.IsCompound())
2437 : {
2438 0 : oSrcSRS.StripVertical();
2439 : }
2440 0 : if (oDstSRS.IsCompound())
2441 : {
2442 0 : oDstSRS.StripVertical();
2443 : }
2444 : }
2445 :
2446 : const bool bMayInsertCenterLong =
2447 2240 : (bCanUseSrcGeoTransform && !oSrcSRS.IsEmpty() && hSrcDS &&
2448 937 : CPLFetchBool(papszOptions, "INSERT_CENTER_LONG", true));
2449 : const char *pszSrcCoordEpoch =
2450 1303 : CSLFetchNameValue(papszOptions, "SRC_COORDINATE_EPOCH");
2451 : const char *pszDstCoordEpoch =
2452 1303 : CSLFetchNameValue(papszOptions, "DST_COORDINATE_EPOCH");
2453 2402 : if ((!oSrcSRS.IsEmpty() && !oDstSRS.IsEmpty() &&
2454 1003 : (pszSrcCoordEpoch || pszDstCoordEpoch || !oSrcSRS.IsSame(&oDstSRS) ||
2455 2402 : (oSrcSRS.IsGeographic() && bMayInsertCenterLong))) ||
2456 : pszCO)
2457 : {
2458 704 : CPLStringList aosOptions;
2459 :
2460 704 : if (bMayInsertCenterLong)
2461 : {
2462 687 : InsertCenterLong(hSrcDS, &oSrcSRS, aosOptions);
2463 : }
2464 :
2465 704 : if (CPLFetchBool(papszOptions, "PROMOTE_TO_3D", false))
2466 : {
2467 19 : oSrcSRS.PromoteTo3D(nullptr);
2468 19 : oDstSRS.PromoteTo3D(nullptr);
2469 : }
2470 :
2471 704 : if (!(dfWestLongitudeDeg == 0.0 && dfSouthLatitudeDeg == 0.0 &&
2472 28 : dfEastLongitudeDeg == 0.0 && dfNorthLatitudeDeg == 0.0))
2473 : {
2474 : aosOptions.SetNameValue(
2475 : "AREA_OF_INTEREST",
2476 : CPLSPrintf("%.16g,%.16g,%.16g,%.16g", dfWestLongitudeDeg,
2477 : dfSouthLatitudeDeg, dfEastLongitudeDeg,
2478 676 : dfNorthLatitudeDeg));
2479 : }
2480 704 : if (pszCO)
2481 : {
2482 7 : aosOptions.SetNameValue("COORDINATE_OPERATION", pszCO);
2483 : }
2484 :
2485 : const char *pszCoordEpoch =
2486 704 : CSLFetchNameValue(papszOptions, "COORDINATE_EPOCH");
2487 704 : if (pszCoordEpoch)
2488 : {
2489 1 : aosOptions.SetNameValue("COORDINATE_EPOCH", pszCoordEpoch);
2490 : }
2491 :
2492 704 : if (pszSrcCoordEpoch)
2493 : {
2494 0 : aosOptions.SetNameValue("SRC_COORDINATE_EPOCH", pszSrcCoordEpoch);
2495 0 : oSrcSRS.SetCoordinateEpoch(CPLAtof(pszSrcCoordEpoch));
2496 : }
2497 :
2498 704 : if (pszDstCoordEpoch)
2499 : {
2500 0 : aosOptions.SetNameValue("DST_COORDINATE_EPOCH", pszDstCoordEpoch);
2501 0 : oDstSRS.SetCoordinateEpoch(CPLAtof(pszDstCoordEpoch));
2502 : }
2503 :
2504 708 : psInfo->pReprojectArg = GDALCreateReprojectionTransformerEx(
2505 704 : !oSrcSRS.IsEmpty() ? OGRSpatialReference::ToHandle(&oSrcSRS)
2506 : : nullptr,
2507 704 : !oDstSRS.IsEmpty() ? OGRSpatialReference::ToHandle(&oDstSRS)
2508 : : nullptr,
2509 704 : aosOptions.List());
2510 :
2511 704 : if (pszCO)
2512 : {
2513 7 : psInfo->bHasCustomTransformationPipeline = true;
2514 : }
2515 :
2516 704 : if (psInfo->pReprojectArg == nullptr)
2517 : {
2518 1 : GDALDestroyGenImgProjTransformer(psInfo);
2519 1 : return nullptr;
2520 : }
2521 703 : psInfo->pReproject = GDALReprojectionTransform;
2522 :
2523 : /* --------------------------------------------------------------------
2524 : */
2525 : /* Handle optional reprojection approximation transformer. */
2526 : /* --------------------------------------------------------------------
2527 : */
2528 703 : const char *psApproxErrorFwd = CSLFetchNameValue(
2529 : papszOptions, "REPROJECTION_APPROX_ERROR_IN_DST_SRS_UNIT");
2530 703 : const char *psApproxErrorReverse = CSLFetchNameValue(
2531 : papszOptions, "REPROJECTION_APPROX_ERROR_IN_SRC_SRS_UNIT");
2532 703 : if (psApproxErrorFwd && psApproxErrorReverse)
2533 : {
2534 1 : void *pArg = GDALCreateApproxTransformer2(
2535 : psInfo->pReproject, psInfo->pReprojectArg,
2536 : CPLAtof(psApproxErrorFwd), CPLAtof(psApproxErrorReverse));
2537 1 : if (pArg == nullptr)
2538 : {
2539 0 : GDALDestroyGenImgProjTransformer(psInfo);
2540 0 : return nullptr;
2541 : }
2542 1 : psInfo->pReprojectArg = pArg;
2543 1 : psInfo->pReproject = GDALApproxTransform;
2544 1 : GDALApproxTransformerOwnsSubtransformer(psInfo->pReprojectArg,
2545 : TRUE);
2546 : }
2547 : }
2548 :
2549 1302 : return psInfo;
2550 : }
2551 :
2552 : /************************************************************************/
2553 : /* GDALRefreshGenImgProjTransformer() */
2554 : /************************************************************************/
2555 :
2556 906 : void GDALRefreshGenImgProjTransformer(void *hTransformArg)
2557 : {
2558 906 : GDALGenImgProjTransformInfo *psInfo =
2559 : static_cast<GDALGenImgProjTransformInfo *>(hTransformArg);
2560 :
2561 1530 : if (psInfo->pReprojectArg &&
2562 624 : psInfo->bCheckWithInvertPROJ != GetCurrentCheckWithInvertPROJ())
2563 : {
2564 71 : psInfo->bCheckWithInvertPROJ = !psInfo->bCheckWithInvertPROJ;
2565 :
2566 : CPLXMLNode *psXML =
2567 71 : GDALSerializeTransformer(psInfo->pReproject, psInfo->pReprojectArg);
2568 71 : GDALDestroyTransformer(psInfo->pReprojectArg);
2569 71 : GDALDeserializeTransformer(psXML, &psInfo->pReproject,
2570 : &psInfo->pReprojectArg);
2571 71 : CPLDestroyXMLNode(psXML);
2572 : }
2573 906 : }
2574 :
2575 : /************************************************************************/
2576 : /* GDALCreateGenImgProjTransformer3() */
2577 : /************************************************************************/
2578 :
2579 : /**
2580 : * Create image to image transformer.
2581 : *
2582 : * This function creates a transformation object that maps from pixel/line
2583 : * coordinates on one image to pixel/line coordinates on another image. The
2584 : * images may potentially be georeferenced in different coordinate systems,
2585 : * and may used GCPs to map between their pixel/line coordinates and
2586 : * georeferenced coordinates (as opposed to the default assumption that their
2587 : * geotransform should be used).
2588 : *
2589 : * This transformer potentially performs three concatenated transformations.
2590 : *
2591 : * The first stage is from source image pixel/line coordinates to source
2592 : * image georeferenced coordinates, and may be done using the geotransform,
2593 : * or if not defined using a polynomial model derived from GCPs. If GCPs
2594 : * are used this stage is accomplished using GDALGCPTransform().
2595 : *
2596 : * The second stage is to change projections from the source coordinate system
2597 : * to the destination coordinate system, assuming they differ. This is
2598 : * accomplished internally using GDALReprojectionTransform().
2599 : *
2600 : * The third stage is converting from destination image georeferenced
2601 : * coordinates to destination image coordinates. This is done using the
2602 : * destination image geotransform, or if not available, using a polynomial
2603 : * model derived from GCPs. If GCPs are used this stage is accomplished using
2604 : * GDALGCPTransform(). This stage is skipped if hDstDS is NULL when the
2605 : * transformation is created.
2606 : *
2607 : * @param pszSrcWKT source WKT (or NULL).
2608 : * @param padfSrcGeoTransform source geotransform (or NULL).
2609 : * @param pszDstWKT destination WKT (or NULL).
2610 : * @param padfDstGeoTransform destination geotransform (or NULL).
2611 : *
2612 : * @return handle suitable for use GDALGenImgProjTransform(), and to be
2613 : * deallocated with GDALDestroyGenImgProjTransformer() or NULL on failure.
2614 : */
2615 :
2616 0 : void *GDALCreateGenImgProjTransformer3(const char *pszSrcWKT,
2617 : const double *padfSrcGeoTransform,
2618 : const char *pszDstWKT,
2619 : const double *padfDstGeoTransform)
2620 :
2621 : {
2622 0 : OGRSpatialReference oSrcSRS;
2623 0 : if (pszSrcWKT)
2624 : {
2625 0 : oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2626 0 : if (pszSrcWKT[0] != '\0' &&
2627 0 : oSrcSRS.importFromWkt(pszSrcWKT) != OGRERR_NONE)
2628 : {
2629 0 : CPLError(CE_Failure, CPLE_AppDefined,
2630 : "Failed to import coordinate system `%s'.", pszSrcWKT);
2631 0 : return nullptr;
2632 : }
2633 : }
2634 :
2635 0 : OGRSpatialReference oDstSRS;
2636 0 : if (pszDstWKT)
2637 : {
2638 0 : oDstSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2639 0 : if (pszDstWKT[0] != '\0' &&
2640 0 : oDstSRS.importFromWkt(pszDstWKT) != OGRERR_NONE)
2641 : {
2642 0 : CPLError(CE_Failure, CPLE_AppDefined,
2643 : "Failed to import coordinate system `%s'.", pszDstWKT);
2644 0 : return nullptr;
2645 : }
2646 : }
2647 0 : return GDALCreateGenImgProjTransformer4(
2648 : OGRSpatialReference::ToHandle(&oSrcSRS), padfSrcGeoTransform,
2649 0 : OGRSpatialReference::ToHandle(&oDstSRS), padfDstGeoTransform, nullptr);
2650 : }
2651 :
2652 : /************************************************************************/
2653 : /* GDALCreateGenImgProjTransformer4() */
2654 : /************************************************************************/
2655 :
2656 : /**
2657 : * Create image to image transformer.
2658 : *
2659 : * Similar to GDALCreateGenImgProjTransformer3(), except that it takes
2660 : * OGRSpatialReferenceH objects and options.
2661 : * The options are the ones supported by GDALCreateReprojectionTransformerEx()
2662 : *
2663 : * @since GDAL 3.0
2664 : */
2665 16 : void *GDALCreateGenImgProjTransformer4(OGRSpatialReferenceH hSrcSRS,
2666 : const double *padfSrcGeoTransform,
2667 : OGRSpatialReferenceH hDstSRS,
2668 : const double *padfDstGeoTransform,
2669 : const char *const *papszOptions)
2670 : {
2671 : /* -------------------------------------------------------------------- */
2672 : /* Initialize the transform info. */
2673 : /* -------------------------------------------------------------------- */
2674 : GDALGenImgProjTransformInfo *psInfo =
2675 16 : GDALCreateGenImgProjTransformerInternal();
2676 :
2677 : /* -------------------------------------------------------------------- */
2678 : /* Get forward and inverse geotransform for the source image. */
2679 : /* -------------------------------------------------------------------- */
2680 16 : if (padfSrcGeoTransform)
2681 : {
2682 16 : memcpy(psInfo->adfSrcGeoTransform, padfSrcGeoTransform,
2683 : sizeof(psInfo->adfSrcGeoTransform));
2684 16 : if (!GDALInvGeoTransform(psInfo->adfSrcGeoTransform,
2685 16 : psInfo->adfSrcInvGeoTransform))
2686 : {
2687 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
2688 1 : GDALDestroyGenImgProjTransformer(psInfo);
2689 1 : return nullptr;
2690 : }
2691 : }
2692 : else
2693 : {
2694 0 : psInfo->adfSrcGeoTransform[0] = 0.0;
2695 0 : psInfo->adfSrcGeoTransform[1] = 1.0;
2696 0 : psInfo->adfSrcGeoTransform[2] = 0.0;
2697 0 : psInfo->adfSrcGeoTransform[3] = 0.0;
2698 0 : psInfo->adfSrcGeoTransform[4] = 0.0;
2699 0 : psInfo->adfSrcGeoTransform[5] = 1.0;
2700 0 : memcpy(psInfo->adfSrcInvGeoTransform, psInfo->adfSrcGeoTransform,
2701 : sizeof(double) * 6);
2702 : }
2703 :
2704 : /* -------------------------------------------------------------------- */
2705 : /* Setup reprojection. */
2706 : /* -------------------------------------------------------------------- */
2707 15 : OGRSpatialReference *poSrcSRS = OGRSpatialReference::FromHandle(hSrcSRS);
2708 15 : OGRSpatialReference *poDstSRS = OGRSpatialReference::FromHandle(hDstSRS);
2709 30 : if (!poSrcSRS->IsEmpty() && !poDstSRS->IsEmpty() &&
2710 15 : !poSrcSRS->IsSame(poDstSRS))
2711 : {
2712 4 : psInfo->pReprojectArg =
2713 4 : GDALCreateReprojectionTransformerEx(hSrcSRS, hDstSRS, papszOptions);
2714 4 : if (psInfo->pReprojectArg == nullptr)
2715 : {
2716 1 : GDALDestroyGenImgProjTransformer(psInfo);
2717 1 : return nullptr;
2718 : }
2719 3 : psInfo->pReproject = GDALReprojectionTransform;
2720 : }
2721 :
2722 : /* -------------------------------------------------------------------- */
2723 : /* Get forward and inverse geotransform for destination image. */
2724 : /* If we have no destination matrix use a unit transform. */
2725 : /* -------------------------------------------------------------------- */
2726 14 : if (padfDstGeoTransform)
2727 : {
2728 14 : memcpy(psInfo->adfDstGeoTransform, padfDstGeoTransform,
2729 : sizeof(psInfo->adfDstGeoTransform));
2730 14 : if (!GDALInvGeoTransform(psInfo->adfDstGeoTransform,
2731 14 : psInfo->adfDstInvGeoTransform))
2732 : {
2733 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
2734 1 : GDALDestroyGenImgProjTransformer(psInfo);
2735 1 : return nullptr;
2736 : }
2737 : }
2738 : else
2739 : {
2740 0 : psInfo->adfDstGeoTransform[0] = 0.0;
2741 0 : psInfo->adfDstGeoTransform[1] = 1.0;
2742 0 : psInfo->adfDstGeoTransform[2] = 0.0;
2743 0 : psInfo->adfDstGeoTransform[3] = 0.0;
2744 0 : psInfo->adfDstGeoTransform[4] = 0.0;
2745 0 : psInfo->adfDstGeoTransform[5] = 1.0;
2746 0 : memcpy(psInfo->adfDstInvGeoTransform, psInfo->adfDstGeoTransform,
2747 : sizeof(double) * 6);
2748 : }
2749 :
2750 13 : return psInfo;
2751 : }
2752 :
2753 : /************************************************************************/
2754 : /* GDALSetGenImgProjTransformerDstGeoTransform() */
2755 : /************************************************************************/
2756 :
2757 : /**
2758 : * Set GenImgProj output geotransform.
2759 : *
2760 : * Normally the "destination geotransform", or transformation between
2761 : * georeferenced output coordinates and pixel/line coordinates on the
2762 : * destination file is extracted from the destination file by
2763 : * GDALCreateGenImgProjTransformer() and stored in the GenImgProj private
2764 : * info. However, sometimes it is inconvenient to have an output file
2765 : * handle with appropriate geotransform information when creating the
2766 : * transformation. For these cases, this function can be used to apply
2767 : * the destination geotransform.
2768 : *
2769 : * @param hTransformArg the handle to update.
2770 : * @param padfGeoTransform the destination geotransform to apply (six doubles).
2771 : */
2772 :
2773 746 : void GDALSetGenImgProjTransformerDstGeoTransform(void *hTransformArg,
2774 : const double *padfGeoTransform)
2775 :
2776 : {
2777 746 : VALIDATE_POINTER0(hTransformArg,
2778 : "GDALSetGenImgProjTransformerDstGeoTransform");
2779 :
2780 746 : GDALGenImgProjTransformInfo *psInfo =
2781 : static_cast<GDALGenImgProjTransformInfo *>(hTransformArg);
2782 :
2783 746 : memcpy(psInfo->adfDstGeoTransform, padfGeoTransform, sizeof(double) * 6);
2784 746 : if (!GDALInvGeoTransform(psInfo->adfDstGeoTransform,
2785 746 : psInfo->adfDstInvGeoTransform))
2786 : {
2787 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot invert geotransform");
2788 : }
2789 : }
2790 :
2791 : /************************************************************************/
2792 : /* GDALDestroyGenImgProjTransformer() */
2793 : /************************************************************************/
2794 :
2795 : /**
2796 : * GenImgProjTransformer deallocator.
2797 : *
2798 : * This function is used to deallocate the handle created with
2799 : * GDALCreateGenImgProjTransformer().
2800 : *
2801 : * @param hTransformArg the handle to deallocate.
2802 : */
2803 :
2804 1557 : void GDALDestroyGenImgProjTransformer(void *hTransformArg)
2805 :
2806 : {
2807 1557 : if (hTransformArg == nullptr)
2808 0 : return;
2809 :
2810 1557 : GDALGenImgProjTransformInfo *psInfo =
2811 : static_cast<GDALGenImgProjTransformInfo *>(hTransformArg);
2812 :
2813 1557 : if (psInfo->pSrcTransformArg != nullptr)
2814 144 : GDALDestroyTransformer(psInfo->pSrcTransformArg);
2815 :
2816 1557 : if (psInfo->pDstTransformArg != nullptr)
2817 7 : GDALDestroyTransformer(psInfo->pDstTransformArg);
2818 :
2819 1557 : if (psInfo->pReprojectArg != nullptr)
2820 813 : GDALDestroyTransformer(psInfo->pReprojectArg);
2821 :
2822 1557 : CPLFree(psInfo);
2823 : }
2824 :
2825 : /************************************************************************/
2826 : /* GDALGenImgProjTransform() */
2827 : /************************************************************************/
2828 :
2829 : /**
2830 : * Perform general image reprojection transformation.
2831 : *
2832 : * Actually performs the transformation setup in
2833 : * GDALCreateGenImgProjTransformer(). This function matches the signature
2834 : * required by the GDALTransformerFunc(), and more details on the arguments
2835 : * can be found in that topic.
2836 : */
2837 :
2838 : #ifdef DEBUG_APPROX_TRANSFORMER
2839 : int countGDALGenImgProjTransform = 0;
2840 : #endif
2841 :
2842 1876080 : int GDALGenImgProjTransform(void *pTransformArgIn, int bDstToSrc,
2843 : int nPointCount, double *padfX, double *padfY,
2844 : double *padfZ, int *panSuccess)
2845 : {
2846 1876080 : GDALGenImgProjTransformInfo *psInfo =
2847 : static_cast<GDALGenImgProjTransformInfo *>(pTransformArgIn);
2848 :
2849 : #ifdef DEBUG_APPROX_TRANSFORMER
2850 : CPLAssert(nPointCount > 0);
2851 : countGDALGenImgProjTransform += nPointCount;
2852 : #endif
2853 :
2854 21471200 : for (int i = 0; i < nPointCount; i++)
2855 : {
2856 19595100 : panSuccess[i] = (padfX[i] != HUGE_VAL && padfY[i] != HUGE_VAL);
2857 : }
2858 :
2859 : /* -------------------------------------------------------------------- */
2860 : /* Convert from src (dst) pixel/line to src (dst) */
2861 : /* georeferenced coordinates. */
2862 : /* -------------------------------------------------------------------- */
2863 1876080 : double *padfGeoTransform = nullptr;
2864 1876080 : void *pTransformArg = nullptr;
2865 1876080 : GDALTransformerFunc pTransformer = nullptr;
2866 1876080 : if (bDstToSrc)
2867 : {
2868 1522230 : padfGeoTransform = psInfo->adfDstGeoTransform;
2869 1522230 : pTransformArg = psInfo->pDstTransformArg;
2870 1522230 : pTransformer = psInfo->pDstTransformer;
2871 : }
2872 : else
2873 : {
2874 353854 : padfGeoTransform = psInfo->adfSrcGeoTransform;
2875 353854 : pTransformArg = psInfo->pSrcTransformArg;
2876 353854 : pTransformer = psInfo->pSrcTransformer;
2877 : }
2878 :
2879 1876080 : if (pTransformArg != nullptr)
2880 : {
2881 41602 : if (!pTransformer(pTransformArg, FALSE, nPointCount, padfX, padfY,
2882 : padfZ, panSuccess))
2883 0 : return FALSE;
2884 : }
2885 : else
2886 : {
2887 21351500 : for (int i = 0; i < nPointCount; i++)
2888 : {
2889 19517000 : if (!panSuccess[i])
2890 1758 : continue;
2891 :
2892 19515200 : const double dfNewX = padfGeoTransform[0] +
2893 19515200 : padfX[i] * padfGeoTransform[1] +
2894 19515200 : padfY[i] * padfGeoTransform[2];
2895 19515200 : const double dfNewY = padfGeoTransform[3] +
2896 19515200 : padfX[i] * padfGeoTransform[4] +
2897 19515200 : padfY[i] * padfGeoTransform[5];
2898 :
2899 19515200 : padfX[i] = dfNewX;
2900 19515200 : padfY[i] = dfNewY;
2901 : }
2902 : }
2903 :
2904 : /* -------------------------------------------------------------------- */
2905 : /* Reproject if needed. */
2906 : /* -------------------------------------------------------------------- */
2907 1876080 : if (psInfo->pReprojectArg)
2908 : {
2909 1582780 : if (!psInfo->pReproject(psInfo->pReprojectArg, bDstToSrc, nPointCount,
2910 : padfX, padfY, padfZ, panSuccess))
2911 0 : return FALSE;
2912 : }
2913 :
2914 : /* -------------------------------------------------------------------- */
2915 : /* Convert dst (src) georef coordinates back to pixel/line. */
2916 : /* -------------------------------------------------------------------- */
2917 1876080 : if (bDstToSrc)
2918 : {
2919 1522230 : padfGeoTransform = psInfo->adfSrcInvGeoTransform;
2920 1522230 : pTransformArg = psInfo->pSrcTransformArg;
2921 1522230 : pTransformer = psInfo->pSrcTransformer;
2922 : }
2923 : else
2924 : {
2925 353854 : padfGeoTransform = psInfo->adfDstInvGeoTransform;
2926 353854 : pTransformArg = psInfo->pDstTransformArg;
2927 353854 : pTransformer = psInfo->pDstTransformer;
2928 : }
2929 :
2930 1876080 : if (pTransformArg != nullptr)
2931 : {
2932 51654 : if (!pTransformer(pTransformArg, TRUE, nPointCount, padfX, padfY, padfZ,
2933 : panSuccess))
2934 0 : return FALSE;
2935 : }
2936 : else
2937 : {
2938 20531900 : for (int i = 0; i < nPointCount; i++)
2939 : {
2940 18707500 : if (!panSuccess[i])
2941 3522670 : continue;
2942 :
2943 15184800 : const double dfNewX = padfGeoTransform[0] +
2944 15184800 : padfX[i] * padfGeoTransform[1] +
2945 15184800 : padfY[i] * padfGeoTransform[2];
2946 15184800 : const double dfNewY = padfGeoTransform[3] +
2947 15184800 : padfX[i] * padfGeoTransform[4] +
2948 15184800 : padfY[i] * padfGeoTransform[5];
2949 :
2950 15184800 : padfX[i] = dfNewX;
2951 15184800 : padfY[i] = dfNewY;
2952 : }
2953 : }
2954 :
2955 1876080 : return TRUE;
2956 : }
2957 :
2958 : /************************************************************************/
2959 : /* GDALTransformLonLatToDestGenImgProjTransformer() */
2960 : /************************************************************************/
2961 :
2962 2384 : int GDALTransformLonLatToDestGenImgProjTransformer(void *hTransformArg,
2963 : double *pdfX, double *pdfY)
2964 : {
2965 2384 : GDALGenImgProjTransformInfo *psInfo =
2966 : static_cast<GDALGenImgProjTransformInfo *>(hTransformArg);
2967 :
2968 2384 : if (psInfo->pReprojectArg == nullptr ||
2969 1370 : psInfo->pReproject != GDALReprojectionTransform)
2970 1018 : return false;
2971 :
2972 1366 : GDALReprojectionTransformInfo *psReprojInfo =
2973 : static_cast<GDALReprojectionTransformInfo *>(psInfo->pReprojectArg);
2974 2732 : if (psReprojInfo->poForwardTransform == nullptr ||
2975 1366 : psReprojInfo->poForwardTransform->GetSourceCS() == nullptr)
2976 2 : return false;
2977 :
2978 1364 : double z = 0;
2979 1364 : int success = true;
2980 1364 : auto poSourceCRS = psReprojInfo->poForwardTransform->GetSourceCS();
2981 1364 : if (poSourceCRS->IsGeographic())
2982 : {
2983 : // Optimization to avoid creating a OGRCoordinateTransformation
2984 1060 : OGRAxisOrientation eSourceFirstAxisOrient = OAO_Other;
2985 1060 : poSourceCRS->GetAxis(nullptr, 0, &eSourceFirstAxisOrient);
2986 1060 : const auto &mapping = poSourceCRS->GetDataAxisToSRSAxisMapping();
2987 2120 : if ((mapping[0] == 2 && eSourceFirstAxisOrient == OAO_East) ||
2988 1060 : (mapping[0] == 1 && eSourceFirstAxisOrient != OAO_East))
2989 : {
2990 6 : std::swap(*pdfX, *pdfY);
2991 : }
2992 : }
2993 : else
2994 : {
2995 : auto poLongLat =
2996 304 : std::unique_ptr<OGRSpatialReference>(poSourceCRS->CloneGeogCS());
2997 304 : if (poLongLat == nullptr)
2998 0 : return false;
2999 304 : poLongLat->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3000 :
3001 : const bool bCurrentCheckWithInvertProj =
3002 304 : GetCurrentCheckWithInvertPROJ();
3003 304 : if (!bCurrentCheckWithInvertProj)
3004 304 : CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", "YES");
3005 : auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
3006 304 : OGRCreateCoordinateTransformation(poLongLat.get(), poSourceCRS));
3007 304 : if (!bCurrentCheckWithInvertProj)
3008 304 : CPLSetThreadLocalConfigOption("CHECK_WITH_INVERT_PROJ", nullptr);
3009 304 : if (poCT == nullptr)
3010 0 : return false;
3011 :
3012 304 : poCT->SetEmitErrors(false);
3013 304 : if (!poCT->Transform(1, pdfX, pdfY))
3014 2 : return false;
3015 :
3016 302 : if (!psInfo->pReproject(psInfo->pReprojectArg, false, 1, pdfX, pdfY, &z,
3017 604 : &success) ||
3018 302 : !success)
3019 : {
3020 138 : return false;
3021 : }
3022 : }
3023 :
3024 1224 : double *padfGeoTransform = psInfo->adfDstInvGeoTransform;
3025 1224 : void *pTransformArg = psInfo->pDstTransformArg;
3026 1224 : GDALTransformerFunc pTransformer = psInfo->pDstTransformer;
3027 1224 : if (pTransformArg != nullptr)
3028 : {
3029 8 : if (!pTransformer(pTransformArg, TRUE, 1, pdfX, pdfY, &z, &success) ||
3030 4 : !success)
3031 : {
3032 4 : return false;
3033 : }
3034 : }
3035 : else
3036 : {
3037 1220 : const double dfNewX = padfGeoTransform[0] +
3038 1220 : pdfX[0] * padfGeoTransform[1] +
3039 1220 : pdfY[0] * padfGeoTransform[2];
3040 1220 : const double dfNewY = padfGeoTransform[3] +
3041 1220 : pdfX[0] * padfGeoTransform[4] +
3042 1220 : pdfY[0] * padfGeoTransform[5];
3043 :
3044 1220 : pdfX[0] = dfNewX;
3045 1220 : pdfY[0] = dfNewY;
3046 : }
3047 :
3048 1220 : return true;
3049 : }
3050 :
3051 : /************************************************************************/
3052 : /* GDALSerializeGenImgProjTransformer() */
3053 : /************************************************************************/
3054 :
3055 75 : static CPLXMLNode *GDALSerializeGenImgProjTransformer(void *pTransformArg)
3056 :
3057 : {
3058 75 : GDALGenImgProjTransformInfo *psInfo =
3059 : static_cast<GDALGenImgProjTransformInfo *>(pTransformArg);
3060 :
3061 : CPLXMLNode *psTree =
3062 75 : CPLCreateXMLNode(nullptr, CXT_Element, "GenImgProjTransformer");
3063 :
3064 75 : char szWork[200] = {};
3065 :
3066 : /* -------------------------------------------------------------------- */
3067 : /* Handle source transformation. */
3068 : /* -------------------------------------------------------------------- */
3069 75 : if (psInfo->pSrcTransformArg != nullptr)
3070 : {
3071 13 : CPLXMLNode *psTransformer = GDALSerializeTransformer(
3072 : psInfo->pSrcTransformer, psInfo->pSrcTransformArg);
3073 13 : if (psTransformer != nullptr)
3074 : {
3075 : CPLXMLNode *psTransformerContainer =
3076 13 : CPLCreateXMLNode(psTree, CXT_Element,
3077 : CPLSPrintf("Src%s", psTransformer->pszValue));
3078 :
3079 13 : CPLAddXMLChild(psTransformerContainer, psTransformer);
3080 : }
3081 : }
3082 :
3083 : /* -------------------------------------------------------------------- */
3084 : /* Handle source geotransforms. */
3085 : /* -------------------------------------------------------------------- */
3086 : else
3087 : {
3088 62 : CPLsnprintf(
3089 : szWork, sizeof(szWork), "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g",
3090 : psInfo->adfSrcGeoTransform[0], psInfo->adfSrcGeoTransform[1],
3091 : psInfo->adfSrcGeoTransform[2], psInfo->adfSrcGeoTransform[3],
3092 : psInfo->adfSrcGeoTransform[4], psInfo->adfSrcGeoTransform[5]);
3093 62 : CPLCreateXMLElementAndValue(psTree, "SrcGeoTransform", szWork);
3094 :
3095 62 : CPLsnprintf(
3096 : szWork, sizeof(szWork), "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g",
3097 : psInfo->adfSrcInvGeoTransform[0], psInfo->adfSrcInvGeoTransform[1],
3098 : psInfo->adfSrcInvGeoTransform[2], psInfo->adfSrcInvGeoTransform[3],
3099 : psInfo->adfSrcInvGeoTransform[4], psInfo->adfSrcInvGeoTransform[5]);
3100 62 : CPLCreateXMLElementAndValue(psTree, "SrcInvGeoTransform", szWork);
3101 : }
3102 :
3103 : /* -------------------------------------------------------------------- */
3104 : /* Handle dest transformation. */
3105 : /* -------------------------------------------------------------------- */
3106 75 : if (psInfo->pDstTransformArg != nullptr)
3107 : {
3108 0 : CPLXMLNode *psTransformer = GDALSerializeTransformer(
3109 : psInfo->pDstTransformer, psInfo->pDstTransformArg);
3110 0 : if (psTransformer != nullptr)
3111 : {
3112 : CPLXMLNode *psTransformerContainer =
3113 0 : CPLCreateXMLNode(psTree, CXT_Element,
3114 : CPLSPrintf("Dst%s", psTransformer->pszValue));
3115 :
3116 0 : CPLAddXMLChild(psTransformerContainer, psTransformer);
3117 : }
3118 : }
3119 :
3120 : /* -------------------------------------------------------------------- */
3121 : /* Handle destination geotransforms. */
3122 : /* -------------------------------------------------------------------- */
3123 : else
3124 : {
3125 75 : CPLsnprintf(
3126 : szWork, sizeof(szWork), "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g",
3127 : psInfo->adfDstGeoTransform[0], psInfo->adfDstGeoTransform[1],
3128 : psInfo->adfDstGeoTransform[2], psInfo->adfDstGeoTransform[3],
3129 : psInfo->adfDstGeoTransform[4], psInfo->adfDstGeoTransform[5]);
3130 75 : CPLCreateXMLElementAndValue(psTree, "DstGeoTransform", szWork);
3131 :
3132 75 : CPLsnprintf(
3133 : szWork, sizeof(szWork), "%.18g,%.18g,%.18g,%.18g,%.18g,%.18g",
3134 : psInfo->adfDstInvGeoTransform[0], psInfo->adfDstInvGeoTransform[1],
3135 : psInfo->adfDstInvGeoTransform[2], psInfo->adfDstInvGeoTransform[3],
3136 : psInfo->adfDstInvGeoTransform[4], psInfo->adfDstInvGeoTransform[5]);
3137 75 : CPLCreateXMLElementAndValue(psTree, "DstInvGeoTransform", szWork);
3138 : }
3139 :
3140 : /* -------------------------------------------------------------------- */
3141 : /* Do we have a reprojection transformer? */
3142 : /* -------------------------------------------------------------------- */
3143 75 : if (psInfo->pReprojectArg != nullptr)
3144 : {
3145 :
3146 : CPLXMLNode *psTransformerContainer =
3147 50 : CPLCreateXMLNode(psTree, CXT_Element, "ReprojectTransformer");
3148 :
3149 : CPLXMLNode *psTransformer =
3150 50 : GDALSerializeTransformer(psInfo->pReproject, psInfo->pReprojectArg);
3151 50 : if (psTransformer != nullptr)
3152 50 : CPLAddXMLChild(psTransformerContainer, psTransformer);
3153 : }
3154 :
3155 75 : return psTree;
3156 : }
3157 :
3158 : /************************************************************************/
3159 : /* GDALDeserializeGeoTransform() */
3160 : /************************************************************************/
3161 :
3162 707 : static void GDALDeserializeGeoTransform(const char *pszGT,
3163 : double adfGeoTransform[6])
3164 : {
3165 707 : CPLsscanf(pszGT, "%lf,%lf,%lf,%lf,%lf,%lf", adfGeoTransform + 0,
3166 : adfGeoTransform + 1, adfGeoTransform + 2, adfGeoTransform + 3,
3167 : adfGeoTransform + 4, adfGeoTransform + 5);
3168 707 : }
3169 :
3170 : /************************************************************************/
3171 : /* GDALDeserializeGenImgProjTransformer() */
3172 : /************************************************************************/
3173 :
3174 191 : void *GDALDeserializeGenImgProjTransformer(CPLXMLNode *psTree)
3175 :
3176 : {
3177 : /* -------------------------------------------------------------------- */
3178 : /* Initialize the transform info. */
3179 : /* -------------------------------------------------------------------- */
3180 : GDALGenImgProjTransformInfo *psInfo =
3181 191 : GDALCreateGenImgProjTransformerInternal();
3182 :
3183 : /* -------------------------------------------------------------------- */
3184 : /* SrcGeotransform */
3185 : /* -------------------------------------------------------------------- */
3186 191 : if (CPLGetXMLNode(psTree, "SrcGeoTransform") != nullptr)
3187 : {
3188 175 : GDALDeserializeGeoTransform(
3189 : CPLGetXMLValue(psTree, "SrcGeoTransform", ""),
3190 175 : psInfo->adfSrcGeoTransform);
3191 :
3192 175 : if (CPLGetXMLNode(psTree, "SrcInvGeoTransform") != nullptr)
3193 : {
3194 175 : GDALDeserializeGeoTransform(
3195 : CPLGetXMLValue(psTree, "SrcInvGeoTransform", ""),
3196 175 : psInfo->adfSrcInvGeoTransform);
3197 : }
3198 : else
3199 : {
3200 0 : if (!GDALInvGeoTransform(psInfo->adfSrcGeoTransform,
3201 0 : psInfo->adfSrcInvGeoTransform))
3202 : {
3203 0 : CPLError(CE_Failure, CPLE_AppDefined,
3204 : "Cannot invert geotransform");
3205 : }
3206 : }
3207 : }
3208 :
3209 : /* -------------------------------------------------------------------- */
3210 : /* Src Transform */
3211 : /* -------------------------------------------------------------------- */
3212 : else
3213 : {
3214 16 : for (CPLXMLNode *psIter = psTree->psChild; psIter != nullptr;
3215 0 : psIter = psIter->psNext)
3216 : {
3217 16 : if (psIter->eType == CXT_Element &&
3218 16 : STARTS_WITH_CI(psIter->pszValue, "Src"))
3219 : {
3220 16 : GDALDeserializeTransformer(psIter->psChild,
3221 : &psInfo->pSrcTransformer,
3222 : &psInfo->pSrcTransformArg);
3223 16 : break;
3224 : }
3225 : }
3226 : }
3227 :
3228 : /* -------------------------------------------------------------------- */
3229 : /* DstGeotransform */
3230 : /* -------------------------------------------------------------------- */
3231 191 : if (CPLGetXMLNode(psTree, "DstGeoTransform") != nullptr)
3232 : {
3233 191 : GDALDeserializeGeoTransform(
3234 : CPLGetXMLValue(psTree, "DstGeoTransform", ""),
3235 191 : psInfo->adfDstGeoTransform);
3236 :
3237 191 : if (CPLGetXMLNode(psTree, "DstInvGeoTransform") != nullptr)
3238 : {
3239 166 : GDALDeserializeGeoTransform(
3240 : CPLGetXMLValue(psTree, "DstInvGeoTransform", ""),
3241 166 : psInfo->adfDstInvGeoTransform);
3242 : }
3243 : else
3244 : {
3245 25 : if (!GDALInvGeoTransform(psInfo->adfDstGeoTransform,
3246 25 : psInfo->adfDstInvGeoTransform))
3247 : {
3248 0 : CPLError(CE_Failure, CPLE_AppDefined,
3249 : "Cannot invert geotransform");
3250 : }
3251 : }
3252 : }
3253 :
3254 : /* -------------------------------------------------------------------- */
3255 : /* Dst Transform */
3256 : /* -------------------------------------------------------------------- */
3257 : else
3258 : {
3259 0 : for (CPLXMLNode *psIter = psTree->psChild; psIter != nullptr;
3260 0 : psIter = psIter->psNext)
3261 : {
3262 0 : if (psIter->eType == CXT_Element &&
3263 0 : STARTS_WITH_CI(psIter->pszValue, "Dst"))
3264 : {
3265 0 : GDALDeserializeTransformer(psIter->psChild,
3266 : &psInfo->pDstTransformer,
3267 : &psInfo->pDstTransformArg);
3268 0 : break;
3269 : }
3270 : }
3271 : }
3272 :
3273 : /* -------------------------------------------------------------------- */
3274 : /* Reproject transformer */
3275 : /* -------------------------------------------------------------------- */
3276 191 : CPLXMLNode *psSubtree = CPLGetXMLNode(psTree, "ReprojectTransformer");
3277 191 : if (psSubtree != nullptr && psSubtree->psChild != nullptr)
3278 : {
3279 97 : GDALDeserializeTransformer(psSubtree->psChild, &psInfo->pReproject,
3280 : &psInfo->pReprojectArg);
3281 : }
3282 :
3283 191 : return psInfo;
3284 : }
3285 :
3286 : /************************************************************************/
3287 : /* GDALCreateReprojectionTransformer() */
3288 : /************************************************************************/
3289 :
3290 : /**
3291 : * Create reprojection transformer.
3292 : *
3293 : * Creates a callback data structure suitable for use with
3294 : * GDALReprojectionTransformation() to represent a transformation from
3295 : * one geographic or projected coordinate system to another. On input
3296 : * the coordinate systems are described in OpenGIS WKT format.
3297 : *
3298 : * Internally the OGRCoordinateTransformation object is used to implement
3299 : * the reprojection.
3300 : *
3301 : * @param pszSrcWKT the coordinate system for the source coordinate system.
3302 : * @param pszDstWKT the coordinate system for the destination coordinate
3303 : * system.
3304 : *
3305 : * @return Handle for use with GDALReprojectionTransform(), or NULL if the
3306 : * system fails to initialize the reprojection.
3307 : **/
3308 :
3309 0 : void *GDALCreateReprojectionTransformer(const char *pszSrcWKT,
3310 : const char *pszDstWKT)
3311 :
3312 : {
3313 : /* -------------------------------------------------------------------- */
3314 : /* Ingest the SRS definitions. */
3315 : /* -------------------------------------------------------------------- */
3316 0 : OGRSpatialReference oSrcSRS;
3317 0 : oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3318 0 : if (oSrcSRS.importFromWkt(pszSrcWKT) != OGRERR_NONE)
3319 : {
3320 0 : CPLError(CE_Failure, CPLE_AppDefined,
3321 : "Failed to import coordinate system `%s'.", pszSrcWKT);
3322 0 : return nullptr;
3323 : }
3324 :
3325 0 : OGRSpatialReference oDstSRS;
3326 0 : oDstSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3327 0 : if (oDstSRS.importFromWkt(pszDstWKT) != OGRERR_NONE)
3328 : {
3329 0 : CPLError(CE_Failure, CPLE_AppDefined,
3330 : "Failed to import coordinate system `%s'.", pszSrcWKT);
3331 0 : return nullptr;
3332 : }
3333 :
3334 0 : return GDALCreateReprojectionTransformerEx(
3335 : OGRSpatialReference::ToHandle(&oSrcSRS),
3336 0 : OGRSpatialReference::ToHandle(&oDstSRS), nullptr);
3337 : }
3338 :
3339 : /************************************************************************/
3340 : /* GDALCreateReprojectionTransformerEx() */
3341 : /************************************************************************/
3342 :
3343 : /**
3344 : * Create reprojection transformer.
3345 : *
3346 : * Creates a callback data structure suitable for use with
3347 : * GDALReprojectionTransformation() to represent a transformation from
3348 : * one geographic or projected coordinate system to another.
3349 : *
3350 : * Internally the OGRCoordinateTransformation object is used to implement
3351 : * the reprojection.
3352 : *
3353 : * @param hSrcSRS the coordinate system for the source coordinate system.
3354 : * @param hDstSRS the coordinate system for the destination coordinate
3355 : * system.
3356 : * @param papszOptions NULL-terminated list of options, or NULL. Currently
3357 : * supported options are:
3358 : * <ul>
3359 : * <li>AREA_OF_INTEREST=west_long,south_lat,east_long,north_lat: Values in
3360 : * degrees. longitudes in [-180,180], latitudes in [-90,90].</li>
3361 : * <li>COORDINATE_OPERATION=string: PROJ or WKT string representing a
3362 : * coordinate operation, overriding the default computed transformation.</li>
3363 : * <li>COORDINATE_EPOCH=decimal_year: Coordinate epoch, expressed as a
3364 : * decimal year. Useful for time-dependant coordinate operations.</li>
3365 : * <li> SRC_COORDINATE_EPOCH: (GDAL >= 3.4) Coordinate epoch of source CRS,
3366 : * expressed as a decimal year. Useful for time-dependant coordinate
3367 : *operations.</li> <li> DST_COORDINATE_EPOCH: (GDAL >= 3.4) Coordinate epoch
3368 : *of target CRS, expressed as a decimal year. Useful for time-dependant
3369 : *coordinate operations.</li>
3370 : * </ul>
3371 : *
3372 : * @return Handle for use with GDALReprojectionTransform(), or NULL if the
3373 : * system fails to initialize the reprojection.
3374 : *
3375 : * @since GDAL 3.0
3376 : **/
3377 :
3378 886 : void *GDALCreateReprojectionTransformerEx(OGRSpatialReferenceH hSrcSRS,
3379 : OGRSpatialReferenceH hDstSRS,
3380 : const char *const *papszOptions)
3381 : {
3382 886 : OGRSpatialReference *poSrcSRS = OGRSpatialReference::FromHandle(hSrcSRS);
3383 886 : OGRSpatialReference *poDstSRS = OGRSpatialReference::FromHandle(hDstSRS);
3384 :
3385 : /* -------------------------------------------------------------------- */
3386 : /* Build the forward coordinate transformation. */
3387 : /* -------------------------------------------------------------------- */
3388 886 : double dfWestLongitudeDeg = 0.0;
3389 886 : double dfSouthLatitudeDeg = 0.0;
3390 886 : double dfEastLongitudeDeg = 0.0;
3391 886 : double dfNorthLatitudeDeg = 0.0;
3392 886 : const char *pszBBOX = CSLFetchNameValue(papszOptions, "AREA_OF_INTEREST");
3393 886 : if (pszBBOX)
3394 : {
3395 816 : char **papszTokens = CSLTokenizeString2(pszBBOX, ",", 0);
3396 816 : if (CSLCount(papszTokens) == 4)
3397 : {
3398 816 : dfWestLongitudeDeg = CPLAtof(papszTokens[0]);
3399 816 : dfSouthLatitudeDeg = CPLAtof(papszTokens[1]);
3400 816 : dfEastLongitudeDeg = CPLAtof(papszTokens[2]);
3401 816 : dfNorthLatitudeDeg = CPLAtof(papszTokens[3]);
3402 : }
3403 816 : CSLDestroy(papszTokens);
3404 : }
3405 886 : const char *pszCO = CSLFetchNameValue(papszOptions, "COORDINATE_OPERATION");
3406 :
3407 1772 : OGRCoordinateTransformationOptions optionsFwd;
3408 886 : if (!(dfWestLongitudeDeg == 0.0 && dfSouthLatitudeDeg == 0.0 &&
3409 : dfEastLongitudeDeg == 0.0 && dfNorthLatitudeDeg == 0.0))
3410 : {
3411 816 : optionsFwd.SetAreaOfInterest(dfWestLongitudeDeg, dfSouthLatitudeDeg,
3412 : dfEastLongitudeDeg, dfNorthLatitudeDeg);
3413 : }
3414 886 : if (pszCO)
3415 : {
3416 7 : optionsFwd.SetCoordinateOperation(pszCO, false);
3417 : }
3418 :
3419 886 : const char *pszCENTER_LONG = CSLFetchNameValue(papszOptions, "CENTER_LONG");
3420 886 : if (pszCENTER_LONG)
3421 : {
3422 588 : optionsFwd.SetSourceCenterLong(CPLAtof(pszCENTER_LONG));
3423 : }
3424 :
3425 : OGRCoordinateTransformation *poForwardTransform =
3426 886 : OGRCreateCoordinateTransformation(poSrcSRS, poDstSRS, optionsFwd);
3427 :
3428 886 : if (poForwardTransform == nullptr)
3429 : // OGRCreateCoordinateTransformation() will report errors on its own.
3430 2 : return nullptr;
3431 :
3432 884 : poForwardTransform->SetEmitErrors(false);
3433 :
3434 : /* -------------------------------------------------------------------- */
3435 : /* Create a structure to hold the transform info, and also */
3436 : /* build reverse transform. We assume that if the forward */
3437 : /* transform can be created, then so can the reverse one. */
3438 : /* -------------------------------------------------------------------- */
3439 884 : GDALReprojectionTransformInfo *psInfo = new GDALReprojectionTransformInfo();
3440 :
3441 884 : psInfo->papszOptions = CSLDuplicate(papszOptions);
3442 884 : psInfo->poForwardTransform = poForwardTransform;
3443 884 : psInfo->dfTime = CPLAtof(CSLFetchNameValueDef(
3444 : papszOptions, "COORDINATE_EPOCH",
3445 : CSLFetchNameValueDef(
3446 : papszOptions, "DST_COORDINATE_EPOCH",
3447 : CSLFetchNameValueDef(papszOptions, "SRC_COORDINATE_EPOCH", "0"))));
3448 884 : psInfo->poReverseTransform = poForwardTransform->GetInverse();
3449 :
3450 884 : if (psInfo->poReverseTransform)
3451 884 : psInfo->poReverseTransform->SetEmitErrors(false);
3452 :
3453 884 : memcpy(psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
3454 : strlen(GDAL_GTI2_SIGNATURE));
3455 884 : psInfo->sTI.pszClassName = "GDALReprojectionTransformer";
3456 884 : psInfo->sTI.pfnTransform = GDALReprojectionTransform;
3457 884 : psInfo->sTI.pfnCleanup = GDALDestroyReprojectionTransformer;
3458 884 : psInfo->sTI.pfnSerialize = GDALSerializeReprojectionTransformer;
3459 :
3460 884 : return psInfo;
3461 : }
3462 :
3463 : /************************************************************************/
3464 : /* GDALDestroyReprojectionTransformer() */
3465 : /************************************************************************/
3466 :
3467 : /**
3468 : * Destroy reprojection transformation.
3469 : *
3470 : * @param pTransformArg the transformation handle returned by
3471 : * GDALCreateReprojectionTransformer().
3472 : */
3473 :
3474 884 : void GDALDestroyReprojectionTransformer(void *pTransformArg)
3475 :
3476 : {
3477 884 : if (pTransformArg == nullptr)
3478 0 : return;
3479 :
3480 884 : GDALReprojectionTransformInfo *psInfo =
3481 : static_cast<GDALReprojectionTransformInfo *>(pTransformArg);
3482 :
3483 884 : if (psInfo->poForwardTransform)
3484 884 : OGRCoordinateTransformation::DestroyCT(psInfo->poForwardTransform);
3485 :
3486 884 : if (psInfo->poReverseTransform)
3487 884 : OGRCoordinateTransformation::DestroyCT(psInfo->poReverseTransform);
3488 :
3489 884 : CSLDestroy(psInfo->papszOptions);
3490 :
3491 884 : delete psInfo;
3492 : }
3493 :
3494 : /************************************************************************/
3495 : /* GDALReprojectionTransform() */
3496 : /************************************************************************/
3497 :
3498 : /**
3499 : * Perform reprojection transformation.
3500 : *
3501 : * Actually performs the reprojection transformation described in
3502 : * GDALCreateReprojectionTransformer(). This function matches the
3503 : * GDALTransformerFunc() signature. Details of the arguments are described
3504 : * there.
3505 : */
3506 :
3507 1583080 : int GDALReprojectionTransform(void *pTransformArg, int bDstToSrc,
3508 : int nPointCount, double *padfX, double *padfY,
3509 : double *padfZ, int *panSuccess)
3510 :
3511 : {
3512 1583080 : GDALReprojectionTransformInfo *psInfo =
3513 : static_cast<GDALReprojectionTransformInfo *>(pTransformArg);
3514 : int bSuccess;
3515 :
3516 1583080 : std::vector<double> adfTime;
3517 1583080 : double *padfT = nullptr;
3518 1583080 : if (psInfo->dfTime != 0.0 && nPointCount > 0)
3519 : {
3520 1 : adfTime.resize(nPointCount, psInfo->dfTime);
3521 1 : padfT = &adfTime[0];
3522 : }
3523 :
3524 1583080 : if (bDstToSrc)
3525 : {
3526 1353350 : if (psInfo->poReverseTransform == nullptr)
3527 : {
3528 0 : CPLError(
3529 : CE_Failure, CPLE_AppDefined,
3530 : "Inverse coordinate transformation cannot be instantiated");
3531 0 : if (panSuccess)
3532 : {
3533 0 : for (int i = 0; i < nPointCount; i++)
3534 0 : panSuccess[i] = FALSE;
3535 : }
3536 0 : bSuccess = false;
3537 : }
3538 : else
3539 : {
3540 1353340 : bSuccess = psInfo->poReverseTransform->Transform(
3541 1353350 : nPointCount, padfX, padfY, padfZ, padfT, panSuccess);
3542 : }
3543 : }
3544 : else
3545 229731 : bSuccess = psInfo->poForwardTransform->Transform(
3546 229731 : nPointCount, padfX, padfY, padfZ, padfT, panSuccess);
3547 :
3548 3166150 : return bSuccess;
3549 : }
3550 :
3551 : /************************************************************************/
3552 : /* GDALSerializeReprojectionTransformer() */
3553 : /************************************************************************/
3554 :
3555 131 : static CPLXMLNode *GDALSerializeReprojectionTransformer(void *pTransformArg)
3556 :
3557 : {
3558 : CPLXMLNode *psTree;
3559 131 : GDALReprojectionTransformInfo *psInfo =
3560 : static_cast<GDALReprojectionTransformInfo *>(pTransformArg);
3561 :
3562 131 : psTree = CPLCreateXMLNode(nullptr, CXT_Element, "ReprojectionTransformer");
3563 :
3564 : /* -------------------------------------------------------------------- */
3565 : /* Handle SourceCS. */
3566 : /* -------------------------------------------------------------------- */
3567 261 : const auto ExportToWkt = [](const OGRSpatialReference *poSRS)
3568 : {
3569 : // Try first in WKT1 for backward compat
3570 : {
3571 261 : char *pszWKT = nullptr;
3572 261 : const char *const apszOptions[] = {"FORMAT=WKT1", nullptr};
3573 261 : CPLErrorHandlerPusher oHandler(CPLQuietErrorHandler);
3574 261 : CPLErrorStateBackuper oBackuper;
3575 261 : if (poSRS->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE)
3576 : {
3577 522 : std::string osRet(pszWKT);
3578 261 : CPLFree(pszWKT);
3579 261 : return osRet;
3580 : }
3581 1 : CPLFree(pszWKT);
3582 : }
3583 :
3584 1 : char *pszWKT = nullptr;
3585 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
3586 1 : if (poSRS->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE)
3587 : {
3588 2 : std::string osRet(pszWKT);
3589 1 : CPLFree(pszWKT);
3590 1 : return osRet;
3591 : }
3592 0 : CPLFree(pszWKT);
3593 0 : return std::string();
3594 : };
3595 :
3596 130 : auto poSRS = psInfo->poForwardTransform->GetSourceCS();
3597 130 : if (poSRS)
3598 : {
3599 261 : const auto osWKT = ExportToWkt(poSRS);
3600 131 : CPLCreateXMLElementAndValue(psTree, "SourceSRS", osWKT.c_str());
3601 : }
3602 :
3603 : /* -------------------------------------------------------------------- */
3604 : /* Handle DestinationCS. */
3605 : /* -------------------------------------------------------------------- */
3606 131 : poSRS = psInfo->poForwardTransform->GetTargetCS();
3607 131 : if (poSRS)
3608 : {
3609 262 : const auto osWKT = ExportToWkt(poSRS);
3610 131 : CPLCreateXMLElementAndValue(psTree, "TargetSRS", osWKT.c_str());
3611 : }
3612 :
3613 : /* -------------------------------------------------------------------- */
3614 : /* Serialize options. */
3615 : /* -------------------------------------------------------------------- */
3616 131 : if (psInfo->papszOptions)
3617 : {
3618 : CPLXMLNode *psOptions =
3619 112 : CPLCreateXMLNode(psTree, CXT_Element, "Options");
3620 271 : for (auto iter = psInfo->papszOptions; *iter != nullptr; ++iter)
3621 : {
3622 159 : char *pszKey = nullptr;
3623 159 : const char *pszValue = CPLParseNameValue(*iter, &pszKey);
3624 159 : if (pszKey && pszValue)
3625 : {
3626 : auto elt =
3627 159 : CPLCreateXMLElementAndValue(psOptions, "Option", pszValue);
3628 159 : CPLAddXMLAttributeAndValue(elt, "key", pszKey);
3629 : }
3630 159 : CPLFree(pszKey);
3631 : }
3632 : }
3633 :
3634 131 : return psTree;
3635 : }
3636 :
3637 : /************************************************************************/
3638 : /* GDALDeserializeReprojectionTransformer() */
3639 : /************************************************************************/
3640 :
3641 178 : static void *GDALDeserializeReprojectionTransformer(CPLXMLNode *psTree)
3642 :
3643 : {
3644 178 : const char *pszSourceSRS = CPLGetXMLValue(psTree, "SourceSRS", nullptr);
3645 178 : const char *pszTargetSRS = CPLGetXMLValue(psTree, "TargetSRS", nullptr);
3646 178 : char *pszSourceWKT = nullptr, *pszTargetWKT = nullptr;
3647 178 : void *pResult = nullptr;
3648 :
3649 356 : OGRSpatialReference oSrcSRS;
3650 356 : OGRSpatialReference oDstSRS;
3651 :
3652 178 : oSrcSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3653 178 : oDstSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3654 178 : if (pszSourceSRS != nullptr)
3655 : {
3656 178 : oSrcSRS.SetFromUserInput(pszSourceSRS);
3657 : }
3658 :
3659 178 : if (pszTargetSRS != nullptr)
3660 : {
3661 178 : oDstSRS.SetFromUserInput(pszTargetSRS);
3662 : }
3663 :
3664 178 : CPLStringList aosList;
3665 178 : const CPLXMLNode *psOptions = CPLGetXMLNode(psTree, "Options");
3666 178 : if (psOptions)
3667 : {
3668 339 : for (auto iter = psOptions->psChild; iter; iter = iter->psNext)
3669 : {
3670 203 : if (iter->eType == CXT_Element &&
3671 203 : strcmp(iter->pszValue, "Option") == 0)
3672 : {
3673 203 : const char *pszKey = CPLGetXMLValue(iter, "key", nullptr);
3674 203 : const char *pszValue = CPLGetXMLValue(iter, nullptr, nullptr);
3675 203 : if (pszKey && pszValue)
3676 : {
3677 203 : aosList.SetNameValue(pszKey, pszValue);
3678 : }
3679 : }
3680 : }
3681 : }
3682 :
3683 178 : pResult = GDALCreateReprojectionTransformerEx(
3684 178 : !oSrcSRS.IsEmpty() ? OGRSpatialReference::ToHandle(&oSrcSRS) : nullptr,
3685 178 : !oDstSRS.IsEmpty() ? OGRSpatialReference::ToHandle(&oDstSRS) : nullptr,
3686 178 : aosList.List());
3687 :
3688 178 : CPLFree(pszSourceWKT);
3689 178 : CPLFree(pszTargetWKT);
3690 :
3691 356 : return pResult;
3692 : }
3693 :
3694 : /************************************************************************/
3695 : /* ==================================================================== */
3696 : /* Approximate transformer. */
3697 : /* ==================================================================== */
3698 : /************************************************************************/
3699 :
3700 : typedef struct
3701 : {
3702 : GDALTransformerInfo sTI;
3703 :
3704 : GDALTransformerFunc pfnBaseTransformer;
3705 : void *pBaseCBData;
3706 : double dfMaxErrorForward;
3707 : double dfMaxErrorReverse;
3708 :
3709 : int bOwnSubtransformer;
3710 : } ApproxTransformInfo;
3711 :
3712 : /************************************************************************/
3713 : /* GDALCreateSimilarApproxTransformer() */
3714 : /************************************************************************/
3715 :
3716 20 : static void *GDALCreateSimilarApproxTransformer(void *hTransformArg,
3717 : double dfSrcRatioX,
3718 : double dfSrcRatioY)
3719 : {
3720 20 : VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarApproxTransformer",
3721 : nullptr);
3722 :
3723 20 : ApproxTransformInfo *psInfo =
3724 : static_cast<ApproxTransformInfo *>(hTransformArg);
3725 :
3726 : ApproxTransformInfo *psClonedInfo = static_cast<ApproxTransformInfo *>(
3727 20 : CPLMalloc(sizeof(ApproxTransformInfo)));
3728 :
3729 20 : memcpy(psClonedInfo, psInfo, sizeof(ApproxTransformInfo));
3730 20 : if (psClonedInfo->pBaseCBData)
3731 : {
3732 20 : psClonedInfo->pBaseCBData = GDALCreateSimilarTransformer(
3733 : psInfo->pBaseCBData, dfSrcRatioX, dfSrcRatioY);
3734 20 : if (psClonedInfo->pBaseCBData == nullptr)
3735 : {
3736 0 : CPLFree(psClonedInfo);
3737 0 : return nullptr;
3738 : }
3739 : }
3740 20 : psClonedInfo->bOwnSubtransformer = TRUE;
3741 :
3742 20 : return psClonedInfo;
3743 : }
3744 :
3745 : /************************************************************************/
3746 : /* GDALSerializeApproxTransformer() */
3747 : /************************************************************************/
3748 :
3749 55 : static CPLXMLNode *GDALSerializeApproxTransformer(void *pTransformArg)
3750 :
3751 : {
3752 : CPLXMLNode *psTree;
3753 55 : ApproxTransformInfo *psInfo =
3754 : static_cast<ApproxTransformInfo *>(pTransformArg);
3755 :
3756 55 : psTree = CPLCreateXMLNode(nullptr, CXT_Element, "ApproxTransformer");
3757 :
3758 : /* -------------------------------------------------------------------- */
3759 : /* Attach max error. */
3760 : /* -------------------------------------------------------------------- */
3761 55 : if (psInfo->dfMaxErrorForward == psInfo->dfMaxErrorReverse)
3762 : {
3763 53 : CPLCreateXMLElementAndValue(
3764 : psTree, "MaxError",
3765 106 : CPLString().Printf("%g", psInfo->dfMaxErrorForward));
3766 : }
3767 : else
3768 : {
3769 2 : CPLCreateXMLElementAndValue(
3770 : psTree, "MaxErrorForward",
3771 4 : CPLString().Printf("%g", psInfo->dfMaxErrorForward));
3772 2 : CPLCreateXMLElementAndValue(
3773 : psTree, "MaxErrorReverse",
3774 4 : CPLString().Printf("%g", psInfo->dfMaxErrorReverse));
3775 : }
3776 :
3777 : /* -------------------------------------------------------------------- */
3778 : /* Capture underlying transformer. */
3779 : /* -------------------------------------------------------------------- */
3780 : CPLXMLNode *psTransformerContainer =
3781 55 : CPLCreateXMLNode(psTree, CXT_Element, "BaseTransformer");
3782 :
3783 55 : CPLXMLNode *psTransformer = GDALSerializeTransformer(
3784 : psInfo->pfnBaseTransformer, psInfo->pBaseCBData);
3785 55 : if (psTransformer != nullptr)
3786 55 : CPLAddXMLChild(psTransformerContainer, psTransformer);
3787 :
3788 55 : return psTree;
3789 : }
3790 :
3791 : /************************************************************************/
3792 : /* GDALCreateApproxTransformer() */
3793 : /************************************************************************/
3794 :
3795 : /**
3796 : * Create an approximating transformer.
3797 : *
3798 : * This function creates a context for an approximated transformer. Basically
3799 : * a high precision transformer is supplied as input and internally linear
3800 : * approximations are computed to generate results to within a defined
3801 : * precision.
3802 : *
3803 : * The approximation is actually done at the point where GDALApproxTransform()
3804 : * calls are made, and depend on the assumption that they are roughly linear.
3805 : * The first and last point passed in must be the extreme values and the
3806 : * intermediate values should describe a curve between the end points. The
3807 : * approximator transforms and centers using the approximate transformer, and
3808 : * then compares the true middle transformed value to a linear approximation
3809 : * based on the end points. If the error is within the supplied threshold then
3810 : * the end points are used to linearly approximate all the values otherwise the
3811 : * input points are split into two smaller sets, and the function is recursively
3812 : * called until a sufficiently small set of points is found that the linear
3813 : * approximation is OK, or that all the points are exactly computed.
3814 : *
3815 : * This function is very suitable for approximating transformation results
3816 : * from output pixel/line space to input coordinates for warpers that operate
3817 : * on one input scanline at a time. Care should be taken using it in other
3818 : * circumstances as little internal validation is done in order to keep things
3819 : * fast.
3820 : *
3821 : * @param pfnBaseTransformer the high precision transformer which should be
3822 : * approximated.
3823 : * @param pBaseTransformArg the callback argument for the high precision
3824 : * transformer.
3825 : * @param dfMaxError the maximum cartesian error in the "output" space that
3826 : * is to be accepted in the linear approximation.
3827 : *
3828 : * @return callback pointer suitable for use with GDALApproxTransform(). It
3829 : * should be deallocated with GDALDestroyApproxTransformer().
3830 : */
3831 :
3832 844 : void *GDALCreateApproxTransformer(GDALTransformerFunc pfnBaseTransformer,
3833 : void *pBaseTransformArg, double dfMaxError)
3834 :
3835 : {
3836 844 : return GDALCreateApproxTransformer2(pfnBaseTransformer, pBaseTransformArg,
3837 844 : dfMaxError, dfMaxError);
3838 : }
3839 :
3840 : static void *
3841 989 : GDALCreateApproxTransformer2(GDALTransformerFunc pfnBaseTransformer,
3842 : void *pBaseTransformArg, double dfMaxErrorForward,
3843 : double dfMaxErrorReverse)
3844 :
3845 : {
3846 : ApproxTransformInfo *psATInfo = static_cast<ApproxTransformInfo *>(
3847 989 : CPLMalloc(sizeof(ApproxTransformInfo)));
3848 989 : psATInfo->pfnBaseTransformer = pfnBaseTransformer;
3849 989 : psATInfo->pBaseCBData = pBaseTransformArg;
3850 989 : psATInfo->dfMaxErrorForward = dfMaxErrorForward;
3851 989 : psATInfo->dfMaxErrorReverse = dfMaxErrorReverse;
3852 989 : psATInfo->bOwnSubtransformer = FALSE;
3853 :
3854 989 : memcpy(psATInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
3855 : strlen(GDAL_GTI2_SIGNATURE));
3856 989 : psATInfo->sTI.pszClassName = GDAL_APPROX_TRANSFORMER_CLASS_NAME;
3857 989 : psATInfo->sTI.pfnTransform = GDALApproxTransform;
3858 989 : psATInfo->sTI.pfnCleanup = GDALDestroyApproxTransformer;
3859 989 : psATInfo->sTI.pfnSerialize = GDALSerializeApproxTransformer;
3860 989 : psATInfo->sTI.pfnCreateSimilar = GDALCreateSimilarApproxTransformer;
3861 :
3862 989 : return psATInfo;
3863 : }
3864 :
3865 : /************************************************************************/
3866 : /* GDALApproxTransformerOwnsSubtransformer() */
3867 : /************************************************************************/
3868 :
3869 : /** Set bOwnSubtransformer flag */
3870 986 : void GDALApproxTransformerOwnsSubtransformer(void *pCBData, int bOwnFlag)
3871 :
3872 : {
3873 986 : ApproxTransformInfo *psATInfo = static_cast<ApproxTransformInfo *>(pCBData);
3874 :
3875 986 : psATInfo->bOwnSubtransformer = bOwnFlag;
3876 986 : }
3877 :
3878 : /************************************************************************/
3879 : /* GDALDestroyApproxTransformer() */
3880 : /************************************************************************/
3881 :
3882 : /**
3883 : * Cleanup approximate transformer.
3884 : *
3885 : * Deallocates the resources allocated by GDALCreateApproxTransformer().
3886 : *
3887 : * @param pCBData callback data originally returned by
3888 : * GDALCreateApproxTransformer().
3889 : */
3890 :
3891 1009 : void GDALDestroyApproxTransformer(void *pCBData)
3892 :
3893 : {
3894 1009 : if (pCBData == nullptr)
3895 0 : return;
3896 :
3897 1009 : ApproxTransformInfo *psATInfo = static_cast<ApproxTransformInfo *>(pCBData);
3898 :
3899 1009 : if (psATInfo->bOwnSubtransformer)
3900 1006 : GDALDestroyTransformer(psATInfo->pBaseCBData);
3901 :
3902 1009 : CPLFree(pCBData);
3903 : }
3904 :
3905 : /************************************************************************/
3906 : /* GDALRefreshApproxTransformer() */
3907 : /************************************************************************/
3908 :
3909 44 : void GDALRefreshApproxTransformer(void *hTransformArg)
3910 : {
3911 44 : ApproxTransformInfo *psInfo =
3912 : static_cast<ApproxTransformInfo *>(hTransformArg);
3913 :
3914 44 : if (GDALIsTransformer(psInfo->pBaseCBData,
3915 : GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
3916 : {
3917 44 : GDALRefreshGenImgProjTransformer(psInfo->pBaseCBData);
3918 : }
3919 44 : }
3920 :
3921 : /************************************************************************/
3922 : /* GDALApproxTransformInternal() */
3923 : /************************************************************************/
3924 :
3925 1104100 : static int GDALApproxTransformInternal(void *pCBData, int bDstToSrc,
3926 : int nPoints, double *x, double *y,
3927 : double *z, int *panSuccess,
3928 : // SME = Start, Middle, End.
3929 : const double xSMETransformed[3],
3930 : const double ySMETransformed[3],
3931 : const double zSMETransformed[3])
3932 : {
3933 1104100 : ApproxTransformInfo *psATInfo = static_cast<ApproxTransformInfo *>(pCBData);
3934 1104100 : const int nMiddle = (nPoints - 1) / 2;
3935 :
3936 : #ifdef notdef_sanify_check
3937 : {
3938 : double x2[3] = {x[0], x[nMiddle], x[nPoints - 1]};
3939 : double y2[3] = {y[0], y[nMiddle], y[nPoints - 1]};
3940 : double z2[3] = {z[0], z[nMiddle], z[nPoints - 1]};
3941 : int anSuccess2[3] = {};
3942 :
3943 : const int bSuccess = psATInfo->pfnBaseTransformer(
3944 : psATInfo->pBaseCBData, bDstToSrc, 3, x2, y2, z2, anSuccess2);
3945 : CPLAssert(bSuccess);
3946 : CPLAssert(anSuccess2[0]);
3947 : CPLAssert(anSuccess2[1]);
3948 : CPLAssert(anSuccess2[2]);
3949 : CPLAssert(x2[0] == xSMETransformed[0]);
3950 : CPLAssert(y2[0] == ySMETransformed[0]);
3951 : CPLAssert(z2[0] == zSMETransformed[0]);
3952 : CPLAssert(x2[1] == xSMETransformed[1]);
3953 : CPLAssert(y2[1] == ySMETransformed[1]);
3954 : CPLAssert(z2[1] == zSMETransformed[1]);
3955 : CPLAssert(x2[2] == xSMETransformed[2]);
3956 : CPLAssert(y2[2] == ySMETransformed[2]);
3957 : CPLAssert(z2[2] == zSMETransformed[2]);
3958 : }
3959 : #endif
3960 :
3961 : #ifdef DEBUG_APPROX_TRANSFORMER
3962 : fprintf(stderr, "start (%.3f,%.3f) -> (%.3f,%.3f)\n", /*ok*/
3963 : x[0], y[0], xSMETransformed[0], ySMETransformed[0]);
3964 : fprintf(stderr, "middle (%.3f,%.3f) -> (%.3f,%.3f)\n", /*ok*/
3965 : x[nMiddle], y[nMiddle], xSMETransformed[1], ySMETransformed[1]);
3966 : fprintf(stderr, "end (%.3f,%.3f) -> (%.3f,%.3f)\n", /*ok*/
3967 : x[nPoints - 1], y[nPoints - 1], xSMETransformed[2],
3968 : ySMETransformed[2]);
3969 : #endif
3970 :
3971 : /* -------------------------------------------------------------------- */
3972 : /* Is the error at the middle acceptable relative to an */
3973 : /* interpolation of the middle position? */
3974 : /* -------------------------------------------------------------------- */
3975 1104100 : const double dfDeltaX =
3976 1104100 : (xSMETransformed[2] - xSMETransformed[0]) / (x[nPoints - 1] - x[0]);
3977 1104100 : const double dfDeltaY =
3978 1104100 : (ySMETransformed[2] - ySMETransformed[0]) / (x[nPoints - 1] - x[0]);
3979 1104100 : const double dfDeltaZ =
3980 1104100 : (zSMETransformed[2] - zSMETransformed[0]) / (x[nPoints - 1] - x[0]);
3981 :
3982 1104100 : const double dfError =
3983 1104100 : fabs((xSMETransformed[0] + dfDeltaX * (x[nMiddle] - x[0])) -
3984 1104100 : xSMETransformed[1]) +
3985 1104100 : fabs((ySMETransformed[0] + dfDeltaY * (x[nMiddle] - x[0])) -
3986 1104100 : ySMETransformed[1]);
3987 :
3988 1104100 : const double dfMaxError =
3989 1104100 : (bDstToSrc) ? psATInfo->dfMaxErrorReverse : psATInfo->dfMaxErrorForward;
3990 1104100 : if (dfError > dfMaxError)
3991 : {
3992 : #if DEBUG_VERBOSE
3993 : CPLDebug("GDAL",
3994 : "ApproxTransformer - "
3995 : "error %g over threshold %g, subdivide %d points.",
3996 : dfError, dfMaxError, nPoints);
3997 : #endif
3998 :
3999 587063 : double xMiddle[3] = {x[(nMiddle - 1) / 2], x[nMiddle - 1],
4000 587063 : x[nMiddle + (nPoints - nMiddle - 1) / 2]};
4001 587063 : double yMiddle[3] = {y[(nMiddle - 1) / 2], y[nMiddle - 1],
4002 587063 : y[nMiddle + (nPoints - nMiddle - 1) / 2]};
4003 587063 : double zMiddle[3] = {z[(nMiddle - 1) / 2], z[nMiddle - 1],
4004 587063 : z[nMiddle + (nPoints - nMiddle - 1) / 2]};
4005 :
4006 587063 : const bool bUseBaseTransformForHalf1 =
4007 459580 : nMiddle <= 5 || y[0] != y[nMiddle - 1] ||
4008 1506220 : y[0] != y[(nMiddle - 1) / 2] || x[0] == x[nMiddle - 1] ||
4009 459580 : x[0] == x[(nMiddle - 1) / 2];
4010 587063 : const bool bUseBaseTransformForHalf2 =
4011 473168 : nPoints - nMiddle <= 5 || y[nMiddle] != y[nPoints - 1] ||
4012 473168 : y[nMiddle] != y[nMiddle + (nPoints - nMiddle - 1) / 2] ||
4013 1533400 : x[nMiddle] == x[nPoints - 1] ||
4014 473168 : x[nMiddle] == x[nMiddle + (nPoints - nMiddle - 1) / 2];
4015 :
4016 587063 : int anSuccess2[3] = {};
4017 587063 : int bSuccess = FALSE;
4018 587063 : if (!bUseBaseTransformForHalf1 && !bUseBaseTransformForHalf2)
4019 459580 : bSuccess = psATInfo->pfnBaseTransformer(
4020 : psATInfo->pBaseCBData, bDstToSrc, 3, xMiddle, yMiddle, zMiddle,
4021 : anSuccess2);
4022 127483 : else if (!bUseBaseTransformForHalf1)
4023 : {
4024 0 : bSuccess = psATInfo->pfnBaseTransformer(
4025 : psATInfo->pBaseCBData, bDstToSrc, 2, xMiddle, yMiddle, zMiddle,
4026 : anSuccess2);
4027 0 : anSuccess2[2] = TRUE;
4028 : }
4029 127483 : else if (!bUseBaseTransformForHalf2)
4030 : {
4031 13588 : bSuccess = psATInfo->pfnBaseTransformer(
4032 : psATInfo->pBaseCBData, bDstToSrc, 1, xMiddle + 2, yMiddle + 2,
4033 : zMiddle + 2, anSuccess2 + 2);
4034 13588 : anSuccess2[0] = TRUE;
4035 13588 : anSuccess2[1] = TRUE;
4036 : }
4037 :
4038 587063 : if (!bSuccess || !anSuccess2[0] || !anSuccess2[1] || !anSuccess2[2])
4039 : {
4040 113911 : bSuccess = psATInfo->pfnBaseTransformer(
4041 : psATInfo->pBaseCBData, bDstToSrc, nMiddle - 1, x + 1, y + 1,
4042 : z + 1, panSuccess + 1);
4043 227822 : bSuccess &= psATInfo->pfnBaseTransformer(
4044 113911 : psATInfo->pBaseCBData, bDstToSrc, nPoints - nMiddle - 2,
4045 113911 : x + nMiddle + 1, y + nMiddle + 1, z + nMiddle + 1,
4046 113911 : panSuccess + nMiddle + 1);
4047 :
4048 113911 : x[0] = xSMETransformed[0];
4049 113911 : y[0] = ySMETransformed[0];
4050 113911 : z[0] = zSMETransformed[0];
4051 113911 : panSuccess[0] = TRUE;
4052 113911 : x[nMiddle] = xSMETransformed[1];
4053 113911 : y[nMiddle] = ySMETransformed[1];
4054 113911 : z[nMiddle] = zSMETransformed[1];
4055 113911 : panSuccess[nMiddle] = TRUE;
4056 113911 : x[nPoints - 1] = xSMETransformed[2];
4057 113911 : y[nPoints - 1] = ySMETransformed[2];
4058 113911 : z[nPoints - 1] = zSMETransformed[2];
4059 113911 : panSuccess[nPoints - 1] = TRUE;
4060 113911 : return bSuccess;
4061 : }
4062 :
4063 473152 : double x2[3] = {};
4064 473152 : double y2[3] = {};
4065 473152 : double z2[3] = {};
4066 473152 : if (!bUseBaseTransformForHalf1)
4067 : {
4068 459564 : x2[0] = xSMETransformed[0];
4069 459564 : y2[0] = ySMETransformed[0];
4070 459564 : z2[0] = zSMETransformed[0];
4071 459564 : x2[1] = xMiddle[0];
4072 459564 : y2[1] = yMiddle[0];
4073 459564 : z2[1] = zMiddle[0];
4074 459564 : x2[2] = xMiddle[1];
4075 459564 : y2[2] = yMiddle[1];
4076 459564 : z2[2] = zMiddle[1];
4077 :
4078 459564 : bSuccess = GDALApproxTransformInternal(
4079 : psATInfo, bDstToSrc, nMiddle, x, y, z, panSuccess, x2, y2, z2);
4080 : }
4081 : else
4082 : {
4083 13588 : bSuccess = psATInfo->pfnBaseTransformer(
4084 : psATInfo->pBaseCBData, bDstToSrc, nMiddle - 1, x + 1, y + 1,
4085 : z + 1, panSuccess + 1);
4086 13588 : x[0] = xSMETransformed[0];
4087 13588 : y[0] = ySMETransformed[0];
4088 13588 : z[0] = zSMETransformed[0];
4089 13588 : panSuccess[0] = TRUE;
4090 : }
4091 :
4092 473152 : if (!bSuccess)
4093 0 : return FALSE;
4094 :
4095 473152 : if (!bUseBaseTransformForHalf2)
4096 : {
4097 473152 : x2[0] = xSMETransformed[1];
4098 473152 : y2[0] = ySMETransformed[1];
4099 473152 : z2[0] = zSMETransformed[1];
4100 473152 : x2[1] = xMiddle[2];
4101 473152 : y2[1] = yMiddle[2];
4102 473152 : z2[1] = zMiddle[2];
4103 473152 : x2[2] = xSMETransformed[2];
4104 473152 : y2[2] = ySMETransformed[2];
4105 473152 : z2[2] = zSMETransformed[2];
4106 :
4107 473152 : bSuccess = GDALApproxTransformInternal(
4108 473152 : psATInfo, bDstToSrc, nPoints - nMiddle, x + nMiddle,
4109 473152 : y + nMiddle, z + nMiddle, panSuccess + nMiddle, x2, y2, z2);
4110 : }
4111 : else
4112 : {
4113 0 : bSuccess = psATInfo->pfnBaseTransformer(
4114 0 : psATInfo->pBaseCBData, bDstToSrc, nPoints - nMiddle - 2,
4115 0 : x + nMiddle + 1, y + nMiddle + 1, z + nMiddle + 1,
4116 0 : panSuccess + nMiddle + 1);
4117 :
4118 0 : x[nMiddle] = xSMETransformed[1];
4119 0 : y[nMiddle] = ySMETransformed[1];
4120 0 : z[nMiddle] = zSMETransformed[1];
4121 0 : panSuccess[nMiddle] = TRUE;
4122 0 : x[nPoints - 1] = xSMETransformed[2];
4123 0 : y[nPoints - 1] = ySMETransformed[2];
4124 0 : z[nPoints - 1] = zSMETransformed[2];
4125 0 : panSuccess[nPoints - 1] = TRUE;
4126 : }
4127 :
4128 473152 : if (!bSuccess)
4129 0 : return FALSE;
4130 :
4131 473152 : return TRUE;
4132 : }
4133 :
4134 : /* -------------------------------------------------------------------- */
4135 : /* Error is OK since this is just used to compute output bounds */
4136 : /* of newly created file for gdalwarper. So just use affine */
4137 : /* approximation of the reverse transform. Eventually we */
4138 : /* should implement iterative searching to find a result within */
4139 : /* our error threshold. */
4140 : /* NOTE: the above comment is not true: gdalwarp uses approximator */
4141 : /* also to compute the source pixel of each target pixel. */
4142 : /* -------------------------------------------------------------------- */
4143 95978900 : for (int i = nPoints - 1; i >= 0; i--)
4144 : {
4145 : #ifdef check_error
4146 : double xtemp = x[i];
4147 : double ytemp = y[i];
4148 : double ztemp = z[i];
4149 : double x_ori = xtemp;
4150 : double y_ori = ytemp;
4151 : int btemp = FALSE;
4152 : psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, bDstToSrc, 1,
4153 : &xtemp, &ytemp, &ztemp, &btemp);
4154 : #endif
4155 95461900 : const double dfDist = (x[i] - x[0]);
4156 95461900 : x[i] = xSMETransformed[0] + dfDeltaX * dfDist;
4157 95461900 : y[i] = ySMETransformed[0] + dfDeltaY * dfDist;
4158 95461900 : z[i] = zSMETransformed[0] + dfDeltaZ * dfDist;
4159 : #ifdef check_error
4160 : const double dfError2 = fabs(x[i] - xtemp) + fabs(y[i] - ytemp);
4161 : if (dfError2 > 4 /*10 * dfMaxError*/)
4162 : {
4163 : /*ok*/ printf("Error = %f on (%f, %f)\n", dfError2, x_ori, y_ori);
4164 : }
4165 : #endif
4166 95461900 : panSuccess[i] = TRUE;
4167 : }
4168 :
4169 517041 : return TRUE;
4170 : }
4171 :
4172 : /************************************************************************/
4173 : /* GDALApproxTransform() */
4174 : /************************************************************************/
4175 :
4176 : /**
4177 : * Perform approximate transformation.
4178 : *
4179 : * Actually performs the approximate transformation described in
4180 : * GDALCreateApproxTransformer(). This function matches the
4181 : * GDALTransformerFunc() signature. Details of the arguments are described
4182 : * there.
4183 : */
4184 :
4185 508363 : int GDALApproxTransform(void *pCBData, int bDstToSrc, int nPoints, double *x,
4186 : double *y, double *z, int *panSuccess)
4187 :
4188 : {
4189 508363 : ApproxTransformInfo *psATInfo = static_cast<ApproxTransformInfo *>(pCBData);
4190 508363 : double x2[3] = {};
4191 508363 : double y2[3] = {};
4192 508363 : double z2[3] = {};
4193 508363 : int anSuccess2[3] = {};
4194 : int bSuccess;
4195 :
4196 508363 : const int nMiddle = (nPoints - 1) / 2;
4197 :
4198 : /* -------------------------------------------------------------------- */
4199 : /* Bail if our preconditions are not met, or if error is not */
4200 : /* acceptable. */
4201 : /* -------------------------------------------------------------------- */
4202 508363 : int bRet = FALSE;
4203 508363 : if (y[0] != y[nPoints - 1] || y[0] != y[nMiddle] ||
4204 500258 : x[0] == x[nPoints - 1] || x[0] == x[nMiddle] ||
4205 176842 : (psATInfo->dfMaxErrorForward == 0.0 &&
4206 176842 : psATInfo->dfMaxErrorReverse == 0.0) ||
4207 : nPoints <= 5)
4208 : {
4209 331852 : bRet = psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, bDstToSrc,
4210 : nPoints, x, y, z, panSuccess);
4211 331851 : goto end;
4212 : }
4213 :
4214 : /* -------------------------------------------------------------------- */
4215 : /* Transform first, last and middle point. */
4216 : /* -------------------------------------------------------------------- */
4217 176511 : x2[0] = x[0];
4218 176511 : y2[0] = y[0];
4219 176511 : z2[0] = z[0];
4220 176511 : x2[1] = x[nMiddle];
4221 176511 : y2[1] = y[nMiddle];
4222 176511 : z2[1] = z[nMiddle];
4223 176511 : x2[2] = x[nPoints - 1];
4224 176511 : y2[2] = y[nPoints - 1];
4225 176511 : z2[2] = z[nPoints - 1];
4226 :
4227 176511 : bSuccess = psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, bDstToSrc, 3,
4228 : x2, y2, z2, anSuccess2);
4229 176511 : if (!bSuccess || !anSuccess2[0] || !anSuccess2[1] || !anSuccess2[2])
4230 : {
4231 5123 : bRet = psATInfo->pfnBaseTransformer(psATInfo->pBaseCBData, bDstToSrc,
4232 : nPoints, x, y, z, panSuccess);
4233 5123 : goto end;
4234 : }
4235 :
4236 171388 : bRet = GDALApproxTransformInternal(pCBData, bDstToSrc, nPoints, x, y, z,
4237 : panSuccess, x2, y2, z2);
4238 :
4239 508362 : end:
4240 : #ifdef DEBUG_APPROX_TRANSFORMER
4241 : for (int i = 0; i < nPoints; i++)
4242 : fprintf(stderr, "[%d] (%.10f,%.10f) %d\n", /*ok*/
4243 : i, x[i], y[i], panSuccess[i]);
4244 : #endif
4245 :
4246 508362 : return bRet;
4247 : }
4248 :
4249 : /************************************************************************/
4250 : /* GDALDeserializeApproxTransformer() */
4251 : /************************************************************************/
4252 :
4253 143 : static void *GDALDeserializeApproxTransformer(CPLXMLNode *psTree)
4254 :
4255 : {
4256 143 : double dfMaxErrorForward = 0.25;
4257 143 : double dfMaxErrorReverse = 0.25;
4258 143 : const char *pszMaxError = CPLGetXMLValue(psTree, "MaxError", nullptr);
4259 143 : if (pszMaxError != nullptr)
4260 : {
4261 141 : dfMaxErrorForward = CPLAtof(pszMaxError);
4262 141 : dfMaxErrorReverse = dfMaxErrorForward;
4263 : }
4264 : const char *pszMaxErrorForward =
4265 143 : CPLGetXMLValue(psTree, "MaxErrorForward", nullptr);
4266 143 : if (pszMaxErrorForward != nullptr)
4267 : {
4268 2 : dfMaxErrorForward = CPLAtof(pszMaxErrorForward);
4269 : }
4270 : const char *pszMaxErrorReverse =
4271 143 : CPLGetXMLValue(psTree, "MaxErrorReverse", nullptr);
4272 143 : if (pszMaxErrorReverse != nullptr)
4273 : {
4274 2 : dfMaxErrorReverse = CPLAtof(pszMaxErrorReverse);
4275 : }
4276 :
4277 143 : GDALTransformerFunc pfnBaseTransform = nullptr;
4278 143 : void *pBaseCBData = nullptr;
4279 :
4280 143 : CPLXMLNode *psContainer = CPLGetXMLNode(psTree, "BaseTransformer");
4281 :
4282 143 : if (psContainer != nullptr && psContainer->psChild != nullptr)
4283 : {
4284 143 : GDALDeserializeTransformer(psContainer->psChild, &pfnBaseTransform,
4285 : &pBaseCBData);
4286 : }
4287 :
4288 143 : if (pfnBaseTransform == nullptr)
4289 : {
4290 0 : CPLError(CE_Failure, CPLE_AppDefined,
4291 : "Cannot get base transform for approx transformer.");
4292 0 : return nullptr;
4293 : }
4294 :
4295 143 : void *pApproxCBData = GDALCreateApproxTransformer2(
4296 : pfnBaseTransform, pBaseCBData, dfMaxErrorForward, dfMaxErrorReverse);
4297 143 : GDALApproxTransformerOwnsSubtransformer(pApproxCBData, TRUE);
4298 :
4299 143 : return pApproxCBData;
4300 : }
4301 :
4302 : /************************************************************************/
4303 : /* GDALTransformLonLatToDestApproxTransformer() */
4304 : /************************************************************************/
4305 :
4306 1986 : int GDALTransformLonLatToDestApproxTransformer(void *hTransformArg,
4307 : double *pdfX, double *pdfY)
4308 : {
4309 1986 : ApproxTransformInfo *psInfo =
4310 : static_cast<ApproxTransformInfo *>(hTransformArg);
4311 :
4312 1986 : if (GDALIsTransformer(psInfo->pBaseCBData,
4313 : GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
4314 : {
4315 1986 : return GDALTransformLonLatToDestGenImgProjTransformer(
4316 1986 : psInfo->pBaseCBData, pdfX, pdfY);
4317 : }
4318 0 : return false;
4319 : }
4320 :
4321 : /************************************************************************/
4322 : /* GDALApplyGeoTransform() */
4323 : /************************************************************************/
4324 :
4325 : /**
4326 : * Apply GeoTransform to x/y coordinate.
4327 : *
4328 : * Applies the following computation, converting a (pixel, line) coordinate
4329 : * into a georeferenced (geo_x, geo_y) location.
4330 : * \code{.c}
4331 : * *pdfGeoX = padfGeoTransform[0] + dfPixel * padfGeoTransform[1]
4332 : * + dfLine * padfGeoTransform[2];
4333 : * *pdfGeoY = padfGeoTransform[3] + dfPixel * padfGeoTransform[4]
4334 : * + dfLine * padfGeoTransform[5];
4335 : * \endcode
4336 : *
4337 : * @param padfGeoTransform Six coefficient GeoTransform to apply.
4338 : * @param dfPixel Input pixel position.
4339 : * @param dfLine Input line position.
4340 : * @param pdfGeoX output location where geo_x (easting/longitude)
4341 : * location is placed.
4342 : * @param pdfGeoY output location where geo_y (northing/latitude)
4343 : * location is placed.
4344 : */
4345 :
4346 337795 : void CPL_STDCALL GDALApplyGeoTransform(const double *padfGeoTransform,
4347 : double dfPixel, double dfLine,
4348 : double *pdfGeoX, double *pdfGeoY)
4349 : {
4350 337795 : *pdfGeoX = padfGeoTransform[0] + dfPixel * padfGeoTransform[1] +
4351 337795 : dfLine * padfGeoTransform[2];
4352 337795 : *pdfGeoY = padfGeoTransform[3] + dfPixel * padfGeoTransform[4] +
4353 337795 : dfLine * padfGeoTransform[5];
4354 337795 : }
4355 :
4356 : /************************************************************************/
4357 : /* GDALInvGeoTransform() */
4358 : /************************************************************************/
4359 :
4360 : /**
4361 : * Invert Geotransform.
4362 : *
4363 : * This function will invert a standard 3x2 set of GeoTransform coefficients.
4364 : * This converts the equation from being pixel to geo to being geo to pixel.
4365 : *
4366 : * @param gt_in Input geotransform (six doubles - unaltered).
4367 : * @param gt_out Output geotransform (six doubles - updated).
4368 : *
4369 : * @return TRUE on success or FALSE if the equation is uninvertable.
4370 : */
4371 :
4372 2759 : int CPL_STDCALL GDALInvGeoTransform(const double *gt_in, double *gt_out)
4373 :
4374 : {
4375 : // Special case - no rotation - to avoid computing determinate
4376 : // and potential precision issues.
4377 2759 : if (gt_in[2] == 0.0 && gt_in[4] == 0.0 && gt_in[1] != 0.0 &&
4378 2705 : gt_in[5] != 0.0)
4379 : {
4380 : /*X = gt_in[0] + x * gt_in[1]
4381 : Y = gt_in[3] + y * gt_in[5]
4382 : -->
4383 : x = -gt_in[0] / gt_in[1] + (1 / gt_in[1]) * X
4384 : y = -gt_in[3] / gt_in[5] + (1 / gt_in[5]) * Y
4385 : */
4386 2705 : gt_out[0] = -gt_in[0] / gt_in[1];
4387 2705 : gt_out[1] = 1.0 / gt_in[1];
4388 2705 : gt_out[2] = 0.0;
4389 2705 : gt_out[3] = -gt_in[3] / gt_in[5];
4390 2705 : gt_out[4] = 0.0;
4391 2705 : gt_out[5] = 1.0 / gt_in[5];
4392 2705 : return 1;
4393 : }
4394 :
4395 : // Assume a 3rd row that is [1 0 0].
4396 :
4397 : // Compute determinate.
4398 :
4399 54 : const double det = gt_in[1] * gt_in[5] - gt_in[2] * gt_in[4];
4400 108 : const double magnitude = std::max(std::max(fabs(gt_in[1]), fabs(gt_in[2])),
4401 54 : std::max(fabs(gt_in[4]), fabs(gt_in[5])));
4402 :
4403 54 : if (fabs(det) <= 1e-10 * magnitude * magnitude)
4404 5 : return 0;
4405 :
4406 49 : const double inv_det = 1.0 / det;
4407 :
4408 : // Compute adjoint, and divide by determinate.
4409 :
4410 49 : gt_out[1] = gt_in[5] * inv_det;
4411 49 : gt_out[4] = -gt_in[4] * inv_det;
4412 :
4413 49 : gt_out[2] = -gt_in[2] * inv_det;
4414 49 : gt_out[5] = gt_in[1] * inv_det;
4415 :
4416 49 : gt_out[0] = (gt_in[2] * gt_in[3] - gt_in[0] * gt_in[5]) * inv_det;
4417 49 : gt_out[3] = (-gt_in[1] * gt_in[3] + gt_in[0] * gt_in[4]) * inv_det;
4418 :
4419 49 : return 1;
4420 : }
4421 :
4422 : /************************************************************************/
4423 : /* GDALSerializeTransformer() */
4424 : /************************************************************************/
4425 :
4426 264 : CPLXMLNode *GDALSerializeTransformer(GDALTransformerFunc /* pfnFunc */,
4427 : void *pTransformArg)
4428 : {
4429 264 : VALIDATE_POINTER1(pTransformArg, "GDALSerializeTransformer", nullptr);
4430 :
4431 264 : GDALTransformerInfo *psInfo =
4432 : static_cast<GDALTransformerInfo *>(pTransformArg);
4433 :
4434 264 : if (psInfo == nullptr || memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
4435 : strlen(GDAL_GTI2_SIGNATURE)) != 0)
4436 : {
4437 0 : CPLError(CE_Failure, CPLE_AppDefined,
4438 : "Attempt to serialize non-GTI2 transformer.");
4439 0 : return nullptr;
4440 : }
4441 264 : else if (psInfo->pfnSerialize == nullptr)
4442 : {
4443 0 : CPLError(CE_Failure, CPLE_AppDefined,
4444 : "No serialization function available for this transformer.");
4445 0 : return nullptr;
4446 : }
4447 :
4448 264 : return psInfo->pfnSerialize(pTransformArg);
4449 : }
4450 :
4451 : /************************************************************************/
4452 : /* GDALRegisterTransformDeserializer() */
4453 : /************************************************************************/
4454 :
4455 : static CPLList *psListDeserializer = nullptr;
4456 : static CPLMutex *hDeserializerMutex = nullptr;
4457 :
4458 : typedef struct
4459 : {
4460 : char *pszTransformName;
4461 : GDALTransformerFunc pfnTransformerFunc;
4462 : GDALTransformDeserializeFunc pfnDeserializeFunc;
4463 : } TransformDeserializerInfo;
4464 :
4465 0 : void *GDALRegisterTransformDeserializer(
4466 : const char *pszTransformName, GDALTransformerFunc pfnTransformerFunc,
4467 : GDALTransformDeserializeFunc pfnDeserializeFunc)
4468 : {
4469 : TransformDeserializerInfo *psInfo =
4470 : static_cast<TransformDeserializerInfo *>(
4471 0 : CPLMalloc(sizeof(TransformDeserializerInfo)));
4472 0 : psInfo->pszTransformName = CPLStrdup(pszTransformName);
4473 0 : psInfo->pfnTransformerFunc = pfnTransformerFunc;
4474 0 : psInfo->pfnDeserializeFunc = pfnDeserializeFunc;
4475 :
4476 0 : CPLMutexHolderD(&hDeserializerMutex);
4477 0 : psListDeserializer = CPLListInsert(psListDeserializer, psInfo, 0);
4478 :
4479 0 : return psInfo;
4480 : }
4481 :
4482 : /************************************************************************/
4483 : /* GDALUnregisterTransformDeserializer() */
4484 : /************************************************************************/
4485 :
4486 0 : void GDALUnregisterTransformDeserializer(void *pData)
4487 : {
4488 0 : CPLMutexHolderD(&hDeserializerMutex);
4489 0 : CPLList *psList = psListDeserializer;
4490 0 : CPLList *psLast = nullptr;
4491 0 : while (psList)
4492 : {
4493 0 : if (psList->pData == pData)
4494 : {
4495 0 : TransformDeserializerInfo *psInfo =
4496 : static_cast<TransformDeserializerInfo *>(pData);
4497 0 : CPLFree(psInfo->pszTransformName);
4498 0 : CPLFree(pData);
4499 0 : if (psLast)
4500 0 : psLast->psNext = psList->psNext;
4501 : else
4502 0 : psListDeserializer = nullptr;
4503 0 : CPLFree(psList);
4504 0 : break;
4505 : }
4506 0 : psLast = psList;
4507 0 : psList = psList->psNext;
4508 : }
4509 0 : }
4510 :
4511 : /************************************************************************/
4512 : /* GDALUnregisterTransformDeserializer() */
4513 : /************************************************************************/
4514 :
4515 852 : void GDALCleanupTransformDeserializerMutex()
4516 : {
4517 852 : if (hDeserializerMutex != nullptr)
4518 : {
4519 0 : CPLDestroyMutex(hDeserializerMutex);
4520 0 : hDeserializerMutex = nullptr;
4521 : }
4522 852 : }
4523 :
4524 : /************************************************************************/
4525 : /* GDALDeserializeTransformer() */
4526 : /************************************************************************/
4527 :
4528 528 : CPLErr GDALDeserializeTransformer(CPLXMLNode *psTree,
4529 : GDALTransformerFunc *ppfnFunc,
4530 : void **ppTransformArg)
4531 :
4532 : {
4533 528 : *ppfnFunc = nullptr;
4534 528 : *ppTransformArg = nullptr;
4535 :
4536 528 : CPLErrorReset();
4537 :
4538 528 : if (psTree == nullptr || psTree->eType != CXT_Element)
4539 0 : CPLError(CE_Failure, CPLE_AppDefined,
4540 : "Malformed element in GDALDeserializeTransformer");
4541 528 : else if (EQUAL(psTree->pszValue, "GenImgProjTransformer"))
4542 : {
4543 191 : *ppfnFunc = GDALGenImgProjTransform;
4544 191 : *ppTransformArg = GDALDeserializeGenImgProjTransformer(psTree);
4545 : }
4546 337 : else if (EQUAL(psTree->pszValue, "ReprojectionTransformer"))
4547 : {
4548 178 : *ppfnFunc = GDALReprojectionTransform;
4549 178 : *ppTransformArg = GDALDeserializeReprojectionTransformer(psTree);
4550 : }
4551 159 : else if (EQUAL(psTree->pszValue, "GCPTransformer"))
4552 : {
4553 12 : *ppfnFunc = GDALGCPTransform;
4554 12 : *ppTransformArg = GDALDeserializeGCPTransformer(psTree);
4555 : }
4556 147 : else if (EQUAL(psTree->pszValue, "TPSTransformer"))
4557 : {
4558 3 : *ppfnFunc = GDALTPSTransform;
4559 3 : *ppTransformArg = GDALDeserializeTPSTransformer(psTree);
4560 : }
4561 144 : else if (EQUAL(psTree->pszValue, "GeoLocTransformer"))
4562 : {
4563 1 : *ppfnFunc = GDALGeoLocTransform;
4564 1 : *ppTransformArg = GDALDeserializeGeoLocTransformer(psTree);
4565 : }
4566 143 : else if (EQUAL(psTree->pszValue, "RPCTransformer"))
4567 : {
4568 0 : *ppfnFunc = GDALRPCTransform;
4569 0 : *ppTransformArg = GDALDeserializeRPCTransformer(psTree);
4570 : }
4571 143 : else if (EQUAL(psTree->pszValue, "ApproxTransformer"))
4572 : {
4573 143 : *ppfnFunc = GDALApproxTransform;
4574 143 : *ppTransformArg = GDALDeserializeApproxTransformer(psTree);
4575 : }
4576 : else
4577 : {
4578 0 : GDALTransformDeserializeFunc pfnDeserializeFunc = nullptr;
4579 : {
4580 0 : CPLMutexHolderD(&hDeserializerMutex);
4581 0 : CPLList *psList = psListDeserializer;
4582 0 : while (psList)
4583 : {
4584 0 : TransformDeserializerInfo *psInfo =
4585 : static_cast<TransformDeserializerInfo *>(psList->pData);
4586 0 : if (strcmp(psInfo->pszTransformName, psTree->pszValue) == 0)
4587 : {
4588 0 : *ppfnFunc = psInfo->pfnTransformerFunc;
4589 0 : pfnDeserializeFunc = psInfo->pfnDeserializeFunc;
4590 0 : break;
4591 : }
4592 0 : psList = psList->psNext;
4593 : }
4594 : }
4595 :
4596 0 : if (pfnDeserializeFunc != nullptr)
4597 : {
4598 0 : *ppTransformArg = pfnDeserializeFunc(psTree);
4599 : }
4600 : else
4601 : {
4602 0 : CPLError(CE_Failure, CPLE_AppDefined,
4603 : "Unrecognized element '%s' GDALDeserializeTransformer",
4604 : psTree->pszValue);
4605 : }
4606 : }
4607 :
4608 528 : return CPLGetLastErrorType();
4609 : }
4610 :
4611 : /************************************************************************/
4612 : /* GDALDestroyTransformer() */
4613 : /************************************************************************/
4614 :
4615 3345 : void GDALDestroyTransformer(void *pTransformArg)
4616 :
4617 : {
4618 3345 : if (pTransformArg == nullptr)
4619 9 : return;
4620 :
4621 3336 : GDALTransformerInfo *psInfo =
4622 : static_cast<GDALTransformerInfo *>(pTransformArg);
4623 :
4624 3336 : if (memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
4625 : strlen(GDAL_GTI2_SIGNATURE)) != 0)
4626 : {
4627 0 : CPLError(CE_Failure, CPLE_AppDefined,
4628 : "Attempt to destroy non-GTI2 transformer.");
4629 0 : return;
4630 : }
4631 :
4632 3336 : psInfo->pfnCleanup(pTransformArg);
4633 : }
4634 :
4635 : /************************************************************************/
4636 : /* GDALUseTransformer() */
4637 : /************************************************************************/
4638 :
4639 8680 : int GDALUseTransformer(void *pTransformArg, int bDstToSrc, int nPointCount,
4640 : double *x, double *y, double *z, int *panSuccess)
4641 : {
4642 8680 : GDALTransformerInfo *psInfo =
4643 : static_cast<GDALTransformerInfo *>(pTransformArg);
4644 :
4645 8680 : if (psInfo == nullptr || memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
4646 : strlen(GDAL_GTI2_SIGNATURE)) != 0)
4647 : {
4648 0 : CPLError(CE_Failure, CPLE_AppDefined,
4649 : "Attempt to use non-GTI2 transformer.");
4650 0 : return FALSE;
4651 : }
4652 :
4653 8680 : return psInfo->pfnTransform(pTransformArg, bDstToSrc, nPointCount, x, y, z,
4654 8680 : panSuccess);
4655 : }
4656 :
4657 : /************************************************************************/
4658 : /* GDALCloneTransformer() */
4659 : /************************************************************************/
4660 :
4661 20 : void *GDALCloneTransformer(void *pTransformArg)
4662 : {
4663 20 : GDALTransformerInfo *psInfo =
4664 : static_cast<GDALTransformerInfo *>(pTransformArg);
4665 :
4666 20 : if (psInfo == nullptr || memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
4667 : strlen(GDAL_GTI2_SIGNATURE)) != 0)
4668 : {
4669 0 : CPLError(CE_Failure, CPLE_AppDefined,
4670 : "Attempt to clone non-GTI2 transformer.");
4671 0 : return nullptr;
4672 : }
4673 :
4674 20 : if (psInfo->pfnCreateSimilar != nullptr)
4675 : {
4676 10 : return psInfo->pfnCreateSimilar(psInfo, 1.0, 1.0);
4677 : }
4678 :
4679 10 : if (psInfo->pfnSerialize == nullptr)
4680 : {
4681 0 : CPLError(CE_Failure, CPLE_AppDefined,
4682 : "No serialization function available for this transformer.");
4683 0 : return nullptr;
4684 : }
4685 :
4686 10 : CPLXMLNode *pSerialized = psInfo->pfnSerialize(pTransformArg);
4687 10 : if (pSerialized == nullptr)
4688 0 : return nullptr;
4689 10 : GDALTransformerFunc pfnTransformer = nullptr;
4690 10 : void *pClonedTransformArg = nullptr;
4691 10 : if (GDALDeserializeTransformer(pSerialized, &pfnTransformer,
4692 10 : &pClonedTransformArg) != CE_None)
4693 : {
4694 0 : CPLDestroyXMLNode(pSerialized);
4695 0 : CPLFree(pClonedTransformArg);
4696 0 : return nullptr;
4697 : }
4698 :
4699 10 : CPLDestroyXMLNode(pSerialized);
4700 10 : return pClonedTransformArg;
4701 : }
4702 :
4703 : /************************************************************************/
4704 : /* GDALCreateSimilarTransformer() */
4705 : /************************************************************************/
4706 :
4707 51 : void *GDALCreateSimilarTransformer(void *pTransformArg, double dfRatioX,
4708 : double dfRatioY)
4709 : {
4710 51 : GDALTransformerInfo *psInfo =
4711 : static_cast<GDALTransformerInfo *>(pTransformArg);
4712 :
4713 51 : if (psInfo == nullptr || memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
4714 : strlen(GDAL_GTI2_SIGNATURE)) != 0)
4715 : {
4716 0 : CPLError(CE_Failure, CPLE_AppDefined,
4717 : "Attempt to call CreateSimilar on a non-GTI2 transformer.");
4718 0 : return nullptr;
4719 : }
4720 :
4721 51 : if (psInfo->pfnCreateSimilar == nullptr)
4722 : {
4723 0 : CPLError(CE_Failure, CPLE_AppDefined,
4724 : "No CreateSimilar function available for this transformer.");
4725 0 : return nullptr;
4726 : }
4727 :
4728 51 : return psInfo->pfnCreateSimilar(psInfo, dfRatioX, dfRatioY);
4729 : }
4730 :
4731 : /************************************************************************/
4732 : /* GetGenImgProjTransformInfo() */
4733 : /************************************************************************/
4734 :
4735 48 : static GDALTransformerInfo *GetGenImgProjTransformInfo(const char *pszFunc,
4736 : void *pTransformArg)
4737 : {
4738 48 : GDALTransformerInfo *psInfo =
4739 : static_cast<GDALTransformerInfo *>(pTransformArg);
4740 :
4741 48 : if (psInfo == nullptr || memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
4742 : strlen(GDAL_GTI2_SIGNATURE)) != 0)
4743 : {
4744 0 : CPLError(CE_Failure, CPLE_AppDefined,
4745 : "Attempt to call %s on "
4746 : "a non-GTI2 transformer.",
4747 : pszFunc);
4748 0 : return nullptr;
4749 : }
4750 :
4751 48 : if (EQUAL(psInfo->pszClassName, GDAL_APPROX_TRANSFORMER_CLASS_NAME))
4752 : {
4753 20 : ApproxTransformInfo *psATInfo =
4754 : static_cast<ApproxTransformInfo *>(pTransformArg);
4755 20 : psInfo = static_cast<GDALTransformerInfo *>(psATInfo->pBaseCBData);
4756 :
4757 20 : if (psInfo == nullptr ||
4758 20 : memcmp(psInfo->abySignature, GDAL_GTI2_SIGNATURE,
4759 : strlen(GDAL_GTI2_SIGNATURE)) != 0)
4760 : {
4761 0 : CPLError(CE_Failure, CPLE_AppDefined,
4762 : "Attempt to call %s on "
4763 : "a non-GTI2 transformer.",
4764 : pszFunc);
4765 0 : return nullptr;
4766 : }
4767 : }
4768 :
4769 48 : if (EQUAL(psInfo->pszClassName, GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
4770 : {
4771 48 : return psInfo;
4772 : }
4773 :
4774 0 : return nullptr;
4775 : }
4776 :
4777 : /************************************************************************/
4778 : /* GDALSetTransformerDstGeoTransform() */
4779 : /************************************************************************/
4780 :
4781 : /**
4782 : * Set ApproxTransformer or GenImgProj output geotransform.
4783 : *
4784 : * This is a layer above GDALSetGenImgProjTransformerDstGeoTransform() that
4785 : * checks that the passed hTransformArg is compatible.
4786 : *
4787 : * Normally the "destination geotransform", or transformation between
4788 : * georeferenced output coordinates and pixel/line coordinates on the
4789 : * destination file is extracted from the destination file by
4790 : * GDALCreateGenImgProjTransformer() and stored in the GenImgProj private
4791 : * info. However, sometimes it is inconvenient to have an output file
4792 : * handle with appropriate geotransform information when creating the
4793 : * transformation. For these cases, this function can be used to apply
4794 : * the destination geotransform.
4795 : *
4796 : * @param pTransformArg the handle to update.
4797 : * @param padfGeoTransform the destination geotransform to apply (six doubles).
4798 : */
4799 :
4800 24 : void GDALSetTransformerDstGeoTransform(void *pTransformArg,
4801 : const double *padfGeoTransform)
4802 : {
4803 24 : VALIDATE_POINTER0(pTransformArg, "GDALSetTransformerDstGeoTransform");
4804 :
4805 24 : GDALTransformerInfo *psInfo = GetGenImgProjTransformInfo(
4806 : "GDALSetTransformerDstGeoTransform", pTransformArg);
4807 24 : if (psInfo)
4808 : {
4809 24 : GDALSetGenImgProjTransformerDstGeoTransform(psInfo, padfGeoTransform);
4810 : }
4811 : }
4812 :
4813 : /************************************************************************/
4814 : /* GDALGetTransformerDstGeoTransform() */
4815 : /************************************************************************/
4816 :
4817 : /**
4818 : * Get ApproxTransformer or GenImgProj output geotransform.
4819 : *
4820 : * @param pTransformArg transformer handle.
4821 : * @param padfGeoTransform (output) the destination geotransform to return (six
4822 : * doubles).
4823 : */
4824 :
4825 24 : void GDALGetTransformerDstGeoTransform(void *pTransformArg,
4826 : double *padfGeoTransform)
4827 : {
4828 24 : VALIDATE_POINTER0(pTransformArg, "GDALGetTransformerDstGeoTransform");
4829 :
4830 24 : GDALTransformerInfo *psInfo = GetGenImgProjTransformInfo(
4831 : "GDALGetTransformerDstGeoTransform", pTransformArg);
4832 24 : if (psInfo)
4833 : {
4834 24 : GDALGenImgProjTransformInfo *psGenImgProjInfo =
4835 : reinterpret_cast<GDALGenImgProjTransformInfo *>(psInfo);
4836 :
4837 24 : memcpy(padfGeoTransform, psGenImgProjInfo->adfDstGeoTransform,
4838 : sizeof(double) * 6);
4839 : }
4840 : }
4841 :
4842 : /************************************************************************/
4843 : /* GDALTransformIsTranslationOnPixelBoundaries() */
4844 : /************************************************************************/
4845 :
4846 1345 : bool GDALTransformIsTranslationOnPixelBoundaries(GDALTransformerFunc,
4847 : void *pTransformerArg)
4848 : {
4849 1345 : if (GDALIsTransformer(pTransformerArg, GDAL_APPROX_TRANSFORMER_CLASS_NAME))
4850 : {
4851 993 : const auto *pApproxInfo =
4852 : static_cast<const ApproxTransformInfo *>(pTransformerArg);
4853 993 : pTransformerArg = pApproxInfo->pBaseCBData;
4854 : }
4855 1345 : if (GDALIsTransformer(pTransformerArg, GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
4856 : {
4857 1192 : const auto *pGenImgpProjInfo =
4858 : static_cast<GDALGenImgProjTransformInfo *>(pTransformerArg);
4859 370 : const auto IsCloseToInteger = [](double dfVal)
4860 370 : { return std::fabs(dfVal - std::round(dfVal)) <= 1e-6; };
4861 2305 : return pGenImgpProjInfo->pSrcTransformArg == nullptr &&
4862 1113 : pGenImgpProjInfo->pDstTransformArg == nullptr &&
4863 1111 : pGenImgpProjInfo->pReproject == nullptr &&
4864 440 : pGenImgpProjInfo->adfSrcGeoTransform[1] ==
4865 440 : pGenImgpProjInfo->adfDstGeoTransform[1] &&
4866 217 : pGenImgpProjInfo->adfSrcGeoTransform[5] ==
4867 217 : pGenImgpProjInfo->adfDstGeoTransform[5] &&
4868 203 : pGenImgpProjInfo->adfSrcGeoTransform[2] ==
4869 203 : pGenImgpProjInfo->adfDstGeoTransform[2] &&
4870 203 : pGenImgpProjInfo->adfSrcGeoTransform[4] ==
4871 406 : pGenImgpProjInfo->adfDstGeoTransform[4] &&
4872 : // Check that the georeferenced origin of the destination
4873 : // geotransform is close to be an integer value when transformed
4874 : // to source image coordinates
4875 203 : IsCloseToInteger(
4876 203 : pGenImgpProjInfo->adfSrcInvGeoTransform[0] +
4877 203 : pGenImgpProjInfo->adfDstGeoTransform[0] *
4878 203 : pGenImgpProjInfo->adfSrcInvGeoTransform[1] +
4879 203 : pGenImgpProjInfo->adfDstGeoTransform[3] *
4880 2508 : pGenImgpProjInfo->adfSrcInvGeoTransform[2]) &&
4881 167 : IsCloseToInteger(pGenImgpProjInfo->adfSrcInvGeoTransform[3] +
4882 167 : pGenImgpProjInfo->adfDstGeoTransform[0] *
4883 167 : pGenImgpProjInfo->adfSrcInvGeoTransform[4] +
4884 167 : pGenImgpProjInfo->adfDstGeoTransform[3] *
4885 1359 : pGenImgpProjInfo->adfSrcInvGeoTransform[5]);
4886 : }
4887 153 : return false;
4888 : }
4889 :
4890 : /************************************************************************/
4891 : /* GDALTransformIsAffineNoRotation() */
4892 : /************************************************************************/
4893 :
4894 18 : bool GDALTransformIsAffineNoRotation(GDALTransformerFunc, void *pTransformerArg)
4895 : {
4896 18 : if (GDALIsTransformer(pTransformerArg, GDAL_APPROX_TRANSFORMER_CLASS_NAME))
4897 : {
4898 18 : const auto *pApproxInfo =
4899 : static_cast<const ApproxTransformInfo *>(pTransformerArg);
4900 18 : pTransformerArg = pApproxInfo->pBaseCBData;
4901 : }
4902 18 : if (GDALIsTransformer(pTransformerArg, GDAL_GEN_IMG_TRANSFORMER_CLASS_NAME))
4903 : {
4904 18 : const auto *pGenImgpProjInfo =
4905 : static_cast<GDALGenImgProjTransformInfo *>(pTransformerArg);
4906 36 : return pGenImgpProjInfo->pSrcTransformArg == nullptr &&
4907 18 : pGenImgpProjInfo->pDstTransformArg == nullptr &&
4908 18 : pGenImgpProjInfo->pReproject == nullptr &&
4909 8 : pGenImgpProjInfo->adfSrcGeoTransform[2] == 0 &&
4910 8 : pGenImgpProjInfo->adfSrcGeoTransform[4] == 0 &&
4911 44 : pGenImgpProjInfo->adfDstGeoTransform[2] == 0 &&
4912 26 : pGenImgpProjInfo->adfDstGeoTransform[4] == 0;
4913 : }
4914 0 : return false;
4915 : }
|