1. 概述

坐标数据是空间数据文件的核心,空间数据的数据量往往是很大的。数据可视化是GIS的一个核心应用,绘制海量的坐标数据始终是一个考验设备性能的难题,使用GPU进行绘制可有效减少CPU的负载,提升绘制时的速度

shapefile是空间数据文件常用的格式,Shapefile C Library(也叫shapelib)提供了编写简单的 C/C++ 程序以读取、写入和更新ESRI Shapefile 以及关联的属性文件 (.dbf) 的功能

shapelib的官网:Shapefile C Library (maptools.org)

shapelib的GitHub地址:OSGeo/shapelib: Official repository of shapelib (github.com)

本文基于C++语言,使用shapelib库来读取shp文件,并使用基于GLFW和GLAD来使用OpenGL绘制空间数据

2. 环境准备

本文的系统环境为Ubuntu 20.04.3 LTS,关于C++语言的OpenGL环境搭建可参考:

shapelib在Linux上安装参考其GitHub指导:

具体步骤为:

  • $ git clone https://github.com/OSGeo/shapelib.git
  • $ cmake build .
  • $ make
  • $ make install

Windows平台上的安装可以参考:

本文使用的数据为云南的县界,数据信息如图所示:

注意:

  • 本文数据编码是UTF-8
  • 本文数据Geometry是Polygon
  • 本文数据坐标系是投影坐标系

3. 读取shp文件

参考博客:

官方API文档:

这里笔者需要读取shp文件的四至范围和每个Geometry的坐标数据

代码如下:

#include <shapefil.h>

int main()
{
//读取shp
const char * pszShapeFile = "../云南县界/云南县界.shp";
SHPHandle hShp= SHPOpen(pszShapeFile, "r");
int nShapeType, nVertices;
int nEntities = 0;
double* minB = new double[4];
double* maxB = new double[4];
SHPGetInfo(hShp, &nEntities, &nShapeType, minB, maxB);
printf("ShapeType:%d\n", nShapeType);
printf("Entities:%d\n", nEntities);
printf("Xmin, Ymin: %f,%f\n", minB[0], minB[1]);
printf("Xmax, Ymax: %f,%f\n", maxB[0], maxB[1]);
for (int i = 0; i < nEntities;i++)
{
int iShape = i;
SHPObject *obj = SHPReadObject(hShp, iShape);
printf("--------------Feature:%d------------\n",iShape);
int parts = obj->nParts;
int verts=obj->nVertices;
printf("nParts:%d\n", parts);
printf("nVertices:%d\n", verts);
for (size_t i = 0; i < verts; i++)
{
double x=obj->padfX[i];
double y = obj->padfY[i];
printf("%f,%f;", x,y);
}
printf("\n");
}
SHPClose(hShp);
}

结果输出:

Xmin, Ymin: 348122 2.34118e+06
Xmax, Ymax: 1.23349e+06 3.23468e+06
ShapeType:5
Entities:125
......

4. OpenGL绘制

OpenGL的绘制流程参考:

该网站上绘制两个三角形的示例代码:

本文所使用的方法是每个Geometry绑定一个VAO和VBO,然后进行绘制

全部代码如下:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <shapefil.h>
#include <vector>
#include <iostream> void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window); // settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
double XMAX, YMAX, XMIN, YMIN;
std::vector<int> size; const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0"; int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif // glfw window creation
// --------------------
GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
} // build and compile our shader program
// ------------------------------------
// vertex shader
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
<< infoLog << std::endl;
}
// fragment shader
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"
<< infoLog << std::endl;
}
// link shaders
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n"
<< infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader); const char *pszShapeFile = "../云南县界/云南县界.shp";
SHPHandle hShp = SHPOpen(pszShapeFile, "r");
int nShapeType, nVertices;
int nEntities = 0;
double *minB = new double[4];
double *maxB = new double[4];
SHPGetInfo(hShp, &nEntities, &nShapeType, minB, maxB);
std::cout <<"Xmin, Ymin: "<< minB[0] << " " << minB[1] << std::endl;
std::cout <<"Xmax, Ymax: "<< maxB[0] << " " << maxB[1] << std::endl;
XMAX = maxB[0];
XMIN = minB[0];
YMAX = maxB[1];
YMIN = minB[1];
printf("ShapeType:%d\n", nShapeType);
printf("Entities:%d\n", nEntities); unsigned int VBOs[nEntities], VAOs[nEntities];
glGenVertexArrays(nEntities, VAOs); // we can also generate multiple VAOs or buffers at the same time
glGenBuffers(nEntities, VBOs);
for (int i = 0; i < nEntities; i++)
{
std::vector<double> arr;
int iShape = i;
SHPObject *obj = SHPReadObject(hShp, iShape);
// printf("--------------Feature:%d------------\n",iShape);
int parts = obj->nParts;
int verts = obj->nVertices;
// printf("nParts:%d\n", parts);
// printf("nVertices:%d\n", verts);
for (size_t i = 0; i < verts; i++)
{
double x = (obj->padfX[i] - XMIN) / (XMAX - XMIN) * 2 - 1;
double y = (obj->padfY[i] - YMIN) / (YMAX - YMIN) * 2 - 1;
// double x = obj->padfX[i];
// double y = obj->padfY[i];
// printf("%f,%f;", x, y);
arr.push_back(x);
arr.push_back(y);
arr.push_back(0.0f);
} size.push_back(arr.size()/3);
double vertices[arr.size()];
std::copy(arr.begin(), arr.end(), vertices); glBindVertexArray(VAOs[i]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_DOUBLE, GL_FALSE, 3 * sizeof(double), (void *)0); // Vertex attributes stay the same
glEnableVertexAttribArray(0);
}
SHPClose(hShp); // uncomment this call to draw in wireframe polygons.
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window); // render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram);
for (int i = 0; i < size.size(); i++)
{
glBindVertexArray(VAOs[i]);
glDrawArrays(GL_LINE_STRIP, 0, size[i]);
// std::cout<<VAOs[i]<<" "<<size[i]<<std::endl;
} // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
} // optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(size.size(), VAOs);
glDeleteBuffers(size.size(), VBOs);
glDeleteProgram(shaderProgram); // glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
} // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
} // glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}

