Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/alg/gdal_crs.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  Mapinfo Image Warper
4
 * Purpose:  Implementation of the GDALTransformer wrapper around CRS.C
5
 functions
6
 *           to build a polynomial transformation based on ground control
7
 *           points.
8
 * Author:   Frank Warmerdam, warmerdam@pobox.com
9
 *
10
 ***************************************************************************
11
12
    CRS.C - Center for Remote Sensing rectification routines
13
14
    Written By: Brian J. Buckley
15
16
            At: The Center for Remote Sensing
17
                Michigan State University
18
                302 Berkey Hall
19
                East Lansing, MI  48824
20
                (517)353-7195
21
22
    Written: 12/19/91
23
24
    Last Update: 12/26/91 Brian J. Buckley
25
    Last Update:  1/24/92 Brian J. Buckley
26
      Added printout of trnfile. Triggered by BDEBUG.
27
    Last Update:  1/27/92 Brian J. Buckley
28
      Fixed bug so that only the active control points were used.
29
    Last Update:  6/29/2011 C. F. Stallmann & R. van den Dool (South African
30
 National Space Agency) GCP refinement added
31
32
    Copyright (c) 1992, Michigan State University
33
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
34
35
    Permission is hereby granted, free of charge, to any person obtaining a
36
    copy of this software and associated documentation files (the "Software"),
37
    to deal in the Software without restriction, including without limitation
38
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
39
    and/or sell copies of the Software, and to permit persons to whom the
40
    Software is furnished to do so, subject to the following conditions:
41
42
    The above copyright notice and this permission notice shall be included
43
    in all copies or substantial portions of the Software.
44
45
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
48
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
50
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
51
    DEALINGS IN THE SOFTWARE.
52
53
 ****************************************************************************/
54
55
#include "gdal_alg.h"
56
#include "gdal_priv.h"
57
#include "cpl_conv.h"
58
#include "cpl_minixml.h"
59
#include "cpl_string.h"
60
#include "cpl_atomic_ops.h"
61
62
#include <math.h>
63
#include <stdlib.h>
64
#include <string.h>
65
66
34
#define MAXORDER 3
67
68
namespace
69
{
70
struct Control_Points
71
{
72
    int count;
73
    double *e1;
74
    double *n1;
75
    double *e2;
76
    double *n2;
77
    int *status;
78
};
79
}  // namespace
80
81
struct GCPTransformInfo
82
{
83
    GDALTransformerInfo sTI{};
84
85
    double adfToGeoX[20]{};
86
    double adfToGeoY[20]{};
87
88
    double adfFromGeoX[20]{};
89
    double adfFromGeoY[20]{};
90
    double x1_mean{};
91
    double y1_mean{};
92
    double x2_mean{};
93
    double y2_mean{};
94
    int nOrder{};
95
    int bReversed{};
96
97
    std::vector<gdal::GCP> asGCPs{};
98
    int bRefine{};
99
    int nMinimumGcps{};
100
    double dfTolerance{};
101
102
    volatile int nRefCount{};
103
};
104
105
CPL_C_START
106
CPLXMLNode *GDALSerializeGCPTransformer(void *pTransformArg);
107
void *GDALDeserializeGCPTransformer(CPLXMLNode *psTree);
108
CPL_C_END
109
110
/* crs.c */
111
static int CRS_georef(double, double, double *, double *, double[], double[],
112
                      int);
113
static int CRS_compute_georef_equations(GCPTransformInfo *psInfo,
114
                                        struct Control_Points *, double[],
115
                                        double[], double[], double[], int);
116
static int remove_outliers(GCPTransformInfo *);
117
118
13.7k
#define MSUCCESS 1     /* SUCCESS */
119
6
#define MNPTERR 0      /* NOT ENOUGH POINTS */
120
2
#define MUNSOLVABLE -1 /* NOT SOLVABLE */
121
0
#define MMEMERR -2     /* NOT ENOUGH MEMORY */
122
0
#define MPARMERR -3    /* PARAMETER ERROR */
123
0
#define MINTERR -4     /* INTERNAL ERROR */
124
125
static const char *const CRS_error_message[] = {
126
    "Failed to compute GCP transform: Not enough points available",
127
    "Failed to compute GCP transform: Transform is not solvable",
128
    "Failed to compute GCP transform: Not enough memory",
129
    "Failed to compute GCP transform: Parameter error",
130
    "Failed to compute GCP transform: Internal error"};
131
132
/************************************************************************/
133
/*                   GDALCreateSimilarGCPTransformer()                  */
134
/************************************************************************/
135
136
static void *GDALCreateSimilarGCPTransformer(void *hTransformArg,
137
                                             double dfRatioX, double dfRatioY)
138
0
{
139
0
    GCPTransformInfo *psInfo = static_cast<GCPTransformInfo *>(hTransformArg);
140
141
0
    VALIDATE_POINTER1(hTransformArg, "GDALCreateSimilarGCPTransformer",
142
0
                      nullptr);
143
144
0
    if (dfRatioX == 1.0 && dfRatioY == 1.0)
145
0
    {
146
        /* We can just use a ref count, since using the source transformation */
147
        /* is thread-safe */
148
0
        CPLAtomicInc(&(psInfo->nRefCount));
149
0
    }
150
0
    else
151
0
    {
152
0
        auto newGCPs = psInfo->asGCPs;
153
0
        for (auto &gcp : newGCPs)
154
0
        {
155
0
            gcp.Pixel() /= dfRatioX;
156
0
            gcp.Line() /= dfRatioY;
157
0
        }
158
        /* As remove_outliers modifies the provided GCPs we don't need to
159
         * reapply it */
160
0
        psInfo = static_cast<GCPTransformInfo *>(GDALCreateGCPTransformer(
161
0
            static_cast<int>(newGCPs.size()), gdal::GCP::c_ptr(newGCPs),
162
0
            psInfo->nOrder, psInfo->bReversed));
163
0
    }
164
165
0
    return psInfo;
166
0
}
167
168
/************************************************************************/
169
/*                      GDALCreateGCPTransformer()                      */
170
/************************************************************************/
171
172
static void *GDALCreateGCPTransformerEx(int nGCPCount,
173
                                        const GDAL_GCP *pasGCPList,
174
                                        int nReqOrder, bool bReversed,
175
                                        bool bRefine, double dfTolerance,
176
                                        int nMinimumGcps)
