Hi,

Thought I had submitted this before with my previous SHP submissions but it must have slipped.

The attached code (from the 3.6 branch) adds a keepSeparatePoints option to the SHP plugin which makes it possible to avoid merging point features into multi-points, in case you e.g. need to keep separate point attributes.

It also contains a fix in the Xbase DBF parser, converting a numeric shape attribute to double instead of integer. As stated in e.g. https://en.wikipedia.org/wiki/.dbf the numeric field can contain decimals.

Regards,
Andreas

#include <fcntl.h>
#include <osg/Geometry>
#include <osg/Notify>
#include <osgUtil/Tessellator>

#if defined(_MSC_VER)
    #include <io.h>
    #include <stdio.h>
#endif

#include "ESRIShapeParser.h"

using namespace ESRIShape;

ESRIShapeParser::ESRIShapeParser(const std::string fileName, bool useDouble, 
bool keepSeparatePoints) :
    _valid(false),
    _useDouble(useDouble),
    _keepSeparatePoints(keepSeparatePoints)
{
    int fd = 0;
    if( !fileName.empty() )
    {
#ifdef WIN32
        if( (fd = open( fileName.c_str(), O_RDONLY | O_BINARY )) < 0 )
#else
        if( (fd = open( fileName.c_str(), O_RDONLY )) < 0 )
#endif
        {
            perror( fileName.c_str() );
            return ;
        }
    }

    _valid = true;

    ESRIShape::ShapeHeader head;
    head.read(fd);

    //head.print();

    _geode = new osg::Geode;

    switch( head.shapeType )
    {
        case ESRIShape::ShapeTypeNullShape  :
            break;

        case ESRIShape::ShapeTypePoint      :
            {
                std::vector<ESRIShape::Point> pts;
                ESRIShape::PointRecord pointRecord;
                while( pointRecord.read(fd) )
                    pts.push_back( pointRecord.point );
                _process( pts );
            }
            break;

        case ESRIShape::ShapeTypeMultiPoint :
            {
                std::vector<ESRIShape::MultiPoint> mpts;
                ESRIShape::MultiPoint mpoint;

                while( mpoint.read(fd) )
                    mpts.push_back( mpoint );

                _process(  mpts );
            }
            break;

        case ESRIShape::ShapeTypePolyLine   :
            {
                std::vector<ESRIShape::PolyLine> plines;
                ESRIShape::PolyLine pline;

                while( pline.read(fd) )
                    plines.push_back( pline );

                _process( plines );

            }
            break;

        case ESRIShape::ShapeTypePolygon    :
            {
                std::vector<ESRIShape::Polygon> polys;
                ESRIShape::Polygon poly;

                while( poly.read(fd) )
                    polys.push_back( poly );

                _process( polys );
            }
            break;

        case ESRIShape::ShapeTypePointM      :
            {
                std::vector<ESRIShape::PointM> ptms;
                ESRIShape::PointMRecord pointMRecord;
                while( pointMRecord.read(fd) )
                    ptms.push_back( pointMRecord.pointM );
                _process( ptms );
            }
            break;

        case ESRIShape::ShapeTypeMultiPointM :
            {
                std::vector<ESRIShape::MultiPointM> mptms;
                ESRIShape::MultiPointM mpointm;

                while( mpointm.read(fd) )
                    mptms.push_back( mpointm );

                _process(  mptms );
            }
            break;

        case ESRIShape::ShapeTypePolyLineM   :
            {
                std::vector<ESRIShape::PolyLineM> plinems;
                ESRIShape::PolyLineM plinem;

                while( plinem.read(fd) )
                    plinems.push_back( plinem );

                _process( plinems );
            }
            break;

        case ESRIShape::ShapeTypePolygonM    :
            {
                std::vector<ESRIShape::PolygonM> polyms;
                ESRIShape::PolygonM polym;

                while( polym.read(fd) )
                    polyms.push_back( polym );

                _process( polyms );
            }
            break;


        case ESRIShape::ShapeTypePointZ      :
            {
                std::vector<ESRIShape::PointZ> ptzs;
                ESRIShape::PointZ pointZ;
                while( pointZ.read( fd ) )
                    ptzs.push_back( pointZ );
                _process( ptzs );
            }
            break;

        case ESRIShape::ShapeTypeMultiPointZ :
            {
                std::vector<ESRIShape::MultiPointZ> mptzs;
                ESRIShape::MultiPointZ mpointz;

                while( mpointz.read(fd) )
                    mptzs.push_back( mpointz );

                _process(  mptzs );
            }
            break;

        case ESRIShape::ShapeTypePolyLineZ   :
            {
                std::vector<ESRIShape::PolyLineZ> plinezs;
                ESRIShape::PolyLineZ plinez;

                while( plinez.read(fd) )
                    plinezs.push_back( plinez );

                _process( plinezs );
            }
            break;

        case ESRIShape::ShapeTypePolygonZ    :
            {
                std::vector<ESRIShape::PolygonZ> polyzs;
                ESRIShape::PolygonZ polyz;

                while( polyz.read(fd) )
                    polyzs.push_back( polyz );

                _process( polyzs );
            }
            break;


        case ESRIShape::ShapeTypeMultiPatch  :
            {
                std::vector<ESRIShape::MultiPatch> mpatches;
                ESRIShape::MultiPatch mpatch;

                while( mpatch.read( fd ) )
                    mpatches.push_back( mpatch );

                _process(mpatches);
            }
            break;

        default:
            break;
    }

    if(fd)
    {
      close(fd);
      fd = 0;
    }
}

