npm package: @osmix/change
Description: An OSM changeset generator and merger built on top of @osmix/core
Latest version: 0.1.7

# @osmix/change

`@osmix/change` is the change-management companion to [`@osmix/core`](../core/README.md). It builds, inspects, and applies OpenStreetMap changesets on top of `Osmix` datasets, giving you tools to deduplicate entities, reconcile overlaps, generate stats, and orchestrate merge pipelines.

## Highlights

- Construct repeatable `OsmixChangeset`s that track creates, modifies, and deletes with origin metadata and per-entity refs.
- **Augmented diffs**: Automatically captures both old and new entity states for modifications and deletions, following the [Overpass API Augmented Diffs](https://wiki.openstreetmap.org/wiki/Overpass_API/Augmented_Diffs) format.
- Deduplicate coincident nodes or overlapping ways, replace references, and optionally create intersection points where geometry meets.
- Generate summary stats and OSC-friendly XML fragments so downstream systems can audit each change step.
- Run `merge(base, patch, options)` to execute the full dedupe/merge workflow with a single call.
- Export lightweight utilities for measuring distances, pruning duplicate refs, and deciding when ways should connect.

## Installation

```sh
bun install @osmix/change
```

You will typically install this alongside [`@osmix/core`](../core/README.md), which supplies the `Osmix` datasets the changes operate on.

## Usage

### Build and apply a changeset

```ts
import { Osmix } from "osmix"
import { OsmixChangeset, changeStatsSummary, applyChangesToOsm } from "@osmix/change"

const base = await Osmix.fromPbf(Bun.file('./monaco.pbf').stream())
const patch = await Osmix.fromPbf(Bun.file('./monaco-changes.pbf').stream())

const changeset = new OsmixChangeset(base)
changeset.deduplicateNodes(base.nodes)
changeset.deduplicateWays(base.ways)
changeset.generateDirectChanges(patch)

console.log(changeStatsSummary(changeset.stats))

const merged = applyChangesToOsm(changeset)
```

`OsmixChangeset` keeps track of creates/modifies/deletes per entity type. Call the helpers (`deduplicateNodes`, `deduplicateWays`, `generateDirectChanges`, `createIntersectionsForWays`, etc.) in whatever order your workflow requires, then use `applyChangesToOsm()` to produce a new `Osm` instance with the edits applied.

### Run the bundled merge pipeline

```ts
import { merge } from "@osmix/change"

const combined = await merge(base, patch, {
	directMerge: true,
	deduplicateNodes: true,
	deduplicateWays: true,
	createIntersections: true,
})
```

`merge` wraps a sequence of changesets that deduplicate each dataset, optionally create intersections, and (when `directMerge` is true) generate modifications that reconcile the patch into the base. All options default to `false`, so you can enable only the stages you need.

## API

### `OsmixChangeset`

Tracks and orchestrates changes against a base `Osm` dataset.

```ts
constructor(base: Osm)
```

#### Core methods

- `deduplicateNodes(nodes: Nodes)`: Check a set of nodes (usually from the base or patch) for duplicates against the base dataset. Deletes duplicates and maps their IDs to the surviving node.
- `deduplicateWays(ways: Ways)`: Check a set of ways for geometric duplicates. Deletes duplicates and preserves the one with more tags/metadata.
- `generateDirectChanges(patch: Osm)`: Merge a patch dataset into the changeset. Handles creates and updates.
- `createIntersectionsForWays(ways: Ways)`: Checks provided ways for intersections with existing ways in the base dataset. Splits ways and inserts nodes where they cross.
- `applyNodeReplacementsToWays(replacementMap)`: Updates way references based on a map of replaced node IDs (generated by `deduplicateNodes`).
- `applyNodeReplacementsToRelations(replacementMap)`: Updates relation members based on replaced node IDs.

### `merge(base: Osm, patch: Osm, options)`

High-level pipeline to merge `patch` into `base`. Returns a new `Osm` instance.

Options:
- `directMerge` (boolean): Apply creates/updates from patch.
- `deduplicateNodes` (boolean): Run node deduplication.
- `deduplicateWays` (boolean): Run way deduplication.
- `createIntersections` (boolean): Split intersecting ways.

### `applyChangesToOsm(changeset: OsmixChangeset): Osm`

Applies all pending changes in the changeset to produce a **new** `Osm` instance. The original `base` is immutable.

### Augmented Diffs

By default, all `OsmChange` records include an `oldEntity` field for modifications and deletions, capturing the entity's state before the change. This follows the [Overpass API Augmented Diffs](https://wiki.openstreetmap.org/wiki/Overpass_API/Augmented_Diffs) format.

```ts
import { OsmChangeset } from "@osmix/change"

const changeset = new OsmChangeset(base)
changeset.generateDirectChanges(patch)

// Access the old and new state for a modified entity
const wayChange = changeset.wayChanges[wayId]
console.log("Old tags:", wayChange.oldEntity?.tags)
console.log("New tags:", wayChange.entity.tags)
```

### `generateOscChanges(changeset, options?)`

Generates OSC (OSM Change) XML format from a changeset.

```ts
import { generateOscChanges } from "@osmix/change"

// Generate standard OSC for API uploads (default)
const osc = generateOscChanges(changeset)

// Generate augmented diff with old/new sections
const augmentedOsc = generateOscChanges(changeset, { augmented: true })
```

Options:
- `augmented` (boolean, default: `false`): When true, modifications include `<old>` and `<new>` sections, and deletions include `<old>` sections with the full entity data.

## Related Packages

- [`@osmix/core`](../core/README.md) – Typed-array index powering the change operations.
- [`@osmix/pbf`](../pbf/README.md) – Streaming helpers used to read and write `.osm.pbf` data.
- [`@osmix/json`](../json/README.md) – JSON entity adapters that pair with change workflows.
- [Osmix Merge app](../../apps/merge/README.md) – Browser UI built on top of the change pipeline.

## Environment and limitations

- Requires runtimes compatible with `@osmix/core` (Node 20+, Bun, or modern browsers) since the same typed-array data structures are used.
- Deduplication helpers assume datasets store dense node blocks and rely on spatial indexes built via `Osmix.buildIndexes()`.
- Intersections are generated only for highway/footway-style features; polygonal ways are ignored.

## Development

- `bun run test packages/change`
- `bun run lint packages/change`
- `bun run typecheck packages/change`

Run `bun run check` at the repo root before publishing to ensure formatting, lint, and type coverage.