/******************************************************************************
 *
 * Project:  SEG-P1 / UKOOA P1-90 Translator
 * Purpose:  Implements OGRUKOOAP190Layer class.
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
 *
 ******************************************************************************
 * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMSEGUKOOAS OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "ogr_segukooa.h"
#include "cpl_conv.h"
#include "cpl_string.h"
#include "ogr_p.h"
#include "ogr_srs_api.h"

CPL_CVSID("$Id: ogrsegukooalayer.cpp 355b41831cd2685c85d1aabe5b95665a2c6e99b7 2019-06-19 17:07:04 +0200 Even Rouault $")

/************************************************************************/
/*                            ExtractField()                            */
/************************************************************************/

static void ExtractField(char* szField, const char* pszLine, int nOffset, int nLen)
{
    memcpy(szField, pszLine + nOffset, nLen);
    szField[nLen] = '\0';
}

/************************************************************************/
/*                           GetNextFeature()                           */
/************************************************************************/

OGRFeature *OGRSEGUKOOABaseLayer::GetNextFeature()
{
    while( true )
    {
        OGRFeature *poFeature = GetNextRawFeature();
        if (poFeature == nullptr)
            return nullptr;

        if((m_poFilterGeom == nullptr
            || FilterGeometry( poFeature->GetGeometryRef() ) )
        && (m_poAttrQuery == nullptr
            || m_poAttrQuery->Evaluate( poFeature )) )
        {
            return poFeature;
        }
        else
            delete poFeature;
    }
}

/************************************************************************/
/*                         OGRUKOOAP190Layer()                          */
/************************************************************************/

namespace {
typedef struct
{
    const char*     pszName;
    OGRFieldType    eType;
} FieldDesc;
} /* end of anonymous namespace */

static const FieldDesc UKOOAP190Fields[] =
{
    { "LINENAME", OFTString },
    { "VESSEL_ID", OFTString },
    { "SOURCE_ID", OFTString },
    { "OTHER_ID", OFTString },
    { "POINTNUMBER", OFTInteger },
    { "LONGITUDE", OFTReal },
    { "LATITUDE", OFTReal },
    { "EASTING", OFTReal },
    { "NORTHING", OFTReal },
    { "DEPTH", OFTReal },
    { "DAYOFYEAR", OFTInteger },
    { "TIME", OFTTime },
    { "DATETIME", OFTDateTime }
};

constexpr int FIELD_LINENAME    = 0;
constexpr int FIELD_VESSEL_ID   = 1;
constexpr int FIELD_SOURCE_ID   = 2;
constexpr int FIELD_OTHER_ID    = 3;
// constexpr int FIELD_POINTNUMBER = 4;
constexpr int FIELD_LONGITUDE   = 5;
constexpr int FIELD_LATITUDE    = 6;
constexpr int FIELD_EASTING     = 7;
constexpr int FIELD_NORTHING    = 8;
constexpr int FIELD_DEPTH       = 9;
constexpr int FIELD_DAYOFYEAR   = 10;
constexpr int FIELD_TIME        = 11;
constexpr int FIELD_DATETIME    = 12;

OGRUKOOAP190Layer::OGRUKOOAP190Layer( const char* pszFilename,
                                      VSILFILE* fpIn ) :
    poSRS(nullptr),
    fp(fpIn),
    bUseEastingNorthingAsGeometry(CPLTestBool(
        CPLGetConfigOption("UKOOAP190_USE_EASTING_NORTHING", "NO"))),
    nYear(0)
{
    nNextFID = 0;
    bEOF = false;

    poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) );
    SetDescription( poFeatureDefn->GetName() );
    poFeatureDefn->Reference();
    poFeatureDefn->SetGeomType( wkbPoint );

    for( int i = 0;
         i < static_cast<int>(sizeof(UKOOAP190Fields) /
                              sizeof(UKOOAP190Fields[0]));
         i++ )
    {
        OGRFieldDefn    oField( UKOOAP190Fields[i].pszName,
                                UKOOAP190Fields[i].eType );
        poFeatureDefn->AddFieldDefn( &oField );
    }

    ParseHeaders();

    poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
}