osg::Geode *ESRIShapeParser::getGeode()
{
    return _geode.get();
}

void ESRIShapeParser::_combinePointToMultipoint()
{
    if (!_valid || _keepSeparatePoints) return;

    OSG_NOTICE<<"_combinePointToMultipoint()"<<std::endl;

    ArrayHelper coords(_useDouble);

    unsigned int numDrawables = _geode->getNumDrawables();

    for( unsigned int i = 0; i < numDrawables; i++ )
    {
        osg::Geometry *geom = dynamic_cast<osg::Geometry 
*>(_geode->getDrawable(i));
        if( geom != 0L )
        {
            coords.add( geom->getVertexArray(), 0 );
        }
    }

    _geode->removeDrawables( 0, numDrawables );

    osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
    geometry->setVertexArray(coords.get());
    geometry->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::POINTS, 
0, coords.size()));
    _geode->addDrawable( geometry.get() );
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::Point> &pts )
{
    if( !_valid ) return;

    std::vector<ESRIShape::Point>::const_iterator p;
    for( p = pts.begin(); p != pts.end(); p++ )
    {
        ArrayHelper coords(_useDouble);

        coords.add( p->x, p->y, 0.0 );
        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());
        geometry->addPrimitiveSet( new 
osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 1));
        _geode->addDrawable( geometry.get() );
    }
    if( _geode->getNumDrawables() > 1 )
        _combinePointToMultipoint();
}