177
178
34
{
179
    // If no minimumGcp parameter was passed, we  use the default value
180
    // according to the model
181
34
    if (bRefine && nMinimumGcps == -1)
182
0
    {
183
0
        nMinimumGcps = ((nReqOrder + 1) * (nReqOrder + 2)) / 2 + 1;
184
0
    }
185
186
34
    GCPTransformInfo *psInfo = nullptr;
187
34
    double *padfGeoX = nullptr;
188
34
    double *padfGeoY = nullptr;
189
34
    double *padfRasterX = nullptr;
190
34
    double *padfRasterY = nullptr;
191
34
    int *panStatus = nullptr;
192
34
    int iGCP = 0;
193
34
    int nCRSresult = 0;
194
34
    struct Control_Points sPoints;
195
196
34
    double x1_sum = 0;
197
34
    double y1_sum = 0;
198
34
    double x2_sum = 0;
199
34
    double y2_sum = 0;
200
201
34
    memset(&sPoints, 0, sizeof(sPoints));
202
203
34
    if (nReqOrder == 0)
204
26
    {
205
26
        if (nGCPCount >= 10)
206
0
            nReqOrder = 2; /*for now we avoid 3rd order since it is unstable*/
207
26
        else if (nGCPCount >= 6)
208
0
            nReqOrder = 2;
209
26
        else
210
26
            nReqOrder = 1;
211
26
    }
212
213
34
    psInfo = new GCPTransformInfo();
214
34
    psInfo->bReversed = bReversed;
215
34
    psInfo->nOrder = nReqOrder;
216
34
    psInfo->bRefine = bRefine;
217
34
    psInfo->dfTolerance = dfTolerance;
218
34
    psInfo->nMinimumGcps = nMinimumGcps;
219
220
34
    psInfo->nRefCount = 1;
221
222
34
    psInfo->asGCPs = gdal::GCP::fromC(pasGCPList, nGCPCount);
223
34
    if (nGCPCount == 2 && nReqOrder == 1 &&
224
24
        psInfo->asGCPs[0].X() != psInfo->asGCPs[1].X() &&
225
21
        psInfo->asGCPs[0].Y() != psInfo->asGCPs[1].Y())
226
21
    {
227
        // Assumes that the 2 GCPs form opposite corners of a rectangle,
228
        // and synthesize a 3rd corner
229
21
        gdal::GCP newGCP;
230
21
        newGCP.X() = psInfo->asGCPs[1].X();
231
21
        newGCP.Y() = psInfo->asGCPs[0].Y();
232
21
        newGCP.Pixel() = psInfo->asGCPs[1].Pixel();
233
21
        newGCP.Line() = psInfo->asGCPs[0].Line();
234
21
        psInfo->asGCPs.emplace_back(std::move(newGCP));
235
236
21
        nGCPCount = 3;
237
21
        pasGCPList = gdal::GCP::c_ptr(psInfo->asGCPs);
238
21
    }
239
240
34
    memcpy(psInfo->sTI.abySignature, GDAL_GTI2_SIGNATURE,
241
34
           strlen(GDAL_GTI2_SIGNATURE));
242
34
    psInfo->sTI.pszClassName = "GDALGCPTransformer";
243
34
    psInfo->sTI.pfnTransform = GDALGCPTransform;
244
34
    psInfo->sTI.pfnCleanup = GDALDestroyGCPTransformer;
245
34
    psInfo->sTI.pfnSerialize = GDALSerializeGCPTransformer;
246
34
    psInfo->sTI.pfnCreateSimilar = GDALCreateSimilarGCPTransformer;
247
248
    /* -------------------------------------------------------------------- */
249
    /*      Compute the forward and reverse polynomials.                    */
250
    /* -------------------------------------------------------------------- */
251
252
34
    if (nGCPCount == 0)
253
0
    {
254
0
        nCRSresult = MNPTERR;
255
0
    }
256
34
    else if (bRefine)
257
0
    {
258
0
        nCRSresult = remove_outliers(psInfo);
259
0
    }
260
34
    else
261
34
    {
262
        /* --------------------------------------------------------------------
263
         */
264
        /*      Allocate and initialize the working points list. */
265
        /* --------------------------------------------------------------------
266
         */
267
34
        try
268
34
        {
269
34
            padfGeoX = new double[nGCPCount];
270
34
            padfGeoY = new double[nGCPCount];
271
34
            padfRasterX = new double[nGCPCount];
272
34
            padfRasterY = new double[nGCPCount];
273
34
            panStatus = new int[nGCPCount];
274
129
            for (iGCP = 0; iGCP < nGCPCount; iGCP++)
275
95
            {
276
95
                panStatus[iGCP] = 1;
277
95
                padfGeoX[iGCP] = pasGCPList[iGCP].dfGCPX;
278
95
                padfGeoY[iGCP] = pasGCPList[iGCP].dfGCPY;
279
95
                padfRasterX[iGCP] = pasGCPList[iGCP].dfGCPPixel;
280
95
                padfRasterY[iGCP] = pasGCPList[iGCP].dfGCPLine;
281
95
                x1_sum += pasGCPList[iGCP].dfGCPPixel;
282
95
                y1_sum += pasGCPList[iGCP].dfGCPLine;
283
95
                x2_sum += pasGCPList[iGCP].dfGCPX;
284
95
                y2_sum += pasGCPList[iGCP].dfGCPY;
285
95
            }
286
34
            psInfo->x1_mean = x1_sum / nGCPCount;
287
34
            psInfo->y1_mean = y1_sum / nGCPCount;
288
34
            psInfo->x2_mean = x2_sum / nGCPCount;
289
34
            psInfo->y2_mean = y2_sum / nGCPCount;
290
291
34
            sPoints.count = nGCPCount;
292
34
            sPoints.e1 = padfRasterX;
293
34
            sPoints.n1 = padfRasterY;
294
34
            sPoints.e2 = padfGeoX;
295
34
            sPoints.n2 = padfGeoY;
296
34
            sPoints.status = panStatus;
297
34
            nCRSresult = CRS_compute_georef_equations(
298
34
                psInfo, &sPoints, psInfo->adfToGeoX, psInfo->adfToGeoY,
299
34
                psInfo->adfFromGeoX, psInfo->adfFromGeoY, nReqOrder);
300
34
        }
301
34
        catch (const std::exception &e)
302
34
        {
303
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
304
0
            nCRSresult = MINTERR;
305
0
        }
306
34
        delete[] padfGeoX;
307
34
        delete[] padfGeoY;
308
34
        delete[] padfRasterX;
309
34
        delete[] padfRasterY;
310
34
        delete[] panStatus;
311
34
    }
312
313
34
    if (nCRSresult != 1)
314
8
    {
315
8
        CPLError(CE_Failure, CPLE_AppDefined, "%s",
316
8
                 CRS_error_message[-nCRSresult]);
317
8
        GDALDestroyGCPTransformer(psInfo);
318
8
        return nullptr;
319
8
    }
320
26
    else
321
26
    {
322
26
        return psInfo;
323
26
    }
324
34
}
325
326
/**
327
 * Create GCP based polynomial transformer.
328
 *
329
 * Computes least squares fit polynomials from a provided set of GCPs,
330
 * and stores the coefficients for later transformation of points between
331
 * pixel/line and georeferenced coordinates.
332
 *
333
 * The return value should be used as a TransformArg in combination with
334
 * the transformation function GDALGCPTransform which fits the
335
 * GDALTransformerFunc signature.  The returned transform argument should
336
 * be deallocated with GDALDestroyGCPTransformer when no longer needed.
337
 *
338
 * This function may fail (returning nullptr) if the provided set of GCPs
339
 * are inadequate for the requested order, the determinate is zero or they
340
 * are otherwise "ill conditioned".
341
 *
342
 * Note that 2nd order requires at least 6 GCPs, and 3rd order requires at
343
 * least 10 gcps.  If nReqOrder is 0 the highest order possible (limited to 2)
344
 * with the provided gcp count will be used.
345
 *
346
 * @param nGCPCount the number of GCPs in pasGCPList.
347
 * @param pasGCPList an array of GCPs to be used as input.
348
 * @param nReqOrder the requested polynomial order.  It should be 1, 2 or 3.
349
 * Using 3 is not recommended due to potential numeric instabilities issues.
350
 * @param bReversed set it to TRUE to compute the reversed transformation.
351
 *
352
 * @return the transform argument or nullptr if creation fails.
353
 */
