ArcGIS API for Silverlight 实现轨迹回放

我们可能会想到使用timer来实现,每个一段时间来实现“小车”的坐标更新,但是你会发现这样会出现闪动效果,亮点之间不是平滑过渡的,这样用户体验不是太好。参考网络上给出了一个更好的方法,把他整理成了博客分享一下,并添加了车头转向的功能,我们知道在Silverlight中有个故事版方便我们实现动画,并且动画都是连续性的。不过如果要使用动画控制 小车的位置的话,必须要求小车的坐标要为依赖属性。所以这个我们要自己扩展
 首先新建 一个名字为Flag的UserControl 作为小车,在这里我添加一个箭头的图片(一个水平向右的箭头)
0
XAML如下
[csharp] view plain copy
  1. "LayoutRoot" Background="White">  
  2.         "Image1" Height="40" Width="40"   Source="/Car.png"  Stretch="Uniform">  
  3.               
  4.                   
  5.                     "RotateItemCanvas"   CenterX="20" CenterY="20"/>  
  6.                   
  7.               
  8.           
  9.       
上面的代码表示,我添加了一个长宽各位40的图片,并且把图片的中心作为旋转的中心。
我们定义的这个控件的功能就是一个ElementLayer中的一个element 与地图控件中的一条线来绑定,通过调用Start ,Stop,Pause,Resume来控制动画,这里需要定义两个依赖属性,这也是我们选择使用Elment ,而不是Graphic 来表示小车的原因,因为Graphic的坐标不具有对应的依赖属性。不过控制Element的位置我们可以通过自己定义两个依赖属性Xproperty和Yproperty。然后对应的X,Y来与Element的位置绑定,通过ElementLayer的SetEnvelope来设置小车的位置,如果X,Y有变化的话,在Set方法调用了自定义的ResetEnvelope 来重新设定小车的位置,我们同时又定义了CalulateXYAnagle 来根据某一段轨迹的方向来计算小车车头要旋转的的方向。同时可以通过 Interval 属性来控制每个轨迹线段用的时间(动态计算可以设置小车移动的速度);
[csharp] view plain copy
  1. public partial class Flag : UserControl  
  2.    {  
  3.        private Map _PMap = null;  
  4.        private int count = 0;  
  5.        private ESRI.ArcGIS.Client.Geometry.PointCollection PCol = null;  
  6.        private Storyboard sb = new Storyboard();  
  7.        DoubleAnimation dba;  
  8.        DoubleAnimation dba1;  
  9.        ///   
  10.        /// 执行结果路线   
  11.        ///   
  12.        private Graphic _Route = null;  
  13.        public Graphic Route  
  14.        {  
  15.            get { return _Route; }  
  16.            set { _Route = value; }  
  17.        }  
  18.        public Flag()  
  19.        {  
  20.            InitializeComponent();  
  21.        }  
  22.   
  23.        public string ImageSource  
  24.        {  
  25.            set  
  26.            {  
  27.                Image1.Source = new BitmapImage(new Uri(value, UriKind.Relative));  
  28.            }  
  29.        }  
  30.        public Map BindMap  
  31.        {  
  32.            set { _PMap = value; }  
  33.            get { return _PMap; }  
  34.        }  
  35.        public static readonly DependencyProperty Xproperty =   
  36.            DependencyProperty.Register("X", typeof(double),  typeof(Flag), new PropertyMetadata(OnXChanged));  
  37.        public double X  
  38.        {  
  39.            get { return (double)base.GetValue(Xproperty); }  
  40.            set { base.SetValue(Xproperty, value); ResetEnvelop(); }  
  41.        }  
  42.        private static void OnXChanged(object sender, DependencyPropertyChangedEventArgs e)  
  43.        {  
  44.            (sender as Flag).X = (double)e.NewValue;  
  45.        }  
  46.        public static readonly DependencyProperty Yproperty =  
  47.            DependencyProperty.Register("Y", typeof(double), typeof(Flag), new PropertyMetadata(OnYChanged));  
  48.        public double Y  
  49.        {  
  50.            get { return (double)base.GetValue(Yproperty); }  
  51.            set { base.SetValue(Yproperty, value); ResetEnvelop(); }  
  52.        }  
  53.        private static void OnYChanged(object sender, DependencyPropertyChangedEventArgs e)  
  54.        {  
  55.            (sender as Flag).Y = (double)e.NewValue;  
  56.        }  
  57.        private ElementLayer _bindLayer = null;  
  58.        public ElementLayer bindLayer  
  59.        {  
  60.            get { return _bindLayer; }  
  61.            set { _bindLayer = value; }  
  62.        }  
  63.        private void ResetEnvelop()  
  64.        {  
  65.            Flag ele = (Flag)_bindLayer.Children[0];  
  66.            ElementLayer.SetEnvelope(ele, new ESRI.ArcGIS.Client.Geometry.Envelope(X, Y, X, Y));  
  67.            if (_PMap != null)  
  68.            {  
  69.                ESRI.ArcGIS.Client.Geometry.Polygon gon = new ESRI.ArcGIS.Client.Geometry.Polygon();  
  70.                ESRI.ArcGIS.Client.Geometry.PointCollection con = new ESRI.ArcGIS.Client.Geometry.PointCollection();  
  71.            }  
  72.        }  
  73.        ///   
  74.        /// 移动时间间隔 默认1  
  75.        ///   
  76.        ///   
  77.        private double _Interval = 1;  
  78.        public double Interval  
  79.        {  
  80.            get { return _Interval; }  
  81.            set { _Interval = value; }  
  82.        }  
  83.        public void Start()  
  84.        {  
  85.            if (_PMap != null)  
  86.            {  
  87.                _PMap.ZoomTo(_Route.Geometry);  
  88.            }  
  89.            if (_bindLayer.Children.Count > 0) _bindLayer.Children.RemoveAt(0);  
  90.            _bindLayer.Children.Add(this);  
  91.            ESRI.ArcGIS.Client.Geometry.Polyline line = _Route.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline;  
  92.            PCol = line.Paths[0];  
  93.            MapPoint pt1 = PCol[0];  
  94.            MapPoint pt2 = PCol[1];  
  95.            double angle = CalulateXYAnagle(pt1.X, pt1.Y, pt2.X, pt2.Y);  
  96.            RotateItemCanvas.Angle = angle;  
  97.            ElementLayer.SetEnvelope(this, pt1.Extent);  
  98.            if (dba == null)  
  99.            {  
  100.                dba = new DoubleAnimation();  
  101.                Storyboard.SetTarget(dba, this);  
  102.                Storyboard.SetTargetProperty(dba, new PropertyPath("X"));  
  103.                sb.Children.Add(dba);  
  104.                dba1 = new DoubleAnimation();  
  105.                Storyboard.SetTarget(dba1, this);  
  106.                Storyboard.SetTargetProperty(dba1, new PropertyPath("Y"));  
  107.                sb.Children.Add(dba1);  
  108.            }  
  109.            sb.Completed += new EventHandler(sb_Completed);  
  110.            dba.From = pt1.X;  
  111.            dba.To = pt2.X;  
  112.            dba.Duration = new Duration(TimeSpan.FromSeconds(_Interval));  
  113.            dba1.From = pt1.Y;  
  114.            dba1.To = pt2.Y;  
  115.            dba1.Duration = new Duration(TimeSpan.FromSeconds(_Interval));  
  116.            sb.Begin();  
  117.        }  
  118.        public static double CalulateXYAnagle(double startx, double starty, double endx, double endy)  
  119.        {  
  120.            double tan = Math.Atan(Math.Abs((endy - starty) / (endx - startx))) * 180 / Math.PI;  
  121.            if (endx > startx && endy > starty)//第一象限  
  122.            {  
  123.                return -tan;  
  124.            }  
  125.            else if (endx > startx && endy < starty)//第二象限  
  126.            {  
  127.                return tan;  
  128.            }  
  129.            else if (endx < startx && endy > starty)//第三象限  
  130.            {  
  131.                return tan - 180;  
  132.            }  
  133.            else  
  134.            {  
  135.                return 180 - tan;  
  136.            }  
  137.   
  138.        }  
  139.        private void sb_Completed(object sender, EventArgs e)  
  140.        {  
  141.            if (count < PCol.Count - 2)  
  142.            {  
  143.                count++;  
  144.                MapPoint pt1 = PCol[count];  
  145.                MapPoint pt2 = PCol[count + 1];  
  146.                DoubleAnimation db = (DoubleAnimation)(sender as Storyboard).Children[0];  
  147.                db.From = pt1.X;  
  148.                db.To = pt2.X;  
  149.                double angle = CalulateXYAnagle(pt1.X, pt1.Y, pt2.X, pt2.Y);  
  150.                RotateItemCanvas.Angle = angle;  
  151.                DoubleAnimation db1 = (DoubleAnimation)(sender as Storyboard).Children[1];  
  152.                db1.From = pt1.Y;  
  153.                db1.To = pt2.Y;  
  154.                sb.Begin();  
  155.            }  
  156.            else  
  157.            {  
  158.   
  159.            }  
  160.        }  
  161.        public void Stop()  
  162.        {  
  163.            sb.Stop();  
  164.        }  
  165.        public void Pause()  
  166.        {  
  167.            sb.Pause();  
  168.        }  
  169.        public void Resume()  
  170.        {  
  171.            sb.Resume();  
  172.        }  
  173.    }  