void ESRIShapeParser::_process( const std::vector<ESRIShape::MultiPoint> &mpts )
{
    if( !_valid ) return;

    std::vector<ESRIShape::MultiPoint>::const_iterator p;
    for( p = mpts.begin(); p != mpts.end(); p++ )
    {
        ArrayHelper coords(_useDouble);

        for( int i = 0; i < p->numPoints ; i++ )
            coords.add( p->points[i].x, p->points[i].y, 0.0 );

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());
        geometry->addPrimitiveSet( new 
osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, coords.size()));

        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process(const std::vector<ESRIShape::PolyLine> &lines )
{
    if( !_valid ) return;

    std::vector<ESRIShape::PolyLine>::const_iterator p;
    for( p = lines.begin(); p != lines.end(); p++ )
    {
        ArrayHelper coords(_useDouble);

        int i;
        for( i = 0; i < p->numPoints; i++ )
            coords.add( p->points[i].x, p->points[i].y, 0.0 );

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());

        for( i = 0; i < p->numParts; i++ )
        {
            int index = p->parts[i];
            int len = i < p->numParts - 1 ?
                            p->parts[i+1] - p->parts[i] :
                            p->numPoints  - p->parts[i];

            geometry->addPrimitiveSet(
                    new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, index, 
len));
        }
        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::Polygon> &polys )
{
    if( !_valid ) return;

    std::vector<ESRIShape::Polygon>::const_iterator p;
    for( p = polys.begin(); p != polys.end(); p++ )
    {
        ArrayHelper coords(_useDouble);
        int i;
        for( i = 0; i < p->numPoints; i++ )
            coords.add( p->points[i].x, p->points[i].y, 0.0 );

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());

        for( i = 0; i < p->numParts; i++ )
        {
            int index = p->parts[i];
            int len = i < p->numParts - 1 ?
                            p->parts[i+1] - p->parts[i] :
                            p->numPoints  - p->parts[i];

            geometry->addPrimitiveSet(
                    new osg::DrawArrays(osg::PrimitiveSet::POLYGON, index, 
len));
        }

        // Use osgUtil::Tessellator to handle concave polygons
        osg::ref_ptr<osgUtil::Tessellator> tscx=new osgUtil::Tessellator;
        tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
        tscx->setBoundaryOnly(false);
        tscx->setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD);

        tscx->retessellatePolygons(*(geometry.get()));

        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::PointM> &ptms )
{
    if( !_valid ) return;

    std::vector<ESRIShape::PointM>::const_iterator p;
    for( p = ptms.begin(); p != ptms.end(); p++ )
    {
        ArrayHelper coords(_useDouble);
        coords.add( p->x, p->y, 0.0 );
        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());
        geometry->addPrimitiveSet( new 
osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 1));
        _geode->addDrawable( geometry.get() );
    }
    if( _geode->getNumDrawables() > 1 )
        _combinePointToMultipoint();
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::MultiPointM> 
&mptms )
{
    if( !_valid ) return;

    std::vector<ESRIShape::MultiPointM>::const_iterator p;
    for( p = mptms.begin(); p != mptms.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;

        // Here is where we would use the 'M' (?)
        for( int i = 0; i < p->numPoints ; i++ )
            coords->push_back( osg::Vec3( p->points[i].x, p->points[i].y, 0.0 
));

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());
        geometry->addPrimitiveSet( new 
osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, coords->size()));

        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process(const std::vector<ESRIShape::PolyLineM> &linems )
{
    if( !_valid ) return;

    std::vector<ESRIShape::PolyLineM>::const_iterator p;
    for( p = linems.begin(); p != linems.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;

        int i;
        for( i = 0; i < p->numPoints; i++ )
            coords->push_back( osg::Vec3( p->points[i].x, p->points[i].y, 0.0 
));

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());

        for( i = 0; i < p->numParts; i++ )
        {
            int index = p->parts[i];
            int len = i < p->numParts - 1 ?
                            p->parts[i+1] - p->parts[i] :
                            p->numPoints  - p->parts[i];

            geometry->addPrimitiveSet(
                    new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, index, 
len));
        }
        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::PolygonM> &polyms )
{
    if( !_valid ) return;

    std::vector<ESRIShape::PolygonM>::const_iterator p;
    for( p = polyms.begin(); p != polyms.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;
        int i;
        for( i = 0; i < p->numPoints; i++ )
            coords->push_back( osg::Vec3( p->points[i].x, p->points[i].y, 0.0 
));

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());

        for( i = 0; i < p->numParts; i++ )
        {
            int index = p->parts[i];
            int len = i < p->numParts - 1 ?
                            p->parts[i+1] - p->parts[i] :
                            p->numPoints  - p->parts[i];

            geometry->addPrimitiveSet(
                    new osg::DrawArrays(osg::PrimitiveSet::POLYGON, index, 
len));
        }
        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::PointZ> &ptzs )
{
    if( !_valid ) return;

    std::vector<ESRIShape::PointZ>::const_iterator p;
    for( p = ptzs.begin(); p != ptzs.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;
        coords->push_back( osg::Vec3( p->x, p->y, p->z ));
        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());
        geometry->addPrimitiveSet( new 
osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 1));
        _geode->addDrawable( geometry.get() );
    }
    if( _geode->getNumDrawables() > 1 )
        _combinePointToMultipoint();
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::MultiPointZ> 
&mptzs )
{
    if( !_valid ) return;

    std::vector<ESRIShape::MultiPointZ>::const_iterator p;
    for( p = mptzs.begin(); p != mptzs.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;

        // Here is where we would use the 'M' (?)
        for( int i = 0; i < p->numPoints ; i++ )
            coords->push_back( osg::Vec3( p->points[i].x, p->points[i].y, 
p->zArray[i] ));

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());
        geometry->addPrimitiveSet( new 
osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, coords->size()));

        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process(const std::vector<ESRIShape::PolyLineZ> &linezs )
{
    if( !_valid ) return;

    std::vector<ESRIShape::PolyLineZ>::const_iterator p;
    for( p = linezs.begin(); p != linezs.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;

        int i;
        for( i = 0; i < p->numPoints; i++ )
            coords->push_back( osg::Vec3( p->points[i].x, p->points[i].y, 
p->zArray[i] ));

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());

        for( i = 0; i < p->numParts; i++ )
        {
            int index = p->parts[i];
            int len = i < p->numParts - 1 ?
                            p->parts[i+1] - p->parts[i] :
                            p->numPoints  - p->parts[i];

            geometry->addPrimitiveSet(
                    new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, index, 
len));
        }
        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::PolygonZ> &polyzs )
{
    if( !_valid ) return;

    std::vector<ESRIShape::PolygonZ>::const_iterator p;
    for( p = polyzs.begin(); p != polyzs.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;

        int i;
        for( i = 0; i < p->numPoints; i++ )
            coords->push_back( osg::Vec3( p->points[i].x, p->points[i].y, 
p->zArray[i] ));

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());

        for( i = 0; i < p->numParts; i++ )
        {
            int index = p->parts[i];
            int len = i < p->numParts - 1 ?
                            p->parts[i+1] - p->parts[i] :
                            p->numPoints  - p->parts[i];

            geometry->addPrimitiveSet(
                    new osg::DrawArrays(osg::PrimitiveSet::POLYGON, index, 
len));
        }
        _geode->addDrawable( geometry.get() );
    }
}

