/******************************************************************************
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  The OGRSimpleCurve and OGRLineString geometry classes.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 1999, Frank Warmerdam
 * Copyright (c) 2008-2014, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * 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, DAMAGES 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_geometry.h"
#include "ogr_geos.h"
#include "ogr_p.h"

#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <limits>

CPL_CVSID("$Id: ogrlinestring.cpp b0a72128acb6b9509667ee4be06da1f28a010f42 2019-10-21 13:19:06 +0200 Even Rouault $")

namespace {

int DoubleToIntClamp(double dfValue) {
  if( CPLIsNan(dfValue) ) return 0;
  if( dfValue >= std::numeric_limits<int>::max() )
      return std::numeric_limits<int>::max();
  if( dfValue <= std::numeric_limits<int>::min() )
      return std::numeric_limits<int>::min();
  return static_cast<int>(dfValue);
}

}  // namespace

/************************************************************************/
/*                           OGRSimpleCurve()                           */
/************************************************************************/

/** Constructor */
OGRSimpleCurve::OGRSimpleCurve() :
    nPointCount(0),
    paoPoints(nullptr),
    padfZ(nullptr),
    padfM(nullptr)
{}

/************************************************************************/
/*                OGRSimpleCurve( const OGRSimpleCurve& )               */
/************************************************************************/

/**
 * \brief Copy constructor.
 *
 * Note: before GDAL 2.1, only the default implementation of the constructor
 * existed, which could be unsafe to use.
 *
 * @since GDAL 2.1
 */

OGRSimpleCurve::OGRSimpleCurve( const OGRSimpleCurve& other ) :
    OGRCurve(other),
    nPointCount(0),
    paoPoints(nullptr),
    padfZ(nullptr),
    padfM(nullptr)
{
    setPoints( other.nPointCount, other.paoPoints, other.padfZ, other.padfM );
}

/************************************************************************/
/*                          ~OGRSimpleCurve()                           */
/************************************************************************/

OGRSimpleCurve::~OGRSimpleCurve()

{
    CPLFree( paoPoints );
    CPLFree( padfZ );
    CPLFree( padfM );
}

/************************************************************************/
/*                       operator=( const OGRPoint& )                   */
/************************************************************************/

/**
 * \brief Assignment operator.
 *
 * Note: before GDAL 2.1, only the default implementation of the operator
 * existed, which could be unsafe to use.
 *
 * @since GDAL 2.1
 */

OGRSimpleCurve& OGRSimpleCurve::operator=( const OGRSimpleCurve& other )
{
    if( this == &other)
        return *this;

    OGRCurve::operator=( other );

    setPoints(other.nPointCount, other.paoPoints, other.padfZ, other.padfM);

    return *this;
}

/************************************************************************/
/*                            flattenTo2D()                             */
/************************************************************************/

void OGRSimpleCurve::flattenTo2D()

{
    Make2D();
    setMeasured(FALSE);
}

/************************************************************************/
/*                               clone()                                */
/************************************************************************/

OGRGeometry *OGRSimpleCurve::clone() const

{
    OGRSimpleCurve *poCurve =
        OGRGeometryFactory::createGeometry(getGeometryType())->toSimpleCurve();

    poCurve->assignSpatialReference( getSpatialReference() );
    poCurve->setPoints( nPointCount, paoPoints, padfZ, padfM );
    if( poCurve->getNumPoints() != nPointCount )
    {
        delete poCurve;
        return nullptr;
    }
    poCurve->flags = flags;

    return poCurve;
}

/************************************************************************/
/*                               empty()                                */
/************************************************************************/

void OGRSimpleCurve::empty()

{
    setNumPoints( 0 );
}

/************************************************************************/
/*                       setCoordinateDimension()                       */
/************************************************************************/

void OGRSimpleCurve::setCoordinateDimension( int nNewDimension )

{
    if( nNewDimension == 2 )
        Make2D();
    else if( nNewDimension == 3 )
        Make3D();
    setMeasured(FALSE);
}

void OGRSimpleCurve::set3D( OGRBoolean bIs3D )

{
    if( bIs3D )
        Make3D();
    else
        Make2D();
}

void OGRSimpleCurve::setMeasured( OGRBoolean bIsMeasured )

{
    if( bIsMeasured )
        AddM();
    else
        RemoveM();
}

/************************************************************************/
/*                              WkbSize()                               */
/*                                                                      */
/*      Return the size of this object in well known binary             */
/*      representation including the byte order, and type information.  */
/************************************************************************/

int OGRSimpleCurve::WkbSize() const

{
    return 5 + 4 + 8 * nPointCount * CoordinateDimension();
}

//! @cond Doxygen_Suppress

/************************************************************************/
/*                               Make2D()                               */
/************************************************************************/

void OGRSimpleCurve::Make2D()

{
    if( padfZ != nullptr )
    {
        CPLFree( padfZ );
        padfZ = nullptr;
    }
    flags &= ~OGR_G_3D;
}

/************************************************************************/
/*                               Make3D()                               */
/************************************************************************/

void OGRSimpleCurve::Make3D()

{
    if( padfZ == nullptr )
    {
        if( nPointCount == 0 )
            padfZ =
                static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), 1));
        else
            padfZ = static_cast<double *>(VSI_CALLOC_VERBOSE(
                sizeof(double), nPointCount));
        if( padfZ == nullptr )
        {
            flags &= ~OGR_G_3D;
            CPLError(CE_Failure, CPLE_AppDefined,
                     "OGRSimpleCurve::Make3D() failed");
            return;
        }
    }
    flags |= OGR_G_3D;
}

/************************************************************************/
/*                               RemoveM()                              */
/************************************************************************/

void OGRSimpleCurve::RemoveM()

{
    if( padfM != nullptr )
    {
        CPLFree( padfM );
        padfM = nullptr;
    }
    flags &= ~OGR_G_MEASURED;
}

/************************************************************************/
/*                               AddM()                                 */
/************************************************************************/

void OGRSimpleCurve::AddM()

{
    if( padfM == nullptr )
    {
        if( nPointCount == 0 )
            padfM =
                static_cast<double *>(VSI_CALLOC_VERBOSE(sizeof(double), 1));
        else
            padfM = static_cast<double *>(
                VSI_CALLOC_VERBOSE(sizeof(double), nPointCount));
        if( padfM == nullptr )
        {
            flags &= ~OGR_G_MEASURED;
            CPLError(CE_Failure, CPLE_AppDefined,
                     "OGRSimpleCurve::AddM() failed");
            return;
        }
    }
    flags |= OGR_G_MEASURED;
}

//! @endcond

/************************************************************************/
/*                              getPoint()                              */
/************************************************************************/

/**
 * \brief Fetch a point in line string.
 *
 * This method relates to the SFCOM ILineString::get_Point() method.
 *
 * @param i the vertex to fetch, from 0 to getNumPoints()-1.
 * @param poPoint a point to initialize with the fetched point.
 */

void OGRSimpleCurve::getPoint( int i, OGRPoint * poPoint ) const

{
    CPLAssert( i >= 0 );
    CPLAssert( i < nPointCount );
    CPLAssert( poPoint != nullptr );

    poPoint->setX( paoPoints[i].x );
    poPoint->setY( paoPoints[i].y );

    if( (flags & OGR_G_3D) && padfZ != nullptr )
        poPoint->setZ( padfZ[i] );
    if( (flags & OGR_G_MEASURED) && padfM != nullptr )
        poPoint->setM( padfM[i] );
}

/**
 * \fn int OGRSimpleCurve::getNumPoints() const;
 *
 * \brief Fetch vertex count.
 *
 * Returns the number of vertices in the line string.
 *
 * @return vertex count.
 */

/**
 * \fn double OGRSimpleCurve::getX( int iVertex ) const;
 *
 * \brief Get X at vertex.
 *
 * Returns the X value at the indicated vertex.   If iVertex is out of range a
 * crash may occur, no internal range checking is performed.
 *
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
 *
 * @return X value.
 */

/**
 * \fn double OGRSimpleCurve::getY( int iVertex ) const;
 *
 * \brief Get Y at vertex.
 *
 * Returns the Y value at the indicated vertex.   If iVertex is out of range a
 * crash may occur, no internal range checking is performed.
 *
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
 *
 * @return X value.
 */

/************************************************************************/
/*                                getZ()                                */
/************************************************************************/

/**
 * \brief Get Z at vertex.
 *
 * Returns the Z (elevation) value at the indicated vertex.  If no Z
 * value is available, 0.0 is returned.  If iVertex is out of range a
 * crash may occur, no internal range checking is performed.
 *
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
 *
 * @return Z value.
 */

double OGRSimpleCurve::getZ( int iVertex ) const

{
    if( padfZ != nullptr && iVertex >= 0 && iVertex < nPointCount
        && (flags & OGR_G_3D) )
        return( padfZ[iVertex] );
    else
        return 0.0;
}

/************************************************************************/
/*                                getM()                                */
/************************************************************************/

/**
 * \brief Get measure at vertex.
 *
 * Returns the M (measure) value at the indicated vertex.  If no M
 * value is available, 0.0 is returned.  If iVertex is out of range a
 * crash may occur, no internal range checking is performed.
 *
 * @param iVertex the vertex to return, between 0 and getNumPoints()-1.
 *
 * @return M value.
 */

double OGRSimpleCurve::getM( int iVertex ) const

{
    if( padfM != nullptr && iVertex >= 0 && iVertex < nPointCount
        && (flags & OGR_G_MEASURED) )
        return( padfM[iVertex] );
    else
        return 0.0;
}

/************************************************************************/
/*                            setNumPoints()                            */
/************************************************************************/

/**
 * \brief Set number of points in geometry.
 *
 * This method primary exists to preset the number of points in a linestring
 * geometry before setPoint() is used to assign them to avoid reallocating
 * the array larger with each call to addPoint().
 *
 * This method has no SFCOM analog.
 *
 * @param nNewPointCount the new number of points for geometry.
 * @param bZeroizeNewContent whether to set to zero the new elements of arrays
 *                           that are extended.
 */

void OGRSimpleCurve::setNumPoints( int nNewPointCount, int bZeroizeNewContent )

