1
/******************************************************************************
2
* $Id: ogrsosidatasource.cpp 21065 2010-11-05 18:47:30Z rouault $
4
* Project: SOSI Data Source
5
* Purpose: Provide SOSI Data to OGR.
6
* Author: Thomas Hirsch, <thomas.hirsch statkart no>
8
******************************************************************************
9
* Copyright (c) 2010, Thomas Hirsch
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:
18
* The above copyright notice and this permission notice shall be included
19
* in all copies or substantial portions of the Software.
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
****************************************************************************/
33
/* This is the most common encoding for SOSI files. Let's at least try if
34
* it is supported, or generate a meaningful error message. */
35
#ifndef CPL_ENC_ISO8859_10
36
# define CPL_ENC_ISO8859_10 "ISO8859-10"
39
/************************************************************************/
41
/************************************************************************/
43
int epsg2sosi (int nEPSG) {
46
case 27391: /* NGO 1984 Axis I-VIII */
54
nSOSI = nEPSG - 27390;
57
case 3043: /* UTM ZONE 31-36 */
66
case 23031: /* UTM ZONE 31-36 / ED50 */
72
nSOSI = nEPSG - 23000;
75
case 4326: { /* WSG84 */
80
CPLError( CE_Warning, CPLE_AppDefined,
81
"(Yet) unsupported coodinate system writing to SOSI file: %i. Defaulting to EPSG:4326/SOSI 84.", nEPSG);
87
int sosi2epsg (int nSOSI) {
90
case 1: /* NGO 1984 Axis I-VIII */
101
case 21: /* UTM ZONE 31-36 */
110
case 31: /* UTM ZONE 31-36 / ED50 */
119
case 84: { /* WSG84 */
124
CPLError( CE_Warning, CPLE_AppDefined,
125
"(Yet) unsupported coodinate system in SOSI-file: %i. Defaulting to EPSG:4326.", nSOSI);
131
/************************************************************************/
132
/* OGRSOSIDataSource() */
133
/************************************************************************/
135
OGRSOSIDataSource::OGRSOSIDataSource() {
140
papoBuiltGeometries = NULL;
145
poPolyHeaders = NULL;
146
poTextHeaders = NULL;
147
poPointHeaders = NULL;
148
poCurveHeaders = NULL;
150
pszEncoding = CPL_ENC_UTF8;
153
/************************************************************************/
154
/* ~OGRSOSIDataSource() */
155
/************************************************************************/
157
OGRSOSIDataSource::~OGRSOSIDataSource() {
158
if (papoBuiltGeometries != NULL) {
159
for (unsigned int i=0; i<nNumFeatures; i++) {
160
if (papoBuiltGeometries[i] != NULL) {
161
delete papoBuiltGeometries[i];
162
papoBuiltGeometries[i] = NULL;
165
CPLFree(papoBuiltGeometries);
166
papoBuiltGeometries = NULL;
169
if (poPolyHeaders != NULL) delete poPolyHeaders;
170
if (poTextHeaders != NULL) delete poTextHeaders;
171
if (poPointHeaders != NULL) delete poPointHeaders;
172
if (poCurveHeaders != NULL) delete poCurveHeaders;
174
if (nMode == MODE_WRITING) {
175
if (poFileadm != NULL) LC_CloseSos (poFileadm, RESET_IDX );
176
if (poBaseadm != NULL) LC_CloseBase (poBaseadm, RESET_IDX );
178
if (poFileadm != NULL) LC_CloseSos (poFileadm, SAVE_IDX );
179
if (poBaseadm != NULL) LC_CloseBase (poBaseadm, SAVE_IDX );
184
if (papoLayers != NULL) {
185
for ( int i = 0; i < nLayers; i++ ) {
186
delete papoLayers[i];
191
if (poSRS != NULL) poSRS->Release();
192
if (pszName != NULL) CPLFree(pszName);
195
OGRFeatureDefn *defineLayer(char *szName, OGRwkbGeometryType szType, S2I *poHeaders) {
196
OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn( szName );
197
poFeatureDefn->SetGeomType( szType );
199
for (unsigned int n=0; n<poHeaders->size(); n++) { /* adding headers in the correct order again */
200
for (S2I::iterator i=poHeaders->begin(); i!=poHeaders->end(); i++) {
202
OGRFieldDefn oFieldTemplate( i->first.c_str(), OFTString );
203
poFeatureDefn->AddFieldDefn( &oFieldTemplate );
207
return poFeatureDefn;
210
/************************************************************************/
212
/************************************************************************/
214
int OGRSOSIDataSource::Open( const char *pszFilename, int bUpdate ) {
215
papoBuiltGeometries = NULL;
221
CPLError( CE_Failure, CPLE_OpenFailed,
222
"Update access not supported by the SOSI driver." );
226
pszName = CPLStrdup( pszFilename );
227
/* We ignore any layer parameters for now. */
228
pszPos = strchr(pszName, ',');
229
if (pszPos != NULL) {
233
/* Confirm that we are dealing with a SOSI file. Used also by data
234
* format auto-detection in some ogr utilities. */
236
int bIsSosi = HO_TestSOSI ( pszName, &nEnd );
237
if ( bIsSosi == UT_FALSE ) {
238
return FALSE; /* No error message: This is used by file format auto-detection */
241
short nStatus = 0, nDetStatus = 0; /* immediate status, detailed status */
243
/* open index base and sosi file */
244
poBaseadm = LC_OpenBase(LC_BASE);
245
nStatus = LC_OpenSos(pszName, LC_BASE_FRAMGR, LC_GML_IDX, LC_INGEN_STATUS,
246
&poFileadm, &nDetStatus);
247
if ( nStatus == UT_FALSE ) {
248
char *pszErrorMessage;
249
LC_StrError(nDetStatus, &pszErrorMessage);
250
CPLError( CE_Failure, CPLE_OpenFailed,
251
"File %s could not be opened by SOSI Driver: %s", pszName, pszErrorMessage );
255
/* --------------------------------------------------------------------*
256
* Prefetch all the information needed to determine layers *
257
* and prebuild LineString features for later assembly. *
258
* --------------------------------------------------------------------*/
260
/* allocate room for one pointer per feature */
261
nNumFeatures = poFileadm->lAntGr;
262
void* mem = VSIMalloc2(nNumFeatures, sizeof(void*));
264
CPLError( CE_Failure, CPLE_OpenFailed,
265
"Memory allocation for SOSI features failed." );
268
papoBuiltGeometries = (OGRGeometry**)mem;
270
for (unsigned int i=0; i<nNumFeatures; i++) papoBuiltGeometries[i] = NULL;
272
/* Various iterators and return values used to iterate through SOSI features */
273
short nName, nNumLines;
275
unsigned short nInfo;
278
LC_BGR *poNextSerial;
279
poNextSerial =&oNextSerial;
281
bool bPointLayer = FALSE; /* Initialize four layers for the different geometry types */
282
bool bCurveLayer = FALSE;
283
bool bPolyLayer = FALSE;
284
bool bTextLayer = FALSE;
285
poPolyHeaders = new S2I();
286
poPointHeaders = new S2I();
287
poCurveHeaders = new S2I();
288
poTextHeaders = new S2I();
290
LC_SBSn(&oSnradm, poFileadm, 0, nNumFeatures); /* Set FYBA search limits */
291
LC_InitNextBgr(poNextSerial);
293
/* Prebuilding simple features and extracting layer information. */
294
while (LC_NextBgr(poNextSerial,LC_FRAMGR)) {
295
/* Fetch next group information */
296
nName = LC_RxGr(poNextSerial, LES_OPTIMALT, &nNumLines, &nNumCoo, &nInfo);
299
S2S::iterator iHeaders;
301
/* Extract all strings from group header. */
302
for (short i=1; i<=nNumLines; i++) {
303
char *pszLine = LC_GetGi(i); /* Get one header line */
304
if ((pszLine[0] == ':')||(pszLine[0] == '(')) continue; /* If we have a continued REF line, skip it. */
305
if (pszLine[0] == '!') continue; /* If we have a comment line, skip it. */
307
char *pszUTFLine = CPLRecode(pszLine, pszEncoding, CPL_ENC_UTF8); /* switch to UTF encoding here, if it is known. */
309
while (pszUTFLine[0] == '.') pszUTFLine++; /* Skipping the dots at the beginning of a SOSI line */
310
char *pszPos = strstr(pszUTFLine, " "); /* Split header and value */
311
if (pszPos != NULL) {
312
CPLString osKey = CPLString(std::string(pszUTFLine,pszPos)); /* FIXME: clean instantiation of CPLString? */
313
CPLString osValue = CPLString(pszPos+1);
315
oHeaders[osKey]=osValue; /* Add to header map */
316
switch (nName) { /* Add to header list for the corresponding layer, if it is not */
317
case L_FLATE: { /* in there already */
318
if (poPolyHeaders->find(osKey) == poPolyHeaders->end()) {
319
iH = poPolyHeaders->size();
320
(*poPolyHeaders)[osKey] = iH;
325
if (poCurveHeaders->find(osKey) == poCurveHeaders->end()) {
326
iH = poCurveHeaders->size();
327
(*poCurveHeaders)[osKey] = iH;
332
if (poPointHeaders->find(osKey) == poPointHeaders->end()) {
333
iH = poPointHeaders->size();
334
(*poPointHeaders)[osKey] = iH;
339
if (poTextHeaders->find(osKey) == poTextHeaders->end()) {
340
iH = poTextHeaders->size();
341
(*poTextHeaders)[osKey] = iH;
347
//CPLFree(pszUTFLine);
350
/* Feature-specific tasks */
353
/* Pre-build a point feature. Activate point layer. */
355
buildOGRPoint(oNextSerial.lNr);
359
/* Activate polygon layer. */
361
/* cannot build geometries that reference others yet */
365
/* Pre-build a line feature. Activate line/curve layer. */
367
buildOGRLineString(nNumCoo, oNextSerial.lNr);
371
/* Pre-build a text line contour feature. Activate text layer. */
372
/* Todo: observe only points 2ff if more than one point is given for follow mode */
374
buildOGRMultiPoint(nNumCoo, oNextSerial.lNr);
378
/* Get SRS from SOSI header. */
379
unsigned short nMask = LC_TR_ALLT;
381
if (LC_GetTransEx(&nMask,&oTrans) == UT_FALSE) {
382
CPLError( CE_Failure, CPLE_OpenFailed,
383
"TRANSPAR section not found - No reference system information available.");
386
poSRS = new OGRSpatialReference();
389
/* Get coordinate system from SOSI header. */
390
int nEPSG = sosi2epsg(oTrans.sKoordsys);
391
if (poSRS->importFromEPSG(nEPSG) != OGRERR_NONE) {
392
CPLError( CE_Failure, CPLE_OpenFailed,
393
"OGR could not load coordinate system definition EPSG:%i.", nEPSG);
397
/* Get character encoding from SOSI header. */
398
iHeaders = oHeaders.find("TEGNSETT");
399
if (iHeaders != oHeaders.end()) {
400
CPLString osLine = iHeaders->second;
401
if (osLine.compare("ISO8859-1")==0) {
402
pszEncoding = CPL_ENC_ISO8859_1;
403
} else if (osLine.compare("ISO8859-10")==0) {
404
pszEncoding = CPL_ENC_ISO8859_10;
405
} else if (osLine.compare("UTF-8")==0) {
406
pszEncoding = CPL_ENC_UTF8;
418
/* -------------------------------------------------------------------- *
419
* Create a corresponding layers. One per geometry type *
420
* -------------------------------------------------------------------- */
422
if (bPolyLayer) nLayers++;
423
if (bCurveLayer) nLayers++;
424
if (bPointLayer) nLayers++;
425
if (bTextLayer) nLayers++;
426
this->nLayers = nLayers;
427
/* allocate some memory for up to three layers */
428
papoLayers = (OGRSOSILayer **) VSIMalloc2(sizeof(void*), nLayers);
430
/* Define each layer, using a proper feature definition, geometry type,
431
* and adding every SOSI header encountered in the file as field. */
434
OGRFeatureDefn *poFeatureDefn = defineLayer("polygons", wkbPolygon, poPolyHeaders);
435
poFeatureDefn->Reference();
436
papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poPolyHeaders );
438
delete poPolyHeaders;
439
poPolyHeaders = NULL;
442
OGRFeatureDefn *poFeatureDefn = defineLayer("lines", wkbLineString, poCurveHeaders);
443
poFeatureDefn->Reference();
444
papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poCurveHeaders );
446
delete poCurveHeaders;
447
poCurveHeaders = NULL;
450
OGRFeatureDefn *poFeatureDefn = defineLayer("points", wkbPoint, poPointHeaders);
451
poFeatureDefn->Reference();
452
papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poPointHeaders );
454
delete poPointHeaders;
455
poPointHeaders = NULL;
458
OGRFeatureDefn *poFeatureDefn = defineLayer("text", wkbMultiPoint, poTextHeaders);
459
poFeatureDefn->Reference();
460
papoLayers[--nLayers] = new OGRSOSILayer( this, poFeatureDefn, poFileadm, poTextHeaders );
462
delete poTextHeaders;
463
poTextHeaders = NULL;
469
/************************************************************************/
471
/************************************************************************/
473
int OGRSOSIDataSource::Create( const char *pszFilename ) {
477
poBaseadm = LC_OpenBase(LC_KLADD);
478
nStatus = LC_OpenSos(pszFilename, LC_SEKV_SKRIV, LC_NY_IDX, LC_INGEN_STATUS,
479
&poFileadm, &nDetStatus);
480
if (nStatus == UT_FALSE) {
481
CPLError( CE_Failure, CPLE_OpenFailed,
482
"Could not open SOSI file for writing (Status %i).", nDetStatus );
486
LC_NyttHode(); /* Create new file header, will be written to file when all
487
header information elements are set. */
492
/************************************************************************/
494
/************************************************************************/
496
OGRLayer *OGRSOSIDataSource::CreateLayer( const char *pszName, OGRSpatialReference *poSpatialRef, OGRwkbGeometryType eGType, char **papszOptions ) {
497
/* SOSI does not really support layers - so let's first see that the global settings are consistent */
499
if (poSpatialRef!=NULL) {
500
poSRS = poSpatialRef;
503
const char *pszKoosys = poSRS->GetAuthorityCode("PROJCS");
504
if (pszKoosys == NULL) {
505
OGRErr err = poSRS->AutoIdentifyEPSG();
506
if (err == OGRERR_UNSUPPORTED_SRS) {
507
CPLError( CE_Failure, CPLE_OpenFailed,
508
"Could not identify EPSG code for spatial reference system");
511
pszKoosys = poSRS->GetAuthorityCode("PROJCS");
514
if (pszKoosys != NULL) {
515
int nKoosys = epsg2sosi(atoi(pszKoosys));
516
CPLDebug( "[CreateLayer]","Projection set to SOSI %i", nKoosys);
517
LC_PutTrans(nKoosys,0,0,0.01,0.01,0.01);
519
pszKoosys = poSRS->GetAuthorityCode("GEOGCS");
520
if (pszKoosys != NULL) {
521
int nKoosys = epsg2sosi(atoi(pszKoosys));
522
LC_PutTrans(nKoosys,0,0,0.01,0.01,0.01);
524
CPLError( CE_Failure, CPLE_OpenFailed,
525
"Could not retrieve EPSG code for spatial reference system");
530
LC_WsGr(poFileadm); /* Writing the header here! */
533
if (!poSRS->IsSame(poSpatialRef)) {
534
CPLError( CE_Failure, CPLE_AppDefined,
535
"SOSI driver does not support different spatial reference systems in one file.");
539
OGRFeatureDefn *poFeatureDefn = new OGRFeatureDefn( pszName );
540
poFeatureDefn->Reference();
541
poFeatureDefn->SetGeomType( eGType );
542
OGRSOSILayer *poLayer = new OGRSOSILayer( this, poFeatureDefn, poFileadm, NULL /*poHeaderDefn*/);
543
/* todo: where do we delete poLayer and poFeatureDefn? */
547
/************************************************************************/
549
/************************************************************************/
551
OGRLayer *OGRSOSIDataSource::GetLayer( int iLayer ) {
552
if ( iLayer < 0 || iLayer >= nLayers )
555
return papoLayers[iLayer];
558
void OGRSOSIDataSource::buildOGRMultiPoint(int nNumCoo, long iSerial) {
559
if (papoBuiltGeometries[iSerial] != NULL) {
563
OGRMultiPoint *poMP = new OGRMultiPoint();
566
double dfEast = 0, dfNorth = 0;
567
for (i=(nNumCoo>1)?2:1; i<=nNumCoo; i++) {
568
LC_GetTK(i, &dfEast, &dfNorth);
569
OGRPoint poP = OGRPoint(dfEast, dfNorth);
570
poMP->addGeometry(&poP); /*poP will be cloned before returning*/
572
papoBuiltGeometries[iSerial] = poMP;
575
void OGRSOSIDataSource::buildOGRLineString(int nNumCoo, long iSerial) {
576
if (papoBuiltGeometries[iSerial] != NULL) {
580
OGRLineString *poLS = new OGRLineString();
581
poLS->setNumPoints(nNumCoo);
584
double dfEast = 0, dfNorth = 0;
585
for (i=1; i<=nNumCoo; i++) {
586
LC_GetTK(i, &dfEast, &dfNorth);
587
poLS->setPoint(i-1, dfEast, dfNorth);
589
papoBuiltGeometries[iSerial] = poLS;
591
void OGRSOSIDataSource::buildOGRPoint(long iSerial) {
592
double dfEast = 0, dfNorth = 0;
593
LC_GetTK(1, &dfEast, &dfNorth);
594
papoBuiltGeometries[iSerial] = new OGRPoint(dfEast, dfNorth);
597
/************************************************************************/
598
/* TestCapability() */
599
/************************************************************************/
601
int OGRSOSIDataSource::TestCapability( const char * pszCap ) {
602
if (strcmp("CreateLayer",pszCap) == 0) {
605
CPLDebug( "[TestCapability]","Capability %s not supported by SOSI data source", pszCap);