void ESRIShapeParser::_process( const std::vector<ESRIShape::MultiPatch> 
&mpatches )
{
    if( !_valid ) return;

    std::vector<ESRIShape::MultiPatch>::const_iterator p;
    for( p = mpatches.begin(); p != mpatches.end(); p++ )
    {
        osg::ref_ptr<osg::Vec3Array> coords  = new osg::Vec3Array;

        int i;
        for( i = 0; i < p->numPoints; i++ )
            coords->push_back( osg::Vec3( p->points[i].x, p->points[i].y, 
p->zArray[i] ));

        osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;
        geometry->setVertexArray(coords.get());

        // Lets mark poorly supported primitives with red, otherwise white
        osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
        geometry->setColorArray(colors.get(), osg::Array::BIND_PER_VERTEX );

        for( i = 0; i < p->numParts; i++ )
        {
            int index = p->parts[i];
            int len = i < p->numParts - 1 ?
                            p->parts[i+1] - p->parts[i] :
                            p->numPoints  - p->parts[i];

            int  mode =
                p->partTypes[i] == TriangleStrip ? 
osg::PrimitiveSet::TRIANGLE_STRIP :
                p->partTypes[i] == TriangleFan   ? 
osg::PrimitiveSet::TRIANGLE_FAN :
                // HACK for now
                p->partTypes[i] == OuterRing     ? 
osg::PrimitiveSet::LINE_STRIP :
                p->partTypes[i] == InnerRing     ? 
osg::PrimitiveSet::LINE_STRIP :
                p->partTypes[i] == FirstRing     ? 
osg::PrimitiveSet::LINE_STRIP :
                p->partTypes[i] == Ring          ? 
osg::PrimitiveSet::LINE_STRIP :
                                                   osg::PrimitiveSet::POINTS ;

            if( p->partTypes[i] == OuterRing ||
                p->partTypes[i] == InnerRing ||
                p->partTypes[i] == FirstRing || p->partTypes[i] == Ring )
            {
                OSG_WARN << "ESRIShapeParser - MultiPatch type " <<
                    (p->partTypes[i] == TriangleStrip ? "TriangleStrip":
                     p->partTypes[i] == TriangleFan   ? "TriangleFan":
                     p->partTypes[i] == OuterRing     ? "OuterRing":
                     p->partTypes[i] == InnerRing     ? "InnerRing":
                     p->partTypes[i] == FirstRing     ? "FirstRing":
                     p->partTypes[i] == Ring          ? "Ring": "Dunno") <<
                    " poorly supported.  Will be represented by a red line 
strip" << std::endl;
            }



            // Lets mark poorly supported primitives with red, otherwise white
            osg::Vec4 color =
                p->partTypes[i] == TriangleStrip ? osg::Vec4(1.0,1.0,1.0,1.0) :
                p->partTypes[i] == TriangleFan   ? osg::Vec4(1.0,1.0,1.0,1.0) :
                // HACK for now
                p->partTypes[i] == OuterRing     ? osg::Vec4(1.0,0.0,0.0,1.0) :
                p->partTypes[i] == InnerRing     ? osg::Vec4(1.0,0.0,0.0,1.0) :
                p->partTypes[i] == FirstRing     ? osg::Vec4(1.0,0.0,0.0,1.0) :
                p->partTypes[i] == Ring          ? osg::Vec4(1.0,0.0,0.0,1.0) :
                                                   osg::Vec4(1.0,0.0,0.0,1.0) ;
            for( int j = 0; j < len; j++ )
                colors->push_back( color );

            geometry->addPrimitiveSet( new osg::DrawArrays(mode, index, len ));
        }

        _geode->addDrawable( geometry.get() );
    }
}
#ifndef ESRI_SHAPE_PARSER_H
#define ESRI_SHAPE_PARSER_H

