/******************************************************************************
 * $Id: ogrpoint.cpp 33631 2016-03-04 06:28:09Z goatbar $
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  The Point geometry class.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 1999, Frank Warmerdam
 * Copyright (c) 2008-2011, 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_p.h"

/* for std::numeric_limits */
#include <limits>
#include <new>

CPL_CVSID("$Id: ogrpoint.cpp 33631 2016-03-04 06:28:09Z goatbar $");

/************************************************************************/
/*                              OGRPoint()                              */
/************************************************************************/

/**
 * \brief Create an empty point.
 */

OGRPoint::OGRPoint()

{
    empty();
    flags = 0;
}

/************************************************************************/
/*                              OGRPoint()                              */
/*                                                                      */
/*      Initialize point to value.                                      */
/************************************************************************/

OGRPoint::OGRPoint( double xIn, double yIn, double zIn )

{
    x = xIn;
    y = yIn;
    z = zIn;
    m = 0.0;
    flags = OGR_G_NOT_EMPTY_POINT | OGR_G_3D;
}

/************************************************************************/
/*                              OGRPoint()                              */
/*                                                                      */
/*      Initialize point to value.                                      */
/************************************************************************/

OGRPoint::OGRPoint( double xIn, double yIn )

{
    x = xIn;
    y = yIn;
    z = 0.0;
    m = 0.0;
    flags = OGR_G_NOT_EMPTY_POINT;
}

/************************************************************************/
/*                              OGRPoint()                              */
/*                                                                      */
/*      Initialize point to value.                                      */
/************************************************************************/

OGRPoint::OGRPoint( double xIn, double yIn, double zIn, double mIn )

{
    x = xIn;
    y = yIn;
    z = zIn;
    m = mIn;
    flags = OGR_G_NOT_EMPTY_POINT | OGR_G_3D | OGR_G_MEASURED;
}

/************************************************************************/
/*                       OGRPoint( const OGRPoint& )                    */
/************************************************************************/

/**
 * \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
 */

OGRPoint::OGRPoint( const OGRPoint& other ) :
    OGRGeometry( other ),
    x( other.x ),
    y( other.y ),
    z( other.z ),
    m( other.m )
{
}

/************************************************************************/
/*                             ~OGRPoint()                              */
/************************************************************************/

OGRPoint::~OGRPoint()

{
}

/************************************************************************/
/*                       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
 */

OGRPoint& OGRPoint::operator=( const OGRPoint& other )
{
    if( this != &other)
    {
        OGRGeometry::operator=( other );

        x = other.x;
        y = other.y;
        z = other.z;
        m = other.m;
    }
    return *this;
}

/************************************************************************/
/*                               clone()                                */
/*                                                                      */
/*      Make a new object that is a copy of this object.                */
/************************************************************************/

OGRGeometry *OGRPoint::clone() const

{
    OGRPoint    *poNewPoint = new (std::nothrow) OGRPoint( x, y, z, m );
    if( poNewPoint == NULL )
        return NULL;

    poNewPoint->assignSpatialReference( getSpatialReference() );
    poNewPoint->flags = flags;

    return poNewPoint;
}

/************************************************************************/
/*                               empty()                                */
/************************************************************************/
void OGRPoint::empty()

{
    x = y = z = m = 0.0;
    flags &= ~OGR_G_NOT_EMPTY_POINT;
}

/************************************************************************/
/*                            getDimension()                            */
/************************************************************************/

int OGRPoint::getDimension() const

{
    return 0;
}

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

OGRwkbGeometryType OGRPoint::getGeometryType() const

{
    if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
        return wkbPointZM;
    else if( flags & OGR_G_MEASURED )
        return wkbPointM;
    else if( flags & OGR_G_3D )
        return wkbPoint25D;
    else
        return wkbPoint;
}

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

const char * OGRPoint::getGeometryName() const

{
    return "POINT";
}

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

void OGRPoint::flattenTo2D()

{
    z = m = 0;
    flags &= ~OGR_G_3D;
    setMeasured(FALSE);
}

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

void OGRPoint::setCoordinateDimension( int nNewDimension )

{
    if( nNewDimension == 2 )
        flattenTo2D();
    else if( nNewDimension == 3 )
        flags |= OGR_G_3D;

    setMeasured(FALSE);
}

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

int OGRPoint::WkbSize() const

{
    if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
        return 37;
    else if( (flags & OGR_G_3D) || (flags & OGR_G_MEASURED) )
        return 29;
    else
        return 21;
}

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