/************************************************************************/
/*                         ~OGRUKOOAP190Layer()                         */
/************************************************************************/

OGRUKOOAP190Layer::~OGRUKOOAP190Layer()

{
    poFeatureDefn->Release();

    VSIFCloseL( fp );

    if (poSRS)
        poSRS->Release();
}

/************************************************************************/
/*                          ParseHeaders()                              */
/************************************************************************/

void OGRUKOOAP190Layer::ParseHeaders()
{
    while( true )
    {
        const char* pszLine = CPLReadLine2L(fp,81,nullptr);
        if (pszLine == nullptr || STARTS_WITH_CI(pszLine, "EOF"))
        {
            break;
        }

        int nLineLen = static_cast<int>(strlen(pszLine));
        while(nLineLen > 0 && pszLine[nLineLen-1] == ' ')
        {
            ((char*)pszLine)[nLineLen-1] = '\0';
            nLineLen --;
        }

        if (pszLine[0] != 'H')
            break;

        if (nLineLen < 33)
            continue;

        if (!bUseEastingNorthingAsGeometry &&
            STARTS_WITH(pszLine, "H1500") && poSRS == nullptr)
        {
            if (STARTS_WITH(pszLine + 33 - 1, "WGS84") ||
                STARTS_WITH(pszLine + 33 - 1, "WGS-84"))
            {
                poSRS = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
                poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
            }
            else if (STARTS_WITH(pszLine + 33 - 1, "WGS72"))
            {
                poSRS = new OGRSpatialReference();
                poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
                poSRS->SetFromUserInput("WGS72");
            }
        }
        else if (!bUseEastingNorthingAsGeometry &&
                 STARTS_WITH(pszLine, "H1501") && poSRS != nullptr &&
                 nLineLen >= 32 + 6 * 6 + 10)
        {
            char aszParams[6][6+1];
            char szZ[10+1];
            for( int i = 0; i < 6; i++ )
            {
                ExtractField(aszParams[i], pszLine, 33 - 1 + i * 6, 6);
            }
            ExtractField(szZ, pszLine, 33 - 1 + 6 * 6, 10);
            poSRS->SetTOWGS84(CPLAtof(aszParams[0]),
                              CPLAtof(aszParams[1]),
                              CPLAtof(aszParams[2]),
                              CPLAtof(aszParams[3]),
                              CPLAtof(aszParams[4]),
                              CPLAtof(aszParams[5]),
                              CPLAtof(szZ));
        }
        else if (STARTS_WITH(pszLine, "H0200"))
        {
            char** papszTokens = CSLTokenizeString(pszLine + 33 - 1);
            for(int i = 0; papszTokens[i] != nullptr; i++)
            {
                if (strlen(papszTokens[i]) == 4)
                {
                    int nVal = atoi(papszTokens[i]);
                    if (nVal >= 1900)
                    {
                        if( nYear != 0 && nYear != nVal )
                        {
                            CPLDebug("SEGUKOOA",
                                     "Several years found in H0200. Ignoring them!");
                            nYear = 0;
                            break;
                        }
                        nYear = nVal;
                    }
                }
            }
            CSLDestroy(papszTokens);
        }
    }
    VSIFSeekL( fp, 0, SEEK_SET );
}

/************************************************************************/
/*                            ResetReading()                            */
/************************************************************************/

void OGRUKOOAP190Layer::ResetReading()

{
    nNextFID = 0;
    bEOF = false;
    VSIFSeekL( fp, 0, SEEK_SET );
}

/************************************************************************/
/*                         GetNextRawFeature()                          */
/************************************************************************/

static bool isleap( int y)
{
    return
      (y % 4 == 0 &&
       y % 100 != 0)
      || y % 400 == 0;
}

