/*
 * Local function prototypes
 */

static int _calc_shapelen (
    GeomType type,
    int      numpoints,
    int      numparts
    );

static void _get_extrema (
    int       num_points,
    Point    *pt,
    double   *z,
    double   *m,
    double   *xy_bnd,
    double   *z_bnd,
    double   *m_bnd
    );

static int _get_shp_parts (
    char     *cp,
    int       swap_bytes,
    Geometry *geom
    );

static int _get_shp_multipoint (
    char     *cp,
    int       swap_bytes,
    Geometry *geom
    );

static int _get_shp_point (
    char     *cp,
    int       swap_bytes,
    Geometry *geom
    );


/***************************************************************************
*
*  geom_to_shape  - takes a geometry and converts it into a shape
*                   representation.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Purpose:
*    Converts a geometry into its ESRI shape representation.
*  
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Parameters:
*    geom          <Input>  == (Geometry *) The geometry object
*    max_alloced   <In/Out> == (int *) The maximum number of bytes
*                                      allocated to the binary array.
*    data_len      <Output> == (int *) The number of bytes in the shape.
*    binary        <Output> == (char **) The shape binary.
*
*    RETURN        <Output> == (void)
*
****************************************************************************/
void geom_to_shape (
    Geometry *geom,
    int      *max_alloced,
    int      *data_len,
    char    **binary
    )
{
    char     *cp;
    double    xy_bnd[4];
    double    z_bnd[2];
    double    m_bnd[2];
    int       i;
    int       swap;
    int       npoints = geom->num_points;
    int       nparts  = geom->num_parts;
    Point    *pt      = geom->pt;
    double   *m       = geom->m;
    double   *z       = geom->z;
    ShpType   type;

    ENDIAN_TEST (i);  /* Sets i = 0 if big-endian, 1 if little-endian */
    swap = 1 - i;
    
    /* Calculate data_len. */

    *data_len = _calc_shapelen (geom->type, npoints, nparts);


    /* Allocate memory to the binary character array, set max_alloced
       equal to data_len if this is the first call to put_shape.
       Otherwise, if the data_len is larger then max_alloced,
       reallocate the larger data_len to the binary character array. */

    if (*max_alloced == 0)
    {
        *binary = (char *)malloc (*data_len);
        *max_alloced = *data_len;
    }
    else if (*max_alloced < *data_len)
    {
        *binary = (char *) realloc (*binary, *data_len);
        *max_alloced = *data_len;
    }

    /* Copy the address of the binary character array to the
       character pointer cp. */
 
    cp = *binary;

    
    /* The put_integer function byte swaps an integer value if necessary,
       and advances the cp character pointer after the interger value
       just added.

       The put_xy and put_double functions operate in the same manner as
       put_integer does, except they add an array of double precision
       numbers. */

    switch (geom->type)
    {
        case geomEmpty:
            /* Geometry is empty so the buffer only contains its datatype */
            type = shpNil;
            put_integer (&cp, swap, 1, (int *)&type);
            break;
                      
        case geomPoint:   /* Point */
            type = shpPoint;
            put_integer (&cp, swap, 1, (int *)&type);

            put_xy (&cp, swap, 1, pt);            /* Add the X, Y coordinate */
            break;

        case geomPointM:  /* Point with measure */
            type = shpPointM;
            put_integer (&cp, swap, 1, (int *)&type);

            put_xy (&cp, swap, 1, pt);            /* Add the X, Y coordinate */
            put_double (&cp, swap, 1, m);                 /* Add the measure */
            break;

        case geomPointZ:  /* Point with Z coordinate */
            type = shpPointZ;
            put_integer (&cp, swap, 1, (int *)&type);

            put_xy (&cp, swap, 1, pt);            /* Add the X, Y coordinate */
            put_double (&cp, swap, 1, z);            /* Add the Z coordinate */
            break;

        case geomPointZM:  /* Point with Z coordinate and measure. */
            type = shpPointZM;
            put_integer (&cp, swap, 1, (int *)&type);

            put_xy (&cp, swap, 1, pt);            /* Add the X, Y coordinate */
            put_double (&cp, swap, 1, z);            /* Add the Z coordinate */
            put_double (&cp, swap, 1, m);                 /* Add the measure */
            break;

        case geomMultiPoint:  /* Multipoint. */
            type = shpMultiPoint;
            put_integer (&cp, swap, 1, (int *)&type);

            /* Calculate the extreme X, Y. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            put_xy (&cp, swap, npoints, pt);          /* Add the X, Y coords */
            break;

        case geomMultiPointM: /* Multipoint with measure. */
            type = shpMultiPointM;
            put_integer (&cp, swap, 1, (int *)&type);

            /* Calculate the extreme X, Y and measures */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            put_xy (&cp, swap, npoints, pt);          /* Add the X, Y coords */
            put_double (&cp, swap, 2, m_bnd);    /* Add the extreme measures */
            put_double (&cp, swap, npoints, m);          /* Add the measures */
            break;

        case geomMultiPointZ: /* Multipoint with Z ordinates */
            type = shpMultiPointZ;
            put_integer (&cp, swap, 1, (int *)&type);

            /* Calculate the extreme X, Y, Z and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            put_xy (&cp, swap, npoints, pt);     /* Add the X, Y coordinates */
            put_double (&cp, swap, 2, z_bnd);           /* Add the Z extrema */
            put_double (&cp, swap, npoints, z);       /* Add the Z ordinates */
            break;

        case geomMultiPointZM: /* Multipoint with Z ordinates and measures */
            type = shpMultiPointZM;
            put_integer (&cp, swap, 1, (int *)&type);

            /* Calculate the extreme X, Y, Z and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            put_xy (&cp, swap, npoints, pt);          /* Add the X, Y coords */
            put_double (&cp, swap, 2, z_bnd);           /* Add the Z extreme */
            put_double (&cp, swap, npoints, z);       /* Add the Z ordinates */
            put_double (&cp, swap, 2, m_bnd);    /* Add the extreme measures */
            put_double (&cp, swap, npoints, m);          /* Add the measures */
            break;

        case geomLineString:
        case geomMultiLineString:
            type = shpPolyline;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);      /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);          /* Add the point array */
            break;

        case geomLineStringM:
        case geomMultiLineStringM:
            type = shpPolylineM;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);      /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);         /* Add the points array */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            put_double (&cp, swap, 2, m_bnd);    /* Add the measure extremes */
            put_double (&cp, swap, npoints, m);    /* Add the measures array */
            break;

        case geomLineStringZ: 
        case geomMultiLineStringZ: 
            type = shpPolylineZ;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y, Z and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);
     
            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);     /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);       /* Add the no. of points */
            put_double (&cp, swap, 2, z_bnd);         /* Add the Z extremes */
            put_double (&cp, swap, npoints, z);          /* Add the Z array */
            break;

        case geomLineStringZM: 
        case geomMultiLineStringZM: 
            type = shpPolylineZM;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y, Z and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);
     
            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);      /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);        /* Add the no. of points */
            put_double (&cp, swap, 2, z_bnd);           /* Add the Z extemes */
            put_double (&cp, swap, npoints, z);           /* Add the Z array */
            put_double (&cp, swap, 2, m_bnd);    /* Add the measure extremes */
            put_double (&cp, swap, npoints, m);     /* Add the measure array */
            break;

        case geomPolygon:
        case geomMultiPolygon:
            type = shpPolygon;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);      /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);          /* Add the point array */
            break;

        case geomPolygonM:
        case geomMultiPolygonM:
            type = shpPolygonM;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);

            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);      /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);         /* Add the points array */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            put_double (&cp, swap, 2, m_bnd);    /* Add the measure extremes */
            put_double (&cp, swap, npoints, m);    /* Add the measures array */
            break;

        case geomPolygonZ:
        case geomMultiPolygonZ:
            type = shpPolygonZ;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y, Z and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);
     
            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);     /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);        /* Add the no. of points */
            put_double (&cp, swap, 2, z_bnd);          /* Add the Z extremes */
            put_double (&cp, swap, npoints, z);           /* Add the Z array */
            break;

        case geomPolygonZM:
        case geomMultiPolygonZM:
            type = shpPolygonZM;
            put_integer (&cp, swap, 1, (int *)&type);
            /* Calculate the extreme X, Y, Z and measures. */
            _get_extrema (npoints, pt, z, m, xy_bnd, z_bnd, m_bnd);
     
            put_double (&cp, swap, 4, xy_bnd);      /* Add the X, Y extremes */
            put_integer (&cp, swap, 1, &nparts);     /* Add the no. of parts */
            put_integer (&cp, swap, 1, &npoints);   /* Add the no. of points */
            if (nparts > 1)
            {
                /* There is more that one part; add the part offsets array */
                put_integer (&cp, swap, nparts, geom->offsets);
            }
            else
            {
                /* There is only one part; set the part offsets array to 0. */
                memset (cp, 0, sizeof (int));
                cp += sizeof (int);      /* Advance the character pointer cp */
            }
            put_xy (&cp, swap, npoints, pt);        /* Add the no. of points */
            put_double (&cp, swap, 2, z_bnd);           /* Add the Z extemes */
            put_double (&cp, swap, npoints, z);           /* Add the Z array */
            put_double (&cp, swap, 2, m_bnd);    /* Add the measure extremes */
            put_double (&cp, swap, npoints, m);     /* Add the measure array */
            break;
    }

}