{
    CPLAssert( nNewPointCount >= 0 );

    if( nNewPointCount == 0 )
    {
        CPLFree( paoPoints );
        paoPoints = nullptr;

        CPLFree( padfZ );
        padfZ = nullptr;

        CPLFree( padfM );
        padfM = nullptr;

        nPointCount = 0;
        return;
    }

    if( nNewPointCount > nPointCount )
    {
        // Overflow of sizeof(OGRRawPoint) * nNewPointCount can only occur on
        // 32 bit, but we don't really want to allocate 2 billion points even on
        // 64 bit...
        if( nNewPointCount > std::numeric_limits<int>::max() /
                                    static_cast<int>(sizeof(OGRRawPoint)) )
        {
            CPLError(CE_Failure, CPLE_IllegalArg, "Too big point count.");
            return;
        }
        OGRRawPoint* paoNewPoints = static_cast<OGRRawPoint *>(
            VSI_REALLOC_VERBOSE(paoPoints,
                                sizeof(OGRRawPoint) * nNewPointCount));
        if( paoNewPoints == nullptr )
        {
            return;
        }
        paoPoints = paoNewPoints;

        if( bZeroizeNewContent )
        {
            // gcc 8.0 (dev) complains about -Wclass-memaccess since
            // OGRRawPoint() has a constructor. So use a void* pointer.  Doing
            // the memset() here is correct since the constructor sets to 0.  We
            // could instead use a std::fill(), but at every other place, we
            // treat this class as a regular POD (see above use of realloc())
            void* dest = static_cast<void*>(paoPoints + nPointCount);
            memset( dest,
                    0, sizeof(OGRRawPoint) * (nNewPointCount - nPointCount) );
        }

        if( flags & OGR_G_3D )
        {
            double* padfNewZ = static_cast<double *>(
                VSI_REALLOC_VERBOSE(padfZ, sizeof(double) * nNewPointCount));
            if( padfNewZ == nullptr )
            {
                return;
            }
            padfZ = padfNewZ;
            if( bZeroizeNewContent )
                memset( padfZ + nPointCount, 0,
                    sizeof(double) * (nNewPointCount - nPointCount) );
        }

        if( flags & OGR_G_MEASURED )
        {
            double* padfNewM = static_cast<double *>(
                VSI_REALLOC_VERBOSE(padfM, sizeof(double) * nNewPointCount));
            if( padfNewM == nullptr )
            {
                return;
            }
            padfM = padfNewM;
            if( bZeroizeNewContent )
                memset( padfM + nPointCount, 0,
                    sizeof(double) * (nNewPointCount - nPointCount) );
        }
    }

    nPointCount = nNewPointCount;
}

/************************************************************************/
/*                              setPoint()                              */
/************************************************************************/

/**
 * \brief Set the location of a vertex in line string.
 *
 * If iPoint is larger than the number of necessary the number of existing
 * points in the line string, the point count will be increased to
 * accommodate the request.
 *
 * There is no SFCOM analog to this method.
 *
 * @param iPoint the index of the vertex to assign (zero based).
 * @param poPoint the value to assign to the vertex.
 */

void OGRSimpleCurve::setPoint( int iPoint, OGRPoint * poPoint )

{
    if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
        setPoint( iPoint, poPoint->getX(), poPoint->getY(), poPoint->getZ(), poPoint->getM() );
    else if( flags & OGR_G_3D )
        setPoint( iPoint, poPoint->getX(), poPoint->getY(), poPoint->getZ() );
    else if( flags & OGR_G_MEASURED )
        setPointM( iPoint, poPoint->getX(), poPoint->getY(), poPoint->getM() );
    else
        setPoint( iPoint, poPoint->getX(), poPoint->getY() );
}

/************************************************************************/
/*                              setPoint()                              */
/************************************************************************/

/**
 * \brief Set the location of a vertex in line string.
 *
 * If iPoint is larger than the number of necessary the number of existing
 * points in the line string, the point count will be increased to
 * accommodate the request.
 *
 * There is no SFCOM analog to this method.
 *
 * @param iPoint the index of the vertex to assign (zero based).
 * @param xIn input X coordinate to assign.
 * @param yIn input Y coordinate to assign.
 * @param zIn input Z coordinate to assign (defaults to zero).
 */

void OGRSimpleCurve::setPoint( int iPoint, double xIn, double yIn, double zIn )

{
    if( !(flags & OGR_G_3D) )
        Make3D();

    if( iPoint >= nPointCount )
    {
        setNumPoints( iPoint+1 );
        if( nPointCount < iPoint + 1 )
            return;
    }
#ifdef DEBUG
    if( paoPoints == nullptr )
        return;
#endif

    paoPoints[iPoint].x = xIn;
    paoPoints[iPoint].y = yIn;

    if( padfZ != nullptr )
    {
        padfZ[iPoint] = zIn;
    }
}

/**
 * \brief Set the location of a vertex in line string.
 *
 * If iPoint is larger than the number of necessary the number of existing
 * points in the line string, the point count will be increased to
 * accommodate the request.
 *
 * There is no SFCOM analog to this method.
 *
 * @param iPoint the index of the vertex to assign (zero based).
 * @param xIn input X coordinate to assign.
 * @param yIn input Y coordinate to assign.
 * @param mIn input M coordinate to assign (defaults to zero).
 */

void OGRSimpleCurve::setPointM( int iPoint, double xIn, double yIn, double mIn )

{
    if( !(flags & OGR_G_MEASURED) )
        AddM();

    if( iPoint >= nPointCount )
    {
        setNumPoints( iPoint+1 );
        if( nPointCount < iPoint + 1 )
            return;
    }
#ifdef DEBUG
    if( paoPoints == nullptr )
        return;
#endif

    paoPoints[iPoint].x = xIn;
    paoPoints[iPoint].y = yIn;

    if( padfM != nullptr )
    {
        padfM[iPoint] = mIn;
    }
}

/**
 * \brief Set the location of a vertex in line string.
 *
 * If iPoint is larger than the number of necessary the number of existing
 * points in the line string, the point count will be increased to
 * accommodate the request.
 *
 * There is no SFCOM analog to this method.
 *
 * @param iPoint the index of the vertex to assign (zero based).
 * @param xIn input X coordinate to assign.
 * @param yIn input Y coordinate to assign.
 * @param zIn input Z coordinate to assign (defaults to zero).
 * @param mIn input M coordinate to assign (defaults to zero).
 */

void OGRSimpleCurve::setPoint( int iPoint, double xIn, double yIn,
                               double zIn, double mIn )

{
    if( !(flags & OGR_G_3D) )
        Make3D();
    if( !(flags & OGR_G_MEASURED) )
        AddM();

    if( iPoint >= nPointCount )
    {
        setNumPoints( iPoint+1 );
        if( nPointCount < iPoint + 1 )
            return;
    }
#ifdef DEBUG
    if( paoPoints == nullptr )
        return;
#endif

    paoPoints[iPoint].x = xIn;
    paoPoints[iPoint].y = yIn;

    if( padfZ != nullptr )
    {
        padfZ[iPoint] = zIn;
    }
    if( padfM != nullptr )
    {
        padfM[iPoint] = mIn;
    }
}

/**
 * \brief Set the location of a vertex in line string.
 *
 * If iPoint is larger than the number of necessary the number of existing
 * points in the line string, the point count will be increased to
 * accommodate the request.
 *
 * There is no SFCOM analog to this method.
 *
 * @param iPoint the index of the vertex to assign (zero based).
 * @param xIn input X coordinate to assign.
 * @param yIn input Y coordinate to assign.
 */

void OGRSimpleCurve::setPoint( int iPoint, double xIn, double yIn )

{
    if( iPoint >= nPointCount )
    {
        setNumPoints( iPoint+1 );
        if( nPointCount < iPoint + 1 || paoPoints == nullptr )
            return;
    }

    paoPoints[iPoint].x = xIn;
    paoPoints[iPoint].y = yIn;
}

/************************************************************************/
/*                                setZ()                                */
/************************************************************************/

/**
 * \brief Set the Z of a vertex in line string.
 *
 * If iPoint is larger than the number of necessary the number of existing
 * points in the line string, the point count will be increased to
 * accommodate the request.
 *
 * There is no SFCOM analog to this method.
 *
 * @param iPoint the index of the vertex to assign (zero based).
 * @param zIn input Z coordinate to assign.
 */

void OGRSimpleCurve::setZ( int iPoint, double zIn )
{
    if( getCoordinateDimension() == 2 )
        Make3D();

    if( iPoint >= nPointCount )
    {
        setNumPoints( iPoint+1 );
        if( nPointCount < iPoint + 1 )
            return;
    }

    if( padfZ != nullptr )
        padfZ[iPoint] = zIn;
}

/************************************************************************/
/*                                setM()                                */
/************************************************************************/

/**
 * \brief Set the M of a vertex in line string.
 *
 * If iPoint is larger than the number of necessary the number of existing
 * points in the line string, the point count will be increased to
 * accommodate the request.
 *
 * There is no SFCOM analog to this method.
 *
 * @param iPoint the index of the vertex to assign (zero based).
 * @param mIn input M coordinate to assign.
 */

void OGRSimpleCurve::setM( int iPoint, double mIn )
{
    if( !(flags & OGR_G_MEASURED) )
        AddM();

    if( iPoint >= nPointCount )
    {
        setNumPoints( iPoint+1 );
        if( nPointCount < iPoint + 1 )
            return;
    }

    if( padfM != nullptr )
        padfM[iPoint] = mIn;
}

/************************************************************************/
/*                              addPoint()                              */
/************************************************************************/

/**
 * \brief Add a point to a line string.
 *
 * The vertex count of the line string is increased by one, and assigned from
 * the passed location value.
 *
 * There is no SFCOM analog to this method.
 *
 * @param poPoint the point to assign to the new vertex.
 */

void OGRSimpleCurve::addPoint( const OGRPoint * poPoint )

{
    if( poPoint->Is3D() && poPoint->IsMeasured() )
        setPoint( nPointCount, poPoint->getX(), poPoint->getY(), poPoint->getZ(), poPoint->getM() );
    else if( poPoint->Is3D() )
        setPoint( nPointCount, poPoint->getX(), poPoint->getY(), poPoint->getZ() );
    else if( poPoint->IsMeasured() )
        setPointM( nPointCount, poPoint->getX(), poPoint->getY(), poPoint->getM() );
    else
        setPoint( nPointCount, poPoint->getX(), poPoint->getY() );
}

/************************************************************************/
/*                              addPoint()                              */
/************************************************************************/

/**
 * \brief Add a point to a line string.
 *
 * The vertex count of the line string is increased by one, and assigned from
 * the passed location value.
 *
 * There is no SFCOM analog to this method.
 *
 * @param x the X coordinate to assign to the new point.
 * @param y the Y coordinate to assign to the new point.
 * @param z the Z coordinate to assign to the new point (defaults to zero).
 * @param m the M coordinate to assign to the new point (defaults to zero).
 */

void OGRSimpleCurve::addPoint( double x, double y, double z, double m )

{
    setPoint( nPointCount, x, y, z, m );
}

/**
 * \brief Add a point to a line string.
 *
 * The vertex count of the line string is increased by one, and assigned from
 * the passed location value.
 *
 * There is no SFCOM analog to this method.
 *
 * @param x the X coordinate to assign to the new point.
 * @param y the Y coordinate to assign to the new point.
 * @param z the Z coordinate to assign to the new point (defaults to zero).
 */

void OGRSimpleCurve::addPoint( double x, double y, double z )

