#tile #vector #open #gis #low-cost-code #bounding-box

no-std open-vector-tile

此库读取/写入 Open Vector Tiles 1.0 规范

2 个版本 (1 个稳定版)

1.0.0 2024年8月15日
0.1.0 2024年5月17日

#3#bounding-box

Download history 140/week @ 2024-05-17 6/week @ 2024-05-24 106/week @ 2024-08-09

每月下载量 106

MIT 许可证

180KB
4K SLoC

open-vector-tile

GitHub Actions Workflow Status npm crate bundle downloads docs-ts docs-rust code-coverage Discord

关于

A Modified TypeScript implementation of the Mapbox Vector Tile library. It is backwards compatible but offers a lot of new features and improvements including (but not limited to)

  • 🔗 轻量级零依赖构建。
  • 🌴 正确的模块树摇。
  • 🦺 完整的 TypeScript 支持/安全。
  • 🗜 预分片和索引几何图形以快速将数据发送到渲染器。
  • 🧊 支持 3D 几何图形。
  • ♏ 支持每个几何点上的 M-Values(由线和多边形使用)。
  • ♻️ 功能属性和 M-Values 存储为“形状”,这重用只需要对值进行查找的对象。
  • 🏛 数据的列编码以使其更加紧凑。更好的 gzip 和 brotli 压缩。
  • 🪺 支持属性和 m-values 中的嵌套对象。
  • 📦 所有功能都支持像 ID 这样的第一类公民 BBOX 数据。
  • 🫥 线和多边形支持 offsets 以了解其走过的距离(对于正确渲染跨瓦片的虚线线非常有用)。

灵感

非常有才华的 Markus Tremmel 提出了将迁移从基于行的方法迁移到基于列的方法的想法,这是通过他的 COVTiles 实现的。我想测试简化他的项目的想法,看看这是否值得付出努力。一旦我看到 brotli 压缩具有可比较的结果,我就决定完成这个项目。

动机

由于同时正在构建另一个规范,您可能认为这是浪费时间或创建不必要的利益冲突。 因此,我写了关于这个主题的思考以及为什么创建此规范

阅读规范

open-vector-tile-spec

安装

#bun
bun add open-vector-tile
# pnpm
pnpm add open-vector-tile
# yarn
yarn add open-vector-tile
# npm
npm install open-vector-tile

# cargo
cargo install open-vector-tile

项目结构

Dependency Graph

示例使用

const fs = from 'fs'
import { VectorTile } from 'open-vector-tile'

// assume you can read (.pbf | .mvt | .ovt)
const fixture = fs.readFileSync('./x-y-z.vector.pbf')
// Bun const fixture = new Uint8Array(await Bun.file('./x-y-z.vector.pbf').arrayBuffer())
// load the protobuf parsing it directly
const tile = new VectorTile(fixture)

console.log(tile)

// example layer
const { landuse } = tile.layers

// grab the first feature
console.log(landuse.feature(0))
console.log(landuse.feature(0).loadGeometry())

通用目的 API

瓦片

读取瓦片

const tile = new VectorTile(uint8Array)

读取图层

const layer = tile.layers[layerName]

图层

图层属性

type Extents = 512 | 1024 | 2048 | 4096 | 8192
interface Layer {
    // version control helps know what features are available
    version: number;
    // name of the layer
    name: string;
    // extent of the vector tile. MUST be one of `512`, `1024`, `2048`, `4096`, `8192`
    extent: Extents;
    // number of features in the layer
    length: number;
}

读取特征

// returns a VectorFeature
const feature = layer.feature(index)

特征

特征类型