/***************************************************************************
*
*  _calc_shapelen  -  Calculates the number of bytes in the shape.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Purpose:
*    Calculate the number of bytes in the shape.
*  
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Parameters:
*    type          <Input>  == (GeomType) The geometry data type.
*    num_points    <Input>  == (int) The number of points in the geometry.
*    num_parts     <Input>  == (int) The number of parts into the geometry.
*
*    RETURN        <Output> == (void)
*
****************************************************************************/

static int _calc_shapelen (
    GeomType type,
    int      numpoints,
    int      numparts
    )
{

    int data_len;
 
    data_len = sizeof (int);  /* Add the size of the data type */

    switch (type)      
    {
        case geomEmpty:        /* Shape is empty. */
            break;

        case geomPoint:         /* Shape is a point. */
            data_len += (2 * sizeof (double));
            break;

        case geomPointZ:        /* Shape is a point with a Z coordinate */
        case geomPointM:        /* Shape is a point with a measure. */
            data_len += (3 * sizeof (double));
            break;

        case geomPointZM:        /* Shape is a point with a Z coordinate and a measure */
            data_len += (4 * sizeof (double));
            break;

        case geomMultiPoint:    /* Shape is a multipoint. */
            data_len += ((2 * numpoints) + 4) * sizeof(double) + sizeof (int);
            break;

        case geomMultiPointZ:   /* Shape is a multipoint with Z coordinates. */
        case geomMultiPointM:   /* Shape is a multipoint with measures. */
            data_len += ((3 * numpoints) + 6) * sizeof(double) + sizeof (int);
            break;

        case geomMultiPointZM:  /* Shape is a multipoint with Z coords and measures */
            data_len += ((4 * numpoints) + 8) * sizeof(double) + sizeof (int);
            break;

        case geomLineString:  /* Shape is a polyline or polygon. */
        case geomMultiLineString:
        case geomPolygon:
        case geomMultiPolygon:
            data_len += (2 + numparts) * sizeof (int);
            data_len += ((2 * numpoints) + 4) * sizeof(double);
            break;

        case geomLineStringM:  /* Shape is a polyline or polygon with measures. */
        case geomMultiLineStringM:
        case geomPolygonM:
        case geomMultiPolygonM:
        case geomLineStringZ:  /* Shape is a polyline or polygon with Z coords. */
        case geomMultiLineStringZ:
        case geomPolygonZ:
        case geomMultiPolygonZ:
            data_len += (2 + numparts) * sizeof (int);
            data_len += ((3 * numpoints) + 6) * sizeof(double);
            break;

        case geomLineStringZM:  /* Shape is a polyline or polygon with */
        case geomPolygonZM:     /* Z coordinates and measures. */
        case geomMultiLineStringZM:
        case geomMultiPolygonZM:
            data_len += (2 + numparts) * sizeof (int);
            data_len += ((4 * numpoints) + 8) * sizeof(double);
            break;

    }

    return data_len;

}