{
    setPoint( nPointCount, x, y, z );
}

/**
 * \brief Add a point to a line string.
 *
 * The vertex count of the line string is increased by one, and assigned from
 * the passed location value.
 *
 * There is no SFCOM analog to this method.
 *
 * @param x the X coordinate to assign to the new point.
 * @param y the Y coordinate to assign to the new point.
 */

void OGRSimpleCurve::addPoint( double x, double y )

{
    setPoint( nPointCount, x, y );
}

/**
 * \brief Add a point to a line string.
 *
 * The vertex count of the line string is increased by one, and assigned from
 * the passed location value.
 *
 * There is no SFCOM analog to this method.
 *
 * @param x the X coordinate to assign to the new point.
 * @param y the Y coordinate to assign to the new point.
 * @param m the M coordinate to assign to the new point.
 */

void OGRSimpleCurve::addPointM( double x, double y, double m )

{
    setPointM( nPointCount, x, y, m );
}

/************************************************************************/
/*                             setPointsM()                             */
/************************************************************************/

/**
 * \brief Assign all points in a line string.
 *
 * This method clears any existing points assigned to this line string,
 * and assigns a whole new set.  It is the most efficient way of assigning
 * the value of a line string.
 *
 * There is no SFCOM analog to this method.
 *
 * @param nPointsIn number of points being passed in paoPointsIn
 * @param paoPointsIn list of points being assigned.
 * @param padfMIn the M values that go with the points.
 */

void OGRSimpleCurve::setPointsM( int nPointsIn,
                                 const OGRRawPoint * paoPointsIn,
                                 const double * padfMIn )

{
    setNumPoints( nPointsIn, FALSE );
    if( nPointCount < nPointsIn
#ifdef DEBUG
        || paoPoints == nullptr
#endif
        )
        return;

    if( nPointsIn )
        memcpy( paoPoints, paoPointsIn, sizeof(OGRRawPoint) * nPointsIn);

/* -------------------------------------------------------------------- */
/*      Check measures.                                                 */
/* -------------------------------------------------------------------- */
    if( padfMIn == nullptr && (flags & OGR_G_MEASURED) )
    {
        RemoveM();
    }
    else if( padfMIn )
    {
        AddM();
        if( padfM && nPointsIn )
            memcpy( padfM, padfMIn, sizeof(double) * nPointsIn );
    }
}

/************************************************************************/
/*                             setPoints()                              */
/************************************************************************/

/**
 * \brief Assign all points in a line string.
 *
 * This method clears any existing points assigned to this line string,
 * and assigns a whole new set.  It is the most efficient way of assigning
 * the value of a line string.
 *
 * There is no SFCOM analog to this method.
 *
 * @param nPointsIn number of points being passed in paoPointsIn
 * @param paoPointsIn list of points being assigned.
 * @param padfZIn the Z values that go with the points.
 * @param padfMIn the M values that go with the points.
 */

void OGRSimpleCurve::setPoints( int nPointsIn,
                                const OGRRawPoint * paoPointsIn,
                                const double * padfZIn,
                                const double * padfMIn )

{
    setNumPoints( nPointsIn, FALSE );
    if( nPointCount < nPointsIn
#ifdef DEBUG
        || paoPoints == nullptr
#endif
        )
        return;

    if( nPointsIn )
        memcpy( paoPoints, paoPointsIn, sizeof(OGRRawPoint) * nPointsIn);

/* -------------------------------------------------------------------- */
/*      Check 2D/3D.                                                    */
/* -------------------------------------------------------------------- */
    if( padfZIn == nullptr && getCoordinateDimension() > 2 )
    {
        Make2D();
    }
    else if( padfZIn )
    {
        Make3D();
        if( padfZ && nPointsIn )
            memcpy( padfZ, padfZIn, sizeof(double) * nPointsIn );
    }

/* -------------------------------------------------------------------- */
/*      Check measures.                                                 */
/* -------------------------------------------------------------------- */
    if( padfMIn == nullptr && (flags & OGR_G_MEASURED) )
    {
        RemoveM();
    }
    else if( padfMIn )
    {
        AddM();
        if( padfM && nPointsIn )
            memcpy( padfM, padfMIn, sizeof(double) * nPointsIn );
    }
}

/************************************************************************/
/*                             setPoints()                              */
/************************************************************************/

/**
 * \brief Assign all points in a line string.
 *
 * This method clears any existing points assigned to this line string,
 * and assigns a whole new set.  It is the most efficient way of assigning
 * the value of a line string.
 *
 * There is no SFCOM analog to this method.
 *
 * @param nPointsIn number of points being passed in paoPointsIn
 * @param paoPointsIn list of points being assigned.
 * @param padfZIn the Z values that go with the points (optional, may be NULL).
 */

void OGRSimpleCurve::setPoints( int nPointsIn,
                                const OGRRawPoint * paoPointsIn,
                                const double * padfZIn )

{
    setNumPoints( nPointsIn, FALSE );
    if( nPointCount < nPointsIn
#ifdef DEBUG
        || paoPoints == nullptr
#endif
        )
        return;

    if( nPointsIn )
        memcpy( paoPoints, paoPointsIn, sizeof(OGRRawPoint) * nPointsIn);

/* -------------------------------------------------------------------- */
/*      Check 2D/3D.                                                    */
/* -------------------------------------------------------------------- */
    if( padfZIn == nullptr && getCoordinateDimension() > 2 )
    {
        Make2D();
    }
    else if( padfZIn )
    {
        Make3D();
        if( padfZ && nPointsIn )
            memcpy( padfZ, padfZIn, sizeof(double) * nPointsIn );
    }
}

/************************************************************************/
/*                             setPoints()                              */
/************************************************************************/

/**
 * \brief Assign all points in a line string.
 *
 * This method clear any existing points assigned to this line string,
 * and assigns a whole new set.
 *
 * There is no SFCOM analog to this method.
 *
 * @param nPointsIn number of points being passed in padfX and padfY.
 * @param padfX list of X coordinates of points being assigned.
 * @param padfY list of Y coordinates of points being assigned.
 * @param padfZIn list of Z coordinates of points being assigned (defaults to
 * NULL for 2D objects).
 */

void OGRSimpleCurve::setPoints( int nPointsIn,
                                const double * padfX,
                                const double * padfY,
                                const double * padfZIn )

{
/* -------------------------------------------------------------------- */
/*      Check 2D/3D.                                                    */
/* -------------------------------------------------------------------- */
    if( padfZIn == nullptr )
        Make2D();
    else
        Make3D();

/* -------------------------------------------------------------------- */
/*      Assign values.                                                  */
/* -------------------------------------------------------------------- */
    setNumPoints( nPointsIn, FALSE );
    if( nPointCount < nPointsIn )
        return;

    for( int i = 0; i < nPointsIn; i++ )
    {
        paoPoints[i].x = padfX[i];
        paoPoints[i].y = padfY[i];
    }

    if( padfZ == nullptr || !padfZIn || !nPointsIn )
    {
        return;
    }

    memcpy( padfZ, padfZIn, sizeof(double) * nPointsIn );
}

/************************************************************************/
/*                             setPointsM()                             */
/************************************************************************/

/**
 * \brief Assign all points in a line string.
 *
 * This method clear any existing points assigned to this line string,
 * and assigns a whole new set.
 *
 * There is no SFCOM analog to this method.
 *
 * @param nPointsIn number of points being passed in padfX and padfY.
 * @param padfX list of X coordinates of points being assigned.
 * @param padfY list of Y coordinates of points being assigned.
 * @param padfMIn list of M coordinates of points being assigned.
 */

void OGRSimpleCurve::setPointsM( int nPointsIn,
                                 const double * padfX,
                                 const double * padfY,
                                 const double * padfMIn )

{
/* -------------------------------------------------------------------- */
/*      Check 2D/3D.                                                    */
/* -------------------------------------------------------------------- */
    if( padfMIn == nullptr )
        RemoveM();
    else
        AddM();

/* -------------------------------------------------------------------- */
/*      Assign values.                                                  */
/* -------------------------------------------------------------------- */
    setNumPoints( nPointsIn, FALSE );
    if( nPointCount < nPointsIn )
        return;

    for( int i = 0; i < nPointsIn; i++ )
    {
        paoPoints[i].x = padfX[i];
        paoPoints[i].y = padfY[i];
    }

    if( padfMIn == nullptr || !padfM || !nPointsIn )
    {
        return;
    }

    memcpy( padfM, padfMIn, sizeof(double) * nPointsIn );
}

/************************************************************************/
/*                             setPoints()                              */
/************************************************************************/

/**
 * \brief Assign all points in a line string.
 *
 * This method clear any existing points assigned to this line string,
 * and assigns a whole new set.
 *
 * There is no SFCOM analog to this method.
 *
 * @param nPointsIn number of points being passed in padfX and padfY.
 * @param padfX list of X coordinates of points being assigned.
 * @param padfY list of Y coordinates of points being assigned.
 * @param padfZIn list of Z coordinates of points being assigned.
 * @param padfMIn list of M coordinates of points being assigned.
 */

void OGRSimpleCurve::setPoints( int nPointsIn,
                                const double * padfX,
                                const double * padfY,
                                const double * padfZIn,
                                const double * padfMIn )

{
/* -------------------------------------------------------------------- */
/*      Check 2D/3D.                                                    */
/* -------------------------------------------------------------------- */
    if( padfZIn == nullptr )
        Make2D();
    else
        Make3D();

/* -------------------------------------------------------------------- */
/*      Check measures.                                                 */
/* -------------------------------------------------------------------- */
    if( padfMIn == nullptr )
        RemoveM();
    else
        AddM();

/* -------------------------------------------------------------------- */
/*      Assign values.                                                  */
/* -------------------------------------------------------------------- */
    setNumPoints( nPointsIn, FALSE );
    if( nPointCount < nPointsIn )
        return;

    for( int i = 0; i < nPointsIn; i++ )
    {
        paoPoints[i].x = padfX[i];
        paoPoints[i].y = padfY[i];
    }

    if( padfZ != nullptr && padfZIn && nPointsIn )
        memcpy( padfZ, padfZIn, sizeof(double) * nPointsIn );
    if( padfM != nullptr && padfMIn && nPointsIn )
        memcpy( padfM, padfMIn, sizeof(double) * nPointsIn );
}

/************************************************************************/
/*                          getPoints()                                 */
/************************************************************************/

/**
 * \brief Returns all points of line string.
 *
 * This method copies all points into user list. This list must be at
 * least sizeof(OGRRawPoint) * OGRGeometry::getNumPoints() byte in size.
 * It also copies all Z coordinates.
 *
 * There is no SFCOM analog to this method.
 *
 * @param paoPointsOut a buffer into which the points is written.
 * @param padfZOut the Z values that go with the points (optional, may be NULL).
 */

