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 = CPLGetPathSafe(osImgFilename);
230 0 : if (osImgPath.empty())
231 : {
232 : osImgFilename =
233 0 : CPLFormFilenameSafe(CPLGetPathSafe(osCADFilename).c_str(),
234 0 : osImgFilename, nullptr);
235 : }
236 :
237 0 : if (!CPLCheckForFile(const_cast<char *>(osImgFilename.c_str()),
238 : nullptr))
239 0 : return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
240 :
241 0 : poRasterDS = GDALDataset::FromHandle(
242 : GDALOpen(osImgFilename, poOpenInfo->eAccess));
243 0 : if (poRasterDS == nullptr)
244 : {
245 0 : delete pImage;
246 0 : return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
247 : }
248 0 : if (poRasterDS->GetRasterCount() == 0)
249 : {
250 0 : delete pImage;
251 0 : GDALClose(poRasterDS);
252 0 : return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
253 : }
254 :
255 0 : if (poRasterDS->GetGeoTransform(adfGeoTransform) != CE_None)
256 : {
257 : // The external world file have priority
258 0 : double dfUnits = 1.0;
259 0 : if (nullptr != poSpatialRef)
260 0 : dfUnits = poSpatialRef->GetLinearUnits();
261 0 : FillTransform(pImage, dfUnits);
262 : }
263 0 : delete pImage;
264 :
265 0 : nRasterXSize = poRasterDS->GetRasterXSize();
266 0 : nRasterYSize = poRasterDS->GetRasterYSize();
267 0 : if (!GDALCheckDatasetDimensions(nRasterXSize, nRasterYSize))
268 : {
269 0 : GDALClose(poRasterDS);
270 0 : return poOpenInfo->nOpenFlags & GDAL_OF_VECTOR;
271 : }
272 :
273 0 : for (int iBand = 1; iBand <= poRasterDS->GetRasterCount(); iBand++)
274 0 : SetBand(iBand, new CADWrapperRasterBand(
275 0 : poRasterDS->GetRasterBand(iBand)));
276 :
277 0 : char **papszDomainList = poRasterDS->GetMetadataDomainList();
278 0 : while (papszDomainList)
279 : {
280 0 : char **papszMetadata = GetMetadata(*papszDomainList);
281 : char **papszRasterMetadata =
282 0 : poRasterDS->GetMetadata(*papszDomainList);
283 0 : if (nullptr == papszMetadata)
284 0 : SetMetadata(papszRasterMetadata, *papszDomainList);
285 : else
286 : {
287 0 : char **papszMD = CSLMerge(CSLDuplicate(papszMetadata),
288 : papszRasterMetadata);
289 0 : SetMetadata(papszMD, *papszDomainList);
290 0 : CSLDestroy(papszMD);
291 : }
292 0 : papszDomainList++;
293 : }
294 : }
295 : }
296 :
297 8 : return TRUE;
298 : }
299 :
300 10 : OGRLayer *GDALCADDataset::GetLayer(int iLayer)
301 : {
302 10 : if (iLayer < 0 || iLayer >= nLayers)
303 0 : return nullptr;
304 : else
305 10 : return papoLayers[iLayer];
306 : }
307 :
308 0 : int GDALCADDataset::TestCapability(const char *pszCap)
309 : {
310 0 : if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer))
311 0 : return FALSE;
312 0 : else if (EQUAL(pszCap, ODsCCurveGeometries))
313 0 : return TRUE;
314 0 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
315 0 : return TRUE;
316 0 : else if (EQUAL(pszCap, ODsCZGeometries))
317 0 : return TRUE;
318 0 : return FALSE;
319 : }
320 :
321 0 : char **GDALCADDataset::GetFileList()
322 : {
323 0 : char **papszFileList = GDALDataset::GetFileList();
324 :
325 : /* duplicated papszFileList = CSLAddString( papszFileList, osCADFilename
326 : * );*/
327 0 : const std::string osPRJFilename = GetPrjFilePath();
328 0 : if (!osPRJFilename.empty())
329 0 : papszFileList = CSLAddString(papszFileList, osPRJFilename.c_str());
330 :
331 0 : for (size_t i = 0; i < poCADFile->GetLayersCount(); ++i)
332 : {
333 0 : CADLayer &oLayer = poCADFile->GetLayer(i);
334 0 : for (size_t j = 0; j < oLayer.getImageCount(); ++j)
335 : {
336 0 : CADImage *pImage = oLayer.getImage(j);
337 0 : if (pImage)
338 : {
339 0 : CPLString osImgFilename = pImage->getFilePath();
340 0 : if (CPLCheckForFile(const_cast<char *>(osImgFilename.c_str()),
341 0 : nullptr) == TRUE)
342 0 : papszFileList = CSLAddString(papszFileList, osImgFilename);
343 : }
344 : }
345 : }
346 :
347 0 : if (nullptr != poRasterDS)
348 : {
349 0 : papszFileList = CSLMerge(papszFileList, poRasterDS->GetFileList());
350 : }
351 0 : return papszFileList;
352 : }
353 :
354 8 : int GDALCADDataset::GetCadEncoding() const
355 : {
356 8 : if (poCADFile == nullptr)
357 0 : return 0;
358 8 : const CADHeader &header = poCADFile->getHeader();
359 : return static_cast<int>(
360 8 : header.getValue(CADHeader::DWGCODEPAGE, 0).getDecimal());
361 : }
362 :
363 8 : const OGRSpatialReference *GDALCADDataset::GetSpatialRef() const
364 : {
365 8 : if (poSpatialReference)
366 0 : return poSpatialReference;
367 :
368 8 : if (poCADFile != nullptr)
369 : {
370 16 : CPLString sESRISpatRef;
371 8 : poSpatialReference = new OGRSpatialReference();
372 8 : poSpatialReference->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
373 :
374 16 : CADDictionary oNOD = poCADFile->GetNOD();
375 24 : CPLString sESRISpatRefData = oNOD.getRecordByName("ESRI_PRJ");
376 8 : if (!sESRISpatRefData.empty())
377 : {
378 : sESRISpatRef =
379 0 : sESRISpatRefData.substr(sESRISpatRefData.find("GEO"));
380 : }
381 :
382 8 : if (!sESRISpatRef.empty())
383 : {
384 0 : char **papszPRJData = nullptr;
385 0 : papszPRJData = CSLAddString(papszPRJData, sESRISpatRef);
386 0 : if (poSpatialReference->importFromESRI(papszPRJData) != OGRERR_NONE)
387 : {
388 0 : CPLError(CE_Warning, CPLE_AppDefined,
389 : "Failed to parse PRJ section, ignoring.");
390 0 : delete poSpatialReference;
391 0 : poSpatialReference = nullptr;
392 : }
393 :
394 0 : CSLDestroy(papszPRJData);
395 : }
396 : else
397 : {
398 16 : const std::string osPRJFilename = GetPrjFilePath();
399 8 : if (!osPRJFilename.empty()) // check if path exists
400 : {
401 0 : CPLPushErrorHandler(CPLQuietErrorHandler);
402 0 : char **papszPRJData = CSLLoad(osPRJFilename.c_str());
403 0 : CPLPopErrorHandler();
404 :
405 0 : if (poSpatialReference->importFromESRI(papszPRJData) !=
406 : OGRERR_NONE)
407 : {
408 0 : CPLError(CE_Warning, CPLE_AppDefined,
409 : "Failed to parse PRJ file, ignoring.");
410 0 : delete poSpatialReference;
411 0 : poSpatialReference = nullptr;
412 : }
413 :
414 0 : if (papszPRJData)
415 0 : CSLDestroy(papszPRJData);
416 : }
417 : }
418 : }
419 :
420 8 : return poSpatialReference;
421 : }
422 :
423 8 : const std::string GDALCADDataset::GetPrjFilePath() const
424 : {
425 16 : std::string osPRJFilename = CPLResetExtensionSafe(osCADFilename, "prj");
426 8 : if (CPLCheckForFile(osPRJFilename.data(), nullptr) == TRUE)
427 0 : return osPRJFilename;
428 :
429 8 : osPRJFilename = CPLResetExtensionSafe(osCADFilename, "PRJ");
430 8 : if (CPLCheckForFile(osPRJFilename.data(), nullptr) == TRUE)
431 0 : return osPRJFilename;
432 :
433 8 : return std::string();
434 : }
435 :
436 0 : CPLErr GDALCADDataset::GetGeoTransform(double *padfGeoTransform)
437 : {
438 0 : memcpy(padfGeoTransform, adfGeoTransform, sizeof(double) * 6);
439 0 : return CE_None;
440 : }
441 :
442 0 : int GDALCADDataset::GetGCPCount()
443 : {
444 0 : if (nullptr == poRasterDS)
445 0 : return 0;
446 0 : return poRasterDS->GetGCPCount();
447 : }
448 :
449 0 : const OGRSpatialReference *GDALCADDataset::GetGCPSpatialRef() const
450 : {
451 0 : if (nullptr == poRasterDS)
452 0 : return nullptr;
453 0 : return poRasterDS->GetGCPSpatialRef();
454 : }
455 :
456 0 : const GDAL_GCP *GDALCADDataset::GetGCPs()
457 : {
458 0 : if (nullptr == poRasterDS)
459 0 : return nullptr;
460 0 : return poRasterDS->GetGCPs();
461 : }
462 :
463 0 : int GDALCADDataset::CloseDependentDatasets()
464 : {
465 0 : int bRet = GDALDataset::CloseDependentDatasets();
466 0 : if (poRasterDS != nullptr)
467 : {
468 0 : GDALClose(poRasterDS);
469 0 : poRasterDS = nullptr;
470 0 : bRet = TRUE;
471 : }
472 0 : return bRet;
473 : }
|