#include <string>
#include <osg/Geode>

#include "ESRIShape.h"

namespace ESRIShape {

class ArrayHelper
{
    public:
        ArrayHelper(bool useDouble)
        {
            if (useDouble) _vec3darray = new osg::Vec3dArray;
            else _vec3farray = new osg::Vec3Array;
        }

        osg::Array* get() { return _vec3farray.valid() ?
                static_cast<osg::Array*>(_vec3farray.get()) :
                static_cast<osg::Array*>(_vec3darray.get()); }

        unsigned int size() { return _vec3farray.valid() ?
                _vec3farray->size() :
                _vec3darray->size(); }

        void add(double x, double y, double z)
        {
            if (_vec3farray.valid()) _vec3farray->push_back(osg::Vec3(x,y,z));
            else _vec3darray->push_back(osg::Vec3d(x,y,z));
        }

        void add(const osg::Vec3& v)
        {
            if (_vec3farray.valid()) _vec3farray->push_back(v);
            else _vec3darray->push_back(osg::Vec3d(v.x(),v.y(),v.z()));
        }

        void add(const osg::Vec3d& v)
        {
            if (_vec3farray.valid()) 
_vec3farray->push_back(osg::Vec3(v.x(),v.y(),v.z()));
            else _vec3darray->push_back(v);
        }

        void add(osg::Array* array, unsigned int index)
        {
            osg::Vec3Array* vec3Array = dynamic_cast<osg::Vec3Array*>(array);
            if (vec3Array && index<vec3Array->size()) add((*vec3Array)[index]);

            osg::Vec3dArray* vec3dArray = dynamic_cast<osg::Vec3dArray*>(array);
            if (vec3dArray && index<vec3dArray->size()) 
add((*vec3dArray)[index]);
        }

        osg::ref_ptr<osg::Vec3Array> _vec3farray;
        osg::ref_ptr<osg::Vec3dArray> _vec3darray;
};


class ESRIShapeParser
{
    public:

        ESRIShapeParser( const std::string fileName, bool useDouble, bool 
keepSeparatePoints);

        osg::Geode *getGeode();

#if 0
#if 1
        typedef osg::Vec3d ShapeVec3;
        typedef osg::Vec3dArray ShapeVec3Array;
#else
        typedef osg::Vec3 ShapeVec3;
        typedef osg::Vec3Array ShapeVec3Array;
#endif
#endif

    private:



        bool _valid;
        bool _useDouble;
        bool _keepSeparatePoints;

        osg::ref_ptr<osg::Geode> _geode;

        void _combinePointToMultipoint();
        void _process( const std::vector<ESRIShape::Point> &);
        void _process( const std::vector<ESRIShape::MultiPoint> &);
        void _process( const std::vector<ESRIShape::PolyLine> &);
        void _process( const std::vector<ESRIShape::Polygon> &);

        void _process( const std::vector<ESRIShape::PointM> &);
        void _process( const std::vector<ESRIShape::MultiPointM> &);
        void _process( const std::vector<ESRIShape::PolyLineM> &);
        void _process( const std::vector<ESRIShape::PolygonM> &);

        void _process( const std::vector<ESRIShape::PointZ> &);
        void _process( const std::vector<ESRIShape::MultiPointZ> &);
        void _process( const std::vector<ESRIShape::PolyLineZ> &);
        void _process( const std::vector<ESRIShape::PolygonZ> &);
        void _process( const std::vector<ESRIShape::MultiPatch> &);

};

}

