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

static-map-generator

v0.0.2

Published

Node.js library for generating static map images using OpenStreetMap tiles with markers, paths, and smart bounds fitting

Readme

Static Map Generator

A Node.js library for generating static map images using OpenStreetMap tiles with markers, paths, and smart bounds fitting.

Features

  • 🗺️ Generate static map images from OpenStreetMap tiles
  • 📍 Add custom markers with labels and colors
  • 🛣️ Draw paths/routes from coordinate arrays
  • 📏 Smart bounds fitting with auto-calculated center and zoom
  • 🖼️ Support for different image formats (PNG, JPG)
  • 📱 High-resolution output (Retina support)
  • 💻 Full TypeScript support
  • ⚙️ Customizable map size and zoom levels

Installation

npm install static-map-generator

Usage

Basic Usage

import { StaticMap } from 'static-map-generator';

const map = new StaticMap({
  center: { lat: 35.6762, lng: 139.6503 }, // Tokyo
  zoom: 12,
  size: { width: 800, height: 600 },
  format: 'png'
});

const imageBuffer = await map.render();
// Save or use the image buffer

With Markers

import { StaticMap } from 'static-map-generator';

const map = new StaticMap({
  center: { lat: 35.6762, lng: 139.6503 },
  zoom: 12,
  size: { width: 800, height: 600 },
  markers: [
    {
      coordinate: { lat: 35.6762, lng: 139.6503 },
      color: '#FF0000',
      size: 'large',
      label: 'Tokyo'
    },
    {
      coordinate: { lat: 35.6585, lng: 139.7454 },
      color: '#00FF00',
      size: 'medium',
      label: 'Skytree'
    }
  ]
});

const imageBuffer = await map.render();

Auto-fit with Bounds (New!)

Automatically calculate center and zoom to fit a specified area:

import { StaticMap } from 'static-map-generator';

const map = new StaticMap({
  bounds: {
    north: 35.7000,  // Northern latitude
    south: 35.6000,  // Southern latitude  
    east: 139.8000,   // Eastern longitude
    west: 139.7000    // Western longitude
  },
  size: { width: 800, height: 600 },
  padding: 20,  // Padding from bounds (pixels)
  markers: [
    { coordinate: { lat: 35.6762, lng: 139.6503 }, label: 'Tokyo' }
  ]
});

const imageBuffer = await map.render();

// Get auto-calculated values
console.log('Center:', map.getCalculatedCenter());
console.log('Zoom:', map.getCalculatedZoom());

API

StaticMap(options)

Creates a new StaticMap instance.

Two Usage Patterns

Pattern 1: Manual Center & Zoom

const map = new StaticMap({
  center: { lat: 35.6762, lng: 139.6503 },
  zoom: 12,
  size: { width: 800, height: 600 }
});

Pattern 2: Auto-fit with Bounds

const map = new StaticMap({
  bounds: {
    north: 35.7000,
    south: 35.6000,
    east: 139.8000,
    west: 139.7000
  },
  size: { width: 800, height: 600 }
});

Options

Required (choose one):

  • center + zoom: Manual positioning
  • bounds: Auto-calculated positioning

Common Options:

  • size (Size): Width and height of the output image
  • format (string, optional): Output format ('png' or 'jpg', default: 'png')
  • scale (number, optional): Scale factor (1 or 2, default: 1)
  • markers (Marker[], optional): Array of markers to display
  • tileServer (string, optional): Custom tile server URL

Bounds-specific Options:

  • padding (number, optional): Padding from bounds in pixels (default: 20)

Types

interface Coordinate {
  lat: number;
  lng: number;
}

interface Size {
  width: number;
  height: number;
}

interface Bounds {
  north: number;  // Northern latitude
  south: number;  // Southern latitude
  east: number;   // Eastern longitude
  west: number;   // Western longitude
}

interface Marker {
  coordinate: Coordinate;
  color?: string;
  size?: 'small' | 'medium' | 'large';
  label?: string;
}

interface StaticMapOptions {
  center?: Coordinate;    // Required when bounds not specified
  zoom?: number;          // Required when bounds not specified (0-18)
  bounds?: Bounds;        // Alternative to center/zoom
  size: Size;
  format?: 'png' | 'jpg';
  scale?: 1 | 2;
  markers?: Marker[];
  tileServer?: string;
  padding?: number;       // Used with bounds (default: 20)
}

Methods

render(): Promise<Buffer>

Renders the map and returns a Promise that resolves to an image buffer.

getCalculatedCenter(): Coordinate

Returns the center point used for rendering (useful when using bounds).

getCalculatedZoom(): number

Returns the zoom level used for rendering (useful when using bounds).

Examples

Complete Usage Examples

1. Basic Map with Manual Center/Zoom

const { StaticMap } = require('static-map-generator');
const fs = require('fs');

