一、简单的思路

  要实现车辆运行轨迹,我们可能需要一个定时触发的机制用来更新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. apk、图片下载工具(1)

    package com.js.ai.modules.pointwall.util; import java.io.BufferedInputStream; import java.io.Buffere ...

  2. Python多线程-信号量

    信号量就是一个线程中有多个线程 # -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import threading import ...

  3. HTTP及XMLHTTP状态代码一览

    (一) HTTP 1.1支持的状态代码 100 Continue 初始的请求已经接受,客户应当继续发送请求的其余部分 101 Switching Protocols 服务器将遵从客户的请求转换到另外一 ...

  4. Biorhythms(中国剩余定理(模板题))

    Description Some people believe that there are three cycles in a person's life that start the day he ...

  5. Project Browser & Inspector

    [Project Browser] You can drag items from the project structure list to the Favourites and also save ...

  6. 用C/C++扩展你的PHP

    PHP取得成功的一个主要原因之一是她拥有大量的可用扩展.web开发者无论有何种需求,这种需求最有可能在PHP发行包里找到.PHP发行包包括支持各种数据库,图形文件格式,压缩,XML技术扩展在内的许多扩 ...

  7. 使用laravel实现用户的登陆

    首先在 php artisan 里面 make:auth 生产一个门脸类 修改配置文件里面要哪个模型登陆 模型得继承一下才能 先写一下注册 密码必须要使用laravel的加密方法,使用MD5都没用 l ...

  8. Docker01 centos系统安装、centos安装docker、docker安装mongoDB

    1 centos系统安装 本博文是基于 centos6.5 的,利用VMware 虚拟机搭建 centos6.5 系统 1.1 centos6.5资源获取 1.2 安装 1.2.1 新建虚拟机 1.2 ...

  9. css常见问题解决方法

    设置方法: div内的img和span都需要设置vertical-align:middle; 解决inline-block的空格: http://www.w3cplus.com/css/fightin ...

  10. 重命名Docker容器

    重命名Docker容器: Docker rename [Old container name]  [New container name]