Skip to content

符号图层(SymbolLayer)

用来加载点位信息,可设置icon以及text等属性,另还可实现点聚合功能,功能丰富。一般情况下,地图打点应该使用此图层。

非聚合示例

Selected Element Information:

lng: lat:

properties:{}



示例源码
vue
<template>
  <div style="height: 500px">
    <div style="height: 400px">
      <mb-map
        :zoom="mapZoom"
        :center="mapCenter"
        :glyphs="__RESOURCE_URL__ + 'fonts/{range}.pbf'"
        :sprite="__RESOURCE_URL__ + 'sprites/sprite'"
        @created="mapCreatedHandler"
      >
        <mb-tianditu-layer :types="['vec']" />
        <mb-symbol-layer
          :data="symbolDataSource"
          text-field="label"
          icon-image-field="icon"
          text-halo-color="#9e9e9e"
          :text-halo-width="1"
          :text-offset="[0, -1]"
          :cluster="false"
          @mousemove="clickLayerHandler"
          @mouseleave="leaveLayerHandler"
        />
      </mb-map>
    </div>
    <div style="height: 60px; margin: 10px 10px">
      <p>Selected Element Information:</p>
      <p class="text-muted">lng:{{ lng }} lat:{{ lat }}</p>
      <p class="text-muted">properties:{{ JSON.stringify(properties) }}</p>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { Map } from 'mapbox-gl'

const symbolDataSource = ref([])
const lng = ref('')
const lat = ref('')
const properties = ref<Record<string, any>>({})
const mapCenter = [107.5, 37.5]
const mapZoom = 3.5

function getRandomArbitrary(min: number, max: number) {
  return Math.random() * (max - min) + min
}
const mapCreatedHandler = (map: Map) => {
  map.rotateTo(180, { duration: 10000 })
}
const clickLayerHandler = (payload) => {
  lng.value = payload.coordinates[0]
  lat.value = payload.coordinates[1]
  properties.value = payload.properties
}
const leaveLayerHandler = () => {
  lng.value = ''
  lat.value = ''
  properties.value = {}
}

onMounted(() => {
  const data = []
  for (let i = 0; i < 2000; i++) {
    data.push({
      coordinates: [getRandomArbitrary(95, 120), getRandomArbitrary(30, 42)],
      properties: {
        label: `Airport-${i}`,
        icon: 'airport-15',
      },
    })
  }
  symbolDataSource.value = data
})
</script>

聚合示例

示例源码
vue
<template>
  <div style="height: 400px" class="vw-full vh-full">
    <mb-map
      :zoom="4"
      :center="[107.5, 37.5]"
      :glyphs="__RESOURCE_URL__ + 'fonts/{range}.pbf'"
      :sprite="__RESOURCE_URL__ + 'sprites/sprite'"
    >
      <mb-tianditu-layer :types="['vec']" />
      <mb-symbol-layer
        :data="symbolDataSource"
        text-field="label"
        icon-image-field="icon"
        :text-offset="[0, -1]"
        :cluster="true"
        :spiderify="true"
      />
    </mb-map>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'

const symbolDataSource = ref([])

function getRandomArbitrary(min: number, max: number) {
  return Math.random() * (max - min) + min
}

onMounted(() => {
  const data = []
  for (let i = 0; i < 2000; i++) {
    data.push({
      coordinates: [getRandomArbitrary(95, 120), getRandomArbitrary(30, 42)],
      properties: {
        label: `Airport-${i}`,
        icon: 'airport-15',
      },
    })
  }
  for (let i = 0; i < 13; i++) {
    data.push({
      coordinates: [116.678, 31.456],
      properties: {
        label: `same-${i}`,
        icon: 'airport-15',
      },
    })
  }
  symbolDataSource.value = data
})
</script>

点避让示例

放大地图,在聚合点不能在散开以后,可以点击聚合点,聚合的点将以某个形状散开。

效果图

Building Layer

