电子海图上,可由人工设置是否显示经纬线网格(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 |
- 给窗体
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);
- 在窗体状态栏添加
ToolStripStatusLabel
,命名为scaleInfo
,用于显示当前比例尺信息。 - 当窗体尺寸改变时,保存视窗大小。
//窗体尺寸改触发 private void EncViewer_Resize(object sender, EventArgs e) { current_Width = ClientRectangle.Width; current_Height = ClientRectangle.Height; }
- 在窗体加载事件中,根据视窗中心位置及视窗大小,计算出平移量:
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}"; }
- 根据当前比例尺及平移量 ⇒ 求出视窗左下角及右上角的地理位置 ⇒ 参考经、纬差间隔,确定视窗范围内所穿过的经纬线 ⇒ 计算出各经纬线的屏幕坐标 ⇒ 绘制经纬度网格:
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]; } }
最后,效果图如下: