Prática da função de agregação de POI do mapa

introduzir

Ao implementar funções de negócios relacionadas a mapas, muitas vezes nos deparamos com a necessidade de exibir diferentes formas de dados de POI de pontos de interesse do mapa em diferentes níveis de magnitude, focando em valores estatísticos regionais em um grande escopo geográfico e focando em um pequena abrangência geográfica.Os detalhes e operações do POI, por isso é inevitável usar a função de agregação do mapa POI. Em seguida, vamos discutir como o POI do mapa plano bidimensional é agregado.

Plano de implementação

A função de agregação implementada neste artigo usa o algoritmo de centroide de grade como estratégia de agregação de pontos, e a árvore quádrupla é usada para busca rápida. As etapas de implementação são as seguintes: 1. Divida a área de visualização atual do mapa em várias grades, cada grade é um retângulo do mesmo tamanho;Image.png

2. Percorra todas as grades e faça o seguinte para cada grade: encontre todos os POIs em cada grade, calcule o centróide (x, y) dos pontos POI, onde x é o valor médio do eixo x de todos os POIs, y Para a média do eixo y de todos os POIs, use o ponto do centroide como ponto de agregação para substituir o POI original;Imagem [2].png

Imagem [3]. png

3. Renderize esses pontos de agregação e ajuste a aparência de acordo com o número de POIs agregados;

Imagem [4].png

4. Se a visualização do mapa mudar (zoom, mover, redimensionar), repita a etapa 1.

Imagem [5]. png

A dificuldade deste esquema é como encontrar rapidamente todos os POIs dentro de cada intervalo de grade. Claro, podemos usar o método mais mecânico, ou seja, percorrer cada ponto para determinar se ele está dentro de uma determinada faixa de grade que foi delineada, mas o tempo de execução desse método aumentará exponencialmente com o aumento do número de grades e POIs , não é adequado para lidar com POIs massivos. Portanto, é necessário construir uma quadtree para fornecer capacidade de busca rápida.

Construir quadtree

Muitas vezes usamos árvores binárias para encontrar e armazenar dados unidimensionais. Para o mapa POI (com coordenadas xy), é um array bidimensional, que pode ser armazenado e pesquisado usando uma quadtree.

Características dos nós quadtree:

  1. Os atributos que cada nó de árvore precisa conter são os seguintes:
constructor(data, conf) {
    // 每个节点所指定的地址范围 xmin ymin xmax ymax
    this.extent =  new Extent(conf.extent)
    // 节点数据容量,超过该容量则会分裂成4个子节点
    this.bucketLimit = conf.bucketLimit || 10
    // 当前节点的深度,根节点深度为0
    this.deep = conf.deep || 0
    //存储的POI
    this.points = data
  }
复制代码
  1. Cada nó pode ser dividido em 4 nós filhos. A extensão desses quatro nós filhos são os 4 quadrantes geográficos do nó pai, ou seja, noroeste (noroeste) nordeste (nordeste) sudoeste (sudoeste) sudeste (sudeste)

  2. Se o número de pontos POI contidos no intervalo de nós atual exceder a capacidade bucketLimit e a profundidade do nó não exceder a profundidade máxima, os nós filhos serão divididos.

4. Todos os pontos POI serão eventualmente colocados em nós folha

Imagem [6].png

A imagem mostra a quadtree construída com base nos dados existentes. Pode-se ver pela imagem que quanto mais frequentemente os nós são divididos, mais denso é o POI

Encontrar POIs dentro de uma grade

构造好了四叉树,就可以用它来查找单个网格Grid内的POI点,这是一个对四叉树递归遍历的过程,操作如下:

  1. 从根节点开始,判断当前节点extent是否与网格Grid几何相交,如果相交,则进入步骤2

  2. 当前节点有POI点(只有叶子节点有POI点),则遍历这些POI点,将处于Grid范围内的POI点存入数组Arr;否则进入步骤3

  3. 如果当前节点有子节点(如果有的话必定是4个子节点),则按步骤1处理这些子节点

最终我们会得到一个Grid范围内所有POI点的数组Arr。每个网格范围内的POI点到手了,直接求取它们xy轴的平均值,就能得到这个网格质心聚合点的坐标,接下来就是在地图上的渲染工作。

Honeycam 2022-04-11 16-29-40.gif