void OGRSimpleCurve::getPoints( OGRRawPoint * paoPointsOut,
                                double * padfZOut ) const
{
    if( !paoPointsOut || nPointCount == 0 )
        return;

    memcpy( paoPointsOut, paoPoints, sizeof(OGRRawPoint) * nPointCount );

/* -------------------------------------------------------------------- */
/*      Check 2D/3D.                                                    */
/* -------------------------------------------------------------------- */
    if( padfZOut )
    {
        if( padfZ )
            memcpy( padfZOut, padfZ, sizeof(double) * nPointCount );
        else
            memset( padfZOut, 0, sizeof(double) * nPointCount );
    }
}

/************************************************************************/
/*                          getPoints()                                 */
/************************************************************************/

/**
 * \brief Returns all points of line string.
 *
 * This method copies all points into user arrays. The user provides the
 * stride between 2 consecutive elements of the array.
 *
 * On some CPU architectures, care must be taken so that the arrays are properly
 * aligned.
 *
 * There is no SFCOM analog to this method.
 *
 * @param pabyX a buffer of at least (nXStride * nPointCount)
 * bytes, may be NULL.
 * @param nXStride the number of bytes between 2 elements of pabyX.
 * @param pabyY a buffer of at least (nYStride * nPointCount)
 * bytes, may be NULL.
 * @param nYStride the number of bytes between 2 elements of pabyY.
 * @param pabyZ a buffer of at last size (nZStride *
 * nPointCount) bytes, may be NULL.
 * @param nZStride the number of bytes between 2 elements of pabyZ.
 *
 * @since OGR 1.9.0
 */

void OGRSimpleCurve::getPoints( void* pabyX, int nXStride,
                                void* pabyY, int nYStride,
                                void* pabyZ, int nZStride ) const
{
    return getPoints( pabyX, nXStride,
                      pabyY, nYStride,
                      pabyZ, nZStride,
                      nullptr, 0);
}

/**
 * \brief Returns all points of line string.
 *
 * This method copies all points into user arrays. The user provides the
 * stride between 2 consecutive elements of the array.
 *
 * On some CPU architectures, care must be taken so that the arrays are properly aligned.
 *
 * There is no SFCOM analog to this method.
 *
 * @param pabyX a buffer of at least (nXStride * nPointCount) bytes, may be NULL.
 * @param nXStride the number of bytes between 2 elements of pabyX.
 * @param pabyY a buffer of at least (nYStride * nPointCount) bytes, may be NULL.
 * @param nYStride the number of bytes between 2 elements of pabyY.
 * @param pabyZ a buffer of at last size (nZStride * nPointCount) bytes, may be NULL.
 * @param nZStride the number of bytes between 2 elements of pabyZ.
 * @param pabyM a buffer of at last size (nMStride * nPointCount) bytes, may be NULL.
 * @param nMStride the number of bytes between 2 elements of pabyM.
 *
 * @since OGR 2.1.0
 */

void OGRSimpleCurve::getPoints( void* pabyX, int nXStride,
                                void* pabyY, int nYStride,
                                void* pabyZ, int nZStride,
                                void* pabyM, int nMStride ) const
{
    if( pabyX != nullptr && nXStride == 0 )
        return;
    if( pabyY != nullptr && nYStride == 0 )
        return;
    if( pabyZ != nullptr && nZStride == 0 )
        return;
    if( pabyM != nullptr && nMStride == 0 )
        return;
    if( nXStride == sizeof(OGRRawPoint) &&
        nYStride == sizeof(OGRRawPoint) &&
        static_cast<char *>(pabyY) ==
        static_cast<char *>(pabyX) + sizeof(double) &&
        (pabyZ == nullptr || nZStride == sizeof(double)) )
    {
        getPoints(static_cast<OGRRawPoint *>(pabyX),
                  static_cast<double *>(pabyZ));
    }
    else
    {
        for( int i = 0; i < nPointCount; i++ )
        {
            if( pabyX ) *reinterpret_cast<double*>(static_cast<char*>(pabyX) + i * nXStride) = paoPoints[i].x;
            if( pabyY ) *reinterpret_cast<double*>(static_cast<char*>(pabyY) + i * nYStride) = paoPoints[i].y;
        }

        if( pabyZ )
        {
            if( nZStride == sizeof(double) )
            {
                if( padfZ )
                    memcpy( pabyZ, padfZ, sizeof(double) * nPointCount );
                else
                    memset( pabyZ, 0, sizeof(double) * nPointCount );
            }
            else
            {
                for( int i = 0; i < nPointCount; i++ )
                {
                    *reinterpret_cast<double*>(static_cast<char*>(pabyZ) + i * nZStride) = (padfZ) ? padfZ[i] : 0.0;
                }
            }
        }
    }
    if( pabyM )
    {
        if( nMStride == sizeof(double) )
        {
            if( padfM )
                memcpy( pabyM, padfM, sizeof(double) * nPointCount );
            else
                memset( pabyM, 0, sizeof(double) * nPointCount );
        }
        else
        {
            for( int i = 0; i < nPointCount; i++ )
            {
                *reinterpret_cast<double*>(static_cast<char*>(pabyM) + i * nMStride) = (padfM) ? padfM[i] : 0.0;
            }
        }
    }
}

/************************************************************************/
/*                           reversePoints()                            */
/************************************************************************/

/**
 * \brief Reverse point order.
 *
 * This method updates the points in this line string in place
 * reversing the point ordering (first for last, etc).
 */

void OGRSimpleCurve::reversePoints()

{
    for( int i = 0; i < nPointCount/2; i++ )
    {
        std::swap(paoPoints[i], paoPoints[nPointCount-i-1]);
        if( padfZ )
        {
            std::swap(padfZ[i], padfZ[nPointCount-i-1]);
        }

        if( padfM )
        {
            std::swap(padfM[i], padfM[nPointCount-i-1]);
        }
    }
}

/************************************************************************/
/*                          addSubLineString()                          */
/************************************************************************/

/**
 * \brief Add a segment of another linestring to this one.
 *
 * Adds the request range of vertices to the end of this line string
 * in an efficient manner.  If the nStartVertex is larger than the
 * nEndVertex then the vertices will be reversed as they are copied.
 *
 * @param poOtherLine the other OGRLineString.
 * @param nStartVertex the first vertex to copy, defaults to 0 to start
 * with the first vertex in the other linestring.
 * @param nEndVertex the last vertex to copy, defaults to -1 indicating
 * the last vertex of the other line string.
 */

void OGRSimpleCurve::addSubLineString( const OGRLineString *poOtherLine,
                                      int nStartVertex, int nEndVertex )

{
    int nOtherLineNumPoints = poOtherLine->getNumPoints();
    if( nOtherLineNumPoints == 0 )
        return;

/* -------------------------------------------------------------------- */
/*      Do a bit of argument defaulting and validation.                 */
/* -------------------------------------------------------------------- */
    if( nEndVertex == -1 )
        nEndVertex = nOtherLineNumPoints - 1;

    if( nStartVertex < 0 || nEndVertex < 0
        || nStartVertex >= nOtherLineNumPoints
        || nEndVertex >= nOtherLineNumPoints )
    {
        CPLAssert( false );
        return;
    }

/* -------------------------------------------------------------------- */
/*      Grow this linestring to hold the additional points.             */
/* -------------------------------------------------------------------- */
    int nOldPoints = nPointCount;
    int nPointsToAdd = std::abs(nEndVertex - nStartVertex) + 1;

    setNumPoints( nPointsToAdd + nOldPoints, FALSE );
    if( nPointCount < nPointsToAdd + nOldPoints
#ifdef DEBUG
        || paoPoints == nullptr
#endif
        )
        return;

/* -------------------------------------------------------------------- */
/*      Copy the x/y points - forward copies use memcpy.                */
/* -------------------------------------------------------------------- */
    if( nEndVertex >= nStartVertex )
    {
        memcpy( paoPoints + nOldPoints,
                poOtherLine->paoPoints + nStartVertex,
                sizeof(OGRRawPoint) * nPointsToAdd );
        if( poOtherLine->padfZ != nullptr )
        {
            Make3D();
            if( padfZ != nullptr )
            {
                memcpy( padfZ + nOldPoints, poOtherLine->padfZ + nStartVertex,
                        sizeof(double) * nPointsToAdd );
            }
        }
        if( poOtherLine->padfM != nullptr )
        {
            AddM();
            if( padfM != nullptr )
            {
                memcpy( padfM + nOldPoints, poOtherLine->padfM + nStartVertex,
                        sizeof(double) * nPointsToAdd );
            }
        }
    }

/* -------------------------------------------------------------------- */
/*      Copy the x/y points - reverse copies done double by double.     */
/* -------------------------------------------------------------------- */
    else
    {
        for( int i = 0; i < nPointsToAdd; i++ )
        {
            paoPoints[i+nOldPoints].x =
                poOtherLine->paoPoints[nStartVertex-i].x;
            paoPoints[i+nOldPoints].y =
                poOtherLine->paoPoints[nStartVertex-i].y;
        }

        if( poOtherLine->padfZ != nullptr )
        {
            Make3D();
            if( padfZ != nullptr )
            {
                for( int i = 0; i < nPointsToAdd; i++ )
                {
                    padfZ[i+nOldPoints] = poOtherLine->padfZ[nStartVertex-i];
                }
            }
        }
        if( poOtherLine->padfM != nullptr )
        {
            AddM();
            if( padfM != nullptr )
            {
                for( int i = 0; i < nPointsToAdd; i++ )
                {
                    padfM[i+nOldPoints] = poOtherLine->padfM[nStartVertex-i];
                }
            }
        }
    }
}

/************************************************************************/
/*                           importFromWkb()                            */
/*                                                                      */
/*      Initialize from serialized stream in well known binary          */
/*      format.                                                         */
/************************************************************************/

OGRErr OGRSimpleCurve::importFromWkb( const unsigned char *pabyData,
                                      int nSize,
                                      OGRwkbVariant eWkbVariant,
                                      int& nBytesConsumedOut )

{
    OGRwkbByteOrder     eByteOrder;
    int                 nDataOffset = 0;
    int                 nNewNumPoints = 0;

    nBytesConsumedOut = -1;
    OGRErr eErr = importPreambleOfCollectionFromWkb( pabyData,
                                                      nSize,
                                                      nDataOffset,
                                                      eByteOrder,
                                                      16,
                                                      nNewNumPoints,
                                                      eWkbVariant );
    if( eErr != OGRERR_NONE )
        return eErr;

    // Check if the wkb stream buffer is big enough to store
    // fetched number of points.
    const int dim = CoordinateDimension();
    int nPointSize = dim*sizeof(double);
    if( nNewNumPoints < 0 || nNewNumPoints > INT_MAX / nPointSize )
        return OGRERR_CORRUPT_DATA;
    int nBufferMinSize = nPointSize * nNewNumPoints;

    if( nSize != -1 && nBufferMinSize > nSize )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Length of input WKB is too small" );
        return OGRERR_NOT_ENOUGH_DATA;
    }

    setNumPoints( nNewNumPoints, FALSE );
    if( nPointCount < nNewNumPoints )
        return OGRERR_FAILURE;

    nBytesConsumedOut = 9 + 8 * nPointCount *
                                    (2 + ((flags & OGR_G_3D) ? 1 : 0)+
                                         ((flags & OGR_G_MEASURED) ? 1 : 0));

