需求
2016年5月,客户要求在地图上显示路灯及数据,分析数据生成报表,以便查看分析路灯情况。
选型
国外项目就不考虑国内的地图了,开始想使用google的web地图,考虑到地图上标记物过多影响性能及使用体验,对gmap测试后,选用gmap.net。
开发
功能:
1.导入路灯及数据,在地图上添加、编辑、删除路灯,路灯数据软件修正、移除错误数据
2.地图上线时路灯及数据,数据以标记、曲线、表格方式显示
3.导出整个系统数据,下次可以将系统导出的文件导入进行查看;导出生成excel报表
4.多点测距;多边形方式进行选择查看,选择多边形内路灯情况
代码
计算2个坐标点距离
private static double EARTH_RADIUS = 6378137;//赤道半径(单位m)
/**
* 转化为弧度(rad)
* */
private static double rad(double d)
{
return d * Math.PI / 180.0;
}
/**
* 基于余弦定理求两经纬度距离
* @param lon1 第一点的精度
* @param lat1 第一点的纬度
* @param lon2 第二点的精度
* @param lat3 第二点的纬度
* @return 返回的距离,单位m
* */
public static double GetDistance(double lng1, double lat1, double lng2, double lat2)
{
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double radLng1 = rad(lng1);
double radLng2 = rad(lng2);
if (radLat1 < 0)
radLat1 = Math.PI / 2 + Math.Abs(radLat1);// south
if (radLat1 > 0)
radLat1 = Math.PI / 2 - Math.Abs(radLat1);// north
if (radLng1 < 0)
radLng1 = Math.PI * 2 - Math.Abs(radLng1);// west
if (radLat2 < 0)
radLat2 = Math.PI / 2 + Math.Abs(radLat2);// south
if (radLat2 > 0)
radLat2 = Math.PI / 2 - Math.Abs(radLat2);// north
if (radLng2 < 0)
radLng2 = Math.PI * 2 - Math.Abs(radLng2);// west
double x1 = EARTH_RADIUS * Math.Cos(radLng1) * Math.Sin(radLat1);
double y1 = EARTH_RADIUS * Math.Sin(radLng1) * Math.Sin(radLat1);
double z1 = EARTH_RADIUS * Math.Cos(radLat1);
double x2 = EARTH_RADIUS * Math.Cos(radLng2) * Math.Sin(radLat2);
double y2 = EARTH_RADIUS * Math.Sin(radLng2) * Math.Sin(radLat2);
double z2 = EARTH_RADIUS * Math.Cos(radLat2);
double d = Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2));
//余弦定理求夹角
double theta = Math.Acos((EARTH_RADIUS * EARTH_RADIUS + EARTH_RADIUS * EARTH_RADIUS - d * d) / (2 * EARTH_RADIUS * EARTH_RADIUS));
double dist = theta * EARTH_RADIUS;
return dist;
}
获取方向角度
/// <summary>
/// 获取方向角度
/// </summary>
/// <param name="lon1"></param>
/// <param name="lat1"></param>
/// <param name="lon2"></param>
/// <param name="lat2"></param>
/// <returns></returns>
public static double GetDirection(double lon1, double lat1, double lon2, double lat2)
{
double x1 = lon1;
double y1 = lat1;
double x2 = lon2;
double y2 = lat2;
double pi = Math.PI;
double w1 = y1 / 180 * pi;
double j1 = x1 / 180 * pi;
double w2 = y2 / 180 * pi;
double j2 = x2 / 180 * pi;
double ret;
if (j1 == j2)
{
if (w1 > w2) ret = 270; //北半球的情况,南半球忽略
else if (w1 < w2) ret = 90;
else ret =-1;//位置完全相同
}
ret = 4 * Math.Pow(Math.Sin((w1 - w2) / 2), 2) - Math.Pow(Math.Sin((j1 - j2) / 2) * (Math.Cos(w1) - Math.Cos(w2)), 2);
ret = Math.Sqrt(ret);
double temp = (Math.Sin(Math.Abs(j1 - j2) / 2) * (Math.Cos(w1) + Math.Cos(w2)));
ret = ret / temp;
ret = Math.Atan(ret) / pi * 180;
if (j1 > j2) // 1为参考点坐标
{
if (w1 > w2) ret += 180;
else ret = 180 - ret;
}
else if (w1 > w2) ret = 360 - ret;
return ret;
//result.Text = Convert.ToString(ret);
//if ((ret <= 10) || (ret > 350)) angle.Text = "东";
//if ((ret > 10) && (ret <= 80)) angle.Text = "东北";
//if ((ret > 80) && (ret <= 100)) angle.Text = "北";
//if ((ret > 100) && (ret <= 170)) angle.Text = "西北";
//if ((ret > 170) && (ret <= 190)) angle.Text = "西";
//if ((ret > 190) && (ret <= 260)) angle.Text = "西南";
//if ((ret > 260) && (ret <= 280)) angle.Text = "南";
//if ((ret > 280) && (ret <= 350)) angle.Text = "东南";
}
判断坐标是否在多边形内
/// <summary>
/// 判断点是否在多边形内.
/// </summary>
/// <param name="checkPoint">要判断的点</param>
/// <param name="polygonPoints">多边形的顶点</param>
/// <returns></returns>
public static bool IsInPolygon(PointLatLng checkPoint, List<PointLatLng> polygonPoints)
{
int counter = 0;
int i;
double xinters;
PointLatLng p1, p2;
int pointCount = polygonPoints.Count;
p1 = polygonPoints[0];
for (i = 1; i <= pointCount; i++)
{
p2 = polygonPoints[i % pointCount];
if (checkPoint.Lat > Math.Min(p1.Lat, p2.Lat)//校验点的Y大于线段端点的最小Y
&& checkPoint.Lat <= Math.Max(p1.Lat, p2.Lat))//校验点的Y小于线段端点的最大Y
{
if (checkPoint.Lng <= Math.Max(p1.Lng, p2.Lng))//校验点的X小于等线段端点的最大X(使用校验点的左射线判断).
{
if (p1.Lat != p2.Lat)//线段不平行于X轴
{
xinters = (checkPoint.Lat - p1.Lat) * (p2.Lng - p1.Lng) / (p2.Lat - p1.Lat) + p1.Lng;
if (p1.Lng == p2.Lng || checkPoint.Lng <= xinters)
{
counter++;
}
}
}
}
p1 = p2;
}
if (counter % 2 == 0)
{
return false;
}
else
{
return true;
}
}
多点测距
多边形选择
关于测距
测距尝试过使用gmap提供的根据道路测距,发现还是不太准,所以还是选择了手动选择坐标点连线测距。
后记
该项目2018年初还增加了一些功能,开发过程中有不少次沟通和修改,主要就是一些gps和数据拆分、补全上的处理和报表算法上有些繁琐,项目目前暂时告一段落。