Manual de desarrollo de maptalks - Avanzado

prefacio

En el artículo anterior, ya entendí las funciones básicas de maptalks y cómo dibujar mapas, para los socios pequeños que tienen la capacidad de explorar, es posible que hayan completado funciones más avanzadas, pero aquí, a modo de manual, iré registrando lentamente las desarrollo detalles

Los efectos que los clientes necesitan son variados, pero los cambios funcionales no se pueden escapar.

El siguiente ejemplo se basa en el ejemplo anterior.

Dirección del código del proyecto (demostración)

marca

Creación y eliminación en aplicaciones prácticas.

En aplicaciones prácticas, las marcas de marca se muestran junto con el tipo seleccionado por el usuario, lo que implica la eliminación y creación de marcas.

  1. Primero definamos una página, de la siguiente manera, deje una barra de herramientas sobre el mapa
<template>
  <div class="container">
    <div class="map-toolbar">
      <el-checkbox-group v-model="checkList" @change="handleCheckChange">
        <el-checkbox :label="item.label" v-bind:key="item.value" :value="item.value" v-for="item in chooseData"></el-checkbox>
      </el-checkbox-group>
    </div>
    <div
      id="map"
      class="map-container"
    />
  </div>
</template>

<style scoped lang="scss">
  html, body {
      
      
    margin: 0px;
    height: 100%;
    width: 100%;
  }
.container {
      
      
  width: 100%;
  height: 100%;
  position: absolute
}
.map-toolbar {
      
      
  position: relative;
  z-index: 1000;
  width: 500px;
  height: 50px;
  float: right;
  top:10px;
  right: 300px;
  background-color: #fff;
  border-radius:5px;
  padding: 10px;

}
  .map-container {
      
      
    width: 100%;
    height: 100%
  }
</style>

imagen-20211031113311804

  1. Método de definición:

    // 定义两个常量(仅测试)   
    const schoolCoordinate = [
            {
          
          
                adcode: 510104,
                name: "小学校",
                center:  [118.13734735854666, 24.498801064931087],
                parent: {
          
          
                    adcode: 510100
                },
            },  {
          
          
                adcode: 510104,
                name: "大学校",
                center:  [118.11804966596969, 24.47633923130772],
                parent: {
          
          
                    adcode: 510100
                },
            }
        ]
    
        const hospitalCoordinate = [
            {
          
          
                adcode: 510104,
                name: "小医院",
                center:  [118.18303419164465, 24.51143878000599],
                parent: {
          
          
                    adcode: 510100
                },
            },{
          
          
                adcode: 510104,
                name: "大医院",
                center:  [118.19412188942488, 24.53586932160701],
                parent: {
          
          
                    adcode: 510100
                },
            }
    
        ]
    // data 里定义的变量:
    // 类型复选框
    checkList: [],
        chooseData: [{
          
          
            label: '学校',
            value: 'school'
        }, {
          
          
            label: '医院',
            value: 'hospital'
        }],
            // 保持选择的图层的名称
            checkLayers: []
    
    
    // methods定义的方法
    drawMark(centerPointList, layer) {
          
          
         if (!centerPointList) {
          
          
             console.log('无区域中心点数据')
             return
         }
         const info = {
          
           content: '', width: 150, minHeight: 100 }
         const result = []
         // 这里 d 的数据格式是数组,如:[-0.113049, 51.498568]
         centerPointList.forEach(d => {
          
          
             if (!d.info) {
          
          
                 d.info = info
             }
             // 设有高度、高亮的mark
             const mark = new maptalks.Marker(d.center, {
          
          
                 // 设置了这个属性,会替换默认的图标
                 // symbol: {
          
          
                 // markerFile: 'foo.png',
                 // textName: d.name
                 // },
                 properties: {
          
          
                     // 高度设置
                     altitude: 50
                 }
             }).addTo(layer)
             mark.setInfoWindow({
          
          
                 title: d.name,
                 content: '<div>' + d.adcode + '</div>',
                 // autoPan: true,
                 width: d.info.width,
                 minHeight: d.info.minHeight,
             })
    
             mark.setZIndex(1000)
             result.push(mark)
         })
         return result
     },
    
     // 复选框选中
         handleCheckChange(data) {
          
          
             console.log(data)
             const _t = this
             // 清楚图层
             this.checkLayers.forEach(l => {
          
          
                 l.clear()
             })
             const schoolMarkLayer = this.mapEngine.getLayer('schoolMark')
             const hospitalMarkLayer = this.mapEngine.getLayer('hospitalMark')
             data.forEach(d => {
          
          
                 if (d === '学校') {
          
          
                     _t.drawMark(schoolCoordinate, schoolMarkLayer)
                     _t.checkLayers.push('schoolMark')
                 }
                 if (d === '医院') {
          
          
                     _t.drawMark(hospitalCoordinate, hospitalMarkLayer)
                     _t.checkLayers.push('hospitalMark')
                 }
             })
         },
    

no se como subir el video Es posible tener todos los códigos, si no hay post completo, no quiero ocupar mucho espacio;

Permítanme hablar de ello aquí, algunos de sus métodos

  1. layer.clear() , borrará todo en la capa;
  2. mark.remove() , puede usar esto para eliminar la marca, pero esto debe ser llamado por el objeto de marca. Si el front-end quiere lograr las funciones anteriores, entonces es necesario guardar la lista de marcas. Esto es muy imprudente Cuando hay demasiados puntos, el front-end puede no soportarlo, por lo que cada tipo de capa se almacena en caché aquí, y la capa se usa para la operación;

icono personalizado

Aquí hay un ejemplo que usa el logotipo de vue como ícono de reemplazo

顶部require引入静态资源
const logo = require('../../assets/logo.png')


// 创建时,使用symbol替换默认的样式
const mark = new maptalks.Marker(d.center, {
    
    
                        symbol: {
    
    
                            // markerType: 'square',
                            markerFile: logo,
                            markerWidth: 40,
                            markerHeight: 40,
                            markerDx: 0,
                            markerDy: 0,
                            markerOpacity: 1
                        },
                        properties: {
    
    
                            // 高度设置
                            altitude: 0
                        }
                    }).addTo(layer)

