@loaders.gl/pmtiles 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,155 @@
1
+ // loaders.gl, MIT license
2
+
3
+ import type {GetTileParameters, ImageType, DataSourceProps} from '@loaders.gl/loader-utils';
4
+ import type {ImageTileSource, VectorTileSource} from '@loaders.gl/loader-utils';
5
+ import {DataSource, resolvePath} from '@loaders.gl/loader-utils';
6
+ import {ImageLoader} from '@loaders.gl/images';
7
+ import {MVTLoader, MVTLoaderOptions} from '@loaders.gl/mvt';
8
+
9
+ import {PMTiles, Source, RangeResponse} from 'pmtiles';
10
+
11
+ import type {PMTilesMetadata} from './lib/parse-pmtiles';
12
+ import {parsePMTilesHeader} from './lib/parse-pmtiles';
13
+ import {TileLoadParameters} from 'modules/loader-utils/src/lib/sources/tile-source';
14
+
15
+ // export const PMTilesService: Service = {
16
+ // name: 'PMTiles',
17
+ // id: 'pmtiles',
18
+ // module: 'pmtiles',
19
+ // // version: VERSION,
20
+ // extensions: ['pmtiles'],
21
+ // mimeTypes: ['application/octet-stream'],
22
+ // options: {
23
+ // pmtiles: {}
24
+ // }
25
+ // };
26
+
27
+ /**
28
+ * WIP - Loader for pmtiles metadata
29
+ * @note loads metadata only. To load individual tiles, use PMTilesSource
30
+ export const PMTilesLoader: LoaderWithParser<PMTilesMetadata, never, PMTilesLoaderOptions> = {
31
+ name: 'PMTiles',
32
+ id: 'pmtiles',
33
+ module: 'pmtiles',
34
+ version: VERSION,
35
+ worker: true,
36
+ extensions: ['pmtiles'],
37
+ mimeTypes: ['application/octet-stream'],
38
+ options: {
39
+ pmtiles: {}
40
+ },
41
+ parse: async (arrayBuffer, options) => {
42
+ throw new Error('not implemented');
43
+ }
44
+ };
45
+ */
46
+
47
+ export type PMTilesSourceProps = DataSourceProps & {
48
+ url: string | Blob;
49
+ attributions?: string[];
50
+ };
51
+
52
+ export class BlobSource implements Source {
53
+ blob: Blob;
54
+ key: string;
55
+
56
+ constructor(blob: Blob, key: string) {
57
+ this.blob = blob;
58
+ this.key = key;
59
+ }
60
+
61
+ // TODO - how is this used?
62
+ getKey() {
63
+ // @ts-expect-error url is only defined on File subclass
64
+ return this.blob.url || '';
65
+ }
66
+
67
+ async getBytes(offset: number, length: number, signal?: AbortSignal): Promise<RangeResponse> {
68
+ const slice = this.blob.slice(offset, offset + length);
69
+ const data = await slice.arrayBuffer();
70
+ return {
71
+ data
72
+ // etag: response.headers.get('ETag') || undefined,
73
+ // cacheControl: response.headers.get('Cache-Control') || undefined,
74
+ // expires: response.headers.get('Expires') || undefined
75
+ };
76
+ }
77
+ }
78
+ /**
79
+ * A PMTiles data source
80
+ * @note Can be either a raster or vector tile source depending on the contents of the PMTiles file.
81
+ */
82
+ export class PMTilesSource extends DataSource implements ImageTileSource, VectorTileSource {
83
+ props: PMTilesSourceProps;
84
+ pmtiles: PMTiles;
85
+ metadata: Promise<PMTilesMetadata>;
86
+
87
+ constructor(props: PMTilesSourceProps) {
88
+ super(props);
89
+ this.props = props;
90
+ const url =
91
+ typeof props.url === 'string' ? resolvePath(props.url) : new BlobSource(props.url, 'pmtiles');
92
+ this.pmtiles = new PMTiles(url);
93
+ this.getTileData = this.getTileData.bind(this);
94
+ this.metadata = this.getMetadata();
95
+ }
96
+
97
+ async getMetadata(): Promise<PMTilesMetadata> {
98
+ const pmtilesHeader = await this.pmtiles.getHeader();
99
+ const pmtilesMetadata = await this.pmtiles.getMetadata();
100
+ const metadata = parsePMTilesHeader(pmtilesHeader, pmtilesMetadata);
101
+ if (this.props.attributions) {
102
+ metadata.attributions = [...this.props.attributions, ...(metadata.attributions || [])];
103
+ }
104
+ return metadata;
105
+ }
106
+
107
+ async getTile(tileParams: GetTileParameters): Promise<ArrayBuffer | null> {
108
+ const {x, y, zoom: z} = tileParams;
109
+ const rangeResponse = await this.pmtiles.getZxy(z, x, y);
110
+ const arrayBuffer = rangeResponse?.data;
111
+ if (!arrayBuffer) {
112
+ // console.error('No arrayBuffer', tileParams);
113
+ return null;
114
+ }
115
+ return arrayBuffer;
116
+ }
117
+
118
+ // Tile Source interface implementation: deck.gl compatible API
119
+ // TODO - currently only handles image tiles, not vector tiles
120
+
121
+ async getTileData(tileParams: TileLoadParameters): Promise<unknown | null> {
122
+ const {x, y, z} = tileParams.index;
123
+ const metadata = await this.metadata;
124
+ switch (metadata.mimeType) {
125
+ case 'application/vnd.mapbox-vector-tile':
126
+ return await this.getVectorTile({x, y, zoom: z, layers: []});
127
+ default:
128
+ return await this.getImageTile({x, y, zoom: z, layers: []});
129
+ }
130
+ }
131
+
132
+ // ImageTileSource interface implementation
133
+
134
+ async getImageTile(tileParams: GetTileParameters): Promise<ImageType | null> {
135
+ const arrayBuffer = await this.getTile(tileParams);
136
+ return arrayBuffer ? await ImageLoader.parse(arrayBuffer, this.loadOptions) : null;
137
+ }
138
+
139
+ // VectorTileSource interface implementation
140
+
141
+ async getVectorTile(tileParams: GetTileParameters): Promise<unknown | null> {
142
+ const arrayBuffer = await this.getTile(tileParams);
143
+ const loadOptions: MVTLoaderOptions = {
144
+ shape: 'geojson-table',
145
+ mvt: {
146
+ coordinates: 'wgs84',
147
+ tileIndex: {x: tileParams.x, y: tileParams.y, z: tileParams.zoom},
148
+ ...(this.loadOptions as MVTLoaderOptions)?.mvt
149
+ },
150
+ ...this.loadOptions
151
+ };
152
+
153
+ return arrayBuffer ? await MVTLoader.parse(arrayBuffer, loadOptions) : null;
154
+ }
155
+ }