使用GDAL库读取SRTM格式的高程数据

文件格式以及原理参考老外的文章:https://librenepal.com/article/reading-srtm-data-with-python/

 

GDAL包:https://github.com/OSGeo/gdal/releases/tag/v3.1.3

其实文件格式很容易理解,比如是1/3弧度的精度情况下,就是1度分为1/1200份,所以一个文件表示经度和维度各1度的方格,就是切成1200x1200份,存储为二维矩阵是1201x1201,因为边界占了1行1列:

其中二维数组是按照地图来存储的,所以从低维度和低经度取索引时候需要计算下:

Physical representation of data in hgt file

老外是用python写的,我用c++重写的,歌词大意如下:

#pragma once

#include <math.h>
#include <string.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <iostream>
#include <memory>
#include <algorithm>
#include <map>
#include <mutex>
#include <thread>

//#include "include/gdal.h"  
#include <gdal_priv.h>

#ifdef _DEBUG

#pragma comment(lib, "gdal_i_d.lib")
#else
#pragma comment(lib, "gdal_i.lib")
#endif

using namespace std;
// 数据结构
class SRTM
{
public:
	SRTM()
	{
		if (runOnce == false)
		{
			GDALAllRegister();
			
			// windows操作系统使用GBK
			CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
			runOnce = true;
		}
	}
	~SRTM()
	{
		if (pData != nullptr)
			delete[] pData;

		//std::cerr << "SRTM析构释放数据" << endl;
	};
	// 分辨率
	const int sample = 1200;
	static bool runOnce;

private:
	SRTM(const SRTM & other) = delete;
	SRTM & operator = (const SRTM & other) = delete;

	SRTM(SRTM && other) = delete;
	SRTM & operator = (SRTM && other) = delete;

	unsigned int nWidth = 1201;
	unsigned int nHeight = 1201;
	// c++17以前不支持动态数组使用shared_ptr管理
	// std::unique_ptr<short[]> data;
	short int *pData = nullptr;

	double minLat;
	double maxLat;
	double minLon;
	double maxLon;

	static double Mercator2Lon(double lon)//墨卡托转WGS84:经度
	{
		return lon / 20037508.34 * 180.0;
	}
	static double Mercator2Lat(double lat)//墨卡托转WGS84:纬度
	{
		double result = 0;
		double mid = lat / 20037508.34 * 180.0;
		result = 180.0 / M_PI * (2.0 * atan(exp(mid * M_PI / 180.0)) - M_PI / 2.0);
		return result;
	}

public:
	// 从二维数组中查询,但是编号是二维数组的左下角,需要重新计算一下index
	bool query(short & height, double lat, double lon)
	{
		if (pData == nullptr)
			return false;

		int lat_row = int(round((lat - int(lat)) * sample));
		int lon_row = int(round((lon - int(lon)) * sample));

		//lat_row = int(round((lat - minLat) * sample));
		//lon_row = int(round((lon - minLon) * sample));

		lat_row = abs(lat_row);
		lon_row = abs(lon_row);

		size_t index = (nHeight - 1 - lat_row) * nWidth + lon_row;
		if (index >= sample * sample)
			return false;

		height = pData[index];

		return true;

	}
	bool load(const std::string fileName)
	{
		return load(fileName.c_str());
	}
	bool load(const char * fileName)
	{

		GDALDataset *poDataSet;
		GDALRasterBand *pBand;
		poDataSet = (GDALDataset*)GDALOpen(fileName, GA_ReadOnly);
		if (poDataSet == nullptr)
			return false;

		this->nWidth = poDataSet->GetRasterXSize();//获取图像宽度
		this->nHeight = poDataSet->GetRasterYSize();//获取图像高度

		// 存储边界信息
		double adfGeoTransform[6];
		double value[6];
		if (poDataSet->GetGeoTransform(adfGeoTransform) == CE_None)
		{
			value[0] = adfGeoTransform[0];   // 起点,左上经度
			value[1] = adfGeoTransform[3];   // 起点,左上维度
			value[2] = adfGeoTransform[1] * (double)nWidth + adfGeoTransform[0];  // 右侧经度
			value[3] = adfGeoTransform[5] * (double)nHeight + adfGeoTransform[3];  // 右下

			if (value[0] > 180 || value[0] < -180)//墨卡托转WGS84
			{
				value[0] = Mercator2Lon(value[0]);
				value[1] = Mercator2Lat(value[1]);
				value[2] = Mercator2Lon(value[2]);
				value[3] = Mercator2Lat(value[3]);
			}
		}
		this->minLon = value[0];
		this->maxLon = value[2];
		this->minLat = value[3];
		this->maxLat = value[1];

		if (pData != nullptr)
			delete[] pData;

		this->pData = new short[nWidth * nHeight];
		pBand = poDataSet->GetRasterBand(1);
		pBand->RasterIO(GF_Read, 0, 0, nWidth, nHeight, pData, nWidth, nHeight,
			pBand->GetRasterDataType(), 0, 0);

		//int i = pData[1000 * nWidth + 1];

		GDALClose(poDataSet);//关闭数据集

		return true;
	}

public:
	// 通过经纬度计算标准文件名
	static std::string getFileName(double lat, double lon)
	{
		char ns;
		char ew;
		if (lat >= 0)
			ns = 'N';
		else
			ns = 'S';

		if (lon >= 0)
			ew = 'E';
		else
			ew = 'W';

		char buffer[20];
		int i_lat = abs(int(lat));
		int i_lon = abs(int(lon));
		//snprintf(buffer, 20, "%.1s", &ns);
		snprintf(buffer, 20, "%.1s%02d%.1s%03d.hgt", &ns, i_lat, &ew, i_lon);

		return buffer;
	}

};
// 初始化
bool SRTM::runOnce = false;