Aquí se usa markerFile para reemplazar el icono, el tipo de marcador, el tipo de marcador, qué tipo de icono representa, cuando tiene el atributo de archivo de marcador, no se puede sobrescribir, cuando no hay un archivo de marcador, se puede mostrar con tipo de marcador, tiene varios tipos : ellipse cross x diamond bar square triangle pin pie, el efecto se puede configurar por ti mismo para ver.

El efecto es el siguiente:

imagen-20211106190724575

Agregar efectos de animación

Después de agregar su propia marca, es posible que necesite algunos efectos visuales. Mark también proporciona animateun método para establecer su propia animación. Luego, estableceremos una animación cuando aparezca la marca y la expansión horizontal del logotipo de vue.

Agregue el siguiente código sobre la base del código anterior y luego establezca la configuración new maptalks.Markerpredeterminada en 0 y 10, lo que significa que la altura inicial es 0, el ancho inicial es 10 y se transforma en una altura de 40 y un ancho de 40symbol.markerHeightsymbol.markerWidth

mark.animate({
    
    
    symbol: {
    
    
        markerWidth: 40,
        markerHeight: 40
    },
    properties: {
    
    
        altitude: 800
    }
}, {
    
    
    duration: 150,
}, function (frame) {
    
    
    if (frame.state.playState === 'finished') {
    
    
        console.log('animation finished');
    }
});

Cuando hacemos clic en la opción de la escuela o el hospital, estos rociados se expanden automáticamente, y también se puede agregar el monitoreo, y se puede realizar algún procesamiento en cada etapa de la animación.

Código completo:

   drawMark(centerPointList, layer) {
    
    
                if (!centerPointList) {
    
    
                    console.log('无区域中心点数据')
                    return
                }
                const info = {
    
     content: '', width: 150, minHeight: 100 }
                const result = []
                // 这里 d 的数据格式是数组,如:[-0.113049, 51.498568]
                centerPointList.forEach(d => {
    
    
                    if (!d.info) {
    
    
                        d.info = info
                    }
                    // 设有高度、高亮的mark
                    const mark = new maptalks.Marker(d.center, {
    
    
                        symbol: {
    
    
                            markerType: 'square',
                            markerFile: logo,
                            markerWidth: 10,
                            markerHeight: 0,
                            markerDx: 0,
                            markerDy: 0,
                            markerOpacity: 1
                        },
                        properties: {
    
    
                            // 高度设置
                            altitude: 0
                        }
                    }).addTo(layer)
                    mark.setInfoWindow({
    
    
                        title: d.name,
                        content: '<div>' + d.adcode + '</div>',
                        // autoPan: true,
                        width: d.info.width,
                        minHeight: d.info.minHeight,
                    })

                    mark.animate({
    
    
                        symbol: {
    
    
                             markerWidth: 40,
            				 markerHeight: 40
                        },
                        properties: {
    
    
                            altitude: 800
                        }
                    }, {
    
    
                        duration: 150,
                    }, function (frame) {
    
    
                        if (frame.state.playState === 'finished') {
    
    
                            console.log('animation finished');
                        }
                    });

                    mark.setZIndex(1000)
                    result.push(mark)
                })
                return result
            },

Se puede ver que al crear el objeto de marca al principio, el ancho establecido widthes solo 10, que es el ancho inicial de la animación, y luego las propiedades establecidas en el método de animación son las propiedades que deben cambiarse, y son también las propiedades finales Controlando el durationtiempo de ejecución de la animación.

herramienta

Aquí hay básicamente símbolos, así que tengo que mencionarlo nuevamente aquí, es decir, symbolpodemos verificar su documentación para este atributo, porque es un sistema, y ​​está unificado y completo;

El primero de los siguientes comentarios de atributos relacionados está relativamente completo, y los últimos son casi iguales, por lo que no se marcarán todos.

herramienta de medición de distancia

API: Clase: DistanceTool (maptalks.org)

Calcule la distancia, copie el caso oficial, haga una ligera modificación, agregue el evento final,

    /**
       * 测距工具
       * @returns {*}
       */
    addDistanceTool () {
    
    
      return new maptalks.DistanceTool({
    
    
        symbol: {
    
    
          lineColor: '#34495e',
          lineWidth: 2
        },
        // 请看 symbol属性说明文档:https://github.com/maptalks/maptalks.js/wiki/Symbol-Reference
        // 绘制的线的样式
        vertexSymbol: {
    
    
          // 绘制的marker类型,也就是简单形状,支持:ellipse cross x diamond bar square triangle pin pie
          markerType: 'square',
          // 绘制的marker的填充色
          markerFill: '#1bbc9b',
          // 绘制的线的颜色
          markerLineColor: '#000',
          // 绘制的线的宽度
          markerLineWidth: 1,
          // 绘制的marker大小
          markerWidth: 10,
          markerHeight: 10
        },
		// 文本标签属性
        labelOptions: {
    
    
          textSymbol: {
    
    
            // 文本呈现的样式,默认是: monospace, 当你设置 textFont时会覆盖这个属性
            textFaceName: 'monospace',
            // 文本填充色(字体颜色)
            textFill: '#fff',
            // 行距
            textLineSpacing: 1,
            // 对齐方式
            textHorizontalAlignment: 'right',
            // 文本标签与marker的距离,也就是与打点的位置的距离
            textDx: 20,
            // 标签的线的颜色
            markerLineColor: '#b4b3b3',
            // 标签的填充色
            markerFill: '#000'
          },
          boxStyle: {
    
    
            // 标签的padding, 第一个值是左右的padding,第二个是上下的padding
            padding: [6, 5],
            symbol: {
    
    
              // 绘制的marker类型,也就是简单形状,支持:ellipse cross x diamond bar square triangle pin pie
              markerType: 'square',
              markerFill: '#000',
              markerFillOpacity: 0.9,
              markerLineColor: '#b4b3b3'
            }
          }
        },
        // 清楚按钮的symbol
        clearButtonSymbol: [{
    
    
          markerType: 'square',
          markerFill: '#000',
          markerLineColor: '#b4b3b3',
          markerLineWidth: 2,
          markerWidth: 15,
          markerHeight: 15,
          markerDx: 20
        }, {
    
    
          markerType: 'x',
          markerWidth: 10,
          markerHeight: 10,
          markerLineColor: '#fff',
          markerDx: 20
        }],
        language: 'zh-CN',
        once: true
      }).addTo(this.mapEngine)
        .on('drawend', p => {
    
    
          console.log('地图坐标:' + p.coordinate)
          console.log('界面坐标:' + p.containerPoint)
          console.log('测量距离:' + p.drawTool.getLastMeasure() + '米')
        })
    },