OGRFeature *OGRUKOOAP190Layer::GetNextRawFeature()
{
    if( bEOF )
        return nullptr;

    while( true )
    {
        const char* pszLine = CPLReadLine2L(fp, 81, nullptr);
        if (pszLine == nullptr || STARTS_WITH_CI(pszLine, "EOF"))
        {
            bEOF = true;
            return nullptr;
        }

        int nLineLen = static_cast<int>(strlen(pszLine));
        while(nLineLen > 0 && pszLine[nLineLen-1] == ' ')
        {
            ((char*)pszLine)[nLineLen-1] = '\0';
            nLineLen --;
        }

        if (pszLine[0] == 'H' || nLineLen < 46)
            continue;

        OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
        poFeature->SetFID(nNextFID ++);

        char szLineName[12 + 1];
        ExtractField(szLineName, pszLine, 2-1, 12);
        int i = 11;
        while (i >= 0)
        {
            if (szLineName[i] == ' ')
                szLineName[i] = '\0';
            else
                break;
            i --;
        }
        poFeature->SetField(FIELD_LINENAME, szLineName);

        char szVesselId[1+1];
        szVesselId[0] = pszLine[17-1];
        if (szVesselId[0] != ' ')
        {
            szVesselId[1] = '\0';
            poFeature->SetField(FIELD_VESSEL_ID, szVesselId);
        }

        char szSourceId[1+1];
        szSourceId[0] = pszLine[18-1];
        if (szSourceId[0] != ' ')
        {
            szSourceId[1] = '\0';
            poFeature->SetField(FIELD_SOURCE_ID, szSourceId);
        }

        char szOtherId[1+1];
        szOtherId[0] = pszLine[19-1];
        if (szOtherId[0] != ' ')
        {
            szOtherId[1] = '\0';
            poFeature->SetField(FIELD_OTHER_ID, szOtherId);
        }

        char szPointNumber[6+1];
        ExtractField(szPointNumber, pszLine, 20-1, 6);
        poFeature->SetField(4, atoi(szPointNumber));

        char szDeg[3+1];
        char szMin[2+1];
        char szSec[5+1];

        ExtractField(szDeg, pszLine, 26-1, 2);
        ExtractField(szMin, pszLine, 26+2-1, 2);
        ExtractField(szSec, pszLine, 26+2+2-1, 5);
        double dfLat = atoi(szDeg) + atoi(szMin) / 60.0 + CPLAtof(szSec) / 3600.0;
        if (pszLine[26+2+2+5-1] == 'S')
            dfLat = -dfLat;
        poFeature->SetField(FIELD_LATITUDE, dfLat);

        ExtractField(szDeg, pszLine, 36-1, 3);
        ExtractField(szMin, pszLine, 36+3-1, 2);
        ExtractField(szSec, pszLine, 36+3+2-1, 5);
        double dfLon = atoi(szDeg) + atoi(szMin) / 60.0 + CPLAtof(szSec) / 3600.0;
        if (pszLine[36+3+2+5-1] == 'W')
            dfLon = -dfLon;
        poFeature->SetField(FIELD_LONGITUDE, dfLon);

        OGRGeometry* poGeom = nullptr;
        if (!bUseEastingNorthingAsGeometry)
            poGeom = new OGRPoint(dfLon, dfLat);

        if (nLineLen >= 64)
        {
            char szEasting[9+1];
            ExtractField(szEasting, pszLine, 47-1, 9);
            double dfEasting = CPLAtof(szEasting);
            poFeature->SetField(FIELD_EASTING, dfEasting);

            char szNorthing[9+1];
            ExtractField(szNorthing, pszLine, 56-1, 9);
            double dfNorthing = CPLAtof(szNorthing);
            poFeature->SetField(FIELD_NORTHING, dfNorthing);

            if (bUseEastingNorthingAsGeometry)
                poGeom = new OGRPoint(dfEasting, dfNorthing);
        }

        if (poGeom)
        {
            if (poSRS)
                poGeom->assignSpatialReference(poSRS);
            poFeature->SetGeometryDirectly(poGeom);
        }

        if (nLineLen >= 70)
        {
            char szDepth[6+1];
            ExtractField(szDepth, pszLine, 65-1, 6);
            double dfDepth = CPLAtof(szDepth);
            poFeature->SetField(FIELD_DEPTH, dfDepth);
        }

        int nDayOfYear = 0;
        if (nLineLen >= 73)
        {
            char szDayOfYear[3+1];
            ExtractField(szDayOfYear, pszLine, 71-1, 3);
            nDayOfYear = atoi(szDayOfYear);
            poFeature->SetField(FIELD_DAYOFYEAR, nDayOfYear);
        }

        if (nLineLen >= 79)
        {
            char szH[2+1], szM[2+1], szS[2+1];
            ExtractField(szH, pszLine, 74-1, 2);
            ExtractField(szM, pszLine, 74-1+2, 2);
            ExtractField(szS, pszLine, 74-1+2+2, 2);
            poFeature->SetField(FIELD_TIME, 0, 0, 0, atoi(szH), atoi(szM), static_cast<float>(atoi(szS)) );

            if( nYear != 0 )
            {
                constexpr int mon_lengths[2][12] = {
                    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
                    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
                } ;
                const bool bIsLeap = isleap(nYear);
                int nMonth = 0;
                int nDays = 0;
                if ((bIsLeap && nDayOfYear >= 1 && nDayOfYear <= 366) ||
                    (!bIsLeap && nDayOfYear >= 1 && nDayOfYear <= 365))
                {
                    const int leap_offset = bIsLeap ? 1 : 0;
                    while( nDayOfYear >
                           nDays +
                           mon_lengths[leap_offset][nMonth] )
                    {
                        nDays += mon_lengths[leap_offset][nMonth];
                        nMonth ++;
                    }
                    const int nDayOfMonth = nDayOfYear - nDays;
                    nMonth++;

                    poFeature->SetField(FIELD_DATETIME,
                                        nYear, nMonth, nDayOfMonth,
                                        atoi(szH), atoi(szM),
                                        static_cast<float>(atoi(szS)) );
                }
            }
        }

        return poFeature;
    }
}

