LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/cad - gdalcaddataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 87 246 35.4 %
Date: 2024-11-21 22:18:42 Functions: 8 20 40.0 %

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

Generated by: LCOV version 1.14