terra-route
Version:
A library for routing along GeoJSON LineString networks
217 lines (176 loc) • 8.96 kB
text/typescript
import { FeatureCollection, LineString } from 'geojson';
import { graphGetConnectedComponentCount, graphGetConnectedComponents } from './connected';
import { generateTreeFeatureCollection } from '../../test-utils/generate-network';
import { createFeatureCollection, createLineStringFeature } from '../../test-utils/create';
import { readFileSync } from 'fs';
describe('countConnectedComponents', () => {
describe('for an empty feature collection', () => {
it('returns 0', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(0);
});
});
describe('for feature collection with 1 linestring', () => {
it('returns 1', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]])
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(1);
});
});
describe('for feature collection with 2 linestring', () => {
it('returns 1 if connected', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[1, 1], [2, 2]]),
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(1);
});
it('returns 2 if unconnected', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[10, 10], [11, 11]]),
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(2);
});
});
describe('for feature collection with 3 linestring', () => {
it('returns 1 if connected', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[1, 1], [2, 2]]),
createLineStringFeature([[2, 2], [3, 3]]),
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(1);
});
it('returns 3 if unconnected', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[10, 10], [11, 11]]),
createLineStringFeature([[20, 20], [21, 21]]),
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(3);
});
});
describe('for feature collection with multiple linestring', () => {
it('returns 1 when all lines share the same coordinate', () => {
const input = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[1, 1], [2, 2]]),
createLineStringFeature([[1, 1], [3, 3]]),
createLineStringFeature([[4, 4], [1, 1]]),
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(1);
});
it('returns 2 when two disconnected groups exist', () => {
const input = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[1, 1], [2, 2]]),
createLineStringFeature([[10, 10], [11, 11]]),
createLineStringFeature([[11, 11], [12, 12]]),
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(2);
});
it('returns 1 for a loop of connected lines', () => {
const input = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 0]]),
createLineStringFeature([[1, 0], [1, 1]]),
createLineStringFeature([[1, 1], [0, 1]]),
createLineStringFeature([[0, 1], [0, 0]]),
]);
const output = graphGetConnectedComponentCount(input);
expect(output).toBe(1);
});
});
describe('for complex linestring network', () => {
it('returns 1 when tree is connected', () => {
const input = generateTreeFeatureCollection(3, 2)
const output = graphGetConnectedComponentCount(input)
expect(output).toBe(1);
});
it('returns 1 when it is connected correctly', () => {
const input = generateTreeFeatureCollection(3, 2)
const output = graphGetConnectedComponentCount(input)
expect(output).toBe(1);
});
})
});
describe('splitIntoConnectedComponents', () => {
it('returns empty array for empty feature collection', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([]);
const output = graphGetConnectedComponents(input);
expect(output).toEqual([]);
});
it('returns single component for single linestring', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]])
]);
const output = graphGetConnectedComponents(input);
expect(output).toHaveLength(1);
expect(output[0].features).toHaveLength(1);
});
it('returns multiple components for disconnected linestrings', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[10, 10], [11, 11]])
]);
const output = graphGetConnectedComponents(input);
expect(output).toHaveLength(2);
expect(output[0].features).toHaveLength(1);
expect(output[1].features).toHaveLength(1);
});
it('returns single component for connected linestrings', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[1, 1], [2, 2]])
]);
const output = graphGetConnectedComponents(input);
expect(output).toHaveLength(1);
expect(output[0].features).toHaveLength(2);
expect(output[0].features[0].geometry.coordinates).toEqual([[0, 0], [1, 1]]);
expect(output[0].features[1].geometry.coordinates).toEqual([[1, 1], [2, 2]]);
});
it('returns multiple components for selection of connected linestrings', () => {
const input: FeatureCollection<LineString> = createFeatureCollection([
createLineStringFeature([[0, 0], [1, 1]]),
createLineStringFeature([[1, 1], [2, 2]]),
createLineStringFeature([[10, 10], [11, 11]]),
createLineStringFeature([[20, 20], [21, 21]])
]);
const output = graphGetConnectedComponents(input);
expect(output).toHaveLength(3);
expect(output[0].features).toHaveLength(1);
expect(output[1].features).toHaveLength(1);
expect(output[2].features).toHaveLength(2);
});
it('returns single component for complex selection of linestrings', () => {
const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
const output = graphGetConnectedComponents(network);
expect(output).toHaveLength(1);
expect(output[0].features).toHaveLength(network.features.length);
});
it('returns multiple components for complex selection of linestrings', () => {
const network = JSON.parse(readFileSync('src/data/network-5-cc.geojson', 'utf-8')) as FeatureCollection<LineString>;
const output = graphGetConnectedComponents(network);
expect(output).toHaveLength(5);
expect(output[4].features > output[0].features).toBeTruthy();
});
it('ensures splitIntoConnectedComponents and countConnectedComponents are consistent', () => {
const network = JSON.parse(readFileSync('src/data/network.geojson', 'utf-8')) as FeatureCollection<LineString>;
const components = graphGetConnectedComponents(network);
const count = graphGetConnectedComponentCount(network);
expect(components.length).toBe(count);
const networkMultipleCC = JSON.parse(readFileSync('src/data/network-5-cc.geojson', 'utf-8')) as FeatureCollection<LineString>;
const componentsMultipleCC = graphGetConnectedComponents(networkMultipleCC);
const countMultipleCC = graphGetConnectedComponentCount(networkMultipleCC);
expect(componentsMultipleCC.length).toBe(countMultipleCC);
});
})