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