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