npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

map-ol-utils

v1.0.0

Published

OpenLayers utilities with browser-side Tianditu private VTS loading and conversion

Readme

map-ol-utils

目录:packages/map-utils | npm 包名:map-ol-utils

基于 OpenLayers 的地图工具库,封装了地图实例管理、底图创建、矢量/面/线/聚合/海量点图层、要素交互选择、视野自适应、WMTS 接入,以及天地图私有 VTS 的浏览器加载与标准 MVT 转换能力。

特性

  • 单例地图实例:模块内维护一个全局 instance,多数工具函数直接操作该单例,无需层层传参。
  • 多种底图:杭州规自瓦片、全国天地图(WMTS)、浙江省天地图(XYZ)。
  • 业务矢量图层:根据业务数据自动推断几何类型(线/面)并生成 GeoJSON 图层,支持着色映射、点击高亮。
  • 海量点 MassMarks:基于状态值的多套样式(默认 + 激活),可附带伴生线/面图层。
  • 聚合图层:基于 ol/source/Cluster,半径/颜色随聚合数量动态计算。
  • 坐标系转换:内部统一通过 gcoord 转换到 WGS84EPSG:4326)。
  • 事件总线:内置 mitt 实例 emitter,用于派发要素选中等事件。
  • 扩展能力:提供天地图组合、栅格着色、标记、绘制量算、GeoJSON 图层和多图层选择能力。
  • 私有 VTS 转换:通过 tdt-vts-core 复用 PK 编解码、私有 Protobuf/MVT 转换和 Web Worker 解码,本包只保留 OpenLayers 适配。

项目结构

packages/map-utils/
├── src/
│   ├── core/                    # 类型、常量、事件总线与地图上下文
│   ├── interaction/             # 绘制和要素选择交互
│   ├── layer/                   # 底图、矢量、聚合、海量点、WMTS 与图层生命周期
│   ├── map/                     # 地图实例管理与视野适配
│   ├── overlay/                 # Overlay 与动画标注
│   ├── tdt-vts/                 # 天地图私有 VTS 的公共重导出与 OL 适配
│   └── index.ts                 # 唯一公共 API 入口
├── test/
│   ├── architecture.test.ts     # 模块边界与架构约束
│   └── compatibility.test.ts    # 公共 API 兼容性测试
├── package.json                 # 包入口、依赖和构建脚本
├── tsconfig.json                # TypeScript 配置
├── tsdown.config.ts             # ESM/CJS 与类型声明构建配置
└── README.md                    # 安装、API 与使用约定

安装

pnpm add map-ol-utils ol gcoord

ol对等依赖(要求 ^10.10.0),需由宿主项目安装。tdt-vts-core 是普通依赖,由包管理器自动安装。

快速开始

import { createMapInstance, createBusinessLayer, emitter } from 'map-ol-utils'

const map = createMapInstance({ center: [120.15, 30.28], zoom: 11 })
map.setTarget('map') // 容器通过 setTarget 绑定,config 仅透传给 ol/View

const layer = createBusinessLayer(dataList, {
  key: 'coordinates',
  isLine: false,
  openClick: true,
  layerName: 'biz-layer',
  fillColor: '#4247DF',
  strokeColor: '#1DEBFF'
})
map.addLayer(layer)

emitter.on('selected-feature', (data) => console.log('选中要素:', data))

天地图私有 VTS

传入 Mapbox/MapLibre Style v8 JSON 地址或已加载的样式对象,创建 OpenLayers 图层:

import { createTdtVtsLayer } from 'map-ol-utils'

const layer = await createTdtVtsLayer({
  token: 'YOUR_TDT_TOKEN',
  hosts: ['https://tile0.tianditu.gov.cn'],
  style: '/styles/tdt-style.json'
})

map.addLayer(layer)
layer.dispose() // 同时释放解码 Worker

hostsstyle 均为必填项。传入地址时,本包会请求并校验 Style v8 JSON,服务端需允许当前页面跨域访问;也可以直接传入包含 sourceslayers 的样式对象。

