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.
Files changed (37) hide show
  1. package/README.md +7 -17
  2. package/dist/terra-route.cjs +1 -1
  3. package/dist/terra-route.cjs.map +1 -1
  4. package/dist/terra-route.d.ts +1 -2
  5. package/dist/terra-route.modern.js +1 -1
  6. package/dist/terra-route.modern.js.map +1 -1
  7. package/dist/terra-route.module.js +1 -1
  8. package/dist/terra-route.module.js.map +1 -1
  9. package/dist/terra-route.umd.js +1 -1
  10. package/dist/terra-route.umd.js.map +1 -1
  11. package/instructions.md +13 -0
  12. package/package.json +2 -1
  13. package/src/terra-route.compare.spec.ts +81 -0
  14. package/src/terra-route.spec.ts +576 -0
  15. package/src/terra-route.ts +370 -154
  16. package/src/graph/graph.spec.ts +0 -238
  17. package/src/graph/graph.ts +0 -212
  18. package/src/graph/methods/bounding-box.spec.ts +0 -199
  19. package/src/graph/methods/bounding-box.ts +0 -85
  20. package/src/graph/methods/connected.spec.ts +0 -219
  21. package/src/graph/methods/connected.ts +0 -168
  22. package/src/graph/methods/duplicates.spec.ts +0 -161
  23. package/src/graph/methods/duplicates.ts +0 -117
  24. package/src/graph/methods/leaf.spec.ts +0 -224
  25. package/src/graph/methods/leaf.ts +0 -88
  26. package/src/graph/methods/nodes.spec.ts +0 -317
  27. package/src/graph/methods/nodes.ts +0 -77
  28. package/src/graph/methods/spatial-index/geokdbush.spec.ts +0 -86
  29. package/src/graph/methods/spatial-index/geokdbush.ts +0 -189
  30. package/src/graph/methods/spatial-index/kdbush.spec.ts +0 -67
  31. package/src/graph/methods/spatial-index/kdbush.ts +0 -189
  32. package/src/graph/methods/spatial-index/tinyqueue.spec.ts +0 -51
  33. package/src/graph/methods/spatial-index/tinyqueue.ts +0 -108
  34. package/src/graph/methods/unify.spec.ts +0 -475
  35. package/src/graph/methods/unify.ts +0 -132
  36. package/src/graph/methods/unique.spec.ts +0 -65
  37. package/src/graph/methods/unique.ts +0 -69
