2.2.4 电子海图系统解析及开发 海图显示 - 绘制经纬线

电子海图上,可由人工设置是否显示经纬线网格(Graticule)。网格中的经、纬度间距不是固定,是随着比例尺的变化而变化的。t简单情况下,可考虑在当前屏幕范围内,平均显示二条经线与二条纬度,形成九宫格。但当屏幕范围固定的情况下,不管用户缩放还是平面海图,经纬度网格也会固定不变。事实当中,网格上各经纬线所代表的经纬线随显示比例尺变化而变化,但在小范围平移时,其应随鼠标移动而移动,其所代表经纬度应该大致是一个规则的数。因此本项目采用硬编码的方式规定不同比例下,所对应的经差与纬差的范围,实际使用中可根据自己需要酌情调整。

比例尺 经差 纬差 比例尺 经差 纬差
170267536 80 60 388832 0.166666667 0.133333333
113511688 50 40 259221 0.1 0.083333333
75674456 30 20 172814 0.066666667 0.05
50449636 20 15 115209 0.05 0.033333333
33633092 15 10 76806 0.033333333 0.025
22422062 10 6 51204 0.02 0.016666667
14948041 6 5 34136 0.013333333 0.01
9965361 4 3 22757 0.01 0.006666667
6643574 3 2 15171 0.006666667 0.005
4429049 2 1.5 10114 0.003333333 0.003333333
2952699 1 1 6742 0.0025 0.002
1968466 0.833333333 0.666666667 4495 0.002 0.001333333
1312311 0.5 0.333333333 2996 0.001333333 0.001
874874 0.333333333 0.25 1997 0.000833333 0.000666667
583249 0.25 0.2 1331 0.0005 0.000333333
  1. 给窗体EncViewer添加相关字段,记录当前比例尺、平移量、视窗大小等信息。用户设置字段是模拟用户输入,等软件完善后,会将该字段保存进配置文件。
        //用户设置 
        private bool isDisplayGraticule = true;             //是否显示经纬度网格
    
        //当前变量
        private UInt32 current_Scale = 3949147;             //当前比例尺
        private int current_Dx = 0;                         //当前横向平移量
        private int current_Dy = 0;                         //当前纵向平移量
        private int current_Width = 0;                      //当前视窗宽度
        private int current_Height = 0;                     //当前视窗高度
        //当前视窗中心点的位置
        private S57Pos2D initCenterPos = new S57Pos2D(0, 0);
    
  2. 在窗体状态栏添加ToolStripStatusLabel,命名为scaleInfo,用于显示当前比例尺信息。
  3. 当窗体尺寸改变时,保存视窗大小。
        //窗体尺寸改触发
        private void EncViewer_Resize(object sender, EventArgs e)
        {
            current_Width = ClientRectangle.Width;
            current_Height = ClientRectangle.Height;
        }
    
  4. 在窗体加载事件中,根据视窗中心位置及视窗大小,计算出平移量:
        private void EncViewer_Load(object sender, EventArgs e)
        {
            // ...
    
            current_Dx = GeoTools.GetOffsetX(initCenterPos.Longitude, current_Scale, current_Width);
            current_Dy = GeoTools.GetOffsetX(initCenterPos.Latitude, current_Scale, current_Height);
    
            scaleInfo.Text = $"比例尺 1:{current_Scale}";
        }
    
  5. 根据当前比例尺及平移量 ⇒ 求出视窗左下角及右上角的地理位置 ⇒ 参考经、纬差间隔,确定视窗范围内所穿过的经纬线 ⇒ 计算出各经纬线的屏幕坐标 ⇒ 绘制经纬度网格:
        private void skiaView_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
        {
            //...
    
            //绘制经纬线及经纬度
            if (isDisplayGraticule) DrawGraticule(canvas);
        }
    
        private void DrawGraticule(SKCanvas ca)
        {
            //经纬线的画笔
            var paintLatLonLine = new SKPaint()
            {
                Color = new SKColor(180, 180, 180, 128),
                StrokeWidth = 1
            };
            //纬度标签画笔
            var paintLatLabel = new SKPaint()
            {
                Color = new SKColor(150, 0, 0),
                Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal),
                TextSize = 12,
            };
            //经度标签画笔
            SKPaint paintLonLabel = new SKPaint()
            {
                Color = new SKColor(0, 0, 150),
                Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal),
                TextSize = 12,
            };
    
            var tempScales = new[] {170267536, 113511688, 75674456, 50449636, 33633092, 22422062, 14948041, 9965361,
                6643574, 4429049, 2952699, 1968466, 1312311, 874874, 583249, 388832, 259221, 172814, 115209, 76806,
                51204, 34136, 22757, 15171, 10114, 6742, 4495, 2996, 1997, 1331};
    
            var tempLat = new[] {60, 40, 20, 15, 10, 6, 5, 3, 2, 1.5, 1, 0.666666667, 0.333333333, 0.25, 0.2,
                0.133333333, 0.083333333, 0.05, 0.033333333, 0.025, 0.016666667, 0.01, 0.006666667,
                0.005, 0.003333333, 0.002, 0.001333333, 0.001, 0.000666667, 0.000333333};
    
            var tempLon = new[] { 80, 50, 30, 20, 15, 10, 6, 4, 3, 2, 1, 0.833333333, 0.5, 0.333333333, 0.25,
                 0.166666667, 0.1, 0.066666667, 0.05, 0.033333333, 0.02, 0.013333333, 0.01, 0.006666667, 0.003333333,
                 0.0025, 0.002, 0.001333333, 0.000833333, 0.0005 };
    
            var index = -1;
            //确定当前比例级别
            for (int i = 0; i < tempScales.Length; i++)
            {
                if (current_Scale >= tempScales[i])
                {
                    index = i;
                    break;
                }
            }
    
            if (index == -1) index = tempScales.Length - 1;
    
            //经纬度标签的偏移量
            var offsetY = 15;
            var offsetX = 3;
    
            //确定左下及右上角经纬度
            var bottomLeftPos = GeoTools.ScreenPointToGeoPosition(0, current_Height, current_Scale, current_Dx, current_Dy);
            var topRightPos = GeoTools.ScreenPointToGeoPosition(current_Width, 0, current_Scale, current_Dx, current_Dy);
    
            var lat = (int)(bottomLeftPos.Latitude / tempLat[index]) * tempLat[index];
            while (lat < topRightPos.Latitude)
            {
                if (lat > bottomLeftPos.Latitude && lat < topRightPos.Latitude)
                {
                    var py = GeoTools.LatitudeToScreenPoint(lat, current_Scale, current_Dx, current_Dy);
                    ca.DrawLine(0, py, current_Width, py, paintLatLonLine);
                    ca.DrawText(GeoTools.LatLonToString(lat, true, 3), offsetX, py + offsetY, paintLatLabel);
                }
                lat += tempLat[index];
            }
    
            var lon = (int)(bottomLeftPos.Longitude / tempLon[index]) * tempLon[index];
            while (lon < topRightPos.Longitude)
            {
                if (lon > bottomLeftPos.Longitude && lon < topRightPos.Longitude)
                {
                    var px = GeoTools.LongitudeToScreenPoint(lon, current_Scale, current_Dx, current_Dy);
                    ca.DrawLine(px, 0, px, current_Height, paintLatLonLine);
                    ca.DrawText(GeoTools.LatLonToString(lon, true, 3), px + offsetX, offsetY, paintLonLabel);
                }
                lon += tempLon[index];
            }
        }
    

最后,效果图如下:

经纬线网格

你可能感兴趣的:(2.2.4 电子海图系统解析及开发 海图显示 - 绘制经纬线)