Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: NTF Translator
4 : * Purpose: Handle NTF products that aren't recognised generically.
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 <stdarg.h>
14 : #include "ntf.h"
15 : #include "cpl_string.h"
16 :
17 : /************************************************************************/
18 : /* ==================================================================== */
19 : /* NTFGenericClass */
20 : /* */
21 : /* The NTFGenericClass class exists to hold aggregated */
22 : /* information for each type of record encountered in a set of */
23 : /* NTF files, primarily the list of attributes actually */
24 : /* encountered. */
25 : /* ==================================================================== */
26 : /************************************************************************/
27 :
28 : /************************************************************************/
29 : /* NTFGenericClass */
30 : /************************************************************************/
31 :
32 70800 : NTFGenericClass::NTFGenericClass()
33 : : nFeatureCount(0), b3D(FALSE), nAttrCount(0), papszAttrNames(nullptr),
34 : papszAttrFormats(nullptr), panAttrMaxWidth(nullptr),
35 70800 : pabAttrMultiple(nullptr)
36 : {
37 70800 : }
38 :
39 : /************************************************************************/
40 : /* ~NTFGenericClass */
41 : /************************************************************************/
42 :
43 141600 : NTFGenericClass::~NTFGenericClass()
44 :
45 : {
46 70800 : CSLDestroy(papszAttrNames);
47 70800 : CSLDestroy(papszAttrFormats);
48 70800 : CPLFree(panAttrMaxWidth);
49 70800 : CPLFree(pabAttrMultiple);
50 70800 : }
51 :
52 : /************************************************************************/
53 : /* CheckAddAttr() */
54 : /* */
55 : /* Check if an attribute already exists. If not add it with */
56 : /* its format. Note we don't check for format conflicts at */
57 : /* this time. */
58 : /************************************************************************/
59 :
60 0 : void NTFGenericClass::CheckAddAttr(const char *pszName, const char *pszFormat,
61 : int nWidth)
62 :
63 : {
64 0 : if (EQUAL(pszName, "TX"))
65 0 : pszName = "TEXT";
66 0 : if (EQUAL(pszName, "FC"))
67 0 : pszName = "FEAT_CODE";
68 :
69 0 : const int iAttrOffset = CSLFindString(papszAttrNames, pszName);
70 :
71 0 : if (iAttrOffset == -1)
72 : {
73 0 : nAttrCount++;
74 :
75 0 : papszAttrNames = CSLAddString(papszAttrNames, pszName);
76 0 : papszAttrFormats = CSLAddString(papszAttrFormats, pszFormat);
77 :
78 0 : panAttrMaxWidth = static_cast<int *>(
79 0 : CPLRealloc(panAttrMaxWidth, sizeof(int) * nAttrCount));
80 :
81 0 : panAttrMaxWidth[nAttrCount - 1] = nWidth;
82 :
83 0 : pabAttrMultiple = static_cast<int *>(
84 0 : CPLRealloc(pabAttrMultiple, sizeof(int) * nAttrCount));
85 :
86 0 : pabAttrMultiple[nAttrCount - 1] = FALSE;
87 : }
88 : else
89 : {
90 0 : if (panAttrMaxWidth[iAttrOffset] < nWidth)
91 0 : panAttrMaxWidth[iAttrOffset] = nWidth;
92 : }
93 0 : }
94 :
95 : /************************************************************************/
96 : /* SetMultiple() */
97 : /* */
98 : /* Mark this attribute as appearing multiple times on some */
99 : /* features. */
100 : /************************************************************************/
101 :
102 0 : void NTFGenericClass::SetMultiple(const char *pszName)
103 :
104 : {
105 0 : if (EQUAL(pszName, "TX"))
106 0 : pszName = "TEXT";
107 0 : if (EQUAL(pszName, "FC"))
108 0 : pszName = "FEAT_CODE";
109 :
110 0 : const int iAttrOffset = CSLFindString(papszAttrNames, pszName);
111 0 : if (iAttrOffset == -1)
112 0 : return;
113 :
114 0 : pabAttrMultiple[iAttrOffset] = TRUE;
115 : }
116 :
117 : /************************************************************************/
118 : /* WorkupGeneric() */
119 : /* */
120 : /* Scan a whole file, in order to build up a list of attributes */
121 : /* for the generic types. */
122 : /************************************************************************/
123 :
124 0 : void OGRNTFDataSource::WorkupGeneric(NTFFileReader *poReader)
125 :
126 : {
127 0 : NTFRecord **papoGroup = nullptr;
128 :
129 0 : if (poReader->GetNTFLevel() > 2)
130 : {
131 0 : poReader->IndexFile();
132 0 : if (CPLGetLastErrorType() == CE_Failure)
133 0 : return;
134 : }
135 : else
136 0 : poReader->Reset();
137 :
138 : /* ==================================================================== */
139 : /* Read all record groups in the file. */
140 : /* ==================================================================== */
141 : while (true)
142 : {
143 : /* --------------------------------------------------------------------
144 : */
145 : /* Read a record group */
146 : /* --------------------------------------------------------------------
147 : */
148 0 : if (poReader->GetNTFLevel() > 2)
149 0 : papoGroup = poReader->GetNextIndexedRecordGroup(papoGroup);
150 : else
151 0 : papoGroup = poReader->ReadRecordGroup();
152 :
153 0 : if (papoGroup == nullptr || papoGroup[0]->GetType() < 0 ||
154 0 : papoGroup[0]->GetType() >= 99)
155 0 : break;
156 :
157 : /* --------------------------------------------------------------------
158 : */
159 : /* Get the class corresponding to the anchor record. */
160 : /* --------------------------------------------------------------------
161 : */
162 0 : NTFGenericClass *poClass = GetGClass(papoGroup[0]->GetType());
163 0 : char **papszFullAttList = nullptr;
164 :
165 0 : poClass->nFeatureCount++;
166 :
167 : /* --------------------------------------------------------------------
168 : */
169 : /* Loop over constituent records collecting attributes. */
170 : /* --------------------------------------------------------------------
171 : */
172 0 : for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
173 : {
174 0 : NTFRecord *poRecord = papoGroup[iRec];
175 :
176 0 : switch (poRecord->GetType())
177 : {
178 0 : case NRT_ATTREC:
179 : {
180 : char **papszTypes, **papszValues;
181 :
182 0 : poReader->ProcessAttRec(poRecord, nullptr, &papszTypes,
183 : &papszValues);
184 :
185 0 : for (int iAtt = 0;
186 0 : papszTypes != nullptr && papszTypes[iAtt] != nullptr;
187 : iAtt++)
188 : {
189 : NTFAttDesc *poAttDesc =
190 0 : poReader->GetAttDesc(papszTypes[iAtt]);
191 0 : if (poAttDesc != nullptr &&
192 0 : papszValues[iAtt] != nullptr)
193 : {
194 0 : poClass->CheckAddAttr(
195 0 : poAttDesc->val_type, poAttDesc->finter,
196 0 : static_cast<int>(strlen(papszValues[iAtt])));
197 : }
198 :
199 0 : if (CSLFindString(papszFullAttList, papszTypes[iAtt]) ==
200 : -1)
201 0 : papszFullAttList = CSLAddString(papszFullAttList,
202 0 : papszTypes[iAtt]);
203 0 : else if (poAttDesc != nullptr)
204 0 : poClass->SetMultiple(poAttDesc->val_type);
205 : }
206 :
207 0 : CSLDestroy(papszTypes);
208 0 : CSLDestroy(papszValues);
209 : }
210 0 : break;
211 :
212 0 : case NRT_TEXTREP:
213 : case NRT_NAMEPOSTN:
214 0 : poClass->CheckAddAttr("FONT", "I4", 4);
215 0 : poClass->CheckAddAttr("TEXT_HT", "R3,1", 3);
216 0 : poClass->CheckAddAttr("TEXT_HT_GROUND", "R9,3", 9);
217 0 : poClass->CheckAddAttr("TEXT_HT", "R3,1", 3);
218 0 : poClass->CheckAddAttr("DIG_POSTN", "I1", 1);
219 0 : poClass->CheckAddAttr("ORIENT", "R4,1", 4);
220 0 : break;
221 :
222 0 : case NRT_NAMEREC:
223 0 : poClass->CheckAddAttr("TEXT", "A*",
224 : atoi(poRecord->GetField(13, 14)));
225 0 : break;
226 :
227 0 : case NRT_GEOMETRY:
228 : case NRT_GEOMETRY3D:
229 0 : if (atoi(poRecord->GetField(3, 8)) != 0)
230 0 : poClass->CheckAddAttr("GEOM_ID", "I6", 6);
231 0 : if (poRecord->GetType() == NRT_GEOMETRY3D)
232 0 : poClass->b3D = TRUE;
233 0 : break;
234 :
235 0 : case NRT_POINTREC:
236 : case NRT_LINEREC:
237 0 : if (poReader->GetNTFLevel() < 3)
238 : {
239 : NTFAttDesc *poAttDesc =
240 0 : poReader->GetAttDesc(poRecord->GetField(9, 10));
241 0 : if (poAttDesc != nullptr)
242 0 : poClass->CheckAddAttr(poAttDesc->val_type,
243 0 : poAttDesc->finter, 6);
244 :
245 0 : if (!EQUAL(poRecord->GetField(17, 20), " "))
246 0 : poClass->CheckAddAttr("FEAT_CODE", "A4", 4);
247 : }
248 0 : break;
249 :
250 0 : default:
251 0 : break;
252 : }
253 : }
254 :
255 0 : CSLDestroy(papszFullAttList);
256 0 : }
257 :
258 0 : if (GetOption("CACHING") != nullptr && EQUAL(GetOption("CACHING"), "OFF"))
259 0 : poReader->DestroyIndex();
260 :
261 0 : poReader->Reset();
262 : }
263 :
264 : /************************************************************************/
265 : /* AddGenericAttributes() */
266 : /************************************************************************/
267 :
268 0 : static void AddGenericAttributes(NTFFileReader *poReader, NTFRecord **papoGroup,
269 : OGRFeature *poFeature)
270 :
271 : {
272 0 : char **papszTypes = nullptr;
273 0 : char **papszValues = nullptr;
274 :
275 0 : if (!poReader->ProcessAttRecGroup(papoGroup, &papszTypes, &papszValues))
276 0 : return;
277 :
278 0 : for (int iAtt = 0; papszTypes != nullptr && papszTypes[iAtt] != nullptr;
279 : iAtt++)
280 : {
281 0 : int iField = 0;
282 :
283 0 : if (EQUAL(papszTypes[iAtt], "TX"))
284 0 : iField = poFeature->GetFieldIndex("TEXT");
285 0 : else if (EQUAL(papszTypes[iAtt], "FC"))
286 0 : iField = poFeature->GetFieldIndex("FEAT_CODE");
287 : else
288 0 : iField = poFeature->GetFieldIndex(papszTypes[iAtt]);
289 :
290 0 : if (iField == -1)
291 0 : continue;
292 :
293 0 : poReader->ApplyAttributeValue(poFeature, iField, papszTypes[iAtt],
294 : papszTypes, papszValues);
295 :
296 : /* --------------------------------------------------------------------
297 : */
298 : /* Do we have a corresponding list field we should be */
299 : /* accumulating this into? */
300 : /* --------------------------------------------------------------------
301 : */
302 0 : char szListName[128] = {};
303 :
304 0 : snprintf(szListName, sizeof(szListName), "%s_LIST",
305 : poFeature->GetFieldDefnRef(iField)->GetNameRef());
306 0 : const int iListField = poFeature->GetFieldIndex(szListName);
307 :
308 : /* --------------------------------------------------------------------
309 : */
310 : /* Yes, so perform processing similar to ApplyAttributeValue(), */
311 : /* and append to list value. */
312 : /* --------------------------------------------------------------------
313 : */
314 0 : if (iListField != -1)
315 : {
316 0 : const char *pszAttLongName = nullptr;
317 0 : const char *pszAttValue = nullptr;
318 0 : const char *pszCodeDesc = nullptr;
319 :
320 0 : poReader->ProcessAttValue(papszTypes[iAtt], papszValues[iAtt],
321 : &pszAttLongName, &pszAttValue,
322 : &pszCodeDesc);
323 :
324 0 : if (poFeature->IsFieldSetAndNotNull(iListField))
325 : {
326 0 : poFeature->SetField(
327 : iListField,
328 : CPLSPrintf("%s,%s", poFeature->GetFieldAsString(iListField),
329 : pszAttValue));
330 : }
331 : else
332 : {
333 0 : poFeature->SetField(iListField, pszAttValue);
334 : }
335 : }
336 : }
337 :
338 0 : CSLDestroy(papszTypes);
339 0 : CSLDestroy(papszValues);
340 : }
341 :
342 : /************************************************************************/
343 : /* TranslateGenericNode() */
344 : /************************************************************************/
345 :
346 0 : static OGRFeature *TranslateGenericNode(NTFFileReader *poReader,
347 : OGRNTFLayer *poLayer,
348 : NTFRecord **papoGroup)
349 :
350 : {
351 0 : if (CSLCount((char **)papoGroup) < 2 ||
352 0 : papoGroup[0]->GetType() != NRT_NODEREC ||
353 0 : (papoGroup[1]->GetType() != NRT_GEOMETRY &&
354 0 : papoGroup[1]->GetType() != NRT_GEOMETRY3D))
355 : {
356 0 : return nullptr;
357 : }
358 :
359 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
360 :
361 : // NODE_ID
362 0 : poFeature->SetField("NODE_ID", atoi(papoGroup[0]->GetField(3, 8)));
363 :
364 : // Geometry
365 0 : poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
366 0 : poFeature->SetField("GEOM_ID", papoGroup[1]->GetField(3, 8));
367 :
368 : // NUM_LINKS
369 0 : int nLinkCount = 0;
370 0 : if (papoGroup[0]->GetLength() > 18)
371 : {
372 0 : nLinkCount = atoi(papoGroup[0]->GetField(15, 18));
373 0 : if (nLinkCount > 0)
374 : {
375 0 : std::vector<int> anLinks(nLinkCount);
376 :
377 : // GEOM_ID_OF_LINK
378 0 : for (int iLink = 0; iLink < nLinkCount; iLink++)
379 0 : anLinks[iLink] = atoi(
380 0 : papoGroup[0]->GetField(20 + iLink * 12, 25 + iLink * 12));
381 :
382 0 : poFeature->SetField("GEOM_ID_OF_LINK", nLinkCount, anLinks.data());
383 :
384 : // DIR
385 0 : for (int iLink = 0; iLink < nLinkCount; iLink++)
386 0 : anLinks[iLink] = atoi(
387 0 : papoGroup[0]->GetField(19 + iLink * 12, 19 + iLink * 12));
388 :
389 0 : poFeature->SetField("DIR", nLinkCount, anLinks.data());
390 : }
391 : }
392 :
393 0 : poFeature->SetField("NUM_LINKS", nLinkCount);
394 :
395 : // should we add LEVEL and/or ORIENT?
396 :
397 0 : return poFeature;
398 : }
399 :
400 : /************************************************************************/
401 : /* TranslateGenericCollection() */
402 : /************************************************************************/
403 :
404 0 : static OGRFeature *TranslateGenericCollection(NTFFileReader *poReader,
405 : OGRNTFLayer *poLayer,
406 : NTFRecord **papoGroup)
407 :
408 : {
409 0 : if (CSLCount((char **)papoGroup) < 1 ||
410 0 : papoGroup[0]->GetType() != NRT_COLLECT)
411 0 : return nullptr;
412 :
413 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
414 :
415 : // COLL_ID
416 0 : poFeature->SetField("COLL_ID", atoi(papoGroup[0]->GetField(3, 8)));
417 :
418 : // NUM_PARTS
419 0 : int nPartCount = 0;
420 :
421 0 : if (papoGroup[0]->GetLength() >= 20)
422 : {
423 0 : nPartCount = atoi(papoGroup[0]->GetField(9, 12));
424 0 : if (nPartCount > 0 &&
425 0 : nPartCount - 1 <= (papoGroup[0]->GetLength() - 20) / 8)
426 : {
427 0 : std::vector<int> anParts(nPartCount);
428 :
429 : // TYPE
430 0 : for (int iPart = 0; iPart < nPartCount; iPart++)
431 0 : anParts[iPart] = atoi(
432 0 : papoGroup[0]->GetField(13 + iPart * 8, 14 + iPart * 8));
433 :
434 0 : poFeature->SetField("TYPE", nPartCount, anParts.data());
435 :
436 : // ID
437 0 : for (int iPart = 0; iPart < nPartCount; iPart++)
438 0 : anParts[iPart] = atoi(
439 0 : papoGroup[0]->GetField(15 + iPart * 8, 20 + iPart * 8));
440 :
441 0 : poFeature->SetField("ID", nPartCount, anParts.data());
442 : }
443 : else
444 : {
445 0 : nPartCount = 0;
446 : }
447 : }
448 :
449 0 : poFeature->SetField("NUM_PARTS", nPartCount);
450 :
451 : // ATTREC Attributes
452 0 : AddGenericAttributes(poReader, papoGroup, poFeature);
453 :
454 0 : return poFeature;
455 : }
456 :
457 : /************************************************************************/
458 : /* TranslateGenericText() */
459 : /************************************************************************/
460 :
461 0 : static OGRFeature *TranslateGenericText(NTFFileReader *poReader,
462 : OGRNTFLayer *poLayer,
463 : NTFRecord **papoGroup)
464 :
465 : {
466 0 : if (CSLCount((char **)papoGroup) < 2 ||
467 0 : papoGroup[0]->GetType() != NRT_TEXTREC)
468 0 : return nullptr;
469 :
470 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
471 :
472 : // TEXT_ID
473 0 : poFeature->SetField("TEXT_ID", atoi(papoGroup[0]->GetField(3, 8)));
474 :
475 : // Geometry
476 0 : for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
477 : {
478 0 : if (papoGroup[iRec]->GetType() == NRT_GEOMETRY ||
479 0 : papoGroup[iRec]->GetType() == NRT_GEOMETRY3D)
480 : {
481 0 : poFeature->SetGeometryDirectly(
482 0 : poReader->ProcessGeometry(papoGroup[iRec]));
483 0 : poFeature->SetField("GEOM_ID", papoGroup[iRec]->GetField(3, 8));
484 0 : break;
485 : }
486 : }
487 :
488 : // ATTREC Attributes
489 0 : AddGenericAttributes(poReader, papoGroup, poFeature);
490 :
491 : // TEXTREP information
492 0 : for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
493 : {
494 0 : NTFRecord *poRecord = papoGroup[iRec];
495 :
496 0 : if (poRecord->GetType() == NRT_TEXTREP)
497 : {
498 0 : poFeature->SetField("FONT", atoi(poRecord->GetField(9, 12)));
499 0 : poFeature->SetField("TEXT_HT",
500 0 : atoi(poRecord->GetField(13, 15)) * 0.1);
501 0 : poFeature->SetField("TEXT_HT_GROUND",
502 0 : atoi(poRecord->GetField(13, 15)) * 0.1 *
503 0 : poReader->GetPaperToGround());
504 0 : poFeature->SetField("DIG_POSTN", atoi(poRecord->GetField(16, 16)));
505 0 : poFeature->SetField("ORIENT",
506 0 : atoi(poRecord->GetField(17, 20)) * 0.1);
507 0 : break;
508 : }
509 : }
510 :
511 0 : return poFeature;
512 : }
513 :
514 : /************************************************************************/
515 : /* TranslateGenericName() */
516 : /************************************************************************/
517 :
518 0 : static OGRFeature *TranslateGenericName(NTFFileReader *poReader,
519 : OGRNTFLayer *poLayer,
520 : NTFRecord **papoGroup)
521 :
522 : {
523 0 : if (CSLCount((char **)papoGroup) < 2 ||
524 0 : papoGroup[0]->GetType() != NRT_NAMEREC)
525 0 : return nullptr;
526 :
527 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
528 :
529 : // NAME_ID
530 0 : poFeature->SetField("NAME_ID", atoi(papoGroup[0]->GetField(3, 8)));
531 :
532 : // TEXT_CODE
533 0 : poFeature->SetField("TEXT_CODE", papoGroup[0]->GetField(8, 12));
534 :
535 : // TEXT
536 0 : int nNumChar = atoi(papoGroup[0]->GetField(13, 14));
537 :
538 0 : if (nNumChar > 0 && papoGroup[0]->GetLength() >= 15 + nNumChar - 1)
539 0 : poFeature->SetField("TEXT",
540 : papoGroup[0]->GetField(15, 15 + nNumChar - 1));
541 :
542 : // Geometry
543 0 : for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
544 : {
545 0 : if (papoGroup[iRec]->GetType() == NRT_GEOMETRY ||
546 0 : papoGroup[iRec]->GetType() == NRT_GEOMETRY3D)
547 : {
548 0 : poFeature->SetGeometryDirectly(
549 0 : poReader->ProcessGeometry(papoGroup[iRec]));
550 0 : poFeature->SetField("GEOM_ID", papoGroup[iRec]->GetField(3, 8));
551 0 : break;
552 : }
553 : }
554 :
555 : // ATTREC Attributes
556 0 : AddGenericAttributes(poReader, papoGroup, poFeature);
557 :
558 : // NAMEPOSTN information
559 0 : for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
560 : {
561 0 : NTFRecord *poRecord = papoGroup[iRec];
562 :
563 0 : if (poRecord->GetType() == NRT_NAMEPOSTN)
564 : {
565 0 : poFeature->SetField("FONT", atoi(poRecord->GetField(3, 6)));
566 0 : poFeature->SetField("TEXT_HT",
567 0 : atoi(poRecord->GetField(7, 9)) * 0.1);
568 0 : poFeature->SetField("TEXT_HT_GROUND",
569 0 : atoi(poRecord->GetField(7, 9)) * 0.1 *
570 0 : poReader->GetPaperToGround());
571 0 : poFeature->SetField("DIG_POSTN", atoi(poRecord->GetField(10, 10)));
572 0 : poFeature->SetField("ORIENT",
573 0 : atoi(poRecord->GetField(11, 14)) * 0.1);
574 0 : break;
575 : }
576 : }
577 :
578 0 : return poFeature;
579 : }
580 :
581 : /************************************************************************/
582 : /* TranslateGenericPoint() */
583 : /************************************************************************/
584 :
585 0 : static OGRFeature *TranslateGenericPoint(NTFFileReader *poReader,
586 : OGRNTFLayer *poLayer,
587 : NTFRecord **papoGroup)
588 :
589 : {
590 0 : if (CSLCount((char **)papoGroup) < 2 ||
591 0 : papoGroup[0]->GetType() != NRT_POINTREC ||
592 0 : (papoGroup[1]->GetType() != NRT_GEOMETRY &&
593 0 : papoGroup[1]->GetType() != NRT_GEOMETRY3D))
594 : {
595 0 : return nullptr;
596 : }
597 :
598 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
599 :
600 : // POINT_ID
601 0 : poFeature->SetField("POINT_ID", atoi(papoGroup[0]->GetField(3, 8)));
602 :
603 : // Geometry
604 0 : poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
605 0 : poFeature->SetField("GEOM_ID", papoGroup[1]->GetField(3, 8));
606 :
607 : // ATTREC Attributes
608 0 : AddGenericAttributes(poReader, papoGroup, poFeature);
609 :
610 : // Handle singular attribute in pre-level 3 POINTREC.
611 0 : if (poReader->GetNTFLevel() < 3)
612 : {
613 : char szValType[3];
614 :
615 0 : snprintf(szValType, sizeof(szValType), "%s",
616 : papoGroup[0]->GetField(9, 10));
617 0 : if (!EQUAL(szValType, " "))
618 : {
619 0 : const char *pszProcessedValue = nullptr;
620 :
621 0 : if (poReader->ProcessAttValue(szValType,
622 : papoGroup[0]->GetField(11, 16),
623 0 : nullptr, &pszProcessedValue, nullptr))
624 0 : poFeature->SetField(szValType, pszProcessedValue);
625 : }
626 :
627 0 : if (!EQUAL(papoGroup[0]->GetField(17, 20), " "))
628 : {
629 0 : poFeature->SetField("FEAT_CODE", papoGroup[0]->GetField(17, 20));
630 : }
631 : }
632 :
633 0 : return poFeature;
634 : }
635 :
636 : /************************************************************************/
637 : /* TranslateGenericLine() */
638 : /************************************************************************/
639 :
640 0 : static OGRFeature *TranslateGenericLine(NTFFileReader *poReader,
641 : OGRNTFLayer *poLayer,
642 : NTFRecord **papoGroup)
643 :
644 : {
645 0 : if (CSLCount((char **)papoGroup) < 2 ||
646 0 : papoGroup[0]->GetType() != NRT_LINEREC ||
647 0 : (papoGroup[1]->GetType() != NRT_GEOMETRY &&
648 0 : papoGroup[1]->GetType() != NRT_GEOMETRY3D))
649 0 : return nullptr;
650 :
651 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
652 :
653 : // LINE_ID
654 0 : poFeature->SetField("LINE_ID", atoi(papoGroup[0]->GetField(3, 8)));
655 :
656 : // Geometry
657 0 : poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
658 0 : poFeature->SetField("GEOM_ID", papoGroup[1]->GetField(3, 8));
659 :
660 : // ATTREC Attributes
661 0 : AddGenericAttributes(poReader, papoGroup, poFeature);
662 :
663 : // Handle singular attribute in pre-level 3 LINEREC.
664 0 : if (poReader->GetNTFLevel() < 3)
665 : {
666 0 : char szValType[3] = {};
667 :
668 0 : snprintf(szValType, sizeof(szValType), "%s",
669 : papoGroup[0]->GetField(9, 10));
670 0 : if (!EQUAL(szValType, " "))
671 : {
672 0 : const char *pszProcessedValue = nullptr;
673 :
674 0 : if (poReader->ProcessAttValue(szValType,
675 : papoGroup[0]->GetField(11, 16),
676 0 : nullptr, &pszProcessedValue, nullptr))
677 0 : poFeature->SetField(szValType, pszProcessedValue);
678 : }
679 :
680 0 : if (!EQUAL(papoGroup[0]->GetField(17, 20), " "))
681 : {
682 0 : poFeature->SetField("FEAT_CODE", papoGroup[0]->GetField(17, 20));
683 : }
684 : }
685 :
686 0 : return poFeature;
687 : }
688 :
689 : /************************************************************************/
690 : /* TranslateGenericPoly() */
691 : /************************************************************************/
692 :
693 0 : static OGRFeature *TranslateGenericPoly(NTFFileReader *poReader,
694 : OGRNTFLayer *poLayer,
695 : NTFRecord **papoGroup)
696 :
697 : {
698 : /* ==================================================================== */
699 : /* Traditional POLYGON record groups. */
700 : /* ==================================================================== */
701 0 : if (CSLCount((char **)papoGroup) >= 2 &&
702 0 : papoGroup[0]->GetType() == NRT_POLYGON &&
703 0 : papoGroup[1]->GetType() == NRT_CHAIN)
704 : {
705 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
706 :
707 : // POLY_ID
708 0 : poFeature->SetField(0, atoi(papoGroup[0]->GetField(3, 8)));
709 :
710 : // NUM_PARTS
711 0 : int nNumLinks = atoi(papoGroup[1]->GetField(9, 12));
712 :
713 0 : if (nNumLinks < 0 || nNumLinks > MAX_LINK)
714 : {
715 0 : CPLError(CE_Failure, CPLE_AppDefined,
716 : "MAX_LINK exceeded in ntf_generic.cpp.");
717 0 : return poFeature;
718 : }
719 :
720 0 : poFeature->SetField("NUM_PARTS", nNumLinks);
721 :
722 : // DIR
723 0 : int i, anList[MAX_LINK] = {0};
724 :
725 0 : for (i = 0; i < nNumLinks; i++)
726 0 : anList[i] = atoi(papoGroup[1]->GetField(19 + i * 7, 19 + i * 7));
727 :
728 0 : poFeature->SetField("DIR", nNumLinks, anList);
729 :
730 : // GEOM_ID_OF_LINK
731 0 : for (i = 0; i < nNumLinks; i++)
732 0 : anList[i] = atoi(papoGroup[1]->GetField(13 + i * 7, 18 + i * 7));
733 :
734 0 : poFeature->SetField("GEOM_ID_OF_LINK", nNumLinks, anList);
735 :
736 : // RingStart
737 0 : int nRingList = 0;
738 0 : poFeature->SetField("RingStart", 1, &nRingList);
739 :
740 : // ATTREC Attributes
741 0 : AddGenericAttributes(poReader, papoGroup, poFeature);
742 :
743 : // Read point geometry
744 0 : if (papoGroup[2] != nullptr &&
745 0 : (papoGroup[2]->GetType() == NRT_GEOMETRY ||
746 0 : papoGroup[2]->GetType() == NRT_GEOMETRY3D))
747 : {
748 0 : poFeature->SetGeometryDirectly(
749 0 : poReader->ProcessGeometry(papoGroup[2]));
750 0 : poFeature->SetField("GEOM_ID", papoGroup[2]->GetField(3, 8));
751 : }
752 :
753 0 : return poFeature;
754 : }
755 :
756 0 : return nullptr;
757 : }
758 :
759 : /************************************************************************/
760 : /* TranslateGenericCPoly() */
761 : /************************************************************************/
762 :
763 0 : static OGRFeature *TranslateGenericCPoly(NTFFileReader *poReader,
764 : OGRNTFLayer *poLayer,
765 : NTFRecord **papoGroup)
766 :
767 : {
768 : /* -------------------------------------------------------------------- */
769 : /* First we do validation of the grouping. */
770 : /* -------------------------------------------------------------------- */
771 0 : if (papoGroup[0]->GetType() != NRT_CPOLY)
772 0 : return nullptr;
773 :
774 0 : if (papoGroup[1] == nullptr || (papoGroup[1]->GetType() != NRT_GEOMETRY &&
775 0 : papoGroup[1]->GetType() != NRT_GEOMETRY3D))
776 0 : return nullptr;
777 :
778 0 : if (papoGroup[2] != nullptr && papoGroup[2]->GetType() != NRT_ATTREC)
779 0 : return nullptr;
780 :
781 : /* -------------------------------------------------------------------- */
782 : /* collect information for whole complex polygon. */
783 : /* -------------------------------------------------------------------- */
784 0 : OGRFeature *poFeature = new OGRFeature(poLayer->GetLayerDefn());
785 :
786 : // CPOLY_ID
787 0 : poFeature->SetField("CPOLY_ID", atoi(papoGroup[0]->GetField(3, 8)));
788 :
789 : // ATTREC Attributes
790 0 : AddGenericAttributes(poReader, papoGroup, poFeature);
791 :
792 : // Read point geometry
793 0 : if (papoGroup[1] != nullptr && (papoGroup[1]->GetType() == NRT_GEOMETRY ||
794 0 : papoGroup[1]->GetType() == NRT_GEOMETRY3D))
795 : {
796 0 : poFeature->SetGeometryDirectly(poReader->ProcessGeometry(papoGroup[1]));
797 0 : poFeature->SetField("GEOM_ID", atoi(papoGroup[1]->GetField(3, 8)));
798 : }
799 :
800 : /* -------------------------------------------------------------------- */
801 : /* Collect the chains for each of the rings, and just aggregate */
802 : /* these into the master list without any concept of where the */
803 : /* boundaries are. The boundary information will be emitted */
804 : /* in the RingStart field. */
805 : /* -------------------------------------------------------------------- */
806 0 : int nNumLink = 0;
807 0 : int anPolyId[MAX_LINK * 2] = {0};
808 :
809 0 : nNumLink = atoi(papoGroup[0]->GetField(9, 12));
810 0 : if (nNumLink < 0 || nNumLink > MAX_LINK)
811 : {
812 0 : CPLError(CE_Failure, CPLE_AppDefined,
813 : "MAX_LINK exceeded in ntf_generic.cpp.");
814 0 : return poFeature;
815 : }
816 :
817 0 : for (int iLink = 0; iLink < nNumLink; iLink++)
818 : {
819 0 : anPolyId[iLink] =
820 0 : atoi(papoGroup[0]->GetField(13 + iLink * 7, 18 + iLink * 7));
821 : }
822 :
823 : // NUM_PARTS
824 0 : poFeature->SetField("NUM_PARTS", nNumLink);
825 :
826 : // POLY_ID
827 0 : poFeature->SetField("POLY_ID", nNumLink, anPolyId);
828 :
829 0 : return poFeature;
830 : }
831 :
832 : /************************************************************************/
833 : /* EstablishGenericLayers() */
834 : /************************************************************************/
835 :
836 0 : void OGRNTFDataSource::EstablishGenericLayers()
837 :
838 : {
839 : /* -------------------------------------------------------------------- */
840 : /* Pick an initial NTFFileReader to build the layers against. */
841 : /* -------------------------------------------------------------------- */
842 0 : for (int iFile = 0; iFile < nNTFFileCount; iFile++)
843 : {
844 0 : int bHasZ = FALSE;
845 :
846 0 : NTFFileReader *poPReader = papoNTFFileReader[iFile];
847 0 : if (poPReader->GetProductId() != NPC_UNKNOWN)
848 0 : continue;
849 :
850 : /* --------------------------------------------------------------------
851 : */
852 : /* If any of the generic classes are 3D, then assume all our */
853 : /* geometry should be marked as 3D. */
854 : /* --------------------------------------------------------------------
855 : */
856 0 : for (int iType = 0; iType < 99; iType++)
857 : {
858 0 : NTFGenericClass *poClass = aoGenericClass + iType;
859 :
860 0 : if (poClass->nFeatureCount > 0 && poClass->b3D)
861 0 : bHasZ = TRUE;
862 : }
863 :
864 : /* --------------------------------------------------------------------
865 : */
866 : /* Create layers for all recognised layer types with features. */
867 : /* --------------------------------------------------------------------
868 : */
869 0 : for (int iType = 0; iType < 99; iType++)
870 : {
871 0 : NTFGenericClass *poClass = aoGenericClass + iType;
872 :
873 0 : if (poClass->nFeatureCount == 0)
874 0 : continue;
875 :
876 0 : if (iType == NRT_POINTREC)
877 : {
878 0 : poPReader->EstablishLayer(
879 : "GENERIC_POINT", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
880 : TranslateGenericPoint, NRT_POINTREC, poClass, "POINT_ID",
881 : OFTInteger, 6, 0, NULL);
882 : }
883 0 : else if (iType == NRT_LINEREC)
884 : {
885 0 : poPReader->EstablishLayer(
886 : "GENERIC_LINE",
887 : OGR_GT_SetModifier(wkbLineString, bHasZ, FALSE),
888 : TranslateGenericLine, NRT_LINEREC, poClass, "LINE_ID",
889 : OFTInteger, 6, 0, NULL);
890 : }
891 0 : else if (iType == NRT_TEXTREC)
892 : {
893 0 : poPReader->EstablishLayer(
894 : "GENERIC_TEXT", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
895 : TranslateGenericText, NRT_TEXTREC, poClass, "TEXT_ID",
896 : OFTInteger, 6, 0, NULL);
897 : }
898 0 : else if (iType == NRT_NAMEREC)
899 : {
900 0 : poPReader->EstablishLayer(
901 : "GENERIC_NAME", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
902 : TranslateGenericName, NRT_NAMEREC, poClass, "NAME_ID",
903 : OFTInteger, 6, 0, NULL);
904 : }
905 0 : else if (iType == NRT_NODEREC)
906 : {
907 0 : poPReader->EstablishLayer(
908 : "GENERIC_NODE", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
909 : TranslateGenericNode, NRT_NODEREC, poClass, "NODE_ID",
910 : OFTInteger, 6, 0, "NUM_LINKS", OFTInteger, 4, 0,
911 : "GEOM_ID_OF_LINK", OFTIntegerList, 6, 0, "DIR",
912 : OFTIntegerList, 1, 0, NULL);
913 : }
914 0 : else if (iType == NRT_COLLECT)
915 : {
916 0 : poPReader->EstablishLayer(
917 : "GENERIC_COLLECTION", wkbNone, TranslateGenericCollection,
918 : NRT_COLLECT, poClass, "COLL_ID", OFTInteger, 6, 0,
919 : "NUM_PARTS", OFTInteger, 4, 0, "TYPE", OFTIntegerList, 2, 0,
920 : "ID", OFTIntegerList, 6, 0, NULL);
921 : }
922 0 : else if (iType == NRT_POLYGON)
923 : {
924 0 : poPReader->EstablishLayer(
925 : "GENERIC_POLY", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
926 : TranslateGenericPoly, NRT_POLYGON, poClass, "POLY_ID",
927 : OFTInteger, 6, 0, "NUM_PARTS", OFTInteger, 4, 0, "DIR",
928 : OFTIntegerList, 1, 0, "GEOM_ID_OF_LINK", OFTIntegerList, 6,
929 : 0, "RingStart", OFTIntegerList, 6, 0, NULL);
930 : }
931 0 : else if (iType == NRT_CPOLY)
932 : {
933 0 : poPReader->EstablishLayer(
934 : "GENERIC_CPOLY", OGR_GT_SetModifier(wkbPoint, bHasZ, FALSE),
935 : TranslateGenericCPoly, NRT_CPOLY, poClass, "CPOLY_ID",
936 : OFTInteger, 6, 0, "NUM_PARTS", OFTInteger, 4, 0, "POLY_ID",
937 : OFTIntegerList, 1, 0, NULL);
938 : }
939 : }
940 : }
941 0 : }
|