LCOV - code coverage report
Current view: top level - alg - gdal_crs.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 383 440 87.0 %
Date: 2024-05-04 12:52:34 Functions: 17 17 100.0 %

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

Generated by: LCOV version 1.14