NDS莫顿码计算

#pragma once
#include <map>
#include "mesh_info.h"

class NDSMesh
{
public:
	NDSMesh();
	virtual ~NDSMesh();

	/// <summary>
	/// 获取图幅号
	/// </summary>
	/// <param name="longitude">经度</param>
	/// <param name="latitude">纬度</param>
	/// <returns>图幅号</returns>
	static int GetMeshID(double longitude, double latitude);

	/// <summary>
	/// 获取该图幅的经纬度信息
	/// </summary>
	/// <param name="nMeshID"></param>
	/// <returns>图幅信息</returns>
	static MeshInfo GetMeshInfo(int nMeshID);

	/// <summary>
	/// 层级
	/// </summary>
	static int s_level;

protected:

	/// <summary>
	/// 根据经度确定带号
	/// </summary>
	/// <param name="longitude">经度</param>
	/// <returns>带号</returns>
	static int GetZoneID(double longitude);

	/// <summary>
	/// 给定6度带号计算经度范围
	/// </summary>
	/// <param name="nZoneID">带号</param>
	/// <param name="dMinLon">最小经度</param>
	/// <param name="dMaxLon">最大经度</param>
	/// <returns></returns>
	static bool GetZoneBounds(int nZoneID, double& dMinLon, double& dMaxLon);


	/// <summary>
	/// 内插边框
	/// </summary>
	/// <param name="mi"></param>
	static void InterpolateMeshInfoBounds(MeshInfo& mi);


private:
	// 非法NDS图幅号
	static const int INVALID_MESH_ID;

	// 边界插值数
	static const int INTERPOLATION_POINT_COUNT;

	//存放已计算过的mesh信息
	static std::map<int, MeshInfo> map_mesh_infos;
};
/*!
 * file    nds_mesh.h
 * date    2021/04/21 18:31
 * author  yufei.xue
 * contact yufei.xue@horizon.ai
 * todo:   long description
*/

#include "nds_mesh.h"
#include <map_param/include/interface/projection.h>
#include <iostream>

// 小数精度,暂定小数点后10位
#ifndef NUMERIC_ACCURACY
#define NUMERIC_ACCURACY (1e-10) 
#endif

int NDSMesh::s_level = 11;

const int NDSMesh::INVALID_MESH_ID = -1;

const int NDSMesh::INTERPOLATION_POINT_COUNT = 16;

std::map<int, MeshInfo> NDSMesh::map_mesh_infos;


NDSMesh::NDSMesh()
{

}

NDSMesh::~NDSMesh()
{

}

int NDSMesh::GetMeshID(double longitude, double latitude)
{
	// 判断是否在全球经纬度范围之内,如果不在合法范围内,则返回非法图幅号
	if (longitude > 180.0 ||
		longitude < -180.0 ||
		latitude > 90.0 ||
		latitude < -90.0)
	{
		return INVALID_MESH_ID;
	}

	int dwCode = 0; // 返回值,NDS Level13 编码double
	int nLevel = s_level; // 固定的等级,就是13等级

	double dMinX = 0.0;
	double dMaxX = 0.0;

	double dMinY = -90.0;
	double dMaxY = 90.0;

	// 在NDS的编码计算中,Level 0, Level 1 稍微有些许差异,需要单独处理,Level 2以后规律很明显,可以循环处理
	// 先处理0级编码
	// 先判断是在西半球,还是在东半球,Level 0级,西半球编码为1,东半球编码为0
	// 0 级编码
	if (longitude > 0 ||
		std::fabs(longitude) < NUMERIC_ACCURACY)
	{
		dMinX = 0.0;			// 东半球范围
		dMaxX = 180.0;		    // 东半球范围
	}
	else
	{
		dMinX = -180;			// 西半球
		dMaxX = 0;				// 西半球
		dwCode = 1 << (2 * nLevel);		// 将Level0的编码 1 写入到图幅号中
	}

	// 再处理一下1级编码
	// 循环迭代,一直到nLevel等级,目前nLevel固定为13
	for (int i = 1; i <= nLevel; i++)
	{
		// 通过等级,计算当前等级下,判断半球被切分了多少次,2^n
		int nTiles = 1 << i;

		// 当前等级的经纬度间隔数		
		double longitudeInterval = 180.0 / nTiles;
		double latitudeInterval = 180.0 / nTiles;

		double longitudeMiddle = dMinX + longitudeInterval;
		double latitudeMiddle = dMinY + latitudeInterval;

		int dwLevelSubCode = 0;

		if (i % 2 == 0 || i > 2) // 偶数
		{
			// 从下往上,从左往右
			if (longitude > longitudeMiddle ||
				std::fabs(longitude - longitudeMiddle) < NUMERIC_ACCURACY)
			{
				dMinX = longitudeMiddle;

				if (latitude > latitudeMiddle ||
					std::fabs(latitude - latitudeMiddle) < NUMERIC_ACCURACY)
				{
					// 11						
					dMinY = latitudeMiddle;
					dwLevelSubCode = 3; //11
				}
				else
				{
					// 01
					dMaxY = latitudeMiddle;
					dwLevelSubCode = 1; //01
				}
			}
			else
			{
				dMaxX = longitudeMiddle;

				if (latitude > latitudeMiddle ||
					std::fabs(latitude - latitudeMiddle) < NUMERIC_ACCURACY)
				{
					// 10						
					dMinY = latitudeMiddle;
					dwLevelSubCode = 2; //10
				}
				else
				{
					// 00
					dMaxY = latitudeMiddle;
					dwLevelSubCode = 0;
				}
			}

		}
		else // 奇数
		{
			// 从上往下,从左往右
			if (longitude > longitudeMiddle ||
				std::fabs(longitude - longitudeMiddle) < NUMERIC_ACCURACY)
			{
				dMinX = longitudeMiddle;

				if (latitude > latitudeMiddle ||
					std::fabs(latitude - latitudeMiddle) < NUMERIC_ACCURACY)
				{
					// 01
					dMinY = latitudeMiddle;
					dwLevelSubCode = 1;
				}
				else
				{
					// 11
					dMaxY = latitudeMiddle;
					dwLevelSubCode = 3;
				}
			}
			else
			{
				dMaxX = longitudeMiddle;

				if (latitude > latitudeMiddle ||
					std::fabs(latitude - latitudeMiddle) < NUMERIC_ACCURACY)
				{
					// 00
					dMinY = latitudeMiddle;
					dwLevelSubCode = 0;
				}
				else
				{
					// 10
					dMaxY = latitudeMiddle;
					dwLevelSubCode = 2;
				}
			}
		}

		// 更新dwCode
		dwCode |= dwLevelSubCode << ((nLevel - i) * 2);
	}

	return dwCode;
}

MeshInfo NDSMesh::GetMeshInfo(int nMeshID)
{
	auto it = map_mesh_infos.find(nMeshID);
	if (it != map_mesh_infos.end())
	{
		return it->second;
	}

	std::vector<int> masks(s_level + 1, 0);
	for (int i = 0; i <= s_level; i++)
	{
		if(i == 0)
			masks[0] = 0x1 << (s_level * 2);
		else
			masks[i] = 0x3 << (s_level - i) * 2;
	}
	//std::reverse(masks.begin(), masks.end());

	MeshInfo meshInfo;
	meshInfo.level_ = s_level;
	meshInfo.id_ = nMeshID;

	//首先计算图幅的经纬度范围
	meshInfo.min_lon_ = 0;
	meshInfo.max_lon_ = 180;
	meshInfo.min_lat_ = -90;
	meshInfo.max_lat_ = 90;

	if ((masks[0] & nMeshID) == masks[0])
	{
		meshInfo.min_lon_ = -180;
		meshInfo.max_lon_ = 0;
		meshInfo.min_lat_ = -90;
		meshInfo.max_lat_ = 90;
	}

	int nSubCode = masks[1] & nMeshID;
	nSubCode >>= 2 * (s_level - 1);
	switch (nSubCode)
	{
	case 0:
	{
		meshInfo.max_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
		meshInfo.min_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);
	}
	break;
	case 1:
	{
		meshInfo.min_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
		meshInfo.min_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);
	}
	break;
	case 2:
	{
		meshInfo.max_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
		meshInfo.max_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);
	}
	break;
	case 3:
	{
		meshInfo.min_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
		meshInfo.max_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);
	}
	break;

	}

	for (std::size_t i = 2; i < s_level + 1; i++)
	{
		nSubCode = masks[i] & nMeshID;
		nSubCode >>= 2 * (s_level - i);

		switch (nSubCode)
		{
		case 0:
		{
			meshInfo.max_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
			meshInfo.max_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);

		}
		break;
		case 1:
		{
			meshInfo.min_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
			meshInfo.max_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);
		}
		break;
		case 2:
		{
			meshInfo.max_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
			meshInfo.min_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);
		}
		break;
		case 3:
		{
			meshInfo.min_lon_ = meshInfo.min_lon_ + 0.5 * (meshInfo.max_lon_ - meshInfo.min_lon_);
			meshInfo.min_lat_ = meshInfo.min_lat_ + 0.5 * (meshInfo.max_lat_ - meshInfo.min_lat_);
		}
		break;
		}
	}

	// 判断该图幅是否跨带
	int nMinZoneID = GetZoneID(meshInfo.min_lon_);
	int nMaxZoneID = GetZoneID(meshInfo.max_lon_);
	meshInfo.is_cross_zone_ = (nMinZoneID != nMaxZoneID);

	// 计算带号
	if (meshInfo.is_cross_zone_) // 跨带了要计算,应该使用哪一个带
	{
		// 获取最小带号的边界范围
		double dMinZoneMinX = 0.0;
		double dMinZoneMaxX = 0.0;

		GetZoneBounds(nMinZoneID, dMinZoneMinX, dMinZoneMaxX);

		// 将经度范围从[-180, 180],映射到[0, 360]
		if (dMinZoneMaxX < 0)
		{
			dMinZoneMaxX += 360.0;
		}

		// 将NDS图幅范围的经度也映射到[0, 360]
		double dMeshMinX = meshInfo.min_lon_;
		double dMeshMaxX = meshInfo.max_lon_;

		if (dMeshMinX < 0)
		{
			dMeshMinX += 360.0;
		}

		if (dMeshMaxX < 0)
		{
			dMeshMaxX += 360.0;
		}

		// 计算图幅的中央经线
		double dMeshCentralX = dMeshMinX + 0.5 * (dMeshMaxX - dMeshMinX);

		// 查看两个带公共边界处距离NDS图幅中央经线关系
		if (dMeshCentralX < dMinZoneMaxX) // 说明图幅的数据大部分在带号稍微小的带中
		{
			meshInfo.zone_id_ = nMinZoneID;
		}
		else
		{
			meshInfo.zone_id_ = nMaxZoneID;
		}
	}
	else
	{
		meshInfo.zone_id_ = nMinZoneID;
	}

	InterpolateMeshInfoBounds(meshInfo);

	map_mesh_infos[nMeshID] = meshInfo;
	return meshInfo;
}

