UNPKG

tile-batch

Version:

Data serializers and WebGL renderers for tiled layers in vector maps

1,687 lines (1,409 loc) 154 kB
(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)