WPF简易局部地图 实现标记 绘制轨迹

WPF简易局部地图 实现标记 绘制轨迹

控件可以实现:
在知道显示的地图区域时:

  1. 设定地图的左上角和右下角经纬度;
  2. 设置使用的地图图片路径;
  3. 添加地图图钉或者轨迹点;
  4. 绘制点或轨迹。

地图控件所需的数据结构:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace SmartScreen.Controls.Models
{
    public class MapItem
    {
        public Brush Fill {  get; protected set; }
    }

    public class GeoPint
    {
        public GeoPint(double lat, double lon)
        {
            Latitude = lat;
            Longitude = lon;
        }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SmartScreen.Controls.Models
{
    public class MapPolyLineItem : MapItem
    {
        public double Longitude { get; set; }
        public double Latitude { get; set; }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace SmartScreen.Controls.Models
{
    public class MapPushPinItem : MapItem
    {
        public string Title { get; set; }
        public double Longitude { get; set; }
        public double Latitude { get; set; }

        private int warnningStatus;
        /// 
        /// 警示状态,0:安全 1:预警 -1:警告
        /// 
        public int WarnningStatus
        {
            get { return warnningStatus; }
            set
            {
                warnningStatus = value;
                if (warnningStatus == 0)
                {
                    Fill = (SolidColorBrush)Application.Current.FindResource("SafeColor");
                }
                else if (warnningStatus == 1)
                {
                    Fill = (SolidColorBrush)Application.Current.FindResource("WarnningColor");
                }
                else if (warnningStatus == -1)
                {

                    Fill = (SolidColorBrush)Application.Current.FindResource("DangerColor");
                }
            }
        }

    }
}

地图控件XAML(可以去掉zoomPanel:ZoomAndPanControl):

<UserControl x:Class="SmartScreen.Controls.MapControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:SmartScreen.Controls" xmlns:zoomPanel="clr-namespace:ZoomAndPan;assembly=ZoomAndPan"
             mc:Ignorable="d" 
             x:Name="root"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <Style TargetType="Line">
            "Stroke" Value="{StaticResource DangerColor}"/>
            "StrokeThickness" Value="2"/>
            "Effect">
                
                    "Gray" BlurRadius="8" ShadowDepth="0"/>
                
            
        Style>
    UserControl.Resources>
    <Grid>
        <zoomPanel:ZoomAndPanControl x:Name="zoomPanel"  
                                     MouseWheel="map_MouseWheel"
                                     MaxContentScale="2.5"
                                     MinContentScale="1"
                                     MouseLeftButtonDown="Grid_MouseLeftButtonDown" 
                                     MouseLeftButtonUp="Grid_MouseLeftButtonUp" 
                                     MouseMove="Grid_MouseMove">
            <zoomPanel:ZoomAndPanControl.RenderTransform>
                <TransformGroup>
                    <TranslateTransform x:Name="trans"/>
                    <ScaleTransform x:Name="scale"/>
                TransformGroup>
            zoomPanel:ZoomAndPanControl.RenderTransform>
            <Grid>
                <Image x:Name="bottomImage" Width="{Binding ElementName=root,Path=ActualWidth}" Height="{Binding ElementName=root,Path=ActualHeight}" 
               Stretch="Fill" RenderOptions.BitmapScalingMode="Fant"/>
                
                <Canvas x:Name="PushPincanvas">
                Canvas>
                <Canvas x:Name="PolyLinecanvas">
                Canvas>
            Grid>
        zoomPanel:ZoomAndPanControl>
    Grid>
UserControl>

    

地图控件后台代码:

using SmartScreen.Controls.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SmartScreen.Controls
{
    /// 
    /// MapControl.xaml 的交互逻辑
    /// 
    public partial class MapControl : UserControl
    {
        public MapControl()
        {
            InitializeComponent();
            SizeChanged += (s, e) => { RefreshPushPin(); RefreshPolyline(); };
        }

        /// 
        /// 地图图片的路径
        /// 
        public Uri MapPath
        {
            get { return (Uri)GetValue(MapPathProperty); }
            set { SetValue(MapPathProperty, value); }
        }
        public static readonly DependencyProperty MapPathProperty =
            DependencyProperty.Register("MapPath", typeof(Uri), typeof(MapControl), new PropertyMetadata(default(Uri), OnMapPathChanged));

        private static void OnMapPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MapControl ctrl = (MapControl)d;
            ctrl.bottomImage.Source = new BitmapImage(ctrl.MapPath);
        }

        /// 
        /// 左上角经纬度
        /// 
        public Point LeftTop
        {
            get { return (Point)GetValue(LeftTopProperty); }
            set { SetValue(LeftTopProperty, value); }
        }
        public static readonly DependencyProperty LeftTopProperty =
            DependencyProperty.Register("LeftTop", typeof(Point), typeof(MapControl), new PropertyMetadata(default(Point), OnMapReDrawChanged));

        /// 
        /// 右下角经纬度
        /// 
        public Point RightBottom
        {
            get { return (Point)GetValue(RightBottomProperty); }
            set { SetValue(RightBottomProperty, value); }
        }
        public static readonly DependencyProperty RightBottomProperty =
            DependencyProperty.Register("RightBottom", typeof(Point), typeof(MapControl), new PropertyMetadata(default(Point), OnMapReDrawChanged));



        /// 
        /// 地图显示的图钉部件
        /// 
        public ObservableCollection<MapPushPinItem> PushPins
        {
            get { return (ObservableCollection<MapPushPinItem>)GetValue(PushPinsProperty); }
            set { SetValue(PushPinsProperty, value); }
        }
        public static readonly DependencyProperty PushPinsProperty =
            DependencyProperty.Register("PushPins", typeof(ObservableCollection<MapPushPinItem>), typeof(MapControl), new PropertyMetadata(null, OnPushpinChanged));

        private static void OnPushpinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is MapControl map)
            {
                if (map.PushPins != null)
                {
                    map.PushPins.CollectionChanged += (s, er) => map.RefreshPushPin();
                }
                map.RefreshPushPin();
            }
        }


        /// 
        /// 地图显示的轨迹线段
        /// 
        public ObservableCollection<MapPolyLineItem> PolyLines
        {
            get { return (ObservableCollection<MapPolyLineItem>)GetValue(PolyLinesProperty); }
            set { SetValue(PolyLinesProperty, value); }
        }
        public static readonly DependencyProperty PolyLinesProperty =
            DependencyProperty.Register("PolyLines", typeof(ObservableCollection<MapPolyLineItem>), typeof(MapControl), new PropertyMetadata(null, OnPolyLineChaned));

        private static void OnPolyLineChaned(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is MapControl map)
            {
                if (map.PolyLines != null)
                {
                    map.PolyLines.CollectionChanged += (s, er) => map.RefreshPolyline();
                }
                map.RefreshPolyline();
            }
        }

        private static void OnMapReDrawChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MapControl control = (MapControl)d;
            control.RefreshPushPin();
            control.RefreshPolyline();
        }

        /// 
        /// 刷新标记点的显示
        /// 
        private void RefreshPushPin()
        {
            if (RightBottom == default(Point) || LeftTop == default(Point))
            {
                return;
            }

            var mapWidth = ActualWidth;
            var mapHeight = ActualHeight;
            var xRange = RightBottom.X - LeftTop.X;
            var yRange = LeftTop.Y - RightBottom.Y;

            if (PushPins != null)
            {
                PushPincanvas.Children.Clear();

                foreach (var pushpin in PushPins)
                {
                    // 先计算在X Y 轴上的位置
                    var canvasLeft = (pushpin.Longitude - LeftTop.X) / xRange * mapWidth;
                    var canvasTop = (LeftTop.Y - pushpin.Latitude) / yRange * mapHeight;
                    var pushpinCtrl = new MapPushPin();
                    pushpinCtrl.DataContext = pushpin;
                    Canvas.SetLeft(pushpinCtrl, canvasLeft);
                    Canvas.SetTop(pushpinCtrl, canvasTop);
                    PushPincanvas.Children.Add(pushpinCtrl);
                }
            }
        }

        /// 
        /// 刷新轨迹的显示
        /// 
        private void RefreshPolyline()
        {
            if (RightBottom == default(Point) || LeftTop == default(Point))
            {
                return;
            }

            var mapWidth = ActualWidth;
            var mapHeight = ActualHeight;
            var xRange = RightBottom.X - LeftTop.X;
            var yRange = LeftTop.Y - RightBottom.Y;
            if (PolyLines != null)
            {
                PolyLinecanvas.Children.Clear();
                MapPolyLineItem lastPoint = null;
                foreach (var polyline in PolyLines)
                {
                    if (lastPoint == null)
                    {
                        lastPoint = polyline;
                        continue;
                    }

                    Line line = new Line();
                    line.DataContext = polyline;
                    line.X1 = 0;
                    line.Y1 = 0;

                    var canvasLeft = (lastPoint.Longitude - LeftTop.X) / xRange * mapWidth;
                    var canvasTop = (LeftTop.Y - lastPoint.Latitude) / yRange * mapHeight;

                    var point2x = (polyline.Longitude - LeftTop.X) / xRange * mapWidth - canvasLeft;
                    var point2y = (LeftTop.Y - polyline.Latitude) / yRange * mapHeight - canvasTop;
                    line.X2 = point2x;
                    line.Y2 = point2y;

                    Canvas.SetLeft(line, canvasLeft);
                    Canvas.SetTop(line, canvasTop);
                    PolyLinecanvas.Children.Add(line);

                    lastPoint = polyline;
                }
            }
        }

        #region 缩放移动

        public FrameworkElement Container
        {
            get { return (FrameworkElement)GetValue(ContainerProperty); }
            set { SetValue(ContainerProperty, value); }
        }
        public static readonly DependencyProperty ContainerProperty =
            DependencyProperty.Register("Container", typeof(FrameworkElement), typeof(MapControl), new PropertyMetadata(null));



        public bool EnableZoomPan
        {
            get { return (bool)GetValue(EnableZoomPanProperty); }
            set { SetValue(EnableZoomPanProperty, value); }
        }

        public static readonly DependencyProperty EnableZoomPanProperty =
            DependencyProperty.Register("EnableZoomPan", typeof(bool), typeof(MapControl), new PropertyMetadata(false));


        Point lastPoint = default;

        private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (!EnableZoomPan)
            {
                return;
            }
            lastPoint = e.GetPosition(Container);
            zoomPanel.CaptureMouse();
        }

        private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!EnableZoomPan)
            {
                return;
            }

            lastPoint = default;
            zoomPanel.ReleaseMouseCapture();
        }

        private void Grid_MouseMove(object sender, MouseEventArgs e)
        {
            if (!EnableZoomPan)
            {
                return;
            }

            if (e.LeftButton == MouseButtonState.Pressed)
            {
                var nowPoint = e.GetPosition(Container);

                if (lastPoint == default)
                {
                    lastPoint = nowPoint;
                    return;
                }
                var offsetX = (nowPoint.X - lastPoint.X) / scale.ScaleX;
                var offsetY = (nowPoint.Y - lastPoint.Y) / scale.ScaleX;

                lastPoint = nowPoint;
                trans.X += offsetX;
                trans.Y += offsetY;
            }
        }

        private void map_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (!EnableZoomPan)
            {
                return;
            }

            var nowMouse = e.GetPosition(this);

            if (e.Delta > 0)
            {
                zoomPanel.ZoomIn(nowMouse);
            }
            else
            {
                zoomPanel.ZoomOut(nowMouse);
            }

            if (zoomPanel.ContentScale == 1)
            {
                trans.X = 0;
                trans.Y = 0;
            }
        }
        #endregion

    }
}

你可能感兴趣的:(自定义控件,WPF技术,wpf,c#,ui)