/src/gdal/ogr/ogrsf_frmts/s57/s57reader.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: S-57 Translator |
4 | | * Purpose: Implements S57Reader class. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, 2001, Frank Warmerdam |
9 | | * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_conv.h" |
15 | | #include "cpl_string.h" |
16 | | #include "ogr_api.h" |
17 | | #include "s57.h" |
18 | | |
19 | | #include <cmath> |
20 | | |
21 | | #include <algorithm> |
22 | | #include <string> |
23 | | |
24 | | /** |
25 | | * Recode the given string from a source encoding to UTF-8 encoding. The source |
26 | | * encoding is established by inspecting the AALL and NALL fields of the S57 |
27 | | * DSSI record. If first time, the DSSI is read to setup appropriate |
28 | | * variables. Main scope of this function is to have the strings of all |
29 | | * attributes encoded/recoded to the same codepage in the final Shapefiles .DBF. |
30 | | * |
31 | | * @param[in] SourceString source string to be recoded to UTF-8. |
32 | | * LookAtAALL-NALL: flag indicating if the string becomes from an |
33 | | * international attribute (e.g. INFORM, OBJNAM) or national attribute (e.g |
34 | | * NINFOM, NOBJNM). The type of encoding is contained in two different |
35 | | * fields of the S57 DSSI record: AALL for the international attributes, |
36 | | * NAAL for the national ones, so depending on the type of encoding, |
37 | | * different fields must be checked to fetch in which way the source string |
38 | | * is encoded. |
39 | | * |
40 | | * 0: the type of endoding is for international attributes |
41 | | * 1: the type of endoding is for national attributes |
42 | | * |
43 | | * @param[in] LookAtAALL_NALL to be documented |
44 | | * |
45 | | * @return the output string recoded to UTF-8 or left unchanged if no valid |
46 | | * recoding applicable. The recodinf relies on GDAL functions appropriately |
47 | | * called, which allocate themselves the necessary memory to hold the |
48 | | * recoded string. |
49 | | * NOTE: Aall variable is currently not used. |
50 | | *******************************************************************************/ |
51 | | char *S57Reader::RecodeByDSSI(const char *SourceString, bool LookAtAALL_NALL) |
52 | 0 | { |
53 | 0 | if (needAallNallSetup == true) |
54 | 0 | { |
55 | 0 | OGRFeature *dsidFeature = ReadDSID(); |
56 | 0 | if (dsidFeature == nullptr) |
57 | 0 | return CPLStrdup(SourceString); |
58 | 0 | Aall = dsidFeature->GetFieldAsInteger("DSSI_AALL"); |
59 | 0 | Nall = dsidFeature->GetFieldAsInteger("DSSI_NALL"); |
60 | 0 | CPLDebug("S57", "DSSI_AALL = %d, DSSI_NALL = %d", Aall, Nall); |
61 | 0 | needAallNallSetup = false; |
62 | 0 | delete dsidFeature; |
63 | 0 | } |
64 | | |
65 | 0 | char *RecodedString = nullptr; |
66 | 0 | if (!LookAtAALL_NALL) |
67 | 0 | { |
68 | | // In case of international attributes, only ISO8859-1 code page is |
69 | | // used (standard ascii). The result is identical to the source string |
70 | | // if it contains 0..127 ascii code (LL0), can slightly differ if it |
71 | | // contains diacritics 0..255 ascii codes (LL1). |
72 | 0 | RecodedString = |
73 | 0 | CPLRecode(SourceString, CPL_ENC_ISO8859_1, CPL_ENC_UTF8); |
74 | 0 | } |
75 | 0 | else |
76 | 0 | { |
77 | 0 | if (Nall == 2) // national string encoded in UCS-2 |
78 | 0 | { |
79 | 0 | GByte *pabyStr = |
80 | 0 | reinterpret_cast<GByte *>(const_cast<char *>(SourceString)); |
81 | | |
82 | | /* Count the number of characters */ |
83 | 0 | int i = 0; |
84 | 0 | while (!((pabyStr[2 * i] == DDF_UNIT_TERMINATOR && |
85 | 0 | pabyStr[2 * i + 1] == 0) || |
86 | 0 | (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0))) |
87 | 0 | i++; |
88 | |
|
89 | 0 | wchar_t *wideString = |
90 | 0 | static_cast<wchar_t *>(CPLMalloc((i + 1) * sizeof(wchar_t))); |
91 | 0 | i = 0; |
92 | 0 | bool bLittleEndian = true; |
93 | | |
94 | | /* Skip BOM */ |
95 | 0 | if (pabyStr[0] == 0xFF && pabyStr[1] == 0xFE) |
96 | 0 | i++; |
97 | 0 | else if (pabyStr[0] == 0xFE && pabyStr[1] == 0xFF) |
98 | 0 | { |
99 | 0 | bLittleEndian = false; |
100 | 0 | i++; |
101 | 0 | } |
102 | |
|
103 | 0 | int j = 0; |
104 | 0 | while (!((pabyStr[2 * i] == DDF_UNIT_TERMINATOR && |
105 | 0 | pabyStr[2 * i + 1] == 0) || |
106 | 0 | (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0))) |
107 | 0 | { |
108 | 0 | if (bLittleEndian) |
109 | 0 | wideString[j++] = |
110 | 0 | pabyStr[i * 2] | (pabyStr[i * 2 + 1] << 8); |
111 | 0 | else |
112 | 0 | wideString[j++] = |
113 | 0 | pabyStr[i * 2 + 1] | (pabyStr[i * 2] << 8); |
114 | 0 | i++; |
115 | 0 | } |
116 | 0 | wideString[j] = 0; |
117 | 0 | RecodedString = |
118 | 0 | CPLRecodeFromWChar(wideString, CPL_ENC_UCS2, CPL_ENC_UTF8); |
119 | 0 | CPLFree(wideString); |
120 | 0 | } |
121 | 0 | else |
122 | 0 | { |
123 | | // National string encoded as ISO8859-1. |
124 | | // See comment for above on LL0/LL1). |
125 | 0 | RecodedString = |
126 | 0 | CPLRecode(SourceString, CPL_ENC_ISO8859_1, CPL_ENC_UTF8); |
127 | 0 | } |
128 | 0 | } |
129 | |
|
130 | 0 | if (RecodedString == nullptr) |
131 | 0 | RecodedString = CPLStrdup(SourceString); |
132 | |
|
133 | 0 | return RecodedString; |
134 | 0 | } |
135 | | |
136 | | /************************************************************************/ |
137 | | /* S57Reader() */ |
138 | | /************************************************************************/ |
139 | | |
140 | | S57Reader::S57Reader(const char *pszFilename) |
141 | 506 | : poRegistrar(nullptr), poClassContentExplorer(nullptr), nFDefnCount(0), |
142 | 506 | papoFDefnList(nullptr), pszModuleName(CPLStrdup(pszFilename)), |
143 | 506 | pszDSNM(nullptr), poModule(nullptr), nCOMF(1000000), nSOMF(10), |
144 | 506 | bFileIngested(false), nNextVIIndex(0), nNextVCIndex(0), nNextVEIndex(0), |
145 | 506 | nNextVFIndex(0), nNextFEIndex(0), nNextDSIDIndex(0), |
146 | 506 | poDSIDRecord(nullptr), poDSPMRecord(nullptr), papszOptions(nullptr), |
147 | 506 | nOptionFlags(S57M_UPDATES), iPointOffset(0), poMultiPoint(nullptr), |
148 | 506 | Aall(0), // See RecodeByDSSI() function. |
149 | 506 | Nall(0), // See RecodeByDSSI() function. |
150 | 506 | needAallNallSetup(true), // See RecodeByDSSI() function. |
151 | 506 | bMissingWarningIssued(false), bAttrWarningIssued(false) |
152 | 506 | { |
153 | 506 | } |
154 | | |
155 | | /************************************************************************/ |
156 | | /* ~S57Reader() */ |
157 | | /************************************************************************/ |
158 | | |
159 | | S57Reader::~S57Reader() |
160 | | |
161 | 506 | { |
162 | 506 | Close(); |
163 | | |
164 | 506 | CPLFree(pszModuleName); |
165 | 506 | CSLDestroy(papszOptions); |
166 | | |
167 | 506 | CPLFree(papoFDefnList); |
168 | 506 | } |
169 | | |
170 | | /************************************************************************/ |
171 | | /* Open() */ |
172 | | /************************************************************************/ |
173 | | |
174 | | int S57Reader::Open(int bTestOpen) |
175 | | |
176 | 506 | { |
177 | 506 | if (poModule != nullptr) |
178 | 0 | { |
179 | 0 | Rewind(); |
180 | 0 | return TRUE; |
181 | 0 | } |
182 | | |
183 | 506 | poModule = new DDFModule(); |
184 | 506 | if (!poModule->Open(pszModuleName)) |
185 | 49 | { |
186 | | // notdef: test bTestOpen. |
187 | 49 | delete poModule; |
188 | 49 | poModule = nullptr; |
189 | 49 | return FALSE; |
190 | 49 | } |
191 | | |
192 | | // note that the following won't work for catalogs. |
193 | 457 | if (poModule->FindFieldDefn("DSID") == nullptr) |
194 | 29 | { |
195 | 29 | if (!bTestOpen) |
196 | 0 | { |
197 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
198 | 0 | "%s is an ISO8211 file, but not an S-57 data file.\n", |
199 | 0 | pszModuleName); |
200 | 0 | } |
201 | 29 | delete poModule; |
202 | 29 | poModule = nullptr; |
203 | 29 | return FALSE; |
204 | 29 | } |
205 | | |
206 | | // Make sure the FSPT field is marked as repeating. |
207 | 428 | DDFFieldDefn *poFSPT = poModule->FindFieldDefn("FSPT"); |
208 | 428 | if (poFSPT != nullptr && !poFSPT->IsRepeating()) |
209 | 205 | { |
210 | 205 | CPLDebug("S57", "Forcing FSPT field to be repeating."); |
211 | 205 | poFSPT->SetRepeatingFlag(TRUE); |
212 | 205 | } |
213 | | |
214 | 428 | nNextFEIndex = 0; |
215 | 428 | nNextVIIndex = 0; |
216 | 428 | nNextVCIndex = 0; |
217 | 428 | nNextVEIndex = 0; |
218 | 428 | nNextVFIndex = 0; |
219 | 428 | nNextDSIDIndex = 0; |
220 | | |
221 | 428 | return TRUE; |
222 | 457 | } |
223 | | |
224 | | /************************************************************************/ |
225 | | /* Close() */ |
226 | | /************************************************************************/ |
227 | | |
228 | | void S57Reader::Close() |
229 | | |
230 | 506 | { |
231 | 506 | if (poModule != nullptr) |
232 | 428 | { |
233 | 428 | oVI_Index.Clear(); |
234 | 428 | oVC_Index.Clear(); |
235 | 428 | oVE_Index.Clear(); |
236 | 428 | oVF_Index.Clear(); |
237 | 428 | oFE_Index.Clear(); |
238 | | |
239 | 428 | if (poDSIDRecord != nullptr) |
240 | 251 | { |
241 | 251 | delete poDSIDRecord; |
242 | 251 | poDSIDRecord = nullptr; |
243 | 251 | } |
244 | 428 | if (poDSPMRecord != nullptr) |
245 | 19 | { |
246 | 19 | delete poDSPMRecord; |
247 | 19 | poDSPMRecord = nullptr; |
248 | 19 | } |
249 | | |
250 | 428 | ClearPendingMultiPoint(); |
251 | | |
252 | 428 | delete poModule; |
253 | 428 | poModule = nullptr; |
254 | | |
255 | 428 | bFileIngested = false; |
256 | | |
257 | 428 | CPLFree(pszDSNM); |
258 | 428 | pszDSNM = nullptr; |
259 | 428 | } |
260 | 506 | } |
261 | | |
262 | | /************************************************************************/ |
263 | | /* ClearPendingMultiPoint() */ |
264 | | /************************************************************************/ |
265 | | |
266 | | void S57Reader::ClearPendingMultiPoint() |
267 | | |
268 | 704 | { |
269 | 704 | if (poMultiPoint != nullptr) |
270 | 0 | { |
271 | 0 | delete poMultiPoint; |
272 | 0 | poMultiPoint = nullptr; |
273 | 0 | } |
274 | 704 | } |
275 | | |
276 | | /************************************************************************/ |
277 | | /* NextPendingMultiPoint() */ |
278 | | /************************************************************************/ |
279 | | |
280 | | OGRFeature *S57Reader::NextPendingMultiPoint() |
281 | | |
282 | 0 | { |
283 | 0 | CPLAssert(poMultiPoint != nullptr); |
284 | 0 | CPLAssert(wkbFlatten(poMultiPoint->GetGeometryRef()->getGeometryType()) == |
285 | 0 | wkbMultiPoint); |
286 | |
|
287 | 0 | OGRFeatureDefn *poDefn = poMultiPoint->GetDefnRef(); |
288 | 0 | OGRFeature *poPoint = new OGRFeature(poDefn); |
289 | 0 | OGRMultiPoint *poMPGeom = poMultiPoint->GetGeometryRef()->toMultiPoint(); |
290 | |
|
291 | 0 | poPoint->SetFID(poMultiPoint->GetFID()); |
292 | |
|
293 | 0 | for (int i = 0; i < poDefn->GetFieldCount(); i++) |
294 | 0 | { |
295 | 0 | poPoint->SetField(i, poMultiPoint->GetRawFieldRef(i)); |
296 | 0 | } |
297 | |
|
298 | 0 | OGRPoint *poSrcPoint = poMPGeom->getGeometryRef(iPointOffset); |
299 | 0 | iPointOffset++; |
300 | 0 | poPoint->SetGeometry(poSrcPoint); |
301 | |
|
302 | 0 | if ((nOptionFlags & S57M_ADD_SOUNDG_DEPTH)) |
303 | 0 | poPoint->SetField("DEPTH", poSrcPoint->getZ()); |
304 | |
|
305 | 0 | if (iPointOffset >= poMPGeom->getNumGeometries()) |
306 | 0 | ClearPendingMultiPoint(); |
307 | |
|
308 | 0 | return poPoint; |
309 | 0 | } |
310 | | |
311 | | /************************************************************************/ |
312 | | /* SetOptions() */ |
313 | | /************************************************************************/ |
314 | | |
315 | | bool S57Reader::SetOptions(char **papszOptionsIn) |
316 | | |
317 | 506 | { |
318 | 506 | CSLDestroy(papszOptions); |
319 | 506 | papszOptions = CSLDuplicate(papszOptionsIn); |
320 | | |
321 | 506 | const char *pszOptionValue = |
322 | 506 | CSLFetchNameValue(papszOptions, S57O_SPLIT_MULTIPOINT); |
323 | 506 | if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue)) |
324 | 0 | nOptionFlags |= S57M_SPLIT_MULTIPOINT; |
325 | 506 | else |
326 | 506 | nOptionFlags &= ~S57M_SPLIT_MULTIPOINT; |
327 | | |
328 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_ADD_SOUNDG_DEPTH); |
329 | 506 | if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue)) |
330 | 0 | nOptionFlags |= S57M_ADD_SOUNDG_DEPTH; |
331 | 506 | else |
332 | 506 | nOptionFlags &= ~S57M_ADD_SOUNDG_DEPTH; |
333 | | |
334 | 506 | if ((nOptionFlags & S57M_ADD_SOUNDG_DEPTH) && |
335 | 506 | !(nOptionFlags & S57M_SPLIT_MULTIPOINT)) |
336 | 0 | { |
337 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
338 | 0 | "Inconsistent options : ADD_SOUNDG_DEPTH should only be " |
339 | 0 | "enabled if SPLIT_MULTIPOINT is also enabled"); |
340 | 0 | return false; |
341 | 0 | } |
342 | | |
343 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_LNAM_REFS); |
344 | 506 | if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue)) |
345 | 506 | nOptionFlags |= S57M_LNAM_REFS; |
346 | 0 | else |
347 | 0 | nOptionFlags &= ~S57M_LNAM_REFS; |
348 | | |
349 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_UPDATES); |
350 | 506 | if (pszOptionValue == nullptr) |
351 | 506 | /* no change */; |
352 | 0 | else if (!EQUAL(pszOptionValue, "APPLY")) |
353 | 0 | nOptionFlags &= ~S57M_UPDATES; |
354 | 0 | else |
355 | 0 | nOptionFlags |= S57M_UPDATES; |
356 | | |
357 | 506 | pszOptionValue = |
358 | 506 | CSLFetchNameValue(papszOptions, S57O_PRESERVE_EMPTY_NUMBERS); |
359 | 506 | if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue)) |
360 | 0 | nOptionFlags |= S57M_PRESERVE_EMPTY_NUMBERS; |
361 | 506 | else |
362 | 506 | nOptionFlags &= ~S57M_PRESERVE_EMPTY_NUMBERS; |
363 | | |
364 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RETURN_PRIMITIVES); |
365 | 506 | if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue)) |
366 | 0 | nOptionFlags |= S57M_RETURN_PRIMITIVES; |
367 | 506 | else |
368 | 506 | nOptionFlags &= ~S57M_RETURN_PRIMITIVES; |
369 | | |
370 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RETURN_LINKAGES); |
371 | 506 | if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue)) |
372 | 0 | nOptionFlags |= S57M_RETURN_LINKAGES; |
373 | 506 | else |
374 | 506 | nOptionFlags &= ~S57M_RETURN_LINKAGES; |
375 | | |
376 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RETURN_DSID); |
377 | 506 | if (pszOptionValue == nullptr || CPLTestBool(pszOptionValue)) |
378 | 506 | nOptionFlags |= S57M_RETURN_DSID; |
379 | 0 | else |
380 | 0 | nOptionFlags &= ~S57M_RETURN_DSID; |
381 | | |
382 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_RECODE_BY_DSSI); |
383 | 506 | if (pszOptionValue == nullptr || CPLTestBool(pszOptionValue)) |
384 | 506 | nOptionFlags |= S57M_RECODE_BY_DSSI; |
385 | 0 | else |
386 | 0 | nOptionFlags &= ~S57M_RECODE_BY_DSSI; |
387 | | |
388 | 506 | pszOptionValue = CSLFetchNameValue(papszOptions, S57O_LIST_AS_STRING); |
389 | 506 | if (pszOptionValue != nullptr && CPLTestBool(pszOptionValue)) |
390 | 0 | nOptionFlags |= S57M_LIST_AS_STRING; |
391 | 506 | else |
392 | 506 | nOptionFlags &= ~S57M_LIST_AS_STRING; |
393 | | |
394 | 506 | return true; |
395 | 506 | } |
396 | | |
397 | | /************************************************************************/ |
398 | | /* SetClassBased() */ |
399 | | /************************************************************************/ |
400 | | |
401 | | void S57Reader::SetClassBased(S57ClassRegistrar *poReg, |
402 | | S57ClassContentExplorer *poClassContentExplorerIn) |
403 | | |
404 | 0 | { |
405 | 0 | poRegistrar = poReg; |
406 | 0 | poClassContentExplorer = poClassContentExplorerIn; |
407 | 0 | } |
408 | | |
409 | | /************************************************************************/ |
410 | | /* Rewind() */ |
411 | | /************************************************************************/ |
412 | | |
413 | | void S57Reader::Rewind() |
414 | | |
415 | 0 | { |
416 | 0 | ClearPendingMultiPoint(); |
417 | 0 | nNextFEIndex = 0; |
418 | 0 | nNextVIIndex = 0; |
419 | 0 | nNextVCIndex = 0; |
420 | 0 | nNextVEIndex = 0; |
421 | 0 | nNextVFIndex = 0; |
422 | 0 | nNextDSIDIndex = 0; |
423 | 0 | } |
424 | | |
425 | | /************************************************************************/ |
426 | | /* Ingest() */ |
427 | | /* */ |
428 | | /* Read all the records into memory, adding to the appropriate */ |
429 | | /* indexes. */ |
430 | | /************************************************************************/ |
431 | | |
432 | | bool S57Reader::Ingest() |
433 | | |
434 | 1.37k | { |
435 | 1.37k | if (poModule == nullptr || bFileIngested) |
436 | 0 | return true; |
437 | | |
438 | | /* -------------------------------------------------------------------- */ |
439 | | /* Read all the records in the module, and place them in */ |
440 | | /* appropriate indexes. */ |
441 | | /* -------------------------------------------------------------------- */ |
442 | 1.37k | CPLErrorReset(); |
443 | 1.37k | DDFRecord *poRecord = nullptr; |
444 | 765k | while ((poRecord = poModule->ReadRecord()) != nullptr) |
445 | 764k | { |
446 | 764k | DDFField *poKeyField = poRecord->GetField(1); |
447 | 764k | if (poKeyField == nullptr) |
448 | 4 | return false; |
449 | 764k | DDFFieldDefn *poKeyFieldDefn = poKeyField->GetFieldDefn(); |
450 | 764k | if (poKeyFieldDefn == nullptr) |
451 | 87 | continue; |
452 | 763k | const char *pszName = poKeyFieldDefn->GetName(); |
453 | 763k | if (pszName == nullptr) |
454 | 0 | continue; |
455 | | |
456 | 763k | if (EQUAL(pszName, "VRID")) |
457 | 1.45k | { |
458 | 1.45k | int bSuccess = FALSE; |
459 | 1.45k | const int nRCNM = |
460 | 1.45k | poRecord->GetIntSubfield("VRID", 0, "RCNM", 0, &bSuccess); |
461 | 1.45k | if (!bSuccess && CPLGetLastErrorType() == CE_Failure) |
462 | 24 | break; |
463 | 1.43k | const int nRCID = |
464 | 1.43k | poRecord->GetIntSubfield("VRID", 0, "RCID", 0, &bSuccess); |
465 | 1.43k | if (!bSuccess && CPLGetLastErrorType() == CE_Failure) |
466 | 4 | break; |
467 | | |
468 | 1.42k | switch (nRCNM) |
469 | 1.42k | { |
470 | 1.05k | case RCNM_VI: |
471 | 1.05k | oVI_Index.AddRecord(nRCID, poRecord->Clone()); |
472 | 1.05k | break; |
473 | | |
474 | 183 | case RCNM_VC: |
475 | 183 | oVC_Index.AddRecord(nRCID, poRecord->Clone()); |
476 | 183 | break; |
477 | | |
478 | 110 | case RCNM_VE: |
479 | 110 | oVE_Index.AddRecord(nRCID, poRecord->Clone()); |
480 | 110 | break; |
481 | | |
482 | 1 | case RCNM_VF: |
483 | 1 | oVF_Index.AddRecord(nRCID, poRecord->Clone()); |
484 | 1 | break; |
485 | | |
486 | 85 | default: |
487 | 85 | CPLError(CE_Failure, CPLE_AppDefined, |
488 | 85 | "Unhandled value for RCNM ; %d", nRCNM); |
489 | 85 | break; |
490 | 1.42k | } |
491 | 1.42k | } |
492 | | |
493 | 762k | else if (EQUAL(pszName, "FRID")) |
494 | 695k | { |
495 | 695k | int bSuccess = FALSE; |
496 | 695k | int nRCID = |
497 | 695k | poRecord->GetIntSubfield("FRID", 0, "RCID", 0, &bSuccess); |
498 | 695k | if (!bSuccess && CPLGetLastErrorType() == CE_Failure) |
499 | 12 | break; |
500 | | |
501 | 695k | oFE_Index.AddRecord(nRCID, poRecord->Clone()); |
502 | 695k | } |
503 | | |
504 | 67.3k | else if (EQUAL(pszName, "DSID")) |
505 | 67.3k | { |
506 | 67.3k | int bSuccess = FALSE; |
507 | 67.3k | CPLFree(pszDSNM); |
508 | 67.3k | pszDSNM = CPLStrdup( |
509 | 67.3k | poRecord->GetStringSubfield("DSID", 0, "DSNM", 0, &bSuccess)); |
510 | 67.3k | if (!bSuccess && CPLGetLastErrorType() == CE_Failure) |
511 | 7 | break; |
512 | | |
513 | 67.3k | const char *pszEDTN = |
514 | 67.3k | poRecord->GetStringSubfield("DSID", 0, "EDTN", 0); |
515 | 67.3k | if (pszEDTN) |
516 | 27.1k | m_osEDTNUpdate = pszEDTN; |
517 | | |
518 | 67.3k | const char *pszUPDN = |
519 | 67.3k | poRecord->GetStringSubfield("DSID", 0, "UPDN", 0); |
520 | 67.3k | if (pszUPDN) |
521 | 12.4k | m_osUPDNUpdate = pszUPDN; |
522 | | |
523 | 67.3k | const char *pszISDT = |
524 | 67.3k | poRecord->GetStringSubfield("DSID", 0, "ISDT", 0); |
525 | 67.3k | if (pszISDT) |
526 | 4.89k | m_osISDTUpdate = pszISDT; |
527 | | |
528 | 67.3k | if (nOptionFlags & S57M_RETURN_DSID) |
529 | 67.3k | { |
530 | 67.3k | if (poDSIDRecord != nullptr) |
531 | 67.0k | delete poDSIDRecord; |
532 | | |
533 | 67.3k | poDSIDRecord = poRecord->Clone(); |
534 | 67.3k | } |
535 | 67.3k | } |
536 | | |
537 | 25 | else if (EQUAL(pszName, "DSPM")) |
538 | 25 | { |
539 | 25 | int bSuccess = FALSE; |
540 | 25 | nCOMF = std::max( |
541 | 25 | 1, poRecord->GetIntSubfield("DSPM", 0, "COMF", 0, &bSuccess)); |
542 | 25 | if (!bSuccess && CPLGetLastErrorType() == CE_Failure) |
543 | 0 | break; |
544 | 25 | nSOMF = std::max( |
545 | 25 | 1, poRecord->GetIntSubfield("DSPM", 0, "SOMF", 0, &bSuccess)); |
546 | 25 | if (!bSuccess && CPLGetLastErrorType() == CE_Failure) |
547 | 0 | break; |
548 | | |
549 | 25 | if (nOptionFlags & S57M_RETURN_DSID) |
550 | 25 | { |
551 | 25 | if (poDSPMRecord != nullptr) |
552 | 6 | delete poDSPMRecord; |
553 | | |
554 | 25 | poDSPMRecord = poRecord->Clone(); |
555 | 25 | } |
556 | 25 | } |
557 | | |
558 | 0 | else |
559 | 0 | { |
560 | 0 | CPLDebug("S57", "Skipping %s record in S57Reader::Ingest().", |
561 | 0 | pszName); |
562 | 0 | } |
563 | 763k | } |
564 | | |
565 | 1.36k | if (CPLGetLastErrorType() == CE_Failure) |
566 | 1.05k | return false; |
567 | | |
568 | 316 | bFileIngested = true; |
569 | | |
570 | | /* -------------------------------------------------------------------- */ |
571 | | /* If update support is enabled, read and apply them. */ |
572 | | /* -------------------------------------------------------------------- */ |
573 | 316 | if (nOptionFlags & S57M_UPDATES) |
574 | 316 | return FindAndApplyUpdates(); |
575 | | |
576 | 0 | return true; |
577 | 316 | } |
578 | | |
579 | | /************************************************************************/ |
580 | | /* SetNextFEIndex() */ |
581 | | /************************************************************************/ |
582 | | |
583 | | void S57Reader::SetNextFEIndex(int nNewIndex, int nRCNM) |
584 | | |
585 | 15.2k | { |
586 | 15.2k | if (nRCNM == RCNM_VI) |
587 | 0 | nNextVIIndex = nNewIndex; |
588 | 15.2k | else if (nRCNM == RCNM_VC) |
589 | 0 | nNextVCIndex = nNewIndex; |
590 | 15.2k | else if (nRCNM == RCNM_VE) |
591 | 0 | nNextVEIndex = nNewIndex; |
592 | 15.2k | else if (nRCNM == RCNM_VF) |
593 | 0 | nNextVFIndex = nNewIndex; |
594 | 15.2k | else if (nRCNM == RCNM_DSID) |
595 | 428 | nNextDSIDIndex = nNewIndex; |
596 | 14.8k | else |
597 | 14.8k | { |
598 | 14.8k | if (nNextFEIndex != nNewIndex) |
599 | 276 | ClearPendingMultiPoint(); |
600 | | |
601 | 14.8k | nNextFEIndex = nNewIndex; |
602 | 14.8k | } |
603 | 15.2k | } |
604 | | |
605 | | /************************************************************************/ |
606 | | /* GetNextFEIndex() */ |
607 | | /************************************************************************/ |
608 | | |
609 | | int S57Reader::GetNextFEIndex(int nRCNM) |
610 | | |
611 | 15.2k | { |
612 | 15.2k | if (nRCNM == RCNM_VI) |
613 | 0 | return nNextVIIndex; |
614 | 15.2k | if (nRCNM == RCNM_VC) |
615 | 0 | return nNextVCIndex; |
616 | 15.2k | if (nRCNM == RCNM_VE) |
617 | 0 | return nNextVEIndex; |
618 | 15.2k | if (nRCNM == RCNM_VF) |
619 | 0 | return nNextVFIndex; |
620 | 15.2k | if (nRCNM == RCNM_DSID) |
621 | 428 | return nNextDSIDIndex; |
622 | | |
623 | 14.8k | return nNextFEIndex; |
624 | 15.2k | } |
625 | | |
626 | | /************************************************************************/ |
627 | | /* ReadNextFeature() */ |
628 | | /************************************************************************/ |
629 | | |
630 | | OGRFeature *S57Reader::ReadNextFeature(OGRFeatureDefn *poTarget) |
631 | | |
632 | 15.2k | { |
633 | 15.2k | if (!bFileIngested && !Ingest()) |
634 | 1.37k | return nullptr; |
635 | | |
636 | | /* -------------------------------------------------------------------- */ |
637 | | /* Special case for "in progress" multipoints being split up. */ |
638 | | /* -------------------------------------------------------------------- */ |
639 | 13.9k | if (poMultiPoint != nullptr) |
640 | 0 | { |
641 | 0 | if (poTarget == nullptr || poTarget == poMultiPoint->GetDefnRef()) |
642 | 0 | { |
643 | 0 | return NextPendingMultiPoint(); |
644 | 0 | } |
645 | 0 | else |
646 | 0 | { |
647 | 0 | ClearPendingMultiPoint(); |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | /* -------------------------------------------------------------------- */ |
652 | | /* Next vector feature? */ |
653 | | /* -------------------------------------------------------------------- */ |
654 | 13.9k | if ((nOptionFlags & S57M_RETURN_DSID) && nNextDSIDIndex == 0 && |
655 | 13.9k | (poTarget == nullptr || EQUAL(poTarget->GetName(), "DSID"))) |
656 | 0 | { |
657 | 0 | return ReadDSID(); |
658 | 0 | } |
659 | | |
660 | | /* -------------------------------------------------------------------- */ |
661 | | /* Next vector feature? */ |
662 | | /* -------------------------------------------------------------------- */ |
663 | 13.9k | if (nOptionFlags & S57M_RETURN_PRIMITIVES) |
664 | 0 | { |
665 | 0 | int nRCNM = 0; |
666 | 0 | int *pnCounter = nullptr; |
667 | |
|
668 | 0 | if (poTarget == nullptr) |
669 | 0 | { |
670 | 0 | if (nNextVIIndex < oVI_Index.GetCount()) |
671 | 0 | { |
672 | 0 | nRCNM = RCNM_VI; |
673 | 0 | pnCounter = &nNextVIIndex; |
674 | 0 | } |
675 | 0 | else if (nNextVCIndex < oVC_Index.GetCount()) |
676 | 0 | { |
677 | 0 | nRCNM = RCNM_VC; |
678 | 0 | pnCounter = &nNextVCIndex; |
679 | 0 | } |
680 | 0 | else if (nNextVEIndex < oVE_Index.GetCount()) |
681 | 0 | { |
682 | 0 | nRCNM = RCNM_VE; |
683 | 0 | pnCounter = &nNextVEIndex; |
684 | 0 | } |
685 | 0 | else if (nNextVFIndex < oVF_Index.GetCount()) |
686 | 0 | { |
687 | 0 | nRCNM = RCNM_VF; |
688 | 0 | pnCounter = &nNextVFIndex; |
689 | 0 | } |
690 | 0 | } |
691 | 0 | else |
692 | 0 | { |
693 | 0 | if (EQUAL(poTarget->GetName(), OGRN_VI)) |
694 | 0 | { |
695 | 0 | nRCNM = RCNM_VI; |
696 | 0 | pnCounter = &nNextVIIndex; |
697 | 0 | } |
698 | 0 | else if (EQUAL(poTarget->GetName(), OGRN_VC)) |
699 | 0 | { |
700 | 0 | nRCNM = RCNM_VC; |
701 | 0 | pnCounter = &nNextVCIndex; |
702 | 0 | } |
703 | 0 | else if (EQUAL(poTarget->GetName(), OGRN_VE)) |
704 | 0 | { |
705 | 0 | nRCNM = RCNM_VE; |
706 | 0 | pnCounter = &nNextVEIndex; |
707 | 0 | } |
708 | 0 | else if (EQUAL(poTarget->GetName(), OGRN_VF)) |
709 | 0 | { |
710 | 0 | nRCNM = RCNM_VF; |
711 | 0 | pnCounter = &nNextVFIndex; |
712 | 0 | } |
713 | 0 | } |
714 | |
|
715 | 0 | if (nRCNM != 0) |
716 | 0 | { |
717 | 0 | OGRFeature *poFeature = ReadVector(*pnCounter, nRCNM); |
718 | 0 | if (poFeature != nullptr) |
719 | 0 | { |
720 | 0 | *pnCounter += 1; |
721 | 0 | return poFeature; |
722 | 0 | } |
723 | 0 | } |
724 | 0 | } |
725 | | |
726 | | /* -------------------------------------------------------------------- */ |
727 | | /* Next feature. */ |
728 | | /* -------------------------------------------------------------------- */ |
729 | 2.31M | while (nNextFEIndex < oFE_Index.GetCount()) |
730 | 2.30M | { |
731 | 2.30M | OGRFeatureDefn *poFeatureDefn = static_cast<OGRFeatureDefn *>( |
732 | 2.30M | oFE_Index.GetClientInfoByIndex(nNextFEIndex)); |
733 | | |
734 | 2.30M | if (poFeatureDefn == nullptr) |
735 | 694k | { |
736 | 694k | poFeatureDefn = FindFDefn(oFE_Index.GetByIndex(nNextFEIndex)); |
737 | 694k | oFE_Index.SetClientInfoByIndex(nNextFEIndex, poFeatureDefn); |
738 | 694k | } |
739 | | |
740 | 2.30M | if (poFeatureDefn != poTarget && poTarget != nullptr) |
741 | 2.29M | { |
742 | 2.29M | nNextFEIndex++; |
743 | 2.29M | continue; |
744 | 2.29M | } |
745 | | |
746 | 13.1k | OGRFeature *poFeature = ReadFeature(nNextFEIndex++, poTarget); |
747 | 13.1k | if (poFeature != nullptr) |
748 | 13.1k | { |
749 | 13.1k | if ((nOptionFlags & S57M_SPLIT_MULTIPOINT) && |
750 | 13.1k | poFeature->GetGeometryRef() != nullptr && |
751 | 13.1k | wkbFlatten(poFeature->GetGeometryRef()->getGeometryType()) == |
752 | 0 | wkbMultiPoint) |
753 | 0 | { |
754 | 0 | poMultiPoint = poFeature; |
755 | 0 | iPointOffset = 0; |
756 | 0 | return NextPendingMultiPoint(); |
757 | 0 | } |
758 | | |
759 | 13.1k | return poFeature; |
760 | 13.1k | } |
761 | 13.1k | } |
762 | | |
763 | 769 | return nullptr; |
764 | 13.9k | } |
765 | | |
766 | | /************************************************************************/ |
767 | | /* ReadFeature() */ |
768 | | /* */ |
769 | | /* Read the features who's id is provided. */ |
770 | | /************************************************************************/ |
771 | | |
772 | | OGRFeature *S57Reader::ReadFeature(int nFeatureId, OGRFeatureDefn *poTarget) |
773 | | |
774 | 13.1k | { |
775 | 13.1k | if (nFeatureId < 0 || nFeatureId >= oFE_Index.GetCount()) |
776 | 0 | return nullptr; |
777 | | |
778 | 13.1k | OGRFeature *poFeature = nullptr; |
779 | | |
780 | 13.1k | if ((nOptionFlags & S57M_RETURN_DSID) && nFeatureId == 0 && |
781 | 13.1k | (poTarget == nullptr || EQUAL(poTarget->GetName(), "DSID"))) |
782 | 0 | { |
783 | 0 | poFeature = ReadDSID(); |
784 | 0 | } |
785 | 13.1k | else |
786 | 13.1k | { |
787 | 13.1k | poFeature = AssembleFeature(oFE_Index.GetByIndex(nFeatureId), poTarget); |
788 | 13.1k | } |
789 | 13.1k | if (poFeature != nullptr) |
790 | 13.1k | poFeature->SetFID(nFeatureId); |
791 | | |
792 | 13.1k | return poFeature; |
793 | 13.1k | } |
794 | | |
795 | | /************************************************************************/ |
796 | | /* AssembleFeature() */ |
797 | | /* */ |
798 | | /* Assemble an OGR feature based on a feature record. */ |
799 | | /************************************************************************/ |
800 | | |
801 | | OGRFeature *S57Reader::AssembleFeature(DDFRecord *poRecord, |
802 | | OGRFeatureDefn *poTarget) |
803 | | |
804 | 13.1k | { |
805 | | /* -------------------------------------------------------------------- */ |
806 | | /* Find the feature definition to use. Currently this is based */ |
807 | | /* on the primitive, but eventually this should be based on the */ |
808 | | /* object class (FRID.OBJL) in some cases, and the primitive in */ |
809 | | /* others. */ |
810 | | /* -------------------------------------------------------------------- */ |
811 | 13.1k | OGRFeatureDefn *poFDefn = FindFDefn(poRecord); |
812 | 13.1k | if (poFDefn == nullptr) |
813 | 0 | return nullptr; |
814 | | |
815 | | /* -------------------------------------------------------------------- */ |
816 | | /* Does this match our target feature definition? If not skip */ |
817 | | /* this feature. */ |
818 | | /* -------------------------------------------------------------------- */ |
819 | 13.1k | if (poTarget != nullptr && poFDefn != poTarget) |
820 | 0 | return nullptr; |
821 | | |
822 | | /* -------------------------------------------------------------------- */ |
823 | | /* Create the new feature object. */ |
824 | | /* -------------------------------------------------------------------- */ |
825 | 13.1k | OGRFeature *poFeature = new OGRFeature(poFDefn); |
826 | | |
827 | | /* -------------------------------------------------------------------- */ |
828 | | /* Assign a few standard feature attributes. */ |
829 | | /* -------------------------------------------------------------------- */ |
830 | 13.1k | int nOBJL = poRecord->GetIntSubfield("FRID", 0, "OBJL", 0); |
831 | 13.1k | poFeature->SetField("OBJL", nOBJL); |
832 | | |
833 | 13.1k | poFeature->SetField("RCID", poRecord->GetIntSubfield("FRID", 0, "RCID", 0)); |
834 | 13.1k | poFeature->SetField("PRIM", poRecord->GetIntSubfield("FRID", 0, "PRIM", 0)); |
835 | 13.1k | poFeature->SetField("GRUP", poRecord->GetIntSubfield("FRID", 0, "GRUP", 0)); |
836 | 13.1k | poFeature->SetField("RVER", poRecord->GetIntSubfield("FRID", 0, "RVER", 0)); |
837 | 13.1k | poFeature->SetField("AGEN", poRecord->GetIntSubfield("FOID", 0, "AGEN", 0)); |
838 | 13.1k | poFeature->SetField("FIDN", poRecord->GetIntSubfield("FOID", 0, "FIDN", 0)); |
839 | 13.1k | poFeature->SetField("FIDS", poRecord->GetIntSubfield("FOID", 0, "FIDS", 0)); |
840 | | |
841 | | /* -------------------------------------------------------------------- */ |
842 | | /* Generate long name, if requested. */ |
843 | | /* -------------------------------------------------------------------- */ |
844 | 13.1k | if (nOptionFlags & S57M_LNAM_REFS) |
845 | 13.1k | { |
846 | 13.1k | GenerateLNAMAndRefs(poRecord, poFeature); |
847 | 13.1k | } |
848 | | |
849 | | /* -------------------------------------------------------------------- */ |
850 | | /* Generate primitive references if requested. */ |
851 | | /* -------------------------------------------------------------------- */ |
852 | 13.1k | if (nOptionFlags & S57M_RETURN_LINKAGES) |
853 | 0 | GenerateFSPTAttributes(poRecord, poFeature); |
854 | | |
855 | | /* -------------------------------------------------------------------- */ |
856 | | /* Apply object class specific attributes, if supported. */ |
857 | | /* -------------------------------------------------------------------- */ |
858 | 13.1k | if (poRegistrar != nullptr) |
859 | 0 | ApplyObjectClassAttributes(poRecord, poFeature); |
860 | | |
861 | | /* -------------------------------------------------------------------- */ |
862 | | /* Find and assign spatial component. */ |
863 | | /* -------------------------------------------------------------------- */ |
864 | 13.1k | const int nPRIM = poRecord->GetIntSubfield("FRID", 0, "PRIM", 0); |
865 | | |
866 | 13.1k | if (nPRIM == PRIM_P) |
867 | 3.19k | { |
868 | 3.19k | if (nOBJL == 129) /* SOUNDG */ |
869 | 56 | AssembleSoundingGeometry(poRecord, poFeature); |
870 | 3.13k | else |
871 | 3.13k | AssemblePointGeometry(poRecord, poFeature); |
872 | 3.19k | } |
873 | 9.93k | else if (nPRIM == PRIM_L) |
874 | 3.88k | { |
875 | 3.88k | AssembleLineGeometry(poRecord, poFeature); |
876 | 3.88k | } |
877 | 6.05k | else if (nPRIM == PRIM_A) |
878 | 6.05k | { |
879 | 6.05k | AssembleAreaGeometry(poRecord, poFeature); |
880 | 6.05k | } |
881 | | |
882 | 13.1k | return poFeature; |
883 | 13.1k | } |
884 | | |
885 | | /************************************************************************/ |
886 | | /* ApplyObjectClassAttributes() */ |
887 | | /************************************************************************/ |
888 | | |
889 | | void S57Reader::ApplyObjectClassAttributes(DDFRecord *poRecord, |
890 | | OGRFeature *poFeature) |
891 | | |
892 | 0 | { |
893 | | /* -------------------------------------------------------------------- */ |
894 | | /* ATTF Attributes */ |
895 | | /* -------------------------------------------------------------------- */ |
896 | 0 | DDFField *poATTF = poRecord->FindField("ATTF"); |
897 | |
|
898 | 0 | if (poATTF == nullptr) |
899 | 0 | return; |
900 | | |
901 | 0 | int nAttrCount = poATTF->GetRepeatCount(); |
902 | 0 | for (int iAttr = 0; iAttr < nAttrCount; iAttr++) |
903 | 0 | { |
904 | 0 | const int nAttrId = poRecord->GetIntSubfield("ATTF", 0, "ATTL", iAttr); |
905 | |
|
906 | 0 | if (poRegistrar->GetAttrInfo(nAttrId) == nullptr) |
907 | 0 | { |
908 | 0 | if (!bAttrWarningIssued) |
909 | 0 | { |
910 | 0 | bAttrWarningIssued = true; |
911 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
912 | 0 | "Illegal feature attribute id (ATTF:ATTL[%d]) of %d\n" |
913 | 0 | "on feature FIDN=%d, FIDS=%d.\n" |
914 | 0 | "Skipping attribute. " |
915 | 0 | "No more warnings will be issued.", |
916 | 0 | iAttr, nAttrId, poFeature->GetFieldAsInteger("FIDN"), |
917 | 0 | poFeature->GetFieldAsInteger("FIDS")); |
918 | 0 | } |
919 | |
|
920 | 0 | continue; |
921 | 0 | } |
922 | | |
923 | | /* Fetch the attribute value */ |
924 | 0 | const char *pszValue = |
925 | 0 | poRecord->GetStringSubfield("ATTF", 0, "ATVL", iAttr); |
926 | 0 | if (pszValue == nullptr) |
927 | 0 | return; |
928 | | |
929 | | // If needed, recode the string in UTF-8. |
930 | 0 | char *pszValueToFree = nullptr; |
931 | 0 | if (nOptionFlags & S57M_RECODE_BY_DSSI) |
932 | 0 | pszValue = pszValueToFree = RecodeByDSSI(pszValue, false); |
933 | | |
934 | | /* Apply to feature in an appropriate way */ |
935 | 0 | const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId); |
936 | 0 | const int iField = poFeature->GetDefnRef()->GetFieldIndex(pszAcronym); |
937 | 0 | if (iField < 0) |
938 | 0 | { |
939 | 0 | if (!bMissingWarningIssued) |
940 | 0 | { |
941 | 0 | bMissingWarningIssued = true; |
942 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
943 | 0 | "Attributes %s ignored, not in expected schema.\n" |
944 | 0 | "No more warnings will be issued for this dataset.", |
945 | 0 | pszAcronym); |
946 | 0 | } |
947 | 0 | CPLFree(pszValueToFree); |
948 | 0 | continue; |
949 | 0 | } |
950 | | |
951 | 0 | OGRFieldDefn *poFldDefn = poFeature->GetDefnRef()->GetFieldDefn(iField); |
952 | 0 | const auto eType = poFldDefn->GetType(); |
953 | 0 | if (eType == OFTInteger || eType == OFTReal) |
954 | 0 | { |
955 | 0 | if (strlen(pszValue) == 0) |
956 | 0 | { |
957 | 0 | if (nOptionFlags & S57M_PRESERVE_EMPTY_NUMBERS) |
958 | 0 | poFeature->SetField(iField, EMPTY_NUMBER_MARKER); |
959 | 0 | else |
960 | 0 | { |
961 | | /* leave as null if value was empty string */ |
962 | 0 | } |
963 | 0 | } |
964 | 0 | else |
965 | 0 | poFeature->SetField(iField, pszValue); |
966 | 0 | } |
967 | 0 | else if (eType == OFTStringList) |
968 | 0 | { |
969 | 0 | char **papszTokens = CSLTokenizeString2(pszValue, ",", 0); |
970 | 0 | poFeature->SetField(iField, papszTokens); |
971 | 0 | CSLDestroy(papszTokens); |
972 | 0 | } |
973 | 0 | else |
974 | 0 | { |
975 | 0 | poFeature->SetField(iField, pszValue); |
976 | 0 | } |
977 | |
|
978 | 0 | CPLFree(pszValueToFree); |
979 | 0 | } |
980 | | |
981 | | /* -------------------------------------------------------------------- */ |
982 | | /* NATF (national) attributes */ |
983 | | /* -------------------------------------------------------------------- */ |
984 | 0 | DDFField *poNATF = poRecord->FindField("NATF"); |
985 | |
|
986 | 0 | if (poNATF == nullptr) |
987 | 0 | return; |
988 | | |
989 | 0 | nAttrCount = poNATF->GetRepeatCount(); |
990 | 0 | for (int iAttr = 0; iAttr < nAttrCount; iAttr++) |
991 | 0 | { |
992 | 0 | const int nAttrId = poRecord->GetIntSubfield("NATF", 0, "ATTL", iAttr); |
993 | 0 | const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId); |
994 | |
|
995 | 0 | if (pszAcronym == nullptr) |
996 | 0 | { |
997 | 0 | if (!bAttrWarningIssued) |
998 | 0 | { |
999 | 0 | bAttrWarningIssued = true; |
1000 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1001 | 0 | "Illegal feature attribute id (NATF:ATTL[%d]) of %d\n" |
1002 | 0 | "on feature FIDN=%d, FIDS=%d.\n" |
1003 | 0 | "Skipping attribute, no more warnings will be issued.", |
1004 | 0 | iAttr, nAttrId, poFeature->GetFieldAsInteger("FIDN"), |
1005 | 0 | poFeature->GetFieldAsInteger("FIDS")); |
1006 | 0 | } |
1007 | |
|
1008 | 0 | continue; |
1009 | 0 | } |
1010 | | |
1011 | | // If needed, recode the string in UTF-8. |
1012 | 0 | const char *pszValue = |
1013 | 0 | poRecord->GetStringSubfield("NATF", 0, "ATVL", iAttr); |
1014 | 0 | if (pszValue != nullptr) |
1015 | 0 | { |
1016 | 0 | if (nOptionFlags & S57M_RECODE_BY_DSSI) |
1017 | 0 | { |
1018 | 0 | char *pszValueRecoded = RecodeByDSSI(pszValue, true); |
1019 | 0 | poFeature->SetField(pszAcronym, pszValueRecoded); |
1020 | 0 | CPLFree(pszValueRecoded); |
1021 | 0 | } |
1022 | 0 | else |
1023 | 0 | poFeature->SetField(pszAcronym, pszValue); |
1024 | 0 | } |
1025 | 0 | } |
1026 | 0 | } |
1027 | | |
1028 | | /************************************************************************/ |
1029 | | /* GenerateLNAMAndRefs() */ |
1030 | | /************************************************************************/ |
1031 | | |
1032 | | void S57Reader::GenerateLNAMAndRefs(DDFRecord *poRecord, OGRFeature *poFeature) |
1033 | | |
1034 | 13.1k | { |
1035 | | /* -------------------------------------------------------------------- */ |
1036 | | /* Apply the LNAM to the object. */ |
1037 | | /* -------------------------------------------------------------------- */ |
1038 | 13.1k | char szLNAM[32]; |
1039 | 13.1k | snprintf(szLNAM, sizeof(szLNAM), "%04X%08X%04X", |
1040 | 13.1k | poFeature->GetFieldAsInteger("AGEN"), |
1041 | 13.1k | poFeature->GetFieldAsInteger("FIDN"), |
1042 | 13.1k | poFeature->GetFieldAsInteger("FIDS")); |
1043 | 13.1k | poFeature->SetField("LNAM", szLNAM); |
1044 | | |
1045 | | /* -------------------------------------------------------------------- */ |
1046 | | /* Do we have references to other features. */ |
1047 | | /* -------------------------------------------------------------------- */ |
1048 | 13.1k | DDFField *poFFPT = poRecord->FindField("FFPT"); |
1049 | | |
1050 | 13.1k | if (poFFPT == nullptr) |
1051 | 13.1k | return; |
1052 | | |
1053 | | /* -------------------------------------------------------------------- */ |
1054 | | /* Apply references. */ |
1055 | | /* -------------------------------------------------------------------- */ |
1056 | 0 | const int nRefCount = poFFPT->GetRepeatCount(); |
1057 | |
|
1058 | 0 | const DDFSubfieldDefn *poLNAM = |
1059 | 0 | poFFPT->GetFieldDefn()->FindSubfieldDefn("LNAM"); |
1060 | 0 | const DDFSubfieldDefn *poRIND = |
1061 | 0 | poFFPT->GetFieldDefn()->FindSubfieldDefn("RIND"); |
1062 | 0 | if (poLNAM == nullptr || poRIND == nullptr) |
1063 | 0 | { |
1064 | 0 | return; |
1065 | 0 | } |
1066 | | |
1067 | 0 | int *panRIND = static_cast<int *>(CPLMalloc(sizeof(int) * nRefCount)); |
1068 | 0 | char **papszRefs = nullptr; |
1069 | |
|
1070 | 0 | for (int iRef = 0; iRef < nRefCount; iRef++) |
1071 | 0 | { |
1072 | 0 | int nMaxBytes = 0; |
1073 | |
|
1074 | 0 | unsigned char *pabyData = |
1075 | 0 | reinterpret_cast<unsigned char *>(const_cast<char *>( |
1076 | 0 | poFFPT->GetSubfieldData(poLNAM, &nMaxBytes, iRef))); |
1077 | 0 | if (pabyData == nullptr || nMaxBytes < 8) |
1078 | 0 | { |
1079 | 0 | CSLDestroy(papszRefs); |
1080 | 0 | CPLFree(panRIND); |
1081 | 0 | return; |
1082 | 0 | } |
1083 | | |
1084 | 0 | snprintf(szLNAM, sizeof(szLNAM), "%02X%02X%02X%02X%02X%02X%02X%02X", |
1085 | 0 | pabyData[1], pabyData[0], /* AGEN */ |
1086 | 0 | pabyData[5], pabyData[4], pabyData[3], pabyData[2], /* FIDN */ |
1087 | 0 | pabyData[7], pabyData[6]); |
1088 | |
|
1089 | 0 | papszRefs = CSLAddString(papszRefs, szLNAM); |
1090 | |
|
1091 | 0 | pabyData = reinterpret_cast<unsigned char *>(const_cast<char *>( |
1092 | 0 | poFFPT->GetSubfieldData(poRIND, &nMaxBytes, iRef))); |
1093 | 0 | if (pabyData == nullptr || nMaxBytes < 1) |
1094 | 0 | { |
1095 | 0 | CSLDestroy(papszRefs); |
1096 | 0 | CPLFree(panRIND); |
1097 | 0 | return; |
1098 | 0 | } |
1099 | 0 | panRIND[iRef] = pabyData[0]; |
1100 | 0 | } |
1101 | | |
1102 | 0 | poFeature->SetField("LNAM_REFS", papszRefs); |
1103 | 0 | CSLDestroy(papszRefs); |
1104 | |
|
1105 | 0 | poFeature->SetField("FFPT_RIND", nRefCount, panRIND); |
1106 | 0 | CPLFree(panRIND); |
1107 | 0 | } |
1108 | | |
1109 | | /************************************************************************/ |
1110 | | /* GenerateFSPTAttributes() */ |
1111 | | /************************************************************************/ |
1112 | | |
1113 | | void S57Reader::GenerateFSPTAttributes(DDFRecord *poRecord, |
1114 | | OGRFeature *poFeature) |
1115 | | |
1116 | 0 | { |
1117 | | /* -------------------------------------------------------------------- */ |
1118 | | /* Feature the spatial record containing the point. */ |
1119 | | /* -------------------------------------------------------------------- */ |
1120 | 0 | DDFField *poFSPT = poRecord->FindField("FSPT"); |
1121 | 0 | if (poFSPT == nullptr) |
1122 | 0 | return; |
1123 | | |
1124 | 0 | const int nCount = poFSPT->GetRepeatCount(); |
1125 | | |
1126 | | /* -------------------------------------------------------------------- */ |
1127 | | /* Allocate working lists of the attributes. */ |
1128 | | /* -------------------------------------------------------------------- */ |
1129 | 0 | int *const panORNT = static_cast<int *>(CPLMalloc(sizeof(int) * nCount)); |
1130 | 0 | int *const panUSAG = static_cast<int *>(CPLMalloc(sizeof(int) * nCount)); |
1131 | 0 | int *const panMASK = static_cast<int *>(CPLMalloc(sizeof(int) * nCount)); |
1132 | 0 | int *const panRCNM = static_cast<int *>(CPLMalloc(sizeof(int) * nCount)); |
1133 | 0 | int *panRCID = static_cast<int *>(CPLMalloc(sizeof(int) * nCount)); |
1134 | | |
1135 | | /* -------------------------------------------------------------------- */ |
1136 | | /* loop over all entries, decoding them. */ |
1137 | | /* -------------------------------------------------------------------- */ |
1138 | 0 | for (int i = 0; i < nCount; i++) |
1139 | 0 | { |
1140 | 0 | panRCID[i] = ParseName(poFSPT, i, panRCNM + i); |
1141 | 0 | panORNT[i] = poRecord->GetIntSubfield("FSPT", 0, "ORNT", i); |
1142 | 0 | panUSAG[i] = poRecord->GetIntSubfield("FSPT", 0, "USAG", i); |
1143 | 0 | panMASK[i] = poRecord->GetIntSubfield("FSPT", 0, "MASK", i); |
1144 | 0 | } |
1145 | | |
1146 | | /* -------------------------------------------------------------------- */ |
1147 | | /* Assign to feature. */ |
1148 | | /* -------------------------------------------------------------------- */ |
1149 | 0 | poFeature->SetField("NAME_RCNM", nCount, panRCNM); |
1150 | 0 | poFeature->SetField("NAME_RCID", nCount, panRCID); |
1151 | 0 | poFeature->SetField("ORNT", nCount, panORNT); |
1152 | 0 | poFeature->SetField("USAG", nCount, panUSAG); |
1153 | 0 | poFeature->SetField("MASK", nCount, panMASK); |
1154 | | |
1155 | | /* -------------------------------------------------------------------- */ |
1156 | | /* Cleanup. */ |
1157 | | /* -------------------------------------------------------------------- */ |
1158 | 0 | CPLFree(panRCNM); |
1159 | 0 | CPLFree(panRCID); |
1160 | 0 | CPLFree(panORNT); |
1161 | 0 | CPLFree(panUSAG); |
1162 | 0 | CPLFree(panMASK); |
1163 | 0 | } |
1164 | | |
1165 | | /************************************************************************/ |
1166 | | /* ReadDSID() */ |
1167 | | /************************************************************************/ |
1168 | | |
1169 | | OGRFeature *S57Reader::ReadDSID() |
1170 | | |
1171 | 0 | { |
1172 | 0 | if (poDSIDRecord == nullptr && poDSPMRecord == nullptr) |
1173 | 0 | return nullptr; |
1174 | | |
1175 | | /* -------------------------------------------------------------------- */ |
1176 | | /* Find the feature definition to use. */ |
1177 | | /* -------------------------------------------------------------------- */ |
1178 | 0 | OGRFeatureDefn *poFDefn = nullptr; |
1179 | |
|
1180 | 0 | for (int i = 0; i < nFDefnCount; i++) |
1181 | 0 | { |
1182 | 0 | if (EQUAL(papoFDefnList[i]->GetName(), "DSID")) |
1183 | 0 | { |
1184 | 0 | poFDefn = papoFDefnList[i]; |
1185 | 0 | break; |
1186 | 0 | } |
1187 | 0 | } |
1188 | |
|
1189 | 0 | if (poFDefn == nullptr) |
1190 | 0 | { |
1191 | | // CPLAssert( false ); |
1192 | 0 | return nullptr; |
1193 | 0 | } |
1194 | | |
1195 | | /* -------------------------------------------------------------------- */ |
1196 | | /* Create feature. */ |
1197 | | /* -------------------------------------------------------------------- */ |
1198 | 0 | OGRFeature *poFeature = new OGRFeature(poFDefn); |
1199 | | |
1200 | | /* -------------------------------------------------------------------- */ |
1201 | | /* Apply DSID values. */ |
1202 | | /* -------------------------------------------------------------------- */ |
1203 | 0 | if (poDSIDRecord != nullptr) |
1204 | 0 | { |
1205 | 0 | poFeature->SetField("DSID_EXPP", |
1206 | 0 | poDSIDRecord->GetIntSubfield("DSID", 0, "EXPP", 0)); |
1207 | 0 | poFeature->SetField("DSID_INTU", |
1208 | 0 | poDSIDRecord->GetIntSubfield("DSID", 0, "INTU", 0)); |
1209 | 0 | poFeature->SetField( |
1210 | 0 | "DSID_DSNM", poDSIDRecord->GetStringSubfield("DSID", 0, "DSNM", 0)); |
1211 | 0 | if (!m_osEDTNUpdate.empty()) |
1212 | 0 | poFeature->SetField("DSID_EDTN", m_osEDTNUpdate.c_str()); |
1213 | 0 | else |
1214 | 0 | poFeature->SetField("DSID_EDTN", poDSIDRecord->GetStringSubfield( |
1215 | 0 | "DSID", 0, "EDTN", 0)); |
1216 | 0 | if (!m_osUPDNUpdate.empty()) |
1217 | 0 | poFeature->SetField("DSID_UPDN", m_osUPDNUpdate.c_str()); |
1218 | 0 | else |
1219 | 0 | poFeature->SetField("DSID_UPDN", poDSIDRecord->GetStringSubfield( |
1220 | 0 | "DSID", 0, "UPDN", 0)); |
1221 | |
|
1222 | 0 | poFeature->SetField( |
1223 | 0 | "DSID_UADT", poDSIDRecord->GetStringSubfield("DSID", 0, "UADT", 0)); |
1224 | 0 | if (!m_osISDTUpdate.empty()) |
1225 | 0 | poFeature->SetField("DSID_ISDT", m_osISDTUpdate.c_str()); |
1226 | 0 | else |
1227 | 0 | poFeature->SetField("DSID_ISDT", poDSIDRecord->GetStringSubfield( |
1228 | 0 | "DSID", 0, "ISDT", 0)); |
1229 | 0 | poFeature->SetField( |
1230 | 0 | "DSID_STED", poDSIDRecord->GetFloatSubfield("DSID", 0, "STED", 0)); |
1231 | 0 | poFeature->SetField("DSID_PRSP", |
1232 | 0 | poDSIDRecord->GetIntSubfield("DSID", 0, "PRSP", 0)); |
1233 | 0 | poFeature->SetField( |
1234 | 0 | "DSID_PSDN", poDSIDRecord->GetStringSubfield("DSID", 0, "PSDN", 0)); |
1235 | 0 | poFeature->SetField( |
1236 | 0 | "DSID_PRED", poDSIDRecord->GetStringSubfield("DSID", 0, "PRED", 0)); |
1237 | 0 | poFeature->SetField("DSID_PROF", |
1238 | 0 | poDSIDRecord->GetIntSubfield("DSID", 0, "PROF", 0)); |
1239 | 0 | poFeature->SetField("DSID_AGEN", |
1240 | 0 | poDSIDRecord->GetIntSubfield("DSID", 0, "AGEN", 0)); |
1241 | 0 | poFeature->SetField( |
1242 | 0 | "DSID_COMT", poDSIDRecord->GetStringSubfield("DSID", 0, "COMT", 0)); |
1243 | | |
1244 | | /* -------------------------------------------------------------------- |
1245 | | */ |
1246 | | /* Apply DSSI values. */ |
1247 | | /* -------------------------------------------------------------------- |
1248 | | */ |
1249 | 0 | poFeature->SetField("DSSI_DSTR", |
1250 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "DSTR", 0)); |
1251 | 0 | poFeature->SetField("DSSI_AALL", |
1252 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "AALL", 0)); |
1253 | 0 | poFeature->SetField("DSSI_NALL", |
1254 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NALL", 0)); |
1255 | 0 | poFeature->SetField("DSSI_NOMR", |
1256 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOMR", 0)); |
1257 | 0 | poFeature->SetField("DSSI_NOCR", |
1258 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOCR", 0)); |
1259 | 0 | poFeature->SetField("DSSI_NOGR", |
1260 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOGR", 0)); |
1261 | 0 | poFeature->SetField("DSSI_NOLR", |
1262 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOLR", 0)); |
1263 | 0 | poFeature->SetField("DSSI_NOIN", |
1264 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOIN", 0)); |
1265 | 0 | poFeature->SetField("DSSI_NOCN", |
1266 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOCN", 0)); |
1267 | 0 | poFeature->SetField("DSSI_NOED", |
1268 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOED", 0)); |
1269 | 0 | poFeature->SetField("DSSI_NOFA", |
1270 | 0 | poDSIDRecord->GetIntSubfield("DSSI", 0, "NOFA", 0)); |
1271 | 0 | } |
1272 | | |
1273 | | /* -------------------------------------------------------------------- */ |
1274 | | /* Apply DSPM record. */ |
1275 | | /* -------------------------------------------------------------------- */ |
1276 | 0 | if (poDSPMRecord != nullptr) |
1277 | 0 | { |
1278 | 0 | poFeature->SetField("DSPM_HDAT", |
1279 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "HDAT", 0)); |
1280 | 0 | poFeature->SetField("DSPM_VDAT", |
1281 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "VDAT", 0)); |
1282 | 0 | poFeature->SetField("DSPM_SDAT", |
1283 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "SDAT", 0)); |
1284 | 0 | poFeature->SetField("DSPM_CSCL", |
1285 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "CSCL", 0)); |
1286 | 0 | poFeature->SetField("DSPM_DUNI", |
1287 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "DUNI", 0)); |
1288 | 0 | poFeature->SetField("DSPM_HUNI", |
1289 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "HUNI", 0)); |
1290 | 0 | poFeature->SetField("DSPM_PUNI", |
1291 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "PUNI", 0)); |
1292 | 0 | poFeature->SetField("DSPM_COUN", |
1293 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "COUN", 0)); |
1294 | 0 | poFeature->SetField("DSPM_COMF", |
1295 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "COMF", 0)); |
1296 | 0 | poFeature->SetField("DSPM_SOMF", |
1297 | 0 | poDSPMRecord->GetIntSubfield("DSPM", 0, "SOMF", 0)); |
1298 | 0 | poFeature->SetField( |
1299 | 0 | "DSPM_COMT", poDSPMRecord->GetStringSubfield("DSPM", 0, "COMT", 0)); |
1300 | 0 | } |
1301 | |
|
1302 | 0 | poFeature->SetFID(nNextDSIDIndex++); |
1303 | |
|
1304 | 0 | return poFeature; |
1305 | 0 | } |
1306 | | |
1307 | | /************************************************************************/ |
1308 | | /* ReadVector() */ |
1309 | | /* */ |
1310 | | /* Read a vector primitive objects based on the type (RCNM_) */ |
1311 | | /* and index within the related index. */ |
1312 | | /************************************************************************/ |
1313 | | |
1314 | | OGRFeature *S57Reader::ReadVector(int nFeatureId, int nRCNM) |
1315 | | |
1316 | 0 | { |
1317 | 0 | DDFRecordIndex *poIndex = nullptr; |
1318 | 0 | const char *pszFDName = nullptr; |
1319 | | |
1320 | | /* -------------------------------------------------------------------- */ |
1321 | | /* What type of vector are we fetching. */ |
1322 | | /* -------------------------------------------------------------------- */ |
1323 | 0 | switch (nRCNM) |
1324 | 0 | { |
1325 | 0 | case RCNM_VI: |
1326 | 0 | poIndex = &oVI_Index; |
1327 | 0 | pszFDName = OGRN_VI; |
1328 | 0 | break; |
1329 | | |
1330 | 0 | case RCNM_VC: |
1331 | 0 | poIndex = &oVC_Index; |
1332 | 0 | pszFDName = OGRN_VC; |
1333 | 0 | break; |
1334 | | |
1335 | 0 | case RCNM_VE: |
1336 | 0 | poIndex = &oVE_Index; |
1337 | 0 | pszFDName = OGRN_VE; |
1338 | 0 | break; |
1339 | | |
1340 | 0 | case RCNM_VF: |
1341 | 0 | poIndex = &oVF_Index; |
1342 | 0 | pszFDName = OGRN_VF; |
1343 | 0 | break; |
1344 | | |
1345 | 0 | default: |
1346 | 0 | CPLAssert(false); |
1347 | 0 | return nullptr; |
1348 | 0 | } |
1349 | | |
1350 | 0 | if (nFeatureId < 0 || nFeatureId >= poIndex->GetCount()) |
1351 | 0 | return nullptr; |
1352 | | |
1353 | 0 | DDFRecord *poRecord = poIndex->GetByIndex(nFeatureId); |
1354 | | |
1355 | | /* -------------------------------------------------------------------- */ |
1356 | | /* Find the feature definition to use. */ |
1357 | | /* -------------------------------------------------------------------- */ |
1358 | 0 | OGRFeatureDefn *poFDefn = nullptr; |
1359 | |
|
1360 | 0 | for (int i = 0; i < nFDefnCount; i++) |
1361 | 0 | { |
1362 | 0 | if (EQUAL(papoFDefnList[i]->GetName(), pszFDName)) |
1363 | 0 | { |
1364 | 0 | poFDefn = papoFDefnList[i]; |
1365 | 0 | break; |
1366 | 0 | } |
1367 | 0 | } |
1368 | |
|
1369 | 0 | if (poFDefn == nullptr) |
1370 | 0 | { |
1371 | | // CPLAssert( false ); |
1372 | 0 | return nullptr; |
1373 | 0 | } |
1374 | | |
1375 | | /* -------------------------------------------------------------------- */ |
1376 | | /* Create feature, and assign standard fields. */ |
1377 | | /* -------------------------------------------------------------------- */ |
1378 | 0 | OGRFeature *poFeature = new OGRFeature(poFDefn); |
1379 | |
|
1380 | 0 | poFeature->SetFID(nFeatureId); |
1381 | |
|
1382 | 0 | poFeature->SetField("RCNM", poRecord->GetIntSubfield("VRID", 0, "RCNM", 0)); |
1383 | 0 | poFeature->SetField("RCID", poRecord->GetIntSubfield("VRID", 0, "RCID", 0)); |
1384 | 0 | poFeature->SetField("RVER", poRecord->GetIntSubfield("VRID", 0, "RVER", 0)); |
1385 | 0 | poFeature->SetField("RUIN", poRecord->GetIntSubfield("VRID", 0, "RUIN", 0)); |
1386 | | |
1387 | | /* -------------------------------------------------------------------- */ |
1388 | | /* Collect point geometries. */ |
1389 | | /* -------------------------------------------------------------------- */ |
1390 | 0 | if (nRCNM == RCNM_VI || nRCNM == RCNM_VC) |
1391 | 0 | { |
1392 | 0 | if (poRecord->FindField("SG2D") != nullptr) |
1393 | 0 | { |
1394 | 0 | const double dfX = |
1395 | 0 | poRecord->GetIntSubfield("SG2D", 0, "XCOO", 0) / (double)nCOMF; |
1396 | 0 | const double dfY = |
1397 | 0 | poRecord->GetIntSubfield("SG2D", 0, "YCOO", 0) / (double)nCOMF; |
1398 | 0 | poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY)); |
1399 | 0 | } |
1400 | | |
1401 | 0 | else if (poRecord->FindField("SG3D") != nullptr) /* presume sounding*/ |
1402 | 0 | { |
1403 | 0 | const int nVCount = poRecord->FindField("SG3D")->GetRepeatCount(); |
1404 | 0 | if (nVCount == 1) |
1405 | 0 | { |
1406 | 0 | const double dfX = |
1407 | 0 | poRecord->GetIntSubfield("SG3D", 0, "XCOO", 0) / |
1408 | 0 | (double)nCOMF; |
1409 | 0 | const double dfY = |
1410 | 0 | poRecord->GetIntSubfield("SG3D", 0, "YCOO", 0) / |
1411 | 0 | (double)nCOMF; |
1412 | 0 | const double dfZ = |
1413 | 0 | poRecord->GetIntSubfield("SG3D", 0, "VE3D", 0) / |
1414 | 0 | (double)nSOMF; |
1415 | 0 | poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY, dfZ)); |
1416 | 0 | } |
1417 | 0 | else |
1418 | 0 | { |
1419 | 0 | OGRMultiPoint *poMP = new OGRMultiPoint(); |
1420 | |
|
1421 | 0 | for (int i = 0; i < nVCount; i++) |
1422 | 0 | { |
1423 | 0 | const double dfX = |
1424 | 0 | poRecord->GetIntSubfield("SG3D", 0, "XCOO", i) / |
1425 | 0 | static_cast<double>(nCOMF); |
1426 | 0 | const double dfY = |
1427 | 0 | poRecord->GetIntSubfield("SG3D", 0, "YCOO", i) / |
1428 | 0 | static_cast<double>(nCOMF); |
1429 | 0 | const double dfZ = |
1430 | 0 | poRecord->GetIntSubfield("SG3D", 0, "VE3D", i) / |
1431 | 0 | static_cast<double>(nSOMF); |
1432 | |
|
1433 | 0 | poMP->addGeometryDirectly(new OGRPoint(dfX, dfY, dfZ)); |
1434 | 0 | } |
1435 | |
|
1436 | 0 | poFeature->SetGeometryDirectly(poMP); |
1437 | 0 | } |
1438 | 0 | } |
1439 | 0 | } |
1440 | | |
1441 | | /* -------------------------------------------------------------------- */ |
1442 | | /* Collect an edge geometry. */ |
1443 | | /* -------------------------------------------------------------------- */ |
1444 | 0 | else if (nRCNM == RCNM_VE) |
1445 | 0 | { |
1446 | 0 | int nPoints = 0; |
1447 | 0 | OGRLineString *poLine = new OGRLineString(); |
1448 | |
|
1449 | 0 | for (int iField = 0; iField < poRecord->GetFieldCount(); ++iField) |
1450 | 0 | { |
1451 | 0 | DDFField *poSG2D = poRecord->GetField(iField); |
1452 | |
|
1453 | 0 | if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D")) |
1454 | 0 | { |
1455 | 0 | const int nVCount = poSG2D->GetRepeatCount(); |
1456 | |
|
1457 | 0 | poLine->setNumPoints(nPoints + nVCount); |
1458 | |
|
1459 | 0 | for (int i = 0; i < nVCount; ++i) |
1460 | 0 | { |
1461 | 0 | poLine->setPoint( |
1462 | 0 | nPoints++, |
1463 | 0 | poRecord->GetIntSubfield("SG2D", 0, "XCOO", i) / |
1464 | 0 | static_cast<double>(nCOMF), |
1465 | 0 | poRecord->GetIntSubfield("SG2D", 0, "YCOO", i) / |
1466 | 0 | static_cast<double>(nCOMF)); |
1467 | 0 | } |
1468 | 0 | } |
1469 | 0 | } |
1470 | |
|
1471 | 0 | poFeature->SetGeometryDirectly(poLine); |
1472 | 0 | } |
1473 | | |
1474 | | /* -------------------------------------------------------------------- */ |
1475 | | /* Special edge fields. */ |
1476 | | /* Allow either 2 VRPT fields or one VRPT field with 2 rows */ |
1477 | | /* -------------------------------------------------------------------- */ |
1478 | 0 | DDFField *poVRPT = nullptr; |
1479 | |
|
1480 | 0 | if (nRCNM == RCNM_VE && (poVRPT = poRecord->FindField("VRPT")) != nullptr) |
1481 | 0 | { |
1482 | 0 | poFeature->SetField("NAME_RCNM_0", RCNM_VC); |
1483 | 0 | poFeature->SetField("NAME_RCID_0", ParseName(poVRPT)); |
1484 | 0 | poFeature->SetField("ORNT_0", |
1485 | 0 | poRecord->GetIntSubfield("VRPT", 0, "ORNT", 0)); |
1486 | 0 | poFeature->SetField("USAG_0", |
1487 | 0 | poRecord->GetIntSubfield("VRPT", 0, "USAG", 0)); |
1488 | 0 | poFeature->SetField("TOPI_0", |
1489 | 0 | poRecord->GetIntSubfield("VRPT", 0, "TOPI", 0)); |
1490 | 0 | poFeature->SetField("MASK_0", |
1491 | 0 | poRecord->GetIntSubfield("VRPT", 0, "MASK", 0)); |
1492 | |
|
1493 | 0 | int iField = 0; |
1494 | 0 | int iSubField = 1; |
1495 | |
|
1496 | 0 | if (poVRPT->GetRepeatCount() == 1) |
1497 | 0 | { |
1498 | | // Only one row, need a second VRPT field |
1499 | 0 | iField = 1; |
1500 | 0 | iSubField = 0; |
1501 | |
|
1502 | 0 | if ((poVRPT = poRecord->FindField("VRPT", iField)) == nullptr) |
1503 | 0 | { |
1504 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
1505 | 0 | "Unable to fetch last edge node.\n" |
1506 | 0 | "Feature OBJL=%s, RCID=%d may have corrupt or" |
1507 | 0 | " missing geometry.", |
1508 | 0 | poFeature->GetDefnRef()->GetName(), |
1509 | 0 | poFeature->GetFieldAsInteger("RCID")); |
1510 | |
|
1511 | 0 | return poFeature; |
1512 | 0 | } |
1513 | 0 | } |
1514 | | |
1515 | 0 | poFeature->SetField("NAME_RCID_1", ParseName(poVRPT, iSubField)); |
1516 | 0 | poFeature->SetField("NAME_RCNM_1", RCNM_VC); |
1517 | 0 | poFeature->SetField("ORNT_1", poRecord->GetIntSubfield( |
1518 | 0 | "VRPT", iField, "ORNT", iSubField)); |
1519 | 0 | poFeature->SetField("USAG_1", poRecord->GetIntSubfield( |
1520 | 0 | "VRPT", iField, "USAG", iSubField)); |
1521 | 0 | poFeature->SetField("TOPI_1", poRecord->GetIntSubfield( |
1522 | 0 | "VRPT", iField, "TOPI", iSubField)); |
1523 | 0 | poFeature->SetField("MASK_1", poRecord->GetIntSubfield( |
1524 | 0 | "VRPT", iField, "MASK", iSubField)); |
1525 | 0 | } |
1526 | | |
1527 | | /* -------------------------------------------------------------------- */ |
1528 | | /* Geometric attributes */ |
1529 | | /* Retrieve POSACC and QUAPOS attributes */ |
1530 | | /* -------------------------------------------------------------------- */ |
1531 | | |
1532 | 0 | const int posaccField = poRegistrar->FindAttrByAcronym("POSACC"); |
1533 | 0 | const int quaposField = poRegistrar->FindAttrByAcronym("QUAPOS"); |
1534 | |
|
1535 | 0 | DDFField *poATTV = poRecord->FindField("ATTV"); |
1536 | 0 | if (poATTV != nullptr) |
1537 | 0 | { |
1538 | 0 | for (int j = 0; j < poATTV->GetRepeatCount(); j++) |
1539 | 0 | { |
1540 | 0 | const int subField = poRecord->GetIntSubfield("ATTV", 0, "ATTL", j); |
1541 | | // POSACC field |
1542 | 0 | if (subField == posaccField) |
1543 | 0 | { |
1544 | 0 | poFeature->SetField( |
1545 | 0 | "POSACC", poRecord->GetFloatSubfield("ATTV", 0, "ATVL", j)); |
1546 | 0 | } |
1547 | | |
1548 | | // QUAPOS field |
1549 | 0 | if (subField == quaposField) |
1550 | 0 | { |
1551 | 0 | poFeature->SetField( |
1552 | 0 | "QUAPOS", poRecord->GetIntSubfield("ATTV", 0, "ATVL", j)); |
1553 | 0 | } |
1554 | 0 | } |
1555 | 0 | } |
1556 | |
|
1557 | 0 | return poFeature; |
1558 | 0 | } |
1559 | | |
1560 | | /************************************************************************/ |
1561 | | /* FetchPoint() */ |
1562 | | /* */ |
1563 | | /* Fetch the location of a spatial point object. */ |
1564 | | /************************************************************************/ |
1565 | | |
1566 | | bool S57Reader::FetchPoint(int nRCNM, int nRCID, double *pdfX, double *pdfY, |
1567 | | double *pdfZ) |
1568 | | |
1569 | 175 | { |
1570 | 175 | DDFRecord *poSRecord = nullptr; |
1571 | | |
1572 | 175 | if (nRCNM == RCNM_VI) |
1573 | 1 | poSRecord = oVI_Index.FindRecord(nRCID); |
1574 | 174 | else |
1575 | 174 | poSRecord = oVC_Index.FindRecord(nRCID); |
1576 | | |
1577 | 175 | if (poSRecord == nullptr) |
1578 | 102 | return false; |
1579 | | |
1580 | 73 | double dfX = 0.0; |
1581 | 73 | double dfY = 0.0; |
1582 | 73 | double dfZ = 0.0; |
1583 | | |
1584 | 73 | if (poSRecord->FindField("SG2D") != nullptr) |
1585 | 72 | { |
1586 | 72 | dfX = poSRecord->GetIntSubfield("SG2D", 0, "XCOO", 0) / |
1587 | 72 | static_cast<double>(nCOMF); |
1588 | 72 | dfY = poSRecord->GetIntSubfield("SG2D", 0, "YCOO", 0) / |
1589 | 72 | static_cast<double>(nCOMF); |
1590 | 72 | } |
1591 | 1 | else if (poSRecord->FindField("SG3D") != nullptr) |
1592 | 1 | { |
1593 | 1 | dfX = poSRecord->GetIntSubfield("SG3D", 0, "XCOO", 0) / |
1594 | 1 | static_cast<double>(nCOMF); |
1595 | 1 | dfY = poSRecord->GetIntSubfield("SG3D", 0, "YCOO", 0) / |
1596 | 1 | static_cast<double>(nCOMF); |
1597 | 1 | dfZ = poSRecord->GetIntSubfield("SG3D", 0, "VE3D", 0) / |
1598 | 1 | static_cast<double>(nSOMF); |
1599 | 1 | } |
1600 | 0 | else |
1601 | 0 | return false; |
1602 | | |
1603 | 73 | if (pdfX != nullptr) |
1604 | 73 | *pdfX = dfX; |
1605 | 73 | if (pdfY != nullptr) |
1606 | 73 | *pdfY = dfY; |
1607 | 73 | if (pdfZ != nullptr) |
1608 | 1 | *pdfZ = dfZ; |
1609 | | |
1610 | 73 | return true; |
1611 | 73 | } |
1612 | | |
1613 | | /************************************************************************/ |
1614 | | /* S57StrokeArcToOGRGeometry_Angles() */ |
1615 | | /************************************************************************/ |
1616 | | |
1617 | | static OGRLineString * |
1618 | | S57StrokeArcToOGRGeometry_Angles(double dfCenterX, double dfCenterY, |
1619 | | double dfRadius, double dfStartAngle, |
1620 | | double dfEndAngle, int nVertexCount) |
1621 | | |
1622 | 0 | { |
1623 | 0 | OGRLineString *const poLine = new OGRLineString; |
1624 | |
|
1625 | 0 | nVertexCount = std::max(2, nVertexCount); |
1626 | 0 | const double dfSlice = (dfEndAngle - dfStartAngle) / (nVertexCount - 1); |
1627 | |
|
1628 | 0 | poLine->setNumPoints(nVertexCount); |
1629 | |
|
1630 | 0 | for (int iPoint = 0; iPoint < nVertexCount; iPoint++) |
1631 | 0 | { |
1632 | 0 | const double dfAngle = (dfStartAngle + iPoint * dfSlice) * M_PI / 180.0; |
1633 | |
|
1634 | 0 | const double dfArcX = dfCenterX + cos(dfAngle) * dfRadius; |
1635 | 0 | const double dfArcY = dfCenterY + sin(dfAngle) * dfRadius; |
1636 | |
|
1637 | 0 | poLine->setPoint(iPoint, dfArcX, dfArcY); |
1638 | 0 | } |
1639 | |
|
1640 | 0 | return poLine; |
1641 | 0 | } |
1642 | | |
1643 | | /************************************************************************/ |
1644 | | /* S57StrokeArcToOGRGeometry_Points() */ |
1645 | | /************************************************************************/ |
1646 | | |
1647 | | static OGRLineString * |
1648 | | S57StrokeArcToOGRGeometry_Points(double dfStartX, double dfStartY, |
1649 | | double dfCenterX, double dfCenterY, |
1650 | | double dfEndX, double dfEndY, int nVertexCount) |
1651 | | |
1652 | 0 | { |
1653 | 0 | double dfStartAngle = 0.0; |
1654 | 0 | double dfEndAngle = 360.0; |
1655 | |
|
1656 | 0 | if (dfStartX == dfEndX && dfStartY == dfEndY) |
1657 | 0 | { |
1658 | | // dfStartAngle = 0.0; |
1659 | | // dfEndAngle = 360.0; |
1660 | 0 | } |
1661 | 0 | else |
1662 | 0 | { |
1663 | 0 | double dfDeltaX = dfStartX - dfCenterX; |
1664 | 0 | double dfDeltaY = dfStartY - dfCenterY; |
1665 | 0 | dfStartAngle = atan2(dfDeltaY, dfDeltaX) * 180.0 / M_PI; |
1666 | |
|
1667 | 0 | dfDeltaX = dfEndX - dfCenterX; |
1668 | 0 | dfDeltaY = dfEndY - dfCenterY; |
1669 | 0 | dfEndAngle = atan2(dfDeltaY, dfDeltaX) * 180.0 / M_PI; |
1670 | |
|
1671 | | #ifdef notdef |
1672 | | if (dfStartAngle > dfAlongAngle && dfAlongAngle > dfEndAngle) |
1673 | | { |
1674 | | // TODO: Use std::swap. |
1675 | | const double dfTempAngle = dfStartAngle; |
1676 | | dfStartAngle = dfEndAngle; |
1677 | | dfEndAngle = dfTempAngle; |
1678 | | } |
1679 | | #endif |
1680 | |
|
1681 | 0 | while (dfStartAngle < dfEndAngle) |
1682 | 0 | dfStartAngle += 360.0; |
1683 | | |
1684 | | // while( dfAlongAngle < dfStartAngle ) |
1685 | | // dfAlongAngle += 360.0; |
1686 | | |
1687 | | // while( dfEndAngle < dfAlongAngle ) |
1688 | | // dfEndAngle += 360.0; |
1689 | |
|
1690 | 0 | if (dfEndAngle - dfStartAngle > 360.0) |
1691 | 0 | { |
1692 | | // TODO: Use std::swap. |
1693 | 0 | const double dfTempAngle = dfStartAngle; |
1694 | 0 | dfStartAngle = dfEndAngle; |
1695 | 0 | dfEndAngle = dfTempAngle; |
1696 | |
|
1697 | 0 | while (dfEndAngle < dfStartAngle) |
1698 | 0 | dfStartAngle -= 360.0; |
1699 | 0 | } |
1700 | 0 | } |
1701 | |
|
1702 | 0 | const double dfRadius = |
1703 | 0 | sqrt((dfCenterX - dfStartX) * (dfCenterX - dfStartX) + |
1704 | 0 | (dfCenterY - dfStartY) * (dfCenterY - dfStartY)); |
1705 | |
|
1706 | 0 | return S57StrokeArcToOGRGeometry_Angles( |
1707 | 0 | dfCenterX, dfCenterY, dfRadius, dfStartAngle, dfEndAngle, nVertexCount); |
1708 | 0 | } |
1709 | | |
1710 | | /************************************************************************/ |
1711 | | /* FetchLine() */ |
1712 | | /************************************************************************/ |
1713 | | |
1714 | | bool S57Reader::FetchLine(DDFRecord *poSRecord, int iStartVertex, |
1715 | | int iDirection, OGRLineString *poLine) |
1716 | | |
1717 | 58 | { |
1718 | 58 | int nPoints = 0; |
1719 | | |
1720 | | /* -------------------------------------------------------------------- */ |
1721 | | /* Points may be multiple rows in one SG2D/AR2D field or */ |
1722 | | /* multiple SG2D/AR2D fields (or a combination of both) */ |
1723 | | /* Iterate over all the SG2D/AR2D fields in the record */ |
1724 | | /* -------------------------------------------------------------------- */ |
1725 | | |
1726 | 271 | for (int iField = 0; iField < poSRecord->GetFieldCount(); ++iField) |
1727 | 213 | { |
1728 | 213 | const DDFField *poSG2D = poSRecord->GetField(iField); |
1729 | 213 | const DDFField *poAR2D = nullptr; |
1730 | | |
1731 | 213 | if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D")) |
1732 | 31 | { |
1733 | 31 | poAR2D = nullptr; |
1734 | 31 | } |
1735 | 182 | else if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D")) |
1736 | 0 | { |
1737 | 0 | poAR2D = poSG2D; |
1738 | 0 | } |
1739 | 182 | else |
1740 | 182 | { |
1741 | | /* Other types of fields are skipped */ |
1742 | 182 | continue; |
1743 | 182 | } |
1744 | | |
1745 | | /* -------------------------------------------------------------------- |
1746 | | */ |
1747 | | /* Get some basic definitions. */ |
1748 | | /* -------------------------------------------------------------------- |
1749 | | */ |
1750 | | |
1751 | 31 | const DDFSubfieldDefn *poXCOO = |
1752 | 31 | poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO"); |
1753 | 31 | const DDFSubfieldDefn *poYCOO = |
1754 | 31 | poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO"); |
1755 | | |
1756 | 31 | if (poXCOO == nullptr || poYCOO == nullptr) |
1757 | 0 | { |
1758 | 0 | CPLDebug("S57", "XCOO or YCOO are NULL"); |
1759 | 0 | return false; |
1760 | 0 | } |
1761 | | |
1762 | 31 | const int nVCount = poSG2D->GetRepeatCount(); |
1763 | | |
1764 | | /* -------------------------------------------------------------------- |
1765 | | */ |
1766 | | /* It is legitimate to have zero vertices for line segments */ |
1767 | | /* that just have the start and end node (bug 840). */ |
1768 | | /* */ |
1769 | | /* This is bogus! nVCount != 0, because poXCOO != 0 here */ |
1770 | | /* In case of zero vertices, there will not be any SG2D fields */ |
1771 | | /* -------------------------------------------------------------------- |
1772 | | */ |
1773 | 31 | if (nVCount == 0) |
1774 | 0 | continue; |
1775 | | |
1776 | | /* -------------------------------------------------------------------- |
1777 | | */ |
1778 | | /* Make sure out line is long enough to hold all the vertices */ |
1779 | | /* we will apply. */ |
1780 | | /* -------------------------------------------------------------------- |
1781 | | */ |
1782 | 31 | int nVBase = 0; |
1783 | | |
1784 | 31 | if (iDirection < 0) |
1785 | 0 | nVBase = iStartVertex + nPoints + nVCount; |
1786 | 31 | else |
1787 | 31 | nVBase = iStartVertex + nPoints; |
1788 | | |
1789 | 31 | if (poLine->getNumPoints() < iStartVertex + nPoints + nVCount) |
1790 | 31 | poLine->setNumPoints(iStartVertex + nPoints + nVCount); |
1791 | | |
1792 | 31 | nPoints += nVCount; |
1793 | | /* -------------------------------------------------------------------- |
1794 | | */ |
1795 | | /* Are the SG2D and XCOO/YCOO definitions in the form we expect? */ |
1796 | | /* -------------------------------------------------------------------- |
1797 | | */ |
1798 | 31 | const bool bStandardFormat = |
1799 | 31 | (poSG2D->GetFieldDefn()->GetSubfieldCount() == 2) && |
1800 | 31 | EQUAL(poXCOO->GetFormat(), "b24") && |
1801 | 31 | EQUAL(poYCOO->GetFormat(), "b24"); |
1802 | | |
1803 | | /* -------------------------------------------------------------------- |
1804 | | */ |
1805 | | /* Collect the vertices: */ |
1806 | | /* */ |
1807 | | /* This approach assumes that the data is LSB organized int32 */ |
1808 | | /* binary data as per the specification. We avoid lots of */ |
1809 | | /* extra calls to low level DDF methods as they are quite */ |
1810 | | /* expensive. */ |
1811 | | /* -------------------------------------------------------------------- |
1812 | | */ |
1813 | 31 | if (bStandardFormat) |
1814 | 31 | { |
1815 | 31 | int nBytesRemaining = 0; |
1816 | | |
1817 | 31 | const char *pachData = |
1818 | 31 | poSG2D->GetSubfieldData(poYCOO, &nBytesRemaining, 0); |
1819 | | |
1820 | 2.34k | for (int i = 0; i < nVCount; i++) |
1821 | 2.31k | { |
1822 | 2.31k | GInt32 nYCOO = 0; |
1823 | 2.31k | memcpy(&nYCOO, pachData, 4); |
1824 | 2.31k | pachData += 4; |
1825 | | |
1826 | 2.31k | GInt32 nXCOO = 0; |
1827 | 2.31k | memcpy(&nXCOO, pachData, 4); |
1828 | 2.31k | pachData += 4; |
1829 | | |
1830 | | #ifdef CPL_MSB |
1831 | | CPL_SWAP32PTR(&nXCOO); |
1832 | | CPL_SWAP32PTR(&nYCOO); |
1833 | | #endif |
1834 | 2.31k | const double dfX = nXCOO / static_cast<double>(nCOMF); |
1835 | 2.31k | const double dfY = nYCOO / static_cast<double>(nCOMF); |
1836 | | |
1837 | 2.31k | poLine->setPoint(nVBase, dfX, dfY); |
1838 | | |
1839 | 2.31k | nVBase += iDirection; |
1840 | 2.31k | } |
1841 | 31 | } |
1842 | | |
1843 | | /* -------------------------------------------------------------------- |
1844 | | */ |
1845 | | /* Collect the vertices: */ |
1846 | | /* */ |
1847 | | /* The generic case where we use low level but expensive DDF */ |
1848 | | /* methods to get the data. This should work even if some */ |
1849 | | /* things are changed about the SG2D fields such as making them */ |
1850 | | /* floating point or a different byte order. */ |
1851 | | /* -------------------------------------------------------------------- |
1852 | | */ |
1853 | 0 | else |
1854 | 0 | { |
1855 | 0 | for (int i = 0; i < nVCount; i++) |
1856 | 0 | { |
1857 | 0 | int nBytesRemaining = 0; |
1858 | |
|
1859 | 0 | const char *pachData = |
1860 | 0 | poSG2D->GetSubfieldData(poXCOO, &nBytesRemaining, i); |
1861 | |
|
1862 | 0 | const double dfX = |
1863 | 0 | poXCOO->ExtractIntData(pachData, nBytesRemaining, nullptr) / |
1864 | 0 | static_cast<double>(nCOMF); |
1865 | |
|
1866 | 0 | pachData = poSG2D->GetSubfieldData(poYCOO, &nBytesRemaining, i); |
1867 | |
|
1868 | 0 | const double dfY = |
1869 | 0 | poXCOO->ExtractIntData(pachData, nBytesRemaining, nullptr) / |
1870 | 0 | static_cast<double>(nCOMF); |
1871 | |
|
1872 | 0 | poLine->setPoint(nVBase, dfX, dfY); |
1873 | |
|
1874 | 0 | nVBase += iDirection; |
1875 | 0 | } |
1876 | 0 | } |
1877 | | |
1878 | | /* -------------------------------------------------------------------- |
1879 | | */ |
1880 | | /* If this is actually an arc, turn the start, end and center */ |
1881 | | /* of rotation into a "stroked" arc linestring. */ |
1882 | | /* -------------------------------------------------------------------- |
1883 | | */ |
1884 | 31 | if (poAR2D != nullptr && poLine->getNumPoints() >= 3) |
1885 | 0 | { |
1886 | 0 | int iLast = poLine->getNumPoints() - 1; |
1887 | |
|
1888 | 0 | OGRLineString *poArc = S57StrokeArcToOGRGeometry_Points( |
1889 | 0 | poLine->getX(iLast - 0), poLine->getY(iLast - 0), |
1890 | 0 | poLine->getX(iLast - 1), poLine->getY(iLast - 1), |
1891 | 0 | poLine->getX(iLast - 2), poLine->getY(iLast - 2), 30); |
1892 | |
|
1893 | 0 | if (poArc != nullptr) |
1894 | 0 | { |
1895 | 0 | for (int i = 0; i < poArc->getNumPoints(); i++) |
1896 | 0 | poLine->setPoint(iLast - 2 + i, poArc->getX(i), |
1897 | 0 | poArc->getY(i)); |
1898 | |
|
1899 | 0 | delete poArc; |
1900 | 0 | } |
1901 | 0 | } |
1902 | 31 | } |
1903 | | |
1904 | 58 | return true; |
1905 | 58 | } |
1906 | | |
1907 | | /************************************************************************/ |
1908 | | /* AssemblePointGeometry() */ |
1909 | | /************************************************************************/ |
1910 | | |
1911 | | void S57Reader::AssemblePointGeometry(DDFRecord *poFRecord, |
1912 | | OGRFeature *poFeature) |
1913 | | |
1914 | 3.13k | { |
1915 | | /* -------------------------------------------------------------------- */ |
1916 | | /* Feature the spatial record containing the point. */ |
1917 | | /* -------------------------------------------------------------------- */ |
1918 | 3.13k | DDFField *poFSPT = poFRecord->FindField("FSPT"); |
1919 | 3.13k | if (poFSPT == nullptr) |
1920 | 3.11k | return; |
1921 | | |
1922 | 28 | if (poFSPT->GetRepeatCount() != 1) |
1923 | 2 | { |
1924 | | #ifdef DEBUG |
1925 | | fprintf(stderr, /*ok*/ |
1926 | | "Point features with other than one spatial linkage.\n"); |
1927 | | poFRecord->Dump(stderr); |
1928 | | #endif |
1929 | 2 | CPLDebug( |
1930 | 2 | "S57", |
1931 | 2 | "Point feature encountered with other than one spatial linkage."); |
1932 | 2 | } |
1933 | | |
1934 | 28 | int nRCNM = 0; |
1935 | 28 | const int nRCID = ParseName(poFSPT, 0, &nRCNM); |
1936 | | |
1937 | 28 | double dfX = 0.0; |
1938 | 28 | double dfY = 0.0; |
1939 | 28 | double dfZ = 0.0; |
1940 | | |
1941 | 28 | if (nRCID == -1 || !FetchPoint(nRCNM, nRCID, &dfX, &dfY, &dfZ)) |
1942 | 27 | { |
1943 | 27 | CPLError(CE_Warning, CPLE_AppDefined, |
1944 | 27 | "Failed to fetch %d/%d point geometry for point feature.\n" |
1945 | 27 | "Feature will have empty geometry.", |
1946 | 27 | nRCNM, nRCID); |
1947 | 27 | return; |
1948 | 27 | } |
1949 | | |
1950 | 1 | if (dfZ == 0.0) |
1951 | 0 | poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY)); |
1952 | 1 | else |
1953 | 1 | poFeature->SetGeometryDirectly(new OGRPoint(dfX, dfY, dfZ)); |
1954 | 1 | } |
1955 | | |
1956 | | /************************************************************************/ |
1957 | | /* AssembleSoundingGeometry() */ |
1958 | | /************************************************************************/ |
1959 | | |
1960 | | void S57Reader::AssembleSoundingGeometry(DDFRecord *poFRecord, |
1961 | | OGRFeature *poFeature) |
1962 | | |
1963 | 56 | { |
1964 | | /* -------------------------------------------------------------------- */ |
1965 | | /* Feature the spatial record containing the point. */ |
1966 | | /* -------------------------------------------------------------------- */ |
1967 | 56 | DDFField *poFSPT = poFRecord->FindField("FSPT"); |
1968 | 56 | if (poFSPT == nullptr) |
1969 | 27 | return; |
1970 | | |
1971 | 29 | if (poFSPT->GetRepeatCount() != 1) |
1972 | 1 | return; |
1973 | | |
1974 | 28 | int nRCNM = 0; |
1975 | 28 | const int nRCID = ParseName(poFSPT, 0, &nRCNM); |
1976 | | |
1977 | 28 | DDFRecord *poSRecord = nRCNM == RCNM_VI ? oVI_Index.FindRecord(nRCID) |
1978 | 28 | : oVC_Index.FindRecord(nRCID); |
1979 | | |
1980 | 28 | if (poSRecord == nullptr) |
1981 | 19 | return; |
1982 | | |
1983 | | /* -------------------------------------------------------------------- */ |
1984 | | /* Extract vertices. */ |
1985 | | /* -------------------------------------------------------------------- */ |
1986 | 9 | OGRMultiPoint *const poMP = new OGRMultiPoint(); |
1987 | | |
1988 | 9 | DDFField *poField = poSRecord->FindField("SG2D"); |
1989 | 9 | if (poField == nullptr) |
1990 | 9 | poField = poSRecord->FindField("SG3D"); |
1991 | 9 | if (poField == nullptr) |
1992 | 0 | { |
1993 | 0 | delete poMP; |
1994 | 0 | return; |
1995 | 0 | } |
1996 | | |
1997 | 9 | const DDFSubfieldDefn *poXCOO = |
1998 | 9 | poField->GetFieldDefn()->FindSubfieldDefn("XCOO"); |
1999 | 9 | const DDFSubfieldDefn *poYCOO = |
2000 | 9 | poField->GetFieldDefn()->FindSubfieldDefn("YCOO"); |
2001 | 9 | if (poXCOO == nullptr || poYCOO == nullptr) |
2002 | 0 | { |
2003 | 0 | CPLDebug("S57", "XCOO or YCOO are NULL"); |
2004 | 0 | delete poMP; |
2005 | 0 | return; |
2006 | 0 | } |
2007 | 9 | const DDFSubfieldDefn *const poVE3D = |
2008 | 9 | poField->GetFieldDefn()->FindSubfieldDefn("VE3D"); |
2009 | | |
2010 | 9 | const int nPointCount = poField->GetRepeatCount(); |
2011 | | |
2012 | 9 | const char *pachData = poField->GetData(); |
2013 | 9 | int nBytesLeft = poField->GetDataSize(); |
2014 | | |
2015 | 18 | for (int i = 0; i < nPointCount; i++) |
2016 | 9 | { |
2017 | 9 | int nBytesConsumed = 0; |
2018 | | |
2019 | 9 | const double dfY = |
2020 | 9 | poYCOO->ExtractIntData(pachData, nBytesLeft, &nBytesConsumed) / |
2021 | 9 | static_cast<double>(nCOMF); |
2022 | 9 | nBytesLeft -= nBytesConsumed; |
2023 | 9 | pachData += nBytesConsumed; |
2024 | | |
2025 | 9 | const double dfX = |
2026 | 9 | poXCOO->ExtractIntData(pachData, nBytesLeft, &nBytesConsumed) / |
2027 | 9 | static_cast<double>(nCOMF); |
2028 | 9 | nBytesLeft -= nBytesConsumed; |
2029 | 9 | pachData += nBytesConsumed; |
2030 | | |
2031 | 9 | double dfZ = 0.0; |
2032 | 9 | if (poVE3D != nullptr) |
2033 | 9 | { |
2034 | 9 | dfZ = |
2035 | 9 | poYCOO->ExtractIntData(pachData, nBytesLeft, &nBytesConsumed) / |
2036 | 9 | static_cast<double>(nSOMF); |
2037 | 9 | nBytesLeft -= nBytesConsumed; |
2038 | 9 | pachData += nBytesConsumed; |
2039 | 9 | } |
2040 | | |
2041 | 9 | poMP->addGeometryDirectly(new OGRPoint(dfX, dfY, dfZ)); |
2042 | 9 | } |
2043 | | |
2044 | 9 | poFeature->SetGeometryDirectly(poMP); |
2045 | 9 | } |
2046 | | |
2047 | | /************************************************************************/ |
2048 | | /* GetIntSubfield() */ |
2049 | | /************************************************************************/ |
2050 | | |
2051 | | static int GetIntSubfield(const DDFField *poField, const char *pszSubfield, |
2052 | | int iSubfieldIndex) |
2053 | 565 | { |
2054 | 565 | const DDFSubfieldDefn *poSFDefn = |
2055 | 565 | poField->GetFieldDefn()->FindSubfieldDefn(pszSubfield); |
2056 | | |
2057 | 565 | if (poSFDefn == nullptr) |
2058 | 499 | return 0; |
2059 | | |
2060 | | /* -------------------------------------------------------------------- */ |
2061 | | /* Get a pointer to the data. */ |
2062 | | /* -------------------------------------------------------------------- */ |
2063 | 66 | int nBytesRemaining = 0; |
2064 | | |
2065 | 66 | const char *pachData = |
2066 | 66 | poField->GetSubfieldData(poSFDefn, &nBytesRemaining, iSubfieldIndex); |
2067 | | |
2068 | 66 | return poSFDefn->ExtractIntData(pachData, nBytesRemaining, nullptr); |
2069 | 565 | } |
2070 | | |
2071 | | /************************************************************************/ |
2072 | | /* AssembleLineGeometry() */ |
2073 | | /************************************************************************/ |
2074 | | |
2075 | | void S57Reader::AssembleLineGeometry(DDFRecord *poFRecord, |
2076 | | OGRFeature *poFeature) |
2077 | | |
2078 | 3.88k | { |
2079 | 3.88k | OGRLineString *poLine = new OGRLineString(); |
2080 | 3.88k | OGRMultiLineString *poMLS = new OGRMultiLineString(); |
2081 | | |
2082 | | /* -------------------------------------------------------------------- */ |
2083 | | /* Loop collecting edges. */ |
2084 | | /* Iterate over the FSPT fields. */ |
2085 | | /* -------------------------------------------------------------------- */ |
2086 | 3.88k | const int nFieldCount = poFRecord->GetFieldCount(); |
2087 | | |
2088 | 11.8k | for (int iField = 0; iField < nFieldCount; ++iField) |
2089 | 7.99k | { |
2090 | 7.99k | double dlastfX = 0.0; |
2091 | 7.99k | double dlastfY = 0.0; |
2092 | | |
2093 | 7.99k | const DDFField *poFSPT = poFRecord->GetField(iField); |
2094 | | |
2095 | 7.99k | const auto poFieldDefn = poFSPT->GetFieldDefn(); |
2096 | 7.99k | if (!poFieldDefn || !EQUAL(poFieldDefn->GetName(), "FSPT")) |
2097 | 7.93k | continue; |
2098 | | |
2099 | | /* -------------------------------------------------------------------- |
2100 | | */ |
2101 | | /* Loop over the rows of each FSPT field */ |
2102 | | /* -------------------------------------------------------------------- |
2103 | | */ |
2104 | 59 | const int nEdgeCount = poFSPT->GetRepeatCount(); |
2105 | | |
2106 | 141 | for (int iEdge = 0; iEdge < nEdgeCount; ++iEdge) |
2107 | 82 | { |
2108 | 82 | const bool bReverse = (GetIntSubfield(poFSPT, "ORNT", iEdge) == 2); |
2109 | | |
2110 | | /* -------------------------------------------------------------------- |
2111 | | */ |
2112 | | /* Find the spatial record for this edge. */ |
2113 | | /* -------------------------------------------------------------------- |
2114 | | */ |
2115 | 82 | const int nRCID = ParseName(poFSPT, iEdge); |
2116 | | |
2117 | 82 | DDFRecord *poSRecord = oVE_Index.FindRecord(nRCID); |
2118 | 82 | if (poSRecord == nullptr) |
2119 | 60 | { |
2120 | 60 | CPLError(CE_Warning, CPLE_AppDefined, |
2121 | 60 | "Couldn't find spatial record %d.\n" |
2122 | 60 | "Feature OBJL=%s, RCID=%d may have corrupt or" |
2123 | 60 | "missing geometry.", |
2124 | 60 | nRCID, poFeature->GetDefnRef()->GetName(), |
2125 | 60 | GetIntSubfield(poFSPT, "RCID", 0)); |
2126 | 60 | continue; |
2127 | 60 | } |
2128 | | |
2129 | | /* -------------------------------------------------------------------- |
2130 | | */ |
2131 | | /* Get the first and last nodes */ |
2132 | | /* -------------------------------------------------------------------- |
2133 | | */ |
2134 | 22 | DDFField *poVRPT = poSRecord->FindField("VRPT"); |
2135 | 22 | if (poVRPT == nullptr) |
2136 | 0 | { |
2137 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2138 | 0 | "Unable to fetch start node for RCID %d.\n" |
2139 | 0 | "Feature OBJL=%s, RCID=%d may have corrupt or" |
2140 | 0 | "missing geometry.", |
2141 | 0 | nRCID, poFeature->GetDefnRef()->GetName(), |
2142 | 0 | GetIntSubfield(poFSPT, "RCID", 0)); |
2143 | 0 | continue; |
2144 | 0 | } |
2145 | | |
2146 | | // The "VRPT" field has only one row |
2147 | | // Get the next row from a second "VRPT" field |
2148 | 22 | int nVC_RCID_firstnode = 0; |
2149 | 22 | int nVC_RCID_lastnode = 0; |
2150 | | |
2151 | 22 | if (poVRPT->GetRepeatCount() == 1) |
2152 | 0 | { |
2153 | 0 | nVC_RCID_firstnode = ParseName(poVRPT); |
2154 | 0 | poVRPT = poSRecord->FindField("VRPT", 1); |
2155 | |
|
2156 | 0 | if (poVRPT == nullptr) |
2157 | 0 | { |
2158 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2159 | 0 | "Unable to fetch end node for RCID %d.\n" |
2160 | 0 | "Feature OBJL=%s, RCID=%d may have corrupt or" |
2161 | 0 | "missing geometry.", |
2162 | 0 | nRCID, poFeature->GetDefnRef()->GetName(), |
2163 | 0 | GetIntSubfield(poFSPT, "RCID", 0)); |
2164 | 0 | continue; |
2165 | 0 | } |
2166 | | |
2167 | 0 | nVC_RCID_lastnode = ParseName(poVRPT); |
2168 | |
|
2169 | 0 | if (bReverse) |
2170 | 0 | { |
2171 | | // TODO: std::swap. |
2172 | 0 | const int tmp = nVC_RCID_lastnode; |
2173 | 0 | nVC_RCID_lastnode = nVC_RCID_firstnode; |
2174 | 0 | nVC_RCID_firstnode = tmp; |
2175 | 0 | } |
2176 | 0 | } |
2177 | 22 | else if (bReverse) |
2178 | 0 | { |
2179 | 0 | nVC_RCID_lastnode = ParseName(poVRPT); |
2180 | 0 | nVC_RCID_firstnode = ParseName(poVRPT, 1); |
2181 | 0 | } |
2182 | 22 | else |
2183 | 22 | { |
2184 | 22 | nVC_RCID_firstnode = ParseName(poVRPT); |
2185 | 22 | nVC_RCID_lastnode = ParseName(poVRPT, 1); |
2186 | 22 | } |
2187 | | |
2188 | 22 | double dfX = 0.0; |
2189 | 22 | double dfY = 0.0; |
2190 | 22 | if (nVC_RCID_firstnode == -1 || |
2191 | 22 | !FetchPoint(RCNM_VC, nVC_RCID_firstnode, &dfX, &dfY)) |
2192 | 7 | { |
2193 | 7 | CPLError(CE_Warning, CPLE_AppDefined, |
2194 | 7 | "Unable to fetch start node RCID=%d.\n" |
2195 | 7 | "Feature OBJL=%s, RCID=%d may have corrupt or" |
2196 | 7 | " missing geometry.", |
2197 | 7 | nVC_RCID_firstnode, poFeature->GetDefnRef()->GetName(), |
2198 | 7 | poFRecord->GetIntSubfield("FRID", 0, "RCID", 0)); |
2199 | | |
2200 | 7 | continue; |
2201 | 7 | } |
2202 | | |
2203 | | /* -------------------------------------------------------------------- |
2204 | | */ |
2205 | | /* Does the first node match the trailing node on the existing |
2206 | | */ |
2207 | | /* line string? If so, skip it, otherwise if the existing */ |
2208 | | /* linestring is not empty we need to push it out and start a |
2209 | | */ |
2210 | | /* new one as it means things are not connected. */ |
2211 | | /* -------------------------------------------------------------------- |
2212 | | */ |
2213 | 15 | if (poLine->getNumPoints() == 0) |
2214 | 6 | { |
2215 | 6 | poLine->addPoint(dfX, dfY); |
2216 | 6 | } |
2217 | 9 | else if (std::abs(dlastfX - dfX) > 0.00000001 || |
2218 | 9 | std::abs(dlastfY - dfY) > 0.00000001) |
2219 | 0 | { |
2220 | | // we need to start a new linestring. |
2221 | 0 | poMLS->addGeometryDirectly(poLine); |
2222 | 0 | poLine = new OGRLineString(); |
2223 | 0 | poLine->addPoint(dfX, dfY); |
2224 | 0 | } |
2225 | 9 | else |
2226 | 9 | { |
2227 | | /* omit point, already present */ |
2228 | 9 | } |
2229 | | |
2230 | | /* -------------------------------------------------------------------- |
2231 | | */ |
2232 | | /* Collect the vertices. */ |
2233 | | /* Iterate over all the SG2D fields in the Spatial record */ |
2234 | | /* -------------------------------------------------------------------- |
2235 | | */ |
2236 | 75 | for (int iSField = 0; iSField < poSRecord->GetFieldCount(); |
2237 | 60 | ++iSField) |
2238 | 60 | { |
2239 | 60 | const DDFField *poSG2D = poSRecord->GetField(iSField); |
2240 | | |
2241 | 60 | if (EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") || |
2242 | 60 | EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D")) |
2243 | 15 | { |
2244 | 15 | const DDFSubfieldDefn *poXCOO = |
2245 | 15 | poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO"); |
2246 | 15 | const DDFSubfieldDefn *poYCOO = |
2247 | 15 | poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO"); |
2248 | | |
2249 | 15 | if (poXCOO == nullptr || poYCOO == nullptr) |
2250 | 0 | { |
2251 | 0 | CPLDebug("S57", "XCOO or YCOO are NULL"); |
2252 | 0 | delete poLine; |
2253 | 0 | delete poMLS; |
2254 | 0 | return; |
2255 | 0 | } |
2256 | | |
2257 | 15 | const int nVCount = poSG2D->GetRepeatCount(); |
2258 | | |
2259 | 15 | int nStart = 0; |
2260 | 15 | int nEnd = 0; |
2261 | 15 | int nInc = 0; |
2262 | 15 | if (bReverse) |
2263 | 0 | { |
2264 | 0 | nStart = nVCount - 1; |
2265 | 0 | nInc = -1; |
2266 | 0 | } |
2267 | 15 | else |
2268 | 15 | { |
2269 | 15 | nEnd = nVCount - 1; |
2270 | 15 | nInc = 1; |
2271 | 15 | } |
2272 | | |
2273 | 15 | int nVBase = poLine->getNumPoints(); |
2274 | 15 | poLine->setNumPoints(nVBase + nVCount); |
2275 | | |
2276 | 15 | int nBytesRemaining = 0; |
2277 | | |
2278 | 1.63k | for (int i = nStart; i != nEnd + nInc; i += nInc) |
2279 | 1.62k | { |
2280 | 1.62k | const char *pachData = poSG2D->GetSubfieldData( |
2281 | 1.62k | poXCOO, &nBytesRemaining, i); |
2282 | | |
2283 | 1.62k | dfX = poXCOO->ExtractIntData(pachData, nBytesRemaining, |
2284 | 1.62k | nullptr) / |
2285 | 1.62k | static_cast<double>(nCOMF); |
2286 | | |
2287 | 1.62k | pachData = poSG2D->GetSubfieldData(poYCOO, |
2288 | 1.62k | &nBytesRemaining, i); |
2289 | | |
2290 | 1.62k | dfY = poXCOO->ExtractIntData(pachData, nBytesRemaining, |
2291 | 1.62k | nullptr) / |
2292 | 1.62k | static_cast<double>(nCOMF); |
2293 | | |
2294 | 1.62k | poLine->setPoint(nVBase++, dfX, dfY); |
2295 | 1.62k | } |
2296 | 15 | } |
2297 | 60 | } |
2298 | | |
2299 | | // remember the coordinates of the last point |
2300 | 15 | dlastfX = dfX; |
2301 | 15 | dlastfY = dfY; |
2302 | | |
2303 | | /* -------------------------------------------------------------------- |
2304 | | */ |
2305 | | /* Add the end node. */ |
2306 | | /* -------------------------------------------------------------------- |
2307 | | */ |
2308 | 15 | if (nVC_RCID_lastnode != -1 && |
2309 | 15 | FetchPoint(RCNM_VC, nVC_RCID_lastnode, &dfX, &dfY)) |
2310 | 15 | { |
2311 | 15 | poLine->addPoint(dfX, dfY); |
2312 | 15 | dlastfX = dfX; |
2313 | 15 | dlastfY = dfY; |
2314 | 15 | } |
2315 | 0 | else |
2316 | 0 | { |
2317 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
2318 | 0 | "Unable to fetch end node RCID=%d.\n" |
2319 | 0 | "Feature OBJL=%s, RCID=%d may have corrupt or" |
2320 | 0 | " missing geometry.", |
2321 | 0 | nVC_RCID_lastnode, poFeature->GetDefnRef()->GetName(), |
2322 | 0 | poFRecord->GetIntSubfield("FRID", 0, "RCID", 0)); |
2323 | 0 | continue; |
2324 | 0 | } |
2325 | 15 | } |
2326 | 59 | } |
2327 | | |
2328 | | /* -------------------------------------------------------------------- */ |
2329 | | /* Set either the line or multilinestring as the geometry. We */ |
2330 | | /* are careful to just produce a linestring if there are no */ |
2331 | | /* disconnections. */ |
2332 | | /* -------------------------------------------------------------------- */ |
2333 | 3.88k | if (poMLS->getNumGeometries() > 0) |
2334 | 0 | { |
2335 | 0 | poMLS->addGeometryDirectly(poLine); |
2336 | 0 | poFeature->SetGeometryDirectly(poMLS); |
2337 | 0 | } |
2338 | 3.88k | else if (poLine->getNumPoints() >= 2) |
2339 | 6 | { |
2340 | 6 | poFeature->SetGeometryDirectly(poLine); |
2341 | 6 | delete poMLS; |
2342 | 6 | } |
2343 | 3.88k | else |
2344 | 3.88k | { |
2345 | 3.88k | delete poLine; |
2346 | 3.88k | delete poMLS; |
2347 | 3.88k | } |
2348 | 3.88k | } |
2349 | | |
2350 | | /************************************************************************/ |
2351 | | /* AssembleAreaGeometry() */ |
2352 | | /************************************************************************/ |
2353 | | |
2354 | | void S57Reader::AssembleAreaGeometry(const DDFRecord *poFRecord, |
2355 | | OGRFeature *poFeature) |
2356 | | |
2357 | 6.05k | { |
2358 | 6.05k | OGRGeometryCollection *const poLines = new OGRGeometryCollection(); |
2359 | | |
2360 | | /* -------------------------------------------------------------------- */ |
2361 | | /* Find the FSPT fields. */ |
2362 | | /* -------------------------------------------------------------------- */ |
2363 | 6.05k | const int nFieldCount = poFRecord->GetFieldCount(); |
2364 | | |
2365 | 18.4k | for (int iFSPT = 0; iFSPT < nFieldCount; ++iFSPT) |
2366 | 12.3k | { |
2367 | 12.3k | const DDFField *poFSPT = poFRecord->GetField(iFSPT); |
2368 | | |
2369 | 12.3k | const auto poFieldDefn = poFSPT->GetFieldDefn(); |
2370 | 12.3k | if (!poFieldDefn || !EQUAL(poFieldDefn->GetName(), "FSPT")) |
2371 | 12.3k | continue; |
2372 | | |
2373 | 74 | const int nEdgeCount = poFSPT->GetRepeatCount(); |
2374 | | |
2375 | | /* ==================================================================== |
2376 | | */ |
2377 | | /* Loop collecting edges. */ |
2378 | | /* ==================================================================== |
2379 | | */ |
2380 | 555 | for (int iEdge = 0; iEdge < nEdgeCount; iEdge++) |
2381 | 481 | { |
2382 | | /* -------------------------------------------------------------------- |
2383 | | */ |
2384 | | /* Find the spatial record for this edge. */ |
2385 | | /* -------------------------------------------------------------------- |
2386 | | */ |
2387 | 481 | const int nRCID = ParseName(poFSPT, iEdge); |
2388 | | |
2389 | 481 | DDFRecord *poSRecord = oVE_Index.FindRecord(nRCID); |
2390 | 481 | if (poSRecord == nullptr) |
2391 | 423 | { |
2392 | 423 | CPLError(CE_Warning, CPLE_AppDefined, |
2393 | 423 | "Couldn't find spatial record %d.\n" |
2394 | 423 | "Feature OBJL=%s, RCID=%d may have corrupt or" |
2395 | 423 | "missing geometry.", |
2396 | 423 | nRCID, poFeature->GetDefnRef()->GetName(), |
2397 | 423 | GetIntSubfield(poFSPT, "RCID", 0)); |
2398 | 423 | continue; |
2399 | 423 | } |
2400 | | |
2401 | | /* -------------------------------------------------------------------- |
2402 | | */ |
2403 | | /* Create the line string. */ |
2404 | | /* -------------------------------------------------------------------- |
2405 | | */ |
2406 | 58 | OGRLineString *poLine = new OGRLineString(); |
2407 | | |
2408 | | /* -------------------------------------------------------------------- |
2409 | | */ |
2410 | | /* Add the start node. */ |
2411 | | /* -------------------------------------------------------------------- |
2412 | | */ |
2413 | 58 | DDFField *poVRPT = poSRecord->FindField("VRPT"); |
2414 | 58 | if (poVRPT != nullptr) |
2415 | 58 | { |
2416 | 58 | int nVC_RCID = ParseName(poVRPT); |
2417 | 58 | double dfX = 0.0; |
2418 | 58 | double dfY = 0.0; |
2419 | | |
2420 | 58 | if (nVC_RCID != -1 && FetchPoint(RCNM_VC, nVC_RCID, &dfX, &dfY)) |
2421 | 21 | poLine->addPoint(dfX, dfY); |
2422 | 58 | } |
2423 | | |
2424 | | /* -------------------------------------------------------------------- |
2425 | | */ |
2426 | | /* Collect the vertices. */ |
2427 | | /* -------------------------------------------------------------------- |
2428 | | */ |
2429 | 58 | if (!FetchLine(poSRecord, poLine->getNumPoints(), 1, poLine)) |
2430 | 0 | { |
2431 | 0 | CPLDebug("S57", |
2432 | 0 | "FetchLine() failed in AssembleAreaGeometry()!"); |
2433 | 0 | } |
2434 | | |
2435 | | /* -------------------------------------------------------------------- |
2436 | | */ |
2437 | | /* Add the end node. */ |
2438 | | /* -------------------------------------------------------------------- |
2439 | | */ |
2440 | 58 | if (poVRPT != nullptr && poVRPT->GetRepeatCount() > 1) |
2441 | 55 | { |
2442 | 55 | const int nVC_RCID = ParseName(poVRPT, 1); |
2443 | 55 | double dfX = 0.0; |
2444 | 55 | double dfY = 0.0; |
2445 | | |
2446 | 55 | if (nVC_RCID != -1 && FetchPoint(RCNM_VC, nVC_RCID, &dfX, &dfY)) |
2447 | 21 | poLine->addPoint(dfX, dfY); |
2448 | 55 | } |
2449 | 3 | else if ((poVRPT = poSRecord->FindField("VRPT", 1)) != nullptr) |
2450 | 0 | { |
2451 | 0 | const int nVC_RCID = ParseName(poVRPT); |
2452 | 0 | double dfX = 0.0; |
2453 | 0 | double dfY = 0.0; |
2454 | |
|
2455 | 0 | if (nVC_RCID != -1 && FetchPoint(RCNM_VC, nVC_RCID, &dfX, &dfY)) |
2456 | 0 | poLine->addPoint(dfX, dfY); |
2457 | 0 | } |
2458 | | |
2459 | 58 | poLines->addGeometryDirectly(poLine); |
2460 | 58 | } |
2461 | 74 | } |
2462 | | |
2463 | | /* -------------------------------------------------------------------- */ |
2464 | | /* Build lines into a polygon. */ |
2465 | | /* -------------------------------------------------------------------- */ |
2466 | 6.05k | OGRErr eErr; |
2467 | | |
2468 | 6.05k | OGRGeometry *poPolygon = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges( |
2469 | 6.05k | OGRGeometry::ToHandle(poLines), TRUE, FALSE, 0.0, &eErr)); |
2470 | 6.05k | if (eErr != OGRERR_NONE) |
2471 | 13 | { |
2472 | 13 | CPLError(CE_Warning, CPLE_AppDefined, |
2473 | 13 | "Polygon assembly has failed for feature FIDN=%d,FIDS=%d.\n" |
2474 | 13 | "Geometry may be missing or incomplete.", |
2475 | 13 | poFeature->GetFieldAsInteger("FIDN"), |
2476 | 13 | poFeature->GetFieldAsInteger("FIDS")); |
2477 | 13 | } |
2478 | | |
2479 | 6.05k | delete poLines; |
2480 | | |
2481 | 6.05k | if (poPolygon != nullptr) |
2482 | 6.05k | poFeature->SetGeometryDirectly(poPolygon); |
2483 | 6.05k | } |
2484 | | |
2485 | | /************************************************************************/ |
2486 | | /* FindFDefn() */ |
2487 | | /* */ |
2488 | | /* Find the OGRFeatureDefn corresponding to the passed feature */ |
2489 | | /* record. It will search based on geometry class, or object */ |
2490 | | /* class depending on the bClassBased setting. */ |
2491 | | /************************************************************************/ |
2492 | | |
2493 | | OGRFeatureDefn *S57Reader::FindFDefn(DDFRecord *poRecord) |
2494 | | |
2495 | 708k | { |
2496 | 708k | if (poRegistrar != nullptr) |
2497 | 0 | { |
2498 | 0 | const int nOBJL = poRecord->GetIntSubfield("FRID", 0, "OBJL", 0); |
2499 | |
|
2500 | 0 | if (nOBJL < static_cast<int>(apoFDefnByOBJL.size()) && |
2501 | 0 | apoFDefnByOBJL[nOBJL] != nullptr) |
2502 | 0 | return apoFDefnByOBJL[nOBJL]; |
2503 | | |
2504 | 0 | if (!poClassContentExplorer->SelectClass(nOBJL)) |
2505 | 0 | { |
2506 | 0 | for (int i = 0; i < nFDefnCount; i++) |
2507 | 0 | { |
2508 | 0 | if (EQUAL(papoFDefnList[i]->GetName(), "Generic")) |
2509 | 0 | return papoFDefnList[i]; |
2510 | 0 | } |
2511 | 0 | return nullptr; |
2512 | 0 | } |
2513 | | |
2514 | 0 | for (int i = 0; i < nFDefnCount; i++) |
2515 | 0 | { |
2516 | 0 | const char *pszAcronym = poClassContentExplorer->GetAcronym(); |
2517 | 0 | if (pszAcronym != nullptr && |
2518 | 0 | EQUAL(papoFDefnList[i]->GetName(), pszAcronym)) |
2519 | 0 | return papoFDefnList[i]; |
2520 | 0 | } |
2521 | | |
2522 | 0 | return nullptr; |
2523 | 0 | } |
2524 | 708k | else |
2525 | 708k | { |
2526 | 708k | const int nPRIM = poRecord->GetIntSubfield("FRID", 0, "PRIM", 0); |
2527 | 708k | OGRwkbGeometryType eGType; |
2528 | | |
2529 | 708k | if (nPRIM == PRIM_P) |
2530 | 11.0k | eGType = wkbPoint; |
2531 | 696k | else if (nPRIM == PRIM_L) |
2532 | 7.87k | eGType = wkbLineString; |
2533 | 689k | else if (nPRIM == PRIM_A) |
2534 | 12.1k | eGType = wkbPolygon; |
2535 | 676k | else |
2536 | 676k | eGType = wkbNone; |
2537 | | |
2538 | 771k | for (int i = 0; i < nFDefnCount; i++) |
2539 | 771k | { |
2540 | 771k | if (papoFDefnList[i]->GetGeomType() == eGType) |
2541 | 708k | return papoFDefnList[i]; |
2542 | 771k | } |
2543 | 708k | } |
2544 | | |
2545 | 0 | return nullptr; |
2546 | 708k | } |
2547 | | |
2548 | | /************************************************************************/ |
2549 | | /* ParseName() */ |
2550 | | /* */ |
2551 | | /* Pull the RCNM and RCID values from a NAME field. The RCID */ |
2552 | | /* is returned and the RCNM can be gotten via the pnRCNM argument. */ |
2553 | | /* Note: nIndex is the index of the requested 'NAME' instance */ |
2554 | | /************************************************************************/ |
2555 | | |
2556 | | int S57Reader::ParseName(const DDFField *poField, int nIndex, int *pnRCNM) |
2557 | | |
2558 | 776 | { |
2559 | 776 | if (poField == nullptr) |
2560 | 0 | { |
2561 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Missing field in ParseName()."); |
2562 | 0 | return -1; |
2563 | 0 | } |
2564 | | |
2565 | 776 | const DDFSubfieldDefn *poName = |
2566 | 776 | poField->GetFieldDefn()->FindSubfieldDefn("NAME"); |
2567 | 776 | if (poName == nullptr) |
2568 | 42 | return -1; |
2569 | | |
2570 | 734 | int nMaxBytes = 0; |
2571 | 734 | unsigned char *pabyData = |
2572 | 734 | reinterpret_cast<unsigned char *>(const_cast<char *>( |
2573 | 734 | poField->GetSubfieldData(poName, &nMaxBytes, nIndex))); |
2574 | 734 | if (pabyData == nullptr || nMaxBytes < 5) |
2575 | 1 | return -1; |
2576 | | |
2577 | 733 | if (pnRCNM != nullptr) |
2578 | 54 | *pnRCNM = pabyData[0]; |
2579 | | |
2580 | 733 | return CPL_LSBSINT32PTR(pabyData + 1); |
2581 | 734 | } |
2582 | | |
2583 | | /************************************************************************/ |
2584 | | /* AddFeatureDefn() */ |
2585 | | /************************************************************************/ |
2586 | | |
2587 | | void S57Reader::AddFeatureDefn(OGRFeatureDefn *poFDefn) |
2588 | | |
2589 | 2.14k | { |
2590 | 2.14k | nFDefnCount++; |
2591 | 2.14k | papoFDefnList = static_cast<OGRFeatureDefn **>( |
2592 | 2.14k | CPLRealloc(papoFDefnList, sizeof(OGRFeatureDefn *) * nFDefnCount)); |
2593 | | |
2594 | 2.14k | papoFDefnList[nFDefnCount - 1] = poFDefn; |
2595 | | |
2596 | 2.14k | if (poRegistrar != nullptr) |
2597 | 0 | { |
2598 | 0 | if (poClassContentExplorer->SelectClass(poFDefn->GetName())) |
2599 | 0 | { |
2600 | 0 | const int nOBJL = poClassContentExplorer->GetOBJL(); |
2601 | 0 | if (nOBJL >= 0) |
2602 | 0 | { |
2603 | 0 | if (nOBJL >= (int)apoFDefnByOBJL.size()) |
2604 | 0 | apoFDefnByOBJL.resize(nOBJL + 1); |
2605 | 0 | apoFDefnByOBJL[nOBJL] = poFDefn; |
2606 | 0 | } |
2607 | 0 | } |
2608 | 0 | } |
2609 | 2.14k | } |
2610 | | |
2611 | | /************************************************************************/ |
2612 | | /* CollectClassList() */ |
2613 | | /* */ |
2614 | | /* Establish the list of classes (unique OBJL values) that */ |
2615 | | /* occur in this dataset. */ |
2616 | | /************************************************************************/ |
2617 | | |
2618 | | bool S57Reader::CollectClassList(std::vector<int> &anClassCount) |
2619 | | |
2620 | 0 | { |
2621 | 0 | if (!bFileIngested && !Ingest()) |
2622 | 0 | return false; |
2623 | | |
2624 | 0 | bool bSuccess = true; |
2625 | |
|
2626 | 0 | for (int iFEIndex = 0; iFEIndex < oFE_Index.GetCount(); iFEIndex++) |
2627 | 0 | { |
2628 | 0 | DDFRecord *poRecord = oFE_Index.GetByIndex(iFEIndex); |
2629 | 0 | const int nOBJL = poRecord->GetIntSubfield("FRID", 0, "OBJL", 0); |
2630 | |
|
2631 | 0 | if (nOBJL < 0) |
2632 | 0 | bSuccess = false; |
2633 | 0 | else |
2634 | 0 | { |
2635 | 0 | if (nOBJL >= (int)anClassCount.size()) |
2636 | 0 | anClassCount.resize(nOBJL + 1); |
2637 | 0 | anClassCount[nOBJL]++; |
2638 | 0 | } |
2639 | 0 | } |
2640 | |
|
2641 | 0 | return bSuccess; |
2642 | 0 | } |
2643 | | |
2644 | | /************************************************************************/ |
2645 | | /* ApplyRecordUpdate() */ |
2646 | | /* */ |
2647 | | /* Update one target record based on an S-57 update record */ |
2648 | | /* (RUIN=3). */ |
2649 | | /************************************************************************/ |
2650 | | |
2651 | | bool S57Reader::ApplyRecordUpdate(DDFRecord *poTarget, DDFRecord *poUpdate) |
2652 | | |
2653 | 0 | { |
2654 | 0 | const char *pszKey = poUpdate->GetField(1)->GetFieldDefn()->GetName(); |
2655 | | |
2656 | | /* -------------------------------------------------------------------- */ |
2657 | | /* Validate versioning. */ |
2658 | | /* -------------------------------------------------------------------- */ |
2659 | 0 | if (poTarget->GetIntSubfield(pszKey, 0, "RVER", 0) + 1 != |
2660 | 0 | poUpdate->GetIntSubfield(pszKey, 0, "RVER", 0)) |
2661 | 0 | { |
2662 | 0 | CPLDebug("S57", "Mismatched RVER value on RCNM=%d,RCID=%d.\n", |
2663 | 0 | poTarget->GetIntSubfield(pszKey, 0, "RCNM", 0), |
2664 | 0 | poTarget->GetIntSubfield(pszKey, 0, "RCID", 0)); |
2665 | | |
2666 | | // CPLAssert( false ); |
2667 | 0 | return false; |
2668 | 0 | } |
2669 | | |
2670 | | /* -------------------------------------------------------------------- */ |
2671 | | /* Update the target version. */ |
2672 | | /* -------------------------------------------------------------------- */ |
2673 | 0 | const DDFField *poKey = poTarget->FindField(pszKey); |
2674 | |
|
2675 | 0 | if (poKey == nullptr) |
2676 | 0 | { |
2677 | | // CPLAssert( false ); |
2678 | 0 | return false; |
2679 | 0 | } |
2680 | | |
2681 | 0 | const DDFSubfieldDefn *poRVER_SFD = |
2682 | 0 | poKey->GetFieldDefn()->FindSubfieldDefn("RVER"); |
2683 | 0 | if (poRVER_SFD == nullptr) |
2684 | 0 | return false; |
2685 | 0 | if (!EQUAL(poRVER_SFD->GetFormat(), "b12")) |
2686 | 0 | { |
2687 | 0 | CPLError( |
2688 | 0 | CE_Warning, CPLE_NotSupported, |
2689 | 0 | "Subfield RVER of record %s has format=%s, instead of expected b12", |
2690 | 0 | pszKey, poRVER_SFD->GetFormat()); |
2691 | 0 | return false; |
2692 | 0 | } |
2693 | | |
2694 | | /* -------------------------------------------------------------------- */ |
2695 | | /* Update target RVER */ |
2696 | | /* -------------------------------------------------------------------- */ |
2697 | 0 | unsigned short nRVER; |
2698 | 0 | int nBytesRemaining = 0; |
2699 | 0 | unsigned char *pachRVER = |
2700 | 0 | reinterpret_cast<unsigned char *>(const_cast<char *>( |
2701 | 0 | poKey->GetSubfieldData(poRVER_SFD, &nBytesRemaining, 0))); |
2702 | 0 | CPLAssert(nBytesRemaining >= static_cast<int>(sizeof(nRVER))); |
2703 | 0 | memcpy(&nRVER, pachRVER, sizeof(nRVER)); |
2704 | 0 | CPL_LSBPTR16(&nRVER); |
2705 | 0 | nRVER += 1; |
2706 | 0 | CPL_LSBPTR16(&nRVER); |
2707 | 0 | memcpy(pachRVER, &nRVER, sizeof(nRVER)); |
2708 | | |
2709 | | /* -------------------------------------------------------------------- */ |
2710 | | /* Check for, and apply record record to spatial record pointer */ |
2711 | | /* updates. */ |
2712 | | /* -------------------------------------------------------------------- */ |
2713 | 0 | if (poUpdate->FindField("FSPC") != nullptr) |
2714 | 0 | { |
2715 | 0 | const int nFSUI = poUpdate->GetIntSubfield("FSPC", 0, "FSUI", 0); |
2716 | 0 | DDFField *poSrcFSPT = poUpdate->FindField("FSPT"); |
2717 | 0 | DDFField *poDstFSPT = poTarget->FindField("FSPT"); |
2718 | |
|
2719 | 0 | if ((poSrcFSPT == nullptr && nFSUI != 2) || poDstFSPT == nullptr) |
2720 | 0 | { |
2721 | | // CPLAssert( false ); |
2722 | 0 | return false; |
2723 | 0 | } |
2724 | | |
2725 | 0 | const int nFSIX = poUpdate->GetIntSubfield("FSPC", 0, "FSIX", 0); |
2726 | 0 | const int nNSPT = poUpdate->GetIntSubfield("FSPC", 0, "NSPT", 0); |
2727 | |
|
2728 | 0 | int nPtrSize = poDstFSPT->GetFieldDefn()->GetFixedWidth(); |
2729 | |
|
2730 | 0 | if (nFSUI == 1) /* INSERT */ |
2731 | 0 | { |
2732 | 0 | int nInsertionBytes = nPtrSize * nNSPT; |
2733 | |
|
2734 | 0 | if (poSrcFSPT->GetDataSize() < nInsertionBytes) |
2735 | 0 | { |
2736 | 0 | CPLDebug("S57", |
2737 | 0 | "Not enough bytes in source FSPT field. " |
2738 | 0 | "Has %d, requires %d", |
2739 | 0 | poSrcFSPT->GetDataSize(), nInsertionBytes); |
2740 | 0 | return false; |
2741 | 0 | } |
2742 | | |
2743 | 0 | char *pachInsertion = |
2744 | 0 | static_cast<char *>(CPLMalloc(nInsertionBytes + nPtrSize)); |
2745 | 0 | memcpy(pachInsertion, poSrcFSPT->GetData(), nInsertionBytes); |
2746 | | |
2747 | | /* |
2748 | | ** If we are inserting before an instance that already |
2749 | | ** exists, we must add it to the end of the data being |
2750 | | ** inserted. |
2751 | | */ |
2752 | 0 | if (nFSIX <= poDstFSPT->GetRepeatCount()) |
2753 | 0 | { |
2754 | 0 | if (poDstFSPT->GetDataSize() < nPtrSize * nFSIX) |
2755 | 0 | { |
2756 | 0 | CPLDebug("S57", |
2757 | 0 | "Not enough bytes in dest FSPT field. " |
2758 | 0 | "Has %d, requires %d", |
2759 | 0 | poDstFSPT->GetDataSize(), nPtrSize * nFSIX); |
2760 | 0 | CPLFree(pachInsertion); |
2761 | 0 | return false; |
2762 | 0 | } |
2763 | | |
2764 | 0 | memcpy(pachInsertion + nInsertionBytes, |
2765 | 0 | poDstFSPT->GetData() + nPtrSize * (nFSIX - 1), nPtrSize); |
2766 | 0 | nInsertionBytes += nPtrSize; |
2767 | 0 | } |
2768 | | |
2769 | 0 | poTarget->SetFieldRaw(poDstFSPT, nFSIX - 1, pachInsertion, |
2770 | 0 | nInsertionBytes); |
2771 | 0 | CPLFree(pachInsertion); |
2772 | 0 | } |
2773 | 0 | else if (nFSUI == 2) /* DELETE */ |
2774 | 0 | { |
2775 | | /* Wipe each deleted coordinate */ |
2776 | 0 | for (int i = nNSPT - 1; i >= 0; i--) |
2777 | 0 | { |
2778 | 0 | poTarget->SetFieldRaw(poDstFSPT, i + nFSIX - 1, nullptr, 0); |
2779 | 0 | } |
2780 | 0 | } |
2781 | 0 | else if (nFSUI == 3) /* MODIFY */ |
2782 | 0 | { |
2783 | | /* copy over each ptr */ |
2784 | 0 | if (poSrcFSPT->GetDataSize() < nNSPT * nPtrSize) |
2785 | 0 | { |
2786 | 0 | CPLDebug("S57", |
2787 | 0 | "Not enough bytes in source FSPT field. Has %d, " |
2788 | 0 | "requires %d", |
2789 | 0 | poSrcFSPT->GetDataSize(), nNSPT * nPtrSize); |
2790 | 0 | return false; |
2791 | 0 | } |
2792 | | |
2793 | 0 | for (int i = 0; i < nNSPT; i++) |
2794 | 0 | { |
2795 | 0 | const char *pachRawData = poSrcFSPT->GetData() + nPtrSize * i; |
2796 | 0 | poTarget->SetFieldRaw(poDstFSPT, i + nFSIX - 1, pachRawData, |
2797 | 0 | nPtrSize); |
2798 | 0 | } |
2799 | 0 | } |
2800 | 0 | } |
2801 | | |
2802 | | /* -------------------------------------------------------------------- */ |
2803 | | /* Check for, and apply vector record to vector record pointer */ |
2804 | | /* updates. */ |
2805 | | /* -------------------------------------------------------------------- */ |
2806 | 0 | if (poUpdate->FindField("VRPC") != nullptr) |
2807 | 0 | { |
2808 | 0 | const int nVPUI = poUpdate->GetIntSubfield("VRPC", 0, "VPUI", 0); |
2809 | 0 | DDFField *poSrcVRPT = poUpdate->FindField("VRPT"); |
2810 | 0 | DDFField *poDstVRPT = poTarget->FindField("VRPT"); |
2811 | |
|
2812 | 0 | if ((poSrcVRPT == nullptr && nVPUI != 2) || poDstVRPT == nullptr) |
2813 | 0 | { |
2814 | | // CPLAssert( false ); |
2815 | 0 | return false; |
2816 | 0 | } |
2817 | | |
2818 | 0 | const int nVPIX = poUpdate->GetIntSubfield("VRPC", 0, "VPIX", 0); |
2819 | 0 | const int nNVPT = poUpdate->GetIntSubfield("VRPC", 0, "NVPT", 0); |
2820 | |
|
2821 | 0 | const int nPtrSize = poDstVRPT->GetFieldDefn()->GetFixedWidth(); |
2822 | |
|
2823 | 0 | if (nVPUI == 1) /* INSERT */ |
2824 | 0 | { |
2825 | 0 | int nInsertionBytes = nPtrSize * nNVPT; |
2826 | |
|
2827 | 0 | if (poSrcVRPT->GetDataSize() < nInsertionBytes) |
2828 | 0 | { |
2829 | 0 | CPLDebug("S57", |
2830 | 0 | "Not enough bytes in source VRPT field. Has %d, " |
2831 | 0 | "requires %d", |
2832 | 0 | poSrcVRPT->GetDataSize(), nInsertionBytes); |
2833 | 0 | return false; |
2834 | 0 | } |
2835 | | |
2836 | 0 | char *pachInsertion = |
2837 | 0 | static_cast<char *>(CPLMalloc(nInsertionBytes + nPtrSize)); |
2838 | 0 | memcpy(pachInsertion, poSrcVRPT->GetData(), nInsertionBytes); |
2839 | | |
2840 | | /* |
2841 | | ** If we are inserting before an instance that already |
2842 | | ** exists, we must add it to the end of the data being |
2843 | | ** inserted. |
2844 | | */ |
2845 | 0 | if (nVPIX <= poDstVRPT->GetRepeatCount()) |
2846 | 0 | { |
2847 | 0 | if (poDstVRPT->GetDataSize() < nPtrSize * nVPIX) |
2848 | 0 | { |
2849 | 0 | CPLDebug("S57", |
2850 | 0 | "Not enough bytes in dest VRPT field. Has %d, " |
2851 | 0 | "requires %d", |
2852 | 0 | poDstVRPT->GetDataSize(), nPtrSize * nVPIX); |
2853 | 0 | CPLFree(pachInsertion); |
2854 | 0 | return false; |
2855 | 0 | } |
2856 | | |
2857 | 0 | memcpy(pachInsertion + nInsertionBytes, |
2858 | 0 | poDstVRPT->GetData() + nPtrSize * (nVPIX - 1), nPtrSize); |
2859 | 0 | nInsertionBytes += nPtrSize; |
2860 | 0 | } |
2861 | | |
2862 | 0 | poTarget->SetFieldRaw(poDstVRPT, nVPIX - 1, pachInsertion, |
2863 | 0 | nInsertionBytes); |
2864 | 0 | CPLFree(pachInsertion); |
2865 | 0 | } |
2866 | 0 | else if (nVPUI == 2) /* DELETE */ |
2867 | 0 | { |
2868 | | /* Wipe each deleted coordinate */ |
2869 | 0 | for (int i = nNVPT - 1; i >= 0; i--) |
2870 | 0 | { |
2871 | 0 | poTarget->SetFieldRaw(poDstVRPT, i + nVPIX - 1, nullptr, 0); |
2872 | 0 | } |
2873 | 0 | } |
2874 | 0 | else if (nVPUI == 3) /* MODIFY */ |
2875 | 0 | { |
2876 | 0 | if (poSrcVRPT->GetDataSize() < nNVPT * nPtrSize) |
2877 | 0 | { |
2878 | 0 | CPLDebug("S57", |
2879 | 0 | "Not enough bytes in source VRPT field. " |
2880 | 0 | "Has %d, requires %d", |
2881 | 0 | poSrcVRPT->GetDataSize(), nNVPT * nPtrSize); |
2882 | 0 | return false; |
2883 | 0 | } |
2884 | | |
2885 | | /* copy over each ptr */ |
2886 | 0 | for (int i = 0; i < nNVPT; i++) |
2887 | 0 | { |
2888 | 0 | const char *pachRawData = poSrcVRPT->GetData() + nPtrSize * i; |
2889 | |
|
2890 | 0 | poTarget->SetFieldRaw(poDstVRPT, i + nVPIX - 1, pachRawData, |
2891 | 0 | nPtrSize); |
2892 | 0 | } |
2893 | 0 | } |
2894 | 0 | } |
2895 | | |
2896 | | /* -------------------------------------------------------------------- */ |
2897 | | /* Check for, and apply record update to coordinates. */ |
2898 | | /* -------------------------------------------------------------------- */ |
2899 | 0 | if (poUpdate->FindField("SGCC") != nullptr) |
2900 | 0 | { |
2901 | 0 | DDFField *poSrcSG2D = poUpdate->FindField("SG2D"); |
2902 | 0 | DDFField *poDstSG2D = poTarget->FindField("SG2D"); |
2903 | |
|
2904 | 0 | const int nCCUI = poUpdate->GetIntSubfield("SGCC", 0, "CCUI", 0); |
2905 | | |
2906 | | /* If we don't have SG2D, check for SG3D */ |
2907 | 0 | if (poDstSG2D == nullptr) |
2908 | 0 | { |
2909 | 0 | poDstSG2D = poTarget->FindField("SG3D"); |
2910 | 0 | if (poDstSG2D != nullptr) |
2911 | 0 | { |
2912 | 0 | poSrcSG2D = poUpdate->FindField("SG3D"); |
2913 | 0 | } |
2914 | 0 | else |
2915 | 0 | { |
2916 | 0 | if (nCCUI != 1) |
2917 | 0 | { |
2918 | | // CPLAssert( false ); |
2919 | 0 | return false; |
2920 | 0 | } |
2921 | | |
2922 | 0 | poTarget->AddField( |
2923 | 0 | poTarget->GetModule()->FindFieldDefn("SG2D")); |
2924 | 0 | poDstSG2D = poTarget->FindField("SG2D"); |
2925 | 0 | if (poDstSG2D == nullptr) |
2926 | 0 | { |
2927 | | // CPLAssert( false ); |
2928 | 0 | return false; |
2929 | 0 | } |
2930 | | |
2931 | | // Delete null default data that was created |
2932 | 0 | poTarget->SetFieldRaw(poDstSG2D, 0, nullptr, 0); |
2933 | 0 | } |
2934 | 0 | } |
2935 | | |
2936 | 0 | if (poSrcSG2D == nullptr && nCCUI != 2) |
2937 | 0 | { |
2938 | | // CPLAssert( false ); |
2939 | 0 | return false; |
2940 | 0 | } |
2941 | | |
2942 | 0 | int nCoordSize = poDstSG2D->GetFieldDefn()->GetFixedWidth(); |
2943 | 0 | const int nCCIX = poUpdate->GetIntSubfield("SGCC", 0, "CCIX", 0); |
2944 | 0 | const int nCCNC = poUpdate->GetIntSubfield("SGCC", 0, "CCNC", 0); |
2945 | |
|
2946 | 0 | if (nCCUI == 1) /* INSERT */ |
2947 | 0 | { |
2948 | 0 | int nInsertionBytes = nCoordSize * nCCNC; |
2949 | |
|
2950 | 0 | if (poSrcSG2D->GetDataSize() < nInsertionBytes) |
2951 | 0 | { |
2952 | 0 | CPLDebug("S57", |
2953 | 0 | "Not enough bytes in source SG2D field. " |
2954 | 0 | "Has %d, requires %d", |
2955 | 0 | poSrcSG2D->GetDataSize(), nInsertionBytes); |
2956 | 0 | return false; |
2957 | 0 | } |
2958 | | |
2959 | 0 | char *pachInsertion = |
2960 | 0 | static_cast<char *>(CPLMalloc(nInsertionBytes + nCoordSize)); |
2961 | 0 | memcpy(pachInsertion, poSrcSG2D->GetData(), nInsertionBytes); |
2962 | | |
2963 | | /* |
2964 | | ** If we are inserting before an instance that already |
2965 | | ** exists, we must add it to the end of the data being |
2966 | | ** inserted. |
2967 | | */ |
2968 | 0 | if (nCCIX <= poDstSG2D->GetRepeatCount()) |
2969 | 0 | { |
2970 | 0 | if (poDstSG2D->GetDataSize() < nCoordSize * nCCIX) |
2971 | 0 | { |
2972 | 0 | CPLDebug("S57", |
2973 | 0 | "Not enough bytes in dest SG2D field. " |
2974 | 0 | "Has %d, requires %d", |
2975 | 0 | poDstSG2D->GetDataSize(), nCoordSize * nCCIX); |
2976 | 0 | CPLFree(pachInsertion); |
2977 | 0 | return false; |
2978 | 0 | } |
2979 | | |
2980 | 0 | memcpy(pachInsertion + nInsertionBytes, |
2981 | 0 | poDstSG2D->GetData() + nCoordSize * (nCCIX - 1), |
2982 | 0 | nCoordSize); |
2983 | 0 | nInsertionBytes += nCoordSize; |
2984 | 0 | } |
2985 | | |
2986 | 0 | poTarget->SetFieldRaw(poDstSG2D, nCCIX - 1, pachInsertion, |
2987 | 0 | nInsertionBytes); |
2988 | 0 | CPLFree(pachInsertion); |
2989 | 0 | } |
2990 | 0 | else if (nCCUI == 2) /* DELETE */ |
2991 | 0 | { |
2992 | | /* Wipe each deleted coordinate */ |
2993 | 0 | for (int i = nCCNC - 1; i >= 0; i--) |
2994 | 0 | { |
2995 | 0 | poTarget->SetFieldRaw(poDstSG2D, i + nCCIX - 1, nullptr, 0); |
2996 | 0 | } |
2997 | 0 | } |
2998 | 0 | else if (nCCUI == 3) /* MODIFY */ |
2999 | 0 | { |
3000 | 0 | if (poSrcSG2D->GetDataSize() < nCCNC * nCoordSize) |
3001 | 0 | { |
3002 | 0 | CPLDebug("S57", |
3003 | 0 | "Not enough bytes in source SG2D field. " |
3004 | 0 | "Has %d, requires %d", |
3005 | 0 | poSrcSG2D->GetDataSize(), nCCNC * nCoordSize); |
3006 | 0 | return false; |
3007 | 0 | } |
3008 | | |
3009 | | /* copy over each ptr */ |
3010 | 0 | for (int i = 0; i < nCCNC; i++) |
3011 | 0 | { |
3012 | 0 | const char *pachRawData = poSrcSG2D->GetData() + nCoordSize * i; |
3013 | |
|
3014 | 0 | poTarget->SetFieldRaw(poDstSG2D, i + nCCIX - 1, pachRawData, |
3015 | 0 | nCoordSize); |
3016 | 0 | } |
3017 | 0 | } |
3018 | 0 | } |
3019 | | |
3020 | | /* -------------------------------------------------------------------- */ |
3021 | | /* Apply updates to Feature to Feature pointer fields. Note */ |
3022 | | /* INSERT and DELETE are untested. UPDATE tested per bug #5028. */ |
3023 | | /* -------------------------------------------------------------------- */ |
3024 | 0 | if (poUpdate->FindField("FFPC") != nullptr) |
3025 | 0 | { |
3026 | 0 | int nFFUI = poUpdate->GetIntSubfield("FFPC", 0, "FFUI", 0); |
3027 | 0 | DDFField *poSrcFFPT = poUpdate->FindField("FFPT"); |
3028 | 0 | DDFField *poDstFFPT = poTarget->FindField("FFPT"); |
3029 | |
|
3030 | 0 | if ((poSrcFFPT == nullptr && nFFUI != 2) || |
3031 | 0 | (poDstFFPT == nullptr && nFFUI != 1)) |
3032 | 0 | { |
3033 | 0 | CPLDebug("S57", "Missing source or target FFPT applying update."); |
3034 | | // CPLAssert( false ); |
3035 | 0 | return false; |
3036 | 0 | } |
3037 | | |
3038 | | // Create FFPT field on target record, if it does not yet exist. |
3039 | 0 | if (poDstFFPT == nullptr) |
3040 | 0 | { |
3041 | | // Untested! |
3042 | 0 | poTarget->AddField(poTarget->GetModule()->FindFieldDefn("FFPT")); |
3043 | 0 | poDstFFPT = poTarget->FindField("FFPT"); |
3044 | 0 | if (poDstFFPT == nullptr) |
3045 | 0 | { |
3046 | | // CPLAssert( false ); |
3047 | 0 | return false; |
3048 | 0 | } |
3049 | | |
3050 | | // Delete null default data that was created |
3051 | 0 | poTarget->SetFieldRaw(poDstFFPT, 0, nullptr, 0); |
3052 | 0 | } |
3053 | | |
3054 | | // FFPT includes COMT which is variable length which would |
3055 | | // greatly complicate updates. But in practice COMT is always |
3056 | | // an empty string so we will take a chance and assume that so |
3057 | | // we have a fixed record length. We *could* actually verify that |
3058 | | // but I have not done so for now. |
3059 | 0 | const int nFFPTSize = 10; |
3060 | 0 | const int nFFIX = poUpdate->GetIntSubfield("FFPC", 0, "FFIX", 0); |
3061 | 0 | const int nNFPT = poUpdate->GetIntSubfield("FFPC", 0, "NFPT", 0); |
3062 | |
|
3063 | 0 | if (nFFUI == 1) /* INSERT */ |
3064 | 0 | { |
3065 | | // Untested! |
3066 | 0 | CPLDebug("S57", "Using untested FFPT INSERT code!"); |
3067 | |
|
3068 | 0 | int nInsertionBytes = nFFPTSize * nNFPT; |
3069 | |
|
3070 | 0 | if (poSrcFFPT->GetDataSize() < nInsertionBytes) |
3071 | 0 | { |
3072 | 0 | CPLDebug("S57", |
3073 | 0 | "Not enough bytes in source FFPT field. " |
3074 | 0 | "Has %d, requires %d", |
3075 | 0 | poSrcFFPT->GetDataSize(), nInsertionBytes); |
3076 | 0 | return false; |
3077 | 0 | } |
3078 | | |
3079 | 0 | char *pachInsertion = |
3080 | 0 | static_cast<char *>(CPLMalloc(nInsertionBytes + nFFPTSize)); |
3081 | 0 | memcpy(pachInsertion, poSrcFFPT->GetData(), nInsertionBytes); |
3082 | | |
3083 | | /* |
3084 | | ** If we are inserting before an instance that already |
3085 | | ** exists, we must add it to the end of the data being |
3086 | | ** inserted. |
3087 | | */ |
3088 | 0 | if (nFFIX <= poDstFFPT->GetRepeatCount()) |
3089 | 0 | { |
3090 | 0 | if (poDstFFPT->GetDataSize() < nFFPTSize * nFFIX) |
3091 | 0 | { |
3092 | 0 | CPLDebug("S57", |
3093 | 0 | "Not enough bytes in dest FFPT field. " |
3094 | 0 | "Has %d, requires %d", |
3095 | 0 | poDstFFPT->GetDataSize(), nFFPTSize * nFFIX); |
3096 | 0 | CPLFree(pachInsertion); |
3097 | 0 | return false; |
3098 | 0 | } |
3099 | | |
3100 | 0 | memcpy(pachInsertion + nInsertionBytes, |
3101 | 0 | poDstFFPT->GetData() + nFFPTSize * (nFFIX - 1), |
3102 | 0 | nFFPTSize); |
3103 | 0 | nInsertionBytes += nFFPTSize; |
3104 | 0 | } |
3105 | | |
3106 | 0 | poTarget->SetFieldRaw(poDstFFPT, nFFIX - 1, pachInsertion, |
3107 | 0 | nInsertionBytes); |
3108 | 0 | CPLFree(pachInsertion); |
3109 | 0 | } |
3110 | 0 | else if (nFFUI == 2) /* DELETE */ |
3111 | 0 | { |
3112 | | // Untested! |
3113 | 0 | CPLDebug("S57", "Using untested FFPT DELETE code!"); |
3114 | | |
3115 | | /* Wipe each deleted record */ |
3116 | 0 | for (int i = nNFPT - 1; i >= 0; i--) |
3117 | 0 | { |
3118 | 0 | poTarget->SetFieldRaw(poDstFFPT, i + nFFIX - 1, nullptr, 0); |
3119 | 0 | } |
3120 | 0 | } |
3121 | 0 | else if (nFFUI == 3) /* UPDATE */ |
3122 | 0 | { |
3123 | 0 | if (poSrcFFPT->GetDataSize() < nNFPT * nFFPTSize) |
3124 | 0 | { |
3125 | 0 | CPLDebug("S57", |
3126 | 0 | "Not enough bytes in source FFPT field. " |
3127 | 0 | "Has %d, requires %d", |
3128 | 0 | poSrcFFPT->GetDataSize(), nNFPT * nFFPTSize); |
3129 | 0 | return false; |
3130 | 0 | } |
3131 | | |
3132 | | /* copy over each ptr */ |
3133 | 0 | for (int i = 0; i < nNFPT; i++) |
3134 | 0 | { |
3135 | 0 | const char *pachRawData = poSrcFFPT->GetData() + nFFPTSize * i; |
3136 | |
|
3137 | 0 | poTarget->SetFieldRaw(poDstFFPT, i + nFFIX - 1, pachRawData, |
3138 | 0 | nFFPTSize); |
3139 | 0 | } |
3140 | 0 | } |
3141 | 0 | } |
3142 | | |
3143 | | /* -------------------------------------------------------------------- */ |
3144 | | /* Check for and apply changes to attribute lists. */ |
3145 | | /* -------------------------------------------------------------------- */ |
3146 | 0 | if (poUpdate->FindField("ATTF") != nullptr) |
3147 | 0 | { |
3148 | 0 | DDFField *poDstATTF = poTarget->FindField("ATTF"); |
3149 | |
|
3150 | 0 | if (poDstATTF == nullptr) |
3151 | 0 | { |
3152 | | // Create empty ATTF Field (see GDAL/OGR Bug #1648)" ); |
3153 | 0 | poDstATTF = poTarget->AddField(poModule->FindFieldDefn("ATTF")); |
3154 | 0 | } |
3155 | |
|
3156 | 0 | DDFField *poSrcATTF = poUpdate->FindField("ATTF"); |
3157 | 0 | const int nRepeatCount = poSrcATTF->GetRepeatCount(); |
3158 | |
|
3159 | 0 | for (int iAtt = 0; iAtt < nRepeatCount; iAtt++) |
3160 | 0 | { |
3161 | 0 | const int nATTL = poUpdate->GetIntSubfield("ATTF", 0, "ATTL", iAtt); |
3162 | 0 | int iTAtt = poDstATTF->GetRepeatCount() - 1; // Used after for. |
3163 | |
|
3164 | 0 | for (; iTAtt >= 0; iTAtt--) |
3165 | 0 | { |
3166 | 0 | if (poTarget->GetIntSubfield("ATTF", 0, "ATTL", iTAtt) == nATTL) |
3167 | 0 | break; |
3168 | 0 | } |
3169 | 0 | if (iTAtt == -1) |
3170 | 0 | iTAtt = poDstATTF->GetRepeatCount(); |
3171 | |
|
3172 | 0 | int nDataBytes = 0; |
3173 | 0 | const char *pszRawData = |
3174 | 0 | poSrcATTF->GetInstanceData(iAtt, &nDataBytes); |
3175 | 0 | if (pszRawData[2] == 0x7f /* delete marker */) |
3176 | 0 | { |
3177 | 0 | poTarget->SetFieldRaw(poDstATTF, iTAtt, nullptr, 0); |
3178 | 0 | } |
3179 | 0 | else |
3180 | 0 | { |
3181 | 0 | poTarget->SetFieldRaw(poDstATTF, iTAtt, pszRawData, nDataBytes); |
3182 | 0 | } |
3183 | 0 | } |
3184 | 0 | } |
3185 | |
|
3186 | 0 | return true; |
3187 | 0 | } |
3188 | | |
3189 | | /************************************************************************/ |
3190 | | /* ApplyUpdates() */ |
3191 | | /* */ |
3192 | | /* Read records from an update file, and apply them to the */ |
3193 | | /* currently loaded index of features. */ |
3194 | | /************************************************************************/ |
3195 | | |
3196 | | bool S57Reader::ApplyUpdates(DDFModule *poUpdateModule) |
3197 | | |
3198 | 0 | { |
3199 | | /* -------------------------------------------------------------------- */ |
3200 | | /* Ensure base file is loaded. */ |
3201 | | /* -------------------------------------------------------------------- */ |
3202 | 0 | if (!bFileIngested && !Ingest()) |
3203 | 0 | return false; |
3204 | | |
3205 | | /* -------------------------------------------------------------------- */ |
3206 | | /* Read records, and apply as updates. */ |
3207 | | /* -------------------------------------------------------------------- */ |
3208 | 0 | CPLErrorReset(); |
3209 | |
|
3210 | 0 | DDFRecord *poRecord = nullptr; |
3211 | |
|
3212 | 0 | while ((poRecord = poUpdateModule->ReadRecord()) != nullptr) |
3213 | 0 | { |
3214 | 0 | const DDFField *poKeyField = poRecord->GetField(1); |
3215 | 0 | if (poKeyField == nullptr) |
3216 | 0 | return false; |
3217 | | |
3218 | 0 | const char *pszKey = poKeyField->GetFieldDefn()->GetName(); |
3219 | |
|
3220 | 0 | if (EQUAL(pszKey, "VRID") || EQUAL(pszKey, "FRID")) |
3221 | 0 | { |
3222 | 0 | const int nRCNM = poRecord->GetIntSubfield(pszKey, 0, "RCNM", 0); |
3223 | 0 | const int nRCID = poRecord->GetIntSubfield(pszKey, 0, "RCID", 0); |
3224 | 0 | const int nRVER = poRecord->GetIntSubfield(pszKey, 0, "RVER", 0); |
3225 | 0 | const int nRUIN = poRecord->GetIntSubfield(pszKey, 0, "RUIN", 0); |
3226 | 0 | DDFRecordIndex *poIndex = nullptr; |
3227 | |
|
3228 | 0 | if (EQUAL(poKeyField->GetFieldDefn()->GetName(), "VRID")) |
3229 | 0 | { |
3230 | 0 | switch (nRCNM) |
3231 | 0 | { |
3232 | 0 | case RCNM_VI: |
3233 | 0 | poIndex = &oVI_Index; |
3234 | 0 | break; |
3235 | | |
3236 | 0 | case RCNM_VC: |
3237 | 0 | poIndex = &oVC_Index; |
3238 | 0 | break; |
3239 | | |
3240 | 0 | case RCNM_VE: |
3241 | 0 | poIndex = &oVE_Index; |
3242 | 0 | break; |
3243 | | |
3244 | 0 | case RCNM_VF: |
3245 | 0 | poIndex = &oVF_Index; |
3246 | 0 | break; |
3247 | | |
3248 | 0 | default: |
3249 | | // CPLAssert( false ); |
3250 | 0 | return false; |
3251 | 0 | break; |
3252 | 0 | } |
3253 | 0 | } |
3254 | 0 | else |
3255 | 0 | { |
3256 | 0 | poIndex = &oFE_Index; |
3257 | 0 | } |
3258 | | |
3259 | 0 | if (poIndex != nullptr) |
3260 | 0 | { |
3261 | 0 | if (nRUIN == 1) /* insert */ |
3262 | 0 | { |
3263 | 0 | poIndex->AddRecord(nRCID, poRecord->CloneOn(poModule)); |
3264 | 0 | } |
3265 | 0 | else if (nRUIN == 2) /* delete */ |
3266 | 0 | { |
3267 | 0 | DDFRecord *poTarget = poIndex->FindRecord(nRCID); |
3268 | 0 | if (poTarget == nullptr) |
3269 | 0 | { |
3270 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3271 | 0 | "Can't find RCNM=%d,RCID=%d for delete.\n", |
3272 | 0 | nRCNM, nRCID); |
3273 | 0 | } |
3274 | 0 | else if (poTarget->GetIntSubfield(pszKey, 0, "RVER", 0) != |
3275 | 0 | nRVER - 1) |
3276 | 0 | { |
3277 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3278 | 0 | "Mismatched RVER value on RCNM=%d,RCID=%d.\n", |
3279 | 0 | nRCNM, nRCID); |
3280 | 0 | } |
3281 | 0 | else |
3282 | 0 | { |
3283 | 0 | poIndex->RemoveRecord(nRCID); |
3284 | 0 | } |
3285 | 0 | } |
3286 | | |
3287 | 0 | else if (nRUIN == 3) /* modify in place */ |
3288 | 0 | { |
3289 | 0 | DDFRecord *poTarget = poIndex->FindRecord(nRCID); |
3290 | 0 | if (poTarget == nullptr) |
3291 | 0 | { |
3292 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3293 | 0 | "Can't find RCNM=%d,RCID=%d for update.\n", |
3294 | 0 | nRCNM, nRCID); |
3295 | 0 | } |
3296 | 0 | else |
3297 | 0 | { |
3298 | 0 | if (!ApplyRecordUpdate(poTarget, poRecord)) |
3299 | 0 | { |
3300 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
3301 | 0 | "An update to RCNM=%d,RCID=%d failed.\n", |
3302 | 0 | nRCNM, nRCID); |
3303 | 0 | } |
3304 | 0 | } |
3305 | 0 | } |
3306 | 0 | } |
3307 | 0 | } |
3308 | | |
3309 | 0 | else if (EQUAL(pszKey, "DSID")) |
3310 | 0 | { |
3311 | 0 | const char *pszEDTN = |
3312 | 0 | poRecord->GetStringSubfield("DSID", 0, "EDTN", 0); |
3313 | 0 | if (pszEDTN != nullptr) |
3314 | 0 | { |
3315 | 0 | if (!m_osEDTNUpdate.empty()) |
3316 | 0 | { |
3317 | 0 | if (!EQUAL(pszEDTN, "0") && // cancel |
3318 | 0 | !EQUAL(pszEDTN, m_osEDTNUpdate.c_str())) |
3319 | 0 | { |
3320 | 0 | CPLDebug("S57", |
3321 | 0 | "Skipping update as EDTN=%s in update does " |
3322 | 0 | "not match expected %s.", |
3323 | 0 | pszEDTN, m_osEDTNUpdate.c_str()); |
3324 | 0 | return false; |
3325 | 0 | } |
3326 | 0 | } |
3327 | 0 | m_osEDTNUpdate = pszEDTN; |
3328 | 0 | } |
3329 | | |
3330 | 0 | const char *pszUPDN = |
3331 | 0 | poRecord->GetStringSubfield("DSID", 0, "UPDN", 0); |
3332 | 0 | if (pszUPDN != nullptr) |
3333 | 0 | { |
3334 | 0 | if (!m_osUPDNUpdate.empty()) |
3335 | 0 | { |
3336 | 0 | if (atoi(m_osUPDNUpdate.c_str()) + 1 != atoi(pszUPDN)) |
3337 | 0 | { |
3338 | 0 | CPLDebug("S57", |
3339 | 0 | "Skipping update as UPDN=%s in update does " |
3340 | 0 | "not match expected %d.", |
3341 | 0 | pszUPDN, atoi(m_osUPDNUpdate.c_str()) + 1); |
3342 | 0 | return false; |
3343 | 0 | } |
3344 | 0 | } |
3345 | 0 | m_osUPDNUpdate = pszUPDN; |
3346 | 0 | } |
3347 | | |
3348 | 0 | const char *pszISDT = |
3349 | 0 | poRecord->GetStringSubfield("DSID", 0, "ISDT", 0); |
3350 | 0 | if (pszISDT != nullptr) |
3351 | 0 | m_osISDTUpdate = pszISDT; |
3352 | 0 | } |
3353 | | |
3354 | 0 | else |
3355 | 0 | { |
3356 | 0 | CPLDebug("S57", |
3357 | 0 | "Skipping %s record in S57Reader::ApplyUpdates().\n", |
3358 | 0 | pszKey); |
3359 | 0 | } |
3360 | 0 | } |
3361 | | |
3362 | 0 | return CPLGetLastErrorType() != CE_Failure; |
3363 | 0 | } |
3364 | | |
3365 | | /************************************************************************/ |
3366 | | /* FindAndApplyUpdates() */ |
3367 | | /* */ |
3368 | | /* Find all update files that would appear to apply to this */ |
3369 | | /* base file. */ |
3370 | | /************************************************************************/ |
3371 | | |
3372 | | bool S57Reader::FindAndApplyUpdates(const char *pszPath) |
3373 | | |
3374 | 316 | { |
3375 | 316 | if (pszPath == nullptr) |
3376 | 316 | pszPath = pszModuleName; |
3377 | | |
3378 | 316 | if (!EQUAL(CPLGetExtensionSafe(pszPath).c_str(), "000")) |
3379 | 316 | { |
3380 | 316 | CPLError(CE_Failure, CPLE_AppDefined, |
3381 | 316 | "Can't apply updates to a base file with a different\n" |
3382 | 316 | "extension than .000.\n"); |
3383 | 316 | return false; |
3384 | 316 | } |
3385 | | |
3386 | 0 | bool bSuccess = true; |
3387 | |
|
3388 | 0 | for (int iUpdate = 1; bSuccess; iUpdate++) |
3389 | 0 | { |
3390 | | // Creaing file extension |
3391 | 0 | CPLString extension; |
3392 | 0 | CPLString dirname; |
3393 | |
|
3394 | 0 | if (iUpdate < 10) |
3395 | 0 | { |
3396 | 0 | char buf[2]; |
3397 | 0 | CPLsnprintf(buf, sizeof(buf), "%i", iUpdate); |
3398 | 0 | extension.append("00"); |
3399 | 0 | extension.append(buf); |
3400 | 0 | dirname.append(buf); |
3401 | 0 | } |
3402 | 0 | else if (iUpdate < 100) |
3403 | 0 | { |
3404 | 0 | char buf[3]; |
3405 | 0 | CPLsnprintf(buf, sizeof(buf), "%i", iUpdate); |
3406 | 0 | extension.append("0"); |
3407 | 0 | extension.append(buf); |
3408 | 0 | dirname.append(buf); |
3409 | 0 | } |
3410 | 0 | else if (iUpdate < 1000) |
3411 | 0 | { |
3412 | 0 | char buf[4]; |
3413 | 0 | CPLsnprintf(buf, sizeof(buf), "%i", iUpdate); |
3414 | 0 | extension.append(buf); |
3415 | 0 | dirname.append(buf); |
3416 | 0 | } |
3417 | |
|
3418 | 0 | DDFModule oUpdateModule; |
3419 | | |
3420 | | // trying current dir first |
3421 | 0 | char *pszUpdateFilename = CPLStrdup( |
3422 | 0 | CPLResetExtensionSafe(pszPath, extension.c_str()).c_str()); |
3423 | |
|
3424 | 0 | VSILFILE *file = VSIFOpenL(pszUpdateFilename, "r"); |
3425 | 0 | if (file) |
3426 | 0 | { |
3427 | 0 | VSIFCloseL(file); |
3428 | 0 | bSuccess = CPL_TO_BOOL(oUpdateModule.Open(pszUpdateFilename, TRUE)); |
3429 | 0 | if (bSuccess) |
3430 | 0 | { |
3431 | 0 | CPLDebug("S57", "Applying feature updates from %s.", |
3432 | 0 | pszUpdateFilename); |
3433 | 0 | if (!ApplyUpdates(&oUpdateModule)) |
3434 | 0 | return false; |
3435 | 0 | } |
3436 | 0 | } |
3437 | 0 | else // File is store on Primar generated CD. |
3438 | 0 | { |
3439 | 0 | char *pszBaseFileDir = |
3440 | 0 | CPLStrdup(CPLGetDirnameSafe(pszPath).c_str()); |
3441 | 0 | char *pszFileDir = |
3442 | 0 | CPLStrdup(CPLGetDirnameSafe(pszBaseFileDir).c_str()); |
3443 | |
|
3444 | 0 | CPLString remotefile(pszFileDir); |
3445 | 0 | remotefile.append("/"); |
3446 | 0 | remotefile.append(dirname); |
3447 | 0 | remotefile.append("/"); |
3448 | 0 | remotefile.append(CPLGetBasenameSafe(pszPath).c_str()); |
3449 | 0 | remotefile.append("."); |
3450 | 0 | remotefile.append(extension); |
3451 | 0 | bSuccess = |
3452 | 0 | CPL_TO_BOOL(oUpdateModule.Open(remotefile.c_str(), TRUE)); |
3453 | |
|
3454 | 0 | if (bSuccess) |
3455 | 0 | CPLDebug("S57", "Applying feature updates from %s.", |
3456 | 0 | remotefile.c_str()); |
3457 | 0 | CPLFree(pszBaseFileDir); |
3458 | 0 | CPLFree(pszFileDir); |
3459 | 0 | if (bSuccess) |
3460 | 0 | { |
3461 | 0 | if (!ApplyUpdates(&oUpdateModule)) |
3462 | 0 | return false; |
3463 | 0 | } |
3464 | 0 | } // end for if-else |
3465 | 0 | CPLFree(pszUpdateFilename); |
3466 | 0 | } |
3467 | | |
3468 | 0 | return true; |
3469 | 0 | } |
3470 | | |
3471 | | /************************************************************************/ |
3472 | | /* GetExtent() */ |
3473 | | /* */ |
3474 | | /* Scan all the cached records collecting spatial bounds as */ |
3475 | | /* efficiently as possible for this transfer. */ |
3476 | | /************************************************************************/ |
3477 | | |
3478 | | OGRErr S57Reader::GetExtent(OGREnvelope *psExtent, int bForce) |
3479 | | |
3480 | 0 | { |
3481 | | /* -------------------------------------------------------------------- */ |
3482 | | /* If we aren't forced to get the extent say no if we haven't */ |
3483 | | /* already indexed the iso8211 records. */ |
3484 | | /* -------------------------------------------------------------------- */ |
3485 | 0 | if (!bForce && !bFileIngested) |
3486 | 0 | return OGRERR_FAILURE; |
3487 | | |
3488 | 0 | if (!Ingest()) |
3489 | 0 | return OGRERR_FAILURE; |
3490 | | |
3491 | | /* -------------------------------------------------------------------- */ |
3492 | | /* We will scan all the low level vector elements for extents */ |
3493 | | /* coordinates. */ |
3494 | | /* -------------------------------------------------------------------- */ |
3495 | 0 | bool bGotExtents = false; |
3496 | 0 | int nXMin = 0; |
3497 | 0 | int nXMax = 0; |
3498 | 0 | int nYMin = 0; |
3499 | 0 | int nYMax = 0; |
3500 | |
|
3501 | 0 | const int INDEX_COUNT = 4; |
3502 | 0 | DDFRecordIndex *apoIndex[INDEX_COUNT]; |
3503 | |
|
3504 | 0 | apoIndex[0] = &oVI_Index; |
3505 | 0 | apoIndex[1] = &oVC_Index; |
3506 | 0 | apoIndex[2] = &oVE_Index; |
3507 | 0 | apoIndex[3] = &oVF_Index; |
3508 | |
|
3509 | 0 | for (int iIndex = 0; iIndex < INDEX_COUNT; iIndex++) |
3510 | 0 | { |
3511 | 0 | DDFRecordIndex *poIndex = apoIndex[iIndex]; |
3512 | |
|
3513 | 0 | for (int iVIndex = 0; iVIndex < poIndex->GetCount(); iVIndex++) |
3514 | 0 | { |
3515 | 0 | DDFRecord *poRecord = poIndex->GetByIndex(iVIndex); |
3516 | 0 | DDFField *poSG3D = poRecord->FindField("SG3D"); |
3517 | 0 | DDFField *poSG2D = poRecord->FindField("SG2D"); |
3518 | |
|
3519 | 0 | if (poSG3D != nullptr) |
3520 | 0 | { |
3521 | 0 | const int nVCount = poSG3D->GetRepeatCount(); |
3522 | 0 | const GByte *pabyData = (const GByte *)poSG3D->GetData(); |
3523 | 0 | if (poSG3D->GetDataSize() < |
3524 | 0 | 3 * nVCount * static_cast<int>(sizeof(int))) |
3525 | 0 | return OGRERR_FAILURE; |
3526 | | |
3527 | 0 | for (int i = 0; i < nVCount; i++) |
3528 | 0 | { |
3529 | 0 | GInt32 nX = CPL_LSBSINT32PTR(pabyData + 4 * (i * 3 + 1)); |
3530 | 0 | GInt32 nY = CPL_LSBSINT32PTR(pabyData + 4 * (i * 3 + 0)); |
3531 | |
|
3532 | 0 | if (bGotExtents) |
3533 | 0 | { |
3534 | 0 | nXMin = std::min(nXMin, nX); |
3535 | 0 | nXMax = std::max(nXMax, nX); |
3536 | 0 | nYMin = std::min(nYMin, nY); |
3537 | 0 | nYMax = std::max(nYMax, nY); |
3538 | 0 | } |
3539 | 0 | else |
3540 | 0 | { |
3541 | 0 | nXMin = nX; |
3542 | 0 | nXMax = nX; |
3543 | 0 | nYMin = nY; |
3544 | 0 | nYMax = nY; |
3545 | 0 | bGotExtents = true; |
3546 | 0 | } |
3547 | 0 | } |
3548 | 0 | } |
3549 | 0 | else if (poSG2D != nullptr) |
3550 | 0 | { |
3551 | 0 | const int nVCount = poSG2D->GetRepeatCount(); |
3552 | |
|
3553 | 0 | if (poSG2D->GetDataSize() < 2 * nVCount * (int)sizeof(int)) |
3554 | 0 | return OGRERR_FAILURE; |
3555 | | |
3556 | 0 | const GByte *pabyData = (const GByte *)poSG2D->GetData(); |
3557 | |
|
3558 | 0 | for (int i = 0; i < nVCount; i++) |
3559 | 0 | { |
3560 | 0 | const GInt32 nX = |
3561 | 0 | CPL_LSBSINT32PTR(pabyData + 4 * (i * 2 + 1)); |
3562 | 0 | const GInt32 nY = |
3563 | 0 | CPL_LSBSINT32PTR(pabyData + 4 * (i * 2 + 0)); |
3564 | |
|
3565 | 0 | if (bGotExtents) |
3566 | 0 | { |
3567 | 0 | nXMin = std::min(nXMin, nX); |
3568 | 0 | nXMax = std::max(nXMax, nX); |
3569 | 0 | nYMin = std::min(nYMin, nY); |
3570 | 0 | nYMax = std::max(nYMax, nY); |
3571 | 0 | } |
3572 | 0 | else |
3573 | 0 | { |
3574 | 0 | nXMin = nX; |
3575 | 0 | nXMax = nX; |
3576 | 0 | nYMin = nY; |
3577 | 0 | nYMax = nY; |
3578 | 0 | bGotExtents = true; |
3579 | 0 | } |
3580 | 0 | } |
3581 | 0 | } |
3582 | 0 | } |
3583 | 0 | } |
3584 | | |
3585 | 0 | if (!bGotExtents) |
3586 | 0 | { |
3587 | 0 | return OGRERR_FAILURE; |
3588 | 0 | } |
3589 | 0 | else |
3590 | 0 | { |
3591 | 0 | psExtent->MinX = nXMin / static_cast<double>(nCOMF); |
3592 | 0 | psExtent->MaxX = nXMax / static_cast<double>(nCOMF); |
3593 | 0 | psExtent->MinY = nYMin / static_cast<double>(nCOMF); |
3594 | 0 | psExtent->MaxY = nYMax / static_cast<double>(nCOMF); |
3595 | |
|
3596 | 0 | return OGRERR_NONE; |
3597 | 0 | } |
3598 | 0 | } |