#endif
#include <osgDB/FileNameUtils>
#include <osgDB/FileUtils>
#include <osgDB/fstream>
#include <osgDB/Registry>

#include <osgTerrain/Locator>

#include "ESRIType.h"

#include "ESRIShape.h"
#include "ESRIShapeParser.h"

#include "XBaseParser.h"


class ESRIShapeReaderWriter : public osgDB::ReaderWriter
{
    public:
        ESRIShapeReaderWriter()
        {
            supportsExtension("shp","Geospatial Shape file format");
            supportsOption("double","Read x,y,z data as double an stored as 
geometry in osg::Vec3dArray's.");
            supportsOption("keepSeparatePoints", "Avoid combining point 
features into multi-point.");
        }

        virtual const char* className() const { return "ESRI Shape 
ReaderWriter"; }

        virtual bool acceptsExtension(const std::string& extension) const
        {
            return osgDB::equalCaseInsensitive(extension,"shp");
        }

        virtual ReadResult readObject(const std::string& fileName, const 
Options* opt) const
        { return readNode(fileName,opt); }

        virtual ReadResult readNode(const std::string& file, const Options* 
options) const
        {
            std::string ext = osgDB::getFileExtension(file);
            if (!acceptsExtension(ext))
                return ReadResult::FILE_NOT_HANDLED;

            std::string fileName = osgDB::findDataFile(file, options);
            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;

            bool useDouble = false;
            if (options && 
options->getOptionString().find("double")!=std::string::npos)
            {
                useDouble = true;
            }

            bool keepSeparatePoints = false;
            if (options && 
options->getOptionString().find("keepSeparatePoints") != std::string::npos)
            {
              keepSeparatePoints = true;
            }


            ESRIShape::ESRIShapeParser sp(fileName, useDouble, 
keepSeparatePoints);


            std::string xbaseFileName(osgDB::getNameLessExtension(fileName) + 
".dbf");
            ESRIShape::XBaseParser xbp(xbaseFileName);


            if (sp.getGeode() && (xbp.getAttributeList().empty() == false))
            {
                if (sp.getGeode()->getNumDrawables() != 
xbp.getAttributeList().size())
                {
                    OSG_WARN << "ESRIShape loader : .dbf file containe 
different record number that .shp file." << std::endl
                                           << "                   .dbf record 
skipped." << std::endl;
                }
                else
                {
                    osg::Geode * geode = sp.getGeode();
                    unsigned int i = 0;

                    
ESRIShape::XBaseParser::ShapeAttributeListList::const_iterator it, end = 
xbp.getAttributeList().end();
                    for (it = xbp.getAttributeList().begin(); it != end; ++it, 
++i)
                    {
                        geode->getDrawable(i)->setUserData(it->get());
                    }
                }
            }

            if (sp.getGeode())
            {

                std::string projFileName(osgDB::getNameLessExtension(fileName) 
+ ".prj");
                if (osgDB::fileExists(projFileName))
                {
                    osgDB::ifstream fin(projFileName.c_str());
                    if (fin)
                    {
                        std::string projstring;
                        while(!fin.eof())
                        {
                            char readline[4096];
                            *readline = 0;
                            fin.getline(readline, sizeof(readline));
                            if (!projstring.empty() && !fin.eof())
                            {
                                projstring += '\n';
                            }
                            projstring += readline;

                        }

                        if (!projstring.empty())
                        {
                            osgTerrain::Locator* locator = new 
osgTerrain::Locator;
                            sp.getGeode()->setUserData(locator);

                            if (projstring.compare(0,6,"GEOCCS")==0)
                            {
                                
locator->setCoordinateSystemType(osgTerrain::Locator::GEOCENTRIC);
                            }
                            else if (projstring.compare(0,6,"PROJCS")==0)
                            {
                                
locator->setCoordinateSystemType(osgTerrain::Locator::PROJECTED);
                            }
                            else if (projstring.compare(0,6,"GEOGCS")==0)
                            {
                                
locator->setCoordinateSystemType(osgTerrain::Locator::GEOGRAPHIC);
                            }

                            locator->setFormat("WKT");
                            locator->setCoordinateSystem(projstring);
                            locator->setDefinedInFile(false);
                        }
                    }

                }


            }
            return sp.getGeode();
        }
};

REGISTER_OSGPLUGIN(shp, ESRIShapeReaderWriter)
#include "XBaseParser.h"

#include <vector>
#include <string>

#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(_MSC_VER) || defined(__MINGW32__)
    #include <io.h>
#else
    #include <unistd.h>
#endif

#include <fcntl.h>
#include <errno.h>
#include <osg/Notify>

namespace ESRIShape
{


void XBaseHeader::print()
{
    OSG_INFO << "VersionNumber = " << (int) _versionNumber << std::endl
                           << "LastUpdate    = " << 1900 + (int) _lastUpdate[0] 
<< "/" << (int) _lastUpdate[1] << "/" << (int) _lastUpdate[2] << std::endl
                           << "NumRecord     = " << _numRecord << std::endl
                           << "HeaderLength  = " << _headerLength << std::endl
                           << "RecordLength  = " << _recordLength << std::endl;
}

bool XBaseHeader::read(int fd)
{
    int nbytes = 0;

    if ((nbytes = ::read( fd, &_versionNumber, sizeof(_versionNumber))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_lastUpdate, sizeof(_lastUpdate))) <= 0) return 
false;
    if ((nbytes = ::read( fd, &_numRecord, sizeof(_numRecord))) <= 0) return 
false;
    if ((nbytes = ::read( fd, &_headerLength, sizeof(_headerLength))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_recordLength, sizeof(_recordLength))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_reserved, sizeof(_reserved))) <= 0) return 
false;
    if ((nbytes = ::read( fd, &_incompleteTransaction, 
sizeof(_incompleteTransaction))) <= 0) return false;
    if ((nbytes = ::read( fd, &_encryptionFlag, sizeof(_encryptionFlag))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_freeRecordThread, sizeof(_freeRecordThread))) 
<= 0) return false;
    if ((nbytes = ::read( fd, &_reservedMultiUser, sizeof(_reservedMultiUser))) 
<= 0) return false;
    if ((nbytes = ::read( fd, &_mdxflag, sizeof(_mdxflag))) <= 0) return false;
    if ((nbytes = ::read( fd, &_languageDriver, sizeof(_languageDriver))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_reserved2, sizeof(_reserved2))) <= 0) return 
