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