/***************************************************************************
*
*  _get_extrema  -  Calculates the extremes of the X, Y, Z and measures.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Purpose:
*    Calculates the extremes of the X, Y, Z and measures.
*  
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Parameters:
*    num_points  <Input>  == (int) The number of points in the geometry.
*    pt          <Input>  == (Point *) Array of X, Y coordinates.
*    z           <Input>  == (double *) Array of Z ordinates.
*    m           <Input>  == (double *) Array of measures.
*    xy_bnd      <Output> == (double *) X, Y coordinate extremes.
*    z_bnd       <Output> == (double *) Z ordinate extremes.
*    m_bnd       <Output> == (double *) Measure extremes.
*
*    RETURN      <Output> == (void)
*
****************************************************************************/

static void _get_extrema (
    int       num_points,
    Point    *pt,
    double   *z,
    double   *m,
    double   *xy_bnd,
    double   *z_bnd,
    double   *m_bnd
    )
{
    int i;

    /* Initialize the extremes */

    xy_bnd[0] = xy_bnd[1] =  DBL_MAX;     /* min x, min y */
    xy_bnd[2] = xy_bnd[3] = -DBL_MAX;     /* max x, max y */
    z_bnd[0]  = m_bnd[0]  =  DBL_MAX;     /* min z, min m */
    z_bnd[1]  = m_bnd[1]  = -DBL_MAX;     /* max z, max m */

    for (i = 0; i < num_points; i++)
    {
        /* Find the min & max values */

        if (pt[i].x < xy_bnd[0])
            xy_bnd[0] = pt[i].x;
        if (pt[i].y < xy_bnd[1])
            xy_bnd[1] = pt[i].y;
        if (pt[i].x > xy_bnd[2])
            xy_bnd[2] = pt[i].x;
        if (pt[i].y > xy_bnd[3])
            xy_bnd[3] = pt[i].y;

        if (z)
        {
            if (z[i] < z_bnd[0])
                z_bnd[0] = z[i];
            if (z[i] > z_bnd[1])
                z_bnd[1] = z[i];
        }

        if (m)
        {
            if (m[i] < m_bnd[0])
                m_bnd[0] = m[i];
            if (m[i] > m_bnd[1])
                m_bnd[1] = m[i];
        }
    }
}


