LCOV - code coverage report
Current view: top level - ogr - ogr_srs_ozi.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 60 173 34.7 %
Date: 2025-10-21 22:35:35 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  OGRSpatialReference translation from OziExplorer
       5             :  *           georeferencing information.
       6             :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2009, Andrey Kiselev <dron@ak4719.spb.edu>
      10             :  * Copyright (c) 2009-2012, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "ogr_spatialref.h"
      17             : 
      18             : #include <cstdlib>
      19             : #include <cstring>
      20             : 
      21             : #include "cpl_conv.h"
      22             : #include "cpl_csv.h"
      23             : #include "cpl_error.h"
      24             : #include "cpl_string.h"
      25             : #include "ogr_core.h"
      26             : #include "ogr_srs_api.h"
      27             : 
      28             : /************************************************************************/
      29             : /*                          OSRImportFromOzi()                          */
      30             : /************************************************************************/
      31             : 
      32             : /**
      33             :  * Import coordinate system from OziExplorer projection definition.
      34             :  *
      35             :  * This function will import projection definition in style, used by
      36             :  * OziExplorer software.
      37             :  *
      38             :  * @param hSRS spatial reference object.
      39             :  * @param papszLines Map file lines. This is an array of strings containing
      40             :  * the whole OziExplorer .MAP file. The array is terminated by a NULL pointer.
      41             :  *
      42             :  * @return OGRERR_NONE on success or an error code in case of failure.
      43             :  */
      44             : 
      45           3 : OGRErr OSRImportFromOzi(OGRSpatialReferenceH hSRS,
      46             :                         const char *const *papszLines)
      47             : 
      48             : {
      49           3 :     VALIDATE_POINTER1(hSRS, "OSRImportFromOzi", OGRERR_FAILURE);
      50             : 
      51           3 :     return OGRSpatialReference::FromHandle(hSRS)->importFromOzi(papszLines);
      52             : }
      53             : 
      54             : /************************************************************************/
      55             : /*                            importFromOzi()                           */
      56             : /************************************************************************/
      57             : 
      58             : /**
      59             :  * Import coordinate system from OziExplorer projection definition.
      60             :  *
      61             :  * This method will import projection definition in style, used by
      62             :  * OziExplorer software.
      63             :  *
      64             :  * @param papszLines Map file lines. This is an array of strings containing
      65             :  * the whole OziExplorer .MAP file. The array is terminated by a NULL pointer.
      66             :  *
      67             :  * @return OGRERR_NONE on success or an error code in case of failure.
      68             :  *
      69             :  */
      70             : 
      71           3 : OGRErr OGRSpatialReference::importFromOzi(const char *const *papszLines)
      72             : {
      73             :     const char *pszDatum;
      74           3 :     const char *pszProj = nullptr;
      75           3 :     const char *pszProjParams = nullptr;
      76             : 
      77           3 :     Clear();
      78             : 
      79           3 :     const int nLines = CSLCount(papszLines);
      80           3 :     if (nLines < 5)
      81           0 :         return OGRERR_NOT_ENOUGH_DATA;
      82             : 
      83           3 :     pszDatum = papszLines[4];
      84             : 
      85           9 :     for (int iLine = 5; iLine < nLines; iLine++)
      86             :     {
      87           6 :         if (STARTS_WITH_CI(papszLines[iLine], "Map Projection"))
      88             :         {
      89           3 :             pszProj = papszLines[iLine];
      90             :         }
      91           3 :         else if (STARTS_WITH_CI(papszLines[iLine], "Projection Setup"))
      92             :         {
      93           3 :             pszProjParams = papszLines[iLine];
      94             :         }
      95             :     }
      96             : 
      97           3 :     if (!(pszDatum && pszProj && pszProjParams))
      98           0 :         return OGRERR_NOT_ENOUGH_DATA;
      99             : 
     100             :     /* -------------------------------------------------------------------- */
     101             :     /*      Operate on the basis of the projection name.                    */
     102             :     /* -------------------------------------------------------------------- */
     103           3 :     char **papszProj = CSLTokenizeStringComplex(pszProj, ",", TRUE, TRUE);
     104             :     char **papszProjParams =
     105           3 :         CSLTokenizeStringComplex(pszProjParams, ",", TRUE, TRUE);
     106           3 :     char **papszDatum = nullptr;
     107             : 
     108           3 :     if (CSLCount(papszProj) < 2)
     109             :     {
     110           0 :         goto not_enough_data;
     111             :     }
     112             : 
     113           3 :     if (STARTS_WITH_CI(papszProj[1], "Latitude/Longitude"))
     114             :     {
     115             :         // Do nothing.
     116             :     }
     117           1 :     else if (STARTS_WITH_CI(papszProj[1], "Mercator"))
     118             :     {
     119           0 :         if (CSLCount(papszProjParams) < 6)
     120           0 :             goto not_enough_data;
     121           0 :         double dfScale = CPLAtof(papszProjParams[3]);
     122             :         // If unset, default to scale = 1.
     123           0 :         if (papszProjParams[3][0] == 0)
     124           0 :             dfScale = 1;
     125           0 :         SetMercator(CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
     126           0 :                     dfScale, CPLAtof(papszProjParams[4]),
     127           0 :                     CPLAtof(papszProjParams[5]));
     128             :     }
     129           1 :     else if (STARTS_WITH_CI(papszProj[1], "Transverse Mercator"))
     130             :     {
     131           0 :         if (CSLCount(papszProjParams) < 6)
     132           0 :             goto not_enough_data;
     133           0 :         SetTM(CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
     134           0 :               CPLAtof(papszProjParams[3]), CPLAtof(papszProjParams[4]),
     135           0 :               CPLAtof(papszProjParams[5]));
     136             :     }
     137           1 :     else if (STARTS_WITH_CI(papszProj[1], "Lambert Conformal Conic"))
     138             :     {
     139           1 :         if (CSLCount(papszProjParams) < 8)
     140           0 :             goto not_enough_data;
     141           1 :         SetLCC(CPLAtof(papszProjParams[6]), CPLAtof(papszProjParams[7]),
     142           1 :                CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
     143           1 :                CPLAtof(papszProjParams[4]), CPLAtof(papszProjParams[5]));
     144             :     }
     145           0 :     else if (STARTS_WITH_CI(papszProj[1], "Sinusoidal"))
     146             :     {
     147           0 :         if (CSLCount(papszProjParams) < 6)
     148           0 :             goto not_enough_data;
     149           0 :         SetSinusoidal(CPLAtof(papszProjParams[2]), CPLAtof(papszProjParams[4]),
     150           0 :                       CPLAtof(papszProjParams[5]));
     151             :     }
     152           0 :     else if (STARTS_WITH_CI(papszProj[1], "Albers Equal Area"))
     153             :     {
     154           0 :         if (CSLCount(papszProjParams) < 8)
     155           0 :             goto not_enough_data;
     156           0 :         SetACEA(CPLAtof(papszProjParams[6]), CPLAtof(papszProjParams[7]),
     157           0 :                 CPLAtof(papszProjParams[1]), CPLAtof(papszProjParams[2]),
     158           0 :                 CPLAtof(papszProjParams[4]), CPLAtof(papszProjParams[5]));
     159             :     }
     160           0 :     else if (STARTS_WITH_CI(papszProj[1],
     161           0 :                             "(UTM) Universal Transverse Mercator") &&
     162             :              nLines > 5)
     163             :     {
     164             :         // Look for the UTM zone in the calibration point data.
     165           0 :         int iLine = 5;  // Used after for.
     166           0 :         for (; iLine < nLines; iLine++)
     167             :         {
     168           0 :             if (STARTS_WITH_CI(papszLines[iLine], "Point"))
     169             :             {
     170           0 :                 char **papszTok = CSLTokenizeString2(papszLines[iLine], ",",
     171             :                                                      CSLT_ALLOWEMPTYTOKENS |
     172             :                                                          CSLT_STRIPLEADSPACES |
     173             :                                                          CSLT_STRIPENDSPACES);
     174           0 :                 if (CSLCount(papszTok) < 17 || EQUAL(papszTok[2], "") ||
     175           0 :                     EQUAL(papszTok[13], "") || EQUAL(papszTok[14], "") ||
     176           0 :                     EQUAL(papszTok[15], "") || EQUAL(papszTok[16], ""))
     177             :                 {
     178           0 :                     CSLDestroy(papszTok);
     179           0 :                     continue;
     180             :                 }
     181           0 :                 SetUTM(atoi(papszTok[13]), EQUAL(papszTok[16], "N"));
     182           0 :                 CSLDestroy(papszTok);
     183           0 :                 break;
     184             :             }
     185             :         }
     186           0 :         if (iLine == nLines)  // Try to guess the UTM zone.
     187             :         {
     188           0 :             float fMinLongitude = 1000.0f;
     189           0 :             float fMaxLongitude = -1000.0f;
     190           0 :             float fMinLatitude = 1000.0f;
     191           0 :             float fMaxLatitude = -1000.0f;
     192           0 :             bool bFoundMMPLL = false;
     193           0 :             for (iLine = 5; iLine < nLines; iLine++)
     194             :             {
     195           0 :                 if (STARTS_WITH_CI(papszLines[iLine], "MMPLL"))
     196             :                 {
     197           0 :                     char **papszTok = CSLTokenizeString2(
     198           0 :                         papszLines[iLine], ",",
     199             :                         CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES |
     200             :                             CSLT_STRIPENDSPACES);
     201           0 :                     if (CSLCount(papszTok) < 4)
     202             :                     {
     203           0 :                         CSLDestroy(papszTok);
     204           0 :                         continue;
     205             :                     }
     206             :                     const float fLongitude =
     207           0 :                         static_cast<float>(CPLAtofM(papszTok[2]));
     208             :                     const float fLatitude =
     209           0 :                         static_cast<float>(CPLAtofM(papszTok[3]));
     210           0 :                     CSLDestroy(papszTok);
     211             : 
     212           0 :                     bFoundMMPLL = true;
     213             : 
     214           0 :                     if (fMinLongitude > fLongitude)
     215           0 :                         fMinLongitude = fLongitude;
     216           0 :                     if (fMaxLongitude < fLongitude)
     217           0 :                         fMaxLongitude = fLongitude;
     218           0 :                     if (fMinLatitude > fLatitude)
     219           0 :                         fMinLatitude = fLatitude;
     220           0 :                     if (fMaxLatitude < fLatitude)
     221           0 :                         fMaxLatitude = fLatitude;
     222             :                 }
     223             :             }
     224           0 :             const float fMedianLatitude = (fMinLatitude + fMaxLatitude) / 2;
     225           0 :             const float fMedianLongitude = (fMinLongitude + fMaxLongitude) / 2;
     226           0 :             if (bFoundMMPLL && fMaxLatitude <= 90)
     227             :             {
     228           0 :                 int nUtmZone = 0;
     229           0 :                 if (fMedianLatitude >= 56 && fMedianLatitude <= 64 &&
     230           0 :                     fMedianLongitude >= 3 && fMedianLongitude <= 12)
     231           0 :                     nUtmZone = 32;  // Norway exception.
     232           0 :                 else if (fMedianLatitude >= 72 && fMedianLatitude <= 84 &&
     233           0 :                          fMedianLongitude >= 0 && fMedianLongitude <= 42)
     234             :                     // Svalbard exception.
     235           0 :                     nUtmZone =
     236           0 :                         static_cast<int>((fMedianLongitude + 3) / 12) * 2 + 31;
     237             :                 else
     238           0 :                     nUtmZone =
     239           0 :                         static_cast<int>((fMedianLongitude + 180) / 6) + 1;
     240           0 :                 SetUTM(nUtmZone, fMedianLatitude >= 0);
     241             :             }
     242             :             else
     243             :             {
     244           0 :                 CPLDebug("OSR_Ozi", "UTM Zone not found");
     245             :             }
     246           0 :         }
     247             :     }
     248           0 :     else if (STARTS_WITH_CI(papszProj[1], "(I) France Zone I"))
     249             :     {
     250           0 :         SetLCC1SP(49.5, 2.337229167, 0.99987734, 600000, 1200000);
     251             :     }
     252           0 :     else if (STARTS_WITH_CI(papszProj[1], "(II) France Zone II"))
     253             :     {
     254           0 :         SetLCC1SP(46.8, 2.337229167, 0.99987742, 600000, 2200000);
     255             :     }
     256           0 :     else if (STARTS_WITH_CI(papszProj[1], "(III) France Zone III"))
     257             :     {
     258           0 :         SetLCC1SP(44.1, 2.337229167, 0.99987750, 600000, 3200000);
     259             :     }
     260           0 :     else if (STARTS_WITH_CI(papszProj[1], "(IV) France Zone IV"))
     261             :     {
     262           0 :         SetLCC1SP(42.165, 2.337229167, 0.99994471, 234.358, 4185861.369);
     263             :     }
     264             : 
     265             :     /*
     266             :      *  Note: The following projections have not been implemented yet
     267             :      *
     268             :      */
     269             : 
     270             :     /*
     271             :         else if( STARTS_WITH_CI(papszProj[1], "(BNG) British National Grid") )
     272             :         {
     273             :         }
     274             :         else if( STARTS_WITH_CI(papszProj[1], "(IG) Irish Grid") )
     275             :         {
     276             :         }
     277             : 
     278             :         else if( STARTS_WITH_CI(papszProj[1], "(NZG) New Zealand Grid") )
     279             :         {
     280             :         }
     281             :         else if( STARTS_WITH_CI(papszProj[1], "(NZTM2) New Zealand TM 2000") )
     282             :         {
     283             :         }
     284             :         else if( STARTS_WITH_CI(papszProj[1], "(SG) Swedish Grid") )
     285             :         {
     286             :         }
     287             :         else if( STARTS_WITH_CI(papszProj[1], "(SUI) Swiss Grid") )
     288             :         {
     289             :         }
     290             :         else if( STARTS_WITH_CI(papszProj[1], "(A)Lambert Azimuthual Equal
     291             :        Area") )
     292             :         {
     293             :         }
     294             :         else if( STARTS_WITH_CI(papszProj[1], "(EQC) Equidistant Conic") )
     295             :         {
     296             :         }
     297             :         else if( STARTS_WITH_CI(papszProj[1], "Polyconic (American)") )
     298             :         {
     299             :         }
     300             :         else if( STARTS_WITH_CI(papszProj[1], "Van Der Grinten") )
     301             :         {
     302             :         }
     303             :         else if( STARTS_WITH_CI(papszProj[1], "Vertical Near-Sided Perspective")
     304             :        )
     305             :         {
     306             :         }
     307             :         else if( STARTS_WITH_CI(papszProj[1], "(WIV) Wagner IV") )
     308             :         {
     309             :         }
     310             :         else if( STARTS_WITH_CI(papszProj[1], "Bonne") )
     311             :         {
     312             :         }
     313             :         else if( STARTS_WITH_CI(papszProj[1],
     314             :                                 "(MT0) Montana State Plane Zone 2500") )
     315             :         {
     316             :         }
     317             :         else if( STARTS_WITH_CI(papszProj[1], "ITA1) Italy Grid Zone 1") )
     318             :         {
     319             :         }
     320             :         else if( STARTS_WITH_CI(papszProj[1], "ITA2) Italy Grid Zone 2") )
     321             :         {
     322             :         }
     323             :         else if( STARTS_WITH_CI(papszProj[1],
     324             :                                 "(VICMAP-TM) Victoria Aust.(pseudo AMG)") )
     325             :         {
     326             :         }
     327             :         else if( STARTS_WITH_CI(papszProj[1], "VICGRID) Victoria Australia") )
     328             :         {
     329             :         }
     330             :         else if( STARTS_WITH_CI(papszProj[1],
     331             :                                 "(VG94) VICGRID94 Victoria Australia") )
     332             :         {
     333             :         }
     334             :         else if( STARTS_WITH_CI(papszProj[1], "Gnomonic") )
     335             :         {
     336             :         }
     337             :     */
     338             :     else
     339             :     {
     340           0 :         CPLDebug("OSR_Ozi", "Unsupported projection: \"%s\"", papszProj[1]);
     341           0 :         SetLocalCS(
     342           0 :             CPLString().Printf(R"("Ozi" projection "%s")", papszProj[1]));
     343             :     }
     344             : 
     345             :     /* -------------------------------------------------------------------- */
     346             :     /*      Try to translate the datum/spheroid.                            */
     347             :     /* -------------------------------------------------------------------- */
     348           3 :     papszDatum = CSLTokenizeString2(
     349             :         pszDatum, ",",
     350             :         CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
     351           3 :     if (papszDatum == nullptr)
     352           0 :         goto not_enough_data;
     353             : 
     354           3 :     if (!IsLocal())
     355             :     {
     356             :         /* --------------------------------------------------------------------
     357             :          */
     358             :         /*      Verify that we can find the CSV file containing the datums */
     359             :         /* --------------------------------------------------------------------
     360             :          */
     361           3 :         if (CSVScanFileByName(CSVFilename("ozi_datum.csv"), "EPSG_DATUM_CODE",
     362           3 :                               "4326", CC_Integer) == nullptr)
     363             :         {
     364           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     365             :                      "Unable to open OZI support file %s.  "
     366             :                      "Try setting the GDAL_DATA environment variable to point "
     367             :                      "to the directory containing OZI csv files.",
     368             :                      CSVFilename("ozi_datum.csv"));
     369           0 :             goto other_error;
     370             :         }
     371             : 
     372             :         /* --------------------------------------------------------------------
     373             :          */
     374             :         /*      Search for matching datum */
     375             :         /* --------------------------------------------------------------------
     376             :          */
     377           3 :         const char *pszOziDatum = CSVFilename("ozi_datum.csv");
     378             :         CPLString osDName = CSVGetField(pszOziDatum, "NAME", papszDatum[0],
     379           3 :                                         CC_ApproxString, "NAME");
     380           3 :         if (osDName.empty())
     381             :         {
     382           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     383             :                      "Failed to find datum %s in ozi_datum.csv.",
     384             :                      papszDatum[0]);
     385           0 :             goto other_error;
     386             :         }
     387             : 
     388             :         const int nDatumCode =
     389           3 :             atoi(CSVGetField(pszOziDatum, "NAME", papszDatum[0],
     390             :                              CC_ApproxString, "EPSG_DATUM_CODE"));
     391             : 
     392           3 :         if (nDatumCode > 0)  // There is a matching EPSG code
     393             :         {
     394           4 :             OGRSpatialReference oGCS;
     395           2 :             oGCS.importFromEPSG(nDatumCode);
     396           2 :             CopyGeogCSFrom(&oGCS);
     397             :         }
     398             :         else  // We use the parameters from the CSV files
     399             :         {
     400             :             CPLString osEllipseCode =
     401             :                 CSVGetField(pszOziDatum, "NAME", papszDatum[0], CC_ApproxString,
     402           1 :                             "ELLIPSOID_CODE");
     403           1 :             const double dfDeltaX = CPLAtof(CSVGetField(
     404             :                 pszOziDatum, "NAME", papszDatum[0], CC_ApproxString, "DELTAX"));
     405           1 :             const double dfDeltaY = CPLAtof(CSVGetField(
     406             :                 pszOziDatum, "NAME", papszDatum[0], CC_ApproxString, "DELTAY"));
     407           1 :             const double dfDeltaZ = CPLAtof(CSVGetField(
     408             :                 pszOziDatum, "NAME", papszDatum[0], CC_ApproxString, "DELTAZ"));
     409             : 
     410             :             /* --------------------------------------------------------------------
     411             :              */
     412             :             /*     Verify that we can find the CSV file containing the
     413             :              * ellipsoids.  */
     414             :             /* --------------------------------------------------------------------
     415             :              */
     416           1 :             if (CSVScanFileByName(CSVFilename("ozi_ellips.csv"),
     417             :                                   "ELLIPSOID_CODE", "20",
     418           1 :                                   CC_Integer) == nullptr)
     419             :             {
     420           0 :                 CPLError(
     421             :                     CE_Failure, CPLE_OpenFailed,
     422             :                     "Unable to open OZI support file %s.  "
     423             :                     "Try setting the GDAL_DATA environment variable to point "
     424             :                     "to the directory containing OZI csv files.",
     425             :                     CSVFilename("ozi_ellips.csv"));
     426           0 :                 goto other_error;
     427             :             }
     428             : 
     429             :             /* --------------------------------------------------------------------
     430             :              */
     431             :             /*      Lookup the ellipse code. */
     432             :             /* --------------------------------------------------------------------
     433             :              */
     434           1 :             const char *pszOziEllipse = CSVFilename("ozi_ellips.csv");
     435             : 
     436             :             CPLString osEName =
     437             :                 CSVGetField(pszOziEllipse, "ELLIPSOID_CODE", osEllipseCode,
     438           1 :                             CC_ApproxString, "NAME");
     439           1 :             if (osEName.empty())
     440             :             {
     441           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     442             :                          "Failed to find ellipsoid %s in ozi_ellips.csv.",
     443             :                          osEllipseCode.c_str());
     444           0 :                 goto other_error;
     445             :             }
     446             : 
     447             :             const double dfA =
     448           1 :                 CPLAtof(CSVGetField(pszOziEllipse, "ELLIPSOID_CODE",
     449             :                                     osEllipseCode, CC_ApproxString, "A"));
     450             :             const double dfInvF =
     451           1 :                 CPLAtof(CSVGetField(pszOziEllipse, "ELLIPSOID_CODE",
     452             :                                     osEllipseCode, CC_ApproxString, "INVF"));
     453             : 
     454             :             /* --------------------------------------------------------------------
     455             :              */
     456             :             /*      Create geographic coordinate system. */
     457             :             /* --------------------------------------------------------------------
     458             :              */
     459           1 :             SetGeogCS(osDName, osDName, osEName, dfA, dfInvF);
     460           1 :             SetTOWGS84(dfDeltaX, dfDeltaY, dfDeltaZ);
     461             :         }
     462             :     }
     463             : 
     464             :     /* -------------------------------------------------------------------- */
     465             :     /*      Grid units translation                                          */
     466             :     /* -------------------------------------------------------------------- */
     467           3 :     if (IsLocal() || IsProjected())
     468           1 :         SetLinearUnits(SRS_UL_METER, 1.0);
     469             : 
     470           3 :     CSLDestroy(papszProj);
     471           3 :     CSLDestroy(papszProjParams);
     472           3 :     CSLDestroy(papszDatum);
     473             : 
     474           3 :     return OGRERR_NONE;
     475             : 
     476           0 : not_enough_data:
     477             : 
     478           0 :     CSLDestroy(papszProj);
     479           0 :     CSLDestroy(papszProjParams);
     480           0 :     CSLDestroy(papszDatum);
     481             : 
     482           0 :     return OGRERR_NOT_ENOUGH_DATA;
     483             : 
     484           0 : other_error:
     485             : 
     486           0 :     CSLDestroy(papszProj);
     487           0 :     CSLDestroy(papszProjParams);
     488           0 :     CSLDestroy(papszDatum);
     489             : 
     490           0 :     return OGRERR_FAILURE;
     491             : }

Generated by: LCOV version 1.14