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

@meteo-layer/core

v0.3.5

Published

meteo-layer 渲染器无关内核:解码(含 Go wasm 内核与 Worker)、调色板、格式注册、数据源注册、色标组件。可被 Cesium / MapLibre 等渲染器包复用。

Readme

@meteo-layer/core

气象图层渲染器无关内核。提供风粒子 GPU 引擎、二进制栅格解码(含 Go wasm 内核)、调色板、图像格式注册、数据源注册、掩膜运算,可被 Cesium / MapLibre 等渲染器包复用。

直接面向应用开发通常装渲染器包 @meteo-layer/cesium@meteo-layer/maplibre(它们已依赖 core 并重导出公共 API)。core 供二次开发 / 自接渲染器的场景使用。

特性

  • GpuWindField 引擎:原生 WebGL2 Transform Feedback,渲染器无关;投影由适配器通过 prelude 注入(支持 MapLibre mercator/globe 与 Cesium ECEF)。
  • 解码内核:Go wasm 解码 + Web Worker,主线程只做纹理上传;支持 uint8 / int16 / float32 二进制栅格(可选 gzip)。
  • 图像格式抽象:内置 scalar / wind-rg / truecolor,以及高精度 scalar-f16 / scalar-f32(避免 8 位精度丢失),可 registerFormat 自定义。
  • 数据源注册:统一 defineSource 描述栅格元信息(投影、量程、色板、相机),支持时序 frames、雷达分层 slices、垂直剖面 profiles 三种扩展点。
  • 掩膜运算:渲染器无关的点-多边形测试(射线法,含孔洞)+ mercator 纹理栅格化,供两端 GL 层做片元 discard

安装

npm install @meteo-layer/core

协议:Apache-2.0(详见仓库根目录 LICENSE)。core 无对等依赖(零渲染引擎依赖)。

快速开始

1. 注册并使用一个数据源

import { defineSource, registerSource, listSources } from '@meteo-layer/core'

defineSource({
  id: 'temp', title: '2m 温度', format: 'scalar',
  url: 'https://.../temp.png', bounds: [70, 0, 140, 60],
  imageUnscale: [-20, 40], unit: '℃',
  palette: [
    [-20, [30, 60, 160, 0.8]],
    [0, [60, 160, 220, 0.8]],
    [20, [240, 200, 60, 0.8]],
    [40, [220, 60, 40, 0.8]],
  ],
})

const src = getSource('temp')
console.log(listSources())   // ['temp']

2. 自定义图像格式

import { registerFormat } from '@meteo-layer/core'

registerFormat({
  id: 'radar-dbz',
  shader: 'scalar',
  sampleField: (raw, cfg, lon, lat) => { /* 返回 { value, ... } */ },
  describe: (s) => ({ text: `${s.value.toFixed(0)} dBZ` }),
})

3. 解码二进制栅格(bin)

import { binLoader, createBinLoader } from '@meteo-layer/core'

// 单次:直接用 binLoader 作为 source.loader
// 可配置:createBinLoader({ headers: { Authorization: 'Bearer ...' } })

核心 API

数据源与格式

| 导出 | 说明 | | --- | --- | | defineSource(cfg) / registerSource(cfg) | 定义 / 注册数据源 | | getSource(id) / hasSource(id) / listSources() | 数据源查询 | | normalizePalette(stops) | 归一化色板(补全 alpha 等) | | defineProfile(p, parent) | 定义垂直剖面(雷达 RHI 等) | | registerFormat(fmt) / getFormat(id) / hasFormat(id) / listFormats() | 格式注册与查询 | | scalarFormat / windRGFormat / trueColorFormat | 内置格式实例 | | degToCompass(deg) | 度数转汉字方位 |

图像加载与解码

