Library / SDK
mapbox/polylabel avatar
mapbox/polylabel

mapbox/polylabel: finding an optimal polygon label position

A fast algorithm for finding the pole of inaccessibility of a polygon (in JavaScript and C++)

1,558 stars163 forksJavaScriptNOASSERTION

At a glance

What is it?
polylabel is a JavaScript library that searches a polygon for the internal point farthest from its outline. It suits label placement and other geometry tasks that need a precision-controlled interior point rather than a centroid.
Who is it for?
Developers should adopt polylabel when they have a JavaScript polygon represented as coordinate rings and need a practical label anchor near the point farthest from the boundary. It is unsuitable when a centroid, exact analytical result, or built-in collision detection is required.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 120 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why polylabel differs from a polygon centroid

polylabel addresses a precise geometry problem: finding a polygon's pole of inaccessibility, meaning the most distant internal point from the polygon outline. The README explicitly distinguishes this point from a centroid. That distinction matters for concave polygons, where a centroid can fall outside the usable shape or sit close to an edge. A label anchored at the pole of inaccessibility is more likely to have room around it, although polylabel itself does not measure text dimensions or detect collisions.

The intended audience is JavaScript developers working with coordinate arrays, especially applications that place labels inside geographic or planar polygons. The repository describes the use case as "optimal placement of a text label on a polygon," but that word should be understood within the geometry problem. The algorithm optimizes distance from the outline. It does not inspect font metrics, competing labels, visual hierarchy, or map tiles.

Input consists of polygon rings in a GeoJSON-like coordinate format, with each position represented as `[x, y]`. The result is also `[x, y]`, accompanied by a `distance` property containing the distance from the selected point to the closest polygon point in input units. The package metadata describes the narrower purpose as finding an optimal label position inside a polygon.

This is a small geometry library rather than a complete labeling system. It supplies a useful anchor point, but the application still needs to decide whether that point is valid for its particular label size, line breaks, padding, and surrounding labels.

Inside polylabel's priority-queue subdivision

The implementation uses an iterative grid search rather than calculus or an exact polygon-clearance solver. It first creates large square cells that cover the polygon. For every cell center, it calculates the distance to the outer polygon. A point outside the polygon receives a negative distance, with outside status determined through ray casting.

Each cell is inserted into a priority queue ordered by an upper-bound estimate for how far a valid interior point in that cell might be from the boundary. The estimate adds the distance from the cell center to the polygon boundary and the cell radius, calculated as `cell_size * sqrt(2) / 2`. This value determines which region is examined next.

The search begins with the polygon centroid as its first best candidate. It then removes the most promising cell from the queue. If the cell center improves the current distance, that center becomes the new best point. A cell is split into four children only when its upper-bound estimate exceeds the current best by more than the configured precision.

When no queue cells remain, the center of the best cell is returned. The README says this procedure guarantees the global optimum within the requested precision. That is a bounded guarantee, not a claim that the returned coordinate is always the exact mathematical pole. Smaller precision values create a narrower allowed error but can require more subdivision and point-distance calculations.

The process also explains how holes enter the result indirectly. The README describes rings and returns a `distance`, but it does not give a separate hole-processing section. The repository links Shapely's implementation as another-language port, yet the documented algorithm text only explicitly mentions the outer polygon and ray-casting tests. Readers should validate ring order and hole behavior against the actual inputs and tests relevant to their application.

JavaScript package layout and precision choices

The published package metadata identifies `polylabel` as an ES module and points its `main` field to `polylabel.js`. It depends on `tinyqueue` at `^3.0.0`, which corresponds to the priority queue used by the subdivision strategy. The included package files are `include` and `polylabel.js`. Although the repository description mentions JavaScript and C++, the package metadata documents the npm package as JavaScript, and the top-level implementation is `polylabel.js`.

The precision argument controls when subdivision stops. It is expressed in the same coordinate units as the input. For geographic longitude and latitude, the README recommends `0.000001` because the default of `1.0` would be too coarse. For projected coordinates measured in metres, the appropriate value would be tied to the desired spatial tolerance rather than copied from the geographic example. The README does not state the minimum accepted value or describe behavior for malformed or degenerate polygons.

TypeScript declarations are not bundled according to the README. It directs users to the DefinitelyTyped package and documents this installation command:

bash
npm install --save @types/polylabel

That command adds type definitions only. It does not install the runtime package itself, and the README does not document a separate `npm install polylabel` command.

Running a first JavaScript label-position calculation

The README gives a minimal call rather than a complete import or application setup. Its polygon argument is an array of coordinate rings, the precision is the second argument, and the returned position has a `distance` property. The ellipsis below is retained exactly from the documented example and must be replaced with real coordinates before use:

