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