示例源码
vue
<template>
  <div style="height: 400px" class="vw-full vh-full">
    <mb-map
      :zoom="5"
      :center="[116.678, 31.456]"
      :glyphs="__RESOURCE_URL__ + 'fonts/{range}.pbf'"
      :sprite="__RESOURCE_URL__ + 'sprites/sprite'"
    >
      <mb-tianditu-layer :types="['vec']" />
      <mb-image-loader :images="images" />
      <mb-symbol-layer
        :data="symbolDataSource"
        text-field="label"
        icon-image-field="icon"
        :cluster-icons="clusterIcons"
        :cluster-icon-size="0.5"
        :cluster-icon-offset="[1, 5]"
        :cluster="true"
        :spiderify="true"
      />
      <mb-symbol-layer
        :data="symbolDataSource2"
        text-field="label"
        icon-image-field="icon"
        :cluster-icons="clusterIcons2"
        :cluster-icon-size="0.5"
        :cluster-icon-offset="[1, 45]"
        :cluster="true"
        :spiderify="true"
      />
    </mb-map>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
const images = [
  {
    name: 'cluster-icon',
    type: 'link',
    url: `${__RESOURCE_URL__}images/common_toget.png`,
  },
  {
    name: 'cluster-icon2',
    type: 'link',
    url: `${__RESOURCE_URL__}images/eme_team_soc_toget.png`,
  },
]
const clusterIcons = [
  {
    textColor: 'white',
    textSize: 12,
    iconName: 'cluster-icon',
  },
]
const clusterIcons2 = [
  {
    textColor: 'white',
    textSize: 16,
    iconName: 'cluster-icon2',
  },
]
const symbolDataSource = ref([])
const symbolDataSource2 = ref([])

onMounted(() => {
  const data = [],
    data2 = []
  for (let i = 0; i < 13; i++) {
    data.push({
      coordinates: [116.678, 31.456],
      properties: {
        label: `same-${i}`,
        icon: 'airport-15',
      },
    })
    data2.push({
      coordinates: [113.788, 31.956],
      properties: {
        label: `same2-${i}`,
        icon: 'airport-15',
      },
    })
  }
  for (let i = 0; i < 5; i++) {
    data.push({
      coordinates: [116.688, 31.456],
      properties: {
        label: `same-2-${i}`,
        icon: 'airport-15',
      },
    })
    data2.push({
      coordinates: [113.798, 31.956],
      properties: {
        label: `same-2-${i}`,
        icon: 'airport-15',
      },
    })
  }
  symbolDataSource.value = data
  symbolDataSource2.value = data2
})
</script>

动画示例

示例源码
vue
<template>
  <div style="height: 400px" class="vw-full vh-full">
    <mb-map
      :zoom="11"
      :center="[116.32, 39.9]"
      :glyphs="__RESOURCE_URL__ + 'fonts/{range}.pbf'"
      :sprite="__RESOURCE_URL__ + 'sprites/sprite'"
    >
      <div style="position: absolute; top: 5px; left: 5px">
        <button class="primary" @click="start">Restart</button>
      </div>
      <mb-tianditu-layer :types="['vec']" />
      <mb-image-loader :images="images" />
      <mb-polyline-layer :data="polylineCoordinates" :width="3" color="blue" />
      <mb-symbol-layer
        :data="symbolDataSource"
        icon-image-field="icon"
        icon-anchor="center"
        :icon-size="0.5"
        icon-rotation-alignment="map"
        icon-allow-overlap
        :icon-rotate="['get', 'bearing']"
        icon-ignore-placement
      />
    </mb-map>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import * as turf from '@turf/turf'

const images = [
  {
    name: 'cluster-icon',
    type: 'link',
    url: `${__RESOURCE_URL__}images/common_toget.png`,
  },
  {
    name: 'cluster-icon2',
    type: 'link',
    url: `${__RESOURCE_URL__}images/eme_team_soc_toget.png`,
  },
]
let counter = 0
const steps = 9
const polylineCoordinates = [
  {
    coordinates: [
      [116.28, 39.91],
      [116.29, 39.91],
      [116.32, 39.92],
      [116.33, 39.91],
      [116.32, 39.9],
      [116.32, 39.89],
      [116.3, 39.89],
      [116.29, 39.89],
      [116.27, 39.9],
    ],
  },
]
const symbolDataSource = ref([
  {
    coordinates: [116.28, 39.91],
    properties: {
      label: ``,
      icon: 'cluster-icon',
      iconSize: 0.5,
      bearing: 0,
    },
  },
])