以上 是一个封装好的控件,下面再代码里使用如下,需要一个Map,一个ElementLayer,一条多线。
[csharp] view plain copy
  1. ElementLayer elelay = MyMap.Layers["MoveCarLayer"] as ElementLayer;  
  2. ESRI.ArcGIS.Client.Geometry.Polyline Pline = lastRoute.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline;  
  3. //自定义元素  
  4. Flag mele = new Flag();  
  5. mele.BindMap = MyMap;  
  6. mele.bindLayer = elelay;  
  7. mele.Interval = 1; //时间你可以动态设置,根据线的长度除以速度  
  8. mele.Route = lastRoute;  
  9. mele.Start();  
当然也可以在播放过程中进行暂停,停止,恢复动画。 
最后附上一个Demo,我通过调用网络分析服务,来分析出两点间最佳路径,然后通过动画形式来,由于用的服务是网络上的,大家下载后即可运行。下载地址
运行之后 在地图上点击两点结果如下,,
0
0
 
 
 
 
 
 
 
  
在本次GIS项目开发中,需要实现轨迹回放功能;就是从地图上了一个点,经过N个点,一路慢慢的移动,而实现这效果的最好方式就是Siverlight足帧动画了。好了废话不说,先介绍下Demo结构,看图:
 
 
0
persion.xaml用户控件里面显示一张图片。当然有了这个userControl,那基本上什么多可以放了。看Persion.xaml代码:
0
0
 1 "Silverlight.Views.Persion"
 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6     mc:Ignorable="d"
 7     d:DesignHeight="300" d:DesignWidth="400">
 8     
 9     
