Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / OpenGL

Rendering Shapefile in OpenGL-2

4.69/5 (6 votes)
24 Aug 2015CPOL2 min read 18K   832  
Rendering polygon shapefiles and filling them

Introduction

This is a continuation to my previous article for rendering shapefiles in OpenGL environment. Shapefiles have either to be point, line or polygon types. The main focus of this tip is to render polygons and fill them.This revision will also handle polygons with holes i.e, Donut Polygons.

Background

Polygons in shapefiles are organized in a typical manner.Each Polygon element may have single or multiple parts.Each part is called a ring.The rings have vertices which are arranged in clockwise or counter clockwise based on whether they represent outer ring or inner ring. Shapelib library helps us to parse this special structure. The detailed description of this structure can be found in ESRI Shapefile Technical Description.

 Image 1

OpenGL cannot render concave polygonsOpenGL community advises to make use of Triangulation libraries for breaking a polygon into triangle fans and strips. Most popular libraries fail in successfully triangulating complex polygons that are typical in GIS. In his article, we use GLU tesselator to break a polygon into convex shapes and render them.

Using the Code

I assume that the intended audience is familiar with OpenGL and understands concepts like linking with static or dynamic libraries in Microsoft Visual Studio environment.

To parse the shapefile, I have used the Shapelib (http://shapelib.maptools.org/). Include the shapefile.h at the beginning. Make use of the shapelib.lib by adding shapelib\shapelib.lib to object/library modules.

The below structure defines the structure of a polygon. Each polygon has multiple rings. Each ring has a start index and end index within the array of points.

C++
typedef struct MyPolygonElement2D
{
 vector<MyPoint2D>vPointList;
 int nParts;
 vector <int> vPartsStartIndex;
 vector <int> vPartsEndIndex;
 
}MyPolygonElement2D;

We use the below function declarations to interact with GLU Tesselator.

C++
typedef void (__stdcall *TESSCALLBACK)(void);
void CALLBACK tess_edgeFlag( GLboolean );
void CALLBACK edgeCallback(void);
void errorCallback(GLenum errorCode);
void CALLBACK combineCallback(GLdouble coords[3],
GLdouble *vertex_data[4],GLfloat weight[4], GLdouble **dataOut );
void CALLBACK TessBeginCallback(GLenum which);   

Declare a Tesselator object and enclose the rings in contour begin and end calls to GLU tesselator.

C++
GLUtesselator *tobj;
 gluTessBeginPolygon(tobj, NULL);
 gluTessBeginContour(tobj);  

//Pass the vertices of a ring here
  
 gluTessEndContour(tobj);
 gluTessEndPolygon(tobj);

The rendering can be accomplished with the below logic.

C++
    for(int n=0;n<vPolygonElements2D.size();n++)
    {      
        gluTessBeginPolygon(tobj,NULL);
        for(int k=0;k<vPolygonElements2D[n].nParts;k++)
        {	int start=vPolygonElements2D[n].vPartsStartIndex[k];
			int finish=vPolygonElements2D[n].vPartsEndIndex[k];
			const int nNoOfVertices=finish-start;
			double **fPolygonVertices = 0;
			int nVertice=0;
            fPolygonVertices = new double *[nNoOfVertices] ;
            for(int i = 0 ; i < nNoOfVertices ; i++ )
              fPolygonVertices[i] = new double[3];		
		
			
			gluTessBeginContour(tobj);
            
            for(int l=start;l<finish;l++)
            {
                fPolygonVertices[nVertice][0] = vPolygonElements2D[n].vPointList[l].dX;
                fPolygonVertices[nVertice][1] = vPolygonElements2D[n].vPointList[l].dY;
                fPolygonVertices[nVertice][2] = 0.0;          
            
                nVertice++;
           }//for l   
           for( l=0;l<nVertice-1 ;l++)    
              gluTessVertex(tobj, fPolygonVertices[l],fPolygonVertices[l]); //store the vertex
            gluTessEndContour(tobj);
            
        
        }//for  k
           gluTessEndPolygon(tobj); 
    }//for n

Output

Image 2

Points of Interest

Please note the below things:

  1. The approach is only to demo the possibility of drawing filled polygons.
  2. The code is not performance oriented and written in a very legacy way.
  3. The shapefiles shall be WGS 84 and shall not contain multi geometry shapes.
  4. Performance  improved by making use of display lists 
  5. My next article would be to make use of OpenGL Shaders.

History

Refer to my previous article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)