354
void *GDALCreateGCPTransformer(int nGCPCount, const GDAL_GCP *pasGCPList,
355
                               int nReqOrder, int bReversed)
356
357
34
{
358
34
    return GDALCreateGCPTransformerEx(nGCPCount, pasGCPList, nReqOrder,
359
34
                                      CPL_TO_BOOL(bReversed), false, -1, -1);
360
34
}
361
362
/** Create GCP based polynomial transformer, with a tolerance threshold to
363
 * discard GCPs that transform badly.
364
 */
365
void *GDALCreateGCPRefineTransformer(int nGCPCount, const GDAL_GCP *pasGCPList,
366
                                     int nReqOrder, int bReversed,
367
                                     double dfTolerance, int nMinimumGcps)
368
369
0
{
370
0
    return GDALCreateGCPTransformerEx(nGCPCount, pasGCPList, nReqOrder,
371
0
                                      CPL_TO_BOOL(bReversed), true, dfTolerance,
372
0
                                      nMinimumGcps);
373
0
}
374
375
/************************************************************************/
376
/*                     GDALDestroyGCPTransformer()                      */
377
/************************************************************************/
378
379
/**
380
 * Destroy GCP transformer.
381
 *
382
 * This function is used to destroy information about a GCP based
383
 * polynomial transformation created with GDALCreateGCPTransformer().
384
 *
385
 * @param pTransformArg the transform arg previously returned by
386
 * GDALCreateGCPTransformer().
387
 */
388
389
void GDALDestroyGCPTransformer(void *pTransformArg)
390
391
34
{
392
34
    if (pTransformArg == nullptr)
393
0
        return;
394
395
34
    GCPTransformInfo *psInfo = static_cast<GCPTransformInfo *>(pTransformArg);
396
397
34
    if (CPLAtomicDec(&(psInfo->nRefCount)) == 0)
398
34
    {
399
34
        delete psInfo;
400
34
    }
401
34
}
402
403
/************************************************************************/
404
/*                          GDALGCPTransform()                          */
405
/************************************************************************/
406
407
/**
408
 * Transforms point based on GCP derived polynomial model.
409
 *
410
 * This function matches the GDALTransformerFunc signature, and can be
411
 * used to transform one or more points from pixel/line coordinates to
412
 * georeferenced coordinates (SrcToDst) or vice versa (DstToSrc).
413
 *
414
 * @param pTransformArg return value from GDALCreateGCPTransformer().
415
 * @param bDstToSrc TRUE if transformation is from the destination
416
 * (georeferenced) coordinates to pixel/line or FALSE when transforming
417
 * from pixel/line to georeferenced coordinates.
418
 * @param nPointCount the number of values in the x, y and z arrays.
419
 * @param x array containing the X values to be transformed.
420
 * @param y array containing the Y values to be transformed.
421
 * @param z array containing the Z values to be transformed.
422
 * @param panSuccess array in which a flag indicating success (TRUE) or
423
 * failure (FALSE) of the transformation are placed.
424
 *
425
 * @return TRUE if all points have been successfully transformed.
426
 */
427
428
int GDALGCPTransform(void *pTransformArg, int bDstToSrc, int nPointCount,
429
                     double *x, double *y, CPL_UNUSED double *z,
430
                     int *panSuccess)