10         
11             "/Silverlight;component/Images/persion.png">
12         
13     
14 
0
cs文件里面就定义了X和Y两个属性,定义这两个属性的目的是能为Storyboard.SetTargetProperty服务;
0
0
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Net;
 5 using System.Windows;
 6 using System.Windows.Controls;
 7 using System.Windows.Documents;
 8 using System.Windows.Input;
 9 using System.Windows.Media;
10 using System.Windows.Media.Animation;
11 using System.Windows.Shapes;
12 
13 namespace Silverlight.Views
14 {
15     public partial class Persion : UserControl
16     {
17         public Persion()
18         {
19             InitializeComponent();
20         }
21 
22 
23         #region 属性
24          //获取或设置X坐标
25         public  double X 
26         {
27             get { return (double)GetValue(XProperty); }
28             set
29             {
30                 SetValue(XProperty, value);
31                 SetElementEnvelope();
32             }
33         }
34         public static DependencyProperty XProperty = DependencyProperty.Register("X", typeof(double), typeof(Persion), new PropertyMetadata(OnXChange));
35         static void OnXChange(DependencyObject sender, DependencyPropertyChangedEventArgs e)
36         {
37             (sender as Persion).X = (double)e.NewValue;
38         }
39 
40         //获取或设置Y坐标
41         public double Y
42         {
43             get { return (double)GetValue(YProperty); }
44             set
45             {
46                 SetValue(YProperty, value);
47                 SetElementEnvelope();
48             }
49         }
50 
51         public static DependencyProperty YProperty = DependencyProperty.Register("Y", typeof(double), typeof(Persion), new PropertyMetadata(OnYChange));
52         static void OnYChange(DependencyObject sender, DependencyPropertyChangedEventArgs e)
53         {
54             (sender as Persion).Y = (double)e.NewValue;
55         }
56         //设置Envelope属性
57         private void SetElementEnvelope()
58         {
59             SetValue(ESRI.ArcGIS.Client.ElementLayer.EnvelopeProperty, new ESRI.ArcGIS.Client.Geometry.Envelope(X, Y, X, Y));
60         }
61         #endregion
62     }
63 }
0
搞定了persion后到地图上面来实现。
0
0
 1 "http://schemas.esri.com/arcgis/client/2009"  x:Class="Silverlight.MainPage"
 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 5     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 6     mc:Ignorable="d"
 7     d:DesignHeight="300" d:DesignWidth="400">
 8 
 9     "LayoutRoot" Background="White">