const start = () => {
  counter = 0
  animate()
}
const animate = () => {
  const start =
    polylineCoordinates[0].coordinates[counter >= steps ? counter - 1 : counter]
  const end =
    polylineCoordinates[0].coordinates[counter >= steps ? counter : counter + 1]
  if (!start || !end || counter > steps) {
    counter = 0
    return
  }
  // Update point geometry to a new position based on counter denoting
  // the index to access the arc
  symbolDataSource.value[0].coordinates =
    polylineCoordinates[0].coordinates[counter]

  // Calculate the bearing to ensure the icon is rotated to match the route arc
  // The bearing is calculated between the current point and the next point, except
  // at the end of the arc, which uses the previous point and the current point
  symbolDataSource.value[0].properties.bearing = turf.bearing(
    turf.point(start),
    turf.point(end)
  )

  // Request the next frame of animation as long as the end has not been reached
  if (counter < steps) {
    // requestAnimationFrame(this.animate);
    window.setTimeout(animate, 1500)
  }

  counter++
}
</script>

Popup示例

示例源码
vue
<template>
  <div style="height: 400px">
    <mb-map
      :zoom="mapZoom"
      :center="mapCenter"
      :glyphs="__RESOURCE_URL__ + 'fonts/{range}.pbf'"
      :sprite="__RESOURCE_URL__ + 'sprites/sprite'"
      @created="mapCreatedHandler"
    >
      <mb-tianditu-layer :types="['vec']" />
      <mb-symbol-layer
        :data="symbolDataSource"
        icon-image-field="icon"
        :cluster="false"
      />
      <mb-map-popup
        v-for="s in symbolDataSource"
        :key="s.properties.id"
        :coordinates="s.coordinates"
        :offset="[0, -10]"
        :show-tip="false"
        anchor="bottom"
      >
        <div
          style="
            padding: 4px;
            background-color: white;
            box-shadow: 10px 5px 10px grey;
          "
        >
          {{ s.properties.label }}
        </div>
      </mb-map-popup>
    </mb-map>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { Map } from 'mapbox-gl'

const symbolDataSource = ref([])
const mapCenter = [107.5, 37.5]
const mapZoom = 3.5

function getRandomArbitrary(min: number, max: number) {
  return Math.random() * (max - min) + min
}
const mapCreatedHandler = (map: Map) => {
  map.rotateTo(180, { duration: 10000 })
}

onMounted(() => {
  const data = []
  for (let i = 0; i < 10; i++) {
    data.push({
      coordinates: [getRandomArbitrary(95, 120), getRandomArbitrary(30, 42)],
      properties: {
        id: String(Math.random()),
        label: `Airport-${i}`,
        icon: 'airport-15',
      },
    })
  }
  symbolDataSource.value = data
})
</script>

API

PROPS

