Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: UK NTF Reader
4 : * Purpose: Implements OGRNTFDataSource class
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ntf.h"
14 : #include "cpl_conv.h"
15 : #include "cpl_string.h"
16 :
17 : /************************************************************************/
18 : /* OGRNTFDataSource() */
19 : /************************************************************************/
20 :
21 708 : OGRNTFDataSource::OGRNTFDataSource()
22 : : nLayers(0), papoLayers(nullptr), poFCLayer(nullptr), iCurrentFC(0),
23 : iCurrentReader(-1), nCurrentPos(0), nCurrentFID(0), nNTFFileCount(0),
24 : papoNTFFileReader(nullptr), nFCCount(0), papszFCNum(nullptr),
25 : papszFCName(nullptr),
26 : poSpatialRef(new OGRSpatialReference(
27 : "PROJCS[\"OSGB 1936 / British National Grid\",GEOGCS[\"OSGB 1936\","
28 : "DATUM[\"OSGB_1936\",SPHEROID[\"Airy 1830\",6377563.396,299.3249646,"
29 : "AUTHORITY[\"EPSG\",\"7001\"]],AUTHORITY[\"EPSG\",\"6277\"]],"
30 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
31 : "UNIT[\"degree\",0.0174532925199433],AUTHORITY[\"EPSG\",\"4277\"]],"
32 : "PROJECTION[\"Transverse_Mercator\"],"
33 : "PARAMETER[\"latitude_of_origin\",49],"
34 : "PARAMETER[\"central_meridian\",-2],"
35 : "PARAMETER[\"scale_factor\",0.999601272],"
36 : "PARAMETER[\"false_easting\",400000],"
37 : "PARAMETER[\"false_northing\",-100000],"
38 : "UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],"
39 708 : "AUTHORITY[\"EPSG\",\"27700\"]]")),
40 72216 : papszOptions(nullptr)
41 : {
42 708 : poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
43 :
44 : /* -------------------------------------------------------------------- */
45 : /* Allow initialization of options from the environment. */
46 : /* -------------------------------------------------------------------- */
47 708 : if (getenv("OGR_NTF_OPTIONS") != nullptr)
48 : {
49 0 : papszOptions = CSLTokenizeStringComplex(getenv("OGR_NTF_OPTIONS"), ",",
50 : FALSE, FALSE);
51 : }
52 708 : }
53 :
54 : /************************************************************************/
55 : /* ~OGRNTFDataSource() */
56 : /************************************************************************/
57 :
58 72216 : OGRNTFDataSource::~OGRNTFDataSource()
59 :
60 : {
61 708 : for (int i = 0; i < nNTFFileCount; i++)
62 0 : delete papoNTFFileReader[i];
63 :
64 708 : CPLFree(papoNTFFileReader);
65 :
66 708 : for (int i = 0; i < nLayers; i++)
67 0 : delete papoLayers[i];
68 :
69 708 : if (poFCLayer != nullptr)
70 0 : delete poFCLayer;
71 :
72 708 : CPLFree(papoLayers);
73 :
74 708 : CSLDestroy(papszOptions);
75 :
76 708 : CSLDestroy(papszFCNum);
77 708 : CSLDestroy(papszFCName);
78 :
79 708 : if (poSpatialRef)
80 708 : poSpatialRef->Release();
81 1416 : }
82 :
83 : /************************************************************************/
84 : /* TestCapability() */
85 : /************************************************************************/
86 :
87 0 : int OGRNTFDataSource::TestCapability(const char *pszCap)
88 :
89 : {
90 0 : if (EQUAL(pszCap, ODsCZGeometries))
91 0 : return true;
92 :
93 0 : return false;
94 : }
95 :
96 : /************************************************************************/
97 : /* GetNamedLayer() */
98 : /************************************************************************/
99 :
100 0 : OGRNTFLayer *OGRNTFDataSource::GetNamedLayer(const char *pszNameIn)
101 :
102 : {
103 0 : for (int i = 0; i < nLayers; i++)
104 : {
105 0 : if (EQUAL(papoLayers[i]->GetLayerDefn()->GetName(), pszNameIn))
106 0 : return static_cast<OGRNTFLayer *>(papoLayers[i]);
107 : }
108 :
109 0 : return nullptr;
110 : }
111 :
112 : /************************************************************************/
113 : /* AddLayer() */
114 : /************************************************************************/
115 :
116 0 : void OGRNTFDataSource::AddLayer(OGRLayer *poNewLayer)
117 :
118 : {
119 0 : papoLayers = static_cast<OGRLayer **>(
120 0 : CPLRealloc(papoLayers, sizeof(void *) * ++nLayers));
121 :
122 0 : papoLayers[nLayers - 1] = poNewLayer;
123 0 : }
124 :
125 : /************************************************************************/
126 : /* GetLayer() */
127 : /************************************************************************/
128 :
129 0 : OGRLayer *OGRNTFDataSource::GetLayer(int iLayer)
130 :
131 : {
132 0 : if (iLayer < 0 || iLayer > nLayers)
133 0 : return nullptr;
134 0 : else if (iLayer == nLayers)
135 0 : return poFCLayer;
136 : else
137 0 : return papoLayers[iLayer];
138 : }
139 :
140 : /************************************************************************/
141 : /* GetLayerCount() */
142 : /************************************************************************/
143 :
144 0 : int OGRNTFDataSource::GetLayerCount()
145 :
146 : {
147 0 : if (poFCLayer == nullptr)
148 0 : return nLayers;
149 : else
150 0 : return nLayers + 1;
151 : }
152 :
153 : /************************************************************************/
154 : /* Open() */
155 : /************************************************************************/
156 :
157 708 : int OGRNTFDataSource::Open(const char *pszFilename, int bTestOpen,
158 : char **papszLimitedFileList)
159 :
160 : {
161 : VSIStatBufL stat;
162 708 : char **papszFileList = nullptr;
163 :
164 : /* -------------------------------------------------------------------- */
165 : /* Is the given path a directory or a regular file? */
166 : /* -------------------------------------------------------------------- */
167 1416 : if (VSIStatL(pszFilename, &stat) != 0 ||
168 708 : (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)))
169 : {
170 0 : if (!bTestOpen)
171 0 : CPLError(CE_Failure, CPLE_AppDefined,
172 : "%s is neither a file or directory, NTF access failed.\n",
173 : pszFilename);
174 :
175 0 : return FALSE;
176 : }
177 :
178 : /* -------------------------------------------------------------------- */
179 : /* Build a list of filenames we figure are NTF files. */
180 : /* -------------------------------------------------------------------- */
181 708 : if (VSI_ISREG(stat.st_mode))
182 : {
183 52 : papszFileList = CSLAddString(nullptr, pszFilename);
184 : }
185 : else
186 : {
187 656 : char **candidateFileList = VSIReadDir(pszFilename);
188 :
189 27772 : for (int i = 0;
190 27772 : candidateFileList != nullptr && candidateFileList[i] != nullptr;
191 : i++)
192 : {
193 27116 : if (papszLimitedFileList != nullptr &&
194 0 : CSLFindString(papszLimitedFileList, candidateFileList[i]) == -1)
195 : {
196 0 : continue;
197 : }
198 :
199 27116 : if (strlen(candidateFileList[i]) > 4 &&
200 25952 : STARTS_WITH_CI(candidateFileList[i] +
201 : strlen(candidateFileList[i]) - 4,
202 : ".ntf"))
203 : {
204 : char fullFilename[2048];
205 :
206 0 : snprintf(fullFilename, sizeof(fullFilename), "%s%c%s",
207 : pszFilename,
208 : #ifdef _WIN32
209 : '\\',
210 : #else
211 : '/',
212 : #endif
213 0 : candidateFileList[i]);
214 :
215 0 : papszFileList = CSLAddString(papszFileList, fullFilename);
216 : }
217 : }
218 :
219 656 : CSLDestroy(candidateFileList);
220 :
221 656 : if (CSLCount(papszFileList) == 0)
222 : {
223 656 : if (!bTestOpen)
224 0 : CPLError(CE_Failure, CPLE_OpenFailed,
225 : "No candidate NTF files (.ntf) found in\n"
226 : "directory: %s",
227 : pszFilename);
228 656 : CSLDestroy(papszFileList);
229 656 : return FALSE;
230 : }
231 : }
232 :
233 : /* -------------------------------------------------------------------- */
234 : /* Loop over all these files trying to open them. In testopen */
235 : /* mode we first read the first 80 characters, to verify that */
236 : /* it looks like an NTF file. Note that we don't keep the file */
237 : /* open ... we don't want to occupy a lot of file handles when */
238 : /* handling a whole directory. */
239 : /* -------------------------------------------------------------------- */
240 52 : papoNTFFileReader = static_cast<NTFFileReader **>(
241 52 : CPLCalloc(sizeof(void *), CSLCount(papszFileList)));
242 :
243 104 : for (int i = 0; papszFileList != nullptr && papszFileList[i] != nullptr;
244 : i++)
245 : {
246 52 : if (bTestOpen)
247 : {
248 52 : VSILFILE *fp = VSIFOpenL(papszFileList[i], "rb");
249 52 : if (fp == nullptr)
250 52 : continue;
251 :
252 49 : char szHeader[80] = {};
253 49 : if (VSIFReadL(szHeader, 80, 1, fp) < 1)
254 : {
255 49 : VSIFCloseL(fp);
256 49 : continue;
257 : }
258 :
259 0 : VSIFCloseL(fp);
260 :
261 0 : if (!STARTS_WITH_CI(szHeader, "01"))
262 0 : continue;
263 :
264 0 : int j = 0; // Used after for.
265 0 : for (; j < 80; j++)
266 : {
267 0 : if (szHeader[j] == 10 || szHeader[j] == 13)
268 : break;
269 : }
270 :
271 0 : if (j == 80 || (j > 0 && szHeader[j - 1] != '%'))
272 0 : continue;
273 : }
274 :
275 0 : NTFFileReader *poFR = new NTFFileReader(this);
276 :
277 0 : if (!poFR->Open(papszFileList[i]))
278 : {
279 0 : delete poFR;
280 0 : CSLDestroy(papszFileList);
281 :
282 0 : return FALSE;
283 : }
284 :
285 0 : poFR->SetBaseFID(nNTFFileCount * 1000000 + 1);
286 0 : poFR->Close();
287 :
288 0 : EnsureTileNameUnique(poFR);
289 :
290 0 : papoNTFFileReader[nNTFFileCount++] = poFR;
291 : }
292 :
293 52 : CSLDestroy(papszFileList);
294 :
295 52 : if (nNTFFileCount == 0)
296 52 : return FALSE;
297 :
298 : /* -------------------------------------------------------------------- */
299 : /* Establish generic layers. */
300 : /* -------------------------------------------------------------------- */
301 0 : EstablishGenericLayers();
302 :
303 : /* -------------------------------------------------------------------- */
304 : /* Loop over all the files, collecting a unique feature class */
305 : /* listing. */
306 : /* -------------------------------------------------------------------- */
307 0 : for (int iSrcFile = 0; iSrcFile < nNTFFileCount; iSrcFile++)
308 : {
309 0 : NTFFileReader *poSrcReader = papoNTFFileReader[iSrcFile];
310 :
311 0 : for (int iSrcFC = 0; iSrcFC < poSrcReader->GetFCCount(); iSrcFC++)
312 : {
313 0 : char *pszSrcFCName = nullptr;
314 0 : char *pszSrcFCNum = nullptr;
315 :
316 0 : poSrcReader->GetFeatureClass(iSrcFC, &pszSrcFCNum, &pszSrcFCName);
317 :
318 0 : int iDstFC = 0;
319 0 : for (; iDstFC < nFCCount; iDstFC++)
320 : {
321 0 : if (EQUAL(pszSrcFCNum, papszFCNum[iDstFC]))
322 0 : break;
323 : }
324 :
325 0 : if (iDstFC >= nFCCount)
326 : {
327 0 : nFCCount++;
328 0 : papszFCNum = CSLAddString(papszFCNum, pszSrcFCNum);
329 0 : papszFCName = CSLAddString(papszFCName, pszSrcFCName);
330 : }
331 : }
332 : }
333 :
334 : /* -------------------------------------------------------------------- */
335 : /* Create a new layer specifically for feature classes. */
336 : /* -------------------------------------------------------------------- */
337 0 : if (nFCCount > 0)
338 0 : poFCLayer = new OGRNTFFeatureClassLayer(this);
339 : else
340 0 : poFCLayer = nullptr;
341 :
342 0 : return TRUE;
343 : }
344 :
345 : /************************************************************************/
346 : /* ResetReading() */
347 : /* */
348 : /* Cleanup, and start over. */
349 : /************************************************************************/
350 :
351 0 : void OGRNTFDataSource::ResetReading()
352 :
353 : {
354 0 : for (int i = 0; i < nNTFFileCount; i++)
355 0 : papoNTFFileReader[i]->Close();
356 :
357 0 : iCurrentReader = -1;
358 0 : nCurrentPos = (vsi_l_offset)-1;
359 0 : nCurrentFID = 1;
360 0 : iCurrentFC = 0;
361 0 : }
362 :
363 : /************************************************************************/
364 : /* GetNextFeature() */
365 : /************************************************************************/
366 :
367 0 : OGRFeature *OGRNTFDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
368 : double *pdfProgressPct,
369 : GDALProgressFunc /* pfnProgress */,
370 : void * /* pProgressData */)
371 :
372 : {
373 0 : if (pdfProgressPct != nullptr)
374 0 : *pdfProgressPct = 0.0;
375 0 : if (ppoBelongingLayer != nullptr)
376 0 : *ppoBelongingLayer = nullptr;
377 :
378 0 : OGRFeature *poFeature = nullptr;
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* If we have already read all the conventional features, we */
382 : /* should try and return feature class features. */
383 : /* -------------------------------------------------------------------- */
384 0 : if (iCurrentReader == nNTFFileCount)
385 : {
386 0 : if (iCurrentFC < nFCCount)
387 0 : return poFCLayer->GetFeature(iCurrentFC++);
388 : else
389 0 : return nullptr;
390 : }
391 :
392 : /* -------------------------------------------------------------------- */
393 : /* Do we need to open a file? */
394 : /* -------------------------------------------------------------------- */
395 0 : if (iCurrentReader == -1)
396 : {
397 0 : iCurrentReader++;
398 0 : nCurrentPos = (vsi_l_offset)-1;
399 : }
400 :
401 0 : if (papoNTFFileReader[iCurrentReader]->GetFP() == nullptr)
402 : {
403 0 : papoNTFFileReader[iCurrentReader]->Open();
404 : }
405 :
406 : /* -------------------------------------------------------------------- */
407 : /* Ensure we are reading on from the same point we were reading */
408 : /* from for the last feature, even if some other access */
409 : /* mechanism has moved the file pointer. */
410 : /* -------------------------------------------------------------------- */
411 0 : if (nCurrentPos != (vsi_l_offset)-1)
412 0 : papoNTFFileReader[iCurrentReader]->SetFPPos(nCurrentPos, nCurrentFID);
413 :
414 : /* -------------------------------------------------------------------- */
415 : /* Read a feature. If we get NULL the file must be all */
416 : /* consumed, advance to the next file. */
417 : /* -------------------------------------------------------------------- */
418 0 : poFeature = papoNTFFileReader[iCurrentReader]->ReadOGRFeature();
419 0 : if (poFeature == nullptr)
420 : {
421 0 : papoNTFFileReader[iCurrentReader]->Close();
422 0 : if (GetOption("CACHING") != nullptr &&
423 0 : EQUAL(GetOption("CACHING"), "OFF"))
424 0 : papoNTFFileReader[iCurrentReader]->DestroyIndex();
425 :
426 0 : iCurrentReader++;
427 0 : nCurrentPos = (vsi_l_offset)-1;
428 0 : nCurrentFID = 1;
429 :
430 0 : poFeature = GetNextFeature(nullptr, nullptr, nullptr, nullptr);
431 : }
432 : else
433 : {
434 0 : papoNTFFileReader[iCurrentReader]->GetFPPos(&nCurrentPos, &nCurrentFID);
435 : }
436 :
437 0 : return poFeature;
438 : }
439 :
440 : /************************************************************************/
441 : /* GetFeatureClass() */
442 : /************************************************************************/
443 :
444 0 : int OGRNTFDataSource::GetFeatureClass(int iFCIndex, char **ppszFCId,
445 : char **ppszFCName)
446 :
447 : {
448 0 : if (iFCIndex < 0 || iFCIndex >= nFCCount)
449 : {
450 0 : *ppszFCId = nullptr;
451 0 : *ppszFCName = nullptr;
452 0 : return FALSE;
453 : }
454 : else
455 : {
456 0 : *ppszFCId = papszFCNum[iFCIndex];
457 0 : *ppszFCName = papszFCName[iFCIndex];
458 0 : return TRUE;
459 : }
460 : }
461 :
462 : /************************************************************************/
463 : /* SetOptions() */
464 : /************************************************************************/
465 :
466 0 : void OGRNTFDataSource::SetOptionList(char **papszNewOptions)
467 :
468 : {
469 0 : CSLDestroy(papszOptions);
470 0 : papszOptions = CSLDuplicate(papszNewOptions);
471 0 : }
472 :
473 : /************************************************************************/
474 : /* GetOption() */
475 : /************************************************************************/
476 :
477 0 : const char *OGRNTFDataSource::GetOption(const char *pszOption)
478 :
479 : {
480 0 : return CSLFetchNameValue(papszOptions, pszOption);
481 : }
482 :
483 : /************************************************************************/
484 : /* EnsureTileNameUnique() */
485 : /* */
486 : /* This method is called with an NTFFileReader to ensure that */
487 : /* its tilename is unique relative to all the readers already */
488 : /* assigned to this data source. If not, a unique name is */
489 : /* selected for it and assigned. This method should not be */
490 : /* called with readers that are already attached to the data */
491 : /* source. */
492 : /************************************************************************/
493 :
494 0 : void OGRNTFDataSource::EnsureTileNameUnique(NTFFileReader *poNewReader)
495 :
496 : {
497 0 : int iSequenceNumber = -1;
498 0 : bool bIsUnique = false;
499 0 : char szCandidateName[12] = {};
500 :
501 0 : do
502 : {
503 0 : bIsUnique = TRUE;
504 0 : if (iSequenceNumber++ == -1)
505 0 : strncpy(szCandidateName, poNewReader->GetTileName(),
506 : sizeof(szCandidateName) - 1);
507 : else
508 0 : snprintf(szCandidateName, sizeof(szCandidateName), "%010d",
509 : iSequenceNumber);
510 :
511 0 : for (int iReader = 0; iReader < nNTFFileCount && bIsUnique; iReader++)
512 : {
513 0 : const char *pszTileName = GetFileReader(iReader)->GetTileName();
514 0 : if (pszTileName != nullptr &&
515 0 : strcmp(szCandidateName, pszTileName) == 0)
516 : {
517 0 : bIsUnique = FALSE;
518 : }
519 : }
520 0 : } while (!bIsUnique);
521 :
522 0 : if (iSequenceNumber > 0)
523 : {
524 0 : poNewReader->OverrideTileName(szCandidateName);
525 0 : CPLError(CE_Warning, CPLE_AppDefined,
526 : "Forcing TILE_REF to `%s' on file %s\n"
527 : "to avoid conflict with other tiles in this data source.",
528 : szCandidateName, poNewReader->GetFilename());
529 : }
530 0 : }
|