npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@svene/geosvg

v0.1.0

Published

Headless GeoJSON LineString and MultiLineString to SVG engine.

Readme

@svene/geosvg

@svene/geosvg converts GeoJSON line geometry to SVG path records and SVG strings. It handles LineString, MultiLineString, and line geometry inside nested GeometryCollection values.

The package has no DOM dependency. The main entry works in Node 18+ and bundlers. The /browser entry provides a dependency-bundled ESM build for browsers.

Install

pnpm add @svene/geosvg

Quick start

import { toSvg } from "@svene/geosvg";

const { svg } = toSvg(geojson, {
  width: 1200,
  height: 800,
  padding: 64
});

toSvg() returns the serialized SVG, the normalized line document, and the rendered scene:

{
  svg: string;
  document: LineDocument;
  scene: SvgScene;
}

If you only need a file, use svg. If your application needs line IDs, diagnostics, properties, or path data, keep document or scene.

Work with each stage

The same conversion is available as three functions:

import { normalize, layout, serializeSvg } from "@svene/geosvg";

const document = normalize(geojson, {
  sourceName: "routes.geojson"
});

const scene = layout(document, {
  width: 1200,
  height: 800,
  coordinates: "geographic",
  style(line) {
    const color = line.properties.color;

    if (typeof color === "string") {
      return { stroke: color, strokeWidth: 4 };
    }

    return { stroke: "#111111", strokeWidth: 4 };
  }
});

const svg = serializeSvg(scene, {
  background: null,
  metadata: false,
  pretty: true
});

scene.paths contains the final d values and styles. A UI can render those records directly without parsing the SVG string.

Input

normalize() accepts parsed GeoJSON. It keeps line geometry and reports unsupported or unusable geometry through document.diagnostics.

Normalization records the source location of each line:

interface LineSource {
  featureIndex: number;
  partIndex: number;
  geometryPath: readonly number[];
}

geometryPath contains indexes inside nested GeometryCollection values. partIndex identifies a part of a MultiLineString.

Generated IDs come from that source location:

f0
f1-p1
f2-g0
f2-g1-g0-p1

Normalization does not use UUIDs or timestamps.

Missing names

GeoJSON does not require a feature name. When a line has no usable name, normalize() checks these values in order:

  1. name, title, label, and route-name properties
  2. origin and destination properties
  3. a route identifier with a direction
  4. a route identifier
  5. the GeoJSON feature ID
  6. the first useful primitive property
  7. the source filename with the line number
  8. the line number

unknown, unnamed, null, and N/A are ignored as names. Duplicate generated names receive a deterministic qualifier.

You can supply the first naming rule:

const document = normalize(geojson, {
  getName(line) {
    const value = line.properties.my_name;

    if (typeof value === "string") {
      return value;
    }

    return undefined;
  }
});

If getName() returns a blank string, null, or undefined, the built-in naming rules continue.

Rendering

The default coordinate mode treats positions as RFC 7946 longitude and latitude and projects them with Mercator:

layout(document, {
  coordinates: "geographic"
});

For schematic or other planar coordinates, use raw mode:

layout(document, {
  coordinates: "raw",
  reflectY: true
});

The package does not infer a coordinate reference system.

A single style object applies to every line:

layout(document, {
  style: {
    stroke: "#222222",
    strokeWidth: 3,
    strokeOpacity: 1,
    lineCap: "round",
    lineJoin: "round"
  }
});

A style function can vary the result by line:

layout(document, {
  style(line) {
    if (line.properties.kind === "express") {
      return { stroke: "#ef4444" };
    }

    return { stroke: "#3b82f6" };
  }
});

Route categories and color rules stay in the calling application.

Validate JSON text

normalize() expects an object. If your input starts as JSON text, parse and validate it through the /validate entry:

import { parseGeoJSON } from "@svene/geosvg/validate";

const geojson = parseGeoJSON(fileText);

parseGeoJSON() throws GeoJSONParseError for invalid JSON or invalid GeoJSON.

To get validation issues without throwing:

import { validateGeoJSON } from "@svene/geosvg/validate";

const issues = validateGeoJSON(fileText);

Each issue has this shape:

interface ValidationIssue {
  code: "INVALID_JSON" | "INVALID_GEOJSON";
  message: string;
  offset?: number;
  line?: number;
  column?: number;
}

The validator package's own error types are not part of this package's public API.

Diagnostics

document.diagnostics can contain these codes:

UNSUPPORTED_GEOMETRY
EMPTY_GEOMETRY
INVALID_LINE
AUTO_NAMED
DUPLICATE_NAME
DEGENERATE_GEOMETRY

Use diagnostic.code in application logic. Messages are text for people and can change between releases.

A Polygon, for example, produces UNSUPPORTED_GEOMETRY. Polygon rings are not converted to lines.

SVG output

Metadata export is off by default. Set metadata: true to write primitive GeoJSON properties as escaped data-* attributes:

const svg = serializeSvg(scene, {
  metadata: true
});

Objects and arrays are not written as metadata attributes. If two property names normalize to the same data-* name, the serializer adds numeric suffixes to keep the attribute names unique.

For the same input, options, and package version, line order, generated IDs, metadata order, numeric precision, and SVG attribute order stay stable.

Normalization copies line coordinates and JSON properties. Rendering does not mutate the GeoJSON object passed by the caller.

serializeSvg() escapes XML text and metadata values. It also checks a manually created SvgScene before serialization and rejects invalid dimensions, path values, opacity values, line caps, and line joins.

Browser entry

import { toSvg } from "@svene/geosvg/browser";

The browser entry bundles the rendering dependency. It does not include the /validate entry.

Package scope

This package converts GeoJSON line geometry to SVG. It does not include:

  • basemaps or map tiles
  • route editing
  • pan and zoom controls
  • file pickers or drag and drop
  • downloads or clipboard access
  • ZIP creation
  • geometry simplification
  • coordinate-system detection
  • selection state

Those features belong in the application that uses the package.

Development

Install dependencies:

pnpm install

Run the package checks:

pnpm check

pnpm check runs TypeScript, tests, the package build, built-entry smoke tests, publint, and @arethetypeswrong/cli.