OGRErr OGRPoint::importFromWkb( unsigned char * pabyData,
                                int nSize,
                                OGRwkbVariant eWkbVariant )

{
    OGRwkbByteOrder     eByteOrder;

    flags = 0;
    OGRErr eErr = importPreambuleFromWkb( pabyData, nSize, eByteOrder, eWkbVariant );
    pabyData += 5;
    if( eErr != OGRERR_NONE )
        return eErr;

    if( nSize != -1 )
    {
        if( (nSize < 37) && ((flags & OGR_G_3D) && (flags & OGR_G_MEASURED)) )
            return OGRERR_NOT_ENOUGH_DATA;
        else if( (nSize < 29) && ((flags & OGR_G_3D) || (flags & OGR_G_MEASURED)) )
            return OGRERR_NOT_ENOUGH_DATA;
        else if( nSize < 21 )
            return OGRERR_NOT_ENOUGH_DATA;
    }

/* -------------------------------------------------------------------- */
/*      Get the vertex.                                                 */
/* -------------------------------------------------------------------- */
    memcpy( &x, pabyData, 8 );
    pabyData += 8;
    memcpy( &y, pabyData, 8 );
    pabyData += 8;

    if( OGR_SWAP( eByteOrder ) )
    {
        CPL_SWAPDOUBLE( &x );
        CPL_SWAPDOUBLE( &y );
    }

    if( flags & OGR_G_3D )
    {
        memcpy( &z, pabyData, 8 );
        pabyData += 8;
        if( OGR_SWAP( eByteOrder ) )
            CPL_SWAPDOUBLE( &z );
    }
    else
    {
        z = 0;
    }
    if( flags & OGR_G_MEASURED )
    {
        memcpy( &m, pabyData, 8 );
        /*pabyData += 8; */
        if( OGR_SWAP( eByteOrder ) )
        {
            CPL_SWAPDOUBLE( &m );
        }
    }
    else
    {
        m = 0;
    }

    /* Detect coordinates are not NaN --> NOT EMPTY */
    if(!( x != x && y != y ))
        flags |= OGR_G_NOT_EMPTY_POINT;

    return OGRERR_NONE;
}

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

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

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

/* -------------------------------------------------------------------- */
/*      Set the geometry feature type.                                  */
/* -------------------------------------------------------------------- */

    GUInt32 nGType = getGeometryType();

    if( eWkbVariant == wkbVariantPostGIS1 )
    {
        nGType = wkbFlatten(nGType);
        if( Is3D() )
            nGType = (OGRwkbGeometryType)(nGType | wkb25DBitInternalUse); /* yes we explicitly set wkb25DBit */
        if( IsMeasured() )
            nGType = (OGRwkbGeometryType)(nGType | 0x40000000);
    }
    else if ( eWkbVariant == wkbVariantIso )
        nGType = getIsoGeometryType();

    if( eByteOrder == wkbNDR )
        nGType = CPL_LSBWORD32( nGType );
    else
        nGType = CPL_MSBWORD32( nGType );

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

/* -------------------------------------------------------------------- */
/*      Copy in the raw data. Swap if needed.                           */
/* -------------------------------------------------------------------- */

    if ( IsEmpty() && eWkbVariant == wkbVariantIso )
    {
        double dNan = std::numeric_limits<double>::quiet_NaN();
        memcpy( pabyData, &dNan, 8 );
        if( OGR_SWAP( eByteOrder ) )
            CPL_SWAPDOUBLE( pabyData );
        pabyData += 8;
        memcpy( pabyData, &dNan, 8 );
        if( OGR_SWAP( eByteOrder ) )
            CPL_SWAPDOUBLE( pabyData );
        pabyData += 8;
        if( flags & OGR_G_3D ) {
            memcpy( pabyData, &dNan, 8 );
            if( OGR_SWAP( eByteOrder ) )
                CPL_SWAPDOUBLE( pabyData );
            pabyData += 8;
        }
        if( flags & OGR_G_MEASURED ) {
            memcpy( pabyData, &dNan, 8 );
            if( OGR_SWAP( eByteOrder ) )
                CPL_SWAPDOUBLE( pabyData );
        }
    }
    else
    {
        memcpy( pabyData, &x, 8 );
        if( OGR_SWAP( eByteOrder ) )
            CPL_SWAPDOUBLE( pabyData );
        pabyData += 8;
        memcpy( pabyData, &y, 8 );
        if( OGR_SWAP( eByteOrder ) )
            CPL_SWAPDOUBLE( pabyData );
        pabyData += 8;
        if( flags & OGR_G_3D ) {
            memcpy( pabyData, &z, 8 );
            if( OGR_SWAP( eByteOrder ) )
                CPL_SWAPDOUBLE( pabyData );
            pabyData += 8;
        }
        if( flags & OGR_G_MEASURED )
        {
            memcpy( pabyData, &m, 8 );
            if( OGR_SWAP( eByteOrder ) )
                CPL_SWAPDOUBLE( pabyData );
        }
    }

    return OGRERR_NONE;
}

