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