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