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 819 : 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 819 : "AUTHORITY[\"EPSG\",\"27700\"]]")),
40 83538 : papszOptions(nullptr)
41 : {
42 819 : poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
43 :
44 : /* -------------------------------------------------------------------- */
45 : /* Allow initialization of options from the environment. */
46 : /* -------------------------------------------------------------------- */
47 819 : if (getenv("OGR_NTF_OPTIONS") != nullptr)
48 : {
49 0 : papszOptions = CSLTokenizeStringComplex(getenv("OGR_NTF_OPTIONS"), ",",
50 : FALSE, FALSE);
51 : }
52 819 : }
53 :
54 : /************************************************************************/
55 : /* ~OGRNTFDataSource() */
56 : /************************************************************************/
57 :
58 83538 : OGRNTFDataSource::~OGRNTFDataSource()
59 :
60 : {
61 819 : for (int i = 0; i < nNTFFileCount; i++)
62 0 : delete papoNTFFileReader[i];
63 :
64 819 : CPLFree(papoNTFFileReader);
65 :
66 819 : for (int i = 0; i < nLayers; i++)
67 0 : delete papoLayers[i];
68 :
69 819 : if (poFCLayer != nullptr)
70 0 : delete poFCLayer;
71 :
72 819 : CPLFree(papoLayers);
73 :
74 819 : CSLDestroy(papszOptions);
75 :
76 819 : CSLDestroy(papszFCNum);
77 819 : CSLDestroy(papszFCName);
78 :
79 819 : if (poSpatialRef)
80 819 : poSpatialRef->Release();
81 1638 : }
82 :
83 : /************************************************************************/
84 : /* TestCapability() */
85 : /************************************************************************/
86 :
87 0 : int OGRNTFDataSource::TestCapability(const char *pszCap) const
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) const
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() const
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 819 : int OGRNTFDataSource::Open(const char *pszFilename, int bTestOpen,
158 : char **papszLimitedFileList)
159 :
160 : {
161 : VSIStatBufL stat;
162 819 : char **papszFileList = nullptr;
163 :
164 : /* -------------------------------------------------------------------- */
165 : /* Is the given path a directory or a regular file? */
166 : /* -------------------------------------------------------------------- */
167 1638 : if (VSIStatL(pszFilename, &stat) != 0 ||
168 819 : (!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 819 : if (VSI_ISREG(stat.st_mode))
182 : {
183 60 : papszFileList = CSLAddString(nullptr, pszFilename);
184 : }
185 : else
186 : {
187 759 : char **candidateFileList = VSIReadDir(pszFilename);
188 :
189 26841 : for (int i = 0;
190 26841 : candidateFileList != nullptr && candidateFileList[i] != nullptr;
191 : i++)
192 : {
193 26082 : if (papszLimitedFileList != nullptr &&
194 0 : CSLFindString(papszLimitedFileList, candidateFileList[i]) == -1)
195 : {
196 0 : continue;
197 : }
198 :
199 26082 : if (strlen(candidateFileList[i]) > 4 &&
200 24770 : STARTS_WITH_CI(candidateFileList[i] +
201 : strlen(candidateFileList[i]) - 4,
202 : ".ntf"))
203 : {
204 : papszFileList =
205 0 : CSLAddString(papszFileList,
206 0 : CPLFormFilenameSafe(
207 0 : pszFilename, candidateFileList[i], nullptr)
208 : .c_str());
209 : }
210 : }
211 :
212 759 : CSLDestroy(candidateFileList);
213 :
214 759 : if (CSLCount(papszFileList) == 0)
215 : {
216 759 : if (!bTestOpen)
217 0 : CPLError(CE_Failure, CPLE_OpenFailed,
218 : "No candidate NTF files (.ntf) found in\n"
219 : "directory: %s",
220 : pszFilename);
221 759 : CSLDestroy(papszFileList);
222 759 : return FALSE;
223 : }
224 : }
225 :
226 : /* -------------------------------------------------------------------- */
227 : /* Loop over all these files trying to open them. In testopen */
228 : /* mode we first read the first 80 characters, to verify that */
229 : /* it looks like an NTF file. Note that we don't keep the file */
230 : /* open ... we don't want to occupy a lot of file handles when */
231 : /* handling a whole directory. */
232 : /* -------------------------------------------------------------------- */
233 60 : papoNTFFileReader = static_cast<NTFFileReader **>(
234 60 : CPLCalloc(sizeof(void *), CSLCount(papszFileList)));
235 :
236 120 : for (int i = 0; papszFileList != nullptr && papszFileList[i] != nullptr;
237 : i++)
238 : {
239 60 : if (bTestOpen)
240 : {
241 60 : VSILFILE *fp = VSIFOpenL(papszFileList[i], "rb");
242 60 : if (fp == nullptr)
243 60 : continue;
244 :
245 57 : char szHeader[80] = {};
246 57 : if (VSIFReadL(szHeader, 80, 1, fp) < 1)
247 : {
248 57 : VSIFCloseL(fp);
249 57 : continue;
250 : }
251 :
252 0 : VSIFCloseL(fp);
253 :
254 0 : if (!STARTS_WITH_CI(szHeader, "01"))
255 0 : continue;
256 :
257 0 : int j = 0; // Used after for.
258 0 : for (; j < 80; j++)
259 : {
260 0 : if (szHeader[j] == 10 || szHeader[j] == 13)
261 : break;
262 : }
263 :
264 0 : if (j == 80 || (j > 0 && szHeader[j - 1] != '%'))
265 0 : continue;
266 : }
267 :
268 0 : NTFFileReader *poFR = new NTFFileReader(this);
269 :
270 0 : if (!poFR->Open(papszFileList[i]))
271 : {
272 0 : delete poFR;
273 0 : CSLDestroy(papszFileList);
274 :
275 0 : return FALSE;
276 : }
277 :
278 0 : poFR->SetBaseFID(nNTFFileCount * 1000000 + 1);
279 0 : poFR->Close();
280 :
281 0 : EnsureTileNameUnique(poFR);
282 :
283 0 : papoNTFFileReader[nNTFFileCount++] = poFR;
284 : }
285 :
286 60 : CSLDestroy(papszFileList);
287 :
288 60 : if (nNTFFileCount == 0)
289 60 : return FALSE;
290 :
291 : /* -------------------------------------------------------------------- */
292 : /* Establish generic layers. */
293 : /* -------------------------------------------------------------------- */
294 0 : EstablishGenericLayers();
295 :
296 : /* -------------------------------------------------------------------- */
297 : /* Loop over all the files, collecting a unique feature class */
298 : /* listing. */
299 : /* -------------------------------------------------------------------- */
300 0 : for (int iSrcFile = 0; iSrcFile < nNTFFileCount; iSrcFile++)
301 : {
302 0 : NTFFileReader *poSrcReader = papoNTFFileReader[iSrcFile];
303 :
304 0 : for (int iSrcFC = 0; iSrcFC < poSrcReader->GetFCCount(); iSrcFC++)
305 : {
306 0 : char *pszSrcFCName = nullptr;
307 0 : char *pszSrcFCNum = nullptr;
308 :
309 0 : poSrcReader->GetFeatureClass(iSrcFC, &pszSrcFCNum, &pszSrcFCName);
310 :
311 0 : int iDstFC = 0;
312 0 : for (; iDstFC < nFCCount; iDstFC++)
313 : {
314 0 : if (EQUAL(pszSrcFCNum, papszFCNum[iDstFC]))
315 0 : break;
316 : }
317 :
318 0 : if (iDstFC >= nFCCount)
319 : {
320 0 : nFCCount++;
321 0 : papszFCNum = CSLAddString(papszFCNum, pszSrcFCNum);
322 0 : papszFCName = CSLAddString(papszFCName, pszSrcFCName);
323 : }
324 : }
325 : }
326 :
327 : /* -------------------------------------------------------------------- */
328 : /* Create a new layer specifically for feature classes. */
329 : /* -------------------------------------------------------------------- */
330 0 : if (nFCCount > 0)
331 0 : poFCLayer = new OGRNTFFeatureClassLayer(this);
332 : else
333 0 : poFCLayer = nullptr;
334 :
335 0 : return TRUE;
336 : }
337 :
338 : /************************************************************************/
339 : /* ResetReading() */
340 : /* */
341 : /* Cleanup, and start over. */
342 : /************************************************************************/
343 :
344 0 : void OGRNTFDataSource::ResetReading()
345 :
346 : {
347 0 : for (int i = 0; i < nNTFFileCount; i++)
348 0 : papoNTFFileReader[i]->Close();
349 :
350 0 : iCurrentReader = -1;
351 0 : nCurrentPos = (vsi_l_offset)-1;
352 0 : nCurrentFID = 1;
353 0 : iCurrentFC = 0;
354 0 : }
355 :
356 : /************************************************************************/
357 : /* GetNextFeature() */
358 : /************************************************************************/
359 :
360 0 : OGRFeature *OGRNTFDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
361 : double *pdfProgressPct,
362 : GDALProgressFunc /* pfnProgress */,
363 : void * /* pProgressData */)
364 :
365 : {
366 0 : if (pdfProgressPct != nullptr)
367 0 : *pdfProgressPct = 0.0;
368 0 : if (ppoBelongingLayer != nullptr)
369 0 : *ppoBelongingLayer = nullptr;
370 :
371 0 : OGRFeature *poFeature = nullptr;
372 :
373 : /* -------------------------------------------------------------------- */
374 : /* If we have already read all the conventional features, we */
375 : /* should try and return feature class features. */
376 : /* -------------------------------------------------------------------- */
377 0 : if (iCurrentReader == nNTFFileCount)
378 : {
379 0 : if (iCurrentFC < nFCCount)
380 0 : return poFCLayer->GetFeature(iCurrentFC++);
381 : else
382 0 : return nullptr;
383 : }
384 :
385 : /* -------------------------------------------------------------------- */
386 : /* Do we need to open a file? */
387 : /* -------------------------------------------------------------------- */
388 0 : if (iCurrentReader == -1)
389 : {
390 0 : iCurrentReader++;
391 0 : nCurrentPos = (vsi_l_offset)-1;
392 : }
393 :
394 0 : if (papoNTFFileReader[iCurrentReader]->GetFP() == nullptr)
395 : {
396 0 : papoNTFFileReader[iCurrentReader]->Open();
397 : }
398 :
399 : /* -------------------------------------------------------------------- */
400 : /* Ensure we are reading on from the same point we were reading */
401 : /* from for the last feature, even if some other access */
402 : /* mechanism has moved the file pointer. */
403 : /* -------------------------------------------------------------------- */
404 0 : if (nCurrentPos != (vsi_l_offset)-1)
405 0 : papoNTFFileReader[iCurrentReader]->SetFPPos(nCurrentPos, nCurrentFID);
406 :
407 : /* -------------------------------------------------------------------- */
408 : /* Read a feature. If we get NULL the file must be all */
409 : /* consumed, advance to the next file. */
410 : /* -------------------------------------------------------------------- */
411 0 : poFeature = papoNTFFileReader[iCurrentReader]->ReadOGRFeature();
412 0 : if (poFeature == nullptr)
413 : {
414 0 : papoNTFFileReader[iCurrentReader]->Close();
415 0 : if (GetOption("CACHING") != nullptr &&
416 0 : EQUAL(GetOption("CACHING"), "OFF"))
417 0 : papoNTFFileReader[iCurrentReader]->DestroyIndex();
418 :
419 0 : iCurrentReader++;
420 0 : nCurrentPos = (vsi_l_offset)-1;
421 0 : nCurrentFID = 1;
422 :
423 0 : poFeature = GetNextFeature(nullptr, nullptr, nullptr, nullptr);
424 : }
425 : else
426 : {
427 0 : papoNTFFileReader[iCurrentReader]->GetFPPos(&nCurrentPos, &nCurrentFID);
428 : }
429 :
430 0 : return poFeature;
431 : }
432 :
433 : /************************************************************************/
434 : /* GetFeatureClass() */
435 : /************************************************************************/
436 :
437 0 : int OGRNTFDataSource::GetFeatureClass(int iFCIndex, char **ppszFCId,
438 : char **ppszFCName)
439 :
440 : {
441 0 : if (iFCIndex < 0 || iFCIndex >= nFCCount)
442 : {
443 0 : *ppszFCId = nullptr;
444 0 : *ppszFCName = nullptr;
445 0 : return FALSE;
446 : }
447 : else
448 : {
449 0 : *ppszFCId = papszFCNum[iFCIndex];
450 0 : *ppszFCName = papszFCName[iFCIndex];
451 0 : return TRUE;
452 : }
453 : }
454 :
455 : /************************************************************************/
456 : /* SetOptions() */
457 : /************************************************************************/
458 :
459 0 : void OGRNTFDataSource::SetOptionList(char **papszNewOptions)
460 :
461 : {
462 0 : CSLDestroy(papszOptions);
463 0 : papszOptions = CSLDuplicate(papszNewOptions);
464 0 : }
465 :
466 : /************************************************************************/
467 : /* GetOption() */
468 : /************************************************************************/
469 :
470 0 : const char *OGRNTFDataSource::GetOption(const char *pszOption)
471 :
472 : {
473 0 : return CSLFetchNameValue(papszOptions, pszOption);
474 : }
475 :
476 : /************************************************************************/
477 : /* EnsureTileNameUnique() */
478 : /* */
479 : /* This method is called with an NTFFileReader to ensure that */
480 : /* its tilename is unique relative to all the readers already */
481 : /* assigned to this data source. If not, a unique name is */
482 : /* selected for it and assigned. This method should not be */
483 : /* called with readers that are already attached to the data */
484 : /* source. */
485 : /************************************************************************/
486 :
487 0 : void OGRNTFDataSource::EnsureTileNameUnique(NTFFileReader *poNewReader)
488 :
489 : {
490 0 : int iSequenceNumber = -1;
491 0 : bool bIsUnique = false;
492 0 : char szCandidateName[12] = {};
493 :
494 0 : do
495 : {
496 0 : bIsUnique = TRUE;
497 0 : if (iSequenceNumber++ == -1)
498 0 : strncpy(szCandidateName, poNewReader->GetTileName(),
499 : sizeof(szCandidateName) - 1);
500 : else
501 0 : snprintf(szCandidateName, sizeof(szCandidateName), "%010d",
502 : iSequenceNumber);
503 :
504 0 : for (int iReader = 0; iReader < nNTFFileCount && bIsUnique; iReader++)
505 : {
506 0 : const char *pszTileName = GetFileReader(iReader)->GetTileName();
507 0 : if (pszTileName != nullptr &&
508 0 : strcmp(szCandidateName, pszTileName) == 0)
509 : {
510 0 : bIsUnique = FALSE;
511 : }
512 : }
513 0 : } while (!bIsUnique);
514 :
515 0 : if (iSequenceNumber > 0)
516 : {
517 0 : poNewReader->OverrideTileName(szCandidateName);
518 0 : CPLError(CE_Warning, CPLE_AppDefined,
519 : "Forcing TILE_REF to `%s' on file %s\n"
520 : "to avoid conflict with other tiles in this data source.",
521 : szCandidateName, poNewReader->GetFilename());
522 : }
523 0 : }
|