int NDSMesh::GetZoneID(double longitude)
{
	return static_cast<int>((longitude + 180) / 6) + 1;
}

bool NDSMesh::GetZoneBounds(int nZoneID, double& dMinLon, double& dMaxLon)
{
	if (nZoneID < 1 || nZoneID > 60)
	{
		return false;
	}

	dMinLon = 6.0 * (nZoneID - 1) - 180.0;
	dMaxLon = 6.0 * (nZoneID - 1) - 174.0;
	return true;
}

void NDSMesh::InterpolateMeshInfoBounds(MeshInfo& mi)
{
	mi.bounds_.clear();

	double dIntervalLongitude = (mi.max_lon_ - mi.min_lon_) / INTERPOLATION_POINT_COUNT;
	double dIntervalLatitude = (mi.max_lat_ - mi.min_lat_) / INTERPOLATION_POINT_COUNT;

	Eigen::Vector2d pnt3D;

	// 进行坐标转换,转换成平面坐标
	char zone_id[20];
	map_interface::LLtoUTM(mi.min_lat_,
		mi.min_lon_,
		pnt3D.y(),
		pnt3D.x(),
		zone_id
	);

	mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());

	// 每一个边上中间增加16个点,以提高边框精度
	// 左边
	for (int i = 1; i < INTERPOLATION_POINT_COUNT; i++)
	{
		map_interface::LLtoUTM(
			mi.min_lat_ + i * dIntervalLatitude,
			mi.min_lon_,
			pnt3D.y(),
			pnt3D.x(),
			zone_id
		);
		mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());
	}

	map_interface::LLtoUTM(
		mi.max_lat_,
		mi.min_lon_,
		pnt3D.y(),
		pnt3D.x(),
		zone_id
	);

	mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());

	// 上边
	for (int i = 1; i < INTERPOLATION_POINT_COUNT; i++)
	{
		map_interface::LLtoUTM(
			mi.max_lat_,
			mi.min_lon_ + i * dIntervalLongitude,
			pnt3D.y(),
			pnt3D.x(),
			zone_id
		);

		mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());
	}

	map_interface::LLtoUTM(
		mi.max_lat_,
		mi.max_lon_,
		pnt3D.y(),
		pnt3D.x(),
		zone_id
	);

	mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());

	// 右边
	for (int i = 1; i < INTERPOLATION_POINT_COUNT; i++)
	{
		map_interface::LLtoUTM(
			mi.max_lat_ - i * dIntervalLatitude,
			mi.max_lon_,
			pnt3D.y(),
			pnt3D.x(),
			zone_id
		);

		mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());
	}

	map_interface::LLtoUTM(
		mi.min_lat_,
		mi.max_lon_,
		pnt3D.y(),
		pnt3D.x(),
		zone_id
	);

	mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());

	// 下边
	for (int i = 1; i < INTERPOLATION_POINT_COUNT; i++)
	{
		map_interface::LLtoUTM(
			mi.min_lat_,
			mi.max_lon_ - i * dIntervalLongitude,
			pnt3D.y(),
			pnt3D.x(),
			zone_id
		);

		mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());
	}

	map_interface::LLtoUTM(
		mi.min_lat_,
		mi.min_lon_,
		pnt3D.y(),
		pnt3D.x(),
		zone_id
	);

	mi.bounds_.emplace_back(pnt3D.x(), pnt3D.y());
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神气爱哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值