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