Layers

GeoArrow Layers

Render Apache Arrow Table / RecordBatch data with GeoArrow extension types directly — zero GeoJSON parsing, GPU-accelerated.

See Peer Dependencies for the complete list of required packages for each component.

Live Demo — Natural Earth country boundaries rendered from a 178 KB GeoArrow IPC file.

Why GeoArrow

GeoArrow is a memory layout for geospatial data built on top of Apache Arrow. Compared to GeoJSON:

  • No JSON parse pass. Coordinates live in flat typed arrays — the bytes you fetch are the bytes the GPU consumes.
  • Streamable. Arrow IPC and GeoParquet are columnar and chunked; you can render a single RecordBatch while more arrive over the network.
  • Smaller wire size. Float64 coordinates packed at full precision typically beat GeoJSON by 3–10×.
  • Typed. Each layer wrapper reads the GeoArrow extension column (geoarrow.point, geoarrow.linestring, geoarrow.polygon, etc.) directly off the Arrow Table — you rarely need an explicit getPosition accessor.

The six wrappers below extract GeoArrow geometry columns inside the Vue wrapper and feed flat-buffer attributes straight to stock @deck.gl/layers (and @deck.gl/geo-layers for the trips wrapper). No runtime dependency on @geoarrow/deck.gl-geoarrow — the wrappers ship as code-split chunks loaded only when you instantiate them.

Installation

pnpm add @deck.gl/core @deck.gl/mapbox @deck.gl/layers apache-arrow

For VLayerDeckglGeoArrowTrips (animated trips) also add @deck.gl/geo-layers:

pnpm add @deck.gl/geo-layers

VLayerDeckglGeoArrowScatterplot

Renders points from a GeoArrow point or multipoint column. Wraps the stock deck.gl ScatterplotLayer.

<script setup lang="ts">
  import { VMap } from '@geoql/v-maplibre';
  import { VLayerDeckglGeoArrowScatterplot } from '@geoql/v-maplibre/deck.gl';
  import { tableFromIPC } from 'apache-arrow';
  import type { Table } from 'apache-arrow';

  const table = shallowRef<Table | null>(null);

  onMounted(async () => {
    const buffer = await (await fetch('/points.arrows')).arrayBuffer();
    table.value = tableFromIPC(new Uint8Array(buffer));
  });
</script>

<template>
  <VMap :options="{ style, center: [-122, 37], zoom: 10 }">
    <VLayerDeckglGeoArrowScatterplot
      v-if="table"
      id="points"
      :data="table"
      :get-fill-color="[100, 150, 220, 200]"
      :get-radius="50"
      radius-units="meters"
      pickable
    />
  </VMap>
</template>

VLayerDeckglGeoArrowPath

Renders polylines from a GeoArrow linestring or multilinestring column. Useful for GPS traces, route geometries, road networks. Wraps the stock deck.gl PathLayer.

<VLayerDeckglGeoArrowPath
  id="routes"
  :data="table"
  :get-color="[255, 140, 0, 255]"
  :get-width="3"
  width-units="pixels"
  pickable
/>

VLayerDeckglGeoArrowPolygon

Filled polygons with optional stroke. Wraps the stock deck.gl PolygonLayer (composite of SolidPolygonLayer for fill + PathLayer for outline). Consumes a GeoArrow polygon or multipolygon column.

<VLayerDeckglGeoArrowPolygon
  id="countries"
  :data="table"
  :get-fill-color="[100, 150, 220, 180]"
  :get-line-color="[180, 220, 255, 255]"
  :line-width-min-pixels="1"
  stroked
  filled
  pickable
/>

VLayerDeckglGeoArrowSolidPolygon

3D extruded polygons — best for building footprints, height-mapped choropleths, voxel-style visualizations. Wraps the stock deck.gl SolidPolygonLayer.

<VLayerDeckglGeoArrowSolidPolygon
  id="buildings"
  :data="table"
  :get-fill-color="[200, 100, 50, 200]"
  :get-elevation="(d) => d.height_m"
  extruded
  filled
  pickable
/>

VLayerDeckglGeoArrowText

Renders text labels at point positions. The wrapper extracts XY positions from the GeoArrow point column and reads the label string from a sibling column (default name name, overridable via the text-column prop). Wraps the stock deck.gl TextLayer.

<VLayerDeckglGeoArrowText
  id="city-labels"
  :data="table"
  text-column="name"
  :get-color="[230, 230, 230, 255]"
  :get-size="14"
  size-units="pixels"
  billboard
/>

VLayerDeckglGeoArrowTrips

Animated trips along GeoArrow linestrings, with per-vertex timestamps. The wrapper extracts vertices from the GeoArrow linestring column and reads per-vertex timestamps from a parallel List<float64> column (default name timestamps, overridable via the timestamps-column prop). Drive the animation by binding :current-time to a requestAnimationFrame clock. Wraps the stock deck.gl TripsLayer from @deck.gl/geo-layers.

<VLayerDeckglGeoArrowTrips
  id="city-trips"
  :data="table"
  timestamps-column="timestamps"
  :get-color="[255, 140, 0, 200]"
  :get-width="4"
  width-units="pixels"
  :current-time="time"
  :trail-length="180"
  fade-trail
/>

Loading data

Every wrapper accepts an apache-arrow.Table or RecordBatch directly via the data prop — pick whichever is more convenient.

From Arrow IPC (.arrows, .arrow, .feather)

import { tableFromIPC } from 'apache-arrow';

const buffer = await (await fetch(url)).arrayBuffer();
const table = tableFromIPC(new Uint8Array(buffer));
// pass `table` directly, or `table.batches[0]` for a single RecordBatch

From GeoParquet

GeoParquet decoding is not part of apache-arrow itself. Use parquet-wasm or @loaders.gl/parquet to convert a .parquet file into an Arrow Table:

import { readParquet } from 'parquet-wasm';
import { tableFromIPC } from 'apache-arrow';

const wasmTable = readParquet(new Uint8Array(buffer));
const table = tableFromIPC(wasmTable.intoIPCStream());
const batch = table.batches[0];

Sample data

The official geoarrow/geoarrow-data repository hosts CORS-enabled sample files you can use to prototype:

FileSizeGeometry
natural-earth_countries-geography.arrows178 KBPolygon (countries)
natural-earth_cities.arrows~70 KBPoint (cities)
microsoft-buildings_*variesPolygon (buildings)

Stream them via jsDelivr's CDN for snappy reloads:

const url =
  'https://cdn.jsdelivr.net/gh/geoarrow/geoarrow-data@main/natural-earth/files/natural-earth_countries-geography.arrows';

Picking & events

All six wrappers emit @click and @hover with a deck.gl PickingInfo payload. Because the underlying layers consume binary (flat-buffer) attributes, info.object is typically null — read row data on demand via the Arrow Table.get(info.index) API:

<VLayerDeckglGeoArrowPolygon
  id="countries"
  :data="table"
  pickable
  @click="(info) => console.log('row', info.index, table?.get(info.index))"
/>

See also

Copyright © 2026