/***************************************************************************
*
*  geom_to_shape  - takes a geometry and converts it into a shape
*                   representation.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Purpose:
*    Converts a geometry into a shape.
*  
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Parameters:
*    binary         <Input> == (char **) The shape binary.
*
*    RETURN        <Output> == (int) Error code
*
****************************************************************************/
int shape_to_geom (
    char      *binary,
    Geometry  *geom
    )
{
    ShpType    shape_type;
    int        rc;
    char      *cp;
    int        swap_bytes;
    int        i;
    
    ENDIAN_TEST (i);  /* Sets i = 0 if big-endian, 1 if little-endian */
    swap_bytes = 1 - i;

    cp = binary;
    get_integer (&cp, swap_bytes, 1, (int *) &shape_type);

    switch (shape_type)
    {
        /* copy the points based on shape type */

        case shpNil:
            /* Nothing to do, we already made the shape nil */
            break;

        case shpPoint:
            geom->type = geomPoint;
            rc = _get_shp_point (cp, swap_bytes, geom);
            break;

        case shpPointM:
            geom->type = geomPointM;
            rc = _get_shp_point (cp, swap_bytes, geom);
            break;

        case shpPointZ:
            geom->type = geomPointZ;
            rc = _get_shp_point (cp, swap_bytes, geom);
            break;

        case shpPointZM:
            geom->type = geomPointZM;
            rc = _get_shp_point (cp, swap_bytes, geom);
            break;

        case shpMultiPoint:
            geom->type = geomMultiPoint;
            rc = _get_shp_multipoint (cp, swap_bytes, geom);
            break;

        case shpMultiPointM:
            geom->type = geomMultiPoint;
            rc = _get_shp_multipoint (cp, swap_bytes, geom);
            break;

        case shpMultiPointZ:
            geom->type = geomMultiPointZ;
            rc = _get_shp_multipoint (cp, swap_bytes, geom);
            break;

        case shpMultiPointZM:
            geom->type = geomMultiPointZM;
            rc = _get_shp_multipoint (cp, swap_bytes, geom);
            break;

        case shpPolyline:
            geom->type = geomLineString;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        case shpPolygon:
            geom->type = geomPolygon;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        case shpPolylineM:
            geom->type = geomLineStringM;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        case shpPolygonM:
            geom->type = geomPolygonM;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        case shpPolylineZ:
            geom->type = geomLineStringZ;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        case shpPolygonZ:
            geom->type = geomPolygonZ;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        case shpPolylineZM:
            geom->type = geomLineStringZM;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        case shpPolygonZM:
            geom->type = geomPolygonZM;
            rc = _get_shp_parts (cp, swap_bytes, geom);
            break;

        default:
            rc = GEOM_INVALID_SHP_TYPE;
    }

    return rc;
}



