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 : }
|