/************************************************************************/
/*                           importFromWkt()                            */
/*                                                                      */
/*      Instantiate point from well known text format ``POINT           */
/*      (x,y)''.                                                        */
/************************************************************************/

OGRErr OGRPoint::importFromWkt( char ** ppszInput )

{
    int bHasZ = FALSE, bHasM = FALSE;
    bool bIsEmpty = false;
    OGRErr      eErr = importPreambuleFromWkt(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 )
    {
        // we should be at the end
        if( !((*ppszInput[0] == '\000') || (*ppszInput[0] == ',')) )
            return OGRERR_CORRUPT_DATA;
        return OGRERR_NONE;
    } else
        flags |= OGR_G_NOT_EMPTY_POINT;

    const char  *pszInput = *ppszInput;

/* -------------------------------------------------------------------- */
/*      Read the point list which should consist of exactly one point.  */
/* -------------------------------------------------------------------- */
    OGRRawPoint         *poPoints = NULL;
    double              *padfZ = NULL;
    double              *padfM = NULL;
    int                 nMaxPoint = 0, nPoints = 0;
    int                 flagsFromInput = flags;

    pszInput = OGRWktReadPointsM( pszInput, &poPoints, &padfZ, &padfM, &flagsFromInput,
                                  &nMaxPoint, &nPoints );
    if( pszInput == NULL || nPoints != 1 )
    {
        CPLFree( poPoints );
        CPLFree( padfZ );
        CPLFree( padfM );
        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;
    }

    x = poPoints[0].x;
    y = poPoints[0].y;

    CPLFree( poPoints );

    if( bHasZ )
    {
        if( padfZ != NULL )
            z = padfZ[0];
    }
    if( bHasM )
    {
        if( padfM != NULL )
            m = padfM[0];
    }
    if( padfZ != NULL )
        CPLFree( padfZ );
    if( padfM != NULL )
        CPLFree( padfM );

    *ppszInput = (char *) pszInput;

    return OGRERR_NONE;
}

/************************************************************************/
/*                            exportToWkt()                             */
/*                                                                      */
/*      Translate this structure into it's well known text format       */
/*      equivalent.                                                     */
/************************************************************************/

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

{
    char        szTextEquiv[180];
    char        szCoordinate[80];

    if ( IsEmpty() )
    {
        if( eWkbVariant == wkbVariantIso )
        {
            if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
                *ppszDstText = CPLStrdup("POINT ZM EMPTY");
            else if( flags & OGR_G_MEASURED )
                *ppszDstText = CPLStrdup("POINT M EMPTY");
            else if( flags & OGR_G_3D )
                *ppszDstText = CPLStrdup("POINT Z EMPTY");
            else
                *ppszDstText = CPLStrdup("POINT EMPTY");
        }
        else
            *ppszDstText = CPLStrdup("POINT EMPTY");
    }
    else
    {
        if( eWkbVariant == wkbVariantIso )
        {
            OGRMakeWktCoordinateM(szCoordinate, x, y, z, m, Is3D(), IsMeasured());
            if( (flags & OGR_G_3D) && (flags & OGR_G_MEASURED) )
                snprintf( szTextEquiv, sizeof(szTextEquiv), "POINT ZM (%s)", szCoordinate);
            else if( flags & OGR_G_MEASURED )
                snprintf( szTextEquiv, sizeof(szTextEquiv), "POINT M (%s)", szCoordinate);
            else if( flags & OGR_G_3D )
                snprintf( szTextEquiv, sizeof(szTextEquiv), "POINT Z (%s)", szCoordinate);
            else
                snprintf( szTextEquiv, sizeof(szTextEquiv), "POINT (%s)", szCoordinate);
        }
        else {
            OGRMakeWktCoordinateM(szCoordinate, x, y, z, m, Is3D(), FALSE);
            snprintf( szTextEquiv, sizeof(szTextEquiv), "POINT (%s)", szCoordinate );
        }
        *ppszDstText = CPLStrdup( szTextEquiv );
    }

    return OGRERR_NONE;
}

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