/* -------------------------------------------------------------------- */
/*      Get the vertex.                                                 */
/* -------------------------------------------------------------------- */
    int i = 0;

    if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
    {
        for( i = 0; i < nPointCount; i++ )
        {
            memcpy( paoPoints + i, pabyData + 9 + i*32, 16 );
            memcpy( padfZ + i, pabyData + 9 + 16 + i*32, 8 );
            memcpy( padfM + i, pabyData + 9 + 24 + i*32, 8 );
        }
    }
    else if( flags & OGR_G_MEASURED )
    {
        for( i = 0; i < nPointCount; i++ )
        {
            memcpy( paoPoints + i, pabyData + 9 + i*24, 16 );
            memcpy( padfM + i, pabyData + 9 + 16 + i*24, 8 );
        }
    }
    else if( flags & OGR_G_3D )
    {
        for( i = 0; i < nPointCount; i++ )
        {
            memcpy( paoPoints + i, pabyData + 9 + i*24, 16 );
            memcpy( padfZ + i, pabyData + 9 + 16 + i*24, 8 );
        }
    }
    else if( nPointCount )
    {
        memcpy( paoPoints, pabyData + 9, 16 * nPointCount );
    }

/* -------------------------------------------------------------------- */
/*      Byte swap if needed.                                            */
/* -------------------------------------------------------------------- */
    if( OGR_SWAP( eByteOrder ) )
    {
        for( i = 0; i < nPointCount; i++ )
        {
            CPL_SWAPDOUBLE( &(paoPoints[i].x) );
            CPL_SWAPDOUBLE( &(paoPoints[i].y) );
        }

        if( flags & OGR_G_3D )
        {
            for( i = 0; i < nPointCount; i++ )
            {
                CPL_SWAPDOUBLE( padfZ + i );
            }
        }

        if( flags & OGR_G_MEASURED )
        {
            for( i = 0; i < nPointCount; i++ )
            {
                CPL_SWAPDOUBLE( padfM + i );
            }
        }
    }

    return OGRERR_NONE;
}

/************************************************************************/
/*                            exportToWkb()                             */
/*                                                                      */
/*      Build a well known binary representation of this object.        */
/************************************************************************/

OGRErr OGRSimpleCurve::exportToWkb( OGRwkbByteOrder eByteOrder,
                                    unsigned char * pabyData,
                                    OGRwkbVariant eWkbVariant ) const

{
/* -------------------------------------------------------------------- */
/*      Set the byte order.                                             */
/* -------------------------------------------------------------------- */
    pabyData[0] = DB2_V72_UNFIX_BYTE_ORDER(static_cast<unsigned char>(eByteOrder));

/* -------------------------------------------------------------------- */
/*      Set the geometry feature type.                                  */
/* -------------------------------------------------------------------- */
    GUInt32 nGType = getGeometryType();

    if( eWkbVariant == wkbVariantPostGIS1 )
    {
        nGType = wkbFlatten(nGType);
        if( Is3D() )
            // Explicitly set wkb25DBit.
            nGType = static_cast<OGRwkbGeometryType>(nGType | wkb25DBitInternalUse);
        if( IsMeasured() )
            nGType = static_cast<OGRwkbGeometryType>(nGType | 0x40000000);
    }
    else if( eWkbVariant == wkbVariantIso )
        nGType = getIsoGeometryType();

    if( eByteOrder == wkbNDR )
    {
        CPL_LSBPTR32( &nGType );
    }
    else
    {
        CPL_MSBPTR32( &nGType );
    }

    memcpy( pabyData + 1, &nGType, 4 );

/* -------------------------------------------------------------------- */
/*      Copy in the data count.                                         */
/* -------------------------------------------------------------------- */
    memcpy( pabyData+5, &nPointCount, 4 );

/* -------------------------------------------------------------------- */
/*      Copy in the raw data.                                           */
/* -------------------------------------------------------------------- */
    if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
    {
        for( int i = 0; i < nPointCount; i++ )
        {
            memcpy( pabyData + 9 + 32*i, paoPoints+i, 16 );
            memcpy( pabyData + 9 + 16 + 32*i, padfZ+i, 8 );
            memcpy( pabyData + 9 + 24 + 32*i, padfM+i, 8 );
        }
    }
    else if( flags & OGR_G_MEASURED )
    {
        for( int i = 0; i < nPointCount; i++ )
        {
            memcpy( pabyData + 9 + 24*i, paoPoints+i, 16 );
            memcpy( pabyData + 9 + 16 + 24*i, padfM+i, 8 );
        }
    }
    else if( flags & OGR_G_3D )
    {
        for( int i = 0; i < nPointCount; i++ )
        {
            memcpy( pabyData + 9 + 24*i, paoPoints+i, 16 );
            memcpy( pabyData + 9 + 16 + 24*i, padfZ+i, 8 );
        }
    }
    else if( nPointCount )
        memcpy( pabyData+9, paoPoints, 16 * nPointCount );

/* -------------------------------------------------------------------- */
/*      Swap if needed.                                                 */
/* -------------------------------------------------------------------- */
    if( OGR_SWAP( eByteOrder ) )
    {
        int nCount = CPL_SWAP32( nPointCount );
        memcpy( pabyData+5, &nCount, 4 );

        for( int i = CoordinateDimension() * nPointCount - 1; i >= 0; i-- )
        {
            CPL_SWAP64PTR( pabyData + 9 + 8 * i );
        }
    }

    return OGRERR_NONE;
}

/************************************************************************/
/*                           importFromWkt()                            */
/*                                                                      */
/*      Instantiate from well known text format.  Currently this is     */
/*      `LINESTRING ( x y, x y, ...)',                                  */
/************************************************************************/

OGRErr OGRSimpleCurve::importFromWkt( const char ** ppszInput )

{
    int bHasZ = FALSE;
    int bHasM = FALSE;
    bool bIsEmpty = false;
    const OGRErr eErr =
        importPreambleFromWkt(ppszInput, &bHasZ, &bHasM, &bIsEmpty);
    flags = 0;
    if( eErr != OGRERR_NONE )
        return eErr;
    if( bHasZ ) flags |= OGR_G_3D;
    if( bHasM ) flags |= OGR_G_MEASURED;
    if( bIsEmpty )
    {
        return OGRERR_NONE;
    }

    const char *pszInput = *ppszInput;

/* -------------------------------------------------------------------- */
/*      Read the point list.                                            */
/* -------------------------------------------------------------------- */
    int flagsFromInput = flags;
    nPointCount = 0;

    int nMaxPoints = 0;
    pszInput = OGRWktReadPointsM( pszInput, &paoPoints, &padfZ, &padfM,
                                  &flagsFromInput,
                                  &nMaxPoints, &nPointCount );
    if( pszInput == nullptr )
        return OGRERR_CORRUPT_DATA;

    if( (flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D) )
    {
        set3D(TRUE);
    }
    if( (flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED) )
    {
        setMeasured(TRUE);
    }

    *ppszInput = pszInput;

    return OGRERR_NONE;
}

//! @cond Doxygen_Suppress
/************************************************************************/
/*                        importFromWKTListOnly()                       */
/*                                                                      */
/*      Instantiate from "(x y, x y, ...)"                              */
/************************************************************************/

OGRErr OGRSimpleCurve::importFromWKTListOnly( const char ** ppszInput,
                                              int bHasZ, int bHasM,
                                              OGRRawPoint*& paoPointsIn,
                                              int& nMaxPointsIn,
                                              double*& padfZIn )

{
    const char *pszInput = *ppszInput;

/* -------------------------------------------------------------------- */
/*      Read the point list.                                            */
/* -------------------------------------------------------------------- */
    int flagsFromInput = flags;
    int nPointCountRead = 0;
    double *padfMIn = nullptr;
    if( flagsFromInput == 0 )  // Flags was not set, this is not called by us.
    {
        if( bHasM )
            flagsFromInput |= OGR_G_MEASURED;
        if( bHasZ )
            flagsFromInput |= OGR_G_3D;
    }

    pszInput = OGRWktReadPointsM( pszInput, &paoPointsIn, &padfZIn, &padfMIn,
                                  &flagsFromInput,
                                  &nMaxPointsIn, &nPointCountRead );

    if( pszInput == nullptr )
    {
        CPLFree( padfMIn );
        return OGRERR_CORRUPT_DATA;
    }
    if( (flagsFromInput & OGR_G_3D) && !(flags & OGR_G_3D) )
    {
        flags |= OGR_G_3D;
        bHasZ = TRUE;
    }
    if( (flagsFromInput & OGR_G_MEASURED) && !(flags & OGR_G_MEASURED) )
    {
        flags |= OGR_G_MEASURED;
        bHasM = TRUE;
    }

    *ppszInput = pszInput;

    if( bHasM && bHasZ )
        setPoints( nPointCountRead, paoPointsIn, padfZIn, padfMIn );
    else if( bHasM && !bHasZ )
        setPointsM( nPointCountRead, paoPointsIn, padfMIn );
    else
        setPoints( nPointCountRead, paoPointsIn, padfZIn );

    CPLFree( padfMIn );

    return OGRERR_NONE;
}
//! @endcond

/************************************************************************/
/*                            exportToWkt()                             */
/*                                                                      */
/*      Translate this structure into its well known text format       */
/*      equivalent.  This could be made a lot more CPU efficient.       */
/************************************************************************/

OGRErr OGRSimpleCurve::exportToWkt( char ** ppszDstText,
                                   OGRwkbVariant eWkbVariant ) const

