Update

*原文链接:http://blog.csdn.net/tyc129/article/details/70477131*

项目已重构至2.0,启用了新的数据结构和代码结构。基本弥补了初版的大部分功能缺失。还有部分功能未实现,比如指南针、区域的绘制方式等。
其实代码早就写的差不多了,只是最近在准备期末复习,加上智商下线,所以一直忘了更新博客╮(╯_╰)╭。具体的项目地址在此。

2017/06/28

背景

面试失利后,最近在专攻工程设计项目,指望着能通过它咸鱼翻身去名企。项目中需要一种可以自由控制并显示自定义的矢量路径和兴趣点的地图模块,仔细查了查发现并没有现成的轮子可以用,便想着自己定义一种数据格式进行解析和渲染。但真正实践起来,才发现难度颇大,先在这里记录下自己的思路和方法。

预期功能

  • 实现矢量路径的显示,不同道路的绘制样式不同。
  • 可以显示建筑点,可以区分不同建筑或类别,建筑点可以被点击,并返回对应的消息
  • 可以在特定的路径和建筑点上显示标签代表其名称。
  • 可以使用手势进行操作,整体的显示效果类似于主流的几个地图App。
  • 在单机上实现地图导航的功能。

技术原理实现

看了不少源码和资料,比如android-pathview和svg-android,里面的实现方式和项目构架思路都令我受益匪浅。在想了很久以后,根据本人项目的实际情况总结了一下我自己不成熟的想法。


数据结构

数据存储格式

  1. 最好是和原有的SVG的XML文档格式兼容,方便以后的解析和渲染,于是我选择了从SVG转换而来的VectorDrawable XML格式作为数据格式基础。

    这是一个VectorDrawable格式的例子。

    <vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="960dp"android:height="480dp"android:viewportWidth="960.0"android:viewportHeight="480.0">
    <path
        android:pathData="M570.1,440.2l-29.2,-29c-7.1,-8.5 -6.2,-36.7 -6,-40.7H425c0.1,3.9 1.1,32.2 -6,40.7l-29.2,29c-1.2,1.4 -1.7,2.5 -1.6,3.4h-0.1c0.8,7.7 6.7,6.3 13.6,6.3H558c6.7,0 12.4,1.3 13.5,-5.6 0.6,-1 0.3,-2.3 -1.4,-4.1z"android:fillAlpha="1"android:strokeColor="#B4BEC8"android:fillColor="#B4BEC8"android:strokeWidth="2"/>
    <path
        android:pathData="M727.5,355.1c0,8.5 -6.6,15.4 -14.7,15.4h-465.5c-8.1,0 -14.7,-6.9 -14.7,-15.4V45.4c0,-8.5 6.6,-15.4 14.7,-15.4h465.5c8.1,0 14.7,6.9 14.7,15.4v309.7z"android:fillAlpha="1"android:strokeColor="#C8D2DC"android:fillColor="#C8D2DC"android:strokeWidth="2"/>
    <path
        android:pathData="M489,343.7c-0,-4.2 3.4,-6.3 3.6,-6.4 -2,-2.9 -5,-3.3 -6.1,-3.3 -2.6,-0.3 -5.1,1.5 -6.4,1.5s-3.4,-1.5 -5.5,-1.4c-2.8,0 -5.4,1.6 -6.9,4.2 -2.9,5.1 -0.8,12.7 2.1,16.8 1.4,2 3.1,4.3 5.3,4.2 2.1,-0.1 2.9,-1.4 5.5,-1.4 2.6,0 3.3,1.4 5.5,1.3 2.3,-0 3.7,-2.1 5.1,-4.1 1.6,-2.3 2.3,-4.6 2.3,-4.7 -0.1,-0 -4.4,-1.7 -4.5,-6.8M484.8,331.3c1.2,-1.4 2,-3.4 1.7,-5.3 -1.7,0.1 -3.7,1.1 -4.9,2.5 -1.1,1.2 -2,3.2 -1.8,5.2 1.9,0.1 3.8,-0.9 4.9,-2.4"android:fillAlpha="1"android:strokeColor="#C8D2DC"android:fillColor="#fff"android:strokeWidth="2"/>
    <path
        android:pathData="M727.5,315.5V46c0,-8.8 -6.6,-16 -14.7,-16h-465.5c-8.1,0 -14.7,7.2 -14.7,16v269.5H727.5z"android:fillAlpha="1"android:strokeColor="#3C4650"android:fillColor="#3C4650"android:strokeWidth="2"/>
    <path
        android:pathData="M251.2,48.9h457.2v245.5H251.2z"android:fillAlpha="1"android:strokeColor="#141E28"android:fillColor="#141E28"android:strokeWidth="2"/>
    </vector>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
  2. 因为能力不够(笑),舍弃了一些不必要的数据属性。

    • 比如Group标签就暂时没有进行处理,也没有解析一些地图上用的不多的绘制命令如C1、Q2、A3等(可能等以后会再添加上)。
    • 因为地图的路径和兴趣点应有特定的绘制样式,为了增强灵活性所以忽略了Path标签内的原始属性如android:fillAlphaandroid:strokeWidth等。
  3. 将原本的<path>标签修改为了<road>,并添加了一些自定义的Attribute。

    • id代表该路径或兴趣点的ID
    • roadType代表道路类型,比如人行道、单行道、禁止通行等。
  4. 添加全新的自定义标签“兴趣点”<interest>,其中定义了一些自定义属性

    • id 兴趣点的id
    • absoluteX X轴绝对位置
    • absoluteY Y轴绝对位置
    • hasNextLevel是否存在二级地图,即该点是否是一个二级地图的进入点
    • pointType指兴趣点的类型(卫生间、建筑、景点之类的)

    未来还可以添加封闭的地图路径,如湖泊,围栏等等。

    这是一个矢量地图数据格式的测试样例。(瞎写的,很多地方不够完善)

    <mapVector xmlns:map="http://schemas.android.com/apk/res/android"map:width="900px"map:height="1300px"map:viewportWidth="960.0"map:viewportHeight="480.0">
    <roadmap:id="jdj1231"map:pathData="M0,0h800v700l-300,50"map:roadType="default"/>
    <roadmap:id="21gskkdh"map:pathData="M200,0v800l500,200"map:roadType="peopleOnly"/>
    <roadmap:id="gm34340"map:pathData="M0,0l100,900h400v300"map:roadType="peopleOnly"/>
    <roadmap:id="vv56657"map:pathData="M0,700l400,-200h-100v-300"map:roadType="prohibitAll"/>
    <roadmap:id="mmbi39"map:pathData="M0,1200h900v-700h-300"map:roadType="oneWay"/>
    <interestmap:id="gmior56546"map:absoluteX="500"map:absoluteY="650"map:pointType="building"/>
    <interestmap:id="qqwrhhy666"map:absoluteX="300"map:absoluteY="200"map:pointType="building"/>
    <interestmap:id="joidjf13233"map:absoluteX="700"map:absoluteY="600"map:pointType="toilet"/>
    <interestmap:id="zxcml23342"map:absoluteX="500"map:absoluteY="100"map:pointType="entrance"/>
    <interestmap:id="oij5fvvs44"map:absoluteX="300"map:absoluteY="1200"map:pointType="exit"/>
    </mapVector>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