/************************************************************************/
/*                           OGRSEGP1Layer()                            */
/************************************************************************/

static const FieldDesc SEGP1Fields[] =
{
    { "LINENAME", OFTString },
    { "POINTNUMBER", OFTInteger },
    { "RESHOOTCODE", OFTString },
    { "LONGITUDE", OFTReal },
    { "LATITUDE", OFTReal },
    { "EASTING", OFTReal },
    { "NORTHING", OFTReal },
    { "DEPTH", OFTReal },
#if 0
    { "YEAR", OFTInteger },
    { "DAYOFYEAR", OFTInteger },
    { "TIME", OFTTime },
    { "DATETIME", OFTDateTime }
#endif
};

constexpr int SEGP1_FIELD_LINENAME    = 0;
constexpr int SEGP1_FIELD_POINTNUMBER = 1;
constexpr int SEGP1_FIELD_RESHOOTCODE = 2;
constexpr int SEGP1_FIELD_LONGITUDE   = 3;
constexpr int SEGP1_FIELD_LATITUDE    = 4;
constexpr int SEGP1_FIELD_EASTING     = 5;
constexpr int SEGP1_FIELD_NORTHING    = 6;
constexpr int SEGP1_FIELD_DEPTH       = 7;
// constexpr int SEGP1_FIELD_DAYOFYEAR   = 8;
// constexpr int SEGP1_FIELD_TIME        = 9;
// constexpr int SEGP1_FIELD_DATETIME    = 10;