Añadir botones de herramientas:

Aquí agregamos el botón [Medida de rango], que activa la topografía y el mapeo para el Año Nuevo chino y los días festivos cuando se hace clic, y cierra la topografía y el mapeo cuando se vuelve a hacer clic.

    /**
       * 创建工具栏
       */
    addToolbar () {
    
    
      const _t = this
      const map = this.mapEngine
      new maptalks.control.Toolbar({
    
    
        items: [
          {
    
    
            item: '测距',
            click: () => {
    
    
              if (_t.distanceTool.isEnabled()) {
    
    
                _t.distanceTool.disable()
              } else {
    
    
                _t.distanceTool.enable()
              }
            }
          },
          {
    
    
            item: '放大',
            click: () => {
    
    
              map.setZoom(_t.zoom += 1)
            }
          },
          {
    
    
            item: '缩小',
            click: () => {
    
    
              map.setZoom(_t.zoom -= 1)
            }
          },
          {
    
    
            item: '旋转',
            click: () => {
    
    
              map.setBearing(_t.bearing -= 50)
            }
          },
          {
    
    
            item: '重置',
            click: () => {
    
    
              _t.mapDataReset(map)
            }
          },
          {
    
    
            item: '锁定',
            click: (t) => {
    
    
              if (t.target.item === '锁定') {
    
    
                map.setOptions({
    
    
                  // 可拖动
                  draggable: false,
                  // 平移
                  dragPan: false,
                  // 旋转
                  dragRotate: false,
                  // 间距
                  dragPitch: false,
                  // 滚动缩放
                  scrollWheelZoom: false,
                  // 点击 缩放
                  touchZoom: false,
                  // 双击缩放
                  doubleClickZoom: false
                })
                t.target.item = '取消锁定'
              } else {
    
    
                map.setOptions({
    
    
                  // 可拖动
                  draggable: true,
                  // 平移
                  dragPan: true,
                  // 旋转
                  dragRotate: true,
                  // 间距
                  dragPitch: true,
                  // 滚动缩放
                  scrollWheelZoom: true,
                  // 点击 缩放
                  touchZoom: true,
                  // 双击缩放
                  doubleClickZoom: true
                })
                t.target.item = '锁定'
              }
            }
          }
        ]
      }).addTo(map)
    },

Efecto:

imagen-20220108163237187

La función anterior es para iniciar el modo de medición de distancia después de hacer clic en [Rango], pero en algunos casos, haga clic en el botón una vez y mida una vez. Esto también se puede lograr aquí. maptalks proporciona una API de este tipo, solo necesita estar en las opciones Agregar el " onece" y configúrelo como verdadero.

    /**
     * 测距工具
     * @returns {*}
     */
    addDistanceTool () {
    
    
      return new maptalks.DistanceTool({
    
    
   // ... 省略
        language: 'zh-CN',
          // 只测绘一次
        once: true
      }).addTo(this.mapEngine)
        .on('drawend', p => {
    
    
          console.log('地图坐标:' + p.coordinate)
          console.log('界面坐标:' + p.containerPoint)
          console.log('测量距离:' + p.drawTool.getLastMeasure() + '米')
        })
    }

Si se configura como arriba, en addToolbareste método, no necesitamos modificar, porque el atributo de este onece también está deshabilitado después de una encuesta, por lo que nuestro addToolbarmétodo aún puede extenderse.

herramienta de dibujo

Del mismo modo, puede tomar directamente la demostración oficial y modificarla.


/**
       * 绘制工具
       */
    addDrawTool (layer) {
    
    
      const drawTool = new maptalks.DrawTool({
    
    
        mode: 'Point'
      }).addTo(this.mapEngine)
      // 默认是禁用的,当点击按钮后才能使用
        .disable()

      drawTool.on('drawend', function (param) {
    
    
        layer.addGeometry(param.geometry)
      })

      this.drawTool = drawTool
      return drawTool
    }

   /**
     *增加绘制工具栏
     */
    addDrawToolbar (layer) {
    
    
      const _t = this

      const items = [{
    
    code: 'Point', name: '点'},
        {
    
    code: 'LineString', name: '线'},
        {
    
    code: 'Polygon', name: '几何面'},
        {
    
    code: 'Circle', name: '圆'},
        {
    
    code: 'Ellipse', name: '椭圆'},
        {
    
    code: 'Rectangle', name: '矩形'},
        {
    
    code: 'FreeHandLineString', name: '自由绘制'},
        {
    
    code: 'FreeHandPolygon', name: '任意几何面'}]
        .map(function (value) {
    
    
          return {
    
    
            item: value.name,
            click: function () {
    
    
              _t.drawTool.setMode(value.code).enable()
            }
          }
        })
      new maptalks.control.Toolbar({
    
    
        position: {
    
    
          top: 100,
          right: 50
        },
        items: [
          {
    
    
            item: '绘制工具',
            children: items
          },
          {
    
    
            item: '禁用',
            click: function () {
    
    
              _t.drawTool.disable()
            }
          },
          {
    
    
            item: '清除',
            click: function () {
    
    
              layer.clear()
            }
          }
        ]
      }).addTo(this.mapEngine)
    },

Efecto:

imagen-20220108210804183

Herramienta de medición de superficies

