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

Generated by: LCOV version 1.14