数据类

  • PointDef 点。抽象类,定义了矢量地图点的基本格式,如id,XY轴位置等。
  • InterestPointDef 兴趣点。 继承于PointDef,定义了兴趣点的类型和其他属性。
  • PathDef 矢量路径。抽象类,定义了矢量地图路径的描述方式,包括id、画笔Paint和被绘制的Path对象。
  • RoadDef 道路。继承于PathDef,增加了道路类型等。
  • MapSrcDef地图数据资源。包括兴趣点和道路的ArrayList、兴趣点对应兴趣点类型的图标table、兴趣点和路径的标签map、地图尺寸,ViewPort尺寸以及绘制单位等。

项目结构

为了矢量地图项目的可拓展性和灵活性,我采用了Factory的设计模式,通过向RecourseResolver实例中添加必要的资源数据由VectorMapFactory生产出对应的VectorMap实例,具体的数据解析细节则对App编程人员透明。地图显示模块VectorMapView则专注于响应用户的操作并对地图进行变换,而具体的渲染任务则交由VectorMap对象中对应的MapRender实例进行处理。通过这样的结构,可以在一个App运行时,展示多个矢量地图,并可以轻松的进行实时地图数据修改。包括进入二级地图时异步加载并显示、修改兴趣点或路径的tag、兴趣点标更换等。为了管理多个VectorMap实例,我还设计了一个VectorMapManager进行根据LRU规则进行管理(暂时还没用上,也没有写好)。

这是项目的具体结构。没有使用第三方库