Otra herramienta es también una herramienta más para salvar las apariencias:

  /**
     * 测面工具
     */
    addAreaTool () {
    
    
      const areaTool = new maptalks.AreaTool({
    
    
        once: true,
        // 请看 symbol属性说明文档:https://github.com/maptalks/maptalks.js/wiki/Symbol-Reference
        symbol: {
    
    
          lineColor: '#1bbc9b',
          lineWidth: 2,
          polygonFill: '#fff',
          polygonOpacity: 0.3
        },
        vertexSymbol: {
    
    
          // 绘制的marker类型,也就是简单形状,支持:ellipse cross x diamond bar square triangle pin pie
          markerType: 'ellipse',
          // 绘制的marker的填充色
          markerFill: '#34495e',
          // 绘制的线的颜色
          markerLineColor: '#1bbc9b',
          // 绘制的线的宽度
          markerLineWidth: 3,
          // 绘制的marker大小
          markerWidth: 10,
          markerHeight: 10
        },
        // 文本标签属性
        labelOptions: {
    
    
          textSymbol: {
    
    
            // 文本呈现的样式,默认是: monospace, 当你设置 textFont时会覆盖这个属性
            textFaceName: 'monospace',
            // 文本填充色(字体颜色)
            textFill: '#fff',
            // 行距
            textLineSpacing: 1,
            // 对齐方式
            textHorizontalAlignment: 'right',
            // 文本标签与marker的距离,也就是与打点的位置的距离
            textDx: 15
          },
          boxStyle: {
    
    
            // 标签的padding, 第一个值是左右的padding,第二个是上下的padding
            padding: [6, 2],
            symbol: {
    
    
              markerType: 'square',
              markerFill: '#000',
              markerFillOpacity: 0.9,
              markerLineColor: '#b4b3b3'
            }
          }
        },
        clearButtonSymbol: [{
    
    
          markerType: 'square',
          markerFill: '#000',
          markerLineColor: '#b4b3b3',
          markerLineWidth: 2,
          markerWidth: 15,
          markerHeight: 15,
          markerDx: 22
        }, {
    
    
          markerType: 'x',
          markerWidth: 10,
          markerHeight: 10,
          markerLineColor: '#fff',
          markerDx: 22
        }],
        language: 'zh-CN'
      }).addTo(this.mapEngine)
      // 默认关闭
      areaTool.disable()
      this.areaTool = areaTool
      return areaTool
    }

En la barra de herramientas de dibujo, agregue este menú;

 {
    
    
            item: '测面工具',
            click: function () {
    
    
              if (_t.areaTool.isEnabled()) {
    
    
                _t.areaTool.disable()
              } else {
    
    
                _t.areaTool.enable()
              }
            }
          },

Agregue también un menú en la barra de herramientas de dibujo, y luego tendrá el siguiente efecto:

imagen-20220108212851554

animación de mapa

Si, tan pronto como se abre su página, la cámara gira lentamente de arriba a abajo, gira 360 grados y luego ubica el designado como, y luego aparece el ícono, tal efecto debe poder capturar la mayoría de los corazones.

La api también se proporciona en maptalks, simplemente la llamamos:

  /**
     * 地图动画
     */
    mapAnimate () {
    
    
      const _t = this
      _t.mapEngine.animateTo({
    
    
        center: [118.13245430046891, 24.495713873147764],
        zoom: 13,
        pitch: 0,
        bearing: 20
      }, {
    
    
        duration: 5000
      })
      setTimeout(function () {
    
    
        _t.mapEngine.animateTo({
    
    
          center: [118.087828, 24.462059],
          zoom: 14,
          pitch: 65,
          bearing: 360
        }, {
    
    
          duration: 7000
        })
      }, 5000)
    }

Nuestro software no puede cortar la animación, solo ejecuta el código para esto;

Por lo general, se traen marcadores y también se animan, y la combinación de los dos es perfecta.

Entonces, aquí anoto las funciones de la escuela y el hospital hechas arriba, y las combino:

 /**
     * 地图动画
     */
    mapAnimate () {
    
    
      const _t = this
      _t.mapEngine.animateTo({
    
    
        center: [118.13245430046891, 24.495713873147764],
        zoom: 13,
        pitch: 0,
        bearing: 20
      }, {
    
    
        duration: 5000
      })
      setTimeout(function () {
    
    
        _t.mapEngine.animateTo({
    
    
          center: [118.087828, 24.462059],
          zoom: 14,
          pitch: 65,
          bearing: 360
        }, {
    
    
          duration: 7000
        })
      }, 5000)
        // 在地图动画块结束时,撒点动画加入,并设置默认值
      setTimeout(function () {
    
    
        _t.checkList = []
        _t.checkList.push('学校')
        _t.handleCheckChange(_t.checkList)
      }, 9000)
    }

Agregué un botón de reproducción, que se puede reproducir repetidamente, eh. Sin software, sin gifs.

polimerización

También hay que decir la función de agregación, todos los diseños de mapas para hacer zoom tendrán esta función.

Es un poco difícil confiar solo en maptalks, pero la ventaja de usar maptalks es que tiene muchos complementos. También usamos complementos para lograr toda esta función de agregación:

文档:GitHub - maptalks/maptalks.markercluster: Una capa de maptalks para agrupar marcadores.

Importar el complemento:

npm instalar maptalks.markercluster

Úselo de la siguiente manera;

import * as maptalks from 'maptalks'
import {
    
    ClusterLayer} from 'maptalks.markercluster'

Modifiquemos la página de marcador que escribimos antes:

La capa que creamos esta vez es un grupo de marcadores, que será un poco diferente. Solo cambiamos el método de creación de la capa, y he agregado los comentarios correspondientes. Será más claro cuando observe el código.

No escribí todos los símbolos aquí. Uno es copiar el sitio web oficial directamente y el otro es ser perezoso. Es igual que otros símbolos y se puede copiar.

// 先创建图层
 // 创建学校和医院的mark图层
      // new maptalks.VectorLayer('schoolMark').addTo(_t.mapEngine)
      // new maptalks.VectorLayer('hospitalMark').addTo(_t.mapEngine)
      const symbol = {
    
    
        // 聚合最大半径
        maxClusterRadius: 160,
        // 聚合的最大缩放比例,也就是当缩放到某一个层级时,进行聚合
        maxClusterZoom: 19,
        // 是否开启动画,默认true(开启)
        animation: true,
        // 动画时长
        animationDuration: 30,
        // textSumProperty: '',
        // 这里的属性,可以看官网,都是统一的
        // symbol: {},
        textSymbol: {
    
    
          // 文本呈现的样式,默认是: monospace, 当你设置 textFont时会覆盖这个属性
          textFaceName: 'monospace',
          // 字体大小
          textSize: 16
        }
      }
      new ClusterLayer('schoolMark', symbol).addTo(this.mapEngine)
      new ClusterLayer('hospitalMark', symbol).addTo(this.mapEngine)