// c++ 17
//namespace fs = std::filesystem;
// 对缓存进行管理
class SRTM_Cache
{
public:
	SRTM_Cache(const char * dir) : rootDir(dir)
	{
		setRootPath(dir);
	}

	~SRTM_Cache()
	{
		// 自动释放资源
		//dataMap.clear();
	}

	void setRootPath(const char * dir)
	{
		rootDir = dir;

		// 去掉末尾的\\ 
		size_t off = rootDir.rfind('\\', rootDir.size());
		if (off > 0 && (off == rootDir.size()-1))
			rootDir = rootDir.substr(0, off);
	}

	bool query(short & height, double lat, double lon)
	{
		std::string fileName = SRTM::getFileName(lat, lon);
		bool ret = false;
		std::shared_ptr<SRTM> ptr = nullptr;

		{ // 添加作用域,提早解锁
			std::lock_guard<std::mutex> autoLock(mapMutex);   // 加锁

			auto it = dataMap.find(fileName);
			if (it == dataMap.end())
			{
				// 尝试加载文件
				std::string filePath = rootDir + "\\" + fileName;
				ptr = std::make_shared<SRTM>();
				ret = ptr->load(filePath);
				if (ret)
				{
					dataMap.insert(std::pair<std::string, std::shared_ptr<SRTM> >(fileName, ptr));

				}
				else
				{
					return false;
				}
	
				return ret;
			}
			else
			{
				std::shared_ptr<SRTM> ptr = it->second;
			}
		} // 添加作用域,提早解锁

		//std::cout << ptr.use_count() << endl;
		if (ptr != nullptr)
		{
			ret = ptr->query(height, lat, lon);
			return ret;
		}
		return false;
	} // end of query

private:
	SRTM_Cache(const SRTM_Cache & other) = delete;
	SRTM_Cache& operator =(const SRTM_Cache & other) = delete;

	// 用智能指针管理数据类实例,因为已经禁止了赋值和拷贝构造,
	std::map<std::string, std::shared_ptr<SRTM> > dataMap;
	std::string rootDir;

	// 添加多线程互斥
	std::mutex  mapMutex;
};

使用的方法如下:

// readDEM.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "SRTM.h"

int main()
{
	SRTM_Cache manager("D:\\DEM数据\\SRTM3-90米全国DEM\\");

	double lat = 39.990618;
	double lon = 116.169644;

	short heiht;
	bool ret = manager.query(heiht, lat, lon);

	if (ret == false)
	{

		std::cout << "false" << endl;
	}
	else
	{
		string str;
		str.resize(100, '\0');
		snprintf(const_cast<char *>(str.data()), 100, "%.5f, %.5f 高程:%d", lat, lon, heiht);

		std::cout << str.c_str() << endl;
	}

	lat = 41.56;
	ret = manager.query(heiht, lat, lon);

	if (ret == false)
	{

		std::cout << "false" << endl;
	}
	else
	{
		string str;
		str.resize(100, '\0');
		snprintf(const_cast<char *>(str.data()), 100, "%.5f, %.5f 高程:%d", lat, lon, heiht);

		std::cout << str.c_str() << endl;
	}
}

结果:

39.99062, 116.16964 高程:509
41.56000, 116.16964 高程:1792

备注:

使用vcpkg管理各种开源包真的非常的方便,比自己一个一个找强多了。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
语言的好处及其适用范围 C语言是一种非常流行的编程语言,同时又是一种非常灵活和高效的语言,其应用范围广泛。下面我们就一起来看看使用C语言的好处及其适用范围。 C语言的好处: 1.高效性:C语言是一种非常高效的编程语言,其执行速度比大多数其他编程语言快很多,因此对于需要进行处理大量数据和进行复杂计算的场景非常适合。 2.灵活性:C语言提供了非常多的工具和函数,同时又支持用户自定义函数和数据类型,因此可以让程序员根据自己的需要编写出非常灵活的程序。 3.可移植性:C语言可以在不同的操作系统和硬件平台上运行,因此可以让程序更加通用和适用于不同的计算环境。 4.易于学习:C语言的语法相对简单,模块化和结构化编程风格易于理解,因此入门门槛较低,易于学习。 C语言的适用范围: 1.系统软件开发:C语言由于其高效性和可移植性的特点,其广泛用于系统软件开发中,如操作系统、编译器等。 2.嵌入式系统开发:C语言在嵌入式系统开发中也非常流行,其可以用于编写驱动程序、操作系统、网络协议栈等等。 3.科学计算:C语言的执行速度非常快,其广泛用于科学计算领域,如数值分析、机器学习等等。 4.游戏开发:C语言由于其高效性,非常适合用于游戏开发中,其可以用于编写游戏引擎、游戏物理引擎等。 总之,使用C语言可以带来高效、灵活、可移植等多种好处,其可以应用于不同领域的开发工作中。当然,C语言也有其缺点,如容易出错、内存管理较为复杂等,因此使用C语言的开发人员需要具备一定的编程和操作系统相关知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值