国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

你可能不知道的前端算法之文字避讓(inMap)

2019-11-19 14:32:23
字體:
來源:轉載
供稿:網友

前言

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 ? diam : this.textReact.height; //矩形的高度  return {    x,    y,    minX: x,    maxX: this.center.x + this.radius,    minY: this.center.y - maxH / 2,    maxY: this.center.y + maxH / 2  };}

以此類推,描述下面、左面、上面的虛擬矩形坐標。

判斷碰撞

判斷兩個矩形是否覆蓋相交,根據矩形的 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,接下來還會繼續給大家分享干貨。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 贡嘎县| 神农架林区| 元阳县| 佛教| 从江县| 安图县| 湖州市| 叶城县| 沙洋县| 南宫市| 龙泉市| 松滋市| 临澧县| 屏东市| 平凉市| 乌鲁木齐县| 钦州市| 通化县| 阳高县| 梁平县| 海林市| 高邮市| 南开区| 凤翔县| 祁门县| 那坡县| 乌海市| 玉山县| 闵行区| 万盛区| 满洲里市| 萨嘎县| 宝清县| 封开县| 新和县| 旺苍县| 桐城市| 屯门区| 长治市| 黑龙江省| 栾川县|