addMarkerModificación del método:

 drawMark (centerPointList, layer) {
    
    
      if (!centerPointList) {
    
    
        console.log('无区域中心点数据')
        return
      }
      const info = {
    
    content: '', width: 150, minHeight: 100}
      const result = []
      // 这里 d 的数据格式是数组,如:[-0.113049, 51.498568]
      centerPointList.forEach(d => {
    
    
        if (!d.info) {
    
    
          d.info = info
        }
        // 设有高度、高亮的mark
        const mark = new maptalks.Marker(d.center, {
    
    
          symbol: {
    
    
            markerType: 'square',
            markerFile: logo,
            markerWidth: 10,
            markerHeight: 0,
            markerDx: 0,
            markerDy: 0,
            markerOpacity: 1
          },
          properties: {
    
    
            // 高度设置
            altitude: 0
          }
        })
        mark.setInfoWindow({
    
    
          title: d.name,
          content: '<div>' + d.adcode + '</div>',
          // autoPan: true,
          width: d.info.width,
          minHeight: d.info.minHeight
        })

        mark.animate({
    
    
          symbol: {
    
    
            markerWidth: 40,
            markerHeight: 40
          },
          properties: {
    
    
            altitude: 800
          }
        }, {
    
    
          duration: 150
        }, function (frame) {
    
    
          if (frame.state.playState === 'finished') {
    
    
            console.log('animation finished')
          }
        })

        mark.setZIndex(1000)
        result.push(mark)
      })
      layer.addMarker(result)
      return result
    },

El efecto es el siguiente:

imagen-20220110225340346

estilo de mapa base

El diseño del mapa convencional no es bueno, hará que el espectador se canse mucho, así que puedes cambiar el estilo.

Esto es relativamente simple, es un atributo:cssFilter

  /**
       * 初始化地图
       */
    initMap () {
    
    
      const _t = this
      return new maptalks.Map('map', {
    
    
        // 默认中心点点位
        center: _t.center,
        // 缩放层级
        zoom: _t.zoom,
        // 倾斜度
        pitch: _t.pitch,
        // 最小缩放层级
        minZoom: 1,
        // 最大缩放层级
        maxZoom: 18,
        baseLayer: new maptalks.TileLayer('base', {
    
    
          // 电子地图图层
          urlTemplate: _t.urlTemplate,
          subdomains: _t.subdomains,
          attribution: _t.attribution,
          cssFilter: 'sepia(100%) invert(90%)'
        })
      })
    },

Proporciona dos:

imagen-20220110230801619

Efecto:

imagen-20220110230946871

mapa de calor

Mapa de calor, que está integrado con heatmap.js

Agregar complemento de capa térmica

npm instalar maptalks.heatmap

El método de adición también es uno de sus métodos. Sus datos son una matriz de coordenadas. Solo agregue sus propias variables. Aquí lo haremos.

// 获取热力点
const h1 = []
for (let i = 0; i < simingAreaData.length; i++) {
    
    
    const s = simingAreaData[i]
    s.pointList.forEach(ss => {
    
    
        ss.forEach(sss => {
    
    
            sss.push(i + '100')
            h1.push(sss)
        })
    })
}
// 创建图层
new HeatLayer('heat', h1, {
    
    
    // 热力比例(我的理解是,在0-1之间,也就是0-100%,一个热力点,的热力程度是多少
    heatValueScale: 0.7,
    // 旋转时强制刷新
    forceRenderOnRotating: true,
    // 移动时强制刷新
    forceRenderOnMoving: true,
    // 模糊因子, 值越大,越平滑,默认值是15
    // blur: 20,
    // 每个数据点的半径(默认值25,也建议25,我建议不能小于20)
    // radius: 25,
    // 最小不透明读,越小越透明
    // minOpacity: 0.8,
    // 热力梯度,是热力点外围的颜色值,从外围到里,值是递增的,最大值就是中心的位置
    // gradient: {
    
    
    //   0.4: 'blue',
    //   0.6: 'cyan',
    //   0.7: 'lime',
    //   0.8: 'yellow',
    //   1.0: 'red'
    // }
}).addTo(this.mapEngine)

En primer lugar, debemos entender cómo se presenta el calor.

  1. Gradiente hacia afuera desde el centro del punto;
  2. Se pueden superponer múltiples puntos, o se pueden juntar múltiples puntos para presentar una superficie;
  3. La visualización de cada punto es la misma y hay un gradiente térmico, que es un cambio de color de afuera hacia adentro (de claro a oscuro);

Por lo tanto, es diferente de la geometría anterior, la geometría requiere más de 3 puntos y el mapa de calor solo necesita al menos uno:

El código anterior también marca las propiedades térmicas:

  • heatValueScale: el papel de este valor no se entiende bien,
  • forceRenderOnRotating: fuerza la actualización al girar
  • forceRenderOnMoving: fuerza la actualización al moverse
  • desenfoque: factor de desenfoque, cuanto mayor sea el valor, más suave, el valor predeterminado es 15
  • radio: el radio de cada punto de datos (el valor predeterminado es 25, y también se recomienda 25, y recomiendo no menos de 20)
  • minOpacity: la mínima lectura opaca, cuanto más pequeña, más transparente
  • Gradiente: gradiente térmico, que es el valor de color de la periferia del punto térmico.Desde la periferia hacia el interior, el valor aumenta y el valor máximo es la posición del centro

Los valores anteriores tienen configuraciones por defecto, que pueden ser utilizadas directamente o personalizadas;

Interacción del mapa de calor

En respuesta a una pregunta de un amigo, debido al límite de caracteres, lo agregué al artículo.

El mapa de calor en sí se representa mediante el dibujo de puntos, y no hay una interfaz interactiva abierta (aún no la he encontrado), por lo que solo puedo encontrar un método alternativo. Aquí hay dos métodos
:

      // 方法一
      // canvas绘制point,和热力图重合,并添加监听事件,也可以绘制mark,不过它们之间的性能需要分析下
      // 这个方法可以单独对点做弹窗
      // 对应的point文档:https://maptalks.org/maptalks.js/api/1.x/MultiPoint.html#setSymbol
      const h1 = []
      for (let i = 0; i < simingAreaData.simingAreaData.length; i++) {
    
    
        simingAreaData.simingAreaData[i].pointList[0].forEach(d => {
    
    
          h1.push(d)
        })
      }
      var mPoint = new maptalks.MultiPoint(h1, {
    
    
        visible: true,
        // 可交互
        interactive: true,
        cursor: 'pointer',
        // 可拖拽
        draggable: false,
        zIndex: 10,
        symbol: {
    
    
          // 不透明度,这里不设置0是因为,值为0交互会失效
          markerOpacity: 0.1,
          markerFillOpacity: 0.1,
          markerLineOpacity: 0,
          // 填充色,使用热力图相近的颜色
          markerFill: 'red',
          markerType: 'ellipse',
          // 这里保持和热力图(对应heatMap的radiu,而且要大一点,这里我是比热力图的大10)
          markerWidth: 35,
          markerHeight: 35
        }
      })
      mPoint.on('mousemove', function (e) {
    
    
        e.target.openInfoWindow(e.coordinate)
      }).on('mouseout', function (e) {
    
    
        e.target.closeInfoWindow()
      }).setInfoWindow({
    
    
        content: 'hello world',
        title: 'message',
        animationDuration: 0,
        autoOpenOn: false
      })
      new maptalks.VectorLayer('pointLayer', mPoint).addTo(this.mapEngine)