| 导出 | 说明 | | --- | --- | | loadImage(cfg) | 默认 PNG 加载器 | | binLoader / createBinLoader(opts) | 二进制栅格加载器(uint8/int16/float32,可选 gzip) | | parseBinBuffer(buf, cfg?) / parseBinBufferRaw(buf, cfg?) | 直接解析 bin 缓冲 | | floatMockLoader / seriesMockLoader / makeFloatMockRaw / makeSeriesMockRaw | 模拟数据(开发/演示) | | createDecodeWorker() | 解码 Worker 实例(wasm 内核) | | decodeBinWasm(buf, cfg?, engine?) | wasm 解码入口 | | openZ4D(url) / readRegion(meta, region) | Z4D1 4D 二进制:读元数据 + 任意区域取块解码(blosc/gzip/raw,支持 HTTP Range) | | createZ4DImageLoader / createZ4DSliceLoader / createZ4DProfileLoader / createZ4DWindLoader / createZ4DVolumeLoader / createZ4DWindMagSliceLoader / createZ4DWindMagProfileLoader | Z4D1 七类 loader 工厂(填色 / 分层切片 / 垂直剖面 / 时序风场 / 真·体渲染 / 风速幅值单层填色 / 风速幅值垂直剖面) | | openZarrArray(storeUrl, arrayPath) / readRegion(arr, region) | Zarr v2 纯前端读取:读元数据 + 任意区域取块解码 | | createZarrImageLoader / createZarrSliceLoader / createZarrProfileLoader | Zarr 三类 loader 工厂(单图填色 / 分层切片 / 垂直剖面) |

调色板

| 导出 | 说明 | | --- | --- | | usePalette() | 调色板 API(共享实例,含 rebuild / colorizeValue / onChange) |

色标 UI 组件(ColorLegend.vue)已迁移至样例工程,库不再内嵌 Vue 组件。

GpuWindField 引擎

| 导出 | 说明 | | --- | --- | | GpuWindField | 默认导出,渲染器无关的 GPU 风粒子引擎 | | assembleGpuWindFieldUpdateVs(opts) | 组装粒子 update VS(注入投影 prelude) | | DRAW_VS_BODY / DRAW_FS / FALLBACK_PRELUDE / CESIUM_ECEF_PRELUDE | 着色器源码片段与投影 prelude | | WIND_RAINBOW_RAMP / makeWindRainbowLUT(range?, size?, ramp?) / buildWindPaletteLUT(palette, size?, range?) | 风速默认彩虹色标 + LUT 构造(useWhite=false 且源无 palette 时的统一兜底) |

掩膜(渲染器无关)

| 导出 | 说明 | | --- | --- | | MaskType | inside = 1(区域内显示)/ outside = 0(区域外显示) | | rectMask(bounds) / normalizeMask(mask) | 构造 / 归一化掩膜几何 | | pointInMaskPolygon(lng, lat, rings) | 点-多边形测试(射线法) | | pointVisibleUnderMask(lng, lat, mask, type) | 点是否在可见区 | | lngLatToMerc01(lng, lat) | 经纬度 → mercator [0,1] | | rasterizeMask / rasterizeMaskToCanvas / rasterizeMaskEquirectToCanvas | 掩膜纹理栅格化 | | createMaskTexture(gl, mask) | 生成 GL 掩膜纹理(供片元 discard) |

Mock fetcher(矩形查询多层)

| 导出 | 说明 | | --- | --- | | mockRadarFetcher / mockRadarFetcherBin / mockRadarFetcherF32 / mockRadarFetcherF16 | 3D 体视演示用的假数据 fetcher |

Z4D1 自定义 4D 二进制格式

单文件自包含(头部 + 块索引 + 块载荷),替代/补充 Zarr 与多帧 bin。任何 Z4D1 文件都能被读成标准 RawImage,直接喂现有渲染管线(单图层填色 / 分层切片 slices / 垂直剖面 profiles / 时序帧 frames / 风场矢量 / 真·体渲染),渲染·纹理·材质·动画零改动。

字节布局(小端 Little-Endian)