Java类

  • GestureParser 手势解析器。配合GestureDetector实现单指移动地图,双指旋转和缩放。
  • InterestPointDef 兴趣点类。包含兴趣点的定义。
  • MapRender 地图渲染器类。负责渲染地图数据。
  • MapSrcDef 地图资源类。包含道路列表、兴趣点列表、地图大小和地图度量单位等。
  • MathUtils 数学工具类。包含一些静态的数学函数,主要是几何学的,由于Java自带的Math类没有,只好自己实现了。
  • PaintType 画刷类型类。由于在绘制地图时大量数据都需要不同样式的Paint,所以使用该类统一管理笔刷的类型和与其他数据类型的映射关系。现在该类的主要功能是进行笔刷的映射,将道路类型的int变量映射成画刷的int类型。
  • ParseUtils 解析工具类。包括一些小的解析静态函数和解析静态类,负责执行一些数据解析的操作。
  • PathDef 路径抽象类。定义了路径的基本属性。
  • PointDef 点抽象类。定义了点的基本属性
  • RecourseResolver 资源解析类。包含各种XML的ContentHandler和数据读取函数。负责解析数据文件。
  • RoadPathDef 道路类。 定义了一些道路属性。
  • VectorMap 矢量地图类。包含矢量地图数据、地图渲染器、标签哈希Map、图标表、画刷表等。
  • VectorMapFactory 矢量地图工厂类。负责协调其他类完成数据解析以及最终的构建工作。
  • VectorMapManager 矢量地图管理类。负责管理多个矢量地图,包括清理和寻找地图。未来可能添加其他功能。
  • VectorMapView 矢量地图视图类。负责在App上显示地图,并接受用户的手势操作,控制地图的显示方式以及位置。

View属性

直接看代码定义吧。

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="VectorMapView"><!--是否显示标签--><attr name="showTags" format="boolean"/><!--允许旋转--><attr name="allowRotate" format="boolean"/><!--允许缩放--><attr name="allowScale" format="boolean"/><!--允许移动--><attr name="allowTranslate" format="boolean"/><!--允许点击(兴趣点找到对应id)--><attr name="allowClick" format="boolean"/><!--允许视角突破地图边界--><attr name="allowBreakBoundary" format="boolean"/><!--地图填充类型--><attr name="mapFillType" format="enum"><enum name="Default" value="0"/><enum name="Minimum" value="1"/><enum name="Maximum" value="2"/></attr><!--最大缩放绝对比例--><attr name="maxScale" format="float"/><!--最小缩放绝对比例--><attr name="minScale" format="float"/><!--一次放大的相对比例--><attr name="scaleOneTap" format="float"/><!--地图起始视点X轴位置--><attr name="originalPositionX" format="float"/><!--地图起始视点Y轴位置--><attr name="originalPositionY" format="float"/><!--地图起始视点旋转角度--><attr name="originalRotate" format="float"/><!--点击敏感度(点击位置的搜索范围大小)--><attr name="clickSensitivity" format="float"/></declare-styleable>
</resources>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

项目成果

花了快半个月时间,基本实现了预期的功能,地图可以自由移动缩放和旋转(图标和标签方向大小不变),但会受到边界检测和最大最小缩放比例的显示,点击兴趣点会返回对应的id,通过id可以找到对应兴趣点的信息。总体来说效果差强人意,还是有很大改进的空间的。

关于项目源码,由于工程设计项目暂时还没有完成,在实际磨合中应该还会有不少修改,所以我准备在项目完成后再开源到GayHub上。

技术难点

其实对我来说,一个问题,没解决就是难点。解决了,回头一看,其实也就那么回事儿(马后炮的感觉……),所以我就说说对这个项目我还未解决的问题。

  • 项目架构不成熟。这一直是我的心病,或者说是强迫症(笑)。总是会假设面对各种实际业务情况时项目的迭代速度和出现问题时修改的复杂度,想的多了总会有些不尽如人意的地方。这个可能需要我不断的动手来增加经验才能解决。

  • 暂时没法完美解决边界检测以及修正的功能。我自己已经实现了在view和地图角度一致的情况下的边界检测以及控制函数。但当地图发生旋转时,现在的检测机制并不能很好的检测出来,更不知道如何进行修正。看了看主流的几个地图App好像都不需要边界检测,所以也没啥好办法。╮(╯_╰)╭可能还得补补数学才行。

  • 路径导航暂时无法实现(还没想好)。可能还是数据结构的问题,现在路径与路径间没有联系,路径的交点也没有记录。最短路径的算法倒是简单粗暴4,所以还得考虑在移动端单机实现的效率问题。

  • 丑。(没有衔接动画、缺少一些画面修饰)

总结和感想

刚开始准备做的时候总感觉难得不得了,什么自定义view啦,XML解析啦,矩阵换算啦,手势控制啦,感觉根本做不出来。但是真正沉下心查资料一点点写,发现其实也就那样,写着写着也就实现出来了。
我觉得,难的不是技术,而是想法。毕竟在代码的世界里,我们就是上帝,就是马良,就是贝多芬,如果只是填充代码,那和咸鱼(码农)又有什么区别呢?


  1. 三次贝塞尔曲线 ↩
  2. 二次贝塞尔曲线 ↩
  3. 弧线 ↩
  4. BFS ↩

