tile-batch
Version:
Data serializers and WebGL renderers for tiled layers in vector maps
1,687 lines (1,409 loc) • 154 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.tileBatch = {}));
})(this, (function (exports) { 'use strict';
var preamble = `#version 300 es
precision highp float;
in vec3 tileCoords;
uniform vec4 mapCoords; // x, y, z, extent of tileset[0]
uniform vec3 mapShift; // translate and scale of tileset[0]
uniform vec4 screenScale; // 2 / width, -2 / height, pixRatio, cameraScale
vec2 tileToMap(vec2 tilePos) {
// Find distance of this tile from top left tile, in tile units
float zoomFac = exp2(mapCoords.z - tileCoords.z);
vec2 dTile = zoomFac * tileCoords.xy - mapCoords.xy;
// tileCoords.x and mapCoords.x are both wrapped to the range [0..exp2(z)]
// If the right edge of the tile is left of the map, we need to unwrap dTile
dTile.x += (dTile.x + zoomFac <= 0.0) ? exp2(mapCoords.z) : 0.0;
// Convert to a translation in pixels
vec2 tileTranslate = dTile * mapShift.z + mapShift.xy;
// Find scaling between tile coordinates and screen pixels
float tileScale = zoomFac * mapShift.z / mapCoords.w;
return tilePos * tileScale + tileTranslate;
}
vec4 mapToClip(vec2 mapPos, float z) {
vec2 projected = mapPos * screenScale.xy + vec2(-1.0, 1.0);
return vec4(projected, z, 1);
}
`;
var simpleScale = `float styleScale(vec2 tilePos) {
return screenScale.z;
}
`;
var mercatorScale = `const float TWOPI = 6.28318530718;
float mercatorScale(float yWeb) {
// Convert Web Mercator Y to standard Mercator Y
float yMerc = TWOPI * (0.5 - yWeb);
return 0.5 * (exp(yMerc) + exp(-yMerc)); // == cosh(y)
}
float styleScale(vec2 tilePos) {
float y = (tileCoords.y + tilePos.y / mapCoords.w) / exp2(tileCoords.z);
return screenScale.z * mercatorScale(y) / screenScale.w;
}
`;
var defaultPreamble = `#version 300 es
precision highp float;
uniform vec4 screenScale; // 2 / width, -2 / height, pixRatio, cameraScale
vec2 tileToMap(vec2 tilePos) {
return tilePos * screenScale.z;
}
vec4 mapToClip(vec2 mapPos, float z) {
vec2 projected = mapPos * screenScale.xy + vec2(-1.0, 1.0);
return vec4(projected, z, 1.0);
}
float styleScale(vec2 tilePos) {
return screenScale.z;
}
`;
function setParams$1(userParams) {
const {
context, framebuffer, extraAttributes,
preamble = defaultPreamble,
} = userParams;
return { context, framebuffer, preamble, extraAttributes };
}
var vert$4 = `in vec2 quadPos;
void main() {
gl_Position = vec4(quadPos, 0.0, 1.0);
}
`;
var frag$4 = `#version 300 es
precision mediump float;
uniform vec4 backgroundColor;
uniform float backgroundOpacity;
out vec4 pixColor;
void main() {
float alpha = backgroundColor.a * backgroundOpacity;
pixColor = vec4(backgroundColor.rgb * alpha, alpha);
}
`;
function initBackground(context) {
const quadPos = context.initQuad();
const styleKeys = ["background-color", "background-opacity"];
return {
vert: vert$4, frag: frag$4, styleKeys,
getSpecialAttrs: () => ({ quadPos }),
countInstances: () => 1,
};
}
var vert$3 = `in vec2 quadPos; // Vertices of the quad instance
in vec2 circlePos;
in float circleRadius;
in vec4 circleColor;
in float circleOpacity;
out vec2 delta;
out vec4 strokeStyle;
out float radius;
void main() {
vec2 mapPos = tileToMap(circlePos);
// Shift to the appropriate corner of the current instance quad
delta = quadPos * (circleRadius + 1.0);
vec2 dPos = delta * styleScale(circlePos);
strokeStyle = circleColor * circleOpacity;
// TODO: normalize delta? Then can drop one varying
radius = circleRadius;
gl_Position = mapToClip(mapPos + dPos, 0.0);
}
`;
var frag$3 = `#version 300 es
precision mediump float;
in vec2 delta;
in vec4 strokeStyle;
in float radius;
out vec4 pixColor;
void main() {
float r = length(delta);
float dr = fwidth(r);
float taper = 1.0 - smoothstep(radius - dr, radius + dr, r);
pixColor = strokeStyle * taper;
}
`;
function initCircle(context) {
const attrInfo = {
circlePos: { numComponents: 2 },
circleRadius: { numComponents: 1 },
circleColor: { numComponents: 4 },
circleOpacity: { numComponents: 1 },
};
const quadPos = context.initQuad({ x0: -1.0, y0: -1.0, x1: 1.0, y1: 1.0 });
const styleKeys = ["circle-radius", "circle-color", "circle-opacity"];
return {
vert: vert$3, frag: frag$3, attrInfo, styleKeys,
getSpecialAttrs: () => ({ quadPos }),
countInstances: (buffers) => buffers.circlePos.length / 2,
};
}
var vert$2 = `in vec2 quadPos;
in vec3 pointA, pointB, pointC, pointD;
in vec4 lineColor;
in float lineOpacity, lineWidth, lineGapWidth;
uniform float lineMiterLimit;
const int numDashes = 4;
uniform float lineDasharray[numDashes];
out float yCoord;
flat out vec2 lineSize; // lineWidth, lineGapWidth
out vec2 miterCoord1, miterCoord2;
flat out vec4 strokeStyle;
flat out float dashPattern[numDashes];
out float lineSoFar;
mat3 miterTransform(vec2 xHat, vec2 yHat, vec2 v, float pixWidth) {
// Find a coordinate basis vector aligned along the bisector
bool isCap = length(v) < 0.0001; // TODO: think about units
vec2 vHat = (isCap)
? xHat // Treat v = 0 like 180 deg turn
: normalize(v);
vec2 m0 = (dot(xHat, vHat) < -0.9999)
? yHat // For vHat == -xHat
: normalize(xHat + vHat);
// Find a perpendicular basis vector, pointing toward xHat
float x_m0 = dot(xHat, m0);
vec2 m1 = (x_m0 < 0.9999)
? normalize(xHat - vHat)
: yHat;
// Compute miter length
float sin2 = 1.0 - x_m0 * x_m0; // Could be zero!
float miterLength = (sin2 > 0.0001)
? inversesqrt(sin2)
: lineMiterLimit + 1.0;
float bevelLength = abs(dot(yHat, m0));
float tx = (miterLength > lineMiterLimit)
? 0.5 * pixWidth * bevelLength
: 0.5 * pixWidth * miterLength;
float ty = isCap ? 1.2 * pixWidth : 0.0;
return mat3(m0.x, m1.x, 0, m0.y, m1.y, 0, tx, ty, 1);
}
float sumComponents(float[numDashes] v) {
float sum = 0.0;
for (int i = 0; i < v.length(); i++) {
sum += v[i];
}
return sum;
}
void main() {
// Transform vertex positions from tile to map coordinates
vec2 mapA = tileToMap(pointA.xy);
vec2 mapB = tileToMap(pointB.xy);
vec2 mapC = tileToMap(pointC.xy);
vec2 mapD = tileToMap(pointD.xy);
vec2 xAxis = mapC - mapB;
vec2 xBasis = normalize(xAxis);
vec2 yBasis = vec2(-xBasis.y, xBasis.x);
// Get coordinate transforms for the miters
float pixWidth = (lineGapWidth > 0.0)
? (lineGapWidth + 2.0 * lineWidth) * screenScale.z
: lineWidth * screenScale.z;
mat3 m1 = miterTransform(xBasis, yBasis, mapA - mapB, pixWidth);
mat3 m2 = miterTransform(-xBasis, yBasis, mapD - mapC, pixWidth);
// Find the position of the current instance vertex, in 3 coordinate systems
vec2 extend = lineMiterLimit * xBasis * pixWidth * (quadPos.x - 0.5);
// Add one pixel on either side of the line for the anti-alias taper
float y = (pixWidth + 2.0) * quadPos.y;
vec2 point = mapB + xAxis * quadPos.x + yBasis * y + extend;
miterCoord1 = (m1 * vec3(point - mapB, 1)).xy;
miterCoord2 = (m2 * vec3(point - mapC, 1)).xy;
// Remove pixRatio from varying (we taper edges using unscaled value)
yCoord = y / screenScale.z;
lineSize = vec2(lineWidth, lineGapWidth);
// TODO: should this premultiplication be done in tile-stencil?
//vec4 premult = vec4(color.rgb * color.a, color.a);
//strokeStyle = premult * opacity;
strokeStyle = lineColor * lineOpacity;
float dashLength = sumComponents(lineDasharray) * lineWidth;
if (dashLength <= 0.0) dashLength = 1.0;
float dashScale = lineWidth / dashLength;
dashPattern[0] = lineDasharray[0] * dashScale;
for (int i = 1; i < lineDasharray.length(); i++) {
dashPattern[i] = dashPattern[i - 1] + lineDasharray[i] * dashScale;
}
float xLen = length(xAxis) / screenScale.z;
float extendRatio = length(extend) / screenScale.z / xLen;
float stretch = xLen / (pointC.z - pointB.z);
float dist0 = pointB.z * stretch / dashLength;
float dDist = (pointC.z - pointB.z) * stretch / dashLength;
float eDist = dDist * extendRatio;
lineSoFar = dist0 - eDist + quadPos.x * (dDist + 2.0 * eDist);
float z = (min(pointB.z, pointC.z) < 0.0) ? -2.0 : 0.0;
gl_Position = mapToClip(point, z);
}
`;
var frag$2 = `#version 300 es
precision highp float;
in float yCoord;
flat in vec2 lineSize; // lineWidth, lineGapWidth
in vec2 miterCoord1, miterCoord2;
flat in vec4 strokeStyle;
flat in float dashPattern[4];
in float lineSoFar;
out vec4 pixColor;
float taper(float edge, float width, float x) {
return smoothstep(edge - width, edge + width, x);
}
float muteGap(float start, float end, float ramp, float x) {
return (start < end)
? 1.0 - taper(start, ramp, x) * taper(-end, ramp, -x)
: 1.0;
}
void main() {
float step0 = fwidth(yCoord) * 0.707;
vec2 step1 = fwidth(miterCoord1) * 0.707;
vec2 step2 = fwidth(miterCoord2) * 0.707;
// Antialiasing tapers for line edges
float hGap = 0.5 * lineSize.y;
float inner = (hGap > 0.0) ? taper(hGap, step0, abs(yCoord)) : 1.0;
float hWidth = (hGap > 0.0) ? hGap + lineSize.x : 0.5 * lineSize.x;
float outer = taper(-hWidth, step0, -abs(yCoord));
float antialias = inner * outer;
// Bevels, endcaps: Use smooth taper for antialiasing
float taperx =
taper(0.0, step1.x, miterCoord1.x) *
taper(0.0, step2.x, miterCoord2.x);
// Miters: Use hard step, slightly shifted to avoid overlap at center
float tapery =
step(-0.01 * step1.y, miterCoord1.y) *
step(0.01 * step2.y, miterCoord2.y);
// Dashes
float dashX = fract(lineSoFar);
float stepD = fwidth(lineSoFar) * 0.707;
float gap1 = muteGap(dashPattern[0], dashPattern[1], stepD, dashX);
float gap2 = muteGap(dashPattern[2], dashPattern[3], stepD, dashX);
float dashMute = min(gap1, gap2);
pixColor = strokeStyle * antialias * taperx * tapery * dashMute;
}
`;
function initLine(context) {
const { initQuad, createBuffer, initAttribute } = context;
const attrInfo = {
lineColor: { numComponents: 4 },
lineOpacity: { numComponents: 1 },
lineWidth: { numComponents: 1 },
lineGapWidth: { numComponents: 1 },
};
const quadPos = initQuad({ x0: 0.0, y0: -0.5, x1: 1.0, y1: 0.5 });
const numComponents = 3;
const stride = Float32Array.BYTES_PER_ELEMENT * numComponents;
function getSpecialAttrs(buffers) {
// Create buffer containing the vertex positions
const buffer = createBuffer(buffers.lines);
// Construct interleaved attributes pointing to different offsets in buffer
function setupPoint(shift) {
const offset = shift * stride;
return initAttribute({ buffer, numComponents, stride, offset });
}
return {
quadPos,
pointA: setupPoint(0),
pointB: setupPoint(1),
pointC: setupPoint(2),
pointD: setupPoint(3),
};
}
const styleKeys = [
// NOTE: line-miter-limit is a layout property in the style spec
// We copied the function to a paint property in ../main.js
"line-miter-limit",
// Other layout properties not implemented yet:
// "line-cap", "line-join",
// Paint properties:
"line-color", "line-opacity",
"line-width", "line-gap-width", "line-dasharray",
// "line-translate", "line-translate-anchor",
// "line-offset", "line-blur", "line-gradient", "line-pattern"
];
return {
vert: vert$2, frag: frag$2, attrInfo, styleKeys, getSpecialAttrs,
countInstances: (buffers) => buffers.lines.length / numComponents - 3,
};
}
var vert$1 = `in vec2 position;
in vec4 fillColor;
in float fillOpacity;
uniform vec2 fillTranslate;
out vec4 fillStyle;
void main() {
vec2 mapPos = tileToMap(position) + fillTranslate * screenScale.z;
fillStyle = fillColor * fillOpacity;
gl_Position = mapToClip(mapPos, 0.0);
}
`;
var frag$1 = `#version 300 es
precision mediump float;
in vec4 fillStyle;
out vec4 pixColor;
void main() {
pixColor = fillStyle;
}
`;
function initFill() {
const attrInfo = {
position: { numComponents: 2, divisor: 0 },
fillColor: { numComponents: 4, divisor: 0 },
fillOpacity: { numComponents: 1, divisor: 0 },
};
const styleKeys = ["fill-color", "fill-opacity", "fill-translate"];
return {
vert: vert$1, frag: frag$1, attrInfo, styleKeys,
getSpecialAttrs: () => ({}),
};
}
var vert = `in vec2 quadPos; // Vertices of the quad instance
in vec4 labelPos; // x, y, angle, font size scalar (0 for icons)
in vec4 glyphPos; // dx, dy (relative to labelPos), w, h
in vec4 glyphRect; // x, y, w, h
in float iconOpacity;
in vec4 textColor;
in float textOpacity;
in float textHaloBlur;
in vec4 textHaloColor;
in float textHaloWidth;
out vec2 texCoord;
out float opacity;
out vec4 fillColor;
out vec4 haloColor;
out vec2 haloSize; // width, blur
out float taperWidth;
void main() {
// For icons only
opacity = iconOpacity;
// For text only
taperWidth = labelPos.w * screenScale.z; // == 0.0 for icon glyphs
haloSize = vec2(textHaloWidth, textHaloBlur) * screenScale.z;
float fillAlpha = textColor.a * textOpacity;
fillColor = vec4(textColor.rgb * fillAlpha, fillAlpha);
float haloAlpha = textHaloColor.a * textOpacity;
haloColor = vec4(textHaloColor.rgb * haloAlpha, haloAlpha);
// Texture coordinates
texCoord = glyphRect.xy + glyphRect.zw * quadPos;
// Compute glyph position. First transform the label origin
vec2 mapPos = tileToMap(labelPos.xy);
// Shift to the appropriate corner of the current instance quad
vec2 dPos = (glyphPos.xy + glyphPos.zw * quadPos) * styleScale(labelPos.xy);
float cos_a = cos(labelPos.z);
float sin_a = sin(labelPos.z);
float dx = dPos.x * cos_a - dPos.y * sin_a;
float dy = dPos.x * sin_a + dPos.y * cos_a;
gl_Position = mapToClip(mapPos + vec2(dx, dy), 0.0);
}
`;
var frag = `#version 300 es
precision highp float;
uniform sampler2D sprite, sdf;
in vec2 texCoord;
in float opacity;
in vec4 fillColor;
in vec4 haloColor;
in vec2 haloSize; // width, blur
in float taperWidth; // 0 for icons
out vec4 pixColor;
void main() {
// Get color from sprite if this is an icon glyph
vec4 spritePix = texture(sprite, texCoord);
// Input sprite does NOT have pre-multiplied alpha
vec4 iconColor = vec4(spritePix.rgb * spritePix.a, spritePix.a) * opacity;
// Compute fill and halo color from sdf if this is a text glyph
float sdfVal = texture(sdf, texCoord).a;
float screenDist = taperWidth * (191.0 - 255.0 * sdfVal) / 32.0;
float fillAlpha = smoothstep(-0.707, 0.707, -screenDist);
float hEdge = haloSize.x - haloSize.y / 2.0;
float hTaper = haloSize.x + haloSize.y / 2.0;
float haloAlpha = (haloSize.x > 0.0 || haloSize.y > 0.0)
? (1.0 - fillAlpha) * smoothstep(-hTaper, -hEdge, -screenDist)
: 0.0;
vec4 textColor = fillColor * fillAlpha + haloColor * haloAlpha;
// Choose icon or text color based on taperWidth value
pixColor = (taperWidth == 0.0) ? iconColor : textColor;
}
`;
function initSymbol(context) {
const attrInfo = {
labelPos: { numComponents: 4 },
glyphPos: { numComponents: 4 },
glyphRect: { numComponents: 4 },
iconOpacity: { numComponents: 1 },
textColor: { numComponents: 4 },
textOpacity: { numComponents: 1 },
textHaloBlur: { numComponents: 1 },
textHaloColor: { numComponents: 4 },
textHaloWidth: { numComponents: 1 },
};
const quadPos = context.initQuad({ x0: 0.0, y0: 0.0, x1: 1.0, y1: 1.0 });
const styleKeys = [
"icon-opacity",
"text-color",
"text-opacity",
"text-halo-blur",
"text-halo-color",
"text-halo-width",
];
return {
vert, frag, attrInfo, styleKeys,
getSpecialAttrs: () => ({ quadPos }),
countInstances: (buffers) => buffers.labelPos.length / 4,
};
}
function initLoader(context, info, constructVao, extraAttributes) {
const { initAttribute, initIndices } = context;
const { attrInfo, getSpecialAttrs, countInstances } = info;
const allAttrs = Object.assign({}, attrInfo, extraAttributes);
function getAttributes(buffers) {
return Object.entries(allAttrs).reduce((d, [key, info]) => {
const data = buffers[key];
if (data) d[key] = initAttribute(Object.assign({ data }, info));
return d;
}, getSpecialAttrs(buffers));
}
function loadInstanced(buffers) {
const attributes = getAttributes(buffers);
const vao = constructVao({ attributes });
return { vao, instanceCount: countInstances(buffers) };
}
function loadIndexed(buffers) {
const attributes = getAttributes(buffers);
const indices = initIndices({ data: buffers.indices });
const vao = constructVao({ attributes, indices });
return { vao, indices, count: buffers.indices.length };
}
return (countInstances) ? loadInstanced : loadIndexed;
}
function compilePrograms(params) {
const { context, preamble, extraAttributes } = params;
const progInfo = {
background: initBackground(context),
circle: initCircle(context),
line: initLine(context),
fill: initFill(),
symbol: initSymbol(context),
};
function compile(info) {
const { vert, frag, styleKeys } = info;
const program = context.initProgram(preamble + vert, frag);
const { use, constructVao, uniformSetters } = program;
const load = initLoader(context, info, constructVao, extraAttributes);
return { load, use, uniformSetters, styleKeys };
}
return Object.entries(progInfo)
.reduce((d, [k, info]) => (d[k] = compile(info), d), {});
}
function camelCase$2(hyphenated) {
return hyphenated.replace(/-([a-z])/gi, (h, c) => c.toUpperCase());
}
function initStyleProg(style, program, context, framebuffer) {
if (!program) return;
const { id, type, layout, paint } = style;
const { load, use, uniformSetters, styleKeys } = program;
const { sdf, screenScale } = uniformSetters;
if (type === "line") {
// We handle line-miter-limit in the paint phase, not layout phase
paint["line-miter-limit"] = layout["line-miter-limit"];
}
const zoomFuncs = styleKeys
.filter(styleKey => paint[styleKey].type !== "property")
.map(styleKey => {
const get = paint[styleKey];
const shaderVar = camelCase$2(styleKey);
const set = uniformSetters[shaderVar];
return (z, f) => set(get(z, f));
});
function setStyles(zoom, pixRatio = 1.0, cameraScale = 1.0) {
use();
zoomFuncs.forEach(f => f(zoom));
if (!screenScale) return;
const { width, height } = framebuffer.size;
screenScale([2 / width, -2 / height, pixRatio, cameraScale]);
}
const getData = (type === "background") ? initBackgroundData() : getFeatures;
function draw(tile) {
const data = getData(tile);
if (data) context.draw(data.buffers);
}
function initBackgroundData() {
const buffers = load({});
return () => ({ buffers });
}
function getFeatures(tile) {
const { layers: { [id]: layer }, atlas } = tile.data;
if (sdf && atlas) sdf(atlas);
return layer;
}
return { id, type, setStyles, getData, uniformSetters, paint: draw };
}
function initGL$1(userParams) {
const params = setParams$1(userParams);
const { context, framebuffer } = params;
const programs = compilePrograms(params);
let spriteTexture;
const spriteSetters = Object.values(programs)
.map(({ use, uniformSetters }) => ({ use, set: uniformSetters.sprite }))
.filter(setter => setter.set !== undefined);
function loadSprite(image) {
if (image) spriteTexture = context.initTexture({ image, mips: false });
}
return { prep, loadAtlas, loadBuffers, loadSprite, initPainter };
function prep() {
context.bindFramebufferAndSetViewport(framebuffer);
spriteSetters.forEach(({ use, set }) => (use(), set(spriteTexture)));
return context.clear();
}
function loadAtlas(atlas) { // TODO: name like loadSprite, different behavior
const format = context.gl.ALPHA;
const { width, height, data } = atlas;
return context.initTexture({ format, width, height, data, mips: false });
}
function loadBuffers(layer) {
const program = programs[layer.type];
if (!program) throw Error("tile-gl loadBuffers: unknown layer type");
layer.buffers = program.load(layer.buffers);
}
function initPainter(style) {
return initStyleProg(style, programs[style.type], context, framebuffer);
}
}
function antiMeridianSplit(tileset) {
// At low zooms, some tiles may be repeated on opposite ends of the map
// We split them into subsets, one tileset for each copy of the map
const { 0: { x, z }, translate, scale } = tileset;
const numTiles = 1 << z;
function inRange(tile, shift) {
const delta = tile.x - x - shift;
return (0 <= delta && delta < numTiles);
}
return [0, 1, 2]
.map(repeat => repeat * numTiles)
.map(shift => tileset.filter(tile => inRange(tile, shift)))
.map(tiles => Object.assign(tiles, { translate, scale }))
.filter(subset => subset.length);
}
function initTilesetPainter(layer, context, fbSize) {
const { mapCoords, mapShift } = layer.uniformSetters;
return (layer.type === "background") ? paintBackground : paintTileset;
function paintBackground({ zoom, pixRatio = 1.0, cameraScale = 1.0 }) {
layer.setStyles(zoom, pixRatio, cameraScale);
layer.paint();
}
function paintTileset({ tileset, zoom, pixRatio = 1.0, cameraScale = 1.0 }) {
if (!tileset || !tileset.length) return;
layer.setStyles(zoom, pixRatio, cameraScale);
// Set mapCoords
const { x, y, z } = tileset[0];
const numTiles = 1 << z;
const xw = x - Math.floor(x / numTiles) * numTiles;
const extent = 512; // TODO: don't assume this!!
mapCoords([xw, y, z, extent]);
// Draw tiles. Split into subsets if they are repeated across antimeridian
antiMeridianSplit(tileset).forEach(subset => drawSubset(subset, pixRatio));
}
function drawSubset(tileset, pixRatio = 1) {
const { 0: { x, y }, translate, scale: rawScale } = tileset;
const scale = rawScale * pixRatio;
const [dx, dy] = [x, y].map((c, i) => (c + translate[i]) * scale);
mapShift([dx, dy, scale]);
tileset.forEach(tile => drawTileBox(tile, translate, scale));
}
function drawTileBox(box, translate, scale) {
const { x, y, tile } = box;
const data = layer.getData(tile);
if (!data) return;
const [x0, y0] = [x, y].map((c, i) => (c + translate[i]) * scale);
clipRect(x0, y0, scale, scale);
context.draw(data.buffers);
}
function clipRect(x, y, w, h) {
const yflip = fbSize.height - y - h;
context.clipRect(x, yflip, w, h);
}
}
function initGL(userParams) {
const {
context, framebuffer,
projScale = false,
} = userParams;
const scaleCode = (projScale) ? mercatorScale : simpleScale;
const tileContext = initGL$1({
context, framebuffer,
preamble: preamble + scaleCode,
extraAttributes: { tileCoords: { numComponents: 3 } },
});
// Replace initPainter method with a multi-tile program
const initPainter = tileContext.initPainter;
tileContext.initPainter = function(style) {
const layer = initPainter(style);
const painter = (layer)
? initTilesetPainter(layer, context, framebuffer.size)
: () => null;
const { id, type, source, minzoom = 0, maxzoom = 24 } = style;
return Object.assign(painter, { id, type, source, minzoom, maxzoom });
};
return tileContext;
}
function define(constructor, factory, prototype) {
constructor.prototype = factory.prototype = prototype;
prototype.constructor = constructor;
}
function extend$1(parent, definition) {
var prototype = Object.create(parent.prototype);
for (var key in definition) prototype[key] = definition[key];
return prototype;
}
function Color() {}
var darker = 0.7;
var brighter = 1 / darker;
var reI = "\\s*([+-]?\\d+)\\s*",
reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
reHex = /^#([0-9a-f]{3,8})$/,
reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
var named = {
aliceblue: 0xf0f8ff,
antiquewhite: 0xfaebd7,
aqua: 0x00ffff,
aquamarine: 0x7fffd4,
azure: 0xf0ffff,
beige: 0xf5f5dc,
bisque: 0xffe4c4,
black: 0x000000,
blanchedalmond: 0xffebcd,
blue: 0x0000ff,
blueviolet: 0x8a2be2,
brown: 0xa52a2a,
burlywood: 0xdeb887,
cadetblue: 0x5f9ea0,
chartreuse: 0x7fff00,
chocolate: 0xd2691e,
coral: 0xff7f50,
cornflowerblue: 0x6495ed,
cornsilk: 0xfff8dc,
crimson: 0xdc143c,
cyan: 0x00ffff,
darkblue: 0x00008b,
darkcyan: 0x008b8b,
darkgoldenrod: 0xb8860b,
darkgray: 0xa9a9a9,
darkgreen: 0x006400,
darkgrey: 0xa9a9a9,
darkkhaki: 0xbdb76b,
darkmagenta: 0x8b008b,
darkolivegreen: 0x556b2f,
darkorange: 0xff8c00,
darkorchid: 0x9932cc,
darkred: 0x8b0000,
darksalmon: 0xe9967a,
darkseagreen: 0x8fbc8f,
darkslateblue: 0x483d8b,
darkslategray: 0x2f4f4f,
darkslategrey: 0x2f4f4f,
darkturquoise: 0x00ced1,
darkviolet: 0x9400d3,
deeppink: 0xff1493,
deepskyblue: 0x00bfff,
dimgray: 0x696969,
dimgrey: 0x696969,
dodgerblue: 0x1e90ff,
firebrick: 0xb22222,
floralwhite: 0xfffaf0,
forestgreen: 0x228b22,
fuchsia: 0xff00ff,
gainsboro: 0xdcdcdc,
ghostwhite: 0xf8f8ff,
gold: 0xffd700,
goldenrod: 0xdaa520,
gray: 0x808080,
green: 0x008000,
greenyellow: 0xadff2f,
grey: 0x808080,
honeydew: 0xf0fff0,
hotpink: 0xff69b4,
indianred: 0xcd5c5c,
indigo: 0x4b0082,
ivory: 0xfffff0,
khaki: 0xf0e68c,
lavender: 0xe6e6fa,
lavenderblush: 0xfff0f5,
lawngreen: 0x7cfc00,
lemonchiffon: 0xfffacd,
lightblue: 0xadd8e6,
lightcoral: 0xf08080,
lightcyan: 0xe0ffff,
lightgoldenrodyellow: 0xfafad2,
lightgray: 0xd3d3d3,
lightgreen: 0x90ee90,
lightgrey: 0xd3d3d3,
lightpink: 0xffb6c1,
lightsalmon: 0xffa07a,
lightseagreen: 0x20b2aa,
lightskyblue: 0x87cefa,
lightslategray: 0x778899,
lightslategrey: 0x778899,
lightsteelblue: 0xb0c4de,
lightyellow: 0xffffe0,
lime: 0x00ff00,
limegreen: 0x32cd32,
linen: 0xfaf0e6,
magenta: 0xff00ff,
maroon: 0x800000,
mediumaquamarine: 0x66cdaa,
mediumblue: 0x0000cd,
mediumorchid: 0xba55d3,
mediumpurple: 0x9370db,
mediumseagreen: 0x3cb371,
mediumslateblue: 0x7b68ee,
mediumspringgreen: 0x00fa9a,
mediumturquoise: 0x48d1cc,
mediumvioletred: 0xc71585,
midnightblue: 0x191970,
mintcream: 0xf5fffa,
mistyrose: 0xffe4e1,
moccasin: 0xffe4b5,
navajowhite: 0xffdead,
navy: 0x000080,
oldlace: 0xfdf5e6,
olive: 0x808000,
olivedrab: 0x6b8e23,
orange: 0xffa500,
orangered: 0xff4500,
orchid: 0xda70d6,
palegoldenrod: 0xeee8aa,
palegreen: 0x98fb98,
paleturquoise: 0xafeeee,
palevioletred: 0xdb7093,
papayawhip: 0xffefd5,
peachpuff: 0xffdab9,
peru: 0xcd853f,
pink: 0xffc0cb,
plum: 0xdda0dd,
powderblue: 0xb0e0e6,
purple: 0x800080,
rebeccapurple: 0x663399,
red: 0xff0000,
rosybrown: 0xbc8f8f,
royalblue: 0x4169e1,
saddlebrown: 0x8b4513,
salmon: 0xfa8072,
sandybrown: 0xf4a460,
seagreen: 0x2e8b57,
seashell: 0xfff5ee,
sienna: 0xa0522d,
silver: 0xc0c0c0,
skyblue: 0x87ceeb,
slateblue: 0x6a5acd,
slategray: 0x708090,
slategrey: 0x708090,
snow: 0xfffafa,
springgreen: 0x00ff7f,
steelblue: 0x4682b4,
tan: 0xd2b48c,
teal: 0x008080,
thistle: 0xd8bfd8,
tomato: 0xff6347,
turquoise: 0x40e0d0,
violet: 0xee82ee,
wheat: 0xf5deb3,
white: 0xffffff,
whitesmoke: 0xf5f5f5,
yellow: 0xffff00,
yellowgreen: 0x9acd32
};
define(Color, color, {
copy: function(channels) {
return Object.assign(new this.constructor, this, channels);
},
displayable: function() {
return this.rgb().displayable();
},
hex: color_formatHex, // Deprecated! Use color.formatHex.
formatHex: color_formatHex,
formatHsl: color_formatHsl,
formatRgb: color_formatRgb,
toString: color_formatRgb
});
function color_formatHex() {
return this.rgb().formatHex();
}
function color_formatHsl() {
return hslConvert(this).formatHsl();
}
function color_formatRgb() {
return this.rgb().formatRgb();
}
function color(format) {
var m, l;
format = (format + "").trim().toLowerCase();
return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
: l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00
: l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
: l === 4 ? rgba((m >> 12 & 0xf) | (m >> 8 & 0xf0), (m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), (((m & 0xf) << 4) | (m & 0xf)) / 0xff) // #f000
: null) // invalid hex
: (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
: (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
: (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
: (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
: (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
: (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
: named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
: format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
: null;
}
function rgbn(n) {
return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
}
function rgba(r, g, b, a) {
if (a <= 0) r = g = b = NaN;
return new Rgb(r, g, b, a);
}
function rgbConvert(o) {
if (!(o instanceof Color)) o = color(o);
if (!o) return new Rgb;
o = o.rgb();
return new Rgb(o.r, o.g, o.b, o.opacity);
}
function rgb(r, g, b, opacity) {
return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
}
function Rgb(r, g, b, opacity) {
this.r = +r;
this.g = +g;
this.b = +b;
this.opacity = +opacity;
}
define(Rgb, rgb, extend$1(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
},
rgb: function() {
return this;
},
displayable: function() {
return (-0.5 <= this.r && this.r < 255.5)
&& (-0.5 <= this.g && this.g < 255.5)
&& (-0.5 <= this.b && this.b < 255.5)
&& (0 <= this.opacity && this.opacity <= 1);
},
hex: rgb_formatHex, // Deprecated! Use color.formatHex.
formatHex: rgb_formatHex,
formatRgb: rgb_formatRgb,
toString: rgb_formatRgb
}));
function rgb_formatHex() {
return "#" + hex(this.r) + hex(this.g) + hex(this.b);
}
function rgb_formatRgb() {
var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
return (a === 1 ? "rgb(" : "rgba(")
+ Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
+ Math.max(0, Math.min(255, Math.round(this.b) || 0))
+ (a === 1 ? ")" : ", " + a + ")");
}
function hex(value) {
value = Math.max(0, Math.min(255, Math.round(value) || 0));
return (value < 16 ? "0" : "") + value.toString(16);
}
function hsla(h, s, l, a) {
if (a <= 0) h = s = l = NaN;
else if (l <= 0 || l >= 1) h = s = NaN;
else if (s <= 0) h = NaN;
return new Hsl(h, s, l, a);
}
function hslConvert(o) {
if (o instanceof Hsl) return new Hsl(o.h, o.s, o.l, o.opacity);
if (!(o instanceof Color)) o = color(o);
if (!o) return new Hsl;
if (o instanceof Hsl) return o;
o = o.rgb();
var r = o.r / 255,
g = o.g / 255,
b = o.b / 255,
min = Math.min(r, g, b),
max = Math.max(r, g, b),
h = NaN,
s = max - min,
l = (max + min) / 2;
if (s) {
if (r === max) h = (g - b) / s + (g < b) * 6;
else if (g === max) h = (b - r) / s + 2;
else h = (r - g) / s + 4;
s /= l < 0.5 ? max + min : 2 - max - min;
h *= 60;
} else {
s = l > 0 && l < 1 ? 0 : h;
}
return new Hsl(h, s, l, o.opacity);
}
function hsl(h, s, l, opacity) {
return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
}
function Hsl(h, s, l, opacity) {
this.h = +h;
this.s = +s;
this.l = +l;
this.opacity = +opacity;
}
define(Hsl, hsl, extend$1(Color, {
brighter: function(k) {
k = k == null ? brighter : Math.pow(brighter, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
darker: function(k) {
k = k == null ? darker : Math.pow(darker, k);
return new Hsl(this.h, this.s, this.l * k, this.opacity);
},
rgb: function() {
var h = this.h % 360 + (this.h < 0) * 360,
s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
l = this.l,
m2 = l + (l < 0.5 ? l : 1 - l) * s,
m1 = 2 * l - m2;
return new Rgb(
hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
hsl2rgb(h, m1, m2),
hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
this.opacity
);
},
displayable: function() {
return (0 <= this.s && this.s <= 1 || isNaN(this.s))
&& (0 <= this.l && this.l <= 1)
&& (0 <= this.opacity && this.opacity <= 1);
},
formatHsl: function() {
var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
return (a === 1 ? "hsl(" : "hsla(")
+ (this.h || 0) + ", "
+ (this.s || 0) * 100 + "%, "
+ (this.l || 0) * 100 + "%"
+ (a === 1 ? ")" : ", " + a + ")");
}
}));
/* From FvD 13.37, CSS Color Module Level 3 */
function hsl2rgb(h, m1, m2) {
return (h < 60 ? m1 + (m2 - m1) * h / 60
: h < 180 ? m2
: h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
: m1) * 255;
}
function buildInterpolator(stops, base = 1) {
if (!stops || stops.length < 2 || stops[0].length !== 2) return;
// Confirm stops are all the same type, and convert colors to arrays
const type = getType(stops[0][1]);
if (!stops.every(s => getType(s[1]) === type)) return;
stops = stops.map(([x, y]) => [x, convertIfColor(y)]);
const izm = stops.length - 1;
const scale = getScale(base);
const interpolate = getInterpolator(type);
return function(x) {
const iz = stops.findIndex(stop => stop[0] > x);
if (iz === 0) return stops[0][1]; // x is below first stop
if (iz < 0) return stops[izm][1]; // x is above last stop
const [x0, y0] = stops[iz - 1];
const [x1, y1] = stops[iz];
return interpolate(y0, scale(x0, x, x1), y1);
};
}
function getType(v) {
return color(v) ? "color" : typeof v;
}
function convertIfColor(val) {
// Convert CSS color strings to clamped RGBA arrays for WebGL
if (!color(val)) return val;
const c = rgb(val);
return [c.r / 255, c.g / 255, c.b / 255, c.opacity];
}
function getScale(base) {
// Return a function to find the relative position of x between a and b
// Exponential scale follows mapbox-gl-js, style-spec/function/index.js
// NOTE: https://github.com/mapbox/mapbox-gl-js/issues/2698 not addressed!
const scale = (base === 1)
? (a, x, b) => (x - a) / (b - a) // Linear scale
: (a, x, b) => (Math.pow(base, x - a) - 1) / (Math.pow(base, b - a) - 1);
// Add check for zero range
return (a, x, b) => (a === b)
? 0
: scale(a, x, b);
}
function getInterpolator(type) {
// Return a function to find an interpolated value between end values v1, v2,
// given relative position t between the two end positions
switch (type) {
case "number": // Linear interpolator
return (v1, t, v2) => v1 + t * (v2 - v1);
case "color": // Interpolate RGBA
return (v1, t, v2) =>
v1.map((v, i) => v + t * (v2[i] - v));
default: // Assume step function
return (v1) => v1;
}
}
function autoGetters(properties = {}, defaults) {
return Object.entries(defaults).reduce((d, [key, val]) => {
d[key] = buildStyleFunc(properties[key], val);
return d;
}, {});
}
function buildStyleFunc(style, defaultVal) {
if (style === undefined) {
return getConstFunc(defaultVal);
} else if (typeof style !== "object" || Array.isArray(style)) {
return getConstFunc(style);
} else {
return getStyleFunc(style);
} // NOT IMPLEMENTED: zoom-and-property functions
}
function getConstFunc(rawVal) {
const val = convertIfColor(rawVal);
const func = () => val;
return Object.assign(func, { type: "constant" });
}
function getStyleFunc(style) {
const { type, property = "zoom", base = 1, stops } = style;
const getArg = (property === "zoom")
? (zoom) => zoom
: (zoom, feature) => feature.properties[property];
const getVal = (type === "identity")
? convertIfColor
: buildInterpolator(stops, base);
if (!getVal) return console.log("style: " + JSON.stringify(style) +
"\nERROR in tile-stencil: unsupported style!");
const styleFunc = (zoom, feature) => getVal(getArg(zoom, feature));
return Object.assign(styleFunc, {
type: (property === "zoom") ? "zoom" : "property",
property,
});
}
const layoutDefaults = {
"background": {
"visibility": "visible",
},
"fill": {
"visibility": "visible",
},
"line": {
"visibility": "visible",
"line-cap": "butt",
"line-join": "miter",
"line-miter-limit": 2,
"line-round-limit": 1.05,
},
"symbol": {
"visibility": "visible",
"symbol-placement": "point",
"symbol-spacing": 250,
"symbol-avoid-edges": false,
"symbol-sort-key": undefined,
"symbol-z-order": "auto",
"icon-allow-overlap": false,
"icon-ignore-placement": false,
"icon-optional": false,
"icon-rotation-alignment": "auto",
"icon-size": 1,
"icon-text-fit": "none",
"icon-text-fit-padding": [0, 0, 0, 0],
"icon-image": undefined,
"icon-rotate": 0,
"icon-padding": 2,
"icon-keep-upright": false,
"icon-offset": [0, 0],
"icon-anchor": "center",
"icon-pitch-alignment": "auto",
"text-pitch-alignment": "auto",
"text-rotation-alignment": "auto",
"text-field": "",
"text-font": ["Open Sans Regular", "Arial Unicode MS Regular"],
"text-size": 16,
"text-max-width": 10,
"text-line-height": 1.2,
"text-letter-spacing": 0,
"text-justify": "center",
"text-radial-offset": 0,
"text-variable-anchor": undefined,
"text-anchor": "center",
"text-max-angle": 45,
"text-rotate": 0,
"text-padding": 2.0,
"text-keep-upright": true,
"text-transform": "none",
"text-offset": [0, 0],
"text-allow-overlap": false,
"text-ignore-placement": false,
"text-optional": false,
},
"raster": {
"visibility": "visible",
},
"circle": {
"visibility": "visible",
},
"fill-extrusion": {
"visibility": "visible",
},
"heatmap": {
"visibility": "visible",
},
"hillshade": {
"visibility": "visible",
},
};
const paintDefaults = {
"background": {
"background-color": "#000000",
"background-opacity": 1,
"background-pattern": undefined,
},
"fill": {
"fill-antialias": true,
"fill-opacity": 1,
"fill-color": "#000000",
"fill-outline-color": undefined,
"fill-outline-width": 1, // non-standard!
"fill-translate": [0, 0],
"fill-translate-anchor": "map",
"fill-pattern": undefined,
},
"line": {
"line-opacity": 1,
"line-color": "#000000",
"line-translate": [0, 0],
"line-translate-anchor": "map",
"line-width": 1,
"line-gap-width": 0,
"line-offset": 0,
"line-blur": 0,
"line-dasharray": [0, 0, 0, 0],
"line-pattern": undefined,
"line-gradient": undefined,
},
"symbol": {
"icon-opacity": 1,
"icon-color": "#000000",
"icon-halo-color": "rgba(0, 0, 0, 0)",
"icon-halo-width": 0,
"icon-halo-blur": 0,
"icon-translate": [0, 0],
"icon-translate-anchor": "map",
"text-opacity": 1,
"text-color": "#000000",
"text-halo-color": "rgba(0, 0, 0, 0)",
"text-halo-width": 0,
"text-halo-blur": 0,
"text-translate": [0, 0],
"text-translate-anchor": "map",
},
"raster": {
"raster-opacity": 1,
"raster-hue-rotate": 0,
"raster-brighness-min": 0,
"raster-brightness-max": 1,
"raster-saturation": 0,
"raster-contrast": 0,
"raster-resampling": "linear",
"raster-fade-duration": 300,
},
"circle": {
"circle-radius": 5,
"circle-color": "#000000",
"circle-blur": 0,
"circle-opacity": 1,
"circle-translate": [0, 0],
"circle-translate-anchor": "map",
"circle-pitch-scale": "map",
"circle-pitch-alignment": "viewport",
"circle-stroke-width": 0,
"circle-stroke-color": "#000000",
"circle-stroke-opacity": 1,
},
"fill-extrusion": {
"fill-extrusion-opacity": 1,
"fill-extrusion-color": "#000000",
"fill-extrusion-translate": [0, 0],
"fill-extrusion-translate-anchor": "map",
"fill-extrusion-height": 0,
"fill-extrusion-base": 0,
"fill-extrusion-vertical-gradient": true,
},
"heatmap": {
"heatmap-radius": 30,
"heatmap-weight": 1,
"heatmap-intensity": 1,
"heatmap-color": [
"interpolate", ["linear"], ["heatmap-density"],
0, "rgba(0, 0, 255,0)", 0.1, "royalblue", 0.3, "cyan",
0.5, "lime", 0.7, "yellow", 1, "red"
],
"heatmap-opacity": 1,
},
"hillshade": {
"hillshade-illumination-direction": 335,
"hillshade-illumination-anchor": "viewport",
"hillshade-exaggeration": 0.5,
"hillshade-shadow-color": "#000000",
"hillshade-highlight-color": "#FFFFFF",
"hillshade-accent-color": "#000000",
},
};
function getStyleFuncs(inputLayer) {
const layer = Object.assign({}, inputLayer); // Leave input unchanged
// Replace rendering properties with functions
layer.layout = autoGetters(layer.layout, layoutDefaults[layer.type]);
layer.paint = autoGetters(layer.paint, paintDefaults[layer.type] );
return layer;
}
var ieee754 = {};
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
ieee754.read = function (buffer, offset, isLE, mLen, nBytes) {
var e, m;
var eLen = (nBytes * 8) - mLen - 1;
var eMax = (1 << eLen) - 1;
var eBias = eMax >> 1;
var nBits = -7;
var i = isLE ? (nBytes - 1) : 0;
var d = isLE ? -1 : 1;
var s = buffer[offset + i];
i += d;
e = s & ((1 << (-nBits)) - 1);
s >>= (-nBits);
nBits += eLen;
for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
m = e & ((1 << (-nBits)) - 1);
e >>= (-nBits);
nBits += mLen;
for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
if (e === 0) {
e = 1 - eBias;
} else if (e === eMax) {
return m ? NaN : ((s ? -1 : 1) * Infinity)
} else {
m = m + Math.pow(2, mLen);
e = e - eBias;
}
return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
};
ieee754.write = function (buffer, value, offset, isLE, mLen, nBytes) {
var e, m, c;
var eLen = (nBytes * 8) - mLen - 1;
var eMax = (1 << eLen) - 1;
var eBias = eMax >> 1;
var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
var i = isLE ? 0 : (nBytes - 1);
var d = isLE ? 1 : -1;
var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
value = Math.abs(value);
if (isNaN(value) || value === Infinity) {
m = isNaN(value) ? 1 : 0;
e = eMax;
} else {
e = Math.floor(Math.log(value) / Math.LN2);
if (value * (c = Math.pow(2, -e)) < 1) {
e--;
c *= 2;
}
if (e + eBias >= 1) {
value += rt / c;
} else {
value += rt * Math.pow(2, 1 - eBias);
}
if (value * c >= 2) {
e++;
c /= 2;
}
if (e + eBias >= eMax) {
m = 0;
e = eMax;
} else if (e + eBias >= 1) {
m = ((value * c) - 1) * Math.pow(2, mLen);
e = e + eBias;
} else {
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
e = 0;
}
}
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
e = (e << mLen) | m;
eLen += mLen;
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
buffer[offset + i - d] |= s * 128;
};
function readVarintRemainder(l, s, p) {
var buf = p.buf,
h, b;
b = buf[p.pos++]; h = (b & 0x70) >> 4; if (b < 0x80) return toNum(l, h, s);
b = buf[p.pos++]; h |= (b & 0x7f) << 3; if (b < 0x80) return toNum(l, h, s);
b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) return toNum(l, h, s);
b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) return toNum(l, h, s);
b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) return toNum(l, h, s);
b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) return toNum(l, h, s);
throw new Error('Expected varint not more than 10 bytes');
}
function toNum(low, high, isSigned) {
if (isSigned) {
return high * 0x100000000 + (low >>> 0);
}
return ((high >>> 0) * 0x100000000) + (low >>> 0);
}
function writeBigVarint(val, pbf) {
var low, high;
if (val >= 0) {
low = (val % 0x100000000) | 0;
high = (val / 0x100000000) | 0;
} else {
low = ~(-val % 0x100000000);
high = ~(-val / 0x100000000);
if (low ^ 0xffffffff) {
low = (low + 1) | 0;
} else {
low = 0;
high = (high + 1) | 0;
}
}
if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
throw new Error('Given varint doesn\'t fit into 10 bytes');
}
pbf.realloc(10);
writeBigVarintLow(low, high, pbf);
writeBigVarintHigh(high, pbf);
}
function writeBigVarintLow(low, high, pbf) {
pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
pbf.buf[pbf.pos] = low & 0x7f;
}
function writeBigVarintHigh(high, pbf) {
var lsb = (high & 0x07) << 4;
pbf.buf[pbf.pos++] |= lsb | ((high >>>= 3) ? 0x80 : 0); if (!high) return;
pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
pbf.buf[pbf.pos++] = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) return;
pbf.buf[pbf.pos++] = high & 0x7f;
}
// Buffer code below from https://github.com/feross/buffer, MIT-licensed
function readUtf8(buf, pos, end) {
var str = '';
var i = pos;
while (i < end) {
var b0 = buf[i];
var c = null; // codepoint
var bytesPerSequence =
b0 > 0xEF ? 4 :
b0 > 0xDF ? 3 :
b0 > 0xBF ? 2 : 1;
if (i + bytesPerSequence > end) break;
var b1, b2, b3;
if (bytesPerSequence === 1) {
if (b0 < 0x80) c = b0;
} else if (bytesPerSequence === 2) {
b1 = buf[i + 1];
if ((b1 & 0xC0) === 0x80) {
c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);
if (c <= 0x7F) c = null;
}
} else if (bytesPerSequence === 3)