false;

    return true;
}

void XBaseFieldDescriptor::print()
{
    OSG_INFO << "name           = " << _name << std::endl
                           << "type           = " << _fieldType << std::endl
                           << "length         = " << (int) _fieldLength << 
std::endl
                           << "decimalCount   = " << (int) _decimalCount << 
std::endl
                           << "workAreaID     = " << (int) _workAreaID << 
std::endl
                           << "setFieldFlag   = " << (int) _setFieldFlag << 
std::endl
                           << "indexFieldFlag = " << (int) _indexFieldFlag << 
std::endl;
}

bool XBaseFieldDescriptor::read(int fd)
{
    int nbytes = 0;

    if ((nbytes = ::read( fd, &_name, sizeof(_name))) <= 0) return false;
    if ((nbytes = ::read( fd, &_fieldType, sizeof(_fieldType))) <= 0) return 
false;
    if ((nbytes = ::read( fd, &_fieldDataAddress, sizeof(_fieldDataAddress))) 
<= 0) return false;
    if ((nbytes = ::read( fd, &_fieldLength, sizeof(_fieldLength))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_decimalCount, sizeof(_decimalCount))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_reservedMultiUser, sizeof(_reservedMultiUser))) 
<= 0) return false;
    if ((nbytes = ::read( fd, &_workAreaID, sizeof(_workAreaID))) <= 0) return 
false;
    if ((nbytes = ::read( fd, &_reservedMultiUser2, 
sizeof(_reservedMultiUser2))) <= 0) return false;
    if ((nbytes = ::read( fd, &_setFieldFlag, sizeof(_setFieldFlag))) <= 0) 
return false;
    if ((nbytes = ::read( fd, &_reserved, sizeof(_reserved))) <= 0) return 
false;
    if ((nbytes = ::read( fd, &_indexFieldFlag, sizeof(_indexFieldFlag))) <= 0) 
return false;

    return true;
}