@@ -1,238 +0,0 @@
1
- import { LineStringGraph } from "./graph";
2
- import { FeatureCollection, LineString } from "geojson";
3
- import { readFileSync } from "fs";
4
- import { createLineStringFeature, createFeatureCollection } from "../test-utils/create";
5
- import { BoundingBox } from "./methods/bounding-box";
6
-
7
- describe("LineStringGraph", () => {
8
- it("should create a graph from a GeoJSON network", () => {
9
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
10
- const graph = new LineStringGraph(network);
11
- expect(graph.getNetwork()).toEqual(network);
12
- });
13
-
14
- it("should return connected components", () => {
15
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
16
- const graph = new LineStringGraph(network);
17
- const components = graph.getConnectedComponents();
18
- expect(components.length).toBe(1);
19
- });
20
-
21
- it("should return the count of connected components", () => {
22
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
23
- const graph = new LineStringGraph(network);
24
- const count = graph.getConnectedComponentCount();
25
- expect(count).toBe(1);
26
- });
27
-
28
- it("should return node and edge counts", () => {
29
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
30
- const graph = new LineStringGraph(network);
31
- const counts = graph.getNodeAndEdgeCount();
32
- expect(counts.nodeCount).toBe(2598);
33
- expect(counts.edgeCount).toBe(2839);
34
- });
35
-
36
- it("should return nodes as Point features", () => {
37
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
38
- const graph = new LineStringGraph(network);
39
- const nodes = graph.getNodes();
40
- expect(nodes.features.length).toBe(2598);
41
- });
42
-
43
- it("should return the count of unique nodes", () => {
44
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
45
- const graph = new LineStringGraph(network);
46
- const count = graph.getNodeCount();
47
- expect(count).toBe(2598);
48
- });
49
-
50
- it("should return edges as LineString features", () => {
51
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
52
- const graph = new LineStringGraph(network);
53
- const edges = graph.getEdges();
54
- expect(edges.features.length).toBe(2839);
55
- });
56
-
57
- it("should return the count of unique edges", () => {
58
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
59
- const graph = new LineStringGraph(network);
60
- const count = graph.getEdgeCount();
61
- expect(count).toBe(2839);
62
- });
63
-
64
- it("should return the shortest edge between two nodes", () => {
65
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
66
- const graph = new LineStringGraph(network);
67
- const edge = graph.getShortestEdge();
68
- expect(edge).toEqual({
69
- "type": "Feature",
70
- "geometry": {
71
- "type": "LineString",
72
- "coordinates": [
73
- [
74
- -0.0837395,
75
- 51.5394794,
76
- ],
77
- [
78
- -0.083739,
79
- 51.5394744,
80
- ],
81
- ],
82
- },
83
- "properties": {},
84
- })
85
- })
86
-
87
- it("should return the longest edge between two nodes", () => {
88
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
89
- const graph = new LineStringGraph(network);
90
- const edge = graph.getLongestEdge();
91
- expect(edge).toEqual({
92
- "type": "Feature",
93
- "geometry": {
94
- "type": "LineString",
95
- "coordinates": [
96
- [
97
- -0.0755711,
98
- 51.5394099,
99
- ],
100
- [
101
- -0.0753028,
102
- 51.5423462,
103
- ],
104
- ],
105
- },
106
- "properties": {},
107
- })
108
- });
109
-
110
- it("should return the length of the longest edge", () => {
111
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
112
- const graph = new LineStringGraph(network);
113
- const length = graph.getLongestEdgeLength();
114
- expect(length).toBeCloseTo(0.3270284866264399, 1);
115
- });
116
-
117
- it("should return the length of the shortest edge", () => {
118
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
119
- const graph = new LineStringGraph(network);
120
- const length = graph.getShortestEdgeLength();
121
- expect(length).toBeCloseTo(0.0004570489974749478);
122
- });
123
-
124
- it("should set the network", () => {
125
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
126
- const graph = new LineStringGraph(network);
127
- const newNetwork = JSON.parse(readFileSync('src/data/network-5-cc.geojson', 'utf-8')) as FeatureCollection<LineString>;
128
- graph.setNetwork(newNetwork);
129
- });
130
-
131
- it('should get network without duplicates or subsections', () => {
132
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
133
- const graph = new LineStringGraph(network);
134
- const uniqueNetwork = graph.getNetworkWithoutDuplicatesOrSubsections();
135
- expect(uniqueNetwork.features.length).toBe(network.features.length);
136
- });
137
-
138
- it("should return leaf edges and pruned edges", () => {
139
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
140
- const graph = new LineStringGraph(network);
141
- const leafEdges = graph.getLeafEdges();
142
- const nonLeafEdges = graph.getPrunedEdges();
143
- const edgesCount = graph.getEdgeCount();
144
- expect(leafEdges.features.length).toBe(243);
145
- expect(nonLeafEdges.features.length).toBe(edgesCount - leafEdges.features.length);
146
- });
147
-
148
- it("should progressively strips leaf nodes", () => {
149
- const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
150
- const graph = new LineStringGraph(network);
151
-
152
- const graphAfterFirstPrune = graph.getPrunedEdges(1);
153
- const graphAfterSecondPrune = graph.getPrunedEdges(2);
154
- expect(graphAfterSecondPrune.features.length).toBeLessThan(graphAfterFirstPrune.features.length);
155
-
156
- const graphAfterThirdPrune = graph.getPrunedEdges(3);
157
- expect(graphAfterThirdPrune.features.length).toBeLessThan(graphAfterSecondPrune.features.length);
158
- });
159
-
160
- describe('getNetworkInBoundingBox', () => {
161
- it('filters network LineStrings based on bounding box', () => {
162
- const insideLine = createLineStringFeature([[1, 1], [2, 2]]);
163
- const outsideLine = createLineStringFeature([[15, 15], [20, 20]]);
164
-
165
- const network = createFeatureCollection([insideLine, outsideLine]);
166
- const graph = new LineStringGraph(network);
167
-
168
- const boundingBox: BoundingBox = [0, 0, 10, 10];
169
- const filteredNetwork = graph.getNetworkInBoundingBox(boundingBox);
170
-
171
- expect(filteredNetwork.features).toHaveLength(1);
172
- expect(filteredNetwork.features[0]).toEqual(insideLine);
173
- });
174
-
175
- it('returns empty collection when no LineStrings are within bounds', () => {
176
- const outsideLine1 = createLineStringFeature([[15, 15], [20, 20]]);
177
- const outsideLine2 = createLineStringFeature([[25, 25], [30, 30]]);
178
-
179
- const network = createFeatureCollection([outsideLine1, outsideLine2]);
180
- const graph = new LineStringGraph(network);
181
-
182
- const boundingBox: BoundingBox = [0, 0, 10, 10];
183
- const filteredNetwork = graph.getNetworkInBoundingBox(boundingBox);
184
-
185
- expect(filteredNetwork.features).toHaveLength(0);
186
- });
187
-
188
- it('does not modify the original network', () => {
189
- const originalLine = createLineStringFeature([[1, 1], [2, 2]]);
190
- const network = createFeatureCollection([originalLine]);
191
- const graph = new LineStringGraph(network);
192
-
193
- const boundingBox: BoundingBox = [0, 0, 10, 10];
194
- const filteredNetwork = graph.getNetworkInBoundingBox(boundingBox);
195
-
196
- // Original network should remain unchanged
197
- expect(graph.getNetwork().features).toHaveLength(1);
198
- expect(filteredNetwork.features).toHaveLength(1);
199
-
200
- // Modifying filtered result shouldn't affect original
201
- filteredNetwork.features.pop();
202
- expect(graph.getNetwork().features).toHaveLength(1);
203
- });
204
- });
205
-
206
- describe('getUnifiedNetwork', () => {
207
- it('returns the unified network', () => {
208
- const insideLine = createLineStringFeature([[1, 1], [2, 2]]);
209
- const outsideLine = createLineStringFeature([[1.000001, 1.000001], [20, 20]]);
210
-
211
- const network = createFeatureCollection([insideLine, outsideLine]);
212
- const graph = new LineStringGraph(network);
213
-
214
- const filteredNetwork = graph.getUnifiedNetwork(0.5);
215
-
216
- expect(filteredNetwork.features).toHaveLength(2);
217
- expect(filteredNetwork.features).toEqual([
218
- {
219
- "geometry": {
220
- "coordinates": [[1, 1], [2, 2]],
221
- "type": "LineString"
222
- },
223
- "properties": {},
224
- "type": "Feature"
225
- },
226
- {
227
- "geometry": {
228
- "coordinates": [[1, 1], [20, 20]],
229
- "type": "LineString"
230
- },
231
- "properties": {},
232
- "type": "Feature"
233
- }
234
- ]);
235
- });
236
- });
237
-
238
- });
@@ -1,212 +0,0 @@
1
- import { Feature, FeatureCollection, LineString, Point } from "geojson";
2
- import { graphGetConnectedComponentCount, graphGetConnectedComponents } from "./methods/connected";
3
- import { graphGetNodeAndEdgeCount, graphGetNodesAsPoints } from "./methods/nodes";
4
- import { graphGetUniqueSegments } from "./methods/unique";
5
- import { routeLength } from "../test-utils/utils";
6
- import { removeDuplicateAndSubsectionLines } from "./methods/duplicates";
7
- import { getLeafEdges } from "./methods/leaf";
8
- import { getNetworkInBoundingBox, BoundingBox } from "./methods/bounding-box";
9
- import { unifyCloseCoordinates } from "./methods/unify";
10
-
11
- /**
12
- * Represents a graph constructed from a GeoJSON FeatureCollection of LineString features.
13
- * This class provides methods to analyze the graph, including connected components, node and edge counts,
14
- * and shortest paths. Coordinates in the LineStrings are considered connected if they share identical coordinates.
15
- */
16
- export class LineStringGraph {
17
- constructor(network: FeatureCollection<LineString>) {
18
- this.network = network;
19
- }
20
-
21
- private network: FeatureCollection<LineString>;
22
-
23
- /**
24
- * Sets the network for the graph.
25
- * This method replaces the current network with a new one.
26
- * @param network A GeoJSON FeatureCollection of LineString features representing the network.
27
- */
28
- setNetwork(network: FeatureCollection<LineString>) {
29
- this.network = network;
30
- }
31
-
32
- /**
33
- * Gets the current network of the graph.
34
- * @returns A GeoJSON FeatureCollection of LineString features representing the network.
35
- */
36
- getNetwork(): FeatureCollection<LineString> {
37
- return this.network;
38
- }
39
-
40
- /**
41
- * Gets a filtered network containing only LineStrings that are completely within the specified bounding box.
42
- * @param boundingBox A bounding box array in the format [minLng, minLat, maxLng, maxLat]
43
- * @returns A GeoJSON FeatureCollection of LineString features representing the network filtered by the bounding box.
44
- */
45
- getNetworkInBoundingBox(boundingBox: BoundingBox): FeatureCollection<LineString> {
46
- return getNetworkInBoundingBox(this.network, boundingBox);
47
- }
48
-
49
- /**
50
- * Gets the network without duplicate or subsection lines.
51
- * This method processes the network to remove any duplicate lines or lines that are subsections of other lines.
52
- * @returns A FeatureCollection<LineString> representing the network without duplicate or subsection lines.
53
- */
54
- getNetworkWithoutDuplicatesOrSubsections() {
55
- return removeDuplicateAndSubsectionLines(this.network);
56
- }
57
-
58
- /**
59
- * Gets the connected components of the graph.
60
- * @returns An array of FeatureCollection<LineString> representing the connected components.
61
- */
62
- getConnectedComponents(): FeatureCollection<LineString>[] {
63
- return graphGetConnectedComponents(this.network)
64
- }
65
-
66
- /**
67
- * Gets the count of connected components in the graph.
68
- * @returns The number of connected components in the graph.
69
- */
70
- getConnectedComponentCount(): number {
71
- return graphGetConnectedComponentCount(this.network);
72
- }
73
-
74
- /**
75
- * Gets the count of unique nodes and edges in the graph.
76
- * @returns An object containing the counts of nodes and edges.
77
- */
78
- getNodeAndEdgeCount(): { nodeCount: number, edgeCount: number } {
79
- return graphGetNodeAndEdgeCount(this.network);
80
- }
81
-
82
- /**
83
- * Gets the unique nodes of the graph as a FeatureCollection of Point features.
84
- * @returns A FeatureCollection<Point> containing the nodes of the graph.
85
- */
86
- getNodes(): FeatureCollection<Point> {
87
- const nodes = graphGetNodesAsPoints(this.network);
88
- return {
89
- type: "FeatureCollection",
90
- features: nodes
91
- };
92
- }
93
-
94
- /**
95
- * Gets the count of unique nodes in the graph.
96
- * @returns The number of unique nodes in the graph.
97
- */
98
- getNodeCount(): number {
99
- const { nodeCount } = this.getNodeAndEdgeCount();
100
- return nodeCount;
101
- }
102
-
103
- /**
104
- * Gets the unique edges of the graph as a FeatureCollection of LineString features. Each edge is represented as a LineString.
105
- * This method ensures that each edge is unique, meaning that edges are not duplicated in the collection. Each linestring only
106
- * two coordinates, representing the start and end points of the edge.
107
- * @returns A FeatureCollection<LineString> containing the unique edges of the graph.
108
- */
109
- getEdges(): FeatureCollection<LineString> {
110
- return graphGetUniqueSegments(this.network);
111
- }
112
-
113
- /**
114
- * Gets the length of the longest edge in the graph based on the length of the LineString.
115
- * If no edges exist, it returns -1.
116
- * @returns The length of the longest edge in meters, or 0 if no edges exist.
117
- */
118
- getLongestEdgeLength(): number {
119
- const longestEdge = this.getLongestEdge();
120
- if (!longestEdge) {
121
- return -1;
122
- }
123
- return routeLength(longestEdge);
124
- }
125
-
126
- /**
127
- * Gets the length of the shortest edge in the graph based on the length of the LineString.
128
- * If no edges exist, it returns -1.
129
- * @returns The length of the shortest edge in meters, or 0 if no edges exist.
130
- */
131
- getShortestEdgeLength(): number {
132
- const shortestEdge = this.getShortestEdge();
133
- if (!shortestEdge) {
134
- return -1;
135
- }
136
- return routeLength(shortestEdge);
137
- }
138
-
139
- /**
140
- * Gets the longest edge in the graph based on the length of the LineString.
141
- * @returns The longest edge as a Feature<LineString> or null if no edges exist.
142
- */
143
- getLongestEdge(): Feature<LineString> | null {
144
- const edges = this.getEdges().features;
145
- if (edges.length === 0) {
146
- return null;
147
- }
148
- const longestEdges = edges.sort((a, b) => routeLength(a) - routeLength(b));
149
- return longestEdges[longestEdges.length - 1];
150
- }
151
-
152
- /**
153
- * Gets the shortest edge in the graph based on the length of the LineString.
154
- * @returns The shortest edge as a Feature<LineString> or null if no edges exist.
155
- */
156
- getShortestEdge(): Feature<LineString> | null {
157
- const edges = this.getEdges().features;
158
- if (edges.length === 0) {
159
- return null;
160
- }
161
- const shortestEdges = edges.sort((a, b) => routeLength(a) - routeLength(b));
162
- return shortestEdges[0];
163
- }
164
-
165
- /**
166
- * Gets the count of unique edges in the graph.
167
- * @returns The number of unique edges in the graph.
168
- */
169
- getEdgeCount(): number {
170
- const { edgeCount } = this.getNodeAndEdgeCount();
171
- return edgeCount;
172
- }
173
-
174
- /**
175
- * Gets the leaf edges of the graph. A leaf edge is defined as an edge whose start or end node has a degree of 1.
176
- * Here an edge is defined as a LineString with two coordinates, representing the start and end points.
177
- * @returns A FeatureCollection<LineString> containing only the leaf edges of the graph.
178
- */
179
- getLeafEdges(): FeatureCollection<LineString> {
180
- return getLeafEdges(this.network).leafEdges;
181
- }
182
-
183
- /**
184
- * Returns the pruned network, which is the network without the leaf edges.
185
- * i.e. This method removes all leaf edges from the network, leaving only the non-leaf edges
186
- * @return A FeatureCollection<LineString> representing the pruned network without leaf edges.
187
- */
188
- getPrunedEdges(depth?: number): FeatureCollection<LineString> {
189
- if (depth && depth > 0) {
190
-
191
- let currentEdges = this.network;
192
-
193
- for (let i = 0; i < depth; i++) {
194
- const leafEdges = getLeafEdges(currentEdges);
195
- currentEdges = leafEdges.nonLeafEdges;
196
- }
197
-
198
- return currentEdges;
199
-
200
- }
201
- return getLeafEdges(this.network).nonLeafEdges;
202
- }
203
-
204
- /**
205
- * Returns the network where all nodes that are with n meters of each other are unified.
206
- * The function will avoid unifying coordinates in the same linestring.
207
- * @param toleranceMeters the tolerance for unifying nodes in meters.
208
- */
209
- getUnifiedNetwork(toleranceMeters: number) {
210
- return unifyCloseCoordinates(this.network, toleranceMeters);
211
- }
212
- }
@@ -1,199 +0,0 @@
1
- import { FeatureCollection, LineString } from 'geojson';
2
- import { getNetworkInBoundingBox, BoundingBox } from './bounding-box';
3
- import { createFeatureCollection, createLineStringFeature } from '../../test-utils/create';
4
-
5
- describe('getNetworkInBoundingBox', () => {
6
- describe('with an empty feature collection', () => {
7
- it('returns an empty feature collection', () => {
8
- const input: FeatureCollection<LineString> = createFeatureCollection([]);
9
- const boundingBox: BoundingBox = [0, 0, 10, 10];
10
- const output = getNetworkInBoundingBox(input, boundingBox);
11
-
12
- expect(output).toEqual({
13
- type: 'FeatureCollection',
14
- features: []
15
- });
16
- });
17
- });
18
-
19
- describe('with a single LineString completely within bounds', () => {
20
- it('returns the LineString', () => {
21
- const lineString = createLineStringFeature([[1, 1], [2, 2], [3, 3]]);
22
- const input: FeatureCollection<LineString> = createFeatureCollection([lineString]);
23
- const boundingBox: BoundingBox = [0, 0, 10, 10];
24
- const output = getNetworkInBoundingBox(input, boundingBox);
25
-
26
- expect(output).toEqual({
27
- type: 'FeatureCollection',
28
- features: [lineString]
29
- });
30
- });
31
- });
32
-
33
- describe('with a single LineString partially outside bounds', () => {
34
- it('excludes the LineString', () => {
35
- const lineString = createLineStringFeature([[1, 1], [2, 2], [15, 15]]);
36
- const input: FeatureCollection<LineString> = createFeatureCollection([lineString]);
37
- const boundingBox: BoundingBox = [0, 0, 10, 10];
38
- const output = getNetworkInBoundingBox(input, boundingBox);
39
-
40
- expect(output).toEqual({
41
- type: 'FeatureCollection',
42
- features: []
43
- });
44
- });
45
- });
46
-
47
- describe('with a single LineString completely outside bounds', () => {
48
- it('excludes the LineString', () => {
49
- const lineString = createLineStringFeature([[20, 20], [25, 25], [30, 30]]);
50
- const input: FeatureCollection<LineString> = createFeatureCollection([lineString]);
51
- const boundingBox: BoundingBox = [0, 0, 10, 10];
52
- const output = getNetworkInBoundingBox(input, boundingBox);
53
-
54
- expect(output).toEqual({
55
- type: 'FeatureCollection',
56
- features: []
57
- });
58
- });
59
- });
60
-
61
- describe('with multiple LineStrings', () => {
62
- it('returns only those completely within bounds', () => {
63
- const insideLine1 = createLineStringFeature([[1, 1], [2, 2]]);
64
- const insideLine2 = createLineStringFeature([[5, 5], [6, 6], [7, 7]]);
65
- const partiallyOutside = createLineStringFeature([[8, 8], [12, 12]]);
66
- const completelyOutside = createLineStringFeature([[20, 20], [25, 25]]);
67
-
68
- const input: FeatureCollection<LineString> = createFeatureCollection([
69
- insideLine1,
70
- insideLine2,
71
- partiallyOutside,
72
- completelyOutside
73
- ]);
74
- const boundingBox: BoundingBox = [0, 0, 10, 10];
75
- const output = getNetworkInBoundingBox(input, boundingBox);
76
-
77
- expect(output).toEqual({
78
- type: 'FeatureCollection',
79
- features: [insideLine1, insideLine2]
80
- });
81
- });
82
- });
83
-
84
- describe('with LineStrings on the boundary', () => {
85
- it('includes LineStrings with coordinates exactly on the boundary', () => {
86
- const onBoundary = createLineStringFeature([[0, 0], [10, 10]]);
87
- const input: FeatureCollection<LineString> = createFeatureCollection([onBoundary]);
88
- const boundingBox: BoundingBox = [0, 0, 10, 10];
89
- const output = getNetworkInBoundingBox(input, boundingBox);
90
-
91
- expect(output).toEqual({
92
- type: 'FeatureCollection',
93
- features: [onBoundary]
94
- });
95
- });
96
-
97
- it('excludes LineStrings with any coordinate outside the boundary', () => {
98
- const slightlyOutside = createLineStringFeature([[0, 0], [10.001, 10]]);
99
- const input: FeatureCollection<LineString> = createFeatureCollection([slightlyOutside]);
100
- const boundingBox: BoundingBox = [0, 0, 10, 10];
101
- const output = getNetworkInBoundingBox(input, boundingBox);
102
-
103
- expect(output).toEqual({
104
- type: 'FeatureCollection',
105
- features: []
106
- });
107
- });
108
- });
109
-
110
- describe('with negative coordinates', () => {
111
- it('handles negative bounding box coordinates correctly', () => {
112
- const lineString = createLineStringFeature([[-5, -5], [-2, -2], [-1, -1]]);
113
- const input: FeatureCollection<LineString> = createFeatureCollection([lineString]);
114
- const boundingBox: BoundingBox = [-10, -10, 0, 0];
115
- const output = getNetworkInBoundingBox(input, boundingBox);
116
-
117
- expect(output).toEqual({
118
- type: 'FeatureCollection',
119
- features: [lineString]
120
- });
121
- });
122
- });
123
-
124
- describe('with cross-meridian bounding box (longitude crossing 180°)', () => {
125
- it('handles coordinates correctly when bounding box crosses the antimeridian', () => {
126
- // Note: This test assumes the simple implementation doesn't handle cross-meridian cases
127
- // In a real-world scenario, you might need special handling for this
128
- const lineString = createLineStringFeature([[170, 10], [175, 15]]);
129
- const input: FeatureCollection<LineString> = createFeatureCollection([lineString]);
130
- const boundingBox: BoundingBox = [160, 0, 180, 20];
131
- const output = getNetworkInBoundingBox(input, boundingBox);
132
-
133
- expect(output).toEqual({
134
- type: 'FeatureCollection',
135
- features: [lineString]
136
- });
137
- });
138
- });
139
-
140
- describe('input validation', () => {
141
- it('throws an error for invalid bounding box (minLng >= maxLng)', () => {
142
- const input: FeatureCollection<LineString> = createFeatureCollection([]);
143
- const invalidBoundingBox: BoundingBox = [10, 0, 5, 10]; // minLng > maxLng
144
-
145
- expect(() => {
146
- getNetworkInBoundingBox(input, invalidBoundingBox);
147
- }).toThrow('Invalid bounding box: min values must be less than max values');
148
- });
149
-
150
- it('throws an error for invalid bounding box (minLat >= maxLat)', () => {
151
- const input: FeatureCollection<LineString> = createFeatureCollection([]);
152
- const invalidBoundingBox: BoundingBox = [0, 10, 10, 5]; // minLat > maxLat
153
-
154
- expect(() => {
155
- getNetworkInBoundingBox(input, invalidBoundingBox);
156
- }).toThrow('Invalid bounding box: min values must be less than max values');
157
- });
158
-
159
- it('throws an error for invalid bounding box (equal min and max)', () => {
160
- const input: FeatureCollection<LineString> = createFeatureCollection([]);
161
- const invalidBoundingBox: BoundingBox = [0, 0, 0, 0]; // minLng === maxLng
162
-
163
- expect(() => {
164
- getNetworkInBoundingBox(input, invalidBoundingBox);
165
- }).toThrow('Invalid bounding box: min values must be less than max values');
166
- });
167
- });
168
-
169
- describe('with complex LineStrings', () => {
170
- it('handles LineStrings with many coordinates', () => {
171
- const manyCoords = [];
172
- for (let i = 1; i <= 100; i++) {
173
- manyCoords.push([i * 0.1, i * 0.1]);
174
- }
175
- const complexLineString = createLineStringFeature(manyCoords);
176
- const input: FeatureCollection<LineString> = createFeatureCollection([complexLineString]);
177
- const boundingBox: BoundingBox = [0, 0, 20, 20];
178
- const output = getNetworkInBoundingBox(input, boundingBox);
179
-
180
- expect(output).toEqual({
181
- type: 'FeatureCollection',
182
- features: [complexLineString]
183
- });
184
- });
185
-
186
- it('excludes LineStrings when even one coordinate is outside', () => {
187
- const coords = [[1, 1], [2, 2], [3, 3], [4, 4], [15, 15]]; // last coordinate outside
188
- const lineString = createLineStringFeature(coords);
189
- const input: FeatureCollection<LineString> = createFeatureCollection([lineString]);
190
- const boundingBox: BoundingBox = [0, 0, 10, 10];
191
- const output = getNetworkInBoundingBox(input, boundingBox);
192
-
193
- expect(output).toEqual({
194
- type: 'FeatureCollection',
195
- features: []
196
- });
197
- });
198
- });
199
- });