海图绘制流程
- 全局变量存储海图文件夹路径。
public static class MySettings { // ... public static string ChartFolder = "..\\"; //海图文件夹 }
- 遍历海图总文件夹,将子文件夹中的海图文件夹挑选出来,然后加载海图文件,并完成坐标转换、通过查询表匹配符号化指令。
//窗体级变量,存储海图文件 private S57File[] readers;
//遍历海图总文件夹,将子文件夹里的海图文件夹挑选出来 var chartFolders = new List<string>(); foreach (var folder in Directory.GetDirectories(MySettings.ChartFolder).OrderBy(x => x)) { var fileName = Path.GetFileNameWithoutExtension(folder); //包含000文件,是海图文件夹 if(File.Exists($"{folder}\\{fileName}.000")) { chartFolders.Add(folder); } } //加载海图,完成坐标转换 var len = chartFolders.Count; readers = new S57File[len]; for (int i = 0; i < len; i++) { readers[i] = new S57File(chartFolders[i]); var comf = (double)readers[i].COMF; var somf = (double)readers[i].SOMF; foreach (var s in readers[i].Spatials.Values) { if(s.YCOOs != null) { s.Latitudes = new double[s.YCOOs.Length]; s.Longitude = new double[s.XCOOs.Length]; for (int j = 0; j < len; j++) { s.Latitudes[j] = s.YCOOs[j] / comf; s.Longitude[j] = s.XCOOs[j] / comf; } } if (s.VE3Ds != null) { s.Depths = new double[s.VE3Ds.Length]; for (int j = 0; j < len; j++) { s.Depths[j] = s.VE3Ds[j] / somf; } } } if(readers[i].SLAT == 100d) //不存在海图边界信息,则重新计算下 { readers[i].WLON = readers[i].Spatials.Values.Where(x => x.Longitude != null).Min(x => x.Longitude.Min()); readers[i].ELON = readers[i].Spatials.Values.Where(x => x.Longitude != null).Max(x => x.Longitude.Max()); readers[i].SLAT = readers[i].Spatials.Values.Where(x => x.Latitudes != null).Min(x => x.Latitudes.Min()); readers[i].NLAT = readers[i].Spatials.Values.Where(x => x.Latitudes != null).Max(x => x.Latitudes.Max()); } //通过查询表匹配符号化指令 foreach (var f in readers[i].Features.Values) { f.LookUp = S52Tools.FindLookUpEntry(f); } //通过符号化指令排序 //第一排序字段为:显示优先级 //第二排序字段为:面 > 线 > 点 //第三排序字段为:分组 readers[i].Features = readers[i].Features.OrderBy(x => x.Value.LookUp.Priority) .ThenByDescending(x => x.Value.PRIM) .ThenBy(x => x.Value.GRUP) .ToDictionary(k => k.Key, v => v.Value); }
- 确定视窗中心经纬:默认为所加载海图地理范围的中心。
//确定显示区域中心及显示比例尺 double slat = 100, nlat = -100, elon = -200, wlon = 200; uint cscl = 0; for (int i = 0; i < len; i++) { if (slat > readers[i].SLAT) slat = readers[i].SLAT; if (nlat < readers[i].NLAT) nlat = readers[i].NLAT; if (wlon > readers[i].WLON) wlon = readers[i].WLON; if (elon < readers[i].ELON) elon = readers[i].ELON; if (readers[i].CSCL > cscl) cscl = readers[i].CSCL; } //视窗中心位置 initCenterPos.Latitude = (slat + nlat) / 2; initCenterPos.Longitude = (elon + wlon) / 2;
- 确定视窗的初始显示比例尺:默认为所加载海图中最小的编译比例尺。
当海图中编译比例尺信息为空时,默认显示全部加载海图。//显示比例尺 if (cscl == 0) //没有指定编译比例尺,则将所有海图都显示在视窗中 { var deltaY = (nlat - slat) * 0.1; var pix_X = (elon - wlon) * 60 * 1.2; var pix_Y = (GeoTools.MP(nlat + deltaY) - GeoTools.MP(slat - deltaY)); if (pix_X / pix_Y < (double)current_Width / current_Height) { current_Scale = (uint)(pix_X * GeoTools.MinPP / current_Width); } else { current_Scale = (uint)(pix_Y * GeoTools.MinPP / current_Height); } } else { current_Scale = cscl; }
- 比例尺与中心位置定好后,遍历海图文件,通过海图范围粗略判断海图是否在视窗范围内。
private void DrawEnc(SKCanvas ca) { //右上角位置 var trPos = GeoTools.ScreenPointToGeoPosition(current_Width, 0, current_Scale, current_Dx, current_Dy); //左下角位置 var blPos = GeoTools.ScreenPointToGeoPosition(0, current_Height, current_Scale, current_Dx, current_Dy); //区域矩形 var zoneRec = new SKRect(0, 0, current_Width, current_Height); for (int i = 0; i < readers.Length; i++) { var reader = readers[i]; //粗略判断海图范围是否在视窗内 if (GeoTools.ShouldNotDisplay(trPos, blPos, reader.NLAT, reader.SLAT, reader.ELON, reader.WLON)) continue; //下一步操作 } }
- 遍历海图特征记录,只显示点、线、面物标。并且当前显示比例尺在物标比例尺范围之内,则绘制该特标。
//下一步操作 foreach (var f in reader.Features.Values) { if (f.PRIM == 255) continue; //超出比例尺 if (current_Scale > f.ScaleMinium || current_Scale < f.ScaleMaximum) continue; //没有绘制指令 if (f.LookUp.Instructions == null) continue; #region 绘制 }
- 按照物标的符号化指令进行绘制。