名称描述类型默认值
id图层idstring-
cluster是否聚合booleanfalse
cluster-icons聚合图层图标配置信息IndexAny[]见示例说明
cluster-max-zoom聚合效果可用的最大缩放级别number14
cluster-min-points聚合效果最小聚合点数number2
cluster-radius聚合半径,单位像素number50
cluster-icon-sizeIcon放大倍数number1
cluster-icon-translateIcon锚点平移量,正方向为右/下number[][0,0]
cluster-icon-offsetIcon与其锚点的距离 正向为右/下number[][0,0]
cluster-icon-anchorIcon锚点位置stringcenter
data矢量图层的数据 数组中每个对象需包含必需的coordinates属性以及可选的properties属性 每一个对象代表一个点/一条线/一个多边形SymbolLayerData-
geo-json-data-sourcegeojson数据源,可设为geojson数据链接或geojson数据对象;规范链接, 若设置了本属性,则data属性失效string / GeoJSONSource-
icon-allow-overlap是否允许重叠booleanfalse
icon-anchor锚点位置string("center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right")center
icon-color图标颜色,仅在sdf icons生效string#000000
icon-imageIcon资源,预加载好的图片名string / AnyArr-
ico-image-fieldIcon图片字段名,将从dataSource properties中读取 覆盖iconImage属性string-
icon-offsetIcon与其锚点的距离 正向为右/下number[][0,0]
icon-opacityIcon透明度number1
icon-optional若为true,则icon与其他icon产生碰撞但Text未碰撞时,将只显示textbooleanfalse
icon-rotateIcon旋转角度number0
icon-rotation-alignment-stringauto
icon-ignore-placement-booleanfalse
icon-size图标放大倍数number1
icon-translate锚点平移量,正方向为右/下number[][0,0]
maxzoom最大缩放级别number22
minzoom最小缩放级别number0
pickable图层是否响应拾取事件,若为false,则组件不会emit鼠标相关事件booleantrue
show是否显示booleantrue
source-id图层Source的id,若设置了本ID,则geoJsonDataSource与data属性都会失效string-
source-layer-name源数据中图层名称 设置sourceId时,此属性生效string-
text文本值,仅在textFiled未指定时生效string-
text-allow-overlap是否允许重叠booleanfalse
text-anchor锚定位置string("center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right")center
text-color字体颜色string#000000
text-field文本字段值/键值,将从dataSource properties中取值string-
text-font文字字体 Open Sans SemiBoldarray["Open Sans SemiBold"]
text-halo-blur文本边框炫光效果,以像素为单位number0
text-halo-color文本边框颜色stringrgba(0, 0, 0, 0)
text-halo-width文本边框宽度,像素为单位number0
text-letter-spacing文字间距 em为单位number0
text-line-heightem为单位number1.2
text-max-widthem为单位number10
text-offsettext与其锚点的距离 正向为右/下 pxnumber[][0,0]
text-opacity字体透明度number1
text-optional若为true,则text与其他text产生碰撞但icon未碰撞时,将只显示iconbooleanfalse
text-size文本字号大小,单位pxnumber16
text-translate锚点平移量,正方向为右/下 pxnumber[][0,0]
text-padding文字间距number2
spiderify具有相同经纬度点是否可以展开booleanfalse
max-leafes-tospiderify最大展开数量number50
distance-between-spider-points展开点间距number50
spider-leg-color展开点线颜色string#B6CBED
spider-leg-width展开点线宽度number3
compare-precision聚合点比较相等时的小数位数,大于comparePrecision位的小数会被截掉number5
cluster-spiral-points聚合点形展开形式,大于是螺旋形,小于等于是圆形number10

TIP

clusterIcons 可以设置 iconName 使用 ImageLoader 中加载的图片。也可以设置functionNamefunctionProps 属性,使用ImageLoader中内置自定义的图形,内置函数可参考ImageLoader中详细介绍。也支持functionName为自定义函数,参考ImageLoader中的functionName方法。

默认值为:

js
[
  {
    count: 50,
    textColor: "black",
    textSize: 12,
    functionName: "drawDynamicCircle",
    functionProps: {
      color: "#68B8EE",
      radius: 50,
    },
  },
  {
    count: 100,
    textColor: "black",
    textSize: 14,
    functionName: "drawDynamicCircle",
    functionProps: {
      color: "#B1E345",
      radius: 75,
    },
  },
  {
    count: 200,
    textColor: "white",
    textSize: 18,
    functionName: "drawDynamicCircle",
    functionProps: {
      color: "#F7DC00",
      radius: 100,
    },
  },
  {
    count: 500,
    textColor: "white",
    textSize: 20,
    functionName: "drawDynamicCircle",
    functionProps: {
      color: "#FA9800",
      radius: 125,
    },
  },
  {
    textColor: "white",
    textSize: 22,
    functionName: "drawDynamicCircle",
    functionProps: {
      radius: 150,
      color: "#F34A4A",
    },
  },
]

EVENTS

名称描述参数
created地图初始化完成事件-
click图层单击事件object — 包含屏幕坐标pixel、经纬度coordinate与选中元素的属性properties、originalEvent
clusterclick单击图层不能在下钻聚合点事件object — 包含屏幕坐标pixel、经纬度coordinate与聚合数据data、originalEvent
mousemove鼠标移动事件object — 包含屏幕坐标pixel、经纬度coordinate与选中元素的属性properties、originalEvent
mouseenter鼠标移进事件object — 包含屏幕坐标pixel、经纬度coordinate与选中元素的属性properties、originalEvent
mouseleave鼠标移出元素事件MapMouseEvent

SLOTS

名称描述

METHODS

名称描述定义
exportToGeoJson将本图层数据导出为geojson格式数据文本 若图层数据源来自source组件或为url,则输出null()=> object | null