一、简单的思路

  要实现车辆运行轨迹,我们可能需要一个定时触发的机制用来更新Marker的位置,除了位置移动,我们可能还需要动态改变车辆的方向,如下图:

  首先,位置移动是最简单的,关键是方向的动态改变如何实现,稍作观察即可看出,汽车的方向总是和路线的切点平行,看来我们得写个方法用来求路线上任意点的切线了。可能对于有些大神来说这也并不棘手,无非是花点时间写个算法而已,但我觉得仅凭我自己的本事可能做不到,所以我打算借助现有的代码库来实现上述的功能。在wpf中,路径动画是很常用的,而它正好和这里的需求相符合,我们是不是能利用它来实现上述的功能呢?


二、路径动画demo

  博客园有很多关于wpf路径动画的随笔,如果你还未曾了解过,可以看这里的一篇:http://www.cnblogs.com/zhouyinhui/archive/2007/07/31/837893.html,里面很详细的介绍了路径动画的使用方法,并且附带了demo可供下载,我建议先看完这篇随笔后再往下阅读。为了方便的在动画执行过程中获得运动对象的位置坐标和旋转角度,我选择了这篇随笔中介绍的DoubleAnimationUsingPath的方法,我们需要在此基础上订阅任意一个Transform实例的Changed事件,以便车辆在改变位置时能通知我们:

var translate = new TranslateTransform();
var rotate = new RotateTransform();
var group = new TransformGroup();
translate.Changed += (s, e) =>
{
  //在这里获取小车的位置坐标和旋转角度
};

  上面的代码中,我给TranslateTransfor的实例订阅了事件,现在,小车的位置就是new Point(translate.X, translate.Y),小车的旋转角度就是rotate.Angle,好了,该要的东西我们都有了,下面就要在GMap中实现了。


三、自定义Marker

  首先,你看到这篇随笔就代表你对GMap还是有一定了解的,那么自然也知道Marker是个啥,不知道的可以利用搜索引擎了解一下,或者参考这篇随笔:http://www.cnblogs.com/luxiaoxun/p/3475355.html,我在这里就不介绍了。在地图上的小车其实就是个我们自定义的Marker,我们姑且称为CarGMapMarker,在CarGMapMarker内部我们需要维护一个Canvas子类(因为继承了Canvas),这个Canvas是Path的容器,然后我们还需要一个Border来当作运动的物体,其实这些过程都是为了模拟http://www.cnblogs.com/zhouyinhui/archive/2007/07/31/837893.html中创建的情形,接着我们还需要一个事件public event EventHandler<Tuple<double, Point>> PositionChanged,用来通知我们自定义的Marker:喂!我内部维护的那个Border位置和角度改变了,他们分别是xxxxxxxx。而通知的代码就写在二中谈到的Changed事件触发方法中:

var translate = new TranslateTransform();
var rotate = new RotateTransform();
var group = new TransformGroup();
translate.Changed += (s, e) =>
{
OnPositionChanged(new Tuple<double, Point>(rotate.Angle, new Point(translate.X, translate.Y)));
};

  然后我们只要在自定义Marker中订阅这个Canvas子类的PositionChanged事件,并从e中获取一个元组,元组的Item1就是角度,Item2就是坐标,我们可以利用角度改变Marker图片的方向,利用坐标改变MarkerPosition的值。

  不过在此之前我们Canvas子类中的Path还没有给它的Data属性赋值,生成这个Data其实很简单,就是把小车需要经过的关键点用线连起来就可以了,直接上方法:

public void SetPoints(List<Point> list)
{
var geometry = new PathGeometry();
var fi = new PathFigure {StartPoint = list.First() };
foreach (var item in list.Skip())
{
fi.Segments.Add(new LineSegment(item, false));
}
geometry.Figures.Add(fi);
}

  这里要注意的是需要把list的第一个坐标赋值给PathFigure的StartPoint属性,剩余的坐标再一一相连接。最后,只要把这个geometry赋值给Canvas子类中Path的Data属性即可,你可以用方法赋值,也可以在Canvas子类中写个属性赋值,随你,我这里使用了后者。


四、图片处理

  下面要说的是旋转小车的图片,Marker中的图片用的是Bitmap,旋转Bitmap的方法网上有很多,我们有时候可以奉行拿来主义,搜一个拿来用吧。要注意的是,小车的初始状态车头是要朝上的,因为朝上就是0度,和坐标系吻合。

  除此之外还有一个坑需要注意,在GMap中Marker默认都是处于目标点上方的,而不是中心点,可以用以下的图片来理解:

  

  如图,定位点最低点会在路线上,而不是定位点的中心在路线上,如果直接把定位点的图片替换成汽车会如何?会这样子:

  

  你问为什么车没有旋转?好的,那么我就让她旋转一下,和该点的切线平行好了,Bitmap旋转是围绕中心点旋转的,那么旋转后效果就是这样子的:

  

  虽然和切线平行了,但是小车完全脱离了路线,怎么办?往下移呗!移多少?高度的一半!光是这样还不够,我们还需要保证小车图片的高度和宽度都要相等,即要是个正方形才可以,至于为什么,博友们可以自己想想。

  最后的效果如下:

  

  最后的最后,细心的博友可能发现了,小车在开始的位置是偏离的,这就涉及到了GMap内部的bitmap绘制机制,经过测试发现,需要使用以下的代码来稍作修正: 

