{"_id":"@deepinnet/service-mvt-middleware","_rev":"2-c01d8c40e7d87bcaec51c992f88027cc","name":"@deepinnet/service-mvt-middleware","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@deepinnet/service-mvt-middleware","version":"0.0.1","keywords":["nestjs","mvt","vector-tile","postgis","middleware"],"author":{"name":"DeepInNet"},"license":"Apache-2.0","_id":"@deepinnet/service-mvt-middleware@0.0.1","maintainers":[{"name":"weiwei2020","email":"1032159552@qq.com"},{"name":"wxg-james","email":"346775171@qq.com"},{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"}],"dist":{"shasum":"f42e7b67661ed2cb8a5b724d57219b2949c09047","tarball":"https://registry.npmjs.org/@deepinnet/service-mvt-middleware/-/service-mvt-middleware-0.0.1.tgz","fileCount":7,"integrity":"sha512-fsgU62NURc6amhrOHz2rHiGisfNokORmuYTG5/vbutTcVOSntc+AFzMrcWTqbXBI5DxiwGl9YJuiTmNWTzCI5A==","signatures":[{"sig":"MEYCIQDYeE8B5gQf7MXxUG59ppNdxS04nr2XvNgKdlFiJWIEzAIhAPhZ4ahbKwEqFczALNHXkp+9WRYVjc/I8fN9dfy1Guaf","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70582},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"0684ad6771e806d99048408f00d23745d85ef051","scripts":{"test":"pnpm run build && node --test test/**/*.test.mjs","build":"tsup src/index.ts --format esm,cjs --dts --clean --target es2019 && node ../../scripts/obfuscate-dist.mjs dist","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit"},"_npmUser":{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"},"_npmVersion":"10.9.2","description":"NestJS middleware module for serving 2D MVT and custom 3D vector tile endpoints.","directories":{},"_nodeVersion":"22.14.0","dependencies":{"@deepinnet/service-mvt-middleware-core":"^0.0.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"rxjs":"^7.8.2","@nestjs/core":"^11.1.6","@nestjs/common":"^11.1.6","reflect-metadata":"^0.2.2"},"peerDependencies":{"@nestjs/core":"^10.0.0 || ^11.0.0","@nestjs/common":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.1.13 || ^0.2.0"},"_npmOperationalInternal":{"tmp":"tmp/service-mvt-middleware_0.0.1_1777521314120_0.46873743516058175","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@deepinnet/service-mvt-middleware","version":"0.0.2","description":"NestJS middleware module for serving 2D MVT and custom 3D vector tile endpoints.","license":"Apache-2.0","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","publishConfig":{"access":"public"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"keywords":["nestjs","mvt","vector-tile","postgis","middleware"],"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts --clean --target es2019 && node ../../scripts/obfuscate-dist.mjs dist","clean":"rm -rf dist","test":"pnpm run build && node --test test/**/*.test.mjs","typecheck":"tsc -p tsconfig.json --noEmit"},"dependencies":{"@deepinnet/service-mvt-middleware-core":"^0.0.1"},"author":{"name":"DeepInNet"},"peerDependencies":{"@nestjs/common":"^10.0.0 || ^11.0.0","@nestjs/core":"^10.0.0 || ^11.0.0","reflect-metadata":"^0.1.13 || ^0.2.0"},"devDependencies":{"@nestjs/common":"^11.1.6","@nestjs/core":"^11.1.6","reflect-metadata":"^0.2.2","rxjs":"^7.8.2"},"_id":"@deepinnet/service-mvt-middleware@0.0.2","gitHead":"129f81494110e889e2deb72fbcc9e954018d9909","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-v05ftHksNMPf01Q3o78zDRmsdHD0KmpMjH2EEaUJicpIzVLb5MqsGNnqAMzkUwpRY/GikrMaTi1giPVMpyWOiw==","shasum":"a076ffc502f2579cbd0add4a50f05957cca51012","tarball":"https://registry.npmjs.org/@deepinnet/service-mvt-middleware/-/service-mvt-middleware-0.0.2.tgz","fileCount":7,"unpackedSize":74506,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBe2hJosaat73TqyEVkg9NrX8OQzNVCqNEx7nEjFsmBaAiBeAeJrygH0kUvFVi6CZS5fR294dk/fRMr9LngWaDAZ3A=="}]},"_npmUser":{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"},"directories":{},"maintainers":[{"name":"weiwei2020","email":"1032159552@qq.com"},{"name":"wxg-james","email":"346775171@qq.com"},{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/service-mvt-middleware_0.0.2_1777531674197_0.3067282425520541"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-30T03:55:14.039Z","modified":"2026-04-30T06:47:54.533Z","0.0.1":"2026-04-30T03:55:14.264Z","0.0.2":"2026-04-30T06:47:54.355Z"},"author":{"name":"DeepInNet"},"license":"Apache-2.0","keywords":["nestjs","mvt","vector-tile","postgis","middleware"],"description":"NestJS middleware module for serving 2D MVT and custom 3D vector tile endpoints.","maintainers":[{"name":"weiwei2020","email":"1032159552@qq.com"},{"name":"wxg-james","email":"346775171@qq.com"},{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"}],"readme":"# @deepinnet/service-mvt-middleware\n\nNestJS 中间件包，用于把二维 MVT 和三维自定义 PBF 瓦片服务接到 HTTP 控制器层。\n\n这个包只负责 NestJS 集成层：\n\n- 暴露二维 `/tiles/...` 路由\n- 暴露三维 `/tiles-3d/...` 路由\n- 暴露 `/tiles/:layer/metadata` 和 `/health`\n- 把底层 `@deepinnet/service-mvt-middleware-core` 引擎接到 Nest 应用\n\n如果你不需要 NestJS，只想直接使用瓦片引擎和 PostgreSQL adapter，请改用：\n\n- `@deepinnet/service-mvt-middleware-core`\n\n## 安装\n\n```bash\npnpm add @deepinnet/service-mvt-middleware\npnpm add @deepinnet/service-mvt-middleware-core\n```\n\n## 适用场景\n\n- 你在 NestJS 中提供二维矢量瓦片接口\n- 你在 NestJS 中提供三维自定义 PBF 接口\n- 你希望把图层配置、控制器和健康检查统一放在一个 module 中\n\n## 怎么使用\n\n最小接入步骤通常是：\n\n1. 在数据库中准备好 PostGIS 图层表或视图。\n2. 在 Nest `AppModule` 中注册 `TileModule.forRoot(...)`。\n3. 配置 `layers`、`database`、`defaults`。\n4. 启动应用后，通过 `/tiles/...` 或 `/tiles-3d/...` 请求瓦片。\n\n### 1. 在 Nest 中注册模块\n\n```ts\nimport { Module } from \"@nestjs/common\";\nimport { TileModule } from \"@deepinnet/service-mvt-middleware\";\n\n@Module({\n  imports: [\n    TileModule.forRoot({\n      layers: [\n        {\n          layer: \"data\",\n          sourceType: \"table\",\n          sourceObject: \"public.geom\",\n          idField: \"id\",\n          geomField: \"geometry\",\n          geom3dField: \"geometry3d\",\n          sourceCrs: \"4326\",\n          defaultOutputCrs: \"gcj02\",\n          supportedOutputCrs: [\"4326\", \"gcj02\"],\n          propertyWhitelist: [\"name\", \"color\"],\n          gzip: {\n            enabled: true,\n          },\n          clusterEnabled: true,\n          clusterMaxZoom: 15,\n          clusterCellSize: 256,\n          clusterMaxFeatures: 2048,\n          clusterFields: {\n            color: {\n              field: \"color\",\n              op: \"top\",\n            },\n          },\n        },\n      ],\n      database: {\n        connectionString: process.env.DATABASE_URL,\n      },\n      defaults: {\n        sourceCrs: \"4326\",\n        defaultOutputCrs: \"gcj02\",\n      },\n      tile: {\n        extent: 4096,\n        buffer: 64,\n        maxFeatures: 20000,\n      },\n      controllers: {\n        tile: true,\n        tile3d: true,\n        metadata: true,\n        health: true,\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n### 1.1 复用已有 `pg.Pool`\n\n如果你的应用已经自己管理 PostgreSQL 连接池，可以直接把现有 `pool` 传给 `database.pool`：\n\n```ts\nimport { Module } from \"@nestjs/common\";\nimport { Pool } from \"pg\";\nimport { TileModule } from \"@deepinnet/service-mvt-middleware\";\n\nconst pool = new Pool({\n  connectionString: process.env.DATABASE_URL,\n});\n\n@Module({\n  imports: [\n    TileModule.forRoot({\n      layers: [\n        {\n          layer: \"data\",\n          sourceType: \"table\",\n          sourceObject: \"public.geom\",\n          idField: \"id\",\n          geomField: \"geometry\",\n        },\n      ],\n      database: {\n        pool,\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n这种方式下，瓦片查询和默认 health check 都会复用你传入的 `pool`。\n\n连接池生命周期需要区分两种情况：\n\n- 传 `database.connectionString` 或 `database.poolConfig`：模块内部会创建连接池，并在应用关闭时自动 `end()`\n- 传 `database.pool`：连接池归调用方所有，这个包不会自动关闭，你需要在自己的关闭流程里手动执行 `await pool.end()`\n\n### 1.2 配置统一路由前缀\n\n如果你希望把默认路由整体挂到统一前缀下，可以配置 `routePrefix`：\n\n```ts\nTileModule.forRoot({\n  routePrefix: \"api/v1\",\n  layers: [\n    {\n      layer: \"data\",\n      sourceType: \"table\",\n      sourceObject: \"public.geom\",\n      idField: \"id\",\n      geomField: \"geometry\",\n    },\n  ],\n})\n```\n\n这样默认路由会变成：\n\n- `/api/v1/tiles/:layer/:z/:x/:y.pbf`\n- `/api/v1/tiles-3d/:layer/:z/:x/:y.pbf`\n- `/api/v1/tiles/:layer/metadata`\n- `/api/v1/health`\n\n`routePrefix` 会自动去掉首尾多余的 `/`，所以 `\"api\"`、`\"/api\"`、`\"/api/\"` 的效果一致。\n\n### 2. 启动后默认会暴露哪些接口\n\n启用默认 controllers 后，会暴露这些路由：\n\n- `GET /tiles/:layer/:z/:x/:y.pbf`\n- `GET /tiles-3d/:layer/:z/:x/:y.pbf`\n- `GET /tiles/:layer/metadata`\n- `GET /health`\n\n如果配置了 `routePrefix: \"api\"`，上面四类路由会统一变成 `/api/...`。\n\n### 3. 怎么发请求\n\n二维示例：\n\n```bash\ncurl --compressed -i \\\n  \"http://127.0.0.1:3000/tiles/data/8/209/111.pbf?crs=gcj02&fields=name,color\"\n```\n\n三维示例：\n\n```bash\ncurl --compressed -i \\\n  \"http://127.0.0.1:3000/tiles-3d/data/8/209/111.pbf?crs=gcj02\"\n```\n\n读取 metadata：\n\n```bash\ncurl \"http://127.0.0.1:3000/tiles/data/metadata\"\n```\n\n读取 health：\n\n```bash\ncurl \"http://127.0.0.1:3000/health\"\n```\n\n## HTTP 接口详解\n\n### 二维接口\n\n```text\nGET /tiles/:layer/:z/:x/:y.pbf\n```\n\n返回内容：\n\n- 标准二维 MVT\n- `Content-Type: application/vnd.mapbox-vector-tile`\n- 如果开启 `gzip.enabled=true`，响应头会带 `Content-Encoding: gzip`\n\n适合的前端消费方式：\n\n- MapLibre / Mapbox GL 风格 `vector source`\n- 其他能直接消费 MVT 的前端引擎\n\n### 三维接口\n\n```text\nGET /tiles-3d/:layer/:z/:x/:y.pbf\n```\n\n返回内容：\n\n- 自定义三维 PBF\n- `Content-Type: application/octet-stream`\n- 如果开启 `gzip.enabled=true`，响应头会带 `Content-Encoding: gzip`\n\n适合的前端消费方式：\n\n- 自己在前端拿到 `ArrayBuffer` 后解码\n- 不能直接当标准 MVT 使用\n\n### metadata 接口\n\n```text\nGET /tiles/:layer/metadata\n```\n\n返回当前图层的配置摘要，常用于：\n\n- 前端初始化前先确认图层信息\n- 联调时检查图层名、坐标系、字段白名单\n- 运维或健康排查\n\n### health 接口\n\n```text\nGET /health\n```\n\n返回服务和数据库探针状态，常用于：\n\n- 容器健康检查\n- 上线后巡检\n- 联调时确认数据库是否连通\n\n## 参数怎么传\n\n### 路径参数\n\n二维和三维接口都使用同一组路径参数：\n\n| 参数 | 类型 | 是否必填 | 含义 | 示例 |\n| --- | --- | --- | --- | --- |\n| `layer` | `string` | 是 | 图层名，必须命中 `layers` 配置中的某一项。 | `data` |\n| `z` | `number` | 是 | 缩放级别，必须是大于等于 `0` 的整数。 | `8` |\n| `x` | `number` | 是 | 当前 `z` 层级下的瓦片列号。 | `209` |\n| `y` | `number` | 是 | 当前 `z` 层级下的瓦片行号。 | `111` |\n\n### 查询参数\n\n二维和三维接口共用同一组查询参数：\n\n| 参数 | 类型 | 是否必填 | 含义 | 示例 |\n| --- | --- | --- | --- | --- |\n| `crs` | `string` | 否 | 输出坐标系，当前支持 `4326` 和 `gcj02`。未传时使用图层的 `defaultOutputCrs`。 | `crs=gcj02` |\n| `minZoom` | `number` | 否 | 请求级最小缩放限制，会与图层和默认配置一起合并。 | `minZoom=6` |\n| `maxZoom` | `number` | 否 | 请求级最大缩放限制，会与图层和默认配置一起合并。 | `maxZoom=16` |\n| `fields` | `string` | 否 | 逗号分隔的属性字段列表。最终仍受 `propertyWhitelist` 约束。 | `fields=name,color` |\n| `from` | `string` | 否 | 起始时间，要求图层配置了 `timeField`。 | `from=2026-01-01T00:00:00Z` |\n| `to` | `string` | 否 | 结束时间，要求图层配置了 `timeField`。 | `to=2026-12-31T23:59:59Z` |\n| `v` | `string` | 否 | 版本字符串，主要参与缓存键区分。 | `v=2026-04-30` |\n\n`/tiles/:layer/metadata` 只使用路径参数 `layer`，`/health` 不接收业务参数。\n\n如果你需要过滤条件，请在服务端内部构造 `TileQueryInput.filter` 后再调用引擎。默认 HTTP controller 不会从 URL 读取 `filter`；历史上的 `?filter=...` 也会被静默忽略。\n\n## `filter` 现在怎么传\n\n默认公开 HTTP 接口已经移除了 `filter` 查询参数：\n\n- 不再支持 `GET /tiles/...?...&filter=...`\n- 不再支持 `GET /tiles-3d/...?...&filter=...`\n- 旧的 `?filter=...` 不会报错，但会被静默忽略\n\n这样做的目的是不再把过滤条件暴露在 URL、访问日志、缓存键观测面和浏览器历史里。\n\n如果你仍然需要过滤能力，请在服务器内部直接传 `TileQueryInput.filter`，而不是让客户端拼到链接上。\n\n### Nest 服务内部传 `filter`\n\n最直接的方式，是在你自己的 Controller / Service 里调用 `TileService.getTile(...)` 或 `TileService.get3DTile(...)`，把 `filter` 放进 `TileQueryInput`：\n\n```ts\nimport { Controller, Get, Inject, Param, Res } from \"@nestjs/common\";\nimport type { TileFilterExpression } from \"@deepinnet/service-mvt-middleware-core\";\nimport { TileHttpResponder, TileService } from \"@deepinnet/service-mvt-middleware\";\n\nconst onlyPoint1: TileFilterExpression = {\n  op: \"eq\",\n  field: \"name\",\n  value: \"point-1\",\n};\n\n@Controller(\"internal-tiles\")\nexport class InternalTileController {\n  constructor(\n    @Inject(TileService) private readonly tileService: TileService,\n    @Inject(TileHttpResponder) private readonly tileHttpResponder: TileHttpResponder,\n  ) {}\n\n  @Get(\":layer/:z/:x/:y.pbf\")\n  async getTile(\n    @Param(\"layer\") layer: string,\n    @Param(\"z\") z: string,\n    @Param(\"x\") x: string,\n    @Param(\"y\") y: string,\n    @Res() response?: unknown,\n  ): Promise<void> {\n    const result = await this.tileService.getTile({\n      layer,\n      z: Number(z),\n      x: Number(x),\n      y: Number(y),\n      crs: \"4326\",\n      fields: [\"name\", \"color\"],\n      filter: onlyPoint1,\n    });\n\n    this.tileHttpResponder.write(response, result);\n  }\n}\n```\n\n### `filter` 能传什么\n\n`filter` 是 `TileQueryInput` 的一个内部字段，类型是 `TileFilterExpression`。当前支持的结构化操作符仍然是：\n\n- `eq`\n- `in`\n- `range`\n- `and`\n- `or`\n\n它仍然会经过底层白名单校验和参数化 SQL 生成；也就是说，`filter` 只是从公开 URL 上移除了，并没有从服务端内部能力里删除。\n\n## 返回结果怎么理解\n\n### 二维\n\n- 成功时通常返回 `200`\n- 无命中或超出缩放范围时通常返回 `204`\n- body 是标准 MVT 二进制\n- 若开启 `gzip`，客户端需要支持 gzip 解压\n\n### 三维\n\n- 成功时通常返回 `200`\n- 无命中或超出缩放范围时通常返回 `204`\n- body 是自定义 3D PBF 二进制\n- 若开启 `gzip`，客户端需要支持 gzip 解压\n\n### 常见错误\n\n返回结构一般是：\n\n```json\n{\n  \"statusCode\": 400,\n  \"code\": \"INVALID_TILE_QUERY\",\n  \"message\": \"具体错误信息\",\n  \"details\": {}\n}\n```\n\n常见错误原因：\n\n- `z/x/y` 不是合法瓦片坐标\n- `layer` 不存在\n- `crs` 不在图层允许范围内\n- `from/to` 未配置 `timeField`\n- 数据库连接失败\n\n## `TileModule` 配置怎么写\n\n### `TileModule.forRoot(...)`\n\n最常用配置项如下：\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `layers` | `LayerConfigInput[] \\| Record<string, LayerConfigInput>` | 图层配置集合，定义可访问的图层、几何字段、输出坐标系、cluster、gzip 等能力。 |\n| `database` | `PostgresDatabaseOptions` | PostgreSQL / PostGIS 连接配置，会用于默认的二维和三维查询执行器。 |\n| `controllers` | `false \\| TileControllerOptions` | 控制器开关，可按需启用二维、三维、metadata 和 health 路由。 |\n| `routePrefix` | `string` | 给默认 controller 增加统一前缀，例如 `\"api\"` 会生成 `/api/tiles`、`/api/health`。 |\n| `defaults` | `TileEngineDefaults` | 模块级默认配置，会在图层未显式声明时作为回退值。 |\n| `tile` | `TileRenderOptions` | 瓦片渲染参数，例如 `extent`、`buffer`、`maxFeatures`。 |\n| `healthIndicator` | `() => Promise<Record<string, unknown>>` | 自定义健康检查函数，用于覆盖默认数据库探针。 |\n\n### `controllers` 子项\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `tile` | `boolean` | 是否启用二维接口 `/tiles/...`。 |\n| `tile3d` | `boolean` | 是否启用三维接口 `/tiles-3d/...`。 |\n| `metadata` | `boolean` | 是否启用 `/tiles/:layer/metadata`。 |\n| `health` | `boolean` | 是否启用 `/health`。 |\n\n### `defaults` 常用子项\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `sourceCrs` | `string` | 图层未显式声明时的默认源坐标系。 |\n| `defaultOutputCrs` | `string` | 图层未显式声明时的默认输出坐标系。 |\n| `minZoom` | `number` | 图层未显式声明时的默认最小缩放限制。 |\n| `maxZoom` | `number` | 图层未显式声明时的默认最大缩放限制。 |\n| `clusterEnabled` | `boolean` | 图层未显式声明时是否默认开启 cluster。 |\n| `clusterMaxZoom` | `number` | 图层未显式声明时，cluster 的切换阈值。 |\n| `clusterCellSize` | `number` | 图层未显式声明时，cluster 网格尺寸。 |\n| `clusterMaxFeatures` | `number` | 图层未显式声明时，cluster 输出要素上限。 |\n| `clusterFields` | `object` | 图层未显式声明时，聚合属性字段配置。 |\n| `clusterAltitudeMode` | `string` | 三维 cluster 的高程聚合规则，当前支持 `max`。 |\n| `gzip` | `object` | 响应压缩配置，`gzip.enabled=true` 时二维和三维接口都会返回 gzip 响应。 |\n\n### `database` 常用子项\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `connectionString` | `string` | PostgreSQL 连接串。 |\n| `pool` | `Pool` | 已存在的连接池实例。传入后会直接复用，不会由本包自动关闭。 |\n| `poolConfig` | `PoolConfig` | 细粒度连接池配置。 |\n| `healthCheckQuery` | `string` | 自定义数据库健康检查 SQL。 |\n\n如果同时传了 `pool` 和 `connectionString` / `poolConfig`，会优先使用 `pool`。\n\n### `tile` 常用子项\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `extent` | `number` | 瓦片渲染 extent。 |\n| `buffer` | `number` | 瓦片边缘缓冲区。 |\n| `maxFeatures` | `number` | 单次查询允许返回的最大要素数。 |\n\n### `forRootAsync(...)` 什么时候用\n\n如果你的数据库连接或图层配置要从 `ConfigService`、远程配置或其他 provider 动态生成，使用 `forRootAsync(...)`：\n\n```ts\nTileModule.forRootAsync({\n  routePrefix: \"api\",\n  inject: [ConfigService],\n  useFactory: async (config: ConfigService) => ({\n    layers: buildLayers(config),\n    database: {\n      connectionString: config.getOrThrow(\"DATABASE_URL\"),\n    },\n  }),\n})\n```\n\n`forRootAsync(...)` 下如果要配置统一前缀，请放在 `TileModule.forRootAsync({...})` 的顶层 `routePrefix` 字段里，而不是放进 `useFactory` 返回值中。\n\n## 默认行为与约束\n\n- 二维接口返回标准 MVT，三维接口返回自定义 3D PBF；参数集合尽量一致，但响应体不能互换。\n- `routePrefix` 只影响默认 Nest controller 的基础路径，不影响底层 `core` 引擎。\n- `fields` 不会绕过白名单，最终只会返回 `propertyWhitelist` 允许的字段。\n- `filter` 能力仍存在于底层引擎，但默认 HTTP controller 不会把它暴露成公开查询参数。\n- `from/to` 只有在图层配置了 `timeField` 后才可用。\n- `minZoom/maxZoom` 会与图层配置和模块默认值一起计算最终有效范围。\n- `clusterEnabled=true` 时必须配置 `clusterMaxZoom`。\n- `clusterFields` 的输出别名不能与系统保留字段冲突。\n- `gzip.enabled=true` 时响应会带 `Content-Encoding: gzip`。\n- 三维 cluster 的高度规则当前由 `clusterAltitudeMode` 控制，当前实现为 `max`。\n- 若未显式关闭 controller，`TileModule` 默认会启用二维、三维、metadata、health 四类接口。\n- 内部创建的数据库连接池会在应用关闭时自动释放；外部传入的 `database.pool` 需要由调用方自己执行 `pool.end()`。\n\n## 常见接入方式\n\n### 给 MapLibre 用二维瓦片\n\n```js\nconst source = {\n  type: \"vector\",\n  tiles: [\n    \"http://127.0.0.1:3000/tiles/data/{z}/{x}/{y}.pbf?crs=gcj02&fields=name,color\"\n  ]\n};\n```\n\n### 给前端自定义三维加载器用\n\n```js\nimport { decodeTile } from \"@deepinnet/geojson-to-mvt\";\n\nconst response = await fetch(\n  \"http://127.0.0.1:3000/tiles-3d/data/8/209/111.pbf?crs=gcj02\"\n);\nconst buffer = await response.arrayBuffer();\nconst tile = decodeTile(new Uint8Array(buffer));\n```\n\n## 主要导出\n\n运行时导出：\n\n- `TileModule`\n- `TileHttpResponder`\n- `TileService`\n- `TILE_ENGINE`\n- `TILE_MODULE_OPTIONS`\n\n类型导出：\n\n- `TileModuleOptions`\n- `TileModuleAsyncOptions`\n- `TileControllerOptions`\n- `TileControllersSelection`\n- `TileOptionsFactory`\n\n## 与 core 的关系\n\n这个包只负责 NestJS 集成层。\n\n所有底层能力都来自：\n\n- `@deepinnet/service-mvt-middleware-core`\n\n包括：\n\n- 图层配置类型\n- 瓦片引擎\n- PostgreSQL adapter\n- filter / cluster / gzip 等能力\n\n## 协议\n\n本包按 `Apache-2.0` 协议分发。\n","readmeFilename":"README.md"}