/***************************************************************************
*
*  _get_shp_parts  -  Extracts polygon or polyline parts from the
*                     ESRI binary shape representation of a geometry.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Purpose:
*    This function builds a polygon or polyline shape from the ESRI binary 
*    shape representation of a geometry. Since it is a common routine for
*    lines and polygons, and the "multi" equivalents, it does not set
*    set the geometry, or verify the geometry.  This is the caller's
*    responsibility. This function just fills in the numofpts, pt, zpt,
*    and mval fields.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*A  Parameters:
*     cp           <In/Out> == (char *) a pointer into the binary buffer
*     swap_bytes   <Input>  == (int) 1 if bytes need to be swapped   
*     geom         <In/Out> == (Geometry *) The geometry object
*
*     RETURN       <Output> == (int) Error code
*
****************************************************************************/

static int _get_shp_parts (
    char     *cp,
    int       swap_bytes,
    Geometry *geom
    )
{
    int       rc;
    int       num_points;
    int       num_parts;
    int       has_m = geom->type & 0x01;
    int       has_z = geom->type & 0x02;
    int       prev_offset;
    int       i;
    
    /* Skip over the double precision bounding box, we will
       calculate the envelope when needed */
    cp += 4 * sizeof(double);
       
    /* Get the number of parts */
    get_integer (&cp, swap_bytes, 1, &num_parts);
    if (num_parts <= 0)
        return GEOM_INVALID_NUM_PARTS;

    /* Get the total number of points */
    get_integer (&cp, swap_bytes, 1, &num_points);
    if (num_points <= 0)
        return GEOM_TOO_FEW_POINTS;

    /* Allocate the parts offsets, then extract them */
    if (geom->offsets != NULL)
        free (geom->offsets);
    
    geom->offsets = malloc (num_parts * sizeof(int));
    if (NULL == geom->offsets)
        return GEOM_OUT_OF_MEMORY;

    get_integer (&cp, swap_bytes, num_parts, geom->offsets);

    /* make sure each part offset is legal */
    prev_offset = 0;
    if (geom->offsets[0] != 0)
        return GEOM_INVALID_PART_OFFSET;

    for (i = 1; i < num_parts; i++)
    {
        if (geom->offsets[i] < prev_offset + 2)
        {
            /* Offsets should be monotonically increasing,
               and each part needs at least 2 points */
            return GEOM_INVALID_PART_OFFSET;
        }
        prev_offset = geom->offsets[i];
    }

    /* Check the last offset */
    if (num_points < prev_offset + 2)
        return GEOM_INVALID_PART_OFFSET;


    /* Allocate space for the geometry points. */
    rc = geom_allocate (geom, num_points);
    if (rc != GEOM_SUCCESS)
        return rc;
    geom->num_points = num_points;
    
    get_xy (&cp, swap_bytes, num_points, geom->pt);

    if (has_z)
    {
        cp += 2 * sizeof(double);        /* Skip over Z extrema */
        get_double (&cp, swap_bytes, num_points, geom->z);
    }

    if (has_m)
    {
        cp += 2 * sizeof(double);        /* Skip over M extrema */
        get_double (&cp, swap_bytes, num_points, geom->m);
    }
    
  return GEOM_SUCCESS;
}

