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