ESRI shapefile reader (only lines/polygons yet)

The place for artists, modellers, level designers et al to discuss their approaches for creating content for OGRE.
Post Reply
User avatar
SimNation
Halfling
Posts: 67
Joined: Sun Dec 13, 2009 12:39 pm
Location: Paraíba, Brasil
Contact:

ESRI shapefile reader (only lines/polygons yet)

Post by SimNation »

Hello World! :)

that's my first version of a .shp file - lines/polygons reader

usage:

Code: Select all

//Rios
    moRivers->clear();
    ShapeFile* sfRivers = new ShapeFile();
    sfRivers->load("data/rivers/all.shp");
    sfRivers->setCanvas(*moRivers,*mTerrainGroup,"matRivers");
    sfRivers->setCoords(newSide*pixelSize,newSide*pixelSize,LONGMIN,LONGMAX,LATMIN,LATMAX);
    sfRivers->draw();
    delete sfRivers;
The class (I'm yet finishing, have yet to link the dbf file and access point files, not so hard):

Code: Select all

#include <Ogre.h>
#include <Terrain/OgreTerrain.h>
#include <Terrain/OgreTerrainGroup.h>

typedef std::deque <std::string> record_t;
typedef std::deque <record_t>    table_t;
//-------------------------------------------------------------------------------------
bool fileExists(std::string strFilename)
{
  struct stat stFileInfo;
  return (stat(strFilename.c_str(),&stFileInfo) == 0);
}
//-------------------------------------------------------------------------------------
std::string lowerCase(std::string s)
{
    for (unsigned int i=0; i<s.length(); i++)
        s[i] = tolower(s[i]);
    return s;
}
//-------------------------------------------------------------------------------------
std::string upperCase(std::string s)
{
    for (unsigned int i=0; i<s.length(); i++)
        s[i] = toupper(s[i]);
    return s;
}
//-------------------------------------------------------------------------------------
double readDouble(std::ifstream& f)
{
    double X;
    f.read(reinterpret_cast<char*>(&X),8);
    return X;
}
//-------------------------------------------------------------------------------------
Ogre::String strAsBin(char* str, int count)
{
    int i=0;
    char bch;
    uint8_t b, c, ind;
    Ogre::String byte;
    Ogre::String resposta = "";
    do {
        byte = "00000000";
        if (str[i] != 0) {
            bch = str[i];
            b = bch;
            ind = 7;
            while (b > 1) {
                c = b%2;
                if (c == 1)
                    byte[ind] = '1';
                b = b/2;
                ind--;
            }
            byte[ind] = '1';
        }
        i++;
        resposta =  resposta + '|' + byte;
    } while (i < count);
    return resposta;
}
//-------------------------------------------------------------------------------------
uint leBig(std::ifstream& f)
{
    char *buf;
    Ogre::uint32 num;
    Ogre::uint8 num1, num2, num3, num4;
    buf = new char[4];
    f.read(buf,4);
    num1 = buf[0];
    num2 = buf[1];
    num3 = buf[2];
    num4 = buf[3];
    num = num1*0x1000000 +
          num2*0x10000   +
          num3*0x100     +
          num4;
    delete[] buf;
    return num;
}
//-------------------------------------------------------------------------------------
uint leLittle(std::ifstream& f)
{
    char *buf;
    Ogre::uint32 num;
    Ogre::uint8 num1, num2, num3, num4;
    buf = new char[4];
    f.read(buf,4);
    num1 = buf[0];
    num2 = buf[1];
    num3 = buf[2];
    num4 = buf[3];
    num = num4*0x1000000 +
          num3*0x10000   +
          num2*0x100     +
          num1;
    delete[] buf;
    return num;
}
//-------------------------------------------------------------------------------------
bool compareCaseless(std::string s1, std::string s2)
{
    return (upperCase(s1) == upperCase(s2));
}
//-------------------------------------------------------------------------------------
void csv2table(std::istream& ins, table_t& table, char sep)
{
    std::string s;
    table.clear();
    while (std::getline( ins, s ))
    {
        std::istringstream ss( s );
        record_t           record;
        std::string        field;
        bool               final = true;
        while (std::getline( ss, field, sep ))
        {
            record.push_back( field );
            final = ss.eof();
        }
        if (!final)
            record.push_back( std::string() );
        table.push_back( record );
    }
}
//-------------------------------------------------------------------------------------
std::string formatFloat(float number,int decPlaces,char thSep)
{
    int sinal = 1;
    if (number < 0) {
        number = -number;
        sinal = -1;
    }
    float numberOrig = number;
    int pos, count = 0;
    std::string resp = "";
    char respost[2];
    do {
        pos = int(floor(number)) % 10;
        sprintf(respost,"%d",pos); // converts to decimal base (which is faster? sprintf or Ogre::StringConverter::toString?
        resp = respost + resp;
        if ((count%3 == 2) && (number >= 10) && (thSep != ' '))
            resp = thSep + resp;
        number /= 10;
        count++;
    } while (number > 1);
    if (decPlaces > 0) { // decimal places
        number=numberOrig - abs(numberOrig); // fractional part
        count = 0;
        if (thSep == '.')
            resp = resp + ',';
        else
            resp = resp + '.';
        do {
            number *= 10;
            pos = floor(number);
            number -= pos;
            resp = resp + char(48+pos);//Ogre::StringConverter::toString(pos);
            count++;
        } while (count < decPlaces);
    }
    if (sinal < 0)
        resp = "-" + resp;
    return resp;
}
//-------------------------------------------------------------------------------------
//determines if a point is inside a polygon
//from http://www.visibone.com/inpoly/
int                                /*   1=inside, 0=outside                */
inpoly(                            /* is target point inside a 2D polygon? */
unsigned int poly[][2],            /*   polygon points, [0]=x, [1]=y       */
int npoints,                       /*   number of points in polygon        */
unsigned int xt,                   /*   x (horizontal) of target point     */
unsigned int yt)                   /*   y (vertical) of target point       */
{
     unsigned int xnew,ynew;
     unsigned int xold,yold;
     unsigned int x1,y1;
     unsigned int x2,y2;
     int i;
     int inside=0;

     if (npoints < 3) {
          return(0);
     }
     xold=poly[npoints-1][0];
     yold=poly[npoints-1][1];
     for (i=0 ; i < npoints ; i++) {
          xnew=poly[i][0];
          ynew=poly[i][1];
          if (xnew > xold) {
               x1=xold;
               x2=xnew;
               y1=yold;
               y2=ynew;
          }
          else {
               x1=xnew;
               x2=xold;
               y1=ynew;
               y2=yold;
          }
          if ((xnew < xt) == (xt <= xold)          /* edge "open" at one end */
           && ((long)yt-(long)y1)*(long)(x2-x1)
            < ((long)y2-(long)y1)*(long)(xt-x1)) {
               inside=!inside;
          }
          xold=xnew;
          yold=ynew;
     }
     return(inside);
}
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
//  ShapeFile object
//-------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------
using namespace std;

struct point2d
{
    float lat, lon;
};
typedef std::vector<point2d> segment;
typedef std::vector<segment> polygon;
//-------------------------------------------------------------------------------------
class ShapeFile {
private:
    polygon Poly;
    //segment Segm;
    size_t nPoints; //nParts, nPoints, actualPart;
    ifstream fShp;
    Ogre::ManualObject* mo;
    Ogre::TerrainGroup* tg;
    string matName;
    Ogre::Rectangle BBox;
    int W,H;
public:
    ShapeFile(void);
    ~ShapeFile(void);
    int load(std::string fName);
    void readRec(void);
    void linhaTerreno(double x,double z);
    int setCanvas(Ogre::ManualObject &l,Ogre::TerrainGroup &t,string mn);
    int setCoords(int w,int h,float left,float right,float bottom,float top);
    //int setCoords(int w,int h,Ogre::Rectangle bbox);
    int draw(void);
};
//-------------------------------------------------------------------------------------
ShapeFile::ShapeFile(void) :
    //nParts(0),
    nPoints(0),
    mo(0),
    tg(0),
    matName(""),
    W(0),
    H(0)
    //actualPart(0)
{
}
//-------------------------------------------------------------------------------------
ShapeFile::~ShapeFile(void)
{
    if (Poly.size() > 0)
        Poly.clear();
}
//-------------------------------------------------------------------------------------
int ShapeFile::load(std::string fName)
{
    //cout << "shapeFile:" << fName << '\n';
    Ogre::String fName2 = lowerCase(fName);
    if (fileExists(fName) || fileExists(fName2)) {
        if (fileExists(fName))
            fShp.open(fName.c_str(),std::ios::binary);  // with uppercase
        else
            fShp.open(fName2.c_str(),std::ios::binary); // all lowercase
        if (fShp.is_open()) {
            char* offsetHeader = new char[72];
            fShp.read(offsetHeader,24); // reads the begin of the file's header
            uint fSize = leBig(fShp)*2;
            fShp.read(offsetHeader,72); // reads the remaining of the file's header...
            delete[] offsetHeader;      // ... and throws it away
            while (fShp.tellg() < fSize) {
                //linha->begin("matLevel4",Ogre::RenderOperation::OT_LINE_STRIP);
                readRec(); // read and draw the roads
            }
            fShp.close();
        } else return 1; // file couldn't be opened
    } else return 2;     // file couldn't be found
    return 0;            // all right
}
//-------------------------------------------------------------------------------------
// File format explained in "ESRI Shapefile Technical Description" (shapefile.pdf) - http://www.esri.com
void ShapeFile::readRec(void)
{
    uint nparts, npoints, i, part;
    point2d point;
    segment segm;
    size_t j;
    std::vector<uint> parts;
    fShp.ignore(8);
    uint tipo = leLittle(fShp);
    if (tipo == 3 || tipo == 5) { // PolyLine or Polygon
        fShp.ignore(32);
        // reads number of parts and points of this record/polygon
        nparts = leLittle(fShp);
        //nparts += nparts;
        npoints = leLittle(fShp);
        nPoints += npoints;
        // read all the parts at once and put their positions (relative to this record) in parts' vector
        // first part always begins at 0
        for (i=0; i<nparts; i++) {
            part = leLittle(fShp);
            parts.push_back(part);
        }
        if (nparts > 1)
            part = parts[1]; // where the first part will end (the beginning of the 2nd part [1])
        else
            part = 0;
        j = 1; // reading first part
        for (i=0; i<npoints; i++) {
            point.lon = readDouble(fShp); // longitude
            point.lat = readDouble(fShp); // latitude
            if (i == part) // end of the current part (j) or first time of 1 part's record
            {
                if (i > 0) { // won't do if record has only 1 part (and i=part=0)
                    Poly.push_back(segm);
                    //actualPart++;
                    segm.clear();

                    //segm = new segment; // dettach??????????????????????????????????????????????????
                    /*
                    linha->end(); // jump to another polygon
                    linha->begin("matLevel4",Ogre::RenderOperation::OT_LINE_STRIP);
                    */
                }
                segm.push_back(point);
                //linhaTerreno(linha,X,Y); // add point to current polygon
                if (j < parts.size()) { // adjust limit (end point) of next part
                    j++;
                    if (j < parts.size())
                        part = parts[j];
                    else
                        part = -1; // will end the loop by i=nPoints (since its the last part)
                }
            }
            else // i != part, all points inside any part
            {
                segm.push_back(point);
                //linhaTerreno(linha,X,Y);
            }
        } // for i
        // draw remaining points
        Poly.push_back(segm);
        //linha->end();
    } // if (tipo==3 || tipo==5)
}
//-------------------------------------------------------------------------------------
void ShapeFile::linhaTerreno(double x,double z)
{
    mo->position(x,tg->getHeightAtWorldPosition(x,0,z),z);
}
//-------------------------------------------------------------------------------------
int ShapeFile::setCanvas(Ogre::ManualObject &l,Ogre::TerrainGroup &t,string mn)
{
    //cout << "MATERIAL:" << mn << '\n';
    mo = &l;
    tg = &t;
    matName = mn;
    return 0;
}
//-------------------------------------------------------------------------------------
int ShapeFile::setCoords(int w,int h,float left,float right,float bottom,float top)
{
    W = w;
    H = h;
    BBox.left = left;
    BBox.right = right;
    BBox.bottom = bottom;
    BBox.top = top;
    return 0;
}
//-------------------------------------------------------------------------------------
int ShapeFile::draw(void)
{
    if ((W == 0) || (H == 0))
        return 1; // image null
    //cout << "*************************" << Poly.size() << '\n';
    for (size_t p=0; p<Poly.size(); p++) {
        segment segm = Poly[p];
        mo->begin(matName,Ogre::RenderOperation::OT_LINE_STRIP);
        for (size_t s=0; s<segm.size(); s++) {
            double X = W*(segm[s].lon-BBox.left)/(BBox.right-BBox.left) - W/2; // map X
            double Y = H*(BBox.top-segm[s].lat)/(BBox.top-BBox.bottom) - H/2;  // map Y
            linhaTerreno(X,Y);
            //mo->position(x,tg->getHeightAtWorldPosition(x,0,z),z);
        }
        mo->end();
    }
    return 0;
}
//-------------------------------------------------------------------------------------
Cheers! :D

RLD
"Thus without desire you can see its marvels, with desire you can only see its surface". - Dao De Jing
User avatar
Beauty
OGRE Community Helper
OGRE Community Helper
Posts: 767
Joined: Wed Oct 10, 2007 2:36 pm
Location: Germany
x 39
Contact:

Re: ESRI shapefile reader (only lines/polygons yet)

Post by Beauty »

Thanks for your code to read GIS data from the database of the Natural Earth website.

My application is based on C#, so I need to port the code before I can try it.
Currently I have less free time. So I don't do the job now.
I will save it to my GIS link collection and when GIS data import priority growes up for my application, I will try to port it. At the moment I have to concentrate to an important extension of my research application.
Help to add information to the wiki. Also tiny edits will let it grow ... :idea:
Add your country to your profile ... it's interesting to know from where of the world you are.
[Fr] Guillaume
Gnoblar
Posts: 1
Joined: Fri Mar 22, 2013 10:38 am

Re: ESRI shapefile reader (only lines/polygons yet)

Post by [Fr] Guillaume »

Two years after the latest post, I would like to update it.
I am a French student, working on geographic information and I would like to import SHP mesh files into jMonkey.
It seems that I should first convert the format of my file to OgreXML.

Does anyone tried to port the code in Java?
@SimNation : Did you modify the code you posted in your first post?

Thank you for any advice.
Guillaume
Post Reply