OGRSEGP1Layer::OGRSEGP1Layer( const char* pszFilename,
                              VSILFILE* fpIn,
                              int nLatitudeColIn ) :
    poSRS(nullptr),
    fp(fpIn),
    nLatitudeCol(nLatitudeColIn),
    bUseEastingNorthingAsGeometry(CPLTestBool(
        CPLGetConfigOption("SEGP1_USE_EASTING_NORTHING", "NO")))
{
    nNextFID = 0;
    bEOF = false;

    poFeatureDefn = new OGRFeatureDefn( CPLGetBasename(pszFilename) );
    SetDescription( poFeatureDefn->GetName() );
    poFeatureDefn->Reference();
    poFeatureDefn->SetGeomType( wkbPoint );

    for(int i=0;i<(int)(sizeof(SEGP1Fields)/sizeof(SEGP1Fields[0]));i++)
    {
        OGRFieldDefn    oField( SEGP1Fields[i].pszName,
                                SEGP1Fields[i].eType );
        poFeatureDefn->AddFieldDefn( &oField );
    }

    OGRSEGP1Layer::ResetReading();
}

/************************************************************************/
/*                            ~OGRSEGP1Layer()                          */
/************************************************************************/

OGRSEGP1Layer::~OGRSEGP1Layer()

{
    poFeatureDefn->Release();

    VSIFCloseL( fp );

    if (poSRS)
        poSRS->Release();
}

/************************************************************************/
/*                            ResetReading()                            */
/************************************************************************/

void OGRSEGP1Layer::ResetReading()

{
    nNextFID = 0;
    bEOF = false;
    VSIFSeekL( fp, 0, SEEK_SET );

    /* Skip first 20 header lines */
    const char* pszLine = nullptr;
    for(int i=0; i<20;i++)
    {
        pszLine = CPLReadLine2L(fp,81,nullptr);
        if (pszLine == nullptr)
        {
            bEOF = true;
            break;
        }
    }
}

/************************************************************************/
/*                         GetNextRawFeature()                          */
/************************************************************************/

