EBL/VRM是電子海圖系統(tǒng)很常見的功能容达。至少應(yīng)具有如下功能:
- 顯示EBL/VRM辆琅;
- 如果存在當(dāng)前船位,EBL/VRM的初始中心是船位感局,否則是屏幕中心胚想;
- 按住鼠標(biāo)可拖拽調(diào)整EBL的方位琐凭;
- 按住鼠標(biāo)可拖拽調(diào)整VRM的大小浊服;
- 按住鼠標(biāo)可拖拽調(diào)整EBL/VRM中心的位置统屈,
- 與鼠標(biāo)平移、縮放操作不沖突牙躺。
判斷點(diǎn)或線是否移動(dòng)愁憔,都需要計(jì)算鼠標(biāo)位置與點(diǎn)、線之間的距離孽拷。當(dāng)距離小于一定范圍(4像素)即可認(rèn)為需要移動(dòng)吨掌。繪制EBL/VRM過程中,涉及到的距離都是用的屏幕坐標(biāo)脓恕。其中計(jì)算點(diǎn)到直線的距離用到的公式為:
在GeoTools
里添加屏幕距離公式相關(guān)代碼(考慮到開方操作較為復(fù)雜膜宋,實(shí)際返回的為距離的平方數(shù))。
//距離范圍及平方
public const int DisTorelence = 4;
public const int DisTorelenceSqrd = 4*4;
//屏幕上兩點(diǎn)之間的距離的平方
public static double DistanceSqrd(int x1, int y1, int x2, int y2)
{
return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
}
public static double DistanceSqrd(Point pt1, Point pt2)
{
return DistanceSqrd(pt1.X, pt1.Y, pt2.X, pt2.Y);
}
//屏幕上點(diǎn)到直線的距離的平方炼幔,直線用兩坐標(biāo)點(diǎn)表示
public static double DistanceFromLineSqrd(Point pt, int x1, int y1, int x2, int y2)
{
if (x1 == x2 && y1 == y2) return DistanceSqrd(pt.X, pt.Y, x1, y1);
var a = y2 - y1;
var b = x2 - x1;
var c = a * pt.X - b * pt.Y + x2 * y1 - y2 * x1;
return c * c / (a * a + b * b);
}
public static double DistanceFromLineSqrd(Point pt, Point pt1, Point pt2)
{
return DistanceFromLineSqrd(pt, pt1.X, pt1.Y, pt2.X, pt2.Y);
}
- 窗體添加如下EBL/VRM相關(guān)字段:
private S57Pos2D eblVrmCenterPos; //中心點(diǎn)地理坐標(biāo) private S57Pos2D eblVrmOtherPos; //VRM上一點(diǎn)的地理坐標(biāo)激蹲,計(jì)算距離用 private Point eblVrmCenterPoint; //中心點(diǎn)屏幕坐標(biāo) private Point eblVrmOtherPoint; //VRM上一點(diǎn)的屏幕坐標(biāo),計(jì)算距離用 private double eblAngle = 45d; //EBL的默認(rèn)方位 private double vrmNuaticalMile = 0; //VRM的地理距離 private int vrmRange = 100; //VRM的默認(rèn)屏幕距離, 100個(gè)像素 private bool showEBLVRM = false; //是否繪制EBL/VRM private bool canEblVrmCenterMove = false; //是否移動(dòng)中心點(diǎn) private bool canEblMove = false; //是否移動(dòng)EBL private bool canVrmMove = false; //是否移動(dòng)VRM private SKColor eblVrmColor = new SKColor(230, 121, 56); //EBL/VRM的顏色 private SKPaint eblVrmBrush = null; //用于繪制中心點(diǎn) private SKPaint eblVrmPen = null; //用于繪制線 private readonly float[] LongDash = new float[] { 20, 4 }; //虛線類型
- 為窗體添加菜單控件
MenuStrip
江掩,并為其添加菜單項(xiàng)ToolStripMenuItem
学辱,命名為eblVrmToolStripMenuItem
乘瓤。在狀態(tài)欄添加ToolStripStatusLabel
,命名為eblVrmInfo
策泣,用于顯示EBL/VRM信息衙傀。 - 為
eblVrmToolStripMenuItem
綁定單擊事件eblVrmToolStripMenuItem_Click
:private void eblVrmToolStripMenuItem_Click(object sender, EventArgs e) { if (eblVrmBrush == null) //畫刷初始化 { eblVrmBrush = new SKPaint() { Color = eblVrmColor, Style = SKPaintStyle.Fill}; eblVrmPen = new SKPaint() { Color = eblVrmColor, StrokeWidth = 2, PathEffect = SKPathEffect.CreateDash(LongDash, 0), Style = SKPaintStyle.Stroke}; } showEBLVRM = !showEBLVRM; if (showEBLVRM) { eblVrmCenterPos = GeoTools.ScreenPointToGeoPosition( current_Width / 2, current_Width / 2, current_Scale, current_Dx, current_Dy); eBLVRMOtherPos = null; } else { canEblVrmCenterMove = false; canEblMove = false; canVrmMove = false; eblAngle = 45d; vrmRange = 100; this.eblVrmInfo.Text = ""; } showEBLVRMInfo(); this.skiaView.Refresh(); } private void showEBLVRMInfo() { if (showEBLVRM) this.eblVrmInfo.Text = $"VRM: {vrmNuaticalMile:0.0}' EBL: {eblAngle:0.0}°"; else this.eblVrmInfo.Text = ""; }
- 調(diào)整
skiaView_PaintSurface
代碼,在繪制經(jīng)緯線及經(jīng)緯度代碼后萨咕,添加如下代碼://繪制EBL/VRM if (showEBLVRM) DrawEBLVRM(canvas);
- 編寫方法
DrawEBLVRM
统抬。private void DrawEBLVRM(SKCanvas ca) { //繪制中心點(diǎn),半徑為4像素 eblVrmCenterPoint = GeoTools.GeoPositionToScreenPoint(eblVrmCenterPos, current_Scale, current_Dx, current_Dy); ca.DrawCircle(eblVrmCenterPoint.X, eblVrmCenterPoint.Y, 4, eblVrmBrush); //繪制線 //以中心線危队,方位角做射線聪建,判斷方位線與視窗邊緣的交點(diǎn) eblVrmOtherPoint = GeoTools.FindIntersectPoint(eblVrmCenterPoint, eblAngle, current_Width, current_Height); ca.DrawLine(eblVrmCenterPoint.X, eblVrmCenterPoint.Y, eblVrmOtherPoint.X, eblVrmOtherPoint.Y, eblVrmPen); //繪制圓 if (eblVrmOtherPos == null) { eblVrmOtherPos = GeoTools.ScreenPointToGeoPosition(eblVrmCenterPoint.X + vrmRange, eblVrmCenterPoint.Y, current_Scale, current_Dx, current_Dy); } else { var pt = GeoTools.GeoPositionToScreenPoint(eblVrmOtherPos, current_Scale, current_Dx, current_Dy); vrmRange = (int)Math.Sqrt(GeoTools.DistanceSqrd(pt, eblVrmCenterPoint)); } vrmNuaticalMile = GeoTools.Distance(eblVrmCenterPos, eblVrmOtherPos); ca.DrawCircle(eblVrmCenterPoint.X, eblVrmCenterPoint.Y, vrmRange, eblVrmPen); showEBLVRMInfo(); }
- 在顯示EBL/VRM時(shí),涉及到鼠標(biāo)操作茫陆。
- 按下鼠標(biāo)金麸,需要判斷鼠標(biāo)位置是否在EBL/VRM附近。在附近簿盅,則移動(dòng)EBL/VRM挥下,否則移動(dòng)海圖。
private void skiaView_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { //是否繪制VRM if (showEBLVRM) { if (GeoTools.DistanceSqrd(e.Location, eblVrmCenterPoint) <= GeoTools.DisTorelenceSqrd) { canEblVrmCenterMove = true; //中心點(diǎn)移動(dòng) } else { var disToCenter = Math.Sqrt(GeoTools.DistanceSqrd(e.Location, eblVrmCenterPoint)); if (Math.Abs(disToCenter - vrmRange) <= GeoTools.DisTorelence) { canVrmMove = true; //VRM移動(dòng) } var disToEBL = GeoTools.DistanceFromLineSqrd(e.Location, eblVrmCenterPoint, eblVrmOtherPoint); if (disToEBL <= GeoTools.DisTorelenceSqrd) { canEblMove = true; //EBL移動(dòng) } } } if (!canEblMove && !canVrmMove && !canEblVrmCenterMove) { //開始平移桨醋,并記錄鼠標(biāo)位置 this.Cursor = Cursors.Hand; isDragging = true; preMousePosX = e.X; preMousePosY = e.Y; //此處無法直接獲取截圖棚瘟,因此設(shè)為空 screenImage = null; } } }
- 非平移模式下,如果需要移動(dòng)EBL/VRM喜最,則將EBL/VRM移到鼠標(biāo)位置處偎蘸。
private void skiaView_MouseMove(object sender, MouseEventArgs e) { if (isDragging) { //按住鼠標(biāo)移動(dòng),記錄截圖平移量 screenOffsetX = e.X - preMousePosX; screenOffsetY = e.Y - preMousePosY; //只是移動(dòng)截圖 this.skiaView.Refresh(); } else //非平移海圖模式 { if (showEBLVRM) //EBL/VRM顯示 { var needRefresh = false; if (canEblVrmCenterMove) { //計(jì)算新的中心點(diǎn) eblVrmCenterPoint = e.Location; eblVrmCenterPos = GeoTools.ScreenPointToGeoPosition(e.Location.X, e.Location.Y, current_Scale, current_Dx, current_Dy); eblVrmOtherPos = null; needRefresh = true; } if (canVrmMove) { //計(jì)算新距標(biāo)圈的某一位置 eblVrmOtherPos = GeoTools.ScreenPointToGeoPosition(e.Location.X, e.Location.Y, current_Scale, current_Dx, current_Dy); needRefresh = true; } if (canEblMove) { //計(jì)算新方位 double ang = GeoTools.AngleBetweenTwoPoints(e.X, e.Y, eblVrmCenterPoint.X, eblVrmCenterPoint.Y); if (Math.Abs(ang - eblAngle) > 0.1) { eblAngle = ang; needRefresh = true; } } if (needRefresh) this.skiaView.Refresh(); } } var pos = GeoTools.ScreenPointToGeoPosition(e.X, e.Y, current_Scale, current_Dx, current_Dy); mouseInfo.Text = $"屏幕坐標(biāo): {e.X} {e.Y} 地理坐標(biāo): {pos}"; }
- 松開鼠標(biāo)時(shí)瞬内,重置EBL/VRM的可移動(dòng)狀態(tài)迷雪。
private void skiaView_MouseUp(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (showEBLVRM) { canVrmMove = false; canEblMove = false; canEblVrmCenterMove = false; } if (isDragging) { this.Cursor = Cursors.Default; isDragging = false; current_Dx += (e.X - preMousePosX); current_Dy -= (e.Y - preMousePosY); //重繪海圖 this.skiaView.Refresh(); } } }
- 按下鼠標(biāo)金麸,需要判斷鼠標(biāo)位置是否在EBL/VRM附近。在附近簿盅,則移動(dòng)EBL/VRM挥下,否則移動(dòng)海圖。
- 最終效果如下圖所示: