@rapideditor/location-conflation
Version:
Define complex geographic regions by including and excluding country codes and geojson shapes
191 lines (129 loc) • 7.83 kB
Markdown
[](https://github.com/rapideditor/location-conflation/actions?query=workflow%3A%22build%22)
[](https://badge.fury.io/js/%40rapideditor%2Flocation-conflation)
# location-conflation
🧩 Define complex geographic regions ([geofences](https://en.wikipedia.org/wiki/Geo-fence)) by including and excluding country codes and GeoJSON shapes.
⚡️ Try it live at **<https://location-conflation.com/>**
---
## What is it?
**location-conflation** generates [GeoJSON](https://geojson.org/) features by combining other locations and shapes. It's a declarative way to describe geofences, so your application doesn't need to bundle or fetch large amounts of geodata.
A **locationSet** is an object with `include` and `exclude` arrays:
```js
const locationSet = {
include: [ /* locations */ ],
exclude: [ /* locations */ ]
};
```
A **location** can be any of:
| Kind | Example |
|---|---|
| A [country-coder](https://github.com/rapideditor/country-coder#readme) identifier (ISO codes, UN M.49 codes, Wikidata QIDs, etc.) | `"de"`, `"001"`, `"conus"`, `"gb-sct"`, `"Q620634"` |
| A custom `.geojson` filename (add custom features via the constructor or `addFeatures`) | `"de-hamburg.geojson"`, `"new_jersey.geojson"` |
| A circular area `[lon, lat, radius?]` (radius in km, defaults to 25) | `[8.67039, 49.41882]`, `[-88.3726, 39.4818, 32]` |
> [!WARNING]
> For numeric-looking country-coder identifiers (for example UN M.49 codes), pass strings like `"001"` and `"039"`.
> Avoid using numeric literals in JavaScript, because leading-zero values may be treated like octal numbers and represent a different number than you expect.
> [!TIP]
> The full list of recognized country-coder identifiers is browsable at <https://ideditor.codes>.
---
## Installing
### Node
```bash
npm install @rapideditor/location-conflation
```
The package is distributed as both CJS and ESM:
```js
const { LocationConflation } = require('@rapideditor/location-conflation'); // CJS
// or
import { LocationConflation } from '@rapideditor/location-conflation'; // ESM
```
### Browser
Fetch the [IIFE bundle](https://esbuild.github.io/api/#format-iife) from the [jsDelivr CDN](https://www.jsdelivr.com/). Loading it via `<script>` exposes a `LocationConflation` global:
```html
<script src="https://cdn.jsdelivr.net/npm/@rapideditor/location-conflation@latest/dist/js/location-conflation.iife.min.js"></script>
<script>
const loco = new LocationConflation();
</script>
```
> [!NOTE]
> This project uses modern TypeScript syntax. If you need to support legacy environments like ES5 or Internet Explorer, build your own bundle with a tool like [Babel](https://babeljs.io/).
---
## Examples
```js
import { LocationConflation } from '@rapideditor/location-conflation';
import myFeatures from './fixtures/features.json' with { type: 'json' }; // optional
const loco = new LocationConflation(myFeatures);
```
### Southern Europe
```js
const result = loco.resolveLocationSet({ include: ['039'] }); // 039 = Southern Europe
```
<img width="800px" alt="Southern Europe" src="https://raw.githubusercontent.com/rapideditor/location-conflation/main/docs/images/example1.png"/>
### Southern Europe and Northern Africa
```js
const result = loco.resolveLocationSet({ include: ['039', '015'] }); // 015 = Northern Africa
```
<img width="800px" alt="Southern Europe and Northern Africa" src="https://raw.githubusercontent.com/rapideditor/location-conflation/main/docs/images/example2.png"/>
### Southern Europe and Northern Africa, _excluding_ Egypt and Sudan
```js
const result = loco.resolveLocationSet({ include: ['039', '015'], exclude: ['eg', 'sd'] });
```
<img width="800px" alt="Southern Europe and Northern Africa, excluding Egypt and Sudan" src="https://raw.githubusercontent.com/rapideditor/location-conflation/main/docs/images/example3.png"/>
### The Alps, _excluding_ Liechtenstein and regions around Bern and Zürich
```js
const result = loco.resolveLocationSet({
include: ['alps.geojson'],
exclude: ['li', [8.55, 47.36], [7.45, 46.95]]
});
```
<img width="800px" alt="The Alps, excluding Liechtenstein and regions around Bern and Zürich" src="https://raw.githubusercontent.com/rapideditor/location-conflation/main/docs/images/example4.png"/>
---
## Spatial index
If your application has many objects with `locationSet` values (e.g. presets, rules, rendering styles) and you need to test *which of them apply at a given point*, the spatial-index API makes that lookup fast.
Register your objects once:
```js
const presets = [
{ id: 'amenity/cafe', locationSet: { include: ['de'] } },
{ id: 'amenity/atm', locationSet: { include: ['001'] } }, // 001 = world
{ id: 'parking/bike', locationSet: { include: ['us-nj.geojson'] } },
];
loco.registerLocationSets(presets);
// Each preset now has a stable `locationSetID`, e.g. '+[Q183]'
```
Then query by point as often as you like:
```js
const hits = loco.locationSetsAt([8.68, 49.41]); // Heidelberg
// => Map { '+[Q183]' => 357386, '+[Q2]' => 511207893 }
if (hits.has('+[Q183]')) { /* Germany preset applies here */ }
```
> [!NOTE]
> `locationSetsAt` does **no** polygon clipping — lookups are backed by [country-coder](https://github.com/rapideditor/country-coder)'s built-in spatial index for country-coder regions, and by [which-polygon](https://github.com/mapbox/which-polygon) for custom `.geojson` and point-radius features.
> [!IMPORTANT]
> `registerLocationSets` is **tolerant** of bad input so a batch of thousands of objects won't be rejected over a single typo:
> - Objects with a missing, empty, or invalid `locationSet` fall back to world (`+[Q2]`).
> - Individual invalid `include`/`exclude` components are silently ignored.
>
> The single-item `validate*` / `resolve*` methods, by contrast, always throw on invalid input.
See the [API reference](API.md#spatial-index) for full details on `registerLocationSets`, `locationSetsAt`, `rebuildIndex`, and `getLocationSetArea`.
---
## API
Full reference: **[API.md](API.md)**
| Method | Purpose |
|---|---|
| [`new LocationConflation(features?)`](API.md#constructor) | Construct an instance, optionally seeded with custom `.geojson` features. |
| [`addFeatures`](API.md#addfeatures) / [`removeFeatures`](API.md#removefeatures) / [`clearFeatures`](API.md#clearfeatures) | Manage the custom feature cache. |
| [`validateLocation`](API.md#validatelocation) / [`validateLocationSet`](API.md#validatelocationset) | Validate without resolving geometry. |
| [`resolveLocation`](API.md#resolvelocation) / [`resolveLocationSet`](API.md#resolvelocationset) | Validate and resolve to GeoJSON (cached). |
| [`registerLocationSets`](API.md#registerlocationsets) | Register `locationSet`-bearing objects for fast point-in-polygon lookups. |
| [`locationSetsAt`](API.md#locationsetsat) | Query which indexed locationSets cover a point. |
| [`rebuildIndex`](API.md#rebuildindex) / [`getLocationSetArea`](API.md#getlocationsetarea) | Spatial-index maintenance / inspection. |
| [`LocationConflation.stringify`](API.md#stringify) | Pretty-print GeoJSON with sensible defaults. |
---
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
### Thanks!
**location-conflation** is really a thin wrapper around these other great projects:
- [country-coder](https://github.com/rapideditor/country-coder) — world boundaries
- [polyclip-ts](https://github.com/luizbarboza/polyclip-ts) — polygon union / difference
- [which-polygon](https://github.com/mapbox/which-polygon) — spatial index for custom shapes
### License
Available under the [ISC License](https://opensource.org/licenses/ISC). See [LICENSE.md](LICENSE.md).