431
432
537
{
433
537
    int i = 0;
434
537
    GCPTransformInfo *psInfo = static_cast<GCPTransformInfo *>(pTransformArg);
435
436
537
    if (psInfo->bReversed)
437
0
        bDstToSrc = !bDstToSrc;
438
439
537
    int bRet = TRUE;
440
14.2k
    for (i = 0; i < nPointCount; i++)
441
13.6k
    {
442
13.6k
        if (x[i] == HUGE_VAL || y[i] == HUGE_VAL)
443
0
        {
444
0
            bRet = FALSE;
445
0
            panSuccess[i] = FALSE;
446
0
            continue;
447
0
        }
448
449
13.6k
        if (bDstToSrc)
450
0
        {
451
0
            CRS_georef(x[i] - psInfo->x2_mean, y[i] - psInfo->y2_mean, x + i,
452
0
                       y + i, psInfo->adfFromGeoX, psInfo->adfFromGeoY,
453
0
                       psInfo->nOrder);
454
0
        }
455
13.6k
        else
456
13.6k
        {
457
13.6k
            CRS_georef(x[i] - psInfo->x1_mean, y[i] - psInfo->y1_mean, x + i,
458
13.6k
                       y + i, psInfo->adfToGeoX, psInfo->adfToGeoY,
459
13.6k
                       psInfo->nOrder);
460
13.6k
        }
461
13.6k
        panSuccess[i] = TRUE;
462
13.6k
    }
463
464
537
    return bRet;
465
537
}
466
467
/************************************************************************/
468
/*                    GDALSerializeGCPTransformer()                     */
469
/************************************************************************/
470
471
CPLXMLNode *GDALSerializeGCPTransformer(void *pTransformArg)
472
473
0
{
474
0
    CPLXMLNode *psTree = nullptr;
475
0
    GCPTransformInfo *psInfo = static_cast<GCPTransformInfo *>(pTransformArg);
476
477
0
    VALIDATE_POINTER1(pTransformArg, "GDALSerializeGCPTransformer", nullptr);
478
479
0
    psTree = CPLCreateXMLNode(nullptr, CXT_Element, "GCPTransformer");
480
481
    /* -------------------------------------------------------------------- */
482
    /*      Serialize Order and bReversed.                                  */
483
    /* -------------------------------------------------------------------- */
484
0
    CPLCreateXMLElementAndValue(psTree, "Order",
485
0
                                CPLSPrintf("%d", psInfo->nOrder));
486
487
0
    CPLCreateXMLElementAndValue(psTree, "Reversed",
488
0
                                CPLSPrintf("%d", psInfo->bReversed));
489
490
0
    if (psInfo->bRefine)
491
0
    {
492
0
        CPLCreateXMLElementAndValue(psTree, "Refine",
493
0
                                    CPLSPrintf("%d", psInfo->bRefine));
494
495
0
        CPLCreateXMLElementAndValue(psTree, "MinimumGcps",
496
0
                                    CPLSPrintf("%d", psInfo->nMinimumGcps));
497
498
0
        CPLCreateXMLElementAndValue(psTree, "Tolerance",
499
0
                                    CPLSPrintf("%f", psInfo->dfTolerance));
500
0
    }
501
502
    /* -------------------------------------------------------------------- */
503
    /*     Attach GCP List.                                                 */
504
    /* -------------------------------------------------------------------- */
505
0
    if (!psInfo->asGCPs.empty())
506
0
    {
507
0
        if (psInfo->bRefine)
508
0
        {
509
0
            remove_outliers(psInfo);
510
0
        }
511
512
0
        GDALSerializeGCPListToXML(psTree, psInfo->asGCPs, nullptr);
513
0
    }
514
515
0
    return psTree;
516
0
}
517
518
/************************************************************************/
519
/*               GDALDeserializeReprojectionTransformer()               */
520
/************************************************************************/
521
522
void *GDALDeserializeGCPTransformer(CPLXMLNode *psTree)
523
524
8
{
525
8
    std::vector<gdal::GCP> asGCPs;
526
8
    void *pResult = nullptr;
527
8
    int nReqOrder = 0;
528
8
    int bReversed = 0;
529
8
    int bRefine = 0;
530
8
    int nMinimumGcps = 0;
531
8
    double dfTolerance = 0.0;
532
533
    /* -------------------------------------------------------------------- */
534
    /*      Check for GCPs.                                                 */
535
    /* -------------------------------------------------------------------- */
536
8
    CPLXMLNode *psGCPList = CPLGetXMLNode(psTree, "GCPList");
537
538
8
    if (psGCPList != nullptr)
539
8
    {
540
8
        GDALDeserializeGCPListFromXML(psGCPList, asGCPs, nullptr);
541
8
    }
542
543
    /* -------------------------------------------------------------------- */
544
    /*      Get other flags.                                                */
545
    /* -------------------------------------------------------------------- */
546
8
    nReqOrder = atoi(CPLGetXMLValue(psTree, "Order", "3"));
547
8
    bReversed = atoi(CPLGetXMLValue(psTree, "Reversed", "0"));
548
8
    bRefine = atoi(CPLGetXMLValue(psTree, "Refine", "0"));
549
8
    nMinimumGcps = atoi(CPLGetXMLValue(psTree, "MinimumGcps", "6"));
550
8
    dfTolerance = CPLAtof(CPLGetXMLValue(psTree, "Tolerance", "1.0"));
551
552
    /* -------------------------------------------------------------------- */
553
    /*      Generate transformation.                                        */
554
    /* -------------------------------------------------------------------- */
555
8
    if (bRefine)
556
0
    {
557
0
        pResult = GDALCreateGCPRefineTransformer(
558
0
            static_cast<int>(asGCPs.size()), gdal::GCP::c_ptr(asGCPs),
559
0
            nReqOrder, bReversed, dfTolerance, nMinimumGcps);
560
0
    }
561
8
    else
562
8
    {
563
8
        pResult = GDALCreateGCPTransformer(static_cast<int>(asGCPs.size()),
564
8
                                           gdal::GCP::c_ptr(asGCPs), nReqOrder,
565
8
                                           bReversed);
566
8
    }
567
568
8
    return pResult;
569
8
}
570
571
/************************************************************************/
572
/* ==================================================================== */
573
/*      Everything below this point derived from the CRS.C from GRASS.  */
574
/* ==================================================================== */
575
/************************************************************************/
576
577
/* STRUCTURE FOR USE INTERNALLY WITH THESE FUNCTIONS.  THESE FUNCTIONS EXPECT
578
   SQUARE MATRICES SO ONLY ONE VARIABLE IS GIVEN (N) FOR THE MATRIX SIZE */
579
580
struct MATRIX
581
{
582
    int n; /* SIZE OF THIS MATRIX (N x N) */
583
    double *v;
584
};
585
586
/* CALCULATE OFFSET INTO ARRAY BASED ON R/C */
587
588
2.84k
#define M(row, col) m->v[(((row)-1) * (m->n)) + (col)-1]
589
590
/***************************************************************************/
591
/*
592
    FUNCTION PROTOTYPES FOR STATIC (INTERNAL) FUNCTIONS
593
*/
594
/***************************************************************************/
595
596
static int calccoef(struct Control_Points *, double, double, double *, double *,
597
                    int);
598
static int calcls(struct Control_Points *, struct MATRIX *, double, double,
599
                  double *, double *, double *, double *);
600
static int exactdet(struct Control_Points *, struct MATRIX *, double, double,
601
                    double *, double *, double *, double *);