10         "map1">
11             "http://192.168.8.188/ArcGIS/rest/services/kjtxnetMain/MapServer">
12         
13     
14 
0
0
0
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Net;
 5 using System.Windows;
 6 using System.Windows.Controls;
 7 using System.Windows.Documents;
 8 using System.Windows.Input;
 9 using System.Windows.Media;
10 using System.Windows.Media.Animation;
11 using System.Windows.Shapes;
12 using Silverlight.Views;
13 
14 namespace Silverlight
15 {
16     public partial class MainPage : UserControl
17     {
18         private ESRI.ArcGIS.Client.ElementLayer _elementLayer = new ESRI.ArcGIS.Client.ElementLayer()
19         {
20             ID = "elementLayer"
21         };
22         //起始坐标
23         private ESRI.ArcGIS.Client.Geometry.MapPoint starPoint = new ESRI.ArcGIS.Client.Geometry.MapPoint(108.477998481483, 31.2812729500709);
24         //结束坐标
25         private ESRI.ArcGIS.Client.Geometry.MapPoint endPoint = new ESRI.ArcGIS.Client.Geometry.MapPoint(108.144513331365, 30.8702332329334);
26         private Storyboard storyboard = new Storyboard();
27         private Persion persion = null;
28         public MainPage()
29         {
30             InitializeComponent();
31             map1.Layers.Add(_elementLayer);
32 
33             /*
34              把persion添加到ESRI.ArcGIS.Client.ElementLayer,
35              * ESRI.ArcGIS.Client.ElementLayer在添加Child之前必须设置Envelope,
36              */
37             persion = new Persion()
38             {
39                 X= starPoint.X,
40                 Y=starPoint.Y,
41                 Width=50,
42                 Height=50
43             };
44             _elementLayer.Children.Add(persion);
45 
46             Start();
47         }
48 
49         private void Start()
50         {
51             storyboard.Pause();
52             storyboard.Completed -= new EventHandler(storyboard_Completed);
53             storyboard = new Storyboard();
54 
55             DoubleAnimationUsingKeyFrames framX = new DoubleAnimationUsingKeyFrames();
56             DoubleAnimationUsingKeyFrames framy = new DoubleAnimationUsingKeyFrames();
57 
58             LinearDoubleKeyFrame keyX = new LinearDoubleKeyFrame()
59             {
60                 Value=endPoint.X,
61                 KeyTime =KeyTime.FromTimeSpan(TimeSpan.FromSeconds(5))
62             };
63             LinearDoubleKeyFrame keyY = new LinearDoubleKeyFrame()
64             {
65                 Value = endPoint.Y,
66                 KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(5))
67             };
68             framX.KeyFrames.Add(keyX);
69             framy.KeyFrames.Add(keyY);
70 
71             Storyboard.SetTarget(framX,persion);
72             Storyboard.SetTargetProperty(framX,new PropertyPath(Persion.XProperty));
73             Storyboard.SetTarget(framy, persion);
74             Storyboard.SetTargetProperty(framy, new PropertyPath(Persion.YProperty));
75 
76             storyboard.Children.Add(framX);
77             storyboard.Children.Add(framy);
78 
79             storyboard.Completed += new EventHandler(storyboard_Completed);
80             storyboard.Begin();
81         }
82 
83         void storyboard_Completed(object sender, EventArgs e)
84         {
85             
86         }
87 
88       
89     }
90 }
posted @ 2022-07-29 09:26  devgis  阅读(170)  评论(0编辑  收藏  举报