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