SymbolLayer
This layer is used to load point information and allows setting properties such as icon and text. It also supports point clustering and offers rich functionality. Generally, this layer should be used for adding points to the map.
Non-Clustered Example
Selected Element Information:
lng: lat:
properties:{}
Example Source Code
<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>
Clustered Example
Example Source Code
<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>
Point Displacement Example (Spiderfy)
Zoom in on the map. After the clustered points can no longer be separated by zooming, you can click on the cluster point, and the clustered points will spread out in a certain shape.
Preview
Example Source Code
<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>
Animation Example
Example Source Code
<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 Example
Example Source Code
<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 | Layer ID | string | - |
cluster | Whether to enable clustering | boolean | false |
cluster-icons | Configuration information for cluster icons. See example for details | IndexAny[] | 见示例说明 |
cluster-max-zoom | The maximum zoom level at which clustering is enabled | number | 14 |
cluster-min-points | The minimum number of points required for clustering | number | 2 |
cluster-radius | Clustering radius in pixels | number | 50 |
cluster-icon-size | Icon scaling factor | number | 1 |
cluster-icon-translate | Icon anchor translation. Positive values shift right/down | number[] | [0,0] |
cluster-icon-offset | Distance between the icon and its anchor. Positive values shift right/down | number[] | [0,0] |
cluster-icon-anchor | Icon anchor position | string | center |
data | Data for the vector layer. Each object in the array must contain the required coordinates property and the optional properties property. Each object represents a point/line/polygon | SymbolLayerData | - |
geo-json-data-source | GeoJSON data source. Can be a GeoJSON data link or a GeoJSON data object. Specification Link. If this property is set, the data property is ignored. | string / GeoJSONSource | - |
icon-allow-overlap | Whether to allow icon overlap | boolean | false |
icon-anchor | Icon anchor position | string("center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right") | center |
icon-color | Icon color. Only effective for SDF icons | string | #000000 |
icon-image | Icon resource. Pre-loaded image name | string / AnyArr | - |
icon-image-field | Icon image field name. Will be read from the dataSource properties, overriding the iconImage property | string | - |
icon-offset | Distance between the icon and its anchor. Positive values shift right/down | number[] | [0,0] |
icon-opacity | Icon opacity | number | 1 |
icon-optional | If true, the icon will only be displayed if it doesn't collide with other icons, but the text does not collide with other text | boolean | false |
icon-rotate | Icon rotation angle | number | 0 |
icon-rotation-alignment | - | string | auto |
icon-ignore-placement | - | boolean | false |
icon-size | Icon scaling factor | number | 1 |
icon-translate | Icon anchor translation. Positive values shift right/down | number[] | [0,0] |
maxzoom | Maximum zoom level | number | 22 |
minzoom | Minimum zoom level | number | 0 |
pickable | Whether the layer responds to pick events. If false, the component will not emit mouse-related events | boolean | true |
show | Whether to show the layer | boolean | true |
source-id | ID of the layer's source. If this ID is set, the geoJsonDataSource and data properties are ignored | string | - |
source-layer-name | Layer name in the source data. This property is effective when sourceId is set | string | - |
text | Text value. Only effective if textFiled is not specified | string | - |
text-allow-overlap | Whether to allow text overlap | boolean | false |
text-anchor | Text anchor position | string("center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right") | center |
text-color | Text color | string | #000000 |
text-field | Text field value/key. Will be taken from the dataSource properties | string | - |
text-font | Text font | string[] | ["Open Sans SemiBold"] |
text-halo-blur | Text halo blur effect in pixels | number | 0 |
text-halo-color | Text halo color | string | rgba(0, 0, 0, 0) |
text-halo-width | Text halo width in pixels | number | 0 |
text-letter-spacing | Text letter spacing in em units | number | 0 |
text-line-height | Line height in em units | number | 1.2 |
text-max-width | Maximum text width in em units | number | 10 |
text-offset | Distance between the text and its anchor. Positive values shift right/down in pixels | number[] | [0,0] |
text-opacity | Text opacity | number | 1 |
text-optional | If true, the icon will be displayed only if the text collides with other text but the icon does not collide with other icons | boolean | false |
text-size | Text font size in pixels | number | 16 |
text-translate | Text anchor translation. Positive values shift right/down in pixels | number[] | [0,0] |
text-padding | Text padding | number | 2 |
spiderify | Whether points with the same latitude and longitude can be spread out (spiderified) | boolean | false |
max-leafes-tospiderify | Maximum number of points to spiderify | number | 50 |
distance-between-spider-points | Distance between spiderified points | number | 50 |
spider-leg-color | Color of the lines connecting the spiderified points | string | #B6CBED |
spider-leg-width | Width of the lines connecting the spiderified points | number | 3 |
compare-precision | The number of decimal places used when comparing clustered points for equality. Decimal places beyond comparePrecision will be truncated | number | 5 |
cluster-spiral-points | The shape of the cluster expansion. If greater than this value, the expansion is spiral; if less than or equal to this value, the expansion is circular | number | 10 |
TIP
The clusterIcons
property allows you to configure icons for clusters. You can set the iconName
property to use images loaded by the ImageLoader
. Alternatively, you can set the functionName
and functionProps
properties to use built-in custom graphics within the ImageLoader
. Refer to the ImageLoader
documentation for details on the built-in functions. It also supports using custom functions for functionName
; refer to the functionName
method within the ImageLoader
for implementation details.
Default value:
[
{
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
Name | Description | Parameters |
---|---|---|
created | Map initialization completed event | - |
click | Layer click event | object — Contains screen coordinates (pixel), longitude/latitude (coordinate), selected element properties, and originalEvent |
clusterclick | Click event for non-drillable cluster points in layer | object — Contains screen coordinates (pixel), longitude/latitude (coordinate), cluster data, and originalEvent |
mousemove | Mouse move event | object — Contains screen coordinates (pixel), longitude/latitude (coordinate), selected element properties, and originalEvent |
mouseenter | Mouse enter event | object — Contains screen coordinates (pixel), longitude/latitude (coordinate), selected element properties, and originalEvent |
mouseleave | Mouse leave element event | MapMouseEvent |
SLOTS
Name | Description |
---|
METHODS
Name | Description | Definition |
---|---|---|
exportToGeoJson | Translate the layer's data to GeoJSON formatted text. If the layer's data source comes from a source component or a URL, output null. | ()=> object | null |