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 CPL_CVSID("$Id: ogrsosilayer.cpp 327bfdc0f5dd563c3b1c4cbf26d34967c5c9c790 2020-02-28 13:51:40 +0100 Even Rouault $")
38 
39 /************************************************************************/
40 /*                           OGRSOSILayer()                             */
41 /************************************************************************/
42 
OGRSOSILayer(OGRSOSIDataSource * poPar,OGRFeatureDefn * poFeatDefn,LC_FILADM * poFil,std::map<CPLString,unsigned int> * poHeadDefn)43 OGRSOSILayer::OGRSOSILayer( OGRSOSIDataSource *poPar, OGRFeatureDefn *poFeatDefn, LC_FILADM *poFil, std::map<CPLString, unsigned int> *poHeadDefn) {
44     poParent      = poPar;
45     poFileadm     = poFil;
46     poFeatureDefn = poFeatDefn;
47     poHeaderDefn  = poHeadDefn;
48     nNextFID      = 0;
49     poNextSerial  = nullptr;
50 
51     SetDescription( poFeatureDefn->GetName() );
52     if( poFeatureDefn->GetGeomFieldCount() > 0 )
53         poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poParent->poSRS);
54 
55     OGRSOSILayer::ResetReading();
56 }
57 
58 /************************************************************************/
59 /*                           ~OGRSOSILayer()                            */
60 /************************************************************************/
~OGRSOSILayer()61 OGRSOSILayer::~OGRSOSILayer() {
62     poFeatureDefn->Release();
63 }
64 
65 /************************************************************************/
66 /*                           GetLayerDefn()                             */
67 /************************************************************************/
GetLayerDefn()68 OGRFeatureDefn *OGRSOSILayer::GetLayerDefn() {
69     return poFeatureDefn;
70 }
71 
72 #ifdef WRITE_SUPPORT
73 /************************************************************************/
74 /*                           CreateField()                              */
75 /************************************************************************/
CreateField(OGRFieldDefn * poField,CPL_UNUSED int bApproxOK)76 OGRErr OGRSOSILayer::CreateField (OGRFieldDefn *poField, CPL_UNUSED int bApproxOK) {
77     poFeatureDefn->AddFieldDefn( poField );
78     return OGRERR_NONE; /* We'll just gladly accept any "field" we find */
79 }
80 
81 /************************************************************************/
82 /*                           ICreateFeature()                            */
83 /************************************************************************/
ICreateFeature(OGRFeature * poFeature)84 OGRErr OGRSOSILayer::ICreateFeature(OGRFeature *poFeature) {
85     //short nNavn;
86     long nSerial;
87 
88     const char *pszSosi = NULL;
89     switch (poFeatureDefn->GetGeomType()) {
90         case wkbPoint: {
91             pszSosi = ".PUNKT";
92             break;
93         }
94         case wkbLineString: {
95             pszSosi = ".KURVE";
96             break;
97         }
98         case wkbPolygon: {
99             pszSosi = ".FLATE";
100             break;
101         }
102         default: {
103             CPLError( CE_Warning, CPLE_AppDefined, "Unknown geometry type in CreateFeature.");
104             return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
105         }
106     }
107     /*nNavn = */ LC_NyGr(poFileadm, (char *)pszSosi, &oNextSerial, &nSerial);
108     /* === WIP - Work in progress === */
109     /* PutGI for all headers */
110     char pszGi[255];
111     for (int i=0;i<poFeature->GetFieldCount();i++) {
112         int n = snprintf (pszGi, 255, "%s", poFeature->GetFieldDefnRef(i)->GetNameRef());
113         if (n<255) {
114             /*int m = */snprintf (pszGi + (n-1), 255-n, "%s", poFeature->GetFieldAsString(i));
115           /* check overflow */
116         }
117         LC_PutGi(i+2, pszGi); /* should add headers too */
118     }
119     // LC_OppdaterEndret(0);
120     /* PutTK for all coords */
121     /* ... */
122     /* === /WIP - Work in progress === */
123     LC_WsGr(poFileadm); /* Writing the header here! */
124     return OGRERR_NONE;
125 }
126 #endif
127 
128 /************************************************************************/
129 /*                           GetNextFeature()                           */
130 /************************************************************************/
GetNextFeature()131 OGRFeature *OGRSOSILayer::GetNextFeature() {
132     short nName, nNumLines;
133     long  nNumCoo;
134     unsigned short nInfo;
135 
136     // Used to limit the number of limit the fields to be appended.
137     // Default is that duplicates will appended if no appendFieldsMap parameter is sent.
138     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 ':', for FIELD2test it will space and for FIELD3test it will be ;
145 
146     // -oo appendFieldsMap="BEITEBRUKERID&FIELD2test&FIELD3test"
147     // With the example above the append character will be ',' for fields in the list
148 
149     const char * appendFieldsMapInput = CSLFetchNameValue(poParent->GetOpenOptions(),"appendFieldsMap");
150     CPLStringList aosTokens(CSLTokenizeString2(appendFieldsMapInput, "&", 0));
151     for( int i = 0; i < aosTokens.size(); i++ )
152     {
153         std::string filedsAndDelimStr = aosTokens[i];
154         std::size_t found = filedsAndDelimStr.find(":");
155         std::string appfieldName = filedsAndDelimStr;
156         std::string appfieldDelimiter = ",";
157         if (found < filedsAndDelimStr.length()) {
158             appfieldName = filedsAndDelimStr.substr(0,found);
159             appfieldDelimiter = filedsAndDelimStr.substr(found+1);
160         }
161         appendFieldsMap.insert(std::pair<std::string,std::string>(appfieldName,appfieldDelimiter));
162     }
163 
164 
165     /* iterate through the SOSI groups*/
166     while (LC_NextBgr(poNextSerial,LC_FRAMGR)) {
167         nName = LC_RxGr(&oNextSerial, LES_OPTIMALT, &nNumLines, &nNumCoo, &nInfo);
168 
169         S2S oHeaders;
170         S2S::iterator iHeaders;
171         /* extract reference strings from group header */
172         CPLString osKey, osValue;
173         for (short i=1; i<=nNumLines; i++) {
174             char *pszLine = LC_GetGi(i);
175             if (pszLine[0] == '!') continue;                 /* If we have a comment line, skip it. */
176             if ((pszLine[0] == ':')||(pszLine[0] == '(')) {  /* if we have a continued REF line... */
177                 osValue.append(CPLString(pszLine));            /* append to previous line.           */
178                 oHeaders.insert(std::pair<CPLString,CPLString>(osKey,osValue));
179                 continue;
180             }
181             while (pszLine[0] == '.') pszLine++; /* skipping the dots at the beginning of a SOSI line */
182             char *pszUTFLine = CPLRecode(pszLine, poParent->pszEncoding, CPL_ENC_UTF8); /* switch to UTF encoding here */
183             char *pszPos = strstr(pszUTFLine, " ");
184             if (pszPos != nullptr) {
185                 osKey = CPLString(std::string(pszUTFLine,pszPos));
186                 // Check if this oskey is used before in this feature
187                 if (oHeaders.count(osKey) > 0 && appendFieldsMap.count(osKey.c_str()) > 0) {
188                     // get old osvalue so we can append the next value
189                     CPLString newAppendOsValue = oHeaders[osKey];
190 
191                     // append split character
192                     newAppendOsValue.append(appendFieldsMap[osKey]);
193 
194                     // append new value
195                     newAppendOsValue.append(CPLString(pszPos+1));
196 
197                     // the new value
198                     oHeaders[osKey]= newAppendOsValue;
199 
200                     // printf ("Append value for %s is %s \n", osKey.c_str(), newAppendOsValue.c_str());
201                 } else {
202                     osValue = CPLString(pszPos+1);
203                     oHeaders.insert(std::pair<CPLString,CPLString>(osKey,osValue));
204                 }
205             }
206             CPLFree(pszUTFLine);
207         }
208 
209         /* get Feature from fyba, according to feature definition */
210         OGRGeometry *poGeom = nullptr;
211         OGRwkbGeometryType oGType = wkbUnknown;
212 
213         switch (nName) {
214         case INGEN_GRUPPE: {  /* No group */
215             CPLDebug( "[GetNextFeature]", "Could not load further groups - FYBA reported INGEN_GRUPPE.");
216             break;
217         }
218         case L_FLATE: {  /* Area */
219             oGType = wkbPolygon;
220             std::unique_ptr<OGRLinearRing> poOuter(new OGRLinearRing());  /* Initialize a new closed polygon */
221             long nRefNr;
222             unsigned char nRefStatus;
223             long nRefCount;
224             bool correct = true;
225             LC_GRF_STATUS oGrfStat;
226 
227             // Iterate through all objects that constitute this area.
228             LC_InitGetRefFlate(&oGrfStat);
229             nRefCount = LC_GetRefFlate(&oGrfStat, GRF_YTRE, &nRefNr, &nRefStatus, 1);
230             while (nRefCount > 0) {
231                 if (poParent->papoBuiltGeometries[nRefNr] == nullptr) {
232                     // This should not happen under normal operation.
233                     CPLError( CE_Warning, CPLE_AppDefined, "Feature %li referenced by %li, but it was not initialized. Geometry may be broken.", nRefNr, oNextSerial.lNr);
234                     correct = false;
235                     //return NULL;
236                     break;
237                 }
238                 OGRGeometry *geom = poParent->papoBuiltGeometries[nRefNr];
239                 if (geom->getGeometryType() == wkbLineString) {
240                   OGRLineString *poCurve = geom->toLineString();
241                   if (nRefStatus == LC_MED_DIG) {         /* clockwise */
242                     poOuter->addSubLineString(poCurve);
243                   } else if (nRefStatus == LC_MOT_DIG) {  /* counter-clockwise */
244                       poOuter->addSubLineString(poCurve,poCurve->getNumPoints()-1,0);
245                   } else {
246                       CPLError( CE_Failure, CPLE_OpenFailed, "Internal error: GRF_*_OY encountered.");
247                       return nullptr;
248                   }
249                 } else {
250                     CPLError( CE_Warning, CPLE_AppDefined, "Element %li composed of non-linestrings (REF %li of type %i). Ignored.", oNextSerial.lNr, nRefNr, geom->getGeometryType());
251                 }
252                 nRefCount = LC_GetRefFlate(&oGrfStat, GRF_YTRE, &nRefNr, &nRefStatus, 1);
253             }
254 
255             if (correct) {
256               std::unique_ptr<OGRPolygon> poLy(new OGRPolygon());
257               poOuter->closeRings();
258               poLy->addRingDirectly(poOuter.release());
259 
260               std::unique_ptr<OGRLinearRing> poInner;
261               nRefCount = LC_GetRefFlate(&oGrfStat, GRF_INDRE, &nRefNr, &nRefStatus, 1);
262               while (nRefCount > 0) {
263                   if (nRefNr == -1) {
264                     if (poInner && poInner->getNumPoints()>2) {   /* If this is not the first polygon, terminate and add the last */
265                       poInner->closeRings();
266                       poLy->addRingDirectly(poInner.release());
267                     }
268                     poInner.reset(new OGRLinearRing());  /* Initialize a new closed polygon */
269                   } else {
270                     if (poParent->papoBuiltGeometries[nRefNr] == nullptr) { /* this shouldn't happen under normal operation */
271                         CPLError( CE_Fatal, CPLE_AppDefined, "Feature %li referenced by %li, but it was not initialized.", nRefNr, oNextSerial.lNr);
272                         return nullptr;
273                     }
274                     OGRGeometry *geom = poParent->papoBuiltGeometries[nRefNr];
275                     if (geom->getGeometryType() == wkbLineString) {
276                       OGRLineString *poCurve = geom->toLineString();
277                       if (poInner && nRefStatus == LC_MED_DIG) {         /* clockwise */
278                         poInner->addSubLineString(poCurve);
279                       } else if (poInner && nRefStatus == LC_MOT_DIG) {  /* counter-clockwise */
280                           poInner->addSubLineString(poCurve,poCurve->getNumPoints()-1,0);
281                       } else {
282                           CPLError( CE_Failure, CPLE_OpenFailed, "Internal error: GRF_*_OY encountered.");
283                           return nullptr;
284                       }
285                     } else {
286                         CPLError( CE_Warning, CPLE_AppDefined, "Element %li composed of non-linestrings (REF %li of type %i). Ignored.", oNextSerial.lNr, nRefNr, geom->getGeometryType());
287                     }
288                   }
289                   nRefCount = LC_GetRefFlate(&oGrfStat, GRF_INDRE, &nRefNr, &nRefStatus, 1);
290               }
291               poGeom = poLy.release();
292             }
293             break;
294         }
295         case L_KURVE:    /* curve */
296         case L_LINJE:    /* curve, not simplifyable */
297         case L_BUEP:  {  /* curve, interpolated from circular arc */
298             oGType = wkbLineString;
299 
300             OGRLineString *poCurve = poParent->papoBuiltGeometries[oNextSerial.lNr]->toLineString();
301             poGeom = poCurve->clone();
302             break;
303         }
304         case L_TEKST: {  /* text */
305             oGType = wkbMultiPoint;
306 
307             OGRMultiPoint *poMP = poParent->papoBuiltGeometries[oNextSerial.lNr]->toMultiPoint();
308             poGeom = poMP->clone();
309             break;
310         }
311         case L_SYMBOL: {
312             //CPLError( CE_Warning, CPLE_OpenFailed, "Geometry of type SYMBOL treated as point (PUNKT).");
313             CPL_FALLTHROUGH
314         }
315         case L_PUNKT: {  /* point */
316             oGType = wkbPoint;
317             OGRPoint *poPoint = poParent->papoBuiltGeometries[oNextSerial.lNr]->toPoint();
318             poGeom = poPoint->clone();
319             break;
320         }
321         case L_DEF:    /* skip user definitions and headers here */
322         case L_HODE: {
323             break;
324         }
325         default: {     /* complain a bit about anything else that is not implemented */
326             CPLError( CE_Failure, CPLE_OpenFailed, "Unrecognized geometry of type %i.", nName);
327             break;
328         }
329         }
330 
331         if (poGeom == nullptr) continue;                         /* skipping L_HODE and unrecognized groups */
332         if (oGType != poFeatureDefn->GetGeomType()) {
333             delete poGeom;
334             continue; /* skipping features that are not the correct geometry */
335         }
336 
337         OGRFeature *poFeature = new OGRFeature( poFeatureDefn );
338 
339         /* set all headers found in this group - we export everything, just in case */
340         for (iHeaders = oHeaders.begin(); iHeaders != oHeaders.end(); ++iHeaders) {
341             OGRSOSIDataType *poType = SOSIGetType(iHeaders->first);
342             OGRSOSISimpleDataType *poElements = poType->getElements();
343 
344             const char *pszLine = iHeaders->second.c_str();
345             char** tokens = CSLTokenizeString(iHeaders->second.c_str());
346 
347             for (int k=0; k<poType->getElementCount(); k++) {
348                 if (tokens[k] == nullptr) break;
349 
350                 if (strcmp(poElements[k].GetName(),"")==0) continue;
351                 int iHNr = poHeaderDefn->find(poElements[k].GetName())->second;
352                 if (iHNr == -1) {
353                     CPLError( CE_Warning, CPLE_AppDefined, "Could not find field definition for %s.", poElements[k].GetName());
354                     continue;
355                 }
356                 OGRFieldType nType = poElements[k].GetType();
357                 switch (nType) {
358                   case OFTInteger: {
359                     poFeature->SetField( iHNr, SOSITypeToInt(tokens[k]));
360                     break;
361                   }
362                   case OFTDate: {
363                     int date[3];
364                     SOSITypeToDate(tokens[k], date);
365                     poFeature->SetField( iHNr, date[0], date[1], date[2]);
366                     break;
367                   }
368                   case OFTDateTime: {
369                     int date[6];
370                     SOSITypeToDateTime(tokens[k], date);
371                     if (date[0]>0)
372                       poFeature->SetField( iHNr, date[0], date[1], date[2], date[3], date[4], static_cast<float>(date[5]), 1);
373                     break;
374                   }
375                   case OFTReal: {
376                     poFeature->SetField( iHNr, SOSITypeToReal(tokens[k]));
377                     break;
378                   }
379                   default: {
380                     if ((k==0)&&((pszLine[0] == '\'')||(pszLine[0] == '\"'))) { /* If the value is quoted, ignore these */
381                         int nLen = static_cast<int>(strlen(pszLine));
382                         char *pszNline = (char*)CPLMalloc(nLen-1);
383                         strncpy(pszNline, pszLine+1, nLen-2);
384                         pszNline[nLen-2] = '\0';
385                         poFeature->SetField( iHNr, pszNline);
386                         CPLFree(pszNline);
387                     } else {
388                         poFeature->SetField( iHNr, tokens[k]);
389                     }
390                     break;
391                   }
392                 }
393           }
394 
395           CSLDestroy(tokens);
396         }
397 
398         poGeom->assignSpatialReference(poParent->poSRS);
399 
400         poFeature->SetGeometryDirectly( poGeom );
401         poFeature->SetFID( nNextFID++ );
402 
403         /* Loop until we have a feature that matches the definition */
404         if ( (m_poFilterGeom == nullptr || FilterGeometry( poFeature->GetGeometryRef() ) )
405                 && (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate( poFeature )) )
406             return poFeature;
407         delete poFeature;
408     }
409     return nullptr;
410 }
411 
412 /************************************************************************/
413 /*                           ResetReading()                             */
414 /************************************************************************/
ResetReading()415 void OGRSOSILayer::ResetReading() {
416     LC_SBSn(&oSnradm, poFileadm, 0, poFileadm->lAntGr); /* set FYBA Search limits */
417     poNextSerial = &oNextSerial;
418     LC_InitNextBgr(poNextSerial);
419     nNextFID = 0;
420 }
421 
422 /************************************************************************/
423 /*                              TestCapability()                        */
424 /************************************************************************/
425 
TestCapability(const char * pszCap)426 int OGRSOSILayer::TestCapability( const char * pszCap ) {
427 
428     if( EQUAL(pszCap,OLCStringsAsUTF8) )
429         return TRUE;
430 #if 0
431     if( EQUAL(pszCap,OLCCreateField) )
432         return TRUE;
433     else
434 #endif
435         return FALSE;
436 }
437