代码层实现四叉树

我依据代码复用情况建了两个类

1.四叉树QuadTreeNode,提供树的构建和查找功能 findPoints(extent)
2.矩形范围Extent,这里的范围即有四叉树的地理范围,也有网格的地理范围,用于计算点和面,面和面之间的几何关系

QuadTreeNode

  /**
   * 查找指定范围内的坐标点
   * @param extent 指定范围
   * @returns {Array}
   */
  findPoints(extent) {

    if (!(extent instanceof Extent)) {
      extent = new Extent(extent)
    }

    let arr = []

    function travel(node) {

      if (node.isIntersects(extent)) {

        //如果当前四叉树节点有points,则将在extent范围内的points入栈
        if (node.points.length > 0) {
          node.points.forEach(point => {
            if (extent.within(point)) {
              arr.push(point)
            }
          })

        } else {
          const {northWest, northEast, southWest, southEast} = node

          if (northWest && northWest.isIntersects(extent)) {
            travel(northWest)
          }
          if (northEast && northEast.isIntersects(extent)) {
            travel(northEast)
          }
          if (southWest && southWest.isIntersects(extent)) {
            travel(southWest)
          }
          if (southEast && southEast.isIntersects(extent)) {
            travel(southEast)
          }
        }
      }
    }

    travel(this)

    return arr
  }
  
}
复制代码

Extent

class  Extent{
  constructor(conf = {xmin: 0, ymin: 0, xmax: 100, ymax: 100}){

    this.xmin = conf.xmin
    this.ymin = conf.ymin
    this.xmax = conf.xmax
    this.ymax = conf.ymax
  }
  /**
   * 判断当前范围是否与指定范围相交
   * 如果两个矩形相交,则两个矩形中心点间的距离肯定小于两个矩形边长和的1/2
   * @param extent 指定范围
   * @returns {boolean}
   */
  intersects(extent) {

    const {xmin, ymin, xmax, ymax} = extent

    //x轴 两个矩形中心点距离 * 2
    let lx = Math.abs(this.xmax + this.xmin - xmin - xmax)
    //x轴 两个矩形边长和
    let bx = Math.abs(this.xmax - this.xmin + xmax - xmin)
    //y轴 两个矩形中心点距离 * 2
    let ly = Math.abs(this.ymax + this.ymin - ymin - ymax)
    //y轴 两个矩形x轴边长和
    let by = Math.abs(this.ymax - this.ymin + ymax - ymin)

    if (lx <= bx && ly <= by) {
      return true
    } else {
      return false
    }
  }
}
复制代码

聚合功能的应用

1.解决重叠坐标点的选择问题 这是我非常想解决的问题,在实际项目中遇到的真实数据其实没有那么理想,很多情况下有部分POI数据因为地址不够详细或者定位api出错会导致坐标点一模一样,从而导致一堆点叠加在某个位置,鼠标操作只能取到最上方点的属性。既然实现了点聚合功能,那么可以配置缩小聚合的阈值,提取到所有叠加点的属性。

2.为热力图层的渲染提供基础数据 热力图其实可以看做点聚合的另一种展示方式,它所需要的源数据与点聚合非常类似(x坐标,y坐标,点的权值)。有了源数据,只要将做表单做一次地理坐标和平面坐标的转换,权值归一化,再渲染出热力图也是很简单的事。

3.海量数据的统计展示 还是文章开头提到的需求,大比例尺我要看POI的聚合数量,小比例尺我要看单个POI的属性明细。

优点和缺陷

  1. 四叉树其实是个空间换时间的做法,和质心合并算法(求平均值)结合带来的有点就是计算速度快
  2. Há um certo erro no algoritmo da grade, se houver vários pontos cortados na linha de fronteira das duas grades, o algoritmo cortará o grupo de pontos em dois pontos agregados, mas isso não é grande coisa.

Exemplo de artigo

Página de demonstração de agregação de POI do mapa

Código fonte do GitHub

Artigo de referência

"Exploração e Prática de Algoritmos de Agregação de Pontos de Interesse do Mapa" As soluções de algoritmo mencionadas neste artigo são mais detalhadas e vívidas, adequadas para pesquisas mais aprofundadas

Acho que você gosta

Origin juejin.im/post/7085274646949396487
Recomendado
Clasificación