{
    const size_t nMaxString = static_cast<size_t>(nPointCount) * 40 * 4 + 26;

/* -------------------------------------------------------------------- */
/*      Handle special empty case.                                      */
/* -------------------------------------------------------------------- */
    if( IsEmpty() )
    {
        CPLString osEmpty;
        if( eWkbVariant == wkbVariantIso )
        {
            if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
                osEmpty.Printf("%s ZM EMPTY", getGeometryName());
            else if( flags & OGR_G_MEASURED )
                osEmpty.Printf("%s M EMPTY", getGeometryName());
            else if( flags & OGR_G_3D )
                osEmpty.Printf("%s Z EMPTY", getGeometryName());
            else
                osEmpty.Printf("%s EMPTY", getGeometryName());
        }
        else
            osEmpty.Printf("%s EMPTY", getGeometryName());
        *ppszDstText = CPLStrdup(osEmpty);
        return OGRERR_NONE;
    }

/* -------------------------------------------------------------------- */
/*      General case.                                                   */
/* -------------------------------------------------------------------- */
    *ppszDstText = static_cast<char *>(VSI_MALLOC_VERBOSE( nMaxString ));
    if( *ppszDstText == nullptr )
        return OGRERR_NOT_ENOUGH_MEMORY;

    if( eWkbVariant == wkbVariantIso )
    {
        if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
            snprintf( *ppszDstText, nMaxString, "%s ZM (", getGeometryName() );
        else if( flags & OGR_G_MEASURED )
            snprintf( *ppszDstText, nMaxString, "%s M (", getGeometryName() );
        else if( flags & OGR_G_3D )
            snprintf( *ppszDstText, nMaxString, "%s Z (", getGeometryName() );
        else
            snprintf( *ppszDstText, nMaxString, "%s (", getGeometryName() );
    }
    else
        snprintf( *ppszDstText, nMaxString, "%s (", getGeometryName() );

    OGRBoolean hasZ = Is3D();
    OGRBoolean hasM = IsMeasured();
    if( eWkbVariant != wkbVariantIso )
        hasM = FALSE;

    size_t nRetLen = 0;

    for( int i = 0; i < nPointCount; i++ )
    {
        if( nMaxString <= strlen(*ppszDstText+nRetLen) + 32 + nRetLen )
        {
            CPLDebug( "OGR",
                      "OGRSimpleCurve::exportToWkt() ... buffer overflow.\n"
                      "nMaxString=%d, strlen(*ppszDstText) = %d, i=%d\n"
                      "*ppszDstText = %s",
                      static_cast<int>(nMaxString),
                      static_cast<int>(strlen(*ppszDstText)), i, *ppszDstText );

            VSIFree( *ppszDstText );
            *ppszDstText = nullptr;
            return OGRERR_NOT_ENOUGH_MEMORY;
        }

        if( i > 0 )
            strcat( *ppszDstText + nRetLen, "," );

        nRetLen += strlen(*ppszDstText + nRetLen);
        OGRMakeWktCoordinateM( *ppszDstText + nRetLen,
                               paoPoints[i].x,
                               paoPoints[i].y,
                               padfZ ? padfZ[i] : 0.0,
                               padfM ? padfM[i] : 0.0,
                               hasZ, hasM );

        nRetLen += strlen(*ppszDstText + nRetLen);
    }

    strcat( *ppszDstText+nRetLen, ")" );

    return OGRERR_NONE;
}

/************************************************************************/
/*                             get_Length()                             */
/*                                                                      */
/*      For now we return a simple euclidean 2D distance.               */
/************************************************************************/

double OGRSimpleCurve::get_Length() const

{
    double dfLength = 0.0;

    for( int i = 0; i < nPointCount-1; i++ )
    {

        const double dfDeltaX = paoPoints[i+1].x - paoPoints[i].x;
        const double dfDeltaY = paoPoints[i+1].y - paoPoints[i].y;
        dfLength += sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY);
    }

    return dfLength;
}

/************************************************************************/
/*                             StartPoint()                             */
/************************************************************************/

void OGRSimpleCurve::StartPoint( OGRPoint * poPoint ) const

{
    getPoint( 0, poPoint );
}

/************************************************************************/
/*                              EndPoint()                              */
/************************************************************************/

void OGRSimpleCurve::EndPoint( OGRPoint * poPoint ) const

{
    getPoint( nPointCount-1, poPoint );
}

/************************************************************************/
/*                               Value()                                */
/*                                                                      */
/*      Get an interpolated point at some distance along the curve.     */
/************************************************************************/

void OGRSimpleCurve::Value( double dfDistance, OGRPoint * poPoint ) const

{
    if( dfDistance < 0 )
    {
        StartPoint( poPoint );
        return;
    }

    double dfLength = 0.0;

    for( int i = 0; i < nPointCount-1; i++ )
    {
        const double dfDeltaX = paoPoints[i+1].x - paoPoints[i].x;
        const double dfDeltaY = paoPoints[i+1].y - paoPoints[i].y;
        const double dfSegLength = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY);

        if( dfSegLength > 0 )
        {
            if( (dfLength <= dfDistance) && ((dfLength + dfSegLength) >=
                                             dfDistance) )
            {
                double dfRatio = (dfDistance - dfLength) / dfSegLength;

                poPoint->setX( paoPoints[i].x * (1 - dfRatio)
                               + paoPoints[i+1].x * dfRatio );
                poPoint->setY( paoPoints[i].y * (1 - dfRatio)
                               + paoPoints[i+1].y * dfRatio );

                if( getCoordinateDimension() == 3 )
                    poPoint->setZ( padfZ[i] * (1 - dfRatio)
                                   + padfZ[i+1] * dfRatio );

                return;
            }

            dfLength += dfSegLength;
        }
    }

    EndPoint( poPoint );
}

/************************************************************************/
/*                              Project()                               */
/*                                                                      */
/* Return distance of point projected on line from origin of this line. */
/************************************************************************/

/**
* \brief Project point on linestring.
*
* The input point projected on linestring. This is the shortest distance
* from point to the linestring. The distance from begin of linestring to
* the point projection returned.
*
* This method is built on the GEOS library (GEOS >= 3.2.0), check it for the
* definition of the geometry operation.
* If OGR is built without the GEOS library, this method will always return -1,
* issuing a CPLE_NotSupported error.
*
* @return a distance from the begin of the linestring to the projected point.
*
* @since OGR 1.11.0
*/

// GEOS >= 3.2.0 for project capability.
#if defined(HAVE_GEOS)
#if GEOS_VERSION_MAJOR > 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR >= 2)
#define HAVE_GEOS_PROJECT
#endif
#endif

double OGRSimpleCurve::Project(const OGRPoint *
#ifdef HAVE_GEOS_PROJECT
                                poPoint
#endif
                               ) const

{
    double dfResult = -1;
#ifndef HAVE_GEOS_PROJECT
    CPLError(CE_Failure, CPLE_NotSupported,
        "GEOS support not enabled.");
    return dfResult;
#else
    GEOSGeom hThisGeosGeom = nullptr;
    GEOSGeom hPointGeosGeom = nullptr;

    GEOSContextHandle_t hGEOSCtxt = createGEOSContext();
    hThisGeosGeom = exportToGEOS(hGEOSCtxt);
    hPointGeosGeom = poPoint->exportToGEOS(hGEOSCtxt);
    if( hThisGeosGeom != nullptr && hPointGeosGeom != nullptr )
    {
        dfResult = GEOSProject_r(hGEOSCtxt, hThisGeosGeom, hPointGeosGeom);
    }
    GEOSGeom_destroy_r(hGEOSCtxt, hThisGeosGeom);
    GEOSGeom_destroy_r(hGEOSCtxt, hPointGeosGeom);
    freeGEOSContext(hGEOSCtxt);

    return dfResult;

#endif  // HAVE_GEOS
}

/************************************************************************/
/*                            getSubLine()                              */
/*                                                                      */
/*  Extracts a portion of this OGRLineString into a new OGRLineString.  */
/************************************************************************/

/**
* \brief Get the portion of linestring.
*
* The portion of the linestring extracted to new one. The input distances
* (maybe present as ratio of length of linestring) set begin and end of
* extracted portion.
*
* @param dfDistanceFrom The distance from the origin of linestring, where the subline should begins
* @param dfDistanceTo The distance from the origin of linestring, where the subline should ends
* @param bAsRatio The flag indicating that distances are the ratio of the linestring length.
*
* @return a newly allocated linestring now owned by the caller, or NULL on failure.
*
* @since OGR 1.11.0
*/

OGRLineString* OGRSimpleCurve::getSubLine(double dfDistanceFrom,
                                          double dfDistanceTo,
                                          int bAsRatio) const

{
    OGRLineString *poNewLineString = new OGRLineString();

    poNewLineString->assignSpatialReference(getSpatialReference());
    poNewLineString->setCoordinateDimension(getCoordinateDimension());

    const double dfLen = get_Length();
    if( bAsRatio == TRUE )
    {
        // Convert to real distance.
        dfDistanceFrom *= dfLen;
        dfDistanceTo *= dfLen;
    }

    if( dfDistanceFrom < 0 )
        dfDistanceFrom = 0;
    if( dfDistanceTo > dfLen )
        dfDistanceTo = dfLen;

    if( dfDistanceFrom > dfDistanceTo || dfDistanceFrom >= dfLen )
    {
        CPLError(CE_Failure, CPLE_IllegalArg, "Input distances are invalid.");

        return nullptr;
    }

    double dfLength = 0.0;

    // Get first point.

    int i = 0;  // Used after if blocks.
    if( dfDistanceFrom == 0 )
    {
        if( getCoordinateDimension() == 3 )
            poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y, padfZ[0]);
        else
            poNewLineString->addPoint(paoPoints[0].x, paoPoints[0].y);
    }
    else
    {
        for( i = 0; i < nPointCount - 1; i++ )
        {
            const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
            const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
            const double dfSegLength =
                sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY);

            if( dfSegLength > 0 )
            {
                if( (dfLength <= dfDistanceFrom) && ((dfLength + dfSegLength) >=
                    dfDistanceFrom) )
                {
                    double dfRatio = (dfDistanceFrom - dfLength) / dfSegLength;

                    double dfX = paoPoints[i].x * (1 - dfRatio)
                        + paoPoints[i + 1].x * dfRatio;
                    double dfY = paoPoints[i].y * (1 - dfRatio)
                        + paoPoints[i + 1].y * dfRatio;

                    if( getCoordinateDimension() == 3 )
                    {
                        poNewLineString->
                            addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio)
                        + padfZ[i+1] * dfRatio);
                    }
                    else
                    {
                        poNewLineString->addPoint(dfX, dfY);
                    }

                    // Check if dfDistanceTo is in same segment.
                    if( dfLength <= dfDistanceTo &&
                        (dfLength + dfSegLength) >= dfDistanceTo )
                    {
                        dfRatio = (dfDistanceTo - dfLength) / dfSegLength;

                        dfX = paoPoints[i].x * (1 - dfRatio)
                            + paoPoints[i + 1].x * dfRatio;
                        dfY = paoPoints[i].y * (1 - dfRatio)
                            + paoPoints[i + 1].y * dfRatio;

                        if( getCoordinateDimension() == 3 )
                        {
                            poNewLineString->
                                addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio)
                                         + padfZ[i + 1] * dfRatio);
                        }
                        else
                        {
                            poNewLineString->addPoint(dfX, dfY);
                        }

                        if( poNewLineString->getNumPoints() < 2 )
                        {
                            delete poNewLineString;
                            poNewLineString = nullptr;
                        }

                        return poNewLineString;
                    }
                    i++;
                    dfLength += dfSegLength;
                    break;
                }

                dfLength += dfSegLength;
            }
        }
    }

    // Add points.
    for( ; i < nPointCount - 1; i++ )
    {
        if( getCoordinateDimension() == 3 )
            poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y, padfZ[i]);
        else
            poNewLineString->addPoint(paoPoints[i].x, paoPoints[i].y);

        const double dfDeltaX = paoPoints[i + 1].x - paoPoints[i].x;
        const double dfDeltaY = paoPoints[i + 1].y - paoPoints[i].y;
        const double dfSegLength = sqrt(dfDeltaX*dfDeltaX + dfDeltaY*dfDeltaY);

        if( dfSegLength > 0 )
        {
            if( (dfLength <= dfDistanceTo) && ((dfLength + dfSegLength) >=
                dfDistanceTo) )
            {
                const double dfRatio = (dfDistanceTo - dfLength) / dfSegLength;

                const double dfX = paoPoints[i].x * (1 - dfRatio)
                    + paoPoints[i + 1].x * dfRatio;
                const double dfY = paoPoints[i].y * (1 - dfRatio)
                    + paoPoints[i + 1].y * dfRatio;

                if( getCoordinateDimension() == 3 )
                    poNewLineString->addPoint(dfX, dfY, padfZ[i] * (1 - dfRatio)
                    + padfZ[i + 1] * dfRatio);
                else
                    poNewLineString->addPoint(dfX, dfY);

                return poNewLineString;
            }

            dfLength += dfSegLength;
        }
    }

    if( getCoordinateDimension() == 3 )
        poNewLineString->
            addPoint(paoPoints[nPointCount - 1].x,
                     paoPoints[nPointCount - 1].y,
                     padfZ[nPointCount - 1]);
    else
        poNewLineString->
            addPoint(paoPoints[nPointCount - 1].x,
                     paoPoints[nPointCount - 1].y);

    if( poNewLineString->getNumPoints() < 2 )
    {
        delete poNewLineString;
        poNewLineString = nullptr;
    }

    return poNewLineString;
}

