今天我们继续实现如何在地图上绘制得到的路径。在之前的文章中我们的程序使用了天地图作为底图 C# + Esri ArcGISRuntime WPF加载天地图_arcgis runtime api 天地图-CSDN博客
<esri:MapView x:Name="MapView" Margin="5" MouseDown="MapView_MouseDown"/>
private MapPoint ConvertMousePoint2MapPoint(System.Windows.Point windowsPoint)
{
var point = MapView.ScreenToLocation(windowsPoint);
return GeometryEngine.Project(point, SpatialReferences.Wgs84) as MapPoint;
}
这里通过将屏幕点转换为坐标点,可以看到在地图点击事件(MouseDown)中拿到的坐标点(鼠标点中地图位置)为(4326)WGS84坐标系。
UI添加一个Button, 实现在地图点击选择起点、终点,计算路径。
private void RoutePlanClick(object sender, RoutedEventArgs e)
{
graphicsOverlay.Graphics.Clear();
Growl.Clear();
Growl.Info("请选择起点");
Array.Clear(routePlanPoints, 0, routePlanPoints.Length);
currentEnumMapOperation = EnumMapOperation.AddRoutePlanFromPoint;
}
在地图MouseDown事件中,先暂时将起点、终点的位置存起来。通过枚举EnumMapOperation来定义当前地图点击事件执行哪个步骤,将点击的起点、终点暂时绘制到地图上(添加到GraphicsOverlay图层)。
private GraphicsOverlay graphicsOverlay;
private EnumMapOperation currentEnumMapOperation = EnumMapOperation.None;
private MapPoint[] routePlanPoints = new MapPoint[2];//存路径起点、终点
private SimpleMarkerSymbol startEndPointSymbol = new()
{
Color = Color.FromArgb(255, 0, 0),
Style = SimpleMarkerSymbolStyle.Circle,
Size = 15d
};
private void MapView_MouseDown(object sender, MouseButtonEventArgs e)
{
var mouseDownPoint = ConvertMousePoint2MapPoint(e.GetPosition(MapView));
switch (currentEnumMapOperation)
{
case EnumMapOperation.AddRoutePlanFromPoint:
{
//存起点
routePlanPoints[0] = mouseDownPoint;
var point = new MapPoint(mouseDownPoint.X, mouseDownPoint.Y, SpatialReferences.Wgs84);
//设置点样式并加到地图
graphicsOverlay.Graphics.Add(new Graphic(point, startEndPointSymbol) { ZIndex = 99 });
Growl.Clear();
Growl.Info("请选择终点");
currentEnumMapOperation = EnumMapOperation.AddRoutePlanToPoint;
}
break;
case EnumMapOperation.AddRoutePlanToPoint:
{
//存终点
routePlanPoints[1] = mouseDownPoint;
var point = new MapPoint(mouseDownPoint.X, mouseDownPoint.Y, SpatialReferences.Wgs84);
graphicsOverlay.Graphics.Add(new Graphic(point, startEndPointSymbol) { ZIndex = 99 });
Growl.Clear();
Growl.Info("开始计算绘制路径");
currentEnumMapOperation = EnumMapOperation.None;
_ = CalculateDrawRoutes();
}
break;
case EnumMapOperation.None:
default:
break;
}
}
private void InitBasemap()
{
//加载天地图 矢量底图
MapView.Map = new Map();
var webtileBaseLayer = new WebTiledLayer(TianMapBaseMapUri);
MapView.Map.Basemap?.BaseLayers.Add(webtileBaseLayer);
//加载天地图 矢量注记
var webtileNodeLayer = new WebTiledLayer(TianMapLabelUri);
MapView.Map.OperationalLayers.Add(webtileNodeLayer);
//设置地图初始位置
MapView.SetViewpoint(new Viewpoint(
latitude: 34.2784,
longitude: 108.9414,
scale: 120000));
//添加图形临时图层
graphicsOverlay = new GraphicsOverlay();
MapView.GraphicsOverlays.Add(graphicsOverlay);
}
public enum EnumMapOperation
{
None = 0,
AddRoutePlanFromPoint = 1,
AddRoutePlanToPoint = 2,
}
当选择完终点后,就可以调用Api来计算路径了。
private async Task CalculateDrawRoutes()
{
//地图点击起点、终点 Wgs84转为Gcj02火星坐标系
var convertStartPoint = CoordinateTransformation.Wgs84toGcj02(routePlanPoints[0].X, routePlanPoints[0].Y);
var convertEndPoint = CoordinateTransformation.Wgs84toGcj02(routePlanPoints[1].X, routePlanPoints[1].Y);
//调用腾讯API
var routes = await HttpHelper.GetRouteByQQMapApi(convertStartPoint.lng, convertStartPoint.lat, convertEndPoint.lng, convertEndPoint.lat);
var polylineCoords = routes.result?.routes.FirstOrDefault()?.polyline;
if (polylineCoords == null)
{
Growl.Clear();
Growl.Error("计算路径出错");
graphicsOverlay.Graphics.Clear();
return;
}
//解压路径坐标点
CommonFunc.UnzipPolylineCoordsForQMap(polylineCoords);
//构造点对象
var routeCoords = CommonFunc.ConstructCoordsForQMapDrivingRoute(polylineCoords);
//再将所有点从火星坐标系转回Wgs84
var convertWgs84RouteCoords = new List<(double y, double x)>();
foreach (var item in routeCoords)
{
var convertCoord = CoordinateTransformation.Gcj02toWgs84(item.x, item.y);
convertWgs84RouteCoords.Add((convertCoord.lat, convertCoord.lng));
}
//绘制路径点到地图
foreach (var coord in convertWgs84RouteCoords)
{
var point = new MapPoint(coord.x, coord.y, SpatialReferences.Wgs84);
graphicsOverlay.Graphics.Add(new Graphic(point, routePointSymbol));
await Task.Run(() => { Thread.Sleep(20); });
}
//将所有路径点拼成一整条完整路线并绘制
//var pointLst = convertWgs84RouteCoords.Select(t => new MapPoint(t.x, t.y, SpatialReferences.Wgs84));
//var polyLine = new Polyline(pointLst.ToArray(), SpatialReferences.Wgs84);
//graphicsOverlay.Graphics.Add(new Graphic(polyLine, routeLineSymbol));
}
private SimpleMarkerSymbol routePointSymbol = new()
{
Color = Color.FromArgb(1, 180, 115),
Style = SimpleMarkerSymbolStyle.Circle,
Size = 10d
};
private SimpleLineSymbol routeLineSymbol = new()
{
Color = Color.FromArgb(1, 180, 115),
Width = 10d,
Style = SimpleLineSymbolStyle.Solid
};
最终效果是这样,每隔20毫秒绘制上一个路径点。
(地图本身是很清晰的,转成gif就糊了...)
这样路径虽然出来了,但由于是点,而不是线,所以看起来断断续续,效果不太好,再调整一下,将这部分代码放开,把所有路径点拼成一个完整路线绘制。
//将所有路径点拼成一整条完整路线并绘制
var pointLst = convertWgs84RouteCoords.Select(t => new MapPoint(t.x, t.y, SpatialReferences.Wgs84));
var polyLine = new Polyline(pointLst.ToArray(), SpatialReferences.Wgs84);
graphicsOverlay.Graphics.Add(new Graphic(polyLine, routeLineSymbol));
这样最终结果就是:
看起来舒服多了。ok,打完收工!