@osm-editor-kit/osm-way-chain 0.1.0-alpha.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -15
- package/dist/domain/types.d.ts +8 -5
- package/dist/index.d.ts +6 -6
- package/dist/index.js +148 -188
- package/dist/ports/OsmDataAdapter.d.ts +3 -0
- package/dist/session-graph-adapter.d.ts +2 -1
- package/dist/traversal/buildChain.d.ts +24 -6
- package/dist/traversal/direction.d.ts +9 -1
- package/dist/traversal/endpoints.d.ts +7 -0
- package/dist/traversal/mirror-tags.d.ts +2 -1
- package/dist/traversal/neighborMatch.d.ts +5 -5
- package/dist/way-selection-policy.d.ts +8 -3
- package/package.json +6 -7
- package/dist/highway-filter.d.ts +0 -36
package/README.md
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
# `@osm-editor-kit/osm-way-chain`
|
|
2
2
|
|
|
3
|
+
Walk connected OSM ways from a center way and orient them into one chain, for editors that change several segments of a street at once.
|
|
4
|
+
|
|
3
5
|
> [!NOTE]
|
|
4
|
-
>
|
|
6
|
+
> Claude Opus 5.5 wrote and reviewed the code of this package. [@tordans](https://github.com/tordans) specified what it should do and tested and verified its user-facing behavior.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @osm-editor-kit/osm-way-chain
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
It depends on `@osm-editor-kit/osm-data`, which npm installs for you.
|
|
5
15
|
|
|
6
16
|
## What it does
|
|
7
17
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
18
|
+
- Walks connected ways left and right from a center way
|
|
19
|
+
- Ranks neighbor candidates (same kind of road, name, ref)
|
|
20
|
+
- Reports junctions where more than one neighbor is equally good, so the user can choose
|
|
21
|
+
- Flips geometry and directional tags when a way is digitized the "wrong" way (`mirrorTags`)
|
|
22
|
+
- Compiles app-owned way-selection policies to Overpass queries and matches them against in-memory tags
|
|
23
|
+
- Reads from an in-memory `ParsedOsmData` session (`createSessionGraphAdapter`) or any data source that implements `OsmDataAdapter`
|
|
14
24
|
|
|
15
25
|
## Usage
|
|
16
26
|
|
|
@@ -25,7 +35,7 @@ import {
|
|
|
25
35
|
type OsmWaySelectionPolicy,
|
|
26
36
|
} from '@osm-editor-kit/osm-way-chain'
|
|
27
37
|
|
|
28
|
-
|
|
38
|
+
// App-owned contract: which ways to download and chain. The package has no defaults.
|
|
29
39
|
const wayPolicy: OsmWaySelectionPolicy = {
|
|
30
40
|
include: [
|
|
31
41
|
{ all: [tag.oneOf('highway', ['residential', 'cycleway', 'path', 'footway', 'steps'])] },
|
|
@@ -39,15 +49,53 @@ const adapter = createSessionGraphAdapter(graph)
|
|
|
39
49
|
const { chain, pendingJunctions } = await buildChain(adapter, {
|
|
40
50
|
centerWayId: 123456,
|
|
41
51
|
maxPerSide: 5,
|
|
42
|
-
candidateFilter: (candidate,
|
|
43
|
-
matchesOsmWaySelectionSegment(candidate, wayPolicy) &&
|
|
44
|
-
matchesOsmWaySelectionSegment(from, wayPolicy),
|
|
52
|
+
candidateFilter: (candidate) => matchesOsmWaySelectionSegment(candidate, wayPolicy),
|
|
45
53
|
})
|
|
46
54
|
|
|
47
|
-
// Overpass
|
|
48
|
-
buildWaysOverpassQuery(wayPolicy, '52.5,13.4,52.51,13.41')
|
|
55
|
+
// Overpass query that downloads the same ways for a bbox (south,west,north,east):
|
|
56
|
+
const query = buildWaysOverpassQuery(wayPolicy, '52.5,13.4,52.51,13.41')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`buildChain` returns the ordered `chain.segments`, the index of the center way in `chain.centerIndex`, and `pendingJunctions` for places where it could not pick a neighbor.
|
|
60
|
+
|
|
61
|
+
## Junctions
|
|
62
|
+
|
|
63
|
+
When a junction has several equally good neighbors, `buildChain` stops there and lists them in `pendingJunctions`. Let the user pick one, then continue with `extendChainAtJunction(adapter, chain, choice, selectedWayId, { maxPerSide, candidateFilter })`. Use `recenterChain(chain, wayId)` to move the center to another way in the chain without reloading; it returns the chain unchanged when the way is not part of it.
|
|
64
|
+
|
|
65
|
+
## Way-selection policies
|
|
66
|
+
|
|
67
|
+
A policy is a list of `include` clauses (OR), each a list of tag predicates (AND), plus optional `globalAll` predicates added to every clause. Build predicates with `tag.eq`, `tag.neq`, `tag.oneOf`, `tag.noneOf`, `tag.regex`, `tag.nregex`, `tag.present` and `tag.absent`. `compileOverpassWaySelectors` returns one Overpass filter per clause, `matchesOsmWaySelection` tests in-memory tags. A missing tag matches `neq`, `noneOf`, `nregex` and `absent`, as in Overpass.
|
|
68
|
+
|
|
69
|
+
`tag.regex` and `tag.nregex` patterns run as JavaScript `RegExp` in memory but go to Overpass unchanged, where they are POSIX extended regular expressions. Use only syntax both understand: anchors, groups, `|`, `[...]`, `*`, `+`, `?`, `{n,m}`. `\d`, `(?:...)`, lookarounds and lazy quantifiers behave differently or fail in Overpass.
|
|
70
|
+
|
|
71
|
+
## Helpers
|
|
72
|
+
|
|
73
|
+
- `mirrorTags(tags)` returns the tags as if the way were digitized the other way round (left/right, forward/backward, `oneway`, `*:lanes`, `placement`). `normalizeTagsForDirection(tags, reversed)` applies it only when `reversed`; `swapLeftRightKey(key)` swaps one key.
|
|
74
|
+
- `orientNeighbor(neighbor, sharedNodeId, from)` flips a neighbor so it runs in the direction of `from`.
|
|
75
|
+
- `getSharedNodeBetween(a, b)`, `getEndpointNodeId(segment, direction)` and `getOtherEndpointNodeId(segment, nodeId)` read way endpoints.
|
|
76
|
+
- `osmWayToSegment(way, nodeCoords)` turns an `OsmWay` from `@osm-editor-kit/osm-data` into a `Segment`.
|
|
77
|
+
|
|
78
|
+
## Other data sources
|
|
79
|
+
|
|
80
|
+
Implement `OsmDataAdapter` to chain ways from somewhere other than a loaded session:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import type { OsmDataAdapter, Segment } from '@osm-editor-kit/osm-way-chain'
|
|
84
|
+
|
|
85
|
+
const waysById = new Map<number, Segment>() // fill from your own source
|
|
86
|
+
|
|
87
|
+
const adapter: OsmDataAdapter = {
|
|
88
|
+
getWay: async (wayId) => {
|
|
89
|
+
const way = waysById.get(wayId)
|
|
90
|
+
if (!way) throw new Error(`Way ${wayId} not found`)
|
|
91
|
+
return way
|
|
92
|
+
},
|
|
93
|
+
// All ways that use this node
|
|
94
|
+
getWaysForNode: async (nodeId) =>
|
|
95
|
+
[...waysById.values()].filter((way) => way.nodeIds.includes(nodeId)),
|
|
96
|
+
}
|
|
49
97
|
```
|
|
50
98
|
|
|
51
|
-
|
|
99
|
+
## License
|
|
52
100
|
|
|
53
|
-
|
|
101
|
+
MIT
|
package/dist/domain/types.d.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
|
+
import type { OsmTags } from '@osm-editor-kit/osm-data';
|
|
1
2
|
import type { LineString } from 'geojson';
|
|
2
|
-
|
|
3
|
+
/** Walking direction along a way: `forward` follows its node order, `backward` goes against it. */
|
|
4
|
+
export type NeighborDirection = 'backward' | 'forward';
|
|
5
|
+
/** One OSM way as a chain member. */
|
|
3
6
|
export type Segment = {
|
|
4
7
|
id: number;
|
|
5
8
|
version: number;
|
|
6
9
|
nodeIds: number[];
|
|
7
10
|
tags: OsmTags;
|
|
8
11
|
geometry: LineString;
|
|
9
|
-
/** True when geometry
|
|
12
|
+
/** True when node order, geometry and directional tags were flipped to match the chain direction */
|
|
10
13
|
reversed?: boolean;
|
|
11
14
|
};
|
|
12
15
|
export type SegmentChain = {
|
|
13
16
|
/** Ordered segments: predecessors (left), center, successors (right) */
|
|
14
17
|
segments: Segment[];
|
|
15
|
-
/** Index of the
|
|
18
|
+
/** Index of the center segment in `segments` */
|
|
16
19
|
centerIndex: number;
|
|
17
20
|
};
|
|
21
|
+
/** A junction where several neighbors are equally good; the user picks one of `candidates`. */
|
|
18
22
|
export type JunctionChoice = {
|
|
19
23
|
nodeId: number;
|
|
20
|
-
direction:
|
|
24
|
+
direction: NeighborDirection;
|
|
21
25
|
candidates: Segment[];
|
|
22
26
|
};
|
|
23
|
-
export type NeighborDirection = 'backward' | 'forward';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export type { JunctionChoice, NeighborDirection,
|
|
2
|
-
export { createLegacyStreetRoadWayPolicy, DEFAULT_HIGHWAY_INCLUSION_STYLE, isClutterAccessWay, isEditableRoadLikeHighway, isEditableRoadLikeSegment, isRoadLikeHighway, isRoadLikeSegment, matchesHighwayInclusionStyle, overpassRoadLikeSelector, ROAD_LIKE_HIGHWAY_BASE_REGEX, OVERPASS_PUBLIC_ROAD_FILTERS, type HighwayInclusionStyle, } from './highway-filter';
|
|
3
|
-
export { buildWaysOverpassQuery, compileOverpassWaySelector, compileOverpassWaySelectors, matchesOsmWaySelection, matchesOsmWaySelectionSegment, tag, type OsmTagPredicate, type OsmWayFilterClause, type OsmWaySelectionPolicy, } from './way-selection-policy';
|
|
1
|
+
export type { JunctionChoice, NeighborDirection, Segment, SegmentChain } from './domain/types';
|
|
4
2
|
export type { OsmDataAdapter } from './ports/OsmDataAdapter';
|
|
5
3
|
export { createSessionGraphAdapter, osmWayToSegment } from './session-graph-adapter';
|
|
6
|
-
export { buildChain, extendChainAtJunction,
|
|
7
|
-
export {
|
|
4
|
+
export { buildChain, extendChainAtJunction, recenterChain, type BuildChainOptions, type BuildChainResult, type ExtendChainAtJunctionOptions, } from './traversal/buildChain';
|
|
5
|
+
export type { CandidateFilter } from './traversal/neighborMatch';
|
|
6
|
+
export { getEndpointNodeId, getOtherEndpointNodeId, getSharedNodeBetween, } from './traversal/endpoints';
|
|
7
|
+
export { normalizeTagsForDirection, orientNeighbor, swapLeftRightKey } from './traversal/direction';
|
|
8
8
|
export { mirrorTags } from './traversal/mirror-tags';
|
|
9
|
-
export {
|
|
9
|
+
export { buildWaysOverpassQuery, compileOverpassWaySelector, compileOverpassWaySelectors, matchesOsmWaySelection, matchesOsmWaySelectionSegment, tag, type OsmTagPredicate, type OsmWayFilterClause, type OsmWaySelectionPolicy, } from './way-selection-policy';
|
package/dist/index.js
CHANGED
|
@@ -1,155 +1,3 @@
|
|
|
1
|
-
// src/way-selection-policy.ts
|
|
2
|
-
var tag = {
|
|
3
|
-
eq: (key, value) => ({ key, op: "eq", value }),
|
|
4
|
-
neq: (key, value) => ({ key, op: "neq", value }),
|
|
5
|
-
oneOf: (key, values) => ({
|
|
6
|
-
key,
|
|
7
|
-
op: "in",
|
|
8
|
-
values
|
|
9
|
-
}),
|
|
10
|
-
noneOf: (key, values) => ({
|
|
11
|
-
key,
|
|
12
|
-
op: "nin",
|
|
13
|
-
values
|
|
14
|
-
}),
|
|
15
|
-
regex: (key, pattern) => ({ key, op: "regex", pattern }),
|
|
16
|
-
nregex: (key, pattern) => ({ key, op: "nregex", pattern }),
|
|
17
|
-
present: (key) => ({ key, op: "present" }),
|
|
18
|
-
absent: (key) => ({ key, op: "absent" })
|
|
19
|
-
};
|
|
20
|
-
function escapeRegexAlternation(values) {
|
|
21
|
-
return values.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
22
|
-
}
|
|
23
|
-
function quoteOverpassValue(value) {
|
|
24
|
-
if (/^[A-Za-z0-9_:-]+$/.test(value)) return value;
|
|
25
|
-
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
26
|
-
}
|
|
27
|
-
function escapeOverpassRegex(pattern) {
|
|
28
|
-
return pattern.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
29
|
-
}
|
|
30
|
-
function compileTagPredicate(predicate) {
|
|
31
|
-
switch (predicate.op) {
|
|
32
|
-
case "eq":
|
|
33
|
-
return `[${predicate.key}=${quoteOverpassValue(predicate.value)}]`;
|
|
34
|
-
case "neq":
|
|
35
|
-
return `[${predicate.key}!=${quoteOverpassValue(predicate.value)}]`;
|
|
36
|
-
case "in":
|
|
37
|
-
return `[${predicate.key}~"^(${escapeRegexAlternation(predicate.values)})$"]`;
|
|
38
|
-
case "nin":
|
|
39
|
-
return `[${predicate.key}!~"^(${escapeRegexAlternation(predicate.values)})$"]`;
|
|
40
|
-
case "regex":
|
|
41
|
-
return `[${predicate.key}~"${escapeOverpassRegex(predicate.pattern)}"]`;
|
|
42
|
-
case "nregex":
|
|
43
|
-
return `[${predicate.key}!~"${escapeOverpassRegex(predicate.pattern)}"]`;
|
|
44
|
-
case "present":
|
|
45
|
-
return `[${predicate.key}]`;
|
|
46
|
-
case "absent":
|
|
47
|
-
return `[!${predicate.key}]`;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function clausePredicates(policy, clause) {
|
|
51
|
-
return [...clause.all, ...policy.globalAll ?? []];
|
|
52
|
-
}
|
|
53
|
-
function compileOverpassWaySelectors(policy) {
|
|
54
|
-
if (policy.include.length === 0) {
|
|
55
|
-
throw new Error("OsmWaySelectionPolicy.include must contain at least one clause");
|
|
56
|
-
}
|
|
57
|
-
return policy.include.map(
|
|
58
|
-
(clause) => clausePredicates(policy, clause).map(compileTagPredicate).join("")
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
function compileOverpassWaySelector(policy) {
|
|
62
|
-
const selectors = compileOverpassWaySelectors(policy);
|
|
63
|
-
if (selectors.length !== 1) {
|
|
64
|
-
throw new Error(
|
|
65
|
-
`compileOverpassWaySelector expects exactly one include clause, got ${selectors.length}`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
return selectors[0];
|
|
69
|
-
}
|
|
70
|
-
function matchesTagPredicate(tags, predicate) {
|
|
71
|
-
const raw = tags[predicate.key];
|
|
72
|
-
switch (predicate.op) {
|
|
73
|
-
case "eq":
|
|
74
|
-
return raw === predicate.value;
|
|
75
|
-
case "neq":
|
|
76
|
-
return raw !== predicate.value;
|
|
77
|
-
case "in":
|
|
78
|
-
return raw != null && predicate.values.includes(raw);
|
|
79
|
-
case "nin":
|
|
80
|
-
return raw == null || !predicate.values.includes(raw);
|
|
81
|
-
case "regex":
|
|
82
|
-
return raw != null && new RegExp(predicate.pattern).test(raw);
|
|
83
|
-
case "nregex":
|
|
84
|
-
return raw == null || !new RegExp(predicate.pattern).test(raw);
|
|
85
|
-
case "present":
|
|
86
|
-
return raw != null && raw !== "";
|
|
87
|
-
case "absent":
|
|
88
|
-
return raw == null || raw === "";
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function matchesClause(tags, predicates) {
|
|
92
|
-
return predicates.every((predicate) => matchesTagPredicate(tags, predicate));
|
|
93
|
-
}
|
|
94
|
-
function matchesOsmWaySelection(tags, policy) {
|
|
95
|
-
return policy.include.some((clause) => matchesClause(tags, clausePredicates(policy, clause)));
|
|
96
|
-
}
|
|
97
|
-
function matchesOsmWaySelectionSegment(segment, policy) {
|
|
98
|
-
return matchesOsmWaySelection(segment.tags, policy);
|
|
99
|
-
}
|
|
100
|
-
function buildWaysOverpassQuery(policy, bbox, options) {
|
|
101
|
-
const selectors = compileOverpassWaySelectors(policy);
|
|
102
|
-
const timeout = options?.timeoutSeconds ?? 60;
|
|
103
|
-
const out = options?.out ?? "meta";
|
|
104
|
-
const union = selectors.map((selector) => `way${selector}(${bbox});`).join("");
|
|
105
|
-
return `[out:xml][timeout:${timeout}];(${union});(._;>;);out ${out};`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// src/highway-filter.ts
|
|
109
|
-
var DEFAULT_HIGHWAY_INCLUSION_STYLE = "public";
|
|
110
|
-
function createLegacyStreetRoadWayPolicy(style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
|
|
111
|
-
const highway = tag.regex(
|
|
112
|
-
"highway",
|
|
113
|
-
"^(motorway|trunk|primary|secondary|tertiary|unclassified|residential|living_street|service)(_link)?$|^busway$"
|
|
114
|
-
);
|
|
115
|
-
if (style === "inclusive") {
|
|
116
|
-
return { include: [{ all: [highway] }] };
|
|
117
|
-
}
|
|
118
|
-
return {
|
|
119
|
-
include: [{ all: [highway] }],
|
|
120
|
-
globalAll: [
|
|
121
|
-
tag.neq("access", "private"),
|
|
122
|
-
tag.noneOf("service", ["emergency_access", "parking_aisle", "driveway"])
|
|
123
|
-
]
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
var ROAD_LIKE_HIGHWAY_BASE_REGEX = /^motorway|trunk|primary|secondary|tertiary|unclassified|residential|living_street|service/;
|
|
127
|
-
var OVERPASS_PUBLIC_ROAD_FILTERS = "[service!=emergency_access][service!=parking_aisle][service!=driveway][access!=private]";
|
|
128
|
-
function overpassRoadLikeSelector(style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
|
|
129
|
-
return compileOverpassWaySelector(createLegacyStreetRoadWayPolicy(style)).slice(1);
|
|
130
|
-
}
|
|
131
|
-
function isClutterAccessWay(tags) {
|
|
132
|
-
if (tags.access === "private") return true;
|
|
133
|
-
const service = tags.service;
|
|
134
|
-
return service === "driveway" || service === "emergency_access" || service === "parking_aisle";
|
|
135
|
-
}
|
|
136
|
-
function matchesHighwayInclusionStyle(tags, style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
|
|
137
|
-
if (style === "inclusive") return true;
|
|
138
|
-
return !isClutterAccessWay(tags);
|
|
139
|
-
}
|
|
140
|
-
function isRoadLikeHighway(tags) {
|
|
141
|
-
return matchesOsmWaySelection(tags, createLegacyStreetRoadWayPolicy("inclusive"));
|
|
142
|
-
}
|
|
143
|
-
function isEditableRoadLikeHighway(tags, style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
|
|
144
|
-
return matchesOsmWaySelection(tags, createLegacyStreetRoadWayPolicy(style));
|
|
145
|
-
}
|
|
146
|
-
function isRoadLikeSegment(segment) {
|
|
147
|
-
return isRoadLikeHighway(segment.tags);
|
|
148
|
-
}
|
|
149
|
-
function isEditableRoadLikeSegment(segment, style = DEFAULT_HIGHWAY_INCLUSION_STYLE) {
|
|
150
|
-
return isEditableRoadLikeHighway(segment.tags, style);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
1
|
// src/session-graph-adapter.ts
|
|
154
2
|
function wayCoordinates(way, nodeCoords) {
|
|
155
3
|
return way.nodes.map((nodeId) => {
|
|
@@ -315,7 +163,7 @@ function isBareSideKey(key) {
|
|
|
315
163
|
const lower = key.toLowerCase();
|
|
316
164
|
return lower === "sidewalk" || lower === "cycleway" || lower === "busway";
|
|
317
165
|
}
|
|
318
|
-
function mirrorTagValue(key, value,
|
|
166
|
+
function mirrorTagValue(key, value, laneCount) {
|
|
319
167
|
if (isOnewayKey(key)) return mirrorOnewayValue(value);
|
|
320
168
|
if (isPlacementKey(key)) return mirrorPlacementValue(value, laneCount);
|
|
321
169
|
if (isBareSideKey(key)) return mirrorBareSideValue(value);
|
|
@@ -327,7 +175,7 @@ function mirrorTags(tags) {
|
|
|
327
175
|
const mirrored = {};
|
|
328
176
|
for (const [key, value] of Object.entries(tags)) {
|
|
329
177
|
const mirroredKey = mirrorDirectionalKey(key);
|
|
330
|
-
mirrored[mirroredKey] = mirrorTagValue(key, value,
|
|
178
|
+
mirrored[mirroredKey] = mirrorTagValue(key, value, laneCount);
|
|
331
179
|
}
|
|
332
180
|
return mirrored;
|
|
333
181
|
}
|
|
@@ -352,25 +200,44 @@ function normalizeTagsForDirection(tags, reversed) {
|
|
|
352
200
|
function orientNeighbor(neighbor, sharedNodeId, fromSegment) {
|
|
353
201
|
const reversed = isReversedAtNode(fromSegment, neighbor, sharedNodeId);
|
|
354
202
|
if (!reversed) return { ...neighbor, reversed: false };
|
|
355
|
-
const reversedNodeIds = [...neighbor.nodeIds].reverse();
|
|
356
|
-
const reversedCoords = [...neighbor.geometry.coordinates].reverse();
|
|
357
203
|
return {
|
|
358
204
|
...neighbor,
|
|
359
|
-
nodeIds:
|
|
205
|
+
nodeIds: [...neighbor.nodeIds].reverse(),
|
|
360
206
|
geometry: {
|
|
361
207
|
type: "LineString",
|
|
362
|
-
coordinates:
|
|
208
|
+
coordinates: [...neighbor.geometry.coordinates].reverse()
|
|
363
209
|
},
|
|
364
|
-
tags:
|
|
210
|
+
tags: mirrorTags(neighbor.tags),
|
|
365
211
|
reversed: true
|
|
366
212
|
};
|
|
367
213
|
}
|
|
368
214
|
|
|
215
|
+
// src/traversal/endpoints.ts
|
|
216
|
+
function getEndpointNodeId(segment, direction) {
|
|
217
|
+
const { nodeIds } = segment;
|
|
218
|
+
return direction === "forward" ? nodeIds[nodeIds.length - 1] : nodeIds[0];
|
|
219
|
+
}
|
|
220
|
+
function getOtherEndpointNodeId(segment, sharedNodeId) {
|
|
221
|
+
const first = segment.nodeIds[0];
|
|
222
|
+
const last = segment.nodeIds[segment.nodeIds.length - 1];
|
|
223
|
+
if (first === sharedNodeId) return last;
|
|
224
|
+
if (last === sharedNodeId) return first;
|
|
225
|
+
return void 0;
|
|
226
|
+
}
|
|
227
|
+
function getSharedNodeBetween(a, b) {
|
|
228
|
+
const aEnds = /* @__PURE__ */ new Set([a.nodeIds[0], a.nodeIds[a.nodeIds.length - 1]]);
|
|
229
|
+
for (const nodeId of [b.nodeIds[0], b.nodeIds[b.nodeIds.length - 1]]) {
|
|
230
|
+
if (nodeId != null && aEnds.has(nodeId)) return nodeId;
|
|
231
|
+
}
|
|
232
|
+
return void 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
369
235
|
// src/traversal/neighborMatch.ts
|
|
370
236
|
var KIND_KEYS = ["highway", "cycleway", "footway", "path", "railway", "waterway"];
|
|
371
237
|
function getKindKey(tags) {
|
|
372
238
|
for (const key of KIND_KEYS) {
|
|
373
|
-
|
|
239
|
+
const value = tags[key];
|
|
240
|
+
if (value) return { key, value };
|
|
374
241
|
}
|
|
375
242
|
return void 0;
|
|
376
243
|
}
|
|
@@ -380,17 +247,6 @@ function sameKind(a, b) {
|
|
|
380
247
|
if (!kindA || !kindB) return false;
|
|
381
248
|
return kindA.key === kindB.key && kindA.value === kindB.value;
|
|
382
249
|
}
|
|
383
|
-
function getEndpointNodeId(segment, direction) {
|
|
384
|
-
const { nodeIds } = segment;
|
|
385
|
-
return direction === "forward" ? nodeIds[nodeIds.length - 1] : nodeIds[0];
|
|
386
|
-
}
|
|
387
|
-
function getOtherEndpointNodeId(segment, sharedNodeId) {
|
|
388
|
-
const first = segment.nodeIds[0];
|
|
389
|
-
const last = segment.nodeIds[segment.nodeIds.length - 1];
|
|
390
|
-
if (first === sharedNodeId) return last;
|
|
391
|
-
if (last === sharedNodeId) return first;
|
|
392
|
-
return void 0;
|
|
393
|
-
}
|
|
394
250
|
function filterNeighborCandidates(from, candidates, direction, candidateFilter) {
|
|
395
251
|
const endpoint = getEndpointNodeId(from, direction);
|
|
396
252
|
const touching = candidates.filter((c) => {
|
|
@@ -470,10 +326,10 @@ async function buildChain(adapter, options) {
|
|
|
470
326
|
pendingJunctions: [...backwardResult.pendingJunctions, ...forwardResult.pendingJunctions]
|
|
471
327
|
};
|
|
472
328
|
}
|
|
473
|
-
async function extendChainAtJunction(adapter, chain, choice, selectedWayId,
|
|
329
|
+
async function extendChainAtJunction(adapter, chain, choice, selectedWayId, options = {}) {
|
|
330
|
+
const maxPerSide = options.maxPerSide ?? DEFAULT_MAX_PER_SIDE;
|
|
474
331
|
const cache = new Map(chain.segments.map((s) => [s.id, s]));
|
|
475
|
-
const selected =
|
|
476
|
-
cache.set(selected.id, selected);
|
|
332
|
+
const selected = await loadSegment(adapter, selectedWayId, cache);
|
|
477
333
|
const anchorIndex = choice.direction === "forward" ? chain.segments.length - 1 : 0;
|
|
478
334
|
const anchor = chain.segments[anchorIndex];
|
|
479
335
|
const oriented = orientNeighbor(selected, choice.nodeId, anchor);
|
|
@@ -482,15 +338,10 @@ async function extendChainAtJunction(adapter, chain, choice, selectedWayId, maxC
|
|
|
482
338
|
cache,
|
|
483
339
|
oriented,
|
|
484
340
|
choice.direction,
|
|
485
|
-
|
|
486
|
-
candidateFilter
|
|
341
|
+
maxPerSide,
|
|
342
|
+
options.candidateFilter
|
|
487
343
|
);
|
|
488
|
-
|
|
489
|
-
if (choice.direction === "forward") {
|
|
490
|
-
segments = [...chain.segments, oriented, ...extension];
|
|
491
|
-
} else {
|
|
492
|
-
segments = [...extension.reverse(), oriented, ...chain.segments];
|
|
493
|
-
}
|
|
344
|
+
const segments = choice.direction === "forward" ? [...chain.segments, oriented, ...extension] : [...extension.reverse(), oriented, ...chain.segments];
|
|
494
345
|
return {
|
|
495
346
|
chain: {
|
|
496
347
|
segments,
|
|
@@ -504,12 +355,121 @@ function recenterChain(chain, newCenterWayId) {
|
|
|
504
355
|
if (index === -1) return chain;
|
|
505
356
|
return { segments: chain.segments, centerIndex: index };
|
|
506
357
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
358
|
+
|
|
359
|
+
// src/way-selection-policy.ts
|
|
360
|
+
var tag = {
|
|
361
|
+
eq: (key, value) => ({ key, op: "eq", value }),
|
|
362
|
+
neq: (key, value) => ({ key, op: "neq", value }),
|
|
363
|
+
oneOf: (key, values) => ({
|
|
364
|
+
key,
|
|
365
|
+
op: "in",
|
|
366
|
+
values
|
|
367
|
+
}),
|
|
368
|
+
noneOf: (key, values) => ({
|
|
369
|
+
key,
|
|
370
|
+
op: "nin",
|
|
371
|
+
values
|
|
372
|
+
}),
|
|
373
|
+
regex: (key, pattern) => ({ key, op: "regex", pattern }),
|
|
374
|
+
nregex: (key, pattern) => ({ key, op: "nregex", pattern }),
|
|
375
|
+
present: (key) => ({ key, op: "present" }),
|
|
376
|
+
absent: (key) => ({ key, op: "absent" })
|
|
377
|
+
};
|
|
378
|
+
function escapeRegexAlternation(values) {
|
|
379
|
+
return values.map((value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
380
|
+
}
|
|
381
|
+
function quoteOverpassValue(value) {
|
|
382
|
+
if (/^[A-Za-z0-9_:-]+$/.test(value)) return value;
|
|
383
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
384
|
+
}
|
|
385
|
+
function escapeOverpassRegex(pattern) {
|
|
386
|
+
return pattern.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
387
|
+
}
|
|
388
|
+
function compileTagPredicate(predicate) {
|
|
389
|
+
switch (predicate.op) {
|
|
390
|
+
case "eq":
|
|
391
|
+
return `[${predicate.key}=${quoteOverpassValue(predicate.value)}]`;
|
|
392
|
+
case "neq":
|
|
393
|
+
return `[${predicate.key}!=${quoteOverpassValue(predicate.value)}]`;
|
|
394
|
+
case "in":
|
|
395
|
+
return `[${predicate.key}~"^(${escapeRegexAlternation(predicate.values)})$"]`;
|
|
396
|
+
case "nin":
|
|
397
|
+
return `[${predicate.key}!~"^(${escapeRegexAlternation(predicate.values)})$"]`;
|
|
398
|
+
case "regex":
|
|
399
|
+
return `[${predicate.key}~"${escapeOverpassRegex(predicate.pattern)}"]`;
|
|
400
|
+
case "nregex":
|
|
401
|
+
return `[${predicate.key}!~"${escapeOverpassRegex(predicate.pattern)}"]`;
|
|
402
|
+
case "present":
|
|
403
|
+
return `[${predicate.key}]`;
|
|
404
|
+
case "absent":
|
|
405
|
+
return `[!${predicate.key}]`;
|
|
511
406
|
}
|
|
512
|
-
|
|
407
|
+
}
|
|
408
|
+
function clausePredicates(policy, clause) {
|
|
409
|
+
return [...clause.all, ...policy.globalAll ?? []];
|
|
410
|
+
}
|
|
411
|
+
function compileOverpassWaySelectors(policy) {
|
|
412
|
+
if (policy.include.length === 0) {
|
|
413
|
+
throw new Error("OsmWaySelectionPolicy.include must contain at least one clause");
|
|
414
|
+
}
|
|
415
|
+
return policy.include.map(
|
|
416
|
+
(clause) => clausePredicates(policy, clause).map(compileTagPredicate).join("")
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
function compileOverpassWaySelector(policy) {
|
|
420
|
+
const selectors = compileOverpassWaySelectors(policy);
|
|
421
|
+
if (selectors.length !== 1) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
`compileOverpassWaySelector expects exactly one include clause, got ${selectors.length}`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
return selectors[0];
|
|
427
|
+
}
|
|
428
|
+
var regexCache = /* @__PURE__ */ new Map();
|
|
429
|
+
function cachedRegex(pattern) {
|
|
430
|
+
let regex = regexCache.get(pattern);
|
|
431
|
+
if (!regex) {
|
|
432
|
+
regex = new RegExp(pattern);
|
|
433
|
+
regexCache.set(pattern, regex);
|
|
434
|
+
}
|
|
435
|
+
return regex;
|
|
436
|
+
}
|
|
437
|
+
function matchesTagPredicate(tags, predicate) {
|
|
438
|
+
const raw = tags[predicate.key];
|
|
439
|
+
switch (predicate.op) {
|
|
440
|
+
case "eq":
|
|
441
|
+
return raw === predicate.value;
|
|
442
|
+
case "neq":
|
|
443
|
+
return raw !== predicate.value;
|
|
444
|
+
case "in":
|
|
445
|
+
return raw != null && predicate.values.includes(raw);
|
|
446
|
+
case "nin":
|
|
447
|
+
return raw == null || !predicate.values.includes(raw);
|
|
448
|
+
case "regex":
|
|
449
|
+
return raw != null && cachedRegex(predicate.pattern).test(raw);
|
|
450
|
+
case "nregex":
|
|
451
|
+
return raw == null || !cachedRegex(predicate.pattern).test(raw);
|
|
452
|
+
case "present":
|
|
453
|
+
return raw != null && raw !== "";
|
|
454
|
+
case "absent":
|
|
455
|
+
return raw == null || raw === "";
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function matchesClause(tags, predicates) {
|
|
459
|
+
return predicates.every((predicate) => matchesTagPredicate(tags, predicate));
|
|
460
|
+
}
|
|
461
|
+
function matchesOsmWaySelection(tags, policy) {
|
|
462
|
+
return policy.include.some((clause) => matchesClause(tags, clausePredicates(policy, clause)));
|
|
463
|
+
}
|
|
464
|
+
function matchesOsmWaySelectionSegment(segment, policy) {
|
|
465
|
+
return matchesOsmWaySelection(segment.tags, policy);
|
|
466
|
+
}
|
|
467
|
+
function buildWaysOverpassQuery(policy, bbox, options) {
|
|
468
|
+
const selectors = compileOverpassWaySelectors(policy);
|
|
469
|
+
const timeout = options?.timeoutSeconds ?? 60;
|
|
470
|
+
const out = options?.out ?? "meta";
|
|
471
|
+
const union = selectors.map((selector) => `way${selector}(${bbox});`).join("");
|
|
472
|
+
return `[out:xml][timeout:${timeout}];(${union});(._;>;);out ${out};`;
|
|
513
473
|
}
|
|
514
474
|
|
|
515
|
-
export {
|
|
475
|
+
export { buildChain, buildWaysOverpassQuery, compileOverpassWaySelector, compileOverpassWaySelectors, createSessionGraphAdapter, extendChainAtJunction, getEndpointNodeId, getOtherEndpointNodeId, getSharedNodeBetween, matchesOsmWaySelection, matchesOsmWaySelectionSegment, mirrorTags, normalizeTagsForDirection, orientNeighbor, osmWayToSegment, recenterChain, swapLeftRightKey, tag };
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Segment } from '../domain/types';
|
|
2
|
+
/** Data source for chain building. */
|
|
2
3
|
export type OsmDataAdapter = {
|
|
4
|
+
/** Rejects when the way is unknown. */
|
|
3
5
|
getWay: (wayId: number) => Promise<Segment>;
|
|
6
|
+
/** All ways that use this node, at any position. */
|
|
4
7
|
getWaysForNode: (nodeId: number) => Promise<Segment[]>;
|
|
5
8
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { OsmWay, ParsedOsmData } from '@osm-editor-kit/osm-data';
|
|
2
2
|
import type { Segment } from './domain/types';
|
|
3
3
|
import type { OsmDataAdapter } from './ports/OsmDataAdapter';
|
|
4
|
+
/** Nodes without coordinates in `nodeCoords` are left out of the geometry. */
|
|
4
5
|
export declare function osmWayToSegment(way: OsmWay, nodeCoords: ParsedOsmData['nodeCoords']): Segment;
|
|
5
6
|
/**
|
|
6
|
-
* Adapter backed by
|
|
7
|
+
* Adapter backed by already loaded `ParsedOsmData`.
|
|
7
8
|
* Lookups are synchronous but wrapped in Promises to satisfy `OsmDataAdapter`.
|
|
8
9
|
*/
|
|
9
10
|
export declare function createSessionGraphAdapter(graph: ParsedOsmData): OsmDataAdapter;
|
|
@@ -1,20 +1,38 @@
|
|
|
1
1
|
import type { JunctionChoice, Segment, SegmentChain } from '../domain/types';
|
|
2
2
|
import type { OsmDataAdapter } from '../ports/OsmDataAdapter';
|
|
3
|
-
import { type CandidateFilter
|
|
3
|
+
import { type CandidateFilter } from './neighborMatch';
|
|
4
4
|
export type BuildChainOptions = {
|
|
5
5
|
centerWayId: number;
|
|
6
|
+
/** Maximum number of ways to add on each side of the center. Default 5. */
|
|
6
7
|
maxPerSide?: number;
|
|
7
|
-
/** Already loaded segments keyed by id */
|
|
8
|
+
/** Already loaded segments keyed by id; skips `adapter.getWay` for these. */
|
|
8
9
|
knownSegments?: Map<number, Segment>;
|
|
9
|
-
/**
|
|
10
|
+
/** Restricts which ways may join the chain. */
|
|
11
|
+
candidateFilter?: CandidateFilter;
|
|
12
|
+
};
|
|
13
|
+
export type ExtendChainAtJunctionOptions = {
|
|
14
|
+
/** Maximum number of ways to add after the selected way. Default 5. */
|
|
15
|
+
maxPerSide?: number;
|
|
16
|
+
/** Restricts which ways may join the chain. */
|
|
10
17
|
candidateFilter?: CandidateFilter;
|
|
11
18
|
};
|
|
12
19
|
export type BuildChainResult = {
|
|
13
20
|
chain: SegmentChain;
|
|
14
21
|
pendingJunctions: JunctionChoice[];
|
|
15
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Walks connected ways backward and forward from `centerWayId` and orients them into one chain.
|
|
25
|
+
* Stops on each side at `maxPerSide`, at a dead end, or at a junction it cannot decide
|
|
26
|
+
* (reported in `pendingJunctions`).
|
|
27
|
+
*/
|
|
16
28
|
export declare function buildChain(adapter: OsmDataAdapter, options: BuildChainOptions): Promise<BuildChainResult>;
|
|
17
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Continues `chain` at a pending junction with the way the user picked, then keeps walking
|
|
31
|
+
* in the junction's direction. The center way stays the same.
|
|
32
|
+
*/
|
|
33
|
+
export declare function extendChainAtJunction(adapter: OsmDataAdapter, chain: SegmentChain, choice: JunctionChoice, selectedWayId: number, options?: ExtendChainAtJunctionOptions): Promise<BuildChainResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Moves the center to `newCenterWayId` without reloading.
|
|
36
|
+
* Returns `chain` itself, unchanged, when the way is not part of the chain.
|
|
37
|
+
*/
|
|
18
38
|
export declare function recenterChain(chain: SegmentChain, newCenterWayId: number): SegmentChain;
|
|
19
|
-
export declare function getSharedNodeBetween(a: Segment, b: Segment): number | undefined;
|
|
20
|
-
export { getOtherEndpointNodeId };
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import type { OsmTags } from '@osm-editor-kit/osm-data';
|
|
1
2
|
import type { Segment } from '../domain/types';
|
|
3
|
+
/** True when both segments start (or both end) at `sharedNodeId`, so one runs against the other. */
|
|
2
4
|
export declare function isReversedAtNode(current: Segment, neighbor: Segment, sharedNodeId: number): boolean;
|
|
5
|
+
/** Swaps the first `:left` / `:right` part of a tag key, e.g. `cycleway:left:surface`. */
|
|
3
6
|
export declare function swapLeftRightKey(key: string): string;
|
|
4
|
-
|
|
7
|
+
/** `mirrorTags(tags)` when `reversed`, otherwise `tags` itself. */
|
|
8
|
+
export declare function normalizeTagsForDirection(tags: OsmTags, reversed: boolean): OsmTags;
|
|
9
|
+
/**
|
|
10
|
+
* Re-expresses `neighbor` in the direction of `fromSegment`: when they run against each other
|
|
11
|
+
* at `sharedNodeId`, node order, geometry and directional tags are flipped and `reversed` is set.
|
|
12
|
+
*/
|
|
5
13
|
export declare function orientNeighbor(neighbor: Segment, sharedNodeId: number, fromSegment: Segment): Segment;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { NeighborDirection, Segment } from '../domain/types';
|
|
2
|
+
/** Last node for `forward`, first node for `backward`. */
|
|
3
|
+
export declare function getEndpointNodeId(segment: Segment, direction: NeighborDirection): number;
|
|
4
|
+
/** The endpoint opposite `sharedNodeId`, or `undefined` when `sharedNodeId` is not an endpoint. */
|
|
5
|
+
export declare function getOtherEndpointNodeId(segment: Segment, sharedNodeId: number): number | undefined;
|
|
6
|
+
/** The node where the endpoints of `a` and `b` meet, or `undefined` when they do not touch. */
|
|
7
|
+
export declare function getSharedNodeBetween(a: Segment, b: Segment): number | undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { OsmTags } from '@osm-editor-kit/osm-data';
|
|
1
2
|
/**
|
|
2
3
|
* Mirror OSM tags as if the way were digitised in the opposite direction.
|
|
3
4
|
* Applying twice returns the original tags (deep equality).
|
|
4
5
|
*/
|
|
5
|
-
export declare function mirrorTags(tags:
|
|
6
|
+
export declare function mirrorTags(tags: OsmTags): OsmTags;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import type { NeighborDirection, Segment } from '../domain/types';
|
|
2
|
+
/** Restricts which ways may join a chain. */
|
|
2
3
|
export type CandidateFilter = (segment: Segment) => boolean;
|
|
3
4
|
export declare function getKindKey(tags: Segment['tags']): {
|
|
4
|
-
key:
|
|
5
|
+
key: string;
|
|
5
6
|
value: string;
|
|
6
7
|
} | undefined;
|
|
7
8
|
export declare function sameKind(a: Segment, b: Segment): boolean;
|
|
8
|
-
|
|
9
|
-
export declare function getOtherEndpointNodeId(segment: Segment, sharedNodeId: number): number | undefined;
|
|
10
|
-
/** Filter candidates at a junction: exclude self, prefer same kind */
|
|
9
|
+
/** Ways that start or end at `from`'s endpoint in `direction`; same-kind ways win if there are any. */
|
|
11
10
|
export declare function filterNeighborCandidates(from: Segment, candidates: Segment[], direction: NeighborDirection, candidateFilter?: CandidateFilter): Segment[];
|
|
12
|
-
/** Score
|
|
11
|
+
/** Score for auto-follow (higher = better) */
|
|
13
12
|
export declare function scoreNeighborCandidate(from: Segment, candidate: Segment): number;
|
|
13
|
+
/** The neighbor to follow, or `undefined` when there is none or the top two tie below same kind. */
|
|
14
14
|
export declare function pickBestNeighbor(from: Segment, candidates: Segment[]): Segment | undefined;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { OsmTags } from '@osm-editor-kit/osm-data';
|
|
2
2
|
import type { Segment } from './domain/types';
|
|
3
3
|
/**
|
|
4
|
-
* Atomic tag predicate. Compiles 1:1 to Overpass filter
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Atomic tag predicate. Compiles 1:1 to an Overpass tag filter and matches in-memory `OsmTags`
|
|
5
|
+
* the same way: a missing tag matches `neq`, `nin`, `nregex` and `absent`.
|
|
6
|
+
*
|
|
7
|
+
* `regex` / `nregex` patterns run as JavaScript `RegExp` in memory but go to Overpass unchanged,
|
|
8
|
+
* where they are POSIX extended regular expressions. Use only syntax both understand (anchors,
|
|
9
|
+
* groups, `|`, `[...]`, `*`, `+`, `?`, `{n,m}`); `\d`, `(?:...)`, lookarounds or lazy quantifiers
|
|
10
|
+
* will differ or fail in Overpass.
|
|
7
11
|
*/
|
|
8
12
|
export type OsmTagPredicate = {
|
|
9
13
|
readonly key: string;
|
|
@@ -56,6 +60,7 @@ export declare function compileOverpassWaySelectors(policy: OsmWaySelectionPolic
|
|
|
56
60
|
export declare function compileOverpassWaySelector(policy: OsmWaySelectionPolicy): string;
|
|
57
61
|
/** Whether OSM tags match the policy (OR of include clauses, each with globalAll). */
|
|
58
62
|
export declare function matchesOsmWaySelection(tags: OsmTags, policy: OsmWaySelectionPolicy): boolean;
|
|
63
|
+
/** {@link matchesOsmWaySelection} for a segment's tags, e.g. as a `candidateFilter`. */
|
|
59
64
|
export declare function matchesOsmWaySelectionSegment(segment: Segment, policy: OsmWaySelectionPolicy): boolean;
|
|
60
65
|
/** Builds Overpass QL that downloads matching ways (+ their nodes) for a bbox string. */
|
|
61
66
|
export declare function buildWaysOverpassQuery(policy: OsmWaySelectionPolicy, bbox: string, options?: {
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osm-editor-kit/osm-way-chain",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Traverse and orient connected OSM highway ways for multi-segment editing sessions.",
|
|
5
|
-
"homepage": "https://github.com/
|
|
5
|
+
"homepage": "https://github.com/osm-editor-kit/street-space-editor/tree/main/packages/osm-way-chain",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Tobias Jordans",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/
|
|
10
|
+
"url": "git+https://github.com/osm-editor-kit/street-space-editor.git",
|
|
11
11
|
"directory": "packages/osm-way-chain"
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
@@ -23,8 +23,7 @@
|
|
|
23
23
|
"./package.json": "./package.json"
|
|
24
24
|
},
|
|
25
25
|
"publishConfig": {
|
|
26
|
-
"access": "public"
|
|
27
|
-
"tag": "alpha"
|
|
26
|
+
"access": "public"
|
|
28
27
|
},
|
|
29
28
|
"scripts": {
|
|
30
29
|
"build": "tsup && rm -f .tsbuildinfo && tsc -p tsconfig.build.json",
|
|
@@ -36,10 +35,10 @@
|
|
|
36
35
|
"postpack": "bun ../../scripts/package-exports-restore.ts"
|
|
37
36
|
},
|
|
38
37
|
"dependencies": {
|
|
39
|
-
"@osm-editor-kit/osm-data": "^
|
|
38
|
+
"@osm-editor-kit/osm-data": "^1.0.0",
|
|
39
|
+
"@types/geojson": "^7946.0.16"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@types/geojson": "^7946.0.16",
|
|
43
42
|
"bun-types": "^1.2.20",
|
|
44
43
|
"tsup": "^8.5.1",
|
|
45
44
|
"typescript": "^7.0.0"
|
package/dist/highway-filter.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { OsmTags } from '@osm-editor-kit/osm-data';
|
|
2
|
-
import type { Segment } from './domain/types';
|
|
3
|
-
import { type OsmWaySelectionPolicy } from './way-selection-policy';
|
|
4
|
-
/**
|
|
5
|
-
* @deprecated App-owned concept. Prefer {@link OsmWaySelectionPolicy} defined in the app.
|
|
6
|
-
* Kept temporarily for street-space-editor call sites.
|
|
7
|
-
*/
|
|
8
|
-
export type HighwayInclusionStyle = 'public' | 'inclusive';
|
|
9
|
-
/** @deprecated Prefer app-owned policies. */
|
|
10
|
-
export declare const DEFAULT_HIGHWAY_INCLUSION_STYLE: HighwayInclusionStyle;
|
|
11
|
-
/**
|
|
12
|
-
* @deprecated Prefer app-owned {@link OsmWaySelectionPolicy}.
|
|
13
|
-
* Legacy lane-mode car/road selection (incl. `*_link` and `busway`).
|
|
14
|
-
*/
|
|
15
|
-
export declare function createLegacyStreetRoadWayPolicy(style?: HighwayInclusionStyle): OsmWaySelectionPolicy;
|
|
16
|
-
/** @deprecated Prefer matching against an app policy. */
|
|
17
|
-
export declare const ROAD_LIKE_HIGHWAY_BASE_REGEX: RegExp;
|
|
18
|
-
/** @deprecated Prefer policy `globalAll`. */
|
|
19
|
-
export declare const OVERPASS_PUBLIC_ROAD_FILTERS = "[service!=emergency_access][service!=parking_aisle][service!=driveway][access!=private]";
|
|
20
|
-
/**
|
|
21
|
-
* @deprecated Prefer {@link compileOverpassWaySelector} with an app policy.
|
|
22
|
-
* Returns the legacy fragment expected by `way[${tag}]` callers (no leading `[`).
|
|
23
|
-
*/
|
|
24
|
-
export declare function overpassRoadLikeSelector(style?: HighwayInclusionStyle): string;
|
|
25
|
-
/** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
|
|
26
|
-
export declare function isClutterAccessWay(tags: OsmTags): boolean;
|
|
27
|
-
/** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
|
|
28
|
-
export declare function matchesHighwayInclusionStyle(tags: OsmTags, style?: HighwayInclusionStyle): boolean;
|
|
29
|
-
/** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
|
|
30
|
-
export declare function isRoadLikeHighway(tags: OsmTags): boolean;
|
|
31
|
-
/** @deprecated Prefer app policy + {@link matchesOsmWaySelection}. */
|
|
32
|
-
export declare function isEditableRoadLikeHighway(tags: OsmTags, style?: HighwayInclusionStyle): boolean;
|
|
33
|
-
/** @deprecated Prefer app policy + {@link matchesOsmWaySelectionSegment}. */
|
|
34
|
-
export declare function isRoadLikeSegment(segment: Segment): boolean;
|
|
35
|
-
/** @deprecated Prefer app policy + {@link matchesOsmWaySelectionSegment}. */
|
|
36
|
-
export declare function isEditableRoadLikeSegment(segment: Segment, style?: HighwayInclusionStyle): boolean;
|