void OGRPoint::getEnvelope( OGREnvelope * psEnvelope ) const

{
    psEnvelope->MinX = psEnvelope->MaxX = getX();
    psEnvelope->MinY = psEnvelope->MaxY = getY();
}

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

void OGRPoint::getEnvelope( OGREnvelope3D * psEnvelope ) const

{
    psEnvelope->MinX = psEnvelope->MaxX = getX();
    psEnvelope->MinY = psEnvelope->MaxY = getY();
    psEnvelope->MinZ = psEnvelope->MaxZ = getZ();
}


/**
 * \fn double OGRPoint::getX() const;
 *
 * \brief Fetch X coordinate.
 *
 * Relates to the SFCOM IPoint::get_X() method.
 *
 * @return the X coordinate of this point.
 */

/**
 * \fn double OGRPoint::getY() const;
 *
 * \brief Fetch Y coordinate.
 *
 * Relates to the SFCOM IPoint::get_Y() method.
 *
 * @return the Y coordinate of this point.
 */

/**
 * \fn double OGRPoint::getZ() const;
 *
 * \brief Fetch Z coordinate.
 *
 * Relates to the SFCOM IPoint::get_Z() method.
 *
 * @return the Z coordinate of this point, or zero if it is a 2D point.
 */

/**
 * \fn void OGRPoint::setX( double xIn );
 *
 * \brief Assign point X coordinate.
 *
 * There is no corresponding SFCOM method.
 */

/**
 * \fn void OGRPoint::setY( double yIn );
 *
 * \brief Assign point Y coordinate.
 *
 * There is no corresponding SFCOM method.
 */

/**
 * \fn void OGRPoint::setZ( double zIn );
 *
 * \brief Assign point Z coordinate.
 * Calling this method will force the geometry
 * coordinate dimension to 3D (wkbPoint|wkbZ).
 *
 * There is no corresponding SFCOM method.
 */

/************************************************************************/
/*                               Equal()                                */
/************************************************************************/

OGRBoolean OGRPoint::Equals( OGRGeometry * poOther ) const

{
    if( poOther== this )
        return TRUE;

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

    OGRPoint    *poOPoint = (OGRPoint *) poOther;
    if ( flags != poOPoint->flags )
        return FALSE;

    if ( IsEmpty() )
        return TRUE;

    // we should eventually test the SRS.
    if( poOPoint->getX() != getX()
        || poOPoint->getY() != getY()
        || poOPoint->getZ() != getZ() )
        return FALSE;
    else
        return TRUE;
}

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

OGRErr OGRPoint::transform( OGRCoordinateTransformation *poCT )

{
#ifdef DISABLE_OGRGEOM_TRANSFORM
    return OGRERR_FAILURE;
#else
    if( poCT->Transform( 1, &x, &y, &z ) )
    {
        assignSpatialReference( poCT->GetTargetCS() );
        return OGRERR_NONE;
    }
    else
        return OGRERR_FAILURE;
#endif
}

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

void OGRPoint::swapXY()
{
    double dfTemp = x;
    x = y;
    y = dfTemp;
}

/************************************************************************/
/*                               Within()                               */
/************************************************************************/

OGRBoolean OGRPoint::Within( const OGRGeometry *poOtherGeom ) const

{
    if( !IsEmpty() && poOtherGeom != NULL &&
        wkbFlatten(poOtherGeom->getGeometryType()) == wkbCurvePolygon )
    {
        return ((OGRCurvePolygon*)poOtherGeom)->Contains(this);
    }
    else
        return OGRGeometry::Within(poOtherGeom);
}

/************************************************************************/
/*                              Intersects()                            */
/************************************************************************/

OGRBoolean OGRPoint::Intersects( const OGRGeometry *poOtherGeom ) const

{
    if( !IsEmpty() && poOtherGeom != NULL &&
        wkbFlatten(poOtherGeom->getGeometryType()) == wkbCurvePolygon )
    {
        return ((OGRCurvePolygon*)poOtherGeom)->Intersects(this);
    }
    else
        return OGRGeometry::Intersects(poOtherGeom);
}
