#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());
}