text: /******************************************************************************
   * $Id: ogr_sxfdatasource.cpp  $
   *
   * Project:  SXF Translator
   * Purpose:  Definition of classes for OGR SXF Datasource.
   * Author:   Ben Ahmed Daho Ali, bidandou(at)yahoo(dot)fr
   *           Dmitry Baryshnikov, polimax@mail.ru
   *           Alexandr Lisovenko, alexander.lisovenko@gmail.com
   *
   ******************************************************************************
   * Copyright (c) 2011, Ben Ahmed Daho Ali
   * Copyright (c) 2013, NextGIS
   * Copyright (c) 2014, Even Rouault <even dot rouault at mines-paris dot org>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included
   * in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   ****************************************************************************/
  
  #include "cpl_conv.h"
  #include "ogr_sxf.h"
  #include "cpl_string.h"
  #include "cpl_multiproc.h"
  
  #include <math.h>
  #include <map>
  #include <string>
  
  CPL_CVSID("$Id: ogrsxfdatasource.cpp  $");
  
  static const long aoVCS[] =
  {
      0,
      5705,   //1
      5711,   //2
      0,      //3
      5710,   //4
      5710,   //5
      0,      //6
      0,      //7
      0,      //8
      0,      //9
      5716,   //10
      5733,   //11
      0,      //12
      0,      //13
      0,      //14
      0,      //15
      5709,   //16
      5776,   //17
      0,      //18
      0,      //19
      5717,   //20
      5613,   //21
      0,      //22
      5775,   //23
      5702,   //24
      0,      //25
      0,      //26
      5714    //27
  };
  
  #define NUMBER_OF_VERTICALCS    (sizeof(aoVCS)/sizeof(aoVCS[0]))
  
  /************************************************************************/
  /*                         OGRSXFDataSource()                           */
  /************************************************************************/
  
  OGRSXFDataSource::OGRSXFDataSource()
  
  {
      papoLayers = NULL;
      nLayers = 0;
  
      fpSXF = NULL;
      hIOMutex = NULL;
  
      oSXFPassport.stMapDescription.pSpatRef = NULL;
  }
  
  /************************************************************************/
  /*                          ~OGRSXFDataSource()                         */
  /************************************************************************/
  
  OGRSXFDataSource::~OGRSXFDataSource()
  
  {
      for( size_t i = 0; i < nLayers; i++ )
          delete papoLayers[i];
      CPLFree( papoLayers );
  
      if (NULL != oSXFPassport.stMapDescription.pSpatRef)
      {
          oSXFPassport.stMapDescription.pSpatRef->Release();
      }
  
      CloseFile();
  
      if (hIOMutex != NULL)
      {
          CPLDestroyMutex(hIOMutex);
          hIOMutex = NULL;
      }
  }
  
  /************************************************************************/
  /*                     CloseFile()                                      */
  /************************************************************************/
  void  OGRSXFDataSource::CloseFile()
  { 
      if (NULL != fpSXF)
      {
          VSIFCloseL( fpSXF );
          fpSXF = NULL;
      }
  }
  
  /************************************************************************/
  /*                           TestCapability()                           */
  /************************************************************************/
  
  int OGRSXFDataSource::TestCapability( CPL_UNUSED const char * pszCap )
  {
      return FALSE;
  }
  
  /************************************************************************/
  /*                              GetLayer()                              */
  /************************************************************************/
  
  OGRLayer *OGRSXFDataSource::GetLayer( int iLayer )
  
  {
      if( iLayer < 0 || iLayer >= (int)nLayers )
          return NULL;
      else
          return papoLayers[iLayer];
  }
  
  /************************************************************************/
  /*                                Open()                                */
  /************************************************************************/
  
  int OGRSXFDataSource::Open( const char * pszFilename, int bUpdateIn)
  {
      size_t nObjectsRead;
      int nFileHeaderSize;
  
      if (bUpdateIn)
      {
          return FALSE;
      }
  
      pszName = pszFilename;
  
      fpSXF = VSIFOpenL(pszName, "rb");
      if ( fpSXF == NULL )
      {
          CPLError(CE_Warning, CPLE_OpenFailed, "SXF open file %s failed", pszFilename);
          return FALSE;
      }
      
      //read header
      nFileHeaderSize = sizeof(SXFHeader);
      SXFHeader stSXFFileHeader;
      nObjectsRead = VSIFReadL(&stSXFFileHeader, nFileHeaderSize, 1, fpSXF);
  
      if (nObjectsRead != 1)
      {
          CPLError(CE_Failure, CPLE_None, "SXF head read failed");
          CloseFile();
  		return FALSE;
      }
  
      //check version
      oSXFPassport.version = 0;
      if (stSXFFileHeader.nHeaderLength > 256) //if size == 400 then version >= 4
      {
          oSXFPassport.version = stSXFFileHeader.nFormatVersion[2];
      }
      else
      {
          oSXFPassport.version = stSXFFileHeader.nFormatVersion[1];
      }
  
      if ( oSXFPassport.version < 3 )
      {
          CPLError(CE_Failure, CPLE_NotSupported , "SXF File version not supported");
          CloseFile();
          return FALSE;
      }
  
      // read description
      if (ReadSXFDescription(fpSXF, oSXFPassport) != OGRERR_NONE)
      {
          CPLError(CE_Failure, CPLE_NotSupported, "SXF. Wrong description.");
          CloseFile();
          return FALSE;
      }
  
  
      //read flags 
      if (ReadSXFInformationFlags(fpSXF, oSXFPassport) != OGRERR_NONE)
      {
          CPLError(CE_Failure, CPLE_NotSupported, "SXF. Wrong state of the data.");
          CloseFile();
          return FALSE;
      }
  
      if (oSXFPassport.informationFlags.bProjectionDataCompliance == false)
      {
          CPLError( CE_Failure, CPLE_NotSupported,
                    "SXF. Data are not corresponde to the projection." );
          CloseFile();
          return FALSE;
      }
  
      //read spatial data
      if (ReadSXFMapDescription(fpSXF, oSXFPassport) != OGRERR_NONE)
      {
          CPLError(CE_Failure, CPLE_NotSupported, "SXF. Wrong state of the data.");
          CloseFile();
          return FALSE;
      }
  
      if(oSXFPassport.informationFlags.bRealCoordinatesCompliance == false )
      {
          CPLError( CE_Warning, CPLE_NotSupported,
                    "SXF. Given material may be rotated in the conditional system of coordinates" );
      }
  
  /*---------------- TRY READ THE RSC FILE HEADER  -----------------------*/
  
      CPLString soRSCRileName;
      const char* pszRSCRileName = CPLGetConfigOption("SXF_RSC_FILENAME", "");
      if (CPLCheckForFile((char *)pszRSCRileName, NULL) == TRUE)
      {
          soRSCRileName = pszRSCRileName;
      }
  
      if(soRSCRileName.empty())
      {
          pszRSCRileName = CPLResetExtension(pszFilename, "rsc");
          if (CPLCheckForFile((char *)pszRSCRileName, NULL) == TRUE)
          {
              soRSCRileName = pszRSCRileName;
          }
      }
  
      if(soRSCRileName.empty())
      {
          pszRSCRileName = CPLResetExtension(pszFilename, "RSC");
          if (CPLCheckForFile((char *)pszRSCRileName, NULL) == TRUE)
          {
              soRSCRileName = pszRSCRileName;
          }
      }
  
  
      //1. Create layers from RSC file or create default set of layers from osm.rsc
  
      if (soRSCRileName.empty())
      { 
          CPLError(CE_Warning, CPLE_None, "RSC file for %s not exist", pszFilename);
      }
      else
      {
          VSILFILE* fpRSC;
  
          fpRSC = VSIFOpenL(soRSCRileName, "rb");
          if (fpRSC == NULL)
          {
              CPLError(CE_Warning, CPLE_OpenFailed, "RSC file %s open failed",
                       soRSCRileName.c_str());
          }
          else
          {
              CPLDebug( "OGRSXFDataSource", "RSC Filename: %s",
                        soRSCRileName.c_str() );
              CreateLayers(fpRSC);
              VSIFCloseL(fpRSC);
          }
      }
  
      if (nLayers == 0)//create default set of layers
      {
          CreateLayers();
      }
  
      FillLayers();
  
      return TRUE;
  }
  
  OGRErr OGRSXFDataSource::ReadSXFDescription(VSILFILE* fpSXF, SXFPassport& passport)
  {
      /* int nObjectsRead; */
  
      if (passport.version == 3)
      {
          //78
          GByte buff[62];
          /* nObjectsRead = */ VSIFReadL(&buff, 62, 1, fpSXF);
          char date[3] = { 0 };
  
          //read year
          memcpy(date, buff, 2);
          passport.dtCrateDate.nYear = atoi(date);
          if (passport.dtCrateDate.nYear < 50)
              passport.dtCrateDate.nYear += 2000;
          else
              passport.dtCrateDate.nYear += 1900;
  
          memcpy(date, buff + 2, 2);
  
          passport.dtCrateDate.nMonth = atoi(date);
  
          memcpy(date, buff + 4, 2);
  
          passport.dtCrateDate.nDay = atoi(date);
  
          char szName[26] = { 0 };
          memcpy(szName, buff + 8, 24);
          char* pszRecoded = CPLRecode(szName + 2, "CP1251", CPL_ENC_UTF8);
          passport.sMapSheet = pszRecoded; //TODO: check the encoding in SXF created in Linux
          CPLFree(pszRecoded);
  
          memcpy(&passport.nScale, buff + 32, 4);
          CPL_LSBPTR32(&passport.nScale);
  
          memset(szName, 0, 26);
          memcpy(szName, buff + 36, 26);
          pszRecoded = CPLRecode(szName, "CP866", CPL_ENC_UTF8);
          passport.sMapSheetName = pszRecoded; //TODO: check the encoding in SXF created in Linux
          CPLFree(pszRecoded);
  
      }
      else if (passport.version == 4)
      {
          //96
          GByte buff[80];
          /* nObjectsRead = */ VSIFReadL(&buff, 80, 1, fpSXF);
          char date[5] = { 0 };
  
          //read year
          memcpy(date, buff, 4);
          passport.dtCrateDate.nYear = atoi(date);
  
          memset(date, 0, 5);
          memcpy(date, buff + 4, 2);
  
          passport.dtCrateDate.nMonth = atoi(date);
  
          memcpy(date, buff + 6, 2);
  
          passport.dtCrateDate.nDay = atoi(date);
  
          char szName[32] = { 0 };
          memcpy(szName, buff + 12, 32);
          char* pszRecoded = CPLRecode(szName + 2, "CP1251", CPL_ENC_UTF8);
          passport.sMapSheet = pszRecoded; //TODO: check the encoding in SXF created in Linux
          CPLFree(pszRecoded);
          
          memcpy(&passport.nScale, buff + 44, 4);
          CPL_LSBPTR32(&passport.nScale);
  
          memset(szName, 0, 32);
          memcpy(szName, buff + 48, 32);
          pszRecoded = CPLRecode(szName, "CP1251", CPL_ENC_UTF8);
          passport.sMapSheetName = pszRecoded; //TODO: check the encoding in SXF created in Linux
          CPLFree(pszRecoded);
      }
  
      return OGRERR_NONE;
  }
  
  OGRErr OGRSXFDataSource::ReadSXFInformationFlags(VSILFILE* fpSXF, SXFPassport& passport)
  {
      /* int nObjectsRead; */
      GByte val[4];
      /* nObjectsRead = */ VSIFReadL(&val, 4, 1, fpSXF);
  
      if (!(CHECK_BIT(val[0], 0) && CHECK_BIT(val[0], 1)))
      {
          return OGRERR_UNSUPPORTED_OPERATION;
      }
  
      if (CHECK_BIT(val[0], 2))
      {
          passport.informationFlags.bProjectionDataCompliance = true;
      }
      else
      {
          passport.informationFlags.bProjectionDataCompliance = false;
      }
  
      if (CHECK_BIT(val[0], 4))
      {
          passport.informationFlags.bRealCoordinatesCompliance = true;
      }
      else
      {
          passport.informationFlags.bRealCoordinatesCompliance = false;
      }
  
      if (CHECK_BIT(val[0], 6))
      {
          passport.informationFlags.stCodingType = SXF_SEM_TXT;
      }
      else
      {
          if (CHECK_BIT(val[0], 5))
          {
              passport.informationFlags.stCodingType = SXF_SEM_HEX;
          }
          else
          {
              passport.informationFlags.stCodingType = SXF_SEM_DEC;
          }
      }
  
      if (CHECK_BIT(val[0], 7))
      {
          passport.informationFlags.stGenType = SXF_GT_LARGE_SCALE;
      }
      else
      {
          passport.informationFlags.stGenType = SXF_GT_SMALL_SCALE;
      }
  
      //version specific
  
      if (passport.version == 3)
      {
          //degrees are ints * 100 000 000
          //meters are ints / 10
          passport.informationFlags.stEnc = SXF_ENC_DOS;
          passport.informationFlags.stCoordAcc = SXF_COORD_ACC_DM;
          passport.informationFlags.bSort = false;
      }
      else if (passport.version == 4)
      {
          passport.informationFlags.stEnc = (SXFTextEncoding)val[1];
          passport.informationFlags.stCoordAcc = (SXFCoordinatesAccuracy)val[2];
          if (CHECK_BIT(val[3], 0))
          {
              passport.informationFlags.bSort = true;
          }
          else
          {
              passport.informationFlags.bSort = false;
          }
      }
  
      return OGRERR_NONE;
  }
  
  void OGRSXFDataSource::SetVertCS(const long iVCS, SXFPassport& passport)
  {
      if (!CSLTestBoolean(CPLGetConfigOption("SXF_SET_VERTCS", "NO")))
          return;
  
      const long nEPSG = aoVCS[iVCS];
  
      if (nEPSG == 0)
      {
          CPLError(CE_Warning, CPLE_NotSupported, "SXF. Vertical coordinate system (SXF index %ld) not supported", iVCS);
          return;
      }
  
      OGRSpatialReference* sr = new OGRSpatialReference();
      OGRErr eImportFromEPSGErr = sr->importFromEPSG(nEPSG);
      if (eImportFromEPSGErr != OGRERR_NONE)
      {
          CPLError( CE_Warning, CPLE_None, "SXF. Vertical coordinate system (SXF index %ld, EPSG %ld) import from EPSG error", iVCS, nEPSG);
          return;
      }
  
      if (sr->IsVertical() != 1)
      {
          CPLError( CE_Warning, CPLE_None, "SXF. Coordinate system (SXF index %ld, EPSG %ld) is not Vertical", iVCS, nEPSG);
          return;
      }
  
      //passport.stMapDescription.pSpatRef->SetVertCS("Baltic", "Baltic Sea");
      OGRErr eSetVertCSErr = passport.stMapDescription.pSpatRef->SetVertCS(sr->GetAttrValue("VERT_CS"), sr->GetAttrValue("VERT_DATUM"));
      if (eSetVertCSErr != OGRERR_NONE)
      {
          CPLError(CE_Warning, CPLE_None, "SXF. Vertical coordinate system (SXF index %ld, EPSG %ld) set error", iVCS, nEPSG);
          return;
      }
  }
  OGRErr OGRSXFDataSource::ReadSXFMapDescription(VSILFILE* fpSXF, SXFPassport& passport)
  {
      /* int nObjectsRead;*/
      int i;
      passport.stMapDescription.Env.MaxX = -100000000;
      passport.stMapDescription.Env.MinX = 100000000;
      passport.stMapDescription.Env.MaxY = -100000000;
      passport.stMapDescription.Env.MinY = 100000000;
  
      bool bIsX = true;// passport.informationFlags.bRealCoordinatesCompliance; //if real coordinates we need to swap x & y
  
      //version specific
      if (passport.version == 3)
      {
          short nNoObjClass, nNoSemClass;
          /* nObjectsRead = */ VSIFReadL(&nNoObjClass, 2, 1, fpSXF);
          /* nObjectsRead = */ VSIFReadL(&nNoSemClass, 2, 1, fpSXF);
          GByte baMask[8];
          /* nObjectsRead = */ VSIFReadL(&baMask, 8, 1, fpSXF);
  
          int nCorners[8];
  
          //get projected corner coords
          /* nObjectsRead = */ VSIFReadL(&nCorners, 32, 1, fpSXF);
  
          for (i = 0; i < 8; i++)
          {
              passport.stMapDescription.stProjCoords[i] = double(nCorners[i]) / 10.0;
              if (bIsX) //X
              {
                  if (passport.stMapDescription.Env.MaxY < passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MaxY = passport.stMapDescription.stProjCoords[i];
                  if (passport.stMapDescription.Env.MinY > passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MinY = passport.stMapDescription.stProjCoords[i];
              }
              else
              {
                  if (passport.stMapDescription.Env.MaxX < passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MaxX = passport.stMapDescription.stProjCoords[i];
                  if (passport.stMapDescription.Env.MinX > passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MinX = passport.stMapDescription.stProjCoords[i];
              }
              bIsX = !bIsX;
          }
          //get geographic corner coords
          /* nObjectsRead = */ VSIFReadL(&nCorners, 32, 1, fpSXF);
  
          for (i = 0; i < 8; i++)
          {
              passport.stMapDescription.stGeoCoords[i] = double(nCorners[i]) * 0.00000057295779513082; //from radians to degree * 100 000 000
          }
      }
      else if (passport.version == 4)
      {
          int nEPSG;
          /* nObjectsRead = */ VSIFReadL(&nEPSG, 4, 1, fpSXF);
  
          if (nEPSG != 0)
          {
              passport.stMapDescription.pSpatRef = new OGRSpatialReference();
              passport.stMapDescription.pSpatRef->importFromEPSG(nEPSG);
          }
  
          double dfCorners[8];
          /* nObjectsRead = */ VSIFReadL(&dfCorners, 64, 1, fpSXF);
  
          for (i = 0; i < 8; i++)
          {
              passport.stMapDescription.stProjCoords[i] = dfCorners[i];
              if (bIsX) //X
              {
                  if (passport.stMapDescription.Env.MaxY < passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MaxY = passport.stMapDescription.stProjCoords[i];
                  if (passport.stMapDescription.Env.MinY > passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MinY = passport.stMapDescription.stProjCoords[i];
              }
              else
              {
                  if (passport.stMapDescription.Env.MaxX < passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MaxX = passport.stMapDescription.stProjCoords[i];
                  if (passport.stMapDescription.Env.MinX > passport.stMapDescription.stProjCoords[i])
                      passport.stMapDescription.Env.MinX = passport.stMapDescription.stProjCoords[i];
              }
              bIsX = !bIsX;
  
          }
          //get geographic corner coords
          /* nObjectsRead = */ VSIFReadL(&dfCorners, 64, 1, fpSXF);
  
          for (i = 0; i < 8; i++)
          {
              passport.stMapDescription.stGeoCoords[i] = dfCorners[i] * TO_DEGREES; // to degree 
          }
  
      }
  
      if (NULL != passport.stMapDescription.pSpatRef)
      {
          return OGRERR_NONE;
      }
  
      GByte anData[8] = { 0 };
      /* nObjectsRead = */ VSIFReadL(&anData, 8, 1, fpSXF);
      long iEllips = anData[0];
      long iVCS = anData[1];
      long iProjSys = anData[2];
      /* long iDatum = anData[3]; Unused. */
      double dfProjScale = 1;
  
      double adfPrjParams[8] = { 0 };
  
      if (passport.version == 3)
      {
          switch (anData[5])
          {
          case 1:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_DECIMETRE;
              break;
          case 2:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_CENTIMETRE;
              break;
          case 3:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_MILLIMETRE;
              break;
          case 130:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_RADIAN;
              break;
          case 129:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_DEGREE;
              break;
          default:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_METRE;
              break;
          }
  
  
          VSIFSeekL(fpSXF, 212, SEEK_SET);
          struct _buff{
              GUInt32 nRes;
              GInt16 anFrame[8];
              GUInt32 nFrameCode;
          } buff;
          /* nObjectsRead = */ VSIFReadL(&buff, 20, 1, fpSXF);
          passport.stMapDescription.nResolution = buff.nRes; //resolution
  
          for (i = 0; i < 8; i++)
              passport.stMapDescription.stFrameCoords[i] = buff.anFrame[i];
  
          int anParams[5];
          /* nObjectsRead = */ VSIFReadL(&anParams, 20, 1, fpSXF);
  
          if (anParams[0] != -1)
              dfProjScale = double(anParams[0]) / 100000000.0;
  
          if (anParams[2] != -1)
              passport.stMapDescription.dfXOr = double(anParams[2]) / 100000000.0 * TO_DEGREES;
          else
              passport.stMapDescription.dfXOr = 0;
  
          if (anParams[3] != -1)
              passport.stMapDescription.dfYOr = double(anParams[2]) / 100000000.0 * TO_DEGREES;
          else
              passport.stMapDescription.dfYOr = 0;
  
          passport.stMapDescription.dfFalseNorthing = 0;
          passport.stMapDescription.dfFalseEasting = 0;
  
  
          //adfPrjParams[0] = double(anParams[0]) / 100000000.0; // to radians  
          //adfPrjParams[1] = double(anParams[1]) / 100000000.0;
          //adfPrjParams[2] = double(anParams[2]) / 100000000.0;
          //adfPrjParams[3] = double(anParams[3]) / 100000000.0;
          adfPrjParams[4] = dfProjScale;//?
          //adfPrjParams[5] = 0;//?
          //adfPrjParams[6] = 0;//?
          //adfPrjParams[7] = 0;// importFromPanorama calc it by itself
  
      }
      else if (passport.version == 4)
      {
          switch (anData[5])
          {
          case 64:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_RADIAN;
              break;
          case 65:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_DEGREE;
              break;
          default:
              passport.stMapDescription.eUnitInPlan = SXF_COORD_MU_METRE;
              break;
          }
  
          VSIFSeekL(fpSXF, 312, SEEK_SET);
          GUInt32 buff[10];
          /* nObjectsRead = */ VSIFReadL(&buff, 40, 1, fpSXF);
  
          passport.stMapDescription.nResolution = buff[0]; //resolution
          for (i = 0; i < 8; i++)
              passport.stMapDescription.stFrameCoords[i] = buff[1 + i];
  
          double adfParams[6];
          /* nObjectsRead = */ VSIFReadL(&adfParams, 48, 1, fpSXF);
  
          if (adfParams[1] != -1)
              dfProjScale = adfParams[1];
          passport.stMapDescription.dfXOr = adfParams[2] * TO_DEGREES;
          passport.stMapDescription.dfYOr = adfParams[3] * TO_DEGREES;
          passport.stMapDescription.dfFalseNorthing = adfParams[4];
          passport.stMapDescription.dfFalseEasting = adfParams[5];
  
          //adfPrjParams[0] = adfParams[0]; // to radians 
          //adfPrjParams[1] = adfParams[1];
          //adfPrjParams[2] = adfParams[2];
          //adfPrjParams[3] = adfParams[3];
          adfPrjParams[4] = dfProjScale;//?
          //adfPrjParams[5] = adfParams[4];
          //adfPrjParams[6] = adfParams[5];
          //adfPrjParams[7] = 0;// importFromPanorama calc it by itself
      }
  
      passport.stMapDescription.dfScale = passport.nScale;
  
      double dfCoeff = double(passport.stMapDescription.dfScale) / passport.stMapDescription.nResolution;
      passport.stMapDescription.bIsRealCoordinates = passport.informationFlags.bRealCoordinatesCompliance;
      passport.stMapDescription.stCoordAcc = passport.informationFlags.stCoordAcc;
  
      if (!passport.stMapDescription.bIsRealCoordinates)
      {
          if (passport.stMapDescription.stFrameCoords[0] == 0 && passport.stMapDescription.stFrameCoords[1] == 0 && passport.stMapDescription.stFrameCoords[2] == 0 && passport.stMapDescription.stFrameCoords[3] == 0 && passport.stMapDescription.stFrameCoords[4] == 0 && passport.stMapDescription.stFrameCoords[5] == 0 && passport.stMapDescription.stFrameCoords[6] == 0 && passport.stMapDescription.stFrameCoords[7] == 0)
          {
              passport.stMapDescription.bIsRealCoordinates = true;
          }
          else
          {
              //origin
              passport.stMapDescription.dfXOr = passport.stMapDescription.stProjCoords[1] - passport.stMapDescription.stFrameCoords[1] * dfCoeff;
              passport.stMapDescription.dfYOr = passport.stMapDescription.stProjCoords[0] - passport.stMapDescription.stFrameCoords[0] * dfCoeff;
          }
      }
  
      //normalize some coordintatessystems
      if (iEllips == 1 && iProjSys == 1) // Pulkovo 1942 / Gauss-Kruger
      {
          double dfCenterLongEnv = passport.stMapDescription.stGeoCoords[1] + fabs(passport.stMapDescription.stGeoCoords[5] - passport.stMapDescription.stGeoCoords[1]) / 2;
  
          int nZoneEnv = (int)( (dfCenterLongEnv + 3.0) / 6.0 + 0.5 );
  
          if (nZoneEnv > 1 && nZoneEnv < 33)
          {
              int nEPSG = 28400 + nZoneEnv;
              passport.stMapDescription.pSpatRef = new OGRSpatialReference();
              OGRErr eErr = passport.stMapDescription.pSpatRef->importFromEPSG(nEPSG);
              SetVertCS(iVCS, passport);
              return eErr;
          }
          else
          {
              adfPrjParams[7] = nZoneEnv;
  
              if (adfPrjParams[5] == 0)//False Easting
              {
                  if (passport.stMapDescription.Env.MaxX < 500000)
                      adfPrjParams[5] = 500000;
                  else
                      adfPrjParams[5] = nZoneEnv * 1000000 + 500000;
              }
          }
      }
      else if (iEllips == 9 && iProjSys == 17) // WGS84 / UTM
      {
          double dfCenterLongEnv = passport.stMapDescription.stGeoCoords[1] + fabs(passport.stMapDescription.stGeoCoords[5] - passport.stMapDescription.stGeoCoords[1]) / 2;
          int nZoneEnv = (int)(30 + (dfCenterLongEnv + 3.0) / 6.0 + 0.5);
          bool bNorth = passport.stMapDescription.stGeoCoords[6] + (passport.stMapDescription.stGeoCoords[2] - passport.stMapDescription.stGeoCoords[6]) / 2 < 0;
          int nEPSG;
          if (bNorth)
          {
              nEPSG = 32600 + nZoneEnv;
          }
          else
          {
              nEPSG = 32700 + nZoneEnv;
          }
          passport.stMapDescription.pSpatRef = new OGRSpatialReference();
          OGRErr eErr = passport.stMapDescription.pSpatRef->importFromEPSG(nEPSG);
          SetVertCS(iVCS, passport);
          return eErr;
      }
     else if (iEllips == 45 && iProjSys == 35) //Mercator 3395 on sphere wgs84
      {
          passport.stMapDescription.pSpatRef = new OGRSpatialReference("PROJCS[\"WGS_1984_Web_Mercator\",GEOGCS[\"GCS_WGS_1984_Major_Auxiliary_Sphere\",DATUM[\"WGS_1984_Major_Auxiliary_Sphere\",SPHEROID[\"WGS_1984_Major_Auxiliary_Sphere\",6378137.0,0.0]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],PROJECTION[\"Mercator_1SP\"],PARAMETER[\"False_Easting\",0.0],PARAMETER[\"False_Northing\",0.0],PARAMETER[\"Central_Meridian\",0.0],PARAMETER[\"latitude_of_origin\",0.0],UNIT[\"Meter\",1.0]]");
          OGRErr eErr = OGRERR_NONE; //passport.stMapDescription.pSpatRef->importFromEPSG(3395);
          SetVertCS(iVCS, passport);
          return eErr;
      }
      else if (iEllips == 9 && iProjSys == 34) //Miller 54003 on sphere wgs84
      {
          passport.stMapDescription.pSpatRef = new OGRSpatialReference("PROJCS[\"World_Miller_Cylindrical\",GEOGCS[\"GCS_GLOBE\", DATUM[\"GLOBE\", SPHEROID[\"GLOBE\", 6367444.6571, 0.0]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Miller_Cylindrical\"],PARAMETER[\"False_Easting\",0],PARAMETER[\"False_Northing\",0],PARAMETER[\"Central_Meridian\",0],UNIT[\"Meter\",1],AUTHORITY[\"EPSG\",\"54003\"]]");
          OGRErr eErr = OGRERR_NONE; //passport.stMapDescription.pSpatRef->importFromEPSG(3395);
          //OGRErr eErr = passport.stMapDescription.pSpatRef->importFromEPSG(54003);
  
          SetVertCS(iVCS, passport);
          return eErr;
      }
  
      //TODO: Need to normalise more SRS:
      //PAN_PROJ_WAG1
      //PAN_PROJ_MERCAT
      //PAN_PROJ_PS
      //PAN_PROJ_POLYC
      //PAN_PROJ_EC
      //PAN_PROJ_LCC
      //PAN_PROJ_STEREO
      //PAN_PROJ_AE
      //PAN_PROJ_GNOMON
      //PAN_PROJ_MOLL
      //PAN_PROJ_LAEA
      //PAN_PROJ_EQC
      //PAN_PROJ_CEA
      //PAN_PROJ_IMWP
      //
  
      passport.stMapDescription.pSpatRef = new OGRSpatialReference();
      OGRErr eErr = passport.stMapDescription.pSpatRef->importFromPanorama(anData[2], anData[3], anData[0], adfPrjParams);
      SetVertCS(iVCS, passport);
      return eErr;
  }
  
  void OGRSXFDataSource::FillLayers()
  {
      CPLDebug("SXF","Create layers");
  
      //2. Read all records (only classify code and offset) and add this to correspondence layer
      GUInt32 nFID;
      int nObjectsRead = 0;
      size_t i;
      vsi_l_offset nOffset = 0, nOffsetSemantic;
      size_t nDeletedLayerIndex;
  
      //get record count
      GUInt32 nRecordCountMax = 0;
      if (oSXFPassport.version == 3)
      {
          VSIFSeekL(fpSXF, 288, SEEK_SET);
          nObjectsRead = VSIFReadL(&nRecordCountMax, 4, 1, fpSXF);
          nOffset = 300;
      }
      else if (oSXFPassport.version == 4)
      {
          VSIFSeekL(fpSXF, 440, SEEK_SET);
          nObjectsRead = VSIFReadL(&nRecordCountMax, 4, 1, fpSXF);
          nOffset = 452;
      }
      /* else nOffset and nObjectsRead will be 0 */
  
      if (nObjectsRead != 1)
      {
          CPLError(CE_Failure, CPLE_FileIO, "Get record count failed");
          return;
      }
  
      VSIFSeekL(fpSXF, nOffset, SEEK_SET);
  
      for (nFID = 0; nFID < nRecordCountMax; nFID++)
      {
          GInt32 buff[6];
          nObjectsRead = VSIFReadL(&buff, 24, 1, fpSXF);
  
          if (nObjectsRead != 1 || buff[0] != IDSXFOBJ)
          {
              CPLError(CE_Failure, CPLE_FileIO, "Read record %d failed", nFID);
              return;
          }
  
          bool bHasSemantic = CHECK_BIT(buff[5], 9);
          if (bHasSemantic) //check has attributes
          {
              //we have already 24 byte readed
              nOffsetSemantic = 8 + buff[2];
              VSIFSeekL(fpSXF, nOffsetSemantic, SEEK_CUR);
          }
  
          int nSemanticSize = buff[1] - 32 - buff[2];
          if( nSemanticSize < 0 )
          {
              CPLError(CE_Failure, CPLE_AppDefined, "Invalid value");
              break;
          }
          for (i = 0; i < nLayers; i++)
          {
              OGRSXFLayer* pOGRSXFLayer = (OGRSXFLayer*)papoLayers[i];
              if (pOGRSXFLayer && pOGRSXFLayer->AddRecord(nFID, buff[3], nOffset, bHasSemantic, nSemanticSize) == TRUE)
              {
                  break;
              }
          }
          nOffset += buff[1];
          VSIFSeekL(fpSXF, nOffset, SEEK_SET);
      }
  
      //3. delete empty layers
      for (i = 0; i < nLayers; i++)
      {
          OGRSXFLayer* pOGRSXFLayer = (OGRSXFLayer*)papoLayers[i];
          if (pOGRSXFLayer && pOGRSXFLayer->GetFeatureCount() == 0)
          {
              delete pOGRSXFLayer;
              nDeletedLayerIndex = i;
              while (nDeletedLayerIndex < nLayers - 1)
              {
                  papoLayers[nDeletedLayerIndex] = papoLayers[nDeletedLayerIndex + 1];
                  nDeletedLayerIndex++;
              }
              nLayers--;
              i--;
          }
          else if (pOGRSXFLayer)
              pOGRSXFLayer->ResetReading();
      }
  }
  
  OGRSXFLayer* OGRSXFDataSource::GetLayerById(GByte nID)
  {
      for (size_t i = 0; i < nLayers; i++)
      {
          OGRSXFLayer* pOGRSXFLayer = (OGRSXFLayer*)papoLayers[i];
          if (pOGRSXFLayer && pOGRSXFLayer->GetId() == nID)
          {
              return pOGRSXFLayer;
          }
      }
      return NULL;
  }
  
  void OGRSXFDataSource::CreateLayers()
  {
      //codes get from OSM.rsc http://gistoolkit.ru/download/classifiers/osm.zip
  
      //default layers set
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      OGRSXFLayer* pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 0, CPLString("SYSTEM"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      //default codes
      for (size_t i = 1000000001; i < 1000000015; i++)
      {
          pLayer->AddClassifyCode(i);
      }
      pLayer->AddClassifyCode(91000000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 1, CPLString("boundary"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(81110000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 2, CPLString("water"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(31410000);
      pLayer->AddClassifyCode(31120000);
      pLayer->AddClassifyCode(31710000);
      pLayer->AddClassifyCode(72310000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 3, CPLString("city"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(41100000);
      pLayer->AddClassifyCode(91100001);
      pLayer->AddClassifyCode(91100002);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 4, CPLString("poi"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(123);
      pLayer->AddClassifyCode(32410000);
      pLayer->AddClassifyCode(44200000);
      pLayer->AddClassifyCode(44200010);
      pLayer->AddClassifyCode(47140000);
      pLayer->AddClassifyCode(51121000);
      pLayer->AddClassifyCode(51130000);
      pLayer->AddClassifyCode(51410000);
      pLayer->AddClassifyCode(51410001);
      pLayer->AddClassifyCode(51420000);
      pLayer->AddClassifyCode(53110000);
      pLayer->AddClassifyCode(53311400);
      pLayer->AddClassifyCode(53421000);
      pLayer->AddClassifyCode(53510000);
      pLayer->AddClassifyCode(53612000);
      pLayer->AddClassifyCode(53612100);
      pLayer->AddClassifyCode(53612101);
      pLayer->AddClassifyCode(53612102);
      pLayer->AddClassifyCode(53612103);
      pLayer->AddClassifyCode(53612104);
      pLayer->AddClassifyCode(53612105);
      pLayer->AddClassifyCode(53612106);
      pLayer->AddClassifyCode(53612107);
      pLayer->AddClassifyCode(53612200);
      pLayer->AddClassifyCode(53612201);
      pLayer->AddClassifyCode(53612202);
      pLayer->AddClassifyCode(53612203);
      pLayer->AddClassifyCode(53612204);
      pLayer->AddClassifyCode(53612205);
      pLayer->AddClassifyCode(53612211);
      pLayer->AddClassifyCode(53612300);
      pLayer->AddClassifyCode(53612301);
      pLayer->AddClassifyCode(53612302);
      pLayer->AddClassifyCode(53612303);
      pLayer->AddClassifyCode(53612304);
      pLayer->AddClassifyCode(53612400);
      pLayer->AddClassifyCode(53612401);
      pLayer->AddClassifyCode(53612402);
      pLayer->AddClassifyCode(53612403);
      pLayer->AddClassifyCode(53612404);
      pLayer->AddClassifyCode(53623000);
      pLayer->AddClassifyCode(53623100);
      pLayer->AddClassifyCode(53623110);
      pLayer->AddClassifyCode(53623200);
      pLayer->AddClassifyCode(53623300);
      pLayer->AddClassifyCode(53623400);
      pLayer->AddClassifyCode(53623500);
      pLayer->AddClassifyCode(53623600);
      pLayer->AddClassifyCode(53624000);
      pLayer->AddClassifyCode(53624001);
      pLayer->AddClassifyCode(53624002);
      pLayer->AddClassifyCode(53624003);
      pLayer->AddClassifyCode(53624004);
      pLayer->AddClassifyCode(53624005);
      pLayer->AddClassifyCode(53630000);
      pLayer->AddClassifyCode(53631000);
      pLayer->AddClassifyCode(53632101);
      pLayer->AddClassifyCode(53632102);
      pLayer->AddClassifyCode(53632103);
      pLayer->AddClassifyCode(53632104);
      pLayer->AddClassifyCode(53632105);
      pLayer->AddClassifyCode(53632106);
      pLayer->AddClassifyCode(53633001);
      pLayer->AddClassifyCode(53633101);
      pLayer->AddClassifyCode(53633102);
      pLayer->AddClassifyCode(53633112);
      pLayer->AddClassifyCode(53633114);
      pLayer->AddClassifyCode(53635000);
      pLayer->AddClassifyCode(53640000);
      pLayer->AddClassifyCode(53641000);
      pLayer->AddClassifyCode(53642000);
      pLayer->AddClassifyCode(53643000);
      pLayer->AddClassifyCode(53644000);
      pLayer->AddClassifyCode(53646011);
      pLayer->AddClassifyCode(53646013);
      pLayer->AddClassifyCode(53646014);
      pLayer->AddClassifyCode(53650000);
      pLayer->AddClassifyCode(53650001);
      pLayer->AddClassifyCode(53650002);
      pLayer->AddClassifyCode(53650003);
      pLayer->AddClassifyCode(53650004);
      pLayer->AddClassifyCode(53650006);
      pLayer->AddClassifyCode(53660000);
      pLayer->AddClassifyCode(53661001);
      pLayer->AddClassifyCode(53661002);
      pLayer->AddClassifyCode(53661003);
      pLayer->AddClassifyCode(53661004);
      pLayer->AddClassifyCode(53661005);
      pLayer->AddClassifyCode(53661006);
      pLayer->AddClassifyCode(53661007);
      pLayer->AddClassifyCode(53661008);
      pLayer->AddClassifyCode(53661009);
      pLayer->AddClassifyCode(53661010);
      pLayer->AddClassifyCode(53661021);
      pLayer->AddClassifyCode(53661100);
      pLayer->AddClassifyCode(53662001);
      pLayer->AddClassifyCode(53662002);
      pLayer->AddClassifyCode(53662003);
      pLayer->AddClassifyCode(53662004);
      pLayer->AddClassifyCode(53672600);
      pLayer->AddClassifyCode(53673300);
      pLayer->AddClassifyCode(53700000);
      pLayer->AddClassifyCode(53710000);
      pLayer->AddClassifyCode(53720100);
      pLayer->AddClassifyCode(53720200);
      pLayer->AddClassifyCode(53720300);
      pLayer->AddClassifyCode(53720301);
      pLayer->AddClassifyCode(53720400);
      pLayer->AddClassifyCode(53720500);
      pLayer->AddClassifyCode(53720510);
      pLayer->AddClassifyCode(53720520);
      pLayer->AddClassifyCode(53720970);
      pLayer->AddClassifyCode(53890000);
  
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 5, CPLString("highway"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(10715);
      pLayer->AddClassifyCode(11118);
      pLayer->AddClassifyCode(2000253);
      pLayer->AddClassifyCode(2000727);
      pLayer->AddClassifyCode(51133200);
      pLayer->AddClassifyCode(51220000);
      pLayer->AddClassifyCode(61230000);
      pLayer->AddClassifyCode(61230000);
      pLayer->AddClassifyCode(62132000);
      pLayer->AddClassifyCode(62213100);
      pLayer->AddClassifyCode(62213101);
      pLayer->AddClassifyCode(62213102);
      pLayer->AddClassifyCode(62223000);
      pLayer->AddClassifyCode(62331000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 6, CPLString("railway"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(61111000);
      pLayer->AddClassifyCode(61121100);
      pLayer->AddClassifyCode(61121200);
      pLayer->AddClassifyCode(61122000);
      pLayer->AddClassifyCode(62131000);
  
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 7, CPLString("building"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(44100000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 8, CPLString("landuse"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(97);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 9, CPLString("vegetation"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(71111111);
      pLayer->AddClassifyCode(71325000);
      pLayer->AddClassifyCode(53890000);
      pLayer->AddClassifyCode(22700000);
      pLayer->AddClassifyCode(32282000);
      pLayer->AddClassifyCode(71211000);
      pLayer->AddClassifyCode(72120000);
      pLayer->AddClassifyCode(71314000);
      pLayer->AddClassifyCode(71312000);
  
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 10, CPLString("fire"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(96);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 11, CPLString("roaddesign"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(60000000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 13, CPLString("RoadStructure"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(62315000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      pLayer = new OGRSXFLayer(fpSXF, &hIOMutex, 14, CPLString("signature"), oSXFPassport.version, oSXFPassport.stMapDescription);
      papoLayers[nLayers] = pLayer;
      nLayers++;
  
      pLayer->AddClassifyCode(91100000);
      pLayer->AddClassifyCode(91200000);
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, 255, CPLString("Not_Classified"), oSXFPassport.version, oSXFPassport.stMapDescription);
      nLayers++;
  
  }
  
  void OGRSXFDataSource::CreateLayers(VSILFILE* fpRSC)
  {
  
      RSCHeader stRSCFileHeader;
      int nFileHeaderSize = sizeof(stRSCFileHeader);
      int nObjectsRead = VSIFReadL(&stRSCFileHeader, nFileHeaderSize, 1, fpRSC);
  
      if (nObjectsRead != 1)
      {
          CPLError(CE_Warning, CPLE_None, "RSC head read failed");
          return;
      }
  
      GByte szLayersID[4];
      struct _layer{
          GUInt32 nLength;
          char szName[32];
          char szShortName[16];
          GByte nNo;
          GByte nPos;
          GUInt16 nSematicCount;
      };
  
      GUInt32 i;
      size_t nLayerStructSize = sizeof(_layer);
  
      VSIFSeekL(fpRSC, stRSCFileHeader.Layers.nOffset - sizeof(szLayersID), SEEK_SET);
      VSIFReadL(&szLayersID, sizeof(szLayersID), 1, fpRSC);
      vsi_l_offset nOffset = stRSCFileHeader.Layers.nOffset;
      _layer LAYER;
  
      for (i = 0; i < stRSCFileHeader.Layers.nRecordCount; ++i)
      {
          VSIFReadL(&LAYER, nLayerStructSize, 1, fpRSC);
  
          papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
          bool bLayerFullName = CSLTestBoolean(CPLGetConfigOption("SXF_LAYER_FULLNAME", "NO"));
  
          char* pszRecoded;
          if (bLayerFullName)
          {
              if (stRSCFileHeader.nFontEnc == 125)
                  pszRecoded = CPLRecode(LAYER.szName, "KOI8-R", CPL_ENC_UTF8);
              else if (stRSCFileHeader.nFontEnc == 126)
                  pszRecoded = CPLRecode(LAYER.szName, "CP1251", CPL_ENC_UTF8);
              else
                  pszRecoded = CPLStrdup(LAYER.szName);
  
              papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, LAYER.nNo, CPLString(pszRecoded), oSXFPassport.version, oSXFPassport.stMapDescription);
          }
          else
          {
              if (stRSCFileHeader.nFontEnc == 125)
                  pszRecoded = CPLRecode(LAYER.szShortName, "KOI8-R", CPL_ENC_UTF8);
              else if (stRSCFileHeader.nFontEnc == 126)
                  pszRecoded = CPLRecode(LAYER.szShortName, "CP1251", CPL_ENC_UTF8);
              else
                  pszRecoded = CPLStrdup(LAYER.szShortName);
  
              papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, LAYER.nNo, CPLString(pszRecoded), oSXFPassport.version, oSXFPassport.stMapDescription);
          }
          CPLFree(pszRecoded);
          nLayers++;
  
          nOffset += LAYER.nLength;
          VSIFSeekL(fpRSC, nOffset, SEEK_SET);
      }
  
      papoLayers = (OGRLayer**)CPLRealloc(papoLayers, sizeof(OGRLayer*)* (nLayers + 1));
      papoLayers[nLayers] = new OGRSXFLayer(fpSXF, &hIOMutex, 255, CPLString("Not_Classified"), oSXFPassport.version, oSXFPassport.stMapDescription);
      nLayers++;
  
  
      char szObjectsID[4];
      struct _object{
          unsigned nLength;
          unsigned nClassifyCode;
          unsigned nObjectNumber;
          unsigned nObjectCode;
          char szShortName[32];
          char szName[32];
          char szGeomType;
          char szLayernNo;
          char szUnimportantSeg[14];
      };
  
      VSIFSeekL(fpRSC, stRSCFileHeader.Objects.nOffset - sizeof(szObjectsID), SEEK_SET);
      VSIFReadL(&szObjectsID, sizeof(szObjectsID), 1, fpRSC);
      nOffset = stRSCFileHeader.Objects.nOffset;
      _object OBJECT;
  
      for (unsigned i = 0; i < stRSCFileHeader.Objects.nRecordCount; ++i)
      {
          VSIFReadL(&OBJECT, sizeof(_object), 1, fpRSC);
  
          OGRSXFLayer* pLayer = GetLayerById(OBJECT.szLayernNo);
          if (NULL != pLayer)
          {
              char* pszRecoded;
              if (stRSCFileHeader.nFontEnc == 125)
                  pszRecoded = CPLRecode(OBJECT.szName, "KOI8-R", CPL_ENC_UTF8);
              else if (stRSCFileHeader.nFontEnc == 126)
                  pszRecoded = CPLRecode(OBJECT.szName, "CP1251", CPL_ENC_UTF8);
              else
                  pszRecoded = CPLStrdup(OBJECT.szName); //already in  CPL_ENC_UTF8
              pLayer->AddClassifyCode(OBJECT.nClassifyCode, pszRecoded);
              //printf("%d;%s\n", OBJECT.nClassifyCode, OBJECT.szName);
              CPLFree(pszRecoded);
          }
  
          nOffset += OBJECT.nLength;
          VSIFSeekL(fpRSC, nOffset, SEEK_SET);
      }
  }
  