前言
inMap 是一款基于 canvas 的大数据可视化库,专注于大数据方向点线面的可视化效果展示。目前支持散点、围栏、热力、网格、聚合等方式;致力于让大数据可视化变得简单易用。
GitHub 地址:https://github.com/TalkingData/inmap(本地下载)
文档地址:http://inmap.talkingdata.com/
在地理信息可视化中,我们经常会遇到在地图上标记文字的需求,下面展示的是某流行 chart 图表框架的效果:
要显示的文字空间不够时,就会造成文字重叠显示混乱,用户体验很不友好。
怎么解决这个问题呢?我们采用文字避让算法,解决这种坑爹的问题。
下面展示的是 inMap 文字避让效果:
文字标注算法是 GIS 中最复杂的问题之一(属于 NP 复杂度问题,所以通常不能找到最优解,只能找到较优解)。
inMap 避让算法采用的是四分位模型算法,接下来手把手教你写避让算法,老司机带你装逼带你飞。
准备数据
inMap 接收的是经纬度数据,需要把它映射到 canvas 的像素坐标,这就用到了墨卡托转换,墨卡托算法很复杂,以后我们会有单独的一篇文章来讲讲他的原理。经过转换,你得到的数据应该是这样的:
[ { "name": "海门",//要显示的文字 "lng": 121.15, "lat": 31.89, "count": 7, "pixel": { //像素坐标 "x": 968, "y": 736 } }, { "name": "鄂尔多斯", "lng": 109.781327, "lat": 39.608266, "count": 5, "pixel": { "x": 659, "y": 478 } }, ... ]
好了,我们得到转换后的像素坐标数据(x、y),就可以做下面的事情了。
求出每段文字矩形的实际大小
measureText() 是 canvas 内置的方法,返回字体宽度的像素单位:
let ctx = this.container.getContext('2d'); // canvas 上下文 let width= ctx.measureText(name).width;
我们通过 measureText 得到每个文字的宽度,canvas 并没有直接获取文字的方法,那文字的高度如何的得到呢?
我们通过反复测试发现 canvas 的 font 等于 “13px Arial” 字体(别的字体不敢保证)的时候,文字的高度大概是 fontSize 的 1.1 倍。
所以代码如下:
let fontSize = parseInt(ctx.font); let height = fontSize * 1.1;
文字的宽度和高度得到后,我们就可以创建文字矩形的坐标系了。
创建四分位模型
所谓四分位模型,每一个标记点都有上下左右四个放文字的位子,如果左边放不下,那就放右边试试,还不行就放到下面试试,以此类推,原理就这么简单,哈哈。
创建右侧虚拟矩形坐标描述:
右侧虚拟矩形坐标的描述把圆点也包含在内了,是为了防止文字和圆点重叠。
在计算虚拟矩形的高度时有些坑,圆点大小不是固定的,是根据用户动态配置的,圆点的直径可能大于文字的高度,我们就设定虚拟矩形的高度永远都是最大的那个,需要做一些特殊处理。
代码如下:
_getLeftAnchor() { let x = this.center.x - this.radius - this.textReact.width, y = this.center.y - this.textReact.height / 2, diam = this.radius * 2, maxH = diam > this.textReact.height "color: #ff0000">判断碰撞
判断两个矩形是否覆盖相交,根据矩形的 minX,maxX,minY,maxY 判断相交,原理比较简单,代码如下:
/** * 判断分位是否相交 * @param {*} target */ isAnchorMeet(target) { let react = this.getCurrentRect(), targetReact = target.getCurrentRect(); if ((react.minX < targetReact.maxX) && (targetReact.minX < react.maxX) && (react.minY < targetReact.maxY) && (targetReact.minY < react.maxY)) { return true; } return false; }创建虚拟文字集合对象
let labels = pixels.map((val) => { let radius = val.pixel.radius + this.style.normal.borderWidth; //圆点半径 return new Label(val.pixel.x, val.pixel.y, radius, fontSize, byteWidth, val.name); });递归遍历虚拟文字集合、判断是否与其他相交,如果有相交就移动当前文字位子,直到不相交为止。当找不到合适位置时,就选择隐藏当前文字。
代码如下:
do { var meet = false; //本轮是否有相交 for (let i = 0; i < labels.length; i++) { let temp = labels[i]; for (let j = 0; j < labels.length; j++) { if (i != j && temp.show && temp.isAnchorMeet(labels[j])) { temp.next(); meet = true; break; } } } } while (meet);绘画文字
labels.forEach(function (item) { if (item.show) { //是否显示 let pixel = item.getCurrentRect(); ctx.beginPath(); ctx.fillText(item.text, pixel.x, pixel.y); ctx.fill(); } });文字避让算法到目前介绍完了,对应的 inMap 文件地址为https://github.com/TalkingData/inmap/blob/master/src/worker/helper/Label.js,接下来还会继续给大家分享干货。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]