Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_ogr_datasource.cpp
4 : * Project: MapInfo Mid/Mif, Tab ogr support
5 : * Language: C++
6 : * Purpose: Implementation of OGRTABDataSource.
7 : * Author: Stephane Villeneuve, stephane.v@videotron.ca
8 : * and Frank Warmerdam, warmerdam@pobox.com
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999, 2000, Stephane Villeneuve
12 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
13 : *
14 : * SPDX-License-Identifier: MIT
15 : **********************************************************************/
16 :
17 : #include "mitab_ogr_driver.h"
18 :
19 : /*=======================================================================
20 : * OGRTABDataSource
21 : *
22 : * We need one single OGRDataSource/Driver set of classes to handle all
23 : * the MapInfo file types. They all deal with the IMapInfoFile abstract
24 : * class.
25 : *=====================================================================*/
26 :
27 : /************************************************************************/
28 : /* OGRTABDataSource() */
29 : /************************************************************************/
30 :
31 3006 : OGRTABDataSource::OGRTABDataSource()
32 : : m_pszDirectory(nullptr), m_nLayerCount(0), m_papoLayers(nullptr),
33 : m_papszOptions(nullptr), m_bCreateMIF(FALSE), m_bSingleFile(FALSE),
34 : m_bSingleLayerAlreadyCreated(FALSE), m_bQuickSpatialIndexMode(-1),
35 3006 : m_nBlockSize(512)
36 : {
37 3006 : }
38 :
39 : /************************************************************************/
40 : /* ~OGRTABDataSource() */
41 : /************************************************************************/
42 :
43 6012 : OGRTABDataSource::~OGRTABDataSource()
44 :
45 : {
46 3006 : CPLFree(m_pszDirectory);
47 :
48 5316 : for (int i = 0; i < m_nLayerCount; i++)
49 2310 : delete m_papoLayers[i];
50 :
51 3006 : CPLFree(m_papoLayers);
52 3006 : CSLDestroy(m_papszOptions);
53 6012 : }
54 :
55 : /************************************************************************/
56 : /* Create() */
57 : /* */
58 : /* Create a new dataset (directory or file). */
59 : /************************************************************************/
60 :
61 193 : int OGRTABDataSource::Create(const char *pszName, char **papszOptions)
62 :
63 : {
64 193 : SetDescription(pszName);
65 193 : m_papszOptions = CSLDuplicate(papszOptions);
66 193 : eAccess = GA_Update;
67 :
68 193 : const char *pszOpt = CSLFetchNameValue(papszOptions, "FORMAT");
69 193 : if (pszOpt != nullptr && EQUAL(pszOpt, "MIF"))
70 7 : m_bCreateMIF = TRUE;
71 293 : else if (EQUAL(CPLGetExtension(pszName), "mif") ||
72 107 : EQUAL(CPLGetExtension(pszName), "mid"))
73 79 : m_bCreateMIF = TRUE;
74 :
75 193 : if ((pszOpt = CSLFetchNameValue(papszOptions, "SPATIAL_INDEX_MODE")) !=
76 : nullptr)
77 : {
78 0 : if (EQUAL(pszOpt, "QUICK"))
79 0 : m_bQuickSpatialIndexMode = TRUE;
80 0 : else if (EQUAL(pszOpt, "OPTIMIZED"))
81 0 : m_bQuickSpatialIndexMode = FALSE;
82 : }
83 :
84 193 : m_nBlockSize = atoi(CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", "512"));
85 :
86 : // Create a new empty directory.
87 : VSIStatBufL sStat;
88 :
89 193 : if (strlen(CPLGetExtension(pszName)) == 0)
90 : {
91 41 : if (VSIStatL(pszName, &sStat) == 0)
92 : {
93 6 : if (!VSI_ISDIR(sStat.st_mode))
94 : {
95 0 : CPLError(CE_Failure, CPLE_OpenFailed,
96 : "Attempt to create dataset named %s,\n"
97 : "but that is an existing file.",
98 : pszName);
99 0 : return FALSE;
100 : }
101 : }
102 : else
103 : {
104 35 : if (VSIMkdir(pszName, 0755) != 0)
105 : {
106 1 : CPLError(CE_Failure, CPLE_AppDefined,
107 : "Unable to create directory %s.", pszName);
108 1 : return FALSE;
109 : }
110 : }
111 :
112 40 : m_pszDirectory = CPLStrdup(pszName);
113 : }
114 :
115 : // Create a new single file.
116 : else
117 : {
118 152 : IMapInfoFile *poFile = nullptr;
119 152 : const char *pszEncoding(CSLFetchNameValue(papszOptions, "ENCODING"));
120 152 : const char *pszCharset(IMapInfoFile::EncodingToCharset(pszEncoding));
121 152 : bool bStrictLaundering = CPLTestBool(CSLFetchNameValueDef(
122 : papszOptions, "STRICT_FIELDS_NAME_LAUNDERING", "YES"));
123 152 : if (m_bCreateMIF)
124 : {
125 84 : poFile = new MIFFile(this);
126 84 : if (poFile->Open(pszName, TABWrite, FALSE, pszCharset) != 0)
127 : {
128 0 : delete poFile;
129 0 : return FALSE;
130 : }
131 : }
132 : else
133 : {
134 68 : TABFile *poTabFile = new TABFile(this);
135 68 : if (poTabFile->Open(pszName, TABWrite, FALSE, m_nBlockSize,
136 68 : pszCharset) != 0)
137 : {
138 1 : delete poTabFile;
139 1 : return FALSE;
140 : }
141 67 : poFile = poTabFile;
142 : }
143 151 : poFile->SetStrictLaundering(bStrictLaundering);
144 151 : m_nLayerCount = 1;
145 151 : m_papoLayers = static_cast<IMapInfoFile **>(CPLMalloc(sizeof(void *)));
146 151 : m_papoLayers[0] = poFile;
147 :
148 151 : m_pszDirectory = CPLStrdup(CPLGetPath(pszName));
149 151 : m_bSingleFile = TRUE;
150 : }
151 :
152 191 : return TRUE;
153 : }
154 :
155 : /************************************************************************/
156 : /* Open() */
157 : /* */
158 : /* Open an existing file, or directory of files. */
159 : /************************************************************************/
160 :
161 2813 : int OGRTABDataSource::Open(GDALOpenInfo *poOpenInfo, int bTestOpen)
162 :
163 : {
164 2813 : SetDescription(poOpenInfo->pszFilename);
165 2813 : eAccess = poOpenInfo->eAccess;
166 :
167 : // If it is a file, try to open as a Mapinfo file.
168 2813 : if (!poOpenInfo->bIsDirectory)
169 : {
170 4224 : IMapInfoFile *poFile = IMapInfoFile::SmartOpen(
171 2112 : this, poOpenInfo->pszFilename, GetUpdate(), bTestOpen);
172 2112 : if (poFile == nullptr)
173 58 : return FALSE;
174 :
175 2054 : poFile->SetDescription(poFile->GetName());
176 :
177 2054 : m_nLayerCount = 1;
178 2054 : m_papoLayers = static_cast<IMapInfoFile **>(CPLMalloc(sizeof(void *)));
179 2054 : m_papoLayers[0] = poFile;
180 :
181 2054 : m_pszDirectory = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename));
182 :
183 2054 : m_bSingleFile = TRUE;
184 2054 : m_bSingleLayerAlreadyCreated = TRUE;
185 : }
186 :
187 : // Otherwise, we need to scan the whole directory for files
188 : // ending in .tab or .mif.
189 : else
190 : {
191 701 : char **papszFileList = VSIReadDir(poOpenInfo->pszFilename);
192 :
193 701 : m_pszDirectory = CPLStrdup(poOpenInfo->pszFilename);
194 :
195 27643 : for (int iFile = 0;
196 27643 : papszFileList != nullptr && papszFileList[iFile] != nullptr;
197 : iFile++)
198 : {
199 26960 : const char *pszExtension = CPLGetExtension(papszFileList[iFile]);
200 :
201 26960 : if (!EQUAL(pszExtension, "tab") && !EQUAL(pszExtension, "mif"))
202 26895 : continue;
203 :
204 65 : char *pszSubFilename = CPLStrdup(
205 65 : CPLFormFilename(m_pszDirectory, papszFileList[iFile], nullptr));
206 :
207 65 : IMapInfoFile *poFile = IMapInfoFile::SmartOpen(
208 65 : this, pszSubFilename, GetUpdate(), bTestOpen);
209 65 : CPLFree(pszSubFilename);
210 :
211 65 : if (poFile == nullptr)
212 : {
213 18 : CSLDestroy(papszFileList);
214 18 : return FALSE;
215 : }
216 47 : poFile->SetDescription(poFile->GetName());
217 :
218 47 : m_nLayerCount++;
219 47 : m_papoLayers = static_cast<IMapInfoFile **>(
220 47 : CPLRealloc(m_papoLayers, sizeof(void *) * m_nLayerCount));
221 47 : m_papoLayers[m_nLayerCount - 1] = poFile;
222 : }
223 :
224 683 : CSLDestroy(papszFileList);
225 :
226 683 : if (m_nLayerCount == 0)
227 : {
228 654 : if (!bTestOpen)
229 0 : CPLError(CE_Failure, CPLE_OpenFailed,
230 : "No mapinfo files found in directory %s.",
231 : m_pszDirectory);
232 :
233 654 : return FALSE;
234 : }
235 : }
236 :
237 2083 : return TRUE;
238 : }
239 :
240 : /************************************************************************/
241 : /* GetLayerCount() */
242 : /************************************************************************/
243 :
244 2346 : int OGRTABDataSource::GetLayerCount()
245 :
246 : {
247 2346 : if (m_bSingleFile && !m_bSingleLayerAlreadyCreated)
248 12 : return 0;
249 : else
250 2334 : return m_nLayerCount;
251 : }
252 :
253 : /************************************************************************/
254 : /* GetLayer() */
255 : /************************************************************************/
256 :
257 2175 : OGRLayer *OGRTABDataSource::GetLayer(int iLayer)
258 :
259 : {
260 2175 : if (iLayer < 0 || iLayer >= GetLayerCount())
261 8 : return nullptr;
262 : else
263 2167 : return m_papoLayers[iLayer];
264 : }
265 :
266 : /************************************************************************/
267 : /* ICreateLayer() */
268 : /************************************************************************/
269 :
270 : OGRLayer *
271 209 : OGRTABDataSource::ICreateLayer(const char *pszLayerName,
272 : const OGRGeomFieldDefn *poGeomFieldDefnIn,
273 : CSLConstList papszOptions)
274 :
275 : {
276 209 : if (!GetUpdate())
277 : {
278 0 : CPLError(CE_Failure, CPLE_AppDefined,
279 : "Cannot create layer on read-only dataset.");
280 0 : return nullptr;
281 : }
282 :
283 : const auto poSRSIn =
284 209 : poGeomFieldDefnIn ? poGeomFieldDefnIn->GetSpatialRef() : nullptr;
285 :
286 : // If it is a single file mode file, then we may have already
287 : // instantiated the low level layer. We would just need to
288 : // reset the coordinate system and (potentially) bounds.
289 209 : IMapInfoFile *poFile = nullptr;
290 209 : char *pszFullFilename = nullptr;
291 :
292 209 : const char *pszEncoding = CSLFetchNameValue(papszOptions, "ENCODING");
293 209 : const char *pszCharset(IMapInfoFile::EncodingToCharset(pszEncoding));
294 209 : const char *pszDescription(CSLFetchNameValue(papszOptions, "DESCRIPTION"));
295 : const char *pszStrictLaundering =
296 209 : CSLFetchNameValue(papszOptions, "STRICT_FIELDS_NAME_LAUNDERING");
297 209 : if (pszStrictLaundering == nullptr)
298 : {
299 207 : pszStrictLaundering = CSLFetchNameValueDef(
300 207 : m_papszOptions, "STRICT_FIELDS_NAME_LAUNDERING", "YES");
301 : }
302 209 : bool bStrictLaundering = CPLTestBool(pszStrictLaundering);
303 :
304 209 : if (m_bSingleFile)
305 : {
306 151 : if (m_bSingleLayerAlreadyCreated)
307 : {
308 0 : CPLError(
309 : CE_Failure, CPLE_AppDefined,
310 : "Unable to create new layers in this single file dataset.");
311 0 : return nullptr;
312 : }
313 :
314 151 : m_bSingleLayerAlreadyCreated = TRUE;
315 :
316 151 : poFile = m_papoLayers[0];
317 151 : if (pszEncoding)
318 3 : poFile->SetCharset(pszCharset);
319 :
320 151 : if (poFile->GetFileClass() == TABFC_TABFile)
321 67 : poFile->SetMetadataItem("DESCRIPTION", pszDescription);
322 : }
323 :
324 : else
325 : {
326 58 : if (m_bCreateMIF)
327 : {
328 : pszFullFilename =
329 3 : CPLStrdup(CPLFormFilename(m_pszDirectory, pszLayerName, "mif"));
330 :
331 3 : poFile = new MIFFile(this);
332 :
333 3 : if (poFile->Open(pszFullFilename, TABWrite, FALSE, pszCharset) != 0)
334 : {
335 0 : CPLFree(pszFullFilename);
336 0 : delete poFile;
337 0 : return nullptr;
338 : }
339 : }
340 : else
341 : {
342 : pszFullFilename =
343 55 : CPLStrdup(CPLFormFilename(m_pszDirectory, pszLayerName, "tab"));
344 :
345 55 : TABFile *poTABFile = new TABFile(this);
346 :
347 55 : if (poTABFile->Open(pszFullFilename, TABWrite, FALSE, m_nBlockSize,
348 55 : pszCharset) != 0)
349 : {
350 0 : CPLFree(pszFullFilename);
351 0 : delete poTABFile;
352 0 : return nullptr;
353 : }
354 55 : poFile = poTABFile;
355 55 : poFile->SetMetadataItem("DESCRIPTION", pszDescription);
356 : }
357 :
358 58 : m_nLayerCount++;
359 58 : m_papoLayers = static_cast<IMapInfoFile **>(
360 58 : CPLRealloc(m_papoLayers, sizeof(void *) * m_nLayerCount));
361 58 : m_papoLayers[m_nLayerCount - 1] = poFile;
362 :
363 58 : CPLFree(pszFullFilename);
364 : }
365 :
366 209 : poFile->SetDescription(poFile->GetName());
367 209 : poFile->SetStrictLaundering(bStrictLaundering);
368 :
369 : // Assign the coordinate system (if provided) and set
370 : // reasonable bounds.
371 209 : if (poSRSIn != nullptr)
372 : {
373 100 : auto poSRSClone = poSRSIn->Clone();
374 100 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
375 100 : poFile->SetSpatialRef(poSRSClone);
376 100 : poSRSClone->Release();
377 : // SetSpatialRef() has cloned the passed geometry
378 100 : auto poGeomFieldDefn = poFile->GetLayerDefn()->GetGeomFieldDefn(0);
379 200 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
380 100 : poGeomFieldDefn->SetSpatialRef(poFile->GetSpatialRef());
381 : }
382 :
383 : // Pull out the bounds if supplied
384 209 : const char *pszOpt = nullptr;
385 209 : if ((pszOpt = CSLFetchNameValue(papszOptions, "BOUNDS")) != nullptr)
386 : {
387 : double dfBounds[4];
388 5 : if (CPLsscanf(pszOpt, "%lf,%lf,%lf,%lf", &dfBounds[0], &dfBounds[1],
389 5 : &dfBounds[2], &dfBounds[3]) != 4)
390 : {
391 0 : CPLError(
392 : CE_Failure, CPLE_IllegalArg,
393 : "Invalid BOUNDS parameter, expected min_x,min_y,max_x,max_y");
394 : }
395 : else
396 : {
397 5 : poFile->SetBounds(dfBounds[0], dfBounds[1], dfBounds[2],
398 5 : dfBounds[3]);
399 : }
400 : }
401 :
402 209 : if (!poFile->IsBoundsSet() && !m_bCreateMIF)
403 : {
404 103 : if (poSRSIn != nullptr && poSRSIn->IsGeographic())
405 0 : poFile->SetBounds(-1000, -1000, 1000, 1000);
406 103 : else if (poSRSIn != nullptr && poSRSIn->IsProjected())
407 : {
408 11 : const double FE = poSRSIn->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
409 11 : const double FN = poSRSIn->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
410 11 : poFile->SetBounds(-30000000 + FE, -15000000 + FN, 30000000 + FE,
411 11 : 15000000 + FN);
412 : }
413 : else
414 92 : poFile->SetBounds(-30000000, -15000000, 30000000, 15000000);
415 : }
416 :
417 209 : if (m_bQuickSpatialIndexMode == TRUE &&
418 0 : poFile->SetQuickSpatialIndexMode(TRUE) != 0)
419 : {
420 0 : CPLError(CE_Warning, CPLE_AppDefined,
421 : "Setting Quick Spatial Index Mode failed.");
422 : }
423 209 : else if (m_bQuickSpatialIndexMode == FALSE &&
424 0 : poFile->SetQuickSpatialIndexMode(FALSE) != 0)
425 : {
426 0 : CPLError(CE_Warning, CPLE_AppDefined,
427 : "Setting Normal Spatial Index Mode failed.");
428 : }
429 :
430 209 : return poFile;
431 : }
432 :
433 : /************************************************************************/
434 : /* TestCapability() */
435 : /************************************************************************/
436 :
437 92 : int OGRTABDataSource::TestCapability(const char *pszCap)
438 :
439 : {
440 92 : if (EQUAL(pszCap, ODsCCreateLayer))
441 42 : return GetUpdate() && (!m_bSingleFile || !m_bSingleLayerAlreadyCreated);
442 50 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
443 0 : return GetUpdate();
444 : else
445 50 : return FALSE;
446 : }
447 :
448 : /************************************************************************/
449 : /* GetFileList() */
450 : /************************************************************************/
451 :
452 36 : char **OGRTABDataSource::GetFileList()
453 : {
454 : VSIStatBufL sStatBuf;
455 72 : CPLStringList osList;
456 :
457 72 : if (VSIStatL(GetDescription(), &sStatBuf) == 0 &&
458 36 : VSI_ISDIR(sStatBuf.st_mode))
459 : {
460 : static const char *const apszExtensions[] = {
461 : "mif", "mid", "tab", "map", "ind", "dat", "id", nullptr};
462 18 : char **papszDirEntries = VSIReadDir(GetDescription());
463 :
464 156 : for (int iFile = 0;
465 156 : papszDirEntries != nullptr && papszDirEntries[iFile] != nullptr;
466 : iFile++)
467 : {
468 138 : if (CSLFindString(apszExtensions,
469 276 : CPLGetExtension(papszDirEntries[iFile])) != -1)
470 : {
471 : osList.AddString(CPLFormFilename(
472 134 : GetDescription(), papszDirEntries[iFile], nullptr));
473 : }
474 : }
475 :
476 18 : CSLDestroy(papszDirEntries);
477 : }
478 : else
479 : {
480 : static const char *const apszMIFExtensions[] = {"mif", "mid", nullptr};
481 : static const char *const apszTABExtensions[] = {"tab", "map", "ind",
482 : "dat", "id", nullptr};
483 18 : const char *const *papszExtensions = nullptr;
484 32 : if (EQUAL(CPLGetExtension(GetDescription()), "mif") ||
485 14 : EQUAL(CPLGetExtension(GetDescription()), "mid"))
486 : {
487 4 : papszExtensions = apszMIFExtensions;
488 : }
489 : else
490 : {
491 14 : papszExtensions = apszTABExtensions;
492 : }
493 18 : const char *const *papszIter = papszExtensions;
494 96 : while (*papszIter)
495 : {
496 : const char *pszFile =
497 78 : CPLResetExtension(GetDescription(), *papszIter);
498 78 : if (VSIStatL(pszFile, &sStatBuf) != 0)
499 : {
500 14 : pszFile = CPLResetExtension(GetDescription(),
501 28 : CPLString(*papszIter).toupper());
502 14 : if (VSIStatL(pszFile, &sStatBuf) != 0)
503 : {
504 14 : pszFile = nullptr;
505 : }
506 : }
507 78 : if (pszFile)
508 64 : osList.AddString(pszFile);
509 78 : papszIter++;
510 : }
511 : }
512 72 : return osList.StealList();
513 : }
514 :
515 : /************************************************************************/
516 : /* ExecuteSQL() */
517 : /************************************************************************/
518 :
519 35 : OGRLayer *OGRTABDataSource::ExecuteSQL(const char *pszStatement,
520 : OGRGeometry *poSpatialFilter,
521 : const char *pszDialect)
522 : {
523 35 : char **papszTokens = CSLTokenizeString(pszStatement);
524 40 : if (CSLCount(papszTokens) == 6 && EQUAL(papszTokens[0], "CREATE") &&
525 45 : EQUAL(papszTokens[1], "INDEX") && EQUAL(papszTokens[2], "ON") &&
526 5 : EQUAL(papszTokens[4], "USING"))
527 : {
528 : IMapInfoFile *poLayer =
529 5 : dynamic_cast<IMapInfoFile *>(GetLayerByName(papszTokens[3]));
530 5 : if (poLayer == nullptr)
531 : {
532 1 : CPLError(CE_Failure, CPLE_AppDefined,
533 : "`%s' failed failed, no such layer as `%s'.", pszStatement,
534 1 : papszTokens[3]);
535 1 : CSLDestroy(papszTokens);
536 1 : return nullptr;
537 : }
538 4 : int nFieldIdx = poLayer->GetLayerDefn()->GetFieldIndex(papszTokens[5]);
539 4 : CSLDestroy(papszTokens);
540 4 : if (nFieldIdx < 0)
541 : {
542 1 : CPLError(CE_Failure, CPLE_AppDefined,
543 : "`%s' failed, field not found.", pszStatement);
544 1 : return nullptr;
545 : }
546 3 : poLayer->SetFieldIndexed(nFieldIdx);
547 3 : return nullptr;
548 : }
549 :
550 30 : CSLDestroy(papszTokens);
551 30 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
552 : }
|