602
static int solvemat(struct MATRIX *, double *, double *, double *, double *);
603
static double term(int, double, double);
604
605
/***************************************************************************/
606
/*
607
    TRANSFORM A SINGLE COORDINATE PAIR.
608
*/
609
/***************************************************************************/
610
611
static int
612
CRS_georef(double e1,  /* EASTINGS TO BE TRANSFORMED */
613
           double n1,  /* NORTHINGS TO BE TRANSFORMED */
614
           double *e,  /* EASTINGS TO BE TRANSFORMED */
615
           double *n,  /* NORTHINGS TO BE TRANSFORMED */
616
           double E[], /* EASTING COEFFICIENTS */
617
           double N[], /* NORTHING COEFFICIENTS */
618
           int order /* ORDER OF TRANSFORMATION TO BE PERFORMED, MUST MATCH THE
619
                     ORDER USED TO CALCULATE THE COEFFICIENTS */
620
)
621
13.6k
{
622
13.6k
    double e3 = 0.0;
623
13.6k
    double e2n = 0.0;
624
13.6k
    double en2 = 0.0;
625
13.6k
    double n3 = 0.0;
626
13.6k
    double e2 = 0.0;
627
13.6k
    double en = 0.0;
628
13.6k
    double n2 = 0.0;
629
630
13.6k
    switch (order)
631
13.6k
    {
632
13.6k
        case 1:
633
634
13.6k
            *e = E[0] + E[1] * e1 + E[2] * n1;
635
13.6k
            *n = N[0] + N[1] * e1 + N[2] * n1;
636
13.6k
            break;
637
638
0
        case 2:
639
640
0
            e2 = e1 * e1;
641
0
            n2 = n1 * n1;
642
0
            en = e1 * n1;
643
644
0
            *e = E[0] + E[1] * e1 + E[2] * n1 + E[3] * e2 + E[4] * en +
645
0
                 E[5] * n2;
646
0
            *n = N[0] + N[1] * e1 + N[2] * n1 + N[3] * e2 + N[4] * en +
647
0
                 N[5] * n2;
648
0
            break;
649
650
0
        case 3:
651
652
0
            e2 = e1 * e1;
653
0
            en = e1 * n1;
654
0
            n2 = n1 * n1;
655
0
            e3 = e1 * e2;
656
0
            e2n = e2 * n1;
657
0
            en2 = e1 * n2;
658
0
            n3 = n1 * n2;
659
660
0
            *e = E[0] + E[1] * e1 + E[2] * n1 + E[3] * e2 + E[4] * en +
661
0
                 E[5] * n2 + E[6] * e3 + E[7] * e2n + E[8] * en2 + E[9] * n3;
662
0
            *n = N[0] + N[1] * e1 + N[2] * n1 + N[3] * e2 + N[4] * en +
663
0
                 N[5] * n2 + N[6] * e3 + N[7] * e2n + N[8] * en2 + N[9] * n3;
664
0
            break;
665
666
0
        default:
667
668
0
            return (MPARMERR);
669
13.6k
    }
670
671
13.6k
    return (MSUCCESS);
672
13.6k
}
673
674
/***************************************************************************/
675
/*
676
    COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS
677
*/
678
/***************************************************************************/
679
680
static int CRS_compute_georef_equations(GCPTransformInfo *psInfo,
681
                                        struct Control_Points *cp, double E12[],
682
                                        double N12[], double E21[],
683
                                        double N21[], int order)
684
34
{
685
34
    double *tempptr = nullptr;
686
34
    int status = 0;
687
688
34
    if (order < 1 || order > MAXORDER)
689
0
        return (MPARMERR);
690
691
    /* CALCULATE THE FORWARD TRANSFORMATION COEFFICIENTS */
692
693
34
    status = calccoef(cp, psInfo->x1_mean, psInfo->y1_mean, E12, N12, order);
694
34
    if (status != MSUCCESS)
695
8
        return (status);
696
697
    /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS */
698
699
26
    tempptr = cp->e1;
700
26
    cp->e1 = cp->e2;
701
26
    cp->e2 = tempptr;
702
26
    tempptr = cp->n1;
703
26
    cp->n1 = cp->n2;
704
26
    cp->n2 = tempptr;
705
706
    /* CALCULATE THE BACKWARD TRANSFORMATION COEFFICIENTS */
707
708
26
    status = calccoef(cp, psInfo->x2_mean, psInfo->y2_mean, E21, N21, order);
709
710
    /* SWITCH THE 1 AND 2 EASTING AND NORTHING ARRAYS BACK */
711
712
26
    tempptr = cp->e1;
713
26
    cp->e1 = cp->e2;
714
26
    cp->e2 = tempptr;
715
26
    tempptr = cp->n1;
716
26
    cp->n1 = cp->n2;
717
26
    cp->n2 = tempptr;
718
719
26
    return (status);
720
34
}
721
722
/***************************************************************************/
723
/*
724
    COMPUTE THE GEOREFFERENCING COEFFICIENTS BASED ON A SET OF CONTROL POINTS
725
*/
726
/***************************************************************************/
727
728
static int calccoef(struct Control_Points *cp, double x_mean, double y_mean,
729
                    double E[], double N[], int order)