| 字段 | 类型 | 说明 | | --- | --- | --- | | magic | 4B ASCII | 'Z4D1' 校验 | | version | u8 | 版本 | | dtype | u8 | 0=float64 / 1=float32(解码统一转 float32) | | ndim | u8 | 维数(4=4D,2=2D) | | ncomp | u8 | 分量数0=旧标量布局(56B 头);1=标量;2=uv 风;3=uvw | | shape | 4×i32 | [time, level, lat, lon] | | chunks | 4×i32 | 各维分块大小 | | u/v/w_range | 各 2×f32 | 物理量程(u=标量/第0分量;v/w 仅矢量用) | | codec/cname/clevel/shuffle | u8×4 | 压缩(blosc: lz4/zstd/blosclz/zlib/snappy;gzip;raw) | | nchunks | i32 | 块数量 | | 块索引 | nchunks×28B | 每块 [coords(4×i32) | offset(i64) | len(i32)] |

  • 旧布局(56B,ncomp=0imageUnscale 在 40–47,codec/nchunks 在 48–55。
  • 新布局(72B,ncomp≥1u/v/w_range 在 40–63,codec/nchunks 在 64–71。

维度契约shape=[time, level, lat, lon](2D 文件 [lat, lon]);矢量末维每网格点连续存 ncomp 个 float32,风 ncomp=2 即 R=U(东向)/G=V(北向) 且 u/v 同量程;lat 索引 0 = 南,渲染 row0 = 北(与 Cesium 纹理排列一致)。

Loader 工厂(core 导出)

openZ4D(url) 读取并缓存元数据,readRegion(meta, region) 取任意 [start,end) 子块并解码。以下工厂返回标准 loader,直接赋给 defineSource({ loader })frames[].loader

| 工厂 | 取数维度 | 输出 | 用途 | | --- | --- | --- | --- | | createZ4DImageLoader(cfg) | (timeIndex, levelIndex) 单块 → 2D | 标量/矢量色斑图 | 单图层填色 | | createZ4DSliceLoader(cfg) | 指定 levelIndex 切层 | 2D 分层切片 | slices 雷达分层 | | createZ4DProfileLoader(cfg) | 沿 line 读全 level → 沿程×高度幕布 | 垂直剖面 | profiles(RHI) | | createZ4DWindLoader(cfg) | (timeIndex, levelIndex)wind-rg(R=U G=V) | 风场矢量图 | frames 时序风场(frameIndex=timeIndex 自动播放) | | createZ4DVolumeLoader(cfg) | 固定 time,取 levelRange 3D 块 | RGBA 体数据(data3D/width/height/depth) | 真·体渲染(volume:true) | | createZ4DWindMagSliceLoader(cfg, levelIndex?) | 指定层 → 风速幅值单层 2D | scalar 色斑图 | 风场填色(按风速上色) | | createZ4DWindMagProfileLoader(cfg) | 沿 line 读全 level → sqrt(u²+v²) 幕布 | scalar 色斑图 | 风速垂直剖面(profiles,cfg 取 magRange 统一量程) |

cfgurltimeIndex?levelIndex?levelRange?boundsimageUnscale?palette?precision?('float32');loader 接收可选 bbox 做空间裁剪。

用法示例

import { defineSource, createZ4DVolumeLoader, createZ4DWindLoader } from '@meteo-layer/core'

// 1) 真·体渲染:把 3D 子块 loader 交给矩形框选的 fetcher
const meta = await mgr.queryByRectangle({
  fetcher: async (bounds) => ({
    layers: [{
      format: 'scalar', bounds, imageUnscale: [0, 70], palette: [...],
      loader: createZ4DVolumeLoader({ url: '/data/radar.z4d', levelRange: [0, 19], bounds }),
    }],
  }),
})
mgr.setVerticalExaggeration(10)   // computeVolumeModelMatrix 自带垂直夸张(默认 10)

// 2) 时序风场:frameIndex = timeIndex,天然支持播放
await mgr.setSource(defineSource({
  id: 'z4d-wind', format: 'wind-rg', bounds: [70, 0, 140, 60],
  imageUnscale: [-30, 30], unit: 'm/s',
  loader: createZ4DWindLoader({ url: '/data/wind.z4d', levelIndex: 0, bounds: [70, 0, 140, 60] }),
  frames: Array.from({ length: 24 }, (_, t) => ({
    loader: createZ4DWindLoader({ url: '/data/wind.z4d', levelIndex: 0, timeIndex: t, bounds: [70, 0, 140, 60] }),
    time: `T+${t}h`,
  })),
}))

服务端需支持 Range 请求(返回 206 Partial Content)以按需取块;不支持时降级为整文件下载(200)后本地切片,功能仍正确。dev server 已内置 /z4d 中间件支持 Range 206。

场景化封装(标量/矢量 × 剖面/切片)

在底层 createZ4D*Loader 之上的一层封装:约定好气压层表 + 标准大气压高 + 默认色标 + cfg 契约,页面只传 { url, bounds, line(剖面) / bounds(切片) },无需手写 loader 闭包 / altitude 映射 / 色标。标量(温度/雷达)与矢量(风)分开。

| 工厂 | 用途 | 返回 | | --- | --- | --- | | createScalarProfileSource(opts) | 标量垂直剖面(温度/雷达):折线 → 取全 level 第 0 分量 → 沿程×高度幕布 | LayerSourceprofiles[],可直接 MeteoScene.addLayer(createDataSource(source))) | | createVectorProfileSource(opts) | 矢量垂直剖面(风):折线 → (U,V) → sqrt(u²+v²) 风速模长幕布 | 同上 | | createScalarSliceFetcher(opts) | 标量切片堆叠(温度,8 等压面):矩形框选 → 多层按高度堆叠 3D 色斑 | queryByRectangle 的 fetcher (bounds) => layerCfg[] | | createVectorSliceFetcher(opts) | 矢量切片堆叠(风,8 等压面风速模长) | 同上 |

统一 opts{ url, bounds, imageUnscale?, valueRange?, unit?, palette?, levels?(默认 GFS 8 层), levelHeightsKm?, alongN?(默认 256), heightRange?(默认 [0,16000]), precision?, id?, title? };剖面专用再加 line: [[lon,lat],...]

  • 默认气压层 [1000,925,850,700,500,300,200,100]mb,标准大气压高近似 [0,0.8,1.5,3,5.5,9,12,16]km(切片 altitude / 剖面 heightRange 由此推导)。
  • 风默认风速色标(蓝→绿→黄→红→紫),温度默认气温色标(紫→蓝→绿→黄→红)。
import { createVectorProfileSource, createScalarSliceFetcher, createDataSource } from '@meteo-layer/core'

// 风剖面:画折线 → 风速模长垂直幕布
const src = createVectorProfileSource({
  url: '/z4d/gfs_wind.z4d', bounds: [-180,-90,180,90],
  line: [[100,30],[125,45]], valueRange: [0,60], unit: 'm/s',
})
scene.addLayer(createDataSource(src))   // MeteoScene 自动接线 profile 幕布

// 温度切片:画矩形 → 8 等压面 3D 堆叠
const fetcher = createScalarSliceFetcher({
  url: '/z4d/gfs_tmp.z4d', bounds: [-180,-90,180,90],
  imageUnscale: [-88.9,48.6], valueRange: [-90,50], unit: '℃', precision: 'float32',
})
const meta = await scene.queryByRectangle({ fetcher, defaultOpacity: 0.8 })  // → 每层 meta[]

等值线 / 等值域(ContourGenerator)

渲染器无关的等值线生成器(marching squares 追踪版,参考业界主流 WContour 思路)。输入规则经纬网格,输出描边线(lines)带孔洞等值域(bands),供 Cesium / MapLibre 各自绘制。

  • 算法:逐格遍历 → 对跨阈值的网格边线性插值求交点 → 段按端点邻接连接成连续环;相邻 level 的环配对成 isoband(outer + holes,渲染器用 even-odd 填充规则挖洞)。
  • undef 网格(NaN 或指定值)所在 cell 整体跳过,等值线不进入 undef 区。
  • 纯同步无 DOM 依赖,可整体搬进 Web Worker 跑(大网格建议如此,参考业界多线程等值线思路)。
  • 与色斑图严格对齐:坐标用 texel-center 约定(与 GPU 纹理采样一致);interpMode >= 2 时先以 Cardinal(Catmull-Rom c=0.5) 重采样出密网格再做 marching squares,等值线贴合 GPU 三次平滑场的真实等值点(< 2 走原始线性边插值)。
import { generateContours } from '@meteo-layer/core'

// values: number[][] / Float32Array+{width,height} / {data,width,height}
const { lines, bands } = generateContours({
  values,                       // 标量场(ny × nx)
  width: nx, height: ny,        // 仅 TypedArray 形态需要
  west, south, east, north,     // 经纬度范围(规则网格)
  levels: [2, 6, 10, 14, 18],   // 升序阈值
  undef: -9999,                 // 可选,缺失值标记(默认 NaN)
  row0IsNorth: true,            // values[0] 是否为北纬
  interpMode: 3,                // 可选,与色斑图一致的插值模式(1=线性边插值[默认],>=2=Cardinal 重采样)
  maxResampleDim: 480,          // 可选,Cardinal 重采样密网格单边上限(默认 480)
  withLines: true, withFill: true,
})

// lines: [{ level, rings: [[[lon,lat], ...], ...] }]   → 渲染器画 Polyline
// bands: [{ min, max, outer: rings, holes: rings }]    → 渲染器以 even-odd 填充 outer、挖 holes

导出:generateContours / marchingSquaresSegments / linkSegmentsToRings / buildNiceLevels / sampleRamp / cardinalSampleField

多源叠加统一 API(DataSource + AnimationService)

核心思路:把静态 LayerSource cfg 包成有状态广播源 DataSource,多个图层(色斑图 / 风粒子 / 格点)绑定同一 DataSource 时,时间轴一变全员刷新。渲染器包(cesium / maplibre)的 MeteoScene 基于此实现多图层同步播放。

import { createDataSource, AnimationService } from '@meteo-layer/core'

const ds = createDataSource('temp')            // 传 cfg 或已注册的 id
await ds.ready()                                // 等首帧解码
ds.onDecoded((raw, frameIndex) => { /* ... */ })
ds.setFrameIndex(2)                             // 改帧 → 广播 onTChanged → 绑定层刷新

const anim = new AnimationService(ds, { speed: 1, frameDuration: 600, loop: true })
anim.onFrameChange = (i, time, total) => { /* ... */ }
anim.play()

DataSource

| 成员 | 说明 | | --- | --- | | 构造 new DataSource(sourceCfg, opts?) / createDataSource(cfgOrId, opts?) | cfgOrIdLayerSource 对象或已注册 id;opts.seriesWindow 控制时序解码窗口 | | get cfg / get id | 静态源配置 / id | | get isSeries / get frameCount | 是否时序 / 总帧数 | | frameIndex | 当前帧索引(可写) | | get frameTime | 当前帧时间标签 | | ready() | 等待首帧就绪的 Promise | | onTChanged(fn) | 订阅帧变化(返回退订函数) | | onDecoded(fn) | 订阅解码完成(返回退订函数) | | setFrameIndex(i) | 切帧并广播 | | loadCurrentFrame() / preloadFrame(i) | 加载当前 / 预加载指定帧 | | clearCache() / destroy() | 清缓存 / 销毁 |

AnimationService

对应 QE 的 LDataAnimationService:驱动 DataSource.frameIndex 变化 → 广播 → 绑定层刷新。

| 成员 | 说明 | | --- | --- | | 构造 new AnimationService(dataSource, opts?) | opts = { speed, frameDuration(ms), loop } | | get ds / speed / frameDuration / loop | 关联数据源与播放参数 | | onFrameChange | 帧回调 (frameIndex, frameTime, frameCount) => void | | get isPlaying | 是否播放中 | | play() / pause() / seek(i) | 播放 / 暂停 / 跳帧 | | setSpeed(s) / setLoop(b) / setFrameDuration(ms) | 调速 / 循环 / 帧间隔 | | destroy() | 销毁定时器 |


TypeScript

类型声明随包发布(dist/index.d.ts)。运行时为 JS(带 JSDoc)。