/************************************************************************/
/*                            getEnvelope()                             */
/************************************************************************/

void OGRSimpleCurve::getEnvelope( OGREnvelope * psEnvelope ) const

{
    if( IsEmpty() )
    {
        psEnvelope->MinX = 0.0;
        psEnvelope->MaxX = 0.0;
        psEnvelope->MinY = 0.0;
        psEnvelope->MaxY = 0.0;
        return;
    }

    double dfMinX = paoPoints[0].x;
    double dfMaxX = paoPoints[0].x;
    double dfMinY = paoPoints[0].y;
    double dfMaxY = paoPoints[0].y;

    for( int iPoint = 1; iPoint < nPointCount; iPoint++ )
    {
        if( dfMaxX < paoPoints[iPoint].x )
            dfMaxX = paoPoints[iPoint].x;
        if( dfMaxY < paoPoints[iPoint].y )
            dfMaxY = paoPoints[iPoint].y;
        if( dfMinX > paoPoints[iPoint].x )
            dfMinX = paoPoints[iPoint].x;
        if( dfMinY > paoPoints[iPoint].y )
            dfMinY = paoPoints[iPoint].y;
    }

    psEnvelope->MinX = dfMinX;
    psEnvelope->MaxX = dfMaxX;
    psEnvelope->MinY = dfMinY;
    psEnvelope->MaxY = dfMaxY;
}

/************************************************************************/
/*                            getEnvelope()                             */
/************************************************************************/

void OGRSimpleCurve::getEnvelope( OGREnvelope3D * psEnvelope ) const

{
    getEnvelope(static_cast<OGREnvelope*>(psEnvelope));

    if( IsEmpty() || padfZ == nullptr )
    {
        psEnvelope->MinZ = 0.0;
        psEnvelope->MaxZ = 0.0;
        return;
    }

    double dfMinZ = padfZ[0];
    double dfMaxZ = padfZ[0];

    for( int iPoint = 1; iPoint < nPointCount; iPoint++ )
    {
        if( dfMinZ > padfZ[iPoint] )
            dfMinZ = padfZ[iPoint];
        if( dfMaxZ < padfZ[iPoint] )
            dfMaxZ = padfZ[iPoint];
    }

    psEnvelope->MinZ = dfMinZ;
    psEnvelope->MaxZ = dfMaxZ;
}

/************************************************************************/
/*                               Equals()                               */
/************************************************************************/

OGRBoolean OGRSimpleCurve::Equals( const OGRGeometry * poOther ) const

{
    if( poOther == this )
        return TRUE;

    if( poOther->getGeometryType() != getGeometryType() )
        return FALSE;

    if( IsEmpty() && poOther->IsEmpty() )
        return TRUE;

    // TODO(schwehr): Test the SRS.

    auto poOLine = poOther->toSimpleCurve();
    if( getNumPoints() != poOLine->getNumPoints() )
        return FALSE;

    for( int iPoint = 0; iPoint < getNumPoints(); iPoint++ )
    {
        if( getX(iPoint) != poOLine->getX(iPoint)
            || getY(iPoint) != poOLine->getY(iPoint)
            || getZ(iPoint) != poOLine->getZ(iPoint) )
            return FALSE;
    }

    return TRUE;
}

/************************************************************************/
/*                             transform()                              */
/************************************************************************/

OGRErr OGRSimpleCurve::transform( OGRCoordinateTransformation *poCT )

{
/* -------------------------------------------------------------------- */
/*   Make a copy of the points to operate on, so as to be able to       */
/*   keep only valid reprojected points if partial reprojection enabled */
/*   or keeping intact the original geometry if only full reprojection  */
/*   allowed.                                                           */
/* -------------------------------------------------------------------- */
    double *xyz = static_cast<double *>(
        VSI_MALLOC_VERBOSE(sizeof(double) * nPointCount * 3));
    int *pabSuccess = static_cast<int *>(
        VSI_CALLOC_VERBOSE(sizeof(int), nPointCount));
    if( xyz == nullptr || pabSuccess == nullptr )
    {
        VSIFree(xyz);
        VSIFree(pabSuccess);
        return OGRERR_NOT_ENOUGH_MEMORY;
    }

    for( int i = 0; i < nPointCount; i++ )
    {
        xyz[i] = paoPoints[i].x;
        xyz[i+nPointCount] = paoPoints[i].y;
        if( padfZ )
            xyz[i+nPointCount*2] = padfZ[i];
        else
            xyz[i+nPointCount*2] = 0.0;
    }

/* -------------------------------------------------------------------- */
/*      Transform and reapply.                                          */
/* -------------------------------------------------------------------- */
    poCT->Transform( nPointCount, xyz, xyz + nPointCount,
                     xyz+nPointCount*2, nullptr, pabSuccess );

    const char* pszEnablePartialReprojection = nullptr;

    int j = 0;  // Used after for.
    for( int i = 0; i < nPointCount; i++ )
    {
        if( pabSuccess[i] )
        {
            xyz[j] = xyz[i];
            xyz[j+nPointCount] = xyz[i+nPointCount];
            xyz[j+2*nPointCount] = xyz[i+2*nPointCount];
            j++;
        }
        else
        {
            if( pszEnablePartialReprojection == nullptr )
                pszEnablePartialReprojection =
                    CPLGetConfigOption("OGR_ENABLE_PARTIAL_REPROJECTION", nullptr);
            if( pszEnablePartialReprojection == nullptr )
            {
                static bool bHasWarned = false;
                if( !bHasWarned )
                {
                    // Check that there is at least one valid reprojected point
                    // and issue an error giving an hint to use
                    // OGR_ENABLE_PARTIAL_REPROJECTION.
                    bool bHasOneValidPoint = j != 0;
                    for( ; i < nPointCount && !bHasOneValidPoint; i++ )
                    {
                        if( pabSuccess[i] )
                            bHasOneValidPoint = true;
                    }
                    if( bHasOneValidPoint )
                    {
                        bHasWarned = true;
                        CPLError(CE_Failure, CPLE_AppDefined,
                                 "Full reprojection failed, but partial is "
                                 "possible if you define "
                                 "OGR_ENABLE_PARTIAL_REPROJECTION "
                                 "configuration option to TRUE");
                    }
                }

                CPLFree( xyz );
                CPLFree( pabSuccess );
                return OGRERR_FAILURE;
            }
            else if( !CPLTestBool(pszEnablePartialReprojection) )
            {
                CPLFree( xyz );
                CPLFree( pabSuccess );
                return OGRERR_FAILURE;
            }
        }
    }

    if( j == 0 && nPointCount != 0 )
    {
        CPLFree( xyz );
        CPLFree( pabSuccess );
        return OGRERR_FAILURE;
    }

    setPoints( j, xyz, xyz+nPointCount,
            ( padfZ ) ? xyz+nPointCount*2 : nullptr);
    CPLFree( xyz );
    CPLFree( pabSuccess );

    assignSpatialReference( poCT->GetTargetCS() );

    return OGRERR_NONE;
}

/************************************************************************/
/*                               IsEmpty()                              */
/************************************************************************/

OGRBoolean OGRSimpleCurve::IsEmpty() const
{
    return (nPointCount == 0);
}

/************************************************************************/
/*                     OGRSimpleCurve::segmentize()                     */
/************************************************************************/

