图层
创建矢量图层
createVectorLayer
点我查看代码
vue
<script lang="ts" setup>
import { createVectorLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Feature, Map as OLMap } from 'ol'
import { LineString, Point, Polygon } from 'ol/geom'
const olMap = new OLMap()
const { source, layer } = createVectorLayer()
olMap.addLayer(layer)
const point = new Feature({
geometry: new Point([120, 30]),
})
source.addFeature(point)
const line = new Feature({
geometry: new LineString([[120.1, 30.1], [120.2, 30.2]]),
})
source.addFeature(line)
const polygon = new Feature({
geometry: new Polygon([[[120.11, 30.15], [120.15, 30.25], [120.1, 30.2], [120.11, 30.15]]]),
})
source.addFeature(polygon)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[120, 30]" :zoom="10" class="w-100% h-400px" />
</template>添加 OpenStreetMap 地图
createOpenStreetMapLayer
点我查看代码
vue
<script lang="ts" setup>
import { createOpenStreetMapLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Map as OLMap } from 'ol'
const olMap = new OLMap()
const osmLayer = createOpenStreetMapLayer()
olMap.addLayer(osmLayer)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[120, 30]" :zoom="10" class="w-100% h-400px" />
</template>添加天地图
createTianDiTuLayer
点我查看代码
vue
<script lang="ts" setup>
import { createTianDiTuLayer, EPSG_3857, EPSG_4326, OlMap } from '@summeruse/ol'
import { Map as OLMap } from 'ol'
const olMap = new OLMap()
const layer = createTianDiTuLayer({
type: 'img',
key: '8a684acb7b9d38ba08adf8035d0262ee',
projection: EPSG_3857,
})
const label = createTianDiTuLayer({
type: 'cia',
key: '8a684acb7b9d38ba08adf8035d0262ee',
projection: EPSG_3857,
})
olMap.addLayer(layer)
olMap.addLayer(label)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[120, 30]" :zoom="10" class="w-100% h-300px" />
</template>添加 Bing 地图
createBingLayer
点我查看代码
vue
<script lang="ts" setup>
import { createBingLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Map as OLMap } from 'ol'
const olMap = new OLMap()
const bingLayer = createBingLayer({
key: 'AtmBUmOPFg6c61ynLhIbjvrKfuXkMw1lCMTlLh9ALY47Llyetb6lgyRMitoPxKZo',
name: 'RoadOnDemand',
})
olMap.addLayer(bingLayer)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[120, 30]" :zoom="10" class="w-100% h-300px" />
</template>添加 XYZ 图层
createXYZLayer — 通用 XYZ 瓦片图层,适用于任何标准 XYZ 瓦片服务。
点我查看代码
vue
<script lang="ts" setup>
import { createXYZLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Map as OLMap } from 'ol'
const olMap = new OLMap()
// 使用 CartoDB 浅色底图(免费,无需 key)
const xyzLayer = createXYZLayer({
sourceOptions: {
url: 'https://a.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
projection: EPSG_4326,
},
})
olMap.addLayer(xyzLayer)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[120, 30]" :zoom="10" class="w-100% h-400px" />
</template>添加 PMTiles 图层
createPMTilesLayer — 加载 PMTiles 格式的矢量瓦片。
点我查看代码
vue
<script lang="ts" setup>
import { createPMTilesLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Map as OLMap } from 'ol'
const olMap = new OLMap()
// 使用 protomaps 公开示例 PMTiles 文件(佛罗伦萨矢量地图)
const pmtilesLayer = createPMTilesLayer({
url: 'https://air.mtn.tw/flowers.pmtiles',
})
olMap.addLayer(pmtilesLayer)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[121.525398, 25.014998]" :zoom="13" class="w-100% h-400px" />
</template>添加 WebGL 矢量图层
createWebGLVectorLayer — 使用 GPU 渲染的大数据量矢量图层,样式通过 FlatStyle 表达式定义。
点我查看代码
vue
<script lang="ts" setup>
import { createWebGLVectorLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Feature, Map as OLMap } from 'ol'
import { Point } from 'ol/geom'
const olMap = new OLMap()
interface PointData {
name: string
color: string
size: number
}
const features: Feature<Point>[] = [
{ name: '北京', lon: 116.39, lat: 39.91, color: '#e74c3c', size: 20 },
{ name: '上海', lon: 121.47, lat: 31.23, color: '#3498db', size: 16 },
{ name: '广州', lon: 113.26, lat: 23.13, color: '#2ecc71', size: 14 },
{ name: '成都', lon: 104.07, lat: 30.57, color: '#f39c12', size: 14 },
{ name: '武汉', lon: 114.30, lat: 30.60, color: '#9b59b6', size: 12 },
].map((d) => {
return new Feature<Point>({
geometry: new Point([d.lon, d.lat]),
name: d.name,
color: d.color,
size: d.size,
})
})
const { source, layer } = createWebGLVectorLayer({
style: {
'circle-radius': ['get', 'size'],
'circle-fill-color': ['get', 'color'],
'circle-stroke-color': '#ffffff',
'circle-stroke-width': 2,
'circle-opacity': 0.85,
},
sourceOptions: {
features,
},
})
olMap.addLayer(layer)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[115, 32]" :zoom="5" class="w-100% h-400px" />
</template>添加 Canvas 图层
createCanvasLayer — 通过 refresh 回调在 Canvas 上逐帧绘制,适合自定义渲染、动画覆盖层等场景。
点我查看代码
vue
<script lang="ts" setup>
import { createCanvasLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Map as OLMap } from 'ol'
const olMap = new OLMap()
// 使用 Canvas 图层绘制一个简单的标注覆盖层
createCanvasLayer(olMap, (frameState) => {
const canvas = new OffscreenCanvas(300, 100)
const ctx = canvas.getContext('2d')!
// 背景
ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'
ctx.beginPath()
ctx.roundRect(0, 0, 280, 70, 8)
ctx.fill()
// 文字
ctx.fillStyle = '#ffffff'
ctx.font = '16px sans-serif'
ctx.fillText('Canvas 图层示例', 16, 30)
ctx.fillStyle = '#aabbcc'
ctx.font = '12px sans-serif'
ctx.fillText('通过 refresh 回调逐帧绘制', 16, 52)
return {
imageBitmap: canvas.transferToImageBitmap(),
dpi: window.devicePixelRatio,
}
})
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[120, 30]" :zoom="10" class="w-100% h-400px" />
</template>添加热力图
createHeatmapLayer — 热力图图层,数据点为 Feature,通过 weight 字段控制权重。
点我查看代码
vue
<script lang="ts" setup>
import { createHeatmapLayer, EPSG_4326, OlMap } from '@summeruse/ol'
import { Feature, Map as OLMap } from 'ol'
import { Point } from 'ol/geom'
const olMap = new OLMap()
// 生成随机热力点
const points: Feature<Point>[] = []
// 中心区域密集
for (let i = 0; i < 50; i++) {
const lon = 120 + (Math.random() - 0.5) * 2
const lat = 30 + (Math.random() - 0.5) * 2
const feature = new Feature({
geometry: new Point([lon, lat]),
weight: Math.random(),
})
points.push(feature)
}
// 外围稀疏点
for (let i = 0; i < 20; i++) {
const lon = 120 + (Math.random() - 0.5) * 5
const lat = 30 + (Math.random() - 0.5) * 5
const feature = new Feature({
geometry: new Point([lon, lat]),
weight: Math.random() * 0.5,
})
points.push(feature)
}
const { layer, source } = createHeatmapLayer({
blur: 20,
radius: 15,
weight: 'weight',
})
source.addFeatures(points)
olMap.addLayer(layer)
</script>
<template>
<OlMap :ol-map :projection="EPSG_4326" :center="[120, 30]" :zoom="10" class="w-100% h-400px" />
</template>API
底图图层
| 名称 | 类型 | 说明 |
|---|---|---|
| createOpenStreetMapLayer | (data?: CreateOpenStreetMapLayerOptions) => TileLayer | 创建 OpenStreetMap 瓦片图层 |
| createTianDiTuLayer | (data: CreateTianDiTuLayerOptions) => TileLayer | 创建天地图瓦片图层 |
| createTianDiTuUrl | (data: CreateTianDiTuUrlOptions) => string | 生成天地图瓦片 URL |
| createBingLayer | (options: CreateBingLayerOptions) => TileLayer | 创建 Bing 地图瓦片图层 |
| createXYZLayer | (options: XYZLayerOptions) => TileLayer | 创建通用 XYZ 瓦片图层 |
| createPMTilesLayer | (config: PMTilesLayerOptions) => TileLayer | 创建 PMTiles 矢量瓦片图层 |
矢量图层
| 名称 | 类型 | 说明 |
|---|---|---|
| createVectorSource | <T>(options?: VectorSourceOptions<T>) => VectorSource | 创建矢量数据源 |
| createVectorLayer | <T>(options?: VectorLayerOptions<T>) => { source, layer } | 创建矢量图层,返回 { source, layer } |
| createWebGLVectorLayer | <T>(options: WebGLVectorLayerOptions<T>) => { source, layer } | 创建 WebGL 矢量图层,使用 FlatStyle,返回 { source, layer } |
| createHeatmapLayer | (options?: createHeatmapLayerOptions) => { layer, source } | 创建热力图图层,返回 { layer, source } |
画布图层
| 名称 | 类型 | 说明 |
|---|---|---|
| createCanvasLayer | (olMap: OLMap, refresh: (frameState) => ..., options?: CanvasLayerOptions) => { layer } | 创建 Canvas 图层,通过 refresh 回调逐帧绘制 |
主要类型
| 类型 | 说明 |
|---|---|
T_MAP_TYPE | 'vec'|'cva'|'img'|'cia'|'ter'|'cta'|'ibo' 天地图图层类型 |
CreateTianDiTuUrlOptions | 天地图 URL 配置,含 type、key、projection 等 |
CreateTianDiTuLayerOptions | 天地图图层配置,继承 CreateTianDiTuUrlOptions,额外含 layerOptions、sourceOptions |
CreateBingLayerOptions | Bing 图层配置,含 name(影像集名称)、key、layerOptions、sourceOptions |
CreateOpenStreetMapLayerOptions | OSM 图层配置,含 layerOptions、sourceOptions |
TileLayerOptions | OpenLayers TileLayer 构造参数类型 |
XYZ_SourceOptions | OpenLayers XYZ 构造参数类型 |
XYZLayerOptions | XYZ 图层配置,含 sourceOptions 和 TileLayerOptions |
BingMapsSourceOptions | OpenLayers BingMaps 构造参数类型 |
OpenStreetMapSourceOptions | OpenLayers OSM 构造参数类型 |
VectorSourceOptions<T> | VectorSource 构造参数类型 |
VectorLayerOptions<T> | 矢量图层配置,含 styleOptions、sourceOptions 快捷字段 |
WebGLVectorLayerOptions<T> | WebGL 矢量图层配置,必须含 style: FlatStyleLike,可选 sourceOptions |
PMTilesLayerOptions | PMTiles 配置,含 url 和 sourceOptions |
CanvasLayerOptions | Canvas 图层配置(OpenLayers Layer 构造参数) |
createHeatmapLayerOptions | 热力图配置,含 sourceOptions 和 HeatmapLayerOptions |
HeatmapLayerOptions | OpenLayers HeatmapLayer 构造参数类型 |
源代码
点我查看代码
ts
import type { Feature } from 'ol'
import type { Geometry } from 'ol/geom'
import type { FrameState } from 'ol/Map'
import type { FlatStyleLike } from 'ol/style/flat'
import type { OLMap } from '@/types'
import type { StyleOptions } from '@/utils/style'
import { Layer, Tile as TileLayer } from 'ol/layer'
import HeatmapLayer from 'ol/layer/Heatmap'
import VectorLayer from 'ol/layer/Vector'
import WebGLVectorLayer from 'ol/layer/WebGLVector'
import { BingMaps, OSM, Source, XYZ } from 'ol/source'
import ImageTileSource from 'ol/source/ImageTile'
import VectorSource from 'ol/source/Vector'
import { PMTiles } from 'pmtiles'
import { EPSG_3857 } from '@/constants/projection'
import { createStyle } from '@/utils/style'
import { createTileGrid } from '../projection'
export type T_MAP_TYPE = 'vec' | 'cva' | 'img' | 'cia' | 'ter' | 'cta' | 'ibo'
export interface CreateTianDiTuUrlOptions {
url?: string
key: string
type: T_MAP_TYPE
projection?: 'EPSG:3857' | 'EPSG:4326'
}
export function createTianDiTuUrl(data: CreateTianDiTuUrlOptions) {
const { type = 'img', projection = EPSG_3857, key } = data
const url = data.url || `https://t{0-4}.tianditu.gov.cn`
const suffix
= `&tk=${key
// cSpell:disable-next-line
}&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}`
const proj = projection === EPSG_3857 ? 'w' : 'c'
return `${url}/${type}_${proj}/wmts?LAYER=${type}${suffix}`
}
export type TileLayerOptions = Partial<ConstructorParameters<typeof TileLayer>[0]>
export type XYZ_SourceOptions = Partial<ConstructorParameters<typeof XYZ>[0]>
export type CreateTianDiTuLayerOptions = CreateTianDiTuUrlOptions & {
layerOptions?: TileLayerOptions
sourceOptions?: XYZ_SourceOptions
}
export function createTianDiTuLayer(data: CreateTianDiTuLayerOptions) {
const { layerOptions, sourceOptions, ...options } = data
return new TileLayer({
source: new XYZ({
url: createTianDiTuUrl(options),
projection: options.projection,
crossOrigin: 'Anonymous',
...sourceOptions,
}),
...layerOptions,
})
}
export type BingMapsSourceOptions = Partial<ConstructorParameters<typeof BingMaps>[0]>
export interface CreateBingLayerOptions {
name: string
key: string
layerOptions?: TileLayerOptions
sourceOptions?: BingMapsSourceOptions
}
export function createBingLayer({
name,
key,
layerOptions,
sourceOptions,
}: CreateBingLayerOptions) {
const layer = new TileLayer({
source: new BingMaps({
key,
imagerySet: name,
placeholderTiles: false,
...sourceOptions,
}),
...layerOptions,
})
return layer
}
export type OpenStreetMapSourceOptions = Partial<ConstructorParameters<typeof OSM>[0]>
export interface CreateOpenStreetMapLayerOptions {
layerOptions?: TileLayerOptions
sourceOptions?: OpenStreetMapSourceOptions
}
export function createOpenStreetMapLayer(data?: CreateOpenStreetMapLayerOptions) {
const { layerOptions, sourceOptions } = data || {}
const layer = new TileLayer({
source: new OSM({
crossOrigin: 'Anonymous',
...sourceOptions,
}),
...layerOptions,
})
return layer
}
export type _VectorSourceOptions<T extends Geometry = Geometry> = ConstructorParameters<typeof VectorSource<Feature<T>>>[0]
export type VectorSourceOptions<T extends Geometry = Geometry> = _VectorSourceOptions<T>
export function createVectorSource<T extends Geometry = Geometry>(options?: VectorSourceOptions<T>) {
return new VectorSource<Feature<T>>({
...options,
})
}
export type _VectorLayerOptions<T extends Geometry = Geometry> = ConstructorParameters<typeof VectorLayer<VectorSource<Feature<T>>>>[0]
export type VectorLayerOptions<T extends Geometry = Geometry> = _VectorLayerOptions<T> & {
styleOptions?: StyleOptions
sourceOptions?: VectorSourceOptions<T>
}
export function createVectorLayer<T extends Geometry = Geometry>(options?: VectorLayerOptions<T>) {
const { styleOptions, sourceOptions, style: _style, source: _source, ...restOptions } = options || {}
const style = _style || (styleOptions ? createStyle(styleOptions) : undefined)
const source = _source || createVectorSource<T>(sourceOptions)
const layer = new VectorLayer({
...restOptions,
source,
style,
})
return {
source,
layer,
}
}
export type _WebGLVectorLayerOptions<T extends Geometry = Geometry> = ConstructorParameters<typeof WebGLVectorLayer<VectorSource<Feature<T>>>>[0]
export type WebGLVectorLayerOptions<T extends Geometry = Geometry> = _WebGLVectorLayerOptions<T> & {
style: FlatStyleLike
sourceOptions?: VectorSourceOptions<T>
}
export function createWebGLVectorLayer<T extends Geometry = Geometry>(options: WebGLVectorLayerOptions<T>) {
const { style, source: _source, sourceOptions, ...restOptions } = options
const source = _source || createVectorSource<T>(sourceOptions)
const layer = new WebGLVectorLayer({
...restOptions,
source,
style,
})
return {
source,
layer,
}
}
export type ImageTileSourceOptions = ConstructorParameters<typeof ImageTileSource>[0]
export type PMTilesLayerOptions = TileLayerOptions & {
url: string
sourceOptions?: ImageTileSourceOptions
}
function loadImage(src: string) {
return new Promise<HTMLImageElement>((resolve, reject) => {
const img = new Image()
img.addEventListener('load', () => resolve(img))
img.addEventListener('error', () => reject(new Error('load failed')))
img.src = src
})
}
export function createPMTilesLayer(config: PMTilesLayerOptions) {
const { url, sourceOptions, ...layerOptions } = config
const tiles = new PMTiles(url)
const tileGrid = createTileGrid(sourceOptions?.projection)
return new TileLayer({
...layerOptions,
source: new ImageTileSource({
async loader(z, x, y, { signal }) {
const response = await tiles.getZxy(z, x, y, signal)
const blob = new Blob([response!.data])
const src = URL.createObjectURL(blob)
const image = await loadImage(src)
URL.revokeObjectURL(src)
return image
},
crossOrigin: 'anonymous',
tileGrid,
...sourceOptions,
}),
})
}
export type XYZLayerOptions = TileLayerOptions & {
sourceOptions: XYZ_SourceOptions
}
export function createXYZLayer({ sourceOptions, ...layerOptions }: XYZLayerOptions) {
const tileGrid = createTileGrid(sourceOptions?.projection)
return new TileLayer({
...layerOptions,
source: new XYZ({
crossOrigin: 'anonymous',
...sourceOptions,
tileGrid,
}),
})
}
export type CanvasLayerOptions = ConstructorParameters<typeof Layer>[0]
export function createCanvasLayer(olMap: OLMap, refresh: (frameState: FrameState) =>
| {
imageBitmap: ImageBitmap
dpi: number
}
| boolean
| undefined, options?: CanvasLayerOptions) {
const container = document.createElement('div')
container.style.position = 'absolute'
container.style.width = '100%'
container.style.height = '100%'
const _canvas = document.createElement('canvas')
_canvas.style.width = '100%'
_canvas.style.height = '100%'
container.appendChild(_canvas)
const ctx = _canvas.getContext('2d')!
const config = {
size: [0, 0],
dpi: window.devicePixelRatio,
}
const source = new Source({})
const layer = new Layer({
render: (frameState) => {
const size = frameState.size
const res = refresh(frameState)
if (res === false) {
return container
}
if (res === true || !res) {
ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.clearRect(0, 0, _canvas.width, _canvas.height)
return container
}
if (res) {
const { imageBitmap, dpi } = res
if (dpi !== config.dpi || size[0]! !== config.size[0] || size[1]! !== config.size[1]) {
config.size = [size[0]!, size[1]!]
config.dpi = dpi
_canvas.width = size[0]! * dpi
_canvas.height = size[1]! * dpi
}
ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.clearRect(0, 0, _canvas.width, _canvas.height)
ctx.drawImage(imageBitmap, 0, 0, _canvas.width, _canvas.height)
imageBitmap.close()
}
return container
},
source,
...options,
})
olMap.addLayer(layer)
return { layer }
}
export type HeatmapLayerOptions = ConstructorParameters<typeof HeatmapLayer>[0]
export type createHeatmapLayerOptions = HeatmapLayerOptions & {
sourceOptions?: VectorSourceOptions
}
export function createHeatmapLayer(options?: createHeatmapLayerOptions) {
const { sourceOptions, ...restOptions } = options || {}
const source = createVectorSource(sourceOptions)
const layer = new HeatmapLayer({
...restOptions,
source,
})
return { layer, source }
}