Beautiful Vector Maps Made Simple
A modern & lightweight TypeScript library for creating interactive vector maps. Perfect for data visualization, geographic analysis, and interactive mapping applications.
- ๐บ๏ธ Vector Maps - Create interactive vector maps with custom projections and SVG rendering
- ๐ Data Visualization - Powerful tools for visualizing geographical data with customizable legends and scales
- โ๏ธ Framework Support - Seamless integration with React and Vue through dedicated bindings
- ๐ก๏ธ Type Safety - Built with TypeScript for robust type checking and excellent developer experience
- ๐ฏ Event Handling - Rich event system for interactive maps with full TypeScript support
- ๐จ Customization - Extensive styling options with CSS-in-TS and theme support
- ๐ Performance - Optimized for smooth interactions and efficient rendering
- ๐ฆ Zero Dependencies - Lightweight and self-contained
# Using npm
npm install @stacksjs/ts-maps
# Using yarn
yarn add @stacksjs/ts-maps
# Using pnpm
pnpm add @stacksjs/ts-maps
# Using bun
bun add @stacksjs/ts-maps
import { VectorMap } from '@stacksjs/ts-maps'
// Create a basic world map
const worldMap = new VectorMap({
container: 'map-container',
map: 'world',
theme: 'light',
style: {
regions: {
default: {
fill: '#e8e8e8',
stroke: '#fff',
},
hover: {
fill: '#2ca25f',
},
},
},
})
// Add interactivity
worldMap.on('regionClick', (event, region) => {
console.log(`Clicked: ${region.properties.name}`)
})
import { Series, VectorMap } from '@stacksjs/ts-maps'
// Create a choropleth map
const map = new VectorMap({
container: 'map-container',
map: 'world',
})
// Add data series
const series = new Series({
name: 'Population Density',
data: [
{ id: 'US', value: 36 },
{ id: 'CN', value: 153 },
{ id: 'IN', value: 464 },
// ... more data
],
scale: {
type: 'logarithmic',
colors: ['#fee5d9', '#fcae91', '#fb6a4a', '#de2d26', '#a50f15'],
},
})
map.addSeries(series)
The main class for creating and managing vector maps.
interface VectorMapOptions {
container: string | HTMLElement; // Container element or ID
map: string; // Map type ('world', 'usa', etc.)
theme?: 'light' | 'dark'; // Map theme
backgroundColor?: string; // Background color
zoomOnScroll?: boolean; // Enable zoom on scroll
zoomMax?: number; // Maximum zoom level
zoomMin?: number; // Minimum zoom level
projection?: 'mercator' | 'miller'; // Map projection type
style?: { // Map styling options
regions?: {
default?: RegionStyle; // Default region style
hover?: RegionStyle; // Hover state style
selected?: RegionStyle; // Selected state style
};
};
}
interface RegionStyle {
fill?: string; // Fill color
stroke?: string; // Stroke color
strokeWidth?: number; // Stroke width
cursor?: string; // Cursor style
}
// Methods
map.addMarkers(markers: Marker[]); // Add markers to the map
map.removeMarkers(); // Remove all markers
map.addSeries(series: Series); // Add data series
map.removeSeries(); // Remove all series
map.addLines(lines: Line[]); // Add connection lines
map.removeLines(); // Remove all lines
map.setZoom(scale: number); // Set zoom level
map.getSelectedRegions(): string[]; // Get selected region codes
map.clearSelectedRegions(); // Clear region selection
map.destroy(); // Clean up resources
Class for creating data visualizations on the map.
interface SeriesOptions {
name: string // Series name
data: Record<string, number> | Array<{ id: string, value: number }>
scale?: {
type: 'linear' | 'logarithmic'
colors: string[] // Color scale
min?: number // Minimum value
max?: number // Maximum value
steps?: number // Number of color steps
}
normalizeFunction?: 'linear' | 'polynomial'
legend?: {
title?: string
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
}
}
Available events that can be listened to:
// Region events
map.on('regionClick', (event: MouseEvent, code: string) => void);
map.on('regionHover', (event: MouseEvent, code: string) => void);
map.on('regionSelected', (event: MouseEvent, code: string) => void);
// Marker events
map.on('markerClick', (event: MouseEvent, index: number) => void);
map.on('markerHover', (event: MouseEvent, index: number) => void);
// Map events
map.on('zoom', (scale: number) => void);
map.on('pan', (x: number, y: number) => void);
Interface for adding markers to the map:
interface Marker {
coords: [number, number] // Latitude and longitude
name?: string // Marker name
style?: {
fill?: string // Marker fill color
stroke?: string // Marker stroke color
strokeWidth?: number // Marker stroke width
r?: number // Marker radius
}
hover?: {
fill?: string // Hover fill color
r?: number // Hover radius
}
}
Interface for adding connection lines:
interface Line {
from: [number, number] // Starting coordinates
to: [number, number] // Ending coordinates
style?: {
stroke?: string // Line color
strokeWidth?: number // Line width
dashArray?: string // Dash pattern
animation?: boolean // Enable animation
animationSpeed?: number // Animation speed
}
}
For more detailed documentation and examples, visit our documentation site.
Please see our releases page for more information on what has changed recently.
Please see CONTRIBUTING for details.
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
For casual chit-chat with others using this package:
Join the Stacks Discord Server
"Software that is free, but hopes for a postcard." We love receiving postcards from around the world showing where ts-maps
is being used! We showcase them on our website too.
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States ๐
We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
Many thanks for the libraries that laid the groundwork:
- countries: https://github.com/rinvex/countries
The MIT License (MIT). Please see LICENSE for more information.
Made with ๐