730
60
{
731
60
    struct MATRIX m;
732
60
    double *a = nullptr;
733
60
    double *b = nullptr;
734
60
    int numactive = 0; /* NUMBER OF ACTIVE CONTROL POINTS */
735
60
    int status = 0;
736
60
    int i = 0;
737
738
60
    memset(&m, 0, sizeof(m));
739
740
    /* CALCULATE THE NUMBER OF VALID CONTROL POINTS */
741
742
235
    for (i = numactive = 0; i < cp->count; i++)
743
175
    {
744
175
        if (cp->status[i] > 0)
745
175
            numactive++;
746
175
    }
747
748
    /* CALCULATE THE MINIMUM NUMBER OF CONTROL POINTS NEEDED TO DETERMINE
749
       A TRANSFORMATION OF THIS ORDER */
750
751
60
    m.n = ((order + 1) * (order + 2)) / 2;
752
753
60
    if (numactive < m.n)
754
6
        return (MNPTERR);
755
756
    /* INITIALIZE MATRIX */
757
758
54
    m.v = static_cast<double *>(
759
54
        VSICalloc(cpl::fits_on<int>(m.n * m.n), sizeof(double)));
760
54
    if (m.v == nullptr)
761
0
    {
762
0
        return (MMEMERR);
763
0
    }
764
54
    a = static_cast<double *>(VSICalloc(m.n, sizeof(double)));
765
54
    if (a == nullptr)
766
0
    {
767
0
        CPLFree(m.v);
768
0
        return (MMEMERR);
769
0
    }
770
54
    b = static_cast<double *>(VSICalloc(m.n, sizeof(double)));
771
54
    if (b == nullptr)
772
0
    {
773
0
        CPLFree(m.v);
774
0
        CPLFree(a);
775
0
        return (MMEMERR);
776
0
    }
777
778
54
    if (numactive == m.n)
779
50
        status = exactdet(cp, &m, x_mean, y_mean, a, b, E, N);
780
4
    else
781
4
        status = calcls(cp, &m, x_mean, y_mean, a, b, E, N);
782
783
54
    CPLFree(m.v);
784
54
    CPLFree(a);
785
54
    CPLFree(b);
786
787
54
    return (status);
788
54
}
789
790
/***************************************************************************/
791
/*
792
    CALCULATE THE TRANSFORMATION COEFFICIENTS WITH EXACTLY THE MINIMUM
793
    NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION.
794
*/
795
/***************************************************************************/
796
797
static int exactdet(struct Control_Points *cp, struct MATRIX *m, double x_mean,
798
                    double y_mean, double a[], double b[],
799
                    double E[], /* EASTING COEFFICIENTS */
800
                    double N[]  /* NORTHING COEFFICIENTS */
801
)
802
50
{
803
50
    int currow = 1;
804
805
200
    for (int pntnow = 0; pntnow < cp->count; pntnow++)
806
150
    {
807
150
        if (cp->status[pntnow] > 0)
808
150
        {
809
            /* POPULATE MATRIX M */
810
811
600
            for (int j = 1; j <= m->n; j++)
812
450
            {
813
450
                M(currow, j) =
814
450
                    term(j, cp->e1[pntnow] - x_mean, cp->n1[pntnow] - y_mean);
815
450
            }
816
817
            /* POPULATE MATRIX A AND B */
818
819
150
            a[currow - 1] = cp->e2[pntnow];
820
150
            b[currow - 1] = cp->n2[pntnow];
821
822
150
            currow++;
823
150
        }
824
150
    }
825
826
50
    if (currow - 1 != m->n)
827
0
        return (MINTERR);
828
829
50
    return (solvemat(m, a, b, E, N));
830
50
}
831
832
/***************************************************************************/
833
/*
834
    CALCULATE THE TRANSFORMATION COEFFICIENTS WITH MORE THAN THE MINIMUM
835
    NUMBER OF CONTROL POINTS REQUIRED FOR THIS TRANSFORMATION.  THIS
836
    ROUTINE USES THE LEAST SQUARES METHOD TO COMPUTE THE COEFFICIENTS.
837
*/
838
/***************************************************************************/
839
840
static int calcls(struct Control_Points *cp, struct MATRIX *m, double x_mean,
841
                  double y_mean, double a[], double b[],
842
                  double E[], /* EASTING COEFFICIENTS */
843
                  double N[]  /* NORTHING COEFFICIENTS */
844
)
845
4
{
846
4
    int numactive = 0;
847
848
    /* INITIALIZE THE UPPER HALF OF THE MATRIX AND THE TWO COLUMN VECTORS */
849
850
16
    for (int i = 1; i <= m->n; i++)
851
12
    {
852
36
        for (int j = i; j <= m->n; j++)
853
24
            M(i, j) = 0.0;
854
12
        a[i - 1] = b[i - 1] = 0.0;
855
12
    }
856
857
    /* SUM THE UPPER HALF OF THE MATRIX AND THE COLUMN VECTORS ACCORDING TO
858
       THE LEAST SQUARES METHOD OF SOLVING OVER DETERMINED SYSTEMS */
859
860
20
    for (int n = 0; n < cp->count; n++)
861
16
    {
862
16
        if (cp->status[n] > 0)
863
16
        {
864
16
            numactive++;
865
64
            for (int i = 1; i <= m->n; i++)
866
48
            {
867
144
                for (int j = i; j <= m->n; j++)
868
96
                    M(i, j) += term(i, cp->e1[n] - x_mean, cp->n1[n] - y_mean) *
869
96
                               term(j, cp->e1[n] - x_mean, cp->n1[n] - y_mean);
870
871
48
                a[i - 1] +=
872
48
                    cp->e2[n] * term(i, cp->e1[n] - x_mean, cp->n1[n] - y_mean);
873
48
                b[i - 1] +=
874
48
                    cp->n2[n] * term(i, cp->e1[n] - x_mean, cp->n1[n] - y_mean);
875
48
            }
876
16
        }
877
16
    }
878
879
4
    if (numactive <= m->n)
880
0
        return (MINTERR);
881
882
    /* TRANSPOSE VALUES IN UPPER HALF OF M TO OTHER HALF */
883
884
12
    for (int i = 2; i <= m->n; i++)
885
8
    {
886
20
        for (int j = 1; j < i; j++)
887
12
            M(i, j) = M(j, i);
888
8
    }
889
890
4
    return (solvemat(m, a, b, E, N));
891
4
}
892
893
/***************************************************************************/
894
/*
895
    CALCULATE THE X/Y TERM BASED ON THE TERM NUMBER
896
897
ORDER\TERM   1    2    3    4    5    6    7    8    9   10
898
  1        e0n0 e1n0 e0n1
899
  2        e0n0 e1n0 e0n1 e2n0 e1n1 e0n2
900
  3        e0n0 e1n0 e0n1 e2n0 e1n1 e0n2 e3n0 e2n1 e1n2 e0n3
901
*/
902
/***************************************************************************/
903
904
static double term(int nTerm, double e, double n)
905
738
{
906
738
    switch (nTerm)
907
738
    {
908
246
        case 1:
909
246
            return (1.0);
910
246
        case 2:
911
246
            return (e);
912
246
        case 3:
913
246
            return (n);
914
0
        case 4:
915
0
            return ((e * e));
916
0
        case 5:
917
0
            return ((e * n));
918
0
        case 6:
919
0
            return ((n * n));
920
0
        case 7:
921
0
            return ((e * e * e));
922
0
        case 8:
923
0
            return ((e * e * n));
924
0
        case 9:
925
0
            return ((e * n * n));
926
0
        case 10:
927
0
            return ((n * n * n));
928
738
    }
929
0
    return 0.0;
930
738
}
931
932
/***************************************************************************/
933
/*
934
    SOLVE FOR THE 'E' AND 'N' COEFFICIENTS BY USING A SOMEWHAT MODIFIED
935
    GAUSSIAN ELIMINATION METHOD.
936
937
    | M11 M12 ... M1n | | E0   |   | a0   |
938
    | M21 M22 ... M2n | | E1   | = | a1   |
939
    |  .   .   .   .  | | .    |   | .    |
940
    | Mn1 Mn2 ... Mnn | | En-1 |   | an-1 |
941
942
    and
943
944
    | M11 M12 ... M1n | | N0   |   | b0   |
945
    | M21 M22 ... M2n | | N1   | = | b1   |
946
    |  .   .   .   .  | | .    |   | .    |
947
    | Mn1 Mn2 ... Mnn | | Nn-1 |   | bn-1 |
948
*/
949
/***************************************************************************/
950
951
static int solvemat(struct MATRIX *m, double a[], double b[], double E[],
952
                    double N[])