// 6 feature types in total plus the old MapboxVectorFeature
export type VectorFeature =
  // points may be a collection of points or single point
  | OVectorPointsFeature
  // lines may be a collection of lines or single line
  | OVectorLinesFeature
  // polygons may be a collection of polygons or single polygon
  | OVectorPolysFeature
  // 3D points may be a collection of 3D points or single 3D point
  | OVectorPoints3DFeature
  // 3D lines may be a collection of 3D lines or single 3D line
  | OVectorLines3DFeature
  // 3D polygons may be a collection of 3D polygons or single 3D polygon
  | OVectorPolys3DFeature
  // Can be any form of points, lines, or polygons without any of the new features
  // but all the functions. line offsets and bbox will always be defaults.
  | MapboxVectorFeature;

特征属性

type Extents = 512 | 1024 | 2048 | 4096 | 8192
interface Feature {
    // properties of the feature
    properties: any;
    // id of the feature
    id: number;
    // extent of the vector tile. MUST be one of `512`, `1024`, `2048`, `4096`, `8192`
    extent: Extents;
}

获取特征的边界框

export type BBox = [left: number, bottom: number, right: number, top: number];
export type BBox3D = [left: number, bottom: number, right: number, top: number, near: number, far: number];

const bbox: BBox | BBox3D = feature.bbox()

将几何形状作为点集合拉取

// supported by all types, points, lines, and polygons
const geometry: Point[] | Point3D[] = feature.loadPoints()

将几何形状作为线集合拉取

// Supported by any line or polygon type
/// points will return an empty array
interface VectorLineWithOffset {
  /** the offset of the line to start processing the dash position */
  offset: number;
  /** the line data */
  geometry: VectorLine;
}
interface VectorLine3DWithOffset {
  /** the offset of the line to start processing the dash position */
  offset: number;
  /** the line data */
  geometry: VectorLine3D;
}
const geometry: VectorLineWithOffset[] | VectorLine3DWithOffset[] = feature.loadLines()

根据类型相对位置拉取几何形状

const pointFeature: Point[] = (feature as OVectorPointsFeature).loadGeometry()
const lineFeature: VectorLine[] = (feature as OVectorLinesFeature).loadGeometry()
const polyFeature: VectorPoly[] = (feature as OVectorPolysFeature).loadGeometry()
const point3DFeature: Point3D[] = (feature as OVectorPoints3DFeature).loadGeometry()
const line3DFeature: VectorLine3D[] = (feature as OVectorLines3DFeature).loadGeometry()
const poly3DFeature: VectorPoly3D[] = (feature as OVectorPolys3DFeature).loadGeometry()

如果是多边形类型,则拉取带有索引和细分数据的原始几何形状

// works for any polygon or polygon3D type.
// NOTE: If the indices is empty, then the geometry was never pre-earcut and you need to fallback to `loadGeometry` instead.
const geometry: [geometry: number[], indices: number[]] = feature.loadGeometryFlat()

创建和验证您的形状

形状定义了可以存储在矢量瓦片中的数据类型。它们在规范中进行了说明。

如果您想验证形状,请随意使用Ajv库。

import Ajv from 'ajv';
import { ShapeSchema } from 'open-vector-tile'; // Path to the schema

import type { Shape } from 'open-vector-tile';

const ajv = new Ajv();
const validate = ajv.compile(ShapeSchema);

const shape: Shape = {
  a: 'i64',
  b: ['string'],
  c: {
    d: 'f64',
    e: 'bool',
    f: 'null',
    g: 'f32',
    h: {
      i: 'u64',
    },
  },
};

validate(shape); // true

开发

需求

您需要一个名为tarpaulin的工具来生成覆盖率报告。使用以下命令安装它:

cargo install cargo-tarpaulin

bacon coverage工具用于生成覆盖率报告。要使用pycobertura包生成更漂亮的覆盖率报告,请使用以下命令安装它:

pip install pycobertura

运行测试

要运行测试,请使用以下命令:

# TYPESCRIPT
## basic test
bun run test
## live testing
bun run test:dev

# RUST
## basic test
cargo test
# live testing
bacon test

生成覆盖率报告

要生成覆盖率报告,请使用以下命令:

cargo tarpaulin
# bacon
bacon coverage # or type `l` inside the tool

依赖项

~0.9–1.8MB
~39K SLoC