OGRFeature *OGRSEGP1Layer::GetNextRawFeature()
{
    if( bEOF )
        return nullptr;

    const char* pszLine = nullptr;
    while( true )
    {
        pszLine = CPLReadLine2L(fp,81,nullptr);
        if (pszLine == nullptr || STARTS_WITH_CI(pszLine, "EOF"))
        {
            bEOF = true;
            return nullptr;
        }

        int nLineLen = static_cast<int>(strlen(pszLine));
        while(nLineLen > 0 && pszLine[nLineLen-1] == ' ')
        {
            ((char*)pszLine)[nLineLen-1] = '\0';
            nLineLen --;
        }

        char* pszExpandedLine = ExpandTabs(pszLine);
        pszLine = pszExpandedLine;
        nLineLen = static_cast<int>(strlen(pszLine));

        OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
        poFeature->SetFID(nNextFID ++);

        OGRGeometry* poGeom = nullptr;

        if (nLatitudeCol-1 + 19 <= nLineLen)
        {
            char szDeg[3+1];
            char szMin[2+1];
            char szSec[4+1];

            ExtractField(szDeg, pszLine, nLatitudeCol-1, 2);
            ExtractField(szMin, pszLine, nLatitudeCol+2-1, 2);
            ExtractField(szSec, pszLine, nLatitudeCol+2+2-1, 4);
            double dfLat = atoi(szDeg) + atoi(szMin) / 60.0 + atoi(szSec) / 100.0 / 3600.0;
            if (pszLine[nLatitudeCol+2+2+4-1] == 'S')
                dfLat = -dfLat;
            poFeature->SetField(SEGP1_FIELD_LATITUDE, dfLat);

            ExtractField(szDeg, pszLine, nLatitudeCol+9-1, 3);
            ExtractField(szMin, pszLine, nLatitudeCol+9+3-1, 2);
            ExtractField(szSec, pszLine, nLatitudeCol+9+3+2-1, 4);
            double dfLon = atoi(szDeg) + atoi(szMin) / 60.0 + atoi(szSec) / 100.0 / 3600.0;
            if (pszLine[nLatitudeCol+9+3+2+4-1] == 'W')
                dfLon = -dfLon;
            poFeature->SetField(SEGP1_FIELD_LONGITUDE, dfLon);

            if (!bUseEastingNorthingAsGeometry)
                poGeom = new OGRPoint(dfLon, dfLat);
        }

        /* Normal layout -> extract other fields */
        if (nLatitudeCol == 27 && nLineLen >= 26-1+1)
        {
            char szLineName[16 + 1];
            ExtractField(szLineName, pszLine, 2-1, 16);
            int i = 15;
            while (i >= 0)
            {
                if (szLineName[i] == ' ')
                    szLineName[i] = '\0';
                else
                    break;
                i --;
            }
            poFeature->SetField(SEGP1_FIELD_LINENAME, szLineName);

            char szPointNumber[8+1];
            ExtractField(szPointNumber, pszLine, 18-1, 8);
            poFeature->SetField(SEGP1_FIELD_POINTNUMBER, atoi(szPointNumber));

            char szReshootCode[1+1];
            ExtractField(szReshootCode, pszLine, 26-1, 1);
            poFeature->SetField(SEGP1_FIELD_RESHOOTCODE, szReshootCode);

            if (nLineLen >= 61)
            {
                char szEasting[8+1];
                ExtractField(szEasting, pszLine, 46-1, 8);
                double dfEasting = CPLAtof(szEasting);
                poFeature->SetField(SEGP1_FIELD_EASTING, dfEasting);

                char szNorthing[8+1];
                ExtractField(szNorthing, pszLine, 54-1, 8);
                double dfNorthing = CPLAtof(szNorthing);
                poFeature->SetField(SEGP1_FIELD_NORTHING, dfNorthing);

                if (bUseEastingNorthingAsGeometry)
                    poGeom = new OGRPoint(dfEasting, dfNorthing);
            }

            if (nLineLen >= 66)
            {
                char szDepth[5+1];
                ExtractField(szDepth, pszLine, 62-1, 5);
                double dfDepth = CPLAtof(szDepth);
                poFeature->SetField(SEGP1_FIELD_DEPTH, dfDepth);
            }
        }

        if (poGeom)
        {
            if (poSRS)
                poGeom->assignSpatialReference(poSRS);
            poFeature->SetGeometryDirectly(poGeom);
        }

        CPLFree(pszExpandedLine);

        return poFeature;
    }
}

/************************************************************************/
/*                           ExpandTabs()                               */
/************************************************************************/

char* OGRSEGP1Layer::ExpandTabs(const char* pszLine)
{
    char* pszExpandedLine = (char*)CPLMalloc(strlen(pszLine) * 8 + 1);
    int j = 0;
    for(int i=0; pszLine[i] != '\0'; i++)
    {
        /* A tab must be space-expanded to reach the next column number */
        /* which is a multiple of 8 */
        if (pszLine[i] == 9)
        {
            do
            {
                pszExpandedLine[j ++] = ' ';
            } while ((j % 8) != 0);
        }
        else
            pszExpandedLine[j ++] = pszLine[i];
    }
    pszExpandedLine[j] = '\0';

    return pszExpandedLine;
}

/************************************************************************/
/*                        DetectLatitudeColumn()                        */
/************************************************************************/

/* Some SEG-P1 files have unusual offsets for latitude/longitude, so */
/* we try our best to identify it even in case of non-standard layout */
/* Return non-0 if detection is successful (column number starts at 1) */

