Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_ogr_driver.cpp
4 : * Project: MapInfo Mid/Mif, Tab ogr support
5 : * Language: C++
6 : * Purpose: Implementation of the MIDDATAFile class used to handle
7 : * reading/writing of the MID/MIF files
8 : * Author: Stephane Villeneuve, stephane.v@videotron.ca
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 : /* OGRTABDriverIdentify() */
21 : /************************************************************************/
22 :
23 54187 : static int OGRTABDriverIdentify(GDALOpenInfo *poOpenInfo)
24 :
25 : {
26 : // Files not ending with .tab, .mif or .mid are not handled by this driver.
27 54187 : if (!poOpenInfo->bStatOK)
28 43114 : return FALSE;
29 11073 : if (poOpenInfo->bIsDirectory)
30 1383 : return -1; // Unsure.
31 9690 : if (poOpenInfo->fpL == nullptr)
32 83 : return FALSE;
33 17480 : if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MIF") ||
34 7873 : EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MID"))
35 : {
36 1734 : return TRUE;
37 : }
38 7873 : if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "TAB"))
39 : {
40 224878 : for (int i = 0; i < poOpenInfo->nHeaderBytes; i++)
41 : {
42 224878 : const char *pszLine =
43 224878 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader) + i;
44 224878 : if (STARTS_WITH_CI(pszLine, "Fields"))
45 2474 : return TRUE;
46 222404 : else if (STARTS_WITH_CI(pszLine, "create view"))
47 4 : return TRUE;
48 222400 : else if (STARTS_WITH_CI(pszLine, "\"\\IsSeamless\" = \"TRUE\""))
49 0 : return TRUE;
50 : }
51 : }
52 : #ifdef DEBUG
53 : // For AFL, so that .cur_input is detected as the archive filename.
54 10790 : if (!STARTS_WITH(poOpenInfo->pszFilename, "/vsitar/") &&
55 5395 : EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input"))
56 : {
57 4 : return -1;
58 : }
59 : #endif
60 5391 : return FALSE;
61 : }
62 :
63 : /************************************************************************/
64 : /* OGRTABDriver::Open() */
65 : /************************************************************************/
66 :
67 2817 : static GDALDataset *OGRTABDriverOpen(GDALOpenInfo *poOpenInfo)
68 :
69 : {
70 2817 : if (OGRTABDriverIdentify(poOpenInfo) == FALSE)
71 : {
72 2 : return nullptr;
73 : }
74 :
75 4761 : if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MIF") ||
76 1946 : EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "MID"))
77 : {
78 869 : if (poOpenInfo->eAccess == GA_Update)
79 0 : return nullptr;
80 : }
81 :
82 : #ifdef DEBUG
83 : // For AFL, so that .cur_input is detected as the archive filename.
84 7744 : if (poOpenInfo->fpL != nullptr &&
85 4929 : !STARTS_WITH(poOpenInfo->pszFilename, "/vsitar/") &&
86 2114 : EQUAL(CPLGetFilename(poOpenInfo->pszFilename), ".cur_input"))
87 : {
88 : GDALOpenInfo oOpenInfo(
89 4 : (CPLString("/vsitar/") + poOpenInfo->pszFilename).c_str(),
90 6 : poOpenInfo->nOpenFlags);
91 2 : oOpenInfo.papszOpenOptions = poOpenInfo->papszOpenOptions;
92 2 : return OGRTABDriverOpen(&oOpenInfo);
93 : }
94 : #endif
95 :
96 2813 : OGRTABDataSource *poDS = new OGRTABDataSource();
97 2813 : if (!poDS->Open(poOpenInfo, TRUE))
98 : {
99 730 : delete poDS;
100 730 : return nullptr;
101 : }
102 :
103 2083 : return poDS;
104 : }
105 :
106 : /************************************************************************/
107 : /* Create() */
108 : /************************************************************************/
109 :
110 : static GDALDataset *
111 193 : OGRTABDriverCreate(const char *pszName, CPL_UNUSED int nBands,
112 : CPL_UNUSED int nXSize, CPL_UNUSED int nYSize,
113 : CPL_UNUSED GDALDataType eDT, char **papszOptions)
114 : {
115 : // Try to create the data source.
116 193 : OGRTABDataSource *poDS = new OGRTABDataSource();
117 193 : if (!poDS->Create(pszName, papszOptions))
118 : {
119 2 : delete poDS;
120 2 : return nullptr;
121 : }
122 :
123 191 : return poDS;
124 : }
125 :
126 : /************************************************************************/
127 : /* Delete() */
128 : /************************************************************************/
129 :
130 48 : static CPLErr OGRTABDriverDelete(const char *pszDataSource)
131 :
132 : {
133 48 : GDALDataset *poDS = nullptr;
134 : {
135 : // Make sure that the file opened by GDALOpenInfo is closed
136 : // when the object goes out of scope
137 48 : GDALOpenInfo oOpenInfo(pszDataSource, GA_ReadOnly);
138 48 : poDS = OGRTABDriverOpen(&oOpenInfo);
139 : }
140 48 : if (poDS == nullptr)
141 16 : return CE_Failure;
142 32 : char **papszFileList = poDS->GetFileList();
143 32 : delete poDS;
144 :
145 32 : char **papszIter = papszFileList;
146 216 : while (papszIter && *papszIter)
147 : {
148 184 : VSIUnlink(*papszIter);
149 184 : papszIter++;
150 : }
151 32 : CSLDestroy(papszFileList);
152 :
153 : VSIStatBufL sStatBuf;
154 32 : if (VSIStatL(pszDataSource, &sStatBuf) == 0 && VSI_ISDIR(sStatBuf.st_mode))
155 : {
156 16 : VSIRmdir(pszDataSource);
157 : }
158 :
159 32 : return CE_None;
160 : }
161 :
162 : /************************************************************************/
163 : /* OGRTABDriverUnload() */
164 : /************************************************************************/
165 :
166 933 : static void OGRTABDriverUnload(CPL_UNUSED GDALDriver *poDriver)
167 : {
168 933 : MITABFreeCoordSysTable();
169 933 : }
170 :
171 : /************************************************************************/
172 : /* RegisterOGRTAB() */
173 : /************************************************************************/
174 :
175 1595 : void RegisterOGRTAB()
176 :
177 : {
178 1595 : if (GDALGetDriverByName("MapInfo File") != nullptr)
179 302 : return;
180 :
181 1293 : GDALDriver *poDriver = new GDALDriver();
182 :
183 1293 : poDriver->SetDescription("MapInfo File");
184 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
185 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
186 1293 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
187 1293 : poDriver->SetMetadataItem(GDAL_DCAP_DELETE_FIELD, "YES");
188 1293 : poDriver->SetMetadataItem(GDAL_DCAP_REORDER_FIELDS, "YES");
189 1293 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
190 :
191 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "MapInfo File");
192 1293 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "tab mif mid");
193 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/vector/mitab.html");
194 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
195 1293 : poDriver->SetMetadataItem(GDAL_DMD_NUMERIC_FIELD_WIDTH_INCLUDES_SIGN,
196 1293 : "YES");
197 1293 : poDriver->SetMetadataItem(
198 1293 : GDAL_DMD_NUMERIC_FIELD_WIDTH_INCLUDES_DECIMAL_SEPARATOR, "YES");
199 :
200 1293 : poDriver->SetMetadataItem(
201 : GDAL_DS_LAYER_CREATIONOPTIONLIST,
202 : "<LayerCreationOptionList>"
203 : " <Option name='BOUNDS' type='string' "
204 : "description='Custom bounds. Expect format is "
205 : "xmin,ymin,xmax,ymax'/>"
206 : " <Option name='ENCODING' type='string' "
207 : "description='to override the encoding "
208 : "interpretation of the DAT/MID with any encoding "
209 : "supported by CPLRecode or to \"\" to avoid any "
210 : "recoding (Neutral charset)'/>"
211 : " <Option name='DESCRIPTION' type='string' "
212 : "description='Friendly name of table. Only for tab "
213 : "format.'/>" // See
214 : // https://support.pitneybowes.com/SearchArticles/VFP05_KnowledgeWithSidebarHowTo?id=kA180000000CtuHCAS&popup=false&lang=en_US
215 : " <Option name='STRICT_FIELDS_NAME_LAUNDERING' type='boolean' "
216 : "default='YES' description='Field name consisting of alphanumeric "
217 : "only, maximum length 31'/>"
218 1293 : "</LayerCreationOptionList>");
219 :
220 1293 : poDriver->SetMetadataItem(
221 : GDAL_DMD_CREATIONOPTIONLIST,
222 : "<CreationOptionList>"
223 : " <Option name='FORMAT' type='string-select' description='type of "
224 : "MapInfo format'>"
225 : " <Value>MIF</Value>"
226 : " <Value>TAB</Value>"
227 : " </Option>"
228 : " <Option name='SPATIAL_INDEX_MODE' type='string-select' "
229 : "description='type of spatial index' default='QUICK'>"
230 : " <Value>QUICK</Value>"
231 : " <Value>OPTIMIZED</Value>"
232 : " </Option>"
233 : " <Option name='BLOCKSIZE' type='int' description='.map block size' "
234 : "min='512' max='32256' default='512'/>"
235 : " <Option name='ENCODING' type='string' description='to override the "
236 : "encoding interpretation of the DAT/MID with any encoding supported by "
237 : "CPLRecode or to \"\" to avoid any recoding (Neutral charset)'/>"
238 : " <Option name='STRICT_FIELDS_NAME_LAUNDERING' type='boolean' "
239 : "default='YES' description='Field name consisting of alphanumeric "
240 : "only, maximum length 31'/>"
241 1293 : "</CreationOptionList>");
242 :
243 1293 : poDriver->SetMetadataItem(
244 : GDAL_DMD_CREATIONFIELDDATATYPES,
245 1293 : "Integer Integer64 Real String Date DateTime Time");
246 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
247 1293 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
248 1293 : "WidthPrecision");
249 1293 : poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
250 1293 : "Name Type WidthPrecision");
251 1293 : poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES, "YES");
252 1293 : poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES_READ, "YES");
253 1293 : poDriver->SetMetadataItem(GDAL_DCAP_FEATURE_STYLES_WRITE, "YES");
254 :
255 1293 : poDriver->pfnOpen = OGRTABDriverOpen;
256 1293 : poDriver->pfnIdentify = OGRTABDriverIdentify;
257 1293 : poDriver->pfnCreate = OGRTABDriverCreate;
258 1293 : poDriver->pfnDelete = OGRTABDriverDelete;
259 1293 : poDriver->pfnUnloadDriver = OGRTABDriverUnload;
260 :
261 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
262 : }
|