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