UNPKG

protomaps-leaflet

Version:

Vector tile rendering and labeling for [Leaflet](https://github.com/Leaflet/Leaflet).

205 lines (204 loc) 7.99 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import Point from "@mapbox/point-geometry"; import { PmtilesSource, TileCache, ZxySource, } from "./tilecache"; // TODO make this lazy export const transformGeom = (geom, scale, translate) => { const retval = []; for (const arr of geom) { const loop = []; for (const coord of arr) { loop.push(coord.clone().mult(scale).add(translate)); } retval.push(loop); } return retval; }; export const wrap = (val, z) => { const dim = 1 << z; if (val < 0) return dim + val; if (val >= dim) return val % dim; return val; }; /* * @class View * expresses relationship between canvas coordinates and data tiles. */ export class View { constructor(tileCache, maxDataLevel, levelDiff) { this.tileCache = tileCache; this.maxDataLevel = maxDataLevel; this.levelDiff = levelDiff; } dataTilesForBounds(displayZoom, bounds) { const fractional = Math.pow(2, displayZoom) / Math.pow(2, Math.ceil(displayZoom)); const needed = []; let scale = 1; const dim = this.tileCache.tileSize; if (displayZoom < this.levelDiff) { scale = (1 / (1 << (this.levelDiff - displayZoom))) * fractional; needed.push({ dataTile: { z: 0, x: 0, y: 0 }, origin: new Point(0, 0), scale: scale, dim: dim * scale, }); } else if (displayZoom <= this.levelDiff + this.maxDataLevel) { const f = 1 << this.levelDiff; const basetileSize = 256 * fractional; const dataZoom = Math.ceil(displayZoom) - this.levelDiff; const mintileX = Math.floor(bounds.minX / f / basetileSize); const mintileY = Math.floor(bounds.minY / f / basetileSize); const maxtileX = Math.floor(bounds.maxX / f / basetileSize); const maxtileY = Math.floor(bounds.maxY / f / basetileSize); for (let tx = mintileX; tx <= maxtileX; tx++) { for (let ty = mintileY; ty <= maxtileY; ty++) { const origin = new Point(tx * f * basetileSize, ty * f * basetileSize); needed.push({ dataTile: { z: dataZoom, x: wrap(tx, dataZoom), y: wrap(ty, dataZoom), }, origin: origin, scale: fractional, dim: dim * fractional, }); } } } else { const f = 1 << this.levelDiff; scale = (1 << (Math.ceil(displayZoom) - this.maxDataLevel - this.levelDiff)) * fractional; const mintileX = Math.floor(bounds.minX / f / 256 / scale); const mintileY = Math.floor(bounds.minY / f / 256 / scale); const maxtileX = Math.floor(bounds.maxX / f / 256 / scale); const maxtileY = Math.floor(bounds.maxY / f / 256 / scale); for (let tx = mintileX; tx <= maxtileX; tx++) { for (let ty = mintileY; ty <= maxtileY; ty++) { const origin = new Point(tx * f * 256 * scale, ty * f * 256 * scale); needed.push({ dataTile: { z: this.maxDataLevel, x: wrap(tx, this.maxDataLevel), y: wrap(ty, this.maxDataLevel), }, origin: origin, scale: scale, dim: dim * scale, }); } } } return needed; } dataTileForDisplayTile(displayTile) { let dataTile; let scale = 1; let dim = this.tileCache.tileSize; let origin; if (displayTile.z < this.levelDiff) { dataTile = { z: 0, x: 0, y: 0 }; scale = 1 / (1 << (this.levelDiff - displayTile.z)); origin = new Point(0, 0); dim = dim * scale; } else if (displayTile.z <= this.levelDiff + this.maxDataLevel) { const f = 1 << this.levelDiff; dataTile = { z: displayTile.z - this.levelDiff, x: Math.floor(displayTile.x / f), y: Math.floor(displayTile.y / f), }; origin = new Point(dataTile.x * f * 256, dataTile.y * f * 256); } else { scale = 1 << (displayTile.z - this.maxDataLevel - this.levelDiff); const f = 1 << this.levelDiff; dataTile = { z: this.maxDataLevel, x: Math.floor(displayTile.x / f / scale), y: Math.floor(displayTile.y / f / scale), }; origin = new Point(dataTile.x * f * scale * 256, dataTile.y * f * scale * 256); dim = dim * scale; } return { dataTile: dataTile, scale: scale, origin: origin, dim: dim }; } getBbox(displayZoom, bounds) { return __awaiter(this, void 0, void 0, function* () { const needed = this.dataTilesForBounds(displayZoom, bounds); const result = yield Promise.all(needed.map((tt) => this.tileCache.get(tt.dataTile))); return result.map((data, i) => { const tt = needed[i]; return { data: data, z: displayZoom, dataTile: tt.dataTile, scale: tt.scale, dim: tt.dim, origin: tt.origin, }; }); }); } getDisplayTile(displayTile) { return __awaiter(this, void 0, void 0, function* () { const tt = this.dataTileForDisplayTile(displayTile); const data = yield this.tileCache.get(tt.dataTile); return { data: data, z: displayTile.z, dataTile: tt.dataTile, scale: tt.scale, origin: tt.origin, dim: tt.dim, }; }); } } export const sourcesToViews = (options) => { const sourceToViews = (o) => { const levelDiff = o.levelDiff === undefined ? 1 : o.levelDiff; const maxDataZoom = o.maxDataZoom || 15; let source; if (typeof o.url === "string") { if (o.url.endsWith(".pmtiles")) { source = new PmtilesSource(o.url, true); } else { source = new ZxySource(o.url, true); } } else if (o.url) { source = new PmtilesSource(o.url, true); } else { throw new Error(`Invalid source ${o.url}`); } const cache = new TileCache(source, (256 * 1) << levelDiff); return new View(cache, maxDataZoom, levelDiff); }; const sources = new Map(); if (options.sources) { for (const key in options.sources) { sources.set(key, sourceToViews(options.sources[key])); } } else { sources.set("", sourceToViews(options)); } return sources; };