Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: SOSI Translator
4 : * Purpose: Implements OGRSOSILayer.
5 : * Author: Thomas Hirsch, <thomas.hirsch statkart no>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010, Thomas Hirsch
9 : * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_sosi.h"
15 : #include <map>
16 : #include <memory>
17 : #include <stdio.h>
18 : #include <string.h>
19 :
20 : /************************************************************************/
21 : /* OGRSOSILayer() */
22 : /************************************************************************/
23 :
24 6 : OGRSOSILayer::OGRSOSILayer(OGRSOSIDataSource *poPar, OGRFeatureDefn *poFeatDefn,
25 6 : LC_FILADM *poFil, S2I *poHeadDefn)
26 : {
27 6 : poParent = poPar;
28 6 : poFileadm = poFil;
29 6 : poFeatureDefn = poFeatDefn;
30 6 : poHeaderDefn = poHeadDefn;
31 6 : nNextFID = 0;
32 6 : poNextSerial = nullptr;
33 :
34 6 : SetDescription(poFeatureDefn->GetName());
35 6 : if (poFeatureDefn->GetGeomFieldCount() > 0)
36 6 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poParent->poSRS);
37 :
38 6 : OGRSOSILayer::ResetReading();
39 6 : }
40 :
41 : /************************************************************************/
42 : /* ~OGRSOSILayer() */
43 : /************************************************************************/
44 12 : OGRSOSILayer::~OGRSOSILayer()
45 : {
46 6 : poFeatureDefn->Release();
47 12 : }
48 :
49 : /************************************************************************/
50 : /* GetLayerDefn() */
51 : /************************************************************************/
52 0 : OGRFeatureDefn *OGRSOSILayer::GetLayerDefn()
53 : {
54 0 : return poFeatureDefn;
55 : }
56 :
57 : #ifdef WRITE_SUPPORT
58 : /************************************************************************/
59 : /* CreateField() */
60 : /************************************************************************/
61 : OGRErr OGRSOSILayer::CreateField(OGRFieldDefn *poField,
62 : CPL_UNUSED int bApproxOK)
63 : {
64 : poFeatureDefn->AddFieldDefn(poField);
65 : return OGRERR_NONE; /* We'll just gladly accept any "field" we find */
66 : }
67 :
68 : /************************************************************************/
69 : /* ICreateFeature() */
70 : /************************************************************************/
71 : OGRErr OGRSOSILayer::ICreateFeature(OGRFeature *poFeature)
72 : {
73 : // short nNavn;
74 : long nSerial;
75 :
76 : const char *pszSosi = NULL;
77 : switch (poFeatureDefn->GetGeomType())
78 : {
79 : case wkbPoint:
80 : {
81 : pszSosi = ".PUNKT";
82 : break;
83 : }
84 : case wkbLineString:
85 : {
86 : pszSosi = ".KURVE";
87 : break;
88 : }
89 : case wkbPolygon:
90 : {
91 : pszSosi = ".FLATE";
92 : break;
93 : }
94 : default:
95 : {
96 : CPLError(CE_Warning, CPLE_AppDefined,
97 : "Unknown geometry type in CreateFeature.");
98 : return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
99 : }
100 : }
101 : /*nNavn = */ LC_NyGr(poFileadm, (char *)pszSosi, &oNextSerial, &nSerial);
102 : /* === WIP - Work in progress === */
103 : /* PutGI for all headers */
104 : char pszGi[255];
105 : for (int i = 0; i < poFeature->GetFieldCount(); i++)
106 : {
107 : int n = snprintf(pszGi, 255, "%s",
108 : poFeature->GetFieldDefnRef(i)->GetNameRef());
109 : if (n < 255)
110 : {
111 : /*int m = */ snprintf(pszGi + (n - 1), 255 - n, "%s",
112 : poFeature->GetFieldAsString(i));
113 : /* check overflow */
114 : }
115 : LC_PutGi(i + 2, pszGi); /* should add headers too */
116 : }
117 : // LC_OppdaterEndret(0);
118 : /* PutTK for all coords */
119 : /* ... */
120 : /* === /WIP - Work in progress === */
121 : LC_WsGr(poFileadm); /* Writing the header here! */
122 : return OGRERR_NONE;
123 : }
124 : #endif
125 :
126 : /************************************************************************/
127 : /* GetNextFeature() */
128 : /************************************************************************/
129 63 : OGRFeature *OGRSOSILayer::GetNextFeature()
130 : {
131 : short nName, nNumLines;
132 : long nNumCoo;
133 : unsigned short nInfo;
134 :
135 : // Used to limit the number of limit the fields to be appended.
136 : // Default is that duplicates will appended if no appendFieldsMap parameter
137 : // is sent.
138 126 : std::map<std::string, std::string> appendFieldsMap;
139 :
140 : // Get appendFieldsMap and update appendFieldsMap if present;
141 : // The input must on this format
142 :
143 : // -oo appendFieldsMap="BEITEBRUKERID:,&FIELD2test: &FIELD3test:;"
144 : // With the example above the field BEITEBRUKERID append character will be
145 : // ':', for FIELD2test it will space and for FIELD3test it will be ;
146 :
147 : // -oo appendFieldsMap="BEITEBRUKERID&FIELD2test&FIELD3test"
148 : // With the example above the append character will be ',' for fields in the
149 : // list
150 :
151 : const char *appendFieldsMapInput =
152 63 : CSLFetchNameValue(poParent->GetOpenOptions(), "appendFieldsMap");
153 126 : CPLStringList aosTokens(CSLTokenizeString2(appendFieldsMapInput, "&", 0));
154 147 : for (int i = 0; i < aosTokens.size(); i++)
155 : {
156 168 : std::string filedsAndDelimStr = aosTokens[i];
157 84 : std::size_t found = filedsAndDelimStr.find(":");
158 168 : std::string appfieldName = filedsAndDelimStr;
159 84 : std::string appfieldDelimiter = ",";
160 84 : if (found < filedsAndDelimStr.length())
161 : {
162 42 : appfieldName = filedsAndDelimStr.substr(0, found);
163 42 : appfieldDelimiter = filedsAndDelimStr.substr(found + 1);
164 : }
165 168 : appendFieldsMap.insert(std::pair<std::string, std::string>(
166 84 : appfieldName, appfieldDelimiter));
167 : }
168 :
169 : /* iterate through the SOSI groups*/
170 129 : while (LC_NextBgr(poNextSerial, LC_FRAMGR))
171 : {
172 : nName =
173 123 : LC_RxGr(&oNextSerial, LES_OPTIMALT, &nNumLines, &nNumCoo, &nInfo);
174 :
175 123 : S2S oHeaders;
176 123 : S2S::iterator iHeaders;
177 : /* extract reference strings from group header */
178 123 : CPLString osKey, osValue;
179 1116 : for (short i = 1; i <= nNumLines; i++)
180 : {
181 993 : char *pszLine = LC_GetGi(i);
182 993 : if (pszLine[0] == '!')
183 0 : continue; /* If we have a comment line, skip it. */
184 993 : if ((pszLine[0] == ':') || (pszLine[0] == '('))
185 : { /* if we have a continued REF line... */
186 : osValue.append(
187 9 : CPLString(pszLine)); /* append to previous line. */
188 : oHeaders.insert(
189 9 : std::pair<CPLString, CPLString>(osKey, osValue));
190 9 : continue;
191 : }
192 2874 : while (pszLine[0] == '.')
193 1890 : pszLine++; /* skipping the dots at the beginning of a SOSI line
194 : */
195 : char *pszUTFLine =
196 984 : CPLRecode(pszLine, poParent->pszEncoding,
197 : CPL_ENC_UTF8); /* switch to UTF encoding here */
198 984 : char *pszPos = strstr(pszUTFLine, " ");
199 984 : if (pszPos != nullptr)
200 : {
201 957 : osKey = CPLString(std::string(pszUTFLine, pszPos));
202 : // Check if this oskey is used before in this feature
203 966 : if (oHeaders.count(osKey) > 0 &&
204 966 : appendFieldsMap.count(osKey.c_str()) > 0)
205 : {
206 : // get old osvalue so we can append the next value
207 12 : CPLString newAppendOsValue = oHeaders[osKey];
208 :
209 : // append split character
210 6 : newAppendOsValue.append(appendFieldsMap[osKey]);
211 :
212 : // append new value
213 6 : newAppendOsValue.append(CPLString(pszPos + 1));
214 :
215 : // the new value
216 6 : oHeaders[osKey] = std::move(newAppendOsValue);
217 : }
218 : else
219 : {
220 951 : osValue = CPLString(pszPos + 1);
221 : oHeaders.insert(
222 951 : std::pair<CPLString, CPLString>(osKey, osValue));
223 : }
224 : }
225 984 : CPLFree(pszUTFLine);
226 : }
227 :
228 : /* get Feature from fyba, according to feature definition */
229 0 : std::unique_ptr<OGRGeometry> poGeom;
230 123 : OGRwkbGeometryType oGType = wkbUnknown;
231 :
232 123 : switch (nName)
233 : {
234 0 : case INGEN_GRUPPE:
235 : { /* No group */
236 0 : CPLDebug("[GetNextFeature]", "Could not load further groups - "
237 : "FYBA reported INGEN_GRUPPE.");
238 0 : break;
239 : }
240 9 : case L_FLATE:
241 : { /* Area */
242 9 : oGType = wkbPolygon;
243 : auto poOuter =
244 9 : std::make_unique<OGRLinearRing>(); /* Initialize a new
245 : closed polygon */
246 : long nRefNr;
247 : unsigned char nRefStatus;
248 : long nRefCount;
249 9 : bool correct = true;
250 : LC_GRF_STATUS oGrfStat;
251 :
252 : // Iterate through all objects that constitute this area.
253 9 : LC_InitGetRefFlate(&oGrfStat);
254 9 : nRefCount = LC_GetRefFlate(&oGrfStat, GRF_YTRE, &nRefNr,
255 : &nRefStatus, 1);
256 162 : while (nRefCount > 0)
257 : {
258 153 : if (poParent->papoBuiltGeometries[nRefNr] == nullptr)
259 : {
260 : // This should not happen under normal operation.
261 0 : CPLError(CE_Warning, CPLE_AppDefined,
262 : "Feature %li referenced by %li, but it was "
263 : "not initialized. Geometry may be broken.",
264 : nRefNr, oNextSerial.lNr);
265 0 : correct = false;
266 : // return NULL;
267 0 : break;
268 : }
269 153 : OGRGeometry *geom = poParent->papoBuiltGeometries[nRefNr];
270 153 : if (geom->getGeometryType() == wkbLineString)
271 : {
272 153 : OGRLineString *poCurve = geom->toLineString();
273 153 : if (nRefStatus == LC_MED_DIG)
274 : { /* clockwise */
275 90 : poOuter->addSubLineString(poCurve);
276 : }
277 63 : else if (nRefStatus == LC_MOT_DIG)
278 : { /* counter-clockwise */
279 63 : poOuter->addSubLineString(
280 63 : poCurve, poCurve->getNumPoints() - 1, 0);
281 : }
282 : else
283 : {
284 0 : CPLError(CE_Failure, CPLE_OpenFailed,
285 : "Internal error: GRF_*_OY encountered.");
286 0 : return nullptr;
287 : }
288 : }
289 : else
290 : {
291 0 : CPLError(CE_Warning, CPLE_AppDefined,
292 : "Element %li composed of non-linestrings (REF "
293 : "%li of type %i). Ignored.",
294 : oNextSerial.lNr, nRefNr,
295 0 : geom->getGeometryType());
296 : }
297 153 : nRefCount = LC_GetRefFlate(&oGrfStat, GRF_YTRE, &nRefNr,
298 : &nRefStatus, 1);
299 : }
300 :
301 9 : if (correct)
302 : {
303 9 : auto poLy = std::make_unique<OGRPolygon>();
304 9 : poOuter->closeRings();
305 9 : poLy->addRingDirectly(poOuter.release());
306 :
307 0 : std::unique_ptr<OGRLinearRing> poInner;
308 9 : nRefCount = LC_GetRefFlate(&oGrfStat, GRF_INDRE, &nRefNr,
309 : &nRefStatus, 1);
310 9 : while (nRefCount > 0)
311 : {
312 0 : if (nRefNr == -1)
313 : {
314 0 : if (poInner && poInner->getNumPoints() > 2)
315 : { /* If this is not the first polygon, terminate and
316 : add the last */
317 0 : poInner->closeRings();
318 0 : poLy->addRingDirectly(poInner.release());
319 : }
320 0 : poInner.reset(
321 0 : new OGRLinearRing()); /* Initialize a new closed
322 : polygon */
323 : }
324 : else
325 : {
326 0 : if (poParent->papoBuiltGeometries[nRefNr] ==
327 : nullptr)
328 : { /* this shouldn't happen under normal operation */
329 0 : CPLError(CE_Fatal, CPLE_AppDefined,
330 : "Feature %li referenced by %li, but "
331 : "it was not initialized.",
332 : nRefNr, oNextSerial.lNr);
333 0 : return nullptr;
334 : }
335 0 : OGRGeometry *geom =
336 0 : poParent->papoBuiltGeometries[nRefNr];
337 0 : if (geom->getGeometryType() == wkbLineString)
338 : {
339 0 : OGRLineString *poCurve = geom->toLineString();
340 0 : if (poInner && nRefStatus == LC_MED_DIG)
341 : { /* clockwise */
342 0 : poInner->addSubLineString(poCurve);
343 : }
344 0 : else if (poInner && nRefStatus == LC_MOT_DIG)
345 : { /* counter-clockwise */
346 0 : poInner->addSubLineString(
347 0 : poCurve, poCurve->getNumPoints() - 1,
348 : 0);
349 : }
350 : else
351 : {
352 0 : CPLError(CE_Failure, CPLE_OpenFailed,
353 : "Internal error: GRF_*_OY "
354 : "encountered.");
355 0 : return nullptr;
356 : }
357 : }
358 : else
359 : {
360 0 : CPLError(
361 : CE_Warning, CPLE_AppDefined,
362 : "Element %li composed of non-linestrings "
363 : "(REF %li of type %i). Ignored.",
364 : oNextSerial.lNr, nRefNr,
365 0 : geom->getGeometryType());
366 : }
367 : }
368 0 : nRefCount = LC_GetRefFlate(&oGrfStat, GRF_INDRE,
369 : &nRefNr, &nRefStatus, 1);
370 : }
371 9 : poGeom = std::move(poLy);
372 : }
373 9 : break;
374 : }
375 105 : case L_KURVE: /* curve */
376 : case L_LINJE: /* curve, not simplifyable */
377 : case L_BUEP:
378 : { /* curve, interpolated from circular arc */
379 105 : oGType = wkbLineString;
380 210 : if (poParent->papoBuiltGeometries[oNextSerial.lNr] == nullptr ||
381 105 : poParent->papoBuiltGeometries[oNextSerial.lNr]
382 105 : ->getGeometryType() != wkbLineString)
383 : {
384 : // This should not happen under normal operation.
385 0 : CPLError(CE_Warning, CPLE_AppDefined,
386 : "Curve or line %li may have a broken geometry",
387 : oNextSerial.lNr);
388 : // return NULL;
389 0 : break;
390 : }
391 : const OGRLineString *poCurve =
392 105 : poParent->papoBuiltGeometries[oNextSerial.lNr]
393 105 : ->toLineString();
394 105 : poGeom.reset(poCurve->clone());
395 105 : break;
396 : }
397 0 : case L_TEKST:
398 : { /* text */
399 0 : oGType = wkbMultiPoint;
400 0 : if (poParent->papoBuiltGeometries[oNextSerial.lNr] == nullptr ||
401 0 : poParent->papoBuiltGeometries[oNextSerial.lNr]
402 0 : ->getGeometryType() != wkbMultiPoint)
403 : {
404 : // This should not happen under normal operation.
405 0 : CPLError(CE_Warning, CPLE_AppDefined,
406 : "Text point %li may have a broken geometry",
407 : oNextSerial.lNr);
408 : // return NULL;
409 0 : break;
410 : }
411 : const OGRMultiPoint *poMP =
412 0 : poParent->papoBuiltGeometries[oNextSerial.lNr]
413 0 : ->toMultiPoint();
414 0 : poGeom.reset(poMP->clone());
415 0 : break;
416 : }
417 0 : case L_SYMBOL:
418 : {
419 : // CPLError( CE_Warning, CPLE_OpenFailed, "Geometry of type
420 : // SYMBOL treated as point (PUNKT).");
421 : [[fallthrough]];
422 : }
423 : case L_PUNKT:
424 : { /* point */
425 0 : oGType = wkbPoint;
426 0 : if (poParent->papoBuiltGeometries[oNextSerial.lNr] == nullptr ||
427 0 : poParent->papoBuiltGeometries[oNextSerial.lNr]
428 0 : ->getGeometryType() != wkbPoint)
429 : {
430 : // This should not happen under normal operation.
431 0 : CPLError(CE_Warning, CPLE_AppDefined,
432 : "Point or symbol %li may have a broken geometry",
433 : oNextSerial.lNr);
434 : // return NULL;
435 0 : break;
436 : }
437 : const OGRPoint *poPoint =
438 0 : poParent->papoBuiltGeometries[oNextSerial.lNr]->toPoint();
439 0 : poGeom.reset(poPoint->clone());
440 0 : break;
441 : }
442 9 : case L_DEF: /* skip user definitions and headers here */
443 : case L_HODE:
444 : {
445 9 : break;
446 : }
447 0 : default:
448 : { /* complain a bit about anything else that is not implemented */
449 0 : CPLError(CE_Failure, CPLE_OpenFailed,
450 : "Unrecognized geometry of type %i.", nName);
451 0 : break;
452 : }
453 : }
454 :
455 123 : if (poGeom == nullptr)
456 9 : continue; /* skipping L_HODE and unrecognized groups */
457 114 : if (oGType != poFeatureDefn->GetGeomType())
458 : {
459 57 : continue; /* skipping features that are not the correct geometry */
460 : }
461 :
462 57 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
463 :
464 : /* set all headers found in this group - we export everything, just in
465 : * case */
466 492 : for (iHeaders = oHeaders.begin(); iHeaders != oHeaders.end();
467 435 : ++iHeaders)
468 : {
469 435 : OGRSOSIDataType *poType = SOSIGetType(iHeaders->first);
470 435 : OGRSOSISimpleDataType *poElements = poType->getElements();
471 :
472 435 : const char *pszLine = iHeaders->second.c_str();
473 435 : char **tokens = CSLTokenizeString(iHeaders->second.c_str());
474 :
475 909 : for (int k = 0; k < poType->getElementCount(); k++)
476 : {
477 531 : if (tokens[k] == nullptr)
478 57 : break;
479 :
480 474 : if (strcmp(poElements[k].GetName(), "") == 0)
481 63 : continue;
482 411 : const auto oIter = poHeaderDefn->find(poElements[k].GetName());
483 : const int iHNr =
484 411 : oIter == poHeaderDefn->end() ? -1 : oIter->second;
485 411 : if (iHNr == -1)
486 : {
487 0 : CPLError(CE_Warning, CPLE_AppDefined,
488 : "Could not find field definition for %s.",
489 0 : poElements[k].GetName());
490 0 : continue;
491 : }
492 411 : OGRFieldType nType = poElements[k].GetType();
493 411 : switch (nType)
494 : {
495 96 : case OFTInteger:
496 : {
497 96 : poFeature->SetField(iHNr, SOSITypeToInt(tokens[k]));
498 96 : break;
499 : }
500 0 : case OFTDate:
501 : {
502 : int date[3];
503 0 : SOSITypeToDate(tokens[k], date);
504 0 : poFeature->SetField(iHNr, date[0], date[1], date[2]);
505 0 : break;
506 : }
507 117 : case OFTDateTime:
508 : {
509 : int date[6];
510 117 : SOSITypeToDateTime(tokens[k], date);
511 117 : if (date[0] > 0)
512 117 : poFeature->SetField(iHNr, date[0], date[1], date[2],
513 : date[3], date[4],
514 117 : static_cast<float>(date[5]), 1);
515 117 : break;
516 : }
517 0 : case OFTReal:
518 : {
519 0 : poFeature->SetField(iHNr, SOSITypeToReal(tokens[k]));
520 0 : break;
521 : }
522 198 : default:
523 : {
524 198 : if ((k == 0) &&
525 198 : ((pszLine[0] == '\'') || (pszLine[0] == '\"')))
526 : { /* If the value is quoted, ignore these */
527 30 : int nLen = static_cast<int>(strlen(pszLine));
528 30 : char *pszNline = (char *)CPLMalloc(nLen - 1);
529 30 : strncpy(pszNline, pszLine + 1, nLen - 2);
530 30 : pszNline[nLen - 2] = '\0';
531 30 : poFeature->SetField(iHNr, pszNline);
532 30 : CPLFree(pszNline);
533 : }
534 : else
535 : {
536 168 : poFeature->SetField(iHNr, tokens[k]);
537 : }
538 198 : break;
539 : }
540 : }
541 : }
542 :
543 435 : CSLDestroy(tokens);
544 : }
545 :
546 57 : poGeom->assignSpatialReference(poParent->poSRS);
547 :
548 57 : poFeature->SetGeometryDirectly(poGeom.release());
549 57 : poFeature->SetFID(nNextFID++);
550 :
551 : /* Loop until we have a feature that matches the definition */
552 114 : if ((m_poFilterGeom == nullptr ||
553 114 : FilterGeometry(poFeature->GetGeometryRef())) &&
554 57 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
555 57 : return poFeature;
556 0 : delete poFeature;
557 : }
558 6 : return nullptr;
559 : }
560 :
561 : /************************************************************************/
562 : /* ResetReading() */
563 : /************************************************************************/
564 18 : void OGRSOSILayer::ResetReading()
565 : {
566 18 : LC_SBSn(&oSnradm, poFileadm, 0,
567 18 : poFileadm->lAntGr); /* set FYBA Search limits */
568 18 : poNextSerial = &oNextSerial;
569 18 : LC_InitNextBgr(poNextSerial);
570 18 : nNextFID = 0;
571 18 : }
572 :
573 : /************************************************************************/
574 : /* TestCapability() */
575 : /************************************************************************/
576 :
577 0 : int OGRSOSILayer::TestCapability(const char *pszCap)
578 : {
579 :
580 0 : if (EQUAL(pszCap, OLCStringsAsUTF8))
581 0 : return TRUE;
582 : #if 0
583 : if( EQUAL(pszCap,OLCCreateField) )
584 : return TRUE;
585 : else
586 : #endif
587 0 : return FALSE;
588 : }
|