const map = new StaticMap({
  center: { lat: 35.6762, lng: 139.6503 }, // Tokyo Station
  zoom: 12,
  size: { width: 800, height: 600 },
  format: 'png'
});

const imageBuffer = await map.render();
fs.writeFileSync('tokyo.png', imageBuffer);

2. Auto-fit Map using Bounds

const { StaticMap } = require('static-map-generator');
const fs = require('fs');

const map = new StaticMap({
  bounds: {
    north: 35.7000,  // Northern boundary
    south: 35.6000,  // Southern boundary
    east: 139.8000,   // Eastern boundary
    west: 139.7000    // Western boundary
  },
  size: { width: 800, height: 600 },
  padding: 30,  // 30px padding from boundaries
  markers: [
    {
      coordinate: { lat: 35.6762, lng: 139.6503 },
      color: '#FF0000',
      size: 'large',
      label: 'Tokyo'
    }
  ]
});

const imageBuffer = await map.render();
fs.writeFileSync('tokyo-bounds.png', imageBuffer);

// Get the auto-calculated values
console.log('Auto-calculated center:', map.getCalculatedCenter());
console.log('Auto-calculated zoom:', map.getCalculatedZoom());

3. High-Resolution Map with Multiple Markers

const { StaticMap } = require('static-map-generator');
const fs = require('fs');

const map = new StaticMap({
  center: { lat: 35.6762, lng: 139.6503 },
  zoom: 14,
  size: { width: 400, height: 300 },
  scale: 2,  // High-resolution (800x600 actual pixels)
  format: 'png',
  markers: [
    {
      coordinate: { lat: 35.6762, lng: 139.6503 },
      color: '#FF0000',
      size: 'large',
      label: 'Tokyo Station'
    },
    {
      coordinate: { lat: 35.6585, lng: 139.7454 },
      color: '#00FF00',
      size: 'medium',
      label: 'Skytree'
    },
    {
      coordinate: { lat: 35.6586, lng: 139.7016 },
      color: '#0000FF',
      size: 'small',
      label: 'Asakusa'
    }
  ]
});

const imageBuffer = await map.render();
fs.writeFileSync('tokyo-hd.png', imageBuffer);

4. Area Coverage Map (Tourist Route)

const { StaticMap } = require('static-map-generator');
const fs = require('fs');

// Define tourist spots
const touristSpots = [
  { lat: 35.6762, lng: 139.6503, name: 'Tokyo Station' },
  { lat: 35.6585, lng: 139.7454, name: 'Skytree' },
  { lat: 35.6586, lng: 139.7016, name: 'Asakusa' },
  { lat: 35.6785, lng: 139.6823, name: 'Imperial Palace' }
];

// Calculate bounds to include all spots
const lats = touristSpots.map(spot => spot.lat);
const lngs = touristSpots.map(spot => spot.lng);

const map = new StaticMap({
  bounds: {
    north: Math.max(...lats) + 0.005,  // Add small margin
    south: Math.min(...lats) - 0.005,
    east: Math.max(...lngs) + 0.005,
    west: Math.min(...lngs) - 0.005
  },
  size: { width: 1000, height: 800 },
  padding: 40,
  markers: touristSpots.map((spot, index) => ({
    coordinate: { lat: spot.lat, lng: spot.lng },
    color: ['#FF0000', '#00FF00', '#0000FF', '#FF6600'][index],
    size: 'large',
    label: spot.name
  }))
});

const imageBuffer = await map.render();
fs.writeFileSync('tokyo-tourist-route.png', imageBuffer);

console.log(`Map covers area from zoom level: ${map.getCalculatedZoom()}`);
console.log(`Centered at: ${map.getCalculatedCenter().lat}, ${map.getCalculatedCenter().lng}`);

Running the Examples

# Install dependencies
npm install

# Build the library
npm run build

# Run basic demo
node generate-image-now.js

# Run bounds demo
node bounds-demo.js

# Run zoom level test
node zoom-test.js

Demo Files

  • generate-image-now.js - Basic image generation demo
  • bounds-demo.js - Bounds-based auto-calculation demo
  • zoom-test.js - Zoom level testing
  • demo-viewer.html - Browser preview (no execution required)

Tips

Choosing Zoom Levels

  • 0-2: Continental/country level
  • 3-6: City/region level
  • 7-12: Urban area level
  • 13-16: Neighborhood/building level
  • 17-18: Building detail level (maximum)

Using Bounds vs Center/Zoom

  • Use bounds when you want to ensure specific areas are visible
  • Use center/zoom when you want precise control over the view
  • Bounds automatically calculates the optimal zoom to fit the area

Performance Tips

  • Smaller image sizes render faster
  • Lower zoom levels require fewer tiles
  • Use appropriate padding with bounds to avoid edge cropping

License

MIT