2.2.5 电子海图系统解析及开发 海图显示 - 添加平移、缩放、鼠标位置功能

一般电子海图系统都是通过鼠标滚轮来实现海图缩放,按住鼠标移动实现海图平移,鼠标任意移动获取相应的地理位置。因此必须在EncViewer_Load事件中,给绘图控件skiaView绑定如下鼠标事件:

  skiaView.MouseWheel += skiaView_MouseWheel;
  skiaView.MouseDown += skiaView_MouseDown;
  skiaView.MouseMove += skiaView_MouseMove;
  skiaView.MouseUp += skiaView_MouseUp;

实现海图平移

通常情况下,海图平移流程如下:

  1. 用户左键按下鼠标时,记录当前鼠标位置(x1, y1)、海图平移量1,并标识开始移动isDragging = true
  2. 用户按住鼠标移动时,动态获取鼠标位置(x2, y2),得到鼠标平移量。
  3. 海图平移量1加上鼠标平移量,得出新的海图平移量,利用新海图平移量,重新绘制海图;
  4. 用户松开鼠标时,标识平移结束isDragging = false

上述流程中的第2步,鼠标每移动一点,海图都重新绘制,而海图绘制过程十分消耗资源。因此,优化上述流程如下:

  1. 用户左键按下鼠标时,记录当前鼠标位置(x1, y1)、海图平移量1,并标识开始移动isDragging = true获取当前视窗的截图
  2. 用户按住鼠标移动时,动态获取鼠标位置(x2, y2),得到鼠标平移量。
  3. 利用鼠标平移量,将截图绘制到视窗中;平移开始后,不再绘制海图
  4. 用户松开鼠标时,标识平移结束isDragging = false,海图平移量1加上鼠标平移量,得出新的海图平移量,利用新海图平移量,重新绘制海图。

上述流程的平移过程中,海图只在松开鼠标时绘制一次,移动过程只是绘制截图。为实现上述流程,需要对代码进行如下修改:

  1. 窗体中新增如下字段:
        //鼠标平移相关
        private bool isDragging = false;            //平移开始标志
        private int preMousePosX = 0;               //鼠标按下时的位置               
        private int preMousePosY = 0;               
        private int screenOffsetX = 0;              //截图的平移量               
        private int screenOffsetY = 0;              
        private SKImage screenImage;                //鼠标按下时的截图
    
  2. 按下鼠标后,记录鼠标位置,但由于无法在skiaView_PaintSurface获取截图,因此采用变通方法将截图清空:
        private void skiaView_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                //开始平移,并记录鼠标位置
                this.Cursor = Cursors.Hand;
                isDragging = true;
                preMousePosX = e.X;
                preMousePosY = e.Y;
    
                //此处无法直接获取截图,因此设为空
                screenImage = null;
            }
        }
    
  3. 移动鼠标中,记录截图平移量。
        private void skiaView_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                //按住鼠标移动,记录截图平移量
                screenOffsetX = e.X - preMousePosX;
                screenOffsetY = e.Y - preMousePosY;
    
                //只是移动截图
                this.skiaView.Refresh();
            }
        }
    
  4. 鼠标松开后,结束平移,重绘海图。
        private void skiaView_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (isDragging)
                {
                    this.Cursor = Cursors.Default;
                    isDragging = false;
                    current_Dx += (e.X - preMousePosX);
                    current_Dy -= (e.Y - preMousePosY);
    
                    //重绘海图
                    this.skiaView.Refresh();
                }
            }
        }
    
  5. 最后,调整skiaView_PaintSurface的代码。当处于平移状态时,动态绘制截图。
        private void skiaView_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
        {
            //画布
            var canvas = e.Surface.Canvas;
    
            if (isDragging)  //平移中
            {
                if (screenImage == null)
                {
                    screenImage = e.Surface.Snapshot();
                }
    
                ClearCanvas(canvas);
                canvas.DrawImage(screenImage, screenOffsetX, screenOffsetY);
            }
            else
            {
                ClearCanvas(canvas);
    
                //绘制经纬线及经纬度
                if (isDisplayGraticule) DrawGraticule(canvas);
            }
        }
    

平移中,只是绘制截图,因此四周出现部分空白区域;松开鼠标,重绘海图,空白区域消失。效果图如下:

平移效果图

实现海图缩放

缩放的中心点为屏幕中心(即滚动过程中,屏幕中心位置不变,也可用别的逻辑,如鼠标中心为缩放中心等),因此在滚轮滚动之前,记录当前视窗中心地理坐标。滚轮每滚动一次缩放1.5倍(数值可调整),得到新的海图比例尺。利用中心地理坐标、新比例尺、视窗大小,计算出新的海图平移量,最后重绘海图。

        private void skiaView_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta == 0) return;
            var factor = e.Delta > 0 ? 0.6666666667d : 1.5d;
            var newScale = (uint)(current_Scale * factor);
            if ((factor > 1 && newScale > maxScale) || (factor < 1 && newScale < minScale)) return;

            //原比例尺下中心位置对应的经纬度
            var pos = GeoTools.ScreenPointToGeoPosition(current_Width / 2, current_Height / 2, current_Scale, current_Dx, current_Dy);
            current_Scale = newScale;

            //重新计算位置偏移
            current_Dx = GeoTools.GetOffsetX(pos.Longitude, current_Scale, current_Width);
            current_Dy = GeoTools.GetOffsetY(pos.Latitude, current_Scale, current_Height);

            scaleInfo.Text = $"比例尺 1:{current_Scale}";

            this.skiaView.Refresh();
        }

获取鼠标处的地理位置

  1. 在窗体状态栏添加ToolStripStatusLabel,命名为mouseInfo,用于显示鼠标处位置信息。
  2. 修改skiaView_MouseMove代码,直接把屏幕坐标转成地理坐标:
        var pos = GeoTools.ScreenPointToGeoPosition(e.X, e.Y, current_Scale, current_Dx, current_Dy);
        mouseInfo.Text = $"屏幕坐标: {e.X} {e.Y}  地理坐标: {pos}";

你可能感兴趣的:(2.2.5 电子海图系统解析及开发 海图显示 - 添加平移、缩放、鼠标位置功能)