Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: Implements Geolocation array based transformer.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2006, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10 : * Copyright (c) 2021, CLS
11 : * Copyright (c) 2022, Planet Labs
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include "cpl_port.h"
17 : #include "gdal_alg.h"
18 : #include "gdal_alg_priv.h"
19 : #include "gdalgeoloc.h"
20 : #include "gdalgeolocquadtree.h"
21 :
22 : #include <climits>
23 : #include <cmath>
24 : #include <cstddef>
25 : #include <cstdlib>
26 : #include <cstring>
27 :
28 : #include <algorithm>
29 : #include <limits>
30 :
31 : #include "cpl_conv.h"
32 : #include "cpl_error.h"
33 : #include "cpl_minixml.h"
34 : #include "cpl_quad_tree.h"
35 : #include "cpl_string.h"
36 : #include "cpl_vsi.h"
37 : #include "gdal.h"
38 : #include "gdal_priv.h"
39 : #include "memdataset.h"
40 :
41 : constexpr float INVALID_BMXY = -10.0f;
42 :
43 : #include "gdalgeoloc_carray_accessor.h"
44 : #include "gdalgeoloc_dataset_accessor.h"
45 :
46 : // #define DEBUG_GEOLOC
47 :
48 : #ifdef DEBUG_GEOLOC
49 : #include "ogrsf_frmts.h"
50 : #endif
51 :
52 : #ifdef DEBUG_GEOLOC
53 : #warning "Remove me before committing"
54 : #endif
55 :
56 : CPL_C_START
57 : CPLXMLNode *GDALSerializeGeoLocTransformer(void *pTransformArg);
58 : void *GDALDeserializeGeoLocTransformer(CPLXMLNode *psTree);
59 : CPL_C_END
60 :
61 : /************************************************************************/
62 : /* ==================================================================== */
63 : /* GDALGeoLocTransformer */
64 : /* ==================================================================== */
65 : /************************************************************************/
66 :
67 : /************************************************************************/
68 : /* UnshiftGeoX() */
69 : /************************************************************************/
70 :
71 : // Renormalize longitudes to [-180,180] range
72 11212300 : static double UnshiftGeoX(const GDALGeoLocTransformInfo *psTransform,
73 : double dfX)
74 : {
75 11212300 : if (!psTransform->bGeographicSRSWithMinus180Plus180LongRange)
76 2259200 : return dfX;
77 8953050 : if (dfX > 180 || dfX < -180)
78 : {
79 30885 : dfX = fmod(dfX + 180.0, 360.0);
80 30885 : if (dfX < 0)
81 372 : dfX += 180.0;
82 : else
83 30513 : dfX -= 180.0;
84 30885 : return dfX;
85 : }
86 8922160 : return dfX;
87 : }
88 :
89 : /************************************************************************/
90 : /* UpdateMinMax() */
91 : /************************************************************************/
92 :
93 240605 : inline void UpdateMinMax(GDALGeoLocTransformInfo *psTransform, double dfGeoLocX,
94 : double dfGeoLocY)
95 : {
96 240605 : if (dfGeoLocX < psTransform->dfMinX)
97 : {
98 1194 : psTransform->dfMinX = dfGeoLocX;
99 1194 : psTransform->dfYAtMinX = dfGeoLocY;
100 : }
101 240605 : if (dfGeoLocX > psTransform->dfMaxX)
102 : {
103 790 : psTransform->dfMaxX = dfGeoLocX;
104 790 : psTransform->dfYAtMaxX = dfGeoLocY;
105 : }
106 240605 : if (dfGeoLocY < psTransform->dfMinY)
107 : {
108 1208 : psTransform->dfMinY = dfGeoLocY;
109 1208 : psTransform->dfXAtMinY = dfGeoLocX;
110 : }
111 240605 : if (dfGeoLocY > psTransform->dfMaxY)
112 : {
113 3412 : psTransform->dfMaxY = dfGeoLocY;
114 3412 : psTransform->dfXAtMaxY = dfGeoLocX;
115 : }
116 240605 : }
117 :
118 : /************************************************************************/
119 : /* Clamp() */
120 : /************************************************************************/
121 :
122 2978 : inline double Clamp(double v, double minV, double maxV)
123 : {
124 2978 : return std::min(std::max(v, minV), maxV);
125 : }
126 :
127 : /************************************************************************/
128 : /* START_ITER_PER_BLOCK() */
129 : /************************************************************************/
130 :
131 : #define START_ITER_PER_BLOCK(_rasterXSize, _tileXSize, _rasterYSize, \
132 : _tileYSize, INIT_YBLOCK, _iXStart, _iXEnd, \
133 : _iYStart, _iYEnd) \
134 : { \
135 : const int _nYBlocks = DIV_ROUND_UP(_rasterYSize, _tileYSize); \
136 : const int _nXBlocks = DIV_ROUND_UP(_rasterXSize, _tileXSize); \
137 : for (int _iYBlock = 0; _iYBlock < _nYBlocks; ++_iYBlock) \
138 : { \
139 : const int _iYStart = _iYBlock * _tileYSize; \
140 : const int _iYEnd = _iYBlock == _nYBlocks - 1 \
141 : ? _rasterYSize \
142 : : _iYStart + _tileYSize; \
143 : INIT_YBLOCK; \
144 : for (int _iXBlock = 0; _iXBlock < _nXBlocks; ++_iXBlock) \
145 : { \
146 : const int _iXStart = _iXBlock * _tileXSize; \
147 : const int _iXEnd = _iXBlock == _nXBlocks - 1 \
148 : ? _rasterXSize \
149 : : _iXStart + _tileXSize;
150 :
151 : #define END_ITER_PER_BLOCK \
152 : } \
153 : } \
154 : }
155 :
156 : /************************************************************************/
157 : /* GDALGeoLoc::LoadGeolocFinish() */
158 : /************************************************************************/
159 :
160 : /*! @cond Doxygen_Suppress */
161 :
162 : template <class Accessors>
163 49 : void GDALGeoLoc<Accessors>::LoadGeolocFinish(
164 : GDALGeoLocTransformInfo *psTransform)
165 : {
166 49 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
167 49 : CSLConstList papszGeolocationInfo = psTransform->papszGeolocationInfo;
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* Scan forward map for lat/long extents. */
171 : /* -------------------------------------------------------------------- */
172 49 : psTransform->dfMinX = std::numeric_limits<double>::max();
173 49 : psTransform->dfMaxX = -std::numeric_limits<double>::max();
174 49 : psTransform->dfMinY = std::numeric_limits<double>::max();
175 49 : psTransform->dfMaxY = -std::numeric_limits<double>::max();
176 :
177 49 : constexpr int TILE_SIZE = GDALGeoLocDatasetAccessors::TILE_SIZE;
178 153 : START_ITER_PER_BLOCK(psTransform->nGeoLocXSize, TILE_SIZE,
179 : psTransform->nGeoLocYSize, TILE_SIZE, (void)0, iXStart,
180 : iXEnd, iYStart, iYEnd)
181 : {
182 1963 : for (int iY = iYStart; iY < iYEnd; ++iY)
183 : {
184 241419 : for (int iX = iXStart; iX < iXEnd; ++iX)
185 : {
186 239509 : double dfX = pAccessors->geolocXAccessor.Get(iX, iY);
187 239509 : if (!psTransform->bHasNoData || dfX != psTransform->dfNoDataX)
188 : {
189 236567 : dfX = UnshiftGeoX(psTransform, dfX);
190 236567 : UpdateMinMax(psTransform, dfX,
191 : pAccessors->geolocYAccessor.Get(iX, iY));
192 : }
193 : }
194 : }
195 : }
196 : END_ITER_PER_BLOCK
197 :
198 : // Check if the SRS is geographic and the geoloc longitudes are in
199 : // [-180,180]
200 49 : const char *pszSRS = CSLFetchNameValue(papszGeolocationInfo, "SRS");
201 49 : if (!psTransform->bGeographicSRSWithMinus180Plus180LongRange && pszSRS &&
202 46 : psTransform->dfMinX >= -180.0 && psTransform->dfMaxX <= 180.0 &&
203 45 : !psTransform->bSwapXY)
204 : {
205 44 : OGRSpatialReference oSRS;
206 44 : psTransform->bGeographicSRSWithMinus180Plus180LongRange =
207 88 : oSRS.importFromWkt(pszSRS) == OGRERR_NONE &&
208 44 : CPL_TO_BOOL(oSRS.IsGeographic());
209 : }
210 :
211 : #ifdef DEBUG_GEOLOC
212 : if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
213 : {
214 : auto poDS = std::unique_ptr<GDALDataset>(
215 : GDALDriver::FromHandle(GDALGetDriverByName("ESRI Shapefile"))
216 : ->Create("/tmp/geoloc_poly.shp", 0, 0, 0, GDT_Unknown,
217 : nullptr));
218 : auto poLayer =
219 : poDS->CreateLayer("geoloc_poly", nullptr, wkbPolygon, nullptr);
220 : auto poLayerDefn = poLayer->GetLayerDefn();
221 : OGRFieldDefn fieldX("x", OFTInteger);
222 : poLayer->CreateField(&fieldX);
223 : OGRFieldDefn fieldY("y", OFTInteger);
224 : poLayer->CreateField(&fieldY);
225 : for (int iY = 0; iY < psTransform->nGeoLocYSize - 1; iY++)
226 : {
227 : for (int iX = 0; iX < psTransform->nGeoLocXSize - 1; iX++)
228 : {
229 : double x0, y0, x1, y1, x2, y2, x3, y3;
230 : if (!PixelLineToXY(psTransform, iX, iY, x0, y0) ||
231 : !PixelLineToXY(psTransform, iX + 1, iY, x2, y2) ||
232 : !PixelLineToXY(psTransform, iX, iY + 1, x1, y1) ||
233 : !PixelLineToXY(psTransform, iX + 1, iY + 1, x3, y3))
234 : {
235 : break;
236 : }
237 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
238 : std::fabs(x0) > 170 && std::fabs(x1) > 170 &&
239 : std::fabs(x2) > 170 && std::fabs(x3) > 170 &&
240 : (std::fabs(x1 - x0) > 180 || std::fabs(x2 - x0) > 180 ||
241 : std::fabs(x3 - x0) > 180))
242 : {
243 : OGRPolygon *poPoly = new OGRPolygon();
244 : OGRLinearRing *poRing = new OGRLinearRing();
245 : poRing->addPoint(x0 > 0 ? x0 : x0 + 360, y0);
246 : poRing->addPoint(x2 > 0 ? x2 : x2 + 360, y2);
247 : poRing->addPoint(x3 > 0 ? x3 : x3 + 360, y3);
248 : poRing->addPoint(x1 > 0 ? x1 : x1 + 360, y1);
249 : poRing->addPoint(x0 > 0 ? x0 : x0 + 360, y0);
250 : poPoly->addRingDirectly(poRing);
251 : auto poFeature = std::make_unique<OGRFeature>(poLayerDefn);
252 : poFeature->SetField(0, static_cast<int>(iX));
253 : poFeature->SetField(1, static_cast<int>(iY));
254 : poFeature->SetGeometryDirectly(poPoly);
255 : CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature.get()));
256 : if (x0 > 0)
257 : x0 -= 360;
258 : if (x1 > 0)
259 : x1 -= 360;
260 : if (x2 > 0)
261 : x2 -= 360;
262 : if (x3 > 0)
263 : x3 -= 360;
264 : }
265 :
266 : OGRPolygon *poPoly = new OGRPolygon();
267 : OGRLinearRing *poRing = new OGRLinearRing();
268 : poRing->addPoint(x0, y0);
269 : poRing->addPoint(x2, y2);
270 : poRing->addPoint(x3, y3);
271 : poRing->addPoint(x1, y1);
272 : poRing->addPoint(x0, y0);
273 : poPoly->addRingDirectly(poRing);
274 : auto poFeature = std::make_unique<OGRFeature>(poLayerDefn);
275 : poFeature->SetField(0, static_cast<int>(iX));
276 : poFeature->SetField(1, static_cast<int>(iY));
277 : poFeature->SetGeometryDirectly(poPoly);
278 : CPL_IGNORE_RET_VAL(poLayer->CreateFeature(poFeature.get()));
279 : }
280 : }
281 : }
282 : #endif
283 :
284 49 : if (psTransform->bOriginIsTopLeftCorner)
285 : {
286 : // Add "virtual" edge at Y=nGeoLocYSize
287 1779 : for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
288 : {
289 : double dfGeoLocX;
290 : double dfGeoLocY;
291 1752 : if (!PixelLineToXY(psTransform, static_cast<double>(iX),
292 1752 : static_cast<double>(psTransform->nGeoLocYSize),
293 : dfGeoLocX, dfGeoLocY))
294 0 : continue;
295 1752 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
296 988 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
297 1752 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
298 : }
299 :
300 : // Add "virtual" edge at X=nGeoLocXSize
301 1431 : for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
302 : {
303 : double dfGeoLocX;
304 : double dfGeoLocY;
305 1404 : if (!PixelLineToXY(psTransform,
306 1404 : static_cast<double>(psTransform->nGeoLocXSize),
307 : static_cast<double>(iY), dfGeoLocX, dfGeoLocY))
308 0 : continue;
309 1404 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
310 1200 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
311 1404 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
312 : }
313 : }
314 : else
315 : {
316 : // Extend by half-pixel on 4 edges for pixel-center convention
317 :
318 357 : for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
319 : {
320 : double dfGeoLocX;
321 : double dfGeoLocY;
322 335 : if (!PixelLineToXY(psTransform, static_cast<double>(iX), -0.5,
323 : dfGeoLocX, dfGeoLocY))
324 114 : continue;
325 221 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
326 200 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
327 221 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
328 : }
329 :
330 357 : for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
331 : {
332 : double dfGeoLocX;
333 : double dfGeoLocY;
334 335 : if (!PixelLineToXY(
335 : psTransform, static_cast<double>(iX),
336 335 : static_cast<double>(psTransform->nGeoLocYSize - 1 + 0.5),
337 : dfGeoLocX, dfGeoLocY))
338 118 : continue;
339 217 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
340 196 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
341 217 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
342 : }
343 :
344 417 : for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
345 : {
346 : double dfGeoLocX;
347 : double dfGeoLocY;
348 395 : if (!PixelLineToXY(psTransform, -0.5, static_cast<double>(iY),
349 : dfGeoLocX, dfGeoLocY))
350 170 : continue;
351 225 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
352 200 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
353 225 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
354 : }
355 :
356 417 : for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
357 : {
358 : double dfGeoLocX;
359 : double dfGeoLocY;
360 395 : if (!PixelLineToXY(psTransform, psTransform->nGeoLocXSize - 1 + 0.5,
361 : static_cast<double>(iY), dfGeoLocX, dfGeoLocY))
362 176 : continue;
363 219 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
364 194 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
365 219 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
366 : }
367 : }
368 49 : }
369 :
370 : /************************************************************************/
371 : /* GDALGeoLoc::PixelLineToXY() */
372 : /************************************************************************/
373 :
374 : /** Interpolate a position expressed as (floating point) pixel/line in the
375 : * geolocation array to the corresponding bilinearly-interpolated georeferenced
376 : * position.
377 : *
378 : * The interpolation assumes infinite extension beyond borders of available
379 : * data based on closest grid square.
380 : *
381 : * @param psTransform Transformation info
382 : * @param dfGeoLocPixel Position along the column/pixel axis of the geolocation
383 : * array
384 : * @param dfGeoLocLine Position along the row/line axis of the geolocation
385 : * array
386 : * @param[out] dfX Output X of georeferenced position.
387 : * @param[out] dfY Output Y of georeferenced position.
388 : * @return true if success
389 : */
390 :
391 : template <class Accessors>
392 1284383 : bool GDALGeoLoc<Accessors>::PixelLineToXY(
393 : const GDALGeoLocTransformInfo *psTransform, const double dfGeoLocPixel,
394 : const double dfGeoLocLine, double &dfX, double &dfY)
395 : {
396 1284383 : int iX = static_cast<int>(
397 2568768 : std::min(std::max(0.0, dfGeoLocPixel),
398 1284383 : static_cast<double>(psTransform->nGeoLocXSize - 1)));
399 1284383 : int iY = static_cast<int>(
400 2568768 : std::min(std::max(0.0, dfGeoLocLine),
401 1284383 : static_cast<double>(psTransform->nGeoLocYSize - 1)));
402 :
403 1284383 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
404 :
405 1610495 : for (int iAttempt = 0; iAttempt < 2; ++iAttempt)
406 : {
407 1610495 : const double dfGLX_0_0 = pAccessors->geolocXAccessor.Get(iX, iY);
408 1610495 : const double dfGLY_0_0 = pAccessors->geolocYAccessor.Get(iX, iY);
409 1610495 : if (psTransform->bHasNoData && dfGLX_0_0 == psTransform->dfNoDataX)
410 : {
411 13114 : return false;
412 : }
413 :
414 : // This assumes infinite extension beyond borders of available
415 : // data based on closest grid square.
416 1597385 : if (iX + 1 < psTransform->nGeoLocXSize &&
417 1405769 : iY + 1 < psTransform->nGeoLocYSize)
418 : {
419 1271269 : const double dfGLX_1_0 =
420 : pAccessors->geolocXAccessor.Get(iX + 1, iY);
421 1271269 : const double dfGLY_1_0 =
422 : pAccessors->geolocYAccessor.Get(iX + 1, iY);
423 1271269 : const double dfGLX_0_1 =
424 : pAccessors->geolocXAccessor.Get(iX, iY + 1);
425 1271269 : const double dfGLY_0_1 =
426 : pAccessors->geolocYAccessor.Get(iX, iY + 1);
427 1271269 : const double dfGLX_1_1 =
428 : pAccessors->geolocXAccessor.Get(iX + 1, iY + 1);
429 1271269 : const double dfGLY_1_1 =
430 : pAccessors->geolocYAccessor.Get(iX + 1, iY + 1);
431 1271269 : if (!psTransform->bHasNoData ||
432 111833 : (dfGLX_1_0 != psTransform->dfNoDataX &&
433 110333 : dfGLX_0_1 != psTransform->dfNoDataX &&
434 109235 : dfGLX_1_1 != psTransform->dfNoDataX))
435 : {
436 : const double dfGLX_1_0_adjusted =
437 1268483 : ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_0);
438 : const double dfGLX_0_1_adjusted =
439 1268483 : ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_0_1);
440 : const double dfGLX_1_1_adjusted =
441 1268483 : ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_1);
442 1268483 : dfX = (1 - (dfGeoLocLine - iY)) *
443 1268483 : (dfGLX_0_0 + (dfGeoLocPixel - iX) *
444 1268483 : (dfGLX_1_0_adjusted - dfGLX_0_0)) +
445 1268483 : (dfGeoLocLine - iY) *
446 1268483 : (dfGLX_0_1_adjusted +
447 1268483 : (dfGeoLocPixel - iX) *
448 1268483 : (dfGLX_1_1_adjusted - dfGLX_0_1_adjusted));
449 1268483 : dfX = UnshiftGeoX(psTransform, dfX);
450 :
451 1268483 : dfY = (1 - (dfGeoLocLine - iY)) *
452 1268483 : (dfGLY_0_0 +
453 1268483 : (dfGeoLocPixel - iX) * (dfGLY_1_0 - dfGLY_0_0)) +
454 1268483 : (dfGeoLocLine - iY) *
455 1268483 : (dfGLY_0_1 +
456 1268483 : (dfGeoLocPixel - iX) * (dfGLY_1_1 - dfGLY_0_1));
457 1268483 : break;
458 : }
459 : }
460 :
461 328902 : if (iX == psTransform->nGeoLocXSize - 1 && iX >= 1 &&
462 191616 : iY + 1 < psTransform->nGeoLocYSize)
463 : {
464 : // If we are after the right edge, then go one pixel left
465 : // and retry
466 177855 : iX--;
467 177855 : continue;
468 : }
469 151047 : else if (iY == psTransform->nGeoLocYSize - 1 && iY >= 1 &&
470 148261 : iX + 1 < psTransform->nGeoLocXSize)
471 : {
472 : // If we are after the bottom edge, then go one pixel up
473 : // and retry
474 134500 : iY--;
475 134500 : continue;
476 : }
477 16547 : else if (iX == psTransform->nGeoLocXSize - 1 &&
478 13761 : iY == psTransform->nGeoLocYSize - 1 && iX >= 1 && iY >= 1)
479 : {
480 : // If we are after the right and bottom edge, then go one pixel left
481 : // and up and retry
482 13761 : iX--;
483 13761 : iY--;
484 13761 : continue;
485 : }
486 5572 : else if (iX + 1 < psTransform->nGeoLocXSize &&
487 2786 : (!psTransform->bHasNoData ||
488 2786 : pAccessors->geolocXAccessor.Get(iX + 1, iY) !=
489 2786 : psTransform->dfNoDataX))
490 : {
491 1286 : const double dfGLX_1_0 =
492 : pAccessors->geolocXAccessor.Get(iX + 1, iY);
493 1286 : const double dfGLY_1_0 =
494 : pAccessors->geolocYAccessor.Get(iX + 1, iY);
495 1286 : dfX =
496 1286 : dfGLX_0_0 +
497 2572 : (dfGeoLocPixel - iX) *
498 1286 : (ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_0) - dfGLX_0_0);
499 1286 : dfX = UnshiftGeoX(psTransform, dfX);
500 1286 : dfY = dfGLY_0_0 + (dfGeoLocPixel - iX) * (dfGLY_1_0 - dfGLY_0_0);
501 : }
502 3000 : else if (iY + 1 < psTransform->nGeoLocYSize &&
503 1500 : (!psTransform->bHasNoData ||
504 1500 : pAccessors->geolocXAccessor.Get(iX, iY + 1) !=
505 1500 : psTransform->dfNoDataX))
506 : {
507 1448 : const double dfGLX_0_1 =
508 : pAccessors->geolocXAccessor.Get(iX, iY + 1);
509 1448 : const double dfGLY_0_1 =
510 : pAccessors->geolocYAccessor.Get(iX, iY + 1);
511 1448 : dfX =
512 1448 : dfGLX_0_0 +
513 2896 : (dfGeoLocLine - iY) *
514 1448 : (ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_0_1) - dfGLX_0_0);
515 1448 : dfX = UnshiftGeoX(psTransform, dfX);
516 1448 : dfY = dfGLY_0_0 + (dfGeoLocLine - iY) * (dfGLY_0_1 - dfGLY_0_0);
517 : }
518 : else
519 : {
520 52 : dfX = UnshiftGeoX(psTransform, dfGLX_0_0);
521 52 : dfY = dfGLY_0_0;
522 : }
523 2786 : break;
524 : }
525 1271269 : return true;
526 : }
527 :
528 : template <class Accessors>
529 10563450 : bool GDALGeoLoc<Accessors>::PixelLineToXY(
530 : const GDALGeoLocTransformInfo *psTransform, const int nGeoLocPixel,
531 : const int nGeoLocLine, double &dfX, double &dfY)
532 : {
533 10563450 : if (nGeoLocPixel >= 0 && nGeoLocPixel < psTransform->nGeoLocXSize &&
534 9867660 : nGeoLocLine >= 0 && nGeoLocLine < psTransform->nGeoLocYSize)
535 : {
536 9751360 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
537 9751360 : const double dfGLX =
538 : pAccessors->geolocXAccessor.Get(nGeoLocPixel, nGeoLocLine);
539 9751360 : const double dfGLY =
540 : pAccessors->geolocYAccessor.Get(nGeoLocPixel, nGeoLocLine);
541 9751360 : if (psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX)
542 : {
543 46946 : return false;
544 : }
545 9704420 : dfX = UnshiftGeoX(psTransform, dfGLX);
546 9704420 : dfY = dfGLY;
547 9704420 : return true;
548 : }
549 812081 : return PixelLineToXY(psTransform, static_cast<double>(nGeoLocPixel),
550 812081 : static_cast<double>(nGeoLocLine), dfX, dfY);
551 : }
552 :
553 : /************************************************************************/
554 : /* GDALGeoLoc::ExtractSquare() */
555 : /************************************************************************/
556 :
557 : template <class Accessors>
558 3200 : bool GDALGeoLoc<Accessors>::ExtractSquare(
559 : const GDALGeoLocTransformInfo *psTransform, int nX, int nY, double &dfX_0_0,
560 : double &dfY_0_0, double &dfX_1_0, double &dfY_1_0, double &dfX_0_1,
561 : double &dfY_0_1, double &dfX_1_1, double &dfY_1_1)
562 : {
563 6400 : return PixelLineToXY(psTransform, nX, nY, dfX_0_0, dfY_0_0) &&
564 6400 : PixelLineToXY(psTransform, nX + 1, nY, dfX_1_0, dfY_1_0) &&
565 9600 : PixelLineToXY(psTransform, nX, nY + 1, dfX_0_1, dfY_0_1) &&
566 6400 : PixelLineToXY(psTransform, nX + 1, nY + 1, dfX_1_1, dfY_1_1);
567 : }
568 :
569 3200 : bool GDALGeoLocExtractSquare(const GDALGeoLocTransformInfo *psTransform, int nX,
570 : int nY, double &dfX_0_0, double &dfY_0_0,
571 : double &dfX_1_0, double &dfY_1_0, double &dfX_0_1,
572 : double &dfY_0_1, double &dfX_1_1, double &dfY_1_1)
573 : {
574 3200 : if (psTransform->bUseArray)
575 : {
576 3200 : return GDALGeoLoc<GDALGeoLocCArrayAccessors>::ExtractSquare(
577 : psTransform, nX, nY, dfX_0_0, dfY_0_0, dfX_1_0, dfY_1_0, dfX_0_1,
578 3200 : dfY_0_1, dfX_1_1, dfY_1_1);
579 : }
580 : else
581 : {
582 0 : return GDALGeoLoc<GDALGeoLocDatasetAccessors>::ExtractSquare(
583 : psTransform, nX, nY, dfX_0_0, dfY_0_0, dfX_1_0, dfY_1_0, dfX_0_1,
584 0 : dfY_0_1, dfX_1_1, dfY_1_1);
585 : }
586 : }
587 :
588 : /************************************************************************/
589 : /* GDALGeoLocTransform() */
590 : /************************************************************************/
591 :
592 : template <class Accessors>
593 31180 : int GDALGeoLoc<Accessors>::Transform(void *pTransformArg, int bDstToSrc,
594 : int nPointCount, double *padfX,
595 : double *padfY, double * /* padfZ */,
596 : int *panSuccess)
597 : {
598 31180 : int bSuccess = TRUE;
599 31180 : GDALGeoLocTransformInfo *psTransform =
600 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
601 :
602 31180 : if (psTransform->bReversed)
603 0 : bDstToSrc = !bDstToSrc;
604 :
605 31180 : const double dfGeorefConventionOffset =
606 31180 : psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
607 :
608 : /* -------------------------------------------------------------------- */
609 : /* Do original pixel line to target geox/geoy. */
610 : /* -------------------------------------------------------------------- */
611 31180 : if (!bDstToSrc)
612 : {
613 70604 : for (int i = 0; i < nPointCount; i++)
614 : {
615 45698 : if (padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL)
616 : {
617 2745 : bSuccess = FALSE;
618 2745 : panSuccess[i] = FALSE;
619 2745 : continue;
620 : }
621 :
622 42953 : const double dfGeoLocPixel =
623 42953 : (padfX[i] - psTransform->dfPIXEL_OFFSET) /
624 42953 : psTransform->dfPIXEL_STEP -
625 : dfGeorefConventionOffset;
626 42953 : const double dfGeoLocLine =
627 42953 : (padfY[i] - psTransform->dfLINE_OFFSET) /
628 42953 : psTransform->dfLINE_STEP -
629 : dfGeorefConventionOffset;
630 :
631 42953 : if (!PixelLineToXY(psTransform, dfGeoLocPixel, dfGeoLocLine,
632 42953 : padfX[i], padfY[i]))
633 : {
634 2826 : bSuccess = FALSE;
635 2826 : panSuccess[i] = FALSE;
636 2826 : padfX[i] = HUGE_VAL;
637 2826 : padfY[i] = HUGE_VAL;
638 2826 : continue;
639 : }
640 :
641 40127 : if (psTransform->bSwapXY)
642 : {
643 568 : std::swap(padfX[i], padfY[i]);
644 : }
645 :
646 40127 : panSuccess[i] = TRUE;
647 : }
648 : }
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* geox/geoy to pixel/line using backmap. */
652 : /* -------------------------------------------------------------------- */
653 : else
654 : {
655 6274 : if (psTransform->hQuadTree)
656 : {
657 24 : GDALGeoLocInverseTransformQuadtree(psTransform, nPointCount, padfX,
658 : padfY, panSuccess);
659 24 : return TRUE;
660 : }
661 :
662 6250 : const bool bGeolocMaxAccuracy = CPLTestBool(
663 : CPLGetConfigOption("GDAL_GEOLOC_USE_MAX_ACCURACY", "YES"));
664 :
665 : // Keep those objects in this outer scope, so they are reused, to
666 : // save memory allocations.
667 12500 : OGRPoint oPoint;
668 12500 : OGRLinearRing oRing;
669 6250 : oRing.setNumPoints(5);
670 :
671 6250 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
672 :
673 217067 : for (int i = 0; i < nPointCount; i++)
674 : {
675 210817 : if (padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL)
676 : {
677 0 : bSuccess = FALSE;
678 0 : panSuccess[i] = FALSE;
679 0 : continue;
680 : }
681 :
682 210817 : if (psTransform->bSwapXY)
683 : {
684 836 : std::swap(padfX[i], padfY[i]);
685 : }
686 :
687 210817 : const double dfGeoX = padfX[i];
688 210817 : const double dfGeoY = padfY[i];
689 :
690 210817 : const double dfBMX =
691 210817 : ((padfX[i] - psTransform->adfBackMapGeoTransform[0]) /
692 : psTransform->adfBackMapGeoTransform[1]);
693 210817 : const double dfBMY =
694 210817 : ((padfY[i] - psTransform->adfBackMapGeoTransform[3]) /
695 : psTransform->adfBackMapGeoTransform[5]);
696 :
697 210817 : if (!(dfBMX >= 0 && dfBMY >= 0 &&
698 210658 : dfBMX + 1 < psTransform->nBackMapWidth &&
699 209499 : dfBMY + 1 < psTransform->nBackMapHeight))
700 : {
701 2097 : bSuccess = FALSE;
702 2097 : panSuccess[i] = FALSE;
703 2097 : padfX[i] = HUGE_VAL;
704 2097 : padfY[i] = HUGE_VAL;
705 2097 : continue;
706 : }
707 :
708 208720 : const int iBMX = static_cast<int>(dfBMX);
709 208720 : const int iBMY = static_cast<int>(dfBMY);
710 :
711 208720 : const auto fBMX_0_0 = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
712 208720 : const auto fBMY_0_0 = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
713 208720 : if (fBMX_0_0 == INVALID_BMXY)
714 : {
715 86072 : bSuccess = FALSE;
716 86072 : panSuccess[i] = FALSE;
717 86072 : padfX[i] = HUGE_VAL;
718 86072 : padfY[i] = HUGE_VAL;
719 86072 : continue;
720 : }
721 :
722 122648 : const auto fBMX_1_0 =
723 : pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY);
724 122648 : const auto fBMY_1_0 =
725 : pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY);
726 122648 : const auto fBMX_0_1 =
727 : pAccessors->backMapXAccessor.Get(iBMX, iBMY + 1);
728 122648 : const auto fBMY_0_1 =
729 : pAccessors->backMapYAccessor.Get(iBMX, iBMY + 1);
730 122648 : const auto fBMX_1_1 =
731 : pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY + 1);
732 122648 : const auto fBMY_1_1 =
733 : pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY + 1);
734 122648 : if (fBMX_1_0 != INVALID_BMXY && fBMX_0_1 != INVALID_BMXY &&
735 : fBMX_1_1 != INVALID_BMXY)
736 : {
737 120417 : padfX[i] = (1 - (dfBMY - iBMY)) *
738 120417 : (double(fBMX_0_0) +
739 120417 : (dfBMX - iBMX) * double(fBMX_1_0 - fBMX_0_0)) +
740 120417 : (dfBMY - iBMY) *
741 120417 : (double(fBMX_0_1) +
742 120417 : (dfBMX - iBMX) * double(fBMX_1_1 - fBMX_0_1));
743 120417 : padfY[i] = (1 - (dfBMY - iBMY)) *
744 120417 : (double(fBMY_0_0) +
745 120417 : (dfBMX - iBMX) * double(fBMY_1_0 - fBMY_0_0)) +
746 120417 : (dfBMY - iBMY) *
747 120417 : (double(fBMY_0_1) +
748 120417 : (dfBMX - iBMX) * double(fBMY_1_1 - fBMY_0_1));
749 : }
750 2231 : else if (fBMX_1_0 != INVALID_BMXY)
751 : {
752 1686 : padfX[i] = double(fBMX_0_0) +
753 1686 : (dfBMX - iBMX) * double(fBMX_1_0 - fBMX_0_0);
754 1686 : padfY[i] = double(fBMY_0_0) +
755 1686 : (dfBMX - iBMX) * double(fBMY_1_0 - fBMY_0_0);
756 : }
757 545 : else if (fBMX_0_1 != INVALID_BMXY)
758 : {
759 410 : padfX[i] = double(fBMX_0_0) +
760 410 : (dfBMY - iBMY) * double(fBMX_0_1 - fBMX_0_0);
761 410 : padfY[i] = double(fBMY_0_0) +
762 410 : (dfBMY - iBMY) * double(fBMY_0_1 - fBMY_0_0);
763 : }
764 : else
765 : {
766 135 : padfX[i] = double(fBMX_0_0);
767 135 : padfY[i] = double(fBMY_0_0);
768 : }
769 :
770 122648 : const double dfGeoLocPixel =
771 122648 : (padfX[i] - psTransform->dfPIXEL_OFFSET) /
772 122648 : psTransform->dfPIXEL_STEP -
773 : dfGeorefConventionOffset;
774 122648 : const double dfGeoLocLine =
775 122648 : (padfY[i] - psTransform->dfLINE_OFFSET) /
776 122648 : psTransform->dfLINE_STEP -
777 : dfGeorefConventionOffset;
778 : #if 0
779 : CPLDebug("GEOLOC", "%f %f %f %f", padfX[i], padfY[i], dfGeoLocPixel, dfGeoLocLine);
780 : if( !psTransform->bOriginIsTopLeftCorner )
781 : {
782 : if( dfGeoLocPixel + dfGeorefConventionOffset > psTransform->nGeoLocXSize-1 ||
783 : dfGeoLocLine + dfGeorefConventionOffset > psTransform->nGeoLocYSize-1 )
784 : {
785 : panSuccess[i] = FALSE;
786 : padfX[i] = HUGE_VAL;
787 : padfY[i] = HUGE_VAL;
788 : continue;
789 : }
790 : }
791 : #endif
792 122648 : if (!bGeolocMaxAccuracy)
793 : {
794 0 : panSuccess[i] = TRUE;
795 0 : continue;
796 : }
797 :
798 : // Now that we have an approximate solution, identify a matching
799 : // cell in the geolocation array, where we can use inverse bilinear
800 : // interpolation to find the exact solution.
801 :
802 : // NOTE: if the geolocation array is an affine transformation,
803 : // the approximate solution should match the exact one, if the
804 : // backmap has correctly been built.
805 :
806 122648 : oPoint.setX(dfGeoX);
807 122648 : oPoint.setY(dfGeoY);
808 : // The thresholds and radius are rather empirical and have been
809 : // tuned on the product
810 : // S5P_TEST_L2__NO2____20190509T220707_20190509T234837_08137_01_010400_20200220T091343.nc
811 : // that includes the north pole.
812 : // Amended with the test case of
813 : // https://github.com/OSGeo/gdal/issues/5823
814 122648 : const int nSearchRadius =
815 122648 : psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
816 72891 : fabs(dfGeoY) >= 85
817 : ? 5
818 : : 3;
819 122648 : const int nGeoLocPixel =
820 122648 : static_cast<int>(std::floor(dfGeoLocPixel));
821 122648 : const int nGeoLocLine = static_cast<int>(std::floor(dfGeoLocLine));
822 :
823 122648 : bool bDone = false;
824 : // Using the above approximate nGeoLocPixel, nGeoLocLine, try to
825 : // find a forward cell that includes (dfGeoX, dfGeoY), with an
826 : // increasing search radius, up to nSearchRadius.
827 332038 : for (int r = 0; !bDone && r <= nSearchRadius; r++)
828 : {
829 1693997 : for (int iter = 0; !bDone && iter < (r == 0 ? 1 : 8 * r);
830 : ++iter)
831 : {
832 : // For r=1, the below formulas will give the following
833 : // offsets:
834 : // (-1,1), (0,1), (1,1), (1,0), (1,-1), (0,-1), (1,-1)
835 2846570 : const int sx = (r == 0) ? 0
836 1361964 : : (iter < 2 * r) ? -r + iter
837 1000168 : : (iter < 4 * r) ? r
838 656311 : : (iter < 6 * r) ? r - (iter - 4 * r)
839 : : -r;
840 2846570 : const int sy = (r == 0) ? 0
841 1361964 : : (iter < 2 * r) ? r
842 1000168 : : (iter < 4 * r) ? r - (iter - 2 * r)
843 656311 : : (iter < 6 * r) ? -r
844 321886 : : -r + (iter - 6 * r);
845 1809036 : if (nGeoLocPixel >=
846 1484612 : static_cast<int>(psTransform->nGeoLocXSize) - sx ||
847 : nGeoLocLine >=
848 1249792 : static_cast<int>(psTransform->nGeoLocYSize) - sy)
849 : {
850 324429 : continue;
851 : }
852 1160183 : const int iX = nGeoLocPixel + sx;
853 1160183 : const int iY = nGeoLocLine + sy;
854 1160183 : if (iX >= -1 || iY >= -1)
855 : {
856 : double x0, y0, x1, y1, x2, y2, x3, y3;
857 :
858 1159874 : if (!PixelLineToXY(psTransform, iX, iY, x0, y0) ||
859 1120228 : !PixelLineToXY(psTransform, iX + 1, iY, x2, y2) ||
860 3391710 : !PixelLineToXY(psTransform, iX, iY + 1, x1, y1) ||
861 1111616 : !PixelLineToXY(psTransform, iX + 1, iY + 1, x3, y3))
862 : {
863 48686 : continue;
864 : }
865 :
866 1111188 : int nIters = 1;
867 : // For a bounding box crossing the anti-meridian, check
868 : // both around -180 and +180 deg.
869 1111188 : if (psTransform
870 1111188 : ->bGeographicSRSWithMinus180Plus180LongRange &&
871 965179 : std::fabs(x0) > 170 && std::fabs(x1) > 170 &&
872 51767 : std::fabs(x2) > 170 && std::fabs(x3) > 170 &&
873 37344 : (std::fabs(x1 - x0) > 180 ||
874 37032 : std::fabs(x2 - x0) > 180 ||
875 30492 : std::fabs(x3 - x0) > 180))
876 : {
877 7144 : nIters = 2;
878 7144 : if (x0 > 0)
879 40 : x0 -= 360;
880 7144 : if (x1 > 0)
881 312 : x1 -= 360;
882 7144 : if (x2 > 0)
883 6832 : x2 -= 360;
884 7144 : if (x3 > 0)
885 7144 : x3 -= 360;
886 : }
887 2229399 : for (int iIter = 0; !bDone && iIter < nIters; ++iIter)
888 : {
889 1118214 : if (iIter == 1)
890 : {
891 7026 : x0 += 360;
892 7026 : x1 += 360;
893 7026 : x2 += 360;
894 7026 : x3 += 360;
895 : }
896 1118214 : oRing.setPoint(0, x0, y0);
897 1118214 : oRing.setPoint(1, x2, y2);
898 1118214 : oRing.setPoint(2, x3, y3);
899 1118214 : oRing.setPoint(3, x1, y1);
900 1118214 : oRing.setPoint(4, x0, y0);
901 2132786 : if (oRing.isPointInRing(&oPoint) ||
902 1014572 : oRing.isPointOnRingBoundary(&oPoint))
903 : {
904 105074 : double dfX = static_cast<double>(iX);
905 105074 : double dfY = static_cast<double>(iY);
906 105074 : GDALInverseBilinearInterpolation(
907 : dfGeoX, dfGeoY, x0, y0, x1, y1, x2, y2, x3,
908 : y3, dfX, dfY);
909 :
910 105074 : dfX = (dfX + dfGeorefConventionOffset) *
911 105074 : psTransform->dfPIXEL_STEP +
912 105074 : psTransform->dfPIXEL_OFFSET;
913 105074 : dfY = (dfY + dfGeorefConventionOffset) *
914 105074 : psTransform->dfLINE_STEP +
915 105074 : psTransform->dfLINE_OFFSET;
916 :
917 : #ifdef DEBUG_GEOLOC_REALLY_VERBOSE
918 : CPLDebug("GEOLOC",
919 : "value before adjustment: %f %f, "
920 : "after adjustment: %f %f",
921 : padfX[i], padfY[i], dfX, dfY);
922 : #endif
923 :
924 105074 : padfX[i] = dfX;
925 105074 : padfY[i] = dfY;
926 :
927 105074 : bDone = true;
928 : }
929 : }
930 : }
931 : }
932 : }
933 122648 : if (!bDone)
934 : {
935 17574 : bSuccess = FALSE;
936 17574 : panSuccess[i] = FALSE;
937 17574 : padfX[i] = HUGE_VAL;
938 17574 : padfY[i] = HUGE_VAL;
939 17574 : continue;
940 : }
941 :
942 105074 : panSuccess[i] = TRUE;
943 : }
944 : }
945 :
946 31156 : return bSuccess;
947 : }
948 :
949 : /*! @endcond */
950 :
951 : /************************************************************************/
952 : /* GDALInverseBilinearInterpolation() */
953 : /************************************************************************/
954 :
955 : // (i,j) before the call should correspond to the input coordinates that give
956 : // (x0,y0) as output of the forward interpolation
957 : // After the call it will be updated to the input coordinates that give (x,y)
958 : // This assumes that (x,y) is within the polygon formed by
959 : // (x0, y0), (x2, y2), (x3, y3), (x1, y1), (x0, y0)
960 243336 : void GDALInverseBilinearInterpolation(const double x, const double y,
961 : const double x0, const double y0,
962 : const double x1, const double y1,
963 : const double x2, const double y2,
964 : const double x3, const double y3,
965 : double &i, double &j)
966 : {
967 : // Exact inverse bilinear interpolation method.
968 : // Maths from https://stackoverflow.com/a/812077
969 :
970 243336 : const double A = (x0 - x) * (y0 - y2) - (y0 - y) * (x0 - x2);
971 243336 : const double B = (((x0 - x) * (y1 - y3) - (y0 - y) * (x1 - x3)) +
972 243336 : ((x1 - x) * (y0 - y2) - (y1 - y) * (x0 - x2))) /
973 : 2;
974 243336 : const double C = (x1 - x) * (y1 - y3) - (y1 - y) * (x1 - x3);
975 243336 : const double denom = A - 2 * B + C;
976 : double s;
977 243336 : const double magnitudeOfValues = fabs(A) + fabs(B) + fabs(C);
978 243336 : if (fabs(denom) <= 1e-12 * magnitudeOfValues)
979 : {
980 : // Happens typically when the x_i,y_i points form a rectangle
981 : // Can also happen when they form a triangle.
982 130885 : s = A / (A - C);
983 : }
984 : else
985 : {
986 112451 : const double sqrtTerm = sqrt(B * B - A * C);
987 112451 : const double s1 = ((A - B) + sqrtTerm) / denom;
988 112451 : const double s2 = ((A - B) - sqrtTerm) / denom;
989 112451 : if (s1 < 0 || s1 > 1)
990 95924 : s = s2;
991 : else
992 16527 : s = s1;
993 : }
994 :
995 243336 : const double t_denom_x = (1 - s) * (x0 - x2) + s * (x1 - x3);
996 243336 : if (fabs(t_denom_x) > 1e-12 * magnitudeOfValues)
997 : {
998 241826 : i += ((1 - s) * (x0 - x) + s * (x1 - x)) / t_denom_x;
999 : }
1000 : else
1001 : {
1002 1510 : const double t_denom_y = (1 - s) * (y0 - y2) + s * (y1 - y3);
1003 1510 : if (fabs(t_denom_y) > 1e-12 * magnitudeOfValues)
1004 : {
1005 1510 : i += ((1 - s) * (y0 - y) + s * (y1 - y)) / t_denom_y;
1006 : }
1007 : }
1008 :
1009 243336 : j += s;
1010 243336 : }
1011 :
1012 : /************************************************************************/
1013 : /* GeoLocGenerateBackMap() */
1014 : /************************************************************************/
1015 :
1016 : /*! @cond Doxygen_Suppress */
1017 :
1018 : template <class Accessors>
1019 45 : bool GDALGeoLoc<Accessors>::GenerateBackMap(
1020 : GDALGeoLocTransformInfo *psTransform)
1021 :
1022 : {
1023 45 : CPLDebug("GEOLOC", "Starting backmap generation");
1024 45 : const int nXSize = psTransform->nGeoLocXSize;
1025 45 : const int nYSize = psTransform->nGeoLocYSize;
1026 :
1027 : /* -------------------------------------------------------------------- */
1028 : /* Decide on resolution for backmap. We aim for slightly */
1029 : /* higher resolution than the source but we can't easily */
1030 : /* establish how much dead space there is in the backmap, so it */
1031 : /* is approximate. */
1032 : /* -------------------------------------------------------------------- */
1033 45 : const double dfTargetPixels =
1034 45 : static_cast<double>(nXSize) * nYSize * psTransform->dfOversampleFactor;
1035 : const double dfPixelSizeSquare =
1036 45 : sqrt((psTransform->dfMaxX - psTransform->dfMinX) *
1037 45 : (psTransform->dfMaxY - psTransform->dfMinY) / dfTargetPixels);
1038 45 : if (dfPixelSizeSquare == 0.0)
1039 : {
1040 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid pixel size for backmap");
1041 0 : return false;
1042 : }
1043 :
1044 45 : const double dfMinX = psTransform->dfMinX - dfPixelSizeSquare / 2.0;
1045 45 : const double dfMaxX = psTransform->dfMaxX + dfPixelSizeSquare / 2.0;
1046 45 : const double dfMaxY = psTransform->dfMaxY + dfPixelSizeSquare / 2.0;
1047 45 : const double dfMinY = psTransform->dfMinY - dfPixelSizeSquare / 2.0;
1048 45 : const double dfBMXSize = std::ceil((dfMaxX - dfMinX) / dfPixelSizeSquare);
1049 45 : const double dfBMYSize = std::ceil((dfMaxY - dfMinY) / dfPixelSizeSquare);
1050 :
1051 : // +2 : +1 due to afterwards nBMXSize++, and another +1 as security margin
1052 : // for other computations.
1053 45 : if (!(dfBMXSize > 0 && dfBMXSize + 2 < INT_MAX) ||
1054 45 : !(dfBMYSize > 0 && dfBMYSize + 2 < INT_MAX))
1055 : {
1056 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %f x %f",
1057 : dfBMXSize, dfBMYSize);
1058 0 : return false;
1059 : }
1060 :
1061 45 : int nBMXSize = static_cast<int>(dfBMXSize);
1062 45 : int nBMYSize = static_cast<int>(dfBMYSize);
1063 :
1064 90 : if (static_cast<size_t>(1 + nBMYSize) >
1065 45 : std::numeric_limits<size_t>::max() / static_cast<size_t>(1 + nBMXSize))
1066 : {
1067 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %f x %f",
1068 : dfBMXSize, dfBMYSize);
1069 0 : return false;
1070 : }
1071 :
1072 45 : const double dfPixelXSize = (dfMaxX - dfMinX) / nBMXSize;
1073 45 : const double dfPixelYSize = (dfMaxY - dfMinY) / nBMYSize;
1074 :
1075 : // Extra pixel for right-edge and bottom-edge extensions in TOP_LEFT_CORNER
1076 : // convention.
1077 45 : nBMXSize++;
1078 45 : nBMYSize++;
1079 45 : psTransform->nBackMapWidth = nBMXSize;
1080 45 : psTransform->nBackMapHeight = nBMYSize;
1081 :
1082 45 : psTransform->adfBackMapGeoTransform[0] = dfMinX;
1083 45 : psTransform->adfBackMapGeoTransform[1] = dfPixelXSize;
1084 45 : psTransform->adfBackMapGeoTransform[2] = 0.0;
1085 45 : psTransform->adfBackMapGeoTransform[3] = dfMaxY;
1086 45 : psTransform->adfBackMapGeoTransform[4] = 0.0;
1087 45 : psTransform->adfBackMapGeoTransform[5] = -dfPixelYSize;
1088 :
1089 : /* -------------------------------------------------------------------- */
1090 : /* Allocate backmap. */
1091 : /* -------------------------------------------------------------------- */
1092 45 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
1093 45 : if (!pAccessors->AllocateBackMap())
1094 0 : return false;
1095 :
1096 45 : const double dfGeorefConventionOffset =
1097 45 : psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
1098 :
1099 45 : const auto UpdateBackmap =
1100 8962995 : [&](int iBMX, int iBMY, double dfX, double dfY, double tempwt)
1101 : {
1102 822655 : const auto fBMX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1103 822655 : const auto fBMY = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
1104 822655 : const float fUpdatedBMX =
1105 : fBMX +
1106 822655 : static_cast<float>(tempwt * ((dfX + dfGeorefConventionOffset) *
1107 822655 : psTransform->dfPIXEL_STEP +
1108 822655 : psTransform->dfPIXEL_OFFSET));
1109 822655 : const float fUpdatedBMY =
1110 : fBMY +
1111 822655 : static_cast<float>(tempwt * ((dfY + dfGeorefConventionOffset) *
1112 822655 : psTransform->dfLINE_STEP +
1113 822655 : psTransform->dfLINE_OFFSET));
1114 822655 : const float fUpdatedWeight =
1115 822655 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) +
1116 822655 : static_cast<float>(tempwt);
1117 :
1118 : // Only update the backmap if the updated averaged value results in a
1119 : // geoloc position that isn't too different from the original one.
1120 : // (there's no guarantee that if padfGeoLocX[i] ~= padfGeoLoc[j],
1121 : // padfGeoLoc[alpha * i + (1 - alpha) * j] ~= padfGeoLoc[i] )
1122 822655 : if (fUpdatedWeight > 0)
1123 : {
1124 822652 : const float fX = fUpdatedBMX / fUpdatedWeight;
1125 822652 : const float fY = fUpdatedBMY / fUpdatedWeight;
1126 822652 : const double dfGeoLocPixel =
1127 822652 : (double(fX) - psTransform->dfPIXEL_OFFSET) /
1128 822652 : psTransform->dfPIXEL_STEP -
1129 : dfGeorefConventionOffset;
1130 822652 : const double dfGeoLocLine =
1131 822652 : (double(fY) - psTransform->dfLINE_OFFSET) /
1132 822652 : psTransform->dfLINE_STEP -
1133 : dfGeorefConventionOffset;
1134 822652 : int iXAvg = static_cast<int>(std::max(0.0, dfGeoLocPixel));
1135 822652 : iXAvg = std::min(iXAvg, psTransform->nGeoLocXSize - 1);
1136 822652 : int iYAvg = static_cast<int>(std::max(0.0, dfGeoLocLine));
1137 822652 : iYAvg = std::min(iYAvg, psTransform->nGeoLocYSize - 1);
1138 822652 : const double dfGLX = pAccessors->geolocXAccessor.Get(iXAvg, iYAvg);
1139 822652 : const double dfGLY = pAccessors->geolocYAccessor.Get(iXAvg, iYAvg);
1140 :
1141 822652 : const unsigned iX = static_cast<unsigned>(dfX);
1142 822652 : const unsigned iY = static_cast<unsigned>(dfY);
1143 1645290 : if (!(psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX) &&
1144 822642 : ((iX >= static_cast<unsigned>(nXSize - 1) ||
1145 808686 : iY >= static_cast<unsigned>(nYSize - 1)) ||
1146 798875 : (fabs(dfGLX - pAccessors->geolocXAccessor.Get(iX, iY)) <=
1147 798875 : 2 * dfPixelXSize &&
1148 756427 : fabs(dfGLY - pAccessors->geolocYAccessor.Get(iX, iY)) <=
1149 756427 : 2 * dfPixelYSize)))
1150 : {
1151 780051 : pAccessors->backMapXAccessor.Set(iBMX, iBMY, fUpdatedBMX);
1152 780051 : pAccessors->backMapYAccessor.Set(iBMX, iBMY, fUpdatedBMY);
1153 780051 : pAccessors->backMapWeightAccessor.Set(iBMX, iBMY,
1154 : fUpdatedWeight);
1155 : }
1156 : }
1157 : };
1158 :
1159 : // Keep those objects in this outer scope, so they are reused, to
1160 : // save memory allocations.
1161 90 : OGRPoint oPoint;
1162 90 : OGRLinearRing oRing;
1163 45 : oRing.setNumPoints(5);
1164 :
1165 : /* -------------------------------------------------------------------- */
1166 : /* Run through the whole geoloc array forward projecting and */
1167 : /* pushing into the backmap. */
1168 : /* -------------------------------------------------------------------- */
1169 :
1170 : // Iterate over the (i,j) pixel space of the geolocation array, in a
1171 : // sufficiently dense way that if the geolocation array expressed an affine
1172 : // transformation, we would hit every node of the backmap.
1173 45 : const double dfStep = 1. / psTransform->dfOversampleFactor;
1174 :
1175 45 : constexpr int TILE_SIZE = GDALGeoLocDatasetAccessors::TILE_SIZE;
1176 45 : const int nYBlocks = DIV_ROUND_UP(nYSize, TILE_SIZE);
1177 45 : const int nXBlocks = DIV_ROUND_UP(nXSize, TILE_SIZE);
1178 :
1179 : // First compute for each block the start end ending floating-point
1180 : // pixel/line values
1181 90 : std::vector<std::pair<double, double>> yStartEnd(nYBlocks + 1);
1182 90 : std::vector<std::pair<double, double>> xStartEnd(nXBlocks + 1);
1183 :
1184 : {
1185 45 : int iYBlockLast = -1;
1186 45 : double dfY = -dfStep;
1187 2388 : for (; dfY <= static_cast<double>(nYSize) + 2 * dfStep; dfY += dfStep)
1188 : {
1189 2343 : const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1190 2343 : if (iYBlock != iYBlockLast)
1191 : {
1192 47 : CPLAssert(iYBlock == iYBlockLast + 1);
1193 47 : if (iYBlockLast >= 0)
1194 2 : yStartEnd[iYBlockLast].second = dfY + dfStep / 10;
1195 47 : yStartEnd[iYBlock].first = dfY;
1196 47 : iYBlockLast = iYBlock;
1197 : }
1198 : }
1199 45 : const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1200 45 : yStartEnd[iYBlock].second = dfY + dfStep / 10;
1201 : }
1202 :
1203 : {
1204 45 : int iXBlockLast = -1;
1205 45 : double dfX = -dfStep;
1206 2766 : for (; dfX <= static_cast<double>(nXSize) + 2 * dfStep; dfX += dfStep)
1207 : {
1208 2721 : const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1209 2721 : if (iXBlock != iXBlockLast)
1210 : {
1211 47 : CPLAssert(iXBlock == iXBlockLast + 1);
1212 47 : if (iXBlockLast >= 0)
1213 2 : xStartEnd[iXBlockLast].second = dfX + dfStep / 10;
1214 47 : xStartEnd[iXBlock].first = dfX;
1215 47 : iXBlockLast = iXBlock;
1216 : }
1217 : }
1218 45 : const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1219 45 : xStartEnd[iXBlock].second = dfX + dfStep / 10;
1220 : }
1221 :
1222 92 : for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
1223 : {
1224 96 : for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
1225 : {
1226 : #if 0
1227 : CPLDebug("Process geoloc block (y=%d,x=%d) for y in [%f, %f] and x in [%f, %f]",
1228 : iYBlock, iXBlock,
1229 : yStartEnd[iYBlock].first, yStartEnd[iYBlock].second,
1230 : xStartEnd[iXBlock].first, xStartEnd[iXBlock].second);
1231 : #endif
1232 2655 : for (double dfY = yStartEnd[iYBlock].first;
1233 2655 : dfY < yStartEnd[iYBlock].second; dfY += dfStep)
1234 : {
1235 427339 : for (double dfX = xStartEnd[iXBlock].first;
1236 427339 : dfX < xStartEnd[iXBlock].second; dfX += dfStep)
1237 : {
1238 : // Use forward geolocation array interpolation to compute
1239 : // the georeferenced position corresponding to (dfX, dfY)
1240 : double dfGeoLocX;
1241 : double dfGeoLocY;
1242 424733 : if (!PixelLineToXY(psTransform, dfX, dfY, dfGeoLocX,
1243 : dfGeoLocY))
1244 145378 : continue;
1245 :
1246 : // Compute the floating point coordinates in the pixel space
1247 : // of the backmap
1248 418051 : const double dBMX = static_cast<double>(
1249 418051 : (dfGeoLocX - dfMinX) / dfPixelXSize);
1250 :
1251 418051 : const double dBMY = static_cast<double>(
1252 418051 : (dfMaxY - dfGeoLocY) / dfPixelYSize);
1253 :
1254 : // Get top left index by truncation
1255 418051 : const int iBMX = static_cast<int>(std::floor(dBMX));
1256 418051 : const int iBMY = static_cast<int>(std::floor(dBMY));
1257 :
1258 418051 : if (iBMX >= 0 && iBMX < nBMXSize && iBMY >= 0 &&
1259 : iBMY < nBMYSize)
1260 : {
1261 : // Compute the georeferenced position of the top-left
1262 : // index of the backmap
1263 416300 : double dfGeoX = dfMinX + iBMX * dfPixelXSize;
1264 416300 : const double dfGeoY = dfMaxY - iBMY * dfPixelYSize;
1265 :
1266 416300 : bool bMatchingGeoLocCellFound = false;
1267 :
1268 416300 : const int nOuterIters =
1269 416300 : psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
1270 311962 : fabs(dfGeoX) >= 180
1271 : ? 2
1272 : : 1;
1273 :
1274 832624 : for (int iOuterIter = 0; iOuterIter < nOuterIters;
1275 : ++iOuterIter)
1276 : {
1277 416324 : if (iOuterIter == 1 && dfGeoX >= 180)
1278 0 : dfGeoX -= 360;
1279 416324 : else if (iOuterIter == 1 && dfGeoX <= -180)
1280 24 : dfGeoX += 360;
1281 :
1282 : // Identify a cell (quadrilateral in georeferenced
1283 : // space) in the geolocation array in which dfGeoX,
1284 : // dfGeoY falls into.
1285 416324 : oPoint.setX(dfGeoX);
1286 416324 : oPoint.setY(dfGeoY);
1287 416324 : const int nX = static_cast<int>(std::floor(dfX));
1288 416324 : const int nY = static_cast<int>(std::floor(dfY));
1289 1196542 : for (int sx = -1;
1290 1196542 : !bMatchingGeoLocCellFound && sx <= 0; sx++)
1291 : {
1292 2290693 : for (int sy = -1;
1293 2290693 : !bMatchingGeoLocCellFound && sy <= 0; sy++)
1294 : {
1295 1511762 : const int pixel = nX + sx;
1296 1511762 : const int line = nY + sy;
1297 : double x0, y0, x1, y1, x2, y2, x3, y3;
1298 1511762 : if (!PixelLineToXY(psTransform, pixel, line,
1299 1511194 : x0, y0) ||
1300 1511194 : !PixelLineToXY(psTransform, pixel + 1,
1301 1510974 : line, x2, y2) ||
1302 1510974 : !PixelLineToXY(psTransform, pixel,
1303 3022950 : line + 1, x1, y1) ||
1304 1510544 : !PixelLineToXY(psTransform, pixel + 1,
1305 : line + 1, x3, y3))
1306 : {
1307 1288 : break;
1308 : }
1309 :
1310 1510474 : int nIters = 1;
1311 1510474 : if (psTransform
1312 1510474 : ->bGeographicSRSWithMinus180Plus180LongRange &&
1313 1178951 : std::fabs(x0) > 170 &&
1314 13609 : std::fabs(x1) > 170 &&
1315 13359 : std::fabs(x2) > 170 &&
1316 12098 : std::fabs(x3) > 170 &&
1317 11918 : (std::fabs(x1 - x0) > 180 ||
1318 11861 : std::fabs(x2 - x0) > 180 ||
1319 10499 : std::fabs(x3 - x0) > 180))
1320 : {
1321 1490 : nIters = 2;
1322 1490 : if (x0 > 0)
1323 3 : x0 -= 360;
1324 1490 : if (x1 > 0)
1325 60 : x1 -= 360;
1326 1490 : if (x2 > 0)
1327 1422 : x2 -= 360;
1328 1490 : if (x3 > 0)
1329 1487 : x3 -= 360;
1330 : }
1331 3022430 : for (int iIter = 0; iIter < nIters; ++iIter)
1332 : {
1333 1511964 : if (iIter == 1)
1334 : {
1335 1490 : x0 += 360;
1336 1490 : x1 += 360;
1337 1490 : x2 += 360;
1338 1490 : x3 += 360;
1339 : }
1340 :
1341 1511964 : oRing.setPoint(0, x0, y0);
1342 1511964 : oRing.setPoint(1, x2, y2);
1343 1511964 : oRing.setPoint(2, x3, y3);
1344 1511964 : oRing.setPoint(3, x1, y1);
1345 1511964 : oRing.setPoint(4, x0, y0);
1346 2885890 : if (oRing.isPointInRing(&oPoint) ||
1347 1373929 : oRing.isPointOnRingBoundary(
1348 : &oPoint))
1349 : {
1350 138238 : bMatchingGeoLocCellFound = true;
1351 138238 : double dfBMXValue = pixel;
1352 138238 : double dfBMYValue = line;
1353 138238 : GDALInverseBilinearInterpolation(
1354 : dfGeoX, dfGeoY, x0, y0, x1, y1,
1355 : x2, y2, x3, y3, dfBMXValue,
1356 : dfBMYValue);
1357 :
1358 138238 : dfBMXValue =
1359 138238 : (dfBMXValue +
1360 138238 : dfGeorefConventionOffset) *
1361 138238 : psTransform->dfPIXEL_STEP +
1362 138238 : psTransform->dfPIXEL_OFFSET;
1363 138238 : dfBMYValue =
1364 138238 : (dfBMYValue +
1365 138238 : dfGeorefConventionOffset) *
1366 138238 : psTransform->dfLINE_STEP +
1367 138238 : psTransform->dfLINE_OFFSET;
1368 :
1369 138238 : pAccessors->backMapXAccessor.Set(
1370 : iBMX, iBMY,
1371 : static_cast<float>(dfBMXValue));
1372 138238 : pAccessors->backMapYAccessor.Set(
1373 : iBMX, iBMY,
1374 : static_cast<float>(dfBMYValue));
1375 138238 : pAccessors->backMapWeightAccessor
1376 : .Set(iBMX, iBMY, 1.0f);
1377 : }
1378 : }
1379 : }
1380 : }
1381 : }
1382 416300 : if (bMatchingGeoLocCellFound)
1383 138238 : continue;
1384 : }
1385 :
1386 : // We will end up here in non-nominal cases, with nodata,
1387 : // holes, etc.
1388 :
1389 : // Check if the center is in range
1390 279813 : if (iBMX < -1 || iBMY < -1 || iBMX > nBMXSize ||
1391 : iBMY > nBMYSize)
1392 458 : continue;
1393 :
1394 279355 : const double fracBMX = dBMX - iBMX;
1395 279355 : const double fracBMY = dBMY - iBMY;
1396 :
1397 : // Check logic for top left pixel
1398 279049 : if ((iBMX >= 0) && (iBMY >= 0) && (iBMX < nBMXSize) &&
1399 558404 : (iBMY < nBMYSize) &&
1400 278062 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) !=
1401 : 1.0f)
1402 : {
1403 185849 : const double tempwt = (1.0 - fracBMX) * (1.0 - fracBMY);
1404 185849 : UpdateBackmap(iBMX, iBMY, dfX, dfY, tempwt);
1405 : }
1406 :
1407 : // Check logic for top right pixel
1408 278952 : if ((iBMY >= 0) && (iBMX + 1 < nBMXSize) &&
1409 558307 : (iBMY < nBMYSize) &&
1410 278312 : pAccessors->backMapWeightAccessor.Get(iBMX + 1, iBMY) !=
1411 : 1.0f)
1412 : {
1413 189767 : const double tempwt = fracBMX * (1.0 - fracBMY);
1414 189767 : UpdateBackmap(iBMX + 1, iBMY, dfX, dfY, tempwt);
1415 : }
1416 :
1417 : // Check logic for bottom right pixel
1418 557991 : if ((iBMX + 1 < nBMXSize) && (iBMY + 1 < nBMYSize) &&
1419 278636 : pAccessors->backMapWeightAccessor.Get(iBMX + 1,
1420 278636 : iBMY + 1) != 1.0f)
1421 : {
1422 226559 : const double tempwt = fracBMX * fracBMY;
1423 226559 : UpdateBackmap(iBMX + 1, iBMY + 1, dfX, dfY, tempwt);
1424 : }
1425 :
1426 : // Check logic for bottom left pixel
1427 279049 : if ((iBMX >= 0) && (iBMX < nBMXSize) &&
1428 836802 : (iBMY + 1 < nBMYSize) &&
1429 278398 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY + 1) !=
1430 : 1.0f)
1431 : {
1432 220480 : const double tempwt = (1.0 - fracBMX) * fracBMY;
1433 220480 : UpdateBackmap(iBMX, iBMY + 1, dfX, dfY, tempwt);
1434 : }
1435 : }
1436 : }
1437 : }
1438 : }
1439 :
1440 : // Each pixel in the backmap may have multiple entries.
1441 : // We now go in average it out using the weights
1442 151 : START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, (void)0,
1443 : iXStart, iXEnd, iYStart, iYEnd)
1444 : {
1445 2277 : for (int iY = iYStart; iY < iYEnd; ++iY)
1446 : {
1447 328049 : for (int iX = iXStart; iX < iXEnd; ++iX)
1448 : {
1449 : // Check if pixel was only touch during neighbor scan
1450 : // But no real weight was added as source point matched
1451 : // backmap grid node
1452 465582 : const auto weight =
1453 325833 : pAccessors->backMapWeightAccessor.Get(iX, iY);
1454 325833 : if (weight > 0)
1455 : {
1456 236318 : pAccessors->backMapXAccessor.Set(
1457 : iX, iY,
1458 170397 : pAccessors->backMapXAccessor.Get(iX, iY) / weight);
1459 236318 : pAccessors->backMapYAccessor.Set(
1460 : iX, iY,
1461 170397 : pAccessors->backMapYAccessor.Get(iX, iY) / weight);
1462 : }
1463 : else
1464 : {
1465 155436 : pAccessors->backMapXAccessor.Set(iX, iY, INVALID_BMXY);
1466 155436 : pAccessors->backMapYAccessor.Set(iX, iY, INVALID_BMXY);
1467 : }
1468 : }
1469 : }
1470 : }
1471 : END_ITER_PER_BLOCK
1472 :
1473 45 : pAccessors->FreeWghtsBackMap();
1474 :
1475 : // Fill holes in backmap
1476 45 : auto poBackmapDS = pAccessors->GetBackmapDataset();
1477 :
1478 45 : pAccessors->FlushBackmapCaches();
1479 :
1480 : #ifdef DEBUG_GEOLOC
1481 : if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1482 : {
1483 : poBackmapDS->SetGeoTransform(psTransform->adfBackMapGeoTransform);
1484 : GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1485 : "/tmp/geoloc_before_fill.tif", poBackmapDS,
1486 : false, nullptr, nullptr, nullptr));
1487 : }
1488 : #endif
1489 :
1490 45 : constexpr double dfMaxSearchDist = 3.0;
1491 45 : constexpr int nSmoothingIterations = 1;
1492 135 : for (int i = 1; i <= 2; i++)
1493 : {
1494 90 : GDALFillNodata(GDALRasterBand::ToHandle(poBackmapDS->GetRasterBand(i)),
1495 : nullptr, dfMaxSearchDist,
1496 : 0, // unused parameter
1497 : nSmoothingIterations, nullptr, nullptr, nullptr);
1498 : }
1499 :
1500 : #ifdef DEBUG_GEOLOC
1501 : if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1502 : {
1503 : GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1504 : "/tmp/geoloc_after_fill.tif", poBackmapDS,
1505 : false, nullptr, nullptr, nullptr));
1506 : }
1507 : #endif
1508 :
1509 : // A final hole filling logic, proceeding line by line, and filling
1510 : // holes when the backmap values surrounding the hole are close enough.
1511 : struct LastValidStruct
1512 : {
1513 : int iX = -1;
1514 : float bmX = 0;
1515 : };
1516 :
1517 45 : std::vector<LastValidStruct> lastValid(TILE_SIZE);
1518 225 : const auto reinitLine = [&lastValid]()
1519 : {
1520 45 : const size_t nSize = lastValid.size();
1521 45 : lastValid.clear();
1522 45 : lastValid.resize(nSize);
1523 : };
1524 151 : START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, reinitLine(),
1525 : iXStart, iXEnd, iYStart, iYEnd)
1526 : {
1527 61 : const int iYCount = iYEnd - iYStart;
1528 2277 : for (int iYIter = 0; iYIter < iYCount; ++iYIter)
1529 : {
1530 2216 : int iLastValidIX = lastValid[iYIter].iX;
1531 2216 : float bmXLastValid = lastValid[iYIter].bmX;
1532 2216 : const int iBMY = iYStart + iYIter;
1533 328049 : for (int iBMX = iXStart; iBMX < iXEnd; ++iBMX)
1534 : {
1535 325833 : const float bmX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1536 325833 : if (bmX == INVALID_BMXY)
1537 127144 : continue;
1538 199241 : if (iLastValidIX != -1 && iBMX > iLastValidIX + 1 &&
1539 552 : fabs(bmX - bmXLastValid) <= 2)
1540 : {
1541 354 : const float bmY =
1542 239 : pAccessors->backMapYAccessor.Get(iBMX, iBMY);
1543 354 : const float bmYLastValid =
1544 239 : pAccessors->backMapYAccessor.Get(iLastValidIX, iBMY);
1545 239 : if (fabs(bmY - bmYLastValid) <= 2)
1546 : {
1547 266 : for (int iBMXInner = iLastValidIX + 1; iBMXInner < iBMX;
1548 : ++iBMXInner)
1549 : {
1550 145 : const float alpha =
1551 145 : static_cast<float>(iBMXInner - iLastValidIX) /
1552 145 : (iBMX - iLastValidIX);
1553 145 : pAccessors->backMapXAccessor.Set(
1554 : iBMXInner, iBMY,
1555 145 : (1.0f - alpha) * bmXLastValid + alpha * bmX);
1556 145 : pAccessors->backMapYAccessor.Set(
1557 : iBMXInner, iBMY,
1558 145 : (1.0f - alpha) * bmYLastValid + alpha * bmY);
1559 : }
1560 : }
1561 : }
1562 198689 : iLastValidIX = iBMX;
1563 198689 : bmXLastValid = bmX;
1564 : }
1565 2216 : lastValid[iYIter].iX = iLastValidIX;
1566 2216 : lastValid[iYIter].bmX = bmXLastValid;
1567 : }
1568 : }
1569 : END_ITER_PER_BLOCK
1570 :
1571 : #ifdef DEBUG_GEOLOC
1572 : if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1573 : {
1574 : pAccessors->FlushBackmapCaches();
1575 :
1576 : GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1577 : "/tmp/geoloc_after_line_fill.tif", poBackmapDS,
1578 : false, nullptr, nullptr, nullptr));
1579 : }
1580 : #endif
1581 :
1582 45 : pAccessors->ReleaseBackmapDataset(poBackmapDS);
1583 45 : CPLDebug("GEOLOC", "Ending backmap generation");
1584 :
1585 45 : return true;
1586 : }
1587 :
1588 : /*! @endcond */
1589 :
1590 : /************************************************************************/
1591 : /* GDALGeoLocRescale() */
1592 : /************************************************************************/
1593 :
1594 4 : static void GDALGeoLocRescale(char **&papszMD, const char *pszItem,
1595 : double dfRatio, double dfDefaultVal)
1596 : {
1597 : const double dfVal =
1598 4 : dfRatio * CPLAtofM(CSLFetchNameValueDef(
1599 4 : papszMD, pszItem, CPLSPrintf("%.17g", dfDefaultVal)));
1600 :
1601 4 : papszMD = CSLSetNameValue(papszMD, pszItem, CPLSPrintf("%.17g", dfVal));
1602 4 : }
1603 :
1604 : /************************************************************************/
1605 : /* GDALCreateSimilarGeoLocTransformer() */
1606 : /************************************************************************/
1607 :
1608 1 : static void *GDALCreateSimilarGeoLocTransformer(void *hTransformArg,
1609 : double dfRatioX,
1610 : double dfRatioY)
1611 : {
1612 1 : VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarGeoLocTransformer",
1613 : nullptr);
1614 :
1615 1 : GDALGeoLocTransformInfo *psInfo =
1616 : static_cast<GDALGeoLocTransformInfo *>(hTransformArg);
1617 :
1618 1 : char **papszGeolocationInfo = CSLDuplicate(psInfo->papszGeolocationInfo);
1619 :
1620 1 : if (dfRatioX != 1.0 || dfRatioY != 1.0)
1621 : {
1622 1 : GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_OFFSET", dfRatioX, 0.0);
1623 1 : GDALGeoLocRescale(papszGeolocationInfo, "LINE_OFFSET", dfRatioY, 0.0);
1624 1 : GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_STEP", 1.0 / dfRatioX,
1625 : 1.0);
1626 1 : GDALGeoLocRescale(papszGeolocationInfo, "LINE_STEP", 1.0 / dfRatioY,
1627 : 1.0);
1628 : }
1629 :
1630 : auto psInfoNew =
1631 2 : static_cast<GDALGeoLocTransformInfo *>(GDALCreateGeoLocTransformer(
1632 1 : nullptr, papszGeolocationInfo, psInfo->bReversed));
1633 1 : psInfoNew->dfOversampleFactor = psInfo->dfOversampleFactor;
1634 :
1635 1 : CSLDestroy(papszGeolocationInfo);
1636 :
1637 1 : return psInfoNew;
1638 : }
1639 :
1640 : /************************************************************************/
1641 : /* GDALCreateGeolocationMetadata() */
1642 : /************************************************************************/
1643 :
1644 : /** Synthesize the content of a GEOLOCATION metadata domain from a
1645 : * geolocation dataset.
1646 : *
1647 : * This is used when doing gdalwarp -to GEOLOC_ARRAY=some.tif
1648 : */
1649 10 : CPLStringList GDALCreateGeolocationMetadata(GDALDatasetH hBaseDS,
1650 : const char *pszGeolocationDataset,
1651 : bool bIsSource)
1652 : {
1653 20 : CPLStringList aosMD;
1654 :
1655 : // Open geolocation dataset
1656 : auto poGeolocDS = std::unique_ptr<GDALDataset>(
1657 20 : GDALDataset::Open(pszGeolocationDataset, GDAL_OF_RASTER));
1658 10 : if (poGeolocDS == nullptr)
1659 : {
1660 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dataset: %s",
1661 : pszGeolocationDataset);
1662 2 : return CPLStringList();
1663 : }
1664 8 : const int nGeoLocXSize = poGeolocDS->GetRasterXSize();
1665 8 : const int nGeoLocYSize = poGeolocDS->GetRasterYSize();
1666 8 : if (nGeoLocXSize == 0 || nGeoLocYSize == 0)
1667 : {
1668 0 : CPLError(CE_Failure, CPLE_AppDefined,
1669 : "Invalid dataset dimension for %s: %dx%d",
1670 : pszGeolocationDataset, nGeoLocXSize, nGeoLocYSize);
1671 0 : return CPLStringList();
1672 : }
1673 :
1674 : // Import the GEOLOCATION metadata from the geolocation dataset, if existing
1675 8 : auto papszGeolocMDFromGeolocDS = poGeolocDS->GetMetadata("GEOLOCATION");
1676 8 : if (papszGeolocMDFromGeolocDS)
1677 3 : aosMD = CSLDuplicate(papszGeolocMDFromGeolocDS);
1678 :
1679 8 : aosMD.SetNameValue("X_DATASET", pszGeolocationDataset);
1680 8 : aosMD.SetNameValue("Y_DATASET", pszGeolocationDataset);
1681 :
1682 : // Set X_BAND, Y_BAND to 1, 2 if they are not specified in the initial
1683 : // GEOLOC metadata domain.and the geolocation dataset has 2 bands.
1684 13 : if (aosMD.FetchNameValue("X_BAND") == nullptr &&
1685 5 : aosMD.FetchNameValue("Y_BAND") == nullptr)
1686 : {
1687 5 : if (poGeolocDS->GetRasterCount() != 2)
1688 : {
1689 1 : CPLError(CE_Failure, CPLE_AppDefined,
1690 : "Expected 2 bands for %s. Got %d", pszGeolocationDataset,
1691 : poGeolocDS->GetRasterCount());
1692 1 : return CPLStringList();
1693 : }
1694 4 : aosMD.SetNameValue("X_BAND", "1");
1695 4 : aosMD.SetNameValue("Y_BAND", "2");
1696 : }
1697 :
1698 : // Set the geoloc SRS from the geolocation dataset SRS if there's no
1699 : // explicit one in the initial GEOLOC metadata domain.
1700 7 : if (aosMD.FetchNameValue("SRS") == nullptr)
1701 : {
1702 7 : auto poSRS = poGeolocDS->GetSpatialRef();
1703 7 : if (poSRS)
1704 : {
1705 3 : char *pszWKT = nullptr;
1706 3 : poSRS->exportToWkt(&pszWKT);
1707 3 : aosMD.SetNameValue("SRS", pszWKT);
1708 3 : CPLFree(pszWKT);
1709 : }
1710 : }
1711 7 : if (aosMD.FetchNameValue("SRS") == nullptr)
1712 : {
1713 4 : aosMD.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);
1714 : }
1715 :
1716 : // Set default values for PIXEL/LINE_OFFSET/STEP if not present.
1717 7 : if (aosMD.FetchNameValue("PIXEL_OFFSET") == nullptr)
1718 4 : aosMD.SetNameValue("PIXEL_OFFSET", "0");
1719 :
1720 7 : if (aosMD.FetchNameValue("LINE_OFFSET") == nullptr)
1721 4 : aosMD.SetNameValue("LINE_OFFSET", "0");
1722 :
1723 7 : if (aosMD.FetchNameValue("PIXEL_STEP") == nullptr)
1724 : {
1725 : aosMD.SetNameValue(
1726 4 : "PIXEL_STEP", CPLSPrintf("%.17g", static_cast<double>(
1727 4 : GDALGetRasterXSize(hBaseDS)) /
1728 4 : nGeoLocXSize));
1729 : }
1730 :
1731 7 : if (aosMD.FetchNameValue("LINE_STEP") == nullptr)
1732 : {
1733 : aosMD.SetNameValue(
1734 4 : "LINE_STEP", CPLSPrintf("%.17g", static_cast<double>(
1735 4 : GDALGetRasterYSize(hBaseDS)) /
1736 4 : nGeoLocYSize));
1737 : }
1738 :
1739 7 : if (aosMD.FetchNameValue("GEOREFERENCING_CONVENTION") == nullptr)
1740 : {
1741 : const char *pszConvention =
1742 7 : poGeolocDS->GetMetadataItem("GEOREFERENCING_CONVENTION");
1743 7 : if (pszConvention)
1744 2 : aosMD.SetNameValue("GEOREFERENCING_CONVENTION", pszConvention);
1745 : }
1746 :
1747 14 : std::string osDebugMsg;
1748 7 : osDebugMsg = "Synthesized GEOLOCATION metadata for ";
1749 7 : osDebugMsg += bIsSource ? "source" : "target";
1750 7 : osDebugMsg += ":\n";
1751 72 : for (int i = 0; i < aosMD.size(); ++i)
1752 : {
1753 65 : osDebugMsg += " ";
1754 65 : osDebugMsg += aosMD[i];
1755 65 : osDebugMsg += '\n';
1756 : }
1757 7 : CPLDebug("GEOLOC", "%s", osDebugMsg.c_str());
1758 :
1759 7 : return aosMD;
1760 : }
1761 :
1762 : /************************************************************************/
1763 : /* GDALCreateGeoLocTransformer() */
1764 : /************************************************************************/
1765 :
1766 51 : void *GDALCreateGeoLocTransformerEx(GDALDatasetH hBaseDS,
1767 : CSLConstList papszGeolocationInfo,
1768 : int bReversed, const char *pszSourceDataset,
1769 : CSLConstList papszTransformOptions)
1770 :
1771 : {
1772 :
1773 51 : if (CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET") == nullptr ||
1774 49 : CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET") == nullptr ||
1775 49 : CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP") == nullptr ||
1776 49 : CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP") == nullptr ||
1777 149 : CSLFetchNameValue(papszGeolocationInfo, "X_BAND") == nullptr ||
1778 49 : CSLFetchNameValue(papszGeolocationInfo, "Y_BAND") == nullptr)
1779 : {
1780 2 : CPLError(CE_Failure, CPLE_AppDefined,
1781 : "Missing some geolocation fields in "
1782 : "GDALCreateGeoLocTransformer()");
1783 2 : return nullptr;
1784 : }
1785 :
1786 : /* -------------------------------------------------------------------- */
1787 : /* Initialize core info. */
1788 : /* -------------------------------------------------------------------- */
1789 : GDALGeoLocTransformInfo *psTransform =
1790 : static_cast<GDALGeoLocTransformInfo *>(
1791 49 : CPLCalloc(sizeof(GDALGeoLocTransformInfo), 1));
1792 :
1793 49 : psTransform->bReversed = CPL_TO_BOOL(bReversed);
1794 49 : psTransform->dfOversampleFactor = std::max(
1795 98 : 0.1,
1796 98 : std::min(2.0,
1797 49 : CPLAtof(CSLFetchNameValueDef(
1798 : papszTransformOptions, "GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1799 : CPLGetConfigOption("GDAL_GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1800 49 : "1.3")))));
1801 :
1802 49 : memcpy(psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE,
1803 : strlen(GDAL_GTI2_SIGNATURE));
1804 49 : psTransform->sTI.pszClassName = "GDALGeoLocTransformer";
1805 49 : psTransform->sTI.pfnTransform = GDALGeoLocTransform;
1806 49 : psTransform->sTI.pfnCleanup = GDALDestroyGeoLocTransformer;
1807 49 : psTransform->sTI.pfnSerialize = GDALSerializeGeoLocTransformer;
1808 49 : psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarGeoLocTransformer;
1809 :
1810 49 : psTransform->papszGeolocationInfo = CSLDuplicate(papszGeolocationInfo);
1811 :
1812 49 : psTransform->bGeographicSRSWithMinus180Plus180LongRange =
1813 49 : CPLTestBool(CSLFetchNameValueDef(
1814 : papszTransformOptions,
1815 : "GEOLOC_NORMALIZE_LONGITUDE_MINUS_180_PLUS_180", "NO"));
1816 :
1817 : /* -------------------------------------------------------------------- */
1818 : /* Pull geolocation info from the options/metadata. */
1819 : /* -------------------------------------------------------------------- */
1820 49 : psTransform->dfPIXEL_OFFSET =
1821 49 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET"));
1822 49 : psTransform->dfLINE_OFFSET =
1823 49 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET"));
1824 49 : psTransform->dfPIXEL_STEP =
1825 49 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP"));
1826 49 : psTransform->dfLINE_STEP =
1827 49 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP"));
1828 :
1829 49 : psTransform->bOriginIsTopLeftCorner = EQUAL(
1830 : CSLFetchNameValueDef(papszGeolocationInfo, "GEOREFERENCING_CONVENTION",
1831 : "TOP_LEFT_CORNER"),
1832 : "TOP_LEFT_CORNER");
1833 :
1834 : /* -------------------------------------------------------------------- */
1835 : /* Establish access to geolocation dataset(s). */
1836 : /* -------------------------------------------------------------------- */
1837 : const char *pszDSName =
1838 49 : CSLFetchNameValue(papszGeolocationInfo, "X_DATASET");
1839 49 : if (pszDSName != nullptr)
1840 : {
1841 98 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1842 49 : if (CPLTestBool(CSLFetchNameValueDef(
1843 50 : papszGeolocationInfo, "X_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1844 1 : (hBaseDS != nullptr || pszSourceDataset))
1845 : {
1846 8 : const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1847 7 : CPLGetDirnameSafe(pszSourceDataset
1848 : ? pszSourceDataset
1849 3 : : GDALGetDescription(hBaseDS))
1850 : .c_str(),
1851 4 : pszDSName);
1852 4 : psTransform->hDS_X =
1853 4 : GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1854 : }
1855 : else
1856 : {
1857 45 : psTransform->hDS_X = GDALOpenShared(pszDSName, GA_ReadOnly);
1858 : }
1859 : }
1860 : else
1861 : {
1862 0 : psTransform->hDS_X = hBaseDS;
1863 0 : if (hBaseDS)
1864 : {
1865 0 : GDALReferenceDataset(psTransform->hDS_X);
1866 0 : psTransform->papszGeolocationInfo =
1867 0 : CSLSetNameValue(psTransform->papszGeolocationInfo, "X_DATASET",
1868 : GDALGetDescription(hBaseDS));
1869 : }
1870 : }
1871 :
1872 49 : pszDSName = CSLFetchNameValue(papszGeolocationInfo, "Y_DATASET");
1873 49 : if (pszDSName != nullptr)
1874 : {
1875 98 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1876 49 : if (CPLTestBool(CSLFetchNameValueDef(
1877 50 : papszGeolocationInfo, "Y_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1878 1 : (hBaseDS != nullptr || pszSourceDataset))
1879 : {
1880 8 : const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1881 7 : CPLGetDirnameSafe(pszSourceDataset
1882 : ? pszSourceDataset
1883 3 : : GDALGetDescription(hBaseDS))
1884 : .c_str(),
1885 4 : pszDSName);
1886 4 : psTransform->hDS_Y =
1887 4 : GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1888 : }
1889 : else
1890 : {
1891 45 : psTransform->hDS_Y = GDALOpenShared(pszDSName, GA_ReadOnly);
1892 : }
1893 : }
1894 : else
1895 : {
1896 0 : psTransform->hDS_Y = hBaseDS;
1897 0 : if (hBaseDS)
1898 : {
1899 0 : GDALReferenceDataset(psTransform->hDS_Y);
1900 0 : psTransform->papszGeolocationInfo =
1901 0 : CSLSetNameValue(psTransform->papszGeolocationInfo, "Y_DATASET",
1902 : GDALGetDescription(hBaseDS));
1903 : }
1904 : }
1905 :
1906 49 : if (psTransform->hDS_X == nullptr || psTransform->hDS_Y == nullptr)
1907 : {
1908 0 : GDALDestroyGeoLocTransformer(psTransform);
1909 0 : return nullptr;
1910 : }
1911 :
1912 : /* -------------------------------------------------------------------- */
1913 : /* Get the band handles. */
1914 : /* -------------------------------------------------------------------- */
1915 : const int nXBand =
1916 49 : std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "X_BAND")));
1917 49 : psTransform->hBand_X = GDALGetRasterBand(psTransform->hDS_X, nXBand);
1918 :
1919 49 : psTransform->dfNoDataX = GDALGetRasterNoDataValue(
1920 : psTransform->hBand_X, &(psTransform->bHasNoData));
1921 :
1922 : const int nYBand =
1923 49 : std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "Y_BAND")));
1924 49 : psTransform->hBand_Y = GDALGetRasterBand(psTransform->hDS_Y, nYBand);
1925 :
1926 49 : if (psTransform->hBand_X == nullptr || psTransform->hBand_Y == nullptr)
1927 : {
1928 0 : GDALDestroyGeoLocTransformer(psTransform);
1929 0 : return nullptr;
1930 : }
1931 :
1932 49 : psTransform->bSwapXY = CPLTestBool(
1933 : CSLFetchNameValueDef(papszGeolocationInfo, "SWAP_XY", "NO"));
1934 :
1935 : /* -------------------------------------------------------------------- */
1936 : /* Check that X and Y bands have the same dimensions */
1937 : /* -------------------------------------------------------------------- */
1938 49 : const int nXSize_XBand = GDALGetRasterXSize(psTransform->hDS_X);
1939 49 : const int nYSize_XBand = GDALGetRasterYSize(psTransform->hDS_X);
1940 49 : const int nXSize_YBand = GDALGetRasterXSize(psTransform->hDS_Y);
1941 49 : const int nYSize_YBand = GDALGetRasterYSize(psTransform->hDS_Y);
1942 49 : if (nYSize_XBand == 1 || nYSize_YBand == 1)
1943 : {
1944 15 : if (nYSize_XBand != 1 || nYSize_YBand != 1)
1945 : {
1946 0 : CPLError(CE_Failure, CPLE_AppDefined,
1947 : "X_BAND and Y_BAND should have both nYSize == 1");
1948 0 : GDALDestroyGeoLocTransformer(psTransform);
1949 0 : return nullptr;
1950 : }
1951 : }
1952 34 : else if (nXSize_XBand != nXSize_YBand || nYSize_XBand != nYSize_YBand)
1953 : {
1954 0 : CPLError(CE_Failure, CPLE_AppDefined,
1955 : "X_BAND and Y_BAND do not have the same dimensions");
1956 0 : GDALDestroyGeoLocTransformer(psTransform);
1957 0 : return nullptr;
1958 : }
1959 :
1960 49 : if (nXSize_XBand <= 0 || nYSize_XBand <= 0 || nXSize_YBand <= 0 ||
1961 : nYSize_YBand <= 0)
1962 : {
1963 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid X_BAND / Y_BAND size");
1964 0 : GDALDestroyGeoLocTransformer(psTransform);
1965 0 : return nullptr;
1966 : }
1967 :
1968 : // Is it a regular grid ? That is:
1969 : // The XBAND contains the x coordinates for all lines.
1970 : // The YBAND contains the y coordinates for all columns.
1971 49 : const bool bIsRegularGrid = (nYSize_XBand == 1 && nYSize_YBand == 1);
1972 :
1973 49 : const int nXSize = nXSize_XBand;
1974 49 : const int nYSize = bIsRegularGrid ? nXSize_YBand : nYSize_XBand;
1975 :
1976 98 : if (static_cast<size_t>(nXSize) >
1977 49 : std::numeric_limits<size_t>::max() / static_cast<size_t>(nYSize))
1978 : {
1979 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %d x %d", nXSize,
1980 : nYSize);
1981 0 : GDALDestroyGeoLocTransformer(psTransform);
1982 0 : return nullptr;
1983 : }
1984 :
1985 49 : psTransform->nGeoLocXSize = nXSize;
1986 49 : psTransform->nGeoLocYSize = nYSize;
1987 :
1988 49 : if (hBaseDS && psTransform->dfPIXEL_OFFSET == 0 &&
1989 45 : psTransform->dfLINE_OFFSET == 0 && psTransform->dfPIXEL_STEP == 1 &&
1990 38 : psTransform->dfLINE_STEP == 1)
1991 : {
1992 75 : if (GDALGetRasterXSize(hBaseDS) > nXSize ||
1993 37 : GDALGetRasterYSize(hBaseDS) > nYSize)
1994 : {
1995 2 : CPLError(CE_Warning, CPLE_AppDefined,
1996 : "Geolocation array is %d x %d large, "
1997 : "whereas dataset is %d x %d large. Result might be "
1998 : "incorrect due to lack of values in geolocation array.",
1999 : nXSize, nYSize, GDALGetRasterXSize(hBaseDS),
2000 : GDALGetRasterYSize(hBaseDS));
2001 : }
2002 : }
2003 :
2004 : /* -------------------------------------------------------------------- */
2005 : /* Load the geolocation array. */
2006 : /* -------------------------------------------------------------------- */
2007 :
2008 : // The quadtree method is experimental. It simplifies the code
2009 : // significantly, but unfortunately burns more RAM and is slower.
2010 : const bool bUseQuadtree =
2011 49 : EQUAL(CPLGetConfigOption("GDAL_GEOLOC_INVERSE_METHOD", "BACKMAP"),
2012 : "QUADTREE");
2013 :
2014 : // Decide if we should C-arrays for geoloc and backmap, or on-disk
2015 : // temporary datasets.
2016 49 : const char *pszUseTempDatasets = CSLFetchNameValueDef(
2017 : papszTransformOptions, "GEOLOC_USE_TEMP_DATASETS",
2018 : CPLGetConfigOption("GDAL_GEOLOC_USE_TEMP_DATASETS", nullptr));
2019 49 : if (pszUseTempDatasets)
2020 4 : psTransform->bUseArray = !CPLTestBool(pszUseTempDatasets);
2021 : else
2022 : {
2023 45 : constexpr int MEGAPIXEL_LIMIT = 24;
2024 45 : psTransform->bUseArray =
2025 45 : nXSize < MEGAPIXEL_LIMIT * 1000 * 1000 / nYSize;
2026 45 : if (!psTransform->bUseArray)
2027 : {
2028 0 : CPLDebug("GEOLOC",
2029 : "Using temporary GTiff backing to store backmap, because "
2030 : "geoloc arrays require %d megapixels, exceeding the %d "
2031 : "megapixels limit. You can set the "
2032 : "GDAL_GEOLOC_USE_TEMP_DATASETS configuration option to "
2033 : "NO to force RAM storage of backmap",
2034 0 : static_cast<int>(static_cast<int64_t>(nXSize) * nYSize /
2035 : (1000 * 1000)),
2036 : MEGAPIXEL_LIMIT);
2037 : }
2038 : }
2039 :
2040 49 : if (psTransform->bUseArray)
2041 : {
2042 47 : auto pAccessors = new GDALGeoLocCArrayAccessors(psTransform);
2043 47 : psTransform->pAccessors = pAccessors;
2044 47 : if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2045 : {
2046 0 : GDALDestroyGeoLocTransformer(psTransform);
2047 0 : return nullptr;
2048 : }
2049 : }
2050 : else
2051 : {
2052 2 : auto pAccessors = new GDALGeoLocDatasetAccessors(psTransform);
2053 2 : psTransform->pAccessors = pAccessors;
2054 2 : if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2055 : {
2056 0 : GDALDestroyGeoLocTransformer(psTransform);
2057 0 : return nullptr;
2058 : }
2059 : }
2060 :
2061 49 : return psTransform;
2062 : }
2063 :
2064 : /** Create GeoLocation transformer */
2065 1 : void *GDALCreateGeoLocTransformer(GDALDatasetH hBaseDS,
2066 : char **papszGeolocationInfo, int bReversed)
2067 :
2068 : {
2069 1 : return GDALCreateGeoLocTransformerEx(hBaseDS, papszGeolocationInfo,
2070 1 : bReversed, nullptr, nullptr);
2071 : }
2072 :
2073 : /************************************************************************/
2074 : /* GDALDestroyGeoLocTransformer() */
2075 : /************************************************************************/
2076 :
2077 : /** Destroy GeoLocation transformer */
2078 49 : void GDALDestroyGeoLocTransformer(void *pTransformAlg)
2079 :
2080 : {
2081 49 : if (pTransformAlg == nullptr)
2082 0 : return;
2083 :
2084 49 : GDALGeoLocTransformInfo *psTransform =
2085 : static_cast<GDALGeoLocTransformInfo *>(pTransformAlg);
2086 :
2087 49 : CSLDestroy(psTransform->papszGeolocationInfo);
2088 :
2089 49 : if (psTransform->bUseArray)
2090 : delete static_cast<GDALGeoLocCArrayAccessors *>(
2091 47 : psTransform->pAccessors);
2092 : else
2093 : delete static_cast<GDALGeoLocDatasetAccessors *>(
2094 2 : psTransform->pAccessors);
2095 :
2096 98 : if (psTransform->hDS_X != nullptr &&
2097 49 : GDALDereferenceDataset(psTransform->hDS_X) == 0)
2098 28 : GDALClose(psTransform->hDS_X);
2099 :
2100 98 : if (psTransform->hDS_Y != nullptr &&
2101 49 : GDALDereferenceDataset(psTransform->hDS_Y) == 0)
2102 41 : GDALClose(psTransform->hDS_Y);
2103 :
2104 49 : if (psTransform->hQuadTree != nullptr)
2105 4 : CPLQuadTreeDestroy(psTransform->hQuadTree);
2106 :
2107 49 : CPLFree(pTransformAlg);
2108 : }
2109 :
2110 : /** Use GeoLocation transformer */
2111 31180 : int GDALGeoLocTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
2112 : double *padfX, double *padfY, double *padfZ,
2113 : int *panSuccess)
2114 : {
2115 31180 : GDALGeoLocTransformInfo *psTransform =
2116 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2117 31180 : if (psTransform->bUseArray)
2118 : {
2119 29059 : return GDALGeoLoc<GDALGeoLocCArrayAccessors>::Transform(
2120 : pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2121 29059 : panSuccess);
2122 : }
2123 : else
2124 : {
2125 2121 : return GDALGeoLoc<GDALGeoLocDatasetAccessors>::Transform(
2126 : pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2127 2121 : panSuccess);
2128 : }
2129 : }
2130 :
2131 : /************************************************************************/
2132 : /* GDALSerializeGeoLocTransformer() */
2133 : /************************************************************************/
2134 :
2135 1 : CPLXMLNode *GDALSerializeGeoLocTransformer(void *pTransformArg)
2136 :
2137 : {
2138 1 : VALIDATE_POINTER1(pTransformArg, "GDALSerializeGeoLocTransformer", nullptr);
2139 :
2140 1 : GDALGeoLocTransformInfo *psInfo =
2141 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2142 :
2143 : CPLXMLNode *psTree =
2144 1 : CPLCreateXMLNode(nullptr, CXT_Element, "GeoLocTransformer");
2145 :
2146 : /* -------------------------------------------------------------------- */
2147 : /* Serialize bReversed. */
2148 : /* -------------------------------------------------------------------- */
2149 1 : CPLCreateXMLElementAndValue(
2150 : psTree, "Reversed",
2151 2 : CPLString().Printf("%d", static_cast<int>(psInfo->bReversed)));
2152 :
2153 : /* -------------------------------------------------------------------- */
2154 : /* geoloc metadata. */
2155 : /* -------------------------------------------------------------------- */
2156 1 : char **papszMD = psInfo->papszGeolocationInfo;
2157 1 : CPLXMLNode *psMD = CPLCreateXMLNode(psTree, CXT_Element, "Metadata");
2158 :
2159 10 : for (int i = 0; papszMD != nullptr && papszMD[i] != nullptr; i++)
2160 : {
2161 9 : char *pszKey = nullptr;
2162 9 : const char *pszRawValue = CPLParseNameValue(papszMD[i], &pszKey);
2163 :
2164 9 : CPLXMLNode *psMDI = CPLCreateXMLNode(psMD, CXT_Element, "MDI");
2165 9 : CPLSetXMLValue(psMDI, "#key", pszKey);
2166 9 : CPLCreateXMLNode(psMDI, CXT_Text, pszRawValue);
2167 :
2168 9 : CPLFree(pszKey);
2169 : }
2170 :
2171 1 : return psTree;
2172 : }
2173 :
2174 : /************************************************************************/
2175 : /* GDALDeserializeGeoLocTransformer() */
2176 : /************************************************************************/
2177 :
2178 1 : void *GDALDeserializeGeoLocTransformer(CPLXMLNode *psTree)
2179 :
2180 : {
2181 : /* -------------------------------------------------------------------- */
2182 : /* Collect metadata. */
2183 : /* -------------------------------------------------------------------- */
2184 1 : CPLXMLNode *psMetadata = CPLGetXMLNode(psTree, "Metadata");
2185 :
2186 1 : if (psMetadata == nullptr || psMetadata->eType != CXT_Element ||
2187 1 : !EQUAL(psMetadata->pszValue, "Metadata"))
2188 0 : return nullptr;
2189 :
2190 1 : char **papszMD = nullptr;
2191 :
2192 12 : for (CPLXMLNode *psMDI = psMetadata->psChild; psMDI != nullptr;
2193 11 : psMDI = psMDI->psNext)
2194 : {
2195 11 : if (!EQUAL(psMDI->pszValue, "MDI") || psMDI->eType != CXT_Element ||
2196 11 : psMDI->psChild == nullptr || psMDI->psChild->psNext == nullptr ||
2197 11 : psMDI->psChild->eType != CXT_Attribute ||
2198 11 : psMDI->psChild->psChild == nullptr)
2199 0 : continue;
2200 :
2201 11 : papszMD = CSLSetNameValue(papszMD, psMDI->psChild->psChild->pszValue,
2202 11 : psMDI->psChild->psNext->pszValue);
2203 : }
2204 :
2205 : /* -------------------------------------------------------------------- */
2206 : /* Get other flags. */
2207 : /* -------------------------------------------------------------------- */
2208 1 : const int bReversed = atoi(CPLGetXMLValue(psTree, "Reversed", "0"));
2209 :
2210 : /* -------------------------------------------------------------------- */
2211 : /* Generate transformation. */
2212 : /* -------------------------------------------------------------------- */
2213 :
2214 : const char *pszSourceDataset =
2215 1 : CPLGetXMLValue(psTree, "SourceDataset", nullptr);
2216 :
2217 1 : void *pResult = GDALCreateGeoLocTransformerEx(nullptr, papszMD, bReversed,
2218 : pszSourceDataset, nullptr);
2219 :
2220 : /* -------------------------------------------------------------------- */
2221 : /* Cleanup GCP copy. */
2222 : /* -------------------------------------------------------------------- */
2223 1 : CSLDestroy(papszMD);
2224 :
2225 1 : return pResult;
2226 : }
|