# geo-polyline-tools

[![npm version](https://img.shields.io/npm/v/geo-polyline-tools.svg)](https://www.npmjs.com/package/geo-polyline-tools)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Node](https://img.shields.io/node/v/geo-polyline-tools.svg)](https://www.npmjs.com/package/geo-polyline-tools)
[![npm downloads](https://img.shields.io/npm/dm/geo-polyline-tools.svg)](https://www.npmjs.com/package/geo-polyline-tools)
[![minzipped size](https://img.shields.io/badge/minzipped-~1.3_kB-44CC11.svg)](https://www.npmjs.com/package/geo-polyline-tools)
[![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)

A lightweight JavaScript library for encoding and decoding polyline coordinates based on [Google's Encoded Polyline Algorithm](https://developers.google.com/maps/documentation/utilities/polylinealgorithm). Includes full GeoJSON `LineString` support with automatic coordinate axis conversion.

## Features

- **Encode** arrays of `[lat, lng]` coordinates into compressed polyline strings.
- **Decode** polyline strings back into `[lat, lng]` coordinate pairs.
- **GeoJSON conversion** -- encode from and decode to GeoJSON `LineString` geometries with automatic `[lng, lat]` to `[lat, lng]` axis flipping.
- **Configurable precision** -- defaults to 5 decimal places (~1 meter accuracy); adjustable for any use case.
- **Zero dependencies** -- no runtime dependencies, suitable for browser and server environments.
- **TypeScript support** -- type definitions included for both CommonJS and ESM entry points.
- **ES5 compilation target** -- compatible with legacy browsers down to IE11.

## Installation

```bash
npm install geo-polyline-tools
```

Or with yarn:

```bash
yarn add geo-polyline-tools
```

## Compatibility

- **Node.js**: >= 18.19.0
- **Browsers**: All modern browsers and legacy browsers down to IE11 (compiled to ES5)

## Usage

### Importing

**ES Module**

```js
import polyline from 'geo-polyline-tools';
```

**CommonJS**

```js
const polyline = require('geo-polyline-tools');
```

**TypeScript**

```ts
import polyline from 'geo-polyline-tools';

const encoded: string = polyline.encode([[38.5, -120.2], [40.7, -120.95]], 5);
```

**Browser (IIFE)**

Include the script directly, or use a CDN:

```html
<!-- Local -->
<script src="node_modules/geo-polyline-tools/dist/iife/geo-polyline-tools.js"></script>

<!-- unpkg CDN -->
<script src="https://unpkg.com/geo-polyline-tools@latest/dist/iife/geo-polyline-tools.js"></script>

<!-- jsdelivr CDN -->
<script src="https://cdn.jsdelivr.net/npm/geo-polyline-tools@latest/dist/iife/geo-polyline-tools.js"></script>

<script>
  var encoded = geoPolylineTools.encode([[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]], 5);
  console.log(encoded); // '_p~iF~ps|U_ulLnnqC_mqNvxq`@'
</script>
```

### Encoding Coordinates

```js
const coords = [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]];

const encoded = polyline.encode(coords, 5);
// => '_p~iF~ps|U_ulLnnqC_mqNvxq`@'

const encodedDefaultPrecision = polyline.encode(coords);
// => '_p~iF~ps|U_ulLnnqC_mqNvxq`@'  (precision defaults to 5)
```

### Decoding a Polyline String

```js
const str = '_p~iF~ps|U_ulLnnqC_mqNvxq`@';

const decoded = polyline.decode(str, 5);
// => [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]]
```

### Converting GeoJSON to Polyline

```js
const geojson = {
  type: 'Feature',
  geometry: {
    type: 'LineString',
    coordinates: [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]],
  },
};

const encoded = polyline.fromGeoJSON(geojson, 5);
// => '~ps|U_p~iFnnqC_ulLvxq`@_mqN'
```

### Converting Polyline to GeoJSON

```js
const str = '_p~iF~ps|U_ulLnnqC_mqNvxq`@';

const geometry = polyline.toGeoJSON(str, 5);
// => {
//      type: 'LineString',
//      coordinates: [[-120.2, 38.5], [-120.95, 40.7], [-126.453, 43.252]]
//    }
```

### Coordinate Ordering

The library uses two different coordinate conventions, matching the standards of each format:

| Method                | Coordinate order        |
|-----------------------|-------------------------|
| `encode` / `decode`   | `[latitude, longitude]` |
| `fromGeoJSON` (input) | `[longitude, latitude]` |
| `toGeoJSON` (output)  | `[longitude, latitude]` |

The `encode` and `decode` methods follow Google's Encoded Polyline Algorithm convention, while `fromGeoJSON` and `toGeoJSON` follow the [GeoJSON specification (RFC 7946)](https://tools.ietf.org/html/rfc7946#section-3.1.1), which mandates `[longitude, latitude]` ordering. The library handles the axis conversion automatically.

## API Reference

### `polyline.encode(coordinates, precision?)`

Encodes an array of `[latitude, longitude]` coordinate pairs into a polyline string.

| Parameter      | Type         | Default | Description                                |
|----------------|--------------|---------|--------------------------------------------|
| `coordinates`  | `number[][]` | --      | Array of `[lat, lng]` pairs. Required.     |
| `precision`    | `number`     | `5`     | Number of decimal places to preserve.      |

**Returns:** `string` -- the encoded polyline. Returns an empty string when `coordinates` is empty.

### `polyline.decode(str, precision?)`

Decodes a polyline string into an array of `[latitude, longitude]` coordinate pairs.

| Parameter   | Type     | Default | Description                                    |
|-------------|----------|---------|------------------------------------------------|
| `str`       | `string` | --      | The encoded polyline string. Required.         |
| `precision` | `number` | `5`     | Number of decimal places used during encoding. |

**Returns:** `number[][]` -- array of `[lat, lng]` pairs. Returns an empty array when `str` is empty.

### `polyline.fromGeoJSON(geojson, precision?)`

Encodes a GeoJSON `Feature` or `LineString` geometry into a polyline string. Coordinates are expected in GeoJSON `[longitude, latitude]` ordering and are internally flipped to `[latitude, longitude]` before encoding.

| Parameter   | Type     | Default | Description                                                                                                   |
|-------------|----------|---------|---------------------------------------------------------------------------------------------------------------|
| `geojson`   | `object` | --      | A GeoJSON `Feature` with `LineString` geometry, or a `LineString` geometry directly. Required.                |
| `precision` | `number` | `5`     | Number of decimal places to preserve.                                                                         |

**Returns:** `string` -- the encoded polyline.

**Throws:** `Error('Input must be a GeoJSON LineString')` if the input is not a valid GeoJSON `LineString` or `Feature` containing one.

### `polyline.toGeoJSON(str, precision?)`

Decodes a polyline string into a GeoJSON `LineString` geometry object. The resulting coordinates follow GeoJSON's `[longitude, latitude]` ordering.

| Parameter   | Type     | Default | Description                                            |
|-------------|----------|---------|--------------------------------------------------------|
| `str`       | `string` | --      | The encoded polyline string. Required.                 |
| `precision` | `number` | `5`     | Number of decimal places used during encoding.         |

**Returns:** `{ type: 'LineString', coordinates: number[][] }` -- a GeoJSON `LineString` geometry with `[lng, lat]` coordinates.

## License

This library is licensed under the MIT License. See the [LICENSE](https://github.com/barikoi/geo-polyline-tools/blob/main/LICENSE) file for details.

## Support

For issues, questions, or contributions, visit the [GitHub repository](https://github.com/barikoi/geo-polyline-tools) or contact [hello@barikoi.com](mailto:hello@barikoi.com).

---

<img src="https://docs.barikoi.com/img/barikoi-logo-black.svg" height="30" alt="Barikoi" />
