@wemap/geo 0.3.8 → 0.4.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/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import Constants from './src/Constants';
2
+ import Utils from './src/Utils';
2
3
 
3
4
  import Attitude from './src/rotations/Attitude';
4
5
 
@@ -23,6 +24,7 @@ export {
23
24
  MapMatching,
24
25
  Network,
25
26
  Node,
27
+ Utils,
26
28
  WGS84,
27
29
  WGS84UserPosition
28
30
  };
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "directory": "packages/geo"
13
13
  },
14
14
  "name": "@wemap/geo",
15
- "version": "0.3.8",
15
+ "version": "0.4.0",
16
16
  "bugs": {
17
17
  "url": "https://github.com/wemap/wemap-utils-js/issues"
18
18
  },
@@ -31,5 +31,5 @@
31
31
  "lodash.isnumber": "^3.0.3",
32
32
  "lodash.isstring": "^4.0.1"
33
33
  },
34
- "gitHead": "6e107eebc85ff97f17371feac903ddb53657678b"
34
+ "gitHead": "08d7d843add321d899aef07f9e2ec7739270dc70"
35
35
  }
package/src/Utils.js ADDED
@@ -0,0 +1,12 @@
1
+ class Utils {
2
+
3
+ /**
4
+ * Get route duration
5
+ * @param {Number} speed in km/h
6
+ */
7
+ static getDurationFromLength(length, speed = 5) {
8
+ return length / (speed * 1000 / 3600);
9
+ }
10
+ }
11
+
12
+ export default Utils;
@@ -113,7 +113,7 @@ class Level {
113
113
  if (second.isInside(first.val)) {
114
114
  return first;
115
115
  }
116
- return first;
116
+ return null;
117
117
  }