业务调用只需要 createTdtVtsLayer();底层转换 API 保留用于兼容已有调用。


接口(API)详解

约定:独立工具函数默认操作 createMapInstance() 创建的模块单例;createMap() 返回独立地图,其挂载的图层方法只操作自身。

一、地图实例(map/create-map.ts

createMapInstance(config?)

创建并缓存地图单例;若已存在则直接返回,不会重建。

function createMapInstance(config?: Record<string, any>): CustomMap

| 参数 | 类型 | 默认 | 说明 | | -------- | -------- | ---- | ----------------------------------------- | | config | object | {} | 透传给 createMapol/View 的视图配置 |

返回:CustomMap(OpenLayers Map 的扩展,附带 getLayerByName / removeLayerByName / changeLayersByNames 方法与 activeBaseLayer 字段)。

const map = createMapInstance({ center: [120.15, 30.28], zoom: 11 })
map.setTarget('map')

createMap(config?)

始终新建一个 CustomMap。默认挂载杭州规自暗色底图,禁用旋转/双击缩放,并在 pointermove 时根据是否命中要素切换鼠标指针;挂载的图层查询、切换和删除方法绑定到当前地图。config 与以下默认值合并后作为 ol/View 选项:

function createMap(config?: Record<string, any>): CustomMap
// 默认 View 配置:
// { zoom: 9.4, maxZoom: 18, minZoom: 9.4, center, baseLayer: 'vec-layer',
//   projection: 'EPSG:4326', layers: [createBaseLayer()],
//   constrainResolution: false, smoothExtentConstraint: false, tk }

| 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | config | object | {} | ol/View 配置;可额外传 layers 覆盖默认底图、baseLayer 标记当前底图名 |

const map = createMap({ zoom: 12, center: [121.43, 28.66] })

getMapInstance()

function getMapInstance(): CustomMap

返回当前单例(等价于导出的 instance 变量)。

disposeMap()

function disposeMap(): void

销毁单例:移除全部覆盖物、清空交互、移除并释放(source.dispose() + layer.dispose())所有图层,setTarget('') 后将 instance 置为 undefined。未初始化或重复调用时会安全跳过。

resetMap(config?)

function resetMap(config?: { center?: Coordinate; isClearAll?: boolean }): void
// 默认:{ center: 上次中心, isClearAll: true }

清除所有非底图图层(内部调用 removeAllLayers)并复位缩放与中心。

resetZoomAndCenter(center?)

function resetZoomAndCenter(center?: [number, number]): void // 默认上次中心

动画(1s)复位到默认缩放 9.4 与指定中心。

instance

let instance: CustomMap // 导出变量;未创建时为 undefined

二、底图与图层名管理(layer/base-layer.ts

createBaseLayer(layerName?)

杭州规自瓦片底图(EPSG:4326,XYZ,5 路负载)。

function createBaseLayer(layerName?: HzsyType): Tile
// HzsyType = 'hzsyraster' | 'hzsyvector' | 'hzsyvector_dark'(默认 'hzsyvector_dark')

图层附带属性 { _isBaseLayer: true, _layerName: layerName }zIndex: -5

const base = createBaseLayer('hzsyvector')
map.addLayer(base)

createMapWorldLayer(layerName, tk?)

全国天地图 WMTS 图层。

function createMapWorldLayer(layerName: WorldLayerType, tk?: string): Tile
// WorldLayerType = 'vec_c' | 'cva_c' | 'img_c' | 'cia_c' | 'ter_c' | 'cta_c' | 'ibo_c'

| 参数 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | layerName | WorldLayerType | — | 形如 img_c,内部按 _ 拆分为图层名与 matrixSet | | tk | string | 内置默认令牌 | 天地图访问令牌,建议自行传入 |

map.addLayer(createMapWorldLayer('img_c', '你的天地图令牌'))

createZheJiangLayer(layerName?, tk?)

浙江省天地图 XYZ 图层。

function createZheJiangLayer(layerName?: ZheJiangType, tk?: string): Tile
// ZheJiangType = 'emap' | 'emap_lab' | 'imgmap' | 'imgmap_lab'(默认 'emap')

getLayerByName(name)

function getLayerByName(name: string): Layer<Source> | undefined

按图层 _layerName 属性或 className 查找首个匹配图层。

removeLayerByName(name)

function removeLayerByName(name: string): void

查找匹配图层并释放其数据源、从地图移除并执行 dispose

changeLayersByNames(activeNames, allLayerNames)

function changeLayersByNames(
  activeNames: string[],
  allLayerNames: string[]
): void

allLayerNames 范围内,按 activeNames 是否包含来逐一 setVisible,实现批量显隐切换。

changeLayersByNames(['poi-layer'], ['poi-layer', 'line-layer', 'face-layer'])

三、矢量图层(layer/vector-layer.ts

createBusinessLayer(dataList, config)

核心矢量图层构造函数:将业务数据组装为 GeoJSON(自动推断线/面几何类型),生成默认/激活样式并写入要素属性,可选开启点击交互。

function createBusinessLayer(
  dataList: MassData[],
  config: VectorConfig
): VectorLayer

| config 关键字段 | 类型 | 说明 | | --- | --- | --- | | key | string | 数据中存放坐标数组的字段名 | | isLine | boolean | 是否线图层(影响是否生成填充样式) | | layerName | string? | 图层名(className) | | openClick | boolean? | 是否创建单击选择交互 | | colorKey / colorMap | string? / Map<string,string>? | 按数据字段值映射颜色 | | fillColor / strokeColor | string? | 填充 / 描边色 | | fillOpacity / strokeWeight | number? | 填充透明度 / 线宽 | | activeFillColor / activeStrokeColor / activeStrokeWeight | … | 激活态样式 | | crsForm | CRSTypes? | 源坐标系(默认 WGS84) | | isAutoFit | boolean? | 选中后是否自动居中 |

图层附带属性:_isCustom_isVectorLayer_isAutoFit_vectorConfig_layerName_dataList_uuidSet

const layer = createBusinessLayer(dataList, {
  key: 'coordinates',
  isLine: false,
  openClick: true,
  layerName: 'area',
  colorKey: 'level',
  colorMap: new Map([
    ['1', '#f00'],
    ['2', '#0f0']
  ]),
  fillOpacity: 0.3,
  strokeWeight: 2
})
map.addLayer(layer)

createVectorLineLayer(dataList?, config) / createVectorFaceLayer(dataList?, config)

createBusinessLayer 的便捷包装,分别固定 isLine: true / isLine: false

function createVectorLineLayer(
  dataList?: MassData[],
  config: VectorConfig
): VectorLayer
function createVectorFaceLayer(
  dataList?: MassData[],
  config: VectorConfig
): VectorLayer

appendFeaturesByLayer(targetLayer, dataList, crsForm?)

向已有矢量图层追加要素(同步更新其 _uuidSet),复用图层 _vectorConfig 生成样式;未传 crsForm 时继承图层配置的源坐标系。

function appendFeaturesByLayer(
  targetLayer: VectorLayer,
  dataList: MassData[],
  crsForm?: CRSTypes
): void

四、海量点 MassMarks(layer/mass-marks.ts

buildMassMarksStyles(getImgUrlFn)

高阶函数:先注入「图标地址生成器工厂」,再按 IconConfig 生成「默认 + 激活」两段样式数组(长度 statusNum * 2)。

function buildMassMarksStyles(
  getImgUrlFn: GetImgUrlFn
): (iconConfig: IconConfig) => Style[]

type GetImgUrl = (name: string, ext?: string) => string
type GetImgUrlFn = (baseUrl: string) => GetImgUrl

图标地址按 ${markerIcon}-${i} / ${markerIcon}-${i}-active(后缀 .webp)拼接。

const buildStyles = buildMassMarksStyles(
  (baseUrl) => (name, ext) => `${baseUrl}/${name}${ext}`
)
const styles = buildStyles({
  baseUrl: '/icons',
  markerIcon: 'alarm',
  markerSize: [32, 32],
  statusNum: 5
})

createMassMarksLayer(dataList, styleList?)

function createMassMarksLayer(
  dataList: MassData[],
  styleList?: StyleLike[]
): VectorLayer

item.style 索引在 styleList 中取样式创建要素图层(zIndex: 2000)。图层附带 _isMassMarksLayer_statusNum_dataList_uuidSet 等属性。

addMassMarksLayer(getImgUrlFn)

高阶函数,返回的方法会生成样式、创建海量点图层、按 hasLine / hasFace 附加伴生矢量图层,并自动加入地图。

function addMassMarksLayer(
  getImgUrlFn: GetImgUrlFn
): (
  dataList?: MassData[],
  iconConfig: IconConfig,
  openClick?: boolean
) => VectorLayer

| iconConfig 字段 | 类型 | 说明 | | --- | --- | --- | | baseUrl | string | 图标根地址 | | markerIcon | string | 图标名前缀 | | markerSize | [number, number] | 图标尺寸 | | statusNum | number? | 状态数(默认 5) | | activeScale | number? | 激活放大倍数(默认 1.3) | | hasLine / hasFace | boolean? | 是否附带线 / 面图层 | | isDynamic | boolean? | 是否动态图层(选中派发 dynamic-marker) | | isAutoFit | boolean? | 选中是否自动居中 | | layerName | string? | 图层名 | | offset | [number, number]? | 偏移 | | marksStyles | Style[]? | 直接指定样式(跳过自动生成) | | vectorConfig | VectorConfig? | 伴生线/面图层配置覆盖 |

const add = addMassMarksLayer((baseUrl) => (n, e) => `${baseUrl}/${n}${e}`)
const layer = add(dataList, {
  baseUrl: '/icons',
  markerIcon: 'alarm',
  markerSize: [32, 32],
  statusNum: 5,
  hasLine: true
})

addFeaturesByLayer(targetLayer, dataList, config)

向海量点图层增量添加要素(复用图层 _massMarksStyles)。

function addFeaturesByLayer(
  targetLayer: VectorLayer,
  dataList: MassData[],
  config: { clear: boolean }
): void

config.cleartrue 时先清空数据源再添加。

updateMarksLayersByStatus(checkedStatusList?)

function updateMarksLayersByStatus(checkedStatusList?: string[]): MassData[]

遍历所有海量点图层:status 不在勾选列表中的要素设为隐藏样式,否则恢复默认样式。返回当前保留显示的数据数组。


五、聚合图层(layer/cluster-layer.ts

createClusterLayer(dataList, config)

function createClusterLayer(
  dataList: ClusterData[],
  config: ClusterConfig
): VectorLayer

type ClusterData = {
  _uuid: string
  name: string
  style: number
  lnglat: number[]
  rawData: Record<string, any>
}
type ClusterConfig = {
  distance: number
  minDistance: number
  singleStyles: Style[]
  layerName?: string
  openClick?: boolean
  computedRadius?: (size: number) => number
  buildStyle?: (feature: FeatureLike) => Style
}

未传 buildStyle 时使用内置样式:聚合点半径与色相(hsla)随聚合数量变化;size === 1 时使用 singleStyles

const cluster = createClusterLayer(dataList, {
  distance: 80,
  minDistance: 20,
  singleStyles: [singlePointStyle],
  openClick: true
})
map.addLayer(cluster)

六、交互与选中(interaction/select.ts

createSelectByLayer(vectorLayer)

为指定图层创建 ol/interaction/Select(单击、multi: falsehitTolerance: 10),命中要素时切换激活样式并派发 selected-feature 事件,支持聚合图层单点展开。交互会写入图层属性 _selectInteraction

function createSelectByLayer(vectorLayer: VectorLayer): Select

activeFeatureByUid(uid)

function activeFeatureByUid(uid: string): void

在带 _isVectorLayer 且其 _uuidSet 含该 uid 的图层中,程序化触发对应要素的选中(派发 select 事件并加入选中集合)。

resetActiveFeature() / resetActiveVector()

function resetActiveFeature(): void // 清空 overlay + 触发各矢量图层 deselect
function resetActiveVector(): void // 清空 overlay + 清空各图层选择交互的要素集合

七、覆盖物与视野(overlay/overlay.tsmap/view-fit.ts

createOverlay(config?)

function createOverlay(config?: import('ol/Overlay').Options): Overlay
// 默认:{ id: 'popup-container', positioning: 'top-center' }

未显式传 element 时,按 config.iddocument.getElementById(id) 作为容器。

removeOverlays(key?) / removeAllLayers()

function removeOverlays(key?: string): void // 移除带指定属性键的 overlay(默认按 'position')
function removeAllLayers(): void // 移除并释放所有非底图(无 _isBaseLayer)图层 + overlay

createAnimationMarker(imgUrl, data)

function createAnimationMarker(imgUrl: string, data: MassData): void

data.lnglat(经 data.crsFormWGS84 转换)创建动画标注覆盖物,先移除已有动画标注。data 中读取 width / height / lnglat / crsForm

setFitViewByCoords / setFitViewByGeom / setFitViewByLayer

function setFitViewByCoords(
  coords: Coordinate[],
  mapInstance?: CustomMap,
  fitOptions?: FitOptions | AnimationOptions
): void // 单点时 animate 到 zoom 14,多点时 fit 包围盒

function setFitViewByGeom(
  geom: SimpleGeometry,
  mapInstance?: CustomMap,
  fitOptions?: FitOptions
): void

function setFitViewByLayer(
  layer: VectorLayer,
  mapInstance?: CustomMap,
  fitOptions?: FitOptions
): void // 按图层 source 的 extent 自适应

三者默认 padding: [50,50,50,50]duration: 1000,可由 fitOptions 覆盖。

setFitViewByCoords([
  [120.1, 30.2],
  [120.3, 30.4]
])
setFitViewByLayer(layer, undefined, { duration: 500 })

getLayersDataList()

function getLayersDataList(): any[]

汇总所有图层 _dataList 属性并 flat(2),返回当前地图上的全部业务数据。


八、WMTS(layer/ogc-utils.ts

createWmtsLayer(serverUrl)

柯里化函数:先传 GeoServer 服务地址,返回的方法按 WmtsSourceOptions 解析 GetCapabilities 并构建图层。仅成功响应会进入 capabilities 缓存;HTTP 非成功状态直接抛错,后续调用仍可重试。

function createWmtsLayer(
  serverUrl: string
): (config: WmtsSourceOptions) => Promise<TileLayer>

type WmtsSourceOptions = {
  layerName: string // 形如 'workspace:datastore'
  style?: string
  format?: string // 默认 'image/png'
  version?: string // 默认 '1.1.0'
  matrixSet?: string // 默认 'EPSG:4326'
}
const build = createWmtsLayer('https://geoserver.example.com/geoserver')
const layer = await build({ layerName: 'ws:store', matrixSet: 'EPSG:4326' })
map.addLayer(layer)

九、事件总线(emitter

import { emitter } from 'map-ol-utils' // mitt 实例

| 事件名 | 载荷 | 触发时机 | | --- | --- | --- | | selected-feature | 要素属性对象 | 要素被选中(createSelectByLayer 内触发) | | dynamic-marker | 动画标注配置 | 选中动态图层(_isDynamic)要素 1s 后 |


十、扩展能力

以下 API 补充天地图组合、标记、绘制与 GeoJSON 等能力;createMapInstancecreateOverlaydisposeMap 等既有 API 的签名、默认值和行为保持不变。disposeInstance() 直接复用 disposeMap() 的完整清理语义。

天地图组合与栅格处理

const { baseImgLayer, baseCiaLayer, baseVecLayer, baseCvaLayer } =
  baseLayers('你的天地图令牌')

const recoloredLayer = changeLayer(
  baseImgLayer,
  [255, 255, 255],
  [0.05, 0.55, 0.05]
)
  • baseLayers(tk):创建影像、影像注记、矢量、矢量注记四个 XYZ 图层。
  • changeSource(layer, rgbColorArray, ratioArray, options?):创建栅格颜色转换数据源;options.threads 可指定线程数。
  • changeLayer(layer, rgbColorArray, ratioArray, options?):把转换数据源包装为 VectorImageLayer;支持相同的线程配置。

options 类型为 { threads?: number }。未指定时使用浏览器硬件并发数与 4 的较小值(无法读取时为 2),且至少使用 1 个线程。

标记与选择

const markerLayer = createMarkersLayer(markerData, {
  layerName: 'device-layer',
  zIndex: 10
})
map.addLayer(markerLayer)

const select = createLayerSelectByNames(['device-layer'], handleSelected)
map.addInteraction(select)
  • createText(item) / createIconStyle(item, active?):创建标记文本和图标样式。
  • createMarker(item, config) / createMarkersLayer(dataList, config):创建单个标记或标记图层。
  • createElasticMarkerLayer(dataList, config):按缩放级别切换 imgsmallImglargeImg,通过库的生命周期方法移除时自动解绑缩放监听。
  • changeUnselectStyle(featureCollection):清空选择集合并恢复未选中样式。
  • createLayerSelectByNames(layerNames, selectedHandler?, unSelectHandler?):按多个图层名选择标记。
  • createMoveMarkerLayer(dataItem, config):创建单标记图层并加入当前地图。

绘制、GeoJSON 与视野

  • createDrawInteraction(type, vectorLayer, openTip?):创建绘制交互。
  • getDrawStyle(feature, drawStyle?):为线和面绘制追加长度/面积标签。
  • createGeoJsonLayer(geoJson, style, config?):直接从 GeoJSON 创建矢量图层。
  • flyToAnimate(center, zoom?):使用两段缩放动画飞到目标点。

相关类型包括 LayerConfigDataItemConfigElasticDataItemConfig


十一、类型定义(core/types.tsexport * from './core/types'

interface CustomMap extends OlMap {
  activeBaseLayer?: string
  changeBaseLayer?: () => void
  getLayerByName?: (name: string) => Layer<Source> | undefined
  removeLayerByName?: (name: string) => void
  changeLayersByNames?: (activeNames: string[], allLayerNames: string[]) => void
}

type MassData = {
  _uuid?: string
  id: string
  style: number
  name: string
  status: string
  width?: number
  height?: number
  crsForm?: CRSTypes
  rawData: Record<string, any>
  lnglat: [string, string] | [number, number]
}

enum GeometryType {
  LineString = 'LineString',
  MultiLineString = 'MultiLineString',
  Polygon = 'Polygon',
  MultiPolygon = 'MultiPolygon',
  Invalid = 'Invalid'
}

完整导出还包括:VectorConfigClusterConfigClusterDataIconConfigWmtsSourceOptionsGetImgUrlGetImgUrlFnHzsyTypeZheJiangTypeWorldLayerTypeGeometryCoordinates


图层约定属性

工具库通过图层 properties 标记元信息,维护时请保持一致:_isBaseLayer_layerName_isCustom_isVectorLayer_isClusterLayer_isMassMarksLayer_selectInteraction_resolutionChangeKey_uuidSet_dataList_vectorConfig_isAutoFit_massMarksStyles_statusNum

构建

pnpm --dir packages/map-utils build       # tsdown 产出 esm + cjs + d.ts(压缩)
pnpm --dir packages/map-utils check:types # tsc --noEmit
pnpm --dir packages/map-utils test        # build + check:types + node:test
pnpm --dir packages/map-utils dev         # watch 模式

注意事项

  • 单例副作用:绝大多数独立函数依赖模块级 instance,请先调用 createMapInstancecreateMap 只创建独立地图,disposeMapinstanceundefined
  • 坐标系:输入数据可通过 config.crsForm / data.crsForm 指定源坐标系,内部统一转为 WGS84
  • 天地图令牌createMapWorldLayer / createZheJiangLayer 内置了默认 tk,正式项目请传入自有令牌。