Line data Source code
1 : /******************************************************************************
2 : *
3 : * Name: ogrgeoconceptdatasource.h
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRGeoconceptDataSource class.
6 : * Language: C++
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Geoconcept and IGN
10 : * Copyright (c) 2008, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_conv.h"
16 : #include "cpl_string.h"
17 : #include "ogrgeoconceptdatasource.h"
18 : #include "ogrgeoconceptlayer.h"
19 :
20 : /************************************************************************/
21 : /* OGRGeoconceptDataSource() */
22 : /************************************************************************/
23 :
24 34 : OGRGeoconceptDataSource::OGRGeoconceptDataSource()
25 : : _papoLayers(nullptr), _nLayers(0), _pszGCT(nullptr),
26 : _pszDirectory(nullptr), _pszExt(nullptr), _papszOptions(nullptr),
27 34 : _bSingleNewFile(false), _bUpdate(false), _hGXT(nullptr)
28 : {
29 34 : }
30 :
31 : /************************************************************************/
32 : /* ~OGRGeoconceptDataSource() */
33 : /************************************************************************/
34 :
35 68 : OGRGeoconceptDataSource::~OGRGeoconceptDataSource()
36 :
37 : {
38 43 : for (int i = 0; i < _nLayers; i++)
39 : {
40 9 : delete _papoLayers[i];
41 : }
42 34 : CPLFree(_papoLayers);
43 34 : CPLFree(_pszGCT);
44 34 : CPLFree(_pszDirectory);
45 34 : CPLFree(_pszExt);
46 34 : CSLDestroy(_papszOptions);
47 :
48 34 : if (_hGXT)
49 : {
50 25 : Close_GCIO(&_hGXT);
51 : }
52 68 : }
53 :
54 : /************************************************************************/
55 : /* Open() */
56 : /* */
57 : /* Open an existing file. */
58 : /************************************************************************/
59 :
60 16 : int OGRGeoconceptDataSource::Open(const char *pszName, bool bTestOpen,
61 : bool bUpdate)
62 :
63 : {
64 : /* -------------------------------------------------------------------- */
65 : /* Is the given path a directory or a regular file? */
66 : /* -------------------------------------------------------------------- */
67 : VSIStatBufL sStat;
68 :
69 28 : if (VSIStatL(pszName, &sStat) != 0 ||
70 12 : (!VSI_ISDIR(sStat.st_mode) && !VSI_ISREG(sStat.st_mode)))
71 : {
72 4 : if (!bTestOpen)
73 : {
74 0 : CPLError(CE_Failure, CPLE_AppDefined,
75 : "%s is neither a file or directory, "
76 : "Geoconcept access failed.",
77 : pszName);
78 : }
79 :
80 4 : return FALSE;
81 : }
82 :
83 12 : if (VSI_ISDIR(sStat.st_mode))
84 : {
85 0 : CPLDebug("GEOCONCEPT",
86 : "%s is a directory, Geoconcept access is not yet supported.",
87 : pszName);
88 :
89 0 : return FALSE;
90 : }
91 :
92 12 : SetDescription(pszName);
93 :
94 12 : if (VSI_ISREG(sStat.st_mode))
95 : {
96 12 : _bSingleNewFile = false;
97 12 : _bUpdate = bUpdate;
98 12 : if (!LoadFile(_bUpdate ? "a+t" : "rt"))
99 : {
100 4 : CPLDebug("GEOCONCEPT",
101 : "Failed to open Geoconcept %s."
102 : " It may be corrupt.",
103 : pszName);
104 :
105 4 : return FALSE;
106 : }
107 :
108 8 : return TRUE;
109 : }
110 :
111 0 : return _nLayers > 0;
112 : }
113 :
114 : /************************************************************************/
115 : /* LoadFile() */
116 : /************************************************************************/
117 :
118 29 : int OGRGeoconceptDataSource::LoadFile(const char *pszMode)
119 :
120 : {
121 29 : if (_pszExt == nullptr)
122 : {
123 12 : const char *pszExtension = CPLGetExtension(GetDescription());
124 12 : _pszExt = CPLStrdup(pszExtension);
125 : }
126 29 : CPLStrlwr(_pszExt);
127 :
128 29 : if (!_pszDirectory)
129 12 : _pszDirectory = CPLStrdup(CPLGetPath(GetDescription()));
130 :
131 29 : if ((_hGXT = Open_GCIO(GetDescription(), _pszExt, pszMode, _pszGCT)) ==
132 : nullptr)
133 : {
134 4 : return FALSE;
135 : }
136 :
137 : /* Collect layers : */
138 25 : GCExportFileMetadata *Meta = GetGCMeta_GCIO(_hGXT);
139 25 : if (Meta)
140 : {
141 8 : const int nC = CountMetaTypes_GCIO(Meta);
142 :
143 8 : if (nC > 0)
144 : {
145 16 : for (int iC = 0; iC < nC; iC++)
146 : {
147 8 : GCType *aClass = GetMetaType_GCIO(Meta, iC);
148 8 : if (aClass)
149 : {
150 8 : const int nS = CountTypeSubtypes_GCIO(aClass);
151 8 : if (nS)
152 : {
153 16 : for (int iS = 0; iS < nS; iS++)
154 : {
155 : GCSubType *aSubclass =
156 8 : GetTypeSubtype_GCIO(aClass, iS);
157 8 : if (aSubclass)
158 : {
159 : OGRGeoconceptLayer *poFile =
160 8 : new OGRGeoconceptLayer;
161 8 : if (poFile->Open(aSubclass) != OGRERR_NONE)
162 : {
163 0 : delete poFile;
164 0 : return FALSE;
165 : }
166 :
167 : /* Add layer to data source layers list */
168 8 : _papoLayers =
169 : static_cast<OGRGeoconceptLayer **>(
170 16 : CPLRealloc(
171 8 : _papoLayers,
172 : sizeof(OGRGeoconceptLayer *) *
173 8 : (_nLayers + 1)));
174 8 : _papoLayers[_nLayers++] = poFile;
175 :
176 8 : CPLDebug("GEOCONCEPT", "nLayers=%d - last=[%s]",
177 : _nLayers,
178 8 : poFile->GetLayerDefn()->GetName());
179 : }
180 : }
181 : }
182 : }
183 : }
184 : }
185 : }
186 :
187 25 : return TRUE;
188 : }
189 :
190 : /************************************************************************/
191 : /* Create() */
192 : /* */
193 : /* Create a new dataset. */
194 : /* */
195 : /* Options (-dsco) : */
196 : /* EXTENSION : gxt|txt */
197 : /* CONFIG : path to GCT file */
198 : /************************************************************************/
199 :
200 18 : int OGRGeoconceptDataSource::Create(const char *pszName, char **papszOptions)
201 :
202 : {
203 18 : _papszOptions = CSLDuplicate(papszOptions);
204 :
205 18 : const char *pszConf = CSLFetchNameValue(papszOptions, "CONFIG");
206 18 : if (pszConf != nullptr)
207 : {
208 0 : _pszGCT = CPLStrdup(pszConf);
209 : }
210 :
211 18 : _pszExt = (char *)CSLFetchNameValue(papszOptions, "EXTENSION");
212 18 : const char *pszExtension = CSLFetchNameValue(papszOptions, "EXTENSION");
213 18 : if (pszExtension == nullptr)
214 : {
215 18 : _pszExt = CPLStrdup(CPLGetExtension(pszName));
216 : }
217 : else
218 : {
219 0 : _pszExt = CPLStrdup(pszExtension);
220 : }
221 :
222 18 : if (strlen(_pszExt) == 0)
223 : {
224 17 : if (VSIMkdir(pszName, 0755) != 0)
225 : {
226 1 : CPLError(CE_Failure, CPLE_AppDefined,
227 : "Directory %s already exists"
228 : " as geoconcept datastore or"
229 : " is made up of a non existing list of directories.",
230 : pszName);
231 :
232 1 : return FALSE;
233 : }
234 16 : _pszDirectory = CPLStrdup(pszName);
235 16 : CPLFree(_pszExt);
236 16 : _pszExt = CPLStrdup("gxt");
237 16 : char *pszbName = CPLStrdup(CPLGetBasename(pszName));
238 16 : if (strlen(pszbName) == 0)
239 : { /* pszName ends with '/' */
240 0 : CPLFree(pszbName);
241 0 : char *pszNameDup = CPLStrdup(pszName);
242 0 : pszNameDup[strlen(pszName) - 2] = '\0';
243 0 : pszbName = CPLStrdup(CPLGetBasename(pszNameDup));
244 0 : CPLFree(pszNameDup);
245 : }
246 16 : SetDescription(CPLFormFilename(_pszDirectory, pszbName, nullptr));
247 16 : CPLFree(pszbName);
248 : }
249 : else
250 : {
251 1 : _pszDirectory = CPLStrdup(CPLGetPath(pszName));
252 1 : SetDescription(pszName);
253 : }
254 :
255 : /* -------------------------------------------------------------------- */
256 : /* Create a new single file. */
257 : /* OGRGeoconceptDriver::ICreateLayer() will do the job. */
258 : /* -------------------------------------------------------------------- */
259 17 : _bSingleNewFile = true;
260 :
261 17 : if (!LoadFile("wt"))
262 : {
263 0 : CPLDebug("GEOCONCEPT", "Failed to create Geoconcept %s.", pszName);
264 :
265 0 : return FALSE;
266 : }
267 :
268 17 : return TRUE;
269 : }
270 :
271 : /************************************************************************/
272 : /* ICreateLayer() */
273 : /* */
274 : /* Options (-lco) : */
275 : /* FEATURETYPE : TYPE.SUBTYPE */
276 : /************************************************************************/
277 :
278 : OGRLayer *
279 17 : OGRGeoconceptDataSource::ICreateLayer(const char *pszLayerName,
280 : const OGRGeomFieldDefn *poGeomFieldDefn,
281 : CSLConstList papszOptions)
282 :
283 : {
284 17 : if (_hGXT == nullptr)
285 : {
286 0 : CPLError(CE_Failure, CPLE_NotSupported,
287 : "Internal Error : null datasource handler.");
288 0 : return nullptr;
289 : }
290 :
291 : const auto poSRS =
292 17 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
293 17 : if (poSRS == nullptr && !_bUpdate)
294 : {
295 16 : CPLError(CE_Failure, CPLE_NotSupported,
296 : "SRS is mandatory of creating a Geoconcept Layer.");
297 16 : return nullptr;
298 : }
299 :
300 : /*
301 : * pszLayerName Class.Subclass if -nln option used, otherwise file name
302 : */
303 1 : const char *pszFeatureType = nullptr;
304 : char pszln[512];
305 :
306 1 : if (!(pszFeatureType = CSLFetchNameValue(papszOptions, "FEATURETYPE")))
307 : {
308 1 : if (!pszLayerName || !strchr(pszLayerName, '.'))
309 : {
310 1 : snprintf(pszln, 511, "%s.%s",
311 : pszLayerName ? pszLayerName : "ANONCLASS",
312 : pszLayerName ? pszLayerName : "ANONSUBCLASS");
313 1 : pszln[511] = '\0';
314 1 : pszFeatureType = pszln;
315 : }
316 : else
317 0 : pszFeatureType = pszLayerName;
318 : }
319 :
320 1 : char **ft = CSLTokenizeString2(pszFeatureType, ".", 0);
321 1 : if (!ft || CSLCount(ft) != 2)
322 : {
323 0 : CSLDestroy(ft);
324 0 : CPLError(CE_Failure, CPLE_AppDefined,
325 : "Feature type name '%s' is incorrect."
326 : "Correct syntax is : Class.Subclass.",
327 : pszFeatureType);
328 0 : return nullptr;
329 : }
330 :
331 : /* -------------------------------------------------------------------- */
332 : /* Figure out what type of layer we need. */
333 : /* -------------------------------------------------------------------- */
334 : GCTypeKind gcioFeaType;
335 1 : GCDim gcioDim = v2D_GCIO;
336 :
337 1 : const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
338 1 : if (eType == wkbUnknown)
339 0 : gcioFeaType = vUnknownItemType_GCIO;
340 1 : else if (eType == wkbPoint)
341 1 : gcioFeaType = vPoint_GCIO;
342 0 : else if (eType == wkbLineString)
343 0 : gcioFeaType = vLine_GCIO;
344 0 : else if (eType == wkbPolygon)
345 0 : gcioFeaType = vPoly_GCIO;
346 0 : else if (eType == wkbMultiPoint)
347 0 : gcioFeaType = vPoint_GCIO;
348 0 : else if (eType == wkbMultiLineString)
349 0 : gcioFeaType = vLine_GCIO;
350 0 : else if (eType == wkbMultiPolygon)
351 0 : gcioFeaType = vPoly_GCIO;
352 0 : else if (eType == wkbPoint25D)
353 : {
354 0 : gcioFeaType = vPoint_GCIO;
355 0 : gcioDim = v3DM_GCIO;
356 : }
357 0 : else if (eType == wkbLineString25D)
358 : {
359 0 : gcioFeaType = vLine_GCIO;
360 0 : gcioDim = v3DM_GCIO;
361 : }
362 0 : else if (eType == wkbPolygon25D)
363 : {
364 0 : gcioFeaType = vPoly_GCIO;
365 0 : gcioDim = v3DM_GCIO;
366 : }
367 0 : else if (eType == wkbMultiPoint25D)
368 : {
369 0 : gcioFeaType = vPoint_GCIO;
370 0 : gcioDim = v3DM_GCIO;
371 : }
372 0 : else if (eType == wkbMultiLineString25D)
373 : {
374 0 : gcioFeaType = vLine_GCIO;
375 0 : gcioDim = v3DM_GCIO;
376 : }
377 0 : else if (eType == wkbMultiPolygon25D)
378 : {
379 0 : gcioFeaType = vPoly_GCIO;
380 0 : gcioDim = v3DM_GCIO;
381 : }
382 : else
383 : {
384 0 : CSLDestroy(ft);
385 0 : CPLError(CE_Failure, CPLE_NotSupported,
386 : "Geometry type of '%s' not supported in Geoconcept files.",
387 : OGRGeometryTypeToName(eType));
388 0 : return nullptr;
389 : }
390 :
391 : /*
392 : * As long as we use the CONFIG, creating a layer implies the
393 : * layer name to exist in the CONFIG as "Class.Subclass".
394 : * Removing the CONFIG, implies on-the-fly-creation of layers...
395 : */
396 1 : OGRGeoconceptLayer *poFile = nullptr;
397 :
398 1 : if (_nLayers > 0)
399 0 : for (int iLayer = 0; iLayer < _nLayers; iLayer++)
400 : {
401 0 : poFile = reinterpret_cast<OGRGeoconceptLayer *>(GetLayer(iLayer));
402 0 : if (poFile != nullptr &&
403 0 : EQUAL(poFile->GetLayerDefn()->GetName(), pszFeatureType))
404 : {
405 0 : break;
406 : }
407 0 : poFile = nullptr;
408 : }
409 1 : if (!poFile)
410 : {
411 1 : GCSubType *aSubclass = nullptr;
412 1 : GCExportFileMetadata *m = GetGCMeta_GCIO(_hGXT);
413 :
414 1 : if (!m)
415 : {
416 1 : if (!(m = CreateHeader_GCIO()))
417 : {
418 0 : CSLDestroy(ft);
419 0 : return nullptr;
420 : }
421 1 : SetMetaExtent_GCIO(
422 : m, CreateExtent_GCIO(HUGE_VAL, HUGE_VAL, -HUGE_VAL, -HUGE_VAL));
423 1 : SetGCMeta_GCIO(_hGXT, m);
424 : }
425 1 : if (FindFeature_GCIO(_hGXT, pszFeatureType))
426 : {
427 0 : CSLDestroy(ft);
428 0 : CPLError(CE_Failure, CPLE_AppDefined, "Layer '%s' already exists.",
429 : pszFeatureType);
430 0 : return nullptr;
431 : }
432 1 : if (!AddType_GCIO(_hGXT, ft[0], -1L))
433 : {
434 0 : CSLDestroy(ft);
435 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to add layer '%s'.",
436 : pszFeatureType);
437 0 : return nullptr;
438 : }
439 1 : if (!(aSubclass = AddSubType_GCIO(_hGXT, ft[0], ft[1], -1L, gcioFeaType,
440 : gcioDim)))
441 : {
442 0 : CSLDestroy(ft);
443 0 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to add layer '%s'.",
444 : pszFeatureType);
445 0 : return nullptr;
446 : }
447 : /* complete feature type with private fields : */
448 1 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kIdentifier_GCIO, -100,
449 : vIntFld_GCIO, nullptr, nullptr);
450 1 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kClass_GCIO, -101,
451 : vMemoFld_GCIO, nullptr, nullptr);
452 1 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kSubclass_GCIO, -102,
453 : vMemoFld_GCIO, nullptr, nullptr);
454 1 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kName_GCIO, -103,
455 : vMemoFld_GCIO, nullptr, nullptr);
456 1 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kNbFields_GCIO, -104,
457 : vIntFld_GCIO, nullptr, nullptr);
458 1 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kX_GCIO, -105,
459 : vRealFld_GCIO, nullptr, nullptr);
460 1 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kY_GCIO, -106,
461 : vRealFld_GCIO, nullptr, nullptr);
462 : /* user's fields will be added with Layer->CreateField() method ... */
463 1 : switch (gcioFeaType)
464 : {
465 1 : case vPoint_GCIO:
466 1 : break;
467 0 : case vLine_GCIO:
468 0 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kXP_GCIO, -107,
469 : vRealFld_GCIO, nullptr, nullptr);
470 0 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kYP_GCIO, -108,
471 : vRealFld_GCIO, nullptr, nullptr);
472 0 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kGraphics_GCIO,
473 : -109, vUnknownItemType_GCIO, nullptr,
474 : nullptr);
475 0 : break;
476 0 : default:
477 0 : AddSubTypeField_GCIO(_hGXT, ft[0], ft[1], -1L, kGraphics_GCIO,
478 : -109, vUnknownItemType_GCIO, nullptr,
479 : nullptr);
480 0 : break;
481 : }
482 1 : SetSubTypeGCHandle_GCIO(aSubclass, _hGXT);
483 :
484 : /* Add layer to data source layers list */
485 1 : poFile = new OGRGeoconceptLayer;
486 1 : if (poFile->Open(aSubclass) != OGRERR_NONE)
487 : {
488 0 : CSLDestroy(ft);
489 0 : delete poFile;
490 0 : return nullptr;
491 : }
492 :
493 2 : _papoLayers = static_cast<OGRGeoconceptLayer **>(CPLRealloc(
494 1 : _papoLayers, sizeof(OGRGeoconceptLayer *) * (_nLayers + 1)));
495 1 : _papoLayers[_nLayers++] = poFile;
496 :
497 1 : CPLDebug("GEOCONCEPT", "nLayers=%d - last=[%s]", _nLayers,
498 1 : poFile->GetLayerDefn()->GetName());
499 : }
500 1 : CSLDestroy(ft);
501 :
502 : /* -------------------------------------------------------------------- */
503 : /* Assign the coordinate system (if provided) */
504 : /* -------------------------------------------------------------------- */
505 1 : if (poSRS != nullptr)
506 : {
507 1 : auto poSRSClone = poSRS->Clone();
508 1 : poFile->SetSpatialRef(poSRSClone);
509 1 : poSRSClone->Release();
510 : }
511 :
512 1 : return poFile;
513 : }
514 :
515 : /************************************************************************/
516 : /* TestCapability() */
517 : /************************************************************************/
518 :
519 16 : int OGRGeoconceptDataSource::TestCapability(const char *pszCap)
520 :
521 : {
522 16 : if (EQUAL(pszCap, ODsCCreateLayer))
523 16 : return TRUE;
524 0 : else if (EQUAL(pszCap, ODsCZGeometries))
525 0 : return TRUE;
526 :
527 0 : return FALSE;
528 : }
529 :
530 : /************************************************************************/
531 : /* GetLayer() */
532 : /************************************************************************/
533 :
534 8 : OGRLayer *OGRGeoconceptDataSource::GetLayer(int iLayer)
535 :
536 : {
537 8 : if (iLayer < 0 || iLayer >= GetLayerCount())
538 0 : return nullptr;
539 :
540 8 : OGRLayer *poFile = _papoLayers[iLayer];
541 8 : return poFile;
542 : }
|