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