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

@svjeez/geo

v0.6.1

Published

Real maps for svjeez: GeoJSON and TopoJSON projected, simplified and fitted to a scene while it compiles.

Readme

@svjeez/geo

Real maps for svjeez scenes. It projects GeoJSON or TopoJSON into a scene's frame while the scene compiles, and gives back path data, places, great-circle routes and camera targets in scene pixels. The SVG it ends up in is still one file with no JavaScript.

Install

npm i -D @svjeez/geo svjeez

Bring your own borders. world-atlas and us-atlas are good starting points:

npm i -D world-atlas

Use

import { project } from "@svjeez/geo";
import { rect, render } from "svjeez";
import { Camera, flow } from "svjeez/extras";
import { createRequire } from "node:module";

const world = createRequire(import.meta.url)("world-atlas/countries-50m.json");
const map = project(world, { frame: rect(0, 0, 960, 540) });

const newYork = [-74, 40.7] as const;
const london = [-0.13, 51.5] as const;
const route = map.route(newYork, london);
const atlantic = map.bounds([newYork, london], 40);

export default () =>
  render(
    { width: 960, height: 540, duration: 8, title: "New York to London" },
    <Camera frame={rect(0, 0, 960, 540)} stops={[{ during: { at: 2, over: 6 }, target: atlantic }]}>
      <path d={map.land} fill="#1C3A4F" />
      <path d={map.lines} fill="none" stroke="#5D8BA5" strokeWidth={0.6} />
      {flow(route, { at: 3, over: 3, style: "comet" }).node}
    </Camera>,
  );

project(data, options) takes a TopoJSON topology or a GeoJSON FeatureCollection or Feature. Its options are:

  • frame is where the map sits in the scene, in px.
  • projection is any d3-geo projection. It defaults to geoNaturalEarth1().
  • fit says what to fit inside frame: the regions (the default), "sphere" for the whole globe, any GeoJSON object, or false to keep the projection's own scale and translation.
  • object names the TopoJSON object to read. It defaults to the topology's first object.
  • filter keeps only the features it returns true for, before the map is fitted. Leave Antarctica out with filter: (f) => f.properties?.name !== "Antarctica".
  • tolerance is the simplification distance in px. It defaults to 0.5.

The map it returns has:

  • regions, one per feature, each with id, properties, feature, its path data d, and the centroid and bounds of its largest piece. France's centroid sits in France, not out toward French Guiana.
  • region(key), a region by id or properties.name.
  • land, every region filled as one path. From TopoJSON the shared borders are merged away.
  • lines, every outline as one path. From TopoJSON each shared border is drawn once.
  • sphere, the outline of the globe, and graticule(step), lines of longitude and latitude.
  • path(object), path data for any other GeoJSON.
  • point([lon, lat]), a place in scene pixels. It throws for a place the projection clips, such as the far side of an orthographic globe.
  • route(from, to), the great-circle arc as a list of points, in the form travel() and flow() take. It throws when the arc crosses the edge of the map; rotate the projection so both ends sit on one side.
  • bounds(target, pad), a Rect for a Camera stop around a region, a list that mixes regions and [lon, lat] places, or any GeoJSON.

Size

Every path is written for the scene, not the source data. Coordinates round to 0.1px, a point closer than tolerance to the last one kept is dropped, points that sit within half the tolerance of a straight line are dropped, and a ring that keeps fewer than three points goes away. Natural Earth's 1:10m countries come to about 150 KB of region paths in a 960 by 540 frame, down from 2.5 MB.

Zooming a map

Chromium keeps each animated element on its own raster, and when a camera scales hundreds of them it falls behind and flashes blank tiles. Draw a map in with one animated path per region, then hand over to a still copy built from land and lines, two paths that the camera zooms:

<g animation={motion({ opacity: 1 }).at(SWAP).set({ opacity: 0 })}>
  {map.regions.map((r) => <path d={r.d} animation={draw({ at: 0.5, over: 1 })} />)}
</g>
<g animation={motion({ opacity: 0 }).at(SWAP).set({ opacity: 1 })}>
  <Camera frame={frame} stops={stops}>
    <path d={map.land} />
    <path d={map.lines} />
  </Camera>
</g>

examples/world does this end to end.

Not yet

Spinning globes and changing projections change a path's outline over time, which svjeez can't animate yet.