// 方法二
      // 用其他可交互图形替代,使用几何图形,这个要求是对真个热力图都是一样的弹窗,将整个热力图作为一个整体
      // 这里我用的几何图形,有太多限制,不太符合要求,但可以作为一个思路参考,
      const geometry = maptalks.GeoJSON.toGeometry(simingAreaData.geoData)
      if (geometry) {
    
    
        geometry.forEach(g => {
    
    
          g.setSymbol({
    
    
            // 线色
            lineColor: '#f298bc',
            // 线宽
            // 这里保持和热力图(对应heatMap的radiu,而且要大一点)
            lineWidth: 30,
            // 不透明度
            polygonOpacity: 0,
            // 圆角设置
            lineJoin: 'round'
          })
          g.on('mousemove', function (e) {
    
    
            e.target.openInfoWindow(e.coordinate)
          }).on('mouseout', function (e) {
    
    
            e.target.closeInfoWindow()
          }).setInfoWindow({
    
    
            content: 'hello world',
            title: 'message',
            animationDuration: 0,
            autoOpenOn: false
          })
        })
      }
      const options = {
    
    
        enableAltitude: true,
        zIndex: 10
      }
      new maptalks.VectorLayer('lineLayer', options).addGeometry(geometry).addTo(this.mapEngine)

El efecto del primer método:
inserte la descripción de la imagen aquí

Demostración del efecto del segundo método:
inserte la descripción de la imagen aquí

3D - tres.js

Además de usar los echarts anteriores, también hay un marco de desarrollo front-end tridimensional 3.js. Está sincronizado con el funcionamiento del mapa y se siente mucho más simple;

La documentación después de maptalks integra tres: maptalks.tres/API.ZH-CN.md en master maptalks/maptalks.tres GitHub

npm instalar maptalks.tres

// Solo instala la versión 128 aquí, otras versiones reportarán un error

npm instalar [email protected]

crear tablero

maptalks integra three.js, y algunos de ellos están personalizados para maptalks, pero son básicamente lo mismo que 3. Solo mirando la documentación de maptalks.tres no hará ningún progreso. Si necesita crear un efecto particularmente poderoso, puede debería leerlo La documentación oficial de tres.

 drawArea() {
    
    
     const _t = this
	 const layer = new ThreeLayer('three')
     // 图层三维设置
     layer.setOptions({
    
    
         // 地图移动、缩放、旋转时强制渲染
         forceRenderOnMoving: true,
         forceRenderOnZooming: true,
         forceRenderOnRotating: true,
         zIndex: 10
     })
	
     layer.prepareToDraw = function(gl, scene) {
    
    
         _t.create3DPlan2(scene, layer)
     }

     _t.mapEngine.addLayer(layer)

 },
     
  /**
             * 创建3D板块
             * @param scene
             * @param layer
             * @returns {ExtrudePolygons}
             */
     create3DPlan(scene, layer) {
    
    
         // 创建直线光
         const light =  new THREE.DirectionalLight(0xffffff)
         light.position.set(0, -10, 10).normalize()
         scene.add(light)
         // 创建环境光
         scene.add(new THREE.AmbientLight(0xffffff, 0.2))

         // 创建材质
         var material = new THREE.MeshLambertMaterial({
    
     color: cc.defaultPolygonFillColor, opacity: 0.7 })

         const list = []
         const polygons = maptalks.GeoJSON.toGeometry(geoJson)
         polygons.forEach(p => {
    
    
             // 该方法是旧版本方法,官方推荐使用toExtrudePolygon
             var mesh = layer.toExtrudeMesh(p, 400, material)

             if (Array.isArray(mesh)) {
    
    
                 scene.add.apply(scene, mesh)
             } else {
    
    
                 scene.add(mesh.object3d)
             }
             // infowindow test
             mesh.setInfoWindow({
    
    
                 content: '<br style="color:#f00">中心点:' + p.properties.center + ' </br>行政区划:' + p.properties.adcode + ' </br>父级行政区划:' + p.properties.parent.adcode + '</div>',
                 title: 'message',
                 animationDuration: 0,
                 autoOpenOn: false
             })
             list.push(mesh)
         })
         // 批量添加到图层
         layer.addMesh(list)
     },
         
    create3DPlan2(scene, layer) {
    
    
        // 创建直线光
        const light =  new THREE.DirectionalLight(0xffffff)
        light.position.set(0, -10, 10).normalize()
        scene.add(light)
        // 创建环境光
        scene.add(new THREE.AmbientLight(0xffffff, 0.2))

        // 创建材质
        var material = new THREE.MeshLambertMaterial({
    
     color: cc.defaultPolygonFillColor, opacity: 0.7 })

        const options = {
    
    
            altitude: 1,
            height: 400,
            bottomHeight: 0,
            topColor: null,
            bottomColor: cc.defaultPolygonFillColor,
            interactive: true
        }

        const list = []
        const polygons = maptalks.GeoJSON.toGeometry(geoJson)
        polygons.forEach(p => {
    
    
            // 这里源码中,可以找到toExtrudePolygons,但是创建的得到的MultiExtrudePolygons无法正常添加到scence中,所以这里还是用它推荐的方法
            var mesh = layer.toExtrudePolygon(p, options, material)
            if (Array.isArray(mesh)) {
    
    
                scene.add.apply(scene, mesh)
            } else {
    
    
                scene.add(mesh)
            }

            mesh.setInfoWindow({
    
    
                title: p.properties.name,
                content: '<br style="color:#f00">中心点:' + p.properties.center + ' </br>行政区划:' + p.properties.adcode + ' </br>父级行政区划:' + p.properties.parent.adcode + '</div>',
                animationDuration: 0,
                autoOpenOn: false
            })
            list.push(mesh)
        })

        // 批量添加到图层
        layer.addMesh(list)
    }