XBaseParser::XBaseParser(const std::string& fileName):
    _valid(false)
{
    if (!fileName.empty())
    {
        int fd = 0;
#ifdef WIN32
        if( (fd = open( fileName.c_str(), O_RDONLY | O_BINARY )) < 0 )
#else
        if( (fd = ::open( fileName.c_str(), O_RDONLY )) < 0 )
#endif
        {
            perror( fileName.c_str() );
        }
        else
        {
            _valid = parse(fd);
            close(fd);
        }
    }
}

bool XBaseParser::parse(int fd)
{
    int nbytes;
    XBaseHeader _xBaseHeader;
    std::vector<XBaseFieldDescriptor> _xBaseFieldDescriptorList;
    XBaseFieldDescriptor _xBaseFieldDescriptorTmp;


    // ** read the header
    if (_xBaseHeader.read(fd) == false) return false;
//    _xBaseHeader.print();


    // ** read field descriptor
    bool fieldDescriptorDone = false;
    Byte nullTerminator;

    while (fieldDescriptorDone == false)
    {
        // ** store the field descriptor
        if (_xBaseFieldDescriptorTmp.read(fd) == false) return false;
        _xBaseFieldDescriptorList.push_back(_xBaseFieldDescriptorTmp);
//        _xBaseFieldDescriptorTmp.print();

        // ** check the terminator
        if ((nbytes = ::read( fd, &nullTerminator, sizeof(nullTerminator))) <= 
0) return false;
        if (nullTerminator == 0x0D)
            fieldDescriptorDone = true;
        else
        {
            if (::lseek( fd, -1, SEEK_CUR)==-1)
            {
                OSG_WARN<<"File parsing failed, lseek return 
errno="<<errno<<std::endl;
                return false;
            }
        }
    }


    // ** move to the end of the Header
    if (::lseek( fd, _xBaseHeader._headerLength + 1, SEEK_SET)==-1)
    {
        OSG_WARN<<"File parsing failed, lseek return errno="<<errno<<std::endl;
        return false;
    }


    // ** reserve AttributeListList
    _shapeAttributeListList.reserve(_xBaseHeader._numRecord);


    // ** read each record and store them in the ShapeAttributeListList
    char* record = new char[_xBaseHeader._recordLength];

    std::vector<XBaseFieldDescriptor>::iterator it, end = 
_xBaseFieldDescriptorList.end();
    for (Integer i = 0; i < _xBaseHeader._numRecord; ++i)
    {
        if ((nbytes = ::read( fd, record, _xBaseHeader._recordLength)) <= 0) 
break;

        char * recordPtr = record;
        osgSim::ShapeAttributeList * shapeAttributeList = new 
osgSim::ShapeAttributeList;
        shapeAttributeList->reserve(_xBaseFieldDescriptorList.size());

        for (it = _xBaseFieldDescriptorList.begin(); it != end; ++it)
        {
            switch (it->_fieldType)
            {
            case 'C':
            {
                char* str = new char[it->_fieldLength + 1];
                memcpy(str, recordPtr, it->_fieldLength);
                str[it->_fieldLength] = 0;
                shapeAttributeList->push_back(osgSim::ShapeAttribute((const 
char *) it->_name, (char*) str));
                delete [] str;
                break;
            }
            case 'N':
            {
                char* number = new char[it->_fieldLength + 1];
                memcpy(number, recordPtr, it->_fieldLength);
                number[it->_fieldLength] = 0;
                shapeAttributeList->push_back(osgSim::ShapeAttribute((const 
char *) it->_name, atof(number)));
                delete [] number;
                break;
            }
            case 'I':
            {
                int number;
                memcpy(&number, record, it->_fieldLength);
                shapeAttributeList->push_back(osgSim::ShapeAttribute((const 
char *) it->_name, (int) number));
                break;
            }
            case 'O':
            {
                double number;
                memcpy(&number, record, it->_fieldLength);
                shapeAttributeList->push_back(osgSim::ShapeAttribute((const 
char *) it->_name, (double) number));
                break;
            }
            default:
            {
                OSG_WARN << "ESRIShape::XBaseParser : record type "
                                       << it->_fieldType << "not supported, 
skipped" << std::endl;
                shapeAttributeList->push_back(osgSim::ShapeAttribute((const 
char *) it->_name, (double) 0));
                break;
            }


            }

            recordPtr += it->_fieldLength;
        }

        _shapeAttributeListList.push_back(shapeAttributeList);
    }

    delete [] record;

    return true;
}


}


_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to