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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@mappedin/marker-cluster

v6.9.1-beta.0

Published

An extension for [Mappedin JS](https://www.npmjs.com/package/@mappedin/mappedin-js) that allows clustering markers together.

Readme

@mappedin/marker-cluster

An extension for Mappedin JS that allows clustering markers together.

Usage

Installation

With NPM:

npm install @mappedin/marker-cluster

With Yarn:

yarn add @mappedin/marker-cluster

Getting Started

import { show3dMap, Marker } from '@mappedin/mappedin-js';
import { MarkerCluster, ClusterFn } from `@mappedin/marker-clustering`;

const mapView = await show3dMap(...);

/**
 * Add Markers to the map like normal, keeping track of markers that should be clustered.
 */
const markers: Marker[] = []
mapData.getByType('space').forEach(space => {
    const marker = mapView.Markers.add(space, `<div>${space.name}</div>`, {
        rank: 'always-visible', // Markers should have rank: 'always-visible' for the best effect
    });
    markers.push(marker);
});

/**
 * Define a cluster function of type ClusterFn that returns a special Marker.
 */
const clusterFn: ClusterFn = ({ center, markers }) => {
    return mapView.Markers.add(center, `<div>${markers.length}</div>`, {
        rank: 'always-visible', // Cluster markers should also be always-visible
    });
};

/**
 * Create a new MarkerCluster from the MapView and a cluster function, then add all Markers that
 * should be considered for clustering.
 */
const cluster = new MarkerCluster(mapView, clusterFn);
for (const [marker] of markerProperties) {
    cluster.add(marker);
}

/**
 * Dispose the MarkerCluster when it is no longer needed.
 */
cluster.destroy();

Options

type MarkerClusterOptions<Algorithm extends IClusterAlgorithm = ScreenDistanceClustering> = Partial<{
	/**
	 * The floor the clustered marker is on. If unspecified, the floor of the first added marker will be used.
	 */
	floor: Floor | null;
	/**
	 * The minimum number of markers in a cluster.
	 *
	 * @default 2
	 */
	minSize: number;
	/**
	 * The debounce time in milliseconds for computing clusters when the camera is moving. If set to 0, the clusters will
	 * be computed on every camera frame. This could lead to performance issues if there are a lot of markers.
	 *
	 * @default 50
	 */
	debounce: number;
	/**
	 * Whether the original markers should be hidden when clustered together.
	 *
	 * @default true
	 */
	hideClusteredMarkers: boolean;
	/**
	 * A custom algorithm to use for clustering.
	 *
	 * @default ScreenDistanceClustering
	 */
	algorithm: Algorithm;
	/**
	 * Options for the algorithm.
	 */
	algorithmOptions: OptionsOf<Algorithm>;
}

Default Algorithm

/**
 * The default algorithm used by MarkerCluster is the built-in ScreenDistanceClustering algorithm. When run, this algorithm will cluster markers whose anchor coordinates are within a certain number of pixels on screen.
 */
import { ScreenDistanceClustering } from '@mappedin/marker-cluster';

Default Algorithm Options

export type ScreenDistanceClusteringOptions = Partial<{
	/**
	 * The distance in pixels that a marker should be from another marker to be considered part of the same cluster.
	 */
	threshold: number;
}>;

Custom Algorithm

import { MarkeCluster, IClusterAlgorithm } from '@mappedin/marker-cluster';

/**
 * Custom options can be passed to IClusterAlgorithm for stronger typing.
 */
type CustomClusteringOptions = {
	threshold: number;
};

/**
 * Define a custom clustering algorithm that implements IClusterAlgorithm.
 */
class CustomClustering implements IClusterAlgorithm<CustomClusteringOptions> {
	/**
	 * A custom algorithm should probably be constructed with the MapView, but it is not necessary.
	 */
	constructor(mapView: MapView) {}
	/**
	 * setOptions() is called immediately after the custom algorithm is instantiated with the value passed to "MarkerClusteringOptions.algorithmOptions". This happens in the MarkerCluster constructor.
	 */
	setOptions(options: CustomClusteringOptions): void {}
	/**
	 * load() is called whenever the debounce period elapses between Camera moves. It gets passed an array of all markers that were added to the MarkerCluster.
	 */
	load(markers: Marker[]): void {}
	/**
	 * getClusters() is called whenever MarkerCluster wants to update the rendered clusters. This usually happens immediately after load() is called.
	 */
	getClusters(): Marker[][] {}
	/**
	 * destroy() is called when "MarkerCluster.destroy()" is called. Any resources that the ClusteringAlgorithm is holding onto can be freed up here.
	 */
	destroy(): void {}
}

/**
 * Create a new MarkerCluster with the custom algorithm.
 */
const cluster = new MarkerCluster(mapView, clusterFn, {
	algorithm: new CustomAlgorithm(mapView),
	algorithmOptions: { threshold: 100 },
});

PointRBush

import { PointRBush } from '@mappedin/marker-cluster';

/**
 * PointRBush is a utility class that extends the rbush package to work with { x, y } points. It can be used to help build custom clustering algorithms, but it is not necessary.
 */
const rbush = new PointRBush();

/**
 * Insert, load, remove, or clear points the same as rbush.
 */
const points = [
	{ x: 1, y: 2 },
	{ x: 3, y: 4 },
	{ x: 5, y: 6 },
];
rbush.insert(points[0]);
rbush.load([points[1], points[2]]);
rbush.remove(points[0]);
rbush.clear();

/**
 * Search for points with a box the same as rbush.
 */
const results = rbush.search({ minX: 0, maxX: 5, minY: 0, maxY: 5 });

/**
 * Peek at an item in the tree. This can be useful when generating clusters by removing any already clustered points and retrieving a remaining point that still needs to be clustered.
 */
const item = rbush.peek();

/**
 * Check if a point is in the tree. This can be useful for optimizations if previous clusters are cached between updates.
 */
const exists = rbush.has({ x: 1, y: 2 });

/**
 * Retrieve points from the tree. This can be useful for optimizations if previous clusters are cached between updates.
 */
const found = rbushl.find(point => point.x > 2);