自定义数据格式的矢量地图实现相关推荐

  1. mapbox 接入高德矢量地图实战

    Mapbox 作为现如今比较流行的地图框架为我们提供了漂亮的个性化地图,在平常的使用过程中可以方便的接入高德/谷歌等矢量切片地图.由于Mapbox地图数据来源于Open Street Map等国外厂商 ...

  2. OpenLayers之多源数据加载七:矢量地图

    一.矢量地图介绍 矢量地图的图形的元素是一些点.线.矩形.多边形.圆和弧线等等,它们都是通过数学公式计算获得的.由于矢量图形可通过公式计算获得,所以矢量图形文件体积一般较小. 矢量图形最大的优点是无论 ...

  3. echarts中自定义图片的矢量路径设置

    echarts中自定义图片的矢量路径设置 在echarts象形柱图中要使用自定义的图片有三种格式,如下图: 前面两种方式我们项目中也常用到,第三种是矢量路径,如何获取这个矢量路径呢,方法如下: 第一步 ...

  4. Mapbox、GeoServer离线部署矢量地图

    Mapbox.GeoServer离线部署矢量地图 关键词:Mapbox.GeoServer.Tomcat.PostgreSQL.PostGis 一.地图数据获取 使用OpenStreetMap获取中国 ...

  5. osm地图数据 mysql_超实用! 如何下载OSM全球矢量地图数据?

    大家在制作建筑.景观.城市设计等专业的作品集时,通常需要获得基础地理图层数据,来对区域进行分析. 那有什么简单的方法可以获得相关的矢量地图吗?本期克里斯来为大家介绍下如何利用OSM来实现,很方便哦~ ...

  6. 超棒的jQuery矢量地图生成插件 - JQVAMP

    为什么80%的码农都做不了架构师?>>>    日期:2012-5-14  来源:GBin1.com 在线演示  本地下载 是不是也考虑生成一个矢量类型的地图?今天我们将介绍的这款j ...

  7. Unity3D 自定义数据格式

    最近在研究Unity3d, 总的来说感觉还不错, 就是感觉潜规则有点多.............. 数据格式支持有点少, 需要自定义数据格式, 研究了下, 模型, 骨骼, 已经搞定... 转载于:ht ...

  8. OpenLayer学习之矢量地图

    一.首先了解下矢量地图和栅格地图 矢量图使用直线和曲线来描述图形,这些图形的元素是一些点.线.矩形.多边形.圆和弧线等等,矢量地图放大和缩小不会失真(图片你要是放大一定程度明显可以看出一个一个小格→栅 ...

  9. Cesium矢量地图插件CesiumVectorTile 发布新版本

    Cesium矢量地图插件CesiumVectorTile 发布新版本 一周前CesiumVectorTile 1.2.1版本就已经更新到npm里了,本次更新主要解决Cesium最新版本适配问题. Ce ...

最新文章

  1. 网关的作用是什么_SpringCloud中Zuul网关原理及其配置,看它就够了
  2. 搜索重复代码_LeetCode专题——详解搜索算法中的搜索策略和剪枝
  3. linux查看openssh和openssl版本命令
  4. 嵌入式Linux系统编程学习之二十四消息队列
  5. esp32树莓派_用树莓派 DIY 宠物自动喂食机,再也不用担心家里的萌宠了
  6. STM8学习笔记----PWM单脉冲模式输出
  7. 初用WEB IOU,IE LAB备战启航
  8. [JavaScript语法学习]重新认识JavaScript
  9. js—封装原生AJAX
  10. 小米全系列机型代码查询与 制作rom分区架构图示
  11. 人脸识别活体检测技术
  12. python 隐马尔可夫模型的中文分词和词性分类实验 hmm
  13. 一般程序员真实工资是多少?
  14. matlab字符识别ocr,OCR字符识别 matlab
  15. win10系统在文件夹中图片不显示内容问题
  16. 数据库MySQL入门-下
  17. 学习笔记-部署和管理DPM 2016-04文件和应用程序保护
  18. oracle正则表达式匹配字母,oracle正则表达式函数 匹配
  19. 跨页面清除Cookie信息
  20. 纯色背景图片去除底色工具发布,将背景变透明

热门文章

  1. et al、e.g.、i.e.读音及释义
  2. WORD如何比较原文档和别人修改过的文档?
  3. flask-mail异步发送邮件_Python爬虫系列:用邮件来通知爬虫异常状况
  4. VS Code解决Go相关工具无法安装问题
  5. 通过代码实例来说明spark api mapPartitions和mapPartitionsWithIndex的使用
  6. Java判断一个数是不是素数
  7. JBoss 7.1.1启动时遇到Address already in use: bind /127.0.0.1:9990的处理办法
  8. python连乘函数_Python常用的几种常用的内置函数
  9. vs2010括号不成对_C++ VS2010 括号匹配 求强人解答
  10. xampp mysql是空的_xampp中修改mysql默认空密码(root密码)的方法分享