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 3010 : 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 3010 : m_nBlockSize(512)
36 : {
37 3010 : }
38 :
39 : /************************************************************************/
40 : /* ~OGRTABDataSource() */
41 : /************************************************************************/
42 :
43 6020 : OGRTABDataSource::~OGRTABDataSource()
44 :
45 : {
46 3010 : CPLFree(m_pszDirectory);
47 :
48 5321 : for (int i = 0; i < m_nLayerCount; i++)
49 2311 : delete m_papoLayers[i];
50 :
51 3010 : CPLFree(m_papoLayers);
52 3010 : CSLDestroy(m_papszOptions);
53 6020 : }
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 479 : else if (EQUAL(CPLGetExtensionSafe(pszName).c_str(), "mif") ||
72 293 : EQUAL(CPLGetExtensionSafe(pszName).c_str(), "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(CPLGetExtensionSafe(pszName).c_str()) == 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(CPLGetPathSafe(pszName).c_str());
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 2817 : int OGRTABDataSource::Open(GDALOpenInfo *poOpenInfo, int bTestOpen)
162 :
163 : {
164 2817 : SetDescription(poOpenInfo->pszFilename);
165 2817 : eAccess = poOpenInfo->eAccess;
166 :
167 : // If it is a file, try to open as a Mapinfo file.
168 2817 : if (!poOpenInfo->bIsDirectory)
169 : {
170 4226 : IMapInfoFile *poFile = IMapInfoFile::SmartOpen(
171 2113 : this, poOpenInfo->pszFilename, GetUpdate(), bTestOpen);
172 2113 : if (poFile == nullptr)
173 58 : return FALSE;
174 :
175 2055 : poFile->SetDescription(poFile->GetName());
176 :
177 2055 : m_nLayerCount = 1;
178 2055 : m_papoLayers = static_cast<IMapInfoFile **>(CPLMalloc(sizeof(void *)));
179 2055 : m_papoLayers[0] = poFile;
180 :
181 2055 : m_pszDirectory =
182 2055 : CPLStrdup(CPLGetPathSafe(poOpenInfo->pszFilename).c_str());
183 :
184 2055 : m_bSingleFile = TRUE;
185 2055 : m_bSingleLayerAlreadyCreated = TRUE;
186 : }
187 :
188 : // Otherwise, we need to scan the whole directory for files
189 : // ending in .tab or .mif.
190 : else
191 : {
192 704 : char **papszFileList = VSIReadDir(poOpenInfo->pszFilename);
193 :
194 704 : m_pszDirectory = CPLStrdup(poOpenInfo->pszFilename);
195 :
196 27826 : for (int iFile = 0;
197 27826 : papszFileList != nullptr && papszFileList[iFile] != nullptr;
198 : iFile++)
199 : {
200 : const std::string osExtension =
201 27140 : CPLGetExtensionSafe(papszFileList[iFile]);
202 :
203 54221 : if (!EQUAL(osExtension.c_str(), "tab") &&
204 27081 : !EQUAL(osExtension.c_str(), "mif"))
205 27075 : continue;
206 :
207 : char *pszSubFilename =
208 65 : CPLStrdup(CPLFormFilenameSafe(m_pszDirectory,
209 65 : papszFileList[iFile], nullptr)
210 : .c_str());
211 :
212 65 : IMapInfoFile *poFile = IMapInfoFile::SmartOpen(
213 65 : this, pszSubFilename, GetUpdate(), bTestOpen);
214 65 : CPLFree(pszSubFilename);
215 :
216 65 : if (poFile == nullptr)
217 : {
218 18 : CSLDestroy(papszFileList);
219 18 : return FALSE;
220 : }
221 47 : poFile->SetDescription(poFile->GetName());
222 :
223 47 : m_nLayerCount++;
224 47 : m_papoLayers = static_cast<IMapInfoFile **>(
225 47 : CPLRealloc(m_papoLayers, sizeof(void *) * m_nLayerCount));
226 47 : m_papoLayers[m_nLayerCount - 1] = poFile;
227 : }
228 :
229 686 : CSLDestroy(papszFileList);
230 :
231 686 : if (m_nLayerCount == 0)
232 : {
233 657 : if (!bTestOpen)
234 0 : CPLError(CE_Failure, CPLE_OpenFailed,
235 : "No mapinfo files found in directory %s.",
236 : m_pszDirectory);
237 :
238 657 : return FALSE;
239 : }
240 : }
241 :
242 2084 : return TRUE;
243 : }
244 :
245 : /************************************************************************/
246 : /* GetLayerCount() */
247 : /************************************************************************/
248 :
249 2347 : int OGRTABDataSource::GetLayerCount()
250 :
251 : {
252 2347 : if (m_bSingleFile && !m_bSingleLayerAlreadyCreated)
253 12 : return 0;
254 : else
255 2335 : return m_nLayerCount;
256 : }
257 :
258 : /************************************************************************/
259 : /* GetLayer() */
260 : /************************************************************************/
261 :
262 2176 : OGRLayer *OGRTABDataSource::GetLayer(int iLayer)
263 :
264 : {
265 2176 : if (iLayer < 0 || iLayer >= GetLayerCount())
266 8 : return nullptr;
267 : else
268 2168 : return m_papoLayers[iLayer];
269 : }
270 :
271 : /************************************************************************/
272 : /* ICreateLayer() */
273 : /************************************************************************/
274 :
275 : OGRLayer *
276 209 : OGRTABDataSource::ICreateLayer(const char *pszLayerName,
277 : const OGRGeomFieldDefn *poGeomFieldDefnIn,
278 : CSLConstList papszOptions)
279 :
280 : {
281 209 : if (!GetUpdate())
282 : {
283 0 : CPLError(CE_Failure, CPLE_AppDefined,
284 : "Cannot create layer on read-only dataset.");
285 0 : return nullptr;
286 : }
287 :
288 : const auto poSRSIn =
289 209 : poGeomFieldDefnIn ? poGeomFieldDefnIn->GetSpatialRef() : nullptr;
290 :
291 : // If it is a single file mode file, then we may have already
292 : // instantiated the low level layer. We would just need to
293 : // reset the coordinate system and (potentially) bounds.
294 209 : IMapInfoFile *poFile = nullptr;
295 209 : char *pszFullFilename = nullptr;
296 :
297 209 : const char *pszEncoding = CSLFetchNameValue(papszOptions, "ENCODING");
298 209 : const char *pszCharset(IMapInfoFile::EncodingToCharset(pszEncoding));
299 209 : const char *pszDescription(CSLFetchNameValue(papszOptions, "DESCRIPTION"));
300 : const char *pszStrictLaundering =
301 209 : CSLFetchNameValue(papszOptions, "STRICT_FIELDS_NAME_LAUNDERING");
302 209 : if (pszStrictLaundering == nullptr)
303 : {
304 207 : pszStrictLaundering = CSLFetchNameValueDef(
305 207 : m_papszOptions, "STRICT_FIELDS_NAME_LAUNDERING", "YES");
306 : }
307 209 : bool bStrictLaundering = CPLTestBool(pszStrictLaundering);
308 :
309 209 : if (m_bSingleFile)
310 : {
311 151 : if (m_bSingleLayerAlreadyCreated)
312 : {
313 0 : CPLError(
314 : CE_Failure, CPLE_AppDefined,
315 : "Unable to create new layers in this single file dataset.");
316 0 : return nullptr;
317 : }
318 :
319 151 : m_bSingleLayerAlreadyCreated = TRUE;
320 :
321 151 : poFile = m_papoLayers[0];
322 151 : if (pszEncoding)
323 3 : poFile->SetCharset(pszCharset);
324 :
325 151 : if (poFile->GetFileClass() == TABFC_TABFile)
326 67 : poFile->SetMetadataItem("DESCRIPTION", pszDescription);
327 : }
328 :
329 : else
330 : {
331 58 : if (m_bCreateMIF)
332 : {
333 3 : pszFullFilename = CPLStrdup(
334 6 : CPLFormFilenameSafe(m_pszDirectory, pszLayerName, "mif")
335 : .c_str());
336 :
337 3 : poFile = new MIFFile(this);
338 :
339 3 : if (poFile->Open(pszFullFilename, TABWrite, FALSE, pszCharset) != 0)
340 : {
341 0 : CPLFree(pszFullFilename);
342 0 : delete poFile;
343 0 : return nullptr;
344 : }
345 : }
346 : else
347 : {
348 55 : pszFullFilename = CPLStrdup(
349 110 : CPLFormFilenameSafe(m_pszDirectory, pszLayerName, "tab")
350 : .c_str());
351 :
352 55 : TABFile *poTABFile = new TABFile(this);
353 :
354 55 : if (poTABFile->Open(pszFullFilename, TABWrite, FALSE, m_nBlockSize,
355 55 : pszCharset) != 0)
356 : {
357 0 : CPLFree(pszFullFilename);
358 0 : delete poTABFile;
359 0 : return nullptr;
360 : }
361 55 : poFile = poTABFile;
362 55 : poFile->SetMetadataItem("DESCRIPTION", pszDescription);
363 : }
364 :
365 58 : m_nLayerCount++;
366 58 : m_papoLayers = static_cast<IMapInfoFile **>(
367 58 : CPLRealloc(m_papoLayers, sizeof(void *) * m_nLayerCount));
368 58 : m_papoLayers[m_nLayerCount - 1] = poFile;
369 :
370 58 : CPLFree(pszFullFilename);
371 : }
372 :
373 209 : poFile->SetDescription(poFile->GetName());
374 209 : poFile->SetStrictLaundering(bStrictLaundering);
375 :
376 : // Assign the coordinate system (if provided) and set
377 : // reasonable bounds.
378 209 : if (poSRSIn != nullptr)
379 : {
380 100 : auto poSRSClone = poSRSIn->Clone();
381 100 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
382 100 : poFile->SetSpatialRef(poSRSClone);
383 100 : poSRSClone->Release();
384 : // SetSpatialRef() has cloned the passed geometry
385 100 : auto poGeomFieldDefn = poFile->GetLayerDefn()->GetGeomFieldDefn(0);
386 200 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
387 100 : poGeomFieldDefn->SetSpatialRef(poFile->GetSpatialRef());
388 : }
389 :
390 : // Pull out the bounds if supplied
391 209 : const char *pszOpt = nullptr;
392 209 : if ((pszOpt = CSLFetchNameValue(papszOptions, "BOUNDS")) != nullptr)
393 : {
394 : double dfBounds[4];
395 5 : if (CPLsscanf(pszOpt, "%lf,%lf,%lf,%lf", &dfBounds[0], &dfBounds[1],
396 5 : &dfBounds[2], &dfBounds[3]) != 4)
397 : {
398 0 : CPLError(
399 : CE_Failure, CPLE_IllegalArg,
400 : "Invalid BOUNDS parameter, expected min_x,min_y,max_x,max_y");
401 : }
402 : else
403 : {
404 5 : poFile->SetBounds(dfBounds[0], dfBounds[1], dfBounds[2],
405 5 : dfBounds[3]);
406 : }
407 : }
408 :
409 209 : if (!poFile->IsBoundsSet() && !m_bCreateMIF)
410 : {
411 103 : if (poSRSIn != nullptr && poSRSIn->IsGeographic())
412 0 : poFile->SetBounds(-1000, -1000, 1000, 1000);
413 103 : else if (poSRSIn != nullptr && poSRSIn->IsProjected())
414 : {
415 11 : const double FE = poSRSIn->GetProjParm(SRS_PP_FALSE_EASTING, 0.0);
416 11 : const double FN = poSRSIn->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0);
417 11 : poFile->SetBounds(-30000000 + FE, -15000000 + FN, 30000000 + FE,
418 11 : 15000000 + FN);
419 : }
420 : else
421 92 : poFile->SetBounds(-30000000, -15000000, 30000000, 15000000);
422 : }
423 :
424 209 : if (m_bQuickSpatialIndexMode == TRUE &&
425 0 : poFile->SetQuickSpatialIndexMode(TRUE) != 0)
426 : {
427 0 : CPLError(CE_Warning, CPLE_AppDefined,
428 : "Setting Quick Spatial Index Mode failed.");
429 : }
430 209 : else if (m_bQuickSpatialIndexMode == FALSE &&
431 0 : poFile->SetQuickSpatialIndexMode(FALSE) != 0)
432 : {
433 0 : CPLError(CE_Warning, CPLE_AppDefined,
434 : "Setting Normal Spatial Index Mode failed.");
435 : }
436 :
437 209 : return poFile;
438 : }
439 :
440 : /************************************************************************/
441 : /* TestCapability() */
442 : /************************************************************************/
443 :
444 92 : int OGRTABDataSource::TestCapability(const char *pszCap)
445 :
446 : {
447 92 : if (EQUAL(pszCap, ODsCCreateLayer))
448 42 : return GetUpdate() && (!m_bSingleFile || !m_bSingleLayerAlreadyCreated);
449 50 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
450 0 : return GetUpdate();
451 : else
452 50 : return FALSE;
453 : }
454 :
455 : /************************************************************************/
456 : /* GetFileList() */
457 : /************************************************************************/
458 :
459 36 : char **OGRTABDataSource::GetFileList()
460 : {
461 : VSIStatBufL sStatBuf;
462 72 : CPLStringList osList;
463 :
464 72 : if (VSIStatL(GetDescription(), &sStatBuf) == 0 &&
465 36 : VSI_ISDIR(sStatBuf.st_mode))
466 : {
467 : static const char *const apszExtensions[] = {
468 : "mif", "mid", "tab", "map", "ind", "dat", "id", nullptr};
469 18 : char **papszDirEntries = VSIReadDir(GetDescription());
470 :
471 156 : for (int iFile = 0;
472 156 : papszDirEntries != nullptr && papszDirEntries[iFile] != nullptr;
473 : iFile++)
474 : {
475 138 : if (CSLFindString(
476 : apszExtensions,
477 276 : CPLGetExtensionSafe(papszDirEntries[iFile]).c_str()) != -1)
478 : {
479 134 : osList.push_back(CPLFormFilenameSafe(
480 134 : GetDescription(), papszDirEntries[iFile], nullptr));
481 : }
482 : }
483 :
484 18 : CSLDestroy(papszDirEntries);
485 : }
486 : else
487 : {
488 : static const char *const apszMIFExtensions[] = {"mif", "mid", nullptr};
489 : static const char *const apszTABExtensions[] = {"tab", "map", "ind",
490 : "dat", "id", nullptr};
491 18 : const char *const *papszExtensions = nullptr;
492 50 : if (EQUAL(CPLGetExtensionSafe(GetDescription()).c_str(), "mif") ||
493 32 : EQUAL(CPLGetExtensionSafe(GetDescription()).c_str(), "mid"))
494 : {
495 4 : papszExtensions = apszMIFExtensions;
496 : }
497 : else
498 : {
499 14 : papszExtensions = apszTABExtensions;
500 : }
501 18 : const char *const *papszIter = papszExtensions;
502 96 : while (*papszIter)
503 : {
504 : std::string osFile =
505 78 : CPLResetExtensionSafe(GetDescription(), *papszIter);
506 78 : if (VSIStatL(osFile.c_str(), &sStatBuf) != 0)
507 : {
508 28 : osFile = CPLResetExtensionSafe(GetDescription(),
509 42 : CPLString(*papszIter).toupper());
510 14 : if (VSIStatL(osFile.c_str(), &sStatBuf) != 0)
511 : {
512 14 : osFile.clear();
513 : }
514 : }
515 78 : if (!osFile.empty())
516 64 : osList.AddString(osFile.c_str());
517 78 : papszIter++;
518 : }
519 : }
520 72 : return osList.StealList();
521 : }
522 :
523 : /************************************************************************/
524 : /* ExecuteSQL() */
525 : /************************************************************************/
526 :
527 35 : OGRLayer *OGRTABDataSource::ExecuteSQL(const char *pszStatement,
528 : OGRGeometry *poSpatialFilter,
529 : const char *pszDialect)
530 : {
531 35 : char **papszTokens = CSLTokenizeString(pszStatement);
532 40 : if (CSLCount(papszTokens) == 6 && EQUAL(papszTokens[0], "CREATE") &&
533 45 : EQUAL(papszTokens[1], "INDEX") && EQUAL(papszTokens[2], "ON") &&
534 5 : EQUAL(papszTokens[4], "USING"))
535 : {
536 : IMapInfoFile *poLayer =
537 5 : dynamic_cast<IMapInfoFile *>(GetLayerByName(papszTokens[3]));
538 5 : if (poLayer == nullptr)
539 : {
540 1 : CPLError(CE_Failure, CPLE_AppDefined,
541 : "`%s' failed failed, no such layer as `%s'.", pszStatement,
542 1 : papszTokens[3]);
543 1 : CSLDestroy(papszTokens);
544 1 : return nullptr;
545 : }
546 4 : int nFieldIdx = poLayer->GetLayerDefn()->GetFieldIndex(papszTokens[5]);
547 4 : CSLDestroy(papszTokens);
548 4 : if (nFieldIdx < 0)
549 : {
550 1 : CPLError(CE_Failure, CPLE_AppDefined,
551 : "`%s' failed, field not found.", pszStatement);
552 1 : return nullptr;
553 : }
554 3 : poLayer->SetFieldIndexed(nFieldIdx);
555 3 : return nullptr;
556 : }
557 :
558 30 : CSLDestroy(papszTokens);
559 30 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
560 : }
|