/
OsmPath.java
462 lines (378 loc) · 13.8 KB
/
OsmPath.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import java.io.IOException;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction;
import btools.util.CheapRuler;
abstract class OsmPath implements OsmLinkHolder {
/**
* The cost of that path (a modified distance)
*/
public int cost = 0;
// the elevation assumed for that path can have a value
// if the corresponding node has not
public short selev;
public int airdistance = 0; // distance to endpos
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
public OsmPathElement originElement;
public OsmPathElement myElement;
protected float traffic;
private OsmLinkHolder nextForLink = null;
public int treedepth = 0;
// the position of the waypoint just before
// this path position (for angle calculation)
public int originLon;
public int originLat;
// the classifier of the segment just before this paths position
protected float lastClassifier;
protected float lastInitialCost;
protected int priorityclassifier;
private static final int PATH_START_BIT = 1;
private static final int CAN_LEAVE_DESTINATION_BIT = 2;
private static final int IS_ON_DESTINATION_BIT = 4;
private static final int HAD_DESTINATION_START_BIT = 8;
protected int bitfield = PATH_START_BIT;
private boolean getBit(int mask) {
return (bitfield & mask) != 0;
}
private void setBit(int mask, boolean bit) {
if (getBit(mask) != bit) {
bitfield ^= mask;
}
}
public boolean didEnterDestinationArea() {
return !getBit(HAD_DESTINATION_START_BIT) && getBit(IS_ON_DESTINATION_BIT);
}
public MessageData message;
public void unregisterUpTree(RoutingContext rc) {
try {
OsmPathElement pe = originElement;
while (pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic) pe).unregister(rc)) {
pe = pe.origin;
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
public void registerUpTree() {
if (originElement instanceof OsmPathElementWithTraffic) {
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) originElement;
ot.register();
ot.addTraffic(traffic);
}
}
public void init(OsmLink link) {
this.link = link;
targetNode = link.getTarget(null);
selev = targetNode.getSElev();
originLon = -1;
originLat = -1;
}
public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) {
if (origin.myElement == null) {
origin.myElement = OsmPathElement.create(origin, rc.countTraffic);
}
this.originElement = origin.myElement;
this.link = link;
this.sourceNode = origin.targetNode;
this.targetNode = link.getTarget(sourceNode);
this.cost = origin.cost;
this.lastClassifier = origin.lastClassifier;
this.lastInitialCost = origin.lastInitialCost;
this.bitfield = origin.bitfield;
this.priorityclassifier = origin.priorityclassifier;
init(origin);
addAddionalPenalty(refTrack, detailMode, origin, link, rc);
}
protected abstract void init(OsmPath orig);
protected abstract void resetState();
static int seg = 1;
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
byte[] description = link.descriptionBitmap;
if (description == null) { // could be a beeline path
message = new MessageData();
if (message != null) {
message.turnangle = 0;
message.time = (float) 1;
message.energy = (float) 0;
message.priorityclassifier = 0;
message.classifiermask = 0;
message.lon = targetNode.getILon();
message.lat = targetNode.getILat();
message.ele = Short.MIN_VALUE;
message.linkdist = sourceNode.calcDistance(targetNode);
message.wayKeyValues = "direct_segment=" + seg;
seg++;
}
return;
}
boolean recordTransferNodes = detailMode || rc.countTraffic;
rc.nogoCost = 0.;
// extract the 3 positions of the first section
int lon0 = origin.originLon;
int lat0 = origin.originLat;
int lon1 = sourceNode.getILon();
int lat1 = sourceNode.getILat();
short ele1 = origin.selev;
int linkdisttotal = 0;
message = detailMode ? new MessageData() : null;
boolean isReverse = link.isReverse(sourceNode);
// evaluate the way tags
rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
// calculate the costfactor inputs
float costfactor = rc.expctxWay.getCostfactor();
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
int lastpriorityclassifier = priorityclassifier;
priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
float newInitialCost = rc.expctxWay.getInitialcost();
float classifierDiff = newClassifier - lastClassifier;
if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) {
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
if (initialcost >= 1000000.) {
cost = -1;
return;
}
int iicost = (int) initialcost;
if (message != null) {
message.linkinitcost += iicost;
}
cost += iicost;
}
lastClassifier = newClassifier;
lastInitialCost = newInitialCost;
// *** destination logic: no destination access in between
int classifiermask = (int) rc.expctxWay.getClassifierMask();
boolean newDestination = (classifiermask & 64) != 0;
boolean oldDestination = getBit(IS_ON_DESTINATION_BIT);
if (getBit(PATH_START_BIT)) {
setBit(PATH_START_BIT, false);
setBit(CAN_LEAVE_DESTINATION_BIT, newDestination);
setBit(HAD_DESTINATION_START_BIT, newDestination);
} else {
if (oldDestination && !newDestination) {
if (getBit(CAN_LEAVE_DESTINATION_BIT)) {
setBit(CAN_LEAVE_DESTINATION_BIT, false);
} else {
cost = -1;
return;
}
}
}
setBit(IS_ON_DESTINATION_BIT, newDestination);
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry(link.geometry, sourceNode, targetNode, isReverse);
for (int nsection = 0; ; nsection++) {
originLon = lon1;
originLat = lat1;
int lon2;
int lat2;
short ele2;
short originEle2;
if (transferNode == null) {
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
originEle2 = targetNode.selev;
} else {
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
originEle2 = transferNode.selev;
}
ele2 = originEle2;
boolean isStartpoint = lon0 == -1 && lat0 == -1;
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) {
if (rc.inverseDirection
? TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode)
: TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) {
cost = -1;
return;
}
}
// if recording, new MessageData for each section (needed for turn-instructions)
if (message != null && message.wayKeyValues != null) {
originElement.message = message;
message = new MessageData();
}
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
boolean stopAtEndpoint = false;
if (rc.shortestmatch) {
if (rc.isEndpoint) {
stopAtEndpoint = true;
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
} else {
// we just start here, reset everything
cost = 0;
resetState();
lon0 = -1; // reset turncost-pipe
lat0 = -1;
isStartpoint = true;
if (recordTransferNodes) {
if (rc.wayfraction > 0.) {
ele1 = interpolateEle(ele1, ele2, 1. - rc.wayfraction);
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic);
} else {
originElement = null; // prevent duplicate point
}
}
if (rc.checkPendingEndpoint()) {
dist = rc.calcDistance(rc.ilonshortest, rc.ilatshortest, lon2, lat2);
if (rc.shortestmatch) {
stopAtEndpoint = true;
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
}
}
}
}
if (message != null) {
message.linkdist += dist;
}
linkdisttotal += dist;
// apply a start-direction if appropriate (by faking the origin position)
if (isStartpoint) {
if (rc.startDirectionValid) {
double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lon0 + lat1) >> 1);
lon0 = lon1 - (int) (1000. * Math.sin(dir) / lonlat2m[0]);
lat0 = lat1 - (int) (1000. * Math.cos(dir) / lonlat2m[1]);
} else {
lon0 = lon1 - (lon2 - lon1);
lat0 = lat1 - (lat2 - lat1);
}
}
double angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
double cosangle = rc.anglemeter.getCosAngle();
// *** elevation stuff
double delta_h = 0.;
if (ele2 == Short.MIN_VALUE) ele2 = ele1;
if (ele1 != Short.MIN_VALUE) {
delta_h = (ele2 - ele1) / 4.;
if (rc.inverseDirection) {
delta_h = -delta_h;
}
}
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2 / 4.;
double sectionCost = processWaySection(rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier);
if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) {
cost = -1;
return;
}
if (isTrafficBackbone) {
sectionCost = 0.;
}
cost += (int) sectionCost;
// calculate traffic
if (rc.countTraffic) {
int minDist = (int) rc.trafficSourceMinDist;
int cost2 = cost < minDist ? minDist : cost;
traffic += dist * rc.expctxWay.getTrafficSourceDensity() * Math.pow(cost2 / 10000.f, rc.trafficSourceExponent);
}
// compute kinematic
computeKinematic(rc, dist, delta_h, detailMode);
if (message != null) {
message.turnangle = (float) angle;
message.time = (float) getTotalTime();
message.energy = (float) getTotalEnergy();
message.priorityclassifier = priorityclassifier;
message.classifiermask = classifiermask;
message.lon = lon2;
message.lat = lat2;
message.ele = originEle2;
message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description);
}
if (stopAtEndpoint) {
if (recordTransferNodes) {
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement, rc.countTraffic);
originElement.cost = cost;
if (message != null) {
originElement.message = message;
}
}
if (rc.nogoCost < 0) {
cost = -1;
} else {
cost += rc.nogoCost;
}
return;
}
if (transferNode == null) {
// *** penalty for being part of the reference track
if (refTrack != null && refTrack.containsNode(targetNode) && refTrack.containsNode(sourceNode)) {
int reftrackcost = linkdisttotal;
cost += reftrackcost;
}
selev = ele2;
break;
}
transferNode = transferNode.next;
if (recordTransferNodes) {
originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement, rc.countTraffic);
originElement.cost = cost;
originElement.addTraffic(traffic);
traffic = 0;
}
lon0 = lon1;
lat0 = lat1;
lon1 = lon2;
lat1 = lat2;
ele1 = ele2;
}
// check for nogo-matches (after the *actual* start of segment)
if (rc.nogoCost < 0) {
cost = -1;
return;
} else {
cost += rc.nogoCost;
}
// add target-node costs
double targetCost = processTargetNode(rc);
if (targetCost < 0. || targetCost + cost >= 2000000000.) {
cost = -1;
return;
}
cost += (int) targetCost;
}
public short interpolateEle(short e1, short e2, double fraction) {
if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) {
return Short.MIN_VALUE;
}
return (short) (e1 * (1. - fraction) + e2 * fraction);
}
protected abstract double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier);
protected abstract double processTargetNode(RoutingContext rc);
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
}
public abstract int elevationCorrection();
public abstract boolean definitlyWorseThan(OsmPath p);
public OsmNode getSourceNode() {
return sourceNode;
}
public OsmNode getTargetNode() {
return targetNode;
}
public OsmLink getLink() {
return link;
}
public void setNextForLink(OsmLinkHolder holder) {
nextForLink = holder;
}
public OsmLinkHolder getNextForLink() {
return nextForLink;
}
public double getTotalTime() {
return 0.;
}
public double getTotalEnergy() {
return 0.;
}
}