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 10501780 : bool GDALGeoLoc<Accessors>::PixelLineToXY(
524 : const GDALGeoLocTransformInfo *psTransform, const int nGeoLocPixel,
525 : const int nGeoLocLine, double &dfX, double &dfY)
526 : {
527 10501780 : if (nGeoLocPixel >= 0 && nGeoLocPixel < psTransform->nGeoLocXSize &&
528 9826260 : nGeoLocLine >= 0 && nGeoLocLine < psTransform->nGeoLocYSize)
529 : {
530 9711660 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
531 9711660 : const double dfGLX =
532 : pAccessors->geolocXAccessor.Get(nGeoLocPixel, nGeoLocLine);
533 9711660 : const double dfGLY =
534 : pAccessors->geolocYAccessor.Get(nGeoLocPixel, nGeoLocLine);
535 9711660 : if (psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX)
536 : {
537 46946 : return false;
538 : }
539 9664710 : dfX = dfGLX;
540 9664710 : dfY = dfGLY;
541 9664710 : 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 31164 : int GDALGeoLoc<Accessors>::Transform(void *pTransformArg, int bDstToSrc,
588 : int nPointCount, double *padfX,
589 : double *padfY, double * /* padfZ */,
590 : int *panSuccess)
591 : {
592 31164 : int bSuccess = TRUE;
593 31164 : GDALGeoLocTransformInfo *psTransform =
594 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
595 :
596 31164 : if (psTransform->bReversed)
597 0 : bDstToSrc = !bDstToSrc;
598 :
599 31164 : const double dfGeorefConventionOffset =
600 31164 : psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
601 :
602 : /* -------------------------------------------------------------------- */
603 : /* Do original pixel line to target geox/geoy. */
604 : /* -------------------------------------------------------------------- */
605 31164 : if (!bDstToSrc)
606 : {
607 70066 : for (int i = 0; i < nPointCount; i++)
608 : {
609 45165 : if (padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL)
610 : {
611 2745 : bSuccess = FALSE;
612 2745 : panSuccess[i] = FALSE;
613 2745 : continue;
614 : }
615 :
616 42420 : const double dfGeoLocPixel =
617 42420 : (padfX[i] - psTransform->dfPIXEL_OFFSET) /
618 42420 : psTransform->dfPIXEL_STEP -
619 : dfGeorefConventionOffset;
620 42420 : const double dfGeoLocLine =
621 42420 : (padfY[i] - psTransform->dfLINE_OFFSET) /
622 42420 : psTransform->dfLINE_STEP -
623 : dfGeorefConventionOffset;
624 :
625 42420 : if (!PixelLineToXY(psTransform, dfGeoLocPixel, dfGeoLocLine,
626 42420 : padfX[i], padfY[i]))
627 : {
628 2826 : bSuccess = FALSE;
629 2826 : panSuccess[i] = FALSE;
630 2826 : padfX[i] = HUGE_VAL;
631 2826 : padfY[i] = HUGE_VAL;
632 2826 : continue;
633 : }
634 :
635 39594 : if (psTransform->bSwapXY)
636 : {
637 568 : std::swap(padfX[i], padfY[i]);
638 : }
639 :
640 39594 : panSuccess[i] = TRUE;
641 : }
642 : }
643 :
644 : /* -------------------------------------------------------------------- */
645 : /* geox/geoy to pixel/line using backmap. */
646 : /* -------------------------------------------------------------------- */
647 : else
648 : {
649 6263 : if (psTransform->hQuadTree)
650 : {
651 24 : GDALGeoLocInverseTransformQuadtree(psTransform, nPointCount, padfX,
652 : padfY, panSuccess);
653 24 : return TRUE;
654 : }
655 :
656 6239 : const bool bGeolocMaxAccuracy = CPLTestBool(
657 : CPLGetConfigOption("GDAL_GEOLOC_USE_MAX_ACCURACY", "YES"));
658 :
659 : // Keep those objects in this outer scope, so they are re-used, to
660 : // save memory allocations.
661 12478 : OGRPoint oPoint;
662 12478 : OGRLinearRing oRing;
663 6239 : oRing.setNumPoints(5);
664 :
665 6239 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
666 :
667 216480 : for (int i = 0; i < nPointCount; i++)
668 : {
669 210241 : if (padfX[i] == HUGE_VAL || padfY[i] == HUGE_VAL)
670 : {
671 0 : bSuccess = FALSE;
672 0 : panSuccess[i] = FALSE;
673 0 : continue;
674 : }
675 :
676 210241 : if (psTransform->bSwapXY)
677 : {
678 836 : std::swap(padfX[i], padfY[i]);
679 : }
680 :
681 210241 : const double dfGeoX = padfX[i];
682 210241 : const double dfGeoY = padfY[i];
683 :
684 210241 : const double dfBMX =
685 210241 : ((padfX[i] - psTransform->adfBackMapGeoTransform[0]) /
686 : psTransform->adfBackMapGeoTransform[1]);
687 210241 : const double dfBMY =
688 210241 : ((padfY[i] - psTransform->adfBackMapGeoTransform[3]) /
689 : psTransform->adfBackMapGeoTransform[5]);
690 :
691 210241 : if (!(dfBMX >= 0 && dfBMY >= 0 &&
692 210111 : dfBMX + 1 < psTransform->nBackMapWidth &&
693 208952 : dfBMY + 1 < psTransform->nBackMapHeight))
694 : {
695 2068 : bSuccess = FALSE;
696 2068 : panSuccess[i] = FALSE;
697 2068 : padfX[i] = HUGE_VAL;
698 2068 : padfY[i] = HUGE_VAL;
699 2068 : continue;
700 : }
701 :
702 208173 : const int iBMX = static_cast<int>(dfBMX);
703 208173 : const int iBMY = static_cast<int>(dfBMY);
704 :
705 208173 : const auto fBMX_0_0 = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
706 208173 : const auto fBMY_0_0 = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
707 208173 : if (fBMX_0_0 == INVALID_BMXY)
708 : {
709 86072 : bSuccess = FALSE;
710 86072 : panSuccess[i] = FALSE;
711 86072 : padfX[i] = HUGE_VAL;
712 86072 : padfY[i] = HUGE_VAL;
713 86072 : continue;
714 : }
715 :
716 122101 : const auto fBMX_1_0 =
717 : pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY);
718 122101 : const auto fBMY_1_0 =
719 : pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY);
720 122101 : const auto fBMX_0_1 =
721 : pAccessors->backMapXAccessor.Get(iBMX, iBMY + 1);
722 122101 : const auto fBMY_0_1 =
723 : pAccessors->backMapYAccessor.Get(iBMX, iBMY + 1);
724 122101 : const auto fBMX_1_1 =
725 : pAccessors->backMapXAccessor.Get(iBMX + 1, iBMY + 1);
726 122101 : const auto fBMY_1_1 =
727 : pAccessors->backMapYAccessor.Get(iBMX + 1, iBMY + 1);
728 122101 : if (fBMX_1_0 != INVALID_BMXY && fBMX_0_1 != INVALID_BMXY &&
729 : fBMX_1_1 != INVALID_BMXY)
730 : {
731 119870 : padfX[i] =
732 119870 : (1 - (dfBMY - iBMY)) *
733 119870 : (fBMX_0_0 + (dfBMX - iBMX) * (fBMX_1_0 - fBMX_0_0)) +
734 119870 : (dfBMY - iBMY) *
735 119870 : (fBMX_0_1 + (dfBMX - iBMX) * (fBMX_1_1 - fBMX_0_1));
736 119870 : padfY[i] =
737 119870 : (1 - (dfBMY - iBMY)) *
738 119870 : (fBMY_0_0 + (dfBMX - iBMX) * (fBMY_1_0 - fBMY_0_0)) +
739 119870 : (dfBMY - iBMY) *
740 119870 : (fBMY_0_1 + (dfBMX - iBMX) * (fBMY_1_1 - fBMY_0_1));
741 : }
742 2231 : else if (fBMX_1_0 != INVALID_BMXY)
743 : {
744 1686 : padfX[i] = fBMX_0_0 + (dfBMX - iBMX) * (fBMX_1_0 - fBMX_0_0);
745 1686 : padfY[i] = fBMY_0_0 + (dfBMX - iBMX) * (fBMY_1_0 - fBMY_0_0);
746 : }
747 545 : else if (fBMX_0_1 != INVALID_BMXY)
748 : {
749 410 : padfX[i] = fBMX_0_0 + (dfBMY - iBMY) * (fBMX_0_1 - fBMX_0_0);
750 410 : padfY[i] = fBMY_0_0 + (dfBMY - iBMY) * (fBMY_0_1 - fBMY_0_0);
751 : }
752 : else
753 : {
754 135 : padfX[i] = fBMX_0_0;
755 135 : padfY[i] = fBMY_0_0;
756 : }
757 :
758 122101 : const double dfGeoLocPixel =
759 122101 : (padfX[i] - psTransform->dfPIXEL_OFFSET) /
760 122101 : psTransform->dfPIXEL_STEP -
761 : dfGeorefConventionOffset;
762 122101 : const double dfGeoLocLine =
763 122101 : (padfY[i] - psTransform->dfLINE_OFFSET) /
764 122101 : psTransform->dfLINE_STEP -
765 : dfGeorefConventionOffset;
766 : #if 0
767 : CPLDebug("GEOLOC", "%f %f %f %f", padfX[i], padfY[i], dfGeoLocPixel, dfGeoLocLine);
768 : if( !psTransform->bOriginIsTopLeftCorner )
769 : {
770 : if( dfGeoLocPixel + dfGeorefConventionOffset > psTransform->nGeoLocXSize-1 ||
771 : dfGeoLocLine + dfGeorefConventionOffset > psTransform->nGeoLocYSize-1 )
772 : {
773 : panSuccess[i] = FALSE;
774 : padfX[i] = HUGE_VAL;
775 : padfY[i] = HUGE_VAL;
776 : continue;
777 : }
778 : }
779 : #endif
780 122101 : if (!bGeolocMaxAccuracy)
781 : {
782 0 : panSuccess[i] = TRUE;
783 0 : continue;
784 : }
785 :
786 : // Now that we have an approximate solution, identify a matching
787 : // cell in the geolocation array, where we can use inverse bilinear
788 : // interpolation to find the exact solution.
789 :
790 : // NOTE: if the geolocation array is an affine transformation,
791 : // the approximate solution should match the exact one, if the
792 : // backmap has correctly been built.
793 :
794 122101 : oPoint.setX(dfGeoX);
795 122101 : oPoint.setY(dfGeoY);
796 : // The thresholds and radius are rather empirical and have been
797 : // tuned on the product
798 : // S5P_TEST_L2__NO2____20190509T220707_20190509T234837_08137_01_010400_20200220T091343.nc
799 : // that includes the north pole.
800 : // Amended with the test case of
801 : // https://github.com/OSGeo/gdal/issues/5823
802 122101 : const int nSearchRadius =
803 122101 : psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
804 72344 : fabs(dfGeoY) >= 85
805 : ? 5
806 : : 3;
807 122101 : const int nGeoLocPixel =
808 122101 : static_cast<int>(std::floor(dfGeoLocPixel));
809 122101 : const int nGeoLocLine = static_cast<int>(std::floor(dfGeoLocLine));
810 :
811 122101 : bool bDone = false;
812 : // Using the above approximate nGeoLocPixel, nGeoLocLine, try to
813 : // find a forward cell that includes (dfGeoX, dfGeoY), with an
814 : // increasing search radius, up to nSearchRadius.
815 329732 : for (int r = 0; !bDone && r <= nSearchRadius; r++)
816 : {
817 1672277 : for (int iter = 0; !bDone && iter < (r == 0 ? 1 : 8 * r);
818 : ++iter)
819 : {
820 : // For r=1, the below formulas will give the following
821 : // offsets:
822 : // (-1,1), (0,1), (1,1), (1,0), (1,-1), (0,-1), (1,-1)
823 2807190 : const int sx = (r == 0) ? 0
824 1342545 : : (iter < 2 * r) ? -r + iter
825 985782 : : (iter < 4 * r) ? r
826 646809 : : (iter < 6 * r) ? r - (iter - 4 * r)
827 : : -r;
828 2807190 : const int sy = (r == 0) ? 0
829 1342545 : : (iter < 2 * r) ? r
830 985782 : : (iter < 4 * r) ? r - (iter - 2 * r)
831 646809 : : (iter < 6 * r) ? -r
832 317228 : : -r + (iter - 6 * r);
833 1783986 : if (nGeoLocPixel >=
834 1464646 : static_cast<int>(psTransform->nGeoLocXSize) - sx ||
835 : nGeoLocLine >=
836 1233276 : static_cast<int>(psTransform->nGeoLocYSize) - sy)
837 : {
838 319344 : continue;
839 : }
840 1145302 : const int iX = nGeoLocPixel + sx;
841 1145302 : const int iY = nGeoLocLine + sy;
842 1145302 : if (iX >= -1 || iY >= -1)
843 : {
844 : double x0, y0, x1, y1, x2, y2, x3, y3;
845 :
846 1145118 : if (!PixelLineToXY(psTransform, iX, iY, x0, y0) ||
847 1105472 : !PixelLineToXY(psTransform, iX + 1, iY, x2, y2) ||
848 3347450 : !PixelLineToXY(psTransform, iX, iY + 1, x1, y1) ||
849 1096860 : !PixelLineToXY(psTransform, iX + 1, iY + 1, x3, y3))
850 : {
851 48686 : continue;
852 : }
853 :
854 1096432 : int nIters = 1;
855 : // For a bounding box crossing the anti-meridian, check
856 : // both around -180 and +180 deg.
857 1096432 : if (psTransform
858 1096432 : ->bGeographicSRSWithMinus180Plus180LongRange &&
859 950423 : std::fabs(x0) > 170 && std::fabs(x1) > 170 &&
860 50764 : std::fabs(x2) > 170 && std::fabs(x3) > 170 &&
861 37304 : (std::fabs(x1 - x0) > 180 ||
862 37012 : std::fabs(x2 - x0) > 180 ||
863 30492 : std::fabs(x3 - x0) > 180))
864 : {
865 7104 : nIters = 2;
866 7104 : if (x0 > 0)
867 0 : x0 -= 360;
868 7104 : if (x1 > 0)
869 292 : x1 -= 360;
870 7104 : if (x2 > 0)
871 6812 : x2 -= 360;
872 7104 : if (x3 > 0)
873 7104 : x3 -= 360;
874 : }
875 2199849 : for (int iIter = 0; !bDone && iIter < nIters; ++iIter)
876 : {
877 1103418 : if (iIter == 1)
878 : {
879 6986 : x0 += 360;
880 6986 : x1 += 360;
881 6986 : x2 += 360;
882 6986 : x3 += 360;
883 : }
884 1103418 : oRing.setPoint(0, x0, y0);
885 1103418 : oRing.setPoint(1, x2, y2);
886 1103418 : oRing.setPoint(2, x3, y3);
887 1103418 : oRing.setPoint(3, x1, y1);
888 1103418 : oRing.setPoint(4, x0, y0);
889 2103446 : if (oRing.isPointInRing(&oPoint) ||
890 1000033 : oRing.isPointOnRingBoundary(&oPoint))
891 : {
892 104799 : double dfX = static_cast<double>(iX);
893 104799 : double dfY = static_cast<double>(iY);
894 104799 : GDALInverseBilinearInterpolation(
895 : dfGeoX, dfGeoY, x0, y0, x1, y1, x2, y2, x3,
896 : y3, dfX, dfY);
897 :
898 104799 : dfX = (dfX + dfGeorefConventionOffset) *
899 104799 : psTransform->dfPIXEL_STEP +
900 104799 : psTransform->dfPIXEL_OFFSET;
901 104799 : dfY = (dfY + dfGeorefConventionOffset) *
902 104799 : psTransform->dfLINE_STEP +
903 104799 : psTransform->dfLINE_OFFSET;
904 :
905 : #ifdef DEBUG_GEOLOC_REALLY_VERBOSE
906 : CPLDebug("GEOLOC",
907 : "value before adjustment: %f %f, "
908 : "after adjustment: %f %f",
909 : padfX[i], padfY[i], dfX, dfY);
910 : #endif
911 :
912 104799 : padfX[i] = dfX;
913 104799 : padfY[i] = dfY;
914 :
915 104799 : bDone = true;
916 : }
917 : }
918 : }
919 : }
920 : }
921 122101 : if (!bDone)
922 : {
923 17302 : bSuccess = FALSE;
924 17302 : panSuccess[i] = FALSE;
925 17302 : padfX[i] = HUGE_VAL;
926 17302 : padfY[i] = HUGE_VAL;
927 17302 : continue;
928 : }
929 :
930 104799 : panSuccess[i] = TRUE;
931 : }
932 : }
933 :
934 31140 : return bSuccess;
935 : }
936 :
937 : /*! @endcond */
938 :
939 : /************************************************************************/
940 : /* GDALInverseBilinearInterpolation() */
941 : /************************************************************************/
942 :
943 : // (i,j) before the call should correspond to the input coordinates that give
944 : // (x0,y0) as output of the forward interpolation
945 : // After the call it will be updated to the input coordinates that give (x,y)
946 : // This assumes that (x,y) is within the polygon formed by
947 : // (x0, y0), (x2, y2), (x3, y3), (x1, y1), (x0, y0)
948 242996 : void GDALInverseBilinearInterpolation(const double x, const double y,
949 : const double x0, const double y0,
950 : const double x1, const double y1,
951 : const double x2, const double y2,
952 : const double x3, const double y3,
953 : double &i, double &j)
954 : {
955 : // Exact inverse bilinear interpolation method.
956 : // Maths from https://stackoverflow.com/a/812077
957 :
958 242996 : const double A = (x0 - x) * (y0 - y2) - (y0 - y) * (x0 - x2);
959 242996 : const double B = (((x0 - x) * (y1 - y3) - (y0 - y) * (x1 - x3)) +
960 242996 : ((x1 - x) * (y0 - y2) - (y1 - y) * (x0 - x2))) /
961 : 2;
962 242996 : const double C = (x1 - x) * (y1 - y3) - (y1 - y) * (x1 - x3);
963 242996 : const double denom = A - 2 * B + C;
964 : double s;
965 242996 : const double magnitudeOfValues = fabs(A) + fabs(B) + fabs(C);
966 242996 : if (fabs(denom) <= 1e-12 * magnitudeOfValues)
967 : {
968 : // Happens typically when the x_i,y_i points form a rectangle
969 : // Can also happen when they form a triangle.
970 130853 : s = A / (A - C);
971 : }
972 : else
973 : {
974 112143 : const double sqrtTerm = sqrt(B * B - A * C);
975 112143 : const double s1 = ((A - B) + sqrtTerm) / denom;
976 112143 : const double s2 = ((A - B) - sqrtTerm) / denom;
977 112143 : if (s1 < 0 || s1 > 1)
978 95694 : s = s2;
979 : else
980 16449 : s = s1;
981 : }
982 :
983 242996 : const double t_denom_x = (1 - s) * (x0 - x2) + s * (x1 - x3);
984 242996 : if (fabs(t_denom_x) > 1e-12 * magnitudeOfValues)
985 : {
986 241486 : i += ((1 - s) * (x0 - x) + s * (x1 - x)) / t_denom_x;
987 : }
988 : else
989 : {
990 1510 : const double t_denom_y = (1 - s) * (y0 - y2) + s * (y1 - y3);
991 1510 : if (fabs(t_denom_y) > 1e-12 * magnitudeOfValues)
992 : {
993 1510 : i += ((1 - s) * (y0 - y) + s * (y1 - y)) / t_denom_y;
994 : }
995 : }
996 :
997 242996 : j += s;
998 242996 : }
999 :
1000 : /************************************************************************/
1001 : /* GeoLocGenerateBackMap() */
1002 : /************************************************************************/
1003 :
1004 : /*! @cond Doxygen_Suppress */
1005 :
1006 : template <class Accessors>
1007 44 : bool GDALGeoLoc<Accessors>::GenerateBackMap(
1008 : GDALGeoLocTransformInfo *psTransform)
1009 :
1010 : {
1011 44 : CPLDebug("GEOLOC", "Starting backmap generation");
1012 44 : const int nXSize = psTransform->nGeoLocXSize;
1013 44 : const int nYSize = psTransform->nGeoLocYSize;
1014 :
1015 : /* -------------------------------------------------------------------- */
1016 : /* Decide on resolution for backmap. We aim for slightly */
1017 : /* higher resolution than the source but we can't easily */
1018 : /* establish how much dead space there is in the backmap, so it */
1019 : /* is approximate. */
1020 : /* -------------------------------------------------------------------- */
1021 44 : const double dfTargetPixels =
1022 44 : static_cast<double>(nXSize) * nYSize * psTransform->dfOversampleFactor;
1023 : const double dfPixelSizeSquare =
1024 44 : sqrt((psTransform->dfMaxX - psTransform->dfMinX) *
1025 44 : (psTransform->dfMaxY - psTransform->dfMinY) / dfTargetPixels);
1026 44 : if (dfPixelSizeSquare == 0.0)
1027 : {
1028 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid pixel size for backmap");
1029 0 : return false;
1030 : }
1031 :
1032 44 : const double dfMinX = psTransform->dfMinX - dfPixelSizeSquare / 2.0;
1033 44 : const double dfMaxX = psTransform->dfMaxX + dfPixelSizeSquare / 2.0;
1034 44 : const double dfMaxY = psTransform->dfMaxY + dfPixelSizeSquare / 2.0;
1035 44 : const double dfMinY = psTransform->dfMinY - dfPixelSizeSquare / 2.0;
1036 44 : const double dfBMXSize = std::ceil((dfMaxX - dfMinX) / dfPixelSizeSquare);
1037 44 : const double dfBMYSize = std::ceil((dfMaxY - dfMinY) / dfPixelSizeSquare);
1038 :
1039 : // +2 : +1 due to afterwards nBMXSize++, and another +1 as security margin
1040 : // for other computations.
1041 44 : if (!(dfBMXSize > 0 && dfBMXSize + 2 < INT_MAX) ||
1042 44 : !(dfBMYSize > 0 && dfBMYSize + 2 < INT_MAX))
1043 : {
1044 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %f x %f",
1045 : dfBMXSize, dfBMYSize);
1046 0 : return false;
1047 : }
1048 :
1049 44 : int nBMXSize = static_cast<int>(dfBMXSize);
1050 44 : int nBMYSize = static_cast<int>(dfBMYSize);
1051 :
1052 88 : if (static_cast<size_t>(1 + nBMYSize) >
1053 44 : std::numeric_limits<size_t>::max() / static_cast<size_t>(1 + nBMXSize))
1054 : {
1055 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %f x %f",
1056 : dfBMXSize, dfBMYSize);
1057 0 : return false;
1058 : }
1059 :
1060 44 : const double dfPixelXSize = (dfMaxX - dfMinX) / nBMXSize;
1061 44 : const double dfPixelYSize = (dfMaxY - dfMinY) / nBMYSize;
1062 :
1063 : // Extra pixel for right-edge and bottom-edge extensions in TOP_LEFT_CORNER
1064 : // convention.
1065 44 : nBMXSize++;
1066 44 : nBMYSize++;
1067 44 : psTransform->nBackMapWidth = nBMXSize;
1068 44 : psTransform->nBackMapHeight = nBMYSize;
1069 :
1070 44 : psTransform->adfBackMapGeoTransform[0] = dfMinX;
1071 44 : psTransform->adfBackMapGeoTransform[1] = dfPixelXSize;
1072 44 : psTransform->adfBackMapGeoTransform[2] = 0.0;
1073 44 : psTransform->adfBackMapGeoTransform[3] = dfMaxY;
1074 44 : psTransform->adfBackMapGeoTransform[4] = 0.0;
1075 44 : psTransform->adfBackMapGeoTransform[5] = -dfPixelYSize;
1076 :
1077 : /* -------------------------------------------------------------------- */
1078 : /* Allocate backmap. */
1079 : /* -------------------------------------------------------------------- */
1080 44 : auto pAccessors = static_cast<Accessors *>(psTransform->pAccessors);
1081 44 : if (!pAccessors->AllocateBackMap())
1082 0 : return false;
1083 :
1084 44 : const double dfGeorefConventionOffset =
1085 44 : psTransform->bOriginIsTopLeftCorner ? 0 : 0.5;
1086 :
1087 44 : const auto UpdateBackmap =
1088 8957995 : [&](int iBMX, int iBMY, double dfX, double dfY, double tempwt)
1089 : {
1090 822374 : const auto fBMX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1091 822374 : const auto fBMY = pAccessors->backMapYAccessor.Get(iBMX, iBMY);
1092 822374 : const float fUpdatedBMX =
1093 : fBMX +
1094 822374 : static_cast<float>(tempwt * ((dfX + dfGeorefConventionOffset) *
1095 822374 : psTransform->dfPIXEL_STEP +
1096 822374 : psTransform->dfPIXEL_OFFSET));
1097 822374 : const float fUpdatedBMY =
1098 : fBMY +
1099 822374 : static_cast<float>(tempwt * ((dfY + dfGeorefConventionOffset) *
1100 822374 : psTransform->dfLINE_STEP +
1101 822374 : psTransform->dfLINE_OFFSET));
1102 822374 : const float fUpdatedWeight =
1103 822374 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) +
1104 822374 : static_cast<float>(tempwt);
1105 :
1106 : // Only update the backmap if the updated averaged value results in a
1107 : // geoloc position that isn't too different from the original one.
1108 : // (there's no guarantee that if padfGeoLocX[i] ~= padfGeoLoc[j],
1109 : // padfGeoLoc[alpha * i + (1 - alpha) * j] ~= padfGeoLoc[i] )
1110 822374 : if (fUpdatedWeight > 0)
1111 : {
1112 822371 : const float fX = fUpdatedBMX / fUpdatedWeight;
1113 822371 : const float fY = fUpdatedBMY / fUpdatedWeight;
1114 822371 : const double dfGeoLocPixel =
1115 822371 : (fX - psTransform->dfPIXEL_OFFSET) / psTransform->dfPIXEL_STEP -
1116 : dfGeorefConventionOffset;
1117 822371 : const double dfGeoLocLine =
1118 822371 : (fY - psTransform->dfLINE_OFFSET) / psTransform->dfLINE_STEP -
1119 : dfGeorefConventionOffset;
1120 822371 : int iXAvg = static_cast<int>(std::max(0.0, dfGeoLocPixel));
1121 822371 : iXAvg = std::min(iXAvg, psTransform->nGeoLocXSize - 1);
1122 822371 : int iYAvg = static_cast<int>(std::max(0.0, dfGeoLocLine));
1123 822371 : iYAvg = std::min(iYAvg, psTransform->nGeoLocYSize - 1);
1124 822371 : const double dfGLX = pAccessors->geolocXAccessor.Get(iXAvg, iYAvg);
1125 822371 : const double dfGLY = pAccessors->geolocYAccessor.Get(iXAvg, iYAvg);
1126 :
1127 822371 : const unsigned iX = static_cast<unsigned>(dfX);
1128 822371 : const unsigned iY = static_cast<unsigned>(dfY);
1129 1644730 : if (!(psTransform->bHasNoData && dfGLX == psTransform->dfNoDataX) &&
1130 822361 : ((iX >= static_cast<unsigned>(nXSize - 1) ||
1131 808482 : iY >= static_cast<unsigned>(nYSize - 1)) ||
1132 798718 : (fabs(dfGLX - pAccessors->geolocXAccessor.Get(iX, iY)) <=
1133 798718 : 2 * dfPixelXSize &&
1134 756277 : fabs(dfGLY - pAccessors->geolocYAccessor.Get(iX, iY)) <=
1135 756277 : 2 * dfPixelYSize)))
1136 : {
1137 779782 : pAccessors->backMapXAccessor.Set(iBMX, iBMY, fUpdatedBMX);
1138 779782 : pAccessors->backMapYAccessor.Set(iBMX, iBMY, fUpdatedBMY);
1139 779782 : pAccessors->backMapWeightAccessor.Set(iBMX, iBMY,
1140 : fUpdatedWeight);
1141 : }
1142 : }
1143 : };
1144 :
1145 : // Keep those objects in this outer scope, so they are re-used, to
1146 : // save memory allocations.
1147 88 : OGRPoint oPoint;
1148 88 : OGRLinearRing oRing;
1149 44 : oRing.setNumPoints(5);
1150 :
1151 : /* -------------------------------------------------------------------- */
1152 : /* Run through the whole geoloc array forward projecting and */
1153 : /* pushing into the backmap. */
1154 : /* -------------------------------------------------------------------- */
1155 :
1156 : // Iterate over the (i,j) pixel space of the geolocation array, in a
1157 : // sufficiently dense way that if the geolocation array expressed an affine
1158 : // transformation, we would hit every node of the backmap.
1159 44 : const double dfStep = 1. / psTransform->dfOversampleFactor;
1160 :
1161 44 : constexpr int TILE_SIZE = GDALGeoLocDatasetAccessors::TILE_SIZE;
1162 44 : const int nYBlocks = DIV_ROUND_UP(nYSize, TILE_SIZE);
1163 44 : const int nXBlocks = DIV_ROUND_UP(nXSize, TILE_SIZE);
1164 :
1165 : // First compute for each block the start end ending floating-point
1166 : // pixel/line values
1167 88 : std::vector<std::pair<double, double>> yStartEnd(nYBlocks + 1);
1168 88 : std::vector<std::pair<double, double>> xStartEnd(nXBlocks + 1);
1169 :
1170 : {
1171 44 : int iYBlockLast = -1;
1172 44 : double dfY = -dfStep;
1173 2371 : for (; dfY <= static_cast<double>(nYSize) + 2 * dfStep; dfY += dfStep)
1174 : {
1175 2327 : const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1176 2327 : if (iYBlock != iYBlockLast)
1177 : {
1178 46 : CPLAssert(iYBlock == iYBlockLast + 1);
1179 46 : if (iYBlockLast >= 0)
1180 2 : yStartEnd[iYBlockLast].second = dfY + dfStep / 10;
1181 46 : yStartEnd[iYBlock].first = dfY;
1182 46 : iYBlockLast = iYBlock;
1183 : }
1184 : }
1185 44 : const int iYBlock = static_cast<int>(dfY / TILE_SIZE);
1186 44 : yStartEnd[iYBlock].second = dfY + dfStep / 10;
1187 : }
1188 :
1189 : {
1190 44 : int iXBlockLast = -1;
1191 44 : double dfX = -dfStep;
1192 2755 : for (; dfX <= static_cast<double>(nXSize) + 2 * dfStep; dfX += dfStep)
1193 : {
1194 2711 : const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1195 2711 : if (iXBlock != iXBlockLast)
1196 : {
1197 46 : CPLAssert(iXBlock == iXBlockLast + 1);
1198 46 : if (iXBlockLast >= 0)
1199 2 : xStartEnd[iXBlockLast].second = dfX + dfStep / 10;
1200 46 : xStartEnd[iXBlock].first = dfX;
1201 46 : iXBlockLast = iXBlock;
1202 : }
1203 : }
1204 44 : const int iXBlock = static_cast<int>(dfX / TILE_SIZE);
1205 44 : xStartEnd[iXBlock].second = dfX + dfStep / 10;
1206 : }
1207 :
1208 90 : for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
1209 : {
1210 94 : for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
1211 : {
1212 : #if 0
1213 : CPLDebug("Process geoloc block (y=%d,x=%d) for y in [%f, %f] and x in [%f, %f]",
1214 : iYBlock, iXBlock,
1215 : yStartEnd[iYBlock].first, yStartEnd[iYBlock].second,
1216 : xStartEnd[iXBlock].first, xStartEnd[iXBlock].second);
1217 : #endif
1218 2637 : for (double dfY = yStartEnd[iYBlock].first;
1219 2637 : dfY < yStartEnd[iYBlock].second; dfY += dfStep)
1220 : {
1221 427135 : for (double dfX = xStartEnd[iXBlock].first;
1222 427135 : dfX < xStartEnd[iXBlock].second; dfX += dfStep)
1223 : {
1224 : // Use forward geolocation array interpolation to compute
1225 : // the georeferenced position corresponding to (dfX, dfY)
1226 : double dfGeoLocX;
1227 : double dfGeoLocY;
1228 424546 : if (!PixelLineToXY(psTransform, dfX, dfY, dfGeoLocX,
1229 : dfGeoLocY))
1230 145313 : continue;
1231 :
1232 : // Compute the floating point coordinates in the pixel space
1233 : // of the backmap
1234 417864 : const double dBMX = static_cast<double>(
1235 417864 : (dfGeoLocX - dfMinX) / dfPixelXSize);
1236 :
1237 417864 : const double dBMY = static_cast<double>(
1238 417864 : (dfMaxY - dfGeoLocY) / dfPixelYSize);
1239 :
1240 : // Get top left index by truncation
1241 417864 : const int iBMX = static_cast<int>(std::floor(dBMX));
1242 417864 : const int iBMY = static_cast<int>(std::floor(dBMY));
1243 :
1244 417864 : if (iBMX >= 0 && iBMX < nBMXSize && iBMY >= 0 &&
1245 : iBMY < nBMYSize)
1246 : {
1247 : // Compute the georeferenced position of the top-left
1248 : // index of the backmap
1249 416115 : double dfGeoX = dfMinX + iBMX * dfPixelXSize;
1250 416115 : const double dfGeoY = dfMaxY - iBMY * dfPixelYSize;
1251 :
1252 416115 : bool bMatchingGeoLocCellFound = false;
1253 :
1254 416115 : const int nOuterIters =
1255 416115 : psTransform->bGeographicSRSWithMinus180Plus180LongRange &&
1256 311777 : fabs(dfGeoX) >= 180
1257 : ? 2
1258 : : 1;
1259 :
1260 832254 : for (int iOuterIter = 0; iOuterIter < nOuterIters;
1261 : ++iOuterIter)
1262 : {
1263 416139 : if (iOuterIter == 1 && dfGeoX >= 180)
1264 0 : dfGeoX -= 360;
1265 416139 : else if (iOuterIter == 1 && dfGeoX <= -180)
1266 24 : dfGeoX += 360;
1267 :
1268 : // Identify a cell (quadrilateral in georeferenced
1269 : // space) in the geolocation array in which dfGeoX,
1270 : // dfGeoY falls into.
1271 416139 : oPoint.setX(dfGeoX);
1272 416139 : oPoint.setY(dfGeoY);
1273 416139 : const int nX = static_cast<int>(std::floor(dfX));
1274 416139 : const int nY = static_cast<int>(std::floor(dfY));
1275 1196012 : for (int sx = -1;
1276 1196012 : !bMatchingGeoLocCellFound && sx <= 0; sx++)
1277 : {
1278 2289683 : for (int sy = -1;
1279 2289683 : !bMatchingGeoLocCellFound && sy <= 0; sy++)
1280 : {
1281 1511101 : const int pixel = nX + sx;
1282 1511101 : const int line = nY + sy;
1283 : double x0, y0, x1, y1, x2, y2, x3, y3;
1284 1511101 : if (!PixelLineToXY(psTransform, pixel, line,
1285 1510533 : x0, y0) ||
1286 1510533 : !PixelLineToXY(psTransform, pixel + 1,
1287 1510313 : line, x2, y2) ||
1288 1510313 : !PixelLineToXY(psTransform, pixel,
1289 3021630 : line + 1, x1, y1) ||
1290 1509883 : !PixelLineToXY(psTransform, pixel + 1,
1291 : line + 1, x3, y3))
1292 : {
1293 1288 : break;
1294 : }
1295 :
1296 1509813 : int nIters = 1;
1297 1509813 : if (psTransform
1298 1509813 : ->bGeographicSRSWithMinus180Plus180LongRange &&
1299 1178290 : std::fabs(x0) > 170 &&
1300 13548 : std::fabs(x1) > 170 &&
1301 13310 : std::fabs(x2) > 170 &&
1302 12092 : std::fabs(x3) > 170 &&
1303 11914 : (std::fabs(x1 - x0) > 180 ||
1304 11858 : std::fabs(x2 - x0) > 180 ||
1305 10496 : std::fabs(x3 - x0) > 180))
1306 : {
1307 1486 : nIters = 2;
1308 1486 : if (x0 > 0)
1309 0 : x0 -= 360;
1310 1486 : if (x1 > 0)
1311 56 : x1 -= 360;
1312 1486 : if (x2 > 0)
1313 1418 : x2 -= 360;
1314 1486 : if (x3 > 0)
1315 1486 : x3 -= 360;
1316 : }
1317 3021110 : for (int iIter = 0; iIter < nIters; ++iIter)
1318 : {
1319 1511299 : if (iIter == 1)
1320 : {
1321 1486 : x0 += 360;
1322 1486 : x1 += 360;
1323 1486 : x2 += 360;
1324 1486 : x3 += 360;
1325 : }
1326 :
1327 1511299 : oRing.setPoint(0, x0, y0);
1328 1511299 : oRing.setPoint(1, x2, y2);
1329 1511299 : oRing.setPoint(2, x3, y3);
1330 1511299 : oRing.setPoint(3, x1, y1);
1331 1511299 : oRing.setPoint(4, x0, y0);
1332 2884630 : if (oRing.isPointInRing(&oPoint) ||
1333 1373329 : oRing.isPointOnRingBoundary(
1334 : &oPoint))
1335 : {
1336 138173 : bMatchingGeoLocCellFound = true;
1337 138173 : double dfBMXValue = pixel;
1338 138173 : double dfBMYValue = line;
1339 138173 : GDALInverseBilinearInterpolation(
1340 : dfGeoX, dfGeoY, x0, y0, x1, y1,
1341 : x2, y2, x3, y3, dfBMXValue,
1342 : dfBMYValue);
1343 :
1344 138173 : dfBMXValue =
1345 138173 : (dfBMXValue +
1346 138173 : dfGeorefConventionOffset) *
1347 138173 : psTransform->dfPIXEL_STEP +
1348 138173 : psTransform->dfPIXEL_OFFSET;
1349 138173 : dfBMYValue =
1350 138173 : (dfBMYValue +
1351 138173 : dfGeorefConventionOffset) *
1352 138173 : psTransform->dfLINE_STEP +
1353 138173 : psTransform->dfLINE_OFFSET;
1354 :
1355 138173 : pAccessors->backMapXAccessor.Set(
1356 : iBMX, iBMY,
1357 : static_cast<float>(dfBMXValue));
1358 138173 : pAccessors->backMapYAccessor.Set(
1359 : iBMX, iBMY,
1360 : static_cast<float>(dfBMYValue));
1361 138173 : pAccessors->backMapWeightAccessor
1362 : .Set(iBMX, iBMY, 1.0f);
1363 : }
1364 : }
1365 : }
1366 : }
1367 : }
1368 416115 : if (bMatchingGeoLocCellFound)
1369 138173 : continue;
1370 : }
1371 :
1372 : // We will end up here in non-nominal cases, with nodata,
1373 : // holes, etc.
1374 :
1375 : // Check if the center is in range
1376 279691 : if (iBMX < -1 || iBMY < -1 || iBMX > nBMXSize ||
1377 : iBMY > nBMYSize)
1378 458 : continue;
1379 :
1380 279233 : const double fracBMX = dBMX - iBMX;
1381 279233 : const double fracBMY = dBMY - iBMY;
1382 :
1383 : // Check logic for top left pixel
1384 278929 : if ((iBMX >= 0) && (iBMY >= 0) && (iBMX < nBMXSize) &&
1385 558162 : (iBMY < nBMYSize) &&
1386 277942 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY) !=
1387 : 1.0f)
1388 : {
1389 185775 : const double tempwt = (1.0 - fracBMX) * (1.0 - fracBMY);
1390 185775 : UpdateBackmap(iBMX, iBMY, dfX, dfY, tempwt);
1391 : }
1392 :
1393 : // Check logic for top right pixel
1394 278830 : if ((iBMY >= 0) && (iBMX + 1 < nBMXSize) &&
1395 558063 : (iBMY < nBMYSize) &&
1396 278190 : pAccessors->backMapWeightAccessor.Get(iBMX + 1, iBMY) !=
1397 : 1.0f)
1398 : {
1399 189705 : const double tempwt = fracBMX * (1.0 - fracBMY);
1400 189705 : UpdateBackmap(iBMX + 1, iBMY, dfX, dfY, tempwt);
1401 : }
1402 :
1403 : // Check logic for bottom right pixel
1404 557747 : if ((iBMX + 1 < nBMXSize) && (iBMY + 1 < nBMYSize) &&
1405 278514 : pAccessors->backMapWeightAccessor.Get(iBMX + 1,
1406 278514 : iBMY + 1) != 1.0f)
1407 : {
1408 226485 : const double tempwt = fracBMX * fracBMY;
1409 226485 : UpdateBackmap(iBMX + 1, iBMY + 1, dfX, dfY, tempwt);
1410 : }
1411 :
1412 : // Check logic for bottom left pixel
1413 278929 : if ((iBMX >= 0) && (iBMX < nBMXSize) &&
1414 836440 : (iBMY + 1 < nBMYSize) &&
1415 278278 : pAccessors->backMapWeightAccessor.Get(iBMX, iBMY + 1) !=
1416 : 1.0f)
1417 : {
1418 220409 : const double tempwt = (1.0 - fracBMX) * fracBMY;
1419 220409 : UpdateBackmap(iBMX, iBMY + 1, dfX, dfY, tempwt);
1420 : }
1421 : }
1422 : }
1423 : }
1424 : }
1425 :
1426 : // Each pixel in the backmap may have multiple entries.
1427 : // We now go in average it out using the weights
1428 148 : START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, (void)0,
1429 : iXStart, iXEnd, iYStart, iYEnd)
1430 : {
1431 2268 : for (int iY = iYStart; iY < iYEnd; ++iY)
1432 : {
1433 327929 : for (int iX = iXStart; iX < iXEnd; ++iX)
1434 : {
1435 : // Check if pixel was only touch during neighbor scan
1436 : // But no real weight was added as source point matched
1437 : // backmap grid node
1438 465470 : const auto weight =
1439 325721 : pAccessors->backMapWeightAccessor.Get(iX, iY);
1440 325721 : if (weight > 0)
1441 : {
1442 236251 : pAccessors->backMapXAccessor.Set(
1443 : iX, iY,
1444 170330 : pAccessors->backMapXAccessor.Get(iX, iY) / weight);
1445 236251 : pAccessors->backMapYAccessor.Set(
1446 : iX, iY,
1447 170330 : pAccessors->backMapYAccessor.Get(iX, iY) / weight);
1448 : }
1449 : else
1450 : {
1451 155391 : pAccessors->backMapXAccessor.Set(iX, iY, INVALID_BMXY);
1452 155391 : pAccessors->backMapYAccessor.Set(iX, iY, INVALID_BMXY);
1453 : }
1454 : }
1455 : }
1456 : }
1457 : END_ITER_PER_BLOCK
1458 :
1459 44 : pAccessors->FreeWghtsBackMap();
1460 :
1461 : // Fill holes in backmap
1462 44 : auto poBackmapDS = pAccessors->GetBackmapDataset();
1463 :
1464 44 : pAccessors->FlushBackmapCaches();
1465 :
1466 : #ifdef DEBUG_GEOLOC
1467 : if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1468 : {
1469 : poBackmapDS->SetGeoTransform(psTransform->adfBackMapGeoTransform);
1470 : GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1471 : "/tmp/geoloc_before_fill.tif", poBackmapDS,
1472 : false, nullptr, nullptr, nullptr));
1473 : }
1474 : #endif
1475 :
1476 44 : constexpr double dfMaxSearchDist = 3.0;
1477 44 : constexpr int nSmoothingIterations = 1;
1478 132 : for (int i = 1; i <= 2; i++)
1479 : {
1480 88 : GDALFillNodata(GDALRasterBand::ToHandle(poBackmapDS->GetRasterBand(i)),
1481 : nullptr, dfMaxSearchDist,
1482 : 0, // unused parameter
1483 : nSmoothingIterations, nullptr, nullptr, nullptr);
1484 : }
1485 :
1486 : #ifdef DEBUG_GEOLOC
1487 : if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1488 : {
1489 : GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1490 : "/tmp/geoloc_after_fill.tif", poBackmapDS,
1491 : false, nullptr, nullptr, nullptr));
1492 : }
1493 : #endif
1494 :
1495 : // A final hole filling logic, proceeding line by line, and filling
1496 : // holes when the backmap values surrounding the hole are close enough.
1497 : struct LastValidStruct
1498 : {
1499 : int iX = -1;
1500 : float bmX = 0;
1501 : };
1502 :
1503 44 : std::vector<LastValidStruct> lastValid(TILE_SIZE);
1504 220 : const auto reinitLine = [&lastValid]()
1505 : {
1506 44 : const size_t nSize = lastValid.size();
1507 44 : lastValid.clear();
1508 44 : lastValid.resize(nSize);
1509 : };
1510 148 : START_ITER_PER_BLOCK(nBMXSize, TILE_SIZE, nBMYSize, TILE_SIZE, reinitLine(),
1511 : iXStart, iXEnd, iYStart, iYEnd)
1512 : {
1513 60 : const int iYCount = iYEnd - iYStart;
1514 2268 : for (int iYIter = 0; iYIter < iYCount; ++iYIter)
1515 : {
1516 2208 : int iLastValidIX = lastValid[iYIter].iX;
1517 2208 : float bmXLastValid = lastValid[iYIter].bmX;
1518 2208 : const int iBMY = iYStart + iYIter;
1519 327929 : for (int iBMX = iXStart; iBMX < iXEnd; ++iBMX)
1520 : {
1521 325721 : const float bmX = pAccessors->backMapXAccessor.Get(iBMX, iBMY);
1522 325721 : if (bmX == INVALID_BMXY)
1523 127144 : continue;
1524 199129 : if (iLastValidIX != -1 && iBMX > iLastValidIX + 1 &&
1525 552 : fabs(bmX - bmXLastValid) <= 2)
1526 : {
1527 354 : const float bmY =
1528 239 : pAccessors->backMapYAccessor.Get(iBMX, iBMY);
1529 354 : const float bmYLastValid =
1530 239 : pAccessors->backMapYAccessor.Get(iLastValidIX, iBMY);
1531 239 : if (fabs(bmY - bmYLastValid) <= 2)
1532 : {
1533 266 : for (int iBMXInner = iLastValidIX + 1; iBMXInner < iBMX;
1534 : ++iBMXInner)
1535 : {
1536 145 : const float alpha =
1537 145 : static_cast<float>(iBMXInner - iLastValidIX) /
1538 145 : (iBMX - iLastValidIX);
1539 145 : pAccessors->backMapXAccessor.Set(
1540 : iBMXInner, iBMY,
1541 145 : (1.0f - alpha) * bmXLastValid + alpha * bmX);
1542 145 : pAccessors->backMapYAccessor.Set(
1543 : iBMXInner, iBMY,
1544 145 : (1.0f - alpha) * bmYLastValid + alpha * bmY);
1545 : }
1546 : }
1547 : }
1548 198577 : iLastValidIX = iBMX;
1549 198577 : bmXLastValid = bmX;
1550 : }
1551 2208 : lastValid[iYIter].iX = iLastValidIX;
1552 2208 : lastValid[iYIter].bmX = bmXLastValid;
1553 : }
1554 : }
1555 : END_ITER_PER_BLOCK
1556 :
1557 : #ifdef DEBUG_GEOLOC
1558 : if (CPLTestBool(CPLGetConfigOption("GEOLOC_DUMP", "NO")))
1559 : {
1560 : pAccessors->FlushBackmapCaches();
1561 :
1562 : GDALClose(GDALCreateCopy(GDALGetDriverByName("GTiff"),
1563 : "/tmp/geoloc_after_line_fill.tif", poBackmapDS,
1564 : false, nullptr, nullptr, nullptr));
1565 : }
1566 : #endif
1567 :
1568 44 : pAccessors->ReleaseBackmapDataset(poBackmapDS);
1569 44 : CPLDebug("GEOLOC", "Ending backmap generation");
1570 :
1571 44 : return true;
1572 : }
1573 :
1574 : /*! @endcond */
1575 :
1576 : /************************************************************************/
1577 : /* GDALGeoLocRescale() */
1578 : /************************************************************************/
1579 :
1580 4 : static void GDALGeoLocRescale(char **&papszMD, const char *pszItem,
1581 : double dfRatio, double dfDefaultVal)
1582 : {
1583 : const double dfVal =
1584 4 : dfRatio * CPLAtofM(CSLFetchNameValueDef(
1585 4 : papszMD, pszItem, CPLSPrintf("%.17g", dfDefaultVal)));
1586 :
1587 4 : papszMD = CSLSetNameValue(papszMD, pszItem, CPLSPrintf("%.17g", dfVal));
1588 4 : }
1589 :
1590 : /************************************************************************/
1591 : /* GDALCreateSimilarGeoLocTransformer() */
1592 : /************************************************************************/
1593 :
1594 1 : static void *GDALCreateSimilarGeoLocTransformer(void *hTransformArg,
1595 : double dfRatioX,
1596 : double dfRatioY)
1597 : {
1598 1 : VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarGeoLocTransformer",
1599 : nullptr);
1600 :
1601 1 : GDALGeoLocTransformInfo *psInfo =
1602 : static_cast<GDALGeoLocTransformInfo *>(hTransformArg);
1603 :
1604 1 : char **papszGeolocationInfo = CSLDuplicate(psInfo->papszGeolocationInfo);
1605 :
1606 1 : if (dfRatioX != 1.0 || dfRatioY != 1.0)
1607 : {
1608 1 : GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_OFFSET", dfRatioX, 0.0);
1609 1 : GDALGeoLocRescale(papszGeolocationInfo, "LINE_OFFSET", dfRatioY, 0.0);
1610 1 : GDALGeoLocRescale(papszGeolocationInfo, "PIXEL_STEP", 1.0 / dfRatioX,
1611 : 1.0);
1612 1 : GDALGeoLocRescale(papszGeolocationInfo, "LINE_STEP", 1.0 / dfRatioY,
1613 : 1.0);
1614 : }
1615 :
1616 : auto psInfoNew =
1617 2 : static_cast<GDALGeoLocTransformInfo *>(GDALCreateGeoLocTransformer(
1618 1 : nullptr, papszGeolocationInfo, psInfo->bReversed));
1619 1 : psInfoNew->dfOversampleFactor = psInfo->dfOversampleFactor;
1620 :
1621 1 : CSLDestroy(papszGeolocationInfo);
1622 :
1623 1 : return psInfoNew;
1624 : }
1625 :
1626 : /************************************************************************/
1627 : /* GDALCreateGeolocationMetadata() */
1628 : /************************************************************************/
1629 :
1630 : /** Synthetize the content of a GEOLOCATION metadata domain from a
1631 : * geolocation dataset.
1632 : *
1633 : * This is used when doing gdalwarp -to GEOLOC_ARRAY=some.tif
1634 : */
1635 10 : CPLStringList GDALCreateGeolocationMetadata(GDALDatasetH hBaseDS,
1636 : const char *pszGeolocationDataset,
1637 : bool bIsSource)
1638 : {
1639 20 : CPLStringList aosMD;
1640 :
1641 : // Open geolocation dataset
1642 : auto poGeolocDS = std::unique_ptr<GDALDataset>(
1643 20 : GDALDataset::Open(pszGeolocationDataset, GDAL_OF_RASTER));
1644 10 : if (poGeolocDS == nullptr)
1645 : {
1646 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid dataset: %s",
1647 : pszGeolocationDataset);
1648 2 : return CPLStringList();
1649 : }
1650 8 : const int nGeoLocXSize = poGeolocDS->GetRasterXSize();
1651 8 : const int nGeoLocYSize = poGeolocDS->GetRasterYSize();
1652 8 : if (nGeoLocXSize == 0 || nGeoLocYSize == 0)
1653 : {
1654 0 : CPLError(CE_Failure, CPLE_AppDefined,
1655 : "Invalid dataset dimension for %s: %dx%d",
1656 : pszGeolocationDataset, nGeoLocXSize, nGeoLocYSize);
1657 0 : return CPLStringList();
1658 : }
1659 :
1660 : // Import the GEOLOCATION metadata from the geolocation dataset, if existing
1661 8 : auto papszGeolocMDFromGeolocDS = poGeolocDS->GetMetadata("GEOLOCATION");
1662 8 : if (papszGeolocMDFromGeolocDS)
1663 3 : aosMD = CSLDuplicate(papszGeolocMDFromGeolocDS);
1664 :
1665 8 : aosMD.SetNameValue("X_DATASET", pszGeolocationDataset);
1666 8 : aosMD.SetNameValue("Y_DATASET", pszGeolocationDataset);
1667 :
1668 : // Set X_BAND, Y_BAND to 1, 2 if they are not specified in the initial
1669 : // GEOLOC metadata domain.and the geolocation dataset has 2 bands.
1670 13 : if (aosMD.FetchNameValue("X_BAND") == nullptr &&
1671 5 : aosMD.FetchNameValue("Y_BAND") == nullptr)
1672 : {
1673 5 : if (poGeolocDS->GetRasterCount() != 2)
1674 : {
1675 1 : CPLError(CE_Failure, CPLE_AppDefined,
1676 : "Expected 2 bands for %s. Got %d", pszGeolocationDataset,
1677 : poGeolocDS->GetRasterCount());
1678 1 : return CPLStringList();
1679 : }
1680 4 : aosMD.SetNameValue("X_BAND", "1");
1681 4 : aosMD.SetNameValue("Y_BAND", "2");
1682 : }
1683 :
1684 : // Set the geoloc SRS from the geolocation dataset SRS if there's no
1685 : // explicit one in the initial GEOLOC metadata domain.
1686 7 : if (aosMD.FetchNameValue("SRS") == nullptr)
1687 : {
1688 7 : auto poSRS = poGeolocDS->GetSpatialRef();
1689 7 : if (poSRS)
1690 : {
1691 3 : char *pszWKT = nullptr;
1692 3 : poSRS->exportToWkt(&pszWKT);
1693 3 : aosMD.SetNameValue("SRS", pszWKT);
1694 3 : CPLFree(pszWKT);
1695 : }
1696 : }
1697 7 : if (aosMD.FetchNameValue("SRS") == nullptr)
1698 : {
1699 4 : aosMD.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);
1700 : }
1701 :
1702 : // Set default values for PIXEL/LINE_OFFSET/STEP if not present.
1703 7 : if (aosMD.FetchNameValue("PIXEL_OFFSET") == nullptr)
1704 4 : aosMD.SetNameValue("PIXEL_OFFSET", "0");
1705 :
1706 7 : if (aosMD.FetchNameValue("LINE_OFFSET") == nullptr)
1707 4 : aosMD.SetNameValue("LINE_OFFSET", "0");
1708 :
1709 7 : if (aosMD.FetchNameValue("PIXEL_STEP") == nullptr)
1710 : {
1711 : aosMD.SetNameValue(
1712 4 : "PIXEL_STEP", CPLSPrintf("%.17g", static_cast<double>(
1713 4 : GDALGetRasterXSize(hBaseDS)) /
1714 4 : nGeoLocXSize));
1715 : }
1716 :
1717 7 : if (aosMD.FetchNameValue("LINE_STEP") == nullptr)
1718 : {
1719 : aosMD.SetNameValue(
1720 4 : "LINE_STEP", CPLSPrintf("%.17g", static_cast<double>(
1721 4 : GDALGetRasterYSize(hBaseDS)) /
1722 4 : nGeoLocYSize));
1723 : }
1724 :
1725 7 : if (aosMD.FetchNameValue("GEOREFERENCING_CONVENTION") == nullptr)
1726 : {
1727 : const char *pszConvention =
1728 7 : poGeolocDS->GetMetadataItem("GEOREFERENCING_CONVENTION");
1729 7 : if (pszConvention)
1730 2 : aosMD.SetNameValue("GEOREFERENCING_CONVENTION", pszConvention);
1731 : }
1732 :
1733 14 : std::string osDebugMsg;
1734 7 : osDebugMsg = "Synthetized GEOLOCATION metadata for ";
1735 7 : osDebugMsg += bIsSource ? "source" : "target";
1736 7 : osDebugMsg += ":\n";
1737 72 : for (int i = 0; i < aosMD.size(); ++i)
1738 : {
1739 65 : osDebugMsg += " ";
1740 65 : osDebugMsg += aosMD[i];
1741 65 : osDebugMsg += '\n';
1742 : }
1743 7 : CPLDebug("GEOLOC", "%s", osDebugMsg.c_str());
1744 :
1745 7 : return aosMD;
1746 : }
1747 :
1748 : /************************************************************************/
1749 : /* GDALCreateGeoLocTransformer() */
1750 : /************************************************************************/
1751 :
1752 50 : void *GDALCreateGeoLocTransformerEx(GDALDatasetH hBaseDS,
1753 : CSLConstList papszGeolocationInfo,
1754 : int bReversed, const char *pszSourceDataset,
1755 : CSLConstList papszTransformOptions)
1756 :
1757 : {
1758 :
1759 50 : if (CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET") == nullptr ||
1760 48 : CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET") == nullptr ||
1761 48 : CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP") == nullptr ||
1762 48 : CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP") == nullptr ||
1763 146 : CSLFetchNameValue(papszGeolocationInfo, "X_BAND") == nullptr ||
1764 48 : CSLFetchNameValue(papszGeolocationInfo, "Y_BAND") == nullptr)
1765 : {
1766 2 : CPLError(CE_Failure, CPLE_AppDefined,
1767 : "Missing some geolocation fields in "
1768 : "GDALCreateGeoLocTransformer()");
1769 2 : return nullptr;
1770 : }
1771 :
1772 : /* -------------------------------------------------------------------- */
1773 : /* Initialize core info. */
1774 : /* -------------------------------------------------------------------- */
1775 : GDALGeoLocTransformInfo *psTransform =
1776 : static_cast<GDALGeoLocTransformInfo *>(
1777 48 : CPLCalloc(sizeof(GDALGeoLocTransformInfo), 1));
1778 :
1779 48 : psTransform->bReversed = CPL_TO_BOOL(bReversed);
1780 48 : psTransform->dfOversampleFactor = std::max(
1781 96 : 0.1,
1782 96 : std::min(2.0,
1783 48 : CPLAtof(CSLFetchNameValueDef(
1784 : papszTransformOptions, "GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1785 : CPLGetConfigOption("GDAL_GEOLOC_BACKMAP_OVERSAMPLE_FACTOR",
1786 48 : "1.3")))));
1787 :
1788 48 : memcpy(psTransform->sTI.abySignature, GDAL_GTI2_SIGNATURE,
1789 : strlen(GDAL_GTI2_SIGNATURE));
1790 48 : psTransform->sTI.pszClassName = "GDALGeoLocTransformer";
1791 48 : psTransform->sTI.pfnTransform = GDALGeoLocTransform;
1792 48 : psTransform->sTI.pfnCleanup = GDALDestroyGeoLocTransformer;
1793 48 : psTransform->sTI.pfnSerialize = GDALSerializeGeoLocTransformer;
1794 48 : psTransform->sTI.pfnCreateSimilar = GDALCreateSimilarGeoLocTransformer;
1795 :
1796 48 : psTransform->papszGeolocationInfo = CSLDuplicate(papszGeolocationInfo);
1797 :
1798 : /* -------------------------------------------------------------------- */
1799 : /* Pull geolocation info from the options/metadata. */
1800 : /* -------------------------------------------------------------------- */
1801 48 : psTransform->dfPIXEL_OFFSET =
1802 48 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_OFFSET"));
1803 48 : psTransform->dfLINE_OFFSET =
1804 48 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_OFFSET"));
1805 48 : psTransform->dfPIXEL_STEP =
1806 48 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "PIXEL_STEP"));
1807 48 : psTransform->dfLINE_STEP =
1808 48 : CPLAtof(CSLFetchNameValue(papszGeolocationInfo, "LINE_STEP"));
1809 :
1810 48 : psTransform->bOriginIsTopLeftCorner = EQUAL(
1811 : CSLFetchNameValueDef(papszGeolocationInfo, "GEOREFERENCING_CONVENTION",
1812 : "TOP_LEFT_CORNER"),
1813 : "TOP_LEFT_CORNER");
1814 :
1815 : /* -------------------------------------------------------------------- */
1816 : /* Establish access to geolocation dataset(s). */
1817 : /* -------------------------------------------------------------------- */
1818 : const char *pszDSName =
1819 48 : CSLFetchNameValue(papszGeolocationInfo, "X_DATASET");
1820 48 : if (pszDSName != nullptr)
1821 : {
1822 96 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1823 48 : if (CPLTestBool(CSLFetchNameValueDef(
1824 49 : papszGeolocationInfo, "X_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1825 1 : (hBaseDS != nullptr || pszSourceDataset))
1826 : {
1827 8 : const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1828 7 : CPLGetDirnameSafe(pszSourceDataset
1829 : ? pszSourceDataset
1830 3 : : GDALGetDescription(hBaseDS))
1831 : .c_str(),
1832 4 : pszDSName);
1833 4 : psTransform->hDS_X =
1834 4 : GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1835 : }
1836 : else
1837 : {
1838 44 : psTransform->hDS_X = GDALOpenShared(pszDSName, GA_ReadOnly);
1839 : }
1840 : }
1841 : else
1842 : {
1843 0 : psTransform->hDS_X = hBaseDS;
1844 0 : if (hBaseDS)
1845 : {
1846 0 : GDALReferenceDataset(psTransform->hDS_X);
1847 0 : psTransform->papszGeolocationInfo =
1848 0 : CSLSetNameValue(psTransform->papszGeolocationInfo, "X_DATASET",
1849 : GDALGetDescription(hBaseDS));
1850 : }
1851 : }
1852 :
1853 48 : pszDSName = CSLFetchNameValue(papszGeolocationInfo, "Y_DATASET");
1854 48 : if (pszDSName != nullptr)
1855 : {
1856 96 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
1857 48 : if (CPLTestBool(CSLFetchNameValueDef(
1858 49 : papszGeolocationInfo, "Y_DATASET_RELATIVE_TO_SOURCE", "NO")) &&
1859 1 : (hBaseDS != nullptr || pszSourceDataset))
1860 : {
1861 8 : const CPLString osFilename = CPLProjectRelativeFilenameSafe(
1862 7 : CPLGetDirnameSafe(pszSourceDataset
1863 : ? pszSourceDataset
1864 3 : : GDALGetDescription(hBaseDS))
1865 : .c_str(),
1866 4 : pszDSName);
1867 4 : psTransform->hDS_Y =
1868 4 : GDALOpenShared(osFilename.c_str(), GA_ReadOnly);
1869 : }
1870 : else
1871 : {
1872 44 : psTransform->hDS_Y = GDALOpenShared(pszDSName, GA_ReadOnly);
1873 : }
1874 : }
1875 : else
1876 : {
1877 0 : psTransform->hDS_Y = hBaseDS;
1878 0 : if (hBaseDS)
1879 : {
1880 0 : GDALReferenceDataset(psTransform->hDS_Y);
1881 0 : psTransform->papszGeolocationInfo =
1882 0 : CSLSetNameValue(psTransform->papszGeolocationInfo, "Y_DATASET",
1883 : GDALGetDescription(hBaseDS));
1884 : }
1885 : }
1886 :
1887 48 : if (psTransform->hDS_X == nullptr || psTransform->hDS_Y == nullptr)
1888 : {
1889 0 : GDALDestroyGeoLocTransformer(psTransform);
1890 0 : return nullptr;
1891 : }
1892 :
1893 : /* -------------------------------------------------------------------- */
1894 : /* Get the band handles. */
1895 : /* -------------------------------------------------------------------- */
1896 : const int nXBand =
1897 48 : std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "X_BAND")));
1898 48 : psTransform->hBand_X = GDALGetRasterBand(psTransform->hDS_X, nXBand);
1899 :
1900 48 : psTransform->dfNoDataX = GDALGetRasterNoDataValue(
1901 : psTransform->hBand_X, &(psTransform->bHasNoData));
1902 :
1903 : const int nYBand =
1904 48 : std::max(1, atoi(CSLFetchNameValue(papszGeolocationInfo, "Y_BAND")));
1905 48 : psTransform->hBand_Y = GDALGetRasterBand(psTransform->hDS_Y, nYBand);
1906 :
1907 48 : if (psTransform->hBand_X == nullptr || psTransform->hBand_Y == nullptr)
1908 : {
1909 0 : GDALDestroyGeoLocTransformer(psTransform);
1910 0 : return nullptr;
1911 : }
1912 :
1913 48 : psTransform->bSwapXY = CPLTestBool(
1914 : CSLFetchNameValueDef(papszGeolocationInfo, "SWAP_XY", "NO"));
1915 :
1916 : /* -------------------------------------------------------------------- */
1917 : /* Check that X and Y bands have the same dimensions */
1918 : /* -------------------------------------------------------------------- */
1919 48 : const int nXSize_XBand = GDALGetRasterXSize(psTransform->hDS_X);
1920 48 : const int nYSize_XBand = GDALGetRasterYSize(psTransform->hDS_X);
1921 48 : const int nXSize_YBand = GDALGetRasterXSize(psTransform->hDS_Y);
1922 48 : const int nYSize_YBand = GDALGetRasterYSize(psTransform->hDS_Y);
1923 48 : if (nYSize_XBand == 1 || nYSize_YBand == 1)
1924 : {
1925 15 : if (nYSize_XBand != 1 || nYSize_YBand != 1)
1926 : {
1927 0 : CPLError(CE_Failure, CPLE_AppDefined,
1928 : "X_BAND and Y_BAND should have both nYSize == 1");
1929 0 : GDALDestroyGeoLocTransformer(psTransform);
1930 0 : return nullptr;
1931 : }
1932 : }
1933 33 : else if (nXSize_XBand != nXSize_YBand || nYSize_XBand != nYSize_YBand)
1934 : {
1935 0 : CPLError(CE_Failure, CPLE_AppDefined,
1936 : "X_BAND and Y_BAND do not have the same dimensions");
1937 0 : GDALDestroyGeoLocTransformer(psTransform);
1938 0 : return nullptr;
1939 : }
1940 :
1941 48 : if (nXSize_XBand <= 0 || nYSize_XBand <= 0 || nXSize_YBand <= 0 ||
1942 : nYSize_YBand <= 0)
1943 : {
1944 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid X_BAND / Y_BAND size");
1945 0 : GDALDestroyGeoLocTransformer(psTransform);
1946 0 : return nullptr;
1947 : }
1948 :
1949 : // Is it a regular grid ? That is:
1950 : // The XBAND contains the x coordinates for all lines.
1951 : // The YBAND contains the y coordinates for all columns.
1952 48 : const bool bIsRegularGrid = (nYSize_XBand == 1 && nYSize_YBand == 1);
1953 :
1954 48 : const int nXSize = nXSize_XBand;
1955 48 : const int nYSize = bIsRegularGrid ? nXSize_YBand : nYSize_XBand;
1956 :
1957 96 : if (static_cast<size_t>(nXSize) >
1958 48 : std::numeric_limits<size_t>::max() / static_cast<size_t>(nYSize))
1959 : {
1960 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow : %d x %d", nXSize,
1961 : nYSize);
1962 0 : GDALDestroyGeoLocTransformer(psTransform);
1963 0 : return nullptr;
1964 : }
1965 :
1966 48 : psTransform->nGeoLocXSize = nXSize;
1967 48 : psTransform->nGeoLocYSize = nYSize;
1968 :
1969 48 : if (hBaseDS && psTransform->dfPIXEL_OFFSET == 0 &&
1970 44 : psTransform->dfLINE_OFFSET == 0 && psTransform->dfPIXEL_STEP == 1 &&
1971 37 : psTransform->dfLINE_STEP == 1)
1972 : {
1973 73 : if (GDALGetRasterXSize(hBaseDS) > nXSize ||
1974 36 : GDALGetRasterYSize(hBaseDS) > nYSize)
1975 : {
1976 2 : CPLError(CE_Warning, CPLE_AppDefined,
1977 : "Geolocation array is %d x %d large, "
1978 : "whereas dataset is %d x %d large. Result might be "
1979 : "incorrect due to lack of values in geolocation array.",
1980 : nXSize, nYSize, GDALGetRasterXSize(hBaseDS),
1981 : GDALGetRasterYSize(hBaseDS));
1982 : }
1983 : }
1984 :
1985 : /* -------------------------------------------------------------------- */
1986 : /* Load the geolocation array. */
1987 : /* -------------------------------------------------------------------- */
1988 :
1989 : // The quadtree method is experimental. It simplifies the code
1990 : // significantly, but unfortunately burns more RAM and is slower.
1991 : const bool bUseQuadtree =
1992 48 : EQUAL(CPLGetConfigOption("GDAL_GEOLOC_INVERSE_METHOD", "BACKMAP"),
1993 : "QUADTREE");
1994 :
1995 : // Decide if we should C-arrays for geoloc and backmap, or on-disk
1996 : // temporary datasets.
1997 48 : const char *pszUseTempDatasets = CSLFetchNameValueDef(
1998 : papszTransformOptions, "GEOLOC_USE_TEMP_DATASETS",
1999 : CPLGetConfigOption("GDAL_GEOLOC_USE_TEMP_DATASETS", nullptr));
2000 48 : if (pszUseTempDatasets)
2001 4 : psTransform->bUseArray = !CPLTestBool(pszUseTempDatasets);
2002 : else
2003 : {
2004 44 : constexpr int MEGAPIXEL_LIMIT = 24;
2005 44 : psTransform->bUseArray =
2006 44 : nXSize < MEGAPIXEL_LIMIT * 1000 * 1000 / nYSize;
2007 44 : if (!psTransform->bUseArray)
2008 : {
2009 0 : CPLDebug("GEOLOC",
2010 : "Using temporary GTiff backing to store backmap, because "
2011 : "geoloc arrays require %d megapixels, exceeding the %d "
2012 : "megapixels limit. You can set the "
2013 : "GDAL_GEOLOC_USE_TEMP_DATASETS configuration option to "
2014 : "NO to force RAM storage of backmap",
2015 0 : static_cast<int>(static_cast<int64_t>(nXSize) * nYSize /
2016 : (1000 * 1000)),
2017 : MEGAPIXEL_LIMIT);
2018 : }
2019 : }
2020 :
2021 48 : if (psTransform->bUseArray)
2022 : {
2023 46 : auto pAccessors = new GDALGeoLocCArrayAccessors(psTransform);
2024 46 : psTransform->pAccessors = pAccessors;
2025 46 : if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2026 : {
2027 0 : GDALDestroyGeoLocTransformer(psTransform);
2028 0 : return nullptr;
2029 : }
2030 : }
2031 : else
2032 : {
2033 2 : auto pAccessors = new GDALGeoLocDatasetAccessors(psTransform);
2034 2 : psTransform->pAccessors = pAccessors;
2035 2 : if (!pAccessors->Load(bIsRegularGrid, bUseQuadtree))
2036 : {
2037 0 : GDALDestroyGeoLocTransformer(psTransform);
2038 0 : return nullptr;
2039 : }
2040 : }
2041 :
2042 48 : return psTransform;
2043 : }
2044 :
2045 : /** Create GeoLocation transformer */
2046 1 : void *GDALCreateGeoLocTransformer(GDALDatasetH hBaseDS,
2047 : char **papszGeolocationInfo, int bReversed)
2048 :
2049 : {
2050 1 : return GDALCreateGeoLocTransformerEx(hBaseDS, papszGeolocationInfo,
2051 1 : bReversed, nullptr, nullptr);
2052 : }
2053 :
2054 : /************************************************************************/
2055 : /* GDALDestroyGeoLocTransformer() */
2056 : /************************************************************************/
2057 :
2058 : /** Destroy GeoLocation transformer */
2059 48 : void GDALDestroyGeoLocTransformer(void *pTransformAlg)
2060 :
2061 : {
2062 48 : if (pTransformAlg == nullptr)
2063 0 : return;
2064 :
2065 48 : GDALGeoLocTransformInfo *psTransform =
2066 : static_cast<GDALGeoLocTransformInfo *>(pTransformAlg);
2067 :
2068 48 : CSLDestroy(psTransform->papszGeolocationInfo);
2069 :
2070 48 : if (psTransform->bUseArray)
2071 : delete static_cast<GDALGeoLocCArrayAccessors *>(
2072 46 : psTransform->pAccessors);
2073 : else
2074 : delete static_cast<GDALGeoLocDatasetAccessors *>(
2075 2 : psTransform->pAccessors);
2076 :
2077 96 : if (psTransform->hDS_X != nullptr &&
2078 48 : GDALDereferenceDataset(psTransform->hDS_X) == 0)
2079 27 : GDALClose(psTransform->hDS_X);
2080 :
2081 96 : if (psTransform->hDS_Y != nullptr &&
2082 48 : GDALDereferenceDataset(psTransform->hDS_Y) == 0)
2083 40 : GDALClose(psTransform->hDS_Y);
2084 :
2085 48 : if (psTransform->hQuadTree != nullptr)
2086 4 : CPLQuadTreeDestroy(psTransform->hQuadTree);
2087 :
2088 48 : CPLFree(pTransformAlg);
2089 : }
2090 :
2091 : /** Use GeoLocation transformer */
2092 31164 : int GDALGeoLocTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
2093 : double *padfX, double *padfY, double *padfZ,
2094 : int *panSuccess)
2095 : {
2096 31164 : GDALGeoLocTransformInfo *psTransform =
2097 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2098 31164 : if (psTransform->bUseArray)
2099 : {
2100 29043 : return GDALGeoLoc<GDALGeoLocCArrayAccessors>::Transform(
2101 : pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2102 29043 : panSuccess);
2103 : }
2104 : else
2105 : {
2106 2121 : return GDALGeoLoc<GDALGeoLocDatasetAccessors>::Transform(
2107 : pTransformArg, bDstToSrc, nPointCount, padfX, padfY, padfZ,
2108 2121 : panSuccess);
2109 : }
2110 : }
2111 :
2112 : /************************************************************************/
2113 : /* GDALSerializeGeoLocTransformer() */
2114 : /************************************************************************/
2115 :
2116 1 : CPLXMLNode *GDALSerializeGeoLocTransformer(void *pTransformArg)
2117 :
2118 : {
2119 1 : VALIDATE_POINTER1(pTransformArg, "GDALSerializeGeoLocTransformer", nullptr);
2120 :
2121 1 : GDALGeoLocTransformInfo *psInfo =
2122 : static_cast<GDALGeoLocTransformInfo *>(pTransformArg);
2123 :
2124 : CPLXMLNode *psTree =
2125 1 : CPLCreateXMLNode(nullptr, CXT_Element, "GeoLocTransformer");
2126 :
2127 : /* -------------------------------------------------------------------- */
2128 : /* Serialize bReversed. */
2129 : /* -------------------------------------------------------------------- */
2130 1 : CPLCreateXMLElementAndValue(
2131 : psTree, "Reversed",
2132 2 : CPLString().Printf("%d", static_cast<int>(psInfo->bReversed)));
2133 :
2134 : /* -------------------------------------------------------------------- */
2135 : /* geoloc metadata. */
2136 : /* -------------------------------------------------------------------- */
2137 1 : char **papszMD = psInfo->papszGeolocationInfo;
2138 1 : CPLXMLNode *psMD = CPLCreateXMLNode(psTree, CXT_Element, "Metadata");
2139 :
2140 10 : for (int i = 0; papszMD != nullptr && papszMD[i] != nullptr; i++)
2141 : {
2142 9 : char *pszKey = nullptr;
2143 9 : const char *pszRawValue = CPLParseNameValue(papszMD[i], &pszKey);
2144 :
2145 9 : CPLXMLNode *psMDI = CPLCreateXMLNode(psMD, CXT_Element, "MDI");
2146 9 : CPLSetXMLValue(psMDI, "#key", pszKey);
2147 9 : CPLCreateXMLNode(psMDI, CXT_Text, pszRawValue);
2148 :
2149 9 : CPLFree(pszKey);
2150 : }
2151 :
2152 1 : return psTree;
2153 : }
2154 :
2155 : /************************************************************************/
2156 : /* GDALDeserializeGeoLocTransformer() */
2157 : /************************************************************************/
2158 :
2159 1 : void *GDALDeserializeGeoLocTransformer(CPLXMLNode *psTree)
2160 :
2161 : {
2162 : /* -------------------------------------------------------------------- */
2163 : /* Collect metadata. */
2164 : /* -------------------------------------------------------------------- */
2165 1 : CPLXMLNode *psMetadata = CPLGetXMLNode(psTree, "Metadata");
2166 :
2167 1 : if (psMetadata == nullptr || psMetadata->eType != CXT_Element ||
2168 1 : !EQUAL(psMetadata->pszValue, "Metadata"))
2169 0 : return nullptr;
2170 :
2171 1 : char **papszMD = nullptr;
2172 :
2173 12 : for (CPLXMLNode *psMDI = psMetadata->psChild; psMDI != nullptr;
2174 11 : psMDI = psMDI->psNext)
2175 : {
2176 11 : if (!EQUAL(psMDI->pszValue, "MDI") || psMDI->eType != CXT_Element ||
2177 11 : psMDI->psChild == nullptr || psMDI->psChild->psNext == nullptr ||
2178 11 : psMDI->psChild->eType != CXT_Attribute ||
2179 11 : psMDI->psChild->psChild == nullptr)
2180 0 : continue;
2181 :
2182 11 : papszMD = CSLSetNameValue(papszMD, psMDI->psChild->psChild->pszValue,
2183 11 : psMDI->psChild->psNext->pszValue);
2184 : }
2185 :
2186 : /* -------------------------------------------------------------------- */
2187 : /* Get other flags. */
2188 : /* -------------------------------------------------------------------- */
2189 1 : const int bReversed = atoi(CPLGetXMLValue(psTree, "Reversed", "0"));
2190 :
2191 : /* -------------------------------------------------------------------- */
2192 : /* Generate transformation. */
2193 : /* -------------------------------------------------------------------- */
2194 :
2195 : const char *pszSourceDataset =
2196 1 : CPLGetXMLValue(psTree, "SourceDataset", nullptr);
2197 :
2198 1 : void *pResult = GDALCreateGeoLocTransformerEx(nullptr, papszMD, bReversed,
2199 : pszSourceDataset, nullptr);
2200 :
2201 : /* -------------------------------------------------------------------- */
2202 : /* Cleanup GCP copy. */
2203 : /* -------------------------------------------------------------------- */
2204 1 : CSLDestroy(papszMD);
2205 :
2206 1 : return pResult;
2207 : }
|