void OGRSimpleCurve::segmentize( double dfMaxLength )
{
    if( dfMaxLength <= 0 )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "dfMaxLength must be strictly positive");
        return;
    }
    if( nPointCount < 2 )
        return;

    // So as to make sure that the same line followed in both directions
    // result in the same segmentized line.
    if( paoPoints[0].x < paoPoints[nPointCount - 1].x ||
        (paoPoints[0].x == paoPoints[nPointCount - 1].x &&
         paoPoints[0].y < paoPoints[nPointCount - 1].y) )
    {
        reversePoints();
        segmentize(dfMaxLength);
        reversePoints();
        return;
    }

    OGRRawPoint* paoNewPoints = nullptr;
    double* padfNewZ = nullptr;
    double* padfNewM = nullptr;
    int nNewPointCount = 0;
    const double dfSquareMaxLength = dfMaxLength * dfMaxLength;

    for( int i = 0; i < nPointCount; i++ )
    {
        paoNewPoints = static_cast<OGRRawPoint *>(
            CPLRealloc(paoNewPoints,
                       sizeof(OGRRawPoint) * (nNewPointCount + 1)));
        paoNewPoints[nNewPointCount] = paoPoints[i];

        if( padfZ != nullptr )
        {
            padfNewZ = static_cast<double *>(
                CPLRealloc(padfNewZ, sizeof(double) * (nNewPointCount + 1)));
            padfNewZ[nNewPointCount] = padfZ[i];
        }

        if( padfM != nullptr )
        {
            padfNewM = static_cast<double *>(
                CPLRealloc(padfNewM, sizeof(double) * (nNewPointCount + 1)));
            padfNewM[nNewPointCount] = padfM[i];
        }

        nNewPointCount++;

        if( i == nPointCount - 1 )
            break;

        const double dfX = paoPoints[i+1].x - paoPoints[i].x;
        const double dfY = paoPoints[i+1].y - paoPoints[i].y;
        const double dfSquareDist = dfX * dfX + dfY * dfY;
        if( dfSquareDist - dfSquareMaxLength > 1e-5 * dfSquareMaxLength )
        {
            const double dfIntermediatePoints =
                floor(sqrt(dfSquareDist / dfSquareMaxLength) - 1e-2);
            const int nIntermediatePoints =
                DoubleToIntClamp(dfIntermediatePoints);

            // TODO(schwehr): Can these be tighter?
            // Limit allocation of paoNewPoints to a few GB of memory.
            // An OGRRawPoint is 2 doubles.
            // kMax is a guess of what a reasonable max might be.
            const int kMax = 2 << 26;
            if ( nNewPointCount > kMax || nIntermediatePoints > kMax )
            {
                CPLError(CE_Failure, CPLE_AppDefined,
                         "Too many points in a segment: %d or %d",
                         nNewPointCount, nIntermediatePoints);
                CPLFree(paoNewPoints);
                CPLFree(padfNewZ);
                CPLFree(padfNewM);
                return;
            }

            paoNewPoints = static_cast<OGRRawPoint *>(
                CPLRealloc(paoNewPoints,
                           sizeof(OGRRawPoint) * (nNewPointCount +
                                                  nIntermediatePoints)));
            if( padfZ != nullptr )
            {
                padfNewZ = static_cast<double *>(
                    CPLRealloc(padfNewZ,
                               sizeof(double) * (nNewPointCount +
                                                 nIntermediatePoints)));
            }
            if( padfM != nullptr )
            {
                padfNewM = static_cast<double *>(
                    CPLRealloc(padfNewM,
                               sizeof(double) * (nNewPointCount +
                                                 nIntermediatePoints)));
            }

            for( int j = 1; j <= nIntermediatePoints; j++ )
            {
                paoNewPoints[nNewPointCount + j - 1].x =
                    paoPoints[i].x + j * dfX / (nIntermediatePoints + 1);
                paoNewPoints[nNewPointCount + j - 1].y =
                    paoPoints[i].y + j * dfY / (nIntermediatePoints + 1);
                if( padfZ != nullptr )
                {
                    // No interpolation.
                    padfNewZ[nNewPointCount + j - 1] = padfZ[i];
                }
                if( padfM != nullptr )
                {
                    // No interpolation.
                    padfNewM[nNewPointCount + j - 1] = padfM[i];
                }
            }

            nNewPointCount += nIntermediatePoints;
        }
    }

    CPLFree(paoPoints);
    paoPoints = paoNewPoints;
    nPointCount = nNewPointCount;

    if( padfZ != nullptr )
    {
        CPLFree(padfZ);
        padfZ = padfNewZ;
    }
    if( padfM != nullptr )
    {
        CPLFree(padfM);
        padfM = padfNewM;
    }
}

/************************************************************************/
/*                               swapXY()                               */
/************************************************************************/

void OGRSimpleCurve::swapXY()
{
    for( int i = 0; i < nPointCount; i++ )
    {
        std::swap(paoPoints[i].x, paoPoints[i].y);
    }
}

/************************************************************************/
/*                       OGRSimpleCurvePointIterator                    */
/************************************************************************/

class OGRSimpleCurvePointIterator final: public OGRPointIterator
{
        CPL_DISALLOW_COPY_ASSIGN(OGRSimpleCurvePointIterator)

        const OGRSimpleCurve* poSC = nullptr;
        int                   iCurPoint = 0;

    public:
        explicit OGRSimpleCurvePointIterator(const OGRSimpleCurve* poSCIn) :
            poSC(poSCIn) {}

        OGRBoolean getNextPoint( OGRPoint* p ) override;
};

/************************************************************************/
/*                            getNextPoint()                            */
/************************************************************************/

OGRBoolean OGRSimpleCurvePointIterator::getNextPoint(OGRPoint* p)
{
    if( iCurPoint >= poSC->getNumPoints() )
        return FALSE;
    poSC->getPoint(iCurPoint, p);
    iCurPoint++;
    return TRUE;
}

/************************************************************************/
/*                         getPointIterator()                           */
/************************************************************************/

OGRPointIterator* OGRSimpleCurve::getPointIterator() const
{
    return new OGRSimpleCurvePointIterator(this);
}

/************************************************************************/
/*                           OGRLineString()                            */
/************************************************************************/

/**
 * \brief Create an empty line string.
 */

OGRLineString::OGRLineString() = default;

/************************************************************************/
/*                  OGRLineString( const OGRLineString& )               */
/************************************************************************/

/**
 * \brief Copy constructor.
 *
 * Note: before GDAL 2.1, only the default implementation of the constructor
 * existed, which could be unsafe to use.
 *
 * @since GDAL 2.1
 */

OGRLineString::OGRLineString( const OGRLineString& ) = default;

/************************************************************************/
/*                          ~OGRLineString()                            */
/************************************************************************/

OGRLineString::~OGRLineString() = default;

/************************************************************************/
/*                    operator=( const OGRLineString& )                 */
/************************************************************************/

/**
 * \brief Assignment operator.
 *
 * Note: before GDAL 2.1, only the default implementation of the operator
 * existed, which could be unsafe to use.
 *
 * @since GDAL 2.1
 */

OGRLineString& OGRLineString::operator=( const OGRLineString& other )
{
    if( this != &other)
    {
        OGRSimpleCurve::operator=( other );
    }
    return *this;
}

/************************************************************************/
/*                          getGeometryType()                           */
/************************************************************************/

OGRwkbGeometryType OGRLineString::getGeometryType() const

{
    if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
        return wkbLineStringZM;
    else if( flags & OGR_G_MEASURED )
        return wkbLineStringM;
    else if( flags & OGR_G_3D )
        return wkbLineString25D;
    else
        return wkbLineString;
}

/************************************************************************/
/*                          getGeometryName()                           */
/************************************************************************/

const char * OGRLineString::getGeometryName() const

{
    return "LINESTRING";
}

/************************************************************************/
/*                          curveToLine()                               */
/************************************************************************/

OGRLineString* OGRLineString::CurveToLine(
    CPL_UNUSED double /* dfMaxAngleStepSizeDegrees */,
    CPL_UNUSED const char* const* /* papszOptions */ ) const
{
    return clone()->toLineString();
}

/************************************************************************/
/*                          get_LinearArea()                            */
/************************************************************************/

/**
 * \brief Compute area of ring / closed linestring.
 *
 * The area is computed according to Green's Theorem:
 *
 * Area is "Sum(x(i)*(y(i+1) - y(i-1)))/2" for i = 0 to pointCount-1,
 * assuming the last point is a duplicate of the first.
 *
 * @return computed area.
 */

double OGRSimpleCurve::get_LinearArea() const

{
    if( nPointCount < 2 ||
        (WkbSize() != 0 && /* if not a linearring, check it is closed */
            (paoPoints[0].x != paoPoints[nPointCount-1].x ||
             paoPoints[0].y != paoPoints[nPointCount-1].y)) )
    {
        return 0;
    }

    double dfAreaSum =
        paoPoints[0].x * (paoPoints[1].y - paoPoints[nPointCount-1].y);

    for( int i = 1; i < nPointCount-1; i++ )
    {
        dfAreaSum += paoPoints[i].x * (paoPoints[i+1].y - paoPoints[i-1].y);
    }

    dfAreaSum +=
        paoPoints[nPointCount-1].x *
        (paoPoints[0].y - paoPoints[nPointCount-2].y);

    return 0.5 * fabs(dfAreaSum);
}

/************************************************************************/
/*                             getCurveGeometry()                       */
/************************************************************************/

OGRGeometry *
OGRLineString::getCurveGeometry( const char* const* papszOptions ) const
{
    return OGRGeometryFactory::curveFromLineString(this, papszOptions);
}

/************************************************************************/
/*                      TransferMembersAndDestroy()                     */
/************************************************************************/
//! @cond Doxygen_Suppress
OGRLineString* OGRLineString::TransferMembersAndDestroy(
    OGRLineString* poSrc,
    OGRLineString* poDst )
{
    if( poSrc->Is3D())
        poDst->flags |= OGR_G_3D;
    if( poSrc->IsMeasured())
        poDst->flags |= OGR_G_MEASURED;
    poDst->assignSpatialReference(poSrc->getSpatialReference());
    poDst->nPointCount = poSrc->nPointCount;
    poDst->paoPoints = poSrc->paoPoints;
    poDst->padfZ = poSrc->padfZ;
    poDst->padfM = poSrc->padfM;
    poSrc->nPointCount = 0;
    poSrc->paoPoints = nullptr;
    poSrc->padfZ = nullptr;
    poSrc->padfM = nullptr;
    delete poSrc;
    return poDst;
}
//! @endcond
/************************************************************************/
/*                         CastToLinearRing()                           */
/************************************************************************/

/**
 * \brief Cast to linear ring.
 *
 * The passed in geometry is consumed and a new one returned (or NULL in case
 * of failure)
 *
 * @param poLS the input geometry - ownership is passed to the method.
 * @return new geometry.
 */

OGRLinearRing* OGRLineString::CastToLinearRing( OGRLineString* poLS )
{
    if( poLS->nPointCount < 2 || !poLS->get_IsClosed() )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Cannot convert non-closed linestring to linearring");
        delete poLS;
        return nullptr;
    }
    OGRLinearRing* poLR = new OGRLinearRing();
    TransferMembersAndDestroy(poLS, poLR);
    return poLR;
}

//! @cond Doxygen_Suppress

/************************************************************************/
/*                     GetCasterToLineString()                          */
/************************************************************************/

static OGRLineString* CasterToLineString(OGRCurve* poCurve)
{
    return poCurve->toLineString();
}

OGRCurveCasterToLineString OGRLineString::GetCasterToLineString() const
{
    return ::CasterToLineString;
}

/************************************************************************/
/*                        GetCasterToLinearRing()                       */
/************************************************************************/

OGRLinearRing* OGRLineString::CasterToLinearRing(OGRCurve* poCurve)
{
    return OGRLineString::CastToLinearRing(poCurve->toLineString());
}

OGRCurveCasterToLinearRing OGRLineString::GetCasterToLinearRing() const
{
    return OGRLineString::CasterToLinearRing;
}

/************************************************************************/
/*                            get_Area()                                */
/************************************************************************/

double OGRLineString::get_Area() const
{
    return get_LinearArea();
}

/************************************************************************/
/*                       get_AreaOfCurveSegments()                      */
/************************************************************************/

double OGRLineString::get_AreaOfCurveSegments() const
{
    return 0;
}
//! @endcond
