terra-route 0.0.11 → 0.0.13
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 +7 -17
- package/dist/terra-route.cjs +1 -1
- package/dist/terra-route.cjs.map +1 -1
- package/dist/terra-route.d.ts +1 -2
- package/dist/terra-route.modern.js +1 -1
- package/dist/terra-route.modern.js.map +1 -1
- package/dist/terra-route.module.js +1 -1
- package/dist/terra-route.module.js.map +1 -1
- package/dist/terra-route.umd.js +1 -1
- package/dist/terra-route.umd.js.map +1 -1
- package/instructions.md +13 -0
- package/package.json +2 -1
- package/src/terra-route.compare.spec.ts +81 -0
- package/src/terra-route.spec.ts +576 -0
- package/src/terra-route.ts +370 -154
- package/src/graph/graph.spec.ts +0 -238
- package/src/graph/graph.ts +0 -212
- package/src/graph/methods/bounding-box.spec.ts +0 -199
- package/src/graph/methods/bounding-box.ts +0 -85
- package/src/graph/methods/connected.spec.ts +0 -219
- package/src/graph/methods/connected.ts +0 -168
- package/src/graph/methods/duplicates.spec.ts +0 -161
- package/src/graph/methods/duplicates.ts +0 -117
- package/src/graph/methods/leaf.spec.ts +0 -224
- package/src/graph/methods/leaf.ts +0 -88
- package/src/graph/methods/nodes.spec.ts +0 -317
- package/src/graph/methods/nodes.ts +0 -77
- package/src/graph/methods/spatial-index/geokdbush.spec.ts +0 -86
- package/src/graph/methods/spatial-index/geokdbush.ts +0 -189
- package/src/graph/methods/spatial-index/kdbush.spec.ts +0 -67
- package/src/graph/methods/spatial-index/kdbush.ts +0 -189
- package/src/graph/methods/spatial-index/tinyqueue.spec.ts +0 -51
- package/src/graph/methods/spatial-index/tinyqueue.ts +0 -108
- package/src/graph/methods/unify.spec.ts +0 -475
- package/src/graph/methods/unify.ts +0 -132
- package/src/graph/methods/unique.spec.ts +0 -65
- package/src/graph/methods/unique.ts +0 -69
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { FeatureCollection, LineString, Feature, Position } from 'geojson'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Are the two coordinate sequences exactly equal (in same order)?
|
|
5
|
-
*/
|
|
6
|
-
function areCoordinatesEqual(a: Position[], b: Position[]): boolean {
|
|
7
|
-
if (a.length !== b.length) {
|
|
8
|
-
return false
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
for (let i = 0; i < a.length; i++) {
|
|
12
|
-
if (a[i][0] !== b[i][0] || a[i][1] !== b[i][1]) {
|
|
13
|
-
return false
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return true
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Is `sub` a contiguous subsequence of `full`, either forward or reversed?
|
|
22
|
-
*/
|
|
23
|
-
function isSubsequence(sub: Position[], full: Position[]): boolean {
|
|
24
|
-
const subLength = sub.length
|
|
25
|
-
const fullLength = full.length
|
|
26
|
-
|
|
27
|
-
if (subLength > fullLength) {
|
|
28
|
-
return false
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// check forward
|
|
32
|
-
for (let start = 0; start <= fullLength - subLength; start++) {
|
|
33
|
-
let matches = true
|
|
34
|
-
|
|
35
|
-
for (let offset = 0; offset < subLength; offset++) {
|
|
36
|
-
if (
|
|
37
|
-
full[start + offset][0] !== sub[offset][0] ||
|
|
38
|
-
full[start + offset][1] !== sub[offset][1]
|
|
39
|
-
) {
|
|
40
|
-
matches = false
|
|
41
|
-
break
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (matches) {
|
|
46
|
-
return true
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// check reversed
|
|
51
|
-
const reversedSub = [...sub].reverse()
|
|
52
|
-
|
|
53
|
-
for (let start = 0; start <= fullLength - subLength; start++) {
|
|
54
|
-
let matches = true
|
|
55
|
-
|
|
56
|
-
for (let offset = 0; offset < subLength; offset++) {
|
|
57
|
-
if (
|
|
58
|
-
full[start + offset][0] !== reversedSub[offset][0] ||
|
|
59
|
-
full[start + offset][1] !== reversedSub[offset][1]
|
|
60
|
-
) {
|
|
61
|
-
matches = false
|
|
62
|
-
break
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (matches) {
|
|
67
|
-
return true
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return false
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Remove any LineString that is either
|
|
76
|
-
* - an exact duplicate of an earlier one, or
|
|
77
|
-
* - a contiguous subsequence (in either direction) of any other.
|
|
78
|
-
*/
|
|
79
|
-
export function removeDuplicateAndSubsectionLines(
|
|
80
|
-
collection: FeatureCollection<LineString>
|
|
81
|
-
): FeatureCollection<LineString> {
|
|
82
|
-
const features = collection.features
|
|
83
|
-
const toRemove = new Set<number>()
|
|
84
|
-
|
|
85
|
-
for (let i = 0; i < features.length; i++) {
|
|
86
|
-
const coordsI = features[i].geometry.coordinates
|
|
87
|
-
|
|
88
|
-
for (let j = 0; j < features.length; j++) {
|
|
89
|
-
if (i === j) {
|
|
90
|
-
continue
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const coordsJ = features[j].geometry.coordinates
|
|
94
|
-
|
|
95
|
-
// if coordsI are a subsequence of coordsJ, OR exactly equal,
|
|
96
|
-
// then mark coordsI for removal—but only if
|
|
97
|
-
// • coordsI is strictly shorter than coordsJ, or
|
|
98
|
-
// • they’re the same length and this is the later duplicate (i > j)
|
|
99
|
-
if (
|
|
100
|
-
isSubsequence(coordsI, coordsJ) &&
|
|
101
|
-
(
|
|
102
|
-
coordsI.length < coordsJ.length ||
|
|
103
|
-
(coordsI.length === coordsJ.length && i > j)
|
|
104
|
-
)
|
|
105
|
-
) {
|
|
106
|
-
toRemove.add(i)
|
|
107
|
-
break
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const filtered = features.filter((_, index) => !toRemove.has(index))
|
|
113
|
-
return {
|
|
114
|
-
type: 'FeatureCollection',
|
|
115
|
-
features: filtered
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { FeatureCollection, LineString } from 'geojson';
|
|
2
|
-
import { createFeatureCollection } from '../../test-utils/create';
|
|
3
|
-
import { getLeafEdges } from './leaf';
|
|
4
|
-
|
|
5
|
-
describe('getLeafEdges', () => {
|
|
6
|
-
|
|
7
|
-
describe('leafEdges', () => {
|
|
8
|
-
describe('for an empty feature collection', () => {
|
|
9
|
-
it('returns 0 nodes and edges', () => {
|
|
10
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([]);
|
|
11
|
-
const output = getLeafEdges(input).leafEdges
|
|
12
|
-
|
|
13
|
-
expect(output).toEqual(createFeatureCollection([]));
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
describe('for feature collection with 1 linestring', () => {
|
|
18
|
-
it('returns the single linestring as a leaf edge', () => {
|
|
19
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
20
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} }
|
|
21
|
-
]);
|
|
22
|
-
const output = getLeafEdges(input).leafEdges
|
|
23
|
-
|
|
24
|
-
expect(output.features.length).toBe(1);
|
|
25
|
-
expect(output.features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('returns the two linestring as as leaf edge for long linestring', () => {
|
|
29
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
30
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1], [2, 2]] }, properties: {} }
|
|
31
|
-
]);
|
|
32
|
-
const output = getLeafEdges(input).leafEdges
|
|
33
|
-
|
|
34
|
-
expect(output.features.length).toBe(2);
|
|
35
|
-
expect(output.features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
|
|
36
|
-
expect(output.features[1].geometry.coordinates).toEqual([[1, 1], [2, 2]]);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe('for feature collection with 2 linestrings', () => {
|
|
41
|
-
it('returns both linestrings if they are unconnected leaf edges', () => {
|
|
42
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
43
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
44
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[2, 2], [3, 3]] }, properties: {} }
|
|
45
|
-
]);
|
|
46
|
-
const output = getLeafEdges(input).leafEdges
|
|
47
|
-
expect(output.features.length).toBe(2);
|
|
48
|
-
expect(output.features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
|
|
49
|
-
expect(output.features[1].geometry.coordinates).toEqual([[2, 2], [3, 3]]);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('returns both linestrings if they are connected leaf edges', () => {
|
|
53
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
54
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
55
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [2, 2]] }, properties: {} }
|
|
56
|
-
]);
|
|
57
|
-
const output = getLeafEdges(input).leafEdges
|
|
58
|
-
expect(output.features.length).toBe(2);
|
|
59
|
-
expect(output.features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
|
|
60
|
-
expect(output.features[1].geometry.coordinates).toEqual([[1, 1], [2, 2]]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('returns two linestrings if one is a subsection of the other but they are both leaf edges', () => {
|
|
64
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
65
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
66
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1], [2, 2]] }, properties: {} }
|
|
67
|
-
]);
|
|
68
|
-
const output = getLeafEdges(input).leafEdges
|
|
69
|
-
expect(output.features.length).toBe(2);
|
|
70
|
-
expect(output.features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
|
|
71
|
-
expect(output.features[1].geometry.coordinates).toEqual([[1, 1], [2, 2]]);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('handles duplicates', () => {
|
|
75
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
76
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
77
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} }
|
|
78
|
-
]);
|
|
79
|
-
const output = getLeafEdges(input).leafEdges
|
|
80
|
-
expect(output.features.length).toBe(1);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('for feature collection with 3 linestrings', () => {
|
|
85
|
-
it('returns 3 leafs when all are disconnected', () => {
|
|
86
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
87
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
88
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[2, 2], [3, 3]] }, properties: {} },
|
|
89
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[4, 4], [5, 5]] }, properties: {} }
|
|
90
|
-
]);
|
|
91
|
-
const output = getLeafEdges(input).leafEdges
|
|
92
|
-
expect(output.features.length).toBe(3);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('returns 3 leafs when all three linestrings are connected by a shared central point', () => {
|
|
96
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
97
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
98
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [2, 2]] }, properties: {} },
|
|
99
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [3, 3]] }, properties: {} }
|
|
100
|
-
]);
|
|
101
|
-
const output = getLeafEdges(input).leafEdges
|
|
102
|
-
expect(output.features.length).toBe(3);
|
|
103
|
-
expect(output.features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
|
|
104
|
-
expect(output.features[1].geometry.coordinates).toEqual([[1, 1], [2, 2]]);
|
|
105
|
-
expect(output.features[2].geometry.coordinates).toEqual([[1, 1], [3, 3]]);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('returns 3 leafs when two are connected and one is disconnected', () => {
|
|
109
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
110
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
111
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [2, 2]] }, properties: {} },
|
|
112
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[3, 3], [4, 4]] }, properties: {} }
|
|
113
|
-
]);
|
|
114
|
-
const output = getLeafEdges(input).leafEdges
|
|
115
|
-
expect(output.features.length).toBe(3);
|
|
116
|
-
expect(output.features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
|
|
117
|
-
expect(output.features[1].geometry.coordinates).toEqual([[1, 1], [2, 2]]);
|
|
118
|
-
expect(output.features[2].geometry.coordinates).toEqual([[3, 3], [4, 4]]);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('returns 3 leafs when one linestring is a subsection of another and 3rd is disconnected', () => {
|
|
122
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
123
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
124
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1], [2, 2]] }, properties: {} },
|
|
125
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[3, 3], [4, 4]] }, properties: {} }
|
|
126
|
-
]);
|
|
127
|
-
const output = getLeafEdges(input).leafEdges
|
|
128
|
-
expect(output.features.length).toBe(3);
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
describe('nonLeafEdges', () => {
|
|
134
|
-
it('returns an empty feature collection for empty input', () => {
|
|
135
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([]);
|
|
136
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
137
|
-
|
|
138
|
-
expect(output).toEqual(createFeatureCollection([]));
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('returns no linestrings for a single input linestring as it would be a leaf edge', () => {
|
|
142
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
143
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} }
|
|
144
|
-
]);
|
|
145
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
146
|
-
|
|
147
|
-
expect(output.features.length).toBe(0);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('returns no linestrings for two unconnected linestrings as they would be leaf edges', () => {
|
|
151
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
152
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
153
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[2, 2], [3, 3]] }, properties: {} }
|
|
154
|
-
]);
|
|
155
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
156
|
-
|
|
157
|
-
expect(output.features.length).toBe(0);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('returns no linestrings for two connected linestrings as they would be leaf edges', () => {
|
|
161
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
162
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
163
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [2, 2]] }, properties: {} }
|
|
164
|
-
]);
|
|
165
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
166
|
-
|
|
167
|
-
expect(output.features.length).toBe(0);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('returns no linestrings if one is a subsection of the other as they would be leaf edges', () => {
|
|
171
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
172
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
173
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1], [2, 2]] }, properties: {} }
|
|
174
|
-
]);
|
|
175
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
176
|
-
|
|
177
|
-
expect(output.features.length).toBe(0);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('returns no linestrings for two linestrings that are duplicates', () => {
|
|
181
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
182
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
183
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} }
|
|
184
|
-
]);
|
|
185
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
186
|
-
|
|
187
|
-
expect(output.features.length).toBe(0);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('returns no linestrings for two linestrings that are inverse duplicates', () => {
|
|
191
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
192
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
193
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [0, 0]] }, properties: {} }
|
|
194
|
-
]);
|
|
195
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
196
|
-
|
|
197
|
-
expect(output.features.length).toBe(0);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('returns a no linestring for three linestrings where two are connected and one is disconnected', () => {
|
|
201
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
202
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
203
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [2, 2]] }, properties: {} },
|
|
204
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[3, 3], [4, 4]] }, properties: {} }
|
|
205
|
-
]);
|
|
206
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
207
|
-
|
|
208
|
-
expect(output.features.length).toBe(0);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
it('returns a linestring for three linestrings when one is a middle edge and the other two are leaf edges', () => {
|
|
212
|
-
const input: FeatureCollection<LineString> = createFeatureCollection([
|
|
213
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[0, 0], [1, 1]] }, properties: {} },
|
|
214
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[1, 1], [2, 2]] }, properties: {} },
|
|
215
|
-
{ type: 'Feature', geometry: { type: 'LineString', coordinates: [[2, 2], [3, 3]] }, properties: {} }
|
|
216
|
-
]);
|
|
217
|
-
const output = getLeafEdges(input).nonLeafEdges;
|
|
218
|
-
|
|
219
|
-
expect(output.features.length).toBe(1);
|
|
220
|
-
expect(output.features[0].geometry.coordinates).toEqual([[1, 1], [2, 2]]);
|
|
221
|
-
});
|
|
222
|
-
})
|
|
223
|
-
});
|
|
224
|
-
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { FeatureCollection, LineString, Feature } from 'geojson';
|
|
2
|
-
import { graphGetUniqueSegments } from './unique';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Separates a graph's edges into leaf and non-leaf edges.
|
|
6
|
-
* A leaf edge has a start or end node with degree 1.
|
|
7
|
-
*
|
|
8
|
-
* @param edgesFc - FeatureCollection containing LineString features representing edges of a graph
|
|
9
|
-
* @returns Object containing two FeatureCollections: leafEdges and nonLeafEdges
|
|
10
|
-
*/
|
|
11
|
-
export function getLeafEdges(
|
|
12
|
-
edgesFc: FeatureCollection<LineString>
|
|
13
|
-
): {
|
|
14
|
-
leafEdges: FeatureCollection<LineString>;
|
|
15
|
-
nonLeafEdges: FeatureCollection<LineString>;
|
|
16
|
-
} {
|
|
17
|
-
const edges = graphGetUniqueSegments(edgesFc);
|
|
18
|
-
|
|
19
|
-
const endpointCountMap: Map<string, number> = new Map();
|
|
20
|
-
|
|
21
|
-
function coordKey(position: number[]): string {
|
|
22
|
-
return position.join(",");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Count the degree (number of edge endpoints) for each coordinate
|
|
26
|
-
for (let i = 0; i < edges.features.length; i++) {
|
|
27
|
-
const feature = edges.features[i];
|
|
28
|
-
const coordinates = feature.geometry.coordinates;
|
|
29
|
-
|
|
30
|
-
if (coordinates.length < 2) {
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const startKey = coordKey(coordinates[0]);
|
|
35
|
-
const endKey = coordKey(coordinates[coordinates.length - 1]);
|
|
36
|
-
|
|
37
|
-
endpointCountMap.set(
|
|
38
|
-
startKey,
|
|
39
|
-
(endpointCountMap.get(startKey) || 0) + 1
|
|
40
|
-
);
|
|
41
|
-
endpointCountMap.set(
|
|
42
|
-
endKey,
|
|
43
|
-
(endpointCountMap.get(endKey) || 0) + 1
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const leafEdgeMap: Map<string, Feature<LineString>> = new Map();
|
|
48
|
-
const nonLeafEdgeMap: Map<string, Feature<LineString>> = new Map();
|
|
49
|
-
|
|
50
|
-
for (let i = 0; i < edges.features.length; i++) {
|
|
51
|
-
const feature = edges.features[i];
|
|
52
|
-
const coordinates = feature.geometry.coordinates;
|
|
53
|
-
|
|
54
|
-
if (coordinates.length < 2) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const startKey = coordKey(coordinates[0]);
|
|
59
|
-
const endKey = coordKey(coordinates[coordinates.length - 1]);
|
|
60
|
-
|
|
61
|
-
const startCount = endpointCountMap.get(startKey) || 0;
|
|
62
|
-
const endCount = endpointCountMap.get(endKey) || 0;
|
|
63
|
-
|
|
64
|
-
const edgeKey = coordinates.map(coordKey).join(";");
|
|
65
|
-
|
|
66
|
-
if (startCount === 1 || endCount === 1) {
|
|
67
|
-
if (!leafEdgeMap.has(edgeKey)) {
|
|
68
|
-
leafEdgeMap.set(edgeKey, feature);
|
|
69
|
-
}
|
|
70
|
-
} else {
|
|
71
|
-
if (!nonLeafEdgeMap.has(edgeKey)) {
|
|
72
|
-
nonLeafEdgeMap.set(edgeKey, feature);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
leafEdges: {
|
|
79
|
-
type: "FeatureCollection",
|
|
80
|
-
features: Array.from(leafEdgeMap.values())
|
|
81
|
-
},
|
|
82
|
-
nonLeafEdges: {
|
|
83
|
-
type: "FeatureCollection",
|
|
84
|
-
features: Array.from(nonLeafEdgeMap.values())
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|