Hay dos formas de extruir objetos escritos anteriormente: toExtrudeMeshDe toExtrudePolygonhecho, hay otra forma. En el código fuente, toExtrudePolygonsesto puede pasar en una matriz, pero en el uso real, obtiene un objeto y la ejecución scene.add(mesh)informará un error, lo que resultará en El resultado es una superficie, no un objeto 3D.

El efecto es el siguiente:

imagen-20211023225451890

Ahora se ha generado el bloque del mapa, y todavía es un poco feo, como dibujar el área, dividirla en bloques y luego agregar eventos del mouse.

Bloquear

Esto es para agregar una línea divisoria al objeto generado. Puede usar el método anterior para agregar otra capa para generar una superficie. La transparencia de la superficie se establece en 0, la estructura alámbrica, el ancho 1 y un ventilador macho, OK

 drawLine() {
    
    
     const geometry = maptalks.GeoJSON.toGeometry(geoJson)
     if (geometry) {
    
    
         geometry.forEach(g => {
    
    
             g.setSymbol({
    
    
                 // 线色
                 lineColor: '#f298bc',
                 // 线宽
                 lineWidth: 1,
                 // 不透明度
                 polygonOpacity: 0
             })
         })
     }
     new maptalks.VectorLayer("line").addGeometry(geometry).addTo(this.mapEngine)
 }

Veamos el efecto

imagen-20211024134514871

Esto esta pegado en el mapa, es un poco feo, pongamos otra elevacion

drawLine() {
    
    
    const geometry = maptalks.GeoJSON.toGeometry(geoJson)
    if (geometry) {
    
    
        geometry.forEach(g => {
    
    
            g.setSymbol({
    
    
                // 线色
                lineColor: '#f298bc',
                // 线宽
                lineWidth: 1,
                // 不透明度
                polygonOpacity: 0
            })
            // 添加高程
            g.setProperties({
    
    
                // 高度设置(对应挤压的高度height)
                altitude: 400
            })
        })
    }
    const options = {
    
    
        enableAltitude: true,
        zIndex: 9
    }
    new maptalks.VectorLayer("line", options).addGeometry(geometry).addTo(this.mapEngine)
}

Efecto:

imagen-20211024135517544

evento

Documento de objeto creado por tres: maptalks.tres/API.ZH-CN.md en master maptalks/maptalks.tres GitHub

Se puede ver que la adaptación es bastante buena.

onEl método de configuración es el mismo que el del plano de área, pero hay un problema. Veamos primero el código, y el monitoreo también se agrega en el método anterior.

create3DPlan2(scene, layer) {
    
    
                // 创建直线光
                const light =  new THREE.DirectionalLight(0xffffff)
                light.position.set(0, -10, 10).normalize()
                scene.add(light)
                // 创建环境光
                scene.add(new THREE.AmbientLight(0xffffff, 0.2))

                // 创建材质
                var material = new THREE.MeshLambertMaterial({
    
     color: cc.defaultPolygonFillColor, opacity: 0.7 })

                const options = {
    
    
                    altitude: 1,
                    height: 400,
                    bottomHeight: 0,
                    topColor: null,
                    bottomColor: cc.defaultPolygonFillColor,
                    interactive: true
                }

// 定义高亮材质
                const highlightmaterial = new THREE.MeshBasicMaterial({
    
     color: '#ff87a4', transparent: true, opacity: 0.8 })

                const list = []
                const polygons = maptalks.GeoJSON.toGeometry(geoJson)
                polygons.forEach(p => {
    
    
                    // 这里源码中,可以找到toExtrudePolygons,但是创建的得到的MultiExtrudePolygons无法正常添加到scence中,所以这里还是用它推荐的方法
                    var mesh = layer.toExtrudePolygon(p, options, material)
                    if (Array.isArray(mesh)) {
    
    
                        scene.add.apply(scene, mesh)
                    } else {
    
    
                        scene.add(mesh)
                    }

                    mesh.setInfoWindow({
    
    
                        title: p.properties.name,
                        content: '<br style="color:#f00">中心点:' + p.properties.center + ' </br>行政区划:' + p.properties.adcode + ' </br>父级行政区划:' + p.properties.parent.adcode + '</div>',
                        animationDuration: 0,
                        autoOpenOn: false
                    })
                    
                    // 添加鼠标事件
                    mesh.on('mouseover', function(e) {
    
    
                        e.target.setSymbol(highlightmaterial)
                        layer.redraw()
                      }).on('mouseout', function(e) {
    
    
                        e.target.setSymbol(material)
                        layer.redraw()
                    })
                    list.push(mesh)
                })
                // 批量添加到图层
                layer.addMesh(list)
            },

Como podéis ver, los he añadido al evento layer.redraw(), ¿por qué?

No hay mención de esto en los documentos y ejemplos oficiales. Siempre pensé que se renderizaría automáticamente como gráficos vectoriales, pero no fue así. Además, siempre se renderizaba inexplicablemente. Más tarde, descubrí que cada vez que muevo el map, o Sus gráficos solo cambiarán cuando se gire, lo que me recuerda que la capa vectorial tiene una configuración para forzar el renderizado cuando se mueve y gira, que es muy similar a la situación actual, y luego miro su documentación nuevamente, y allí no se menciona el método de renderizado, pero lo que hace feliz a la gente es que la capa de tres se hereda CanvasLayery tiene todos sus métodos. En este punto, el problema está resuelto, solo llame para redraw()volver a dibujar la capa de tres.

Una diferencia es que en maptalks.tres, setSymbolel parámetro es el objeto material pasado

Mira las representaciones:

imagen-20211024141815638

Dibuja 3D sin la ayuda de otros complementos

Sin usar three.js y echarts, también puede construir una estructura 3D a través de su propia superficie. Puede ser más claro si ha utilizado un modelo 3D. En el software 3D, la superficie se puede extruir, de modo que pueda obtener un poliedro. , los tres aquí es la razón.La forma más tradicional es combinar caras en el espacio tridimensional, que también se puede realizar en maptalks.

