LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/cad - gdalcaddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 81 237 34.2 %
Date: 2025-09-10 17:48:50 Functions: 8 18 44.4 %

          Line data    Source code
       1             : /*******************************************************************************
       2             :  *  Project: OGR CAD Driver
       3             :  *  Purpose: Implements driver based on libopencad
       4             :  *  Author: Alexandr Borzykh, mush3d at gmail.com
       5             :  *  Author: Dmitry Baryshnikov, polimax@mail.ru
       6             :  *  Language: C++
       7             :  *******************************************************************************
       8             :  *  The MIT License (MIT)
       9             :  *
      10             :  *  Copyright (c) 2016 Alexandr Borzykh
      11             :  *  Copyright (c) 2016, NextGIS <info@nextgis.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  *******************************************************************************/
      15             : #include "cpl_conv.h"
      16             : #include "gdal_pam.h"
      17             : #include "gdal_proxy.h"
      18             : #include "ogr_cad.h"
      19             : #include "vsilfileio.h"
      20             : 
      21             : class CADWrapperRasterBand : public GDALProxyRasterBand
      22             : {
      23             :     GDALRasterBand *poBaseBand;
      24             : 
      25             :   protected:
      26             :     virtual GDALRasterBand *
      27             :     RefUnderlyingRasterBand(bool /* bForceOpen */) const override;
      28             : 
      29             :   public:
      30           0 :     explicit CADWrapperRasterBand(GDALRasterBand *poBaseBandIn)
      31           0 :         : poBaseBand(poBaseBandIn)
      32             :     {
      33           0 :         eDataType = poBaseBand->GetRasterDataType();
      34           0 :         poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
      35           0 :     }
      36             : };
      37             : 
      38             : GDALRasterBand *
      39           0 : CADWrapperRasterBand::RefUnderlyingRasterBand(bool /* bForceOpen */) const
      40             : {
      41           0 :     return poBaseBand;
      42             : }
      43             : 
      44           9 : GDALCADDataset::GDALCADDataset()
      45             :     : poCADFile(nullptr), papoLayers(nullptr), nLayers(0), poRasterDS(nullptr),
      46           9 :       poSpatialReference(nullptr)
      47             : {
      48           9 : }
      49             : 
      50          18 : GDALCADDataset::~GDALCADDataset()
      51             : {
      52           9 :     if (poRasterDS != nullptr)
      53             :     {
      54           0 :         GDALClose(poRasterDS);
      55           0 :         poRasterDS = nullptr;
      56             :     }
      57             : 
      58          19 :     for (int i = 0; i < nLayers; i++)
      59          10 :         delete papoLayers[i];
      60           9 :     CPLFree(papoLayers);
      61             : 
      62           9 :     if (poSpatialReference)
      63           8 :         poSpatialReference->Release();
      64             : 
      65           9 :     if (poCADFile)
      66           8 :         delete poCADFile;
      67          18 : }
      68             : 
      69           0 : void GDALCADDataset::FillTransform(CADImage *pImage, double dfUnits)
      70             : {
      71           0 :     CADImage::ResolutionUnit eResUnits = pImage->getResolutionUnits();
      72           0 :     double dfMultiply = 1.0;
      73             : 
      74           0 :     switch (eResUnits)  // 0 == none, 2 == centimeters, 5 == inches;
      75             :     {
      76           0 :         case CADImage::ResolutionUnit::CENTIMETER:
      77           0 :             dfMultiply = 100.0 / dfUnits;  // Meters to linear units
      78           0 :             break;
      79           0 :         case CADImage::ResolutionUnit::INCH:
      80           0 :             dfMultiply = 0.0254 / dfUnits;
      81           0 :             break;
      82           0 :         case CADImage::ResolutionUnit::NONE:
      83             :         default:
      84           0 :             dfMultiply = 1.0;
      85             :     }
      86             : 
      87           0 :     CADVector oSizePt = pImage->getImageSizeInPx();
      88           0 :     CADVector oInsPt = pImage->getVertInsertionPoint();
      89           0 :     CADVector oSizeUnitsPt = pImage->getPixelSizeInACADUnits();
      90           0 :     m_gt[0] = oInsPt.getX();
      91           0 :     m_gt[3] = oInsPt.getY() + oSizePt.getY() * oSizeUnitsPt.getX() * dfMultiply;
      92           0 :     m_gt[2] = 0.0;
      93           0 :     m_gt[4] = 0.0;
      94             : 
      95           0 :     m_gt[1] = oSizeUnitsPt.getX() * dfMultiply;
      96           0 :     m_gt[5] = -oSizeUnitsPt.getY() * dfMultiply;
      97           0 : }
      98             : 
      99           9 : int GDALCADDataset::Open(GDALOpenInfo *poOpenInfo, CADFileIO *pFileIO,
     100             :                          long nSubRasterLayer, long nSubRasterFID)
     101             : {
     102           9 :     osCADFilename = pFileIO->GetFilePath();
     103           9 :     SetDescription(poOpenInfo->pszFilename);
     104             : 
     105             :     const char *papszReadOptions =
     106           9 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "MODE", "READ_FAST");
     107          18 :     const char *papszReadUnsupportedGeoms = CSLFetchNameValueDef(
     108           9 :         poOpenInfo->papszOpenOptions, "ADD_UNSUPPORTED_GEOMETRIES_DATA", "NO");
     109             : 
     110           9 :     enum CADFile::OpenOptions openOpts = CADFile::READ_FAST;
     111           9 :     bool bReadUnsupportedGeometries = false;
     112           9 :     if (EQUAL(papszReadOptions, "READ_ALL"))
     113             :     {
     114           0 :         openOpts = CADFile::READ_ALL;
     115             :     }
     116           9 :     else if (EQUAL(papszReadOptions, "READ_FASTEST"))
     117             :     {
     118           0 :         openOpts = CADFile::READ_FASTEST;
     119             :     }
     120             : 
     121           9 :     if (EQUAL(papszReadUnsupportedGeoms, "YES"))
     122             :     {
     123           0 :         bReadUnsupportedGeometries = true;
     124             :     }
     125             : 
     126           9 :     poCADFile = OpenCADFile(pFileIO, openOpts, bReadUnsupportedGeometries);
     127             : 
     128           9 :     if (GetLastErrorCode() == CADErrorCodes::UNSUPPORTED_VERSION)
     129             :     {
     130           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     131             :                  "libopencad %s does not support this version of CAD file.\n"
     132             :                  "Supported formats are:\n%s",
     133             :                  GetVersionString(), GetCADFormats());
     134           1 :         return FALSE;
     135             :     }
     136             : 
     137           8 :     if (GetLastErrorCode() != CADErrorCodes::SUCCESS)
     138             :     {
     139           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     140             :                  "libopencad %s does not support this version of CAD "
     141             :                  "file.\nSupported formats: %s",
     142             :                  GetVersionString(), GetCADFormats());
     143           0 :         return FALSE;
     144             :     }
     145             : 
     146           8 :     const OGRSpatialReference *poSpatialRef = GetSpatialRef();
     147           8 :     int nRasters = 1;
     148             : 
     149           8 :     if (nSubRasterLayer != -1 && nSubRasterFID != -1)
     150             :     {
     151             :         // Indicates that subdataset from CAD layer number nSubRasterLayer and
     152             :         // FID nSubRasterFID is request
     153           0 :         nRasters = 2;
     154             :     }
     155             :     else
     156             :     {
     157             :         // Fill metadata
     158           8 :         const CADHeader &header = poCADFile->getHeader();
     159         576 :         for (size_t i = 0; i < header.getSize(); ++i)
     160             :         {
     161         568 :             short nCode = header.getCode(static_cast<int>(i));
     162        1136 :             const CADVariant &oVal = header.getValue(nCode);
     163         568 :             GDALDataset::SetMetadataItem(header.getValueName(nCode),
     164         568 :                                          oVal.getString().c_str());
     165             :         }
     166             : 
     167             :         // Reading content of .prj file, or extracting it from CAD if not
     168             :         // present
     169           8 :         nLayers = 0;
     170             :         // FIXME: We allocate extra memory, do we need more strict policy here?
     171           8 :         papoLayers = static_cast<OGRCADLayer **>(
     172           8 :             CPLMalloc(sizeof(OGRCADLayer *) * poCADFile->GetLayersCount()));
     173             : 
     174           8 :         int nEncoding = GetCadEncoding();
     175          18 :         for (size_t i = 0; i < poCADFile->GetLayersCount(); ++i)
     176             :         {
     177          10 :             CADLayer &oLayer = poCADFile->GetLayer(i);
     178          20 :             if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR &&
     179          10 :                 oLayer.getGeometryCount() > 0)
     180             :             {
     181             :                 OGRSpatialReference *poSRS =
     182          10 :                     poSpatialRef ? poSpatialRef->Clone() : nullptr;
     183          10 :                 papoLayers[nLayers++] =
     184          10 :                     new OGRCADLayer(this, oLayer, poSRS, nEncoding);
     185          10 :                 if (poSRS)
     186          10 :                     poSRS->Release();
     187             :             }
     188             : 
     189          10 :             if (poOpenInfo->nOpenFlags & GDAL_OF_RASTER)
     190             :             {
     191          10 :                 for (size_t j = 0; j < oLayer.getImageCount(); ++j)
     192             :                 {
     193           0 :                     nSubRasterLayer = static_cast<long>(i);
     194           0 :                     nSubRasterFID = static_cast<long>(j);
     195           0 :                     GDALDataset::SetMetadataItem(
     196             :                         CPLSPrintf("SUBDATASET_%d_NAME", nRasters),
     197             :                         CPLSPrintf("CAD:%s:%ld:%ld", osCADFilename.c_str(),
     198             :                                    nSubRasterLayer, nSubRasterFID),
     199             :                         "SUBDATASETS");
     200           0 :                     GDALDataset::SetMetadataItem(
     201             :                         CPLSPrintf("SUBDATASET_%d_DESC", nRasters),
     202           0 :                         CPLSPrintf("%s - %ld", oLayer.getName().c_str(),
     203             :                                    nSubRasterFID),
     204             :                         "SUBDATASETS");
     205           0 :                     nRasters++;
     206             :                 }
     207             :             }
     208             :         }
     209             :         // If nRasters == 2 we have the only one raster in CAD file
     210             :     }
     211             : 
     212             :     // The only one raster layer in dataset is present or subdataset is request
     213           8 :     if (nRasters == 2)
     214             :     {
     215           0 :         CADLayer &oLayer = poCADFile->GetLayer(nSubRasterLayer);
     216           0 :         CADImage *pImage = oLayer.getImage(nSubRasterFID);
     217           0 :         if (pImage)
     218             :         {
     219             :             // TODO: Add support clipping region in neatline
     220           0 :             CPLString osImgFilename = pImage->getFilePath();
     221           0 :             CPLString osImgPath = CPLGetPathSafe(osImgFilename);
     222           0 :             if (osImgPath.empty())
     223             :             {
     224             :                 osImgFilename =
     225           0 :                     CPLFormFilenameSafe(CPLGetPathSafe(osCADFilename).c_str(),
     226           0 :                                         osImgFilename, nullptr);
     227             :             }
     228             : 
     229           0 :             if (!CPLCheckForFile(const_cast<char *>(osImgFilename.c_str()),
     230             :                                  nullptr))
     231           0 :                 return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
     232             : 
     233           0 :             poRasterDS = GDALDataset::FromHandle(
     234             :                 GDALOpen(osImgFilename, poOpenInfo->eAccess));
     235           0 :             if (poRasterDS == nullptr)
     236             :             {
     237           0 :                 delete pImage;
     238           0 :                 return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
     239             :             }
     240           0 :             if (poRasterDS->GetRasterCount() == 0)
     241             :             {
     242           0 :                 delete pImage;
     243           0 :                 GDALClose(poRasterDS);
     244           0 :                 return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
     245             :             }
     246             : 
     247           0 :             if (poRasterDS->GetGeoTransform(m_gt) != CE_None)
     248             :             {
     249             :                 // The external world file have priority
     250           0 :                 double dfUnits = 1.0;
     251           0 :                 if (nullptr != poSpatialRef)
     252           0 :                     dfUnits = poSpatialRef->GetLinearUnits();
     253           0 :                 FillTransform(pImage, dfUnits);
     254             :             }
     255           0 :             delete pImage;
     256             : 
     257           0 :             nRasterXSize = poRasterDS->GetRasterXSize();
     258           0 :             nRasterYSize = poRasterDS->GetRasterYSize();
     259           0 :             if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
     260             :             {
     261           0 :                 GDALClose(poRasterDS);
     262           0 :                 return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
     263             :             }
     264             : 
     265           0 :             for (int iBand = 1; iBand <= poRasterDS->GetRasterCount(); iBand++)
     266           0 :                 SetBand(iBand, new CADWrapperRasterBand(
     267           0 :                                    poRasterDS->GetRasterBand(iBand)));
     268             : 
     269           0 :             char **papszDomainList = poRasterDS->GetMetadataDomainList();
     270           0 :             while (papszDomainList)
     271             :             {
     272           0 :                 char **papszMetadata = GetMetadata(*papszDomainList);
     273             :                 char **papszRasterMetadata =
     274           0 :                     poRasterDS->GetMetadata(*papszDomainList);
     275           0 :                 if (nullptr == papszMetadata)
     276           0 :                     SetMetadata(papszRasterMetadata, *papszDomainList);
     277             :                 else
     278             :                 {
     279           0 :                     char **papszMD = CSLMerge(CSLDuplicate(papszMetadata),
     280             :                                               papszRasterMetadata);
     281           0 :                     SetMetadata(papszMD, *papszDomainList);
     282           0 :                     CSLDestroy(papszMD);
     283             :                 }
     284           0 :                 papszDomainList++;
     285             :             }
     286             :         }
     287             :     }
     288             : 
     289           8 :     return TRUE;
     290             : }
     291             : 
     292          10 : const OGRLayer *GDALCADDataset::GetLayer(int iLayer) const
     293             : {
     294          10 :     if (iLayer < 0 || iLayer >= nLayers)
     295           0 :         return nullptr;
     296             :     else
     297          10 :         return papoLayers[iLayer];
     298             : }
     299             : 
     300           0 : int GDALCADDataset::TestCapability(const char *pszCap) const
     301             : {
     302           0 :     if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer))
     303           0 :         return FALSE;
     304           0 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
     305           0 :         return TRUE;
     306           0 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     307           0 :         return TRUE;
     308           0 :     else if (EQUAL(pszCap, ODsCZGeometries))
     309           0 :         return TRUE;
     310           0 :     return FALSE;
     311             : }
     312             : 
     313           0 : char **GDALCADDataset::GetFileList()
     314             : {
     315           0 :     char **papszFileList = GDALDataset::GetFileList();
     316             : 
     317             :     /* duplicated papszFileList = CSLAddString( papszFileList, osCADFilename
     318             :      * );*/
     319           0 :     const std::string osPRJFilename = GetPrjFilePath();
     320           0 :     if (!osPRJFilename.empty())
     321           0 :         papszFileList = CSLAddString(papszFileList, osPRJFilename.c_str());
     322             : 
     323           0 :     for (size_t i = 0; i < poCADFile->GetLayersCount(); ++i)
     324             :     {
     325           0 :         CADLayer &oLayer = poCADFile->GetLayer(i);
     326           0 :         for (size_t j = 0; j < oLayer.getImageCount(); ++j)
     327             :         {
     328           0 :             CADImage *pImage = oLayer.getImage(j);
     329           0 :             if (pImage)
     330             :             {
     331           0 :                 CPLString osImgFilename = pImage->getFilePath();
     332           0 :                 if (CPLCheckForFile(const_cast<char *>(osImgFilename.c_str()),
     333           0 :                                     nullptr) == TRUE)
     334           0 :                     papszFileList = CSLAddString(papszFileList, osImgFilename);
     335             :             }
     336             :         }
     337             :     }
     338             : 
     339           0 :     if (nullptr != poRasterDS)
     340             :     {
     341           0 :         papszFileList = CSLMerge(papszFileList, poRasterDS->GetFileList());
     342             :     }
     343           0 :     return papszFileList;
     344             : }
     345             : 
     346           8 : int GDALCADDataset::GetCadEncoding() const
     347             : {
     348           8 :     if (poCADFile == nullptr)
     349           0 :         return 0;
     350           8 :     const CADHeader &header = poCADFile->getHeader();
     351             :     return static_cast<int>(
     352           8 :         header.getValue(CADHeader::DWGCODEPAGE, 0).getDecimal());
     353             : }
     354             : 
     355           8 : const OGRSpatialReference *GDALCADDataset::GetSpatialRef() const
     356             : {
     357           8 :     if (poSpatialReference)
     358           0 :         return poSpatialReference;
     359             : 
     360           8 :     if (poCADFile != nullptr)
     361             :     {
     362          16 :         CPLString sESRISpatRef;
     363           8 :         poSpatialReference = new OGRSpatialReference();
     364           8 :         poSpatialReference->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     365             : 
     366          16 :         CADDictionary oNOD = poCADFile->GetNOD();
     367          24 :         CPLString sESRISpatRefData = oNOD.getRecordByName("ESRI_PRJ");
     368           8 :         if (!sESRISpatRefData.empty())
     369             :         {
     370             :             sESRISpatRef =
     371           0 :                 sESRISpatRefData.substr(sESRISpatRefData.find("GEO"));
     372             :         }
     373             : 
     374           8 :         if (!sESRISpatRef.empty())
     375             :         {
     376           0 :             char **papszPRJData = nullptr;
     377           0 :             papszPRJData = CSLAddString(papszPRJData, sESRISpatRef);
     378           0 :             if (poSpatialReference->importFromESRI(papszPRJData) != OGRERR_NONE)
     379             :             {
     380           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     381             :                          "Failed to parse PRJ section, ignoring.");
     382           0 :                 delete poSpatialReference;
     383           0 :                 poSpatialReference = nullptr;
     384             :             }
     385             : 
     386           0 :             CSLDestroy(papszPRJData);
     387             :         }
     388             :         else
     389             :         {
     390          16 :             const std::string osPRJFilename = GetPrjFilePath();
     391           8 :             if (!osPRJFilename.empty())  // check if path exists
     392             :             {
     393           0 :                 CPLPushErrorHandler(CPLQuietErrorHandler);
     394           0 :                 char **papszPRJData = CSLLoad(osPRJFilename.c_str());
     395           0 :                 CPLPopErrorHandler();
     396             : 
     397           0 :                 if (poSpatialReference->importFromESRI(papszPRJData) !=
     398             :                     OGRERR_NONE)
     399             :                 {
     400           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     401             :                              "Failed to parse PRJ file, ignoring.");
     402           0 :                     delete poSpatialReference;
     403           0 :                     poSpatialReference = nullptr;
     404             :                 }
     405             : 
     406           0 :                 if (papszPRJData)
     407           0 :                     CSLDestroy(papszPRJData);
     408             :             }
     409             :         }
     410             :     }
     411             : 
     412           8 :     return poSpatialReference;
     413             : }
     414             : 
     415           8 : const std::string GDALCADDataset::GetPrjFilePath() const
     416             : {
     417          16 :     std::string osPRJFilename = CPLResetExtensionSafe(osCADFilename, "prj");
     418           8 :     if (CPLCheckForFile(osPRJFilename.data(), nullptr) == TRUE)
     419           0 :         return osPRJFilename;
     420             : 
     421           8 :     osPRJFilename = CPLResetExtensionSafe(osCADFilename, "PRJ");
     422           8 :     if (CPLCheckForFile(osPRJFilename.data(), nullptr) == TRUE)
     423           0 :         return osPRJFilename;
     424             : 
     425           8 :     return std::string();
     426             : }
     427             : 
     428           0 : CPLErr GDALCADDataset::GetGeoTransform(GDALGeoTransform &gt) const
     429             : {
     430           0 :     gt = m_gt;
     431           0 :     return CE_None;
     432             : }
     433             : 
     434           0 : int GDALCADDataset::GetGCPCount()
     435             : {
     436           0 :     if (nullptr == poRasterDS)
     437           0 :         return 0;
     438           0 :     return poRasterDS->GetGCPCount();
     439             : }
     440             : 
     441           0 : const OGRSpatialReference *GDALCADDataset::GetGCPSpatialRef() const
     442             : {
     443           0 :     if (nullptr == poRasterDS)
     444           0 :         return nullptr;
     445           0 :     return poRasterDS->GetGCPSpatialRef();
     446             : }
     447             : 
     448           0 : const GDAL_GCP *GDALCADDataset::GetGCPs()
     449             : {
     450           0 :     if (nullptr == poRasterDS)
     451           0 :         return nullptr;
     452           0 :     return poRasterDS->GetGCPs();
     453             : }
     454             : 
     455           0 : int GDALCADDataset::CloseDependentDatasets()
     456             : {
     457           0 :     int bRet = GDALDataset::CloseDependentDatasets();
     458           0 :     if (poRasterDS != nullptr)
     459             :     {
     460           0 :         GDALClose(poRasterDS);
     461           0 :         poRasterDS = nullptr;
     462           0 :         bRet = TRUE;
     463             :     }
     464           0 :     return bRet;
     465             : }

Generated by: LCOV version 1.14