js
const p = polylabel([[[0, 0], [1, 0], ...]], 1.0);
const distance = p.distance;

For a real first call, use a complete closed ring and a precision matched to the coordinate units. The document does not show module loading in this snippet, so it also does not establish whether `polylabel` is already in scope in the reader's chosen build setup. Since `package.json` declares `"type": "module"`, applications will need module-aware loading, but the exact import statement is not supplied by the README and is therefore not invented here.

A useful application workflow is to retain both outputs. The `[x, y]` coordinate is the label anchor, while `distance` reports its clearance from the nearest polygon point in input units. A renderer could apply label-specific padding before placement, but any threshold, collision pass, or fallback position belongs to the consuming application.

The repository's scripts define a local verification path. After dependencies and source are available, `npm test` runs `node test/test.js`, while the `pretest` script runs ESLint over the root JavaScript file and test files. The README does not document these commands, but `package.json` does. They are useful project commands, not evidence of performance in any particular deployment.

Limits of geometric optimality in label systems

The strongest limitation is the gap between finding the farthest interior point and placing a readable label. Distance from the boundary does not account for the label's rectangular footprint, line wrapping, anchor behavior, or overlap with other labels. A long label can extend beyond a polygon even when its anchor is at the pole of inaccessibility. Applications that need complete placement behavior still require rendering-specific logic.

The default precision is also a poor universal choice. It is `1.0`, which is appropriate only when one input unit is the desired tolerance. Geographic coordinates require a much smaller value. Conversely, choosing an extremely small precision can increase work because more candidate cells may survive the `cell_max - best_dist > precision` condition. The documentation does not provide a formula for translating label width into the best numeric precision.

The algorithm expects polygon coordinate input and does not document validation for self-intersections, empty rings, repeated points, or invalid topology. The README also does not document behavior when no valid interior point exists. These are implementation boundaries worth checking before relying on it in an automated labeling service.

A simpler centroid calculation is the natural alternative for code that only needs a cheap average position and can tolerate a point outside a concave polygon. Another alternative is to evaluate a grid or candidate set against label geometry and collision constraints. Such approaches do not specifically maximize boundary clearance, but they can optimize the final visual result directly. polylabel is the better fit when the desired anchor is specifically the farthest valid interior point.

Release activity, dependency scope, and licence verification

The repository is not archived, and its last push was on 2026-06-25. Release `v2.1.0` was also published on 2026-06-25, following `v2.0.1` on 2024-07-06 and `v2.0.0` on 2024-06-26. These dates show recent repository activity, but the README does not document a compatibility policy or long-term support schedule.

The runtime dependency list is narrow: `tinyqueue` is the sole production dependency. Development dependencies are ESLint and `eslint-config-mourner`. This keeps the npm dependency surface small, although adopting the package still means evaluating its dependency and licence terms under the application's normal review process.

Licence metadata needs direct inspection. `package.json` declares `"license": "ISC"`, while the repository is classified as using a custom licence that GitHub cannot classify and directs readers to `LICENSE`. The contents of that file are not available here, so they cannot be compared with the package field. This review does not provide legal advice. A commercial deployment should first confirm which text governs the repository and distributed package.

TypeScript users also face a separate maintenance surface because the README points to `@types/polylabel` in DefinitelyTyped rather than including declarations in the package. Version compatibility between the runtime package and those external definitions is not documented.

Editorial conclusion

Developers should adopt polylabel when they have a JavaScript polygon represented as coordinate rings and need a practical label anchor near the point farthest from the boundary. It is unsuitable when a centroid, exact analytical result, or built-in collision detection is required. Before adoption, verify the `LICENSE` file because the package metadata says ISC while the repository classification points to a custom licence, then test the chosen precision against the units and scale of production polygons.

Frequently asked questions

What does polylabel return for a JavaScript polygon?

It returns a `[x, y]` coordinate for the pole of inaccessibility. The result also includes a `distance` property giving the distance to the closest polygon point in input units.

What precision should I use with polylabel?

Precision must match the input coordinate units. The README suggests `0.000001` for geographic longitude and latitude because the default `1.0` is too imprecise for that case.

Does polylabel automatically place labels and detect collisions?

No. The documented function finds an interior point suitable as a label position, but the README does not describe text sizing, rendering, label overlap detection, or automatic placement across multiple polygons.

Official sources

  1. Issues
  2. mapbox/polylabel on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mapbox-polylabel.svg)](https://hysenlabs.com/projects/mapbox-polylabel)

Was this useful?