int OGRSEGP1Layer::DetectLatitudeColumn(const char* pszLine)
{
    int nLen = static_cast<int>(strlen(pszLine));
    if (nLen >= 45 && pszLine[0] == ' ' &&
        (pszLine[35-1] == 'N' || pszLine[35-1] == 'S') &&
        (pszLine[45-1] == 'E' || pszLine[45-1] == 'W'))
        return 27;

    for(int i=8;i<nLen-10;i++)
    {
        if ((pszLine[i] == 'N' || pszLine[i] == 'S') &&
            (pszLine[i+10] == 'E' || pszLine[i+10] == 'W'))
            return i - 8 + 1;
    }

    return 0;
}

/************************************************************************/
/*                        OGRSEGUKOOALineLayer()                        */
/************************************************************************/

OGRSEGUKOOALineLayer::OGRSEGUKOOALineLayer( const char* pszFilename,
                                            OGRLayer *poBaseLayerIn ) :
    poBaseLayer(poBaseLayerIn),
    poNextBaseFeature(nullptr)
{
    nNextFID = 0;
    bEOF = false;

    poFeatureDefn = new OGRFeatureDefn(
        CPLSPrintf("%s_lines",
                   CPLGetBasename(pszFilename)) );
    poFeatureDefn->Reference();
    poFeatureDefn->SetGeomType( wkbLineString );
    poFeatureDefn->GetGeomFieldDefn(0)->
        SetSpatialRef(poBaseLayer->GetSpatialRef());

    OGRFieldDefn oField( "LINENAME", OFTString );
    poFeatureDefn->AddFieldDefn( &oField );
}

/************************************************************************/
/*                       ~OGRSEGUKOOALineLayer()                        */
/************************************************************************/

OGRSEGUKOOALineLayer::~OGRSEGUKOOALineLayer()
{
    delete poNextBaseFeature;
    delete poBaseLayer;

    poFeatureDefn->Release();
}

/************************************************************************/
/*                            ResetReading()                            */
/************************************************************************/

void OGRSEGUKOOALineLayer::ResetReading()

{
    nNextFID = 0;
    bEOF = false;
    delete poNextBaseFeature;
    poNextBaseFeature = nullptr;
    poBaseLayer->ResetReading();
}

/************************************************************************/
/*                         GetNextRawFeature()                          */
/************************************************************************/

OGRFeature *OGRSEGUKOOALineLayer::GetNextRawFeature()
{
    if( bEOF )
        return nullptr;

    /* Merge points of base layer that have same value for attribute(0) */
    /* into a single linestring */

    OGRFeature* poFeature = nullptr;
    OGRLineString* poLS = nullptr;

    if (poNextBaseFeature == nullptr)
        poNextBaseFeature = poBaseLayer->GetNextFeature();

    while(poNextBaseFeature != nullptr)
    {
        if (poNextBaseFeature->IsFieldSetAndNotNull(0) &&
            poNextBaseFeature->GetFieldAsString(0)[0] != '\0')
        {
            if (poFeature != nullptr &&
                strcmp(poFeature->GetFieldAsString(0),
                    poNextBaseFeature->GetFieldAsString(0)) != 0)
            {
                poFeature->SetGeometryDirectly(poLS);
                return poFeature;
            }

            OGRGeometry* poGeom =
                poNextBaseFeature->GetGeometryRef();
            OGRPoint* poPoint = poGeom ? poGeom->toPoint(): nullptr;
            if (poPoint != nullptr)
            {
                if (poFeature == nullptr)
                {
                    poFeature = new OGRFeature(poFeatureDefn);
                    poFeature->SetFID(nNextFID ++);
                    poFeature->SetField(0,
                        poNextBaseFeature->GetFieldAsString(0));
                    poLS = new OGRLineString();
                    if (poBaseLayer->GetSpatialRef())
                        poLS->assignSpatialReference(
                                    poBaseLayer->GetSpatialRef());
                }

                poLS->addPoint(poPoint);
            }
        }

        delete poNextBaseFeature;
        poNextBaseFeature = poBaseLayer->GetNextFeature();
    }

    bEOF = true;
    if( poFeature )
        poFeature->SetGeometryDirectly(poLS);
    return poFeature;
}