本文基于CMake构建,CMakeLists.txt代码如下:

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8) # 项目信息
project (Demo) # 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
# aux_source_directory(.. DIR_SRCS) find_package(shapelib)
find_package(glfw3 REQUIRED)
find_package( OpenGL REQUIRED )
include_directories( ${OPENGL_INCLUDE_DIRS} ) # 指定生成目标
# add_executable(Demo ${DIR_SRCS})
add_executable(Demo ../glad.c ../shapelib_opengl.cpp) target_link_libraries(${PROJECT_NAME} ${shapelib_LIBRARIES} ${OPENGL_LIBRARIES} glfw)

注意

  • shapelib_opengl.cpp是上述代码的文件名

构建:

$ cmake build .

编译:

$ make

运行:

./Demo

结果如图所示:

5. 参考资料

[1]OSGeo/shapelib: Official repository of shapelib (github.com)

[2]Shapefile C Library (maptools.org)

[3]shapelib库 VS2017X64编译并调用 | 码农家园 (codenong.com)

[4]GIS开源库shapeLib的使用方法(一) - cjingzm - 博客园 (cnblogs.com)

[5][Shapefile C Library]读写shp图形(C++&.net Wapper) - 归山须尽丘壑美 - 博客园 (cnblogs.com)

[6]你好,三角形 - LearnOpenGL CN (learnopengl-cn.github.io)

最新文章

  1. Redis集群搭建与简单使用
  2. 修改内联CSS(点击按钮连续改变文字大小、位置,.animate()方法)
  3. mysql概要(三)having
  4. android自定义控件实例(Linearlayout组合TextView和ImageView)
  5. Oracle 10g bigfile表空间、smallfile 表空间
  6. Python之简单工厂模式实现
  7. WordPress主题制作全过程
  8. hibernate4 spring3.2 事务不提交分析
  9. WebService初入
  10. 【网络爬虫入门03】爬虫解析利器beautifulSoup模块的基本应用
  11. motor helper
  12. Linux 第十天
  13. Codeforces 758C-Unfair Poll
  14. 8.2.1.2-MySQL如何优化 WHERE 语句
  15. bzoj [ZJOI2008]生日聚会Party
  16. chapter02 PCA主成分分析在手写数字识别分类的应用
  17. node启动时候报错 Error: Cannot find module &#39;express&#39;
  18. Python中的偏函数
  19. Lambda 表达式语法
  20. 使用 C# 开发智能手机软件:推箱子(十四)

热门文章

  1. 如何使用 IdGen 生成 UID
  2. Dubbo 3 之 Triple 流控反压原理解析
  3. [OpenCV实战]18 Opencv中的单应性矩阵Homography
  4. Educational Codeforces Round 141 (Rated for Div. 2) A-E
  5. Vue + Element 自定义上传封面组件
  6. RSA非对称加密算法浅析
  7. WeetCode4 —— 二叉树遍历与树型DP
  8. 特定领域知识图谱融合方案:文本匹配算法(Simnet、Simcse、Diffcse)
  9. Python openpyxl使用教程
  10. 【Oculus Interaction SDK】(一)设置 VR 相机与控制器 &amp;&amp; 实现简单的抓取功能