953
54
{
954
213
    for (int i = 1; i <= m->n; i++)
955
161
    {
956
161
        int j = i;
957
958
        /* find row with largest magnitude value for pivot value */
959
960
161
        double pivot =
961
161
            M(i, j); /* ACTUAL VALUE OF THE LARGEST PIVOT CANDIDATE */
962
161
        int imark = i;
963
323
        for (int i2 = i + 1; i2 <= m->n; i2++)
964
162
        {
965
162
            if (fabs(M(i2, j)) > fabs(pivot))
966
2
            {
967
2
                pivot = M(i2, j);
968
2
                imark = i2;
969
2
            }
970
162
        }
971
972
        /* if the pivot is very small then the points are nearly co-linear */
973
        /* co-linear points result in an undefined matrix, and nearly */
974
        /* co-linear points results in a solution with rounding error */
975
976
161
        if (pivot == 0.0)
977
2
            return (MUNSOLVABLE);
978
979
        /* if row with highest pivot is not the current row, switch them */
980
981
159
        if (imark != i)
982
2
        {
983
8
            for (int j2 = 1; j2 <= m->n; j2++)
984
6
            {
985
6
                std::swap(M(imark, j2), M(i, j2));
986
6
            }
987
988
2
            std::swap(a[imark - 1], a[i - 1]);
989
2
            std::swap(b[imark - 1], b[i - 1]);
990
2
        }
991
992
        /* compute zeros above and below the pivot, and compute
993
           values for the rest of the row as well */
994
995
636
        for (int i2 = 1; i2 <= m->n; i2++)
996
477
        {
997
477
            if (i2 != i)
998
318
            {
999
318
                const double factor = M(i2, j) / pivot;
1000
958
                for (int j2 = j; j2 <= m->n; j2++)
1001
640
                    M(i2, j2) -= factor * M(i, j2);
1002
318
                a[i2 - 1] -= factor * a[i - 1];
1003
318
                b[i2 - 1] -= factor * b[i - 1];
1004
318
            }
1005
477
        }
1006
159
    }
1007
1008
    /* SINCE ALL OTHER VALUES IN THE MATRIX ARE ZERO NOW, CALCULATE THE
1009
       COEFFICIENTS BY DIVIDING THE COLUMN VECTORS BY THE DIAGONAL VALUES. */
1010
1011
208
    for (int i = 1; i <= m->n; i++)
1012
156
    {
1013
156
        E[i - 1] = a[i - 1] / M(i, i);
1014
156
        N[i - 1] = b[i - 1] / M(i, i);
1015
156
    }
1016
1017
52
    return (MSUCCESS);
1018
54
}
1019
1020
/***************************************************************************/
1021
/*
1022
  DETECTS THE WORST OUTLIER IN THE GCP LIST AND RETURNS THE INDEX OF THE
1023
  OUTLIER.
1024
1025
  THE WORST OUTLIER IS CALCULATED BASED ON THE CONTROL POINTS, COEFFICIENTS
1026
  AND THE ALLOWED TOLERANCE:
1027
1028
  sampleAdj = a0 + a1*sample + a2*line + a3*line*sample
1029
  lineAdj = b0 + b1*sample + b2*line + b3*line*sample
1030
1031
  WHERE sampleAdj AND lineAdj ARE CORRELATED GCPS
1032
1033
  [residualSample] = [A1][sampleCoefficients] - [b1]
1034
  [residualLine] = [A2][lineCoefficients] - [b2]
1035
1036
  sampleResidual^2 = sum( [residualSample]^2 )
1037
  lineResidual^2 = sum( [lineSample]^2 )
1038
1039
  residuals(i) = squareRoot( residualSample(i)^2 + residualLine(i)^2 )
1040
1041
  THE GCP WITH THE GREATEST DISTANCE residual(i) GREATER THAN THE TOLERANCE WILL
1042
  CONSIDERED THE WORST OUTLIER.
1043
1044
  IF NO OUTLIER CAN BE FOUND, -1 WILL BE RETURNED.
1045
*/
1046
/***************************************************************************/
1047
static int worst_outlier(struct Control_Points *cp, double x_mean,
1048
                         double y_mean, int nOrder, double E[], double N[],
1049
                         double dfTolerance)
