ArcGIS API for Silverlight 实现轨迹回放
我们可能会想到使用timer来实现,每个一段时间来实现“小车”的坐标更新,但是你会发现这样会出现闪动效果,亮点之间不是平滑过渡的,这样用户体验不是太好。参考网络上给出了一个更好的方法,把他整理成了博客分享一下,并添加了车头转向的功能,我们知道在Silverlight中有个故事版方便我们实现动画,并且动画都是连续性的。不过如果要使用动画控制 小车的位置的话,必须要求小车的坐标要为依赖属性。所以这个我们要自己扩展
首先新建 一个名字为Flag的UserControl 作为小车,在这里我添加一个箭头的图片(一个水平向右的箭头)
XAML如下
[csharp] view plain copy
- "LayoutRoot" Background="White">
- "Image1" Height="40" Width="40" Source="/Car.png" Stretch="Uniform">
- "RotateItemCanvas" CenterX="20" CenterY="20"/>
上面的代码表示,我添加了一个长宽各位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
- public partial class Flag : UserControl
- {
- private Map _PMap = null;
- private int count = 0;
- private ESRI.ArcGIS.Client.Geometry.PointCollection PCol = null;
- private Storyboard sb = new Storyboard();
- DoubleAnimation dba;
- DoubleAnimation dba1;
- ///
- /// 执行结果路线
- ///
- private Graphic _Route = null;
- public Graphic Route
- {
- get { return _Route; }
- set { _Route = value; }
- }
- public Flag()
- {
- InitializeComponent();
- }
- public string ImageSource
- {
- set
- {
- Image1.Source = new BitmapImage(new Uri(value, UriKind.Relative));
- }
- }
- public Map BindMap
- {
- set { _PMap = value; }
- get { return _PMap; }
- }
- public static readonly DependencyProperty Xproperty =
- DependencyProperty.Register("X", typeof(double), typeof(Flag), new PropertyMetadata(OnXChanged));
- public double X
- {
- get { return (double)base.GetValue(Xproperty); }
- set { base.SetValue(Xproperty, value); ResetEnvelop(); }
- }
- private static void OnXChanged(object sender, DependencyPropertyChangedEventArgs e)
- {
- (sender as Flag).X = (double)e.NewValue;
- }
- public static readonly DependencyProperty Yproperty =
- DependencyProperty.Register("Y", typeof(double), typeof(Flag), new PropertyMetadata(OnYChanged));
- public double Y
- {
- get { return (double)base.GetValue(Yproperty); }
- set { base.SetValue(Yproperty, value); ResetEnvelop(); }
- }
- private static void OnYChanged(object sender, DependencyPropertyChangedEventArgs e)
- {
- (sender as Flag).Y = (double)e.NewValue;
- }
- private ElementLayer _bindLayer = null;
- public ElementLayer bindLayer
- {
- get { return _bindLayer; }
- set { _bindLayer = value; }
- }
- private void ResetEnvelop()
- {
- Flag ele = (Flag)_bindLayer.Children[0];
- ElementLayer.SetEnvelope(ele, new ESRI.ArcGIS.Client.Geometry.Envelope(X, Y, X, Y));
- if (_PMap != null)
- {
- ESRI.ArcGIS.Client.Geometry.Polygon gon = new ESRI.ArcGIS.Client.Geometry.Polygon();
- ESRI.ArcGIS.Client.Geometry.PointCollection con = new ESRI.ArcGIS.Client.Geometry.PointCollection();
- }
- }
- ///
- /// 移动时间间隔 默认1
- ///
- ///
- private double _Interval = 1;
- public double Interval
- {
- get { return _Interval; }
- set { _Interval = value; }
- }
- public void Start()
- {
- if (_PMap != null)
- {
- _PMap.ZoomTo(_Route.Geometry);
- }
- if (_bindLayer.Children.Count > 0) _bindLayer.Children.RemoveAt(0);
- _bindLayer.Children.Add(this);
- ESRI.ArcGIS.Client.Geometry.Polyline line = _Route.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline;
- PCol = line.Paths[0];
- MapPoint pt1 = PCol[0];
- MapPoint pt2 = PCol[1];
- double angle = CalulateXYAnagle(pt1.X, pt1.Y, pt2.X, pt2.Y);
- RotateItemCanvas.Angle = angle;
- ElementLayer.SetEnvelope(this, pt1.Extent);
- if (dba == null)
- {
- dba = new DoubleAnimation();
- Storyboard.SetTarget(dba, this);
- Storyboard.SetTargetProperty(dba, new PropertyPath("X"));
- sb.Children.Add(dba);
- dba1 = new DoubleAnimation();
- Storyboard.SetTarget(dba1, this);
- Storyboard.SetTargetProperty(dba1, new PropertyPath("Y"));
- sb.Children.Add(dba1);
- }
- sb.Completed += new EventHandler(sb_Completed);
- dba.From = pt1.X;
- dba.To = pt2.X;
- dba.Duration = new Duration(TimeSpan.FromSeconds(_Interval));
- dba1.From = pt1.Y;
- dba1.To = pt2.Y;
- dba1.Duration = new Duration(TimeSpan.FromSeconds(_Interval));
- sb.Begin();
- }
- public static double CalulateXYAnagle(double startx, double starty, double endx, double endy)
- {
- double tan = Math.Atan(Math.Abs((endy - starty) / (endx - startx))) * 180 / Math.PI;
- if (endx > startx && endy > starty)//第一象限
- {
- return -tan;
- }
- else if (endx > startx && endy < starty)//第二象限
- {
- return tan;
- }
- else if (endx < startx && endy > starty)//第三象限
- {
- return tan - 180;
- }
- else
- {
- return 180 - tan;
- }
- }
- private void sb_Completed(object sender, EventArgs e)
- {
- if (count < PCol.Count - 2)
- {
- count++;
- MapPoint pt1 = PCol[count];
- MapPoint pt2 = PCol[count + 1];
- DoubleAnimation db = (DoubleAnimation)(sender as Storyboard).Children[0];
- db.From = pt1.X;
- db.To = pt2.X;
- double angle = CalulateXYAnagle(pt1.X, pt1.Y, pt2.X, pt2.Y);
- RotateItemCanvas.Angle = angle;
- DoubleAnimation db1 = (DoubleAnimation)(sender as Storyboard).Children[1];
- db1.From = pt1.Y;
- db1.To = pt2.Y;
- sb.Begin();
- }
- else
- {
- }
- }
- public void Stop()
- {
- sb.Stop();
- }
- public void Pause()
- {
- sb.Pause();
- }
- public void Resume()
- {
- sb.Resume();
- }
- }
以上 是一个封装好的控件,下面再代码里使用如下,需要一个Map,一个ElementLayer,一条多线。
[csharp] view plain copy
- ElementLayer elelay = MyMap.Layers["MoveCarLayer"] as ElementLayer;
- ESRI.ArcGIS.Client.Geometry.Polyline Pline = lastRoute.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline;
- //自定义元素
- Flag mele = new Flag();
- mele.BindMap = MyMap;
- mele.bindLayer = elelay;
- mele.Interval = 1; //时间你可以动态设置,根据线的长度除以速度
- mele.Route = lastRoute;
- mele.Start();
当然也可以在播放过程中进行暂停,停止,恢复动画。
最后附上一个Demo,我通过调用网络分析服务,来分析出两点间最佳路径,然后通过动画形式来,由于用的服务是网络上的,大家下载后即可运行。下载地址
运行之后 在地图上点击两点结果如下,,
在本次GIS项目开发中,需要实现轨迹回放功能;就是从地图上了一个点,经过N个点,一路慢慢的移动,而实现这效果的最好方式就是Siverlight足帧动画了。好了废话不说,先介绍下Demo结构,看图:
persion.xaml用户控件里面显示一张图片。当然有了这个userControl,那基本上什么多可以放了。看Persion.xaml代码:
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
cs文件里面就定义了X和Y两个属性,定义这两个属性的目的是能为Storyboard.SetTargetProperty服务;
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 }
搞定了persion后到地图上面来实现。
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
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 }
专业从事基于C#,WinForm ,WPF,Silverlight,WCF以及MS Sql Server 2000/2005/2008/2012 Oracle 9i/10g/11g数据库系统的ERP,CRM,企业进销存等各种数据库管理系统开发。Asp.net,Asp.net mvc,Webservice,WCF, Webapi等服务程序开发。
基于Oracle MySQL MSSql postgresql各种数据库的管理系统数据同步服务。以及基于MapXtreme, Arcgis Engine ,以及基于Arcgis for silverlight/Javascript的WebGIS等相关的GIS系统二次开发。基于Windows 10 Mobile的移动端开发方案。针对各种系统的二次开发维护,并提供相关开发的技术性支持,如程序BUG解决,应用系统架构,技术难题攻克等相关技术服务。
联系方式: QQ :80163278(devgis) 邮箱:devgis@qq.com