/***************************************************************************
*
*  _get_shp_multipoint  -  Extracts a multipoint shape from the ESRI binary
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Purpose:
*    This function builds a multipoint shape from the ESRI binary shape
*    representation of a geometry.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Parameters:
*    cp           <In/Out> == (char *) a pointer into the binary buffer
*    swap_bytes    <Input> == (int) 1 if bytes need to be swapped   
*    geom         <In/Out> == (Geometry *) The geometry object
*
*    RETURN       <Output> == (int) Error code
*
****************************************************************************/

static int _get_shp_multipoint (
    char     *cp,
    int       swap_bytes,
    Geometry *geom
    )
{
    int       rc;
    int       num_points;
    int       has_m = geom->type & 0x01;
    int       has_z = geom->type & 0x02;
    
    /* Skip over the double precision bounding box, we will
       calculate the envelope when needed */

    cp += 4 * sizeof(double);
       
    get_integer (&cp, swap_bytes, 1, &num_points);
    if (num_points <= 0)
        return GEOM_TOO_FEW_POINTS;

    /* Allocate space for the shape points.  */
    rc = geom_allocate(geom, num_points);
    if (rc != GEOM_SUCCESS)
        return rc;
    geom->num_points = num_points;

    
    /* Get an array of xy points */
    get_xy (&cp, swap_bytes, num_points, geom->pt);

    if (has_z)
    {
        /* Skip over the double precision Z extrema */
        cp += 2 * sizeof(double);
  
        /* Get the Z values */
        get_double (&cp, swap_bytes, num_points, geom->z);
    }

    if (has_m)
    {
        /* Skip over the double precision M extrema */
        cp += 2 * sizeof(double);

        /* Get the measure values */
        get_double (&cp, swap_bytes, num_points, geom->m);
    }

    return GEOM_SUCCESS;
}

/***************************************************************************
*
*  _get_shp_point  -  Extracts a point shape from the ESRI binary
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Purpose:
*    This function builds a point shape from the ESRI binary shape
*    representation of a shape.
*
*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
*
*  Parameters:
*    swap_bytes    <Input> == (int) 1 if bytes need to be swapped   
*    cp           <In/Out> == (char *) a pointer into the binary buffer
*    geom         <Output> == (Geometry *) The geometry object
*
*    RETURN       <Output> == (int) Error code
*
****************************************************************************/

static int _get_shp_point (
    char     *cp,
    int       swap_bytes,
    Geometry *geom
    )
{
    int       rc;
    int       has_m = geom->type & 0x01;
    int       has_z = geom->type & 0x02;
  
    /* Allocate space for the shape point */
    rc = geom_allocate(geom, 1);
    if (rc != GEOM_SUCCESS)
        return rc;
    geom->num_points = 1;
  
    /* Get a single xy point, followed by the Z and/or M values */
    get_xy (&cp, swap_bytes, 1, geom->pt);

    if (has_z)
        get_double (&cp, swap_bytes, 1, geom->z);

    if (has_m)
        get_double (&cp, swap_bytes, 1, geom->m);

    return GEOM_SUCCESS;
}