1050
0
{
1051
    // double dfSampleResidual = 0.0;
1052
    // double dfLineResidual = 0.0;
1053
0
    double *padfResiduals =
1054
0
        static_cast<double *>(CPLCalloc(sizeof(double), cp->count));
1055
1056
0
    for (int nI = 0; nI < cp->count; nI++)
1057
0
    {
1058
0
        double dfSampleRes = 0.0;
1059
0
        double dfLineRes = 0.0;
1060
0
        CRS_georef(cp->e1[nI] - x_mean, cp->n1[nI] - y_mean, &dfSampleRes,
1061
0
                   &dfLineRes, E, N, nOrder);
1062
0
        dfSampleRes -= cp->e2[nI];
1063
0
        dfLineRes -= cp->n2[nI];
1064
        // dfSampleResidual += dfSampleRes*dfSampleRes;
1065
        // dfLineResidual += dfLineRes*dfLineRes;
1066
1067
0
        padfResiduals[nI] =
1068
0
            sqrt(dfSampleRes * dfSampleRes + dfLineRes * dfLineRes);
1069
0
    }
1070
1071
0
    int nIndex = -1;
1072
0
    double dfDifference = -1.0;
1073
0
    for (int nI = 0; nI < cp->count; nI++)
1074
0
    {
1075
0
        double dfCurrentDifference = padfResiduals[nI];
1076
0
        if (fabs(dfCurrentDifference) < 1.19209290E-07 /*FLT_EPSILON*/)
1077
0
        {
1078
0
            dfCurrentDifference = 0.0;
1079
0
        }
1080
0
        if (dfCurrentDifference > dfDifference &&
1081
0
            dfCurrentDifference >= dfTolerance)
1082
0
        {
1083
0
            dfDifference = dfCurrentDifference;
1084
0
            nIndex = nI;
1085
0
        }
1086
0
    }
1087
0
    CPLFree(padfResiduals);
1088
0
    return nIndex;
1089
0
}
1090
1091
/***************************************************************************/
1092
/*
1093
  REMOVES THE WORST OUTLIERS ITERATIVELY UNTIL THE MINIMUM NUMBER OF GCPS
1094
  ARE REACHED OR NO OUTLIERS CAN BE DETECTED.
1095
1096
  1. WE CALCULATE THE COEFFICIENTS FOR ALL THE GCPS.
1097
  2. THE GCP LIST WILL BE SCANNED TO DETERMINE THE WORST OUTLIER USING
1098
     THE CALCULATED COEFFICIENTS.
1099
  3. THE WORST OUTLIER WILL BE REMOVED FROM THE GCP LIST.
1100
  4. THE COEFFICIENTS WILL BE RECALCULATED WITHOUT THE WORST OUTLIER.
1101
  5. STEP 1 TO 4 ARE EXECUTED UNTIL THE MINIMUM NUMBER OF GCPS WERE REACHED
1102
     OR IF NO GCP IS CONSIDERED AN OUTLIER WITH THE PASSED TOLERANCE.
1103
*/
1104
/***************************************************************************/
1105
static int remove_outliers(GCPTransformInfo *psInfo)
1106
0
{
1107
0
    double *padfGeoX = nullptr;
1108
0
    double *padfGeoY = nullptr;
1109
0
    double *padfRasterX = nullptr;
1110
0
    double *padfRasterY = nullptr;
1111
0
    int *panStatus = nullptr;
1112
0
    int nCRSresult = 0;
1113
0
    int nGCPCount = 0;
1114
0
    int nMinimumGcps = 0;
1115
0
    int nReqOrder = 0;
1116
0
    double dfTolerance = 0;
1117
0
    struct Control_Points sPoints;
1118
1119
0
    double x1_sum = 0;
1120
0
    double y1_sum = 0;
1121
0
    double x2_sum = 0;
1122
0
    double y2_sum = 0;
1123
0
    memset(&sPoints, 0, sizeof(sPoints));
1124
1125
0
    nGCPCount = static_cast<int>(psInfo->asGCPs.size());
1126
0
    nMinimumGcps = psInfo->nMinimumGcps;
1127
0
    nReqOrder = psInfo->nOrder;
1128
0
    dfTolerance = psInfo->dfTolerance;
1129
1130
0
    try
1131
0
    {
1132
0
        padfGeoX = new double[nGCPCount];
1133
0
        padfGeoY = new double[nGCPCount];
1134
0
        padfRasterX = new double[nGCPCount];
1135
0
        padfRasterY = new double[nGCPCount];
1136
0
        panStatus = new int[nGCPCount];
1137
1138
0
        for (int nI = 0; nI < nGCPCount; nI++)
1139
0
        {
1140
0
            panStatus[nI] = 1;
1141
0
            padfGeoX[nI] = psInfo->asGCPs[nI].X();
1142
0
            padfGeoY[nI] = psInfo->asGCPs[nI].Y();
1143
0
            padfRasterX[nI] = psInfo->asGCPs[nI].Pixel();
1144
0
            padfRasterY[nI] = psInfo->asGCPs[nI].Line();
1145
0
            x1_sum += psInfo->asGCPs[nI].Pixel();
1146
0
            y1_sum += psInfo->asGCPs[nI].Line();
1147
0
            x2_sum += psInfo->asGCPs[nI].X();
1148
0
            y2_sum += psInfo->asGCPs[nI].Y();
1149
0
        }
1150
0
        psInfo->x1_mean = x1_sum / nGCPCount;
1151
0
        psInfo->y1_mean = y1_sum / nGCPCount;
1152
0
        psInfo->x2_mean = x2_sum / nGCPCount;
1153
0
        psInfo->y2_mean = y2_sum / nGCPCount;
1154
1155
0
        sPoints.count = nGCPCount;
1156
0
        sPoints.e1 = padfRasterX;
1157
0
        sPoints.n1 = padfRasterY;
1158
0
        sPoints.e2 = padfGeoX;
1159
0
        sPoints.n2 = padfGeoY;
1160
0
        sPoints.status = panStatus;
1161
1162
0
        nCRSresult = CRS_compute_georef_equations(
1163
0
            psInfo, &sPoints, psInfo->adfToGeoX, psInfo->adfToGeoY,
1164
0
            psInfo->adfFromGeoX, psInfo->adfFromGeoY, nReqOrder);
1165
1166
0
        while (sPoints.count > nMinimumGcps)
1167
0
        {
1168
0
            int nIndex = worst_outlier(
1169
0
                &sPoints, psInfo->x1_mean, psInfo->y1_mean, psInfo->nOrder,
1170
0
                psInfo->adfToGeoX, psInfo->adfToGeoY, dfTolerance);
1171
1172
            // If no outliers were detected, stop the GCP elimination
1173
0
            if (nIndex == -1)
1174
0
            {
1175
0
                break;
1176
0
            }
1177
1178
0
            for (int nI = nIndex; nI < sPoints.count - 1; nI++)
1179
0
            {
1180
0
                sPoints.e1[nI] = sPoints.e1[nI + 1];
1181
0
                sPoints.n1[nI] = sPoints.n1[nI + 1];
1182
0
                sPoints.e2[nI] = sPoints.e2[nI + 1];
1183
0
                sPoints.n2[nI] = sPoints.n2[nI + 1];
1184
0
                psInfo->asGCPs[nI].SetId(psInfo->asGCPs[nI + 1].Id());
1185
0
                psInfo->asGCPs[nI].SetInfo(psInfo->asGCPs[nI + 1].Info());
1186
0
            }
1187
1188
0
            sPoints.count = sPoints.count - 1;
1189
1190
0
            nCRSresult = CRS_compute_georef_equations(
1191
0
                psInfo, &sPoints, psInfo->adfToGeoX, psInfo->adfToGeoY,
1192
0
                psInfo->adfFromGeoX, psInfo->adfFromGeoY, nReqOrder);
1193
0
        }
1194
1195
0
        for (int nI = 0; nI < sPoints.count; nI++)
1196
0
        {
1197
0
            psInfo->asGCPs[nI].X() = sPoints.e2[nI];
1198
0
            psInfo->asGCPs[nI].Y() = sPoints.n2[nI];
1199
0
            psInfo->asGCPs[nI].Pixel() = sPoints.e1[nI];
1200
0
            psInfo->asGCPs[nI].Line() = sPoints.n1[nI];
1201
0
        }
1202
0
        psInfo->asGCPs.resize(sPoints.count);
1203
0
    }
1204
0
    catch (const std::exception &e)
1205
0
    {
1206
0
        CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
1207
0
        nCRSresult = MINTERR;
1208
0
    }
1209
0
    delete[] padfGeoX;
1210
0
    delete[] padfGeoY;
1211
0
    delete[] padfRasterX;
1212
0
    delete[] padfRasterY;
1213
0
    delete[] panStatus;
1214
1215
0
    return nCRSresult;
1216
0
}