Mapbox GL JS

repository·main·Indexed 11 days ago

https://github.com/mapbox/mapbox-gl-js

A high-performance WebGL-based JavaScript library for rendering interactive vector maps. Version 3.28.1 uses the Mapbox Style and Vector Tile specifications to provide customizable map experiences, including 3D terrain and data-driven styling.

Tokens
11.1K
Snippets
38
Records
55
Agent score
95%

What's inside Mapbox GL JS

  1. What is Mapbox GL JS?

    main
    Mapbox GL JS is a JavaScript library designed for creating interactive, customizable vector maps on the web. It functions by taking map styles that follow the Mapbox Style Specification and applying them to vector tiles that follow the Mapbox Vector Tile Specification. The rendering is performed using WebGL, enabling high-performance map visualizations including 3D terrain, data-driven styling, and complex layers.
  2. How vector tile rendering works

    main

    Vector tile rendering in Mapbox GL JS is a multi-stage process that splits heavy computation (parsing and layout) onto WebWorker threads to keep the main thread responsive.

    1. Parsing and Layout (Worker Thread)

    Vector tiles are fetched and processed in WebWorkers through the following steps:

    • Deserialization: Source layers, feature properties, and geometries are extracted from the PBF format (using @mapbox/vector-tile).
    • Layout: Data is transformed into render-ready formats used by WebGL shaders. This is managed by WorkerTile, Bucket classes, and ProgramConfiguration.
    • Indexing: Geometries are indexed into a FeatureIndex to enable spatial queries like queryRenderedFeatures.
    • Bucketing: WorkerTile#parse() creates a Bucket for each 'family' of style layers that share the same underlying features and layout properties.

    2. Rendering (Main Thread)

    Once layout is complete, data is transferred to the main thread. The Bucket serves as the single point of knowledge for turning vector tiles into WebGL buffers, holding vertex and element array data in ArrayGroup objects.

    Rendering follows this flow:

    • Pass Management: Painter#renderPass() iterates through style layers.
    • Layer Drawing: The painter delegates to layer-specific drawXxxx() methods (e.g., drawLine, drawSymbol).
    • WebGL Execution: For each tile, the drawer obtains a shader program from the Painter, sets uniform values based on style properties, binds layout buffer data via BufferGroup, and executes gl.drawElements().
  3. Understand the data structure of a Tile and its Buckets

    main

    When vector tile data is transferred from the worker to the main thread, it is organized into a hierarchy of Tiles and Buckets. A single Tile contains multiple Bucket instances. A Bucket represents a group of style layers that share the same layout 'family'.

    The data structure follows this pattern:

    Tile
      |
      +- buckets[layer-id]: Bucket
      |    |
      |    + ArrayGroup {
      |        globalProperties: { zoom }
      |        layoutVertexArray,
      |        indexArray,
      |        indexArray2,
      |        layerData: {
      |          [style layer id]: {
      |            programConfiguration,
      |            paintVertexArray,
      |            paintPropertyStatistics
      |          }
      |          ...
      |        }
      |    }
      |
      +- buckets[...]: Bucket
            ...

    Note: A particular bucket may appear multiple times in tile.buckets—once for each layer in a given layout 'family'.

  4. Use Mapbox GL pragmas to manage variable scope and types

    main

    Mapbox GL Shaders use pragmas to abstract over how variables are declared based on their context (e.g., whether they are uniforms, attributes, or varyings). This allows you to write shader code that works regardless of whether a variable is constant for all features or unique to each feature.

    Pragma Syntax

    Pragmas follow this pattern: #pragma mapbox: (define|initialize) (lowp|mediump|highp) (float|vec2|vec3|vec4) {name}

    Usage Requirements

    To correctly use pragma-defined variables, you must follow these rules:

    1. Dual Declaration: Every variable must have both a define pragma and an initialize pragma.
    2. Scope:
      • define pragmas must be placed in the file scope.
      • initialize pragmas must be placed in the function scope (e.g., inside main()).
    3. Vertex/Fragment Synchronization: If a variable is defined and initialized in the fragment shader, it must also be defined and initialized in the vertex shader. This is because attributes are not directly accessible from the fragment shader and must be passed through via interpolation.
    #pragma mapbox: define highp vec4 color
    
    main() {
        #pragma mapbox: initialize highp vec4 color
        // ... logic to set color ...
        gl_FragColor = color;
    }
  5. Understand the Mapbox GL Shader Prelude

    main

    The Mapbox GL shader compiler automatically includes two prelude files in every shader you write. You do not need to manually include them, but you should be aware of their existence as they provide the base environment for your shaders.

    • _prelude.fragment.glsl: Automatically included in fragment shaders.
    • _prelude.vertex.glsl: Automatically included in vertex shaders.
  6. How shader programs and data-driven properties are managed

    main

    Mapbox GL JS uses a specialized system to handle data-driven styling within WebGL shaders. This is managed by the Painter and ProgramConfiguration classes.

    Shader Compilation

    ProgramConfiguration handles the expansion of #pragma mapbox statements in shader source code. It determines whether a style property should be treated as a:

    • Uniform: For constant values across a layer.
    • Attribute, Varying, or Local variable: When the property is data-driven (varying per feature).

    Data-Driven Paint Properties

    For properties that change per feature (data-driven), ProgramConfiguration creates and populates a paint vertex array during the layout phase on the worker side. This array corresponds to the attributes declared in the shader, allowing the GPU to access unique values for every feature during the render pass.

  7. Filter syntax for Mapbox GL features

    main

    Filters are defined using nested arrays following the Mapbox GL JS specification. They allow for logical operations and property comparisons to target specific data within a layer.

    Common logical operators include:

    • "all": Matches if all expressions are true.
    • "any": Matches if any expression is true.
    • "none": Matches if no expressions are true.
    • "in": Checks if a value exists within a provided list.

    Comparison operators include ==, !=, <=, >=, <, >, etc. You can filter by feature properties or the special $type key to check the geometry type.

  8. Publish @mapbox/mapbox-gl-pmtiles-provider to the CDN

    main

    To publish a new version of the provider to the Mapbox CDN, follow these steps:

    1. Bump the version in package.json.
    2. Update the default CDN URL in the Mapbox GL JS TILE_PROVIDER_URLS configuration.
    3. Build the project and run the publishing script.

    Files are uploaded to https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-pmtiles-provider/v{version}/mapbox-gl-pmtiles-provider.js with immutable cache headers.

    npm run build
    ./publish_cdn.sh --dry-run  # Preview commands
    ./publish_cdn.sh            # Requires AWS credentials
  9. Mapbox GL JS ESLint Configuration Overview

    main

    The project uses a highly customized ESLint configuration designed for TypeScript and browser compatibility. It enforces strict JSDoc requirements for core files, manages specific import restrictions to ensure bundle compatibility (e.g., preventing process.env or import.meta.url in UMD bundles), and includes custom Mapbox rules.

    Key configuration aspects include:

    • Strict JSDoc: Enforced on core files like src/index.ts and src/ui/** to ensure high-quality documentation.
    • Browser Compatibility: Rules are configured to prevent the use of Node.js-specific globals or syntax that would break browser bundles.
    • TypeScript Integration: Uses typescript-eslint with type-checked rules.
    • Custom Rules: Includes mapbox/devtools-must-use-debug-run and mapbox/no-object-methods-on-collections.
    • File-Specific Overrides: Different rules apply to DEV_FILES, STRICT_JSDOC_FILES, and UNTYPED_FILES to balance strictness with developer velocity.