Modifica ligeramente el código anterior:

   /**
             * 再图层上画面
             */
            drawArea() {
    
    
                const options = this.getOptions()

                const lowSymbol = {
    
    }
                const topSymbol = {
    
    }
                Object.assign(lowSymbol, options.symbol)
                Object.assign(topSymbol, options.symbol)

                const lowOptions = {
    
    
                    zIndex: 9,
                    symbol: lowSymbol,
                    properties: {
    
    
                        altitude: 0
                    }
                }

                const topOptions = {
    
    
                    zIndex: 11,
                    symbol: topSymbol,
                    properties: {
    
    
                        altitude: 400
                    }
                }

                const _t = this

                const layer = new maptalks.VectorLayer('vector')
                // 创建底面
                _t.drawAreaPlan(_t.geoJson, layer, lowOptions)
                // 高面
                const centerCoordinate = _t.drawAreaPlan(_t.geoJson, layer, topOptions, true)

                const lineOptions = {
    
    
                    zIndex: 10,
                    symbol: options.symbol,
                    properties: {
    
    
                        altitude: 400
                    }
                }
                const lineCoordinate = []
                if (geoJson.type === 'FeatureCollection') {
    
    
                    geoJson.features.forEach(c => {
    
    
                        lineCoordinate.push(c.geometry.coordinates[0])
                    })
                }
                lineCoordinate.forEach(l => {
    
    
                  const line = new maptalks.LineString(l[0], lineOptions).addTo(layer)
                })

                _t.drawMark(centerCoordinate, layer)
                layer.setOptions({
    
    
                    // 启用高度绘制
                    enableAltitude: true,
                    altitudeProperty: 'altitude',
                    // 绘制高度线
                    drawAltitude: {
    
    
                        // lineWidth: 1,
                        // lineColor: '#000',
                        // 高度面的填充色
                        polygonFill: options.symbol.polygonFill,
                        // 高度面的透明度
                        polygonOpacity: options.symbol.polygonOpacity
                    }
                })
                layer.addTo(_t.mapEngine)
            },

            /**
             * 根据geojson画区域面
             * @param geoJson geoJson数据
             * @param layer 需要话的图层
             */
            drawAreaPlan(geoJson, layer, options, isActions) {
    
    
                const geometry = maptalks.GeoJSON.toGeometry(geoJson)

                const centerCoordinates = []

                if (geometry) {
    
    
                    geometry.forEach(g => {
    
    
                        g.setSymbol(options.symbol)
                        const gProperties = g.properties
                        // 避免信息覆盖
                        gProperties.altitude = options.properties.altitude
                        g.setProperties(gProperties)
                        // 设置图形层级
                        g.setZIndex(options.zIndex)
                        // 设置信息框
                        g.setInfoWindow({
    
    
                            title: g.properties.name,
                            content: '<br style="color:#f00">中心点:' + g.properties.center + ' </br>行政区划:' + g.properties.adcode + ' </br>父级行政区划:' + g.properties.parent.adcode + '</div>'
                        })
                        g.setMenu({
    
    
                            width: 160,
                            custom: false,
                            items: [
                                {
    
     item: '菜单一', click: function() {
    
     alert('Query Clicked!'); return false } },
                                '-',
                                {
    
     item: '菜单二', click: function() {
    
     alert('Edit Clicked!') } },
                                {
    
     item: '菜单三', click: function() {
    
     alert('About Clicked!') } }
                            ]
                        })
                        // 鼠标交互事件监听
                        g.on('mouseenter', function(e) {
    
    
                            e.target.updateSymbol({
    
    
                                polygonFill: '#ffaeb0'
                            })
                        }).on('mouseout', function(e) {
    
    
                            e.target.updateSymbol({
    
    
                                polygonFill: 'rgb(135,196,240)'
                            })
                        })

                        if (isActions) {
    
    
                            g.on('click', function(e) {
    
    
                                e.target.openInfoWindow(e.coordinate)
                            });
                        }

                        // 获取中心坐标
                        centerCoordinates.push(g.properties)
                    })
                }
                layer.addGeometry(geometry)

               return centerCoordinates
            },
// mark
            drawMark(centerPointList, layer) {
    
    
                if (!centerPointList) {
    
    
                    console.log('无区域中心点数据')
                    return
                }
                const info = {
    
     content: '', width: 150, minHeight: 100 }
                const result = []
                // 这里 d 的数据格式是数组,如:[-0.113049, 51.498568]
                centerPointList.forEach(d => {
    
    
                    if (!d.info) {
    
    
                        d.info = info
                    }
                    // 设有高度、高亮的mark
                    const mark = new maptalks.Marker(d.center, {
    
    
                        // 设置了这个属性,会替换默认的图标
                        // symbol: {
    
    
                        // markerFile: 'foo.png',
                        // textName: d.name
                        // },
                        properties: {
    
    
                            // 高度设置
                            altitude: 400
                        }
                    }).addTo(layer)
                    mark.setInfoWindow({
    
    
                        title: d.name,
                        content: '<div>' + d.adcode + '</div>',
                        // autoPan: true,
                        width: d.info.width,
                        minHeight: d.info.minHeight,
                        // 'custom': false,
                        // 点击打开和关闭
                        // autoOpenOn: 'click',
                        // autoCloseOn: 'click'
                    })

                    // 没有高度的mark
                    // new maptalks.Marker(d).updateSymbol({
    
    
                    //   markerOpacity: 0.5,
                    //   markerFill: '#bbb'
                    // }).addTo(layer)

                    mark.setZIndex(1000)
                    result.push(mark)
                })
                return result
            },

            getOptions() {
    
    
                return {
    
    
                    zIndex: 10,
                    symbol: {
    
    
                        // 线色
                        lineColor: '#ff87a4',
                        // 线宽
                        lineWidth: 1,
                        // 填充色
                        polygonFill: 'rgb(135,196,240)',
                        // 不透明度
                        polygonOpacity: 0.9,
                    },
                    properties: {
    
    
                        // 高度设置(不生效,需要设置图层的高度polygonOpacity属性)
                        altitude: 400
                    }
                }
            }

El efecto es el siguiente:

Aquí solo estoy brindando ideas. Para decirlo sin rodeos, el efecto de implementación de este método no es tan bueno como el de tres.

imagen-20211030152616191.png

Supongo que te gusta

Origin blog.csdn.net/qq_28911061/article/details/122515860
Recomendado
Clasificación