Skip to content

Repository files navigation

catmap

catmap is a TypeScript visualization library for geotechnical monitoring systems. It provides time-series charts, cross-sections, borehole logs, data utilities, and React wrappers while keeping the core contracts framework-agnostic.

The library is split by responsibility: data parsing and decimation live in @catmap/data, chart renderers live in @catmap/charts, geotechnical views live in @catmap/geotech, and React bindings live in @catmap/react.

Features

  • Piezometer, settlement, rainfall-response, inclinometer, sensor-health, multi-instrument, and spectral waterfall charts
  • Chart analysis tools for inspection, viewport zooming, panning, and reset callbacks
  • SVG cross-section views and borehole logs
  • Time-series decimation, bucket aggregation, in-memory data sources, and Arrow-compatible column loading
  • uPlot time-series rendering, Canvas 2D spectral waterfall rendering, and a lightweight WebGL point renderer with optional OffscreenCanvas support
  • Browser PNG/PDF export helpers for canvas and SVG output
  • React wrappers for geotechnical charts, spectral waterfalls, cross-sections, and borehole logs

Packages

Package Purpose
@catmap/core Framework-agnostic contracts for charts, layers, renderer adapters, events, plugins, and data sources
@catmap/data Time-series types, data sources, decimation, aggregation, mock data, and Arrow-like column import
@catmap/charts uPlot adapter, spectral waterfall chart, chart layer helpers, WebGL point renderer, and browser export utilities
@catmap/geotech Domain APIs for geotechnical charts, cross-sections, and borehole logs
@catmap/react React components wrapping @catmap/charts and @catmap/geotech APIs
apps/playground Vite playground using mock geotechnical data

Installation

This repository is a pnpm workspace. Inside the monorepo, packages consume each other through workspace:*.

pnpm install
pnpm dev

For an external application, install only the packages you need once they are published:

pnpm add @catmap/react @catmap/charts @catmap/geotech @catmap/data

Peer/runtime dependencies depend on the package surface you use:

  • React components require react >=18.
  • Time-series charts require uplot styles to be loaded by the application.
import "uplot/dist/uPlot.min.css";

Quick Start

React

import {
  BoreholeLog,
  CrossSectionView,
  PiezometerChart,
  SpectralWaterfallChart
} from "@catmap/react";

export function MonitoringDashboard() {
  return (
    <>
      <PiezometerChart
        instrument={{ id: "PZ-001", name: "Piezometer PZ-001" }}
        readings={[
          { timestamp: Date.UTC(2026, 0, 1), waterLevel: 1210.4 },
          { timestamp: Date.UTC(2026, 0, 2), waterLevel: 1211.2 }
        ]}
        thresholds={[
          { value: 1211.6, label: "Alert", severity: "warning" },
          { value: 1212.6, label: "Action", severity: "critical" }
        ]}
        yAxis="waterLevel"
        showThresholds
        tools={{
          onInspect: (event) => console.log(event),
          onViewportChange: (range) => console.log(range)
        }}
      />

      <CrossSectionView
        title="Section A"
        series={[
          {
            id: "ground",
            label: "Ground surface",
            points: [
              { distance: 0, elevation: 1240 },
              { distance: 100, elevation: 1233 }
            ]
          }
        ]}
      />

      <BoreholeLog
        boreholeId="BH-01"
        waterLevel={10.5}
        intervals={[
          { from: 0, to: 4, label: "Fill" },
          { from: 4, to: 12, label: "Silty sand" },
          { from: 12, to: 24, label: "Weathered rock" }
        ]}
      />

      <SpectralWaterfallChart
        title="Interactive waterfall spectral chart"
        x={[0, 1, 2, 3]}
        spectra={[
          { id: "s0", label: "Spectra 0", values: [0, 3, 1, 2] },
          { id: "s1", label: "Spectra 1", values: [1, 4, 2, 1] }
        ]}
        height={420}
      />
    </>
  );
}

Framework-Agnostic APIs

import { PiezometerChart } from "@catmap/geotech";

const chart = new PiezometerChart(container, {
  instrument: { id: "PZ-001", name: "Piezometer PZ-001" },
  readings,
  thresholds,
  yAxis: "waterLevel",
  showThresholds: true
});

chart.updateData(nextReadings);
chart.resetViewport();
chart.resize();
chart.destroy();
import { SpectralWaterfallChart } from "@catmap/charts";

const waterfall = new SpectralWaterfallChart(container, {
  x,
  spectra,
  height: 560,
  onSelectionChange: (selection) => console.log(selection)
});

waterfall.updateData(nextSpectra);
waterfall.resize();
waterfall.destroy();

Data Utilities

import {
  TimeSeriesDataSource,
  bucketAggregation,
  decimateTimeSeries,
  timeSeriesFromArrow
} from "@catmap/data";

const source = new TimeSeriesDataSource(points);
const { data, total } = await source.query({
  from: Date.UTC(2026, 0, 1),
  to: Date.UTC(2026, 0, 31),
  maxPoints: 600
});

const hourly = bucketAggregation(data, 60 * 60 * 1000);
const decimated = await decimateTimeSeries(points, { maxPoints: 1000, useWorker: true });

const fromArrow = timeSeriesFromArrow(arrowTable, {
  timestampColumn: "timestamp",
  valueColumn: "water_level",
  qualityColumn: "quality",
  timestampUnit: "ms"
});

console.log(total, hourly.length, decimated.length, fromArrow.length);

timeSeriesFromArrow accepts Arrow-compatible table objects that expose getChild(name) and vector columns with get(index). It intentionally does not parse Arrow IPC files; parse those upstream and pass the table object into catmap.

Export Utilities

import { canvasToPngBlob, svgToPdfBlob } from "@catmap/charts";

const png = await canvasToPngBlob(canvas);
const pdf = await svgToPdfBlob(svgElement, {
  width: 900,
  height: 500,
  background: "#ffffff"
});

Exports run in browser environments. Server-side export should use a browser renderer or a dedicated PDF/image pipeline.

Development

pnpm install
pnpm dev
pnpm typecheck
pnpm test
pnpm build

Workspace scripts:

  • pnpm dev: starts the Vite playground.
  • pnpm typecheck: runs TypeScript checks across packages.
  • pnpm test: runs Vitest across packages.
  • pnpm build: builds package outputs with tsup and the playground with Vite.
  • pnpm lint: runs ESLint with zero warnings.

See DEVELOPMENT.md for completed phases, design constraints, known limitations, and future work.

Architecture Notes

  • @catmap/core stays framework-agnostic and renderer-agnostic.
  • React components live only in @catmap/react.
  • Concrete rendering engines stay behind package boundaries: uPlot and Canvas 2D in @catmap/charts.
  • Large-data paths prefer typed arrays, Arrow-compatible columns, chunks, or tiles over object-heavy hot paths.
  • WebGPU, Rust/WASM, backend tiling, and custom parsers should be added only after benchmarks show the current path is the bottleneck.

License

See LICENSE.

About

A small TypeScript library for geotechnical visualization.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Packages

Contributors

Languages