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 12063200 : static double UnshiftGeoX(const GDALGeoLocTransformInfo *psTransform,
73 : double dfX)
74 : {
75 12063200 : if (!psTransform->bGeographicSRSWithMinus180Plus180LongRange)
76 2747650 : return dfX;
77 9315580 : if (dfX > 180 || dfX < -180)
78 : {
79 35493 : dfX = fmod(dfX + 180.0, 360.0);
80 35493 : if (dfX < 0)
81 372 : dfX += 180.0;
82 : else
83 35121 : dfX -= 180.0;
84 35493 : return dfX;
85 : }
86 9280080 : return dfX;
87 : }
88 :
89 : /************************************************************************/
90 : /* UpdateMinMax() */
91 : /************************************************************************/
92 :
93 253897 : inline void UpdateMinMax(GDALGeoLocTransformInfo *psTransform, double dfGeoLocX,
94 : double dfGeoLocY)
95 : {
96 253897 : if (dfGeoLocX < psTransform->dfMinX)
97 : {
98 1350 : psTransform->dfMinX = dfGeoLocX;
99 1350 : psTransform->dfYAtMinX = dfGeoLocY;
100 : }
101 253897 : if (dfGeoLocX > psTransform->dfMaxX)
102 : {
103 1095 : psTransform->dfMaxX = dfGeoLocX;
104 1095 : psTransform->dfYAtMaxX = dfGeoLocY;
105 : }
106 253897 : if (dfGeoLocY < psTransform->dfMinY)
107 : {
108 1393 : psTransform->dfMinY = dfGeoLocY;
109 1393 : psTransform->dfXAtMinY = dfGeoLocX;
110 : }
111 253897 : if (dfGeoLocY > psTransform->dfMaxY)
112 : {
113 3641 : psTransform->dfMaxY = dfGeoLocY;
114 3641 : psTransform->dfXAtMaxY = dfGeoLocX;
115 : }
116 253897 : }
117 :
118 : /************************************************************************/
119 : /* Clamp() */
120 : /************************************************************************/
121 :
122 3268 : inline double Clamp(double v, double minV, double maxV)
123 : {
124 3268 : 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 53 : void GDALGeoLoc<Accessors>::LoadGeolocFinish(
164 : GDALGeoLocTransformInfo *psTransform)
165 : {
166 53 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
167 53 : CSLConstList papszGeolocationInfo = psTransform->papszGeolocationInfo;
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* Scan forward map for lat/long extents. */
171 : /* -------------------------------------------------------------------- */
172 53 : psTransform->dfMinX = std::numeric_limits<double>::max();
173 53 : psTransform->dfMaxX = -std::numeric_limits<double>::max();
174 53 : psTransform->dfMinY = std::numeric_limits<double>::max();
175 53 : psTransform->dfMaxY = -std::numeric_limits<double>::max();
176 :
177 53 : constexpr int TILE_SIZE = GDALGeoLocDatasetAccessors::TILE_SIZE;
178 165 : START_ITER_PER_BLOCK(psTransform->nGeoLocXSize, TILE_SIZE,
179 : psTransform->nGeoLocYSize, TILE_SIZE, (void)0, iXStart,
180 : iXEnd, iYStart, iYEnd)
181 : {
182 2127 : for (int iY = iYStart; iY < iYEnd; ++iY)
183 : {
184 254379 : for (int iX = iXStart; iX < iXEnd; ++iX)
185 : {
186 252309 : double dfX = pAccessors->geolocXAccessor.Get(iX, iY);
187 252309 : if (!psTransform->bHasNoData || dfX != psTransform->dfNoDataX)
188 : {
189 249367 : dfX = UnshiftGeoX(psTransform, dfX);
190 249367 : 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 53 : const char *pszSRS = CSLFetchNameValue(papszGeolocationInfo, "SRS");
201 53 : if (!psTransform->bGeographicSRSWithMinus180Plus180LongRange && pszSRS &&
202 49 : psTransform->dfMinX >= -180.0 && psTransform->dfMaxX <= 180.0 &&
203 48 : !psTransform->bSwapXY)
204 : {
205 47 : OGRSpatialReference oSRS;
206 47 : psTransform->bGeographicSRSWithMinus180Plus180LongRange =
207 94 : oSRS.importFromWkt(pszSRS) == OGRERR_NONE &&
208 47 : 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 53 : if (psTransform->bOriginIsTopLeftCorner)
285 : {
286 : // Add "virtual" edge at Y=nGeoLocYSize
287 1983 : for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
288 : {
289 : double dfGeoLocX;
290 : double dfGeoLocY;
291 1954 : if (!PixelLineToXY(psTransform, static_cast<double>(iX),
292 1954 : static_cast<double>(psTransform->nGeoLocYSize),
293 : dfGeoLocX, dfGeoLocY))
294 0 : continue;
295 1954 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
296 1089 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
297 1954 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
298 : }
299 :
300 : // Add "virtual" edge at X=nGeoLocXSize
301 1555 : for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
302 : {
303 : double dfGeoLocX;
304 : double dfGeoLocY;
305 1526 : if (!PixelLineToXY(psTransform,
306 1526 : static_cast<double>(psTransform->nGeoLocXSize),
307 : static_cast<double>(iY), dfGeoLocX, dfGeoLocY))
308 0 : continue;
309 1526 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
310 1221 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
311 1526 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
312 : }
313 : }
314 : else
315 : {
316 : // Extend by half-pixel on 4 edges for pixel-center convention
317 :
318 401 : for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
319 : {
320 : double dfGeoLocX;
321 : double dfGeoLocY;
322 377 : if (!PixelLineToXY(psTransform, static_cast<double>(iX), -0.5,
323 : dfGeoLocX, dfGeoLocY))
324 114 : continue;
325 263 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
326 242 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
327 263 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
328 : }
329 :
330 401 : for (int iX = 0; iX <= psTransform->nGeoLocXSize; iX++)
331 : {
332 : double dfGeoLocX;
333 : double dfGeoLocY;
334 377 : if (!PixelLineToXY(
335 : psTransform, static_cast<double>(iX),
336 377 : static_cast<double>(psTransform->nGeoLocYSize - 1 + 0.5),
337 : dfGeoLocX, dfGeoLocY))
338 118 : continue;
339 259 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
340 238 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
341 259 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
342 : }
343 :
344 461 : for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
345 : {
346 : double dfGeoLocX;
347 : double dfGeoLocY;
348 437 : if (!PixelLineToXY(psTransform, -0.5, static_cast<double>(iY),
349 : dfGeoLocX, dfGeoLocY))
350 170 : continue;
351 267 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
352 242 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
353 267 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
354 : }
355 :
356 461 : for (int iY = 0; iY <= psTransform->nGeoLocYSize; iY++)
357 : {
358 : double dfGeoLocX;
359 : double dfGeoLocY;
360 437 : if (!PixelLineToXY(psTransform, psTransform->nGeoLocXSize - 1 + 0.5,
361 : static_cast<double>(iY), dfGeoLocX, dfGeoLocY))
362 176 : continue;
363 261 : if (psTransform->bGeographicSRSWithMinus180Plus180LongRange)
364 236 : dfGeoLocX = Clamp(dfGeoLocX, -180.0, 180.0);
365 261 : UpdateMinMax(psTransform, dfGeoLocX, dfGeoLocY);
366 : }
367 : }
368 53 : }
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 1460784 : bool GDALGeoLoc<Accessors>::PixelLineToXY(
393 : const GDALGeoLocTransformInfo *psTransform, const double dfGeoLocPixel,
394 : const double dfGeoLocLine, double &dfX, double &dfY)
395 : {
396 1460784 : int iX = static_cast<int>(
397 2921568 : std::min(std::max(0.0, dfGeoLocPixel),
398 1460784 : static_cast<double>(psTransform->nGeoLocXSize - 1)));
399 1460784 : int iY = static_cast<int>(
400 2921568 : std::min(std::max(0.0, dfGeoLocLine),
401 1460784 : static_cast<double>(psTransform->nGeoLocYSize - 1)));
402 :
403 1460784 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
404 :
405 1852441 : for (int iAttempt = 0; iAttempt < 2; ++iAttempt)
406 : {
407 1852441 : const double dfGLX_0_0 = pAccessors->geolocXAccessor.Get(iX, iY);
408 1852441 : const double dfGLY_0_0 = pAccessors->geolocYAccessor.Get(iX, iY);
409 1852441 : if (psTransform->bHasNoData && dfGLX_0_0 == psTransform->dfNoDataX)
410 : {
411 13386 : return false;
412 : }
413 :
414 : // This assumes infinite extension beyond borders of available
415 : // data based on closest grid square.
416 1839051 : if (iX + 1 < psTransform->nGeoLocXSize &&
417 1615807 : iY + 1 < psTransform->nGeoLocYSize)
418 : {
419 1447394 : const double dfGLX_1_0 =
420 : pAccessors->geolocXAccessor.Get(iX + 1, iY);
421 1447394 : const double dfGLY_1_0 =
422 : pAccessors->geolocYAccessor.Get(iX + 1, iY);
423 1447394 : const double dfGLX_0_1 =
424 : pAccessors->geolocXAccessor.Get(iX, iY + 1);
425 1447394 : const double dfGLY_0_1 =
426 : pAccessors->geolocYAccessor.Get(iX, iY + 1);
427 1447394 : const double dfGLX_1_1 =
428 : pAccessors->geolocXAccessor.Get(iX + 1, iY + 1);
429 1447394 : const double dfGLY_1_1 =
430 : pAccessors->geolocYAccessor.Get(iX + 1, iY + 1);
431 1447394 : if (!psTransform->bHasNoData ||
432 189529 : (dfGLX_1_0 != psTransform->dfNoDataX &&
433 187991 : dfGLX_0_1 != psTransform->dfNoDataX &&
434 186841 : dfGLX_1_1 != psTransform->dfNoDataX))
435 : {
436 : const double dfGLX_1_0_adjusted =
437 1444514 : ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_0);
438 : const double dfGLX_0_1_adjusted =
439 1444514 : ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_0_1);
440 : const double dfGLX_1_1_adjusted =
441 1444514 : ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_1);
442 1444514 : dfX = (1 - (dfGeoLocLine - iY)) *
443 1444514 : (dfGLX_0_0 + (dfGeoLocPixel - iX) *
444 1444514 : (dfGLX_1_0_adjusted - dfGLX_0_0)) +
445 1444514 : (dfGeoLocLine - iY) *
446 1444514 : (dfGLX_0_1_adjusted +
447 1444514 : (dfGeoLocPixel - iX) *
448 1444514 : (dfGLX_1_1_adjusted - dfGLX_0_1_adjusted));
449 1444514 : dfX = UnshiftGeoX(psTransform, dfX);
450 :
451 1444514 : dfY = (1 - (dfGeoLocLine - iY)) *
452 1444514 : (dfGLY_0_0 +
453 1444514 : (dfGeoLocPixel - iX) * (dfGLY_1_0 - dfGLY_0_0)) +
454 1444514 : (dfGeoLocLine - iY) *
455 1444514 : (dfGLY_0_1 +
456 1444514 : (dfGeoLocPixel - iX) * (dfGLY_1_1 - dfGLY_0_1));
457 1444514 : break;
458 : }
459 : }
460 :
461 394538 : if (iX == psTransform->nGeoLocXSize - 1 && iX >= 1 &&
462 223247 : iY + 1 < psTransform->nGeoLocYSize)
463 : {
464 : // If we are after the right edge, then go one pixel left
465 : // and retry
466 207090 : iX--;
467 207090 : continue;
468 : }
469 187448 : else if (iY == psTransform->nGeoLocYSize - 1 && iY >= 1 &&
470 184568 : iX + 1 < psTransform->nGeoLocXSize)
471 : {
472 : // If we are after the bottom edge, then go one pixel up
473 : // and retry
474 168411 : iY--;
475 168411 : continue;
476 : }
477 19037 : else if (iX == psTransform->nGeoLocXSize - 1 &&
478 16157 : 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 16157 : iX--;
483 16157 : iY--;
484 16157 : continue;
485 : }
486 5760 : else if (iX + 1 < psTransform->nGeoLocXSize &&
487 2880 : (!psTransform->bHasNoData ||
488 2880 : pAccessors->geolocXAccessor.Get(iX + 1, iY) !=
489 2880 : psTransform->dfNoDataX))
490 : {
491 1342 : const double dfGLX_1_0 =
492 : pAccessors->geolocXAccessor.Get(iX + 1, iY);
493 1342 : const double dfGLY_1_0 =
494 : pAccessors->geolocYAccessor.Get(iX + 1, iY);
495 1342 : dfX =
496 1342 : dfGLX_0_0 +
497 2684 : (dfGeoLocPixel - iX) *
498 1342 : (ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_1_0) - dfGLX_0_0);
499 1342 : dfX = UnshiftGeoX(psTransform, dfX);
500 1342 : dfY = dfGLY_0_0 + (dfGeoLocPixel - iX) * (dfGLY_1_0 - dfGLY_0_0);
501 : }
502 3076 : else if (iY + 1 < psTransform->nGeoLocYSize &&
503 1538 : (!psTransform->bHasNoData ||
504 1538 : pAccessors->geolocXAccessor.Get(iX, iY + 1) !=
505 1538 : psTransform->dfNoDataX))
506 : {
507 1486 : const double dfGLX_0_1 =
508 : pAccessors->geolocXAccessor.Get(iX, iY + 1);
509 1486 : const double dfGLY_0_1 =
510 : pAccessors->geolocYAccessor.Get(iX, iY + 1);
511 1486 : dfX =
512 1486 : dfGLX_0_0 +
513 2972 : (dfGeoLocLine - iY) *
514 1486 : (ShiftGeoX(psTransform, dfGLX_0_0, dfGLX_0_1) - dfGLX_0_0);
515 1486 : dfX = UnshiftGeoX(psTransform, dfX);
516 1486 : 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 2880 : break;
524 : }
525 1447394 : return true;
526 : }
527 :
528 : template <class Accessors>
529 11375640 : bool GDALGeoLoc<Accessors>::PixelLineToXY(
530 : const GDALGeoLocTransformInfo *psTransform, const int nGeoLocPixel,
531 : const int nGeoLocLine, double &dfX, double &dfY)
532 : {
533 11375640 : if (nGeoLocPixel >= 0 && nGeoLocPixel < psTransform->nGeoLocXSize &&
534 10563540 : nGeoLocLine >= 0 && nGeoLocLine < psTransform->nGeoLocYSize)
535 : {
536 10417200 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
537 10417200 : const double dfGLX =
538 : pAccessors->geolocXAccessor.Get(nGeoLocPixel, nGeoLocLine);
539 10417200 : const double dfGLY =
540 : pAccessors->geolocYAccessor.Get(nGeoLocPixel, nGeoLocLine);
541 10417200 : if (psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX)
542 : {
543 50742 : return false;
544 : }
545 10366460 : dfX = UnshiftGeoX(psTransform, dfGLX);
546 10366460 : dfY = dfGLY;
547 10366460 : return true;
548 : }
549 958432 : return PixelLineToXY(psTransform, static_cast<double>(nGeoLocPixel),
550 958432 : 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 32118 : int GDALGeoLoc<Accessors>::Transform(void *pTransformArg, int bDstToSrc,
594 : int nPointCount, double *padfX,
595 : double *padfY, double * /* padfZ */,
596 : int *panSuccess)
597 : {
598 32118 : int bSuccess = TRUE;
599 32118 : GDALGeoLocTransformInfo *psTransform =
600 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
601 :
602 32118 : if (psTransform->bReversed)
603 0 : bDstToSrc = !bDstToSrc;
604 :
605 32118 : const double dfGeorefConventionOffset =
606 32118 : psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
607 :
608 : /* -------------------------------------------------------------------- */
609 : /* Do original pixel line to target geox/geoy. */
610 : /* -------------------------------------------------------------------- */
611 32118 : if (!bDstToSrc)
612 : {
613 76744 : for (int i = 0; i < nPointCount; i++)
614 : {
615 51795 : if (padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL)
616 : {
617 3061 : bSuccess = FALSE;
618 3061 : panSuccess[i] = FALSE;
619 3061 : continue;
620 : }
621 :
622 48734 : const double dfGeoLocPixel =
623 48734 : (padfX[i] - psTransform->dfPIXEL_OFFSET) /
624 48734 : psTransform->dfPIXEL_STEP -
625 : dfGeorefConventionOffset;
626 48734 : const double dfGeoLocLine =
627 48734 : (padfY[i] - psTransform->dfLINE_OFFSET) /
628 48734 : psTransform->dfLINE_STEP -
629 : dfGeorefConventionOffset;
630 :
631 48734 : if (!PixelLineToXY(psTransform, dfGeoLocPixel, dfGeoLocLine,
632 48734 : 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 45908 : if (psTransform->bSwapXY)
642 : {
643 568 : std::swap(padfX[i], padfY[i]);
644 : }
645 :
646 45908 : panSuccess[i] = TRUE;
647 : }
648 : }
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* geox/geoy to pixel/line using backmap. */
652 : /* -------------------------------------------------------------------- */
653 : else
654 : {
655 7169 : if (psTransform->hQuadTree)
656 : {
657 24 : GDALGeoLocInverseTransformQuadtree(psTransform, nPointCount, padfX,
658 : padfY, panSuccess);
659 24 : return TRUE;
660 : }
661 :
662 7145 : 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 14290 : OGRPoint oPoint;
668 14290 : OGRLinearRing oRing;
669 7145 : oRing.setNumPoints(5);
670 :
671 7145 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
672 :
673 243302 : for (int i = 0; i < nPointCount; i++)
674 : {
675 236157 : 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 236157 : if (psTransform->bSwapXY)
683 : {
684 964 : std::swap(padfX[i], padfY[i]);
685 : }
686 :
687 236157 : const double dfGeoX = padfX[i];
688 236157 : const double dfGeoY = padfY[i];
689 :
690 236157 : const double dfBMX =
691 236157 : ((padfX[i] - psTransform->adfBackMapGeoTransform[0]) /
692 : psTransform->adfBackMapGeoTransform[1]);
693 236157 : const double dfBMY =
694 236157 : ((padfY[i] - psTransform->adfBackMapGeoTransform[3]) /
695 : psTransform->adfBackMapGeoTransform[5]);
696 :
697 236157 : if (!(dfBMX >= 0 && dfBMY >= 0 &&
698 235901 : dfBMX + 1 < psTransform->nBackMapWidth &&
699 234494 : dfBMY + 1 < psTransform->nBackMapHeight))
700 : {
701 2577 : bSuccess = FALSE;
702 2577 : panSuccess[i] = FALSE;
703 2577 : padfX[i] = HUGE_VAL;
704 2577 : padfY[i] = HUGE_VAL;
705 2577 : continue;
706 : }
707 :
708 233580 : const int iBMX = static_cast<int>(dfBMX);
709 233580 : const int iBMY = static_cast<int>(dfBMY);
710 :
711 233580 : const auto fBMX_0_0 = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
712 233580 : const auto fBMY_0_0 = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
713 233580 : if (fBMX_0_0 == INVALID_BMXY)
714 : {
715 92043 : bSuccess = FALSE;
716 92043 : panSuccess[i] = FALSE;
717 92043 : padfX[i] = HUGE_VAL;
718 92043 : padfY[i] = HUGE_VAL;
719 92043 : continue;
720 : }
721 :
722 141537 : const auto fBMX_1_0 =
723 : pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY);
724 141537 : const auto fBMY_1_0 =
725 : pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY);
726 141537 : const auto fBMX_0_1 =
727 : pAccessors->backMapXAccessor.Get(iBMX, iBMY + 1);
728 141537 : const auto fBMY_0_1 =
729 : pAccessors->backMapYAccessor.Get(iBMX, iBMY + 1);
730 141537 : const auto fBMX_1_1 =
731 : pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY + 1);
732 141537 : const auto fBMY_1_1 =
733 : pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY + 1);
734 141537 : if (fBMX_1_0 != INVALID_BMXY && fBMX_0_1 != INVALID_BMXY &&
735 : fBMX_1_1 != INVALID_BMXY)
736 : {
737 138931 : padfX[i] = (1 - (dfBMY - iBMY)) *
738 138931 : (double(fBMX_0_0) +
739 138931 : (dfBMX - iBMX) * double(fBMX_1_0 - fBMX_0_0)) +
740 138931 : (dfBMY - iBMY) *
741 138931 : (double(fBMX_0_1) +
742 138931 : (dfBMX - iBMX) * double(fBMX_1_1 - fBMX_0_1));
743 138931 : padfY[i] = (1 - (dfBMY - iBMY)) *
744 138931 : (double(fBMY_0_0) +
745 138931 : (dfBMX - iBMX) * double(fBMY_1_0 - fBMY_0_0)) +
746 138931 : (dfBMY - iBMY) *
747 138931 : (double(fBMY_0_1) +
748 138931 : (dfBMX - iBMX) * double(fBMY_1_1 - fBMY_0_1));
749 : }
750 2606 : else if (fBMX_1_0 != INVALID_BMXY)
751 : {
752 1897 : padfX[i] = double(fBMX_0_0) +
753 1897 : (dfBMX - iBMX) * double(fBMX_1_0 - fBMX_0_0);
754 1897 : padfY[i] = double(fBMY_0_0) +
755 1897 : (dfBMX - iBMX) * double(fBMY_1_0 - fBMY_0_0);
756 : }
757 709 : else if (fBMX_0_1 != INVALID_BMXY)
758 : {
759 528 : padfX[i] = double(fBMX_0_0) +
760 528 : (dfBMY - iBMY) * double(fBMX_0_1 - fBMX_0_0);
761 528 : padfY[i] = double(fBMY_0_0) +
762 528 : (dfBMY - iBMY) * double(fBMY_0_1 - fBMY_0_0);
763 : }
764 : else
765 : {
766 181 : padfX[i] = double(fBMX_0_0);
767 181 : padfY[i] = double(fBMY_0_0);
768 : }
769 :
770 141537 : const double dfGeoLocPixel =
771 141537 : (padfX[i] - psTransform->dfPIXEL_OFFSET) /
772 141537 : psTransform->dfPIXEL_STEP -
773 : dfGeorefConventionOffset;
774 141537 : const double dfGeoLocLine =
775 141537 : (padfY[i] - psTransform->dfLINE_OFFSET) /
776 141537 : 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 141537 : 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 141537 : oPoint.setX(dfGeoX);
807 141537 : 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 141537 : const int nSearchRadius =
815 141537 : psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
816 84397 : fabs(dfGeoY) >= 85
817 : ? 5
818 : : 3;
819 141537 : const int nGeoLocPixel =
820 141537 : static_cast<int>(std::floor(dfGeoLocPixel));
821 141537 : const int nGeoLocLine = static_cast<int>(std::floor(dfGeoLocLine));
822 :
823 141537 : 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 384479 : for (int r = 0; !bDone && r <= nSearchRadius; r++)
828 : {
829 1926383 : 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 3225340 : const int sx = (r == 0) ? 0
836 1541899 : : (iter < 2 * r) ? -r + iter
837 1130365 : : (iter < 4 * r) ? r
838 741355 : : (iter < 6 * r) ? r - (iter - 4 * r)
839 : : -r;
840 3225340 : const int sy = (r == 0) ? 0
841 1541899 : : (iter < 2 * r) ? r
842 1130365 : : (iter < 4 * r) ? r - (iter - 2 * r)
843 741355 : : (iter < 6 * r) ? -r
844 363473 : : -r + (iter - 6 * r);
845 2089447 : if (nGeoLocPixel >=
846 1683435 : static_cast<int>(psTransform->nGeoLocXSize) - sx ||
847 : nGeoLocLine >=
848 1408282 : static_cast<int>(psTransform->nGeoLocYSize) - sy)
849 : {
850 406013 : continue;
851 : }
852 1277426 : const int iX = nGeoLocPixel + sx;
853 1277426 : const int iY = nGeoLocLine + sy;
854 1277426 : if (iX >= -1 || iY >= -1)
855 : {
856 : double x0, y0, x1, y1, x2, y2, x3, y3;
857 :
858 1277003 : if (!PixelLineToXY(psTransform, iX, iY, x0, y0) ||
859 1233947 : !PixelLineToXY(psTransform, iX + 1, iY, x2, y2) ||
860 3735680 : !PixelLineToXY(psTransform, iX, iY + 1, x1, y1) ||
861 1224727 : !PixelLineToXY(psTransform, iX + 1, iY + 1, x3, y3))
862 : {
863 52754 : continue;
864 : }
865 :
866 1224249 : int nIters = 1;
867 : // For a bounding box crossing the anti-meridian, check
868 : // both around -180 and +180 deg.
869 1224249 : if (psTransform
870 1224249 : ->bGeographicSRSWithMinus180Plus180LongRange &&
871 1035101 : std::fabs(x0) > 170 && std::fabs(x1) > 170 &&
872 53449 : std::fabs(x2) > 170 && std::fabs(x3) > 170 &&
873 38624 : (std::fabs(x1 - x0) > 180 ||
874 38293 : std::fabs(x2 - x0) > 180 ||
875 31416 : std::fabs(x3 - x0) > 180))
876 : {
877 7510 : nIters = 2;
878 7510 : if (x0 > 0)
879 58 : x0 -= 360;
880 7510 : if (x1 > 0)
881 331 : x1 -= 360;
882 7510 : if (x2 > 0)
883 7179 : x2 -= 360;
884 7510 : if (x3 > 0)
885 7510 : x3 -= 360;
886 : }
887 2455875 : for (int iIter = 0; !bDone && iIter < nIters; ++iIter)
888 : {
889 1231621 : if (iIter == 1)
890 : {
891 7372 : x0 += 360;
892 7372 : x1 += 360;
893 7372 : x2 += 360;
894 7372 : x3 += 360;
895 : }
896 1231621 : oRing.setPoint(0, x0, y0);
897 1231621 : oRing.setPoint(1, x2, y2);
898 1231621 : oRing.setPoint(2, x3, y3);
899 1231621 : oRing.setPoint(3, x1, y1);
900 1231621 : oRing.setPoint(4, x0, y0);
901 2344090 : if (oRing.isPointInRing(&oPoint) ||
902 1112472 : oRing.isPointOnRingBoundary(&oPoint))
903 : {
904 120969 : double dfX = static_cast<double>(iX);
905 120969 : double dfY = static_cast<double>(iY);
906 120969 : GDALInverseBilinearInterpolation(
907 : dfGeoX, dfGeoY, x0, y0, x1, y1, x2, y2, x3,
908 : y3, dfX, dfY);
909 :
910 120969 : dfX = (dfX + dfGeorefConventionOffset) *
911 120969 : psTransform->dfPIXEL_STEP +
912 120969 : psTransform->dfPIXEL_OFFSET;
913 120969 : dfY = (dfY + dfGeorefConventionOffset) *
914 120969 : psTransform->dfLINE_STEP +
915 120969 : 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 120969 : padfX[i] = dfX;
925 120969 : padfY[i] = dfY;
926 :
927 120969 : bDone = true;
928 : }
929 : }
930 : }
931 : }
932 : }
933 141537 : if (!bDone)
934 : {
935 20568 : bSuccess = FALSE;
936 20568 : panSuccess[i] = FALSE;
937 20568 : padfX[i] = HUGE_VAL;
938 20568 : padfY[i] = HUGE_VAL;
939 20568 : continue;
940 : }
941 :
942 120969 : panSuccess[i] = TRUE;
943 : }
944 : }
945 :
946 32094 : 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 266866 : 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 266866 : const double A = (x0 - x) * (y0 - y2) - (y0 - y) * (x0 - x2);
971 266866 : const double B = (((x0 - x) * (y1 - y3) - (y0 - y) * (x1 - x3)) +
972 266866 : ((x1 - x) * (y0 - y2) - (y1 - y) * (x0 - x2))) /
973 : 2;
974 266866 : const double C = (x1 - x) * (y1 - y3) - (y1 - y) * (x1 - x3);
975 266866 : const double denom = A - 2 * B + C;
976 : double s;
977 266866 : const double magnitudeOfValues = fabs(A) + fabs(B) + fabs(C);
978 266866 : 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 143370 : s = A / (A - C);
983 : }
984 : else
985 : {
986 123496 : const double sqrtTerm = sqrt(B * B - A * C);
987 123496 : const double s1 = ((A - B) + sqrtTerm) / denom;
988 123496 : const double s2 = ((A - B) - sqrtTerm) / denom;
989 123496 : if (s1 < 0 || s1 > 1)
990 102726 : s = s2;
991 : else
992 20770 : s = s1;
993 : }
994 :
995 266866 : const double t_denom_x = (1 - s) * (x0 - x2) + s * (x1 - x3);
996 266866 : if (fabs(t_denom_x) > 1e-12 * magnitudeOfValues)
997 : {
998 265071 : i += ((1 - s) * (x0 - x) + s * (x1 - x)) / t_denom_x;
999 : }
1000 : else
1001 : {
1002 1795 : const double t_denom_y = (1 - s) * (y0 - y2) + s * (y1 - y3);
1003 1795 : if (fabs(t_denom_y) > 1e-12 * magnitudeOfValues)
1004 : {
1005 1795 : i += ((1 - s) * (y0 - y) + s * (y1 - y)) / t_denom_y;
1006 : }
1007 : }
1008 :
1009 266866 : j += s;
1010 266866 : }
1011 :
1012 : /************************************************************************/
1013 : /* GeoLocGenerateBackMap() */
1014 : /************************************************************************/
1015 :
1016 : /*! @cond Doxygen_Suppress */
1017 :
1018 : template <class Accessors>
1019 49 : bool GDALGeoLoc<Accessors>::GenerateBackMap(
1020 : GDALGeoLocTransformInfo *psTransform)
1021 :
1022 : {
1023 49 : CPLDebug("GEOLOC", "Starting backmap generation");
1024 49 : const int nXSize = psTransform->nGeoLocXSize;
1025 49 : 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 49 : const double dfTargetPixels =
1034 49 : static_cast<double>(nXSize) * nYSize * psTransform->dfOversampleFactor;
1035 : const double dfPixelSizeSquare =
1036 49 : sqrt((psTransform->dfMaxX - psTransform->dfMinX) *
1037 49 : (psTransform->dfMaxY - psTransform->dfMinY) / dfTargetPixels);
1038 49 : if (dfPixelSizeSquare == 0.0)
1039 : {
1040 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid pixel size for backmap");
1041 0 : return false;
1042 : }
1043 :
1044 49 : const double dfMinX = psTransform->dfMinX - dfPixelSizeSquare / 2.0;
1045 49 : const double dfMaxX = psTransform->dfMaxX + dfPixelSizeSquare / 2.0;
1046 49 : const double dfMaxY = psTransform->dfMaxY + dfPixelSizeSquare / 2.0;
1047 49 : const double dfMinY = psTransform->dfMinY - dfPixelSizeSquare / 2.0;
1048 49 : const double dfBMXSize = std::ceil((dfMaxX - dfMinX) / dfPixelSizeSquare);
1049 49 : 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 49 : if (!(dfBMXSize > 0 && dfBMXSize + 2 < INT_MAX) ||
1054 49 : !(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 49 : int nBMXSize = static_cast<int>(dfBMXSize);
1062 49 : int nBMYSize = static_cast<int>(dfBMYSize);
1063 :
1064 98 : if (static_cast<size_t>(1 + nBMYSize) >
1065 49 : 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 49 : const double dfPixelXSize = (dfMaxX - dfMinX) / nBMXSize;
1073 49 : const double dfPixelYSize = (dfMaxY - dfMinY) / nBMYSize;
1074 :
1075 : // Extra pixel for right-edge and bottom-edge extensions in TOP_LEFT_CORNER
1076 : // convention.
1077 49 : nBMXSize++;
1078 49 : nBMYSize++;
1079 49 : psTransform->nBackMapWidth = nBMXSize;
1080 49 : psTransform->nBackMapHeight = nBMYSize;
1081 :
1082 49 : psTransform->adfBackMapGeoTransform[0] = dfMinX;
1083 49 : psTransform->adfBackMapGeoTransform[1] = dfPixelXSize;
1084 49 : psTransform->adfBackMapGeoTransform[2] = 0.0;
1085 49 : psTransform->adfBackMapGeoTransform[3] = dfMaxY;
1086 49 : psTransform->adfBackMapGeoTransform[4] = 0.0;
1087 49 : psTransform->adfBackMapGeoTransform[5] = -dfPixelYSize;
1088 :
1089 : /* -------------------------------------------------------------------- */
1090 : /* Allocate backmap. */
1091 : /* -------------------------------------------------------------------- */
1092 49 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
1093 49 : if (!pAccessors->AllocateBackMap())
1094 0 : return false;
1095 :
1096 49 : const double dfGeorefConventionOffset =
1097 49 : psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
1098 :
1099 49 : const auto UpdateBackmap =
1100 9857135 : [&](int iBMX, int iBMY, double dfX, double dfY, double tempwt)
1101 : {
1102 870170 : const auto fBMX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1103 870170 : const auto fBMY = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
1104 870170 : const float fUpdatedBMX =
1105 : fBMX +
1106 870170 : static_cast<float>(tempwt * ((dfX + dfGeorefConventionOffset) *
1107 870170 : psTransform->dfPIXEL_STEP +
1108 870170 : psTransform->dfPIXEL_OFFSET));
1109 870170 : const float fUpdatedBMY =
1110 : fBMY +
1111 870170 : static_cast<float>(tempwt * ((dfY + dfGeorefConventionOffset) *
1112 870170 : psTransform->dfLINE_STEP +
1113 870170 : psTransform->dfLINE_OFFSET));
1114 870170 : const float fUpdatedWeight =
1115 870170 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) +
1116 870170 : 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 870170 : if (fUpdatedWeight > 0)
1123 : {
1124 870167 : const float fX = fUpdatedBMX / fUpdatedWeight;
1125 870167 : const float fY = fUpdatedBMY / fUpdatedWeight;
1126 870167 : const double dfGeoLocPixel =
1127 870167 : (double(fX) - psTransform->dfPIXEL_OFFSET) /
1128 870167 : psTransform->dfPIXEL_STEP -
1129 : dfGeorefConventionOffset;
1130 870167 : const double dfGeoLocLine =
1131 870167 : (double(fY) - psTransform->dfLINE_OFFSET) /
1132 870167 : psTransform->dfLINE_STEP -
1133 : dfGeorefConventionOffset;
1134 870167 : int iXAvg = static_cast<int>(std::max(0.0, dfGeoLocPixel));
1135 870167 : iXAvg = std::min(iXAvg, psTransform->nGeoLocXSize - 1);
1136 870167 : int iYAvg = static_cast<int>(std::max(0.0, dfGeoLocLine));
1137 870167 : iYAvg = std::min(iYAvg, psTransform->nGeoLocYSize - 1);
1138 870167 : const double dfGLX = pAccessors->geolocXAccessor.Get(iXAvg, iYAvg);
1139 870167 : const double dfGLY = pAccessors->geolocYAccessor.Get(iXAvg, iYAvg);
1140 :
1141 870167 : const unsigned iX = static_cast<unsigned>(dfX);
1142 870167 : const unsigned iY = static_cast<unsigned>(dfY);
1143 1740320 : if (!(psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX) &&
1144 870157 : ((iX >= static_cast<unsigned>(nXSize - 1) ||
1145 854459 : iY >= static_cast<unsigned>(nYSize - 1)) ||
1146 843268 : (fabs(dfGLX - pAccessors->geolocXAccessor.Get(iX, iY)) <=
1147 843268 : 2 * dfPixelXSize &&
1148 800820 : fabs(dfGLY - pAccessors->geolocYAccessor.Get(iX, iY)) <=
1149 800820 : 2 * dfPixelYSize)))
1150 : {
1151 827564 : pAccessors->backMapXAccessor.Set(iBMX, iBMY, fUpdatedBMX);
1152 827564 : pAccessors->backMapYAccessor.Set(iBMX, iBMY, fUpdatedBMY);
1153 827564 : 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 98 : OGRPoint oPoint;
1162 98 : OGRLinearRing oRing;
1163 49 : 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 49 : const double dfStep = 1. / psTransform->dfOversampleFactor;
1174 :
1175 49 : constexpr int TILE_SIZE = GDALGeoLocDatasetAccessors::TILE_SIZE;
1176 49 : const int nYBlocks = DIV_ROUND_UP(nYSize, TILE_SIZE);
1177 49 : 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 98 : std::vector<std::pair<double, double>> yStartEnd(nYBlocks + 1);
1182 98 : std::vector<std::pair<double, double>> xStartEnd(nXBlocks + 1);
1183 :
1184 : {
1185 49 : int iYBlockLast = -1;
1186 49 : double dfY = -dfStep;
1187 2612 : for (; dfY <= static_cast<double>(nYSize) + 2 * dfStep; dfY += dfStep)
1188 : {
1189 2563 : const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1190 2563 : if (iYBlock != iYBlockLast)
1191 : {
1192 51 : CPLAssert(iYBlock == iYBlockLast + 1);
1193 51 : if (iYBlockLast >= 0)
1194 2 : yStartEnd[iYBlockLast].second = dfY + dfStep / 10;
1195 51 : yStartEnd[iYBlock].first = dfY;
1196 51 : iYBlockLast = iYBlock;
1197 : }
1198 : }
1199 49 : const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1200 49 : yStartEnd[iYBlock].second = dfY + dfStep / 10;
1201 : }
1202 :
1203 : {
1204 49 : int iXBlockLast = -1;
1205 49 : double dfX = -dfStep;
1206 3094 : for (; dfX <= static_cast<double>(nXSize) + 2 * dfStep; dfX += dfStep)
1207 : {
1208 3045 : const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1209 3045 : if (iXBlock != iXBlockLast)
1210 : {
1211 51 : CPLAssert(iXBlock == iXBlockLast + 1);
1212 51 : if (iXBlockLast >= 0)
1213 2 : xStartEnd[iXBlockLast].second = dfX + dfStep / 10;
1214 51 : xStartEnd[iXBlock].first = dfX;
1215 51 : iXBlockLast = iXBlock;
1216 : }
1217 : }
1218 49 : const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1219 49 : xStartEnd[iXBlock].second = dfX + dfStep / 10;
1220 : }
1221 :
1222 100 : for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
1223 : {
1224 104 : 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 2883 : for (double dfY = yStartEnd[iYBlock].first;
1233 2883 : dfY < yStartEnd[iYBlock].second; dfY += dfStep)
1234 : {
1235 451339 : for (double dfX = xStartEnd[iXBlock].first;
1236 451339 : 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 448509 : if (!PixelLineToXY(psTransform, dfX, dfY, dfGeoLocX,
1243 : dfGeoLocY))
1244 153033 : continue;
1245 :
1246 : // Compute the floating point coordinates in the pixel space
1247 : // of the backmap
1248 441827 : const double dBMX = static_cast<double>(
1249 441827 : (dfGeoLocX - dfMinX) / dfPixelXSize);
1250 :
1251 441827 : const double dBMY = static_cast<double>(
1252 441827 : (dfMaxY - dfGeoLocY) / dfPixelYSize);
1253 :
1254 : // Get top left index by truncation
1255 441827 : const int iBMX = static_cast<int>(std::floor(dBMX));
1256 441827 : const int iBMY = static_cast<int>(std::floor(dBMY));
1257 :
1258 441827 : 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 439907 : double dfGeoX = dfMinX + iBMX * dfPixelXSize;
1264 439907 : const double dfGeoY = dfMaxY - iBMY * dfPixelYSize;
1265 :
1266 439907 : bool bMatchingGeoLocCellFound = false;
1267 :
1268 439907 : const int nOuterIters =
1269 439907 : psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
1270 317622 : fabs(dfGeoX) >= 180
1271 : ? 2
1272 : : 1;
1273 :
1274 879838 : for (int iOuterIter = 0; iOuterIter < nOuterIters;
1275 : ++iOuterIter)
1276 : {
1277 439931 : if (iOuterIter == 1 && dfGeoX >= 180)
1278 0 : dfGeoX -= 360;
1279 439931 : 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 439931 : oPoint.setX(dfGeoX);
1286 439931 : oPoint.setY(dfGeoY);
1287 439931 : const int nX = static_cast<int>(std::floor(dfX));
1288 439931 : const int nY = static_cast<int>(std::floor(dfY));
1289 1264706 : for (int sx = -1;
1290 1264706 : !bMatchingGeoLocCellFound && sx <= 0; sx++)
1291 : {
1292 2423953 : for (int sy = -1;
1293 2423953 : !bMatchingGeoLocCellFound && sy <= 0; sy++)
1294 : {
1295 1600470 : const int pixel = nX + sx;
1296 1600470 : const int line = nY + sy;
1297 : double x0, y0, x1, y1, x2, y2, x3, y3;
1298 1600470 : if (!PixelLineToXY(psTransform, pixel, line,
1299 1599902 : x0, y0) ||
1300 1599902 : !PixelLineToXY(psTransform, pixel + 1,
1301 1599682 : line, x2, y2) ||
1302 1599682 : !PixelLineToXY(psTransform, pixel,
1303 3200370 : line + 1, x1, y1) ||
1304 1599252 : !PixelLineToXY(psTransform, pixel + 1,
1305 : line + 1, x3, y3))
1306 : {
1307 1288 : break;
1308 : }
1309 :
1310 1599182 : int nIters = 1;
1311 1599182 : if (psTransform
1312 1599182 : ->bGeographicSRSWithMinus180Plus180LongRange &&
1313 1196821 : 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 3199850 : for (int iIter = 0; iIter < nIters; ++iIter)
1332 : {
1333 1600672 : if (iIter == 1)
1334 : {
1335 1490 : x0 += 360;
1336 1490 : x1 += 360;
1337 1490 : x2 += 360;
1338 1490 : x3 += 360;
1339 : }
1340 :
1341 1600672 : oRing.setPoint(0, x0, y0);
1342 1600672 : oRing.setPoint(1, x2, y2);
1343 1600672 : oRing.setPoint(2, x3, y3);
1344 1600672 : oRing.setPoint(3, x1, y1);
1345 1600672 : oRing.setPoint(4, x0, y0);
1346 3055670 : if (oRing.isPointInRing(&oPoint) ||
1347 1455002 : oRing.isPointOnRingBoundary(
1348 : &oPoint))
1349 : {
1350 145873 : bMatchingGeoLocCellFound = true;
1351 145873 : double dfBMXValue = pixel;
1352 145873 : double dfBMYValue = line;
1353 145873 : GDALInverseBilinearInterpolation(
1354 : dfGeoX, dfGeoY, x0, y0, x1, y1,
1355 : x2, y2, x3, y3, dfBMXValue,
1356 : dfBMYValue);
1357 :
1358 145873 : dfBMXValue =
1359 145873 : (dfBMXValue +
1360 145873 : dfGeorefConventionOffset) *
1361 145873 : psTransform->dfPIXEL_STEP +
1362 145873 : psTransform->dfPIXEL_OFFSET;
1363 145873 : dfBMYValue =
1364 145873 : (dfBMYValue +
1365 145873 : dfGeorefConventionOffset) *
1366 145873 : psTransform->dfLINE_STEP +
1367 145873 : psTransform->dfLINE_OFFSET;
1368 :
1369 145873 : pAccessors->backMapXAccessor.Set(
1370 : iBMX, iBMY,
1371 : static_cast<float>(dfBMXValue));
1372 145873 : pAccessors->backMapYAccessor.Set(
1373 : iBMX, iBMY,
1374 : static_cast<float>(dfBMYValue));
1375 145873 : pAccessors->backMapWeightAccessor
1376 : .Set(iBMX, iBMY, 1.0f);
1377 : }
1378 : }
1379 : }
1380 : }
1381 : }
1382 439907 : if (bMatchingGeoLocCellFound)
1383 145873 : 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 295954 : if (iBMX < -1 || iBMY < -1 || iBMX > nBMXSize ||
1391 : iBMY > nBMYSize)
1392 478 : continue;
1393 :
1394 295476 : const double fracBMX = dBMX - iBMX;
1395 295476 : const double fracBMY = dBMY - iBMY;
1396 :
1397 : // Check logic for top left pixel
1398 295042 : if ((iBMX >= 0) && (iBMY >= 0) && (iBMX < nBMXSize) &&
1399 590518 : (iBMY < nBMYSize) &&
1400 294034 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) !=
1401 : 1.0f)
1402 : {
1403 201764 : const double tempwt = (1.0 - fracBMX) * (1.0 - fracBMY);
1404 201764 : UpdateBackmap(iBMX, iBMY, dfX, dfY, tempwt);
1405 : }
1406 :
1407 : // Check logic for top right pixel
1408 295062 : if ((iBMY >= 0) && (iBMX + 1 < nBMXSize) &&
1409 590538 : (iBMY < nBMYSize) &&
1410 294410 : pAccessors->backMapWeightAccessor.Get(iBMX + 1, iBMY) !=
1411 : 1.0f)
1412 : {
1413 202030 : const double tempwt = fracBMX * (1.0 - fracBMY);
1414 202030 : UpdateBackmap(iBMX + 1, iBMY, dfX, dfY, tempwt);
1415 : }
1416 :
1417 : // Check logic for bottom right pixel
1418 590210 : if ((iBMX + 1 < nBMXSize) && (iBMY + 1 < nBMYSize) &&
1419 294734 : pAccessors->backMapWeightAccessor.Get(iBMX + 1,
1420 294734 : iBMY + 1) != 1.0f)
1421 : {
1422 235771 : const double tempwt = fracBMX * fracBMY;
1423 235771 : UpdateBackmap(iBMX + 1, iBMY + 1, dfX, dfY, tempwt);
1424 : }
1425 :
1426 : // Check logic for bottom left pixel
1427 295042 : if ((iBMX >= 0) && (iBMX < nBMXSize) &&
1428 884888 : (iBMY + 1 < nBMYSize) &&
1429 294370 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY + 1) !=
1430 : 1.0f)
1431 : {
1432 230605 : const double tempwt = (1.0 - fracBMX) * fracBMY;
1433 230605 : 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 163 : START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, (void)0,
1443 : iXStart, iXEnd, iYStart, iYEnd)
1444 : {
1445 2559 : for (int iY = iYStart; iY < iYEnd; ++iY)
1446 : {
1447 346177 : 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 483432 : const auto weight =
1453 343683 : pAccessors->backMapWeightAccessor.Get(iX, iY);
1454 343683 : if (weight > 0)
1455 : {
1456 247361 : pAccessors->backMapXAccessor.Set(
1457 : iX, iY,
1458 181440 : pAccessors->backMapXAccessor.Get(iX, iY) / weight);
1459 247361 : pAccessors->backMapYAccessor.Set(
1460 : iX, iY,
1461 181440 : pAccessors->backMapYAccessor.Get(iX, iY) / weight);
1462 : }
1463 : else
1464 : {
1465 162243 : pAccessors->backMapXAccessor.Set(iX, iY, INVALID_BMXY);
1466 162243 : pAccessors->backMapYAccessor.Set(iX, iY, INVALID_BMXY);
1467 : }
1468 : }
1469 : }
1470 : }
1471 : END_ITER_PER_BLOCK
1472 :
1473 49 : pAccessors->FreeWghtsBackMap();
1474 :
1475 : // Fill holes in backmap
1476 49 : auto poBackmapDS = pAccessors->GetBackmapDataset();
1477 :
1478 49 : 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 49 : constexpr double dfMaxSearchDist = 3.0;
1491 49 : constexpr int nSmoothingIterations = 1;
1492 147 : for (int i = 1; i <= 2; i++)
1493 : {
1494 98 : 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 49 : std::vector<LastValidStruct> lastValid(TILE_SIZE);
1518 245 : const auto reinitLine = [&lastValid]()
1519 : {
1520 49 : const size_t nSize = lastValid.size();
1521 49 : lastValid.clear();
1522 49 : lastValid.resize(nSize);
1523 : };
1524 163 : START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, reinitLine(),
1525 : iXStart, iXEnd, iYStart, iYEnd)
1526 : {
1527 65 : const int iYCount = iYEnd - iYStart;
1528 2559 : for (int iYIter = 0; iYIter < iYCount; ++iYIter)
1529 : {
1530 2494 : int iLastValidIX = lastValid[iYIter].iX;
1531 2494 : float bmXLastValid = lastValid[iYIter].bmX;
1532 2494 : const int iBMY = iYStart + iYIter;
1533 346177 : for (int iBMX = iXStart; iBMX < iXEnd; ++iBMX)
1534 : {
1535 343683 : const float bmX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1536 343683 : if (bmX == INVALID_BMXY)
1537 132704 : continue;
1538 211531 : 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 210979 : iLastValidIX = iBMX;
1563 210979 : bmXLastValid = bmX;
1564 : }
1565 2494 : lastValid[iYIter].iX = iLastValidIX;
1566 2494 : 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 49 : pAccessors->ReleaseBackmapDataset(poBackmapDS);
1583 49 : CPLDebug("GEOLOC", "Ending backmap generation");
1584 :
1585 49 : 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 13 : CPLStringList GDALCreateGeolocationMetadata(GDALDatasetH hBaseDS,
1650 : const char *pszGeolocationDataset,
1651 : bool bIsSource)
1652 : {
1653 26 : CPLStringList aosMD;
1654 :
1655 : // Open geolocation dataset
1656 : auto poGeolocDS = std::unique_ptr<GDALDataset>(
1657 26 : GDALDataset::Open(pszGeolocationDataset, GDAL_OF_RASTER));
1658 13 : if (poGeolocDS == nullptr)
1659 : {
1660 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dataset: %s",
1661 : pszGeolocationDataset);
1662 2 : return CPLStringList();
1663 : }
1664 11 : const int nGeoLocXSize = poGeolocDS->GetRasterXSize();
1665 11 : const int nGeoLocYSize = poGeolocDS->GetRasterYSize();
1666 11 : 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 : auto papszGeolocMDFromGeolocDS =
1676 11 : poGeolocDS->GetMetadata(GDAL_MDD_GEOLOCATION);
1677 11 : if (papszGeolocMDFromGeolocDS)
1678 3 : aosMD = CSLDuplicate(papszGeolocMDFromGeolocDS);
1679 :
1680 11 : aosMD.SetNameValue("X_DATASET", pszGeolocationDataset);
1681 11 : aosMD.SetNameValue("Y_DATASET", pszGeolocationDataset);
1682 :
1683 : // Set X_BAND, Y_BAND to 1, 2 if they are not specified in the initial
1684 : // GEOLOC metadata domain.and the geolocation dataset has 2 bands.
1685 19 : if (aosMD.FetchNameValue("X_BAND") == nullptr &&
1686 8 : aosMD.FetchNameValue("Y_BAND") == nullptr)
1687 : {
1688 8 : if (poGeolocDS->GetRasterCount() != 2)
1689 : {
1690 1 : CPLError(CE_Failure, CPLE_AppDefined,
1691 : "Expected 2 bands for %s. Got %d", pszGeolocationDataset,
1692 : poGeolocDS->GetRasterCount());
1693 1 : return CPLStringList();
1694 : }
1695 7 : aosMD.SetNameValue("X_BAND", "1");
1696 7 : aosMD.SetNameValue("Y_BAND", "2");
1697 : }
1698 :
1699 : // Set the geoloc SRS from the geolocation dataset SRS if there's no
1700 : // explicit one in the initial GEOLOC metadata domain.
1701 10 : if (aosMD.FetchNameValue("SRS") == nullptr)
1702 : {
1703 10 : auto poSRS = poGeolocDS->GetSpatialRef();
1704 10 : if (poSRS)
1705 : {
1706 3 : char *pszWKT = nullptr;
1707 3 : poSRS->exportToWkt(&pszWKT);
1708 3 : aosMD.SetNameValue("SRS", pszWKT);
1709 3 : CPLFree(pszWKT);
1710 : }
1711 : }
1712 10 : if (aosMD.FetchNameValue("SRS") == nullptr)
1713 : {
1714 7 : aosMD.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);
1715 : }
1716 :
1717 : // Set default values for PIXEL/LINE_OFFSET/STEP if not present.
1718 10 : if (aosMD.FetchNameValue("PIXEL_OFFSET") == nullptr)
1719 7 : aosMD.SetNameValue("PIXEL_OFFSET", "0");
1720 :
1721 10 : if (aosMD.FetchNameValue("LINE_OFFSET") == nullptr)
1722 7 : aosMD.SetNameValue("LINE_OFFSET", "0");
1723 :
1724 10 : if (aosMD.FetchNameValue("PIXEL_STEP") == nullptr)
1725 : {
1726 : aosMD.SetNameValue(
1727 7 : "PIXEL_STEP", CPLSPrintf("%.17g", static_cast<double>(
1728 7 : GDALGetRasterXSize(hBaseDS)) /
1729 7 : nGeoLocXSize));
1730 : }
1731 :
1732 10 : if (aosMD.FetchNameValue("LINE_STEP") == nullptr)
1733 : {
1734 : aosMD.SetNameValue(
1735 7 : "LINE_STEP", CPLSPrintf("%.17g", static_cast<double>(
1736 7 : GDALGetRasterYSize(hBaseDS)) /
1737 7 : nGeoLocYSize));
1738 : }
1739 :
1740 10 : if (aosMD.FetchNameValue("GEOREFERENCING_CONVENTION") == nullptr)
1741 : {
1742 : const char *pszConvention =
1743 10 : poGeolocDS->GetMetadataItem("GEOREFERENCING_CONVENTION");
1744 10 : if (pszConvention)
1745 2 : aosMD.SetNameValue("GEOREFERENCING_CONVENTION", pszConvention);
1746 : }
1747 :
1748 20 : std::string osDebugMsg;
1749 10 : osDebugMsg = "Synthesized GEOLOCATION metadata for ";
1750 10 : osDebugMsg += bIsSource ? "source" : "target";
1751 10 : osDebugMsg += ":\n";
1752 102 : for (int i = 0; i < aosMD.size(); ++i)
1753 : {
1754 92 : osDebugMsg += " ";
1755 92 : osDebugMsg += aosMD[i];
1756 92 : osDebugMsg += '\n';
1757 : }
1758 10 : CPLDebug("GEOLOC", "%s", osDebugMsg.c_str());
1759 :
1760 10 : return aosMD;
1761 : }
1762 :
1763 : /************************************************************************/
1764 : /* GDALCreateGeoLocTransformer() */
1765 : /************************************************************************/
1766 :
1767 55 : void *GDALCreateGeoLocTransformerEx(GDALDatasetH hBaseDS,
1768 : CSLConstList papszGeolocationInfo,
1769 : int bReversed, const char *pszSourceDataset,
1770 : CSLConstList papszTransformOptions)
1771 :
1772 : {
1773 :
1774 55 : if (CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET") == nullptr ||
1775 53 : CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET") == nullptr ||
1776 53 : CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP") == nullptr ||
1777 53 : CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP") == nullptr ||
1778 161 : CSLFetchNameValue(papszGeolocationInfo, "X_BAND") == nullptr ||
1779 53 : CSLFetchNameValue(papszGeolocationInfo, "Y_BAND") == nullptr)
1780 : {
1781 2 : CPLError(CE_Failure, CPLE_AppDefined,
1782 : "Missing some geolocation fields in "
1783 : "GDALCreateGeoLocTransformer()");
1784 2 : return nullptr;
1785 : }
1786 :
1787 : /* -------------------------------------------------------------------- */
1788 : /* Initialize core info. */
1789 : /* -------------------------------------------------------------------- */
1790 : GDALGeoLocTransformInfo *psTransform =
1791 : static_cast<GDALGeoLocTransformInfo *>(
1792 53 : CPLCalloc(sizeof(GDALGeoLocTransformInfo), 1));
1793 :
1794 53 : psTransform->bReversed = CPL_TO_BOOL(bReversed);
1795 53 : psTransform->dfOversampleFactor = std::max(
1796 106 : 0.1,
1797 106 : std::min(2.0,
1798 53 : CPLAtof(CSLFetchNameValueDef(
1799 : papszTransformOptions, "GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1800 : CPLGetConfigOption("GDAL_GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1801 53 : "1.3")))));
1802 :
1803 53 : memcpy(psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE,
1804 : strlen(GDAL_GTI2_SIGNATURE));
1805 53 : psTransform->sTI.pszClassName = "GDALGeoLocTransformer";
1806 53 : psTransform->sTI.pfnTransform = GDALGeoLocTransform;
1807 53 : psTransform->sTI.pfnCleanup = GDALDestroyGeoLocTransformer;
1808 53 : psTransform->sTI.pfnSerialize = GDALSerializeGeoLocTransformer;
1809 53 : psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarGeoLocTransformer;
1810 :
1811 53 : psTransform->papszGeolocationInfo = CSLDuplicate(papszGeolocationInfo);
1812 :
1813 53 : psTransform->bGeographicSRSWithMinus180Plus180LongRange =
1814 53 : CPLTestBool(CSLFetchNameValueDef(
1815 : papszTransformOptions,
1816 : "GEOLOC_NORMALIZE_LONGITUDE_MINUS_180_PLUS_180", "NO"));
1817 :
1818 : /* -------------------------------------------------------------------- */
1819 : /* Pull geolocation info from the options/metadata. */
1820 : /* -------------------------------------------------------------------- */
1821 53 : psTransform->dfPIXEL_OFFSET =
1822 53 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET"));
1823 53 : psTransform->dfLINE_OFFSET =
1824 53 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET"));
1825 53 : psTransform->dfPIXEL_STEP =
1826 53 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP"));
1827 53 : psTransform->dfLINE_STEP =
1828 53 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP"));
1829 :
1830 53 : psTransform->bOriginIsTopLeftCorner = EQUAL(
1831 : CSLFetchNameValueDef(papszGeolocationInfo, "GEOREFERENCING_CONVENTION",
1832 : "TOP_LEFT_CORNER"),
1833 : "TOP_LEFT_CORNER");
1834 :
1835 : /* -------------------------------------------------------------------- */
1836 : /* Establish access to geolocation dataset(s). */
1837 : /* -------------------------------------------------------------------- */
1838 : const char *pszDSName =
1839 53 : CSLFetchNameValue(papszGeolocationInfo, "X_DATASET");
1840 53 : if (pszDSName != nullptr)
1841 : {
1842 106 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1843 53 : if (CPLTestBool(CSLFetchNameValueDef(
1844 54 : papszGeolocationInfo, "X_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1845 1 : (hBaseDS != nullptr || pszSourceDataset))
1846 : {
1847 8 : const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1848 7 : CPLGetDirnameSafe(pszSourceDataset
1849 : ? pszSourceDataset
1850 3 : : GDALGetDescription(hBaseDS))
1851 : .c_str(),
1852 4 : pszDSName);
1853 4 : psTransform->hDS_X =
1854 4 : GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1855 : }
1856 : else
1857 : {
1858 49 : psTransform->hDS_X = GDALOpenShared(pszDSName, GA_ReadOnly);
1859 : }
1860 : }
1861 : else
1862 : {
1863 0 : psTransform->hDS_X = hBaseDS;
1864 0 : if (hBaseDS)
1865 : {
1866 0 : GDALReferenceDataset(psTransform->hDS_X);
1867 0 : psTransform->papszGeolocationInfo =
1868 0 : CSLSetNameValue(psTransform->papszGeolocationInfo, "X_DATASET",
1869 : GDALGetDescription(hBaseDS));
1870 : }
1871 : }
1872 :
1873 53 : pszDSName = CSLFetchNameValue(papszGeolocationInfo, "Y_DATASET");
1874 53 : if (pszDSName != nullptr)
1875 : {
1876 106 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1877 53 : if (CPLTestBool(CSLFetchNameValueDef(
1878 54 : papszGeolocationInfo, "Y_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1879 1 : (hBaseDS != nullptr || pszSourceDataset))
1880 : {
1881 8 : const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1882 7 : CPLGetDirnameSafe(pszSourceDataset
1883 : ? pszSourceDataset
1884 3 : : GDALGetDescription(hBaseDS))
1885 : .c_str(),
1886 4 : pszDSName);
1887 4 : psTransform->hDS_Y =
1888 4 : GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1889 : }
1890 : else
1891 : {
1892 49 : psTransform->hDS_Y = GDALOpenShared(pszDSName, GA_ReadOnly);
1893 : }
1894 : }
1895 : else
1896 : {
1897 0 : psTransform->hDS_Y = hBaseDS;
1898 0 : if (hBaseDS)
1899 : {
1900 0 : GDALReferenceDataset(psTransform->hDS_Y);
1901 0 : psTransform->papszGeolocationInfo =
1902 0 : CSLSetNameValue(psTransform->papszGeolocationInfo, "Y_DATASET",
1903 : GDALGetDescription(hBaseDS));
1904 : }
1905 : }
1906 :
1907 53 : if (psTransform->hDS_X == nullptr || psTransform->hDS_Y == nullptr)
1908 : {
1909 0 : GDALDestroyGeoLocTransformer(psTransform);
1910 0 : return nullptr;
1911 : }
1912 :
1913 : /* -------------------------------------------------------------------- */
1914 : /* Get the band handles. */
1915 : /* -------------------------------------------------------------------- */
1916 : const int nXBand =
1917 53 : std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "X_BAND")));
1918 53 : psTransform->hBand_X = GDALGetRasterBand(psTransform->hDS_X, nXBand);
1919 :
1920 53 : psTransform->dfNoDataX = GDALGetRasterNoDataValue(
1921 : psTransform->hBand_X, &(psTransform->bHasNoData));
1922 :
1923 : const int nYBand =
1924 53 : std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "Y_BAND")));
1925 53 : psTransform->hBand_Y = GDALGetRasterBand(psTransform->hDS_Y, nYBand);
1926 :
1927 53 : if (psTransform->hBand_X == nullptr || psTransform->hBand_Y == nullptr)
1928 : {
1929 0 : GDALDestroyGeoLocTransformer(psTransform);
1930 0 : return nullptr;
1931 : }
1932 :
1933 53 : psTransform->bSwapXY = CPLTestBool(
1934 : CSLFetchNameValueDef(papszGeolocationInfo, "SWAP_XY", "NO"));
1935 :
1936 : /* -------------------------------------------------------------------- */
1937 : /* Check that X and Y bands have the same dimensions */
1938 : /* -------------------------------------------------------------------- */
1939 53 : const int nXSize_XBand = GDALGetRasterXSize(psTransform->hDS_X);
1940 53 : const int nYSize_XBand = GDALGetRasterYSize(psTransform->hDS_X);
1941 53 : const int nXSize_YBand = GDALGetRasterXSize(psTransform->hDS_Y);
1942 53 : const int nYSize_YBand = GDALGetRasterYSize(psTransform->hDS_Y);
1943 53 : if (nYSize_XBand == 1 || nYSize_YBand == 1)
1944 : {
1945 15 : if (nYSize_XBand != 1 || nYSize_YBand != 1)
1946 : {
1947 0 : CPLError(CE_Failure, CPLE_AppDefined,
1948 : "X_BAND and Y_BAND should have both nYSize == 1");
1949 0 : GDALDestroyGeoLocTransformer(psTransform);
1950 0 : return nullptr;
1951 : }
1952 : }
1953 38 : else if (nXSize_XBand != nXSize_YBand || nYSize_XBand != nYSize_YBand)
1954 : {
1955 0 : CPLError(CE_Failure, CPLE_AppDefined,
1956 : "X_BAND and Y_BAND do not have the same dimensions");
1957 0 : GDALDestroyGeoLocTransformer(psTransform);
1958 0 : return nullptr;
1959 : }
1960 :
1961 53 : if (nXSize_XBand <= 0 || nYSize_XBand <= 0 || nXSize_YBand <= 0 ||
1962 : nYSize_YBand <= 0)
1963 : {
1964 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid X_BAND / Y_BAND size");
1965 0 : GDALDestroyGeoLocTransformer(psTransform);
1966 0 : return nullptr;
1967 : }
1968 :
1969 : // Is it a regular grid ? That is:
1970 : // The XBAND contains the x coordinates for all lines.
1971 : // The YBAND contains the y coordinates for all columns.
1972 53 : const bool bIsRegularGrid = (nYSize_XBand == 1 && nYSize_YBand == 1);
1973 :
1974 53 : const int nXSize = nXSize_XBand;
1975 53 : const int nYSize = bIsRegularGrid ? nXSize_YBand : nYSize_XBand;
1976 :
1977 106 : if (static_cast<size_t>(nXSize) >
1978 53 : std::numeric_limits<size_t>::max() / static_cast<size_t>(nYSize))
1979 : {
1980 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %d x %d", nXSize,
1981 : nYSize);
1982 0 : GDALDestroyGeoLocTransformer(psTransform);
1983 0 : return nullptr;
1984 : }
1985 :
1986 53 : psTransform->nGeoLocXSize = nXSize;
1987 53 : psTransform->nGeoLocYSize = nYSize;
1988 :
1989 53 : if (hBaseDS && psTransform->dfPIXEL_OFFSET == 0 &&
1990 49 : psTransform->dfLINE_OFFSET == 0 && psTransform->dfPIXEL_STEP == 1 &&
1991 41 : psTransform->dfLINE_STEP == 1)
1992 : {
1993 81 : if (GDALGetRasterXSize(hBaseDS) > nXSize ||
1994 40 : GDALGetRasterYSize(hBaseDS) > nYSize)
1995 : {
1996 2 : CPLError(CE_Warning, CPLE_AppDefined,
1997 : "Geolocation array is %d x %d large, "
1998 : "whereas dataset is %d x %d large. Result might be "
1999 : "incorrect due to lack of values in geolocation array.",
2000 : nXSize, nYSize, GDALGetRasterXSize(hBaseDS),
2001 : GDALGetRasterYSize(hBaseDS));
2002 : }
2003 : }
2004 :
2005 : /* -------------------------------------------------------------------- */
2006 : /* Load the geolocation array. */
2007 : /* -------------------------------------------------------------------- */
2008 :
2009 : // The quadtree method is experimental. It simplifies the code
2010 : // significantly, but unfortunately burns more RAM and is slower.
2011 : const bool bUseQuadtree =
2012 53 : EQUAL(CPLGetConfigOption("GDAL_GEOLOC_INVERSE_METHOD", "BACKMAP"),
2013 : "QUADTREE");
2014 :
2015 : // Decide if we should C-arrays for geoloc and backmap, or on-disk
2016 : // temporary datasets.
2017 53 : const char *pszUseTempDatasets = CSLFetchNameValueDef(
2018 : papszTransformOptions, "GEOLOC_USE_TEMP_DATASETS",
2019 : CPLGetConfigOption("GDAL_GEOLOC_USE_TEMP_DATASETS", nullptr));
2020 53 : if (pszUseTempDatasets)
2021 4 : psTransform->bUseArray = !CPLTestBool(pszUseTempDatasets);
2022 : else
2023 : {
2024 49 : constexpr int MEGAPIXEL_LIMIT = 24;
2025 49 : psTransform->bUseArray =
2026 49 : nXSize < MEGAPIXEL_LIMIT * 1000 * 1000 / nYSize;
2027 49 : if (!psTransform->bUseArray)
2028 : {
2029 0 : CPLDebug("GEOLOC",
2030 : "Using temporary GTiff backing to store backmap, because "
2031 : "geoloc arrays require %d megapixels, exceeding the %d "
2032 : "megapixels limit. You can set the "
2033 : "GDAL_GEOLOC_USE_TEMP_DATASETS configuration option to "
2034 : "NO to force RAM storage of backmap",
2035 0 : static_cast<int>(static_cast<int64_t>(nXSize) * nYSize /
2036 : (1000 * 1000)),
2037 : MEGAPIXEL_LIMIT);
2038 : }
2039 : }
2040 :
2041 53 : if (psTransform->bUseArray)
2042 : {
2043 51 : auto pAccessors = new GDALGeoLocCArrayAccessors(psTransform);
2044 51 : psTransform->pAccessors = pAccessors;
2045 51 : if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2046 : {
2047 0 : GDALDestroyGeoLocTransformer(psTransform);
2048 0 : return nullptr;
2049 : }
2050 : }
2051 : else
2052 : {
2053 2 : auto pAccessors = new GDALGeoLocDatasetAccessors(psTransform);
2054 2 : psTransform->pAccessors = pAccessors;
2055 2 : if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2056 : {
2057 0 : GDALDestroyGeoLocTransformer(psTransform);
2058 0 : return nullptr;
2059 : }
2060 : }
2061 :
2062 53 : return psTransform;
2063 : }
2064 :
2065 : /** Create GeoLocation transformer */
2066 1 : void *GDALCreateGeoLocTransformer(GDALDatasetH hBaseDS,
2067 : char **papszGeolocationInfo, int bReversed)
2068 :
2069 : {
2070 1 : return GDALCreateGeoLocTransformerEx(hBaseDS, papszGeolocationInfo,
2071 1 : bReversed, nullptr, nullptr);
2072 : }
2073 :
2074 : /************************************************************************/
2075 : /* GDALDestroyGeoLocTransformer() */
2076 : /************************************************************************/
2077 :
2078 : /** Destroy GeoLocation transformer */
2079 53 : void GDALDestroyGeoLocTransformer(void *pTransformAlg)
2080 :
2081 : {
2082 53 : if (pTransformAlg == nullptr)
2083 0 : return;
2084 :
2085 53 : GDALGeoLocTransformInfo *psTransform =
2086 : static_cast<GDALGeoLocTransformInfo *>(pTransformAlg);
2087 :
2088 53 : CSLDestroy(psTransform->papszGeolocationInfo);
2089 :
2090 53 : if (psTransform->bUseArray)
2091 : delete static_cast<GDALGeoLocCArrayAccessors *>(
2092 51 : psTransform->pAccessors);
2093 : else
2094 : delete static_cast<GDALGeoLocDatasetAccessors *>(
2095 2 : psTransform->pAccessors);
2096 :
2097 106 : if (psTransform->hDS_X != nullptr &&
2098 53 : GDALDereferenceDataset(psTransform->hDS_X) == 0)
2099 31 : GDALClose(psTransform->hDS_X);
2100 :
2101 106 : if (psTransform->hDS_Y != nullptr &&
2102 53 : GDALDereferenceDataset(psTransform->hDS_Y) == 0)
2103 45 : GDALClose(psTransform->hDS_Y);
2104 :
2105 53 : if (psTransform->hQuadTree != nullptr)
2106 4 : CPLQuadTreeDestroy(psTransform->hQuadTree);
2107 :
2108 53 : CPLFree(pTransformAlg);
2109 : }
2110 :
2111 : /** Use GeoLocation transformer */
2112 32118 : int GDALGeoLocTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
2113 : double *padfX, double *padfY, double *padfZ,
2114 : int *panSuccess)
2115 : {
2116 32118 : GDALGeoLocTransformInfo *psTransform =
2117 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2118 32118 : if (psTransform->bUseArray)
2119 : {
2120 29995 : return GDALGeoLoc<GDALGeoLocCArrayAccessors>::Transform(
2121 : pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2122 29995 : panSuccess);
2123 : }
2124 : else
2125 : {
2126 2123 : return GDALGeoLoc<GDALGeoLocDatasetAccessors>::Transform(
2127 : pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2128 2123 : panSuccess);
2129 : }
2130 : }
2131 :
2132 : /************************************************************************/
2133 : /* GDALSerializeGeoLocTransformer() */
2134 : /************************************************************************/
2135 :
2136 1 : CPLXMLNode *GDALSerializeGeoLocTransformer(void *pTransformArg)
2137 :
2138 : {
2139 1 : VALIDATE_POINTER1(pTransformArg, "GDALSerializeGeoLocTransformer", nullptr);
2140 :
2141 1 : GDALGeoLocTransformInfo *psInfo =
2142 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2143 :
2144 : CPLXMLNode *psTree =
2145 1 : CPLCreateXMLNode(nullptr, CXT_Element, "GeoLocTransformer");
2146 :
2147 : /* -------------------------------------------------------------------- */
2148 : /* Serialize bReversed. */
2149 : /* -------------------------------------------------------------------- */
2150 1 : CPLCreateXMLElementAndValue(
2151 : psTree, "Reversed",
2152 2 : CPLString().Printf("%d", static_cast<int>(psInfo->bReversed)));
2153 :
2154 : /* -------------------------------------------------------------------- */
2155 : /* geoloc metadata. */
2156 : /* -------------------------------------------------------------------- */
2157 1 : char **papszMD = psInfo->papszGeolocationInfo;
2158 1 : CPLXMLNode *psMD = CPLCreateXMLNode(psTree, CXT_Element, "Metadata");
2159 :
2160 10 : for (int i = 0; papszMD != nullptr && papszMD[i] != nullptr; i++)
2161 : {
2162 9 : char *pszKey = nullptr;
2163 9 : const char *pszRawValue = CPLParseNameValue(papszMD[i], &pszKey);
2164 :
2165 9 : CPLXMLNode *psMDI = CPLCreateXMLNode(psMD, CXT_Element, "MDI");
2166 9 : CPLSetXMLValue(psMDI, "#key", pszKey);
2167 9 : CPLCreateXMLNode(psMDI, CXT_Text, pszRawValue);
2168 :
2169 9 : CPLFree(pszKey);
2170 : }
2171 :
2172 1 : return psTree;
2173 : }
2174 :
2175 : /************************************************************************/
2176 : /* GDALDeserializeGeoLocTransformer() */
2177 : /************************************************************************/
2178 :
2179 1 : void *GDALDeserializeGeoLocTransformer(CPLXMLNode *psTree)
2180 :
2181 : {
2182 : /* -------------------------------------------------------------------- */
2183 : /* Collect metadata. */
2184 : /* -------------------------------------------------------------------- */
2185 1 : CPLXMLNode *psMetadata = CPLGetXMLNode(psTree, "Metadata");
2186 :
2187 1 : if (psMetadata == nullptr || psMetadata->eType != CXT_Element ||
2188 1 : !EQUAL(psMetadata->pszValue, "Metadata"))
2189 0 : return nullptr;
2190 :
2191 1 : char **papszMD = nullptr;
2192 :
2193 12 : for (CPLXMLNode *psMDI = psMetadata->psChild; psMDI != nullptr;
2194 11 : psMDI = psMDI->psNext)
2195 : {
2196 11 : if (!EQUAL(psMDI->pszValue, "MDI") || psMDI->eType != CXT_Element ||
2197 11 : psMDI->psChild == nullptr || psMDI->psChild->psNext == nullptr ||
2198 11 : psMDI->psChild->eType != CXT_Attribute ||
2199 11 : psMDI->psChild->psChild == nullptr)
2200 0 : continue;
2201 :
2202 11 : papszMD = CSLSetNameValue(papszMD, psMDI->psChild->psChild->pszValue,
2203 11 : psMDI->psChild->psNext->pszValue);
2204 : }
2205 :
2206 : /* -------------------------------------------------------------------- */
2207 : /* Get other flags. */
2208 : /* -------------------------------------------------------------------- */
2209 1 : const int bReversed = atoi(CPLGetXMLValue(psTree, "Reversed", "0"));
2210 :
2211 : /* -------------------------------------------------------------------- */
2212 : /* Generate transformation. */
2213 : /* -------------------------------------------------------------------- */
2214 :
2215 : const char *pszSourceDataset =
2216 1 : CPLGetXMLValue(psTree, "SourceDataset", nullptr);
2217 :
2218 1 : void *pResult = GDALCreateGeoLocTransformerEx(nullptr, papszMD, bReversed,
2219 : pszSourceDataset, nullptr);
2220 :
2221 : /* -------------------------------------------------------------------- */
2222 : /* Cleanup GCP copy. */
2223 : /* -------------------------------------------------------------------- */
2224 1 : CSLDestroy(papszMD);
2225 :
2226 1 : return pResult;
2227 : }
|