public override void OnRender(Graphics g)
{
var bitmap = PictureHelper.RotateImage(_bitmap, _angle);
var offsetX = LocalPosition.X-((LocalPosition.X - )+bitmap.Width/);
var offsetY = LocalPosition.Y - ((LocalPosition.Y - ) + bitmap.Height / );
g.DrawImageUnscaled(bitmap, LocalPosition.X + offsetX, LocalPosition.Y+offsetY);
}

如何较为方便的在GMap.Net中实现车辆运行轨迹的更多相关文章

  1. MATLAB中绘制质点轨迹动图并保存成GIF

    工作需要在MATLAB中绘制质点轨迹并保存成GIF以便展示. 绘制质点轨迹动图可用comet和comet3命令,使用例子如下: t = 0:.01:2*pi;x = cos(2*t).*(cos(t) ...

  2. ROS在rviz中实时显示轨迹(nav_msgs/Path消息的使用)

    消息结构说明nav_msgs/Path.msg结构#An array of poses that represents a Path for a robot to followHeader heade ...

  3. WPF中在Gmap.net中将Marker动起来

    前一段时间说过一篇绘制极坐标的,这段时间对它进行了改造已经今非昔比了,功能实现了很多,我目的是让Marker动起来,然后还会绘制Route,上篇也就是简单的绘制了Route,没有关于Marker的相关 ...

  4. 大前端学习笔记整理【五】关于JavaScript中的关键字——this

    写在前面 工作有那么一段时间了,但是在工作中,发现自己的理论知识还是有所欠缺.特别是在javascript上,很多东西其实自己属于知道要用这个,但是不知道为什么要这么用...这种情况很是尴尬了,所以写 ...

  5. Python中optionParser模块的使用方法[转]

    本文以实例形式较为详尽的讲述了Python中optionParser模块的使用方法,对于深入学习Python有很好的借鉴价值.分享给大家供大家参考之用.具体分析如下: 一般来说,Python中有两个内 ...

  6. ThinkPHP中的三大自动简介

    ThinkPHP中的三大自动简介 文章TAG:thinkphp 自动简介 时间:2014-08-23来源:商业源码网 作者:源码库 文章热度: 186 ℃ 过期已备案域名,注册就能用!终身VIP会员, ...

  7. javascript和jquery中获取列表的索引

    网页中的图片预览一般都需要获取图片列表的索引,或则图片对应的标签的索引,以此达到点击相应的标签获取索引,显示相应的图片 列表有很多种表达的方式,一种是 <ul> <li>苹果& ...

  8. mahout中kmeans算法和Canopy算法实现原理

    本文讲一下mahout中kmeans算法和Canopy算法实现原理. 一. Kmeans是一个很经典的聚类算法,我想大家都非常熟悉.虽然算法较为简单,在实际应用中却可以有不错的效果:其算法原理也决定了 ...

  9. Keil C51中函数指针的使用

    函数指针在C语言中应用较为灵活.在单片机系统中,嵌入式操作系统.文件系统和网络协议栈等一些较为复杂的应用都大量地使用了函数指针.Keil公司推出的C51编译器是事实上80C51 C编程的工业标准,它针 ...

随机推荐

  1. java代码=--数组复制

    总结:arraycopy注意数组定义的长度.不足会补0 package clientFrame; //数组的复制arraycopy() public class Xiang { public stat ...

  2. 杂项-Java:JCP

    ylbtech-杂项-Java:JCP JCP(Java Community Process) 是一个开放的国际组织,主要由Java开发者以及被授权者组成,职能是发展和更新. 1. 中文名:jcp 外 ...

  3. PHP命名空间带来的干扰

    有时候,不想受命名空间约束,就可以整一个全局类. protected function sendDayuSms($tel,$code,$template_type,$product = "[ ...

  4. Hadoop入门介绍一

    Hadoop1.是一个由Apache基金会所开发的分布式系统基础架构.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力进行高速运算和存储.2.Hadoop实现了一个分布式文件 ...

  5. DVWA平台v1.8-反射型XSS(low级别)

    源代码 <?php if(!array_key_exists ("name", $_GET) || $_GET['name'] == NULL || $_GET['name' ...

  6. 科学家开发新AI系统,可读取大脑信息并表达复杂思想

    我们终于找到了一种方法,可以在核磁共振成像的信号中看到这种复杂的想法.美国卡内基梅隆大学的Marcel Just说,思维和大脑活动模式之间的对应关系告诉我们这些想法是如何构建的. 人工智能系统表明,大 ...

  7. 前端学习---html基础知识

    HTML基本知识 学习html首先我们先看看HTML本质: web框架本质 我们在学socket,我们创建一个socketserver,然后运行起来,有一个client客户端要连接socket服务端, ...

  8. mysql 存储过程动态执行sql语句

    之前经常在程序中拼接sql语句,其实我们也可以在存储过程中拼接sql 语句,动态的执行~~ 代码如下: DROP PROCEDURE IF EXISTS SearchByDoctor;CREATE P ...

  9. Linux系统设置Samba共享笔记

    # "不允许一个用户使用一个以上用户名与服务器或共享资源的多重连接" C: 之前曾经与主机成功进行连接,连接断开后未将连接状态清除 $: net use */del /y # &q ...

  10. BeX5 常见问题解决办法

    1.获取当前Activity的名称 Activity activity = ProcessUtils.getActivityInProcessContext(); String activityNam ...