118
118
  if (first.isRange && second.isRange) {
119
119
  const up = Math.min(first.up, second.up);
@@ -119,6 +119,20 @@ describe('Level', () => {
119
119
  expect(Level.equalsTo(new Level(-1.5, 1).clone(), new Level(-1.5, 1))).true;
120
120
  });
121
121
 
122
+ it('isInside', () => {
123
+ expect(new Level(0).isInside(0)).true;
124
+ expect(new Level(1).isInside(0)).false;
125
+ expect(new Level(-1).isInside(0)).false;
126
+ expect(new Level(0).isInside(1)).false;
127
+ expect(new Level(0).isInside(-1)).false;
128
+ expect(new Level(0, 2).isInside(0)).true;
129
+ expect(new Level(0, 2).isInside(1)).true;
130
+ expect(new Level(0, 2).isInside(-1)).false;
131
+ expect(new Level(2, 0).isInside(0)).true;
132
+ expect(new Level(2, 0).isInside(1)).true;
133
+ expect(new Level(2, 0).isInside(-1)).false;
134
+ });
135
+
122
136
  it('intersect', () => {
123
137
  expect(Level.intersect(null, null)).is.null;
124
138
 
@@ -138,6 +152,8 @@ describe('Level', () => {
138
152
  expect(Level.equalsTo(Level.intersect(new Level(-3, -1), new Level(-1, 2)), new Level(-1)));
139
153
  expect(Level.intersect(new Level(-3, -2), new Level(-1, 2))).is.null;
140
154
  expect(Level.intersect(new Level(-1, 2), new Level(-3, -2))).is.null;
155
+ expect(Level.intersect(new Level(0), new Level(1, 2))).is.null;
156
+ expect(Level.intersect(new Level(1, 2), new Level(0))).is.null;
141
157
  });
142
158
 
143
159
  it('union', () => {
@@ -159,6 +175,8 @@ describe('Level', () => {
159
175
  expect(Level.equalsTo(Level.union(new Level(-3, -1), new Level(-1, 2)), new Level(-3, 2)));
160
176
  expect(Level.equalsTo(Level.union(new Level(-3, -2), new Level(-1, 2)), new Level(-3, 2)));
161
177
  expect(Level.equalsTo(Level.union(new Level(-1, 2), new Level(-3, -2)), new Level(-3, 2)));
178
+ expect(Level.equalsTo(Level.union(new Level(0), new Level(1, 2)), new Level(0, 2)));
179
+ expect(Level.equalsTo(Level.union(new Level(1, 2), new Level(0)), new Level(0, 2)));
162
180
  });
163
181
 
164
182
  });
@@ -7,16 +7,31 @@ import Edge from './Edge';
7
7
  import Itinerary from './Itinerary';
8
8
  import Node from './Node';
9
9
  import MapMatching from './MapMatching';
10
+ import Utils from '../Utils';
11
+
12
+ const DEFAULT_ACCEPT_EDGE_FN = () => true;
13
+ const DEFAULT_ACCEPT_ONEWAY_FN = () => true;
14
+ const DEFAULT_WEIGHT_FN = edge => Utils.getDurationFromLength(edge.length);
10
15
 
11
16
  class GraphRouter {
12
17
 
13
- constructor(network) {
18
+ constructor(network, mapMatchingMaxDistance = 50) {
14
19
  this.network = network;
15
20
  this.mapMatching = new MapMatching(network);
16
- this.mapMatching.maxDistance = 50;
21
+ this.mapMatching.maxDistance = mapMatchingMaxDistance;
17
22
  }
18
23
 
19
- getShortestPath(_start, _end, acceptEdgeFn = () => true) {
24
+ getShortestPath(
25
+ _start,
26
+ _end,
27
+ _acceptEdgeFn,
28
+ _weightFn,
29
+ _acceptOneWayFn) {
30
+
31
+ const acceptEdgeFn = _acceptEdgeFn || DEFAULT_ACCEPT_EDGE_FN;
32
+ const weightFn = _weightFn || DEFAULT_WEIGHT_FN;
33
+ const acceptOneWayFn = _acceptOneWayFn || DEFAULT_ACCEPT_ONEWAY_FN;
34
+
20
35
  let start;
21
36
  let startNodeCreated;
22
37
 
@@ -74,7 +89,8 @@ class GraphRouter {
74
89
  throw new Error('Cannot provide path because no end point has been found');
75
90
  }
76
91
 
77
- const path = this.getShortestPathInternal(start, end, acceptEdgeFn);
92
+ const path = this.getShortestPathInternal(start, end,
93
+ acceptEdgeFn, weightFn, acceptOneWayFn);
78
94
 
79
95
  let route;
80
96
  if (path && path.length !== 0) {
@@ -138,7 +154,7 @@ class GraphRouter {
138
154
  );
139
155
  }
140
156
 
141
- getShortestPathInternal(start, end, acceptEdgeFn) {
157
+ getShortestPathInternal(start, end, acceptEdgeFn, weightFn, acceptOneWayFn) {
142
158
  const distanceMap = {},
143
159
  checking = {},
144
160
  vertexList = {},
@@ -189,7 +205,7 @@ class GraphRouter {
189
205
  })
190
206
  // for each vertex we can reach
191
207
  .forEach(edge => {
192
- let to, from;
208
+ let to, from, reversed = false;
193
209
  // determine the direction of travel
194
210
  if (edge.node1.uniqueRouterId === current) {
195
211
  to = edge.node2.uniqueRouterId;
@@ -197,10 +213,16 @@ class GraphRouter {
197
213
  } else {
198
214
  to = edge.node1.uniqueRouterId;
199
215
  from = edge.node2.uniqueRouterId;
216
+ reversed = true;
200
217
  }
218
+
219
+ if (!acceptOneWayFn(edge, reversed)) {
220
+ return;
221
+ }
222
+
201
223
  // distance is how far we travelled to reach the
202
224
  // current vertex, plus cost of travel the next(to)
203
- const distance = distanceMap[current] + edge.length;
225
+ const distance = distanceMap[current] + weightFn(edge);
204
226
 
205
227
  // if we have found a cheaper path
206
228
  // update the hash of costs
@@ -1,23 +1,30 @@
1
1
  import { expect } from 'chai';
2
2
 
3
+ import WGS84 from '../coordinates/WGS84';
3
4
  import GraphRouter from './GraphRouter';
5
+ import Node from './Node';
6
+ import Edge from './Edge';
4
7
 
5
8
  import {
6
- network, nodes, itineraryStart, itineraryEnd
9
+ network, nodes, itineraryStart, itineraryEnd, routingOptions
7
10
  } from '../../tests/CommonTest';
11
+ import Network from './Network';
8
12
 
9
- describe('GraphRouter', () => {
10
13
 
14
+ const {
15
+ acceptEdgeWithoutStairsFn, weightFn, acceptOneWayFn
16
+ } = routingOptions;
17
+
18
+ describe('GraphRouter', () => {
11
19
 
12
20
  const router = new GraphRouter(network);
13
21
  let itinerary;
14
22
 
15
-
16
23
  it('network did not change', () => {
17
24
  const nodesBefore = network.nodes.slice(0);
18
25
  const edgesBefore = network.edges.slice(0);
19
26
 
20
- itinerary = router.getShortestPath(itineraryStart, itineraryEnd);
27
+ itinerary = router.getShortestPath(itineraryStart, itineraryEnd, null, weightFn);
21
28
 
22
29
  const isEdgeInArray = (edgeArray, edge) => edgeArray.find(_edge =>
23
30
  edge.node1 === _edge.node1
@@ -62,11 +69,91 @@ describe('GraphRouter', () => {
62
69
  }
63
70
 
64
71
  });
72
+ });
73
+
74
+
75
+ describe('GraphRouter - Itinerary without stairs', () => {
76
+
77
+ const router = new GraphRouter(network);
78
+ const itinerary = router.getShortestPath(
79
+ itineraryStart,
80
+ itineraryEnd,
81
+ acceptEdgeWithoutStairsFn,
82
+ weightFn
83
+ );
84
+ const itineraryWithoutRestriction = router.getShortestPath(
85
+ itineraryStart,
86
+ itineraryEnd,
87
+ null,
88
+ weightFn
89
+ );
90
+
91
+ it('no stairs found', () => {
92
+ expect(itinerary).is.not.undefined;
93
+ expect(itinerary.edges.some(node => node.data && node.data.stairs)).false;
94
+
95
+ expect(itineraryWithoutRestriction).is.not.undefined;
96
+ expect(itineraryWithoutRestriction.edges.some(node => node.data && node.data.stairs)).true;
97
+ });
98
+ });
99
+
100
+
101
+ describe('GraphRouter - Itinerary one way', () => {
102
+
103
+ const owNodes = [
104
+ new Node(new WGS84(43.6094442, 3.8842049), { name: 'p0' }),
105
+ new Node(new WGS84(43.6094286, 3.8842011), { name: 'p1' }),
106
+ new Node(new WGS84(43.6094058, 3.8841955), { name: 'p2' }),
107
+ new Node(new WGS84(43.6093893, 3.8841914), { name: 'p3' }),
108
+ new Node(new WGS84(43.6094256, 3.8842247), { name: 'p4' }),
109
+ new Node(new WGS84(43.6094028, 3.8842191), { name: 'p5' })
110
+ ];
111
+
112
+ const owEdges = [
113
+ new Edge(owNodes[0], owNodes[1], { name: 'e0' }),
114
+ new Edge(owNodes[1], owNodes[2], { name: 'e1', oneWay: true }),
115
+ new Edge(owNodes[2], owNodes[3], { name: 'e2' }),
116
+ new Edge(owNodes[1], owNodes[4], { name: 'e3' }),
117
+ new Edge(owNodes[4], owNodes[5], { name: 'e4' }),
118
+ new Edge(owNodes[5], owNodes[2], { name: 'e5' })
119
+ ];
120
+ const owNetwork = new Network(owNodes, owEdges);
121
+ const owRouter = new GraphRouter(owNetwork);
122
+
123
+ const start = new WGS84(43.6094542, 3.8842072);
124
+ const end = new WGS84(43.6093792, 3.8841889);
125
+
126
+ const itinerary = owRouter.getShortestPath(
127
+ start,
128
+ end,
129
+ null,
130
+ weightFn,
131
+ acceptOneWayFn
132
+ );
133
+
134
+ const itineraryOtherWay = owRouter.getShortestPath(
135
+ end,
136
+ start,
137
+ null,
138
+ weightFn,
139
+ acceptOneWayFn
140
+ );
141
+
142
+ it('do not use oneway segments', () => {
143
+
144
+ expect(itinerary).is.not.undefined;
145
+ expect(itinerary.nodes[0]).equals(owNodes[0]);
146
+ expect(itinerary.nodes[1]).equals(owNodes[1]);
147
+ expect(itinerary.nodes[2]).equals(owNodes[2]);
148
+ expect(itinerary.nodes[3]).equals(owNodes[3]);
149
+
150
+ expect(itineraryOtherWay).is.not.undefined;
151
+ expect(itineraryOtherWay.nodes[0]).equals(owNodes[3]);
152
+ expect(itineraryOtherWay.nodes[1]).equals(owNodes[2]);
153
+ expect(itineraryOtherWay.nodes[2]).equals(owNodes[5]);
154
+ expect(itineraryOtherWay.nodes[3]).equals(owNodes[4]);
155
+ expect(itineraryOtherWay.nodes[4]).equals(owNodes[1]);
156
+ expect(itineraryOtherWay.nodes[5]).equals(owNodes[0]);
65
157
 
66
- it('no route found', () => {
67
- const itineraryWithoutStairs = router.getShortestPath(itineraryStart, itineraryEnd,
68
- edge => !edge.data || !edge.data.hasOwnProperty('stairs') || !edge.data.stairs
69
- );
70
- expect(itineraryWithoutStairs).is.undefined;
71
158
  });
72
159
  });
@@ -1,14 +1,15 @@
1
1
  /* eslint-disable max-statements */
2
2
  import { WGS84, Level } from '@wemap/geo';
3
+ import {
4
+ diffAngle, deg2rad
5
+ } from '@wemap/maths';
3
6
 
4
7
  import Network from './Network';
5
8
  import Edge from './Edge';
6
9
  import Node from './Node';
7
10
  import Step from './Step';
8
- import {
9
- diffAngle, deg2rad
10
- } from '@wemap/maths';
11
11
  import MapMatching from './MapMatching';
12
+ import Utils from '../Utils';
12
13
 
13
14
  const SKIP_STEP_ANGLE_MAX = deg2rad(20);
14
15
 
@@ -67,15 +68,7 @@ class Itinerary extends Network {
67
68
  * @param {Number} speed in km/h
68
69
  */
69
70
  getDuration(speed) {
70
- return Itinerary.getDurationFromLength(this.length, speed);
71
- }
72
-
73
- /**
74
- * Get route duration
75
- * @param {Number} speed in km/h
76
- */
77
- static getDurationFromLength(length, speed = 4) {
78
- return length / (speed * 1000 / 3600);
71
+ return Utils.getDurationFromLength(this.length, speed);
79
72
  }
80
73
 
81
74
  get steps() {
@@ -4,7 +4,7 @@ import { expect } from 'chai';
4
4
  import GraphRouter from './GraphRouter';
5
5
 
6
6
  import {
7
- network, nodes, itineraryStart, itineraryEnd, edges
7
+ network, nodes, itineraryStart, itineraryEnd, edges, routingOptions
8
8
  } from '../../tests/CommonTest';
9
9
  import Level from '../coordinates/Level';
10
10
  import WGS84 from '../coordinates/WGS84';
@@ -12,7 +12,8 @@ import WGS84 from '../coordinates/WGS84';
12
12
  describe('Itinerary', () => {
13
13
 
14
14
  const router = new GraphRouter(network);
15
- const itinerary = router.getShortestPath(itineraryStart, itineraryEnd);
15
+ const itinerary = router.getShortestPath(itineraryStart, itineraryEnd, null,
16
+ routingOptions.weightFn);
16
17
 
17
18
  it('is readable', () => {
18
19
 
@@ -34,10 +34,6 @@ describe('MapMatching', () => {
34
34
  it('matching node levels', () => {
35
35
  let currentPosition, projection;
36
36
 
37
- /**
38
- * Projection on Nodes
39
- */
40
-
41
37
  currentPosition = new WGS84(43.6091773, 3.8842584, null, null);
42
38
  projection = mapMatching.getProjection(currentPosition);
43
39
  expect(projection).is.null;
@@ -57,10 +53,6 @@ describe('MapMatching', () => {
57
53
  it('matching edge levels', () => {
58
54
  let currentPosition, projection;
59
55
 
60
- /**
61
- * Projection on Edges
62
- */
63
-
64
56
  currentPosition = new WGS84(43.6092811, 3.8842406, null, null);
65
57
  projection = mapMatching.getProjection(currentPosition);
66
58
  expect(projection).is.null;
@@ -80,10 +72,6 @@ describe('MapMatching', () => {
80
72
  it('matching stairs nodes levels', () => {
81
73
  let currentPosition, projection;
82
74
 
83
- /**
84
- * Projection on Edges
85
- */
86
-
87
75
  currentPosition = new WGS84(43.6093691, 3.8842057, null, null);
88
76
  projection = mapMatching.getProjection(currentPosition);
89
77
  expect(projection).is.null;
@@ -103,10 +91,6 @@ describe('MapMatching', () => {
103
91
  it('matching stairs edge levels', () => {
104
92
  let currentPosition, projection;
105
93
 
106
- /**
107
- * Projection on Edges
108
- */
109
-
110
94
  currentPosition = new WGS84(43.6093476, 3.8841978, null, null);
111
95
  projection = mapMatching.getProjection(currentPosition);
112
96
  expect(projection).is.null;
@@ -125,4 +109,25 @@ describe('MapMatching', () => {
125
109
 
126
110
  });
127
111
 
112
+ it('useMultiLevelSegments', () => {
113
+ let currentPosition, projection;
114
+
115
+ // On Nodes
116
+
117
+ currentPosition = new WGS84(43.6093691, 3.8842057, null, new Level(0));
118
+ projection = mapMatching.getProjection(currentPosition, true, false, false);
119
+ expect(projection).is.null;
120
+
121
+ // Next two are complex cases and not yet handled by our system
122
+
123
+ // currentPosition = new WGS84(43.6093691, 3.8842057, null, new Level(1));
124
+ // projection = mapMatching.getProjection(currentPosition, true, false, false);
125
+ // expect(projection.nearestElement).equals(nodes[14]);
126
+
127
+ // currentPosition = new WGS84(43.6093691, 3.8842057, null, new Level(2));
128
+ // projection = mapMatching.getProjection(currentPosition, true, false, false);
129
+ // expect(projection.nearestElement).equals(nodes[11]);
130
+
131
+ });
132
+
128
133
  });
@@ -3,9 +3,9 @@
3
3
  */
4
4
  class Network {
5
5
 
6
- constructor() {
7
- this.nodes = [];
8
- this.edges = [];
6
+ constructor(nodes, edges) {
7
+ this.nodes = Array.isArray(nodes) ? nodes : [];
8
+ this.edges = Array.isArray(edges) ? edges : [];
9
9
  }
10
10
 
11
11
  getNodeByCoords(coords) {
@@ -27,12 +27,12 @@ class Network {
27
27
 
28
28
  let nodeToStringFn = _nodeToStringFn;
29
29
  if (!nodeToStringFn) {
30
- nodeToStringFn = node => `${node.data.name}`;
30
+ nodeToStringFn = node => `${node.data}`;
31
31
  }
32
32
 
33
33
  let edgeToStringFn = _edgeToStringFn;
34
34
  if (!_edgeToStringFn) {
35
- edgeToStringFn = edge => `${edge.data.name}`;
35
+ edgeToStringFn = edge => `${edge.data}`;
36
36
  }
37
37
 
38
38
  let output
package/src/graph/Step.js CHANGED
@@ -1,4 +1,4 @@
1
- import Itinerary from './Itinerary';
1
+ import Utils from '../Utils';
2
2
 
3
3
  class Step {
4
4
 
@@ -49,7 +49,7 @@ class Step {
49
49
  * @param {Number} speed in km/h
50
50
  */
51
51
  getDuration(speed) {
52
- return Itinerary.getDurationFromLength(this.length, speed);
52
+ return Utils.getDurationFromLength(this.length, speed);
53
53
  }
54
54
 
55
55
  }
@@ -5,6 +5,7 @@ import {
5
5
  import Edge from '../src/graph/Edge';
6
6
  import Node from '../src/graph/Node';
7
7
  import Network from '../src/graph/Network';
8
+ import Utils from '../src/Utils';
8
9
 
9
10
  const nodes = [
10
11
  new Node(new WGS84(43.6092404, 3.884099), { name: 'p0' }),
@@ -23,7 +24,11 @@ const nodes = [
23
24
  new Node(new WGS84(43.6093597, 3.8842336), { name: 'p13' }),
24
25
  new Node(new WGS84(43.6093202, 3.8842218), { name: 'p14' }),
25
26
  new Node(new WGS84(43.6093123, 3.8842731), { name: 'p15' }),
26
- new Node(new WGS84(43.6092681, 3.8842604), { name: 'p16' })
27
+ new Node(new WGS84(43.6092681, 3.8842604), { name: 'p16' }),
28
+ new Node(new WGS84(43.6093279, 3.8842777), { name: 'p17' }),
29
+ new Node(new WGS84(43.6093279, 3.8842777), { name: 'p18' }),
30
+ new Node(new WGS84(43.6093323, 3.8842483), { name: 'p19a' }),
31
+ new Node(new WGS84(43.6093323, 3.8842483), { name: 'p19b' })
27
32
  ];
28
33
 
29
34
  const edges = [
@@ -42,7 +47,12 @@ const edges = [
42
47
  new Edge(nodes[12], nodes[13], { name: 'e12', stairs: true }, new Level(1, 2)),
43
48
  new Edge(nodes[13], nodes[14], { name: 'e13', stairs: true }, new Level(1, 2)),
44
49
  new Edge(nodes[14], nodes[15], { name: 'e14' }, new Level(1)),
45
- new Edge(nodes[15], nodes[16], { name: 'e15' }, new Level(1))
50
+ new Edge(nodes[15], nodes[16], { name: 'e15' }, new Level(1)),
51
+ new Edge(nodes[15], nodes[17], { name: 'e16' }, new Level(1)),
52
+ new Edge(nodes[19], nodes[17], { name: 'e17' }, new Level(1)),
53
+ new Edge(nodes[10], nodes[18], { name: 'e18' }, new Level(2)),
54
+ new Edge(nodes[20], nodes[18], { name: 'e19' }, new Level(2)),
55
+ new Edge(nodes[19], nodes[20], { name: 'e20', elevator: true }, new Level(1, 2))
46
56
  ];
47
57
 
48
58
  const network = new Network();
@@ -52,6 +62,25 @@ network.nodes = nodes;
52
62
  const itineraryStart = new WGS84(43.6092754, 3.8842306, null, new Level(2));
53
63
  const itineraryEnd = new WGS84(43.6092602, 3.8842669, null, new Level(1));
54
64
 
65
+ const detailedStringArgs = [
66
+ node => !node.data ? 'created' : `${node.data.name}`,
67
+ edge => !edge.data ? 'created' : `${edge.data.name}`
68
+ ];
69
+
70
+ const routingOptions = {
71
+
72
+ acceptOneWayFn: (edge, reversed) => !(edge.data && edge.data.oneWay && reversed),
73
+
74
+ acceptEdgeWithoutStairsFn: edge => !(edge.data && edge.data.stairs),
75
+
76
+ weightFn: edge => {
77
+ if (edge.data && edge.data.elevator) {
78
+ return 30;
79
+ }
80
+ return Utils.getDurationFromLength(edge.length);
81
+ }
82
+ };
83
+
55
84
  export {
56
- nodes, edges, network, itineraryStart, itineraryEnd
85
+ nodes, edges, network, itineraryStart, itineraryEnd, routingOptions, detailedStringArgs
57
86
  };