Compare commits
723 commits
revert-342
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 10268a02b2 | |||
| 6259e00005 | |||
| 153f0a3777 | |||
| 4d04047def | |||
| 73b75da8ee | |||
| 2b4c9d8fad | |||
| 24b4f6e43a | |||
|
|
fade673ac7 | ||
|
|
23c19d5014 | ||
|
|
a3090970ac | ||
|
|
c0a48f8f05 | ||
|
|
71889ba9fd | ||
|
|
c616bccabe | ||
|
|
e63cc9888f | ||
|
|
15bf08aaef | ||
|
|
e46444cf57 | ||
|
|
b234d48c00 | ||
|
|
e379b7abb0 | ||
|
|
f9c6ad1ae8 | ||
|
|
1f2f655863 | ||
|
|
f289b0cd83 | ||
|
|
8d22a2d0eb | ||
|
|
b1e9208be6 | ||
|
|
dec6cc8ba0 | ||
|
|
c631714c1f | ||
|
|
9dcb7ca92e | ||
|
|
928bd0e28f | ||
|
|
77e9bd316b | ||
|
|
2b3bbca448 | ||
|
|
6c69e9cea4 | ||
|
|
8f50671b98 | ||
|
|
e011343ab0 | ||
|
|
2a77f71c85 | ||
|
|
646f805b99 | ||
|
|
be0aa77ee8 | ||
|
|
cbf172656b | ||
|
|
7001c4cbc7 | ||
|
|
73e7873583 | ||
|
|
0b6500cdad | ||
|
|
a6611ed303 | ||
|
|
4e858f5e49 | ||
|
|
6d7b8f0d77 | ||
|
|
2a94b7f300 | ||
|
|
8270ae6638 | ||
|
|
584a2a82d6 | ||
|
|
f2c01b80d3 | ||
|
|
36e169ab48 | ||
|
|
47f58126e7 | ||
|
|
83f001e3a1 | ||
|
|
23c8123931 | ||
|
|
51ebfd346b | ||
|
|
a148ba70eb | ||
|
|
f3af9d6f4b | ||
|
|
d969ac11cb | ||
|
|
13781fb1fc | ||
|
|
8e3c9a9512 | ||
|
|
258a0c107d | ||
|
|
5d4065d141 | ||
|
|
b8929ab414 | ||
|
|
616266084c | ||
|
|
6c22d7d012 | ||
|
|
c73a8cebb8 | ||
|
|
dd896347a2 | ||
|
|
2f7ce42480 | ||
|
|
1573aa52e0 | ||
|
|
e2752c78bb | ||
|
|
a4388ce5c9 | ||
|
|
eebf22de84 | ||
|
|
6330325d04 | ||
|
|
7b176b4b6f | ||
|
|
107a6725e6 | ||
|
|
7229bc4c54 | ||
|
|
385009b9e6 | ||
|
|
7f880a5826 | ||
|
|
aa393ab7dd | ||
|
|
b9b629185a | ||
|
|
28e6523ab9 | ||
|
|
e75adfb555 | ||
|
|
1a0b38d375 | ||
|
|
09248679db | ||
|
|
531f913c5b | ||
|
|
dcc9719ba6 | ||
|
|
6e858b6c91 | ||
|
|
526bb53b70 | ||
|
|
86e62e1163 | ||
|
|
260e960baf | ||
|
|
e94b80e579 | ||
|
|
b2009cf7e8 | ||
|
|
47ee77bc35 | ||
|
|
97e9a824be | ||
|
|
f1e5732dc2 | ||
|
|
6427dab483 | ||
|
|
503ceb625b | ||
|
|
3baf7a08fa | ||
|
|
4ebd378800 | ||
|
|
e289571471 | ||
|
|
9c5b380105 | ||
|
|
6a0f69d546 | ||
|
|
91ccb858dd | ||
|
|
b3002a78e3 | ||
|
|
26879159da | ||
|
|
1bf367b43e | ||
|
|
e5ecd14ce1 | ||
|
|
ae7411d4a0 | ||
|
|
bf07e2e6d2 | ||
|
|
2f1422352e | ||
|
|
d2e183c625 | ||
|
|
226f677b26 | ||
|
|
58dc4afa1e | ||
|
|
56ba451888 | ||
|
|
a3ae1ce5e7 | ||
|
|
0a3aa3a84c | ||
|
|
5979df131b | ||
|
|
6b659def02 | ||
|
|
ae951d9aa5 | ||
|
|
152833386b | ||
|
|
e73d0e8001 | ||
|
|
7ffee3a911 | ||
|
|
01ac57a929 | ||
|
|
c5f158ec43 | ||
|
|
c31c38a5d6 | ||
|
|
0f8bdfee6c | ||
|
|
f405b0e16e | ||
|
|
29673062b5 | ||
|
|
12309f298c | ||
|
|
ec3461d8a2 | ||
|
|
9ef31e6d2c | ||
|
|
158dc5e54d | ||
|
|
77014ffddb | ||
|
|
a70c95c576 | ||
|
|
de70dec44a | ||
|
|
e918700bca | ||
|
|
f37c77267a | ||
|
|
a21dee5923 | ||
|
|
1e9783819b | ||
|
|
4c310bf4b4 | ||
|
|
bcf6a7f630 | ||
|
|
30d4fbb0b0 | ||
|
|
28ca2b7217 | ||
|
|
3acbd96575 | ||
|
|
b9cd75b7a0 | ||
|
|
2669ae9558 | ||
|
|
7657985c1a | ||
|
|
03bbbcfc0c | ||
|
|
a3d561e144 | ||
|
|
068a5ff714 | ||
|
|
16d019c1d0 | ||
|
|
477c675d46 | ||
|
|
0e6cd542cf | ||
|
|
15b5e42533 | ||
|
|
c52bd86dd5 | ||
|
|
cb0b1d8855 | ||
|
|
8a7fa9fa81 | ||
|
|
fcbaf598aa | ||
|
|
ac0d3ae518 | ||
|
|
24e15b5466 | ||
|
|
149b83056e | ||
|
|
ad3db9c004 | ||
|
|
c47444e0f8 | ||
|
|
56dbd52065 | ||
|
|
c6473055f4 | ||
|
|
cae367025f | ||
|
|
fbad694746 | ||
|
|
36d692da84 | ||
|
|
5198559c77 | ||
|
|
50eb62361d | ||
|
|
2be7e0c19c | ||
|
|
69489c2b7e | ||
|
|
859b401c8b | ||
|
|
c7786f03ec | ||
|
|
254aff19b8 | ||
|
|
109782d362 | ||
|
|
c22d64945f | ||
|
|
94089d2b60 | ||
|
|
94a823f805 | ||
|
|
eb43a6cf2a | ||
|
|
94b3727840 | ||
|
|
890e7f9824 | ||
|
|
3fae9246d6 | ||
|
|
5825047847 | ||
|
|
c07454a8ba | ||
|
|
90cc045404 | ||
|
|
8d4012211e | ||
|
|
48c8c3edd1 | ||
|
|
298893352c | ||
|
|
3acb0b1fdb | ||
|
|
fe08674632 | ||
|
|
42f0ac6627 | ||
|
|
90fbe8345a | ||
|
|
50306100e9 | ||
|
|
0177b8fea4 | ||
|
|
5c050f7ed1 | ||
|
|
1e061d7157 | ||
|
|
3d3617e79f | ||
|
|
93b13be1d4 | ||
|
|
88ec15f1d6 | ||
|
|
8f793150b0 | ||
|
|
9d22709017 | ||
|
|
093d400c5b | ||
|
|
2b17ae9255 | ||
|
|
d26f77380d | ||
|
|
e954d2174b | ||
|
|
e697734d64 | ||
|
|
1ffd42904b | ||
|
|
6b3cfb4c91 | ||
|
|
ed7f473556 | ||
|
|
5ed5259912 | ||
|
|
3b650a51c2 | ||
|
|
5fea70d588 | ||
|
|
fba51cc7b9 | ||
|
|
dad4ea583c | ||
|
|
790152770f | ||
|
|
8d123e3375 | ||
|
|
b15bdf3192 | ||
|
|
1600c4356e | ||
|
|
f6c5953241 | ||
|
|
76265e7713 | ||
|
|
8ae614cfa4 | ||
|
|
071ff5863f | ||
|
|
2dbb57dd4e | ||
|
|
71dfbac13c | ||
|
|
a610255529 | ||
|
|
03aab82e1e | ||
|
|
3bb0693fa3 | ||
|
|
838141f597 | ||
|
|
5ccc6ef766 | ||
|
|
c38a9186fd | ||
|
|
67f923b96e | ||
|
|
0b6608eddb | ||
|
|
cabdea1e94 | ||
|
|
ff73608f0a | ||
|
|
f14b8795ea | ||
|
|
ab60c442de | ||
|
|
3eed1e18c8 | ||
|
|
ead951d149 | ||
|
|
1109021018 | ||
|
|
38fc780055 | ||
|
|
188280b448 | ||
|
|
22cf0bba68 | ||
|
|
bd6d22101c | ||
|
|
9125481aed | ||
|
|
c454b5a71d | ||
|
|
1c4fcc8969 | ||
|
|
3706c0cb57 | ||
|
|
0d89754ecf | ||
|
|
2762744a84 | ||
|
|
c825f60eb1 | ||
|
|
67bc763188 | ||
|
|
f96b83750e | ||
|
|
d98b1060d4 | ||
|
|
82fecf95f6 | ||
|
|
bcc028606a | ||
|
|
6084db93d3 | ||
|
|
993a3aa859 | ||
|
|
c02ebecf65 | ||
|
|
0fbf6ea096 | ||
|
|
086503e529 | ||
|
|
565cdde223 | ||
|
|
94e29852d0 | ||
|
|
9a8fd48418 | ||
|
|
62595b2553 | ||
|
|
9a029af8dd | ||
|
|
58e9ec301b | ||
|
|
20ee509d39 | ||
|
|
6cc5ae7717 | ||
|
|
60c99500fa | ||
|
|
30f548096b | ||
|
|
cc265269e6 | ||
|
|
36dcc88a85 | ||
|
|
5628b885e2 | ||
|
|
b2abdd720f | ||
|
|
89b71c2bfb | ||
|
|
488d37b070 | ||
|
|
daa33e3d34 | ||
|
|
9b2a2b2b0a | ||
|
|
fc1e2ebc35 | ||
|
|
e66468b091 | ||
|
|
79aa07ae84 | ||
|
|
65953faec0 | ||
|
|
18f2cb548f | ||
|
|
c109caac2a | ||
|
|
21b0431a1a | ||
|
|
1a2bb197d1 | ||
|
|
cada37b4de | ||
|
|
de0acb77c5 | ||
|
|
df19fcf891 | ||
|
|
ef73d468c0 | ||
|
|
99eba591fa | ||
|
|
8d711bf73a | ||
|
|
f7bce89b7c | ||
|
|
bfe1f4a6a4 | ||
|
|
624edc63ee | ||
|
|
6158c44d82 | ||
|
|
f702100e8a | ||
|
|
72195d3b4c | ||
|
|
7e57824d9f | ||
|
|
4fbe368f2a | ||
|
|
7ce31e3c16 | ||
|
|
fb5f293dc9 | ||
|
|
d508337d7e | ||
|
|
1b45d203f0 | ||
|
|
0831f94750 | ||
|
|
c2400a96e7 | ||
|
|
3602e4202c | ||
|
|
781661ea12 | ||
|
|
64cabbe42f | ||
|
|
fcab1a31fd | ||
|
|
511ac2752a | ||
|
|
1142c28343 | ||
|
|
3ea409078e | ||
|
|
7750ba98c5 | ||
|
|
e362e5c6c0 | ||
|
|
14b1ece960 | ||
|
|
c0245df07b | ||
|
|
c058cc57ef | ||
|
|
93c2d676d0 | ||
|
|
78f33ee479 | ||
|
|
11a9843f41 | ||
|
|
0388d5534f | ||
|
|
4e9d3d90eb | ||
|
|
2b8270f075 | ||
|
|
53a5f44645 | ||
|
|
7c184a03f9 | ||
|
|
8c6be04228 | ||
|
|
8bf0125fa3 | ||
|
|
b21ca106dd | ||
|
|
f071df5dd5 | ||
|
|
ec942243be | ||
|
|
40b4794573 | ||
|
|
3c5ac660bf | ||
|
|
c20b2ba686 | ||
|
|
fa1a6b3c27 | ||
|
|
28f205c1ad | ||
|
|
7a6d3bd9d9 | ||
|
|
2e1722150c | ||
|
|
3dffea1753 | ||
|
|
0c32770cfd | ||
|
|
7b04e0bde2 | ||
|
|
a2c5ed68fc | ||
|
|
580b7b2421 | ||
|
|
43ea1ef054 | ||
|
|
3976750f75 | ||
|
|
cdda6ee32c | ||
|
|
2c707c977b | ||
|
|
355e893644 | ||
|
|
79b1eda1ed | ||
|
|
52185f9860 | ||
|
|
340227016a | ||
|
|
80d9a47927 | ||
|
|
242a1d7b93 | ||
|
|
b7422c0ca7 | ||
|
|
8a6579ef4d | ||
|
|
1779b1d3b5 | ||
|
|
1df5a468b0 | ||
|
|
d43edb311d | ||
|
|
8190aaa92d | ||
|
|
e1766792ac | ||
|
|
60de94f9e4 | ||
|
|
7657496af8 | ||
|
|
4529a40640 | ||
|
|
4c75de08c6 | ||
|
|
4559f17d85 | ||
|
|
13aad459b7 | ||
|
|
b4ad0c4b38 | ||
|
|
3675a2c9dd | ||
|
|
9a61ddac93 | ||
|
|
575c24c93d | ||
|
|
c9b3cc8457 | ||
|
|
7e2973222f | ||
|
|
f5f3a7a6d6 | ||
|
|
fa00520f1e | ||
|
|
4fd6788bbb | ||
|
|
fa64ff9192 | ||
|
|
2eb47300cf | ||
|
|
0bc24c121c | ||
|
|
5e217ff67b | ||
|
|
9dd91bf004 | ||
|
|
560358d9cb | ||
|
|
b7842e1722 | ||
|
|
5d56bb9abe | ||
|
|
443b01e9fd | ||
|
|
609f62d6d5 | ||
|
|
7b89db71a0 | ||
|
|
4d3edd0571 | ||
|
|
9d2e4171c4 | ||
|
|
ab2780424f | ||
|
|
49c146e0eb | ||
|
|
3653cfec59 | ||
|
|
0b8a7fda39 | ||
|
|
88977cca3a | ||
|
|
4147405362 | ||
|
|
dc5602b816 | ||
|
|
b225dd22d9 | ||
|
|
22c92635b2 | ||
|
|
2f0b5f18e1 | ||
|
|
86ae6d2b2b | ||
|
|
a75ee2b5e6 | ||
|
|
b304789e43 | ||
|
|
4c2bf8f8bf | ||
|
|
fc22892a66 | ||
|
|
ba18a93cfe | ||
|
|
63912941f0 | ||
|
|
0b3e6b19b0 | ||
|
|
db180ef76c | ||
|
|
8a7e973bda | ||
|
|
1925cbecab | ||
|
|
585724dbf8 | ||
|
|
1649b07faa | ||
|
|
594e6e3193 | ||
|
|
8ab74b87bd | ||
|
|
c903ae7417 | ||
|
|
fa977b6cc3 | ||
|
|
9e542ab541 | ||
|
|
1d9b85c4b7 | ||
|
|
9e772cb12e | ||
|
|
1f246297e2 | ||
|
|
b16b7d2362 | ||
|
|
25be21fec9 | ||
|
|
3d31b8284c | ||
|
|
fbc01c0ba9 | ||
|
|
d315f4e33e | ||
|
|
0cf83456f7 | ||
|
|
e49e039d73 | ||
|
|
59199d7339 | ||
|
|
7e581ccb9e | ||
|
|
d85905035c | ||
|
|
7434c12b31 | ||
|
|
7edc35009f | ||
|
|
16a5ebe737 | ||
|
|
b735cd3e4e | ||
|
|
b0eb0840d9 | ||
|
|
0e74fd5240 | ||
|
|
e905eefc6c | ||
|
|
c9ae7c8681 | ||
|
|
15dd1f30f1 | ||
|
|
c586245db5 | ||
|
|
5571eee82c | ||
|
|
8903939176 | ||
|
|
c588daa68f | ||
|
|
ca5279d7c7 | ||
|
|
101c72b6dc | ||
|
|
480977ec46 | ||
|
|
829baba037 | ||
|
|
f46c94083e | ||
|
|
c3508c2adc | ||
|
|
4495952625 | ||
|
|
2387513a1f | ||
|
|
23d55aa40c | ||
|
|
3d34340e14 | ||
|
|
32b258c188 | ||
|
|
25e506dcbe | ||
|
|
4867368296 | ||
|
|
2b9a9d5bdd | ||
|
|
d081e5eb18 | ||
|
|
a49c43d1ef | ||
|
|
1e819cf5bd | ||
|
|
b75a6cdab1 | ||
|
|
c03f21b72f | ||
|
|
b98a576fe3 | ||
|
|
d2297c0c52 | ||
|
|
06328dec3d | ||
|
|
d39501bb04 | ||
|
|
d67b3c0ec9 | ||
|
|
3e53659f18 | ||
|
|
8fa27bcf6e | ||
|
|
a764c788ba | ||
|
|
49295eb850 | ||
|
|
41d25cd523 | ||
|
|
5f942cc458 | ||
|
|
b68f1587b2 | ||
|
|
b1a88b01ab | ||
|
|
09a9c1a104 | ||
|
|
a07fc132d2 | ||
|
|
3b77f93c00 | ||
|
|
1bff48b649 | ||
|
|
30be64cbbe | ||
|
|
c75a8cb703 | ||
|
|
9d0703f898 | ||
|
|
91459dbeb3 | ||
|
|
fce160e89d | ||
|
|
0566f8154c | ||
|
|
cebcd566c6 | ||
|
|
5c970ed71f | ||
|
|
f6afafb46c | ||
|
|
f6d4eee763 | ||
|
|
78cd395ca1 | ||
|
|
31e10c5475 | ||
|
|
70be61a81e | ||
|
|
556555b8ae | ||
|
|
06f0315cf4 | ||
|
|
3a4d743eb5 | ||
|
|
fed171fd06 | ||
|
|
15df3d392d | ||
|
|
da878abb90 | ||
|
|
717f8c03d4 | ||
|
|
6f6d99b3e3 | ||
|
|
227596eb90 | ||
|
|
78ce7b659b | ||
|
|
fb7c73bb0f | ||
|
|
81b27b3199 | ||
|
|
54a7ad6b9d | ||
|
|
eba0b18689 | ||
|
|
5c12140c55 | ||
|
|
00a2183153 | ||
|
|
fd10303d0f | ||
|
|
fd158dc0ce | ||
|
|
3787076839 | ||
|
|
93e8a81005 | ||
|
|
8d2a2db770 | ||
|
|
e2c6811761 | ||
|
|
35f4e3312e | ||
|
|
94a91c3baf | ||
|
|
13a41bd9f8 | ||
|
|
51dc7fb1fb | ||
|
|
27b609f050 | ||
|
|
9662e50a43 | ||
|
|
14bcd6e4b4 | ||
|
|
872c88b268 | ||
|
|
70c0e7ca3e | ||
|
|
993c194621 | ||
|
|
33e3183259 | ||
|
|
04f8f925dd | ||
|
|
f8d6aa7906 | ||
|
|
4e0dcbd0bf | ||
|
|
adc14df33a | ||
|
|
c15913c1ab | ||
|
|
d5322667d5 | ||
|
|
08161b47fd | ||
|
|
2dd8682f13 | ||
|
|
f18cded3a7 | ||
|
|
c80e34fdc5 | ||
|
|
74d6683ac8 | ||
|
|
9d37e2c648 | ||
|
|
08df1d9909 | ||
|
|
f42f10ba4b | ||
|
|
669ea28d1b | ||
|
|
bdecc2e1b9 | ||
|
|
a767ed6dbb | ||
|
|
3cfa1d954b | ||
|
|
56b7c108e4 | ||
|
|
9d93d25a84 | ||
|
|
01ad4dc09a | ||
|
|
924a33ccb5 | ||
|
|
fc524fb4db | ||
|
|
b5895e1fd8 | ||
|
|
fc9deccad7 | ||
|
|
1d2809de70 | ||
|
|
11f104afe9 | ||
|
|
c67374a268 | ||
|
|
eae79d3425 | ||
|
|
9445361f28 | ||
|
|
0adc618c13 | ||
|
|
3a81a45b97 | ||
|
|
a5e9d29ba1 | ||
|
|
903308e870 | ||
|
|
13b8b1d3a1 | ||
|
|
1765ec4523 | ||
|
|
244596bdab | ||
|
|
89bf71376f | ||
|
|
a75570a027 | ||
|
|
8c15dc54b2 | ||
|
|
6c5b1ddec6 | ||
|
|
76feb56cdf | ||
|
|
ced79f61ef | ||
|
|
aaa8862e12 | ||
|
|
7a6f54e24d | ||
|
|
4b6e34ca55 | ||
|
|
56c9030406 | ||
|
|
4794b28774 | ||
|
|
4edc1b3c11 | ||
|
|
f858064238 | ||
|
|
62444f8387 | ||
|
|
e4a0cac8b6 | ||
|
|
c562d58ca9 | ||
|
|
6794ef320e | ||
|
|
72671bd448 | ||
|
|
cc9732ea91 | ||
|
|
02eddeff81 | ||
|
|
f0df9f94d4 | ||
|
|
952ea803b2 | ||
|
|
3a2c109ded | ||
|
|
ecc4def40c | ||
|
|
21abce0139 | ||
|
|
d74d0af687 | ||
|
|
a091b07cb6 | ||
|
|
0a8d4dd1f2 | ||
|
|
13f5ad0bcf | ||
|
|
db42ae9f33 | ||
|
|
c80ad5f03b | ||
|
|
d92c3beb3e | ||
|
|
cde4606760 | ||
|
|
7fef58a7b6 | ||
|
|
eb89f9c999 | ||
|
|
96ab3bf5c2 | ||
|
|
ffba802c9c | ||
|
|
182426a4a2 | ||
|
|
f577756433 | ||
|
|
fc5cf1f88a | ||
|
|
518f045d16 | ||
|
|
1640bafa80 | ||
|
|
771770af22 | ||
|
|
8fd38da5c9 | ||
|
|
7173e78214 | ||
|
|
8624511fec | ||
|
|
5f62723cbb | ||
|
|
80d0a30729 | ||
|
|
1a3a77de72 | ||
|
|
f54d0f2f97 | ||
|
|
cb684efc31 | ||
|
|
395586cdda | ||
|
|
31e7c4ebbd | ||
|
|
712bff8459 | ||
|
|
b8496ffe5e | ||
|
|
50a7c2244f | ||
|
|
0eba6cb345 | ||
|
|
7b6fce1481 | ||
|
|
da7569b0a0 | ||
|
|
51ef5c6aad | ||
|
|
6045a18a61 | ||
|
|
89f075fa61 | ||
|
|
806ae6250e | ||
|
|
e045a732fb | ||
|
|
32747a1f6f | ||
|
|
64a80e763b | ||
|
|
dd7a2fcd98 | ||
|
|
de7dd71a94 | ||
|
|
89ef74f95b | ||
|
|
553f064ce0 | ||
|
|
d9b8f69f59 | ||
|
|
b5f6acf63a | ||
|
|
82d28ed08a | ||
|
|
236c65d8ed | ||
|
|
f2d4c755f6 | ||
|
|
80681b78c6 | ||
|
|
76f20ca864 | ||
|
|
d45a811720 | ||
|
|
90b08e3fe5 | ||
|
|
0fba5307fe | ||
|
|
ca30bc86b9 | ||
|
|
76736072bf | ||
|
|
0b9ea9b4c0 | ||
|
|
15e84c81ea | ||
|
|
7b460d25d3 | ||
|
|
010141af47 | ||
|
|
f5c3103dcf | ||
|
|
f1b21fc270 | ||
|
|
a7276c9be8 | ||
|
|
d74b82944e | ||
|
|
7394ff151a | ||
|
|
06550dcbc8 | ||
|
|
c128a45cc2 | ||
|
|
50c5581e03 | ||
|
|
a85c742d7a | ||
|
|
623f3c0279 | ||
|
|
905fe6df19 | ||
|
|
a5b8ba459b | ||
|
|
492d79d42e | ||
|
|
c6bdf1709c | ||
|
|
87a5cd21e8 | ||
|
|
dfc4bff7fd | ||
|
|
d3482098f0 | ||
|
|
f0853adaca | ||
|
|
19a021b36e | ||
|
|
d1a5911b34 | ||
|
|
a37ee5b3d9 | ||
|
|
f349a4c2c5 | ||
|
|
54d5c5e943 | ||
|
|
13e0e382c1 | ||
|
|
43028e0722 | ||
|
|
227e7394b8 | ||
|
|
168970ea34 | ||
|
|
2794f5376e | ||
|
|
7fa59d65db | ||
|
|
3fddf3bd32 | ||
|
|
8088b13d63 | ||
|
|
c92c289a3f | ||
|
|
f29616eefc | ||
|
|
67bbc3d2ac | ||
|
|
1e594574b5 | ||
|
|
6a8f5036b2 | ||
|
|
18e015a3b5 | ||
|
|
a528630af9 | ||
|
|
a2c5b76105 | ||
|
|
31594880ef | ||
|
|
30b2c5d6aa | ||
|
|
78baefcfeb | ||
|
|
83693903ee | ||
|
|
0f0d7db18f | ||
|
|
c9baec210a | ||
|
|
db77728d4c | ||
|
|
80a043568c | ||
|
|
e4a29a163e | ||
|
|
dc95984199 | ||
|
|
ac15951eeb | ||
|
|
d383d271ac | ||
|
|
61e648df0d | ||
|
|
5f00e94a4c | ||
|
|
bfe50a349b | ||
|
|
12148f6a5d | ||
|
|
0e04c1a849 | ||
|
|
cf4a188e40 | ||
|
|
4d9046d0f5 | ||
|
|
555fa98914 | ||
|
|
4e8b8643e6 | ||
|
|
f5a415bd68 | ||
|
|
e8d8dcda4a | ||
|
|
ac13b1fe34 | ||
|
|
51291237c5 | ||
|
|
7fc66c153f | ||
|
|
e835e9d5f2 | ||
|
|
3173658690 | ||
|
|
abb338afc9 | ||
|
|
4b39e6ea66 | ||
|
|
2849271e24 | ||
|
|
23bca4ccb3 | ||
|
|
a2c5007552 | ||
|
|
014079898a | ||
|
|
151cb6b60e | ||
|
|
4ba29cac95 | ||
|
|
7c56886eec | ||
|
|
33c984d558 | ||
|
|
41b4008330 | ||
|
|
f39d8834f1 |
453 changed files with 71677 additions and 27856 deletions
14
.editorconfig
Normal file
14
.editorconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.java]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.gradle]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
12
.git-blame-ignore-revs
Normal file
12
.git-blame-ignore-revs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Commits which should be ignored by git blame
|
||||||
|
|
||||||
|
# You have to instruct git to use this file using either
|
||||||
|
# `git blame --ignore-revs-file .git-blame-ignore-revs`
|
||||||
|
# or update you git config to always include those commits
|
||||||
|
# `git config blame.ignoreRevsFile .git-blame-ignore-revs`
|
||||||
|
|
||||||
|
# Reformat brouter-routing-app using Android Studio
|
||||||
|
54d5c5e9439be2c3df4c95b6fc12d33fdcc9b389
|
||||||
|
|
||||||
|
# Reformat whole codebase using Android Studio
|
||||||
|
c15913c1ab9befd8d583d4a7716d5043d2966f64
|
||||||
98
.github/workflows/docker-publish.yml
vendored
Normal file
98
.github/workflows/docker-publish.yml
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
name: Docker
|
||||||
|
|
||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '21 9 * * *'
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
# Publish semver tags as releases.
|
||||||
|
tags: [ 'v*.*.*' ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Use docker.io for Docker Hub if empty
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
# github.repository as <account>/<repo>
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
# This is used to complete the identity challenge
|
||||||
|
# with sigstore/fulcio when running outside of PRs.
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# Install the cosign tool except on PR
|
||||||
|
# https://github.com/sigstore/cosign-installer
|
||||||
|
- name: Install cosign
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1
|
||||||
|
with:
|
||||||
|
cosign-release: 'v2.1.1'
|
||||||
|
|
||||||
|
# Set up BuildKit Docker container builder to be able to build
|
||||||
|
# multi-platform images and export cache
|
||||||
|
# https://github.com/docker/setup-buildx-action
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||||
|
|
||||||
|
# Login against a Docker registry except on PR
|
||||||
|
# https://github.com/docker/login-action
|
||||||
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Extract metadata (tags, labels) for Docker
|
||||||
|
# https://github.com/docker/metadata-action
|
||||||
|
- name: Extract Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
|
# Build and push Docker image with Buildx (don't push on PR)
|
||||||
|
# https://github.com/docker/build-push-action
|
||||||
|
- name: Build and push Docker image
|
||||||
|
id: build-and-push
|
||||||
|
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
# Sign the resulting Docker image digest except on PRs.
|
||||||
|
# This will only write to the public Rekor transparency log when the Docker
|
||||||
|
# repository is public to avoid leaking data. If you would like to publish
|
||||||
|
# transparency data even for private images, pass --force to cosign below.
|
||||||
|
# https://github.com/sigstore/cosign
|
||||||
|
# - name: Sign the published Docker image
|
||||||
|
# if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
# env:
|
||||||
|
# # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
|
||||||
|
# TAGS: ${{ steps.meta.outputs.tags }}
|
||||||
|
# DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||||
|
# # This step uses the identity token to provision an ephemeral certificate
|
||||||
|
# # against the sigstore community Fulcio instance.
|
||||||
|
# run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
|
||||||
24
.github/workflows/gradle-publish.yml
vendored
24
.github/workflows/gradle-publish.yml
vendored
|
|
@ -4,7 +4,7 @@
|
||||||
name: Gradle Package
|
name: Gradle Package
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
|
|
@ -12,25 +12,35 @@ jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment: BRouter
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '11'
|
java-version: '17'
|
||||||
distribution: 'adopt'
|
distribution: 'temurin'
|
||||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||||
|
|
||||||
|
- name: Setup keystore
|
||||||
|
env:
|
||||||
|
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
|
||||||
|
run: |
|
||||||
|
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
|
env:
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
|
||||||
run: gradle build
|
run: gradle build
|
||||||
|
|
||||||
|
|
||||||
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
|
# The USERNAME and TOKEN need to correspond to the credentials environment variables used in
|
||||||
# the publishing section of your build.gradle
|
# the publishing section of your build.gradle
|
||||||
- name: Publish to GitHub Packages
|
- name: Publish to GitHub Packages
|
||||||
|
|
|
||||||
29
.github/workflows/gradle.yml
vendored
29
.github/workflows/gradle.yml
vendored
|
|
@ -13,14 +13,31 @@ jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
environment: BRouter
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Set up JDK 8
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '8'
|
java-version: '17'
|
||||||
distribution: 'zulu'
|
distribution: 'temurin'
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
- name: Create local.properties
|
||||||
|
run: touch local.properties
|
||||||
|
- name: Setup keystore
|
||||||
|
env:
|
||||||
|
BROUTER_KEYSTORE_BASE64: ${{ secrets.BROUTER_KEYSTORE_BASE64 }}
|
||||||
|
run: |
|
||||||
|
echo $BROUTER_KEYSTORE_BASE64 | base64 -di > ${{ github.workspace }}/brouter.jks
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
|
env:
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_STORE_FILE: ${{ secrets.BROUTER_KEYSTORE_FILE }}
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_KEY_ALIAS: ${{ secrets.BROUTER_KEY_ALIAS }}
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_KEY_PASSWORD: ${{ secrets.BROUTER_KEY_PASSWORD }}
|
||||||
|
ORG_GRADLE_PROJECT_RELEASE_STORE_PASSWORD: ${{ secrets.BROUTER_STORE_PASSWORD }}
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
- name: Upload ZIP
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ZIP
|
||||||
|
path: brouter-server/build/distributions/brouter-*.zip
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
.idea/
|
.idea/
|
||||||
|
build
|
||||||
/local.properties
|
/local.properties
|
||||||
/.idea/caches
|
/.idea/caches
|
||||||
/.idea/gradle.xml
|
/.idea/gradle.xml
|
||||||
|
|
@ -10,7 +11,6 @@
|
||||||
/.idea/navEditor.xml
|
/.idea/navEditor.xml
|
||||||
/.idea/assetWizardSettings.xml
|
/.idea/assetWizardSettings.xml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
|
||||||
/captures
|
/captures
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
|
|
|
||||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
FROM gradle:jdk17-jammy as build
|
||||||
|
|
||||||
|
RUN mkdir /tmp/brouter
|
||||||
|
WORKDIR /tmp/brouter
|
||||||
|
COPY . .
|
||||||
|
RUN ./gradlew clean build
|
||||||
|
|
||||||
|
FROM openjdk:17.0.1-jdk-slim
|
||||||
|
COPY --from=build /tmp/brouter/brouter-server/build/libs/brouter-*-all.jar /brouter.jar
|
||||||
|
COPY --from=build /tmp/brouter/misc/scripts/standalone/server.sh /bin/
|
||||||
|
COPY --from=build /tmp/brouter/misc/* /profiles2
|
||||||
|
|
||||||
|
CMD /bin/server.sh
|
||||||
|
|
||||||
121
README.md
121
README.md
|
|
@ -1,6 +1,63 @@
|
||||||
BRouter
|
BRouter
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
# Come fare
|
||||||
|
|
||||||
|
To build the Docker image run (in the project's top level directory):
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t brouter .
|
||||||
|
```
|
||||||
|
|
||||||
|
Download the segment files
|
||||||
|
```
|
||||||
|
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/segments4/
|
||||||
|
```
|
||||||
|
|
||||||
|
Download the profile files from github https://github.com/gpxstudio/brouter/tree/master/misc/profiles2 using
|
||||||
|
```
|
||||||
|
https://downgit.github.io/
|
||||||
|
```
|
||||||
|
or directly copy misc/profiles2 from git clone folder
|
||||||
|
|
||||||
|
si possono creare anche i profili con le varianti usando
|
||||||
|
```
|
||||||
|
generate_profile_variants.sh
|
||||||
|
```
|
||||||
|
si possono scaricare anche i profili di brouter originale usando
|
||||||
|
```
|
||||||
|
wget -rkpN -np -e robots=off -l1 https://brouter.de/brouter/profiles2/
|
||||||
|
```
|
||||||
|
e metterli nei folder che poi verranno caricati con i volumi condivisi nel docker
|
||||||
|
```
|
||||||
|
mv ./brouter.de/brouter/segments4 /home/nvme/dockerdata/brouter/segments
|
||||||
|
mv mv ./brouter.de/brouter/profiles2 /home/nvme/dockerdata/brouter/profiles
|
||||||
|
```
|
||||||
|
oppure usando lo script
|
||||||
|
```
|
||||||
|
download_segments.sh
|
||||||
|
```
|
||||||
|
far partire il docker
|
||||||
|
```
|
||||||
|
services:
|
||||||
|
brouterserver:
|
||||||
|
container_name: brouter
|
||||||
|
image: brouter
|
||||||
|
ports:
|
||||||
|
- 17777:17777
|
||||||
|
volumes:
|
||||||
|
- /home/nvme/dockerdata/brouter/segments:/segments4
|
||||||
|
- /home/nvme/dockerdata/brouter/profiles:/profiles2
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
## Come si interroga
|
||||||
|
|
||||||
|
```
|
||||||
|
https://brouter.patachina.it/?lonlats=12.08349699,44.25067665|12.09165011,44.24834552&profile=Trekking-dry&format=geojson&alternativeidx=0
|
||||||
|
```
|
||||||
|
|
||||||
|
# Original
|
||||||
|
|
||||||
BRouter is a configurable OSM offline router with elevation awareness, Java +
|
BRouter is a configurable OSM offline router with elevation awareness, Java +
|
||||||
Android. Designed to be multi-modal with a particular emphasis on bicycle
|
Android. Designed to be multi-modal with a particular emphasis on bicycle
|
||||||
and energy-based car routing.
|
and energy-based car routing.
|
||||||
|
|
@ -15,7 +72,7 @@ You can install the BRouter app on your Android device from
|
||||||
Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
|
Store](https://play.google.com/store/apps/details?id=btools.routingapp). You
|
||||||
can also [build BRouter](#build-and-install) yourself. You can find detailed
|
can also [build BRouter](#build-and-install) yourself. You can find detailed
|
||||||
documentation of the BRouter Android app in
|
documentation of the BRouter Android app in
|
||||||
[`misc/readmes/readme.txt`](misc/readmes/readme.txt).
|
[`docs/users/android_quickstart.md`](docs/users/android_quickstart.md).
|
||||||
|
|
||||||
<a href="https://f-droid.org/packages/btools.routingapp" target="_blank">
|
<a href="https://f-droid.org/packages/btools.routingapp" target="_blank">
|
||||||
<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
|
<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>
|
||||||
|
|
@ -39,7 +96,7 @@ Alternatively, you can also use BRouter as the offline routing engine for
|
||||||
[OSMAnd](https://osmand.net/) on your Android device.
|
[OSMAnd](https://osmand.net/) on your Android device.
|
||||||
|
|
||||||
A full documentation on how to set this up is available at
|
A full documentation on how to set this up is available at
|
||||||
[`misc/readmes/osmand/README.md`](misc/readmes/osmand/README.md).
|
[`docs/users/osmand.md`](docs/users/osmand.md).
|
||||||
|
|
||||||
|
|
||||||
## BRouter on Windows/Linux/Mac OS
|
## BRouter on Windows/Linux/Mac OS
|
||||||
|
|
@ -98,7 +155,7 @@ Segments files from the whole planet are generated weekly at
|
||||||
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
|
[https://brouter.de/brouter/segments4/](http://brouter.de/brouter/segments4/).
|
||||||
|
|
||||||
You can download one or more segments files, covering the area of the planet
|
You can download one or more segments files, covering the area of the planet
|
||||||
your want to route, into the `misc/segments4` directory.
|
you want to route, into the `misc/segments4` directory.
|
||||||
|
|
||||||
#### Generate your own segments files
|
#### Generate your own segments files
|
||||||
|
|
||||||
|
|
@ -106,7 +163,7 @@ You can also generate the segments files you need directly from a planet dump
|
||||||
of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
|
of OpenStreetMap data (or a [GeoFabrik extract](https://download.geofabrik.de/)).
|
||||||
|
|
||||||
More documentation of this is available in the
|
More documentation of this is available in the
|
||||||
[`misc/readmes/mapcreation.md`](misc/readmes/mapcreation.md) file.
|
[`docs/developers/build_segments.md`](docs/developers/build_segments.md) file.
|
||||||
|
|
||||||
|
|
||||||
### (Optional) Generate profile variants
|
### (Optional) Generate profile variants
|
||||||
|
|
@ -120,7 +177,7 @@ to help you quickly generate variants based on the default profiles, to create
|
||||||
a default set of profiles covering most of the basic use cases.
|
a default set of profiles covering most of the basic use cases.
|
||||||
|
|
||||||
Have a look at the
|
Have a look at the
|
||||||
[`misc/readmes/profile_developers_guide.txt`](misc/readmes/profile_developers_guide.txt)
|
[`docs/developers/profile_developers_guide.md`](docs/developers/profile_developers_guide.md)
|
||||||
for an in-depth guide on profiles edition and customization.
|
for an in-depth guide on profiles edition and customization.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -137,10 +194,62 @@ The API endpoints exposed by this HTTP server are documented in the
|
||||||
[`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java)
|
[`brouter-server/src/main/java/btools/server/request/ServerHandler.java`](brouter-server/src/main/java/btools/server/request/ServerHandler.java)
|
||||||
file.
|
file.
|
||||||
|
|
||||||
|
The server emits log data for each routing request on stdout. For each routing
|
||||||
|
request a line with the following eight fields is printed. The fields are
|
||||||
|
separated by whitespace.
|
||||||
|
|
||||||
|
- timestamp, in ISO8601 format, e.g. `2024-05-14T21:11:26.499+02:00`
|
||||||
|
- current server session count (integer number 1-999) or "new" when a new
|
||||||
|
IP address is detected
|
||||||
|
- IP address (IPv4 or IPv6), prefixed by `ip=`
|
||||||
|
- duration of routing request in ms, prefixed by `ms=`
|
||||||
|
- divider `->`
|
||||||
|
- HTTP request method
|
||||||
|
- HTTP request URL
|
||||||
|
- HTTP request version
|
||||||
|
|
||||||
|
Example log output:
|
||||||
|
|
||||||
|
```
|
||||||
|
2024-05-14T21:11:26.499+02:00 new ip=127.0.0.1 ms=189 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1
|
||||||
|
2024-05-14T21:11:33.229+02:00 1 ip=127.0.0.1 ms=65 -> GET /brouter?lonlats=13.377485,52.516247%7C13.351221,52.515004&profile=trekking&alternativeidx=0&format=geojson HTTP/1.1
|
||||||
|
```
|
||||||
|
|
||||||
|
## BRouter with Docker
|
||||||
|
|
||||||
|
To build the Docker image run (in the project's top level directory):
|
||||||
|
|
||||||
|
```
|
||||||
|
docker build -t brouter .
|
||||||
|
```
|
||||||
|
|
||||||
|
Download the segment files as described in the previous chapter. The folder containing the
|
||||||
|
segment files can be mounted into the container. Run BRouter as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm \
|
||||||
|
-v ./misc/scripts/segments4:/segments4 \
|
||||||
|
-p 17777:17777 \
|
||||||
|
--name brouter \
|
||||||
|
brouter
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start brouter with a set of default routing profiles. It will be accessible on port 17777.
|
||||||
|
|
||||||
|
If you want to provide your own routing profiles, you can also mount the folder containing the custom profiles:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run --rm \
|
||||||
|
-v ./misc/scripts/segments4:/segments4 \
|
||||||
|
-v /path/to/custom/profiles:/profiles2 \
|
||||||
|
-p 17777:17777 \
|
||||||
|
--name brouter \
|
||||||
|
brouter
|
||||||
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
More documentation is available in the [`misc/readmes`](misc/readmes) folder.
|
More documentation is available in the [`docs`](docs) folder.
|
||||||
|
|
||||||
|
|
||||||
## Related Projects
|
## Related Projects
|
||||||
|
|
|
||||||
1
brouter-codec/.gitignore
vendored
1
brouter-codec/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/build/
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id 'brouter.library-conventions'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
testImplementation 'junit:junit:4.13.1'
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<manifest package="btools.codec" />
|
|
||||||
|
|
@ -5,8 +5,7 @@ import btools.util.BitCoderContext;
|
||||||
/**
|
/**
|
||||||
* Container for some re-usable databuffers for the decoder
|
* Container for some re-usable databuffers for the decoder
|
||||||
*/
|
*/
|
||||||
public final class DataBuffers
|
public final class DataBuffers {
|
||||||
{
|
|
||||||
public byte[] iobuffer;
|
public byte[] iobuffer;
|
||||||
public byte[] tagbuf1 = new byte[256];
|
public byte[] tagbuf1 = new byte[256];
|
||||||
public BitCoderContext bctx1 = new BitCoderContext(tagbuf1);
|
public BitCoderContext bctx1 = new BitCoderContext(tagbuf1);
|
||||||
|
|
@ -17,8 +16,7 @@ public final class DataBuffers
|
||||||
public int[] alon = new int[2048];
|
public int[] alon = new int[2048];
|
||||||
public int[] alat = new int[2048];
|
public int[] alat = new int[2048];
|
||||||
|
|
||||||
public DataBuffers()
|
public DataBuffers() {
|
||||||
{
|
|
||||||
this(new byte[65636]);
|
this(new byte[65636]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,8 +24,7 @@ public final class DataBuffers
|
||||||
* construct a set of databuffers except
|
* construct a set of databuffers except
|
||||||
* for 'iobuffer', where the given array is used
|
* for 'iobuffer', where the given array is used
|
||||||
*/
|
*/
|
||||||
public DataBuffers( byte[] iobuffer )
|
public DataBuffers(byte[] iobuffer) {
|
||||||
{
|
|
||||||
this.iobuffer = iobuffer;
|
this.iobuffer = iobuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,14 @@ package btools.codec;
|
||||||
/**
|
/**
|
||||||
* Special integer fifo suitable for 3-pass encoding
|
* Special integer fifo suitable for 3-pass encoding
|
||||||
*/
|
*/
|
||||||
public class IntegerFifo3Pass
|
public class IntegerFifo3Pass {
|
||||||
{
|
|
||||||
private int[] a;
|
private int[] a;
|
||||||
private int size;
|
private int size;
|
||||||
private int pos;
|
private int pos;
|
||||||
|
|
||||||
private int pass;
|
private int pass;
|
||||||
|
|
||||||
public IntegerFifo3Pass( int capacity )
|
public IntegerFifo3Pass(int capacity) {
|
||||||
{
|
|
||||||
a = capacity < 4 ? new int[4] : new int[capacity];
|
a = capacity < 4 ? new int[4] : new int[capacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,8 +18,7 @@ public class IntegerFifo3Pass
|
||||||
* Starts a new encoding pass and resets the reading pointer
|
* Starts a new encoding pass and resets the reading pointer
|
||||||
* from the stats collected in pass2 and writes that to the given context
|
* from the stats collected in pass2 and writes that to the given context
|
||||||
*/
|
*/
|
||||||
public void init()
|
public void init() {
|
||||||
{
|
|
||||||
pass++;
|
pass++;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -29,12 +26,9 @@ public class IntegerFifo3Pass
|
||||||
/**
|
/**
|
||||||
* writes to the fifo in pass2
|
* writes to the fifo in pass2
|
||||||
*/
|
*/
|
||||||
public void add( int value )
|
public void add(int value) {
|
||||||
{
|
if (pass == 2) {
|
||||||
if ( pass == 2 )
|
if (size == a.length) {
|
||||||
{
|
|
||||||
if ( size == a.length )
|
|
||||||
{
|
|
||||||
int[] aa = new int[2 * size];
|
int[] aa = new int[2 * size];
|
||||||
System.arraycopy(a, 0, aa, 0, size);
|
System.arraycopy(a, 0, aa, 0, size);
|
||||||
a = aa;
|
a = aa;
|
||||||
|
|
@ -46,15 +40,12 @@ public class IntegerFifo3Pass
|
||||||
/**
|
/**
|
||||||
* reads from the fifo in pass3 (in pass1/2 returns just 1)
|
* reads from the fifo in pass3 (in pass1/2 returns just 1)
|
||||||
*/
|
*/
|
||||||
public int getNext()
|
public int getNext() {
|
||||||
{
|
|
||||||
return pass == 3 ? get(pos++) : 1;
|
return pass == 3 ? get(pos++) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int get( int idx )
|
private int get(int idx) {
|
||||||
{
|
if (idx >= size) {
|
||||||
if ( idx >= size )
|
|
||||||
{
|
|
||||||
throw new IndexOutOfBoundsException("list size=" + size + " idx=" + idx);
|
throw new IndexOutOfBoundsException("list size=" + size + " idx=" + idx);
|
||||||
}
|
}
|
||||||
return a[idx];
|
return a[idx];
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ package btools.codec;
|
||||||
/**
|
/**
|
||||||
* Simple container for a list of lists of integers
|
* Simple container for a list of lists of integers
|
||||||
*/
|
*/
|
||||||
public class LinkedListContainer
|
public class LinkedListContainer {
|
||||||
{
|
|
||||||
private int[] ia; // prev, data, prev, data, ...
|
private int[] ia; // prev, data, prev, data, ...
|
||||||
private int size;
|
private int size;
|
||||||
private int[] startpointer; // 0=void, odd=head-data-cell
|
private int[] startpointer; // 0=void, odd=head-data-cell
|
||||||
|
|
@ -12,15 +11,14 @@ public class LinkedListContainer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a container for the given number of lists
|
* Construct a container for the given number of lists
|
||||||
*
|
* <p>
|
||||||
* If no default-buffer is given, an int[nlists*4] is constructed,
|
* If no default-buffer is given, an int[nlists*4] is constructed,
|
||||||
* able to hold 2 entries per list on average
|
* able to hold 2 entries per list on average
|
||||||
*
|
*
|
||||||
* @param nlists the number of lists
|
* @param nlists the number of lists
|
||||||
* @param defaultbuffer an optional data array for re-use (gets replaced if too small)
|
* @param defaultbuffer an optional data array for re-use (gets replaced if too small)
|
||||||
*/
|
*/
|
||||||
public LinkedListContainer( int nlists, int[] defaultbuffer )
|
public LinkedListContainer(int nlists, int[] defaultbuffer) {
|
||||||
{
|
|
||||||
ia = defaultbuffer == null ? new int[nlists * 4] : defaultbuffer;
|
ia = defaultbuffer == null ? new int[nlists * 4] : defaultbuffer;
|
||||||
startpointer = new int[nlists];
|
startpointer = new int[nlists];
|
||||||
}
|
}
|
||||||
|
|
@ -31,10 +29,8 @@ public class LinkedListContainer
|
||||||
* @param listNr the list to add the data to
|
* @param listNr the list to add the data to
|
||||||
* @param data the data value
|
* @param data the data value
|
||||||
*/
|
*/
|
||||||
public void addDataElement( int listNr, int data )
|
public void addDataElement(int listNr, int data) {
|
||||||
{
|
if (size + 2 > ia.length) {
|
||||||
if ( size + 2 > ia.length )
|
|
||||||
{
|
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
ia[size++] = startpointer[listNr];
|
ia[size++] = startpointer[listNr];
|
||||||
|
|
@ -48,12 +44,10 @@ public class LinkedListContainer
|
||||||
* @param listNr the list to initialize
|
* @param listNr the list to initialize
|
||||||
* @return the number of entries in that list
|
* @return the number of entries in that list
|
||||||
*/
|
*/
|
||||||
public int initList( int listNr )
|
public int initList(int listNr) {
|
||||||
{
|
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
int lp = listpointer = startpointer[listNr];
|
int lp = listpointer = startpointer[listNr];
|
||||||
while( lp != 0 )
|
while (lp != 0) {
|
||||||
{
|
|
||||||
lp = ia[lp - 1];
|
lp = ia[lp - 1];
|
||||||
cnt++;
|
cnt++;
|
||||||
}
|
}
|
||||||
|
|
@ -67,10 +61,8 @@ public class LinkedListContainer
|
||||||
* @return the data element
|
* @return the data element
|
||||||
* @throws IllegalArgumentException if no more element
|
* @throws IllegalArgumentException if no more element
|
||||||
*/
|
*/
|
||||||
public int getDataElement()
|
public int getDataElement() {
|
||||||
{
|
if (listpointer == 0) {
|
||||||
if ( listpointer == 0 )
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("no more element!");
|
throw new IllegalArgumentException("no more element!");
|
||||||
}
|
}
|
||||||
int data = ia[listpointer];
|
int data = ia[listpointer];
|
||||||
|
|
@ -78,8 +70,7 @@ public class LinkedListContainer
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resize()
|
private void resize() {
|
||||||
{
|
|
||||||
int[] ia2 = new int[2 * ia.length];
|
int[] ia2 = new int[2 * ia.length];
|
||||||
System.arraycopy(ia, 0, ia2, 0, ia.length);
|
System.arraycopy(ia, 0, ia2, 0, ia.length);
|
||||||
ia = ia2;
|
ia = ia2;
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,20 @@ import btools.util.ByteDataWriter;
|
||||||
/**
|
/**
|
||||||
* a micro-cache is a data cache for an area of some square kilometers or some
|
* a micro-cache is a data cache for an area of some square kilometers or some
|
||||||
* hundreds or thousands nodes
|
* hundreds or thousands nodes
|
||||||
*
|
* <p>
|
||||||
* This is the basic io-unit: always a full microcache is loaded from the
|
* This is the basic io-unit: always a full microcache is loaded from the
|
||||||
* data-file if a node is requested at a position not yet covered by the caches
|
* data-file if a node is requested at a position not yet covered by the caches
|
||||||
* already loaded
|
* already loaded
|
||||||
*
|
* <p>
|
||||||
* The nodes are represented in a compact way (typical 20-50 bytes per node),
|
* The nodes are represented in a compact way (typical 20-50 bytes per node),
|
||||||
* but in a way that they do not depend on each other, and garbage collection is
|
* but in a way that they do not depend on each other, and garbage collection is
|
||||||
* supported to remove the nodes already consumed from the cache.
|
* supported to remove the nodes already consumed from the cache.
|
||||||
*
|
* <p>
|
||||||
* The cache-internal data representation is different from that in the
|
* The cache-internal data representation is different from that in the
|
||||||
* data-files, where a cache is encoded as a whole, allowing more
|
* data-files, where a cache is encoded as a whole, allowing more
|
||||||
* redundancy-removal for a more compact encoding
|
* redundancy-removal for a more compact encoding
|
||||||
*/
|
*/
|
||||||
public class MicroCache extends ByteDataWriter
|
public class MicroCache extends ByteDataWriter {
|
||||||
{
|
|
||||||
protected int[] faid;
|
protected int[] faid;
|
||||||
protected int[] fapos;
|
protected int[] fapos;
|
||||||
protected int size = 0;
|
protected int size = 0;
|
||||||
|
|
@ -35,25 +34,21 @@ public class MicroCache extends ByteDataWriter
|
||||||
|
|
||||||
public static boolean debug = false;
|
public static boolean debug = false;
|
||||||
|
|
||||||
protected MicroCache( byte[] ab )
|
protected MicroCache(byte[] ab) {
|
||||||
{
|
|
||||||
super(ab);
|
super(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final static MicroCache emptyNonVirgin = new MicroCache(null);
|
public final static MicroCache emptyNonVirgin = new MicroCache(null);
|
||||||
|
|
||||||
static
|
static {
|
||||||
{
|
|
||||||
emptyNonVirgin.virgin = false;
|
emptyNonVirgin.virgin = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MicroCache emptyCache()
|
public static MicroCache emptyCache() {
|
||||||
{
|
|
||||||
return new MicroCache(null); // TODO: singleton?
|
return new MicroCache(null); // TODO: singleton?
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void init( int size )
|
protected void init(int size) {
|
||||||
{
|
|
||||||
this.size = size;
|
this.size = size;
|
||||||
delcount = 0;
|
delcount = 0;
|
||||||
delbytes = 0;
|
delbytes = 0;
|
||||||
|
|
@ -62,35 +57,31 @@ public class MicroCache extends ByteDataWriter
|
||||||
p2size >>= 1;
|
p2size >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void finishNode( long id )
|
public final void finishNode(long id) {
|
||||||
{
|
|
||||||
fapos[size] = aboffset;
|
fapos[size] = aboffset;
|
||||||
faid[size] = shrinkId(id);
|
faid[size] = shrinkId(id);
|
||||||
size++;
|
size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void discardNode()
|
public final void discardNode() {
|
||||||
{
|
|
||||||
aboffset = startPos(size);
|
aboffset = startPos(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getSize()
|
public final int getSize() {
|
||||||
{
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getDataSize()
|
public final int getDataSize() {
|
||||||
{
|
|
||||||
return ab == null ? 0 : ab.length;
|
return ab == null ? 0 : ab.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id
|
* Set the internal reader (aboffset, aboffsetEnd) to the body data for the given id
|
||||||
*
|
* <p>
|
||||||
* If a node is not found in an empty cache, this is usually an edge-effect
|
* If a node is not found in an empty cache, this is usually an edge-effect
|
||||||
* (data-file does not exist or neighboured data-files of differnt age),
|
* (data-file does not exist or neighboured data-files of differnt age),
|
||||||
* but is can as well be a symptom of a node-identity breaking bug.
|
* but is can as well be a symptom of a node-identity breaking bug.
|
||||||
*
|
* <p>
|
||||||
* Current implementation always returns false for not-found, however, for
|
* Current implementation always returns false for not-found, however, for
|
||||||
* regression testing, at least for the case that is most likely a bug
|
* regression testing, at least for the case that is most likely a bug
|
||||||
* (node found but marked as deleted = ready for garbage collection
|
* (node found but marked as deleted = ready for garbage collection
|
||||||
|
|
@ -98,10 +89,8 @@ public class MicroCache extends ByteDataWriter
|
||||||
*
|
*
|
||||||
* @return true if id was found
|
* @return true if id was found
|
||||||
*/
|
*/
|
||||||
public final boolean getAndClear( long id64 )
|
public final boolean getAndClear(long id64) {
|
||||||
{
|
if (size == 0) {
|
||||||
if ( size == 0 )
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int id = shrinkId(id64);
|
int id = shrinkId(id64);
|
||||||
|
|
@ -109,27 +98,22 @@ public class MicroCache extends ByteDataWriter
|
||||||
int offset = p2size;
|
int offset = p2size;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
|
|
||||||
while (offset > 0)
|
while (offset > 0) {
|
||||||
{
|
|
||||||
int nn = n + offset;
|
int nn = n + offset;
|
||||||
if ( nn < size && a[nn] <= id )
|
if (nn < size && a[nn] <= id) {
|
||||||
{
|
|
||||||
n = nn;
|
n = nn;
|
||||||
}
|
}
|
||||||
offset >>= 1;
|
offset >>= 1;
|
||||||
}
|
}
|
||||||
if ( a[n] == id )
|
if (a[n] == id) {
|
||||||
{
|
if ((fapos[n] & 0x80000000) == 0) {
|
||||||
if ( ( fapos[n] & 0x80000000 ) == 0 )
|
|
||||||
{
|
|
||||||
aboffset = startPos(n);
|
aboffset = startPos(n);
|
||||||
aboffsetEnd = fapos[n];
|
aboffsetEnd = fapos[n];
|
||||||
fapos[n] |= 0x80000000; // mark deleted
|
fapos[n] |= 0x80000000; // mark deleted
|
||||||
delbytes += aboffsetEnd - aboffset;
|
delbytes += aboffsetEnd - aboffset;
|
||||||
delcount++;
|
delcount++;
|
||||||
return true;
|
return true;
|
||||||
}
|
} else // .. marked as deleted
|
||||||
else // .. marked as deleted
|
|
||||||
{
|
{
|
||||||
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
|
// throw new RuntimeException( "MicroCache: node already consumed: id=" + id );
|
||||||
}
|
}
|
||||||
|
|
@ -137,39 +121,31 @@ public class MicroCache extends ByteDataWriter
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int startPos( int n )
|
protected final int startPos(int n) {
|
||||||
{
|
|
||||||
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
|
return n > 0 ? fapos[n - 1] & 0x7fffffff : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int collect( int threshold )
|
public final int collect(int threshold) {
|
||||||
{
|
if (delcount <= threshold) {
|
||||||
if ( delcount <= threshold )
|
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
virgin = false;
|
virgin = false;
|
||||||
|
|
||||||
int nsize = size - delcount;
|
int nsize = size - delcount;
|
||||||
if ( nsize == 0 )
|
if (nsize == 0) {
|
||||||
{
|
|
||||||
faid = null;
|
faid = null;
|
||||||
fapos = null;
|
fapos = null;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
int[] nfaid = new int[nsize];
|
int[] nfaid = new int[nsize];
|
||||||
int[] nfapos = new int[nsize];
|
int[] nfapos = new int[nsize];
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|
||||||
byte[] nab = new byte[ab.length - delbytes];
|
byte[] nab = new byte[ab.length - delbytes];
|
||||||
int nab_off = 0;
|
int nab_off = 0;
|
||||||
for ( int i = 0; i < size; i++ )
|
for (int i = 0; i < size; i++) {
|
||||||
{
|
|
||||||
int pos = fapos[i];
|
int pos = fapos[i];
|
||||||
if ( ( pos & 0x80000000 ) == 0 )
|
if ((pos & 0x80000000) == 0) {
|
||||||
{
|
|
||||||
int start = startPos(i);
|
int start = startPos(i);
|
||||||
int end = fapos[i];
|
int end = fapos[i];
|
||||||
int len = end - start;
|
int len = end - start;
|
||||||
|
|
@ -189,13 +165,11 @@ public class MicroCache extends ByteDataWriter
|
||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void unGhost()
|
public final void unGhost() {
|
||||||
{
|
|
||||||
ghost = false;
|
ghost = false;
|
||||||
delcount = 0;
|
delcount = 0;
|
||||||
delbytes = 0;
|
delbytes = 0;
|
||||||
for ( int i = 0; i < size; i++ )
|
for (int i = 0; i < size; i++) {
|
||||||
{
|
|
||||||
fapos[i] &= 0x7fffffff; // clear deleted flags
|
fapos[i] &= 0x7fffffff; // clear deleted flags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -203,8 +177,7 @@ public class MicroCache extends ByteDataWriter
|
||||||
/**
|
/**
|
||||||
* @return the 64-bit global id for the given cache-position
|
* @return the 64-bit global id for the given cache-position
|
||||||
*/
|
*/
|
||||||
public final long getIdForIndex( int i )
|
public final long getIdForIndex(int i) {
|
||||||
{
|
|
||||||
int id32 = faid[i];
|
int id32 = faid[i];
|
||||||
return expandId(id32);
|
return expandId(id32);
|
||||||
}
|
}
|
||||||
|
|
@ -214,8 +187,7 @@ public class MicroCache extends ByteDataWriter
|
||||||
*
|
*
|
||||||
* @see #shrinkId
|
* @see #shrinkId
|
||||||
*/
|
*/
|
||||||
public long expandId( int id32 )
|
public long expandId(int id32) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("expandId for empty cache");
|
throw new IllegalArgumentException("expandId for empty cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,28 +196,24 @@ public class MicroCache extends ByteDataWriter
|
||||||
*
|
*
|
||||||
* @see #expandId
|
* @see #expandId
|
||||||
*/
|
*/
|
||||||
public int shrinkId( long id64 )
|
public int shrinkId(long id64) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("shrinkId for empty cache");
|
throw new IllegalArgumentException("shrinkId for empty cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the given lon/lat position is internal for that micro-cache
|
* @return true if the given lon/lat position is internal for that micro-cache
|
||||||
*/
|
*/
|
||||||
public boolean isInternal( int ilon, int ilat )
|
public boolean isInternal(int ilon, int ilat) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("isInternal for empty cache");
|
throw new IllegalArgumentException("isInternal for empty cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (stasticially) encode the micro-cache into the format used in the datafiles
|
* (stasticially) encode the micro-cache into the format used in the datafiles
|
||||||
*
|
*
|
||||||
* @param buffer
|
* @param buffer byte array to encode into (considered big enough)
|
||||||
* byte array to encode into (considered big enough)
|
|
||||||
* @return the size of the encoded data
|
* @return the size of the encoded data
|
||||||
*/
|
*/
|
||||||
public int encodeMicroCache( byte[] buffer )
|
public int encodeMicroCache(byte[] buffer) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("encodeMicroCache for empty cache");
|
throw new IllegalArgumentException("encodeMicroCache for empty cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,11 +222,9 @@ public class MicroCache extends ByteDataWriter
|
||||||
*
|
*
|
||||||
* @return null if equals, else a diff-report
|
* @return null if equals, else a diff-report
|
||||||
*/
|
*/
|
||||||
public String compareWith( MicroCache mc )
|
public String compareWith(MicroCache mc) {
|
||||||
{
|
|
||||||
String msg = _compareWith(mc);
|
String msg = _compareWith(mc);
|
||||||
if ( msg != null )
|
if (msg != null) {
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder(msg);
|
StringBuilder sb = new StringBuilder(msg);
|
||||||
sb.append("\nencode cache:\n").append(summary());
|
sb.append("\nencode cache:\n").append(summary());
|
||||||
sb.append("\ndecode cache:\n").append(mc.summary());
|
sb.append("\ndecode cache:\n").append(mc.summary());
|
||||||
|
|
@ -267,96 +233,75 @@ public class MicroCache extends ByteDataWriter
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String summary()
|
private String summary() {
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder("size=" + size + " aboffset=" + aboffset);
|
StringBuilder sb = new StringBuilder("size=" + size + " aboffset=" + aboffset);
|
||||||
for ( int i = 0; i < size; i++ )
|
for (int i = 0; i < size; i++) {
|
||||||
{
|
|
||||||
sb.append("\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i]);
|
sb.append("\nidx=" + i + " faid=" + faid[i] + " fapos=" + fapos[i]);
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String _compareWith( MicroCache mc )
|
private String _compareWith(MicroCache mc) {
|
||||||
{
|
if (size != mc.size) {
|
||||||
if ( size != mc.size )
|
return "size mismatch: " + size + "->" + mc.size;
|
||||||
{
|
|
||||||
return "size missmatch: " + size + "->" + mc.size;
|
|
||||||
}
|
}
|
||||||
for ( int i = 0; i < size; i++ )
|
for (int i = 0; i < size; i++) {
|
||||||
{
|
if (faid[i] != mc.faid[i]) {
|
||||||
if ( faid[i] != mc.faid[i] )
|
return "faid mismatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
|
||||||
{
|
|
||||||
return "faid missmatch at index " + i + ":" + faid[i] + "->" + mc.faid[i];
|
|
||||||
}
|
}
|
||||||
int start = i > 0 ? fapos[i - 1] : 0;
|
int start = i > 0 ? fapos[i - 1] : 0;
|
||||||
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
|
int end = fapos[i] < mc.fapos[i] ? fapos[i] : mc.fapos[i];
|
||||||
int len = end - start;
|
int len = end - start;
|
||||||
for ( int offset = 0; offset < len; offset++ )
|
for (int offset = 0; offset < len; offset++) {
|
||||||
{
|
if (mc.ab.length <= start + offset) {
|
||||||
if ( mc.ab.length <= start + offset )
|
|
||||||
{
|
|
||||||
return "data buffer too small";
|
return "data buffer too small";
|
||||||
}
|
}
|
||||||
if ( ab[start + offset] != mc.ab[start + offset] )
|
if (ab[start + offset] != mc.ab[start + offset]) {
|
||||||
{
|
return "data mismatch at index " + i + " offset=" + offset;
|
||||||
return "data missmatch at index " + i + " offset=" + offset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( fapos[i] != mc.fapos[i] )
|
if (fapos[i] != mc.fapos[i]) {
|
||||||
{
|
return "fapos mismatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
|
||||||
return "fapos missmatch at index " + i + ":" + fapos[i] + "->" + mc.fapos[i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( aboffset != mc.aboffset )
|
if (aboffset != mc.aboffset) {
|
||||||
{
|
return "datasize mismatch: " + aboffset + "->" + mc.aboffset;
|
||||||
return "datasize missmatch: " + aboffset + "->" + mc.aboffset;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void calcDelta( MicroCache mc1, MicroCache mc2 )
|
public void calcDelta(MicroCache mc1, MicroCache mc2) {
|
||||||
{
|
|
||||||
int idx1 = 0;
|
int idx1 = 0;
|
||||||
int idx2 = 0;
|
int idx2 = 0;
|
||||||
|
|
||||||
while( idx1 < mc1.size || idx2 < mc2.size )
|
while (idx1 < mc1.size || idx2 < mc2.size) {
|
||||||
{
|
|
||||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||||
int id;
|
int id;
|
||||||
if ( id1 >= id2 )
|
if (id1 >= id2) {
|
||||||
{
|
|
||||||
id = id2;
|
id = id2;
|
||||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||||
int len2 = mc2.fapos[idx2++] - start2;
|
int len2 = mc2.fapos[idx2++] - start2;
|
||||||
|
|
||||||
if ( id1 == id2 )
|
if (id1 == id2) {
|
||||||
{
|
|
||||||
// id exists in both caches, compare data
|
// id exists in both caches, compare data
|
||||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||||
int len1 = mc1.fapos[idx1++] - start1;
|
int len1 = mc1.fapos[idx1++] - start1;
|
||||||
if ( len1 == len2 )
|
if (len1 == len2) {
|
||||||
{
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while( i<len1 )
|
while (i < len1) {
|
||||||
{
|
if (mc1.ab[start1 + i] != mc2.ab[start2 + i]) {
|
||||||
if ( mc1.ab[start1+i] != mc2.ab[start2+i] )
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if ( i == len1 )
|
if (i == len1) {
|
||||||
{
|
|
||||||
continue; // same data -> do nothing
|
continue; // same data -> do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write(mc2.ab, start2, len2);
|
write(mc2.ab, start2, len2);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
idx1++;
|
idx1++;
|
||||||
id = id1; // deleted node
|
id = id1; // deleted node
|
||||||
}
|
}
|
||||||
|
|
@ -366,31 +311,25 @@ public class MicroCache extends ByteDataWriter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDelta( MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes )
|
public void addDelta(MicroCache mc1, MicroCache mc2, boolean keepEmptyNodes) {
|
||||||
{
|
|
||||||
int idx1 = 0;
|
int idx1 = 0;
|
||||||
int idx2 = 0;
|
int idx2 = 0;
|
||||||
|
|
||||||
while( idx1 < mc1.size || idx2 < mc2.size )
|
while (idx1 < mc1.size || idx2 < mc2.size) {
|
||||||
{
|
|
||||||
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
int id1 = idx1 < mc1.size ? mc1.faid[idx1] : Integer.MAX_VALUE;
|
||||||
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
int id2 = idx2 < mc2.size ? mc2.faid[idx2] : Integer.MAX_VALUE;
|
||||||
if ( id1 >= id2 ) // data from diff file wins
|
if (id1 >= id2) { // data from diff file wins
|
||||||
{
|
|
||||||
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
int start2 = idx2 > 0 ? mc2.fapos[idx2 - 1] : 0;
|
||||||
int len2 = mc2.fapos[idx2++] - start2;
|
int len2 = mc2.fapos[idx2++] - start2;
|
||||||
if ( keepEmptyNodes || len2 > 0 )
|
if (keepEmptyNodes || len2 > 0) {
|
||||||
{
|
|
||||||
write(mc2.ab, start2, len2);
|
write(mc2.ab, start2, len2);
|
||||||
fapos[size] = aboffset;
|
fapos[size] = aboffset;
|
||||||
faid[size++] = id2;
|
faid[size++] = id2;
|
||||||
}
|
}
|
||||||
if ( id1 == id2 ) // // id exists in both caches
|
if (id1 == id2) { // // id exists in both caches
|
||||||
{
|
|
||||||
idx1++;
|
idx1++;
|
||||||
}
|
}
|
||||||
}
|
} else // use data from base file
|
||||||
else // use data from base file
|
|
||||||
{
|
{
|
||||||
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
int start1 = idx1 > 0 ? mc1.fapos[idx1 - 1] : 0;
|
||||||
int len1 = mc1.fapos[idx1++] - start1;
|
int len1 = mc1.fapos[idx1++] - start1;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package btools.codec;
|
package btools.codec;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import btools.util.ByteDataReader;
|
import btools.util.ByteDataReader;
|
||||||
import btools.util.IByteArrayUnifier;
|
import btools.util.IByteArrayUnifier;
|
||||||
|
|
@ -9,14 +10,12 @@ import btools.util.IByteArrayUnifier;
|
||||||
* MicroCache2 is the new format that uses statistical encoding and
|
* MicroCache2 is the new format that uses statistical encoding and
|
||||||
* is able to do access filtering and waypoint matching during encoding
|
* is able to do access filtering and waypoint matching during encoding
|
||||||
*/
|
*/
|
||||||
public final class MicroCache2 extends MicroCache
|
public final class MicroCache2 extends MicroCache {
|
||||||
{
|
|
||||||
private int lonBase;
|
private int lonBase;
|
||||||
private int latBase;
|
private int latBase;
|
||||||
private int cellsize;
|
private int cellsize;
|
||||||
|
|
||||||
public MicroCache2( int size, byte[] databuffer, int lonIdx, int latIdx, int divisor ) throws Exception
|
public MicroCache2(int size, byte[] databuffer, int lonIdx, int latIdx, int divisor) {
|
||||||
{
|
|
||||||
super(databuffer); // sets ab=databuffer, aboffset=0
|
super(databuffer); // sets ab=databuffer, aboffset=0
|
||||||
|
|
||||||
faid = new int[size];
|
faid = new int[size];
|
||||||
|
|
@ -27,15 +26,13 @@ public final class MicroCache2 extends MicroCache
|
||||||
latBase = latIdx * cellsize;
|
latBase = latIdx * cellsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] readUnified( int len, IByteArrayUnifier u )
|
public byte[] readUnified(int len, IByteArrayUnifier u) {
|
||||||
{
|
|
||||||
byte[] b = u.unify(ab, aboffset, len);
|
byte[] b = u.unify(ab, aboffset, len);
|
||||||
aboffset += len;
|
aboffset += len;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MicroCache2( StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher ) throws Exception
|
public MicroCache2(StatCoderContext bc, DataBuffers dataBuffers, int lonIdx, int latIdx, int divisor, TagValueValidator wayValidator, WaypointMatcher waypointMatcher) {
|
||||||
{
|
|
||||||
super(null);
|
super(null);
|
||||||
cellsize = 1000000 / divisor;
|
cellsize = 1000000 / divisor;
|
||||||
lonBase = lonIdx * cellsize;
|
lonBase = lonIdx * cellsize;
|
||||||
|
|
@ -54,16 +51,15 @@ public final class MicroCache2 extends MicroCache
|
||||||
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
|
fapos = size > dataBuffers.ibuf3.length ? new int[size] : dataBuffers.ibuf3;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
|
int[] alon = size > dataBuffers.alon.length ? new int[size] : dataBuffers.alon;
|
||||||
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
|
int[] alat = size > dataBuffers.alat.length ? new int[size] : dataBuffers.alat;
|
||||||
|
|
||||||
if ( debug ) System.out.println( "*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx );
|
if (debug)
|
||||||
|
System.out.println("*** decoding cache of size=" + size + " for lonIdx=" + lonIdx + " latIdx=" + latIdx);
|
||||||
|
|
||||||
bc.decodeSortedArray(faid, 0, size, 29, 0);
|
bc.decodeSortedArray(faid, 0, size, 29, 0);
|
||||||
|
|
||||||
for( int n = 0; n<size; n++ )
|
for (int n = 0; n < size; n++) {
|
||||||
{
|
|
||||||
long id64 = expandId(faid[n]);
|
long id64 = expandId(faid[n]);
|
||||||
alon[n] = (int) (id64 >> 32);
|
alon[n] = (int) (id64 >> 32);
|
||||||
alat[n] = (int) (id64 & 0xffffffff);
|
alat[n] = (int) (id64 & 0xffffffff);
|
||||||
|
|
@ -80,30 +76,24 @@ public final class MicroCache2 extends MicroCache
|
||||||
LinkedListContainer reverseLinks = new LinkedListContainer(size, dataBuffers.ibuf1);
|
LinkedListContainer reverseLinks = new LinkedListContainer(size, dataBuffers.ibuf1);
|
||||||
|
|
||||||
int selev = 0;
|
int selev = 0;
|
||||||
for( int n=0; n<size; n++ ) // loop over nodes
|
for (int n = 0; n < size; n++) { // loop over nodes
|
||||||
{
|
|
||||||
int ilon = alon[n];
|
int ilon = alon[n];
|
||||||
int ilat = alat[n];
|
int ilat = alat[n];
|
||||||
|
|
||||||
// future escapes (turn restrictions?)
|
// future escapes (turn restrictions?)
|
||||||
short trExceptions = 0;
|
short trExceptions = 0;
|
||||||
int featureId = bc.decodeVarBits();
|
int featureId = bc.decodeVarBits();
|
||||||
if ( featureId == 13 )
|
if (featureId == 13) {
|
||||||
{
|
|
||||||
fapos[n] = aboffset;
|
fapos[n] = aboffset;
|
||||||
validBits[n >> 5] |= 1 << n; // mark dummy-node valid
|
validBits[n >> 5] |= 1 << n; // mark dummy-node valid
|
||||||
continue; // empty node escape (delta files only)
|
continue; // empty node escape (delta files only)
|
||||||
}
|
}
|
||||||
while( featureId != 0 )
|
while (featureId != 0) {
|
||||||
{
|
|
||||||
int bitsize = bc.decodeNoisyNumber(5);
|
int bitsize = bc.decodeNoisyNumber(5);
|
||||||
|
|
||||||
if ( featureId == 2 ) // exceptions to turn-restriction
|
if (featureId == 2) { // exceptions to turn-restriction
|
||||||
{
|
|
||||||
trExceptions = (short) bc.decodeBounded(1023);
|
trExceptions = (short) bc.decodeBounded(1023);
|
||||||
}
|
} else if (featureId == 1) { // turn-restriction
|
||||||
else if ( featureId == 1 ) // turn-restriction
|
|
||||||
{
|
|
||||||
writeBoolean(true);
|
writeBoolean(true);
|
||||||
writeShort(trExceptions); // exceptions from previous feature
|
writeShort(trExceptions); // exceptions from previous feature
|
||||||
trExceptions = 0;
|
trExceptions = 0;
|
||||||
|
|
@ -113,9 +103,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
writeInt(ilat + bc.decodeNoisyDiff(10)); // fromLat
|
writeInt(ilat + bc.decodeNoisyDiff(10)); // fromLat
|
||||||
writeInt(ilon + bc.decodeNoisyDiff(10)); // toLon
|
writeInt(ilon + bc.decodeNoisyDiff(10)); // toLon
|
||||||
writeInt(ilat + bc.decodeNoisyDiff(10)); // toLat
|
writeInt(ilat + bc.decodeNoisyDiff(10)); // toLat
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = 0; i < bitsize; i++) bc.decodeBit(); // unknown feature, just skip
|
for (int i = 0; i < bitsize; i++) bc.decodeBit(); // unknown feature, just skip
|
||||||
}
|
}
|
||||||
featureId = bc.decodeVarBits();
|
featureId = bc.decodeVarBits();
|
||||||
|
|
@ -128,9 +116,9 @@ public final class MicroCache2 extends MicroCache
|
||||||
writeVarBytes(nodeTags == null ? null : nodeTags.data);
|
writeVarBytes(nodeTags == null ? null : nodeTags.data);
|
||||||
|
|
||||||
int links = bc.decodeNoisyNumber(1);
|
int links = bc.decodeNoisyNumber(1);
|
||||||
if ( debug ) System.out.println( "*** decoding node " + ilon + "/" + ilat + " with links=" + links );
|
if (debug)
|
||||||
for( int li=0; li<links; li++ )
|
System.out.println("*** decoding node " + ilon + "/" + ilat + " with links=" + links);
|
||||||
{
|
for (int li = 0; li < links; li++) {
|
||||||
int sizeoffset = 0;
|
int sizeoffset = 0;
|
||||||
int nodeIdx = n + nodeIdxDiff.decodeSignedValue();
|
int nodeIdx = n + nodeIdxDiff.decodeSignedValue();
|
||||||
|
|
||||||
|
|
@ -138,24 +126,21 @@ public final class MicroCache2 extends MicroCache
|
||||||
int dlat_remaining;
|
int dlat_remaining;
|
||||||
|
|
||||||
boolean isReverse = false;
|
boolean isReverse = false;
|
||||||
if ( nodeIdx != n ) // internal (forward-) link
|
if (nodeIdx != n) { // internal (forward-) link
|
||||||
{
|
|
||||||
dlon_remaining = alon[nodeIdx] - ilon;
|
dlon_remaining = alon[nodeIdx] - ilon;
|
||||||
dlat_remaining = alat[nodeIdx] - ilat;
|
dlat_remaining = alat[nodeIdx] - ilat;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
isReverse = bc.decodeBit();
|
isReverse = bc.decodeBit();
|
||||||
dlon_remaining = extLonDiff.decodeSignedValue();
|
dlon_remaining = extLonDiff.decodeSignedValue();
|
||||||
dlat_remaining = extLatDiff.decodeSignedValue();
|
dlat_remaining = extLatDiff.decodeSignedValue();
|
||||||
}
|
}
|
||||||
if ( debug ) System.out.println( "*** decoding link to " + (ilon+dlon_remaining) + "/" + (ilat+dlat_remaining) + " extern=" + (nodeIdx == n) );
|
if (debug)
|
||||||
|
System.out.println("*** decoding link to " + (ilon + dlon_remaining) + "/" + (ilat + dlat_remaining) + " extern=" + (nodeIdx == n));
|
||||||
|
|
||||||
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
|
TagValueWrapper wayTags = wayTagCoder.decodeTagValueSet();
|
||||||
|
|
||||||
boolean linkValid = wayTags != null || wayValidator == null;
|
boolean linkValid = wayTags != null || wayValidator == null;
|
||||||
if ( linkValid )
|
if (linkValid) {
|
||||||
{
|
|
||||||
int startPointer = aboffset;
|
int startPointer = aboffset;
|
||||||
sizeoffset = writeSizePlaceHolder();
|
sizeoffset = writeSizePlaceHolder();
|
||||||
|
|
||||||
|
|
@ -163,8 +148,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
writeVarLengthSigned(dlat_remaining);
|
writeVarLengthSigned(dlat_remaining);
|
||||||
|
|
||||||
validBits[n >> 5] |= 1 << n; // mark source-node valid
|
validBits[n >> 5] |= 1 << n; // mark source-node valid
|
||||||
if ( nodeIdx != n ) // valid internal (forward-) link
|
if (nodeIdx != n) { // valid internal (forward-) link
|
||||||
{
|
|
||||||
reverseLinks.addDataElement(nodeIdx, n); // register reverse link
|
reverseLinks.addDataElement(nodeIdx, n); // register reverse link
|
||||||
finaldatasize += 1 + aboffset - startPointer; // reserve place for reverse
|
finaldatasize += 1 + aboffset - startPointer; // reserve place for reverse
|
||||||
validBits[nodeIdx >> 5] |= 1 << nodeIdx; // mark target-node valid
|
validBits[nodeIdx >> 5] |= 1 << nodeIdx; // mark target-node valid
|
||||||
|
|
@ -172,15 +156,12 @@ public final class MicroCache2 extends MicroCache
|
||||||
writeModeAndDesc(isReverse, wayTags == null ? null : wayTags.data);
|
writeModeAndDesc(isReverse, wayTags == null ? null : wayTags.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !isReverse ) // write geometry for forward links only
|
if (!isReverse) { // write geometry for forward links only
|
||||||
{
|
|
||||||
WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher;
|
WaypointMatcher matcher = wayTags == null || wayTags.accessType < 2 ? null : waypointMatcher;
|
||||||
int ilontarget = ilon + dlon_remaining;
|
int ilontarget = ilon + dlon_remaining;
|
||||||
int ilattarget = ilat + dlat_remaining;
|
int ilattarget = ilat + dlat_remaining;
|
||||||
if ( matcher != null )
|
if (matcher != null) {
|
||||||
{
|
if (!matcher.start(ilon, ilat, ilontarget, ilattarget)) {
|
||||||
if ( !matcher.start( ilon, ilat, ilontarget, ilattarget ) )
|
|
||||||
{
|
|
||||||
matcher = null;
|
matcher = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,27 +169,25 @@ public final class MicroCache2 extends MicroCache
|
||||||
int transcount = bc.decodeVarBits();
|
int transcount = bc.decodeVarBits();
|
||||||
if (debug) System.out.println("*** decoding geometry with count=" + transcount);
|
if (debug) System.out.println("*** decoding geometry with count=" + transcount);
|
||||||
int count = transcount + 1;
|
int count = transcount + 1;
|
||||||
for( int i=0; i<transcount; i++ )
|
for (int i = 0; i < transcount; i++) {
|
||||||
{
|
|
||||||
int dlon = bc.decodePredictedValue(dlon_remaining / count);
|
int dlon = bc.decodePredictedValue(dlon_remaining / count);
|
||||||
int dlat = bc.decodePredictedValue(dlat_remaining / count);
|
int dlat = bc.decodePredictedValue(dlat_remaining / count);
|
||||||
dlon_remaining -= dlon;
|
dlon_remaining -= dlon;
|
||||||
dlat_remaining -= dlat;
|
dlat_remaining -= dlat;
|
||||||
count--;
|
count--;
|
||||||
int elediff = transEleDiff.decodeSignedValue();
|
int elediff = transEleDiff.decodeSignedValue();
|
||||||
if ( wayTags != null )
|
if (wayTags != null) {
|
||||||
{
|
|
||||||
writeVarLengthSigned(dlon);
|
writeVarLengthSigned(dlon);
|
||||||
writeVarLengthSigned(dlat);
|
writeVarLengthSigned(dlat);
|
||||||
writeVarLengthSigned(elediff);
|
writeVarLengthSigned(elediff);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( matcher != null ) matcher.transferNode( ilontarget - dlon_remaining, ilattarget - dlat_remaining );
|
if (matcher != null)
|
||||||
|
matcher.transferNode(ilontarget - dlon_remaining, ilattarget - dlat_remaining);
|
||||||
}
|
}
|
||||||
if (matcher != null) matcher.end();
|
if (matcher != null) matcher.end();
|
||||||
}
|
}
|
||||||
if ( linkValid )
|
if (linkValid) {
|
||||||
{
|
|
||||||
injectSize(sizeoffset);
|
injectSize(sizeoffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -218,11 +197,9 @@ public final class MicroCache2 extends MicroCache
|
||||||
// calculate final data size
|
// calculate final data size
|
||||||
int finalsize = 0;
|
int finalsize = 0;
|
||||||
int startpos = 0;
|
int startpos = 0;
|
||||||
for( int i=0; i<size; i++ )
|
for (int i = 0; i < size; i++) {
|
||||||
{
|
|
||||||
int endpos = fapos[i];
|
int endpos = fapos[i];
|
||||||
if ( ( validBits[ i >> 5 ] & (1 << i ) ) != 0 )
|
if ((validBits[i >> 5] & (1 << i)) != 0) {
|
||||||
{
|
|
||||||
finaldatasize += endpos - startpos;
|
finaldatasize += endpos - startpos;
|
||||||
finalsize++;
|
finalsize++;
|
||||||
}
|
}
|
||||||
|
|
@ -240,11 +217,9 @@ public final class MicroCache2 extends MicroCache
|
||||||
size = 0;
|
size = 0;
|
||||||
|
|
||||||
startpos = 0;
|
startpos = 0;
|
||||||
for ( int n = 0; n < sizeOld; n++ )
|
for (int n = 0; n < sizeOld; n++) {
|
||||||
{
|
|
||||||
int endpos = faposOld[n];
|
int endpos = faposOld[n];
|
||||||
if ( ( validBits[ n >> 5 ] & (1 << n ) ) != 0 )
|
if ((validBits[n >> 5] & (1 << n)) != 0) {
|
||||||
{
|
|
||||||
int len = endpos - startpos;
|
int len = endpos - startpos;
|
||||||
System.arraycopy(abOld, startpos, ab, aboffset, len);
|
System.arraycopy(abOld, startpos, ab, aboffset, len);
|
||||||
if (debug)
|
if (debug)
|
||||||
|
|
@ -255,8 +230,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
if (debug)
|
if (debug)
|
||||||
System.out.println("*** appending " + cnt + " reverse links for node " + n);
|
System.out.println("*** appending " + cnt + " reverse links for node " + n);
|
||||||
|
|
||||||
for ( int ri = 0; ri < cnt; ri++ )
|
for (int ri = 0; ri < cnt; ri++) {
|
||||||
{
|
|
||||||
int nodeIdx = reverseLinks.getDataElement();
|
int nodeIdx = reverseLinks.getDataElement();
|
||||||
int sizeoffset = writeSizePlaceHolder();
|
int sizeoffset = writeSizePlaceHolder();
|
||||||
writeVarLengthSigned(alon[nodeIdx] - alon[n]);
|
writeVarLengthSigned(alon[nodeIdx] - alon[n]);
|
||||||
|
|
@ -274,13 +248,11 @@ public final class MicroCache2 extends MicroCache
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long expandId( int id32 )
|
public long expandId(int id32) {
|
||||||
{
|
|
||||||
int dlon = 0;
|
int dlon = 0;
|
||||||
int dlat = 0;
|
int dlat = 0;
|
||||||
|
|
||||||
for( int bm = 1; bm < 0x8000; bm <<= 1 )
|
for (int bm = 1; bm < 0x8000; bm <<= 1) {
|
||||||
{
|
|
||||||
if ((id32 & 1) != 0) dlon |= bm;
|
if ((id32 & 1) != 0) dlon |= bm;
|
||||||
if ((id32 & 2) != 0) dlat |= bm;
|
if ((id32 & 2) != 0) dlat |= bm;
|
||||||
id32 >>= 2;
|
id32 >>= 2;
|
||||||
|
|
@ -293,16 +265,14 @@ public final class MicroCache2 extends MicroCache
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int shrinkId( long id64 )
|
public int shrinkId(long id64) {
|
||||||
{
|
|
||||||
int lon32 = (int) (id64 >> 32);
|
int lon32 = (int) (id64 >> 32);
|
||||||
int lat32 = (int) (id64 & 0xffffffff);
|
int lat32 = (int) (id64 & 0xffffffff);
|
||||||
int dlon = lon32 - lonBase;
|
int dlon = lon32 - lonBase;
|
||||||
int dlat = lat32 - latBase;
|
int dlat = lat32 - latBase;
|
||||||
int id32 = 0;
|
int id32 = 0;
|
||||||
|
|
||||||
for( int bm = 0x4000; bm > 0; bm >>= 1 )
|
for (int bm = 0x4000; bm > 0; bm >>= 1) {
|
||||||
{
|
|
||||||
id32 <<= 2;
|
id32 <<= 2;
|
||||||
if ((dlon & bm) != 0) id32 |= 1;
|
if ((dlon & bm) != 0) id32 |= 1;
|
||||||
if ((dlat & bm) != 0) id32 |= 2;
|
if ((dlat & bm) != 0) id32 |= 2;
|
||||||
|
|
@ -311,19 +281,16 @@ public final class MicroCache2 extends MicroCache
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInternal( int ilon, int ilat )
|
public boolean isInternal(int ilon, int ilat) {
|
||||||
{
|
|
||||||
return ilon >= lonBase && ilon < lonBase + cellsize
|
return ilon >= lonBase && ilon < lonBase + cellsize
|
||||||
&& ilat >= latBase && ilat < latBase + cellsize;
|
&& ilat >= latBase && ilat < latBase + cellsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int encodeMicroCache( byte[] buffer )
|
public int encodeMicroCache(byte[] buffer) {
|
||||||
{
|
Map<Long, Integer> idMap = new HashMap<>();
|
||||||
HashMap<Long,Integer> idMap = new HashMap<Long,Integer>();
|
for (int n = 0; n < size; n++) { // loop over nodes
|
||||||
for( int n=0; n<size; n++ ) // loop over nodes
|
idMap.put(expandId(faid[n]), n);
|
||||||
{
|
|
||||||
idMap.put( Long.valueOf( expandId( faid[n] ) ), Integer.valueOf( n ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass(256);
|
IntegerFifo3Pass linkCounts = new IntegerFifo3Pass(256);
|
||||||
|
|
@ -340,8 +307,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
|
|
||||||
int netdatasize = 0;
|
int netdatasize = 0;
|
||||||
|
|
||||||
for(int pass=1;; pass++) // 3 passes: counters, stat-collection, encoding
|
for (int pass = 1; ; pass++) { // 3 passes: counters, stat-collection, encoding
|
||||||
{
|
|
||||||
boolean dostats = pass == 3;
|
boolean dostats = pass == 3;
|
||||||
boolean dodebug = debug && pass == 3;
|
boolean dodebug = debug && pass == 3;
|
||||||
|
|
||||||
|
|
@ -372,28 +338,25 @@ public final class MicroCache2 extends MicroCache
|
||||||
if (dodebug) System.out.println("*** encoding cache of size=" + size);
|
if (dodebug) System.out.println("*** encoding cache of size=" + size);
|
||||||
int lastSelev = 0;
|
int lastSelev = 0;
|
||||||
|
|
||||||
for( int n=0; n<size; n++ ) // loop over nodes
|
for (int n = 0; n < size; n++) { // loop over nodes
|
||||||
{
|
|
||||||
aboffset = startPos(n);
|
aboffset = startPos(n);
|
||||||
aboffsetEnd = fapos[n];
|
aboffsetEnd = fapos[n];
|
||||||
if ( dodebug ) System.out.println( "*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd );
|
if (dodebug)
|
||||||
|
System.out.println("*** encoding node " + n + " from " + aboffset + " to " + aboffsetEnd);
|
||||||
|
|
||||||
long id64 = expandId(faid[n]);
|
long id64 = expandId(faid[n]);
|
||||||
int ilon = (int) (id64 >> 32);
|
int ilon = (int) (id64 >> 32);
|
||||||
int ilat = (int) (id64 & 0xffffffff);
|
int ilat = (int) (id64 & 0xffffffff);
|
||||||
|
|
||||||
if ( aboffset == aboffsetEnd )
|
if (aboffset == aboffsetEnd) {
|
||||||
{
|
|
||||||
bc.encodeVarBits(13); // empty node escape (delta files only)
|
bc.encodeVarBits(13); // empty node escape (delta files only)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write turn restrictions
|
// write turn restrictions
|
||||||
while( readBoolean() )
|
while (readBoolean()) {
|
||||||
{
|
|
||||||
short exceptions = readShort(); // except bikes, psv, ...
|
short exceptions = readShort(); // except bikes, psv, ...
|
||||||
if ( exceptions != 0 )
|
if (exceptions != 0) {
|
||||||
{
|
|
||||||
bc.encodeVarBits(2); // 2 = tr exceptions
|
bc.encodeVarBits(2); // 2 = tr exceptions
|
||||||
bc.encodeNoisyNumber(10, 5); // bit-count
|
bc.encodeNoisyNumber(10, 5); // bit-count
|
||||||
bc.encodeBounded(1023, exceptions & 1023);
|
bc.encodeBounded(1023, exceptions & 1023);
|
||||||
|
|
@ -424,8 +387,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
if (dostats) bc.assignBits("link-counts");
|
if (dostats) bc.assignBits("link-counts");
|
||||||
|
|
||||||
nlinks = 0;
|
nlinks = 0;
|
||||||
while( hasMoreData() ) // loop over links
|
while (hasMoreData()) { // loop over links
|
||||||
{
|
|
||||||
// read link data
|
// read link data
|
||||||
int startPointer = aboffset;
|
int startPointer = aboffset;
|
||||||
int endPointer = getEndPointer();
|
int endPointer = getEndPointer();
|
||||||
|
|
@ -437,35 +399,32 @@ public final class MicroCache2 extends MicroCache
|
||||||
boolean isReverse = (sizecode & 1) != 0;
|
boolean isReverse = (sizecode & 1) != 0;
|
||||||
int descSize = sizecode >> 1;
|
int descSize = sizecode >> 1;
|
||||||
byte[] description = null;
|
byte[] description = null;
|
||||||
if ( descSize > 0 )
|
if (descSize > 0) {
|
||||||
{
|
|
||||||
description = new byte[descSize];
|
description = new byte[descSize];
|
||||||
readFully(description);
|
readFully(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
long link64 = ((long) ilonlink) << 32 | ilatlink;
|
long link64 = ((long) ilonlink) << 32 | ilatlink;
|
||||||
Integer idx = idMap.get( Long.valueOf( link64 ) );
|
Integer idx = idMap.get(link64);
|
||||||
boolean isInternal = idx != null;
|
boolean isInternal = idx != null;
|
||||||
|
|
||||||
if ( isReverse && isInternal )
|
if (isReverse && isInternal) {
|
||||||
{
|
if (dodebug)
|
||||||
if ( dodebug ) System.out.println( "*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal );
|
System.out.println("*** NOT encoding link reverse=" + isReverse + " internal=" + isInternal);
|
||||||
netdatasize -= aboffset - startPointer;
|
netdatasize -= aboffset - startPointer;
|
||||||
continue; // do not encode internal reverse links
|
continue; // do not encode internal reverse links
|
||||||
}
|
}
|
||||||
if ( dodebug ) System.out.println( "*** encoding link reverse=" + isReverse + " internal=" + isInternal );
|
if (dodebug)
|
||||||
|
System.out.println("*** encoding link reverse=" + isReverse + " internal=" + isInternal);
|
||||||
nlinks++;
|
nlinks++;
|
||||||
|
|
||||||
if ( isInternal )
|
if (isInternal) {
|
||||||
{
|
int nodeIdx = idx;
|
||||||
int nodeIdx = idx.intValue();
|
|
||||||
if (dodebug) System.out.println("*** target nodeIdx=" + nodeIdx);
|
if (dodebug) System.out.println("*** target nodeIdx=" + nodeIdx);
|
||||||
if (nodeIdx == n) throw new RuntimeException("ups: self ref?");
|
if (nodeIdx == n) throw new RuntimeException("ups: self ref?");
|
||||||
nodeIdxDiff.encodeSignedValue(nodeIdx - n);
|
nodeIdxDiff.encodeSignedValue(nodeIdx - n);
|
||||||
if (dostats) bc.assignBits("nodeIdx");
|
if (dostats) bc.assignBits("nodeIdx");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
nodeIdxDiff.encodeSignedValue(0);
|
nodeIdxDiff.encodeSignedValue(0);
|
||||||
bc.encodeBit(isReverse);
|
bc.encodeBit(isReverse);
|
||||||
extLonDiff.encodeSignedValue(ilonlink - ilon);
|
extLonDiff.encodeSignedValue(ilonlink - ilon);
|
||||||
|
|
@ -475,8 +434,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
wayTagCoder.encodeTagValueSet(description);
|
wayTagCoder.encodeTagValueSet(description);
|
||||||
if (dostats) bc.assignBits("wayDescIdx");
|
if (dostats) bc.assignBits("wayDescIdx");
|
||||||
|
|
||||||
if ( !isReverse )
|
if (!isReverse) {
|
||||||
{
|
|
||||||
byte[] geometry = readDataUntil(endPointer);
|
byte[] geometry = readDataUntil(endPointer);
|
||||||
// write transition nodes
|
// write transition nodes
|
||||||
int count = transCounts.getNext();
|
int count = transCounts.getNext();
|
||||||
|
|
@ -484,14 +442,12 @@ public final class MicroCache2 extends MicroCache
|
||||||
bc.encodeVarBits(count++);
|
bc.encodeVarBits(count++);
|
||||||
if (dostats) bc.assignBits("transcount");
|
if (dostats) bc.assignBits("transcount");
|
||||||
int transcount = 0;
|
int transcount = 0;
|
||||||
if ( geometry != null )
|
if (geometry != null) {
|
||||||
{
|
|
||||||
int dlon_remaining = ilonlink - ilon;
|
int dlon_remaining = ilonlink - ilon;
|
||||||
int dlat_remaining = ilatlink - ilat;
|
int dlat_remaining = ilatlink - ilat;
|
||||||
|
|
||||||
ByteDataReader r = new ByteDataReader(geometry);
|
ByteDataReader r = new ByteDataReader(geometry);
|
||||||
while ( r.hasMoreData() )
|
while (r.hasMoreData()) {
|
||||||
{
|
|
||||||
transcount++;
|
transcount++;
|
||||||
|
|
||||||
int dlon = r.readVarLengthSigned();
|
int dlon = r.readVarLengthSigned();
|
||||||
|
|
@ -511,8 +467,7 @@ public final class MicroCache2 extends MicroCache
|
||||||
}
|
}
|
||||||
linkCounts.add(nlinks);
|
linkCounts.add(nlinks);
|
||||||
}
|
}
|
||||||
if ( pass == 3 )
|
if (pass == 3) {
|
||||||
{
|
|
||||||
return bc.closeAndGetEncodedLength();
|
return bc.closeAndGetEncodedLength();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,11 @@ package btools.codec;
|
||||||
* Encoder/Decoder for signed integers that automatically detects the typical
|
* Encoder/Decoder for signed integers that automatically detects the typical
|
||||||
* range of these numbers to determine a noisy-bit count as a very simple
|
* range of these numbers to determine a noisy-bit count as a very simple
|
||||||
* dictionary
|
* dictionary
|
||||||
*
|
* <p>
|
||||||
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
||||||
* but doesn't do anything at pass1
|
* but doesn't do anything at pass1
|
||||||
*/
|
*/
|
||||||
public final class NoisyDiffCoder
|
public final class NoisyDiffCoder {
|
||||||
{
|
|
||||||
private int tot;
|
private int tot;
|
||||||
private int[] freqs;
|
private int[] freqs;
|
||||||
private int noisybits;
|
private int noisybits;
|
||||||
|
|
@ -19,8 +18,7 @@ public final class NoisyDiffCoder
|
||||||
/**
|
/**
|
||||||
* Create a decoder and read the noisy-bit count from the gibe context
|
* Create a decoder and read the noisy-bit count from the gibe context
|
||||||
*/
|
*/
|
||||||
public NoisyDiffCoder( StatCoderContext bc )
|
public NoisyDiffCoder(StatCoderContext bc) {
|
||||||
{
|
|
||||||
noisybits = bc.decodeVarBits();
|
noisybits = bc.decodeVarBits();
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
@ -28,21 +26,16 @@ public final class NoisyDiffCoder
|
||||||
/**
|
/**
|
||||||
* Create an encoder for 3-pass-encoding
|
* Create an encoder for 3-pass-encoding
|
||||||
*/
|
*/
|
||||||
public NoisyDiffCoder()
|
public NoisyDiffCoder() {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* encodes a signed int (pass3 only, stats collection in pass2)
|
* encodes a signed int (pass3 only, stats collection in pass2)
|
||||||
*/
|
*/
|
||||||
public void encodeSignedValue( int value )
|
public void encodeSignedValue(int value) {
|
||||||
{
|
if (pass == 3) {
|
||||||
if ( pass == 3 )
|
|
||||||
{
|
|
||||||
bc.encodeNoisyDiff(value, noisybits);
|
bc.encodeNoisyDiff(value, noisybits);
|
||||||
}
|
} else if (pass == 2) {
|
||||||
else if ( pass == 2 )
|
|
||||||
{
|
|
||||||
count(value < 0 ? -value : value);
|
count(value < 0 ? -value : value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -50,8 +43,7 @@ public final class NoisyDiffCoder
|
||||||
/**
|
/**
|
||||||
* decodes a signed int
|
* decodes a signed int
|
||||||
*/
|
*/
|
||||||
public int decodeSignedValue()
|
public int decodeSignedValue() {
|
||||||
{
|
|
||||||
return bc.decodeNoisyDiff(noisybits);
|
return bc.decodeNoisyDiff(noisybits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,13 +51,10 @@ public final class NoisyDiffCoder
|
||||||
* Starts a new encoding pass and (in pass3) calculates the noisy-bit count
|
* Starts a new encoding pass and (in pass3) calculates the noisy-bit count
|
||||||
* from the stats collected in pass2 and writes that to the given context
|
* from the stats collected in pass2 and writes that to the given context
|
||||||
*/
|
*/
|
||||||
public void encodeDictionary( StatCoderContext bc )
|
public void encodeDictionary(StatCoderContext bc) {
|
||||||
{
|
if (++pass == 3) {
|
||||||
if ( ++pass == 3 )
|
|
||||||
{
|
|
||||||
// how many noisy bits?
|
// how many noisy bits?
|
||||||
for ( noisybits = 0; noisybits < 14 && tot > 0; noisybits++ )
|
for (noisybits = 0; noisybits < 14 && tot > 0; noisybits++) {
|
||||||
{
|
|
||||||
if (freqs[noisybits] < (tot >> 1))
|
if (freqs[noisybits] < (tot >> 1))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -74,13 +63,11 @@ public final class NoisyDiffCoder
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void count( int value )
|
private void count(int value) {
|
||||||
{
|
|
||||||
if (freqs == null)
|
if (freqs == null)
|
||||||
freqs = new int[14];
|
freqs = new int[14];
|
||||||
int bm = 1;
|
int bm = 1;
|
||||||
for ( int i = 0; i < 14; i++ )
|
for (int i = 0; i < 14; i++) {
|
||||||
{
|
|
||||||
if (value < bm)
|
if (value < bm)
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,23 @@
|
||||||
package btools.codec;
|
package btools.codec;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import btools.util.BitCoderContext;
|
import btools.util.BitCoderContext;
|
||||||
|
|
||||||
public final class StatCoderContext extends BitCoderContext
|
public final class StatCoderContext extends BitCoderContext {
|
||||||
{
|
private static Map<String, long[]> statsPerName;
|
||||||
private static TreeMap<String, long[]> statsPerName;
|
|
||||||
private long lastbitpos = 0;
|
private long lastbitpos = 0;
|
||||||
|
|
||||||
|
|
||||||
private static final int[] noisy_bits = new int[1024];
|
private static final int[] noisy_bits = new int[1024];
|
||||||
|
|
||||||
static
|
static {
|
||||||
{
|
|
||||||
// noisybits lookup
|
// noisybits lookup
|
||||||
for( int i=0; i<1024; i++ )
|
for (int i = 0; i < 1024; i++) {
|
||||||
{
|
|
||||||
int p = i;
|
int p = i;
|
||||||
int noisybits = 0;
|
int noisybits = 0;
|
||||||
while (p > 2)
|
while (p > 2) {
|
||||||
{
|
|
||||||
noisybits++;
|
noisybits++;
|
||||||
p >>= 1;
|
p >>= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -29,8 +26,7 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public StatCoderContext( byte[] ab )
|
public StatCoderContext(byte[] ab) {
|
||||||
{
|
|
||||||
super(ab);
|
super(ab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,16 +36,13 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #getBitReport
|
* @see #getBitReport
|
||||||
*/
|
*/
|
||||||
public void assignBits( String name )
|
public void assignBits(String name) {
|
||||||
{
|
|
||||||
long bitpos = getWritingBitPosition();
|
long bitpos = getWritingBitPosition();
|
||||||
if ( statsPerName == null )
|
if (statsPerName == null) {
|
||||||
{
|
statsPerName = new TreeMap<>();
|
||||||
statsPerName = new TreeMap<String, long[]>();
|
|
||||||
}
|
}
|
||||||
long[] stats = statsPerName.get(name);
|
long[] stats = statsPerName.get(name);
|
||||||
if ( stats == null )
|
if (stats == null) {
|
||||||
{
|
|
||||||
stats = new long[2];
|
stats = new long[2];
|
||||||
statsPerName.put(name, stats);
|
statsPerName.put(name, stats);
|
||||||
}
|
}
|
||||||
|
|
@ -63,15 +56,12 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #assignBits
|
* @see #assignBits
|
||||||
*/
|
*/
|
||||||
public static String getBitReport()
|
public static String getBitReport() {
|
||||||
{
|
if (statsPerName == null) {
|
||||||
if ( statsPerName == null )
|
|
||||||
{
|
|
||||||
return "<empty bit report>";
|
return "<empty bit report>";
|
||||||
}
|
}
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for ( String name : statsPerName.keySet() )
|
for (String name : statsPerName.keySet()) {
|
||||||
{
|
|
||||||
long[] stats = statsPerName.get(name);
|
long[] stats = statsPerName.get(name);
|
||||||
sb.append(name + " count=" + stats[1] + " bits=" + stats[0] + "\n");
|
sb.append(name + " count=" + stats[1] + " bits=" + stats[0] + "\n");
|
||||||
}
|
}
|
||||||
|
|
@ -85,14 +75,11 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #decodeNoisyNumber
|
* @see #decodeNoisyNumber
|
||||||
*/
|
*/
|
||||||
public void encodeNoisyNumber( int value, int noisybits )
|
public void encodeNoisyNumber(int value, int noisybits) {
|
||||||
{
|
if (value < 0) {
|
||||||
if ( value < 0 )
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("encodeVarBits expects positive value");
|
throw new IllegalArgumentException("encodeVarBits expects positive value");
|
||||||
}
|
}
|
||||||
if ( noisybits > 0 )
|
if (noisybits > 0) {
|
||||||
{
|
|
||||||
int mask = 0xffffffff >>> (32 - noisybits);
|
int mask = 0xffffffff >>> (32 - noisybits);
|
||||||
encodeBounded(mask, value & mask);
|
encodeBounded(mask, value & mask);
|
||||||
value >>= noisybits;
|
value >>= noisybits;
|
||||||
|
|
@ -106,8 +93,7 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #encodeNoisyNumber
|
* @see #encodeNoisyNumber
|
||||||
*/
|
*/
|
||||||
public int decodeNoisyNumber( int noisybits )
|
public int decodeNoisyNumber(int noisybits) {
|
||||||
{
|
|
||||||
int value = decodeBits(noisybits);
|
int value = decodeBits(noisybits);
|
||||||
return value | (decodeVarBits() << noisybits);
|
return value | (decodeVarBits() << noisybits);
|
||||||
}
|
}
|
||||||
|
|
@ -118,18 +104,15 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #decodeNoisyDiff
|
* @see #decodeNoisyDiff
|
||||||
*/
|
*/
|
||||||
public void encodeNoisyDiff( int value, int noisybits )
|
public void encodeNoisyDiff(int value, int noisybits) {
|
||||||
{
|
if (noisybits > 0) {
|
||||||
if ( noisybits > 0 )
|
|
||||||
{
|
|
||||||
value += 1 << (noisybits - 1);
|
value += 1 << (noisybits - 1);
|
||||||
int mask = 0xffffffff >>> (32 - noisybits);
|
int mask = 0xffffffff >>> (32 - noisybits);
|
||||||
encodeBounded(mask, value & mask);
|
encodeBounded(mask, value & mask);
|
||||||
value >>= noisybits;
|
value >>= noisybits;
|
||||||
}
|
}
|
||||||
encodeVarBits(value < 0 ? -value : value);
|
encodeVarBits(value < 0 ? -value : value);
|
||||||
if ( value != 0 )
|
if (value != 0) {
|
||||||
{
|
|
||||||
encodeBit(value < 0);
|
encodeBit(value < 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,18 +123,14 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #encodeNoisyDiff
|
* @see #encodeNoisyDiff
|
||||||
*/
|
*/
|
||||||
public int decodeNoisyDiff( int noisybits )
|
public int decodeNoisyDiff(int noisybits) {
|
||||||
{
|
|
||||||
int value = 0;
|
int value = 0;
|
||||||
if ( noisybits > 0 )
|
if (noisybits > 0) {
|
||||||
{
|
|
||||||
value = decodeBits(noisybits) - (1 << (noisybits - 1));
|
value = decodeBits(noisybits) - (1 << (noisybits - 1));
|
||||||
}
|
}
|
||||||
int val2 = decodeVarBits() << noisybits;
|
int val2 = decodeVarBits() << noisybits;
|
||||||
if ( val2 != 0 )
|
if (val2 != 0) {
|
||||||
{
|
if (decodeBit()) {
|
||||||
if ( decodeBit() )
|
|
||||||
{
|
|
||||||
val2 = -val2;
|
val2 = -val2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -164,13 +143,11 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #decodePredictedValue
|
* @see #decodePredictedValue
|
||||||
*/
|
*/
|
||||||
public void encodePredictedValue( int value, int predictor )
|
public void encodePredictedValue(int value, int predictor) {
|
||||||
{
|
|
||||||
int p = predictor < 0 ? -predictor : predictor;
|
int p = predictor < 0 ? -predictor : predictor;
|
||||||
int noisybits = 0;
|
int noisybits = 0;
|
||||||
|
|
||||||
while (p > 2)
|
while (p > 2) {
|
||||||
{
|
|
||||||
noisybits++;
|
noisybits++;
|
||||||
p >>= 1;
|
p >>= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -183,12 +160,10 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
*
|
*
|
||||||
* @see #encodePredictedValue
|
* @see #encodePredictedValue
|
||||||
*/
|
*/
|
||||||
public int decodePredictedValue( int predictor )
|
public int decodePredictedValue(int predictor) {
|
||||||
{
|
|
||||||
int p = predictor < 0 ? -predictor : predictor;
|
int p = predictor < 0 ? -predictor : predictor;
|
||||||
int noisybits = 0;
|
int noisybits = 0;
|
||||||
while (p > 1023)
|
while (p > 1023) {
|
||||||
{
|
|
||||||
noisybits++;
|
noisybits++;
|
||||||
p >>= 1;
|
p >>= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -202,29 +177,20 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
* bits per value that only depends on the typical distance between subsequent
|
* bits per value that only depends on the typical distance between subsequent
|
||||||
* values and also benefits
|
* values and also benefits
|
||||||
*
|
*
|
||||||
* @param values
|
* @param values the array to encode
|
||||||
* the array to encode
|
* @param offset position in this array where to start
|
||||||
* @param offset
|
* @param subsize number of values to encode
|
||||||
* position in this array where to start
|
* @param nextbit bitmask with the most significant bit set to 1
|
||||||
* @param subsize
|
* @param mask should be 0
|
||||||
* number of values to encode
|
|
||||||
* @param nextbit
|
|
||||||
* bitmask with the most significant bit set to 1
|
|
||||||
* @param mask
|
|
||||||
* should be 0
|
|
||||||
*/
|
*/
|
||||||
public void encodeSortedArray( int[] values, int offset, int subsize, int nextbit, int mask )
|
public void encodeSortedArray(int[] values, int offset, int subsize, int nextbit, int mask) {
|
||||||
{
|
if (subsize == 1) { // last-choice shortcut
|
||||||
if ( subsize == 1 ) // last-choice shortcut
|
while (nextbit != 0) {
|
||||||
{
|
|
||||||
while (nextbit != 0)
|
|
||||||
{
|
|
||||||
encodeBit((values[offset] & nextbit) != 0);
|
encodeBit((values[offset] & nextbit) != 0);
|
||||||
nextbit >>= 1;
|
nextbit >>= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( nextbit == 0 )
|
if (nextbit == 0) {
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,10 +200,8 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
// count 0-bit-fraction
|
// count 0-bit-fraction
|
||||||
int i = offset;
|
int i = offset;
|
||||||
int end = subsize + offset;
|
int end = subsize + offset;
|
||||||
for ( ; i < end; i++ )
|
for (; i < end; i++) {
|
||||||
{
|
if ((values[i] & mask) != data) {
|
||||||
if ( ( values[i] & mask ) != data )
|
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -245,45 +209,32 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
int size2 = subsize - size1;
|
int size2 = subsize - size1;
|
||||||
|
|
||||||
encodeBounded(subsize, size1);
|
encodeBounded(subsize, size1);
|
||||||
if ( size1 > 0 )
|
if (size1 > 0) {
|
||||||
{
|
|
||||||
encodeSortedArray(values, offset, size1, nextbit >> 1, mask);
|
encodeSortedArray(values, offset, size1, nextbit >> 1, mask);
|
||||||
}
|
}
|
||||||
if ( size2 > 0 )
|
if (size2 > 0) {
|
||||||
{
|
|
||||||
encodeSortedArray(values, i, size2, nextbit >> 1, mask);
|
encodeSortedArray(values, i, size2, nextbit >> 1, mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param values the array to encode
|
||||||
|
* @param offset position in this array where to start
|
||||||
|
* @param subsize number of values to encode
|
||||||
|
* @param nextbit bitmask with the most significant bit set to 1
|
||||||
|
* @param value should be 0
|
||||||
* @see #encodeSortedArray
|
* @see #encodeSortedArray
|
||||||
*
|
|
||||||
* @param values
|
|
||||||
* the array to encode
|
|
||||||
* @param offset
|
|
||||||
* position in this array where to start
|
|
||||||
* @param subsize
|
|
||||||
* number of values to encode
|
|
||||||
* @param nextbit
|
|
||||||
* bitmask with the most significant bit set to 1
|
|
||||||
* @param value
|
|
||||||
* should be 0
|
|
||||||
*/
|
*/
|
||||||
public void decodeSortedArray( int[] values, int offset, int subsize, int nextbitpos, int value )
|
public void decodeSortedArray(int[] values, int offset, int subsize, int nextbitpos, int value) {
|
||||||
{
|
if (subsize == 1) { // last-choice shortcut
|
||||||
if ( subsize == 1 ) // last-choice shortcut
|
if (nextbitpos >= 0) {
|
||||||
{
|
|
||||||
if ( nextbitpos >= 0 )
|
|
||||||
{
|
|
||||||
value |= decodeBitsReverse(nextbitpos + 1);
|
value |= decodeBitsReverse(nextbitpos + 1);
|
||||||
}
|
}
|
||||||
values[offset] = value;
|
values[offset] = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( nextbitpos < 0 )
|
if (nextbitpos < 0) {
|
||||||
{
|
while (subsize-- > 0) {
|
||||||
while (subsize-- > 0)
|
|
||||||
{
|
|
||||||
values[offset++] = value;
|
values[offset++] = value;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -292,12 +243,10 @@ public final class StatCoderContext extends BitCoderContext
|
||||||
int size1 = decodeBounded(subsize);
|
int size1 = decodeBounded(subsize);
|
||||||
int size2 = subsize - size1;
|
int size2 = subsize - size1;
|
||||||
|
|
||||||
if ( size1 > 0 )
|
if (size1 > 0) {
|
||||||
{
|
|
||||||
decodeSortedArray(values, offset, size1, nextbitpos - 1, value);
|
decodeSortedArray(values, offset, size1, nextbitpos - 1, value);
|
||||||
}
|
}
|
||||||
if ( size2 > 0 )
|
if (size2 > 0) {
|
||||||
{
|
|
||||||
decodeSortedArray(values, offset + size1, size2, nextbitpos - 1, value | (1 << nextbitpos));
|
decodeSortedArray(values, offset + size1, size2, nextbitpos - 1, value | (1 << nextbitpos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,44 +2,39 @@ package btools.codec;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
import btools.util.BitCoderContext;
|
import btools.util.BitCoderContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encoder/Decoder for way-/node-descriptions
|
* Encoder/Decoder for way-/node-descriptions
|
||||||
*
|
* <p>
|
||||||
* It detects identical descriptions and sorts them
|
* It detects identical descriptions and sorts them
|
||||||
* into a huffman-tree according to their frequencies
|
* into a huffman-tree according to their frequencies
|
||||||
*
|
* <p>
|
||||||
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
* Adapted for 3-pass encoding (counters -> statistics -> encoding )
|
||||||
* but doesn't do anything at pass1
|
* but doesn't do anything at pass1
|
||||||
*/
|
*/
|
||||||
public final class TagValueCoder
|
public final class TagValueCoder {
|
||||||
{
|
private Map<TagValueSet, TagValueSet> identityMap;
|
||||||
private HashMap<TagValueSet, TagValueSet> identityMap;
|
|
||||||
private Object tree;
|
private Object tree;
|
||||||
private BitCoderContext bc;
|
private BitCoderContext bc;
|
||||||
private int pass;
|
private int pass;
|
||||||
private int nextTagValueSetId;
|
private int nextTagValueSetId;
|
||||||
|
|
||||||
public void encodeTagValueSet( byte[] data )
|
public void encodeTagValueSet(byte[] data) {
|
||||||
{
|
if (pass == 1) {
|
||||||
if ( pass == 1 )
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TagValueSet tvsProbe = new TagValueSet(nextTagValueSetId);
|
TagValueSet tvsProbe = new TagValueSet(nextTagValueSetId);
|
||||||
tvsProbe.data = data;
|
tvsProbe.data = data;
|
||||||
TagValueSet tvs = identityMap.get(tvsProbe);
|
TagValueSet tvs = identityMap.get(tvsProbe);
|
||||||
if ( pass == 3 )
|
if (pass == 3) {
|
||||||
{
|
|
||||||
bc.encodeBounded(tvs.range - 1, tvs.code);
|
bc.encodeBounded(tvs.range - 1, tvs.code);
|
||||||
}
|
} else if (pass == 2) {
|
||||||
else if ( pass == 2 )
|
if (tvs == null) {
|
||||||
{
|
|
||||||
if ( tvs == null )
|
|
||||||
{
|
|
||||||
tvs = tvsProbe;
|
tvs = tvsProbe;
|
||||||
nextTagValueSetId++;
|
nextTagValueSetId++;
|
||||||
identityMap.put(tvs, tvs);
|
identityMap.put(tvs, tvs);
|
||||||
|
|
@ -48,11 +43,9 @@ public final class TagValueCoder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagValueWrapper decodeTagValueSet()
|
public TagValueWrapper decodeTagValueSet() {
|
||||||
{
|
|
||||||
Object node = tree;
|
Object node = tree;
|
||||||
while (node instanceof TreeNode)
|
while (node instanceof TreeNode) {
|
||||||
{
|
|
||||||
TreeNode tn = (TreeNode) node;
|
TreeNode tn = (TreeNode) node;
|
||||||
boolean nextBit = bc.decodeBit();
|
boolean nextBit = bc.decodeBit();
|
||||||
node = nextBit ? tn.child2 : tn.child1;
|
node = nextBit ? tn.child2 : tn.child1;
|
||||||
|
|
@ -60,19 +53,15 @@ public final class TagValueCoder
|
||||||
return (TagValueWrapper) node;
|
return (TagValueWrapper) node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encodeDictionary( BitCoderContext bc )
|
public void encodeDictionary(BitCoderContext bc) {
|
||||||
{
|
if (++pass == 3) {
|
||||||
if ( ++pass == 3 )
|
if (identityMap.size() == 0) {
|
||||||
{
|
|
||||||
if ( identityMap.size() == 0 )
|
|
||||||
{
|
|
||||||
TagValueSet dummy = new TagValueSet(nextTagValueSetId++);
|
TagValueSet dummy = new TagValueSet(nextTagValueSetId++);
|
||||||
identityMap.put(dummy, dummy);
|
identityMap.put(dummy, dummy);
|
||||||
}
|
}
|
||||||
PriorityQueue<TagValueSet> queue = new PriorityQueue<TagValueSet>(2*identityMap.size(), new TagValueSet.FrequencyComparator());
|
Queue<TagValueSet> queue = new PriorityQueue<>(2 * identityMap.size(), new TagValueSet.FrequencyComparator());
|
||||||
queue.addAll(identityMap.values());
|
queue.addAll(identityMap.values());
|
||||||
while (queue.size() > 1)
|
while (queue.size() > 1) {
|
||||||
{
|
|
||||||
TagValueSet node = new TagValueSet(nextTagValueSetId++);
|
TagValueSet node = new TagValueSet(nextTagValueSetId++);
|
||||||
node.child1 = queue.poll();
|
node.child1 = queue.poll();
|
||||||
node.child2 = queue.poll();
|
node.child2 = queue.poll();
|
||||||
|
|
@ -85,22 +74,18 @@ public final class TagValueCoder
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagValueCoder( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
|
public TagValueCoder(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
|
||||||
{
|
|
||||||
tree = decodeTree(bc, buffers, validator);
|
tree = decodeTree(bc, buffers, validator);
|
||||||
this.bc = bc;
|
this.bc = bc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagValueCoder()
|
public TagValueCoder() {
|
||||||
{
|
identityMap = new HashMap<>();
|
||||||
identityMap = new HashMap<TagValueSet, TagValueSet>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object decodeTree( BitCoderContext bc, DataBuffers buffers, TagValueValidator validator )
|
private Object decodeTree(BitCoderContext bc, DataBuffers buffers, TagValueValidator validator) {
|
||||||
{
|
|
||||||
boolean isNode = bc.decodeBit();
|
boolean isNode = bc.decodeBit();
|
||||||
if ( isNode )
|
if (isNode) {
|
||||||
{
|
|
||||||
TreeNode node = new TreeNode();
|
TreeNode node = new TreeNode();
|
||||||
node.child1 = decodeTree(bc, buffers, validator);
|
node.child1 = decodeTree(bc, buffers, validator);
|
||||||
node.child2 = decodeTree(bc, buffers, validator);
|
node.child2 = decodeTree(bc, buffers, validator);
|
||||||
|
|
@ -115,18 +100,14 @@ public final class TagValueCoder
|
||||||
int lastEncodedInum = 0;
|
int lastEncodedInum = 0;
|
||||||
|
|
||||||
boolean hasdata = false;
|
boolean hasdata = false;
|
||||||
for ( ;; )
|
for (; ; ) {
|
||||||
{
|
|
||||||
int delta = bc.decodeVarBits();
|
int delta = bc.decodeVarBits();
|
||||||
if ( !hasdata )
|
if (!hasdata) {
|
||||||
{
|
if (delta == 0) {
|
||||||
if ( delta == 0 )
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( delta == 0 )
|
if (delta == 0) {
|
||||||
{
|
|
||||||
ctx.encodeVarBits(0);
|
ctx.encodeVarBits(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -134,8 +115,7 @@ public final class TagValueCoder
|
||||||
|
|
||||||
int data = bc.decodeVarBits();
|
int data = bc.decodeVarBits();
|
||||||
|
|
||||||
if ( validator == null || validator.isLookupIdxUsed( inum ) )
|
if (validator == null || validator.isLookupIdxUsed(inum)) {
|
||||||
{
|
|
||||||
hasdata = true;
|
hasdata = true;
|
||||||
ctx.encodeVarBits(inum - lastEncodedInum);
|
ctx.encodeVarBits(inum - lastEncodedInum);
|
||||||
ctx.encodeVarBits(data);
|
ctx.encodeVarBits(data);
|
||||||
|
|
@ -145,19 +125,15 @@ public final class TagValueCoder
|
||||||
|
|
||||||
byte[] res;
|
byte[] res;
|
||||||
int len = ctx.closeAndGetEncodedLength();
|
int len = ctx.closeAndGetEncodedLength();
|
||||||
if ( validator == null )
|
if (validator == null) {
|
||||||
{
|
|
||||||
res = new byte[len];
|
res = new byte[len];
|
||||||
System.arraycopy(buffer, 0, res, 0, len);
|
System.arraycopy(buffer, 0, res, 0, len);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
res = validator.unify(buffer, 0, len);
|
res = validator.unify(buffer, 0, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int accessType = validator == null ? 2 : validator.accessType(res);
|
int accessType = validator == null ? 2 : validator.accessType(res);
|
||||||
if ( accessType > 0 )
|
if (accessType > 0) {
|
||||||
{
|
|
||||||
TagValueWrapper w = new TagValueWrapper();
|
TagValueWrapper w = new TagValueWrapper();
|
||||||
w.data = res;
|
w.data = res;
|
||||||
w.accessType = accessType;
|
w.accessType = accessType;
|
||||||
|
|
@ -166,14 +142,12 @@ public final class TagValueCoder
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class TreeNode
|
public static final class TreeNode {
|
||||||
{
|
|
||||||
public Object child1;
|
public Object child1;
|
||||||
public Object child2;
|
public Object child2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class TagValueSet
|
public static final class TagValueSet {
|
||||||
{
|
|
||||||
public byte[] data;
|
public byte[] data;
|
||||||
public int frequency;
|
public int frequency;
|
||||||
public int code;
|
public int code;
|
||||||
|
|
@ -182,36 +156,28 @@ public final class TagValueCoder
|
||||||
public TagValueSet child2;
|
public TagValueSet child2;
|
||||||
private int id; // serial number to make the comparator well defined in case of equal frequencies
|
private int id; // serial number to make the comparator well defined in case of equal frequencies
|
||||||
|
|
||||||
public TagValueSet( int id )
|
public TagValueSet(int id) {
|
||||||
{
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encode( BitCoderContext bc, int range, int code )
|
public void encode(BitCoderContext bc, int range, int code) {
|
||||||
{
|
|
||||||
this.range = range;
|
this.range = range;
|
||||||
this.code = code;
|
this.code = code;
|
||||||
boolean isNode = child1 != null;
|
boolean isNode = child1 != null;
|
||||||
bc.encodeBit(isNode);
|
bc.encodeBit(isNode);
|
||||||
if ( isNode )
|
if (isNode) {
|
||||||
{
|
|
||||||
child1.encode(bc, range << 1, code);
|
child1.encode(bc, range << 1, code);
|
||||||
child2.encode(bc, range << 1, code + range);
|
child2.encode(bc, range << 1, code + range);
|
||||||
}
|
} else {
|
||||||
else
|
if (data == null) {
|
||||||
{
|
|
||||||
if ( data == null )
|
|
||||||
{
|
|
||||||
bc.encodeVarBits(0);
|
bc.encodeVarBits(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BitCoderContext src = new BitCoderContext(data);
|
BitCoderContext src = new BitCoderContext(data);
|
||||||
for ( ;; )
|
for (; ; ) {
|
||||||
{
|
|
||||||
int delta = src.decodeVarBits();
|
int delta = src.decodeVarBits();
|
||||||
bc.encodeVarBits(delta);
|
bc.encodeVarBits(delta);
|
||||||
if ( delta == 0 )
|
if (delta == 0) {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int data = src.decodeVarBits();
|
int data = src.decodeVarBits();
|
||||||
|
|
@ -221,27 +187,20 @@ public final class TagValueCoder
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals( Object o )
|
public boolean equals(Object o) {
|
||||||
{
|
if (o instanceof TagValueSet) {
|
||||||
if ( o instanceof TagValueSet )
|
|
||||||
{
|
|
||||||
TagValueSet tvs = (TagValueSet) o;
|
TagValueSet tvs = (TagValueSet) o;
|
||||||
if ( data == null )
|
if (data == null) {
|
||||||
{
|
|
||||||
return tvs.data == null;
|
return tvs.data == null;
|
||||||
}
|
}
|
||||||
if ( tvs.data == null )
|
if (tvs.data == null) {
|
||||||
{
|
|
||||||
return data == null;
|
return data == null;
|
||||||
}
|
}
|
||||||
if ( data.length != tvs.data.length )
|
if (data.length != tvs.data.length) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for ( int i = 0; i < data.length; i++ )
|
for (int i = 0; i < data.length; i++) {
|
||||||
{
|
if (data[i] != tvs.data[i]) {
|
||||||
if ( data[i] != tvs.data[i] )
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -251,22 +210,18 @@ public final class TagValueCoder
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode()
|
public int hashCode() {
|
||||||
{
|
if (data == null) {
|
||||||
if ( data == null )
|
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int h = 17;
|
int h = 17;
|
||||||
for ( int i = 0; i < data.length; i++ )
|
for (int i = 0; i < data.length; i++) {
|
||||||
{
|
|
||||||
h = (h << 8) + data[i];
|
h = (h << 8) + data[i];
|
||||||
}
|
}
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FrequencyComparator implements Comparator<TagValueSet>
|
public static class FrequencyComparator implements Comparator<TagValueSet> {
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compare(TagValueSet tvs1, TagValueSet tvs2) {
|
public int compare(TagValueSet tvs1, TagValueSet tvs2) {
|
||||||
|
|
@ -281,8 +236,7 @@ public final class TagValueCoder
|
||||||
if (tvs1.id > tvs2.id)
|
if (tvs1.id > tvs2.id)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if ( tvs1 != tvs2 )
|
if (tvs1 != tvs2) {
|
||||||
{
|
|
||||||
throw new RuntimeException("identity corruption!");
|
throw new RuntimeException("identity corruption!");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
package btools.codec;
|
package btools.codec;
|
||||||
|
|
||||||
|
|
||||||
public interface TagValueValidator
|
public interface TagValueValidator {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* @param tagValueSet the way description to check
|
* @param tagValueSet the way description to check
|
||||||
* @return 0 = nothing, 1=no matching, 2=normal
|
* @return 0 = nothing, 1=no matching, 2=normal
|
||||||
*/
|
*/
|
||||||
public int accessType( byte[] tagValueSet );
|
int accessType(byte[] tagValueSet);
|
||||||
|
|
||||||
public byte[] unify( byte[] tagValueSet, int offset, int len );
|
byte[] unify(byte[] tagValueSet, int offset, int len);
|
||||||
|
|
||||||
public boolean isLookupIdxUsed( int idx );
|
boolean isLookupIdxUsed(int idx);
|
||||||
|
|
||||||
public void setDecodeForbidden( boolean decodeForbidden );
|
void setDecodeForbidden(boolean decodeForbidden);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ package btools.codec;
|
||||||
* TagValueWrapper wrapps a description bitmap
|
* TagValueWrapper wrapps a description bitmap
|
||||||
* to add the access-type
|
* to add the access-type
|
||||||
*/
|
*/
|
||||||
public final class TagValueWrapper
|
public final class TagValueWrapper {
|
||||||
{
|
|
||||||
public byte[] data;
|
public byte[] data;
|
||||||
public int accessType;
|
public int accessType;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@ package btools.codec;
|
||||||
* from the decoder to find the closest
|
* from the decoder to find the closest
|
||||||
* matches to the waypoints
|
* matches to the waypoints
|
||||||
*/
|
*/
|
||||||
public interface WaypointMatcher
|
public interface WaypointMatcher {
|
||||||
{
|
|
||||||
boolean start(int ilonStart, int ilatStart, int ilonTarget, int ilatTarget);
|
boolean start(int ilonStart, int ilatStart, int ilonTarget, int ilatTarget);
|
||||||
|
|
||||||
void transferNode(int ilon, int ilat);
|
void transferNode(int ilon, int ilat);
|
||||||
|
|
||||||
void end();
|
void end();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,50 +3,35 @@ package btools.codec;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class LinkedListContainerTest
|
public class LinkedListContainerTest {
|
||||||
{
|
|
||||||
@Test
|
@Test
|
||||||
public void linkedListTest1()
|
public void linkedListTest1() {
|
||||||
{
|
|
||||||
int nlists = 553;
|
int nlists = 553;
|
||||||
|
|
||||||
LinkedListContainer llc = new LinkedListContainer(nlists, null);
|
LinkedListContainer llc = new LinkedListContainer(nlists, null);
|
||||||
|
|
||||||
for ( int ln = 0; ln < nlists; ln++ )
|
for (int ln = 0; ln < nlists; ln++) {
|
||||||
{
|
for (int i = 0; i < 10; i++) {
|
||||||
for ( int i = 0; i < 10; i++ )
|
|
||||||
{
|
|
||||||
llc.addDataElement(ln, ln * i);
|
llc.addDataElement(ln, ln * i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int i = 0; i < 10; i++ )
|
for (int i = 0; i < 10; i++) {
|
||||||
{
|
for (int ln = 0; ln < nlists; ln++) {
|
||||||
for ( int ln = 0; ln < nlists; ln++ )
|
|
||||||
{
|
|
||||||
llc.addDataElement(ln, ln * i);
|
llc.addDataElement(ln, ln * i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( int ln = 0; ln < nlists; ln++ )
|
for (int ln = 0; ln < nlists; ln++) {
|
||||||
{
|
|
||||||
int cnt = llc.initList(ln);
|
int cnt = llc.initList(ln);
|
||||||
Assert.assertTrue( "list size test", cnt == 20 );
|
Assert.assertEquals("list size test", 20, cnt);
|
||||||
|
|
||||||
for ( int i = 19; i >= 0; i-- )
|
for (int i = 19; i >= 0; i--) {
|
||||||
{
|
|
||||||
int data = llc.getDataElement();
|
int data = llc.getDataElement();
|
||||||
Assert.assertTrue( "data value test", data == ln * ( i % 10 ) );
|
Assert.assertEquals("data value test", ln * (i % 10), data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
Assert.assertThrows("no more elements expected", IllegalArgumentException.class, () -> llc.getDataElement());
|
||||||
{
|
|
||||||
llc.getDataElement();
|
|
||||||
Assert.fail( "no more elements expected" );
|
|
||||||
}
|
|
||||||
catch (IllegalArgumentException e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,30 +6,23 @@ import java.util.Random;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class StatCoderContextTest
|
public class StatCoderContextTest {
|
||||||
{
|
|
||||||
@Test
|
@Test
|
||||||
public void noisyVarBitsEncodeDecodeTest()
|
public void noisyVarBitsEncodeDecodeTest() {
|
||||||
{
|
|
||||||
byte[] ab = new byte[40000];
|
byte[] ab = new byte[40000];
|
||||||
StatCoderContext ctx = new StatCoderContext(ab);
|
StatCoderContext ctx = new StatCoderContext(ab);
|
||||||
for ( int noisybits = 1; noisybits < 12; noisybits++ )
|
for (int noisybits = 1; noisybits < 12; noisybits++) {
|
||||||
{
|
for (int i = 0; i < 1000; i++) {
|
||||||
for ( int i = 0; i < 1000; i++ )
|
|
||||||
{
|
|
||||||
ctx.encodeNoisyNumber(i, noisybits);
|
ctx.encodeNoisyNumber(i, noisybits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.closeAndGetEncodedLength();
|
ctx.closeAndGetEncodedLength();
|
||||||
ctx = new StatCoderContext(ab);
|
ctx = new StatCoderContext(ab);
|
||||||
|
|
||||||
for ( int noisybits = 1; noisybits < 12; noisybits++ )
|
for (int noisybits = 1; noisybits < 12; noisybits++) {
|
||||||
{
|
for (int i = 0; i < 1000; i++) {
|
||||||
for ( int i = 0; i < 1000; i++ )
|
|
||||||
{
|
|
||||||
int value = ctx.decodeNoisyNumber(noisybits);
|
int value = ctx.decodeNoisyNumber(noisybits);
|
||||||
if ( value != i )
|
if (value != i) {
|
||||||
{
|
|
||||||
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
|
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,27 +30,21 @@ public class StatCoderContextTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void noisySignedVarBitsEncodeDecodeTest()
|
public void noisySignedVarBitsEncodeDecodeTest() {
|
||||||
{
|
|
||||||
byte[] ab = new byte[80000];
|
byte[] ab = new byte[80000];
|
||||||
StatCoderContext ctx = new StatCoderContext(ab);
|
StatCoderContext ctx = new StatCoderContext(ab);
|
||||||
for ( int noisybits = 0; noisybits < 12; noisybits++ )
|
for (int noisybits = 0; noisybits < 12; noisybits++) {
|
||||||
{
|
for (int i = -1000; i < 1000; i++) {
|
||||||
for ( int i = -1000; i < 1000; i++ )
|
|
||||||
{
|
|
||||||
ctx.encodeNoisyDiff(i, noisybits);
|
ctx.encodeNoisyDiff(i, noisybits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.closeAndGetEncodedLength();
|
ctx.closeAndGetEncodedLength();
|
||||||
ctx = new StatCoderContext(ab);
|
ctx = new StatCoderContext(ab);
|
||||||
|
|
||||||
for ( int noisybits = 0; noisybits < 12; noisybits++ )
|
for (int noisybits = 0; noisybits < 12; noisybits++) {
|
||||||
{
|
for (int i = -1000; i < 1000; i++) {
|
||||||
for ( int i = -1000; i < 1000; i++ )
|
|
||||||
{
|
|
||||||
int value = ctx.decodeNoisyDiff(noisybits);
|
int value = ctx.decodeNoisyDiff(noisybits);
|
||||||
if ( value != i )
|
if (value != i) {
|
||||||
{
|
|
||||||
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
|
Assert.fail("value mismatch: noisybits=" + noisybits + " i=" + i + " value=" + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -65,27 +52,21 @@ public class StatCoderContextTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void predictedValueEncodeDecodeTest()
|
public void predictedValueEncodeDecodeTest() {
|
||||||
{
|
|
||||||
byte[] ab = new byte[80000];
|
byte[] ab = new byte[80000];
|
||||||
StatCoderContext ctx = new StatCoderContext(ab);
|
StatCoderContext ctx = new StatCoderContext(ab);
|
||||||
for ( int value = -100; value < 100; value += 5 )
|
for (int value = -100; value < 100; value += 5) {
|
||||||
{
|
for (int predictor = -200; predictor < 200; predictor += 7) {
|
||||||
for ( int predictor = -200; predictor < 200; predictor += 7 )
|
|
||||||
{
|
|
||||||
ctx.encodePredictedValue(value, predictor);
|
ctx.encodePredictedValue(value, predictor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.closeAndGetEncodedLength();
|
ctx.closeAndGetEncodedLength();
|
||||||
ctx = new StatCoderContext(ab);
|
ctx = new StatCoderContext(ab);
|
||||||
|
|
||||||
for ( int value = -100; value < 100; value += 5 )
|
for (int value = -100; value < 100; value += 5) {
|
||||||
{
|
for (int predictor = -200; predictor < 200; predictor += 7) {
|
||||||
for ( int predictor = -200; predictor < 200; predictor += 7 )
|
|
||||||
{
|
|
||||||
int decodedValue = ctx.decodePredictedValue(predictor);
|
int decodedValue = ctx.decodePredictedValue(predictor);
|
||||||
if ( value != decodedValue )
|
if (value != decodedValue) {
|
||||||
{
|
|
||||||
Assert.fail("value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue);
|
Assert.fail("value mismatch: value=" + value + " predictor=" + predictor + " decodedValue=" + decodedValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,13 +74,11 @@ public class StatCoderContextTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sortedArrayEncodeDecodeTest()
|
public void sortedArrayEncodeDecodeTest() {
|
||||||
{
|
|
||||||
Random rand = new Random();
|
Random rand = new Random();
|
||||||
int size = 1000000;
|
int size = 1000000;
|
||||||
int[] values = new int[size];
|
int[] values = new int[size];
|
||||||
for ( int i = 0; i < size; i++ )
|
for (int i = 0; i < size; i++) {
|
||||||
{
|
|
||||||
values[i] = rand.nextInt() & 0x0fffffff;
|
values[i] = rand.nextInt() & 0x0fffffff;
|
||||||
}
|
}
|
||||||
values[5] = 175384; // force collision
|
values[5] = 175384; // force collision
|
||||||
|
|
@ -120,10 +99,8 @@ public class StatCoderContextTest
|
||||||
int[] decodedValues = new int[size];
|
int[] decodedValues = new int[size];
|
||||||
ctx.decodeSortedArray(decodedValues, 0, size, 27, 0);
|
ctx.decodeSortedArray(decodedValues, 0, size, 27, 0);
|
||||||
|
|
||||||
for ( int i = 0; i < size; i++ )
|
for (int i = 0; i < size; i++) {
|
||||||
{
|
if (values[i] != decodedValues[i]) {
|
||||||
if ( values[i] != decodedValues[i] )
|
|
||||||
{
|
|
||||||
Assert.fail("mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i]);
|
Assert.fail("mismatch at i=" + i + " " + values[i] + "<>" + decodedValues[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
brouter-core/.gitignore
vendored
1
brouter-core/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/build/
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id 'brouter.library-conventions'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation project(':brouter-mapaccess')
|
implementation project(':brouter-mapaccess')
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
implementation project(':brouter-expressions')
|
implementation project(':brouter-expressions')
|
||||||
implementation project(':brouter-codec')
|
implementation project(':brouter-codec')
|
||||||
testImplementation 'junit:junit:4.13.1'
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapcreatorTest generates segments which are used in tests
|
||||||
|
test.dependsOn ':brouter-map-creator:test'
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<manifest package="btools.router" />
|
|
||||||
43
brouter-core/src/main/java/btools/router/FormatCsv.java
Normal file
43
brouter-core/src/main/java/btools/router/FormatCsv.java
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
|
||||||
|
public class FormatCsv extends Formatter {
|
||||||
|
|
||||||
|
|
||||||
|
public FormatCsv(RoutingContext rc) {
|
||||||
|
super(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(OsmTrack t) {
|
||||||
|
try {
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
BufferedWriter bw = new BufferedWriter(sw);
|
||||||
|
writeMessages(bw, t);
|
||||||
|
return sw.toString();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return "Error: " + ex.getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeMessages(BufferedWriter bw, OsmTrack t) throws Exception {
|
||||||
|
dumpLine(bw, MESSAGES_HEADER);
|
||||||
|
for (String m : t.aggregateMessages()) {
|
||||||
|
dumpLine(bw, m);
|
||||||
|
}
|
||||||
|
if (bw != null)
|
||||||
|
bw.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dumpLine(BufferedWriter bw, String s) throws Exception {
|
||||||
|
if (bw == null) {
|
||||||
|
System.out.println(s);
|
||||||
|
} else {
|
||||||
|
bw.write(s);
|
||||||
|
bw.write("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
534
brouter-core/src/main/java/btools/router/FormatGpx.java
Normal file
534
brouter-core/src/main/java/btools/router/FormatGpx.java
Normal file
|
|
@ -0,0 +1,534 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import btools.mapaccess.MatchedWaypoint;
|
||||||
|
import btools.util.StringUtils;
|
||||||
|
|
||||||
|
public class FormatGpx extends Formatter {
|
||||||
|
public FormatGpx(RoutingContext rc) {
|
||||||
|
super(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(OsmTrack t) {
|
||||||
|
try {
|
||||||
|
StringWriter sw = new StringWriter(8192);
|
||||||
|
BufferedWriter bw = new BufferedWriter(sw);
|
||||||
|
formatAsGpx(bw, t);
|
||||||
|
bw.close();
|
||||||
|
return sw.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatAsGpx(BufferedWriter sb, OsmTrack t) throws IOException {
|
||||||
|
int turnInstructionMode = t.voiceHints != null ? t.voiceHints.turnInstructionMode : 0;
|
||||||
|
|
||||||
|
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||||
|
if (turnInstructionMode != 9) {
|
||||||
|
for (int i = t.messageList.size() - 1; i >= 0; i--) {
|
||||||
|
String message = t.messageList.get(i);
|
||||||
|
if (i < t.messageList.size() - 1)
|
||||||
|
message = "(alt-index " + i + ": " + message + " )";
|
||||||
|
if (message != null)
|
||||||
|
sb.append("<!-- ").append(message).append(" -->\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnInstructionMode == 4) { // comment style
|
||||||
|
sb.append("<!-- $transport-mode$").append(t.voiceHints.getTransportMode()).append("$ -->\n");
|
||||||
|
sb.append("<!-- cmd idx lon lat d2next geometry -->\n");
|
||||||
|
sb.append("<!-- $turn-instruction-start$\n");
|
||||||
|
for (VoiceHint hint : t.voiceHints.list) {
|
||||||
|
sb.append(String.format(" $turn$%6s;%6d;%10s;%10s;%6d;%s$\n", hint.getCommandString(), hint.indexInTrack,
|
||||||
|
formatILon(hint.ilon), formatILat(hint.ilat), (int) (hint.distanceToNext), hint.formatGeometry()));
|
||||||
|
}
|
||||||
|
sb.append(" $turn-instruction-end$ -->\n");
|
||||||
|
}
|
||||||
|
sb.append("<gpx \n");
|
||||||
|
sb.append(" xmlns=\"http://www.topografix.com/GPX/1/1\" \n");
|
||||||
|
sb.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n");
|
||||||
|
if (turnInstructionMode == 9) { // BRouter style
|
||||||
|
sb.append(" xmlns:brouter=\"Not yet documented\" \n");
|
||||||
|
}
|
||||||
|
if (turnInstructionMode == 7) { // old locus style
|
||||||
|
sb.append(" xmlns:locus=\"http://www.locusmap.eu\" \n");
|
||||||
|
}
|
||||||
|
sb.append(" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n");
|
||||||
|
|
||||||
|
if (turnInstructionMode == 3) {
|
||||||
|
sb.append(" creator=\"OsmAndRouter\" version=\"1.1\">\n");
|
||||||
|
} else {
|
||||||
|
sb.append(" creator=\"BRouter-" + t.version + "\" version=\"1.1\">\n");
|
||||||
|
}
|
||||||
|
if (turnInstructionMode == 9) {
|
||||||
|
sb.append(" <metadata>\n");
|
||||||
|
sb.append(" <name>").append(t.name).append("</name>\n");
|
||||||
|
sb.append(" <extensions>\n");
|
||||||
|
sb.append(" <brouter:info>").append(t.messageList.get(0)).append("</brouter:info>\n");
|
||||||
|
if (t.params != null && t.params.size() > 0) {
|
||||||
|
sb.append(" <brouter:params><![CDATA[");
|
||||||
|
int i = 0;
|
||||||
|
for (Map.Entry<String, String> e : t.params.entrySet()) {
|
||||||
|
if (i++ != 0) sb.append("&");
|
||||||
|
sb.append(e.getKey()).append("=").append(e.getValue());
|
||||||
|
}
|
||||||
|
sb.append("]]></brouter:params>\n");
|
||||||
|
}
|
||||||
|
sb.append(" </extensions>\n");
|
||||||
|
sb.append(" </metadata>\n");
|
||||||
|
}
|
||||||
|
if (turnInstructionMode == 3 || turnInstructionMode == 8) { // osmand style, cruiser
|
||||||
|
float lastRteTime = 0;
|
||||||
|
|
||||||
|
sb.append(" <rte>\n");
|
||||||
|
|
||||||
|
float rteTime = t.getVoiceHintTime(0);
|
||||||
|
StringBuffer first = new StringBuffer();
|
||||||
|
// define start point
|
||||||
|
{
|
||||||
|
first.append(" <rtept lat=\"").append(formatILat(t.nodes.get(0).getILat())).append("\" lon=\"")
|
||||||
|
.append(formatILon(t.nodes.get(0).getILon())).append("\">\n")
|
||||||
|
.append(" <desc>start</desc>\n <extensions>\n");
|
||||||
|
if (rteTime != lastRteTime) { // add timing only if available
|
||||||
|
double ti = rteTime - lastRteTime;
|
||||||
|
first.append(" <time>").append("" + (int) (ti + 0.5)).append("</time>\n");
|
||||||
|
lastRteTime = rteTime;
|
||||||
|
}
|
||||||
|
first.append(" <offset>0</offset>\n </extensions>\n </rtept>\n");
|
||||||
|
}
|
||||||
|
if (turnInstructionMode == 8) {
|
||||||
|
if (t.matchedWaypoints.get(0).direct && t.voiceHints.list.get(0).indexInTrack == 0) {
|
||||||
|
// has a voice hint do nothing, voice hint will do
|
||||||
|
} else {
|
||||||
|
sb.append(first.toString());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append(first.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < t.voiceHints.list.size(); i++) {
|
||||||
|
VoiceHint hint = t.voiceHints.list.get(i);
|
||||||
|
sb.append(" <rtept lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
|
||||||
|
.append(formatILon(hint.ilon)).append("\">\n")
|
||||||
|
.append(" <desc>")
|
||||||
|
.append(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString())
|
||||||
|
.append("</desc>\n <extensions>\n");
|
||||||
|
|
||||||
|
rteTime = t.getVoiceHintTime(i + 1);
|
||||||
|
|
||||||
|
if (rteTime != lastRteTime) { // add timing only if available
|
||||||
|
double ti = rteTime - lastRteTime;
|
||||||
|
sb.append(" <time>").append("" + (int) (ti + 0.5)).append("</time>\n");
|
||||||
|
lastRteTime = rteTime;
|
||||||
|
}
|
||||||
|
sb.append(" <turn>")
|
||||||
|
.append(turnInstructionMode == 3 ? hint.getCommandString() : hint.getCruiserCommandString())
|
||||||
|
.append("</turn>\n <turn-angle>").append("" + (int) hint.angle)
|
||||||
|
.append("</turn-angle>\n <offset>").append("" + hint.indexInTrack).append("</offset>\n </extensions>\n </rtept>\n");
|
||||||
|
}
|
||||||
|
sb.append(" <rtept lat=\"").append(formatILat(t.nodes.get(t.nodes.size() - 1).getILat())).append("\" lon=\"")
|
||||||
|
.append(formatILon(t.nodes.get(t.nodes.size() - 1).getILon())).append("\">\n")
|
||||||
|
.append(" <desc>destination</desc>\n <extensions>\n");
|
||||||
|
sb.append(" <time>0</time>\n");
|
||||||
|
sb.append(" <offset>").append("" + (t.nodes.size() - 1)).append("</offset>\n </extensions>\n </rtept>\n");
|
||||||
|
|
||||||
|
sb.append("</rte>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnInstructionMode == 7) { // old locus style
|
||||||
|
float lastRteTime = t.getVoiceHintTime(0);
|
||||||
|
|
||||||
|
for (int i = 0; i < t.voiceHints.list.size(); i++) {
|
||||||
|
VoiceHint hint = t.voiceHints.list.get(i);
|
||||||
|
sb.append(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
|
||||||
|
.append(formatILat(hint.ilat)).append("\">")
|
||||||
|
.append(hint.selev == Short.MIN_VALUE ? "" : "<ele>" + (hint.selev / 4.) + "</ele>")
|
||||||
|
.append("<name>")
|
||||||
|
.append(hint.getMessageString())
|
||||||
|
.append("</name>")
|
||||||
|
.append("<extensions><locus:rteDistance>").append("" + hint.distanceToNext).append("</locus:rteDistance>");
|
||||||
|
float rteTime = t.getVoiceHintTime(i + 1);
|
||||||
|
if (rteTime != lastRteTime) { // add timing only if available
|
||||||
|
double ti = rteTime - lastRteTime;
|
||||||
|
double speed = hint.distanceToNext / ti;
|
||||||
|
sb.append("<locus:rteTime>").append("" + ti).append("</locus:rteTime>")
|
||||||
|
.append("<locus:rteSpeed>").append("" + speed).append("</locus:rteSpeed>");
|
||||||
|
lastRteTime = rteTime;
|
||||||
|
}
|
||||||
|
sb.append("<locus:rtePointAction>").append("" + hint.getLocusAction()).append("</locus:rtePointAction></extensions>")
|
||||||
|
.append("</wpt>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (turnInstructionMode == 5) { // gpsies style
|
||||||
|
for (VoiceHint hint : t.voiceHints.list) {
|
||||||
|
sb.append(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
|
||||||
|
.append(formatILat(hint.ilat)).append("\">")
|
||||||
|
.append("<name>").append(hint.getMessageString()).append("</name>")
|
||||||
|
.append("<sym>").append(hint.getSymbolString().toLowerCase()).append("</sym>")
|
||||||
|
.append("<type>").append(hint.getSymbolString()).append("</type>")
|
||||||
|
.append("</wpt>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnInstructionMode == 6) { // orux style
|
||||||
|
for (VoiceHint hint : t.voiceHints.list) {
|
||||||
|
sb.append(" <wpt lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
|
||||||
|
.append(formatILon(hint.ilon)).append("\">")
|
||||||
|
.append(hint.selev == Short.MIN_VALUE ? "" : "<ele>" + (hint.selev / 4.) + "</ele>")
|
||||||
|
.append("<extensions>\n" +
|
||||||
|
" <om:oruxmapsextensions xmlns:om=\"http://www.oruxmaps.com/oruxmapsextensions/1/0\">\n" +
|
||||||
|
" <om:ext type=\"ICON\" subtype=\"0\">").append("" + hint.getOruxAction())
|
||||||
|
.append("</om:ext>\n" +
|
||||||
|
" </om:oruxmapsextensions>\n" +
|
||||||
|
" </extensions>\n" +
|
||||||
|
" </wpt>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i <= t.pois.size() - 1; i++) {
|
||||||
|
OsmNodeNamed poi = t.pois.get(i);
|
||||||
|
sb.append(" <wpt lon=\"").append(formatILon(poi.ilon)).append("\" lat=\"")
|
||||||
|
.append(formatILat(poi.ilat)).append("\">\n")
|
||||||
|
.append(" <name>").append(StringUtils.escapeXml10(poi.name)).append("</name>\n")
|
||||||
|
.append(" </wpt>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.exportWaypoints) {
|
||||||
|
for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) {
|
||||||
|
MatchedWaypoint wt = t.matchedWaypoints.get(i);
|
||||||
|
sb.append(" <wpt lon=\"").append(formatILon(wt.waypoint.ilon)).append("\" lat=\"")
|
||||||
|
.append(formatILat(wt.waypoint.ilat)).append("\">\n")
|
||||||
|
.append(" <name>").append(StringUtils.escapeXml10(wt.name)).append("</name>\n");
|
||||||
|
if (i == 0) {
|
||||||
|
sb.append(" <type>from</type>\n");
|
||||||
|
} else if (i == t.matchedWaypoints.size() - 1) {
|
||||||
|
sb.append(" <type>to</type>\n");
|
||||||
|
} else {
|
||||||
|
sb.append(" <type>via</type>\n");
|
||||||
|
}
|
||||||
|
sb.append(" </wpt>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(" <trk>\n");
|
||||||
|
if (turnInstructionMode == 9
|
||||||
|
|| turnInstructionMode == 2
|
||||||
|
|| turnInstructionMode == 8
|
||||||
|
|| turnInstructionMode == 4) { // Locus, comment, cruise, brouter style
|
||||||
|
sb.append(" <src>").append(t.name).append("</src>\n");
|
||||||
|
sb.append(" <type>").append(t.voiceHints.getTransportMode()).append("</type>\n");
|
||||||
|
} else {
|
||||||
|
sb.append(" <name>").append(t.name).append("</name>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnInstructionMode == 7) {
|
||||||
|
sb.append(" <extensions>\n");
|
||||||
|
sb.append(" <locus:rteComputeType>").append("" + t.voiceHints.getLocusRouteType()).append("</locus:rteComputeType>\n");
|
||||||
|
sb.append(" <locus:rteSimpleRoundabouts>1</locus:rteSimpleRoundabouts>\n");
|
||||||
|
sb.append(" </extensions>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// all points
|
||||||
|
sb.append(" <trkseg>\n");
|
||||||
|
String lastway = "";
|
||||||
|
boolean bNextDirect = false;
|
||||||
|
OsmPathElement nn = null;
|
||||||
|
String aSpeed;
|
||||||
|
|
||||||
|
for (int idx = 0; idx < t.nodes.size(); idx++) {
|
||||||
|
OsmPathElement n = t.nodes.get(idx);
|
||||||
|
String sele = n.getSElev() == Short.MIN_VALUE ? "" : "<ele>" + n.getElev() + "</ele>";
|
||||||
|
VoiceHint hint = t.getVoiceHint(idx);
|
||||||
|
MatchedWaypoint mwpt = t.getMatchedWaypoint(idx);
|
||||||
|
|
||||||
|
if (t.showTime) {
|
||||||
|
sele += "<time>" + getFormattedTime3(n.getTime()) + "</time>";
|
||||||
|
}
|
||||||
|
if (turnInstructionMode == 8) {
|
||||||
|
if (mwpt != null &&
|
||||||
|
!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||||
|
sele += "<name>" + mwpt.name + "</name>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean bNeedHeader = false;
|
||||||
|
if (turnInstructionMode == 9) { // trkpt/sym style
|
||||||
|
|
||||||
|
if (hint != null) {
|
||||||
|
|
||||||
|
if (mwpt != null &&
|
||||||
|
!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||||
|
sele += "<name>" + mwpt.name + "</name>";
|
||||||
|
}
|
||||||
|
sele += "<desc>" + hint.getCruiserMessageString() + "</desc>";
|
||||||
|
sele += "<sym>" + hint.getCommandString(hint.cmd) + "</sym>";
|
||||||
|
if (mwpt != null) {
|
||||||
|
sele += "<type>Via</type>";
|
||||||
|
}
|
||||||
|
sele += "<extensions>";
|
||||||
|
if (t.showspeed) {
|
||||||
|
double speed = 0;
|
||||||
|
if (nn != null) {
|
||||||
|
int dist = n.calcDistance(nn);
|
||||||
|
float dt = n.getTime() - nn.getTime();
|
||||||
|
if (dt != 0.f) {
|
||||||
|
speed = ((3.6f * dist) / dt + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sele += "<brouter:speed>" + (((int) (speed * 10)) / 10.f) + "</brouter:speed>";
|
||||||
|
}
|
||||||
|
|
||||||
|
sele += "<brouter:voicehint>" + hint.getCommandString() + ";" + (int) (hint.distanceToNext) + "," + hint.formatGeometry() + "</brouter:voicehint>";
|
||||||
|
if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
|
||||||
|
sele += "<brouter:way>" + n.message.wayKeyValues + "</brouter:way>";
|
||||||
|
lastway = n.message.wayKeyValues;
|
||||||
|
}
|
||||||
|
if (n.message != null && n.message.nodeKeyValues != null) {
|
||||||
|
sele += "<brouter:node>" + n.message.nodeKeyValues + "</brouter:node>";
|
||||||
|
}
|
||||||
|
sele += "</extensions>";
|
||||||
|
|
||||||
|
}
|
||||||
|
if (idx == 0 && hint == null) {
|
||||||
|
if (mwpt != null && mwpt.direct) {
|
||||||
|
sele += "<desc>beeline</desc>";
|
||||||
|
} else {
|
||||||
|
sele += "<desc>start</desc>";
|
||||||
|
}
|
||||||
|
sele += "<type>Via</type>";
|
||||||
|
|
||||||
|
} else if (idx == t.nodes.size() - 1 && hint == null) {
|
||||||
|
|
||||||
|
sele += "<desc>end</desc>";
|
||||||
|
sele += "<type>Via</type>";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (mwpt != null && hint == null) {
|
||||||
|
if (mwpt.direct) {
|
||||||
|
// bNextDirect = true;
|
||||||
|
sele += "<desc>beeline</desc>";
|
||||||
|
} else {
|
||||||
|
sele += "<desc>" + mwpt.name + "</desc>";
|
||||||
|
}
|
||||||
|
sele += "<type>Via</type>";
|
||||||
|
bNextDirect = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (hint == null) {
|
||||||
|
bNeedHeader = (t.showspeed || (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway))) ||
|
||||||
|
(n.message != null && n.message.nodeKeyValues != null);
|
||||||
|
if (bNeedHeader) {
|
||||||
|
sele += "<extensions>";
|
||||||
|
if (t.showspeed) {
|
||||||
|
double speed = 0;
|
||||||
|
if (nn != null) {
|
||||||
|
int dist = n.calcDistance(nn);
|
||||||
|
float dt = n.getTime() - nn.getTime();
|
||||||
|
if (dt != 0.f) {
|
||||||
|
speed = ((3.6f * dist) / dt + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sele += "<brouter:speed>" + (((int) (speed * 10)) / 10.f) + "</brouter:speed>";
|
||||||
|
}
|
||||||
|
if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
|
||||||
|
sele += "<brouter:way>" + n.message.wayKeyValues + "</brouter:way>";
|
||||||
|
lastway = n.message.wayKeyValues;
|
||||||
|
}
|
||||||
|
if (n.message != null && n.message.nodeKeyValues != null) {
|
||||||
|
sele += "<brouter:node>" + n.message.nodeKeyValues + "</brouter:node>";
|
||||||
|
}
|
||||||
|
sele += "</extensions>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnInstructionMode == 2) { // locus style new
|
||||||
|
if (hint != null) {
|
||||||
|
if (mwpt != null) {
|
||||||
|
if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||||
|
sele += "<name>" + mwpt.name + "</name>";
|
||||||
|
}
|
||||||
|
if (mwpt.direct && bNextDirect) {
|
||||||
|
sele += "<src>" + hint.getLocusSymbolString() + "</src><sym>pass_place</sym><type>Shaping</type>";
|
||||||
|
// bNextDirect = false;
|
||||||
|
} else if (mwpt.direct) {
|
||||||
|
if (idx == 0)
|
||||||
|
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||||
|
else
|
||||||
|
sele += "<sym>pass_place</sym><type>Shaping</type>";
|
||||||
|
bNextDirect = true;
|
||||||
|
} else if (bNextDirect) {
|
||||||
|
sele += "<src>beeline</src><sym>" + hint.getLocusSymbolString() + "</sym><type>Shaping</type>";
|
||||||
|
bNextDirect = false;
|
||||||
|
} else {
|
||||||
|
sele += "<sym>" + hint.getLocusSymbolString() + "</sym><type>Via</type>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sele += "<sym>" + hint.getLocusSymbolString() + "</sym>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (idx == 0 && hint == null) {
|
||||||
|
|
||||||
|
int pos = sele.indexOf("<sym");
|
||||||
|
if (pos != -1) {
|
||||||
|
sele = sele.substring(0, pos);
|
||||||
|
}
|
||||||
|
if (mwpt != null && !mwpt.name.startsWith("from"))
|
||||||
|
sele += "<name>" + mwpt.name + "</name>";
|
||||||
|
if (mwpt != null && mwpt.direct) {
|
||||||
|
bNextDirect = true;
|
||||||
|
}
|
||||||
|
sele += "<sym>pass_place</sym>";
|
||||||
|
sele += "<type>Via</type>";
|
||||||
|
|
||||||
|
} else if (idx == t.nodes.size() - 1 && hint == null) {
|
||||||
|
|
||||||
|
int pos = sele.indexOf("<sym");
|
||||||
|
if (pos != -1) {
|
||||||
|
sele = sele.substring(0, pos);
|
||||||
|
}
|
||||||
|
if (mwpt != null && mwpt.name != null && !mwpt.name.startsWith("to"))
|
||||||
|
sele += "<name>" + mwpt.name + "</name>";
|
||||||
|
if (bNextDirect) {
|
||||||
|
sele += "<src>beeline</src>";
|
||||||
|
}
|
||||||
|
sele += "<sym>pass_place</sym>";
|
||||||
|
sele += "<type>Via</type>";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (mwpt != null) {
|
||||||
|
if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||||
|
sele += "<name>" + mwpt.name + "</name>";
|
||||||
|
}
|
||||||
|
if (mwpt.direct && bNextDirect) {
|
||||||
|
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
|
||||||
|
} else if (mwpt.direct) {
|
||||||
|
if (idx == 0)
|
||||||
|
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||||
|
else
|
||||||
|
sele += "<sym>pass_place</sym><type>Shaping</type>";
|
||||||
|
bNextDirect = true;
|
||||||
|
} else if (bNextDirect) {
|
||||||
|
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
|
||||||
|
bNextDirect = false;
|
||||||
|
} else if (mwpt.name.startsWith("via") ||
|
||||||
|
mwpt.name.startsWith("from") ||
|
||||||
|
mwpt.name.startsWith("to")) {
|
||||||
|
if (bNextDirect) {
|
||||||
|
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
|
||||||
|
} else {
|
||||||
|
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||||
|
}
|
||||||
|
bNextDirect = false;
|
||||||
|
} else {
|
||||||
|
sele += "<name>" + mwpt.name + "</name>";
|
||||||
|
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(" <trkpt lon=\"").append(formatILon(n.getILon())).append("\" lat=\"")
|
||||||
|
.append(formatILat(n.getILat())).append("\">").append(sele).append("</trkpt>\n");
|
||||||
|
|
||||||
|
nn = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(" </trkseg>\n");
|
||||||
|
sb.append(" </trk>\n");
|
||||||
|
sb.append("</gpx>\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatAsWaypoint(OsmNodeNamed n) {
|
||||||
|
try {
|
||||||
|
StringWriter sw = new StringWriter(8192);
|
||||||
|
BufferedWriter bw = new BufferedWriter(sw);
|
||||||
|
formatGpxHeader(bw);
|
||||||
|
formatWaypointGpx(bw, n);
|
||||||
|
formatGpxFooter(bw);
|
||||||
|
bw.close();
|
||||||
|
sw.close();
|
||||||
|
return sw.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void formatGpxHeader(BufferedWriter sb) throws IOException {
|
||||||
|
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||||
|
sb.append("<gpx \n");
|
||||||
|
sb.append(" xmlns=\"http://www.topografix.com/GPX/1/1\" \n");
|
||||||
|
sb.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n");
|
||||||
|
sb.append(" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n");
|
||||||
|
sb.append(" creator=\"BRouter-" + OsmTrack.version + "\" version=\"1.1\">\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void formatGpxFooter(BufferedWriter sb) throws IOException {
|
||||||
|
sb.append("</gpx>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void formatWaypointGpx(BufferedWriter sb, OsmNodeNamed n) throws IOException {
|
||||||
|
sb.append(" <wpt lon=\"").append(formatILon(n.ilon)).append("\" lat=\"")
|
||||||
|
.append(formatILat(n.ilat)).append("\">");
|
||||||
|
if (n.getSElev() != Short.MIN_VALUE) {
|
||||||
|
sb.append("<ele>").append("" + n.getElev()).append("</ele>");
|
||||||
|
}
|
||||||
|
if (n.name != null) {
|
||||||
|
sb.append("<name>").append(StringUtils.escapeXml10(n.name)).append("</name>");
|
||||||
|
}
|
||||||
|
if (n.nodeDescription != null && rc != null) {
|
||||||
|
sb.append("<desc>").append(rc.expctxWay.getKeyValueDescription(false, n.nodeDescription)).append("</desc>");
|
||||||
|
}
|
||||||
|
sb.append("</wpt>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getWaypoint(int ilon, int ilat, String name, String desc) {
|
||||||
|
return "<wpt lon=\"" + formatILon(ilon) + "\" lat=\"" + formatILat(ilat) + "\"><name>" + name + "</name>" + (desc != null ? "<desc>" + desc + "</desc>" : "") + "</wpt>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsmTrack read(String filename) throws Exception {
|
||||||
|
File f = new File(filename);
|
||||||
|
if (!f.exists()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
OsmTrack track = new OsmTrack();
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
|
||||||
|
|
||||||
|
for (; ; ) {
|
||||||
|
String line = br.readLine();
|
||||||
|
if (line == null)
|
||||||
|
break;
|
||||||
|
|
||||||
|
int idx0 = line.indexOf("<trkpt ");
|
||||||
|
if (idx0 >= 0) {
|
||||||
|
idx0 = line.indexOf(" lon=\"");
|
||||||
|
idx0 += 6;
|
||||||
|
int idx1 = line.indexOf('"', idx0);
|
||||||
|
int ilon = (int) ((Double.parseDouble(line.substring(idx0, idx1)) + 180.) * 1000000. + 0.5);
|
||||||
|
int idx2 = line.indexOf(" lat=\"");
|
||||||
|
if (idx2 < 0)
|
||||||
|
continue;
|
||||||
|
idx2 += 6;
|
||||||
|
int idx3 = line.indexOf('"', idx2);
|
||||||
|
int ilat = (int) ((Double.parseDouble(line.substring(idx2, idx3)) + 90.) * 1000000. + 0.5);
|
||||||
|
track.nodes.add(OsmPathElement.create(ilon, ilat, (short) 0, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
br.close();
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
246
brouter-core/src/main/java/btools/router/FormatJson.java
Normal file
246
brouter-core/src/main/java/btools/router/FormatJson.java
Normal file
|
|
@ -0,0 +1,246 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import btools.mapaccess.MatchedWaypoint;
|
||||||
|
import btools.util.StringUtils;
|
||||||
|
|
||||||
|
public class FormatJson extends Formatter {
|
||||||
|
|
||||||
|
public FormatJson(RoutingContext rc) {
|
||||||
|
super(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(OsmTrack t) {
|
||||||
|
int turnInstructionMode = t.voiceHints != null ? t.voiceHints.turnInstructionMode : 0;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(8192);
|
||||||
|
|
||||||
|
sb.append("{\n");
|
||||||
|
sb.append(" \"type\": \"FeatureCollection\",\n");
|
||||||
|
sb.append(" \"features\": [\n");
|
||||||
|
sb.append(" {\n");
|
||||||
|
sb.append(" \"type\": \"Feature\",\n");
|
||||||
|
sb.append(" \"properties\": {\n");
|
||||||
|
sb.append(" \"creator\": \"BRouter-" + t.version + "\",\n");
|
||||||
|
sb.append(" \"name\": \"").append(t.name).append("\",\n");
|
||||||
|
sb.append(" \"track-length\": \"").append(t.distance).append("\",\n");
|
||||||
|
sb.append(" \"filtered ascend\": \"").append(t.ascend).append("\",\n");
|
||||||
|
sb.append(" \"plain-ascend\": \"").append(t.plainAscend).append("\",\n");
|
||||||
|
sb.append(" \"total-time\": \"").append(t.getTotalSeconds()).append("\",\n");
|
||||||
|
sb.append(" \"total-energy\": \"").append(t.energy).append("\",\n");
|
||||||
|
sb.append(" \"cost\": \"").append(t.cost).append("\",\n");
|
||||||
|
if (t.voiceHints != null && !t.voiceHints.list.isEmpty()) {
|
||||||
|
sb.append(" \"voicehints\": [\n");
|
||||||
|
for (VoiceHint hint : t.voiceHints.list) {
|
||||||
|
sb.append(" [");
|
||||||
|
sb.append(hint.indexInTrack);
|
||||||
|
sb.append(',').append(hint.getJsonCommandIndex());
|
||||||
|
sb.append(',').append(hint.getExitNumber());
|
||||||
|
sb.append(',').append(hint.distanceToNext);
|
||||||
|
sb.append(',').append((int) hint.angle);
|
||||||
|
|
||||||
|
// not always include geometry because longer and only needed for comment style
|
||||||
|
if (turnInstructionMode == 4) { // comment style
|
||||||
|
sb.append(",\"").append(hint.formatGeometry()).append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("],\n");
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||||
|
sb.append(" ],\n");
|
||||||
|
}
|
||||||
|
if (t.showSpeedProfile) { // set in profile
|
||||||
|
List<String> sp = t.aggregateSpeedProfile();
|
||||||
|
if (sp.size() > 0) {
|
||||||
|
sb.append(" \"speedprofile\": [\n");
|
||||||
|
for (int i = sp.size() - 1; i >= 0; i--) {
|
||||||
|
sb.append(" [").append(sp.get(i)).append(i > 0 ? "],\n" : "]\n");
|
||||||
|
}
|
||||||
|
sb.append(" ],\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ... traditional message list
|
||||||
|
{
|
||||||
|
sb.append(" \"messages\": [\n");
|
||||||
|
sb.append(" [\"").append(MESSAGES_HEADER.replaceAll("\t", "\", \"")).append("\"],\n");
|
||||||
|
for (String m : t.aggregateMessages()) {
|
||||||
|
sb.append(" [\"").append(m.replaceAll("\t", "\", \"")).append("\"],\n");
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||||
|
sb.append(" ],\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.getTotalSeconds() > 0) {
|
||||||
|
sb.append(" \"times\": [");
|
||||||
|
DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);
|
||||||
|
decimalFormat.applyPattern("0.###");
|
||||||
|
for (OsmPathElement n : t.nodes) {
|
||||||
|
sb.append(decimalFormat.format(n.getTime())).append(",");
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||||
|
sb.append("]\n");
|
||||||
|
} else {
|
||||||
|
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(" },\n");
|
||||||
|
|
||||||
|
if (t.iternity != null) {
|
||||||
|
sb.append(" \"iternity\": [\n");
|
||||||
|
for (String s : t.iternity) {
|
||||||
|
sb.append(" \"").append(s).append("\",\n");
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||||
|
sb.append(" ],\n");
|
||||||
|
}
|
||||||
|
sb.append(" \"geometry\": {\n");
|
||||||
|
sb.append(" \"type\": \"LineString\",\n");
|
||||||
|
sb.append(" \"coordinates\": [\n");
|
||||||
|
|
||||||
|
OsmPathElement nn = null;
|
||||||
|
for (OsmPathElement n : t.nodes) {
|
||||||
|
String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev();
|
||||||
|
if (t.showspeed) { // hack: show speed instead of elevation
|
||||||
|
double speed = 0;
|
||||||
|
if (nn != null) {
|
||||||
|
int dist = n.calcDistance(nn);
|
||||||
|
float dt = n.getTime() - nn.getTime();
|
||||||
|
if (dt != 0.f) {
|
||||||
|
speed = ((3.6f * dist) / dt + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sele = ", " + (((int) (speed * 10)) / 10.f);
|
||||||
|
}
|
||||||
|
sb.append(" [").append(formatILon(n.getILon())).append(", ").append(formatILat(n.getILat()))
|
||||||
|
.append(sele).append("],\n");
|
||||||
|
nn = n;
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||||
|
|
||||||
|
sb.append(" ]\n");
|
||||||
|
sb.append(" }\n");
|
||||||
|
if (t.exportWaypoints || !t.pois.isEmpty()) {
|
||||||
|
sb.append(" },\n");
|
||||||
|
for (int i = 0; i <= t.pois.size() - 1; i++) {
|
||||||
|
OsmNodeNamed poi = t.pois.get(i);
|
||||||
|
addFeature(sb, "poi", poi.name, poi.ilat, poi.ilon);
|
||||||
|
if (i < t.matchedWaypoints.size() - 1) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append(" \n");
|
||||||
|
}
|
||||||
|
if (t.exportWaypoints) {
|
||||||
|
for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) {
|
||||||
|
String type;
|
||||||
|
if (i == 0) {
|
||||||
|
type = "from";
|
||||||
|
} else if (i == t.matchedWaypoints.size() - 1) {
|
||||||
|
type = "to";
|
||||||
|
} else {
|
||||||
|
type = "via";
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchedWaypoint wp = t.matchedWaypoints.get(i);
|
||||||
|
addFeature(sb, type, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
|
||||||
|
if (i < t.matchedWaypoints.size() - 1) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
sb.append(" \n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.append(" }\n");
|
||||||
|
}
|
||||||
|
sb.append(" ]\n");
|
||||||
|
sb.append("}\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFeature(StringBuilder sb, String type, String name, int ilat, int ilon) {
|
||||||
|
sb.append(" {\n");
|
||||||
|
sb.append(" \"type\": \"Feature\",\n");
|
||||||
|
sb.append(" \"properties\": {\n");
|
||||||
|
sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n");
|
||||||
|
sb.append(" \"type\": \"" + type + "\"\n");
|
||||||
|
sb.append(" },\n");
|
||||||
|
sb.append(" \"geometry\": {\n");
|
||||||
|
sb.append(" \"type\": \"Point\",\n");
|
||||||
|
sb.append(" \"coordinates\": [\n");
|
||||||
|
sb.append(" " + formatILon(ilon) + ",\n");
|
||||||
|
sb.append(" " + formatILat(ilat) + "\n");
|
||||||
|
sb.append(" ]\n");
|
||||||
|
sb.append(" }\n");
|
||||||
|
sb.append(" }");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatAsWaypoint(OsmNodeNamed n) {
|
||||||
|
try {
|
||||||
|
StringWriter sw = new StringWriter(8192);
|
||||||
|
BufferedWriter bw = new BufferedWriter(sw);
|
||||||
|
addJsonHeader(bw);
|
||||||
|
addJsonFeature(bw, "info", "wpinfo", n.ilon, n.ilat, n.getElev(), (n.nodeDescription != null ? rc.expctxWay.getKeyValueDescription(false, n.nodeDescription) : null));
|
||||||
|
addJsonFooter(bw);
|
||||||
|
bw.close();
|
||||||
|
sw.close();
|
||||||
|
return sw.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addJsonFeature(BufferedWriter sb, String type, String name, int ilon, int ilat, double elev, String desc) {
|
||||||
|
try {
|
||||||
|
sb.append(" {\n");
|
||||||
|
sb.append(" \"type\": \"Feature\",\n");
|
||||||
|
sb.append(" \"properties\": {\n");
|
||||||
|
sb.append(" \"creator\": \"BRouter-" + OsmTrack.version + "\",\n");
|
||||||
|
sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n");
|
||||||
|
sb.append(" \"type\": \"" + type + "\"");
|
||||||
|
if (desc != null) {
|
||||||
|
sb.append(",\n \"message\": \"" + desc + "\"\n");
|
||||||
|
} else {
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
sb.append(" },\n");
|
||||||
|
sb.append(" \"geometry\": {\n");
|
||||||
|
sb.append(" \"type\": \"Point\",\n");
|
||||||
|
sb.append(" \"coordinates\": [\n");
|
||||||
|
sb.append(" " + formatILon(ilon) + ",\n");
|
||||||
|
sb.append(" " + formatILat(ilat) + ",\n");
|
||||||
|
sb.append(" " + elev + "\n");
|
||||||
|
sb.append(" ]\n");
|
||||||
|
sb.append(" }\n");
|
||||||
|
sb.append(" }\n");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addJsonHeader(BufferedWriter sb) {
|
||||||
|
try {
|
||||||
|
sb.append("{\n");
|
||||||
|
sb.append(" \"type\": \"FeatureCollection\",\n");
|
||||||
|
sb.append(" \"features\": [\n");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addJsonFooter(BufferedWriter sb) {
|
||||||
|
try {
|
||||||
|
sb.append(" ]\n");
|
||||||
|
sb.append("}\n");
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
91
brouter-core/src/main/java/btools/router/FormatKml.java
Normal file
91
brouter-core/src/main/java/btools/router/FormatKml.java
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import btools.mapaccess.MatchedWaypoint;
|
||||||
|
import btools.util.StringUtils;
|
||||||
|
|
||||||
|
public class FormatKml extends Formatter {
|
||||||
|
public FormatKml(RoutingContext rc) {
|
||||||
|
super(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String format(OsmTrack t) {
|
||||||
|
StringBuilder sb = new StringBuilder(8192);
|
||||||
|
|
||||||
|
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||||
|
|
||||||
|
sb.append("<kml xmlns=\"http://earth.google.com/kml/2.0\">\n");
|
||||||
|
sb.append(" <Document>\n");
|
||||||
|
sb.append(" <name>KML Samples</name>\n");
|
||||||
|
sb.append(" <open>1</open>\n");
|
||||||
|
sb.append(" <distance>3.497064</distance>\n");
|
||||||
|
sb.append(" <traveltime>872</traveltime>\n");
|
||||||
|
sb.append(" <description>To enable simple instructions add: 'instructions=1' as parameter to the URL</description>\n");
|
||||||
|
sb.append(" <Folder>\n");
|
||||||
|
sb.append(" <name>Paths</name>\n");
|
||||||
|
sb.append(" <visibility>0</visibility>\n");
|
||||||
|
sb.append(" <description>Examples of paths.</description>\n");
|
||||||
|
sb.append(" <Placemark>\n");
|
||||||
|
sb.append(" <name>Tessellated</name>\n");
|
||||||
|
sb.append(" <visibility>0</visibility>\n");
|
||||||
|
sb.append(" <description><![CDATA[If the <tessellate> tag has a value of 1, the line will contour to the underlying terrain]]></description>\n");
|
||||||
|
sb.append(" <LineString>\n");
|
||||||
|
sb.append(" <tessellate>1</tessellate>\n");
|
||||||
|
sb.append(" <coordinates>");
|
||||||
|
|
||||||
|
for (OsmPathElement n : t.nodes) {
|
||||||
|
sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(" </coordinates>\n");
|
||||||
|
sb.append(" </LineString>\n");
|
||||||
|
sb.append(" </Placemark>\n");
|
||||||
|
sb.append(" </Folder>\n");
|
||||||
|
if (t.exportWaypoints || !t.pois.isEmpty()) {
|
||||||
|
if (!t.pois.isEmpty()) {
|
||||||
|
sb.append(" <Folder>\n");
|
||||||
|
sb.append(" <name>poi</name>\n");
|
||||||
|
for (int i = 0; i < t.pois.size(); i++) {
|
||||||
|
OsmNodeNamed poi = t.pois.get(i);
|
||||||
|
createPlaceMark(sb, poi.name, poi.ilat, poi.ilon);
|
||||||
|
}
|
||||||
|
sb.append(" </Folder>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t.exportWaypoints) {
|
||||||
|
int size = t.matchedWaypoints.size();
|
||||||
|
createFolder(sb, "start", t.matchedWaypoints.subList(0, 1));
|
||||||
|
if (t.matchedWaypoints.size() > 2) {
|
||||||
|
createFolder(sb, "via", t.matchedWaypoints.subList(1, size - 1));
|
||||||
|
}
|
||||||
|
createFolder(sb, "end", t.matchedWaypoints.subList(size - 1, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(" </Document>\n");
|
||||||
|
sb.append("</kml>\n");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFolder(StringBuilder sb, String type, List<MatchedWaypoint> waypoints) {
|
||||||
|
sb.append(" <Folder>\n");
|
||||||
|
sb.append(" <name>" + type + "</name>\n");
|
||||||
|
for (int i = 0; i < waypoints.size(); i++) {
|
||||||
|
MatchedWaypoint wp = waypoints.get(i);
|
||||||
|
createPlaceMark(sb, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
|
||||||
|
}
|
||||||
|
sb.append(" </Folder>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) {
|
||||||
|
sb.append(" <Placemark>\n");
|
||||||
|
sb.append(" <name>" + StringUtils.escapeXml10(name) + "</name>\n");
|
||||||
|
sb.append(" <Point>\n");
|
||||||
|
sb.append(" <coordinates>" + formatILon(ilon) + "," + formatILat(ilat) + "</coordinates>\n");
|
||||||
|
sb.append(" </Point>\n");
|
||||||
|
sb.append(" </Placemark>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
110
brouter-core/src/main/java/btools/router/Formatter.java
Normal file
110
brouter-core/src/main/java/btools/router/Formatter.java
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public abstract class Formatter {
|
||||||
|
|
||||||
|
static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy";
|
||||||
|
|
||||||
|
RoutingContext rc;
|
||||||
|
|
||||||
|
Formatter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Formatter(RoutingContext rc) {
|
||||||
|
this.rc = rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes the track in gpx-format to a file
|
||||||
|
*
|
||||||
|
* @param filename the filename to write to
|
||||||
|
* @param t the track to write
|
||||||
|
*/
|
||||||
|
public void write(String filename, OsmTrack t) throws Exception {
|
||||||
|
BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
|
||||||
|
bw.write(format(t));
|
||||||
|
bw.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsmTrack read(String filename) throws Exception {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* writes the track in a selected output format to a string
|
||||||
|
*
|
||||||
|
* @param t the track to format
|
||||||
|
* @return the formatted string
|
||||||
|
*/
|
||||||
|
public abstract String format(OsmTrack t);
|
||||||
|
|
||||||
|
|
||||||
|
static String formatILon(int ilon) {
|
||||||
|
return formatPos(ilon - 180000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String formatILat(int ilat) {
|
||||||
|
return formatPos(ilat - 90000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatPos(int p) {
|
||||||
|
boolean negative = p < 0;
|
||||||
|
if (negative)
|
||||||
|
p = -p;
|
||||||
|
char[] ac = new char[12];
|
||||||
|
int i = 11;
|
||||||
|
while (p != 0 || i > 3) {
|
||||||
|
ac[i--] = (char) ('0' + (p % 10));
|
||||||
|
p /= 10;
|
||||||
|
if (i == 5)
|
||||||
|
ac[i--] = '.';
|
||||||
|
}
|
||||||
|
if (negative)
|
||||||
|
ac[i--] = '-';
|
||||||
|
return new String(ac, i + 1, 11 - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFormattedTime2(int s) {
|
||||||
|
int seconds = (int) (s + 0.5);
|
||||||
|
int hours = seconds / 3600;
|
||||||
|
int minutes = (seconds - hours * 3600) / 60;
|
||||||
|
seconds = seconds - hours * 3600 - minutes * 60;
|
||||||
|
String time = "";
|
||||||
|
if (hours != 0)
|
||||||
|
time = "" + hours + "h ";
|
||||||
|
if (minutes != 0)
|
||||||
|
time = time + minutes + "m ";
|
||||||
|
if (seconds != 0)
|
||||||
|
time = time + seconds + "s";
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public String getFormattedEnergy(int energy) {
|
||||||
|
return format1(energy / 3600000.) + "kwh";
|
||||||
|
}
|
||||||
|
|
||||||
|
static private String format1(double n) {
|
||||||
|
String s = "" + (long) (n * 10 + 0.5);
|
||||||
|
int len = s.length();
|
||||||
|
return s.substring(0, len - 1) + "." + s.charAt(len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static final String dateformat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
||||||
|
|
||||||
|
static public String getFormattedTime3(float time) {
|
||||||
|
SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat(dateformat, Locale.US);
|
||||||
|
TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
// yyyy-mm-ddThh:mm:ss.SSSZ
|
||||||
|
Date d = new Date((long) (time * 1000f));
|
||||||
|
return TIMESTAMP_FORMAT.format(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -11,15 +11,12 @@ import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
|
|
||||||
|
|
||||||
final class KinematicModel extends OsmPathModel
|
final class KinematicModel extends OsmPathModel {
|
||||||
{
|
public OsmPrePath createPrePath() {
|
||||||
public OsmPrePath createPrePath()
|
|
||||||
{
|
|
||||||
return new KinematicPrePath();
|
return new KinematicPrePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath()
|
public OsmPath createPath() {
|
||||||
{
|
|
||||||
return new KinematicPath();
|
return new KinematicPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,10 +52,8 @@ final class KinematicModel extends OsmPathModel
|
||||||
private double lastBreakingSpeed;
|
private double lastBreakingSpeed;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> extraParams )
|
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> extraParams) {
|
||||||
{
|
if (!initDone) {
|
||||||
if ( !initDone )
|
|
||||||
{
|
|
||||||
ctxWay = expctxWay;
|
ctxWay = expctxWay;
|
||||||
ctxNode = expctxNode;
|
ctxNode = expctxNode;
|
||||||
wayIdxMaxspeed = ctxWay.getOutputVariableIndex("maxspeed", false);
|
wayIdxMaxspeed = ctxWay.getOutputVariableIndex("maxspeed", false);
|
||||||
|
|
@ -86,46 +81,38 @@ final class KinematicModel extends OsmPathModel
|
||||||
cost0 = (pw + p_standby) / vmax + f_roll + f_air * vmax * vmax;
|
cost0 = (pw + p_standby) / vmax + f_roll + f_air * vmax * vmax;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected float getParam( String name, float defaultValue )
|
protected float getParam(String name, float defaultValue) {
|
||||||
{
|
|
||||||
String sval = params == null ? null : params.get(name);
|
String sval = params == null ? null : params.get(name);
|
||||||
if ( sval != null )
|
if (sval != null) {
|
||||||
{
|
|
||||||
return Float.parseFloat(sval);
|
return Float.parseFloat(sval);
|
||||||
}
|
}
|
||||||
float v = ctxWay.getVariableValue(name, defaultValue);
|
float v = ctxWay.getVariableValue(name, defaultValue);
|
||||||
if ( params != null )
|
if (params != null) {
|
||||||
{
|
|
||||||
params.put(name, "" + v);
|
params.put(name, "" + v);
|
||||||
}
|
}
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getWayMaxspeed()
|
public float getWayMaxspeed() {
|
||||||
{
|
|
||||||
return ctxWay.getBuildInVariable(wayIdxMaxspeed) / 3.6f;
|
return ctxWay.getBuildInVariable(wayIdxMaxspeed) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getWayMaxspeedExplicit()
|
public float getWayMaxspeedExplicit() {
|
||||||
{
|
|
||||||
return ctxWay.getBuildInVariable(wayIdxMaxspeedExplicit) / 3.6f;
|
return ctxWay.getBuildInVariable(wayIdxMaxspeedExplicit) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getWayMinspeed()
|
public float getWayMinspeed() {
|
||||||
{
|
|
||||||
return ctxWay.getBuildInVariable(wayIdxMinspeed) / 3.6f;
|
return ctxWay.getBuildInVariable(wayIdxMinspeed) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getNodeMaxspeed()
|
public float getNodeMaxspeed() {
|
||||||
{
|
|
||||||
return ctxNode.getBuildInVariable(nodeIdxMaxspeed) / 3.6f;
|
return ctxNode.getBuildInVariable(nodeIdxMaxspeed) / 3.6f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the effective speed limit from the way-limit and vmax/vmin
|
* get the effective speed limit from the way-limit and vmax/vmin
|
||||||
*/
|
*/
|
||||||
public double getEffectiveSpeedLimit( )
|
public double getEffectiveSpeedLimit() {
|
||||||
{
|
|
||||||
// performance related inline coding
|
// performance related inline coding
|
||||||
double minspeed = getWayMinspeed();
|
double minspeed = getWayMinspeed();
|
||||||
double espeed = minspeed > vmax ? minspeed : vmax;
|
double espeed = minspeed > vmax ? minspeed : vmax;
|
||||||
|
|
@ -136,10 +123,8 @@ final class KinematicModel extends OsmPathModel
|
||||||
/**
|
/**
|
||||||
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
|
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
|
||||||
*/
|
*/
|
||||||
public double getBreakingSpeed( double vl )
|
public double getBreakingSpeed(double vl) {
|
||||||
{
|
if (vl == lastEffectiveLimit) {
|
||||||
if ( vl == lastEffectiveLimit )
|
|
||||||
{
|
|
||||||
return lastBreakingSpeed;
|
return lastBreakingSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,8 +132,7 @@ final class KinematicModel extends OsmPathModel
|
||||||
double pw2 = pw + p_standby;
|
double pw2 = pw + p_standby;
|
||||||
double e = recup_efficiency;
|
double e = recup_efficiency;
|
||||||
double x0 = pw2 / vl + f_air * e * vl * vl + (1. - e) * f_roll;
|
double x0 = pw2 / vl + f_air * e * vl * vl + (1. - e) * f_roll;
|
||||||
for(int i=0;i<5;i++)
|
for (int i = 0; i < 5; i++) {
|
||||||
{
|
|
||||||
double v2 = v * v;
|
double v2 = v * v;
|
||||||
double x = pw2 / v + f_air * e * v2 - x0;
|
double x = pw2 / v + f_air * e * v2 - x0;
|
||||||
double dx = 2. * e * f_air * v - pw2 / v2;
|
double dx = 2. * e * f_air * v - pw2 / v2;
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
import btools.util.FastMath;
|
final class KinematicPath extends OsmPath {
|
||||||
|
|
||||||
|
|
||||||
final class KinematicPath extends OsmPath
|
|
||||||
{
|
|
||||||
private double ekin; // kinetic energy (Joule)
|
private double ekin; // kinetic energy (Joule)
|
||||||
private double totalTime; // travel time (seconds)
|
private double totalTime; // travel time (seconds)
|
||||||
private double totalEnergy; // total route energy (Joule)
|
private double totalEnergy; // total route energy (Joule)
|
||||||
|
|
@ -17,20 +13,17 @@ final class KinematicPath extends OsmPath
|
||||||
private float floatingAngleRight; // sliding average right bend (degree)
|
private float floatingAngleRight; // sliding average right bend (degree)
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init( OsmPath orig )
|
protected void init(OsmPath orig) {
|
||||||
{
|
|
||||||
KinematicPath origin = (KinematicPath) orig;
|
KinematicPath origin = (KinematicPath) orig;
|
||||||
ekin = origin.ekin;
|
ekin = origin.ekin;
|
||||||
totalTime = origin.totalTime;
|
totalTime = origin.totalTime;
|
||||||
totalEnergy = origin.totalEnergy;
|
totalEnergy = origin.totalEnergy;
|
||||||
floatingAngleLeft = origin.floatingAngleLeft;
|
floatingAngleLeft = origin.floatingAngleLeft;
|
||||||
floatingAngleRight = origin.floatingAngleRight;
|
floatingAngleRight = origin.floatingAngleRight;
|
||||||
priorityclassifier = origin.priorityclassifier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetState()
|
protected void resetState() {
|
||||||
{
|
|
||||||
ekin = 0.;
|
ekin = 0.;
|
||||||
totalTime = 0.;
|
totalTime = 0.;
|
||||||
totalEnergy = 0.;
|
totalEnergy = 0.;
|
||||||
|
|
@ -39,45 +32,37 @@ final class KinematicPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
|
protected double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
|
||||||
{
|
|
||||||
KinematicModel km = (KinematicModel) rc.pm;
|
KinematicModel km = (KinematicModel) rc.pm;
|
||||||
|
|
||||||
double cost = 0.;
|
double cost = 0.;
|
||||||
double extraTime = 0.;
|
double extraTime = 0.;
|
||||||
|
|
||||||
if ( isStartpoint )
|
if (isStartpoint) {
|
||||||
{
|
|
||||||
// for forward direction, we start with target speed
|
// for forward direction, we start with target speed
|
||||||
if ( !rc.inverseDirection )
|
if (!rc.inverseDirection) {
|
||||||
{
|
|
||||||
extraTime = 0.5 * (1. - cosangle) * 40.; // 40 seconds turn penalty
|
extraTime = 0.5 * (1. - cosangle) * 40.; // 40 seconds turn penalty
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
double turnspeed = 999.; // just high
|
double turnspeed = 999.; // just high
|
||||||
|
|
||||||
if ( km.turnAngleDecayTime != 0. ) // process turn-angle slowdown
|
if (km.turnAngleDecayTime != 0.) { // process turn-angle slowdown
|
||||||
{
|
|
||||||
if (angle < 0) floatingAngleLeft -= (float) angle;
|
if (angle < 0) floatingAngleLeft -= (float) angle;
|
||||||
else floatingAngleRight += (float) angle;
|
else floatingAngleRight += (float) angle;
|
||||||
float aa = Math.max(floatingAngleLeft, floatingAngleRight);
|
float aa = Math.max(floatingAngleLeft, floatingAngleRight);
|
||||||
|
|
||||||
double curveSpeed = aa > 10. ? 200. / aa : 20.;
|
double curveSpeed = aa > 10. ? 200. / aa : 20.;
|
||||||
double distanceTime = dist / curveSpeed;
|
double distanceTime = dist / curveSpeed;
|
||||||
double decayFactor = FastMath.exp( - distanceTime / km.turnAngleDecayTime );
|
double decayFactor = Math.exp(-distanceTime / km.turnAngleDecayTime);
|
||||||
floatingAngleLeft = (float) (floatingAngleLeft * decayFactor);
|
floatingAngleLeft = (float) (floatingAngleLeft * decayFactor);
|
||||||
floatingAngleRight = (float) (floatingAngleRight * decayFactor);
|
floatingAngleRight = (float) (floatingAngleRight * decayFactor);
|
||||||
|
|
||||||
if ( curveSpeed < 20. )
|
if (curveSpeed < 20.) {
|
||||||
{
|
|
||||||
turnspeed = curveSpeed;
|
turnspeed = curveSpeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( nsection == 0 ) // process slowdown by crossing geometry
|
if (nsection == 0) { // process slowdown by crossing geometry
|
||||||
{
|
|
||||||
double junctionspeed = 999.; // just high
|
double junctionspeed = 999.; // just high
|
||||||
|
|
||||||
int classifiermask = (int) rc.expctxWay.getClassifierMask();
|
int classifiermask = (int) rc.expctxWay.getClassifierMask();
|
||||||
|
|
@ -86,22 +71,18 @@ final class KinematicPath extends OsmPath
|
||||||
boolean hasLeftWay = false;
|
boolean hasLeftWay = false;
|
||||||
boolean hasRightWay = false;
|
boolean hasRightWay = false;
|
||||||
boolean hasResidential = false;
|
boolean hasResidential = false;
|
||||||
for( OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next )
|
for (OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next) {
|
||||||
{
|
|
||||||
KinematicPrePath pp = (KinematicPrePath) prePath;
|
KinematicPrePath pp = (KinematicPrePath) prePath;
|
||||||
|
|
||||||
if ( ( (pp.classifiermask ^ classifiermask) & 8 ) != 0 ) // exactly one is linktype
|
if (((pp.classifiermask ^ classifiermask) & 8) != 0) { // exactly one is linktype
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
|
if ((pp.classifiermask & 32) != 0) { // touching a residential?
|
||||||
{
|
|
||||||
hasResidential = true;
|
hasResidential = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20 )
|
if (pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20) {
|
||||||
{
|
|
||||||
double diff = pp.angle - angle;
|
double diff = pp.angle - angle;
|
||||||
if (diff < -40. && diff > -140.) hasLeftWay = true;
|
if (diff < -40. && diff > -140.) hasLeftWay = true;
|
||||||
if (diff > 40. && diff < 140.) hasRightWay = true;
|
if (diff > 40. && diff < 140.) hasRightWay = true;
|
||||||
|
|
@ -113,20 +94,17 @@ final class KinematicPath extends OsmPath
|
||||||
if (hasRightWay && junctionspeed > km.rightWaySpeed) junctionspeed = km.rightWaySpeed;
|
if (hasRightWay && junctionspeed > km.rightWaySpeed) junctionspeed = km.rightWaySpeed;
|
||||||
if (hasResidential && junctionspeed > residentialSpeed) junctionspeed = residentialSpeed;
|
if (hasResidential && junctionspeed > residentialSpeed) junctionspeed = residentialSpeed;
|
||||||
|
|
||||||
if ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) )
|
if ((lastpriorityclassifier < 20) ^ (priorityclassifier < 20)) {
|
||||||
{
|
|
||||||
extraTime += 10.;
|
extraTime += 10.;
|
||||||
junctionspeed = 0; // full stop for entering or leaving road network
|
junctionspeed = 0; // full stop for entering or leaving road network
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0 )
|
if (lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0) {
|
||||||
{
|
|
||||||
extraTime += 2.; // two seconds for entering a link-type
|
extraTime += 2.; // two seconds for entering a link-type
|
||||||
}
|
}
|
||||||
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
|
turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed;
|
||||||
|
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.vnode0 = (int) (junctionspeed * 3.6 + 0.5);
|
message.vnode0 = (int) (junctionspeed * 3.6 + 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,8 +121,7 @@ final class KinematicPath extends OsmPath
|
||||||
|
|
||||||
double distanceCost = evolveDistance(km, dist, delta_h, f_air);
|
double distanceCost = evolveDistance(km, dist, delta_h, f_air);
|
||||||
|
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.costfactor = (float) (distanceCost / dist);
|
message.costfactor = (float) (distanceCost / dist);
|
||||||
message.vmax = (int) (km.getWayMaxspeed() * 3.6 + 0.5);
|
message.vmax = (int) (km.getWayMaxspeed() * 3.6 + 0.5);
|
||||||
message.vmaxExplicit = (int) (km.getWayMaxspeedExplicit() * 3.6 + 0.5);
|
message.vmaxExplicit = (int) (km.getWayMaxspeedExplicit() * 3.6 + 0.5);
|
||||||
|
|
@ -159,15 +136,13 @@ final class KinematicPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected double evolveDistance( KinematicModel km, double dist, double delta_h, double f_air )
|
protected double evolveDistance(KinematicModel km, double dist, double delta_h, double f_air) {
|
||||||
{
|
|
||||||
// elevation force
|
// elevation force
|
||||||
double fh = delta_h * km.totalweight * 9.81 / dist;
|
double fh = delta_h * km.totalweight * 9.81 / dist;
|
||||||
|
|
||||||
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
|
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
|
||||||
double emax = 0.5 * km.totalweight * effectiveSpeedLimit * effectiveSpeedLimit;
|
double emax = 0.5 * km.totalweight * effectiveSpeedLimit * effectiveSpeedLimit;
|
||||||
if ( emax <= 0. )
|
if (emax <= 0.) {
|
||||||
{
|
|
||||||
return -1.;
|
return -1.;
|
||||||
}
|
}
|
||||||
double vb = km.getBreakingSpeed(effectiveSpeedLimit);
|
double vb = km.getBreakingSpeed(effectiveSpeedLimit);
|
||||||
|
|
@ -178,8 +153,7 @@ final class KinematicPath extends OsmPath
|
||||||
|
|
||||||
double v = Math.sqrt(2. * ekin / km.totalweight);
|
double v = Math.sqrt(2. * ekin / km.totalweight);
|
||||||
double d = dist;
|
double d = dist;
|
||||||
while( d > 0. )
|
while (d > 0.) {
|
||||||
{
|
|
||||||
boolean slow = ekin < elow;
|
boolean slow = ekin < elow;
|
||||||
boolean fast = ekin >= emax;
|
boolean fast = ekin >= emax;
|
||||||
double etarget = slow ? elow : emax;
|
double etarget = slow ? elow : emax;
|
||||||
|
|
@ -190,30 +164,24 @@ final class KinematicPath extends OsmPath
|
||||||
double delta_ekin;
|
double delta_ekin;
|
||||||
double timeStep;
|
double timeStep;
|
||||||
double x;
|
double x;
|
||||||
if ( fast )
|
if (fast) {
|
||||||
{
|
|
||||||
x = d;
|
x = d;
|
||||||
delta_ekin = x * f;
|
delta_ekin = x * f;
|
||||||
timeStep = x / v;
|
timeStep = x / v;
|
||||||
ekin = etarget;
|
ekin = etarget;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
delta_ekin = etarget - ekin;
|
delta_ekin = etarget - ekin;
|
||||||
double b = 2. * f_air / km.totalweight;
|
double b = 2. * f_air / km.totalweight;
|
||||||
double x0 = delta_ekin / f;
|
double x0 = delta_ekin / f;
|
||||||
double x0b = x0 * b;
|
double x0b = x0 * b;
|
||||||
x = x0 * (1. - x0b * (0.5 + x0b * (0.333333333 - x0b * 0.25))); // = ln( delta_ekin*b/f + 1.) / b;
|
x = x0 * (1. - x0b * (0.5 + x0b * (0.333333333 - x0b * 0.25))); // = ln( delta_ekin*b/f + 1.) / b;
|
||||||
double maxstep = Math.min(50., d);
|
double maxstep = Math.min(50., d);
|
||||||
if ( x >= maxstep )
|
if (x >= maxstep) {
|
||||||
{
|
|
||||||
x = maxstep;
|
x = maxstep;
|
||||||
double xb = x * b;
|
double xb = x * b;
|
||||||
delta_ekin = x * f * (1. + xb * (0.5 + xb * (0.166666667 + xb * 0.0416666667))); // = f/b* exp(xb-1)
|
delta_ekin = x * f * (1. + xb * (0.5 + xb * (0.166666667 + xb * 0.0416666667))); // = f/b* exp(xb-1)
|
||||||
ekin += delta_ekin;
|
ekin += delta_ekin;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
ekin = etarget;
|
ekin = etarget;
|
||||||
}
|
}
|
||||||
double v2 = Math.sqrt(2. * ekin / km.totalweight);
|
double v2 = Math.sqrt(2. * ekin / km.totalweight);
|
||||||
|
|
@ -242,23 +210,19 @@ final class KinematicPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processTargetNode( RoutingContext rc )
|
protected double processTargetNode(RoutingContext rc) {
|
||||||
{
|
|
||||||
KinematicModel km = (KinematicModel) rc.pm;
|
KinematicModel km = (KinematicModel) rc.pm;
|
||||||
|
|
||||||
// finally add node-costs for target node
|
// finally add node-costs for target node
|
||||||
if ( targetNode.nodeDescription != null )
|
if (targetNode.nodeDescription != null) {
|
||||||
{
|
|
||||||
rc.expctxNode.evaluate(false, targetNode.nodeDescription);
|
rc.expctxNode.evaluate(false, targetNode.nodeDescription);
|
||||||
float initialcost = rc.expctxNode.getInitialcost();
|
float initialcost = rc.expctxNode.getInitialcost();
|
||||||
if ( initialcost >= 1000000. )
|
if (initialcost >= 1000000.) {
|
||||||
{
|
|
||||||
return -1.;
|
return -1.;
|
||||||
}
|
}
|
||||||
cutEkin(km.totalweight, km.getNodeMaxspeed()); // apply node maxspeed
|
cutEkin(km.totalweight, km.getNodeMaxspeed()); // apply node maxspeed
|
||||||
|
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.linknodecost += (int) initialcost;
|
message.linknodecost += (int) initialcost;
|
||||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(false, targetNode.nodeDescription);
|
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(false, targetNode.nodeDescription);
|
||||||
|
|
||||||
|
|
@ -269,22 +233,19 @@ final class KinematicPath extends OsmPath
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cutEkin( double weight, double speed )
|
private void cutEkin(double weight, double speed) {
|
||||||
{
|
|
||||||
double e = 0.5 * weight * speed * speed;
|
double e = 0.5 * weight * speed * speed;
|
||||||
if (ekin > e) ekin = e;
|
if (ekin > e) ekin = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int elevationCorrection( RoutingContext rc )
|
public int elevationCorrection() {
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
|
public boolean definitlyWorseThan(OsmPath path) {
|
||||||
{
|
|
||||||
KinematicPath p = (KinematicPath) path;
|
KinematicPath p = (KinematicPath) path;
|
||||||
|
|
||||||
int c = p.cost;
|
int c = p.cost;
|
||||||
|
|
@ -292,14 +253,12 @@ final class KinematicPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalTime()
|
public double getTotalTime() {
|
||||||
{
|
|
||||||
return totalTime;
|
return totalTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalEnergy()
|
public double getTotalEnergy() {
|
||||||
{
|
|
||||||
return totalEnergy;
|
return totalEnergy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,12 @@ package btools.router;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.mapaccess.OsmTransferNode;
|
import btools.mapaccess.OsmTransferNode;
|
||||||
|
|
||||||
final class KinematicPrePath extends OsmPrePath
|
final class KinematicPrePath extends OsmPrePath {
|
||||||
{
|
|
||||||
public double angle;
|
public double angle;
|
||||||
public int priorityclassifier;
|
public int priorityclassifier;
|
||||||
public int classifiermask;
|
public int classifiermask;
|
||||||
|
|
||||||
protected void initPrePath(OsmPath origin, RoutingContext rc )
|
protected void initPrePath(OsmPath origin, RoutingContext rc) {
|
||||||
{
|
|
||||||
byte[] description = link.descriptionBitmap;
|
byte[] description = link.descriptionBitmap;
|
||||||
if (description == null) throw new IllegalArgumentException("null description for: " + link);
|
if (description == null) throw new IllegalArgumentException("null description for: " + link);
|
||||||
|
|
||||||
|
|
@ -38,13 +36,10 @@ final class KinematicPrePath extends OsmPrePath
|
||||||
int lon2;
|
int lon2;
|
||||||
int lat2;
|
int lat2;
|
||||||
|
|
||||||
if ( transferNode == null )
|
if (transferNode == null) {
|
||||||
{
|
|
||||||
lon2 = targetNode.ilon;
|
lon2 = targetNode.ilon;
|
||||||
lat2 = targetNode.ilat;
|
lat2 = targetNode.ilat;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
lon2 = transferNode.ilon;
|
lon2 = transferNode.ilon;
|
||||||
lat2 = transferNode.ilat;
|
lat2 = transferNode.ilat;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
|
|
||||||
|
final class MessageData implements Cloneable {
|
||||||
final class MessageData implements Cloneable
|
|
||||||
{
|
|
||||||
int linkdist = 0;
|
int linkdist = 0;
|
||||||
int linkelevationcost = 0;
|
int linkelevationcost = 0;
|
||||||
int linkturncost = 0;
|
int linkturncost = 0;
|
||||||
|
|
@ -37,10 +35,8 @@ final class MessageData implements Cloneable
|
||||||
int vnode1 = 999;
|
int vnode1 = 999;
|
||||||
int extraTime = 0;
|
int extraTime = 0;
|
||||||
|
|
||||||
String toMessage()
|
String toMessage() {
|
||||||
{
|
if (wayKeyValues == null) {
|
||||||
if ( wayKeyValues == null )
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,8 +56,7 @@ final class MessageData implements Cloneable
|
||||||
+ "\t" + ((int) energy);
|
+ "\t" + ((int) energy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add( MessageData d )
|
void add(MessageData d) {
|
||||||
{
|
|
||||||
linkdist += d.linkdist;
|
linkdist += d.linkdist;
|
||||||
linkelevationcost += d.linkelevationcost;
|
linkelevationcost += d.linkelevationcost;
|
||||||
linkturncost += d.linkturncost;
|
linkturncost += d.linkturncost;
|
||||||
|
|
@ -69,51 +64,40 @@ final class MessageData implements Cloneable
|
||||||
linkinitcost += d.linkinitcost;
|
linkinitcost += d.linkinitcost;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageData copy()
|
MessageData copy() {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
return (MessageData) clone();
|
return (MessageData) clone();
|
||||||
}
|
} catch (CloneNotSupportedException e) {
|
||||||
catch( CloneNotSupportedException e )
|
|
||||||
{
|
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString() {
|
||||||
{
|
|
||||||
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
|
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPrio()
|
public int getPrio() {
|
||||||
{
|
|
||||||
return priorityclassifier;
|
return priorityclassifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isBadOneway()
|
public boolean isBadOneway() {
|
||||||
{
|
|
||||||
return (classifiermask & 1) != 0;
|
return (classifiermask & 1) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGoodOneway()
|
public boolean isGoodOneway() {
|
||||||
{
|
|
||||||
return (classifiermask & 2) != 0;
|
return (classifiermask & 2) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRoundabout()
|
public boolean isRoundabout() {
|
||||||
{
|
|
||||||
return (classifiermask & 4) != 0;
|
return (classifiermask & 4) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLinktType()
|
public boolean isLinktType() {
|
||||||
{
|
|
||||||
return (classifiermask & 8) != 0;
|
return (classifiermask & 8) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGoodForCars()
|
public boolean isGoodForCars() {
|
||||||
{
|
|
||||||
return (classifiermask & 16) != 0;
|
return (classifiermask & 16) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,25 +8,22 @@ package btools.router;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class OsmNodeNamed extends OsmNode
|
public class OsmNodeNamed extends OsmNode {
|
||||||
{
|
|
||||||
public String name;
|
public String name;
|
||||||
public double radius; // radius of nogopoint (in meters)
|
public double radius; // radius of nogopoint (in meters)
|
||||||
public double nogoWeight; // weight for nogopoint
|
public double nogoWeight; // weight for nogopoint
|
||||||
public boolean isNogo = false;
|
public boolean isNogo = false;
|
||||||
|
public boolean direct = false; // mark direct routing
|
||||||
|
|
||||||
public OsmNodeNamed()
|
public OsmNodeNamed() {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmNodeNamed( OsmNode n)
|
public OsmNodeNamed(OsmNode n) {
|
||||||
{
|
|
||||||
super(n.ilon, n.ilat);
|
super(n.ilon, n.ilat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString() {
|
||||||
{
|
|
||||||
if (Double.isNaN(nogoWeight)) {
|
if (Double.isNaN(nogoWeight)) {
|
||||||
return ilon + "," + ilat + "," + name;
|
return ilon + "," + ilat + "," + name;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -82,8 +79,7 @@ public class OsmNodeNamed extends OsmNode
|
||||||
return 2 * halfDistanceWithin;
|
return 2 * halfDistanceWithin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OsmNodeNamed decodeNogo( String s )
|
public static OsmNodeNamed decodeNogo(String s) {
|
||||||
{
|
|
||||||
OsmNodeNamed n = new OsmNodeNamed();
|
OsmNodeNamed n = new OsmNodeNamed();
|
||||||
int idx1 = s.indexOf(',');
|
int idx1 = s.indexOf(',');
|
||||||
n.ilon = Integer.parseInt(s.substring(0, idx1));
|
n.ilon = Integer.parseInt(s.substring(0, idx1));
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
http://geomalgorithms.com/a03-_inclusion.html
|
http://geomalgorithms.com/a03-_inclusion.html
|
||||||
|
|
||||||
cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
|
cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
|
||||||
|
|
||||||
**********************************************************************************************/
|
**********************************************************************************************/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
|
|
@ -14,33 +13,28 @@ import java.util.List;
|
||||||
|
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class OsmNogoPolygon extends OsmNodeNamed
|
public class OsmNogoPolygon extends OsmNodeNamed {
|
||||||
{
|
public final static class Point {
|
||||||
public final static class Point
|
|
||||||
{
|
|
||||||
public final int y;
|
public final int y;
|
||||||
public final int x;
|
public final int x;
|
||||||
|
|
||||||
Point(final int lon, final int lat)
|
Point(final int lon, final int lat) {
|
||||||
{
|
|
||||||
x = lon;
|
x = lon;
|
||||||
y = lat;
|
y = lat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final List<Point> points = new ArrayList<Point>();
|
public final List<Point> points = new ArrayList<>();
|
||||||
|
|
||||||
public final boolean isClosed;
|
public final boolean isClosed;
|
||||||
|
|
||||||
public OsmNogoPolygon(boolean closed)
|
public OsmNogoPolygon(boolean closed) {
|
||||||
{
|
|
||||||
this.isClosed = closed;
|
this.isClosed = closed;
|
||||||
this.isNogo = true;
|
this.isNogo = true;
|
||||||
this.name = "";
|
this.name = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void addVertex(int lon, int lat)
|
public final void addVertex(int lon, int lat) {
|
||||||
{
|
|
||||||
points.add(new Point(lon, lat));
|
points.add(new Point(lon, lat));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,36 +48,30 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
* with each iteration.
|
* with each iteration.
|
||||||
* This is done to ensure the calculated radius being used
|
* This is done to ensure the calculated radius being used
|
||||||
* in RoutingContext.calcDistance will actually contain the whole polygon.
|
* in RoutingContext.calcDistance will actually contain the whole polygon.
|
||||||
*
|
* <p>
|
||||||
* For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
|
* For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
|
||||||
* As this is only run once on initialization of OsmNogoPolygon this methods
|
* As this is only run once on initialization of OsmNogoPolygon this methods
|
||||||
* overall usage of cpu is neglegible in comparism to the cpu-usage of the
|
* overall usage of cpu is neglegible in comparism to the cpu-usage of the
|
||||||
* actual routing algoritm.
|
* actual routing algoritm.
|
||||||
*/
|
*/
|
||||||
public void calcBoundingCircle()
|
public void calcBoundingCircle() {
|
||||||
{
|
|
||||||
int cxmin, cxmax, cymin, cymax;
|
int cxmin, cxmax, cymin, cymax;
|
||||||
cxmin = cymin = Integer.MAX_VALUE;
|
cxmin = cymin = Integer.MAX_VALUE;
|
||||||
cxmax = cymax = Integer.MIN_VALUE;
|
cxmax = cymax = Integer.MIN_VALUE;
|
||||||
|
|
||||||
// first calculate a starting center point as center of boundingbox
|
// first calculate a starting center point as center of boundingbox
|
||||||
for (int i = 0; i < points.size(); i++)
|
for (int i = 0; i < points.size(); i++) {
|
||||||
{
|
|
||||||
final Point p = points.get(i);
|
final Point p = points.get(i);
|
||||||
if (p.x < cxmin)
|
if (p.x < cxmin) {
|
||||||
{
|
|
||||||
cxmin = p.x;
|
cxmin = p.x;
|
||||||
}
|
}
|
||||||
if (p.x > cxmax)
|
if (p.x > cxmax) {
|
||||||
{
|
|
||||||
cxmax = p.x;
|
cxmax = p.x;
|
||||||
}
|
}
|
||||||
if (p.y < cymin)
|
if (p.y < cymin) {
|
||||||
{
|
|
||||||
cymin = p.y;
|
cymin = p.y;
|
||||||
}
|
}
|
||||||
if (p.y > cymax)
|
if (p.y > cymax) {
|
||||||
{
|
|
||||||
cymax = p.y;
|
cymax = p.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,11 +88,9 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
double dmax = 0; // length of vector from center to point
|
double dmax = 0; // length of vector from center to point
|
||||||
int i_max = -1;
|
int i_max = -1;
|
||||||
|
|
||||||
do
|
do {
|
||||||
{
|
|
||||||
// now identify the point outside of the circle that has the greatest distance
|
// now identify the point outside of the circle that has the greatest distance
|
||||||
for (int i = 0; i < points.size(); i++)
|
for (int i = 0; i < points.size(); i++) {
|
||||||
{
|
|
||||||
final Point p = points.get(i);
|
final Point p = points.get(i);
|
||||||
|
|
||||||
// to get precisely the same results as in RoutingContext.calcDistance()
|
// to get precisely the same results as in RoutingContext.calcDistance()
|
||||||
|
|
@ -113,19 +99,16 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
final double y1 = (cy - p.y) * dlat2m;
|
final double y1 = (cy - p.y) * dlat2m;
|
||||||
final double dist = Math.sqrt(x1 * x1 + y1 * y1);
|
final double dist = Math.sqrt(x1 * x1 + y1 * y1);
|
||||||
|
|
||||||
if (dist <= rad)
|
if (dist <= rad) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (dist > dmax)
|
if (dist > dmax) {
|
||||||
{
|
|
||||||
// new maximum distance found
|
// new maximum distance found
|
||||||
dmax = dist;
|
dmax = dist;
|
||||||
i_max = i;
|
i_max = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i_max < 0)
|
if (i_max < 0) {
|
||||||
{
|
|
||||||
break; // leave loop when no point outside the circle is found any more.
|
break; // leave loop when no point outside the circle is found any more.
|
||||||
}
|
}
|
||||||
final double dd = 0.5 * (1 - rad / dmax);
|
final double dd = 0.5 * (1 - rad / dmax);
|
||||||
|
|
@ -149,7 +132,6 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
ilon = cx;
|
ilon = cx;
|
||||||
ilat = cy;
|
ilat = cy;
|
||||||
radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision
|
radius = rad * 1.001 + 1.0; // ensure the outside-of-enclosing-circle test in RoutingContext.calcDistance() is not passed by segments ending very close to the radius due to limited numerical precision
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -165,18 +147,15 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
* @param lat1 latitude of start point
|
* @param lat1 latitude of start point
|
||||||
* @return true if segment or any of it's points are 'inside' of polygon
|
* @return true if segment or any of it's points are 'inside' of polygon
|
||||||
*/
|
*/
|
||||||
public boolean intersects(int lon0, int lat0, int lon1, int lat1)
|
public boolean intersects(int lon0, int lat0, int lon1, int lat1) {
|
||||||
{
|
|
||||||
final Point p0 = new Point(lon0, lat0);
|
final Point p0 = new Point(lon0, lat0);
|
||||||
final Point p1 = new Point(lon1, lat1);
|
final Point p1 = new Point(lon1, lat1);
|
||||||
int i_last = points.size() - 1;
|
int i_last = points.size() - 1;
|
||||||
Point p2 = points.get(isClosed ? i_last : 0);
|
Point p2 = points.get(isClosed ? i_last : 0);
|
||||||
for (int i = isClosed ? 0 : 1 ; i <= i_last; i++)
|
for (int i = isClosed ? 0 : 1; i <= i_last; i++) {
|
||||||
{
|
|
||||||
Point p3 = points.get(i);
|
Point p3 = points.get(i);
|
||||||
// does it intersect with at least one of the polygon's segments?
|
// does it intersect with at least one of the polygon's segments?
|
||||||
if (intersect2D_2Segments(p0,p1,p2,p3) > 0)
|
if (intersect2D_2Segments(p0, p1, p2, p3) > 0) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
p2 = p3;
|
p2 = p3;
|
||||||
|
|
@ -184,15 +163,12 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOnPolyline( long px, long py )
|
public boolean isOnPolyline(long px, long py) {
|
||||||
{
|
|
||||||
int i_last = points.size() - 1;
|
int i_last = points.size() - 1;
|
||||||
Point p1 = points.get(0);
|
Point p1 = points.get(0);
|
||||||
for (int i = 1 ; i <= i_last; i++)
|
for (int i = 1; i <= i_last; i++) {
|
||||||
{
|
|
||||||
final Point p2 = points.get(i);
|
final Point p2 = points.get(i);
|
||||||
if (OsmNogoPolygon.isOnLine(px,py,p1.x,p1.y,p2.x,p2.y))
|
if (isOnLine(px, py, p1.x, p1.y, p2.x, p2.y)) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
p1 = p2;
|
p1 = p2;
|
||||||
|
|
@ -200,37 +176,30 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isOnLine( long px, long py, long p0x, long p0y, long p1x, long p1y )
|
public static boolean isOnLine(long px, long py, long p0x, long p0y, long p1x, long p1y) {
|
||||||
{
|
|
||||||
final double v10x = px - p0x;
|
final double v10x = px - p0x;
|
||||||
final double v10y = py - p0y;
|
final double v10y = py - p0y;
|
||||||
final double v12x = p1x - p0x;
|
final double v12x = p1x - p0x;
|
||||||
final double v12y = p1y - p0y;
|
final double v12y = p1y - p0y;
|
||||||
|
|
||||||
if ( v10x == 0 ) // P0->P1 vertical?
|
if (v10x == 0) { // P0->P1 vertical?
|
||||||
{
|
if (v10y == 0) { // P0 == P1?
|
||||||
if ( v10y == 0 ) // P0 == P1?
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if ( v12x != 0 ) // P1->P2 not vertical?
|
if (v12x != 0) { // P1->P2 not vertical?
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (v12y / v10y) >= 1; // P1->P2 at least as long as P1->P0?
|
return (v12y / v10y) >= 1; // P1->P2 at least as long as P1->P0?
|
||||||
}
|
}
|
||||||
if ( v10y == 0 ) // P0->P1 horizontal?
|
if (v10y == 0) { // P0->P1 horizontal?
|
||||||
{
|
if (v12y != 0) { // P1->P2 not horizontal?
|
||||||
if ( v12y != 0 ) // P1->P2 not horizontal?
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// if ( P10x == 0 ) // P0 == P1? already tested
|
// if ( P10x == 0 ) // P0 == P1? already tested
|
||||||
return (v12x / v10x) >= 1; // P1->P2 at least as long as P1->P0?
|
return (v12x / v10x) >= 1; // P1->P2 at least as long as P1->P0?
|
||||||
}
|
}
|
||||||
final double kx = v12x / v10x;
|
final double kx = v12x / v10x;
|
||||||
if ( kx < 1 )
|
if (kx < 1) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return kx == v12y / v10y;
|
return kx == v12y / v10y;
|
||||||
|
|
@ -242,6 +211,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
this code, and cannot be held liable for any real or imagined damage
|
this code, and cannot be held liable for any real or imagined damage
|
||||||
resulting from its use. Users of this code must verify correctness for
|
resulting from its use. Users of this code must verify correctness for
|
||||||
their application. */
|
their application. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* winding number test for a point in a polygon
|
* winding number test for a point in a polygon
|
||||||
*
|
*
|
||||||
|
|
@ -249,8 +219,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
* @param py latitude of the point to check
|
* @param py latitude of the point to check
|
||||||
* @return a boolean whether the point is within the polygon or not.
|
* @return a boolean whether the point is within the polygon or not.
|
||||||
*/
|
*/
|
||||||
public boolean isWithin(final long px, final long py)
|
public boolean isWithin(final long px, final long py) {
|
||||||
{
|
|
||||||
int wn = 0; // the winding number counter
|
int wn = 0; // the winding number counter
|
||||||
|
|
||||||
// loop through all edges of the polygon
|
// loop through all edges of the polygon
|
||||||
|
|
@ -259,34 +228,26 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
long p0x = p0.x; // need to use long to avoid overflow in products
|
long p0x = p0.x; // need to use long to avoid overflow in products
|
||||||
long p0y = p0.y;
|
long p0y = p0.y;
|
||||||
|
|
||||||
for (int i = isClosed ? 0 : 1; i <= i_last; i++) // edge from v[i] to v[i+1]
|
for (int i = isClosed ? 0 : 1; i <= i_last; i++) { // edge from v[i] to v[i+1]
|
||||||
{
|
|
||||||
final Point p1 = points.get(i);
|
final Point p1 = points.get(i);
|
||||||
|
|
||||||
final long p1x = p1.x;
|
final long p1x = p1.x;
|
||||||
final long p1y = p1.y;
|
final long p1y = p1.y;
|
||||||
|
|
||||||
if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y))
|
if (isOnLine(px, py, p0x, p0y, p1x, p1y)) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p0y <= py) // start y <= p.y
|
if (p0y <= py) // start y <= p.y
|
||||||
{
|
{
|
||||||
if (p1y > py) // an upward crossing
|
if (p1y > py) { // an upward crossing, p left of edge
|
||||||
{ // p left of edge
|
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0) {
|
||||||
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0)
|
|
||||||
{
|
|
||||||
++wn; // have a valid up intersect
|
++wn; // have a valid up intersect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else { // start y > p.y (no test needed)
|
||||||
else // start y > p.y (no test needed)
|
if (p1y <= py) { // a downward crossing, p right of edge
|
||||||
{
|
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0) {
|
||||||
if (p1y <= py) // a downward crossing
|
|
||||||
{ // p right of edge
|
|
||||||
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0)
|
|
||||||
{
|
|
||||||
--wn; // have a valid down intersect
|
--wn; // have a valid down intersect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +265,6 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
* @param lat1 Integer latitude of the first point of the segment.
|
* @param lat1 Integer latitude of the first point of the segment.
|
||||||
* @param lon2 Integer longitude of the last point of the segment.
|
* @param lon2 Integer longitude of the last point of the segment.
|
||||||
* @param lat2 Integer latitude of the last point of the segment.
|
* @param lat2 Integer latitude of the last point of the segment.
|
||||||
*
|
|
||||||
* @return The length, in meters, of the portion of the segment which is
|
* @return The length, in meters, of the portion of the segment which is
|
||||||
* included in the polygon.
|
* included in the polygon.
|
||||||
*/
|
*/
|
||||||
|
|
@ -316,8 +276,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
final Point p2 = new Point(lon2, lat2);
|
final Point p2 = new Point(lon2, lat2);
|
||||||
|
|
||||||
Point previousIntersectionOnSegment = null;
|
Point previousIntersectionOnSegment = null;
|
||||||
if (isWithin(lon1, lat1))
|
if (isWithin(lon1, lat1)) {
|
||||||
{
|
|
||||||
// Start point of the segment is within the polygon, this is the first
|
// Start point of the segment is within the polygon, this is the first
|
||||||
// "intersection".
|
// "intersection".
|
||||||
previousIntersectionOnSegment = p1;
|
previousIntersectionOnSegment = p1;
|
||||||
|
|
@ -325,14 +284,12 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
|
|
||||||
// Loop over edges of the polygon to find intersections
|
// Loop over edges of the polygon to find intersections
|
||||||
int i_last = points.size() - 1;
|
int i_last = points.size() - 1;
|
||||||
for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++)
|
for (int i = (isClosed ? 0 : 1), j = (isClosed ? i_last : 0); i <= i_last; j = i++) {
|
||||||
{
|
|
||||||
Point edgePoint1 = points.get(j);
|
Point edgePoint1 = points.get(j);
|
||||||
Point edgePoint2 = points.get(i);
|
Point edgePoint2 = points.get(i);
|
||||||
int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2);
|
int intersectsEdge = intersect2D_2Segments(p1, p2, edgePoint1, edgePoint2);
|
||||||
|
|
||||||
if (isClosed && intersectsEdge == 1)
|
if (isClosed && intersectsEdge == 1) {
|
||||||
{
|
|
||||||
// Intersects with a (closed) polygon edge on a single point
|
// Intersects with a (closed) polygon edge on a single point
|
||||||
// Distance is zero when crossing a polyline.
|
// Distance is zero when crossing a polyline.
|
||||||
// Let's find this intersection point
|
// Let's find this intersection point
|
||||||
|
|
@ -363,8 +320,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
previousIntersectionOnSegment = intersection;
|
previousIntersectionOnSegment = intersection;
|
||||||
}
|
} else if (intersectsEdge == 2) {
|
||||||
else if (intersectsEdge == 2) {
|
|
||||||
// Segment and edge overlaps
|
// Segment and edge overlaps
|
||||||
// FIXME: Could probably be done in a smarter way
|
// FIXME: Could probably be done in a smarter way
|
||||||
distance += Math.min(
|
distance += Math.min(
|
||||||
|
|
@ -401,6 +357,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
this code, and cannot be held liable for any real or imagined damage
|
this code, and cannot be held liable for any real or imagined damage
|
||||||
resulting from its use. Users of this code must verify correctness for
|
resulting from its use. Users of this code must verify correctness for
|
||||||
their application. */
|
their application. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inSegment(): determine if a point is inside a segment
|
* inSegment(): determine if a point is inside a segment
|
||||||
*
|
*
|
||||||
|
|
@ -410,35 +367,28 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
* @return 1 = P is inside S
|
* @return 1 = P is inside S
|
||||||
* 0 = P is not inside S
|
* 0 = P is not inside S
|
||||||
*/
|
*/
|
||||||
private static boolean inSegment( final Point p, final Point seg_p0, final Point seg_p1)
|
private static boolean inSegment(final Point p, final Point seg_p0, final Point seg_p1) {
|
||||||
{
|
|
||||||
final int sp0x = seg_p0.x;
|
final int sp0x = seg_p0.x;
|
||||||
final int sp1x = seg_p1.x;
|
final int sp1x = seg_p1.x;
|
||||||
|
|
||||||
if (sp0x != sp1x) // S is not vertical
|
if (sp0x != sp1x) { // S is not vertical
|
||||||
{
|
|
||||||
final int px = p.x;
|
final int px = p.x;
|
||||||
if (sp0x <= px && px <= sp1x)
|
if (sp0x <= px && px <= sp1x) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sp0x >= px && px >= sp1x)
|
if (sp0x >= px && px >= sp1x) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
} else // S is vertical, so test y coordinate
|
||||||
else // S is vertical, so test y coordinate
|
|
||||||
{
|
{
|
||||||
final int sp0y = seg_p0.y;
|
final int sp0y = seg_p0.y;
|
||||||
final int sp1y = seg_p1.y;
|
final int sp1y = seg_p1.y;
|
||||||
final int py = p.y;
|
final int py = p.y;
|
||||||
|
|
||||||
if (sp0y <= py && py <= sp1y)
|
if (sp0y <= py && py <= sp1y) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (sp0y >= py && py >= sp1y)
|
if (sp0y >= py && py >= sp1y) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -451,8 +401,10 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
this code, and cannot be held liable for any real or imagined damage
|
this code, and cannot be held liable for any real or imagined damage
|
||||||
resulting from its use. Users of this code must verify correctness for
|
resulting from its use. Users of this code must verify correctness for
|
||||||
their application. */
|
their application. */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments
|
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments
|
||||||
|
*
|
||||||
* @param s1p0 start point of segment 1
|
* @param s1p0 start point of segment 1
|
||||||
* @param s1p1 end point of segment 1
|
* @param s1p1 end point of segment 1
|
||||||
* @param s2p0 start point of segment 2
|
* @param s2p0 start point of segment 2
|
||||||
|
|
@ -461,8 +413,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
* 1=intersect in unique point I0
|
* 1=intersect in unique point I0
|
||||||
* 2=overlap in segment from I0 to I1
|
* 2=overlap in segment from I0 to I1
|
||||||
*/
|
*/
|
||||||
private static int intersect2D_2Segments( final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1 )
|
private static int intersect2D_2Segments(final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1) {
|
||||||
{
|
|
||||||
final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
|
final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
|
||||||
final long uy = s1p1.y - s1p0.y;
|
final long uy = s1p1.y - s1p0.y;
|
||||||
final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
|
final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
|
||||||
|
|
@ -475,8 +426,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
// test if they are parallel (includes either being a point)
|
// test if they are parallel (includes either being a point)
|
||||||
if (d == 0) // S1 and S2 are parallel
|
if (d == 0) // S1 and S2 are parallel
|
||||||
{
|
{
|
||||||
if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0)
|
if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0) {
|
||||||
{
|
|
||||||
return 0; // they are NOT collinear
|
return 0; // they are NOT collinear
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,13 +450,10 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
double t0, t1; // endpoints of S1 in eqn for S2
|
double t0, t1; // endpoints of S1 in eqn for S2
|
||||||
final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
|
final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
|
||||||
final int w2y = s1p1.y - s2p0.y;
|
final int w2y = s1p1.y - s2p0.y;
|
||||||
if (vx != 0)
|
if (vx != 0) {
|
||||||
{
|
|
||||||
t0 = wx / vx;
|
t0 = wx / vx;
|
||||||
t1 = w2x / vx;
|
t1 = w2x / vx;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
t0 = wy / vy;
|
t0 = wy / vy;
|
||||||
t1 = w2y / vy;
|
t1 = w2y / vy;
|
||||||
}
|
}
|
||||||
|
|
@ -516,8 +463,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
|
||||||
t0 = t1;
|
t0 = t1;
|
||||||
t1 = t;
|
t1 = t;
|
||||||
}
|
}
|
||||||
if (t0 > 1 || t1 < 0)
|
if (t0 > 1 || t1 < 0) {
|
||||||
{
|
|
||||||
return 0; // NO overlap
|
return 0; // NO overlap
|
||||||
}
|
}
|
||||||
t0 = t0 < 0 ? 0 : t0; // clip to min 0
|
t0 = t0 < 0 ? 0 : t0; // clip to min 0
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import btools.mapaccess.OsmLink;
|
import btools.mapaccess.OsmLink;
|
||||||
import btools.mapaccess.OsmLinkHolder;
|
import btools.mapaccess.OsmLinkHolder;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
|
|
@ -14,8 +12,7 @@ import btools.mapaccess.OsmTransferNode;
|
||||||
import btools.mapaccess.TurnRestriction;
|
import btools.mapaccess.TurnRestriction;
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
abstract class OsmPath implements OsmLinkHolder
|
abstract class OsmPath implements OsmLinkHolder {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* The cost of that path (a modified distance)
|
* The cost of that path (a modified distance)
|
||||||
*/
|
*/
|
||||||
|
|
@ -34,8 +31,6 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
public OsmPathElement originElement;
|
public OsmPathElement originElement;
|
||||||
public OsmPathElement myElement;
|
public OsmPathElement myElement;
|
||||||
|
|
||||||
protected float traffic;
|
|
||||||
|
|
||||||
private OsmLinkHolder nextForLink = null;
|
private OsmLinkHolder nextForLink = null;
|
||||||
|
|
||||||
public int treedepth = 0;
|
public int treedepth = 0;
|
||||||
|
|
@ -57,54 +52,23 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
private static final int HAD_DESTINATION_START_BIT = 8;
|
private static final int HAD_DESTINATION_START_BIT = 8;
|
||||||
protected int bitfield = PATH_START_BIT;
|
protected int bitfield = PATH_START_BIT;
|
||||||
|
|
||||||
private boolean getBit( int mask )
|
private boolean getBit(int mask) {
|
||||||
{
|
|
||||||
return (bitfield & mask) != 0;
|
return (bitfield & mask) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBit( int mask, boolean bit )
|
private void setBit(int mask, boolean bit) {
|
||||||
{
|
if (getBit(mask) != bit) {
|
||||||
if ( getBit( mask ) != bit )
|
|
||||||
{
|
|
||||||
bitfield ^= mask;
|
bitfield ^= mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean didEnterDestinationArea()
|
public boolean didEnterDestinationArea() {
|
||||||
{
|
|
||||||
return !getBit(HAD_DESTINATION_START_BIT) && getBit(IS_ON_DESTINATION_BIT);
|
return !getBit(HAD_DESTINATION_START_BIT) && getBit(IS_ON_DESTINATION_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MessageData message;
|
public MessageData message;
|
||||||
|
|
||||||
public void unregisterUpTree( RoutingContext rc )
|
public void init(OsmLink link) {
|
||||||
{
|
|
||||||
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;
|
this.link = link;
|
||||||
targetNode = link.getTarget(null);
|
targetNode = link.getTarget(null);
|
||||||
selev = targetNode.getSElev();
|
selev = targetNode.getSElev();
|
||||||
|
|
@ -113,11 +77,9 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
originLat = -1;
|
originLat = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
|
public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) {
|
||||||
{
|
if (origin.myElement == null) {
|
||||||
if ( origin.myElement == null )
|
origin.myElement = OsmPathElement.create(origin);
|
||||||
{
|
|
||||||
origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
|
|
||||||
}
|
}
|
||||||
this.originElement = origin.myElement;
|
this.originElement = origin.myElement;
|
||||||
this.link = link;
|
this.link = link;
|
||||||
|
|
@ -127,6 +89,7 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
this.lastClassifier = origin.lastClassifier;
|
this.lastClassifier = origin.lastClassifier;
|
||||||
this.lastInitialCost = origin.lastInitialCost;
|
this.lastInitialCost = origin.lastInitialCost;
|
||||||
this.bitfield = origin.bitfield;
|
this.bitfield = origin.bitfield;
|
||||||
|
this.priorityclassifier = origin.priorityclassifier;
|
||||||
init(origin);
|
init(origin);
|
||||||
addAddionalPenalty(refTrack, detailMode, origin, link, rc);
|
addAddionalPenalty(refTrack, detailMode, origin, link, rc);
|
||||||
}
|
}
|
||||||
|
|
@ -135,16 +98,29 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
|
|
||||||
protected abstract void resetState();
|
protected abstract void resetState();
|
||||||
|
|
||||||
|
static int seg = 1;
|
||||||
|
|
||||||
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
|
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
|
||||||
{
|
|
||||||
byte[] description = link.descriptionBitmap;
|
byte[] description = link.descriptionBitmap;
|
||||||
if ( description == null )
|
if (description == null) { // could be a beeline path
|
||||||
{
|
message = new MessageData();
|
||||||
return; // could be a beeline path
|
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;
|
boolean recordTransferNodes = detailMode;
|
||||||
|
|
||||||
rc.nogoCost = 0.;
|
rc.nogoCost = 0.;
|
||||||
|
|
||||||
|
|
@ -176,18 +152,15 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
float newClassifier = rc.expctxWay.getInitialClassifier();
|
float newClassifier = rc.expctxWay.getInitialClassifier();
|
||||||
float newInitialCost = rc.expctxWay.getInitialcost();
|
float newInitialCost = rc.expctxWay.getInitialcost();
|
||||||
float classifierDiff = newClassifier - lastClassifier;
|
float classifierDiff = newClassifier - lastClassifier;
|
||||||
if ( newClassifier != 0. && lastClassifier != 0. && ( classifierDiff > 0.0005 || classifierDiff < -0.0005 ) )
|
if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) {
|
||||||
{
|
|
||||||
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
|
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
|
||||||
if ( initialcost >= 1000000. )
|
if (initialcost >= 1000000.) {
|
||||||
{
|
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int iicost = (int) initialcost;
|
int iicost = (int) initialcost;
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.linkinitcost += iicost;
|
message.linkinitcost += iicost;
|
||||||
}
|
}
|
||||||
cost += iicost;
|
cost += iicost;
|
||||||
|
|
@ -199,22 +172,15 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
int classifiermask = (int) rc.expctxWay.getClassifierMask();
|
int classifiermask = (int) rc.expctxWay.getClassifierMask();
|
||||||
boolean newDestination = (classifiermask & 64) != 0;
|
boolean newDestination = (classifiermask & 64) != 0;
|
||||||
boolean oldDestination = getBit(IS_ON_DESTINATION_BIT);
|
boolean oldDestination = getBit(IS_ON_DESTINATION_BIT);
|
||||||
if ( getBit( PATH_START_BIT ) )
|
if (getBit(PATH_START_BIT)) {
|
||||||
{
|
|
||||||
setBit(PATH_START_BIT, false);
|
setBit(PATH_START_BIT, false);
|
||||||
setBit(CAN_LEAVE_DESTINATION_BIT, newDestination);
|
setBit(CAN_LEAVE_DESTINATION_BIT, newDestination);
|
||||||
setBit(HAD_DESTINATION_START_BIT, newDestination);
|
setBit(HAD_DESTINATION_START_BIT, newDestination);
|
||||||
}
|
} else {
|
||||||
else
|
if (oldDestination && !newDestination) {
|
||||||
{
|
if (getBit(CAN_LEAVE_DESTINATION_BIT)) {
|
||||||
if ( oldDestination && !newDestination )
|
|
||||||
{
|
|
||||||
if ( getBit( CAN_LEAVE_DESTINATION_BIT ) )
|
|
||||||
{
|
|
||||||
setBit(CAN_LEAVE_DESTINATION_BIT, false);
|
setBit(CAN_LEAVE_DESTINATION_BIT, false);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -226,8 +192,7 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
OsmTransferNode transferNode = link.geometry == null ? null
|
OsmTransferNode transferNode = link.geometry == null ? null
|
||||||
: rc.geometryDecoder.decodeGeometry(link.geometry, sourceNode, targetNode, isReverse);
|
: rc.geometryDecoder.decodeGeometry(link.geometry, sourceNode, targetNode, isReverse);
|
||||||
|
|
||||||
for(int nsection=0; ;nsection++)
|
for (int nsection = 0; ; nsection++) {
|
||||||
{
|
|
||||||
|
|
||||||
originLon = lon1;
|
originLon = lon1;
|
||||||
originLat = lat1;
|
originLat = lat1;
|
||||||
|
|
@ -235,37 +200,33 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
int lon2;
|
int lon2;
|
||||||
int lat2;
|
int lat2;
|
||||||
short ele2;
|
short ele2;
|
||||||
|
short originEle2;
|
||||||
|
|
||||||
if ( transferNode == null )
|
if (transferNode == null) {
|
||||||
{
|
|
||||||
lon2 = targetNode.ilon;
|
lon2 = targetNode.ilon;
|
||||||
lat2 = targetNode.ilat;
|
lat2 = targetNode.ilat;
|
||||||
ele2 = targetNode.selev;
|
originEle2 = targetNode.selev;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
lon2 = transferNode.ilon;
|
lon2 = transferNode.ilon;
|
||||||
lat2 = transferNode.ilat;
|
lat2 = transferNode.ilat;
|
||||||
ele2 = transferNode.selev;
|
originEle2 = transferNode.selev;
|
||||||
}
|
}
|
||||||
|
ele2 = originEle2;
|
||||||
|
|
||||||
boolean isStartpoint = lon0 == -1 && lat0 == -1;
|
boolean isStartpoint = lon0 == -1 && lat0 == -1;
|
||||||
|
|
||||||
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
|
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
|
||||||
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
|
if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) {
|
||||||
{
|
|
||||||
if (rc.inverseDirection
|
if (rc.inverseDirection
|
||||||
? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode, rc.carMode )
|
? 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.carMode ) )
|
: TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) {
|
||||||
{
|
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if recording, new MessageData for each section (needed for turn-instructions)
|
// if recording, new MessageData for each section (needed for turn-instructions)
|
||||||
if ( message != null && message.wayKeyValues != null )
|
if (message != null && message.wayKeyValues != null) {
|
||||||
{
|
|
||||||
originElement.message = message;
|
originElement.message = message;
|
||||||
message = new MessageData();
|
message = new MessageData();
|
||||||
}
|
}
|
||||||
|
|
@ -273,15 +234,11 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
|
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
|
||||||
|
|
||||||
boolean stopAtEndpoint = false;
|
boolean stopAtEndpoint = false;
|
||||||
if ( rc.shortestmatch )
|
if (rc.shortestmatch) {
|
||||||
{
|
if (rc.isEndpoint) {
|
||||||
if ( rc.isEndpoint )
|
|
||||||
{
|
|
||||||
stopAtEndpoint = true;
|
stopAtEndpoint = true;
|
||||||
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
|
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// we just start here, reset everything
|
// we just start here, reset everything
|
||||||
cost = 0;
|
cost = 0;
|
||||||
resetState();
|
resetState();
|
||||||
|
|
@ -289,24 +246,18 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
lat0 = -1;
|
lat0 = -1;
|
||||||
isStartpoint = true;
|
isStartpoint = true;
|
||||||
|
|
||||||
if ( recordTransferNodes )
|
if (recordTransferNodes) {
|
||||||
{
|
if (rc.wayfraction > 0.) {
|
||||||
if ( rc.wayfraction > 0. )
|
|
||||||
{
|
|
||||||
ele1 = interpolateEle(ele1, ele2, 1. - rc.wayfraction);
|
ele1 = interpolateEle(ele1, ele2, 1. - rc.wayfraction);
|
||||||
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic );
|
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele1, null);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
originElement = null; // prevent duplicate point
|
originElement = null; // prevent duplicate point
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( rc.checkPendingEndpoint() )
|
if (rc.checkPendingEndpoint()) {
|
||||||
{
|
|
||||||
dist = rc.calcDistance(rc.ilonshortest, rc.ilatshortest, lon2, lat2);
|
dist = rc.calcDistance(rc.ilonshortest, rc.ilatshortest, lon2, lat2);
|
||||||
if ( rc.shortestmatch )
|
if (rc.shortestmatch) {
|
||||||
{
|
|
||||||
stopAtEndpoint = true;
|
stopAtEndpoint = true;
|
||||||
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
|
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
|
||||||
}
|
}
|
||||||
|
|
@ -314,24 +265,19 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.linkdist += dist;
|
message.linkdist += dist;
|
||||||
}
|
}
|
||||||
linkdisttotal += dist;
|
linkdisttotal += dist;
|
||||||
|
|
||||||
// apply a start-direction if appropriate (by faking the origin position)
|
// apply a start-direction if appropriate (by faking the origin position)
|
||||||
if ( isStartpoint )
|
if (isStartpoint) {
|
||||||
{
|
if (rc.startDirectionValid) {
|
||||||
if ( rc.startDirectionValid )
|
double dir = rc.startDirection * CheapRuler.DEG_TO_RAD;
|
||||||
{
|
|
||||||
double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
|
|
||||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lon0 + lat1) >> 1);
|
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lon0 + lat1) >> 1);
|
||||||
lon0 = lon1 - (int) (1000. * Math.sin(dir) / lonlat2m[0]);
|
lon0 = lon1 - (int) (1000. * Math.sin(dir) / lonlat2m[0]);
|
||||||
lat0 = lat1 - (int) (1000. * Math.cos(dir) / lonlat2m[1]);
|
lat0 = lat1 - (int) (1000. * Math.cos(dir) / lonlat2m[1]);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
lon0 = lon1 - (lon2 - lon1);
|
lon0 = lon1 - (lon2 - lon1);
|
||||||
lat0 = lat1 - (lat2 - lat1);
|
lat0 = lat1 - (lat2 - lat1);
|
||||||
}
|
}
|
||||||
|
|
@ -342,47 +288,32 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
// *** elevation stuff
|
// *** elevation stuff
|
||||||
double delta_h = 0.;
|
double delta_h = 0.;
|
||||||
if (ele2 == Short.MIN_VALUE) ele2 = ele1;
|
if (ele2 == Short.MIN_VALUE) ele2 = ele1;
|
||||||
if ( ele1 != Short.MIN_VALUE )
|
if (ele1 != Short.MIN_VALUE) {
|
||||||
{
|
|
||||||
delta_h = (ele2 - ele1) / 4.;
|
delta_h = (ele2 - ele1) / 4.;
|
||||||
if ( rc.inverseDirection )
|
if (rc.inverseDirection) {
|
||||||
{
|
|
||||||
delta_h = -delta_h;
|
delta_h = -delta_h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2 / 4.;
|
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2 / 4.;
|
||||||
|
|
||||||
double sectionCost = processWaySection(rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier);
|
double sectionCost = processWaySection(rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier);
|
||||||
if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
|
if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) {
|
||||||
{
|
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isTrafficBackbone )
|
if (isTrafficBackbone) {
|
||||||
{
|
|
||||||
sectionCost = 0.;
|
sectionCost = 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
cost += (int) sectionCost;
|
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
|
// compute kinematic
|
||||||
computeKinematic(rc, dist, delta_h, detailMode);
|
computeKinematic(rc, dist, delta_h, detailMode);
|
||||||
|
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.turnangle = (float) angle;
|
message.turnangle = (float) angle;
|
||||||
message.time = (float) getTotalTime();
|
message.time = (float) getTotalTime();
|
||||||
message.energy = (float) getTotalEnergy();
|
message.energy = (float) getTotalEnergy();
|
||||||
|
|
@ -390,37 +321,29 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
message.classifiermask = classifiermask;
|
message.classifiermask = classifiermask;
|
||||||
message.lon = lon2;
|
message.lon = lon2;
|
||||||
message.lat = lat2;
|
message.lat = lat2;
|
||||||
message.ele = ele2;
|
message.ele = originEle2;
|
||||||
message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description);
|
message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( stopAtEndpoint )
|
if (stopAtEndpoint) {
|
||||||
{
|
if (recordTransferNodes) {
|
||||||
if ( recordTransferNodes )
|
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement);
|
||||||
{
|
|
||||||
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
|
|
||||||
originElement.cost = cost;
|
originElement.cost = cost;
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
originElement.message = message;
|
originElement.message = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( rc.nogoCost < 0)
|
if (rc.nogoCost < 0) {
|
||||||
{
|
|
||||||
cost = -1;
|
cost = -1;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
cost += rc.nogoCost;
|
cost += rc.nogoCost;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( transferNode == null )
|
if (transferNode == null) {
|
||||||
{
|
|
||||||
// *** penalty for being part of the reference track
|
// *** penalty for being part of the reference track
|
||||||
if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) )
|
if (refTrack != null && refTrack.containsNode(targetNode) && refTrack.containsNode(sourceNode)) {
|
||||||
{
|
|
||||||
int reftrackcost = linkdisttotal;
|
int reftrackcost = linkdisttotal;
|
||||||
cost += reftrackcost;
|
cost += reftrackcost;
|
||||||
}
|
}
|
||||||
|
|
@ -429,12 +352,9 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
}
|
}
|
||||||
transferNode = transferNode.next;
|
transferNode = transferNode.next;
|
||||||
|
|
||||||
if ( recordTransferNodes )
|
if (recordTransferNodes) {
|
||||||
{
|
originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement);
|
||||||
originElement = OsmPathElement.create( lon2, lat2, ele2, originElement, rc.countTraffic );
|
|
||||||
originElement.cost = cost;
|
originElement.cost = cost;
|
||||||
originElement.addTraffic( traffic );
|
|
||||||
traffic = 0;
|
|
||||||
}
|
}
|
||||||
lon0 = lon1;
|
lon0 = lon1;
|
||||||
lat0 = lat1;
|
lat0 = lat1;
|
||||||
|
|
@ -444,20 +364,16 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for nogo-matches (after the *actual* start of segment)
|
// check for nogo-matches (after the *actual* start of segment)
|
||||||
if ( rc.nogoCost < 0)
|
if (rc.nogoCost < 0) {
|
||||||
{
|
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
cost += rc.nogoCost;
|
cost += rc.nogoCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add target-node costs
|
// add target-node costs
|
||||||
double targetCost = processTargetNode(rc);
|
double targetCost = processTargetNode(rc);
|
||||||
if ( targetCost < 0. || targetCost + cost >= 2000000000. )
|
if (targetCost < 0. || targetCost + cost >= 2000000000.) {
|
||||||
{
|
|
||||||
cost = -1;
|
cost = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -465,10 +381,8 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public short interpolateEle( short e1, short e2, double fraction )
|
public short interpolateEle(short e1, short e2, double fraction) {
|
||||||
{
|
if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) {
|
||||||
if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
|
|
||||||
{
|
|
||||||
return Short.MIN_VALUE;
|
return Short.MIN_VALUE;
|
||||||
}
|
}
|
||||||
return (short) (e1 * (1. - fraction) + e2 * fraction);
|
return (short) (e1 * (1. - fraction) + e2 * fraction);
|
||||||
|
|
@ -478,47 +392,39 @@ abstract class OsmPath implements OsmLinkHolder
|
||||||
|
|
||||||
protected abstract double processTargetNode(RoutingContext rc);
|
protected abstract double processTargetNode(RoutingContext rc);
|
||||||
|
|
||||||
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
|
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract int elevationCorrection( RoutingContext rc );
|
public abstract int elevationCorrection();
|
||||||
|
|
||||||
public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
|
public abstract boolean definitlyWorseThan(OsmPath p);
|
||||||
|
|
||||||
public OsmNode getSourceNode()
|
public OsmNode getSourceNode() {
|
||||||
{
|
|
||||||
return sourceNode;
|
return sourceNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmNode getTargetNode()
|
public OsmNode getTargetNode() {
|
||||||
{
|
|
||||||
return targetNode;
|
return targetNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmLink getLink()
|
public OsmLink getLink() {
|
||||||
{
|
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setNextForLink( OsmLinkHolder holder )
|
public void setNextForLink(OsmLinkHolder holder) {
|
||||||
{
|
|
||||||
nextForLink = holder;
|
nextForLink = holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmLinkHolder getNextForLink()
|
public OsmLinkHolder getNextForLink() {
|
||||||
{
|
|
||||||
return nextForLink;
|
return nextForLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getTotalTime()
|
public double getTotalTime() {
|
||||||
{
|
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getTotalEnergy()
|
public double getTotalEnergy() {
|
||||||
{
|
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ import btools.util.CheapRuler;
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class OsmPathElement implements OsmPos
|
public class OsmPathElement implements OsmPos {
|
||||||
{
|
|
||||||
private int ilat; // latitude
|
private int ilat; // latitude
|
||||||
private int ilon; // longitude
|
private int ilon; // longitude
|
||||||
private short selev; // longitude
|
private short selev; // longitude
|
||||||
|
|
@ -25,77 +24,73 @@ public class OsmPathElement implements OsmPos
|
||||||
public int cost;
|
public int cost;
|
||||||
|
|
||||||
// interface OsmPos
|
// interface OsmPos
|
||||||
public final int getILat()
|
public final int getILat() {
|
||||||
{
|
|
||||||
return ilat;
|
return ilat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getILon()
|
public final int getILon() {
|
||||||
{
|
|
||||||
return ilon;
|
return ilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final short getSElev()
|
public final short getSElev() {
|
||||||
{
|
|
||||||
return selev;
|
return selev;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final double getElev()
|
public final void setSElev(short s) {
|
||||||
{
|
selev = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final double getElev() {
|
||||||
return selev / 4.;
|
return selev / 4.;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final float getTime()
|
public final float getTime() {
|
||||||
{
|
|
||||||
return message == null ? 0.f : message.time;
|
return message == null ? 0.f : message.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setTime( float t )
|
public final void setTime(float t) {
|
||||||
{
|
if (message != null) {
|
||||||
if ( message != null )
|
|
||||||
{
|
|
||||||
message.time = t;
|
message.time = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final float getEnergy()
|
public final float getEnergy() {
|
||||||
{
|
|
||||||
return message == null ? 0.f : message.energy;
|
return message == null ? 0.f : message.energy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setEnergy( float e )
|
public final void setEnergy(float e) {
|
||||||
{
|
if (message != null) {
|
||||||
if ( message != null )
|
|
||||||
{
|
|
||||||
message.energy = e;
|
message.energy = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getIdFromPos()
|
public final void setAngle(float e) {
|
||||||
{
|
if (message != null) {
|
||||||
|
message.turnangle = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final long getIdFromPos() {
|
||||||
return ((long) ilon) << 32 | ilat;
|
return ((long) ilon) << 32 | ilat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int calcDistance( OsmPos p )
|
public final int calcDistance(OsmPos p) {
|
||||||
{
|
return (int) Math.max(1.0, Math.round(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat())));
|
||||||
return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPathElement origin;
|
public OsmPathElement origin;
|
||||||
|
|
||||||
// construct a path element from a path
|
// construct a path element from a path
|
||||||
public static final OsmPathElement create( OsmPath path, boolean countTraffic )
|
public static final OsmPathElement create(OsmPath path) {
|
||||||
{
|
|
||||||
OsmNode n = path.getTargetNode();
|
OsmNode n = path.getTargetNode();
|
||||||
OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic );
|
OsmPathElement pe = create(n.getILon(), n.getILat(), n.getSElev(), path.originElement);
|
||||||
pe.cost = path.cost;
|
pe.cost = path.cost;
|
||||||
pe.message = path.message;
|
pe.message = path.message;
|
||||||
return pe;
|
return pe;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final OsmPathElement create( int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic )
|
public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin) {
|
||||||
{
|
OsmPathElement pe = new OsmPathElement();
|
||||||
OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
|
|
||||||
pe.ilon = ilon;
|
pe.ilon = ilon;
|
||||||
pe.ilat = ilat;
|
pe.ilat = ilat;
|
||||||
pe.selev = selev;
|
pe.selev = selev;
|
||||||
|
|
@ -103,29 +98,25 @@ public class OsmPathElement implements OsmPos
|
||||||
return pe;
|
return pe;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected OsmPathElement()
|
protected OsmPathElement() {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTraffic( float traffic )
|
public String toString() {
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return ilon + "_" + ilat;
|
return ilon + "_" + ilat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeToStream( DataOutput dos ) throws IOException
|
public boolean positionEquals(OsmPathElement e) {
|
||||||
{
|
return this.ilat == e.ilat && this.ilon == e.ilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeToStream(DataOutput dos) throws IOException {
|
||||||
dos.writeInt(ilat);
|
dos.writeInt(ilat);
|
||||||
dos.writeInt(ilon);
|
dos.writeInt(ilon);
|
||||||
dos.writeShort(selev);
|
dos.writeShort(selev);
|
||||||
dos.writeInt(cost);
|
dos.writeInt(cost);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OsmPathElement readFromStream( DataInput dis ) throws IOException
|
public static OsmPathElement readFromStream(DataInput dis) throws IOException {
|
||||||
{
|
|
||||||
OsmPathElement pe = new OsmPathElement();
|
OsmPathElement pe = new OsmPathElement();
|
||||||
pe.ilat = dis.readInt();
|
pe.ilat = dis.readInt();
|
||||||
pe.ilon = dis.readInt();
|
pe.ilon = dis.readInt();
|
||||||
|
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
package btools.router;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extension to OsmPathElement to count traffic load
|
|
||||||
*
|
|
||||||
* @author ab
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class OsmPathElementWithTraffic extends OsmPathElement
|
|
||||||
{
|
|
||||||
private int registerCount;
|
|
||||||
private float farTraffic;
|
|
||||||
private float nearTraffic;
|
|
||||||
|
|
||||||
public void register()
|
|
||||||
{
|
|
||||||
if ( registerCount++ == 0 )
|
|
||||||
{
|
|
||||||
if ( origin instanceof OsmPathElementWithTraffic )
|
|
||||||
{
|
|
||||||
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)origin;
|
|
||||||
ot.register();
|
|
||||||
ot.farTraffic += farTraffic;
|
|
||||||
ot.nearTraffic += nearTraffic;
|
|
||||||
farTraffic = 0;
|
|
||||||
nearTraffic = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTraffic( float traffic )
|
|
||||||
{
|
|
||||||
this.farTraffic += traffic;
|
|
||||||
this.nearTraffic += traffic;
|
|
||||||
}
|
|
||||||
|
|
||||||
// unregister from origin if our registercount is 0, else do nothing
|
|
||||||
|
|
||||||
public static double maxtraffic = 0.;
|
|
||||||
|
|
||||||
public boolean unregister( RoutingContext rc ) throws IOException
|
|
||||||
{
|
|
||||||
if ( --registerCount == 0 )
|
|
||||||
{
|
|
||||||
if ( origin instanceof OsmPathElementWithTraffic )
|
|
||||||
{
|
|
||||||
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)origin;
|
|
||||||
|
|
||||||
int costdelta = cost-ot.cost;
|
|
||||||
ot.farTraffic += farTraffic*Math.exp(-costdelta/rc.farTrafficDecayLength);
|
|
||||||
ot.nearTraffic += nearTraffic*Math.exp(-costdelta/rc.nearTrafficDecayLength);
|
|
||||||
|
|
||||||
if ( costdelta > 0 && farTraffic > maxtraffic ) maxtraffic = farTraffic;
|
|
||||||
|
|
||||||
int t2 = cost == ot.cost ? -1 : (int)(rc.farTrafficWeight*farTraffic + rc.nearTrafficWeight*nearTraffic);
|
|
||||||
|
|
||||||
if ( t2 > 4000 || t2 == -1 )
|
|
||||||
{
|
|
||||||
// System.out.println( "unregistered: " + this + " origin=" + ot + " farTraffic =" + farTraffic + " nearTraffic =" + nearTraffic + " cost=" + cost );
|
|
||||||
if ( rc.trafficOutputStream != null )
|
|
||||||
{
|
|
||||||
rc.trafficOutputStream.writeLong( getIdFromPos());
|
|
||||||
rc.trafficOutputStream.writeLong( ot.getIdFromPos());
|
|
||||||
rc.trafficOutputStream.writeInt( t2 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
farTraffic = 0;
|
|
||||||
nearTraffic = 0;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -11,8 +11,7 @@ import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
|
|
||||||
|
|
||||||
abstract class OsmPathModel
|
abstract class OsmPathModel {
|
||||||
{
|
|
||||||
public abstract OsmPrePath createPrePath();
|
public abstract OsmPrePath createPrePath();
|
||||||
|
|
||||||
public abstract OsmPath createPath();
|
public abstract OsmPath createPath();
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,15 @@ package btools.router;
|
||||||
|
|
||||||
import btools.mapaccess.OsmLink;
|
import btools.mapaccess.OsmLink;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.mapaccess.OsmTransferNode;
|
|
||||||
|
|
||||||
public abstract class OsmPrePath
|
public abstract class OsmPrePath {
|
||||||
{
|
|
||||||
protected OsmNode sourceNode;
|
protected OsmNode sourceNode;
|
||||||
protected OsmNode targetNode;
|
protected OsmNode targetNode;
|
||||||
protected OsmLink link;
|
protected OsmLink link;
|
||||||
|
|
||||||
public OsmPrePath next;
|
public OsmPrePath next;
|
||||||
|
|
||||||
public void init( OsmPath origin, OsmLink link, RoutingContext rc )
|
public void init(OsmPath origin, OsmLink link, RoutingContext rc) {
|
||||||
{
|
|
||||||
this.link = link;
|
this.link = link;
|
||||||
this.sourceNode = origin.getTargetNode();
|
this.sourceNode = origin.getTargetNode();
|
||||||
this.targetNode = link.getTarget(sourceNode);
|
this.targetNode = link.getTarget(sourceNode);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -11,8 +11,7 @@ import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
import btools.expressions.BExpressionMetaData;
|
import btools.expressions.BExpressionMetaData;
|
||||||
|
|
||||||
public final class ProfileCache
|
public final class ProfileCache {
|
||||||
{
|
|
||||||
|
|
||||||
private static File lastLookupFile;
|
private static File lastLookupFile;
|
||||||
private static long lastLookupTimestamp;
|
private static long lastLookupTimestamp;
|
||||||
|
|
@ -27,23 +26,18 @@ public final class ProfileCache
|
||||||
private static ProfileCache[] apc = new ProfileCache[1];
|
private static ProfileCache[] apc = new ProfileCache[1];
|
||||||
private static boolean debug = Boolean.getBoolean("debugProfileCache");
|
private static boolean debug = Boolean.getBoolean("debugProfileCache");
|
||||||
|
|
||||||
public static synchronized void setSize( int size )
|
public static synchronized void setSize(int size) {
|
||||||
{
|
|
||||||
apc = new ProfileCache[size];
|
apc = new ProfileCache[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized boolean parseProfile( RoutingContext rc )
|
public static synchronized boolean parseProfile(RoutingContext rc) {
|
||||||
{
|
|
||||||
String profileBaseDir = System.getProperty("profileBaseDir");
|
String profileBaseDir = System.getProperty("profileBaseDir");
|
||||||
File profileDir;
|
File profileDir;
|
||||||
File profileFile;
|
File profileFile;
|
||||||
if ( profileBaseDir == null )
|
if (profileBaseDir == null) {
|
||||||
{
|
|
||||||
profileDir = new File(rc.localFunction).getParentFile();
|
profileDir = new File(rc.localFunction).getParentFile();
|
||||||
profileFile = new File(rc.localFunction);
|
profileFile = new File(rc.localFunction);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
profileDir = new File(profileBaseDir);
|
profileDir = new File(profileBaseDir);
|
||||||
profileFile = new File(profileDir, rc.localFunction + ".brf");
|
profileFile = new File(profileDir, rc.localFunction + ".brf");
|
||||||
}
|
}
|
||||||
|
|
@ -52,10 +46,8 @@ public final class ProfileCache
|
||||||
File lookupFile = new File(profileDir, "lookups.dat");
|
File lookupFile = new File(profileDir, "lookups.dat");
|
||||||
|
|
||||||
// invalidate cache at lookup-table update
|
// invalidate cache at lookup-table update
|
||||||
if ( !(lookupFile.equals( lastLookupFile ) && lookupFile.lastModified() == lastLookupTimestamp ) )
|
if (!(lookupFile.equals(lastLookupFile) && lookupFile.lastModified() == lastLookupTimestamp)) {
|
||||||
{
|
if (lastLookupFile != null) {
|
||||||
if ( lastLookupFile != null )
|
|
||||||
{
|
|
||||||
System.out.println("******** invalidating profile-cache after lookup-file update ******** ");
|
System.out.println("******** invalidating profile-cache after lookup-file update ******** ");
|
||||||
}
|
}
|
||||||
apc = new ProfileCache[apc.length];
|
apc = new ProfileCache[apc.length];
|
||||||
|
|
@ -67,16 +59,12 @@ public final class ProfileCache
|
||||||
int unusedSlot = -1;
|
int unusedSlot = -1;
|
||||||
|
|
||||||
// check for re-use
|
// check for re-use
|
||||||
for( int i=0; i<apc.length; i++)
|
for (int i = 0; i < apc.length; i++) {
|
||||||
{
|
|
||||||
ProfileCache pc = apc[i];
|
ProfileCache pc = apc[i];
|
||||||
|
|
||||||
if ( pc != null )
|
if (pc != null) {
|
||||||
{
|
if ((!pc.profilesBusy) && profileFile.equals(pc.lastProfileFile)) {
|
||||||
if ( (!pc.profilesBusy) && profileFile.equals( pc.lastProfileFile ) )
|
if (rc.profileTimestamp == pc.lastProfileTimestamp) {
|
||||||
{
|
|
||||||
if ( rc.profileTimestamp == pc.lastProfileTimestamp )
|
|
||||||
{
|
|
||||||
rc.expctxWay = pc.expctxWay;
|
rc.expctxWay = pc.expctxWay;
|
||||||
rc.expctxNode = pc.expctxNode;
|
rc.expctxNode = pc.expctxNode;
|
||||||
rc.readGlobalConfig();
|
rc.readGlobalConfig();
|
||||||
|
|
@ -87,13 +75,10 @@ public final class ProfileCache
|
||||||
unusedSlot = -1;
|
unusedSlot = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ( lru == null || lru.lastUseTime > pc.lastUseTime )
|
if (lru == null || lru.lastUseTime > pc.lastUseTime) {
|
||||||
{
|
|
||||||
lru = pc;
|
lru = pc;
|
||||||
}
|
}
|
||||||
}
|
} else if (unusedSlot < 0) {
|
||||||
else if ( unusedSlot < 0 )
|
|
||||||
{
|
|
||||||
unusedSlot = i;
|
unusedSlot = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,29 +91,27 @@ public final class ProfileCache
|
||||||
|
|
||||||
meta.readMetaData(new File(profileDir, "lookups.dat"));
|
meta.readMetaData(new File(profileDir, "lookups.dat"));
|
||||||
|
|
||||||
rc.expctxWay.parseFile( profileFile, "global" );
|
rc.expctxWay.parseFile(profileFile, "global", rc.keyValues);
|
||||||
rc.expctxNode.parseFile( profileFile, "global" );
|
rc.expctxNode.parseFile(profileFile, "global", rc.keyValues);
|
||||||
|
|
||||||
rc.readGlobalConfig();
|
rc.readGlobalConfig();
|
||||||
|
|
||||||
if ( rc.processUnusedTags )
|
if (rc.processUnusedTags) {
|
||||||
{
|
|
||||||
rc.expctxWay.setAllTagsUsed();
|
rc.expctxWay.setAllTagsUsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( lru == null || unusedSlot >= 0 )
|
if (lru == null || unusedSlot >= 0) {
|
||||||
{
|
|
||||||
lru = new ProfileCache();
|
lru = new ProfileCache();
|
||||||
if ( unusedSlot >= 0 )
|
if (unusedSlot >= 0) {
|
||||||
{
|
|
||||||
apc[unusedSlot] = lru;
|
apc[unusedSlot] = lru;
|
||||||
if ( debug ) System.out.println( "******* adding new profile at idx=" + unusedSlot + " for " + profileFile );
|
if (debug)
|
||||||
|
System.out.println("******* adding new profile at idx=" + unusedSlot + " for " + profileFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( lru.lastProfileFile != null )
|
if (lru.lastProfileFile != null) {
|
||||||
{
|
if (debug)
|
||||||
if ( debug ) System.out.println( "******* replacing profile of age " + ((System.currentTimeMillis()-lru.lastUseTime)/1000L) + " sec " + lru.lastProfileFile + "->" + profileFile );
|
System.out.println("******* replacing profile of age " + ((System.currentTimeMillis() - lru.lastUseTime) / 1000L) + " sec " + lru.lastProfileFile + "->" + profileFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
lru.lastProfileTimestamp = rc.profileTimestamp;
|
lru.lastProfileTimestamp = rc.profileTimestamp;
|
||||||
|
|
@ -140,17 +123,13 @@ public final class ProfileCache
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized void releaseProfile( RoutingContext rc )
|
public static synchronized void releaseProfile(RoutingContext rc) {
|
||||||
{
|
for (int i = 0; i < apc.length; i++) {
|
||||||
for( int i=0; i<apc.length; i++)
|
|
||||||
{
|
|
||||||
ProfileCache pc = apc[i];
|
ProfileCache pc = apc[i];
|
||||||
|
|
||||||
if ( pc != null )
|
if (pc != null) {
|
||||||
{
|
|
||||||
// only the thread that holds the cached instance can release it
|
// only the thread that holds the cached instance can release it
|
||||||
if ( rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode )
|
if (rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode) {
|
||||||
{
|
|
||||||
pc.profilesBusy = false;
|
pc.profilesBusy = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
import java.io.DataOutput;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -15,21 +14,21 @@ import btools.expressions.BExpressionContext;
|
||||||
import btools.expressions.BExpressionContextNode;
|
import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
import btools.mapaccess.GeometryDecoder;
|
import btools.mapaccess.GeometryDecoder;
|
||||||
|
import btools.mapaccess.MatchedWaypoint;
|
||||||
import btools.mapaccess.OsmLink;
|
import btools.mapaccess.OsmLink;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
import btools.util.CheapAngleMeter;
|
import btools.util.CheapAngleMeter;
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public final class RoutingContext
|
public final class RoutingContext {
|
||||||
{
|
public void setAlternativeIdx(int idx) {
|
||||||
public void setAlternativeIdx(int idx )
|
|
||||||
{
|
|
||||||
alternativeIdx = idx;
|
alternativeIdx = idx;
|
||||||
}
|
}
|
||||||
public int getAlternativeIdx(int min, int max)
|
|
||||||
{
|
public int getAlternativeIdx(int min, int max) {
|
||||||
return alternativeIdx < min ? min : (alternativeIdx > max ? max : alternativeIdx);
|
return alternativeIdx < min ? min : (alternativeIdx > max ? max : alternativeIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int alternativeIdx = 0;
|
public int alternativeIdx = 0;
|
||||||
public String localFunction;
|
public String localFunction;
|
||||||
public long profileTimestamp;
|
public long profileTimestamp;
|
||||||
|
|
@ -38,8 +37,7 @@ public final class RoutingContext
|
||||||
|
|
||||||
public String rawTrackPath;
|
public String rawTrackPath;
|
||||||
|
|
||||||
public String getProfileName()
|
public String getProfileName() {
|
||||||
{
|
|
||||||
String name = localFunction == null ? "unknown" : localFunction;
|
String name = localFunction == null ? "unknown" : localFunction;
|
||||||
if (name.endsWith(".brf")) name = name.substring(0, localFunction.length() - 4);
|
if (name.endsWith(".brf")) name = name.substring(0, localFunction.length() - 4);
|
||||||
int idx = name.lastIndexOf(File.separatorChar);
|
int idx = name.lastIndexOf(File.separatorChar);
|
||||||
|
|
@ -54,10 +52,6 @@ public final class RoutingContext
|
||||||
|
|
||||||
public int memoryclass = 64;
|
public int memoryclass = 64;
|
||||||
|
|
||||||
public int downhillcostdiv;
|
|
||||||
public int downhillcutoff;
|
|
||||||
public int uphillcostdiv;
|
|
||||||
public int uphillcutoff;
|
|
||||||
public boolean carMode;
|
public boolean carMode;
|
||||||
public boolean bikeMode;
|
public boolean bikeMode;
|
||||||
public boolean footMode;
|
public boolean footMode;
|
||||||
|
|
@ -80,75 +74,52 @@ public final class RoutingContext
|
||||||
public boolean transitonly;
|
public boolean transitonly;
|
||||||
|
|
||||||
public double waypointCatchingRange;
|
public double waypointCatchingRange;
|
||||||
|
public boolean correctMisplacedViaPoints;
|
||||||
|
public double correctMisplacedViaPointsDistance;
|
||||||
|
|
||||||
private void setModel( String className )
|
private void setModel(String className) {
|
||||||
{
|
if (className == null) {
|
||||||
if ( className == null )
|
|
||||||
{
|
|
||||||
pm = new StdModel();
|
pm = new StdModel();
|
||||||
}
|
} else {
|
||||||
else
|
try {
|
||||||
{
|
Class<?> clazz = Class.forName(className);
|
||||||
try
|
pm = (OsmPathModel) clazz.getDeclaredConstructor().newInstance();
|
||||||
{
|
} catch (Exception e) {
|
||||||
Class clazz = Class.forName( className );
|
|
||||||
pm = (OsmPathModel) clazz.newInstance();
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
throw new RuntimeException("Cannot create path-model: " + e);
|
throw new RuntimeException("Cannot create path-model: " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
initModel();
|
initModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initModel()
|
public void initModel() {
|
||||||
{
|
|
||||||
pm.init(expctxWay, expctxNode, keyValues);
|
pm.init(expctxWay, expctxNode, keyValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKeyValueChecksum()
|
public long getKeyValueChecksum() {
|
||||||
{
|
|
||||||
long s = 0L;
|
long s = 0L;
|
||||||
if ( keyValues != null )
|
if (keyValues != null) {
|
||||||
{
|
for (Map.Entry<String, String> e : keyValues.entrySet()) {
|
||||||
for( Map.Entry<String,String> e : keyValues.entrySet() )
|
|
||||||
{
|
|
||||||
s += e.getKey().hashCode() + e.getValue().hashCode();
|
s += e.getKey().hashCode() + e.getValue().hashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readGlobalConfig()
|
public void readGlobalConfig() {
|
||||||
{
|
|
||||||
BExpressionContext expctxGlobal = expctxWay; // just one of them...
|
BExpressionContext expctxGlobal = expctxWay; // just one of them...
|
||||||
|
|
||||||
if (keyValues != null) {
|
|
||||||
// add parameter to context
|
|
||||||
for (Map.Entry<String, String> e : keyValues.entrySet()) {
|
|
||||||
float f = Float.parseFloat(e.getValue());
|
|
||||||
expctxWay.setVariableValue(e.getKey(), f, false );
|
|
||||||
expctxNode.setVariableValue(e.getKey(), f, false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setModel(expctxGlobal._modelClass);
|
setModel(expctxGlobal._modelClass);
|
||||||
|
|
||||||
downhillcostdiv = (int)expctxGlobal.getVariableValue( "downhillcost", 0.f );
|
|
||||||
downhillcutoff = (int)(expctxGlobal.getVariableValue( "downhillcutoff", 0.f )*10000);
|
|
||||||
uphillcostdiv = (int)expctxGlobal.getVariableValue( "uphillcost", 0.f );
|
|
||||||
uphillcutoff = (int)(expctxGlobal.getVariableValue( "uphillcutoff", 0.f )*10000);
|
|
||||||
if ( downhillcostdiv != 0 ) downhillcostdiv = 1000000/downhillcostdiv;
|
|
||||||
if ( uphillcostdiv != 0 ) uphillcostdiv = 1000000/uphillcostdiv;
|
|
||||||
carMode = 0.f != expctxGlobal.getVariableValue("validForCars", 0.f);
|
carMode = 0.f != expctxGlobal.getVariableValue("validForCars", 0.f);
|
||||||
bikeMode = 0.f != expctxGlobal.getVariableValue("validForBikes", 0.f);
|
bikeMode = 0.f != expctxGlobal.getVariableValue("validForBikes", 0.f);
|
||||||
footMode = 0.f != expctxGlobal.getVariableValue("validForFoot", 0.f);
|
footMode = 0.f != expctxGlobal.getVariableValue("validForFoot", 0.f);
|
||||||
|
|
||||||
waypointCatchingRange = expctxGlobal.getVariableValue("waypointCatchingRange", 250.f);
|
waypointCatchingRange = expctxGlobal.getVariableValue("waypointCatchingRange", 250.f);
|
||||||
|
|
||||||
// turn-restrictions used per default for car profiles
|
// turn-restrictions not used per default for foot profiles
|
||||||
considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue( "considerTurnRestrictions", 1.f );
|
considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue("considerTurnRestrictions", footMode ? 0.f : 1.f);
|
||||||
|
|
||||||
|
correctMisplacedViaPoints = 0.f != expctxGlobal.getVariableValue("correctMisplacedViaPoints", 1.f);
|
||||||
|
correctMisplacedViaPointsDistance = expctxGlobal.getVariableValue("correctMisplacedViaPointsDistance", 40.f);
|
||||||
|
|
||||||
// process tags not used in the profile (to have them in the data-tab)
|
// process tags not used in the profile (to have them in the data-tab)
|
||||||
processUnusedTags = 0.f != expctxGlobal.getVariableValue("processUnusedTags", 0.f);
|
processUnusedTags = 0.f != expctxGlobal.getVariableValue("processUnusedTags", 0.f);
|
||||||
|
|
@ -169,21 +140,13 @@ public final class RoutingContext
|
||||||
starttimeoffset = expctxGlobal.getVariableValue("starttimeoffset", 0.f);
|
starttimeoffset = expctxGlobal.getVariableValue("starttimeoffset", 0.f);
|
||||||
transitonly = expctxGlobal.getVariableValue("transitonly", 0.f) != 0.f;
|
transitonly = expctxGlobal.getVariableValue("transitonly", 0.f) != 0.f;
|
||||||
|
|
||||||
farTrafficWeight = expctxGlobal.getVariableValue( "farTrafficWeight", 2.f );
|
|
||||||
nearTrafficWeight = expctxGlobal.getVariableValue( "nearTrafficWeight", 2.f );
|
|
||||||
farTrafficDecayLength = expctxGlobal.getVariableValue( "farTrafficDecayLength", 30000.f );
|
|
||||||
nearTrafficDecayLength = expctxGlobal.getVariableValue( "nearTrafficDecayLength", 3000.f );
|
|
||||||
trafficDirectionFactor = expctxGlobal.getVariableValue( "trafficDirectionFactor", 0.9f );
|
|
||||||
trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f );
|
|
||||||
trafficSourceMinDist = expctxGlobal.getVariableValue( "trafficSourceMinDist", 3000.f );
|
|
||||||
|
|
||||||
showspeed = 0.f != expctxGlobal.getVariableValue("showspeed", 0.f);
|
showspeed = 0.f != expctxGlobal.getVariableValue("showspeed", 0.f);
|
||||||
showSpeedProfile = 0.f != expctxGlobal.getVariableValue("showSpeedProfile", 0.f);
|
showSpeedProfile = 0.f != expctxGlobal.getVariableValue("showSpeedProfile", 0.f);
|
||||||
inverseRouting = 0.f != expctxGlobal.getVariableValue("inverseRouting", 0.f);
|
inverseRouting = 0.f != expctxGlobal.getVariableValue("inverseRouting", 0.f);
|
||||||
|
showTime = 0.f != expctxGlobal.getVariableValue("showtime", 0.f);
|
||||||
|
|
||||||
int tiMode = (int) expctxGlobal.getVariableValue("turnInstructionMode", 0.f);
|
int tiMode = (int) expctxGlobal.getVariableValue("turnInstructionMode", 0.f);
|
||||||
if ( tiMode != 1 ) // automatic selection from coordinate source
|
if (tiMode != 1) { // automatic selection from coordinate source
|
||||||
{
|
|
||||||
turnInstructionMode = tiMode;
|
turnInstructionMode = tiMode;
|
||||||
}
|
}
|
||||||
turnInstructionCatchingRange = expctxGlobal.getVariableValue("turnInstructionCatchingRange", 40.f);
|
turnInstructionCatchingRange = expctxGlobal.getVariableValue("turnInstructionCatchingRange", 40.f);
|
||||||
|
|
@ -227,21 +190,15 @@ public final class RoutingContext
|
||||||
public int ilatshortest;
|
public int ilatshortest;
|
||||||
public int ilonshortest;
|
public int ilonshortest;
|
||||||
|
|
||||||
public boolean countTraffic;
|
|
||||||
public boolean inverseDirection;
|
public boolean inverseDirection;
|
||||||
public DataOutput trafficOutputStream;
|
|
||||||
|
|
||||||
public double farTrafficWeight;
|
|
||||||
public double nearTrafficWeight;
|
|
||||||
public double farTrafficDecayLength;
|
|
||||||
public double nearTrafficDecayLength;
|
|
||||||
public double trafficDirectionFactor;
|
|
||||||
public double trafficSourceExponent;
|
|
||||||
public double trafficSourceMinDist;
|
|
||||||
|
|
||||||
public boolean showspeed;
|
public boolean showspeed;
|
||||||
public boolean showSpeedProfile;
|
public boolean showSpeedProfile;
|
||||||
public boolean inverseRouting;
|
public boolean inverseRouting;
|
||||||
|
public boolean showTime;
|
||||||
|
|
||||||
|
public String outputFormat = "gpx";
|
||||||
|
public boolean exportWaypoints = false;
|
||||||
|
|
||||||
public OsmPrePath firstPrePath;
|
public OsmPrePath firstPrePath;
|
||||||
|
|
||||||
|
|
@ -256,22 +213,19 @@ public final class RoutingContext
|
||||||
public double defaultC_r;
|
public double defaultC_r;
|
||||||
public double bikerPower;
|
public double bikerPower;
|
||||||
|
|
||||||
public static void prepareNogoPoints( List<OsmNodeNamed> nogos )
|
public static void prepareNogoPoints(List<OsmNodeNamed> nogos) {
|
||||||
{
|
for (OsmNodeNamed nogo : nogos) {
|
||||||
for( OsmNodeNamed nogo : nogos )
|
if (nogo instanceof OsmNogoPolygon) {
|
||||||
{
|
|
||||||
if (nogo instanceof OsmNogoPolygon)
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String s = nogo.name;
|
String s = nogo.name;
|
||||||
int idx = s.indexOf(' ');
|
int idx = s.indexOf(' ');
|
||||||
if (idx > 0) s = s.substring(0, idx);
|
if (idx > 0) s = s.substring(0, idx);
|
||||||
int ir = 20; // default radius
|
int ir = 20; // default radius
|
||||||
if ( s.length() > 4 )
|
if (s.length() > 4) {
|
||||||
{
|
try {
|
||||||
try { ir = Integer.parseInt( s.substring( 4 ) ); }
|
ir = Integer.parseInt(s.substring(4));
|
||||||
catch( Exception e ) { /* ignore */ }
|
} catch (Exception e) { /* ignore */ }
|
||||||
}
|
}
|
||||||
// Radius of the nogo point in meters
|
// Radius of the nogo point in meters
|
||||||
nogo.radius = ir;
|
nogo.radius = ir;
|
||||||
|
|
@ -281,8 +235,7 @@ public final class RoutingContext
|
||||||
/**
|
/**
|
||||||
* restore the full nogolist previously saved by cleanNogoList
|
* restore the full nogolist previously saved by cleanNogoList
|
||||||
*/
|
*/
|
||||||
public void restoreNogoList()
|
public void restoreNogoList() {
|
||||||
{
|
|
||||||
nogopoints = nogopoints_all;
|
nogopoints = nogopoints_all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,22 +245,18 @@ public final class RoutingContext
|
||||||
*
|
*
|
||||||
* @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode)
|
* @return true if all wayoints are all in the same (full-weigth) nogo area (triggering bee-line-mode)
|
||||||
*/
|
*/
|
||||||
public void cleanNogoList( List<OsmNode> waypoints )
|
public void cleanNogoList(List<OsmNode> waypoints) {
|
||||||
{
|
|
||||||
nogopoints_all = nogopoints;
|
nogopoints_all = nogopoints;
|
||||||
if (nogopoints == null) return;
|
if (nogopoints == null) return;
|
||||||
List<OsmNodeNamed> nogos = new ArrayList<OsmNodeNamed>();
|
List<OsmNodeNamed> nogos = new ArrayList<>();
|
||||||
for( OsmNodeNamed nogo : nogopoints )
|
for (OsmNodeNamed nogo : nogopoints) {
|
||||||
{
|
|
||||||
boolean goodGuy = true;
|
boolean goodGuy = true;
|
||||||
for( OsmNode wp : waypoints )
|
for (OsmNode wp : waypoints) {
|
||||||
{
|
|
||||||
if (wp.calcDistance(nogo) < nogo.radius
|
if (wp.calcDistance(nogo) < nogo.radius
|
||||||
&& (!(nogo instanceof OsmNogoPolygon)
|
&& (!(nogo instanceof OsmNogoPolygon)
|
||||||
|| (((OsmNogoPolygon) nogo).isClosed
|
|| (((OsmNogoPolygon) nogo).isClosed
|
||||||
? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
|
? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
|
||||||
: ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
|
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
|
||||||
{
|
|
||||||
goodGuy = false;
|
goodGuy = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -316,22 +265,73 @@ public final class RoutingContext
|
||||||
nogopoints = nogos.isEmpty() ? null : nogos;
|
nogopoints = nogos.isEmpty() ? null : nogos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allInOneNogo( List<OsmNode> waypoints )
|
public void checkMatchedWaypointAgainstNogos(List<MatchedWaypoint> matchedWaypoints) {
|
||||||
{
|
if (nogopoints == null) return;
|
||||||
|
int theSize = matchedWaypoints.size();
|
||||||
|
if (theSize<2) return;
|
||||||
|
int removed = 0;
|
||||||
|
List<MatchedWaypoint> newMatchedWaypoints = new ArrayList<>();
|
||||||
|
MatchedWaypoint prevMwp = null;
|
||||||
|
boolean prevMwpIsInside = false;
|
||||||
|
for (int i = 0; i < theSize; i++) {
|
||||||
|
MatchedWaypoint mwp = matchedWaypoints.get(i);
|
||||||
|
boolean isInsideNogo = false;
|
||||||
|
OsmNode wp = mwp.crosspoint;
|
||||||
|
for (OsmNodeNamed nogo : nogopoints) {
|
||||||
|
if (Double.isNaN(nogo.nogoWeight)
|
||||||
|
&& wp.calcDistance(nogo) < nogo.radius
|
||||||
|
&& (!(nogo instanceof OsmNogoPolygon)
|
||||||
|
|| (((OsmNogoPolygon) nogo).isClosed
|
||||||
|
? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
|
||||||
|
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
|
||||||
|
isInsideNogo = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isInsideNogo) {
|
||||||
|
boolean useAnyway = false;
|
||||||
|
if (prevMwp == null) useAnyway = true;
|
||||||
|
else if (mwp.direct) useAnyway = true;
|
||||||
|
else if (prevMwp.direct) useAnyway = true;
|
||||||
|
else if (prevMwpIsInside) useAnyway = true;
|
||||||
|
else if (i == theSize-1) {
|
||||||
|
throw new IllegalArgumentException("last wpt in restricted area ");
|
||||||
|
}
|
||||||
|
if (useAnyway) {
|
||||||
|
prevMwpIsInside = true;
|
||||||
|
newMatchedWaypoints.add(mwp);
|
||||||
|
} else {
|
||||||
|
removed++;
|
||||||
|
prevMwpIsInside = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
prevMwpIsInside = false;
|
||||||
|
newMatchedWaypoints.add(mwp);
|
||||||
|
}
|
||||||
|
prevMwp = mwp;
|
||||||
|
}
|
||||||
|
if (newMatchedWaypoints.size() < 2) {
|
||||||
|
throw new IllegalArgumentException("a wpt in restricted area ");
|
||||||
|
}
|
||||||
|
if (removed > 0) {
|
||||||
|
matchedWaypoints.clear();
|
||||||
|
matchedWaypoints.addAll(newMatchedWaypoints);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allInOneNogo(List<OsmNode> waypoints) {
|
||||||
if (nogopoints == null) return false;
|
if (nogopoints == null) return false;
|
||||||
boolean allInTotal = false;
|
boolean allInTotal = false;
|
||||||
for( OsmNodeNamed nogo : nogopoints )
|
for (OsmNodeNamed nogo : nogopoints) {
|
||||||
{
|
|
||||||
boolean allIn = Double.isNaN(nogo.nogoWeight);
|
boolean allIn = Double.isNaN(nogo.nogoWeight);
|
||||||
for( OsmNode wp : waypoints )
|
for (OsmNode wp : waypoints) {
|
||||||
{
|
|
||||||
int dist = wp.calcDistance(nogo);
|
int dist = wp.calcDistance(nogo);
|
||||||
if (dist < nogo.radius
|
if (dist < nogo.radius
|
||||||
&& (!(nogo instanceof OsmNogoPolygon)
|
&& (!(nogo instanceof OsmNogoPolygon)
|
||||||
|| (((OsmNogoPolygon) nogo).isClosed
|
|| (((OsmNogoPolygon) nogo).isClosed
|
||||||
? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
|
? ((OsmNogoPolygon) nogo).isWithin(wp.ilon, wp.ilat)
|
||||||
: ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
|
: ((OsmNogoPolygon) nogo).isOnPolyline(wp.ilon, wp.ilat)))) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
allIn = false;
|
allIn = false;
|
||||||
|
|
@ -341,12 +341,10 @@ public final class RoutingContext
|
||||||
return allInTotal;
|
return allInTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] getNogoChecksums()
|
public long[] getNogoChecksums() {
|
||||||
{
|
|
||||||
long[] cs = new long[3];
|
long[] cs = new long[3];
|
||||||
int n = nogopoints == null ? 0 : nogopoints.size();
|
int n = nogopoints == null ? 0 : nogopoints.size();
|
||||||
for( int i=0; i<n; i++ )
|
for (int i = 0; i < n; i++) {
|
||||||
{
|
|
||||||
OsmNodeNamed nogo = nogopoints.get(i);
|
OsmNodeNamed nogo = nogopoints.get(i);
|
||||||
cs[0] += nogo.ilon;
|
cs[0] += nogo.ilon;
|
||||||
cs[1] += nogo.ilat;
|
cs[1] += nogo.ilat;
|
||||||
|
|
@ -356,25 +354,21 @@ public final class RoutingContext
|
||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWaypoint( OsmNodeNamed wp, boolean endpoint )
|
public void setWaypoint(OsmNodeNamed wp, boolean endpoint) {
|
||||||
{
|
|
||||||
setWaypoint(wp, null, endpoint);
|
setWaypoint(wp, null, endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWaypoint( OsmNodeNamed wp, OsmNodeNamed pendingEndpoint, boolean endpoint )
|
public void setWaypoint(OsmNodeNamed wp, OsmNodeNamed pendingEndpoint, boolean endpoint) {
|
||||||
{
|
|
||||||
keepnogopoints = nogopoints;
|
keepnogopoints = nogopoints;
|
||||||
nogopoints = new ArrayList<OsmNodeNamed>();
|
nogopoints = new ArrayList<>();
|
||||||
nogopoints.add(wp);
|
nogopoints.add(wp);
|
||||||
if (keepnogopoints != null) nogopoints.addAll(keepnogopoints);
|
if (keepnogopoints != null) nogopoints.addAll(keepnogopoints);
|
||||||
isEndpoint = endpoint;
|
isEndpoint = endpoint;
|
||||||
this.pendingEndpoint = pendingEndpoint;
|
this.pendingEndpoint = pendingEndpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean checkPendingEndpoint()
|
public boolean checkPendingEndpoint() {
|
||||||
{
|
if (pendingEndpoint != null) {
|
||||||
if ( pendingEndpoint != null )
|
|
||||||
{
|
|
||||||
isEndpoint = true;
|
isEndpoint = true;
|
||||||
nogopoints.set(0, pendingEndpoint);
|
nogopoints.set(0, pendingEndpoint);
|
||||||
pendingEndpoint = null;
|
pendingEndpoint = null;
|
||||||
|
|
@ -383,15 +377,13 @@ public final class RoutingContext
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsetWaypoint()
|
public void unsetWaypoint() {
|
||||||
{
|
|
||||||
nogopoints = keepnogopoints;
|
nogopoints = keepnogopoints;
|
||||||
pendingEndpoint = null;
|
pendingEndpoint = null;
|
||||||
isEndpoint = false;
|
isEndpoint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int calcDistance( int lon1, int lat1, int lon2, int lat2 )
|
public int calcDistance(int lon1, int lat1, int lon2, int lat2) {
|
||||||
{
|
|
||||||
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lat1 + lat2) >> 1);
|
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lat1 + lat2) >> 1);
|
||||||
double dlon2m = lonlat2m[0];
|
double dlon2m = lonlat2m[0];
|
||||||
double dlat2m = lonlat2m[1];
|
double dlat2m = lonlat2m[1];
|
||||||
|
|
@ -401,10 +393,8 @@ public final class RoutingContext
|
||||||
|
|
||||||
shortestmatch = false;
|
shortestmatch = false;
|
||||||
|
|
||||||
if ( nogopoints != null && !nogopoints.isEmpty() && d > 0. )
|
if (nogopoints != null && !nogopoints.isEmpty() && d > 0.) {
|
||||||
{
|
for (int ngidx = 0; ngidx < nogopoints.size(); ngidx++) {
|
||||||
for( int ngidx = 0; ngidx < nogopoints.size(); ngidx++ )
|
|
||||||
{
|
|
||||||
OsmNodeNamed nogo = nogopoints.get(ngidx);
|
OsmNodeNamed nogo = nogopoints.get(ngidx);
|
||||||
double x1 = (lon1 - nogo.ilon) * dlon2m;
|
double x1 = (lon1 - nogo.ilon) * dlon2m;
|
||||||
double y1 = (lat1 - nogo.ilat) * dlat2m;
|
double y1 = (lat1 - nogo.ilat) * dlat2m;
|
||||||
|
|
@ -414,20 +404,20 @@ public final class RoutingContext
|
||||||
double r22 = x2 * x2 + y2 * y2;
|
double r22 = x2 * x2 + y2 * y2;
|
||||||
double radius = Math.abs(r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy) / d;
|
double radius = Math.abs(r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy) / d;
|
||||||
|
|
||||||
if ( radius < nogo.radius ) // 20m
|
if (radius < nogo.radius) { // 20m
|
||||||
{
|
|
||||||
double s1 = x1 * dx + y1 * dy;
|
double s1 = x1 * dx + y1 * dy;
|
||||||
double s2 = x2 * dx + y2 * dy;
|
double s2 = x2 * dx + y2 * dy;
|
||||||
|
|
||||||
|
|
||||||
if ( s1 < 0. ) { s1 = -s1; s2 = -s2; }
|
if (s1 < 0.) {
|
||||||
if ( s2 > 0. )
|
s1 = -s1;
|
||||||
{
|
s2 = -s2;
|
||||||
|
}
|
||||||
|
if (s2 > 0.) {
|
||||||
radius = Math.sqrt(s1 < s2 ? r12 : r22);
|
radius = Math.sqrt(s1 < s2 ? r12 : r22);
|
||||||
if (radius > nogo.radius) continue;
|
if (radius > nogo.radius) continue;
|
||||||
}
|
}
|
||||||
if ( nogo.isNogo )
|
if (nogo.isNogo) {
|
||||||
{
|
|
||||||
if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle
|
if (!(nogo instanceof OsmNogoPolygon)) { // nogo is a circle
|
||||||
if (Double.isNaN(nogo.nogoWeight)) {
|
if (Double.isNaN(nogo.nogoWeight)) {
|
||||||
// default nogo behaviour (ignore completely)
|
// default nogo behaviour (ignore completely)
|
||||||
|
|
@ -436,9 +426,7 @@ public final class RoutingContext
|
||||||
// nogo weight, compute distance within the circle
|
// nogo weight, compute distance within the circle
|
||||||
nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight;
|
nogoCost = nogo.distanceWithinRadius(lon1, lat1, lon2, lat2, d) * nogo.nogoWeight;
|
||||||
}
|
}
|
||||||
}
|
} else if (((OsmNogoPolygon) nogo).intersects(lon1, lat1, lon2, lat2)) {
|
||||||
else if (((OsmNogoPolygon)nogo).intersects(lon1, lat1, lon2, lat2))
|
|
||||||
{
|
|
||||||
// nogo is a polyline/polygon, we have to check there is indeed
|
// nogo is a polyline/polygon, we have to check there is indeed
|
||||||
// an intersection in this case (radius check is not enough).
|
// an intersection in this case (radius check is not enough).
|
||||||
if (Double.isNaN(nogo.nogoWeight)) {
|
if (Double.isNaN(nogo.nogoWeight)) {
|
||||||
|
|
@ -454,28 +442,21 @@ public final class RoutingContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
shortestmatch = true;
|
shortestmatch = true;
|
||||||
nogo.radius = radius; // shortest distance to way
|
nogo.radius = radius; // shortest distance to way
|
||||||
// calculate remaining distance
|
// calculate remaining distance
|
||||||
if ( s2 < 0. )
|
if (s2 < 0.) {
|
||||||
{
|
|
||||||
wayfraction = -s2 / (d * d);
|
wayfraction = -s2 / (d * d);
|
||||||
double xm = x2 - wayfraction * dx;
|
double xm = x2 - wayfraction * dx;
|
||||||
double ym = y2 - wayfraction * dy;
|
double ym = y2 - wayfraction * dy;
|
||||||
ilonshortest = (int) (xm / dlon2m + nogo.ilon);
|
ilonshortest = (int) (xm / dlon2m + nogo.ilon);
|
||||||
ilatshortest = (int) (ym / dlat2m + nogo.ilat);
|
ilatshortest = (int) (ym / dlat2m + nogo.ilat);
|
||||||
}
|
} else if (s1 > s2) {
|
||||||
else if ( s1 > s2 )
|
|
||||||
{
|
|
||||||
wayfraction = 0.;
|
wayfraction = 0.;
|
||||||
ilonshortest = lon2;
|
ilonshortest = lon2;
|
||||||
ilatshortest = lat2;
|
ilatshortest = lat2;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
wayfraction = 1.;
|
wayfraction = 1.;
|
||||||
ilonshortest = lon1;
|
ilonshortest = lon1;
|
||||||
ilatshortest = lat1;
|
ilatshortest = lat1;
|
||||||
|
|
@ -485,14 +466,11 @@ public final class RoutingContext
|
||||||
// *after* the shortest distance point. In case of a shortest-match
|
// *after* the shortest distance point. In case of a shortest-match
|
||||||
// we use the reduced way segment for nogo-matching, in order not
|
// we use the reduced way segment for nogo-matching, in order not
|
||||||
// to cut our escape-way if we placed a nogo just in front of where we are
|
// to cut our escape-way if we placed a nogo just in front of where we are
|
||||||
if ( isEndpoint )
|
if (isEndpoint) {
|
||||||
{
|
|
||||||
wayfraction = 1. - wayfraction;
|
wayfraction = 1. - wayfraction;
|
||||||
lon2 = ilonshortest;
|
lon2 = ilonshortest;
|
||||||
lat2 = ilatshortest;
|
lat2 = ilatshortest;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
nogoCost = 0.;
|
nogoCost = 0.;
|
||||||
lon1 = ilonshortest;
|
lon1 = ilonshortest;
|
||||||
lat1 = ilatshortest;
|
lat1 = ilatshortest;
|
||||||
|
|
@ -504,30 +482,26 @@ public final class RoutingContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (int)(d + 1.0 );
|
return (int) Math.max(1.0, Math.round(d));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPathModel pm;
|
public OsmPathModel pm;
|
||||||
|
|
||||||
public OsmPrePath createPrePath( OsmPath origin, OsmLink link )
|
public OsmPrePath createPrePath(OsmPath origin, OsmLink link) {
|
||||||
{
|
|
||||||
OsmPrePath p = pm.createPrePath();
|
OsmPrePath p = pm.createPrePath();
|
||||||
if ( p != null )
|
if (p != null) {
|
||||||
{
|
|
||||||
p.init(origin, link, this);
|
p.init(origin, link, this);
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath( OsmLink link )
|
public OsmPath createPath(OsmLink link) {
|
||||||
{
|
|
||||||
OsmPath p = pm.createPath();
|
OsmPath p = pm.createPath();
|
||||||
p.init(link);
|
p.init(link);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode )
|
public OsmPath createPath(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode) {
|
||||||
{
|
|
||||||
OsmPath p = pm.createPath();
|
OsmPath p = pm.createPath();
|
||||||
p.init(origin, link, refTrack, detailMode, this);
|
p.init(origin, link, refTrack, detailMode, this);
|
||||||
return p;
|
return p;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -9,39 +9,31 @@ import java.io.File;
|
||||||
|
|
||||||
import btools.mapaccess.StorageConfigHelper;
|
import btools.mapaccess.StorageConfigHelper;
|
||||||
|
|
||||||
public final class RoutingHelper
|
public final class RoutingHelper {
|
||||||
{
|
public static File getAdditionalMaptoolDir(File segmentDir) {
|
||||||
public static File getAdditionalMaptoolDir( File segmentDir )
|
|
||||||
{
|
|
||||||
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
|
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getSecondarySegmentDir( File segmentDir )
|
public static File getSecondarySegmentDir(File segmentDir) {
|
||||||
{
|
|
||||||
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static boolean hasDirectoryAnyDatafiles( File segmentDir )
|
public static boolean hasDirectoryAnyDatafiles(File segmentDir) {
|
||||||
{
|
if (hasAnyDatafiles(segmentDir)) {
|
||||||
if ( hasAnyDatafiles( segmentDir ) )
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// check secondary, too
|
// check secondary, too
|
||||||
File secondary = StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
File secondary = StorageConfigHelper.getSecondarySegmentDir(segmentDir);
|
||||||
if ( secondary != null )
|
if (secondary != null) {
|
||||||
{
|
|
||||||
return hasAnyDatafiles(secondary);
|
return hasAnyDatafiles(secondary);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasAnyDatafiles( File dir )
|
private static boolean hasAnyDatafiles(File dir) {
|
||||||
{
|
|
||||||
String[] fileNames = dir.list();
|
String[] fileNames = dir.list();
|
||||||
for( String fileName : fileNames )
|
for (String fileName : fileNames) {
|
||||||
{
|
|
||||||
if (fileName.endsWith(".rd5")) return true;
|
if (fileName.endsWith(".rd5")) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
public class RoutingIslandException extends RuntimeException
|
public class RoutingIslandException extends RuntimeException {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,356 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
public class RoutingParamCollector {
|
||||||
|
|
||||||
|
final static boolean DEBUG = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a list of points and optional extra info for the points
|
||||||
|
*
|
||||||
|
* @param lonLats linked list separated by ';' or '|'
|
||||||
|
* @return a list
|
||||||
|
*/
|
||||||
|
public List<OsmNodeNamed> getWayPointList(String lonLats) {
|
||||||
|
if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set");
|
||||||
|
|
||||||
|
String[] coords = lonLats.split(";|\\|"); // use both variantes
|
||||||
|
if (coords.length < 1 || !coords[0].contains(","))
|
||||||
|
throw new IllegalArgumentException("we need one lat/lon point at least!");
|
||||||
|
|
||||||
|
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||||
|
for (int i = 0; i < coords.length; i++) {
|
||||||
|
String[] lonLat = coords[i].split(",");
|
||||||
|
if (lonLat.length < 1)
|
||||||
|
throw new IllegalArgumentException("we need one lat/lon point at least!");
|
||||||
|
wplist.add(readPosition(lonLat[0], lonLat[1], "via" + i));
|
||||||
|
if (lonLat.length > 2) {
|
||||||
|
if (lonLat[2].equals("d")) {
|
||||||
|
wplist.get(wplist.size() - 1).direct = true;
|
||||||
|
} else {
|
||||||
|
wplist.get(wplist.size() - 1).name = lonLat[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wplist.get(0).name.startsWith("via")) wplist.get(0).name = "from";
|
||||||
|
if (wplist.get(wplist.size() - 1).name.startsWith("via")) {
|
||||||
|
wplist.get(wplist.size() - 1).name = "to";
|
||||||
|
}
|
||||||
|
|
||||||
|
return wplist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a list of points (old style, positions only)
|
||||||
|
*
|
||||||
|
* @param lons array with longitudes
|
||||||
|
* @param lats array with latitudes
|
||||||
|
* @return a list
|
||||||
|
*/
|
||||||
|
public List<OsmNodeNamed> readPositions(double[] lons, double[] lats) {
|
||||||
|
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||||
|
|
||||||
|
if (lats == null || lats.length < 2 || lons == null || lons.length < 2) {
|
||||||
|
return wplist;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < lats.length && i < lons.length; i++) {
|
||||||
|
OsmNodeNamed n = new OsmNodeNamed();
|
||||||
|
n.name = "via" + i;
|
||||||
|
n.ilon = (int) ((lons[i] + 180.) * 1000000. + 0.5);
|
||||||
|
n.ilat = (int) ((lats[i] + 90.) * 1000000. + 0.5);
|
||||||
|
wplist.add(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wplist.get(0).name.startsWith("via")) wplist.get(0).name = "from";
|
||||||
|
if (wplist.get(wplist.size() - 1).name.startsWith("via")) {
|
||||||
|
wplist.get(wplist.size() - 1).name = "to";
|
||||||
|
}
|
||||||
|
|
||||||
|
return wplist;
|
||||||
|
}
|
||||||
|
|
||||||
|
private OsmNodeNamed readPosition(String vlon, String vlat, String name) {
|
||||||
|
if (vlon == null) throw new IllegalArgumentException("lon " + name + " not found in input");
|
||||||
|
if (vlat == null) throw new IllegalArgumentException("lat " + name + " not found in input");
|
||||||
|
|
||||||
|
return readPosition(Double.parseDouble(vlon), Double.parseDouble(vlat), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OsmNodeNamed readPosition(double lon, double lat, String name) {
|
||||||
|
OsmNodeNamed n = new OsmNodeNamed();
|
||||||
|
n.name = name;
|
||||||
|
n.ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||||
|
n.ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* read a url like parameter list linked with '&'
|
||||||
|
*
|
||||||
|
* @param url parameter list
|
||||||
|
* @return a hashmap of the parameter
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
public Map<String, String> getUrlParams(String url) throws UnsupportedEncodingException {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
String decoded = URLDecoder.decode(url, "UTF-8");
|
||||||
|
StringTokenizer tk = new StringTokenizer(decoded, "?&");
|
||||||
|
while (tk.hasMoreTokens()) {
|
||||||
|
String t = tk.nextToken();
|
||||||
|
StringTokenizer tk2 = new StringTokenizer(t, "=");
|
||||||
|
if (tk2.hasMoreTokens()) {
|
||||||
|
String key = tk2.nextToken();
|
||||||
|
if (tk2.hasMoreTokens()) {
|
||||||
|
String value = tk2.nextToken();
|
||||||
|
params.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fill a parameter map into the routing context
|
||||||
|
*
|
||||||
|
* @param rctx the context
|
||||||
|
* @param wplist the list of way points needed for 'straight' parameter
|
||||||
|
* @param params the list of parameters
|
||||||
|
*/
|
||||||
|
public void setParams(RoutingContext rctx, List<OsmNodeNamed> wplist, Map<String, String> params) {
|
||||||
|
if (params != null) {
|
||||||
|
if (params.size() == 0) return;
|
||||||
|
|
||||||
|
// prepare nogos extra
|
||||||
|
if (params.containsKey("profile")) {
|
||||||
|
rctx.localFunction = params.get("profile");
|
||||||
|
}
|
||||||
|
if (params.containsKey("nogoLats") && params.get("nogoLats").length() > 0) {
|
||||||
|
List<OsmNodeNamed> nogoList = readNogos(params.get("nogoLons"), params.get("nogoLats"), params.get("nogoRadi"));
|
||||||
|
if (nogoList != null) {
|
||||||
|
RoutingContext.prepareNogoPoints(nogoList);
|
||||||
|
if (rctx.nogopoints == null) {
|
||||||
|
rctx.nogopoints = nogoList;
|
||||||
|
} else {
|
||||||
|
rctx.nogopoints.addAll(nogoList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params.remove("nogoLats");
|
||||||
|
params.remove("nogoLons");
|
||||||
|
params.remove("nogoRadi");
|
||||||
|
}
|
||||||
|
if (params.containsKey("nogos")) {
|
||||||
|
List<OsmNodeNamed> nogoList = readNogoList(params.get("nogos"));
|
||||||
|
if (nogoList != null) {
|
||||||
|
RoutingContext.prepareNogoPoints(nogoList);
|
||||||
|
if (rctx.nogopoints == null) {
|
||||||
|
rctx.nogopoints = nogoList;
|
||||||
|
} else {
|
||||||
|
rctx.nogopoints.addAll(nogoList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
params.remove("nogos");
|
||||||
|
}
|
||||||
|
if (params.containsKey("polylines")) {
|
||||||
|
List<OsmNodeNamed> result = new ArrayList<>();
|
||||||
|
parseNogoPolygons(params.get("polylines"), result, false);
|
||||||
|
if (rctx.nogopoints == null) {
|
||||||
|
rctx.nogopoints = result;
|
||||||
|
} else {
|
||||||
|
rctx.nogopoints.addAll(result);
|
||||||
|
}
|
||||||
|
params.remove("polylines");
|
||||||
|
}
|
||||||
|
if (params.containsKey("polygons")) {
|
||||||
|
List<OsmNodeNamed> result = new ArrayList<>();
|
||||||
|
parseNogoPolygons(params.get("polygons"), result, true);
|
||||||
|
if (rctx.nogopoints == null) {
|
||||||
|
rctx.nogopoints = result;
|
||||||
|
} else {
|
||||||
|
rctx.nogopoints.addAll(result);
|
||||||
|
}
|
||||||
|
params.remove("polygons");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> e : params.entrySet()) {
|
||||||
|
String key = e.getKey();
|
||||||
|
String value = e.getValue();
|
||||||
|
if (DEBUG) System.out.println("params " + key + " " + value);
|
||||||
|
|
||||||
|
if (key.equals("straight")) {
|
||||||
|
try {
|
||||||
|
String[] sa = value.split(",");
|
||||||
|
for (int i = 0; i < sa.length; i++) {
|
||||||
|
int v = Integer.parseInt(sa[i]);
|
||||||
|
if (wplist.size() > v) wplist.get(v).direct = true;
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println("error " + ex.getStackTrace()[0].getLineNumber() + " " + ex.getStackTrace()[0] + "\n" + ex);
|
||||||
|
}
|
||||||
|
} else if (key.equals("pois")) {
|
||||||
|
rctx.poipoints = readPoisList(value);
|
||||||
|
} else if (key.equals("heading")) {
|
||||||
|
rctx.startDirection = Integer.valueOf(value);
|
||||||
|
rctx.forceUseStartDirection = true;
|
||||||
|
} else if (key.equals("direction")) {
|
||||||
|
rctx.startDirection = Integer.valueOf(value);
|
||||||
|
} else if (key.equals("alternativeidx")) {
|
||||||
|
rctx.setAlternativeIdx(Integer.parseInt(value));
|
||||||
|
} else if (key.equals("turnInstructionMode")) {
|
||||||
|
rctx.turnInstructionMode = Integer.parseInt(value);
|
||||||
|
} else if (key.equals("timode")) {
|
||||||
|
rctx.turnInstructionMode = Integer.parseInt(value);
|
||||||
|
} else if (key.equals("turnInstructionFormat")) {
|
||||||
|
if ("osmand".equalsIgnoreCase(value)) {
|
||||||
|
rctx.turnInstructionMode = 3;
|
||||||
|
} else if ("locus".equalsIgnoreCase(value)) {
|
||||||
|
rctx.turnInstructionMode = 2;
|
||||||
|
}
|
||||||
|
} else if (key.equals("exportWaypoints")) {
|
||||||
|
rctx.exportWaypoints = (Integer.parseInt(value) == 1);
|
||||||
|
} else if (key.equals("format")) {
|
||||||
|
rctx.outputFormat = ((String) value).toLowerCase();
|
||||||
|
} else if (key.equals("trackFormat")) {
|
||||||
|
rctx.outputFormat = ((String) value).toLowerCase();
|
||||||
|
} else if (key.startsWith("profile:")) {
|
||||||
|
if (rctx.keyValues == null) rctx.keyValues = new HashMap<>();
|
||||||
|
rctx.keyValues.put(key.substring(8), value);
|
||||||
|
}
|
||||||
|
// ignore other params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fill profile parameter list
|
||||||
|
*
|
||||||
|
* @param rctx the routing context
|
||||||
|
* @param params the list of parameters
|
||||||
|
*/
|
||||||
|
public void setProfileParams(RoutingContext rctx, Map<String, String> params) {
|
||||||
|
if (params != null) {
|
||||||
|
if (params.size() == 0) return;
|
||||||
|
if (rctx.keyValues == null) rctx.keyValues = new HashMap<>();
|
||||||
|
for (Map.Entry<String, String> e : params.entrySet()) {
|
||||||
|
String key = e.getKey();
|
||||||
|
String value = e.getValue();
|
||||||
|
if (DEBUG) System.out.println("params " + key + " " + value);
|
||||||
|
rctx.keyValues.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseNogoPolygons(String polygons, List<OsmNodeNamed> result, boolean closed) {
|
||||||
|
if (polygons != null) {
|
||||||
|
String[] polygonList = polygons.split("\\|");
|
||||||
|
for (int i = 0; i < polygonList.length; i++) {
|
||||||
|
String[] lonLatList = polygonList[i].split(",");
|
||||||
|
if (lonLatList.length > 1) {
|
||||||
|
OsmNogoPolygon polygon = new OsmNogoPolygon(closed);
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < 2 * (lonLatList.length / 2) - 1; ) {
|
||||||
|
String slon = lonLatList[j++];
|
||||||
|
String slat = lonLatList[j++];
|
||||||
|
int lon = (int) ((Double.parseDouble(slon) + 180.) * 1000000. + 0.5);
|
||||||
|
int lat = (int) ((Double.parseDouble(slat) + 90.) * 1000000. + 0.5);
|
||||||
|
polygon.addVertex(lon, lat);
|
||||||
|
}
|
||||||
|
|
||||||
|
String nogoWeight = "NaN";
|
||||||
|
if (j < lonLatList.length) {
|
||||||
|
nogoWeight = lonLatList[j];
|
||||||
|
}
|
||||||
|
polygon.nogoWeight = Double.parseDouble(nogoWeight);
|
||||||
|
if (polygon.points.size() > 0) {
|
||||||
|
polygon.calcBoundingCircle();
|
||||||
|
result.add(polygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OsmNodeNamed> readPoisList(String pois) {
|
||||||
|
// lon,lat,name|...
|
||||||
|
if (pois == null) return null;
|
||||||
|
|
||||||
|
String[] lonLatNameList = pois.split("\\|");
|
||||||
|
|
||||||
|
List<OsmNodeNamed> poisList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < lonLatNameList.length; i++) {
|
||||||
|
String[] lonLatName = lonLatNameList[i].split(",");
|
||||||
|
|
||||||
|
if (lonLatName.length != 3)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
OsmNodeNamed n = new OsmNodeNamed();
|
||||||
|
n.ilon = (int) ((Double.parseDouble(lonLatName[0]) + 180.) * 1000000. + 0.5);
|
||||||
|
n.ilat = (int) ((Double.parseDouble(lonLatName[1]) + 90.) * 1000000. + 0.5);
|
||||||
|
n.name = lonLatName[2];
|
||||||
|
poisList.add(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return poisList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OsmNodeNamed> readNogoList(String nogos) {
|
||||||
|
// lon,lat,radius[,weight]|...
|
||||||
|
|
||||||
|
if (nogos == null) return null;
|
||||||
|
|
||||||
|
String[] lonLatRadList = nogos.split("\\|");
|
||||||
|
|
||||||
|
List<OsmNodeNamed> nogoList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < lonLatRadList.length; i++) {
|
||||||
|
String[] lonLatRad = lonLatRadList[i].split(",");
|
||||||
|
String nogoWeight = "NaN";
|
||||||
|
if (lonLatRad.length > 3) {
|
||||||
|
nogoWeight = lonLatRad[3];
|
||||||
|
}
|
||||||
|
nogoList.add(readNogo(lonLatRad[0], lonLatRad[1], lonLatRad[2], nogoWeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nogoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OsmNodeNamed> readNogos(String nogoLons, String nogoLats, String nogoRadi) {
|
||||||
|
if (nogoLons == null || nogoLats == null || nogoRadi == null) return null;
|
||||||
|
List<OsmNodeNamed> nogoList = new ArrayList<>();
|
||||||
|
|
||||||
|
String[] lons = nogoLons.split(",");
|
||||||
|
String[] lats = nogoLats.split(",");
|
||||||
|
String[] radi = nogoRadi.split(",");
|
||||||
|
String nogoWeight = "undefined";
|
||||||
|
for (int i = 0; i < lons.length && i < lats.length && i < radi.length; i++) {
|
||||||
|
OsmNodeNamed n = readNogo(lons[i].trim(), lats[i].trim(), radi[i].trim(), nogoWeight);
|
||||||
|
nogoList.add(n);
|
||||||
|
}
|
||||||
|
return nogoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private OsmNodeNamed readNogo(String lon, String lat, String radius, String nogoWeight) {
|
||||||
|
double weight = "undefined".equals(nogoWeight) ? Double.NaN : Double.parseDouble(nogoWeight);
|
||||||
|
return readNogo(Double.parseDouble(lon), Double.parseDouble(lat), (int) Double.parseDouble(radius), weight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OsmNodeNamed readNogo(double lon, double lat, int radius, double nogoWeight) {
|
||||||
|
OsmNodeNamed n = new OsmNodeNamed();
|
||||||
|
n.name = "nogo" + radius;
|
||||||
|
n.ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||||
|
n.ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||||
|
n.isNogo = true;
|
||||||
|
n.nogoWeight = nogoWeight;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -8,8 +8,7 @@ package btools.router;
|
||||||
import btools.mapaccess.OsmNode;
|
import btools.mapaccess.OsmNode;
|
||||||
|
|
||||||
|
|
||||||
public final class SearchBoundary
|
public final class SearchBoundary {
|
||||||
{
|
|
||||||
|
|
||||||
private int minlon0;
|
private int minlon0;
|
||||||
private int minlat0;
|
private int minlat0;
|
||||||
|
|
@ -28,8 +27,7 @@ public final class SearchBoundary
|
||||||
/**
|
/**
|
||||||
* @param radius Search radius in meters.
|
* @param radius Search radius in meters.
|
||||||
*/
|
*/
|
||||||
public SearchBoundary( OsmNode n, int radius, int direction )
|
public SearchBoundary(OsmNode n, int radius, int direction) {
|
||||||
{
|
|
||||||
this.radius = radius;
|
this.radius = radius;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
|
|
||||||
|
|
@ -49,8 +47,7 @@ public final class SearchBoundary
|
||||||
maxlat = lat + 6000000;
|
maxlat = lat + 6000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getFileName( OsmNode n )
|
public static String getFileName(OsmNode n) {
|
||||||
{
|
|
||||||
int lon = (n.ilon / 5000000) * 5000000;
|
int lon = (n.ilon / 5000000) * 5000000;
|
||||||
int lat = (n.ilat / 5000000) * 5000000;
|
int lat = (n.ilat / 5000000) * 5000000;
|
||||||
|
|
||||||
|
|
@ -62,28 +59,28 @@ public final class SearchBoundary
|
||||||
return slon + "_" + slat + ".trf";
|
return slon + "_" + slat + ".trf";
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInBoundary( OsmNode n, int cost )
|
public boolean isInBoundary(OsmNode n, int cost) {
|
||||||
{
|
if (radius > 0) {
|
||||||
if ( radius > 0 )
|
|
||||||
{
|
|
||||||
return n.calcDistance(p) < radius;
|
return n.calcDistance(p) < radius;
|
||||||
}
|
}
|
||||||
if ( cost == 0 )
|
if (cost == 0) {
|
||||||
{
|
|
||||||
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
|
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
|
||||||
}
|
}
|
||||||
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
|
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBoundaryDistance( OsmNode n )
|
public int getBoundaryDistance(OsmNode n) {
|
||||||
{
|
switch (direction) {
|
||||||
switch( direction )
|
case 0:
|
||||||
{
|
return n.calcDistance(new OsmNode(n.ilon, minlat));
|
||||||
case 0: return n.calcDistance( new OsmNode( n.ilon, minlat ) );
|
case 1:
|
||||||
case 1: return n.calcDistance( new OsmNode( minlon, n.ilat ) );
|
return n.calcDistance(new OsmNode(minlon, n.ilat));
|
||||||
case 2: return n.calcDistance( new OsmNode( n.ilon, maxlat ) );
|
case 2:
|
||||||
case 3: return n.calcDistance( new OsmNode( maxlon, n.ilat ) );
|
return n.calcDistance(new OsmNode(n.ilon, maxlat));
|
||||||
default: throw new IllegalArgumentException( "undefined direction: "+ direction );
|
case 3:
|
||||||
|
return n.calcDistance(new OsmNode(maxlon, n.ilat));
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("undefined direction: " + direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,12 @@ import btools.expressions.BExpressionContextNode;
|
||||||
import btools.expressions.BExpressionContextWay;
|
import btools.expressions.BExpressionContextWay;
|
||||||
|
|
||||||
|
|
||||||
final class StdModel extends OsmPathModel
|
final class StdModel extends OsmPathModel {
|
||||||
{
|
public OsmPrePath createPrePath() {
|
||||||
public OsmPrePath createPrePath()
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OsmPath createPath()
|
public OsmPath createPath() {
|
||||||
{
|
|
||||||
return new StdPath();
|
return new StdPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,8 +26,7 @@ final class StdModel extends OsmPathModel
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues )
|
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues) {
|
||||||
{
|
|
||||||
ctxWay = expctxWay;
|
ctxWay = expctxWay;
|
||||||
ctxNode = expctxNode;
|
ctxNode = expctxNode;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
import btools.util.FastMath;
|
final class StdPath extends OsmPath {
|
||||||
|
|
||||||
final class StdPath extends OsmPath
|
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* The elevation-hysteresis-buffer (0-10 m)
|
* The elevation-hysteresis-buffer (0-10 m)
|
||||||
*/
|
*/
|
||||||
|
|
@ -19,12 +16,14 @@ final class StdPath extends OsmPath
|
||||||
private float totalEnergy; // total route energy (Joule)
|
private float totalEnergy; // total route energy (Joule)
|
||||||
private float elevation_buffer; // just another elevation buffer (for travel time)
|
private float elevation_buffer; // just another elevation buffer (for travel time)
|
||||||
|
|
||||||
|
private int uphillcostdiv;
|
||||||
|
private int downhillcostdiv;
|
||||||
|
|
||||||
// Gravitational constant, g
|
// Gravitational constant, g
|
||||||
private static final double GRAVITY = 9.81; // in meters per second^(-2)
|
private static final double GRAVITY = 9.81; // in meters per second^(-2)
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init( OsmPath orig )
|
public void init(OsmPath orig) {
|
||||||
{
|
|
||||||
StdPath origin = (StdPath) orig;
|
StdPath origin = (StdPath) orig;
|
||||||
this.ehbd = origin.ehbd;
|
this.ehbd = origin.ehbd;
|
||||||
this.ehbu = origin.ehbu;
|
this.ehbu = origin.ehbu;
|
||||||
|
|
@ -34,32 +33,61 @@ final class StdPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetState()
|
protected void resetState() {
|
||||||
{
|
|
||||||
ehbd = 0;
|
ehbd = 0;
|
||||||
ehbu = 0;
|
ehbu = 0;
|
||||||
totalTime = 0.f;
|
totalTime = 0.f;
|
||||||
totalEnergy = 0.f;
|
totalEnergy = 0.f;
|
||||||
|
uphillcostdiv = 0;
|
||||||
|
downhillcostdiv = 0;
|
||||||
elevation_buffer = 0.f;
|
elevation_buffer = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processWaySection( RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
|
protected double processWaySection(RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
|
||||||
{
|
|
||||||
// calculate the costfactor inputs
|
// calculate the costfactor inputs
|
||||||
float turncostbase = rc.expctxWay.getTurncost();
|
float turncostbase = rc.expctxWay.getTurncost();
|
||||||
|
float uphillcutoff = rc.expctxWay.getUphillcutoff() * 10000;
|
||||||
|
float downhillcutoff = rc.expctxWay.getDownhillcutoff() * 10000;
|
||||||
|
float uphillmaxslope = rc.expctxWay.getUphillmaxslope() * 10000;
|
||||||
|
float downhillmaxslope = rc.expctxWay.getDownhillmaxslope() * 10000;
|
||||||
float cfup = rc.expctxWay.getUphillCostfactor();
|
float cfup = rc.expctxWay.getUphillCostfactor();
|
||||||
float cfdown = rc.expctxWay.getDownhillCostfactor();
|
float cfdown = rc.expctxWay.getDownhillCostfactor();
|
||||||
float cf = rc.expctxWay.getCostfactor();
|
float cf = rc.expctxWay.getCostfactor();
|
||||||
cfup = cfup == 0.f ? cf : cfup;
|
cfup = cfup == 0.f ? cf : cfup;
|
||||||
cfdown = cfdown == 0.f ? cf : cfdown;
|
cfdown = cfdown == 0.f ? cf : cfdown;
|
||||||
|
|
||||||
|
downhillcostdiv = (int) rc.expctxWay.getDownhillcost();
|
||||||
|
if (downhillcostdiv > 0) {
|
||||||
|
downhillcostdiv = 1000000 / downhillcostdiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int downhillmaxslopecostdiv = (int) rc.expctxWay.getDownhillmaxslopecost();
|
||||||
|
if (downhillmaxslopecostdiv > 0) {
|
||||||
|
downhillmaxslopecostdiv = 1000000 / downhillmaxslopecostdiv;
|
||||||
|
} else {
|
||||||
|
// if not given, use legacy behavior
|
||||||
|
downhillmaxslopecostdiv = downhillcostdiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
uphillcostdiv = (int) rc.expctxWay.getUphillcost();
|
||||||
|
if (uphillcostdiv > 0) {
|
||||||
|
uphillcostdiv = 1000000 / uphillcostdiv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uphillmaxslopecostdiv = (int) rc.expctxWay.getUphillmaxslopecost();
|
||||||
|
if (uphillmaxslopecostdiv > 0) {
|
||||||
|
uphillmaxslopecostdiv = 1000000 / uphillmaxslopecostdiv;
|
||||||
|
} else {
|
||||||
|
// if not given, use legacy behavior
|
||||||
|
uphillmaxslopecostdiv = uphillcostdiv;
|
||||||
|
}
|
||||||
|
|
||||||
int dist = (int) distance; // legacy arithmetics needs int
|
int dist = (int) distance; // legacy arithmetics needs int
|
||||||
|
|
||||||
// penalty for turning angle
|
// penalty for turning angle
|
||||||
int turncost = (int) ((1. - cosangle) * turncostbase + 0.2); // e.g. turncost=90 -> 90 degree = 90m penalty
|
int turncost = (int) ((1. - cosangle) * turncostbase + 0.2); // e.g. turncost=90 -> 90 degree = 90m penalty
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.linkturncost += turncost;
|
message.linkturncost += turncost;
|
||||||
message.turnangle = (float) angle;
|
message.turnangle = (float) angle;
|
||||||
}
|
}
|
||||||
|
|
@ -71,80 +99,77 @@ final class StdPath extends OsmPath
|
||||||
// leads to an immediate penalty
|
// leads to an immediate penalty
|
||||||
|
|
||||||
int delta_h_micros = (int) (1000000. * delta_h);
|
int delta_h_micros = (int) (1000000. * delta_h);
|
||||||
ehbd += -delta_h_micros - dist * rc.downhillcutoff;
|
ehbd += -delta_h_micros - dist * downhillcutoff;
|
||||||
ehbu += delta_h_micros - dist * rc.uphillcutoff;
|
ehbu += delta_h_micros - dist * uphillcutoff;
|
||||||
|
|
||||||
float downweight = 0.f;
|
float downweight = 0.f;
|
||||||
if ( ehbd > rc.elevationpenaltybuffer )
|
if (ehbd > rc.elevationpenaltybuffer) {
|
||||||
{
|
|
||||||
downweight = 1.f;
|
downweight = 1.f;
|
||||||
|
|
||||||
int excess = ehbd - rc.elevationpenaltybuffer;
|
int excess = ehbd - rc.elevationpenaltybuffer;
|
||||||
int reduce = dist * rc.elevationbufferreduce;
|
int reduce = dist * rc.elevationbufferreduce;
|
||||||
if ( reduce > excess )
|
if (reduce > excess) {
|
||||||
{
|
|
||||||
downweight = ((float) excess) / reduce;
|
downweight = ((float) excess) / reduce;
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
excess = ehbd - rc.elevationmaxbuffer;
|
excess = ehbd - rc.elevationmaxbuffer;
|
||||||
if ( reduce < excess )
|
if (reduce < excess) {
|
||||||
{
|
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
ehbd -= reduce;
|
ehbd -= reduce;
|
||||||
if ( rc.downhillcostdiv > 0 )
|
float elevationCost = 0.f;
|
||||||
{
|
if (downhillcostdiv > 0) {
|
||||||
int elevationCost = reduce/rc.downhillcostdiv;
|
elevationCost += Math.min(reduce, dist * downhillmaxslope) / downhillcostdiv;
|
||||||
|
}
|
||||||
|
if (downhillmaxslopecostdiv > 0) {
|
||||||
|
elevationCost += Math.max(0, reduce - dist * downhillmaxslope) / downhillmaxslopecostdiv;
|
||||||
|
}
|
||||||
|
if (elevationCost > 0) {
|
||||||
sectionCost += elevationCost;
|
sectionCost += elevationCost;
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.linkelevationcost += elevationCost;
|
message.linkelevationcost += elevationCost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (ehbd < 0) {
|
||||||
else if ( ehbd < 0 )
|
|
||||||
{
|
|
||||||
ehbd = 0;
|
ehbd = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float upweight = 0.f;
|
float upweight = 0.f;
|
||||||
if ( ehbu > rc.elevationpenaltybuffer )
|
if (ehbu > rc.elevationpenaltybuffer) {
|
||||||
{
|
|
||||||
upweight = 1.f;
|
upweight = 1.f;
|
||||||
|
|
||||||
int excess = ehbu - rc.elevationpenaltybuffer;
|
int excess = ehbu - rc.elevationpenaltybuffer;
|
||||||
int reduce = dist * rc.elevationbufferreduce;
|
int reduce = dist * rc.elevationbufferreduce;
|
||||||
if ( reduce > excess )
|
if (reduce > excess) {
|
||||||
{
|
|
||||||
upweight = ((float) excess) / reduce;
|
upweight = ((float) excess) / reduce;
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
excess = ehbu - rc.elevationmaxbuffer;
|
excess = ehbu - rc.elevationmaxbuffer;
|
||||||
if ( reduce < excess )
|
if (reduce < excess) {
|
||||||
{
|
|
||||||
reduce = excess;
|
reduce = excess;
|
||||||
}
|
}
|
||||||
ehbu -= reduce;
|
ehbu -= reduce;
|
||||||
if ( rc.uphillcostdiv > 0 )
|
float elevationCost = 0.f;
|
||||||
{
|
if (uphillcostdiv > 0) {
|
||||||
int elevationCost = reduce/rc.uphillcostdiv;
|
elevationCost += Math.min(reduce, dist * uphillmaxslope) / uphillcostdiv;
|
||||||
|
}
|
||||||
|
if (uphillmaxslopecostdiv > 0) {
|
||||||
|
elevationCost += Math.max(0, reduce - dist * uphillmaxslope) / uphillmaxslopecostdiv;
|
||||||
|
}
|
||||||
|
if (elevationCost > 0) {
|
||||||
sectionCost += elevationCost;
|
sectionCost += elevationCost;
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.linkelevationcost += elevationCost;
|
message.linkelevationcost += elevationCost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (ehbu < 0) {
|
||||||
else if ( ehbu < 0 )
|
|
||||||
{
|
|
||||||
ehbu = 0;
|
ehbu = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the effective costfactor (slope dependent)
|
// get the effective costfactor (slope dependent)
|
||||||
float costfactor = cfup * upweight + cf * (1.f - upweight - downweight) + cfdown * downweight;
|
float costfactor = cfup * upweight + cf * (1.f - upweight - downweight) + cfdown * downweight;
|
||||||
|
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.costfactor = costfactor;
|
message.costfactor = costfactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -154,20 +179,16 @@ final class StdPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected double processTargetNode( RoutingContext rc )
|
protected double processTargetNode(RoutingContext rc) {
|
||||||
{
|
|
||||||
// finally add node-costs for target node
|
// finally add node-costs for target node
|
||||||
if ( targetNode.nodeDescription != null )
|
if (targetNode.nodeDescription != null) {
|
||||||
{
|
|
||||||
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
|
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
|
||||||
rc.expctxNode.evaluate(nodeAccessGranted, targetNode.nodeDescription);
|
rc.expctxNode.evaluate(nodeAccessGranted, targetNode.nodeDescription);
|
||||||
float initialcost = rc.expctxNode.getInitialcost();
|
float initialcost = rc.expctxNode.getInitialcost();
|
||||||
if ( initialcost >= 1000000. )
|
if (initialcost >= 1000000.) {
|
||||||
{
|
|
||||||
return -1.;
|
return -1.;
|
||||||
}
|
}
|
||||||
if ( message != null )
|
if (message != null) {
|
||||||
{
|
|
||||||
message.linknodecost += (int) initialcost;
|
message.linknodecost += (int) initialcost;
|
||||||
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(nodeAccessGranted, targetNode.nodeDescription);
|
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(nodeAccessGranted, targetNode.nodeDescription);
|
||||||
}
|
}
|
||||||
|
|
@ -177,49 +198,37 @@ final class StdPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int elevationCorrection( RoutingContext rc )
|
public int elevationCorrection() {
|
||||||
{
|
return (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0)
|
||||||
return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
|
+ (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
|
||||||
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
|
public boolean definitlyWorseThan(OsmPath path) {
|
||||||
{
|
|
||||||
StdPath p = (StdPath) path;
|
StdPath p = (StdPath) path;
|
||||||
|
|
||||||
int c = p.cost;
|
int c = p.cost;
|
||||||
if ( rc.downhillcostdiv > 0 )
|
if (p.downhillcostdiv > 0) {
|
||||||
{
|
int delta = p.ehbd / p.downhillcostdiv - (downhillcostdiv > 0 ? ehbd / downhillcostdiv : 0);
|
||||||
int delta = p.ehbd - ehbd;
|
if (delta > 0) c += delta;
|
||||||
if ( delta > 0 ) c += delta/rc.downhillcostdiv;
|
|
||||||
}
|
}
|
||||||
if ( rc.uphillcostdiv > 0 )
|
if (p.uphillcostdiv > 0) {
|
||||||
{
|
int delta = p.ehbu / p.uphillcostdiv - (uphillcostdiv > 0 ? ehbu / uphillcostdiv : 0);
|
||||||
int delta = p.ehbu - ehbu;
|
if (delta > 0) c += delta;
|
||||||
if ( delta > 0 ) c += delta/rc.uphillcostdiv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cost > c;
|
return cost > c;
|
||||||
}
|
}
|
||||||
|
|
||||||
private double calcIncline( double dist )
|
private double calcIncline(double dist) {
|
||||||
{
|
|
||||||
double min_delta = 3.;
|
double min_delta = 3.;
|
||||||
double shift;
|
double shift = 0.;
|
||||||
if ( elevation_buffer > min_delta )
|
if (elevation_buffer > min_delta) {
|
||||||
{
|
|
||||||
shift = -min_delta;
|
shift = -min_delta;
|
||||||
|
} else if (elevation_buffer < -min_delta) {
|
||||||
|
shift = min_delta;
|
||||||
}
|
}
|
||||||
else if ( elevation_buffer < min_delta )
|
double decayFactor = Math.exp(-dist / 100.);
|
||||||
{
|
|
||||||
shift = -min_delta;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0.;
|
|
||||||
}
|
|
||||||
double decayFactor = FastMath.exp( - dist / 100. );
|
|
||||||
float new_elevation_buffer = (float) ((elevation_buffer + shift) * decayFactor - shift);
|
float new_elevation_buffer = (float) ((elevation_buffer + shift) * decayFactor - shift);
|
||||||
double incline = (elevation_buffer - new_elevation_buffer) / dist;
|
double incline = (elevation_buffer - new_elevation_buffer) / dist;
|
||||||
elevation_buffer = new_elevation_buffer;
|
elevation_buffer = new_elevation_buffer;
|
||||||
|
|
@ -227,10 +236,8 @@ final class StdPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
|
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
|
||||||
{
|
if (!detailMode) {
|
||||||
if ( !detailMode )
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,57 +245,41 @@ final class StdPath extends OsmPath
|
||||||
elevation_buffer += delta_h;
|
elevation_buffer += delta_h;
|
||||||
double incline = calcIncline(dist);
|
double incline = calcIncline(dist);
|
||||||
|
|
||||||
double wayMaxspeed;
|
double maxSpeed = rc.maxSpeed;
|
||||||
|
double speedLimit = rc.expctxWay.getMaxspeed() / 3.6f;
|
||||||
wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6f;
|
if (speedLimit > 0) {
|
||||||
if (wayMaxspeed == 0)
|
maxSpeed = Math.min(maxSpeed, speedLimit);
|
||||||
{
|
|
||||||
wayMaxspeed = rc.maxSpeed;
|
|
||||||
}
|
}
|
||||||
wayMaxspeed = Math.min(wayMaxspeed,rc.maxSpeed);
|
|
||||||
|
|
||||||
double speed; // Travel speed
|
double speed = maxSpeed; // Travel speed
|
||||||
double f_roll = rc.totalMass * GRAVITY * (rc.defaultC_r + incline);
|
double f_roll = rc.totalMass * GRAVITY * (rc.defaultC_r + incline);
|
||||||
if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9 )
|
if (rc.footMode) {
|
||||||
{
|
|
||||||
// Use Tobler's hiking function for walking sections
|
// Use Tobler's hiking function for walking sections
|
||||||
speed = rc.maxSpeed * 3.6;
|
speed = rc.maxSpeed * Math.exp(-3.5 * Math.abs(incline + 0.05));
|
||||||
speed = (speed * FastMath.exp(-3.5 * Math.abs( incline + 0.05))) / 3.6;
|
} else if (rc.bikeMode) {
|
||||||
}
|
|
||||||
else if (rc.bikeMode)
|
|
||||||
{
|
|
||||||
speed = solveCubic(rc.S_C_x, f_roll, rc.bikerPower);
|
speed = solveCubic(rc.S_C_x, f_roll, rc.bikerPower);
|
||||||
speed = Math.min(speed, wayMaxspeed);
|
speed = Math.min(speed, maxSpeed);
|
||||||
}
|
|
||||||
else // all other
|
|
||||||
{
|
|
||||||
speed = wayMaxspeed;
|
|
||||||
}
|
}
|
||||||
float dt = (float) (dist / speed);
|
float dt = (float) (dist / speed);
|
||||||
totalTime += dt;
|
totalTime += dt;
|
||||||
// Calc energy assuming biking (no good model yet for hiking)
|
// Calc energy assuming biking (no good model yet for hiking)
|
||||||
// (Count only positive, negative would mean breaking to enforce maxspeed)
|
// (Count only positive, negative would mean breaking to enforce maxspeed)
|
||||||
double energy = dist * (rc.S_C_x * speed * speed + f_roll);
|
double energy = dist * (rc.S_C_x * speed * speed + f_roll);
|
||||||
if ( energy > 0. )
|
if (energy > 0.) {
|
||||||
{
|
|
||||||
totalEnergy += energy;
|
totalEnergy += energy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double solveCubic( double a, double c, double d )
|
private static double solveCubic(double a, double c, double d) {
|
||||||
{
|
|
||||||
// Solves a * v^3 + c * v = d with a Newton method
|
// Solves a * v^3 + c * v = d with a Newton method
|
||||||
// to get the speed v for the section.
|
// to get the speed v for the section.
|
||||||
|
|
||||||
double v = 8.;
|
double v = 8.;
|
||||||
boolean findingStartvalue = true;
|
boolean findingStartvalue = true;
|
||||||
for ( int i = 0; i < 10; i++ )
|
for (int i = 0; i < 10; i++) {
|
||||||
{
|
|
||||||
double y = (a * v * v + c) * v - d;
|
double y = (a * v * v + c) * v - d;
|
||||||
if ( y < .1 )
|
if (y < .1) {
|
||||||
{
|
if (findingStartvalue) {
|
||||||
if ( findingStartvalue )
|
|
||||||
{
|
|
||||||
v *= 2.;
|
v *= 2.;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -302,14 +293,12 @@ final class StdPath extends OsmPath
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalTime()
|
public double getTotalTime() {
|
||||||
{
|
|
||||||
return totalTime;
|
return totalTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTotalEnergy()
|
public double getTotalEnergy() {
|
||||||
{
|
|
||||||
return totalEnergy;
|
return totalEnergy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
59
brouter-core/src/main/java/btools/router/SuspectInfo.java
Normal file
59
brouter-core/src/main/java/btools/router/SuspectInfo.java
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SuspectInfo {
|
||||||
|
public static final int TRIGGER_DEAD_END = 1;
|
||||||
|
public static final int TRIGGER_DEAD_START = 2;
|
||||||
|
public static final int TRIGGER_NODE_BLOCK = 4;
|
||||||
|
public static final int TRIGGER_BAD_ACCESS = 8;
|
||||||
|
public static final int TRIGGER_UNK_ACCESS = 16;
|
||||||
|
public static final int TRIGGER_SHARP_EXIT = 32;
|
||||||
|
public static final int TRIGGER_SHARP_ENTRY = 64;
|
||||||
|
public static final int TRIGGER_SHARP_LINK = 128;
|
||||||
|
public static final int TRIGGER_BAD_TR = 256;
|
||||||
|
|
||||||
|
public int prio;
|
||||||
|
public int triggers;
|
||||||
|
|
||||||
|
public static void addSuspect(Map<Long, SuspectInfo> map, long id, int prio, int trigger) {
|
||||||
|
Long iD = id;
|
||||||
|
SuspectInfo info = map.get(iD);
|
||||||
|
if (info == null) {
|
||||||
|
info = new SuspectInfo();
|
||||||
|
map.put(iD, info);
|
||||||
|
}
|
||||||
|
info.prio = Math.max(info.prio, prio);
|
||||||
|
info.triggers |= trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SuspectInfo addTrigger(SuspectInfo old, int prio, int trigger) {
|
||||||
|
if (old == null) {
|
||||||
|
old = new SuspectInfo();
|
||||||
|
}
|
||||||
|
old.prio = Math.max(old.prio, prio);
|
||||||
|
old.triggers |= trigger;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getTriggerText(int triggers) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
addText(sb, "dead-end", triggers, TRIGGER_DEAD_END);
|
||||||
|
addText(sb, "dead-start", triggers, TRIGGER_DEAD_START);
|
||||||
|
addText(sb, "node-block", triggers, TRIGGER_NODE_BLOCK);
|
||||||
|
addText(sb, "bad-access", triggers, TRIGGER_BAD_ACCESS);
|
||||||
|
addText(sb, "unkown-access", triggers, TRIGGER_UNK_ACCESS);
|
||||||
|
addText(sb, "sharp-exit", triggers, TRIGGER_SHARP_EXIT);
|
||||||
|
addText(sb, "sharp-entry", triggers, TRIGGER_SHARP_ENTRY);
|
||||||
|
addText(sb, "sharp-link", triggers, TRIGGER_SHARP_LINK);
|
||||||
|
addText(sb, "bad-tr", triggers, TRIGGER_BAD_TR);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addText(StringBuilder sb, String text, int mask, int bit) {
|
||||||
|
if ((bit & mask) == 0) return;
|
||||||
|
if (sb.length() > 0) sb.append(",");
|
||||||
|
sb.append(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -9,8 +9,7 @@ package btools.router;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class VoiceHint
|
public class VoiceHint {
|
||||||
{
|
|
||||||
static final int C = 1; // continue (go straight)
|
static final int C = 1; // continue (go straight)
|
||||||
static final int TL = 2; // turn left
|
static final int TL = 2; // turn left
|
||||||
static final int TSLL = 3; // turn slightly left
|
static final int TSLL = 3; // turn slightly left
|
||||||
|
|
@ -20,11 +19,13 @@ public class VoiceHint
|
||||||
static final int TSHR = 7; // turn sharply right
|
static final int TSHR = 7; // turn sharply right
|
||||||
static final int KL = 8; // keep left
|
static final int KL = 8; // keep left
|
||||||
static final int KR = 9; // keep right
|
static final int KR = 9; // keep right
|
||||||
static final int TU = 10; // U-turn
|
static final int TLU = 10; // U-turn
|
||||||
static final int TRU = 11; // Right U-turn
|
static final int TRU = 11; // Right U-turn
|
||||||
static final int OFFR = 12; // Off route
|
static final int OFFR = 12; // Off route
|
||||||
static final int RNDB = 13; // Roundabout
|
static final int RNDB = 13; // Roundabout
|
||||||
static final int RNLB = 14; // Roundabout left
|
static final int RNLB = 14; // Roundabout left
|
||||||
|
static final int TU = 15; // 180 degree u-turn
|
||||||
|
static final int BL = 16; // Beeline routing
|
||||||
|
|
||||||
int ilon;
|
int ilon;
|
||||||
int ilat;
|
int ilat;
|
||||||
|
|
@ -36,168 +37,450 @@ public class VoiceHint
|
||||||
double distanceToNext;
|
double distanceToNext;
|
||||||
int indexInTrack;
|
int indexInTrack;
|
||||||
|
|
||||||
public float getTime()
|
public float getTime() {
|
||||||
{
|
|
||||||
return oldWay == null ? 0.f : oldWay.time;
|
return oldWay == null ? 0.f : oldWay.time;
|
||||||
}
|
}
|
||||||
|
|
||||||
float angle;
|
float angle = Float.MAX_VALUE;
|
||||||
boolean turnAngleConsumed;
|
boolean turnAngleConsumed;
|
||||||
boolean needsRealTurn;
|
boolean needsRealTurn;
|
||||||
|
int maxBadPrio = -1;
|
||||||
|
|
||||||
int roundaboutExit;
|
int roundaboutExit;
|
||||||
|
|
||||||
boolean isRoundabout()
|
boolean isRoundabout() {
|
||||||
{
|
|
||||||
return roundaboutExit != 0;
|
return roundaboutExit != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBadWay( MessageData badWay )
|
public void addBadWay(MessageData badWay) {
|
||||||
{
|
if (badWay == null) {
|
||||||
if ( badWay == null )
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( badWays == null )
|
if (badWays == null) {
|
||||||
{
|
badWays = new ArrayList<>();
|
||||||
badWays = new ArrayList<MessageData>();
|
|
||||||
}
|
}
|
||||||
badWays.add(badWay);
|
badWays.add(badWay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCommand()
|
public int getJsonCommandIndex() {
|
||||||
{
|
switch (cmd) {
|
||||||
return cmd;
|
case TLU:
|
||||||
|
return 10;
|
||||||
|
case TU:
|
||||||
|
return 15;
|
||||||
|
case TSHL:
|
||||||
|
return 4;
|
||||||
|
case TL:
|
||||||
|
return 2;
|
||||||
|
case TSLL:
|
||||||
|
return 3;
|
||||||
|
case KL:
|
||||||
|
return 8;
|
||||||
|
case C:
|
||||||
|
return 1;
|
||||||
|
case KR:
|
||||||
|
return 9;
|
||||||
|
case TSLR:
|
||||||
|
return 6;
|
||||||
|
case TR:
|
||||||
|
return 5;
|
||||||
|
case TSHR:
|
||||||
|
return 7;
|
||||||
|
case TRU:
|
||||||
|
return 11;
|
||||||
|
case RNDB:
|
||||||
|
return 13;
|
||||||
|
case RNLB:
|
||||||
|
return 14;
|
||||||
|
case BL:
|
||||||
|
return 16;
|
||||||
|
case OFFR:
|
||||||
|
return 12;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getExitNumber()
|
public int getExitNumber() {
|
||||||
{
|
|
||||||
return roundaboutExit;
|
return roundaboutExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCommandString()
|
/*
|
||||||
{
|
* used by comment style, osmand style
|
||||||
switch ( cmd )
|
*/
|
||||||
{
|
public String getCommandString() {
|
||||||
case TU : return "TU";
|
switch (cmd) {
|
||||||
case TSHL : return "TSHL";
|
case TLU:
|
||||||
case TL : return "TL";
|
return "TU"; // should be changed to TLU when osmand uses new voice hint constants
|
||||||
case TSLL : return "TSLL";
|
case TU:
|
||||||
case KL : return "KL";
|
return "TU";
|
||||||
case C : return "C";
|
case TSHL:
|
||||||
case KR : return "KR";
|
return "TSHL";
|
||||||
case TSLR : return "TSLR";
|
case TL:
|
||||||
case TR : return "TR";
|
return "TL";
|
||||||
case TSHR : return "TSHR";
|
case TSLL:
|
||||||
case TRU : return "TRU";
|
return "TSLL";
|
||||||
case RNDB : return "RNDB" + roundaboutExit;
|
case KL:
|
||||||
case RNLB : return "RNLB" + (-roundaboutExit);
|
return "KL";
|
||||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
case C:
|
||||||
|
return "C";
|
||||||
|
case KR:
|
||||||
|
return "KR";
|
||||||
|
case TSLR:
|
||||||
|
return "TSLR";
|
||||||
|
case TR:
|
||||||
|
return "TR";
|
||||||
|
case TSHR:
|
||||||
|
return "TSHR";
|
||||||
|
case TRU:
|
||||||
|
return "TRU";
|
||||||
|
case RNDB:
|
||||||
|
return "RNDB" + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return "RNLB" + (-roundaboutExit);
|
||||||
|
case BL:
|
||||||
|
return "BL";
|
||||||
|
case OFFR:
|
||||||
|
return "OFFR";
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSymbolString()
|
/*
|
||||||
{
|
* used by trkpt/sym style
|
||||||
switch ( cmd )
|
*/
|
||||||
{
|
public String getCommandString(int c) {
|
||||||
case TU : return "TU";
|
switch (c) {
|
||||||
case TSHL : return "TSHL";
|
case TLU:
|
||||||
case TL : return "Left";
|
return "TLU";
|
||||||
case TSLL : return "TSLL";
|
case TU:
|
||||||
case KL : return "TSLL"; // ?
|
return "TU";
|
||||||
case C : return "Straight";
|
case TSHL:
|
||||||
case KR : return "TSLR"; // ?
|
return "TSHL";
|
||||||
case TSLR : return "TSLR";
|
case TL:
|
||||||
case TR : return "Right";
|
return "TL";
|
||||||
case TSHR : return "TSHR";
|
case TSLL:
|
||||||
case TRU : return "TU";
|
return "TSLL";
|
||||||
case RNDB : return "RNDB" + roundaboutExit;
|
case KL:
|
||||||
case RNLB : return "RNLB" + (-roundaboutExit);
|
return "KL";
|
||||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
case C:
|
||||||
|
return "C";
|
||||||
|
case KR:
|
||||||
|
return "KR";
|
||||||
|
case TSLR:
|
||||||
|
return "TSLR";
|
||||||
|
case TR:
|
||||||
|
return "TR";
|
||||||
|
case TSHR:
|
||||||
|
return "TSHR";
|
||||||
|
case TRU:
|
||||||
|
return "TRU";
|
||||||
|
case RNDB:
|
||||||
|
return "RNDB" + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return "RNLB" + (-roundaboutExit);
|
||||||
|
case BL:
|
||||||
|
return "BL";
|
||||||
|
case OFFR:
|
||||||
|
return "OFFR";
|
||||||
|
default:
|
||||||
|
return "unknown command: " + c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessageString()
|
/*
|
||||||
{
|
* used by gpsies style
|
||||||
switch ( cmd )
|
*/
|
||||||
{
|
public String getSymbolString() {
|
||||||
case TU : return "u-turn";
|
switch (cmd) {
|
||||||
case TSHL : return "sharp left";
|
case TLU:
|
||||||
case TL : return "left";
|
return "TU";
|
||||||
case TSLL : return "slight left";
|
case TU:
|
||||||
case KL : return "keep left";
|
return "TU";
|
||||||
case C : return "straight";
|
case TSHL:
|
||||||
case KR : return "keep right";
|
return "TSHL";
|
||||||
case TSLR : return "slight right";
|
case TL:
|
||||||
case TR : return "right";
|
return "Left";
|
||||||
case TSHR : return "sharp right";
|
case TSLL:
|
||||||
case TRU : return "u-turn";
|
return "TSLL";
|
||||||
case RNDB : return "Take exit " + roundaboutExit;
|
case KL:
|
||||||
case RNLB : return "Take exit " + (-roundaboutExit);
|
return "TSLL"; // ?
|
||||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
case C:
|
||||||
|
return "Straight";
|
||||||
|
case KR:
|
||||||
|
return "TSLR"; // ?
|
||||||
|
case TSLR:
|
||||||
|
return "TSLR";
|
||||||
|
case TR:
|
||||||
|
return "Right";
|
||||||
|
case TSHR:
|
||||||
|
return "TSHR";
|
||||||
|
case TRU:
|
||||||
|
return "TU";
|
||||||
|
case RNDB:
|
||||||
|
return "RNDB" + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return "RNLB" + (-roundaboutExit);
|
||||||
|
case BL:
|
||||||
|
return "BL";
|
||||||
|
case OFFR:
|
||||||
|
return "OFFR";
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLocusAction()
|
/*
|
||||||
{
|
* used by new locus trkpt style
|
||||||
switch ( cmd )
|
*/
|
||||||
{
|
public String getLocusSymbolString() {
|
||||||
case TU : return 13;
|
switch (cmd) {
|
||||||
case TSHL : return 5;
|
case TLU:
|
||||||
case TL : return 4;
|
return "u-turn_left";
|
||||||
case TSLL : return 3;
|
case TU:
|
||||||
case KL : return 9; // ?
|
return "u-turn";
|
||||||
case C : return 1;
|
case TSHL:
|
||||||
case KR : return 10; // ?
|
return "left_sharp";
|
||||||
case TSLR : return 6;
|
case TL:
|
||||||
case TR : return 7;
|
return "left";
|
||||||
case TSHR : return 8;
|
case TSLL:
|
||||||
case TRU : return 14;
|
return "left_slight";
|
||||||
case RNDB : return 26 + roundaboutExit;
|
case KL:
|
||||||
case RNLB : return 26 - roundaboutExit;
|
return "stay_left"; // ?
|
||||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
case C:
|
||||||
|
return "straight";
|
||||||
|
case KR:
|
||||||
|
return "stay_right"; // ?
|
||||||
|
case TSLR:
|
||||||
|
return "right_slight";
|
||||||
|
case TR:
|
||||||
|
return "right";
|
||||||
|
case TSHR:
|
||||||
|
return "right_sharp";
|
||||||
|
case TRU:
|
||||||
|
return "u-turn_right";
|
||||||
|
case RNDB:
|
||||||
|
return "roundabout_e" + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return "roundabout_e" + (-roundaboutExit);
|
||||||
|
case BL:
|
||||||
|
return "beeline";
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOruxAction()
|
/*
|
||||||
{
|
* used by osmand style
|
||||||
switch ( cmd )
|
*/
|
||||||
{
|
public String getMessageString() {
|
||||||
case TU : return 1003;
|
switch (cmd) {
|
||||||
case TSHL : return 1019;
|
case TLU:
|
||||||
case TL : return 1000;
|
return "u-turn"; // should be changed to u-turn-left when osmand uses new voice hint constants
|
||||||
case TSLL : return 1017;
|
case TU:
|
||||||
case KL : return 1015; // ?
|
return "u-turn";
|
||||||
case C : return 1002;
|
case TSHL:
|
||||||
case KR : return 1014; // ?
|
return "sharp left";
|
||||||
case TSLR : return 1016;
|
case TL:
|
||||||
case TR : return 1001;
|
return "left";
|
||||||
case TSHR : return 1018;
|
case TSLL:
|
||||||
case TRU : return 1003;
|
return "slight left";
|
||||||
case RNDB : return 1008 + roundaboutExit;
|
case KL:
|
||||||
case RNLB : return 1008 + roundaboutExit;
|
return "keep left";
|
||||||
default : throw new IllegalArgumentException( "unknown command: " + cmd );
|
case C:
|
||||||
|
return "straight";
|
||||||
|
case KR:
|
||||||
|
return "keep right";
|
||||||
|
case TSLR:
|
||||||
|
return "slight right";
|
||||||
|
case TR:
|
||||||
|
return "right";
|
||||||
|
case TSHR:
|
||||||
|
return "sharp right";
|
||||||
|
case TRU:
|
||||||
|
return "u-turn"; // should be changed to u-turn-right when osmand uses new voice hint constants
|
||||||
|
case RNDB:
|
||||||
|
return "Take exit " + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return "Take exit " + (-roundaboutExit);
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void calcCommand()
|
/*
|
||||||
{
|
* used by old locus style
|
||||||
|
*/
|
||||||
|
public int getLocusAction() {
|
||||||
|
switch (cmd) {
|
||||||
|
case TLU:
|
||||||
|
return 13;
|
||||||
|
case TU:
|
||||||
|
return 12;
|
||||||
|
case TSHL:
|
||||||
|
return 5;
|
||||||
|
case TL:
|
||||||
|
return 4;
|
||||||
|
case TSLL:
|
||||||
|
return 3;
|
||||||
|
case KL:
|
||||||
|
return 9; // ?
|
||||||
|
case C:
|
||||||
|
return 1;
|
||||||
|
case KR:
|
||||||
|
return 10; // ?
|
||||||
|
case TSLR:
|
||||||
|
return 6;
|
||||||
|
case TR:
|
||||||
|
return 7;
|
||||||
|
case TSHR:
|
||||||
|
return 8;
|
||||||
|
case TRU:
|
||||||
|
return 14;
|
||||||
|
case RNDB:
|
||||||
|
return 26 + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return 26 - roundaboutExit;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* used by orux style
|
||||||
|
*/
|
||||||
|
public int getOruxAction() {
|
||||||
|
switch (cmd) {
|
||||||
|
case TLU:
|
||||||
|
return 1003;
|
||||||
|
case TU:
|
||||||
|
return 1003;
|
||||||
|
case TSHL:
|
||||||
|
return 1019;
|
||||||
|
case TL:
|
||||||
|
return 1000;
|
||||||
|
case TSLL:
|
||||||
|
return 1017;
|
||||||
|
case KL:
|
||||||
|
return 1015; // ?
|
||||||
|
case C:
|
||||||
|
return 1002;
|
||||||
|
case KR:
|
||||||
|
return 1014; // ?
|
||||||
|
case TSLR:
|
||||||
|
return 1016;
|
||||||
|
case TR:
|
||||||
|
return 1001;
|
||||||
|
case TSHR:
|
||||||
|
return 1018;
|
||||||
|
case TRU:
|
||||||
|
return 1003;
|
||||||
|
case RNDB:
|
||||||
|
return 1008 + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return 1008 + roundaboutExit;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* used by cruiser, equivalent to getCommandString() - osmand style - when osmand changes the voice hint constants
|
||||||
|
*/
|
||||||
|
public String getCruiserCommandString() {
|
||||||
|
switch (cmd) {
|
||||||
|
case TLU:
|
||||||
|
return "TLU";
|
||||||
|
case TU:
|
||||||
|
return "TU";
|
||||||
|
case TSHL:
|
||||||
|
return "TSHL";
|
||||||
|
case TL:
|
||||||
|
return "TL";
|
||||||
|
case TSLL:
|
||||||
|
return "TSLL";
|
||||||
|
case KL:
|
||||||
|
return "KL";
|
||||||
|
case C:
|
||||||
|
return "C";
|
||||||
|
case KR:
|
||||||
|
return "KR";
|
||||||
|
case TSLR:
|
||||||
|
return "TSLR";
|
||||||
|
case TR:
|
||||||
|
return "TR";
|
||||||
|
case TSHR:
|
||||||
|
return "TSHR";
|
||||||
|
case TRU:
|
||||||
|
return "TRU";
|
||||||
|
case RNDB:
|
||||||
|
return "RNDB" + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return "RNLB" + (-roundaboutExit);
|
||||||
|
case BL:
|
||||||
|
return "BL";
|
||||||
|
case OFFR:
|
||||||
|
return "OFFR";
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* used by cruiser, equivalent to getMessageString() - osmand style - when osmand changes the voice hint constants
|
||||||
|
*/
|
||||||
|
public String getCruiserMessageString() {
|
||||||
|
switch (cmd) {
|
||||||
|
case TLU:
|
||||||
|
return "u-turn left";
|
||||||
|
case TU:
|
||||||
|
return "u-turn";
|
||||||
|
case TSHL:
|
||||||
|
return "sharp left";
|
||||||
|
case TL:
|
||||||
|
return "left";
|
||||||
|
case TSLL:
|
||||||
|
return "slight left";
|
||||||
|
case KL:
|
||||||
|
return "keep left";
|
||||||
|
case C:
|
||||||
|
return "straight";
|
||||||
|
case KR:
|
||||||
|
return "keep right";
|
||||||
|
case TSLR:
|
||||||
|
return "slight right";
|
||||||
|
case TR:
|
||||||
|
return "right";
|
||||||
|
case TSHR:
|
||||||
|
return "sharp right";
|
||||||
|
case TRU:
|
||||||
|
return "u-turn right";
|
||||||
|
case RNDB:
|
||||||
|
return "take exit " + roundaboutExit;
|
||||||
|
case RNLB:
|
||||||
|
return "take exit " + (-roundaboutExit);
|
||||||
|
case BL:
|
||||||
|
return "beeline";
|
||||||
|
case OFFR:
|
||||||
|
return "offroad";
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown command: " + cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void calcCommand() {
|
||||||
float lowerBadWayAngle = -181;
|
float lowerBadWayAngle = -181;
|
||||||
float higherBadWayAngle = 181;
|
float higherBadWayAngle = 181;
|
||||||
if ( badWays != null )
|
if (badWays != null) {
|
||||||
{
|
for (MessageData badWay : badWays) {
|
||||||
for ( MessageData badWay : badWays )
|
if (badWay.isBadOneway()) {
|
||||||
{
|
|
||||||
if ( badWay.isBadOneway() )
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle )
|
if (lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle) {
|
||||||
{
|
|
||||||
lowerBadWayAngle = badWay.turnangle;
|
lowerBadWayAngle = badWay.turnangle;
|
||||||
}
|
}
|
||||||
if ( higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle )
|
if (higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle) {
|
||||||
{
|
|
||||||
higherBadWayAngle = badWay.turnangle;
|
higherBadWayAngle = badWay.turnangle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -206,100 +489,105 @@ public class VoiceHint
|
||||||
float cmdAngle = angle;
|
float cmdAngle = angle;
|
||||||
|
|
||||||
// fall back to local angle if otherwise inconsistent
|
// fall back to local angle if otherwise inconsistent
|
||||||
if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
|
//if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
|
||||||
{
|
//{
|
||||||
|
//cmdAngle = goodWay.turnangle;
|
||||||
|
//}
|
||||||
|
if (angle == Float.MAX_VALUE) {
|
||||||
cmdAngle = goodWay.turnangle;
|
cmdAngle = goodWay.turnangle;
|
||||||
}
|
}
|
||||||
|
if (cmd == BL) return;
|
||||||
|
|
||||||
if (roundaboutExit > 0)
|
if (roundaboutExit > 0) {
|
||||||
{
|
|
||||||
cmd = RNDB;
|
cmd = RNDB;
|
||||||
}
|
} else if (roundaboutExit < 0) {
|
||||||
else if (roundaboutExit < 0)
|
|
||||||
{
|
|
||||||
cmd = RNLB;
|
cmd = RNLB;
|
||||||
}
|
} else if (is180DegAngle(cmdAngle) && cmdAngle <= -179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
|
||||||
else if ( cmdAngle < -159. )
|
|
||||||
{
|
|
||||||
cmd = TU;
|
cmd = TU;
|
||||||
}
|
} else if (cmdAngle < -159.f) {
|
||||||
else if ( cmdAngle < -135. )
|
cmd = TLU;
|
||||||
{
|
} else if (cmdAngle < -135.f) {
|
||||||
cmd = TSHL;
|
cmd = TSHL;
|
||||||
}
|
} else if (cmdAngle < -45.f) {
|
||||||
else if ( cmdAngle < -45. )
|
|
||||||
{
|
|
||||||
// a TL can be pushed in either direction by a close-by alternative
|
// a TL can be pushed in either direction by a close-by alternative
|
||||||
if ( higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180. )
|
if (cmdAngle < -95.f && higherBadWayAngle < -30.f && lowerBadWayAngle < -180.f) {
|
||||||
{
|
|
||||||
cmd = TSHL;
|
cmd = TSHL;
|
||||||
}
|
} else if (cmdAngle > -85.f && lowerBadWayAngle > -180.f && higherBadWayAngle > -10.f) {
|
||||||
else if ( lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0. )
|
|
||||||
{
|
|
||||||
cmd = TSLL;
|
cmd = TSLL;
|
||||||
}
|
} else {
|
||||||
else
|
if (cmdAngle < -110.f) {
|
||||||
{
|
cmd = TSHL;
|
||||||
|
} else if (cmdAngle > -60.f) {
|
||||||
|
cmd = TSLL;
|
||||||
|
} else {
|
||||||
cmd = TL;
|
cmd = TL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( cmdAngle < -21. )
|
} else if (cmdAngle < -21.f) {
|
||||||
{
|
if (cmd != KR) { // don't overwrite KR with TSLL
|
||||||
if ( cmd != KR ) // don't overwrite KR with TSLL
|
|
||||||
{
|
|
||||||
cmd = TSLL;
|
cmd = TSLL;
|
||||||
}
|
}
|
||||||
}
|
} else if (cmdAngle < -5.f) {
|
||||||
else if ( cmdAngle < 21. )
|
if (lowerBadWayAngle < -100.f && higherBadWayAngle < 45.f) {
|
||||||
{
|
cmd = TSLL;
|
||||||
if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
|
} else if (lowerBadWayAngle >= -100.f && higherBadWayAngle < 45.f) {
|
||||||
{
|
cmd = KL;
|
||||||
|
} else {
|
||||||
cmd = C;
|
cmd = C;
|
||||||
}
|
}
|
||||||
|
} else if (cmdAngle < 5.f) {
|
||||||
|
if (lowerBadWayAngle > -30.f) {
|
||||||
|
cmd = KR;
|
||||||
|
} else if (higherBadWayAngle < 30.f) {
|
||||||
|
cmd = KL;
|
||||||
|
} else {
|
||||||
|
cmd = C;
|
||||||
}
|
}
|
||||||
else if ( cmdAngle < 45. )
|
} else if (cmdAngle < 21.f) {
|
||||||
{
|
|
||||||
if ( cmd != KL ) // don't overwrite KL with TSLR
|
|
||||||
{
|
|
||||||
cmd = TSLR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( cmdAngle < 135. )
|
|
||||||
{
|
|
||||||
// a TR can be pushed in either direction by a close-by alternative
|
// a TR can be pushed in either direction by a close-by alternative
|
||||||
if ( higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0. )
|
if (lowerBadWayAngle > -45.f && higherBadWayAngle > 100.f) {
|
||||||
{
|
|
||||||
cmd = TSLR;
|
cmd = TSLR;
|
||||||
|
} else if (lowerBadWayAngle > -45.f && higherBadWayAngle <= 100.f) {
|
||||||
|
cmd = KR;
|
||||||
|
} else {
|
||||||
|
cmd = C;
|
||||||
}
|
}
|
||||||
else if ( lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180. )
|
} else if (cmdAngle < 45.f) {
|
||||||
{
|
cmd = TSLR;
|
||||||
|
} else if (cmdAngle < 135.f) {
|
||||||
|
if (cmdAngle < 85.f && higherBadWayAngle < 180.f && lowerBadWayAngle < 10.f) {
|
||||||
|
cmd = TSLR;
|
||||||
|
} else if (cmdAngle > 95.f && lowerBadWayAngle > 30.f && higherBadWayAngle > 180.f) {
|
||||||
cmd = TSHR;
|
cmd = TSHR;
|
||||||
}
|
} else {
|
||||||
else
|
if (cmdAngle > 110.) {
|
||||||
{
|
cmd = TSHR;
|
||||||
|
} else if (cmdAngle < 60.) {
|
||||||
|
cmd = TSLR;
|
||||||
|
} else {
|
||||||
cmd = TR;
|
cmd = TR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ( cmdAngle < 159. )
|
} else if (cmdAngle < 159.f) {
|
||||||
{
|
|
||||||
cmd = TSHR;
|
cmd = TSHR;
|
||||||
}
|
} else if (is180DegAngle(cmdAngle) && cmdAngle >= 179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
|
||||||
else
|
cmd = TU;
|
||||||
{
|
} else {
|
||||||
cmd = TRU;
|
cmd = TRU;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String formatGeometry()
|
static boolean is180DegAngle(float angle) {
|
||||||
{
|
return (Math.abs(angle) <= 180.f && Math.abs(angle) >= 179.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatGeometry() {
|
||||||
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
|
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
|
||||||
StringBuilder sb = new StringBuilder(30);
|
StringBuilder sb = new StringBuilder(30);
|
||||||
sb.append(' ').append((int) oldPrio);
|
sb.append(' ').append((int) oldPrio);
|
||||||
appendTurnGeometry(sb, goodWay);
|
appendTurnGeometry(sb, goodWay);
|
||||||
if ( badWays != null )
|
if (badWays != null) {
|
||||||
{
|
for (MessageData badWay : badWays) {
|
||||||
for ( MessageData badWay : badWays )
|
|
||||||
{
|
|
||||||
sb.append(" ");
|
sb.append(" ");
|
||||||
appendTurnGeometry(sb, badWay);
|
appendTurnGeometry(sb, badWay);
|
||||||
}
|
}
|
||||||
|
|
@ -307,8 +595,7 @@ public class VoiceHint
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendTurnGeometry( StringBuilder sb, MessageData msg )
|
private void appendTurnGeometry(StringBuilder sb, MessageData msg) {
|
||||||
{
|
|
||||||
sb.append("(").append((int) (msg.turnangle + 0.5)).append(")").append((int) (msg.priorityclassifier));
|
sb.append("(").append((int) (msg.turnangle + 0.5)).append(")").append((int) (msg.priorityclassifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,30 +9,51 @@ package btools.router;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class VoiceHintList
|
public class VoiceHintList {
|
||||||
{
|
|
||||||
private String transportMode;
|
|
||||||
int turnInstructionMode;
|
|
||||||
ArrayList<VoiceHint> list = new ArrayList<VoiceHint>();
|
|
||||||
|
|
||||||
public void setTransportMode( boolean isCar, boolean isBike )
|
static final int TRANS_MODE_NONE = 0;
|
||||||
{
|
static final int TRANS_MODE_FOOT = 1;
|
||||||
transportMode = isCar ? "car" : ( isBike ? "bike" : "foot" );
|
static final int TRANS_MODE_BIKE = 2;
|
||||||
|
static final int TRANS_MODE_CAR = 3;
|
||||||
|
|
||||||
|
private int transportMode = TRANS_MODE_BIKE;
|
||||||
|
int turnInstructionMode;
|
||||||
|
List<VoiceHint> list = new ArrayList<>();
|
||||||
|
|
||||||
|
public void setTransportMode(boolean isCar, boolean isBike) {
|
||||||
|
transportMode = isCar ? TRANS_MODE_CAR : (isBike ? TRANS_MODE_BIKE : TRANS_MODE_FOOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTransportMode()
|
public void setTransportMode(int mode) {
|
||||||
{
|
transportMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTransportMode() {
|
||||||
|
String ret;
|
||||||
|
switch (transportMode) {
|
||||||
|
case TRANS_MODE_FOOT:
|
||||||
|
ret = "foot";
|
||||||
|
break;
|
||||||
|
case TRANS_MODE_CAR:
|
||||||
|
ret = "car";
|
||||||
|
break;
|
||||||
|
case TRANS_MODE_BIKE:
|
||||||
|
default:
|
||||||
|
ret = "bike";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int transportMode() {
|
||||||
return transportMode;
|
return transportMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLocusRouteType()
|
public int getLocusRouteType() {
|
||||||
{
|
if (transportMode == TRANS_MODE_CAR) {
|
||||||
if ( "car".equals( transportMode ) )
|
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ( "bike".equals( transportMode ) )
|
if (transportMode == TRANS_MODE_BIKE) {
|
||||||
{
|
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
return 3; // foot
|
return 3; // foot
|
||||||
|
|
|
||||||
|
|
@ -8,26 +8,27 @@ package btools.router;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class VoiceHintProcessor
|
public final class VoiceHintProcessor {
|
||||||
{
|
|
||||||
private double catchingRange; // range to catch angles and merge turns
|
|
||||||
private boolean explicitRoundabouts;
|
|
||||||
|
|
||||||
public VoiceHintProcessor( double catchingRange, boolean explicitRoundabouts )
|
double SIGNIFICANT_ANGLE = 22.5;
|
||||||
{
|
double INTERNAL_CATCHING_RANGE = 2.;
|
||||||
this.catchingRange = catchingRange;
|
|
||||||
|
// private double catchingRange; // range to catch angles and merge turns
|
||||||
|
private boolean explicitRoundabouts;
|
||||||
|
private int transportMode;
|
||||||
|
|
||||||
|
public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts, int transportMode) {
|
||||||
|
// this.catchingRange = catchingRange;
|
||||||
this.explicitRoundabouts = explicitRoundabouts;
|
this.explicitRoundabouts = explicitRoundabouts;
|
||||||
|
this.transportMode = transportMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float sumNonConsumedWithinCatchingRange( List<VoiceHint> inputs, int offset )
|
private float sumNonConsumedWithinCatchingRange(List<VoiceHint> inputs, int offset) {
|
||||||
{
|
|
||||||
double distance = 0.;
|
double distance = 0.;
|
||||||
float angle = 0.f;
|
float angle = 0.f;
|
||||||
while( offset >= 0 && distance < catchingRange )
|
while (offset >= 0 && distance < INTERNAL_CATCHING_RANGE) {
|
||||||
{
|
|
||||||
VoiceHint input = inputs.get(offset--);
|
VoiceHint input = inputs.get(offset--);
|
||||||
if ( input.turnAngleConsumed )
|
if (input.turnAngleConsumed) {
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
angle += input.goodWay.turnangle;
|
angle += input.goodWay.turnangle;
|
||||||
|
|
@ -44,10 +45,10 @@ public final class VoiceHintProcessor
|
||||||
* order (from target to start), but output is
|
* order (from target to start), but output is
|
||||||
* returned in travel-direction and only for
|
* returned in travel-direction and only for
|
||||||
* those nodes that trigger a voice hint.
|
* those nodes that trigger a voice hint.
|
||||||
*
|
* <p>
|
||||||
* Input objects are expected for every segment
|
* Input objects are expected for every segment
|
||||||
* of the track, also for those without a junction
|
* of the track, also for those without a junction
|
||||||
*
|
* <p>
|
||||||
* VoiceHint objects in the output list are enriched
|
* VoiceHint objects in the output list are enriched
|
||||||
* by the voice-command, the total angle and the distance
|
* by the voice-command, the total angle and the distance
|
||||||
* to the next hint
|
* to the next hint
|
||||||
|
|
@ -55,18 +56,21 @@ public final class VoiceHintProcessor
|
||||||
* @param inputs tracknodes, un reverse order
|
* @param inputs tracknodes, un reverse order
|
||||||
* @return voice hints, in forward order
|
* @return voice hints, in forward order
|
||||||
*/
|
*/
|
||||||
public List<VoiceHint> process( List<VoiceHint> inputs )
|
public List<VoiceHint> process(List<VoiceHint> inputs) {
|
||||||
{
|
List<VoiceHint> results = new ArrayList<>();
|
||||||
List<VoiceHint> results = new ArrayList<VoiceHint>();
|
|
||||||
double distance = 0.;
|
double distance = 0.;
|
||||||
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
|
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
|
||||||
|
|
||||||
int roundaboutExit = 0;
|
int roundaboutExit = 0;
|
||||||
|
int roundaboudStartIdx = -1;
|
||||||
|
|
||||||
for ( int hintIdx = 0; hintIdx < inputs.size(); hintIdx++ )
|
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
|
||||||
{
|
|
||||||
VoiceHint input = inputs.get(hintIdx);
|
VoiceHint input = inputs.get(hintIdx);
|
||||||
|
|
||||||
|
if (input.cmd == VoiceHint.BL) {
|
||||||
|
results.add(input);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
float turnAngle = input.goodWay.turnangle;
|
float turnAngle = input.goodWay.turnangle;
|
||||||
distance += input.goodWay.linkdist;
|
distance += input.goodWay.linkdist;
|
||||||
int currentPrio = input.goodWay.getPrio();
|
int currentPrio = input.goodWay.getPrio();
|
||||||
|
|
@ -74,37 +78,69 @@ public final class VoiceHintProcessor
|
||||||
int minPrio = Math.min(oldPrio, currentPrio);
|
int minPrio = Math.min(oldPrio, currentPrio);
|
||||||
|
|
||||||
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
|
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
|
||||||
|
boolean isHighway2Link = !input.oldWay.isLinktType() && input.goodWay.isLinktType();
|
||||||
|
|
||||||
if ( input.oldWay.isRoundabout() )
|
if (explicitRoundabouts && input.oldWay.isRoundabout()) {
|
||||||
{
|
if (roundaboudStartIdx == -1) roundaboudStartIdx = hintIdx;
|
||||||
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||||
|
if (roundaboudStartIdx == hintIdx) {
|
||||||
|
if (input.badWays != null) {
|
||||||
|
// remove goodWay
|
||||||
|
roundAboutTurnAngle -= input.goodWay.turnangle;
|
||||||
|
// add a badWay
|
||||||
|
for (MessageData badWay : input.badWays) {
|
||||||
|
if (!badWay.isBadOneway()) roundAboutTurnAngle += badWay.turnangle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
boolean isExit = roundaboutExit == 0; // exit point is always exit
|
boolean isExit = roundaboutExit == 0; // exit point is always exit
|
||||||
if ( input.badWays != null )
|
if (input.badWays != null) {
|
||||||
{
|
for (MessageData badWay : input.badWays) {
|
||||||
for ( MessageData badWay : input.badWays )
|
if (!badWay.isBadOneway() &&
|
||||||
{
|
badWay.isGoodForCars()) {
|
||||||
if ( !badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
|
|
||||||
{
|
|
||||||
isExit = true;
|
isExit = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( isExit )
|
if (isExit) {
|
||||||
{
|
|
||||||
roundaboutExit++;
|
roundaboutExit++;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( roundaboutExit > 0 )
|
if (roundaboutExit > 0) {
|
||||||
{
|
//roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||||
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
|
//double startTurn = (roundaboudStartIdx != -1 ? inputs.get(roundaboudStartIdx + 1).goodWay.turnangle : turnAngle);
|
||||||
input.angle = roundAboutTurnAngle;
|
input.angle = roundAboutTurnAngle;
|
||||||
|
input.goodWay.turnangle = roundAboutTurnAngle;
|
||||||
input.distanceToNext = distance;
|
input.distanceToNext = distance;
|
||||||
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
|
//input.roundaboutExit = startTurn < 0 ? roundaboutExit : -roundaboutExit;
|
||||||
|
input.roundaboutExit = roundAboutTurnAngle < 0 ? roundaboutExit : -roundaboutExit;
|
||||||
|
float tmpangle = 0;
|
||||||
|
VoiceHint tmpRndAbt = new VoiceHint();
|
||||||
|
tmpRndAbt.badWays = new ArrayList<>();
|
||||||
|
for (int i = hintIdx-1; i > roundaboudStartIdx; i--) {
|
||||||
|
VoiceHint vh = inputs.get(i);
|
||||||
|
tmpangle += inputs.get(i).goodWay.turnangle;
|
||||||
|
if (vh.badWays != null) {
|
||||||
|
for (MessageData badWay : vh.badWays) {
|
||||||
|
if (!badWay.isBadOneway()) {
|
||||||
|
MessageData md = new MessageData();
|
||||||
|
md.linkdist = vh.goodWay.linkdist;
|
||||||
|
md.priorityclassifier = vh.goodWay.priorityclassifier;
|
||||||
|
md.turnangle = tmpangle;
|
||||||
|
tmpRndAbt.badWays.add(md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
distance = 0.;
|
distance = 0.;
|
||||||
|
|
||||||
|
input.badWays = tmpRndAbt.badWays;
|
||||||
|
|
||||||
results.add(input);
|
results.add(input);
|
||||||
roundAboutTurnAngle = 0.f;
|
roundAboutTurnAngle = 0.f;
|
||||||
roundaboutExit = 0;
|
roundaboutExit = 0;
|
||||||
|
roundaboudStartIdx = -1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int maxPrioAll = -1; // max prio of all detours
|
int maxPrioAll = -1; // max prio of all detours
|
||||||
|
|
@ -114,134 +150,265 @@ public final class VoiceHintProcessor
|
||||||
float minAngle = 180.f;
|
float minAngle = 180.f;
|
||||||
float minAbsAngeRaw = 180.f;
|
float minAbsAngeRaw = 180.f;
|
||||||
|
|
||||||
if ( input.badWays != null )
|
boolean isBadwayLink = false;
|
||||||
{
|
|
||||||
for ( MessageData badWay : input.badWays )
|
if (input.badWays != null) {
|
||||||
{
|
for (MessageData badWay : input.badWays) {
|
||||||
int badPrio = badWay.getPrio();
|
int badPrio = badWay.getPrio();
|
||||||
float badTurn = badWay.turnangle;
|
float badTurn = badWay.turnangle;
|
||||||
|
if (badWay.isLinktType()) {
|
||||||
|
isBadwayLink = true;
|
||||||
|
}
|
||||||
|
boolean isBadHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
|
||||||
|
|
||||||
boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
|
if (badPrio > maxPrioAll && !isBadHighway2Link) {
|
||||||
|
|
||||||
if ( badPrio > maxPrioAll && !isHighway2Link )
|
|
||||||
{
|
|
||||||
maxPrioAll = badPrio;
|
maxPrioAll = badPrio;
|
||||||
|
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( badWay.costfactor < 20.f && Math.abs( badTurn ) < minAbsAngeRaw )
|
if (badPrio < minPrio) {
|
||||||
{
|
|
||||||
minAbsAngeRaw = Math.abs( badTurn );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( badPrio < minPrio )
|
|
||||||
{
|
|
||||||
continue; // ignore low prio ways
|
continue; // ignore low prio ways
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( badWay.isBadOneway() )
|
if (badWay.isBadOneway()) {
|
||||||
{
|
|
||||||
continue; // ignore wrong oneways
|
continue; // ignore wrong oneways
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
|
if (Math.abs(badTurn) - Math.abs(turnAngle) > 80.f) {
|
||||||
{
|
|
||||||
continue; // ways from the back should not trigger a slight turn
|
continue; // ways from the back should not trigger a slight turn
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( badPrio > maxPrioCandidates )
|
if (badWay.costfactor < 20.f && Math.abs(badTurn) < minAbsAngeRaw) {
|
||||||
{
|
minAbsAngeRaw = Math.abs(badTurn);
|
||||||
maxPrioCandidates = badPrio;
|
|
||||||
}
|
}
|
||||||
if ( badTurn > maxAngle )
|
|
||||||
{
|
if (badPrio > maxPrioCandidates) {
|
||||||
|
maxPrioCandidates = badPrio;
|
||||||
|
input.maxBadPrio = Math.max(input.maxBadPrio, badPrio);
|
||||||
|
}
|
||||||
|
if (badTurn > maxAngle) {
|
||||||
maxAngle = badTurn;
|
maxAngle = badTurn;
|
||||||
}
|
}
|
||||||
if ( badTurn < minAngle )
|
if (badTurn < minAngle) {
|
||||||
{
|
|
||||||
minAngle = badTurn;
|
minAngle = badTurn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasSomethingMoreStraight = Math.abs( turnAngle ) - minAbsAngeRaw > 20.;
|
// boolean hasSomethingMoreStraight = (Math.abs(turnAngle) - minAbsAngeRaw) > 20.;
|
||||||
|
boolean hasSomethingMoreStraight = (Math.abs(turnAngle - minAbsAngeRaw)) > 20. && input.badWays != null; // && !ignoreBadway;
|
||||||
|
|
||||||
// unconditional triggers are all junctions with
|
// unconditional triggers are all junctions with
|
||||||
// - higher detour prios than the minimum route prio (except link->highway junctions)
|
// - higher detour prios than the minimum route prio (except link->highway junctions)
|
||||||
// - or candidate detours with higher prio then the route exit leg
|
// - or candidate detours with higher prio then the route exit leg
|
||||||
boolean unconditionalTrigger = hasSomethingMoreStraight || ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
|
boolean unconditionalTrigger = hasSomethingMoreStraight ||
|
||||||
|
(maxPrioAll > minPrio && !isLink2Highway) ||
|
||||||
|
(maxPrioCandidates > currentPrio) ||
|
||||||
|
VoiceHint.is180DegAngle(turnAngle) ||
|
||||||
|
(!isHighway2Link && isBadwayLink && Math.abs(turnAngle) > 5.f) ||
|
||||||
|
(isHighway2Link && !isBadwayLink && Math.abs(turnAngle) < 5.f);
|
||||||
|
|
||||||
// conditional triggers (=real turning angle required) are junctions
|
// conditional triggers (=real turning angle required) are junctions
|
||||||
// with candidate detours equal in priority than the route exit leg
|
// with candidate detours equal in priority than the route exit leg
|
||||||
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
|
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
|
||||||
|
|
||||||
if ( unconditionalTrigger || conditionalTrigger )
|
if (unconditionalTrigger || conditionalTrigger) {
|
||||||
{
|
|
||||||
input.angle = turnAngle;
|
input.angle = turnAngle;
|
||||||
input.calcCommand();
|
input.calcCommand();
|
||||||
boolean isStraight = input.cmd == VoiceHint.C;
|
boolean isStraight = input.cmd == VoiceHint.C;
|
||||||
input.needsRealTurn = (!unconditionalTrigger) && isStraight;
|
input.needsRealTurn = (!unconditionalTrigger) && isStraight;
|
||||||
|
|
||||||
// check for KR/KL
|
// check for KR/KL
|
||||||
if ( maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f ) )
|
if (Math.abs(turnAngle) > 5.) { // don't use too small angles
|
||||||
{
|
if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (Math.max(turnAngle, 0.f))) {
|
||||||
input.cmd = VoiceHint.KR;
|
input.cmd = VoiceHint.KR;
|
||||||
}
|
}
|
||||||
if ( minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f ) )
|
if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (Math.min(turnAngle, 0.f))) {
|
||||||
{
|
|
||||||
input.cmd = VoiceHint.KL;
|
input.cmd = VoiceHint.KL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||||
input.distanceToNext = distance;
|
input.distanceToNext = distance;
|
||||||
distance = 0.;
|
distance = 0.;
|
||||||
results.add(input);
|
results.add(input);
|
||||||
}
|
}
|
||||||
if ( results.size() > 0 && distance < catchingRange )
|
if (results.size() > 0 && distance < INTERNAL_CATCHING_RANGE) { //catchingRange
|
||||||
{
|
|
||||||
results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// go through the hint list again in reverse order (=travel direction)
|
// go through the hint list again in reverse order (=travel direction)
|
||||||
// and filter out non-signficant hints and hints too close to it's predecessor
|
// and filter out non-significant hints and hints too close to its predecessor
|
||||||
|
|
||||||
List<VoiceHint> results2 = new ArrayList<VoiceHint>();
|
List<VoiceHint> results2 = new ArrayList<>();
|
||||||
int i = results.size();
|
int i = results.size();
|
||||||
while( i > 0 )
|
while (i > 0) {
|
||||||
{
|
|
||||||
VoiceHint hint = results.get(--i);
|
VoiceHint hint = results.get(--i);
|
||||||
if ( hint.cmd == 0 )
|
if (hint.cmd == 0) {
|
||||||
{
|
|
||||||
hint.calcCommand();
|
hint.calcCommand();
|
||||||
}
|
}
|
||||||
if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
|
if (!(hint.needsRealTurn && (hint.cmd == VoiceHint.C || hint.cmd == VoiceHint.BL))) {
|
||||||
{
|
|
||||||
double dist = hint.distanceToNext;
|
double dist = hint.distanceToNext;
|
||||||
// sum up other hints within the catching range (e.g. 40m)
|
// sum up other hints within the catching range (e.g. 40m)
|
||||||
while( dist < catchingRange && i > 0 )
|
while (dist < INTERNAL_CATCHING_RANGE && i > 0) {
|
||||||
{
|
|
||||||
VoiceHint h2 = results.get(i - 1);
|
VoiceHint h2 = results.get(i - 1);
|
||||||
dist = h2.distanceToNext;
|
dist = h2.distanceToNext;
|
||||||
hint.distanceToNext += dist;
|
hint.distanceToNext += dist;
|
||||||
hint.angle += h2.angle;
|
hint.angle += h2.angle;
|
||||||
i--;
|
i--;
|
||||||
if ( h2.isRoundabout() ) // if we hit a roundabout, use that as the trigger
|
if (h2.isRoundabout()) { // if we hit a roundabout, use that as the trigger
|
||||||
{
|
|
||||||
h2.angle = hint.angle;
|
h2.angle = hint.angle;
|
||||||
hint = h2;
|
hint = h2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !explicitRoundabouts )
|
if (!explicitRoundabouts) {
|
||||||
{
|
|
||||||
hint.roundaboutExit = 0; // use an angular hint instead
|
hint.roundaboutExit = 0; // use an angular hint instead
|
||||||
}
|
}
|
||||||
hint.calcCommand();
|
hint.calcCommand();
|
||||||
results2.add(hint);
|
results2.add(hint);
|
||||||
|
} else if (hint.cmd == VoiceHint.BL) {
|
||||||
|
results2.add(hint);
|
||||||
|
} else {
|
||||||
|
if (results2.size() > 0)
|
||||||
|
results2.get(results2.size() - 1).distanceToNext += hint.distanceToNext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results2;
|
return results2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<VoiceHint> postProcess(List<VoiceHint> inputs, double catchingRange, double minRange) {
|
||||||
|
List<VoiceHint> results = new ArrayList<>();
|
||||||
|
double distance = 0;
|
||||||
|
VoiceHint inputLast = null;
|
||||||
|
VoiceHint inputLastSaved = null;
|
||||||
|
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
|
||||||
|
VoiceHint input = inputs.get(hintIdx);
|
||||||
|
VoiceHint nextInput = null;
|
||||||
|
if (hintIdx + 1 < inputs.size()) {
|
||||||
|
nextInput = inputs.get(hintIdx + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextInput == null) {
|
||||||
|
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
||||||
|
if (input.goodWay.getPrio() < input.maxBadPrio && (inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange)) {
|
||||||
|
results.add(input);
|
||||||
|
} else {
|
||||||
|
if (inputLast != null) { // when drop add distance to last
|
||||||
|
inputLast.distanceToNext += input.distanceToNext;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results.add(input);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((inputLastSaved != null && inputLastSaved.distanceToNext > catchingRange) || input.distanceToNext > catchingRange) {
|
||||||
|
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
||||||
|
if (input.goodWay.getPrio() < input.maxBadPrio
|
||||||
|
&& (inputLastSaved != null && inputLastSaved.distanceToNext > minRange)
|
||||||
|
&& (input.distanceToNext > minRange)) {
|
||||||
|
// add only on prio
|
||||||
|
results.add(input);
|
||||||
|
inputLastSaved = input;
|
||||||
|
} else {
|
||||||
|
if (inputLastSaved != null) { // when drop add distance to last
|
||||||
|
inputLastSaved.distanceToNext += input.distanceToNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// add all others
|
||||||
|
// ignore motorway / primary continue
|
||||||
|
if (((input.goodWay.getPrio() != 28) &&
|
||||||
|
(input.goodWay.getPrio() != 30) &&
|
||||||
|
(input.goodWay.getPrio() != 26))
|
||||||
|
|| input.isRoundabout()
|
||||||
|
|| Math.abs(input.angle) > 21.f) {
|
||||||
|
results.add(input);
|
||||||
|
inputLastSaved = input;
|
||||||
|
} else {
|
||||||
|
if (inputLastSaved != null) { // when drop add distance to last
|
||||||
|
inputLastSaved.distanceToNext += input.distanceToNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (input.distanceToNext < catchingRange) {
|
||||||
|
double dist = input.distanceToNext;
|
||||||
|
float angles = input.angle;
|
||||||
|
int i = 1;
|
||||||
|
boolean save = false;
|
||||||
|
|
||||||
|
dist += nextInput.distanceToNext;
|
||||||
|
angles += nextInput.angle;
|
||||||
|
|
||||||
|
if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) {
|
||||||
|
if (input.goodWay.getPrio() < input.maxBadPrio) {
|
||||||
|
if (inputLastSaved != null && inputLastSaved.cmd != VoiceHint.C
|
||||||
|
&& (inputLastSaved != null && inputLastSaved.distanceToNext > minRange)
|
||||||
|
&& transportMode != VoiceHintList.TRANS_MODE_CAR) {
|
||||||
|
// add when straight and not linktype
|
||||||
|
// and last vh not straight
|
||||||
|
save = true;
|
||||||
|
// remove when next straight and not linktype
|
||||||
|
if (nextInput != null &&
|
||||||
|
nextInput.cmd == VoiceHint.C &&
|
||||||
|
!nextInput.goodWay.isLinktType()) {
|
||||||
|
input.distanceToNext += nextInput.distanceToNext;
|
||||||
|
hintIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (inputLastSaved != null) { // when drop add distance to last
|
||||||
|
inputLastSaved.distanceToNext += input.distanceToNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (VoiceHint.is180DegAngle(input.angle)) {
|
||||||
|
// add u-turn, 180 degree
|
||||||
|
save = true;
|
||||||
|
} else if (transportMode == VoiceHintList.TRANS_MODE_CAR && Math.abs(angles) > 180 - SIGNIFICANT_ANGLE) {
|
||||||
|
// add when inc car mode and u-turn, collects e.g. two left turns in range
|
||||||
|
input.angle = angles;
|
||||||
|
input.calcCommand();
|
||||||
|
input.distanceToNext += nextInput.distanceToNext;
|
||||||
|
save = true;
|
||||||
|
hintIdx++;
|
||||||
|
} else if (Math.abs(angles) < SIGNIFICANT_ANGLE && input.distanceToNext < minRange) {
|
||||||
|
input.angle = angles;
|
||||||
|
input.calcCommand();
|
||||||
|
input.distanceToNext += nextInput.distanceToNext;
|
||||||
|
save = true;
|
||||||
|
hintIdx++;
|
||||||
|
} else if (Math.abs(input.angle) > SIGNIFICANT_ANGLE) {
|
||||||
|
// add when angle above 22.5 deg
|
||||||
|
save = true;
|
||||||
|
} else if (Math.abs(input.angle) < SIGNIFICANT_ANGLE) {
|
||||||
|
// add when angle below 22.5 deg ???
|
||||||
|
// save = true;
|
||||||
|
} else {
|
||||||
|
// otherwise ignore but add distance to next
|
||||||
|
if (nextInput != null) { // when drop add distance to last
|
||||||
|
nextInput.distanceToNext += input.distanceToNext;
|
||||||
|
}
|
||||||
|
save = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save) {
|
||||||
|
results.add(input); // add when last
|
||||||
|
inputLastSaved = input;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
results.add(input);
|
||||||
|
inputLastSaved = input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputLast = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
package btools.router;
|
package btools.router;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import btools.router.OsmNogoPolygon.Point;
|
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class OsmNogoPolygonTest {
|
public class OsmNogoPolygonTest {
|
||||||
|
|
@ -77,9 +76,9 @@ public class OsmNogoPolygonTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsWithin() {
|
public void testIsWithin() {
|
||||||
double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
|
double[] plons = {0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5};
|
||||||
double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
|
double[] plats = {0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1};
|
||||||
boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
|
boolean[] within = {true, false, false, false, false, true, true, true, true, true};
|
||||||
|
|
||||||
for (int i = 0; i < plons.length; i++) {
|
for (int i = 0; i < plons.length; i++) {
|
||||||
assertEquals("(" + plons[i] + "," + plats[i] + ")", within[i], polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
|
assertEquals("(" + plons[i] + "," + plats[i] + ")", within[i], polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
|
||||||
|
|
|
||||||
68
brouter-core/src/test/java/btools/router/RouteParamTest.java
Normal file
68
brouter-core/src/test/java/btools/router/RouteParamTest.java
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
public class RouteParamTest {
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void readWptsNull() {
|
||||||
|
|
||||||
|
RoutingParamCollector rpc = new RoutingParamCollector();
|
||||||
|
List<OsmNodeNamed> map = rpc.getWayPointList(null);
|
||||||
|
|
||||||
|
Assert.assertEquals("result content null", 0, map.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readWpts() {
|
||||||
|
String data = "1.0,1.2;2.0,2.2";
|
||||||
|
RoutingParamCollector rpc = new RoutingParamCollector();
|
||||||
|
List<OsmNodeNamed> map = rpc.getWayPointList(data);
|
||||||
|
|
||||||
|
Assert.assertEquals("result content 1 ", 2, map.size());
|
||||||
|
|
||||||
|
data = "1.0,1.1|2.0,2.2|3.0,3.3";
|
||||||
|
map = rpc.getWayPointList(data);
|
||||||
|
|
||||||
|
Assert.assertEquals("result content 2 ", 3, map.size());
|
||||||
|
|
||||||
|
data = "1.0,1.2,Name;2.0,2.2";
|
||||||
|
map = rpc.getWayPointList(data);
|
||||||
|
|
||||||
|
Assert.assertEquals("result content 3 ", "Name", map.get(0).name);
|
||||||
|
|
||||||
|
data = "1.0,1.2,d;2.0,2.2";
|
||||||
|
map = rpc.getWayPointList(data);
|
||||||
|
|
||||||
|
Assert.assertTrue("result content 4 ", map.get(0).direct);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readUrlParams() throws UnsupportedEncodingException {
|
||||||
|
String url = "lonlats=1,1;2,2&profile=test&more=1";
|
||||||
|
RoutingParamCollector rpc = new RoutingParamCollector();
|
||||||
|
Map<String, String> map = rpc.getUrlParams(url);
|
||||||
|
|
||||||
|
Assert.assertEquals("result content ", 3, map.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readParamsFromList() throws UnsupportedEncodingException {
|
||||||
|
Map<String, String> params = new HashMap<>();
|
||||||
|
params.put("timode", "3");
|
||||||
|
RoutingContext rc = new RoutingContext();
|
||||||
|
RoutingParamCollector rpc = new RoutingParamCollector();
|
||||||
|
rpc.setParams(rc, null, params);
|
||||||
|
|
||||||
|
Assert.assertEquals("result content timode ", 3, rc.turnInstructionMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
package btools.router;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RoutingEngineTest {
|
||||||
|
private File workingDir;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
URL resulturl = this.getClass().getResource("/testtrack0.gpx");
|
||||||
|
Assert.assertNotNull("reference result not found: ", resulturl);
|
||||||
|
File resultfile = new File(resulturl.getFile());
|
||||||
|
workingDir = resultfile.getParentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void routeCrossingSegmentBorder() {
|
||||||
|
String msg = calcRoute(8.720897, 50.002515, 8.723658, 49.997510, "testtrack", new RoutingContext());
|
||||||
|
// error message from router?
|
||||||
|
Assert.assertNull("routing failed: " + msg, msg);
|
||||||
|
|
||||||
|
// if the track didn't change, we expect the first alternative also
|
||||||
|
File a1 = new File(workingDir, "testtrack1.gpx");
|
||||||
|
a1.deleteOnExit();
|
||||||
|
Assert.assertTrue("result content mismatch", a1.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void routeDestinationPointFarOff() {
|
||||||
|
String msg = calcRoute(8.720897, 50.002515, 16.723658, 49.997510, "notrack", new RoutingContext());
|
||||||
|
Assert.assertTrue(msg, msg != null && msg.contains("not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void overrideParam() {
|
||||||
|
RoutingContext rctx = new RoutingContext();
|
||||||
|
rctx.keyValues = new HashMap<>();
|
||||||
|
rctx.keyValues.put("avoid_unsafe", "1.0");
|
||||||
|
String msg = calcRoute(8.723037, 50.000491, 8.712737, 50.002899, "paramTrack", rctx);
|
||||||
|
Assert.assertNull("routing failed: " + msg, msg);
|
||||||
|
|
||||||
|
File trackFile = new File(workingDir, "paramTrack1.gpx");
|
||||||
|
trackFile.deleteOnExit();
|
||||||
|
Assert.assertTrue("result content mismatch", trackFile.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String calcRoute(double flon, double flat, double tlon, double tlat, String trackname, RoutingContext rctx) {
|
||||||
|
String wd = workingDir.getAbsolutePath();
|
||||||
|
|
||||||
|
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||||
|
OsmNodeNamed n;
|
||||||
|
n = new OsmNodeNamed();
|
||||||
|
n.name = "from";
|
||||||
|
n.ilon = 180000000 + (int) (flon * 1000000 + 0.5);
|
||||||
|
n.ilat = 90000000 + (int) (flat * 1000000 + 0.5);
|
||||||
|
wplist.add(n);
|
||||||
|
|
||||||
|
n = new OsmNodeNamed();
|
||||||
|
n.name = "to";
|
||||||
|
n.ilon = 180000000 + (int) (tlon * 1000000 + 0.5);
|
||||||
|
n.ilat = 90000000 + (int) (tlat * 1000000 + 0.5);
|
||||||
|
wplist.add(n);
|
||||||
|
|
||||||
|
rctx.localFunction = wd + "/../../../../misc/profiles2/trekking.brf";
|
||||||
|
|
||||||
|
RoutingEngine re = new RoutingEngine(
|
||||||
|
wd + "/" + trackname,
|
||||||
|
wd + "/" + trackname,
|
||||||
|
new File(wd, "/../../../../brouter-map-creator/build/resources/test/tmp/segments"),
|
||||||
|
wplist,
|
||||||
|
rctx);
|
||||||
|
|
||||||
|
re.doRun(0);
|
||||||
|
|
||||||
|
return re.getErrorMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
67
brouter-core/src/test/resources/paramTrack0.gpx
Normal file
67
brouter-core/src/test/resources/paramTrack0.gpx
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- track-length = 1570 filtered ascend = 4 plain-ascend = -15 cost=2840 energy=.0kwh time=3m 45s -->
|
||||||
|
<gpx
|
||||||
|
xmlns="http://www.topografix.com/GPX/1/1"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||||
|
creator="BRouter-1.7.0" version="1.1">
|
||||||
|
<trk>
|
||||||
|
<name>brouter_trekking_0</name>
|
||||||
|
<trkseg>
|
||||||
|
<trkpt lon="8.723027" lat="50.000499"><ele>175.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.723285" lat="50.000610"><ele>176.75</ele></trkpt>
|
||||||
|
<trkpt lon="8.724003" lat="50.000939"><ele>179.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.723553" lat="50.001028"><ele>177.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.723041" lat="50.001194"><ele>174.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.722781" lat="50.001312"><ele>173.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.722027" lat="50.001834"><ele>169.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.721982" lat="50.001865"><ele>169.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.722320" lat="50.002050"><ele>171.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.722449" lat="50.002197"><ele>171.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.722497" lat="50.002337"><ele>171.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.722506" lat="50.002463"><ele>171.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.722486" lat="50.002697"><ele>170.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.721892" lat="50.002631"><ele>167.75</ele></trkpt>
|
||||||
|
<trkpt lon="8.721836" lat="50.002624"><ele>167.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.721209" lat="50.002553"><ele>165.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.721118" lat="50.002538"><ele>164.75</ele></trkpt>
|
||||||
|
<trkpt lon="8.721021" lat="50.002493"><ele>164.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.720994" lat="50.002509"><ele>164.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.720960" lat="50.002518"><ele>164.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.720888" lat="50.002517"><ele>163.75</ele></trkpt>
|
||||||
|
<trkpt lon="8.720853" lat="50.002586"><ele>163.75</ele></trkpt>
|
||||||
|
<trkpt lon="8.720782" lat="50.002704"><ele>163.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.720554" lat="50.002937"><ele>162.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.720469" lat="50.003004"><ele>162.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.718899" lat="50.003724"><ele>160.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.718254" lat="50.004051"><ele>159.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.718123" lat="50.004087"><ele>159.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.717543" lat="50.004244"><ele>159.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.717181" lat="50.004357"><ele>159.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.716729" lat="50.004515"><ele>158.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.716463" lat="50.004600"><ele>157.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.715713" lat="50.004799"><ele>156.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.715490" lat="50.004843"><ele>156.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.714977" lat="50.004918"><ele>155.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.714539" lat="50.005012"><ele>154.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.713784" lat="50.005136"><ele>152.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713582" lat="50.005177"><ele>152.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713316" lat="50.005086"><ele>153.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.713067" lat="50.005001"><ele>153.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.712848" lat="50.004896"><ele>153.75</ele></trkpt>
|
||||||
|
<trkpt lon="8.712781" lat="50.004859"><ele>154.0</ele></trkpt>
|
||||||
|
<trkpt lon="8.712667" lat="50.004765"><ele>154.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.712563" lat="50.004683"><ele>154.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.712154" lat="50.004321"><ele>156.25</ele></trkpt>
|
||||||
|
<trkpt lon="8.712066" lat="50.004245"><ele>156.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713422" lat="50.003599"><ele>158.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713452" lat="50.003572"><ele>158.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713592" lat="50.003347"><ele>158.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713620" lat="50.003326"><ele>158.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713956" lat="50.003142"><ele>158.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713468" lat="50.002781"><ele>159.5</ele></trkpt>
|
||||||
|
<trkpt lon="8.713293" lat="50.002684"><ele>159.75</ele></trkpt>
|
||||||
|
<trkpt lon="8.712770" lat="50.002929"><ele>159.5</ele></trkpt>
|
||||||
|
</trkseg>
|
||||||
|
</trk>
|
||||||
|
</gpx>
|
||||||
64
brouter-core/src/test/resources/testtrack0.gpx
Normal file
64
brouter-core/src/test/resources/testtrack0.gpx
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- track-length = 736 filtered ascend = 0 plain-ascend = 0 cost=736 energy=.0kwh time=1m 53s -->
|
||||||
|
<gpx
|
||||||
|
xmlns="http://www.topografix.com/GPX/1/1"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||||
|
creator="BRouter-1.6.3" version="1.1">
|
||||||
|
<trk>
|
||||||
|
<name>brouter_trekking_0</name>
|
||||||
|
<trkseg>
|
||||||
|
<trkpt lon="8.720895" lat="50.002517"></trkpt>
|
||||||
|
<trkpt lon="8.720888" lat="50.002517"></trkpt>
|
||||||
|
<trkpt lon="8.720832" lat="50.002494"></trkpt>
|
||||||
|
<trkpt lon="8.720814" lat="50.002476"></trkpt>
|
||||||
|
<trkpt lon="8.720802" lat="50.002433"></trkpt>
|
||||||
|
<trkpt lon="8.720810" lat="50.002412"></trkpt>
|
||||||
|
<trkpt lon="8.720682" lat="50.002377"></trkpt>
|
||||||
|
<trkpt lon="8.720553" lat="50.002342"></trkpt>
|
||||||
|
<trkpt lon="8.720339" lat="50.002251"></trkpt>
|
||||||
|
<trkpt lon="8.720068" lat="50.002110"></trkpt>
|
||||||
|
<trkpt lon="8.719973" lat="50.002051"></trkpt>
|
||||||
|
<trkpt lon="8.719838" lat="50.001948"></trkpt>
|
||||||
|
<trkpt lon="8.719759" lat="50.001864"></trkpt>
|
||||||
|
<trkpt lon="8.719712" lat="50.001780"></trkpt>
|
||||||
|
<trkpt lon="8.719678" lat="50.001789"></trkpt>
|
||||||
|
<trkpt lon="8.719641" lat="50.001790"></trkpt>
|
||||||
|
<trkpt lon="8.719600" lat="50.001783"></trkpt>
|
||||||
|
<trkpt lon="8.719564" lat="50.001768"></trkpt>
|
||||||
|
<trkpt lon="8.719539" lat="50.001745"></trkpt>
|
||||||
|
<trkpt lon="8.719527" lat="50.001719"></trkpt>
|
||||||
|
<trkpt lon="8.719530" lat="50.001692"></trkpt>
|
||||||
|
<trkpt lon="8.719546" lat="50.001667"></trkpt>
|
||||||
|
<trkpt lon="8.719574" lat="50.001647"></trkpt>
|
||||||
|
<trkpt lon="8.719610" lat="50.001634"></trkpt>
|
||||||
|
<trkpt lon="8.719652" lat="50.001630"></trkpt>
|
||||||
|
<trkpt lon="8.719693" lat="50.001634"></trkpt>
|
||||||
|
<trkpt lon="8.719730" lat="50.001647"></trkpt>
|
||||||
|
<trkpt lon="8.719788" lat="50.001576"></trkpt>
|
||||||
|
<trkpt lon="8.719887" lat="50.001483"></trkpt>
|
||||||
|
<trkpt lon="8.719994" lat="50.001425"></trkpt>
|
||||||
|
<trkpt lon="8.720206" lat="50.001297"></trkpt>
|
||||||
|
<trkpt lon="8.720324" lat="50.001211"></trkpt>
|
||||||
|
<trkpt lon="8.720403" lat="50.001137"></trkpt>
|
||||||
|
<trkpt lon="8.720482" lat="50.001041"></trkpt>
|
||||||
|
<trkpt lon="8.720539" lat="50.000948"></trkpt>
|
||||||
|
<trkpt lon="8.720600" lat="50.000799"></trkpt>
|
||||||
|
<trkpt lon="8.720672" lat="50.000551"></trkpt>
|
||||||
|
<trkpt lon="8.720760" lat="50.000387"></trkpt>
|
||||||
|
<trkpt lon="8.720921" lat="50.000228"></trkpt>
|
||||||
|
<trkpt lon="8.721129" lat="50.000074"></trkpt>
|
||||||
|
<trkpt lon="8.721391" lat="49.999871"></trkpt>
|
||||||
|
<trkpt lon="8.721602" lat="49.999714"></trkpt>
|
||||||
|
<trkpt lon="8.722176" lat="49.999309"></trkpt>
|
||||||
|
<trkpt lon="8.722416" lat="49.999100"></trkpt>
|
||||||
|
<trkpt lon="8.722474" lat="49.999038"></trkpt>
|
||||||
|
<trkpt lon="8.722547" lat="49.998975"></trkpt>
|
||||||
|
<trkpt lon="8.722669" lat="49.998853"></trkpt>
|
||||||
|
<trkpt lon="8.723033" lat="49.998411"></trkpt>
|
||||||
|
<trkpt lon="8.723136" lat="49.998266"></trkpt>
|
||||||
|
<trkpt lon="8.723659" lat="49.997526"></trkpt>
|
||||||
|
<trkpt lon="8.723669" lat="49.997514"></trkpt>
|
||||||
|
</trkseg>
|
||||||
|
</trk>
|
||||||
|
</gpx>
|
||||||
1
brouter-expressions/.gitignore
vendored
1
brouter-expressions/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/build/
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id 'brouter.library-conventions'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
implementation project(':brouter-codec')
|
implementation project(':brouter-codec')
|
||||||
testImplementation 'junit:junit:4.13.1'
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<manifest package="btools.expressions" />
|
|
||||||
|
|
@ -2,22 +2,22 @@ package btools.expressions;
|
||||||
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
final class BExpression
|
final class BExpression {
|
||||||
{
|
|
||||||
private static final int OR_EXP = 10;
|
private static final int OR_EXP = 10;
|
||||||
private static final int AND_EXP = 11;
|
private static final int AND_EXP = 11;
|
||||||
private static final int NOT_EXP = 12;
|
private static final int NOT_EXP = 12;
|
||||||
|
|
||||||
private static final int ADD_EXP = 20;
|
private static final int ADD_EXP = 20;
|
||||||
private static final int MULTIPLY_EXP = 21;
|
private static final int MULTIPLY_EXP = 21;
|
||||||
private static final int MAX_EXP = 22;
|
private static final int DIVIDE_EXP = 22;
|
||||||
private static final int EQUAL_EXP = 23;
|
private static final int MAX_EXP = 23;
|
||||||
private static final int GREATER_EXP = 24;
|
private static final int EQUAL_EXP = 24;
|
||||||
private static final int MIN_EXP = 25;
|
private static final int GREATER_EXP = 25;
|
||||||
|
private static final int MIN_EXP = 26;
|
||||||
|
|
||||||
private static final int SUB_EXP = 26;
|
private static final int SUB_EXP = 27;
|
||||||
private static final int LESSER_EXP = 27;
|
private static final int LESSER_EXP = 28;
|
||||||
private static final int XOR_EXP = 28;
|
private static final int XOR_EXP = 29;
|
||||||
|
|
||||||
private static final int SWITCH_EXP = 30;
|
private static final int SWITCH_EXP = 30;
|
||||||
private static final int ASSIGN_EXP = 31;
|
private static final int ASSIGN_EXP = 31;
|
||||||
|
|
@ -33,154 +33,176 @@ final class BExpression
|
||||||
private BExpression op3;
|
private BExpression op3;
|
||||||
private float numberValue;
|
private float numberValue;
|
||||||
private int variableIdx;
|
private int variableIdx;
|
||||||
private int lookupNameIdx;
|
private int lookupNameIdx = -1;
|
||||||
private int[] lookupValueIdxArray;
|
private int[] lookupValueIdxArray;
|
||||||
|
private boolean doNotChange;
|
||||||
|
|
||||||
// Parse the expression and all subexpression
|
// Parse the expression and all subexpression
|
||||||
public static BExpression parse( BExpressionContext ctx, int level ) throws Exception
|
public static BExpression parse(BExpressionContext ctx, int level) throws Exception {
|
||||||
{
|
|
||||||
return parse(ctx, level, null);
|
return parse(ctx, level, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BExpression parse( BExpressionContext ctx, int level, String optionalToken ) throws Exception
|
private static BExpression parse(BExpressionContext ctx, int level, String optionalToken) throws Exception {
|
||||||
{
|
BExpression e = parseRaw(ctx, level, optionalToken);
|
||||||
|
if (e == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ASSIGN_EXP == e.typ) {
|
||||||
|
// manage assined an injected values
|
||||||
|
BExpression assignedBefore = ctx.lastAssignedExpression.get(e.variableIdx);
|
||||||
|
if (assignedBefore != null && assignedBefore.doNotChange) {
|
||||||
|
e.op1 = assignedBefore; // was injected as key-value
|
||||||
|
e.op1.doNotChange = false; // protect just once, can be changed in second assignement
|
||||||
|
}
|
||||||
|
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
|
||||||
|
}
|
||||||
|
else if (!ctx.skipConstantExpressionOptimizations) {
|
||||||
|
// try to simplify the expression
|
||||||
|
if (VARIABLE_EXP == e.typ) {
|
||||||
|
BExpression ae = ctx.lastAssignedExpression.get(e.variableIdx);
|
||||||
|
if (ae != null && ae.typ == NUMBER_EXP) {
|
||||||
|
e = ae;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BExpression eCollapsed = e.tryCollapse();
|
||||||
|
if (e != eCollapsed) {
|
||||||
|
e = eCollapsed; // allow breakpoint..
|
||||||
|
}
|
||||||
|
BExpression eEvaluated = e.tryEvaluateConstant();
|
||||||
|
if (e != eEvaluated) {
|
||||||
|
e = eEvaluated; // allow breakpoint..
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (level == 0) {
|
||||||
|
// mark the used lookups after the
|
||||||
|
// expression is collapsed to not mark
|
||||||
|
// lookups as used that appear in the profile
|
||||||
|
// but are de-activated by constant expressions
|
||||||
|
int nodeCount = e.markLookupIdxUsed(ctx);
|
||||||
|
ctx.expressionNodeCount += nodeCount;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int markLookupIdxUsed(BExpressionContext ctx) {
|
||||||
|
int nodeCount = 1;
|
||||||
|
if (lookupNameIdx >= 0) {
|
||||||
|
ctx.markLookupIdxUsed(lookupNameIdx);
|
||||||
|
}
|
||||||
|
if (op1 != null) {
|
||||||
|
nodeCount += op1.markLookupIdxUsed(ctx);
|
||||||
|
}
|
||||||
|
if (op2 != null) {
|
||||||
|
nodeCount += op2.markLookupIdxUsed(ctx);
|
||||||
|
}
|
||||||
|
if (op3 != null) {
|
||||||
|
nodeCount += op3.markLookupIdxUsed(ctx);
|
||||||
|
}
|
||||||
|
return nodeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BExpression parseRaw(BExpressionContext ctx, int level, String optionalToken) throws Exception {
|
||||||
|
|
||||||
boolean brackets = false;
|
boolean brackets = false;
|
||||||
String operator = ctx.parseToken();
|
String operator = ctx.parseToken();
|
||||||
if ( optionalToken != null && optionalToken.equals( operator ) )
|
if (optionalToken != null && optionalToken.equals(operator)) {
|
||||||
{
|
|
||||||
operator = ctx.parseToken();
|
operator = ctx.parseToken();
|
||||||
}
|
}
|
||||||
if ( "(".equals( operator ) )
|
if ("(".equals(operator)) {
|
||||||
{
|
|
||||||
brackets = true;
|
brackets = true;
|
||||||
operator = ctx.parseToken();
|
operator = ctx.parseToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( operator == null )
|
if (operator == null) {
|
||||||
{
|
|
||||||
if (level == 0) return null;
|
if (level == 0) return null;
|
||||||
else throw new IllegalArgumentException("unexpected end of file");
|
else throw new IllegalArgumentException("unexpected end of file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( level == 0 )
|
if (level == 0) {
|
||||||
{
|
if (!"assign".equals(operator)) {
|
||||||
if ( !"assign".equals( operator ) )
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("operator " + operator + " is invalid on toplevel (only 'assign' allowed)");
|
throw new IllegalArgumentException("operator " + operator + " is invalid on toplevel (only 'assign' allowed)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BExpression exp = new BExpression();
|
BExpression exp = new BExpression();
|
||||||
int nops = 3;
|
int nops = 3;
|
||||||
|
|
||||||
boolean ifThenElse = false;
|
boolean ifThenElse = false;
|
||||||
|
|
||||||
if ( "switch".equals( operator ) )
|
if ("switch".equals(operator)) {
|
||||||
{
|
|
||||||
exp.typ = SWITCH_EXP;
|
exp.typ = SWITCH_EXP;
|
||||||
}
|
} else if ("if".equals(operator)) {
|
||||||
else if ( "if".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = SWITCH_EXP;
|
exp.typ = SWITCH_EXP;
|
||||||
ifThenElse = true;
|
ifThenElse = true;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
nops = 2; // check binary expressions
|
nops = 2; // check binary expressions
|
||||||
|
|
||||||
if ( "or".equals( operator ) )
|
if ("or".equals(operator)) {
|
||||||
{
|
|
||||||
exp.typ = OR_EXP;
|
exp.typ = OR_EXP;
|
||||||
}
|
} else if ("and".equals(operator)) {
|
||||||
else if ( "and".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = AND_EXP;
|
exp.typ = AND_EXP;
|
||||||
}
|
} else if ("multiply".equals(operator)) {
|
||||||
else if ( "multiply".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = MULTIPLY_EXP;
|
exp.typ = MULTIPLY_EXP;
|
||||||
}
|
} else if ("divide".equals(operator)) {
|
||||||
else if ( "add".equals( operator ) )
|
exp.typ = DIVIDE_EXP;
|
||||||
{
|
} else if ("add".equals(operator)) {
|
||||||
exp.typ = ADD_EXP;
|
exp.typ = ADD_EXP;
|
||||||
}
|
} else if ("max".equals(operator)) {
|
||||||
else if ( "max".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = MAX_EXP;
|
exp.typ = MAX_EXP;
|
||||||
}
|
} else if ("min".equals(operator)) {
|
||||||
else if ( "min".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = MIN_EXP;
|
exp.typ = MIN_EXP;
|
||||||
}
|
} else if ("equal".equals(operator)) {
|
||||||
else if ( "equal".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = EQUAL_EXP;
|
exp.typ = EQUAL_EXP;
|
||||||
}
|
} else if ("greater".equals(operator)) {
|
||||||
else if ( "greater".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = GREATER_EXP;
|
exp.typ = GREATER_EXP;
|
||||||
}
|
} else if ("sub".equals(operator)) {
|
||||||
else if ( "sub".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = SUB_EXP;
|
exp.typ = SUB_EXP;
|
||||||
}
|
} else if ("lesser".equals(operator)) {
|
||||||
else if ( "lesser".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = LESSER_EXP;
|
exp.typ = LESSER_EXP;
|
||||||
}
|
} else if ("xor".equals(operator)) {
|
||||||
else if ( "xor".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.typ = XOR_EXP;
|
exp.typ = XOR_EXP;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
nops = 1; // check unary expressions
|
nops = 1; // check unary expressions
|
||||||
if ( "assign".equals( operator ) )
|
if ("assign".equals(operator)) {
|
||||||
{
|
|
||||||
if (level > 0) throw new IllegalArgumentException("assign operator within expression");
|
if (level > 0) throw new IllegalArgumentException("assign operator within expression");
|
||||||
exp.typ = ASSIGN_EXP;
|
exp.typ = ASSIGN_EXP;
|
||||||
String variable = ctx.parseToken();
|
String variable = ctx.parseToken();
|
||||||
if (variable == null) throw new IllegalArgumentException("unexpected end of file");
|
if (variable == null) throw new IllegalArgumentException("unexpected end of file");
|
||||||
if ( variable.indexOf( '=' ) >= 0 ) throw new IllegalArgumentException( "variable name cannot contain '=': " + variable );
|
if (variable.indexOf('=') >= 0)
|
||||||
if ( variable.indexOf( ':' ) >= 0 ) throw new IllegalArgumentException( "cannot assign context-prefixed variable: " + variable );
|
throw new IllegalArgumentException("variable name cannot contain '=': " + variable);
|
||||||
|
if (variable.indexOf(':') >= 0)
|
||||||
|
throw new IllegalArgumentException("cannot assign context-prefixed variable: " + variable);
|
||||||
exp.variableIdx = ctx.getVariableIdx(variable, true);
|
exp.variableIdx = ctx.getVariableIdx(variable, true);
|
||||||
if ( exp.variableIdx < ctx.getMinWriteIdx() ) throw new IllegalArgumentException( "cannot assign to readonly variable " + variable );
|
if (exp.variableIdx < ctx.getMinWriteIdx())
|
||||||
}
|
throw new IllegalArgumentException("cannot assign to readonly variable " + variable);
|
||||||
else if ( "not".equals( operator ) )
|
} else if ("not".equals(operator)) {
|
||||||
{
|
|
||||||
exp.typ = NOT_EXP;
|
exp.typ = NOT_EXP;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
nops = 0; // check elemantary expressions
|
nops = 0; // check elemantary expressions
|
||||||
int idx = operator.indexOf('=');
|
int idx = operator.indexOf('=');
|
||||||
if ( idx >= 0 )
|
if (idx >= 0) {
|
||||||
{
|
|
||||||
exp.typ = LOOKUP_EXP;
|
exp.typ = LOOKUP_EXP;
|
||||||
String name = operator.substring(0, idx);
|
String name = operator.substring(0, idx);
|
||||||
String values = operator.substring(idx + 1);
|
String values = operator.substring(idx + 1);
|
||||||
|
|
||||||
exp.lookupNameIdx = ctx.getLookupNameIdx(name);
|
exp.lookupNameIdx = ctx.getLookupNameIdx(name);
|
||||||
if ( exp.lookupNameIdx < 0 )
|
if (exp.lookupNameIdx < 0) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("unknown lookup name: " + name);
|
throw new IllegalArgumentException("unknown lookup name: " + name);
|
||||||
}
|
}
|
||||||
ctx.markLookupIdxUsed( exp.lookupNameIdx );
|
|
||||||
StringTokenizer tk = new StringTokenizer(values, "|");
|
StringTokenizer tk = new StringTokenizer(values, "|");
|
||||||
int nt = tk.countTokens();
|
int nt = tk.countTokens();
|
||||||
int nt2 = nt == 0 ? 1 : nt;
|
int nt2 = nt == 0 ? 1 : nt;
|
||||||
exp.lookupValueIdxArray = new int[nt2];
|
exp.lookupValueIdxArray = new int[nt2];
|
||||||
for( int ti=0; ti<nt2; ti++ )
|
for (int ti = 0; ti < nt2; ti++) {
|
||||||
{
|
|
||||||
String value = ti < nt ? tk.nextToken() : "";
|
String value = ti < nt ? tk.nextToken() : "";
|
||||||
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx(exp.lookupNameIdx, value);
|
exp.lookupValueIdxArray[ti] = ctx.getLookupValueIdx(exp.lookupNameIdx, value);
|
||||||
if ( exp.lookupValueIdxArray[ti] < 0 )
|
if (exp.lookupValueIdxArray[ti] < 0) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("unknown lookup value: " + value);
|
throw new IllegalArgumentException("unknown lookup value: " + value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if ((idx = operator.indexOf(':')) >= 0) {
|
||||||
else if ( ( idx = operator.indexOf( ':' ) ) >= 0 )
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
use of variable values
|
use of variable values
|
||||||
assign no_height
|
assign no_height
|
||||||
|
|
@ -198,31 +220,20 @@ final class BExpression
|
||||||
exp.typ = FOREIGN_VARIABLE_EXP;
|
exp.typ = FOREIGN_VARIABLE_EXP;
|
||||||
exp.variableIdx = ctx.getForeignVariableIdx(context, varname);
|
exp.variableIdx = ctx.getForeignVariableIdx(context, varname);
|
||||||
}
|
}
|
||||||
}
|
} else if ((idx = ctx.getVariableIdx(operator, false)) >= 0) {
|
||||||
else if ( (idx = ctx.getVariableIdx( operator, false )) >= 0 )
|
|
||||||
{
|
|
||||||
exp.typ = VARIABLE_EXP;
|
exp.typ = VARIABLE_EXP;
|
||||||
exp.variableIdx = idx;
|
exp.variableIdx = idx;
|
||||||
}
|
} else if ("true".equals(operator)) {
|
||||||
else if ( "true".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.numberValue = 1.f;
|
exp.numberValue = 1.f;
|
||||||
exp.typ = NUMBER_EXP;
|
exp.typ = NUMBER_EXP;
|
||||||
}
|
} else if ("false".equals(operator)) {
|
||||||
else if ( "false".equals( operator ) )
|
|
||||||
{
|
|
||||||
exp.numberValue = 0.f;
|
exp.numberValue = 0.f;
|
||||||
exp.typ = NUMBER_EXP;
|
exp.typ = NUMBER_EXP;
|
||||||
}
|
} else {
|
||||||
else
|
try {
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
exp.numberValue = Float.parseFloat(operator);
|
exp.numberValue = Float.parseFloat(operator);
|
||||||
exp.typ = NUMBER_EXP;
|
exp.typ = NUMBER_EXP;
|
||||||
}
|
} catch (NumberFormatException nfe) {
|
||||||
catch( NumberFormatException nfe )
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("unknown expression: " + operator);
|
throw new IllegalArgumentException("unknown expression: " + operator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,71 +241,166 @@ final class BExpression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// parse operands
|
// parse operands
|
||||||
if ( nops > 0 )
|
if (nops > 0) {
|
||||||
{
|
exp.op1 = parse(ctx, level + 1, exp.typ == ASSIGN_EXP ? "=" : null);
|
||||||
exp.op1 = BExpression.parse( ctx, level+1, exp.typ == ASSIGN_EXP ? "=" : null );
|
|
||||||
}
|
}
|
||||||
if ( nops > 1 )
|
if (nops > 1) {
|
||||||
{
|
|
||||||
if (ifThenElse) checkExpectedToken(ctx, "then");
|
if (ifThenElse) checkExpectedToken(ctx, "then");
|
||||||
exp.op2 = BExpression.parse( ctx, level+1, null );
|
exp.op2 = parse(ctx, level + 1, null);
|
||||||
}
|
}
|
||||||
if ( nops > 2 )
|
if (nops > 2) {
|
||||||
{
|
|
||||||
if (ifThenElse) checkExpectedToken(ctx, "else");
|
if (ifThenElse) checkExpectedToken(ctx, "else");
|
||||||
exp.op3 = BExpression.parse( ctx, level+1, null );
|
exp.op3 = parse(ctx, level + 1, null);
|
||||||
}
|
}
|
||||||
if ( brackets )
|
if (brackets) {
|
||||||
{
|
|
||||||
checkExpectedToken(ctx, ")");
|
checkExpectedToken(ctx, ")");
|
||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkExpectedToken( BExpressionContext ctx, String expected ) throws Exception
|
private static void checkExpectedToken(BExpressionContext ctx, String expected) throws Exception {
|
||||||
{
|
|
||||||
String token = ctx.parseToken();
|
String token = ctx.parseToken();
|
||||||
if ( ! expected.equals( token ) )
|
if (!expected.equals(token)) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("unexpected token: " + token + ", expected: " + expected);
|
throw new IllegalArgumentException("unexpected token: " + token + ", expected: " + expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the expression
|
// Evaluate the expression
|
||||||
public float evaluate( BExpressionContext ctx )
|
public float evaluate(BExpressionContext ctx) {
|
||||||
{
|
switch (typ) {
|
||||||
switch( typ )
|
case OR_EXP:
|
||||||
{
|
return op1.evaluate(ctx) != 0.f ? 1.f : (op2.evaluate(ctx) != 0.f ? 1.f : 0.f);
|
||||||
case OR_EXP: return op1.evaluate(ctx) != 0.f ? 1.f : ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f );
|
case XOR_EXP:
|
||||||
case XOR_EXP: return ( (op1.evaluate(ctx) != 0.f) ^ ( op2.evaluate(ctx) != 0.f ) ? 1.f : 0.f );
|
return ((op1.evaluate(ctx) != 0.f) ^ (op2.evaluate(ctx) != 0.f) ? 1.f : 0.f);
|
||||||
case AND_EXP: return op1.evaluate(ctx) != 0.f ? ( op2.evaluate(ctx) != 0.f ? 1.f : 0.f ) : 0.f;
|
case AND_EXP:
|
||||||
case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx);
|
return op1.evaluate(ctx) != 0.f ? (op2.evaluate(ctx) != 0.f ? 1.f : 0.f) : 0.f;
|
||||||
case SUB_EXP: return op1.evaluate(ctx) - op2.evaluate(ctx);
|
case ADD_EXP:
|
||||||
case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx);
|
return op1.evaluate(ctx) + op2.evaluate(ctx);
|
||||||
case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) );
|
case SUB_EXP:
|
||||||
case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) );
|
return op1.evaluate(ctx) - op2.evaluate(ctx);
|
||||||
case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
|
case MULTIPLY_EXP:
|
||||||
case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
|
return op1.evaluate(ctx) * op2.evaluate(ctx);
|
||||||
case LESSER_EXP: return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
|
case DIVIDE_EXP:
|
||||||
case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
|
return divide(op1.evaluate(ctx), op2.evaluate(ctx));
|
||||||
case ASSIGN_EXP: return ctx.assign( variableIdx, op1.evaluate(ctx) );
|
case MAX_EXP:
|
||||||
case LOOKUP_EXP: return ctx.getLookupMatch( lookupNameIdx, lookupValueIdxArray );
|
return max(op1.evaluate(ctx), op2.evaluate(ctx));
|
||||||
case NUMBER_EXP: return numberValue;
|
case MIN_EXP:
|
||||||
case VARIABLE_EXP: return ctx.getVariableValue( variableIdx );
|
return min(op1.evaluate(ctx), op2.evaluate(ctx));
|
||||||
case FOREIGN_VARIABLE_EXP: return ctx.getForeignVariableValue( variableIdx );
|
case EQUAL_EXP:
|
||||||
case VARIABLE_GET_EXP: return ctx.getLookupValue(lookupNameIdx);
|
return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
|
||||||
case NOT_EXP: return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
|
case GREATER_EXP:
|
||||||
default: throw new IllegalArgumentException( "unknown op-code: " + typ );
|
return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
|
||||||
|
case LESSER_EXP:
|
||||||
|
return op1.evaluate(ctx) < op2.evaluate(ctx) ? 1.f : 0.f;
|
||||||
|
case SWITCH_EXP:
|
||||||
|
return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
|
||||||
|
case ASSIGN_EXP:
|
||||||
|
return ctx.assign(variableIdx, op1.evaluate(ctx));
|
||||||
|
case LOOKUP_EXP:
|
||||||
|
return ctx.getLookupMatch(lookupNameIdx, lookupValueIdxArray);
|
||||||
|
case NUMBER_EXP:
|
||||||
|
return numberValue;
|
||||||
|
case VARIABLE_EXP:
|
||||||
|
return ctx.getVariableValue(variableIdx);
|
||||||
|
case FOREIGN_VARIABLE_EXP:
|
||||||
|
return ctx.getForeignVariableValue(variableIdx);
|
||||||
|
case VARIABLE_GET_EXP:
|
||||||
|
return ctx.getLookupValue(lookupNameIdx);
|
||||||
|
case NOT_EXP:
|
||||||
|
return op1.evaluate(ctx) == 0.f ? 1.f : 0.f;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("unknown op-code: " + typ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float max( float v1, float v2 )
|
// Try to collapse the expression
|
||||||
{
|
// if logically possible
|
||||||
|
private BExpression tryCollapse() {
|
||||||
|
switch (typ) {
|
||||||
|
case OR_EXP:
|
||||||
|
return NUMBER_EXP == op1.typ ?
|
||||||
|
(op1.numberValue != 0.f ? op1 : op2)
|
||||||
|
: (NUMBER_EXP == op2.typ ?
|
||||||
|
(op2.numberValue != 0.f ? op2 : op1)
|
||||||
|
: this);
|
||||||
|
case AND_EXP:
|
||||||
|
return NUMBER_EXP == op1.typ ?
|
||||||
|
(op1.numberValue == 0.f ? op1 : op2)
|
||||||
|
: (NUMBER_EXP == op2.typ ?
|
||||||
|
(op2.numberValue == 0.f ? op2 : op1)
|
||||||
|
: this);
|
||||||
|
case ADD_EXP:
|
||||||
|
return NUMBER_EXP == op1.typ ?
|
||||||
|
(op1.numberValue == 0.f ? op2 : this)
|
||||||
|
: (NUMBER_EXP == op2.typ ?
|
||||||
|
(op2.numberValue == 0.f ? op1 : this)
|
||||||
|
: this);
|
||||||
|
case SWITCH_EXP:
|
||||||
|
return NUMBER_EXP == op1.typ ?
|
||||||
|
(op1.numberValue == 0.f ? op3 : op2) : this;
|
||||||
|
default:
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to evaluate the expression
|
||||||
|
// if all operands are constant
|
||||||
|
private BExpression tryEvaluateConstant() {
|
||||||
|
if (op1 != null && NUMBER_EXP == op1.typ
|
||||||
|
&& (op2 == null || NUMBER_EXP == op2.typ)
|
||||||
|
&& (op3 == null || NUMBER_EXP == op3.typ)) {
|
||||||
|
BExpression exp = new BExpression();
|
||||||
|
exp.typ = NUMBER_EXP;
|
||||||
|
exp.numberValue = evaluate(null);
|
||||||
|
return exp;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float max(float v1, float v2) {
|
||||||
return v1 > v2 ? v1 : v2;
|
return v1 > v2 ? v1 : v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float min( float v1, float v2 )
|
private float min(float v1, float v2) {
|
||||||
{
|
|
||||||
return v1 < v2 ? v1 : v2;
|
return v1 < v2 ? v1 : v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float divide(float v1, float v2) {
|
||||||
|
if (v2 == 0f) throw new IllegalArgumentException("div by zero");
|
||||||
|
return v1 / v2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (typ == NUMBER_EXP) {
|
||||||
|
return "" + numberValue;
|
||||||
|
}
|
||||||
|
if (typ == VARIABLE_EXP) {
|
||||||
|
return "vidx=" + variableIdx;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder("typ=" + typ + " ops=(");
|
||||||
|
addOp(sb, op1);
|
||||||
|
addOp(sb, op2);
|
||||||
|
addOp(sb, op3);
|
||||||
|
sb.append(')');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOp(StringBuilder sb, BExpression e) {
|
||||||
|
if (e != null) {
|
||||||
|
sb.append('[').append(e.toString()).append(']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BExpression createAssignExpressionFromKeyValue(BExpressionContext ctx, String key, String value) {
|
||||||
|
BExpression e = new BExpression();
|
||||||
|
e.typ = ASSIGN_EXP;
|
||||||
|
e.variableIdx = ctx.getVariableIdx(key, true);
|
||||||
|
e.op1 = new BExpression();
|
||||||
|
e.op1.typ = NUMBER_EXP;
|
||||||
|
e.op1.numberValue = Float.parseFloat(value);
|
||||||
|
e.op1.doNotChange = true;
|
||||||
|
ctx.lastAssignedExpression.set(e.variableIdx, e.op1);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,22 +7,20 @@
|
||||||
package btools.expressions;
|
package btools.expressions;
|
||||||
|
|
||||||
|
|
||||||
|
public final class BExpressionContextNode extends BExpressionContext {
|
||||||
public final class BExpressionContextNode extends BExpressionContext
|
|
||||||
{
|
|
||||||
private static String[] buildInVariables =
|
private static String[] buildInVariables =
|
||||||
{"initialcost"};
|
{"initialcost"};
|
||||||
|
|
||||||
protected String[] getBuildInVariableNames()
|
protected String[] getBuildInVariableNames() {
|
||||||
{
|
|
||||||
return buildInVariables;
|
return buildInVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getInitialcost() { return getBuildInVariable(0); }
|
public float getInitialcost() {
|
||||||
|
return getBuildInVariable(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public BExpressionContextNode( BExpressionMetaData meta )
|
public BExpressionContextNode(BExpressionMetaData meta) {
|
||||||
{
|
|
||||||
super("node", meta);
|
super("node", meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,8 +29,7 @@ public final class BExpressionContextNode extends BExpressionContext
|
||||||
*
|
*
|
||||||
* @param hashSize size of hashmap for result caching
|
* @param hashSize size of hashmap for result caching
|
||||||
*/
|
*/
|
||||||
public BExpressionContextNode( int hashSize, BExpressionMetaData meta )
|
public BExpressionContextNode(int hashSize, BExpressionMetaData meta) {
|
||||||
{
|
|
||||||
super("node", hashSize, meta);
|
super("node", hashSize, meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,33 +8,97 @@ package btools.expressions;
|
||||||
|
|
||||||
import btools.codec.TagValueValidator;
|
import btools.codec.TagValueValidator;
|
||||||
|
|
||||||
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator
|
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator {
|
||||||
{
|
|
||||||
private boolean decodeForbidden = true;
|
private boolean decodeForbidden = true;
|
||||||
|
|
||||||
private static String[] buildInVariables =
|
private static String[] buildInVariables =
|
||||||
{ "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed" };
|
{"costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask", "maxspeed", "uphillcost", "downhillcost", "uphillcutoff", "downhillcutoff", "uphillmaxslope", "downhillmaxslope", "uphillmaxslopecost", "downhillmaxslopecost"};
|
||||||
|
|
||||||
protected String[] getBuildInVariableNames()
|
protected String[] getBuildInVariableNames() {
|
||||||
{
|
|
||||||
return buildInVariables;
|
return buildInVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getCostfactor() { return getBuildInVariable(0); }
|
public float getCostfactor() {
|
||||||
public float getTurncost() { return getBuildInVariable(1); }
|
return getBuildInVariable(0);
|
||||||
public float getUphillCostfactor() { return getBuildInVariable(2); }
|
}
|
||||||
public float getDownhillCostfactor() { return getBuildInVariable(3); }
|
|
||||||
public float getInitialcost() { return getBuildInVariable(4); }
|
|
||||||
public float getNodeAccessGranted() { return getBuildInVariable(5); }
|
|
||||||
public float getInitialClassifier() { return getBuildInVariable(6); }
|
|
||||||
public float getTrafficSourceDensity() { return getBuildInVariable(7); }
|
|
||||||
public float getIsTrafficBackbone() { return getBuildInVariable(8); }
|
|
||||||
public float getPriorityClassifier() { return getBuildInVariable(9); }
|
|
||||||
public float getClassifierMask() { return getBuildInVariable(10); }
|
|
||||||
public float getMaxspeed() { return getBuildInVariable(11); }
|
|
||||||
|
|
||||||
public BExpressionContextWay( BExpressionMetaData meta )
|
public float getTurncost() {
|
||||||
{
|
return getBuildInVariable(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getUphillCostfactor() {
|
||||||
|
return getBuildInVariable(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDownhillCostfactor() {
|
||||||
|
return getBuildInVariable(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getInitialcost() {
|
||||||
|
return getBuildInVariable(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getNodeAccessGranted() {
|
||||||
|
return getBuildInVariable(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getInitialClassifier() {
|
||||||
|
return getBuildInVariable(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getTrafficSourceDensity() {
|
||||||
|
return getBuildInVariable(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getIsTrafficBackbone() {
|
||||||
|
return getBuildInVariable(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPriorityClassifier() {
|
||||||
|
return getBuildInVariable(9);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getClassifierMask() {
|
||||||
|
return getBuildInVariable(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getMaxspeed() {
|
||||||
|
return getBuildInVariable(11);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getUphillcost() {
|
||||||
|
return getBuildInVariable(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDownhillcost() {
|
||||||
|
return getBuildInVariable(13);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getUphillcutoff() {
|
||||||
|
return getBuildInVariable(14);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDownhillcutoff() {
|
||||||
|
return getBuildInVariable(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getUphillmaxslope() {
|
||||||
|
return getBuildInVariable(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDownhillmaxslope() {
|
||||||
|
return getBuildInVariable(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getUphillmaxslopecost() {
|
||||||
|
return getBuildInVariable(18);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getDownhillmaxslopecost() {
|
||||||
|
return getBuildInVariable(19);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BExpressionContextWay(BExpressionMetaData meta) {
|
||||||
super("way", meta);
|
super("way", meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,22 +107,18 @@ public final class BExpressionContextWay extends BExpressionContext implements T
|
||||||
*
|
*
|
||||||
* @param hashSize size of hashmap for result caching
|
* @param hashSize size of hashmap for result caching
|
||||||
*/
|
*/
|
||||||
public BExpressionContextWay( int hashSize, BExpressionMetaData meta )
|
public BExpressionContextWay(int hashSize, BExpressionMetaData meta) {
|
||||||
{
|
|
||||||
super("way", hashSize, meta);
|
super("way", hashSize, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int accessType( byte[] description )
|
public int accessType(byte[] description) {
|
||||||
{
|
|
||||||
evaluate(false, description);
|
evaluate(false, description);
|
||||||
float minCostFactor = getCostfactor();
|
float minCostFactor = getCostfactor();
|
||||||
if ( minCostFactor >= 9999.f )
|
if (minCostFactor >= 9999.f) {
|
||||||
{
|
|
||||||
setInverseVars();
|
setInverseVars();
|
||||||
float reverseCostFactor = getCostfactor();
|
float reverseCostFactor = getCostfactor();
|
||||||
if ( reverseCostFactor < minCostFactor )
|
if (reverseCostFactor < minCostFactor) {
|
||||||
{
|
|
||||||
minCostFactor = reverseCostFactor;
|
minCostFactor = reverseCostFactor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,8 +126,7 @@ public final class BExpressionContextWay extends BExpressionContext implements T
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDecodeForbidden( boolean decodeForbidden )
|
public void setDecodeForbidden(boolean decodeForbidden) {
|
||||||
{
|
|
||||||
this.decodeForbidden = decodeForbidden;
|
this.decodeForbidden = decodeForbidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* A lookup value with optional aliases
|
* A lookup value with optional aliases
|
||||||
*
|
* <p>
|
||||||
* toString just gives the primary value,
|
* toString just gives the primary value,
|
||||||
* equals just compares against primary value
|
* equals just compares against primary value
|
||||||
* matches() also compares aliases
|
* matches() also compares aliases
|
||||||
|
|
@ -10,39 +10,33 @@
|
||||||
package btools.expressions;
|
package btools.expressions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
final class BExpressionLookupValue
|
final class BExpressionLookupValue {
|
||||||
{
|
|
||||||
String value;
|
String value;
|
||||||
ArrayList<String> aliases;
|
List<String> aliases;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString() {
|
||||||
{
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BExpressionLookupValue( String value )
|
public BExpressionLookupValue(String value) {
|
||||||
{
|
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAlias( String alias )
|
public void addAlias(String alias) {
|
||||||
{
|
if (aliases == null) aliases = new ArrayList<>();
|
||||||
if ( aliases == null ) aliases = new ArrayList<String>();
|
|
||||||
aliases.add(alias);
|
aliases.add(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals( Object o )
|
public boolean equals(Object o) {
|
||||||
{
|
if (o instanceof String) {
|
||||||
if ( o instanceof String )
|
|
||||||
{
|
|
||||||
String v = (String) o;
|
String v = (String) o;
|
||||||
return value.equals(v);
|
return value.equals(v);
|
||||||
}
|
}
|
||||||
if ( o instanceof BExpressionLookupValue )
|
if (o instanceof BExpressionLookupValue) {
|
||||||
{
|
|
||||||
BExpressionLookupValue v = (BExpressionLookupValue) o;
|
BExpressionLookupValue v = (BExpressionLookupValue) o;
|
||||||
|
|
||||||
return value.equals(v.value);
|
return value.equals(v.value);
|
||||||
|
|
@ -50,13 +44,10 @@ final class BExpressionLookupValue
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean matches( String s )
|
public boolean matches(String s) {
|
||||||
{
|
|
||||||
if (value.equals(s)) return true;
|
if (value.equals(s)) return true;
|
||||||
if ( aliases != null )
|
if (aliases != null) {
|
||||||
{
|
for (String alias : aliases) {
|
||||||
for( String alias : aliases )
|
|
||||||
{
|
|
||||||
if (alias.equals(s)) return true;
|
if (alias.equals(s)) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,80 +9,66 @@ package btools.expressions;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import btools.util.BitCoderContext;
|
|
||||||
import btools.util.Crc32;
|
|
||||||
|
|
||||||
|
|
||||||
public final class BExpressionMetaData
|
public final class BExpressionMetaData {
|
||||||
{
|
|
||||||
private static final String CONTEXT_TAG = "---context:";
|
private static final String CONTEXT_TAG = "---context:";
|
||||||
private static final String VERSION_TAG = "---lookupversion:";
|
private static final String VERSION_TAG = "---lookupversion:";
|
||||||
private static final String MINOR_VERSION_TAG = "---minorversion:";
|
private static final String MINOR_VERSION_TAG = "---minorversion:";
|
||||||
private static final String VARLENGTH_TAG = "---readvarlength";
|
private static final String VARLENGTH_TAG = "---readvarlength";
|
||||||
|
private static final String MIN_APP_VERSION_TAG = "---minappversion:";
|
||||||
|
|
||||||
public short lookupVersion = -1;
|
public short lookupVersion = -1;
|
||||||
public short lookupMinorVersion = -1;
|
public short lookupMinorVersion = -1;
|
||||||
|
public short minAppVersion = -1;
|
||||||
|
|
||||||
private HashMap<String,BExpressionContext> listeners = new HashMap<String,BExpressionContext>();
|
private Map<String, BExpressionContext> listeners = new HashMap<>();
|
||||||
|
|
||||||
public void registerListener( String context, BExpressionContext ctx )
|
public void registerListener(String context, BExpressionContext ctx) {
|
||||||
{
|
|
||||||
listeners.put(context, ctx);
|
listeners.put(context, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readMetaData( File lookupsFile )
|
public void readMetaData(File lookupsFile) {
|
||||||
{
|
try {
|
||||||
try
|
|
||||||
{
|
|
||||||
BufferedReader br = new BufferedReader(new FileReader(lookupsFile));
|
BufferedReader br = new BufferedReader(new FileReader(lookupsFile));
|
||||||
|
|
||||||
BExpressionContext ctx = null;
|
BExpressionContext ctx = null;
|
||||||
|
|
||||||
for(;;)
|
for (; ; ) {
|
||||||
{
|
|
||||||
String line = br.readLine();
|
String line = br.readLine();
|
||||||
if (line == null) break;
|
if (line == null) break;
|
||||||
line = line.trim();
|
line = line.trim();
|
||||||
if (line.length() == 0 || line.startsWith("#")) continue;
|
if (line.length() == 0 || line.startsWith("#")) continue;
|
||||||
if ( line.startsWith( CONTEXT_TAG ) )
|
if (line.startsWith(CONTEXT_TAG)) {
|
||||||
{
|
|
||||||
ctx = listeners.get(line.substring(CONTEXT_TAG.length()));
|
ctx = listeners.get(line.substring(CONTEXT_TAG.length()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( line.startsWith( VERSION_TAG ) )
|
if (line.startsWith(VERSION_TAG)) {
|
||||||
{
|
|
||||||
lookupVersion = Short.parseShort(line.substring(VERSION_TAG.length()));
|
lookupVersion = Short.parseShort(line.substring(VERSION_TAG.length()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( line.startsWith( MINOR_VERSION_TAG ) )
|
if (line.startsWith(MINOR_VERSION_TAG)) {
|
||||||
{
|
|
||||||
lookupMinorVersion = Short.parseShort(line.substring(MINOR_VERSION_TAG.length()));
|
lookupMinorVersion = Short.parseShort(line.substring(MINOR_VERSION_TAG.length()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( line.startsWith( VARLENGTH_TAG ) ) // tag removed...
|
if (line.startsWith(MIN_APP_VERSION_TAG)) {
|
||||||
{
|
minAppVersion = Short.parseShort(line.substring(MIN_APP_VERSION_TAG.length()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.startsWith(VARLENGTH_TAG)) { // tag removed...
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ctx != null) ctx.parseMetaLine(line);
|
if (ctx != null) ctx.parseMetaLine(line);
|
||||||
}
|
}
|
||||||
br.close();
|
br.close();
|
||||||
|
|
||||||
for( BExpressionContext c : listeners.values() )
|
for (BExpressionContext c : listeners.values()) {
|
||||||
{
|
|
||||||
c.finishMetaParsing();
|
c.finishMetaParsing();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,27 +4,22 @@ import java.util.Arrays;
|
||||||
|
|
||||||
import btools.util.LruMapNode;
|
import btools.util.LruMapNode;
|
||||||
|
|
||||||
public final class CacheNode extends LruMapNode
|
public final class CacheNode extends LruMapNode {
|
||||||
{
|
|
||||||
byte[] ab;
|
byte[] ab;
|
||||||
float[] vars;
|
float[] vars;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode()
|
public int hashCode() {
|
||||||
{
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals( Object o )
|
public boolean equals(Object o) {
|
||||||
{
|
|
||||||
CacheNode n = (CacheNode) o;
|
CacheNode n = (CacheNode) o;
|
||||||
if ( hash != n.hash )
|
if (hash != n.hash) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ( ab == null )
|
if (ab == null) {
|
||||||
{
|
|
||||||
return true; // hack: null = crc match only
|
return true; // hack: null = crc match only
|
||||||
}
|
}
|
||||||
return Arrays.equals(ab, n.ab);
|
return Arrays.equals(ab, n.ab);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package btools.expressions;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class IntegrityCheckProfile {
|
||||||
|
|
||||||
|
public static void main(final String[] args) {
|
||||||
|
if (args.length != 2) {
|
||||||
|
System.out.println("usage: java IntegrityCheckProfile <lookup-file> <profile-folder>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IntegrityCheckProfile test = new IntegrityCheckProfile();
|
||||||
|
try {
|
||||||
|
File lookupFile = new File(args[0]);
|
||||||
|
File profileDir = new File(args[1]);
|
||||||
|
test.integrityTestProfiles(lookupFile, profileDir);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void integrityTestProfiles(File lookupFile, File profileDir) {
|
||||||
|
File[] files = profileDir.listFiles();
|
||||||
|
|
||||||
|
if (files == null) {
|
||||||
|
System.err.println("no files " + profileDir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!lookupFile.exists()) {
|
||||||
|
System.err.println("no lookup file " + lookupFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (File f : files) {
|
||||||
|
if (f.getName().endsWith(".brf")) {
|
||||||
|
BExpressionMetaData meta = new BExpressionMetaData();
|
||||||
|
BExpressionContext expctxWay = new BExpressionContextWay(meta);
|
||||||
|
BExpressionContext expctxNode = new BExpressionContextNode(meta);
|
||||||
|
meta.readMetaData(lookupFile);
|
||||||
|
expctxNode.setForeignContext(expctxWay);
|
||||||
|
expctxWay.parseFile(f, "global");
|
||||||
|
expctxNode.parseFile(f, "global");
|
||||||
|
System.out.println("test " + meta.lookupVersion + "." + meta.lookupMinorVersion + " " + f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -3,12 +3,9 @@ package btools.expressions;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public final class ProfileComparator
|
public final class ProfileComparator {
|
||||||
{
|
public static void main(String[] args) {
|
||||||
public static void main( String[] args )
|
if (args.length != 4) {
|
||||||
{
|
|
||||||
if ( args.length != 4 )
|
|
||||||
{
|
|
||||||
System.out.println("usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>");
|
System.out.println("usage: java ProfileComparator <lookup-file> <profile1> <profile2> <nsamples>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -22,21 +19,29 @@ public final class ProfileComparator
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testContext( File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext )
|
private static void testContext(File lookupFile, File profile1File, File profile2File, int nsamples, boolean nodeContext) {
|
||||||
{
|
|
||||||
// read lookup.dat + profiles
|
// read lookup.dat + profiles
|
||||||
BExpressionMetaData meta1 = new BExpressionMetaData();
|
BExpressionMetaData meta1 = new BExpressionMetaData();
|
||||||
BExpressionMetaData meta2 = new BExpressionMetaData();
|
BExpressionMetaData meta2 = new BExpressionMetaData();
|
||||||
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode(meta1) : new BExpressionContextWay(meta1);
|
BExpressionContext expctx1 = nodeContext ? new BExpressionContextNode(meta1) : new BExpressionContextWay(meta1);
|
||||||
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode(meta2) : new BExpressionContextWay(meta2);
|
BExpressionContext expctx2 = nodeContext ? new BExpressionContextNode(meta2) : new BExpressionContextWay(meta2);
|
||||||
|
|
||||||
|
// if same profiles, compare different optimization levels
|
||||||
|
if (profile1File.getName().equals(profile2File.getName())) {
|
||||||
|
expctx2.skipConstantExpressionOptimizations = true;
|
||||||
|
}
|
||||||
|
|
||||||
meta1.readMetaData(lookupFile);
|
meta1.readMetaData(lookupFile);
|
||||||
meta2.readMetaData(lookupFile);
|
meta2.readMetaData(lookupFile);
|
||||||
expctx1.parseFile(profile1File, "global");
|
expctx1.parseFile(profile1File, "global");
|
||||||
|
System.out.println("usedTags1=" + expctx1.usedTagList());
|
||||||
expctx2.parseFile(profile2File, "global");
|
expctx2.parseFile(profile2File, "global");
|
||||||
|
System.out.println("usedTags2=" + expctx2.usedTagList());
|
||||||
|
|
||||||
|
System.out.println("nodeContext=" + nodeContext + " nodeCount1=" + expctx1.expressionNodeCount + " nodeCount2=" + expctx2.expressionNodeCount);
|
||||||
|
|
||||||
Random rnd = new Random();
|
Random rnd = new Random();
|
||||||
for( int i=0; i<nsamples; i++ )
|
for (int i = 0; i < nsamples; i++) {
|
||||||
{
|
|
||||||
int[] data = expctx1.generateRandomValues(rnd);
|
int[] data = expctx1.generateRandomValues(rnd);
|
||||||
expctx1.evaluate(data);
|
expctx1.evaluate(data);
|
||||||
expctx2.evaluate(data);
|
expctx2.evaluate(data);
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,18 @@ import java.util.Arrays;
|
||||||
|
|
||||||
import btools.util.LruMapNode;
|
import btools.util.LruMapNode;
|
||||||
|
|
||||||
public final class VarWrapper extends LruMapNode
|
public final class VarWrapper extends LruMapNode {
|
||||||
{
|
|
||||||
float[] vars;
|
float[] vars;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode()
|
public int hashCode() {
|
||||||
{
|
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals( Object o )
|
public boolean equals(Object o) {
|
||||||
{
|
|
||||||
VarWrapper n = (VarWrapper) o;
|
VarWrapper n = (VarWrapper) o;
|
||||||
if ( hash != n.hash )
|
if (hash != n.hash) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return Arrays.equals(vars, n.vars);
|
return Arrays.equals(vars, n.vars);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package btools.expressions;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class ConstantOptimizerTest {
|
||||||
|
@Test
|
||||||
|
public void compareOptimizerModesTest() {
|
||||||
|
|
||||||
|
File lookupFile = new File(getClass().getResource("/lookups_test.dat").getPath());
|
||||||
|
File profileFile = new File(getClass().getResource("/profile_test.brf").getPath());
|
||||||
|
|
||||||
|
BExpressionMetaData meta1 = new BExpressionMetaData();
|
||||||
|
BExpressionMetaData meta2 = new BExpressionMetaData();
|
||||||
|
BExpressionContext expctx1 = new BExpressionContextWay(meta1);
|
||||||
|
BExpressionContext expctx2 = new BExpressionContextWay(meta2);
|
||||||
|
expctx2.skipConstantExpressionOptimizations = true;
|
||||||
|
|
||||||
|
Map<String, String> keyValue = new HashMap<>();
|
||||||
|
keyValue.put("global_inject1", "5");
|
||||||
|
keyValue.put("global_inject2", "6");
|
||||||
|
keyValue.put("global_inject3", "7");
|
||||||
|
|
||||||
|
meta1.readMetaData(lookupFile);
|
||||||
|
meta2.readMetaData(lookupFile);
|
||||||
|
expctx1.parseFile(profileFile, "global", keyValue);
|
||||||
|
expctx2.parseFile(profileFile, "global", keyValue);
|
||||||
|
|
||||||
|
float d = 0.0001f;
|
||||||
|
Assert.assertEquals(5f, expctx1.getVariableValue("global_inject1", 0f), d);
|
||||||
|
Assert.assertEquals(9f, expctx1.getVariableValue("global_inject2", 0f), d); // should be modified in 2. assign!
|
||||||
|
Assert.assertEquals(7f, expctx1.getVariableValue("global_inject3", 0f), d);
|
||||||
|
Assert.assertEquals(3f, expctx1.getVariableValue("global_inject4", 3f), d); // un-assigned
|
||||||
|
|
||||||
|
Assert.assertTrue("expected far less exporessions nodes if optimized", expctx2.expressionNodeCount - expctx1.expressionNodeCount >= 311-144);
|
||||||
|
|
||||||
|
Random rnd = new Random(17464); // fixed seed for unit test...
|
||||||
|
for (int i = 0; i < 10000; i++) {
|
||||||
|
int[] data = expctx1.generateRandomValues(rnd);
|
||||||
|
expctx1.evaluate(data);
|
||||||
|
expctx2.evaluate(data);
|
||||||
|
expctx1.assertAllVariablesEqual(expctx2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,14 @@
|
||||||
package btools.expressions;
|
package btools.expressions;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class EncodeDecodeTest
|
import java.io.File;
|
||||||
{
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class EncodeDecodeTest {
|
||||||
@Test
|
@Test
|
||||||
public void encodeDecodeTest()
|
public void encodeDecodeTest() {
|
||||||
{
|
|
||||||
URL testpurl = this.getClass().getResource("/dummy.txt");
|
URL testpurl = this.getClass().getResource("/dummy.txt");
|
||||||
File workingDir = new File(testpurl.getFile()).getParentFile();
|
File workingDir = new File(testpurl.getFile()).getParentFile();
|
||||||
File profileDir = new File(workingDir, "/../../../../misc/profiles2");
|
File profileDir = new File(workingDir, "/../../../../misc/profiles2");
|
||||||
|
|
@ -32,16 +29,16 @@ public class EncodeDecodeTest
|
||||||
"depth=1'6\"",
|
"depth=1'6\"",
|
||||||
// "depth=6 feet",
|
// "depth=6 feet",
|
||||||
"maxheight=5.1m",
|
"maxheight=5.1m",
|
||||||
"maxdraft=~3 mt",
|
"maxdraft=~3 m - 4 m",
|
||||||
"reversedirection=yes"
|
"reversedirection=yes"
|
||||||
};
|
};
|
||||||
|
|
||||||
// encode the tags into 64 bit description word
|
// encode the tags into 64 bit description word
|
||||||
int[] lookupData = expctxWay.createNewLookupData();
|
int[] lookupData = expctxWay.createNewLookupData();
|
||||||
for( String arg: tags )
|
for (String arg : tags) {
|
||||||
{
|
|
||||||
int idx = arg.indexOf('=');
|
int idx = arg.indexOf('=');
|
||||||
if ( idx < 0 ) throw new IllegalArgumentException( "bad argument (should be <tag>=<value>): " + arg );
|
if (idx < 0)
|
||||||
|
throw new IllegalArgumentException("bad argument (should be <tag>=<value>): " + arg);
|
||||||
String key = arg.substring(0, idx);
|
String key = arg.substring(0, idx);
|
||||||
String value = arg.substring(idx + 1);
|
String value = arg.substring(idx + 1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package btools.expressions;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class IntegrityCheckProfileTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void integrityTestProfiles() throws IOException {
|
||||||
|
File workingDir = new File(".").getCanonicalFile();
|
||||||
|
File profileDir = new File(workingDir, "../misc/profiles2");
|
||||||
|
File[] files = profileDir.listFiles();
|
||||||
|
|
||||||
|
assertNotNull("Missing profiles", files);
|
||||||
|
|
||||||
|
for (File f : files) {
|
||||||
|
if (f.getName().endsWith(".brf")) {
|
||||||
|
BExpressionMetaData meta = new BExpressionMetaData();
|
||||||
|
BExpressionContext expctxWay = new BExpressionContextWay(meta);
|
||||||
|
BExpressionContext expctxNode = new BExpressionContextNode(meta);
|
||||||
|
meta.readMetaData(new File(profileDir, "lookups.dat"));
|
||||||
|
expctxNode.setForeignContext(expctxWay);
|
||||||
|
expctxWay.parseFile(f, "global");
|
||||||
|
expctxNode.parseFile(f, "global");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -687,6 +687,35 @@ construction;0000000037 driveway
|
||||||
construction;0000000021 mini_roundabout
|
construction;0000000021 mini_roundabout
|
||||||
construction;0000000020 turning_loop
|
construction;0000000020 turning_loop
|
||||||
|
|
||||||
|
estimated_forest_class;0000000001 1
|
||||||
|
estimated_forest_class;0000000001 2
|
||||||
|
estimated_forest_class;0000000001 3
|
||||||
|
estimated_forest_class;0000000001 4
|
||||||
|
estimated_forest_class;0000000001 5
|
||||||
|
estimated_forest_class;0000000001 6
|
||||||
|
|
||||||
|
estimated_noise_class;0000000001 1
|
||||||
|
estimated_noise_class;0000000001 2
|
||||||
|
estimated_noise_class;0000000001 3
|
||||||
|
estimated_noise_class;0000000001 4
|
||||||
|
estimated_noise_class;0000000001 5
|
||||||
|
estimated_noise_class;0000000001 6
|
||||||
|
|
||||||
|
estimated_river_class;0000000001 1
|
||||||
|
estimated_river_class;0000000001 2
|
||||||
|
estimated_river_class;0000000001 3
|
||||||
|
estimated_river_class;0000000001 4
|
||||||
|
estimated_river_class;0000000001 5
|
||||||
|
estimated_river_class;0000000001 6
|
||||||
|
|
||||||
|
estimated_town_class;0000000001 1
|
||||||
|
estimated_town_class;0000000001 2
|
||||||
|
estimated_town_class;0000000001 3
|
||||||
|
estimated_town_class;0000000001 4
|
||||||
|
estimated_town_class;0000000001 5
|
||||||
|
estimated_town_class;0000000001 6
|
||||||
|
|
||||||
|
|
||||||
---context:node
|
---context:node
|
||||||
|
|
||||||
highway;0001314954 bus_stop
|
highway;0001314954 bus_stop
|
||||||
|
|
|
||||||
88
brouter-expressions/src/test/resources/profile_test.brf
Normal file
88
brouter-expressions/src/test/resources/profile_test.brf
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
---context:global # following code refers to global config
|
||||||
|
|
||||||
|
assign global_false = false
|
||||||
|
assign global_true = true
|
||||||
|
assign global_and = and false global_true
|
||||||
|
|
||||||
|
assign global_inject1 = 5
|
||||||
|
assign global_inject2 = 13
|
||||||
|
assign global_inject2 = add global_inject2 3
|
||||||
|
|
||||||
|
assign global_or = or ( or global_true global_false ) ( or global_false global_true )
|
||||||
|
assign global_and = and ( and global_true true ) false
|
||||||
|
|
||||||
|
---context:way # following code refers to way-tags
|
||||||
|
|
||||||
|
assign v = highway=primary
|
||||||
|
assign w = surface=asphalt
|
||||||
|
|
||||||
|
# test constant or/and
|
||||||
|
assign costfactor =
|
||||||
|
add multiply 1 or 1 1
|
||||||
|
add multiply 2 or 1 0
|
||||||
|
add multiply 4 or 0 1
|
||||||
|
add multiply 8 or 0 0
|
||||||
|
add multiply 16 and 1 1
|
||||||
|
add multiply 32 and 1 1
|
||||||
|
add multiply 64 and 1 1
|
||||||
|
multiply 128 and 1 1
|
||||||
|
|
||||||
|
# test variable or
|
||||||
|
assign turncost =
|
||||||
|
add multiply 1 or v 1
|
||||||
|
add multiply 2 or v 0
|
||||||
|
add multiply 4 or 0 v
|
||||||
|
add multiply 8 or 1 v
|
||||||
|
multiply 16 or v w
|
||||||
|
|
||||||
|
# test variable and
|
||||||
|
assign uphillcostfactor =
|
||||||
|
add multiply 1 and v 1
|
||||||
|
add multiply 2 and v 0
|
||||||
|
add multiply 4 and 0 v
|
||||||
|
add multiply 8 and 1 v
|
||||||
|
multiply 16 and v w
|
||||||
|
|
||||||
|
# test add
|
||||||
|
assign downhillcostfactor =
|
||||||
|
add multiply 1 add 1 1
|
||||||
|
add multiply 2 add 1 0
|
||||||
|
add multiply 4 add 0 1
|
||||||
|
add multiply 8 add 0 0
|
||||||
|
add multiply 16 add v 1
|
||||||
|
add multiply 32 add v 0
|
||||||
|
add multiply 64 add 1 v
|
||||||
|
multiply 128 add 0 v
|
||||||
|
|
||||||
|
# test max
|
||||||
|
assign initialcost =
|
||||||
|
add multiply 1 max 1 2
|
||||||
|
add multiply 2 max multiply 2 v 1
|
||||||
|
add multiply 4 max 1 multiply 2 v
|
||||||
|
multiply 8 max multiply 2 v v
|
||||||
|
|
||||||
|
# test switch
|
||||||
|
assign initialclassifier =
|
||||||
|
add multiply 1 switch 1 1 0
|
||||||
|
add multiply 2 switch 0 1 0
|
||||||
|
add multiply 4 switch 1 0 1
|
||||||
|
add multiply 8 switch 0 0 1
|
||||||
|
add multiply 16 switch v 1 1
|
||||||
|
add multiply 32 switch v 0 1
|
||||||
|
add multiply 64 switch v 1 0
|
||||||
|
add multiply 128 switch v 0 1
|
||||||
|
multiply 256 switch 1 v w
|
||||||
|
|
||||||
|
# test global calcs
|
||||||
|
assign priorityclassifier =
|
||||||
|
add multiply 1 global_false
|
||||||
|
add multiply 2 global_true
|
||||||
|
add multiply 4 global_and
|
||||||
|
add multiply 8 global_inject1
|
||||||
|
add multiply 16 global_inject2
|
||||||
|
add multiply 32 global_or
|
||||||
|
multiply 64 global_and
|
||||||
|
|
||||||
|
---context:node # following code refers to node tags
|
||||||
|
|
||||||
|
assign initialcost = 1
|
||||||
1
brouter-map-creator/.gitignore
vendored
1
brouter-map-creator/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
/build/
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'java-library'
|
id 'brouter.application-conventions'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':brouter-codec')
|
implementation project(':brouter-codec')
|
||||||
implementation project(':brouter-util')
|
implementation project(':brouter-util')
|
||||||
implementation project(':brouter-expressions')
|
implementation project(':brouter-expressions')
|
||||||
|
|
||||||
testImplementation('junit:junit:4.13.1')
|
implementation group: 'org.openstreetmap.osmosis', name: 'osmosis-osm-binary', version: '0.48.3'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
import org.openstreetmap.osmosis.osmbinary.Fileformat;
|
||||||
|
import org.openstreetmap.osmosis.osmbinary.Osmformat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.DataFormatException;
|
||||||
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
|
import btools.util.LongList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts PBF block data into decoded entities ready to be passed into an Osmosis pipeline. This
|
||||||
|
* class is designed to be passed into a pool of worker threads to allow multi-threaded decoding.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @author Brett Henderson
|
||||||
|
*/
|
||||||
|
public class BPbfBlobDecoder {
|
||||||
|
private String blobType;
|
||||||
|
private byte[] rawBlob;
|
||||||
|
|
||||||
|
private OsmParser parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @param blobType The type of blob.
|
||||||
|
* @param rawBlob The raw data of the blob.
|
||||||
|
* @param listener The listener for receiving decoding results.
|
||||||
|
*/
|
||||||
|
public BPbfBlobDecoder(String blobType, byte[] rawBlob, OsmParser parser) {
|
||||||
|
this.blobType = blobType;
|
||||||
|
this.rawBlob = rawBlob;
|
||||||
|
this.parser = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process() throws Exception {
|
||||||
|
if ("OSMHeader".equals(blobType)) {
|
||||||
|
processOsmHeader(readBlobContent());
|
||||||
|
|
||||||
|
} else if ("OSMData".equals(blobType)) {
|
||||||
|
processOsmPrimitives(readBlobContent());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
System.out.println("Skipping unrecognised blob type " + blobType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readBlobContent() throws IOException {
|
||||||
|
Fileformat.Blob blob = Fileformat.Blob.parseFrom(rawBlob);
|
||||||
|
byte[] blobData;
|
||||||
|
|
||||||
|
if (blob.hasRaw()) {
|
||||||
|
blobData = blob.getRaw().toByteArray();
|
||||||
|
} else if (blob.hasZlibData()) {
|
||||||
|
Inflater inflater = new Inflater();
|
||||||
|
inflater.setInput(blob.getZlibData().toByteArray());
|
||||||
|
blobData = new byte[blob.getRawSize()];
|
||||||
|
try {
|
||||||
|
inflater.inflate(blobData);
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
throw new RuntimeException("Unable to decompress PBF blob.", e);
|
||||||
|
}
|
||||||
|
if (!inflater.finished()) {
|
||||||
|
throw new RuntimeException("PBF blob contains incomplete compressed data.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("PBF blob uses unsupported compression, only raw or zlib may be used.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return blobData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processOsmHeader(byte[] data) throws InvalidProtocolBufferException {
|
||||||
|
Osmformat.HeaderBlock header = Osmformat.HeaderBlock.parseFrom(data);
|
||||||
|
|
||||||
|
// Build the list of active and unsupported features in the file.
|
||||||
|
List<String> supportedFeatures = Arrays.asList("OsmSchema-V0.6", "DenseNodes");
|
||||||
|
List<String> activeFeatures = new ArrayList<>();
|
||||||
|
List<String> unsupportedFeatures = new ArrayList<>();
|
||||||
|
for (String feature : header.getRequiredFeaturesList()) {
|
||||||
|
if (supportedFeatures.contains(feature)) {
|
||||||
|
activeFeatures.add(feature);
|
||||||
|
} else {
|
||||||
|
unsupportedFeatures.add(feature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't continue if there are any unsupported features. We wait
|
||||||
|
// until now so that we can display all unsupported features instead of
|
||||||
|
// just the first one we encounter.
|
||||||
|
if (unsupportedFeatures.size() > 0) {
|
||||||
|
throw new RuntimeException("PBF file contains unsupported features " + unsupportedFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> buildTags(List<Integer> keys, List<Integer> values, BPbfFieldDecoder fieldDecoder) {
|
||||||
|
|
||||||
|
Iterator<Integer> keyIterator = keys.iterator();
|
||||||
|
Iterator<Integer> valueIterator = values.iterator();
|
||||||
|
if (keyIterator.hasNext()) {
|
||||||
|
Map<String, String> tags = new HashMap<>();
|
||||||
|
while (keyIterator.hasNext()) {
|
||||||
|
String key = fieldDecoder.decodeString(keyIterator.next());
|
||||||
|
String value = fieldDecoder.decodeString(valueIterator.next());
|
||||||
|
tags.put(key, value);
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processNodes(List<Osmformat.Node> nodes, BPbfFieldDecoder fieldDecoder) {
|
||||||
|
for (Osmformat.Node node : nodes) {
|
||||||
|
Map<String, String> tags = buildTags(node.getKeysList(), node.getValsList(), fieldDecoder);
|
||||||
|
|
||||||
|
parser.addNode(node.getId(), tags, fieldDecoder.decodeLatitude(node
|
||||||
|
.getLat()), fieldDecoder.decodeLatitude(node.getLon()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processNodes(Osmformat.DenseNodes nodes, BPbfFieldDecoder fieldDecoder) {
|
||||||
|
List<Long> idList = nodes.getIdList();
|
||||||
|
List<Long> latList = nodes.getLatList();
|
||||||
|
List<Long> lonList = nodes.getLonList();
|
||||||
|
|
||||||
|
Iterator<Integer> keysValuesIterator = nodes.getKeysValsList().iterator();
|
||||||
|
|
||||||
|
long nodeId = 0;
|
||||||
|
long latitude = 0;
|
||||||
|
long longitude = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < idList.size(); i++) {
|
||||||
|
// Delta decode node fields.
|
||||||
|
nodeId += idList.get(i);
|
||||||
|
latitude += latList.get(i);
|
||||||
|
longitude += lonList.get(i);
|
||||||
|
|
||||||
|
// Build the tags. The key and value string indexes are sequential
|
||||||
|
// in the same PBF array. Each set of tags is delimited by an index
|
||||||
|
// with a value of 0.
|
||||||
|
Map<String, String> tags = null;
|
||||||
|
while (keysValuesIterator.hasNext()) {
|
||||||
|
int keyIndex = keysValuesIterator.next();
|
||||||
|
if (keyIndex == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int valueIndex = keysValuesIterator.next();
|
||||||
|
|
||||||
|
if (tags == null) {
|
||||||
|
tags = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
tags.put(fieldDecoder.decodeString(keyIndex), fieldDecoder.decodeString(valueIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.addNode(nodeId, tags, ((double) latitude) / 10000000, ((double) longitude) / 10000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processWays(List<Osmformat.Way> ways, BPbfFieldDecoder fieldDecoder) {
|
||||||
|
for (Osmformat.Way way : ways) {
|
||||||
|
Map<String, String> tags = buildTags(way.getKeysList(), way.getValsList(), fieldDecoder);
|
||||||
|
|
||||||
|
// Build up the list of way nodes for the way. The node ids are
|
||||||
|
// delta encoded meaning that each id is stored as a delta against
|
||||||
|
// the previous one.
|
||||||
|
long nodeId = 0;
|
||||||
|
LongList wayNodes = new LongList(16);
|
||||||
|
for (long nodeIdOffset : way.getRefsList()) {
|
||||||
|
nodeId += nodeIdOffset;
|
||||||
|
wayNodes.add(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.addWay(way.getId(), tags, wayNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LongList fromWid;
|
||||||
|
private LongList toWid;
|
||||||
|
private LongList viaNid;
|
||||||
|
|
||||||
|
private LongList addLong(LongList ll, long l) {
|
||||||
|
if (ll == null) {
|
||||||
|
ll = new LongList(1);
|
||||||
|
}
|
||||||
|
ll.add(l);
|
||||||
|
return ll;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LongList buildRelationMembers(
|
||||||
|
List<Long> memberIds, List<Integer> memberRoles, List<Osmformat.Relation.MemberType> memberTypes,
|
||||||
|
BPbfFieldDecoder fieldDecoder) {
|
||||||
|
LongList wayIds = new LongList(16);
|
||||||
|
|
||||||
|
fromWid = toWid = viaNid = null;
|
||||||
|
|
||||||
|
Iterator<Long> memberIdIterator = memberIds.iterator();
|
||||||
|
Iterator<Integer> memberRoleIterator = memberRoles.iterator();
|
||||||
|
Iterator<Osmformat.Relation.MemberType> memberTypeIterator = memberTypes.iterator();
|
||||||
|
|
||||||
|
// Build up the list of relation members for the way. The member ids are
|
||||||
|
// delta encoded meaning that each id is stored as a delta against
|
||||||
|
// the previous one.
|
||||||
|
long refId = 0;
|
||||||
|
while (memberIdIterator.hasNext()) {
|
||||||
|
Osmformat.Relation.MemberType memberType = memberTypeIterator.next();
|
||||||
|
refId += memberIdIterator.next();
|
||||||
|
|
||||||
|
String role = fieldDecoder.decodeString(memberRoleIterator.next());
|
||||||
|
|
||||||
|
if (memberType == Osmformat.Relation.MemberType.WAY) { // currently just waymembers
|
||||||
|
wayIds.add(refId);
|
||||||
|
if ("from".equals(role)) fromWid = addLong(fromWid, refId);
|
||||||
|
if ("to".equals(role)) toWid = addLong(toWid, refId);
|
||||||
|
}
|
||||||
|
if (memberType == Osmformat.Relation.MemberType.NODE) { // currently just waymembers
|
||||||
|
if ("via".equals(role)) viaNid = addLong(viaNid, refId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wayIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processRelations(List<Osmformat.Relation> relations, BPbfFieldDecoder fieldDecoder) {
|
||||||
|
for (Osmformat.Relation relation : relations) {
|
||||||
|
Map<String, String> tags = buildTags(relation.getKeysList(), relation.getValsList(), fieldDecoder);
|
||||||
|
|
||||||
|
LongList wayIds = buildRelationMembers(relation.getMemidsList(), relation.getRolesSidList(),
|
||||||
|
relation.getTypesList(), fieldDecoder);
|
||||||
|
|
||||||
|
parser.addRelation(relation.getId(), tags, wayIds, fromWid, toWid, viaNid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processOsmPrimitives(byte[] data) throws InvalidProtocolBufferException {
|
||||||
|
Osmformat.PrimitiveBlock block = Osmformat.PrimitiveBlock.parseFrom(data);
|
||||||
|
BPbfFieldDecoder fieldDecoder = new BPbfFieldDecoder(block);
|
||||||
|
|
||||||
|
for (Osmformat.PrimitiveGroup primitiveGroup : block.getPrimitivegroupList()) {
|
||||||
|
processNodes(primitiveGroup.getDense(), fieldDecoder);
|
||||||
|
processNodes(primitiveGroup.getNodesList(), fieldDecoder);
|
||||||
|
processWays(primitiveGroup.getWaysList(), fieldDecoder);
|
||||||
|
processRelations(primitiveGroup.getRelationsList(), fieldDecoder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.openstreetmap.osmosis.osmbinary.Osmformat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages decoding of the lower level PBF data structures.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @author Brett Henderson
|
||||||
|
* <p/>
|
||||||
|
*/
|
||||||
|
public class BPbfFieldDecoder {
|
||||||
|
private static final double COORDINATE_SCALING_FACTOR = 0.000000001;
|
||||||
|
private String[] strings;
|
||||||
|
private int coordGranularity;
|
||||||
|
private long coordLatitudeOffset;
|
||||||
|
private long coordLongitudeOffset;
|
||||||
|
private int dateGranularity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @param primitiveBlock The primitive block containing the fields to be decoded.
|
||||||
|
*/
|
||||||
|
public BPbfFieldDecoder(Osmformat.PrimitiveBlock primitiveBlock) {
|
||||||
|
this.coordGranularity = primitiveBlock.getGranularity();
|
||||||
|
this.coordLatitudeOffset = primitiveBlock.getLatOffset();
|
||||||
|
this.coordLongitudeOffset = primitiveBlock.getLonOffset();
|
||||||
|
this.dateGranularity = primitiveBlock.getDateGranularity();
|
||||||
|
|
||||||
|
Osmformat.StringTable stringTable = primitiveBlock.getStringtable();
|
||||||
|
strings = new String[stringTable.getSCount()];
|
||||||
|
for (int i = 0; i < strings.length; i++) {
|
||||||
|
strings[i] = stringTable.getS(i).toStringUtf8();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a raw latitude value into degrees.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @param rawLatitude The PBF encoded value.
|
||||||
|
* @return The latitude in degrees.
|
||||||
|
*/
|
||||||
|
public double decodeLatitude(long rawLatitude) {
|
||||||
|
return COORDINATE_SCALING_FACTOR * (coordLatitudeOffset + (coordGranularity * rawLatitude));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a raw longitude value into degrees.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @param rawLongitude The PBF encoded value.
|
||||||
|
* @return The longitude in degrees.
|
||||||
|
*/
|
||||||
|
public double decodeLongitude(long rawLongitude) {
|
||||||
|
return COORDINATE_SCALING_FACTOR * (coordLongitudeOffset + (coordGranularity * rawLongitude));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a raw timestamp value into a Date.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @param rawTimestamp The PBF encoded timestamp.
|
||||||
|
* @return The timestamp as a Date.
|
||||||
|
*/
|
||||||
|
public Date decodeTimestamp(long rawTimestamp) {
|
||||||
|
return new Date(dateGranularity * rawTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a raw string into a String.
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @param rawString The PBF encoding string.
|
||||||
|
* @return The string as a String.
|
||||||
|
*/
|
||||||
|
public String decodeString(int rawString) {
|
||||||
|
return strings[rawString];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,220 +0,0 @@
|
||||||
package btools.mapcreator;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
|
|
||||||
public class ConvertLidarTile
|
|
||||||
{
|
|
||||||
public static int NROWS;
|
|
||||||
public static int NCOLS;
|
|
||||||
|
|
||||||
public static final short NODATA2 = -32767; // hgt-formats nodata
|
|
||||||
public static final short NODATA = Short.MIN_VALUE;
|
|
||||||
|
|
||||||
static short[] imagePixels;
|
|
||||||
|
|
||||||
private static void readHgtZip( String filename, int rowOffset, int colOffset ) throws Exception
|
|
||||||
{
|
|
||||||
ZipInputStream zis = new ZipInputStream( new BufferedInputStream( new FileInputStream( filename ) ) );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
ZipEntry ze = zis.getNextEntry();
|
|
||||||
if ( ze.getName().endsWith( ".hgt" ) )
|
|
||||||
{
|
|
||||||
readHgtFromStream( zis, rowOffset, colOffset );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
zis.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void readHgtFromStream( InputStream is, int rowOffset, int colOffset )
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
DataInputStream dis = new DataInputStream( new BufferedInputStream( is ) );
|
|
||||||
for ( int ir = 0; ir < 1201; ir++ )
|
|
||||||
{
|
|
||||||
int row = rowOffset + ir;
|
|
||||||
|
|
||||||
for ( int ic = 0; ic < 1201; ic++ )
|
|
||||||
{
|
|
||||||
int col = colOffset + ic;
|
|
||||||
|
|
||||||
int i1 = dis.read(); // msb first!
|
|
||||||
int i0 = dis.read();
|
|
||||||
|
|
||||||
if ( i0 == -1 || i1 == -1 )
|
|
||||||
throw new RuntimeException( "unexcepted end of file reading hgt entry!" );
|
|
||||||
|
|
||||||
short val = (short) ( ( i1 << 8 ) | i0 );
|
|
||||||
|
|
||||||
if ( val == NODATA2 )
|
|
||||||
{
|
|
||||||
val = NODATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPixel( row, col, val );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void setPixel( int row, int col, short val )
|
|
||||||
{
|
|
||||||
if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
|
|
||||||
{
|
|
||||||
imagePixels[row * NCOLS + col] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static short getPixel( int row, int col )
|
|
||||||
{
|
|
||||||
if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
|
|
||||||
{
|
|
||||||
return imagePixels[row * NCOLS + col];
|
|
||||||
}
|
|
||||||
return NODATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void doConvert( String inputDir, int lonDegreeStart, int latDegreeStart, String outputFile ) throws Exception
|
|
||||||
{
|
|
||||||
int extraBorder = 0;
|
|
||||||
|
|
||||||
NROWS = 5 * 1200 + 1 + 2 * extraBorder;
|
|
||||||
NCOLS = 5 * 1200 + 1 + 2 * extraBorder;
|
|
||||||
|
|
||||||
imagePixels = new short[NROWS * NCOLS]; // 650 MB !
|
|
||||||
|
|
||||||
// prefill as NODATA
|
|
||||||
for ( int row = 0; row < NROWS; row++ )
|
|
||||||
{
|
|
||||||
for ( int col = 0; col < NCOLS; col++ )
|
|
||||||
{
|
|
||||||
imagePixels[row * NCOLS + col] = NODATA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( int latIdx = -1; latIdx <= 5; latIdx++ )
|
|
||||||
{
|
|
||||||
int latDegree = latDegreeStart + latIdx;
|
|
||||||
int rowOffset = extraBorder + ( 4 - latIdx ) * 1200;
|
|
||||||
|
|
||||||
for ( int lonIdx = -1; lonIdx <= 5; lonIdx++ )
|
|
||||||
{
|
|
||||||
int lonDegree = lonDegreeStart + lonIdx;
|
|
||||||
int colOffset = extraBorder + lonIdx * 1200;
|
|
||||||
|
|
||||||
String filename = inputDir + "/" + formatLat( latDegree ) + formatLon( lonDegree ) + ".zip";
|
|
||||||
File f = new File( filename );
|
|
||||||
if ( f.exists() && f.length() > 0 )
|
|
||||||
{
|
|
||||||
System.out.println( "exist: " + filename );
|
|
||||||
readHgtZip( filename, rowOffset, colOffset );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
System.out.println( "none : " + filename );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean halfCol5 = false; // no halfcol tiles in lidar data (?)
|
|
||||||
|
|
||||||
|
|
||||||
SrtmRaster raster = new SrtmRaster();
|
|
||||||
raster.nrows = NROWS;
|
|
||||||
raster.ncols = NCOLS;
|
|
||||||
raster.halfcol = halfCol5;
|
|
||||||
raster.noDataValue = NODATA;
|
|
||||||
raster.cellsize = 1 / 1200.;
|
|
||||||
raster.xllcorner = lonDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
|
|
||||||
raster.yllcorner = latDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
|
|
||||||
raster.eval_array = imagePixels;
|
|
||||||
|
|
||||||
// encode the raster
|
|
||||||
OutputStream os = new BufferedOutputStream( new FileOutputStream( outputFile ) );
|
|
||||||
new RasterCoder().encodeRaster( raster, os );
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
// decode the raster
|
|
||||||
InputStream is = new BufferedInputStream( new FileInputStream( outputFile ) );
|
|
||||||
SrtmRaster raster2 = new RasterCoder().decodeRaster( is );
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
short[] pix2 = raster2.eval_array;
|
|
||||||
if ( pix2.length != imagePixels.length )
|
|
||||||
throw new RuntimeException( "length mismatch!" );
|
|
||||||
|
|
||||||
// compare decoding result
|
|
||||||
for ( int row = 0; row < NROWS; row++ )
|
|
||||||
{
|
|
||||||
int colstep = halfCol5 ? 2 : 1;
|
|
||||||
for ( int col = 0; col < NCOLS; col += colstep )
|
|
||||||
{
|
|
||||||
int idx = row * NCOLS + col;
|
|
||||||
short p2 = pix2[idx];
|
|
||||||
if ( p2 != imagePixels[idx] )
|
|
||||||
{
|
|
||||||
throw new RuntimeException( "content mismatch: p2=" + p2 + " p1=" + imagePixels[idx] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatLon( int lon )
|
|
||||||
{
|
|
||||||
if ( lon >= 180 )
|
|
||||||
lon -= 180; // TODO: w180 oder E180 ?
|
|
||||||
|
|
||||||
String s = "E";
|
|
||||||
if ( lon < 0 )
|
|
||||||
{
|
|
||||||
lon = -lon;
|
|
||||||
s = "E";
|
|
||||||
}
|
|
||||||
String n = "000" + lon;
|
|
||||||
return s + n.substring( n.length() - 3 );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatLat( int lat )
|
|
||||||
{
|
|
||||||
String s = "N";
|
|
||||||
if ( lat < 0 )
|
|
||||||
{
|
|
||||||
lat = -lat;
|
|
||||||
s = "S";
|
|
||||||
}
|
|
||||||
String n = "00" + lat;
|
|
||||||
return s + n.substring( n.length() - 2 );
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main( String[] args ) throws Exception
|
|
||||||
{
|
|
||||||
String filename90 = args[0];
|
|
||||||
String filename30 = filename90.substring( 0, filename90.length() - 3 ) + "bef";
|
|
||||||
|
|
||||||
int srtmLonIdx = Integer.parseInt( filename90.substring( 5, 7 ).toLowerCase() );
|
|
||||||
int srtmLatIdx = Integer.parseInt( filename90.substring( 8, 10 ).toLowerCase() );
|
|
||||||
|
|
||||||
int ilon_base = ( srtmLonIdx - 1 ) * 5 - 180;
|
|
||||||
int ilat_base = 150 - srtmLatIdx * 5 - 90;
|
|
||||||
|
|
||||||
doConvert( args[1], ilon_base, ilat_base, filename30 );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,311 +0,0 @@
|
||||||
package btools.mapcreator;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.zip.*;
|
|
||||||
|
|
||||||
public class ConvertSrtmTile
|
|
||||||
{
|
|
||||||
public static int NROWS;
|
|
||||||
public static int NCOLS;
|
|
||||||
|
|
||||||
public static final short SKIPDATA = -32766; // >50 degree skipped pixel
|
|
||||||
public static final short NODATA2 = -32767; // bil-formats nodata
|
|
||||||
public static final short NODATA = Short.MIN_VALUE;
|
|
||||||
|
|
||||||
static short[] imagePixels;
|
|
||||||
|
|
||||||
public static int[] diffs = new int[100];
|
|
||||||
|
|
||||||
private static void readBilZip( String filename, int rowOffset, int colOffset, boolean halfCols ) throws Exception
|
|
||||||
{
|
|
||||||
ZipInputStream zis = new ZipInputStream( new BufferedInputStream( new FileInputStream( filename ) ) );
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
ZipEntry ze = zis.getNextEntry();
|
|
||||||
if ( ze.getName().endsWith( ".bil" ) )
|
|
||||||
{
|
|
||||||
readBilFromStream( zis, rowOffset, colOffset, halfCols );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
zis.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void readBilFromStream( InputStream is, int rowOffset, int colOffset, boolean halfCols )
|
|
||||||
throws Exception
|
|
||||||
{
|
|
||||||
DataInputStream dis = new DataInputStream( new BufferedInputStream( is ) );
|
|
||||||
for ( int ir = 0; ir < 3601; ir++ )
|
|
||||||
{
|
|
||||||
int row = rowOffset + ir;
|
|
||||||
|
|
||||||
for ( int ic = 0; ic < 3601; ic++ )
|
|
||||||
{
|
|
||||||
int col = colOffset + ic;
|
|
||||||
|
|
||||||
if ( ( ic % 2 ) == 1 && halfCols )
|
|
||||||
{
|
|
||||||
if ( getPixel( row, col ) == NODATA )
|
|
||||||
{
|
|
||||||
setPixel( row, col, SKIPDATA );
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i0 = dis.read();
|
|
||||||
int i1 = dis.read();
|
|
||||||
|
|
||||||
if ( i0 == -1 || i1 == -1 )
|
|
||||||
throw new RuntimeException( "unexcepted end of file reading bil entry!" );
|
|
||||||
|
|
||||||
short val = (short) ( ( i1 << 8 ) | i0 );
|
|
||||||
|
|
||||||
if ( val == NODATA2 )
|
|
||||||
{
|
|
||||||
val = NODATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPixel( row, col, val );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void setPixel( int row, int col, short val )
|
|
||||||
{
|
|
||||||
if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
|
|
||||||
{
|
|
||||||
imagePixels[row * NCOLS + col] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static short getPixel( int row, int col )
|
|
||||||
{
|
|
||||||
if ( row >= 0 && row < NROWS && col >= 0 && col < NCOLS )
|
|
||||||
{
|
|
||||||
return imagePixels[row * NCOLS + col];
|
|
||||||
}
|
|
||||||
return NODATA;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void doConvert( String inputDir, String v1Dir, int lonDegreeStart, int latDegreeStart, String outputFile, SrtmRaster raster90 ) throws Exception
|
|
||||||
{
|
|
||||||
int extraBorder = 10;
|
|
||||||
int datacells = 0;
|
|
||||||
int mismatches = 0;
|
|
||||||
|
|
||||||
NROWS = 5 * 3600 + 1 + 2 * extraBorder;
|
|
||||||
NCOLS = 5 * 3600 + 1 + 2 * extraBorder;
|
|
||||||
|
|
||||||
imagePixels = new short[NROWS * NCOLS]; // 650 MB !
|
|
||||||
|
|
||||||
// prefill as NODATA
|
|
||||||
for ( int row = 0; row < NROWS; row++ )
|
|
||||||
{
|
|
||||||
for ( int col = 0; col < NCOLS; col++ )
|
|
||||||
{
|
|
||||||
imagePixels[row * NCOLS + col] = NODATA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( int latIdx = -1; latIdx <= 5; latIdx++ )
|
|
||||||
{
|
|
||||||
int latDegree = latDegreeStart + latIdx;
|
|
||||||
int rowOffset = extraBorder + ( 4 - latIdx ) * 3600;
|
|
||||||
|
|
||||||
for ( int lonIdx = -1; lonIdx <= 5; lonIdx++ )
|
|
||||||
{
|
|
||||||
int lonDegree = lonDegreeStart + lonIdx;
|
|
||||||
int colOffset = extraBorder + lonIdx * 3600;
|
|
||||||
|
|
||||||
String filename = inputDir + "/" + formatLat( latDegree ) + "_" + formatLon( lonDegree ) + "_1arc_v3_bil.zip";
|
|
||||||
File f = new File( filename );
|
|
||||||
if ( f.exists() && f.length() > 0 )
|
|
||||||
{
|
|
||||||
System.out.println( "exist: " + filename );
|
|
||||||
boolean halfCol = latDegree >= 50 || latDegree < -50;
|
|
||||||
readBilZip( filename, rowOffset, colOffset, halfCol );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
System.out.println( "none : " + filename );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean halfCol5 = latDegreeStart >= 50 || latDegreeStart < -50;
|
|
||||||
|
|
||||||
for ( int row90 = 0; row90 < 6001; row90++ )
|
|
||||||
{
|
|
||||||
int crow = 3 * row90 + extraBorder; // center row of 3x3
|
|
||||||
for ( int col90 = 0; col90 < 6001; col90++ )
|
|
||||||
{
|
|
||||||
int ccol = 3 * col90 + extraBorder; // center col of 3x3
|
|
||||||
|
|
||||||
// evaluate 3x3 area
|
|
||||||
if ( raster90 != null && (!halfCol5 || (col90 % 2) == 0 ) )
|
|
||||||
{
|
|
||||||
short v90 = raster90.eval_array[row90 * 6001 + col90];
|
|
||||||
|
|
||||||
int sum = 0;
|
|
||||||
int nodatas = 0;
|
|
||||||
int datas = 0;
|
|
||||||
int colstep = halfCol5 ? 2 : 1;
|
|
||||||
for ( int row = crow - 1; row <= crow + 1; row++ )
|
|
||||||
{
|
|
||||||
for ( int col = ccol - colstep; col <= ccol + colstep; col += colstep )
|
|
||||||
{
|
|
||||||
short v30 = imagePixels[row * NCOLS + col];
|
|
||||||
if ( v30 == NODATA )
|
|
||||||
{
|
|
||||||
nodatas++;
|
|
||||||
}
|
|
||||||
else if ( v30 != SKIPDATA )
|
|
||||||
{
|
|
||||||
sum += v30;
|
|
||||||
datas++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean doReplace = nodatas > 0 || v90 == NODATA || datas < 7;
|
|
||||||
if ( !doReplace )
|
|
||||||
{
|
|
||||||
datacells++;
|
|
||||||
int diff = sum - datas * v90;
|
|
||||||
if ( diff < -4 || diff > 4 )
|
|
||||||
{
|
|
||||||
doReplace = true;
|
|
||||||
mismatches++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( diff > -50 && diff < 50 && ( row90 % 1200 ) != 0 && ( col90 % 1200 ) != 0 )
|
|
||||||
{
|
|
||||||
diffs[diff + 50]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( doReplace )
|
|
||||||
{
|
|
||||||
for ( int row = crow - 1; row <= crow + 1; row++ )
|
|
||||||
{
|
|
||||||
for ( int col = ccol - colstep; col <= ccol + colstep; col += colstep )
|
|
||||||
{
|
|
||||||
imagePixels[row * NCOLS + col] = v90;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SrtmRaster raster = new SrtmRaster();
|
|
||||||
raster.nrows = NROWS;
|
|
||||||
raster.ncols = NCOLS;
|
|
||||||
raster.halfcol = halfCol5;
|
|
||||||
raster.noDataValue = NODATA;
|
|
||||||
raster.cellsize = 1 / 3600.;
|
|
||||||
raster.xllcorner = lonDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
|
|
||||||
raster.yllcorner = latDegreeStart - ( 0.5 + extraBorder ) * raster.cellsize;
|
|
||||||
raster.eval_array = imagePixels;
|
|
||||||
|
|
||||||
// encode the raster
|
|
||||||
OutputStream os = new BufferedOutputStream( new FileOutputStream( outputFile ) );
|
|
||||||
new RasterCoder().encodeRaster( raster, os );
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
// decode the raster
|
|
||||||
InputStream is = new BufferedInputStream( new FileInputStream( outputFile ) );
|
|
||||||
SrtmRaster raster2 = new RasterCoder().decodeRaster( is );
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
short[] pix2 = raster2.eval_array;
|
|
||||||
if ( pix2.length != imagePixels.length )
|
|
||||||
throw new RuntimeException( "length mismatch!" );
|
|
||||||
|
|
||||||
// compare decoding result
|
|
||||||
for ( int row = 0; row < NROWS; row++ )
|
|
||||||
{
|
|
||||||
int colstep = halfCol5 ? 2 : 1;
|
|
||||||
for ( int col = 0; col < NCOLS; col += colstep )
|
|
||||||
{
|
|
||||||
int idx = row * NCOLS + col;
|
|
||||||
if ( imagePixels[idx] == SKIPDATA )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
short p2 = pix2[idx];
|
|
||||||
if ( p2 > SKIPDATA )
|
|
||||||
{
|
|
||||||
p2 /= 2;
|
|
||||||
}
|
|
||||||
if ( p2 != imagePixels[idx] )
|
|
||||||
{
|
|
||||||
throw new RuntimeException( "content mismatch!" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i=1; i<100;i++) System.out.println( "diff[" + (i-50) + "] = " + diffs[i] );
|
|
||||||
System.out.println( "datacells=" + datacells + " mismatch%=" + (100.*mismatches)/datacells );
|
|
||||||
btools.util.MixCoderDataOutputStream.stats();
|
|
||||||
// test( raster );
|
|
||||||
// raster.calcWeights( 50. );
|
|
||||||
// test( raster );
|
|
||||||
// 39828330 &lon=3115280&layer=OpenStreetMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void test( SrtmRaster raster )
|
|
||||||
{
|
|
||||||
int lat0 = 39828330;
|
|
||||||
int lon0 = 3115280;
|
|
||||||
|
|
||||||
for ( int iy = -9; iy <= 9; iy++ )
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for ( int ix = -9; ix <= 9; ix++ )
|
|
||||||
{
|
|
||||||
int lat = lat0 + 90000000 - 100 * iy;
|
|
||||||
int lon = lon0 + 180000000 + 100 * ix;
|
|
||||||
int ival = (int) ( raster.getElevation( lon, lat ) / 4. );
|
|
||||||
String sval = " " + ival;
|
|
||||||
sb.append( sval.substring( sval.length() - 4 ) );
|
|
||||||
}
|
|
||||||
System.out.println( sb );
|
|
||||||
System.out.println();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatLon( int lon )
|
|
||||||
{
|
|
||||||
if ( lon >= 180 )
|
|
||||||
lon -= 180; // TODO: w180 oder E180 ?
|
|
||||||
|
|
||||||
String s = "e";
|
|
||||||
if ( lon < 0 )
|
|
||||||
{
|
|
||||||
lon = -lon;
|
|
||||||
s = "w";
|
|
||||||
}
|
|
||||||
String n = "000" + lon;
|
|
||||||
return s + n.substring( n.length() - 3 );
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String formatLat( int lat )
|
|
||||||
{
|
|
||||||
String s = "n";
|
|
||||||
if ( lat < 0 )
|
|
||||||
{
|
|
||||||
lat = -lat;
|
|
||||||
s = "s";
|
|
||||||
}
|
|
||||||
String n = "00" + lat;
|
|
||||||
return s + n.substring( n.length() - 2 );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
package btools.mapcreator;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
|
|
||||||
public class ConvertUrlList
|
|
||||||
{
|
|
||||||
public static final short NODATA = -32767;
|
|
||||||
|
|
||||||
public static void main( String[] args ) throws Exception
|
|
||||||
{
|
|
||||||
BufferedReader br = new BufferedReader( new FileReader( args[0] ) );
|
|
||||||
|
|
||||||
for ( ;; )
|
|
||||||
{
|
|
||||||
String line = br.readLine();
|
|
||||||
if ( line == null )
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int idx1 = line.indexOf( "srtm_" );
|
|
||||||
if ( idx1 < 0 )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String filename90 = line.substring( idx1 );
|
|
||||||
String filename30 = filename90.substring( 0, filename90.length() - 3 ) + "bef";
|
|
||||||
|
|
||||||
if ( new File( filename30 ).exists() )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// int srtmLonIdx = (ilon+5000000)/5000000; -> ilon = (srtmLonIdx-1)*5
|
|
||||||
// int srtmLatIdx = (154999999-ilat)/5000000; -> ilat = 155 - srtmLatIdx*5
|
|
||||||
|
|
||||||
int srtmLonIdx = Integer.parseInt( filename90.substring( 5, 7 ).toLowerCase() );
|
|
||||||
int srtmLatIdx = Integer.parseInt( filename90.substring( 8, 10 ).toLowerCase() );
|
|
||||||
|
|
||||||
int ilon_base = ( srtmLonIdx - 1 ) * 5 - 180;
|
|
||||||
int ilat_base = 150 - srtmLatIdx * 5 - 90;
|
|
||||||
|
|
||||||
SrtmRaster raster90 = null;
|
|
||||||
|
|
||||||
File file90 = new File( new File( args[1] ), filename90 );
|
|
||||||
if ( file90.exists() )
|
|
||||||
{
|
|
||||||
System.out.println( "reading " + file90 );
|
|
||||||
raster90 = new SrtmData( file90 ).getRaster();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConvertSrtmTile.doConvert( args[2], args[3], ilon_base, ilat_base, filename30, raster90 );
|
|
||||||
}
|
|
||||||
br.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,335 @@
|
||||||
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.DataBufferInt;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
|
||||||
|
public class CreateElevationRasterImage {
|
||||||
|
|
||||||
|
final static boolean DEBUG = false;
|
||||||
|
|
||||||
|
int[] data;
|
||||||
|
ElevationRaster lastSrtmRaster;
|
||||||
|
Map<String, ElevationRaster> srtmmap;
|
||||||
|
int lastSrtmLonIdx;
|
||||||
|
int lastSrtmLatIdx;
|
||||||
|
short maxElev = Short.MIN_VALUE;
|
||||||
|
short minElev = Short.MAX_VALUE;
|
||||||
|
String srtmdir;
|
||||||
|
boolean missingData;
|
||||||
|
Map<Short, Color> colorMap;
|
||||||
|
|
||||||
|
|
||||||
|
private void createImage(double lon, double lat, String dir, String imageName, int maxX, int maxY, int downscale, String format, String colors) throws Exception {
|
||||||
|
srtmdir = dir;
|
||||||
|
if (colors != null) {
|
||||||
|
loadColors(colors);
|
||||||
|
}
|
||||||
|
if (format.equals("hgt")) {
|
||||||
|
createImageFromHgt(lon, lat, dir, imageName, maxX, maxY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!format.equals("bef")) {
|
||||||
|
System.out.println("wrong format (bef|hgt)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
srtmmap = new HashMap<>();
|
||||||
|
lastSrtmLonIdx = -1;
|
||||||
|
lastSrtmLatIdx = -1;
|
||||||
|
lastSrtmRaster = null;
|
||||||
|
NodeData n = new NodeData(1, lon, lat);
|
||||||
|
ElevationRaster srtm = srtmForNode(n.ilon, n.ilat);
|
||||||
|
if (srtm == null) {
|
||||||
|
System.out.println("no data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.out.println("srtm " + srtm.toString());
|
||||||
|
//System.out.println("srtm elev " + srtm.getElevation(n.ilon, n.ilat));
|
||||||
|
double[] pos = getElevationPos(srtm, n.ilon, n.ilat);
|
||||||
|
//System.out.println("srtm pos " + Math.round(pos[0]) + " " + Math.round(pos[1]));
|
||||||
|
short[] raster = srtm.eval_array;
|
||||||
|
int rasterX = srtm.ncols;
|
||||||
|
int rasterY = srtm.nrows;
|
||||||
|
|
||||||
|
int tileSize = 1000 / downscale;
|
||||||
|
int sizeX = (maxX);
|
||||||
|
int sizeY = (maxY);
|
||||||
|
int[] imgraster = new int[sizeX * sizeY];
|
||||||
|
for (int y = 0; y < sizeY; y++) {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
//short e = getElevationXY(srtm, pos[0] + (sizeY - y) * downscale, pos[1] + (x * downscale));
|
||||||
|
short e = get(srtm, (int) Math.round(pos[0]) + (sizeY - y), x + (int) Math.round(pos[1]));
|
||||||
|
if (e != Short.MIN_VALUE && e < minElev) minElev = e;
|
||||||
|
if (e != Short.MIN_VALUE && e > maxElev) maxElev = e;
|
||||||
|
|
||||||
|
if (e == Short.MIN_VALUE) {
|
||||||
|
imgraster[sizeY * y + x] = 0xffff;
|
||||||
|
} else {
|
||||||
|
//imgraster[sizeY * y + x] = getColorForHeight((short)(e/4)); //(int)(e/4.);
|
||||||
|
imgraster[sizeY * y + x] = getColorForHeight(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("srtm target " + sizeX + " " + sizeY + " (" + rasterX + " " + rasterY + ")" + " min " + minElev + " max " + maxElev);
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
String out = "short ";
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
out += " " + get(srtm, sizeY - 0, i);
|
||||||
|
}
|
||||||
|
System.out.println(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage argbImage = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
data = ((DataBufferInt) argbImage.getRaster().getDataBuffer()).getData();
|
||||||
|
|
||||||
|
for (int y = 0; y < sizeY; y++) {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
int v0 = imgraster[sizeX * y + x];
|
||||||
|
|
||||||
|
int rgb;
|
||||||
|
if (v0 != 0xffff)
|
||||||
|
rgb = 0xff000000 | v0; //(v0 << 8);
|
||||||
|
else
|
||||||
|
rgb = 0xff000000;
|
||||||
|
data[y * sizeX + x] = rgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageIO.write(argbImage, "png", new FileOutputStream(imageName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createImageFromHgt(double lon, double lat, String dir, String imageName, int maxX, int maxY) throws Exception {
|
||||||
|
HgtReader rdr = new HgtReader(dir);
|
||||||
|
short[] data = rdr.getElevationDataFromHgt(lat, lon);
|
||||||
|
if (data == null) {
|
||||||
|
System.out.println("no data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = (data != null ? data.length : 0);
|
||||||
|
int rowlen = (int) Math.sqrt(size);
|
||||||
|
int sizeX = (maxX);
|
||||||
|
int sizeY = (maxY);
|
||||||
|
int[] imgraster = new int[sizeX * sizeY];
|
||||||
|
|
||||||
|
for (int y = 0; y < sizeY; y++) {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
short e = data[(rowlen * y) + x];
|
||||||
|
if (e != HgtReader.HGT_VOID && e < minElev) minElev = e;
|
||||||
|
if (e != HgtReader.HGT_VOID && e > maxElev) maxElev = e;
|
||||||
|
|
||||||
|
if (e == HgtReader.HGT_VOID) {
|
||||||
|
imgraster[sizeY * y + x] = 0xffff;
|
||||||
|
} else if (e == 0) {
|
||||||
|
imgraster[sizeY * y + x] = 0xffff;
|
||||||
|
} else {
|
||||||
|
imgraster[sizeY * y + x] = getColorForHeight((short) (e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("hgt size " + rowlen + " x " + rowlen + " min " + minElev + " max " + maxElev);
|
||||||
|
if (DEBUG) {
|
||||||
|
String out = "short ";
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
out += " " + data[i];
|
||||||
|
}
|
||||||
|
System.out.println(out);
|
||||||
|
}
|
||||||
|
BufferedImage argbImage = new BufferedImage(sizeX, sizeY, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
int[] idata = ((DataBufferInt) argbImage.getRaster().getDataBuffer()).getData();
|
||||||
|
|
||||||
|
for (int y = 0; y < sizeY; y++) {
|
||||||
|
for (int x = 0; x < sizeX; x++) {
|
||||||
|
int v0 = imgraster[sizeX * y + x];
|
||||||
|
|
||||||
|
int rgb;
|
||||||
|
if (v0 != 0xffff)
|
||||||
|
rgb = 0xff000000 | v0; //(v0 << 8);
|
||||||
|
else
|
||||||
|
rgb = 0xff000000;
|
||||||
|
idata[y * sizeX + x] = rgb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageIO.write(argbImage, "png", new FileOutputStream(imageName));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadColors(String colors) {
|
||||||
|
if (DEBUG) System.out.println("colors=" + colors);
|
||||||
|
File colFile = new File(colors);
|
||||||
|
if (colFile.exists()) {
|
||||||
|
BufferedReader reader = null;
|
||||||
|
colorMap = new TreeMap<>();
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new FileReader(colors));
|
||||||
|
String line = reader.readLine();
|
||||||
|
|
||||||
|
while (line != null) {
|
||||||
|
if (DEBUG) System.out.println(line);
|
||||||
|
String[] sa = line.split(",");
|
||||||
|
if (!line.startsWith("#") && sa.length == 4) {
|
||||||
|
short e = Short.parseShort(sa[0].trim());
|
||||||
|
short r = Short.parseShort(sa[1].trim());
|
||||||
|
short g = Short.parseShort(sa[2].trim());
|
||||||
|
short b = Short.parseShort(sa[3].trim());
|
||||||
|
colorMap.put(e, new Color(r, g, b));
|
||||||
|
}
|
||||||
|
// read next line
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
colorMap = null;
|
||||||
|
} finally {
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("color file " + colors + " not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] getElevationPos(ElevationRaster srtm, int ilon, int ilat) {
|
||||||
|
double lon = ilon / 1000000. - 180.;
|
||||||
|
double lat = ilat / 1000000. - 90.;
|
||||||
|
|
||||||
|
double dcol = (lon - srtm.xllcorner) / srtm.cellsize - 0.5;
|
||||||
|
double drow = (lat - srtm.yllcorner) / srtm.cellsize - 0.5;
|
||||||
|
int row = (int) drow;
|
||||||
|
int col = (int) dcol;
|
||||||
|
if (col < 0) col = 0;
|
||||||
|
if (row < 0) row = 0;
|
||||||
|
|
||||||
|
return new double[]{drow, dcol};
|
||||||
|
}
|
||||||
|
|
||||||
|
private short get(ElevationRaster srtm, int r, int c) {
|
||||||
|
short e = srtm.eval_array[(srtm.nrows - 1 - r) * srtm.ncols + c];
|
||||||
|
if (e == Short.MIN_VALUE) missingData = true;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getElevationXY(ElevationRaster srtm, double drow, double dcol) {
|
||||||
|
int row = (int) drow;
|
||||||
|
int col = (int) dcol;
|
||||||
|
if (col < 0) col = 0;
|
||||||
|
if (col >= srtm.ncols - 1) col = srtm.ncols - 2;
|
||||||
|
if (row < 0) row = 0;
|
||||||
|
if (row >= srtm.nrows - 1) row = srtm.nrows - 2;
|
||||||
|
double wrow = drow - row;
|
||||||
|
double wcol = dcol - col;
|
||||||
|
missingData = false;
|
||||||
|
|
||||||
|
double eval = (1. - wrow) * (1. - wcol) * get(srtm, row, col)
|
||||||
|
+ (wrow) * (1. - wcol) * get(srtm, row + 1, col)
|
||||||
|
+ (1. - wrow) * (wcol) * get(srtm, row, col + 1)
|
||||||
|
+ (wrow) * (wcol) * get(srtm, row + 1, col + 1);
|
||||||
|
|
||||||
|
return missingData ? Short.MIN_VALUE : (short) (eval * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getColorForHeight(short h) {
|
||||||
|
if (colorMap == null) {
|
||||||
|
colorMap = new TreeMap<>();
|
||||||
|
colorMap.put((short) 0, new Color(102, 153, 153));
|
||||||
|
colorMap.put((short) 1, new Color(0, 102, 0));
|
||||||
|
colorMap.put((short) 500, new Color(251, 255, 128));
|
||||||
|
colorMap.put((short) 1200, new Color(224, 108, 31));
|
||||||
|
colorMap.put((short) 2500, new Color(200, 55, 55));
|
||||||
|
colorMap.put((short) 4000, new Color(215, 244, 244));
|
||||||
|
colorMap.put((short) 8000, new Color(255, 244, 244));
|
||||||
|
}
|
||||||
|
Color lastColor = null;
|
||||||
|
short lastKey = 0;
|
||||||
|
for (Entry<Short, Color> entry : colorMap.entrySet()) {
|
||||||
|
short key = entry.getKey();
|
||||||
|
Color value = entry.getValue();
|
||||||
|
if (key == h) return value.getRGB();
|
||||||
|
if (lastColor != null && lastKey < h && key > h) {
|
||||||
|
double between = (double) (h - lastKey) / (key - lastKey);
|
||||||
|
return mixColors(value, lastColor, between);
|
||||||
|
}
|
||||||
|
lastColor = value;
|
||||||
|
lastKey = key;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int mixColors(Color color1, Color color2, double percent) {
|
||||||
|
double inverse_percent = 1.0 - percent;
|
||||||
|
int redPart = (int) (color1.getRed() * percent + color2.getRed() * inverse_percent);
|
||||||
|
int greenPart = (int) (color1.getGreen() * percent + color2.getGreen() * inverse_percent);
|
||||||
|
int bluePart = (int) (color1.getBlue() * percent + color2.getBlue() * inverse_percent);
|
||||||
|
return new Color(redPart, greenPart, bluePart).getRGB();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ElevationRaster srtmForNode(int ilon, int ilat) throws Exception {
|
||||||
|
int srtmLonIdx = (ilon + 5000000) / 5000000;
|
||||||
|
int srtmLatIdx = (654999999 - ilat) / 5000000 - 100; // ugly negative rounding...
|
||||||
|
|
||||||
|
if (srtmLonIdx == lastSrtmLonIdx && srtmLatIdx == lastSrtmLatIdx) {
|
||||||
|
return lastSrtmRaster;
|
||||||
|
}
|
||||||
|
lastSrtmLonIdx = srtmLonIdx;
|
||||||
|
lastSrtmLatIdx = srtmLatIdx;
|
||||||
|
|
||||||
|
String slonidx = "0" + srtmLonIdx;
|
||||||
|
String slatidx = "0" + srtmLatIdx;
|
||||||
|
String filename = "srtm_" + slonidx.substring(slonidx.length() - 2) + "_" + slatidx.substring(slatidx.length() - 2);
|
||||||
|
|
||||||
|
lastSrtmRaster = srtmmap.get(filename);
|
||||||
|
if (lastSrtmRaster == null && !srtmmap.containsKey(filename)) {
|
||||||
|
File f = new File(new File(srtmdir), filename + ".bef");
|
||||||
|
if (f.exists()) {
|
||||||
|
System.out.println("*** reading: " + f);
|
||||||
|
try {
|
||||||
|
InputStream isc = new BufferedInputStream(new FileInputStream(f));
|
||||||
|
lastSrtmRaster = new ElevationRasterCoder().decodeRaster(isc);
|
||||||
|
isc.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("**** ERROR reading " + f + " ****");
|
||||||
|
}
|
||||||
|
srtmmap.put(filename, lastSrtmRaster);
|
||||||
|
return lastSrtmRaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
srtmmap.put(filename, lastSrtmRaster);
|
||||||
|
}
|
||||||
|
return lastSrtmRaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
if (args.length < 6) {
|
||||||
|
System.out.println("usage: java CreateLidarImage <lon> <lat> <srtm-folder> <imageFileName> <maxX> <maxY> <downscale> [type] [color_file]");
|
||||||
|
System.out.println("\nwhere: type = [bef|hgt] downscale = [1|2|4|..]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String format = args.length >= 8 ? args[7] : "bef";
|
||||||
|
String colors = args.length == 9 ? args[8] : null;
|
||||||
|
new CreateElevationRasterImage().createImage(Double.parseDouble(args[0]), Double.parseDouble(args[1]), args[2], args[3],
|
||||||
|
Integer.parseInt(args[4]), Integer.parseInt(args[5]), Integer.parseInt(args[6]), format, colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,38 +5,32 @@
|
||||||
*/
|
*/
|
||||||
package btools.mapcreator;
|
package btools.mapcreator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
|
|
||||||
import btools.util.CheapRuler;
|
import btools.util.CheapRuler;
|
||||||
|
|
||||||
public class DPFilter
|
public class DPFilter {
|
||||||
{
|
|
||||||
private static double dp_sql_threshold = 0.4 * 0.4;
|
private static double dp_sql_threshold = 0.4 * 0.4;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT
|
* for each node (except first+last), eventually set the DP_SURVIVOR_BIT
|
||||||
*/
|
*/
|
||||||
public static void doDPFilter( ArrayList<OsmNodeP> nodes )
|
public static void doDPFilter(List<OsmNodeP> nodes) {
|
||||||
{
|
|
||||||
int first = 0;
|
int first = 0;
|
||||||
int last = nodes.size() - 1;
|
int last = nodes.size() - 1;
|
||||||
while( first < last && (nodes.get(first+1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
|
while (first < last && (nodes.get(first + 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
|
||||||
{
|
|
||||||
first++;
|
first++;
|
||||||
}
|
}
|
||||||
while( first < last && (nodes.get(last-1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0 )
|
while (first < last && (nodes.get(last - 1).bits & OsmNodeP.DP_SURVIVOR_BIT) != 0) {
|
||||||
{
|
|
||||||
last--;
|
last--;
|
||||||
}
|
}
|
||||||
if ( last - first > 1 )
|
if (last - first > 1) {
|
||||||
{
|
|
||||||
doDPFilter(nodes, first, last);
|
doDPFilter(nodes, first, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void doDPFilter( ArrayList<OsmNodeP> nodes, int first, int last )
|
public static void doDPFilter(List<OsmNodeP> nodes, int first, int last) {
|
||||||
{
|
|
||||||
double maxSqDist = -1.;
|
double maxSqDist = -1.;
|
||||||
int index = -1;
|
int index = -1;
|
||||||
OsmNodeP p1 = nodes.get(first);
|
OsmNodeP p1 = nodes.get(first);
|
||||||
|
|
@ -48,36 +42,29 @@ public class DPFilter
|
||||||
double dx = (p2.ilon - p1.ilon) * dlon2m;
|
double dx = (p2.ilon - p1.ilon) * dlon2m;
|
||||||
double dy = (p2.ilat - p1.ilat) * dlat2m;
|
double dy = (p2.ilat - p1.ilat) * dlat2m;
|
||||||
double d2 = dx * dx + dy * dy;
|
double d2 = dx * dx + dy * dy;
|
||||||
for ( int i = first + 1; i < last; i++ )
|
for (int i = first + 1; i < last; i++) {
|
||||||
{
|
|
||||||
OsmNodeP p = nodes.get(i);
|
OsmNodeP p = nodes.get(i);
|
||||||
double t = 0.;
|
double t = 0.;
|
||||||
if ( d2 != 0f )
|
if (d2 != 0f) {
|
||||||
{
|
|
||||||
t = ((p.ilon - p1.ilon) * dlon2m * dx + (p.ilat - p1.ilat) * dlat2m * dy) / d2;
|
t = ((p.ilon - p1.ilon) * dlon2m * dx + (p.ilat - p1.ilat) * dlat2m * dy) / d2;
|
||||||
t = t > 1. ? 1. : (t < 0. ? 0. : t);
|
t = t > 1. ? 1. : (t < 0. ? 0. : t);
|
||||||
}
|
}
|
||||||
double dx2 = (p.ilon - (p1.ilon + t * (p2.ilon - p1.ilon))) * dlon2m;
|
double dx2 = (p.ilon - (p1.ilon + t * (p2.ilon - p1.ilon))) * dlon2m;
|
||||||
double dy2 = (p.ilat - (p1.ilat + t * (p2.ilat - p1.ilat))) * dlat2m;
|
double dy2 = (p.ilat - (p1.ilat + t * (p2.ilat - p1.ilat))) * dlat2m;
|
||||||
double sqDist = dx2 * dx2 + dy2 * dy2;
|
double sqDist = dx2 * dx2 + dy2 * dy2;
|
||||||
if ( sqDist > maxSqDist )
|
if (sqDist > maxSqDist) {
|
||||||
{
|
|
||||||
index = i;
|
index = i;
|
||||||
maxSqDist = sqDist;
|
maxSqDist = sqDist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( index >= 0 )
|
if (index >= 0) {
|
||||||
{
|
if (index - first > 1) {
|
||||||
if ( index - first > 1 )
|
|
||||||
{
|
|
||||||
doDPFilter(nodes, first, index);
|
doDPFilter(nodes, first, index);
|
||||||
}
|
}
|
||||||
if ( maxSqDist >= dp_sql_threshold )
|
if (maxSqDist >= dp_sql_threshold) {
|
||||||
{
|
|
||||||
nodes.get(index).bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
nodes.get(index).bits |= OsmNodeP.DP_SURVIVOR_BIT;
|
||||||
}
|
}
|
||||||
if ( last - index > 1 )
|
if (last - index > 1) {
|
||||||
{
|
|
||||||
doDPFilter(nodes, index, last);
|
doDPFilter(nodes, index, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
/**
|
||||||
|
* DatabasePseudoTagProvider reads Pseudo Tags from a database and adds them
|
||||||
|
* to the osm-data
|
||||||
|
*/
|
||||||
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
import btools.util.CompactLongMap;
|
||||||
|
import btools.util.FrozenLongMap;
|
||||||
|
|
||||||
|
public class DatabasePseudoTagProvider {
|
||||||
|
|
||||||
|
private long cntOsmWays = 0L;
|
||||||
|
private long cntWayModified = 0L;
|
||||||
|
|
||||||
|
private Map<String, Long> pseudoTagsFound = new HashMap<>();
|
||||||
|
|
||||||
|
FrozenLongMap<Map<String, String>> dbData;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String jdbcurl = args[0];
|
||||||
|
String filename = args[1];
|
||||||
|
|
||||||
|
try (Connection conn = DriverManager.getConnection(jdbcurl);
|
||||||
|
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
|
||||||
|
filename.endsWith(".gz") ? new GZIPOutputStream(new FileOutputStream(filename)) : new FileOutputStream(filename)))) {
|
||||||
|
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
|
||||||
|
System.out.println("DatabasePseudoTagProvider dumping data from " + jdbcurl + " to file " + filename);
|
||||||
|
|
||||||
|
bw.write("losmid;noise_class;river_class;forest_class;town_class;traffic_class\n");
|
||||||
|
|
||||||
|
String sql_all_tags = "SELECT * from all_tags";
|
||||||
|
try(PreparedStatement psAllTags = conn.prepareStatement(sql_all_tags)) {
|
||||||
|
|
||||||
|
psAllTags.setFetchSize(100);
|
||||||
|
|
||||||
|
// process the results
|
||||||
|
ResultSet rs = psAllTags.executeQuery();
|
||||||
|
|
||||||
|
long dbRows = 0L;
|
||||||
|
while (rs.next()) {
|
||||||
|
StringBuilder line = new StringBuilder();
|
||||||
|
line.append(rs.getLong("losmid"));
|
||||||
|
appendDBTag(line, rs, "noise_class");
|
||||||
|
appendDBTag(line, rs, "river_class");
|
||||||
|
appendDBTag(line, rs, "forest_class");
|
||||||
|
appendDBTag(line, rs, "town_class");
|
||||||
|
appendDBTag(line, rs, "traffic_class");
|
||||||
|
line.append('\n');
|
||||||
|
bw.write(line.toString());
|
||||||
|
dbRows++;
|
||||||
|
if (dbRows % 1000000L == 0L) {
|
||||||
|
System.out.println(".. from database: rows =" + dbRows);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException g) {
|
||||||
|
System.err.format("DatabasePseudoTagProvider execute sql .. SQL State: %s\n%s\n", g.getSQLState(), g.getMessage());
|
||||||
|
System.exit(1);
|
||||||
|
} catch (Exception f) {
|
||||||
|
f.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendDBTag(StringBuilder sb, ResultSet rs, String name) throws SQLException {
|
||||||
|
sb.append(';');
|
||||||
|
String v = rs.getString(name);
|
||||||
|
if (v != null) {
|
||||||
|
sb.append(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabasePseudoTagProvider(String filename) {
|
||||||
|
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(
|
||||||
|
filename.endsWith(".gz") ? new GZIPInputStream(new FileInputStream(filename)) : new FileInputStream(filename)))) {
|
||||||
|
|
||||||
|
System.out.println("DatabasePseudoTagProvider reading from file: " + filename);
|
||||||
|
|
||||||
|
br.readLine(); // skip header line
|
||||||
|
|
||||||
|
Map<Map<String, String>, Map<String, String>> mapUnifier = new HashMap<>();
|
||||||
|
CompactLongMap<Map<String, String>> data = new CompactLongMap<>();
|
||||||
|
|
||||||
|
long dbRows = 0L;
|
||||||
|
for (;;) {
|
||||||
|
String line = br.readLine();
|
||||||
|
if (line == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
List<String> tokens = tokenize(line);
|
||||||
|
long osm_id = Long.parseLong(tokens.get(0));
|
||||||
|
Map<String, String> row = new HashMap<>(5);
|
||||||
|
addTag(row, tokens.get(1), "estimated_noise_class");
|
||||||
|
addTag(row, tokens.get(2), "estimated_river_class");
|
||||||
|
addTag(row, tokens.get(3), "estimated_forest_class");
|
||||||
|
addTag(row, tokens.get(4), "estimated_town_class");
|
||||||
|
addTag(row, tokens.get(5), "estimated_traffic_class");
|
||||||
|
|
||||||
|
// apply the instance-unifier for the row-map
|
||||||
|
Map<String, String> knownRow = mapUnifier.get(row);
|
||||||
|
if (knownRow != null) {
|
||||||
|
row = knownRow;
|
||||||
|
} else {
|
||||||
|
mapUnifier.put(row, row);
|
||||||
|
}
|
||||||
|
data.put(osm_id, row);
|
||||||
|
dbRows++;
|
||||||
|
|
||||||
|
if (dbRows % 1000000L == 0L) {
|
||||||
|
System.out.println(".. from database: rows =" + data.size() + " unique rows=" + mapUnifier.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("freezing result map..");
|
||||||
|
dbData = new FrozenLongMap<>(data);
|
||||||
|
System.out.println("read from file: rows =" + dbData.size() + " unique rows=" + mapUnifier.size());
|
||||||
|
} catch (Exception f) {
|
||||||
|
f.printStackTrace();
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use own tokenizer as String.split, StringTokenizer
|
||||||
|
// etc. have issues with empty elements
|
||||||
|
private List<String> tokenize(String s) {
|
||||||
|
List<String> l = new ArrayList<>();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i=0; i<s.length(); i++) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
if (c == ';') {
|
||||||
|
l.add(sb.toString());
|
||||||
|
sb.setLength(0);
|
||||||
|
} else {
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.add(sb.toString());
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addTag(Map<String, String> row, String s, String name) {
|
||||||
|
if (!s.isEmpty()) {
|
||||||
|
row.put(name, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTags(long osm_id, Map<String, String> map) {
|
||||||
|
|
||||||
|
if (map == null || !map.containsKey("highway")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cntOsmWays++;
|
||||||
|
if ((cntOsmWays % 1000000L) == 0) {
|
||||||
|
String out = "Osm Ways processed=" + cntOsmWays + " way modifs=" + cntWayModified;
|
||||||
|
for (String key : pseudoTagsFound.keySet()) {
|
||||||
|
out += " " + key + "=" + pseudoTagsFound.get(key);
|
||||||
|
}
|
||||||
|
System.out.println(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> dbTags = dbData.get(osm_id);
|
||||||
|
if (dbTags == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cntWayModified++;
|
||||||
|
for (String key : dbTags.keySet()) {
|
||||||
|
map.put(key, dbTags.get(key));
|
||||||
|
Long cnt = pseudoTagsFound.get(key);
|
||||||
|
if (cnt == null) {
|
||||||
|
cnt = 0L;
|
||||||
|
}
|
||||||
|
pseudoTagsFound.put(key, cnt + 1L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import btools.util.ReducedMedianFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Container for a elevation raster + it's meta-data
|
||||||
|
*
|
||||||
|
* @author ab
|
||||||
|
*/
|
||||||
|
public class ElevationRaster {
|
||||||
|
public int ncols;
|
||||||
|
public int nrows;
|
||||||
|
public boolean halfcol;
|
||||||
|
public double xllcorner;
|
||||||
|
public double yllcorner;
|
||||||
|
public double cellsize;
|
||||||
|
public short[] eval_array;
|
||||||
|
public short noDataValue;
|
||||||
|
|
||||||
|
public boolean usingWeights = false;
|
||||||
|
|
||||||
|
private boolean missingData = false;
|
||||||
|
|
||||||
|
public short getElevation(int ilon, int ilat) {
|
||||||
|
double lon = ilon / 1000000. - 180.;
|
||||||
|
double lat = ilat / 1000000. - 90.;
|
||||||
|
|
||||||
|
if (usingWeights) {
|
||||||
|
return getElevationFromShiftWeights(lon, lat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no weights calculated, use 2d linear interpolation
|
||||||
|
double dcol = (lon - xllcorner) / cellsize - 0.5;
|
||||||
|
double drow = (lat - yllcorner) / cellsize - 0.5;
|
||||||
|
int row = (int) drow;
|
||||||
|
int col = (int) dcol;
|
||||||
|
if (col < 0) col = 0;
|
||||||
|
if (col >= ncols - 1) col = ncols - 2;
|
||||||
|
if (row < 0) row = 0;
|
||||||
|
if (row >= nrows - 1) row = nrows - 2;
|
||||||
|
double wrow = drow - row;
|
||||||
|
double wcol = dcol - col;
|
||||||
|
missingData = false;
|
||||||
|
|
||||||
|
// System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col );
|
||||||
|
double eval = (1. - wrow) * (1. - wcol) * get(row, col)
|
||||||
|
+ (wrow) * (1. - wcol) * get(row + 1, col)
|
||||||
|
+ (1. - wrow) * (wcol) * get(row, col + 1)
|
||||||
|
+ (wrow) * (wcol) * get(row + 1, col + 1);
|
||||||
|
// System.out.println( "eval=" + eval );
|
||||||
|
return missingData ? Short.MIN_VALUE : (short) (eval * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private short get(int r, int c) {
|
||||||
|
short e = eval_array[(nrows - 1 - r) * ncols + c];
|
||||||
|
if (e == Short.MIN_VALUE) missingData = true;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private short getElevationFromShiftWeights(double lon, double lat) {
|
||||||
|
// calc lat-idx and -weight
|
||||||
|
double alat = lat < 0. ? -lat : lat;
|
||||||
|
alat /= 5.;
|
||||||
|
int latIdx = (int) alat;
|
||||||
|
double wlat = alat - latIdx;
|
||||||
|
|
||||||
|
double dcol = (lon - xllcorner) / cellsize;
|
||||||
|
double drow = (lat - yllcorner) / cellsize;
|
||||||
|
int row = (int) drow;
|
||||||
|
int col = (int) dcol;
|
||||||
|
|
||||||
|
double dgx = (dcol - col) * gridSteps;
|
||||||
|
double dgy = (drow - row) * gridSteps;
|
||||||
|
|
||||||
|
// System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col );
|
||||||
|
|
||||||
|
int gx = (int) (dgx);
|
||||||
|
int gy = (int) (dgy);
|
||||||
|
|
||||||
|
double wx = dgx - gx;
|
||||||
|
double wy = dgy - gy;
|
||||||
|
|
||||||
|
double w00 = (1. - wx) * (1. - wy);
|
||||||
|
double w01 = (1. - wx) * (wy);
|
||||||
|
double w10 = (wx) * (1. - wy);
|
||||||
|
double w11 = (wx) * (wy);
|
||||||
|
|
||||||
|
Weights[][] w0 = getWeights(latIdx);
|
||||||
|
Weights[][] w1 = getWeights(latIdx + 1);
|
||||||
|
|
||||||
|
missingData = false;
|
||||||
|
|
||||||
|
double m0 = w00 * getElevation(w0[gx][gy], row, col)
|
||||||
|
+ w01 * getElevation(w0[gx][gy + 1], row, col)
|
||||||
|
+ w10 * getElevation(w0[gx + 1][gy], row, col)
|
||||||
|
+ w11 * getElevation(w0[gx + 1][gy + 1], row, col);
|
||||||
|
double m1 = w00 * getElevation(w1[gx][gy], row, col)
|
||||||
|
+ w01 * getElevation(w1[gx][gy + 1], row, col)
|
||||||
|
+ w10 * getElevation(w1[gx + 1][gy], row, col)
|
||||||
|
+ w11 * getElevation(w1[gx + 1][gy + 1], row, col);
|
||||||
|
|
||||||
|
if (missingData) return Short.MIN_VALUE;
|
||||||
|
double m = (1. - wlat) * m0 + wlat * m1;
|
||||||
|
return (short) (m * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReducedMedianFilter rmf = new ReducedMedianFilter(256);
|
||||||
|
|
||||||
|
private double getElevation(Weights w, int row, int col) {
|
||||||
|
if (missingData) {
|
||||||
|
return 0.;
|
||||||
|
}
|
||||||
|
int nx = w.nx;
|
||||||
|
int ny = w.ny;
|
||||||
|
int mx = nx / 2; // mean pixels
|
||||||
|
int my = ny / 2;
|
||||||
|
|
||||||
|
// System.out.println( "nx="+ nx + " ny=" + ny );
|
||||||
|
|
||||||
|
rmf.reset();
|
||||||
|
|
||||||
|
for (int ix = 0; ix < nx; ix++) {
|
||||||
|
for (int iy = 0; iy < ny; iy++) {
|
||||||
|
short val = get(row + iy - my, col + ix - mx);
|
||||||
|
rmf.addSample(w.getWeight(ix, iy), val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return missingData ? 0. : rmf.calcEdgeReducedMedian(filterCenterFraction);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class Weights {
|
||||||
|
int nx;
|
||||||
|
int ny;
|
||||||
|
double[] weights;
|
||||||
|
long total = 0;
|
||||||
|
|
||||||
|
Weights(int nx, int ny) {
|
||||||
|
this.nx = nx;
|
||||||
|
this.ny = ny;
|
||||||
|
weights = new double[nx * ny];
|
||||||
|
}
|
||||||
|
|
||||||
|
void inc(int ix, int iy) {
|
||||||
|
weights[iy * nx + ix] += 1.;
|
||||||
|
total++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void normalize(boolean verbose) {
|
||||||
|
for (int iy = 0; iy < ny; iy++) {
|
||||||
|
StringBuilder sb = verbose ? new StringBuilder() : null;
|
||||||
|
for (int ix = 0; ix < nx; ix++) {
|
||||||
|
weights[iy * nx + ix] /= total;
|
||||||
|
if (sb != null) {
|
||||||
|
int iweight = (int) (1000 * weights[iy * nx + ix] + 0.5);
|
||||||
|
String sval = " " + iweight;
|
||||||
|
sb.append(sval.substring(sval.length() - 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sb != null) {
|
||||||
|
System.out.println(sb);
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double getWeight(int ix, int iy) {
|
||||||
|
return weights[iy * nx + ix];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int gridSteps = 10;
|
||||||
|
private static Weights[][][] allShiftWeights = new Weights[17][][];
|
||||||
|
|
||||||
|
private static double filterCenterFraction = 0.2;
|
||||||
|
private static double filterDiscRadius = 4.999; // in pixels
|
||||||
|
|
||||||
|
static {
|
||||||
|
String sRadius = System.getProperty("filterDiscRadius");
|
||||||
|
if (sRadius != null && sRadius.length() > 0) {
|
||||||
|
filterDiscRadius = Integer.parseInt(sRadius);
|
||||||
|
System.out.println("using filterDiscRadius = " + filterDiscRadius);
|
||||||
|
}
|
||||||
|
String sFraction = System.getProperty("filterCenterFraction");
|
||||||
|
if (sFraction != null && sFraction.length() > 0) {
|
||||||
|
filterCenterFraction = Integer.parseInt(sFraction) / 100.;
|
||||||
|
System.out.println("using filterCenterFraction = " + filterCenterFraction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// calculate interpolation weights from the overlap of a probe disc of given radius at given latitude
|
||||||
|
// ( latIndex = 0 -> 0 deg, latIndex = 16 -> 80 degree)
|
||||||
|
|
||||||
|
private static Weights[][] getWeights(int latIndex) {
|
||||||
|
int idx = latIndex < 16 ? latIndex : 16;
|
||||||
|
|
||||||
|
Weights[][] res = allShiftWeights[idx];
|
||||||
|
if (res == null) {
|
||||||
|
res = calcWeights(idx);
|
||||||
|
allShiftWeights[idx] = res;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Weights[][] calcWeights(int latIndex) {
|
||||||
|
double coslat = Math.cos(latIndex * 5. / 57.3);
|
||||||
|
|
||||||
|
// radius in pixel units
|
||||||
|
double ry = filterDiscRadius;
|
||||||
|
double rx = ry / coslat;
|
||||||
|
|
||||||
|
// gridsize is 2*radius + 1 cell
|
||||||
|
int nx = ((int) rx) * 2 + 3;
|
||||||
|
int ny = ((int) ry) * 2 + 3;
|
||||||
|
|
||||||
|
System.out.println("nx=" + nx + " ny=" + ny);
|
||||||
|
|
||||||
|
int mx = nx / 2; // mean pixels
|
||||||
|
int my = ny / 2;
|
||||||
|
|
||||||
|
// create a matrix for the relative intergrid-position
|
||||||
|
|
||||||
|
Weights[][] shiftWeights = new Weights[gridSteps + 1][];
|
||||||
|
|
||||||
|
// loop the intergrid-position
|
||||||
|
for (int gx = 0; gx <= gridSteps; gx++) {
|
||||||
|
shiftWeights[gx] = new Weights[gridSteps + 1];
|
||||||
|
double x0 = mx + ((double) gx) / gridSteps;
|
||||||
|
|
||||||
|
for (int gy = 0; gy <= gridSteps; gy++) {
|
||||||
|
double y0 = my + ((double) gy) / gridSteps;
|
||||||
|
|
||||||
|
// create the weight-matrix
|
||||||
|
Weights weights = new Weights(nx, ny);
|
||||||
|
shiftWeights[gx][gy] = weights;
|
||||||
|
|
||||||
|
double sampleStep = 0.001;
|
||||||
|
|
||||||
|
for (double x = -1. + sampleStep / 2.; x < 1.; x += sampleStep) {
|
||||||
|
double mx2 = 1. - x * x;
|
||||||
|
|
||||||
|
int x_idx = (int) (x0 + x * rx);
|
||||||
|
|
||||||
|
for (double y = -1. + sampleStep / 2.; y < 1.; y += sampleStep) {
|
||||||
|
if (y * y > mx2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// we are in the ellipse, see what pixel we are on
|
||||||
|
int y_idx = (int) (y0 + y * ry);
|
||||||
|
weights.inc(x_idx, y_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
weights.normalize(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shiftWeights;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ncols + "," + nrows + "," + halfcol + "," + xllcorner + "," + yllcorner + "," + cellsize + "," + noDataValue + "," + usingWeights;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
package btools.mapcreator;
|
package btools.mapcreator;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.DataInputStream;
|
||||||
import btools.util.*;
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import btools.util.MixCoderDataInputStream;
|
||||||
|
import btools.util.MixCoderDataOutputStream;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Encode/decode a raster
|
// Encode/decode a raster
|
||||||
//
|
//
|
||||||
|
|
||||||
public class RasterCoder
|
public class ElevationRasterCoder {
|
||||||
{
|
public void encodeRaster(ElevationRaster raster, OutputStream os) throws IOException {
|
||||||
public void encodeRaster(SrtmRaster raster, OutputStream os) throws IOException
|
|
||||||
{
|
|
||||||
DataOutputStream dos = new DataOutputStream(os);
|
DataOutputStream dos = new DataOutputStream(os);
|
||||||
|
|
||||||
long t0 = System.currentTimeMillis();
|
long t0 = System.currentTimeMillis();
|
||||||
|
|
@ -29,13 +33,12 @@ public class RasterCoder
|
||||||
System.out.println("finished encoding in " + (t1 - t0) + " ms");
|
System.out.println("finished encoding in " + (t1 - t0) + " ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
public SrtmRaster decodeRaster(InputStream is) throws IOException
|
public ElevationRaster decodeRaster(InputStream is) throws IOException {
|
||||||
{
|
|
||||||
DataInputStream dis = new DataInputStream(is);
|
DataInputStream dis = new DataInputStream(is);
|
||||||
|
|
||||||
long t0 = System.currentTimeMillis();
|
long t0 = System.currentTimeMillis();
|
||||||
|
|
||||||
SrtmRaster raster = new SrtmRaster();
|
ElevationRaster raster = new ElevationRaster();
|
||||||
raster.ncols = dis.readInt();
|
raster.ncols = dis.readInt();
|
||||||
raster.nrows = dis.readInt();
|
raster.nrows = dis.readInt();
|
||||||
raster.halfcol = dis.readBoolean();
|
raster.halfcol = dis.readBoolean();
|
||||||
|
|
@ -47,7 +50,7 @@ public class RasterCoder
|
||||||
|
|
||||||
_decodeRaster(raster, is);
|
_decodeRaster(raster, is);
|
||||||
|
|
||||||
raster.usingWeights = raster.ncols > 6001;
|
raster.usingWeights = false; // raster.ncols > 6001;
|
||||||
|
|
||||||
long t1 = System.currentTimeMillis();
|
long t1 = System.currentTimeMillis();
|
||||||
System.out.println("finished decoding in " + (t1 - t0) + " ms ncols=" + raster.ncols + " nrows=" + raster.nrows);
|
System.out.println("finished decoding in " + (t1 - t0) + " ms ncols=" + raster.ncols + " nrows=" + raster.nrows);
|
||||||
|
|
@ -55,26 +58,20 @@ public class RasterCoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void _encodeRaster(SrtmRaster raster, OutputStream os) throws IOException
|
private void _encodeRaster(ElevationRaster raster, OutputStream os) throws IOException {
|
||||||
{
|
|
||||||
MixCoderDataOutputStream mco = new MixCoderDataOutputStream(os);
|
MixCoderDataOutputStream mco = new MixCoderDataOutputStream(os);
|
||||||
int nrows = raster.nrows;
|
int nrows = raster.nrows;
|
||||||
int ncols = raster.ncols;
|
int ncols = raster.ncols;
|
||||||
short[] pixels = raster.eval_array;
|
short[] pixels = raster.eval_array;
|
||||||
int colstep = raster.halfcol ? 2 : 1;
|
int colstep = raster.halfcol ? 2 : 1;
|
||||||
|
|
||||||
for (int row = 0; row < nrows; row++)
|
for (int row = 0; row < nrows; row++) {
|
||||||
{
|
|
||||||
short lastval = Short.MIN_VALUE; // nodata
|
short lastval = Short.MIN_VALUE; // nodata
|
||||||
for (int col = 0; col < ncols; col += colstep )
|
for (int col = 0; col < ncols; col += colstep) {
|
||||||
{
|
|
||||||
short val = pixels[row * ncols + col];
|
short val = pixels[row * ncols + col];
|
||||||
if ( val == -32766 )
|
if (val == -32766) {
|
||||||
{
|
|
||||||
val = lastval; // replace remaining (border) skips
|
val = lastval; // replace remaining (border) skips
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
lastval = val;
|
lastval = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,37 +83,30 @@ public class RasterCoder
|
||||||
mco.flush();
|
mco.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _decodeRaster(SrtmRaster raster, InputStream is) throws IOException
|
private void _decodeRaster(ElevationRaster raster, InputStream is) throws IOException {
|
||||||
{
|
|
||||||
MixCoderDataInputStream mci = new MixCoderDataInputStream(is);
|
MixCoderDataInputStream mci = new MixCoderDataInputStream(is);
|
||||||
int nrows = raster.nrows;
|
int nrows = raster.nrows;
|
||||||
int ncols = raster.ncols;
|
int ncols = raster.ncols;
|
||||||
short[] pixels = raster.eval_array;
|
short[] pixels = raster.eval_array;
|
||||||
int colstep = raster.halfcol ? 2 : 1;
|
int colstep = raster.halfcol ? 2 : 1;
|
||||||
|
|
||||||
for (int row = 0; row < nrows; row++)
|
for (int row = 0; row < nrows; row++) {
|
||||||
{
|
for (int col = 0; col < ncols; col += colstep) {
|
||||||
for (int col = 0; col < ncols; col += colstep )
|
|
||||||
{
|
|
||||||
int code = mci.readMixed();
|
int code = mci.readMixed();
|
||||||
|
|
||||||
// remap nodata
|
// remap nodata
|
||||||
int v30 = code == -1 ? Short.MIN_VALUE : (code < 0 ? code + 1 : code);
|
int v30 = code == -1 ? Short.MIN_VALUE : (code < 0 ? code + 1 : code);
|
||||||
if ( raster.usingWeights && v30 > -32766 )
|
if (raster.usingWeights && v30 > -32766) {
|
||||||
{
|
|
||||||
v30 *= 2;
|
v30 *= 2;
|
||||||
}
|
}
|
||||||
pixels[row * ncols + col] = (short) (v30);
|
pixels[row * ncols + col] = (short) (v30);
|
||||||
}
|
}
|
||||||
if ( raster.halfcol )
|
if (raster.halfcol) {
|
||||||
{
|
for (int col = 1; col < ncols - 1; col += colstep) {
|
||||||
for (int col = 1; col < ncols-1; col += colstep )
|
|
||||||
{
|
|
||||||
int l = (int) pixels[row * ncols + col - 1];
|
int l = (int) pixels[row * ncols + col - 1];
|
||||||
int r = (int) pixels[row * ncols + col + 1];
|
int r = (int) pixels[row * ncols + col + 1];
|
||||||
short v30 = Short.MIN_VALUE; // nodata
|
short v30 = Short.MIN_VALUE; // nodata
|
||||||
if ( l > -32766 && r > -32766 )
|
if (l > -32766 && r > -32766) {
|
||||||
{
|
|
||||||
v30 = (short) ((l + r) / 2);
|
v30 = (short) ((l + r) / 2);
|
||||||
}
|
}
|
||||||
pixels[row * ncols + col] = v30;
|
pixels[row * ncols + col] = v30;
|
||||||
|
|
@ -0,0 +1,545 @@
|
||||||
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
public class ElevationRasterTileConverter {
|
||||||
|
|
||||||
|
public static final boolean DEBUG = false;
|
||||||
|
|
||||||
|
public static final short NODATA2 = -32767; // hgt-formats nodata
|
||||||
|
public static final short NODATA = Short.MIN_VALUE;
|
||||||
|
|
||||||
|
private static final String HGT_FILE_EXT = ".hgt";
|
||||||
|
private static final int HGT_BORDER_OVERLAP = 1;
|
||||||
|
private static final int HGT_3ASEC_ROWS = 1201; // 3 arc second resolution (90m)
|
||||||
|
private static final int HGT_3ASEC_FILE_SIZE = HGT_3ASEC_ROWS * HGT_3ASEC_ROWS * Short.BYTES;
|
||||||
|
private static final int HGT_1ASEC_ROWS = 3601; // 1 arc second resolution (30m)
|
||||||
|
private static final int SRTM3_ROW_LENGTH = 1200; // number of elevation values per line
|
||||||
|
private static final int SRTM1_ROW_LENGTH = 3600;
|
||||||
|
private static final boolean SRTM_NO_ZERO = true;
|
||||||
|
|
||||||
|
private int NROWS;
|
||||||
|
private int NCOLS;
|
||||||
|
private int ROW_LENGTH;
|
||||||
|
private short[] imagePixels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This generates elevation raster files with a 5x5 degree scope
|
||||||
|
* The output can be for 1sec (18000x18000 points)
|
||||||
|
* or for 3sec (6000x6000 points)
|
||||||
|
* When using 1sec input files a not found area can be called from 3sec pool
|
||||||
|
* The input can be 1x1 degree 1sec/3sec hgt files (also packed as zip)
|
||||||
|
* or 5x5 degree 3sec asc files (delivered as zip)
|
||||||
|
* Arguments for single file generation:
|
||||||
|
* ElevationRasterTileConverter <srtm-filename | all> <hgt-data-dir> <srtm-output-dir> [arc seconds (1 or 3,default=3)] [hgt-fallback-data-dir]
|
||||||
|
* Samples
|
||||||
|
* $ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt3sec ./srtm/srtm3_bef
|
||||||
|
* $ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt1sec ./srtm/srtm1_bef 1
|
||||||
|
* $ ... ElevationRasterTileConverter srtm_34_-1 ./srtm/hgt1sec ./srtm/srtm1_bef 1 ./srtm/hgt3sec
|
||||||
|
* <p>
|
||||||
|
* Arguments for multi file generation (world wide):
|
||||||
|
* $ ... ElevationRasterTileConverter all ./srtm/hgt3sec ./srtm/srtm3_bef
|
||||||
|
* $ ... ElevationRasterTileConverter all ./srtm/hgt1sec ./srtm/srtm1_bef 1
|
||||||
|
* $ ... ElevationRasterTileConverter all ./srtm/hgt1sec ./srtm/srtm1_bef 1 ./srtm/hgt3sec
|
||||||
|
*
|
||||||
|
* @param args
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
if (args.length == 3 || args.length == 4 || args.length == 5) {
|
||||||
|
String filename90 = args[0];
|
||||||
|
if ("all".equals(filename90)) {
|
||||||
|
//if (DEBUG)
|
||||||
|
System.out.println("raster convert all ");
|
||||||
|
new ElevationRasterTileConverter().doConvertAll(args[1], args[2], (args.length > 3 ? args[3] : null), (args.length == 5 ? args[4] : null));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// old filenames only
|
||||||
|
String filename30 = filename90 + ".bef"; //filename90.substring(0, filename90.length() - 3) + "bef";
|
||||||
|
|
||||||
|
int srtmLonIdx = Integer.parseInt(filename90.substring(5, 7).toLowerCase());
|
||||||
|
int srtmLatIdx = Integer.parseInt(filename90.substring(8, 10).toLowerCase());
|
||||||
|
|
||||||
|
int ilon_base = (srtmLonIdx - 1) * 5 - 180;
|
||||||
|
int ilat_base = 150 - srtmLatIdx * 5 - 90;
|
||||||
|
int row_length = SRTM3_ROW_LENGTH;
|
||||||
|
String fallbackdir = null;
|
||||||
|
if (args.length > 3) {
|
||||||
|
row_length = (Integer.parseInt(args[3]) == 1 ? SRTM1_ROW_LENGTH : SRTM3_ROW_LENGTH);
|
||||||
|
fallbackdir = (args.length == 5 ? args[4] : null);
|
||||||
|
}
|
||||||
|
//if (DEBUG)
|
||||||
|
System.out.println("raster convert " + ilon_base + " " + ilat_base + " from " + srtmLonIdx + " " + srtmLatIdx + " f: " + filename90 + " rowl " + row_length);
|
||||||
|
|
||||||
|
new ElevationRasterTileConverter().doConvert(args[1], ilon_base, ilat_base, args[2] + "/" + filename30, row_length, fallbackdir);
|
||||||
|
} else {
|
||||||
|
System.out.println("usage: java <srtm-filename> <hgt-data-dir> <srtm-output-dir> [arc seconds (1 or 3,default=3)] [hgt-fallback-data-dir]");
|
||||||
|
System.out.println("or java all <hgt-data-dir> <srtm-output-dir> [arc seconds (1 or 3, default=3)] [hgt-fallback-data-dir]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doConvertAll(String hgtdata, String outdir, String rlen, String hgtfallbackdata) throws Exception {
|
||||||
|
int row_length = SRTM3_ROW_LENGTH;
|
||||||
|
if (rlen != null) {
|
||||||
|
row_length = (Integer.parseInt(rlen) == 1 ? SRTM1_ROW_LENGTH : SRTM3_ROW_LENGTH);
|
||||||
|
}
|
||||||
|
String filename30;
|
||||||
|
for (int ilon_base = -180; ilon_base < 180; ilon_base += 5) {
|
||||||
|
for (int ilat_base = 85; ilat_base > -90; ilat_base -= 5) {
|
||||||
|
if (PosUnifier.UseRasterRd5FileName) {
|
||||||
|
filename30 = genFilenameRd5(ilon_base, ilat_base);
|
||||||
|
} else {
|
||||||
|
filename30 = genFilenameOld(ilon_base, ilat_base);
|
||||||
|
}
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("lidar convert all: " + filename30);
|
||||||
|
doConvert(hgtdata, ilon_base, ilat_base, outdir + "/" + filename30, row_length, hgtfallbackdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String genFilenameOld(int ilon_base, int ilat_base) {
|
||||||
|
int srtmLonIdx = ((ilon_base + 180) / 5) + 1;
|
||||||
|
int srtmLatIdx = (60 - ilat_base) / 5;
|
||||||
|
return String.format(Locale.US, "srtm_%02d_%02d.bef", srtmLonIdx, srtmLatIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String genFilenameRd5(int ilon_base, int ilat_base) {
|
||||||
|
return String.format("srtm_%s_%s.bef", ilon_base < 0 ? "W" + (-ilon_base) : "E" + ilon_base,
|
||||||
|
ilat_base < 0 ? "S" + (-ilat_base) : "N" + ilat_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readHgtZip(String filename, int rowOffset, int colOffset, int row_length, int scale) throws Exception {
|
||||||
|
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(filename)));
|
||||||
|
try {
|
||||||
|
for (; ; ) {
|
||||||
|
ZipEntry ze = zis.getNextEntry();
|
||||||
|
if (ze == null) break;
|
||||||
|
if (ze.getName().toLowerCase().endsWith(HGT_FILE_EXT)) {
|
||||||
|
readHgtFromStream(zis, rowOffset, colOffset, row_length, scale);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readHgtFromStream(InputStream is, int rowOffset, int colOffset, int rowLength, int scale)
|
||||||
|
throws Exception {
|
||||||
|
DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
|
||||||
|
for (int ir = 0; ir < rowLength; ir++) {
|
||||||
|
int row = rowOffset + ir * scale;
|
||||||
|
|
||||||
|
for (int ic = 0; ic < rowLength; ic++) {
|
||||||
|
int col = colOffset + ic * scale;
|
||||||
|
|
||||||
|
int i1 = dis.read(); // msb first!
|
||||||
|
int i0 = dis.read();
|
||||||
|
|
||||||
|
if (i0 == -1 || i1 == -1)
|
||||||
|
throw new RuntimeException("unexpected end of file reading hgt entry!");
|
||||||
|
|
||||||
|
short val = (short) ((i1 << 8) | i0);
|
||||||
|
|
||||||
|
if (val == NODATA2) {
|
||||||
|
val = NODATA;
|
||||||
|
}
|
||||||
|
if (scale == 3) {
|
||||||
|
setPixel(row, col, val);
|
||||||
|
setPixel(row + 1, col, val);
|
||||||
|
setPixel(row + 2, col, val);
|
||||||
|
setPixel(row, col + 1, val);
|
||||||
|
setPixel(row + 1, col + 1, val);
|
||||||
|
setPixel(row + 2, col + 1, val);
|
||||||
|
setPixel(row, col + 2, val);
|
||||||
|
setPixel(row + 1, col + 2, val);
|
||||||
|
setPixel(row + 2, col + 2, val);
|
||||||
|
} else {
|
||||||
|
setPixel(row, col, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readHgtFile(File file, int rowOffset, int colOffset, int row_length, int scale)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("read: " + file + " " + row_length);
|
||||||
|
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
try {
|
||||||
|
readHgtFromStream(fis, rowOffset, colOffset, row_length, scale);
|
||||||
|
} finally {
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private void readFallbackFile(File file, int rowOffset, int colOffset, int row_length)
|
||||||
|
throws Exception {
|
||||||
|
int rowLength;
|
||||||
|
int scale;
|
||||||
|
if (file.length() > HGT_3ASEC_FILE_SIZE) {
|
||||||
|
rowLength = HGT_1ASEC_ROWS;
|
||||||
|
scale = 1;
|
||||||
|
} else {
|
||||||
|
rowLength = HGT_3ASEC_ROWS;
|
||||||
|
scale = 3;
|
||||||
|
}
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("read fallback: " + file + " " + rowLength);
|
||||||
|
|
||||||
|
FileInputStream fis = new FileInputStream(file);
|
||||||
|
try {
|
||||||
|
readHgtFromStream(fis, rowOffset, colOffset, rowLength, scale);
|
||||||
|
} finally {
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void readAscZip(File file, ElevationRaster raster) throws Exception {
|
||||||
|
|
||||||
|
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(file)));
|
||||||
|
try {
|
||||||
|
for (; ; ) {
|
||||||
|
ZipEntry ze = zis.getNextEntry();
|
||||||
|
if (ze.getName().endsWith(".asc")) {
|
||||||
|
readAscFromStream(zis, raster);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String secondToken(String s) {
|
||||||
|
StringTokenizer tk = new StringTokenizer(s, " ");
|
||||||
|
tk.nextToken();
|
||||||
|
return tk.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readAscFromStream(InputStream is, ElevationRaster raster) throws Exception {
|
||||||
|
BufferedReader br = new BufferedReader(new InputStreamReader(is));
|
||||||
|
int linenr = 0;
|
||||||
|
for (; ; ) {
|
||||||
|
linenr++;
|
||||||
|
if (linenr <= 6) {
|
||||||
|
String line = br.readLine();
|
||||||
|
if (linenr == 1)
|
||||||
|
raster.ncols = Integer.parseInt(secondToken(line));
|
||||||
|
else if (linenr == 2)
|
||||||
|
raster.nrows = Integer.parseInt(secondToken(line));
|
||||||
|
else if (linenr == 3)
|
||||||
|
raster.xllcorner = Double.parseDouble(secondToken(line));
|
||||||
|
else if (linenr == 4)
|
||||||
|
raster.yllcorner = Double.parseDouble(secondToken(line));
|
||||||
|
else if (linenr == 5)
|
||||||
|
raster.cellsize = Double.parseDouble(secondToken(line));
|
||||||
|
else if (linenr == 6) {
|
||||||
|
// nodata ignored here ( < -250 assumed nodata... )
|
||||||
|
// raster.noDataValue = Short.parseShort( secondToken( line ) );
|
||||||
|
raster.eval_array = new short[raster.ncols * raster.nrows];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int row = 0;
|
||||||
|
int col = 0;
|
||||||
|
int n = 0;
|
||||||
|
boolean negative = false;
|
||||||
|
for (; ; ) {
|
||||||
|
int c = br.read();
|
||||||
|
if (c < 0)
|
||||||
|
break;
|
||||||
|
if (c == ' ') {
|
||||||
|
if (negative)
|
||||||
|
n = -n;
|
||||||
|
short val = n < -250 ? Short.MIN_VALUE : (short) (n);
|
||||||
|
|
||||||
|
raster.eval_array[row * raster.ncols + col] = val;
|
||||||
|
if (++col == raster.ncols) {
|
||||||
|
col = 0;
|
||||||
|
++row;
|
||||||
|
}
|
||||||
|
n = 0;
|
||||||
|
negative = false;
|
||||||
|
} else if (c >= '0' && c <= '9') {
|
||||||
|
n = 10 * n + (c - '0');
|
||||||
|
} else if (c == '-') {
|
||||||
|
negative = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
br.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setPixel(int row, int col, short val) {
|
||||||
|
if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) {
|
||||||
|
imagePixels[row * NCOLS + col] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private short getPixel(int row, int col) {
|
||||||
|
if (row >= 0 && row < NROWS && col >= 0 && col < NCOLS) {
|
||||||
|
return imagePixels[row * NCOLS + col];
|
||||||
|
}
|
||||||
|
return NODATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void doConvert(String inputDir, int lonDegreeStart, int latDegreeStart, String outputFile, int row_length, String hgtfallbackdata) throws Exception {
|
||||||
|
int extraBorder = 0;
|
||||||
|
|
||||||
|
//List<String> foundList = new ArrayList<>();
|
||||||
|
//List<String> notfoundList = new ArrayList<>();
|
||||||
|
|
||||||
|
boolean hgtfound = false;
|
||||||
|
boolean ascfound = false;
|
||||||
|
String filename = null;
|
||||||
|
//if (row_length == SRTM1_ROW_LENGTH)
|
||||||
|
{
|
||||||
|
// check for sources w/o border
|
||||||
|
for (int latIdx = 0; latIdx < 5; latIdx++) {
|
||||||
|
int latDegree = latDegreeStart + latIdx;
|
||||||
|
|
||||||
|
for (int lonIdx = 0; lonIdx < 5; lonIdx++) {
|
||||||
|
int lonDegree = lonDegreeStart + lonIdx;
|
||||||
|
|
||||||
|
filename = inputDir + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".zip";
|
||||||
|
File f = new File(filename);
|
||||||
|
if (f.exists() && f.length() > 0) {
|
||||||
|
hgtfound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
filename = filename.substring(0, filename.length() - 4) + ".hgt";
|
||||||
|
f = new File(filename);
|
||||||
|
if (f.exists() && f.length() > 0) {
|
||||||
|
hgtfound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hgtfound) {
|
||||||
|
filename = inputDir + "/" + genFilenameOld(lonDegreeStart, latDegreeStart).substring(0, 10) + ".zip";
|
||||||
|
File f = new File(filename);
|
||||||
|
if (f.exists() && f.length() > 0) {
|
||||||
|
ascfound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hgtfound) { // init when found
|
||||||
|
NROWS = 5 * row_length + 1 + 2 * extraBorder;
|
||||||
|
NCOLS = 5 * row_length + 1 + 2 * extraBorder;
|
||||||
|
imagePixels = new short[NROWS * NCOLS]; // 650 MB !
|
||||||
|
|
||||||
|
// prefill as NODATA
|
||||||
|
Arrays.fill(imagePixels, NODATA);
|
||||||
|
} else if (!ascfound) {
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("none data: " + lonDegreeStart + " " + latDegreeStart);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hgtfound) {
|
||||||
|
for (int latIdx = -1; latIdx <= 5; latIdx++) {
|
||||||
|
int latDegree = latDegreeStart + latIdx;
|
||||||
|
int rowOffset = extraBorder + (4 - latIdx) * row_length;
|
||||||
|
|
||||||
|
for (int lonIdx = -1; lonIdx <= 5; lonIdx++) {
|
||||||
|
int lonDegree = lonDegreeStart + lonIdx;
|
||||||
|
int colOffset = extraBorder + lonIdx * row_length;
|
||||||
|
|
||||||
|
filename = inputDir + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".zip";
|
||||||
|
File f = new File(filename);
|
||||||
|
if (f.exists() && f.length() > 0) {
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("exist: " + filename);
|
||||||
|
readHgtZip(filename, rowOffset, colOffset, row_length + 1, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
filename = filename.substring(0, filename.length() - 4) + ".hgt";
|
||||||
|
f = new File(filename);
|
||||||
|
if (f.exists() && f.length() > 0) {
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("exist: " + filename);
|
||||||
|
readHgtFile(f, rowOffset, colOffset, row_length + 1, 1);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (hgtfallbackdata != null) {
|
||||||
|
filename = hgtfallbackdata + "/" + formatLat(latDegree) + formatLon(lonDegree) + ".hgt";
|
||||||
|
f = new File(filename);
|
||||||
|
if (f.exists() && f.length() > 0) {
|
||||||
|
readHgtFile(f, rowOffset, colOffset, SRTM3_ROW_LENGTH + 1, 3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
filename = filename.substring(0, filename.length() - 4) + ".zip";
|
||||||
|
f = new File(filename);
|
||||||
|
if (f.exists() && f.length() > 0) {
|
||||||
|
readHgtZip(filename, rowOffset, colOffset, SRTM3_ROW_LENGTH + 1, 3);
|
||||||
|
} else {
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("none : " + filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// post fill zero
|
||||||
|
if (SRTM_NO_ZERO) {
|
||||||
|
for (int row = 0; row < NROWS; row++) {
|
||||||
|
for (int col = 0; col < NCOLS; col++) {
|
||||||
|
if (imagePixels[row * NCOLS + col] == 0) imagePixels[row * NCOLS + col] = NODATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean halfCol5 = false; // no halfcol tiles in lidar data (?)
|
||||||
|
|
||||||
|
|
||||||
|
ElevationRaster raster = new ElevationRaster();
|
||||||
|
if (hgtfound) {
|
||||||
|
raster.nrows = NROWS;
|
||||||
|
raster.ncols = NCOLS;
|
||||||
|
raster.halfcol = halfCol5;
|
||||||
|
raster.noDataValue = NODATA;
|
||||||
|
raster.cellsize = 1. / row_length;
|
||||||
|
raster.xllcorner = lonDegreeStart - (0.5 + extraBorder) * raster.cellsize;
|
||||||
|
raster.yllcorner = latDegreeStart - (0.5 + extraBorder) * raster.cellsize;
|
||||||
|
raster.eval_array = imagePixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ascfound) {
|
||||||
|
File f = new File(filename);
|
||||||
|
readAscZip(f, raster);
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode the raster
|
||||||
|
OutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile));
|
||||||
|
new ElevationRasterCoder().encodeRaster(raster, os);
|
||||||
|
os.close();
|
||||||
|
|
||||||
|
// decode the raster
|
||||||
|
InputStream is = new BufferedInputStream(new FileInputStream(outputFile));
|
||||||
|
ElevationRaster raster2 = new ElevationRasterCoder().decodeRaster(is);
|
||||||
|
is.close();
|
||||||
|
|
||||||
|
short[] pix2 = raster2.eval_array;
|
||||||
|
if (pix2.length != raster.eval_array.length)
|
||||||
|
throw new RuntimeException("length mismatch!");
|
||||||
|
|
||||||
|
// compare decoding result
|
||||||
|
for (int row = 0; row < raster.nrows; row++) {
|
||||||
|
int colstep = halfCol5 ? 2 : 1;
|
||||||
|
for (int col = 0; col < raster.ncols; col += colstep) {
|
||||||
|
int idx = row * raster.ncols + col;
|
||||||
|
short p2 = pix2[idx];
|
||||||
|
if (p2 != raster.eval_array[idx]) {
|
||||||
|
throw new RuntimeException("content mismatch: p2=" + p2 + " p1=" + raster.eval_array[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imagePixels = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatLon(int lon) {
|
||||||
|
if (lon >= 180)
|
||||||
|
lon -= 180; // TODO: w180 oder E180 ?
|
||||||
|
|
||||||
|
String s = "E";
|
||||||
|
if (lon < 0) {
|
||||||
|
lon = -lon;
|
||||||
|
s = "W";
|
||||||
|
}
|
||||||
|
String n = "000" + lon;
|
||||||
|
return s + n.substring(n.length() - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatLat(int lat) {
|
||||||
|
String s = "N";
|
||||||
|
if (lat < 0) {
|
||||||
|
lat = -lat;
|
||||||
|
s = "S";
|
||||||
|
}
|
||||||
|
String n = "00" + lat;
|
||||||
|
return s + n.substring(n.length() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ElevationRaster getRaster(File f, double lon, double lat) throws Exception {
|
||||||
|
long fileSize;
|
||||||
|
InputStream inputStream;
|
||||||
|
|
||||||
|
if (f.getName().toLowerCase().endsWith(".zip")) {
|
||||||
|
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(f)));
|
||||||
|
for (; ; ) {
|
||||||
|
ZipEntry ze = zis.getNextEntry();
|
||||||
|
if (ze == null) {
|
||||||
|
throw new FileNotFoundException(f.getName() + " doesn't contain a " + HGT_FILE_EXT + " file.");
|
||||||
|
}
|
||||||
|
if (ze.getName().toLowerCase().endsWith(HGT_FILE_EXT)) {
|
||||||
|
fileSize = ze.getSize();
|
||||||
|
inputStream = zis;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fileSize = f.length();
|
||||||
|
inputStream = new FileInputStream(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rowLength;
|
||||||
|
if (fileSize > HGT_3ASEC_FILE_SIZE) {
|
||||||
|
rowLength = HGT_1ASEC_ROWS;
|
||||||
|
} else {
|
||||||
|
rowLength = HGT_3ASEC_ROWS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stay at 1 deg * 1 deg raster
|
||||||
|
NROWS = rowLength;
|
||||||
|
NCOLS = rowLength;
|
||||||
|
|
||||||
|
imagePixels = new short[NROWS * NCOLS];
|
||||||
|
|
||||||
|
// prefill as NODATA
|
||||||
|
Arrays.fill(imagePixels, NODATA);
|
||||||
|
readHgtFromStream(inputStream, 0, 0, rowLength, 1);
|
||||||
|
inputStream.close();
|
||||||
|
|
||||||
|
ElevationRaster raster = new ElevationRaster();
|
||||||
|
raster.nrows = NROWS;
|
||||||
|
raster.ncols = NCOLS;
|
||||||
|
raster.halfcol = false; // assume full resolution
|
||||||
|
raster.noDataValue = NODATA;
|
||||||
|
raster.cellsize = 1. / (double) (rowLength - HGT_BORDER_OVERLAP);
|
||||||
|
raster.xllcorner = (int) (lon < 0 ? lon - 1 : lon); //onDegreeStart - raster.cellsize;
|
||||||
|
raster.yllcorner = (int) (lat < 0 ? lat - 1 : lat); //latDegreeStart - raster.cellsize;
|
||||||
|
raster.eval_array = imagePixels;
|
||||||
|
|
||||||
|
return raster;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,342 @@
|
||||||
|
// License: GPL. For details, see LICENSE file.
|
||||||
|
package btools.mapcreator;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adapted from https://github.com/JOSM/josm-plugins/blob/master/ElevationProfile/src/org/openstreetmap/josm/plugins/elevation/HgtReader.java
|
||||||
|
* <p>
|
||||||
|
* Class HgtReader reads data from SRTM HGT files. Currently this class is restricted to a resolution of 3 arc seconds.
|
||||||
|
* <p>
|
||||||
|
* SRTM data files are available at the <a href="http://dds.cr.usgs.gov/srtm/version2_1/SRTM3">NASA SRTM site</a>
|
||||||
|
*
|
||||||
|
* @author Oliver Wieland <oliver.wieland@online.de>
|
||||||
|
*/
|
||||||
|
public class HgtReader {
|
||||||
|
final static boolean DEBUG = false;
|
||||||
|
|
||||||
|
private static final int SECONDS_PER_MINUTE = 60;
|
||||||
|
|
||||||
|
public static final String HGT_EXT = ".hgt";
|
||||||
|
public static final String ZIP_EXT = ".zip";
|
||||||
|
|
||||||
|
// alter these values for different SRTM resolutions
|
||||||
|
public static final int HGT3_RES = 3; // resolution in arc seconds
|
||||||
|
public static final int HGT3_ROW_LENGTH = 1201; // number of elevation values per line
|
||||||
|
public static final int HGT_VOID = -32768; // magic number which indicates 'void data' in HGT file
|
||||||
|
public static final int HGT1_RES = 1; // <<- The new SRTM is 1-ARCSEC
|
||||||
|
public static final int HGT1_ROW_LENGTH = 3601; //-- New file resolution is 3601x3601
|
||||||
|
/**
|
||||||
|
* The 'no elevation' data magic.
|
||||||
|
*/
|
||||||
|
public static double NO_ELEVATION = Double.NaN;
|
||||||
|
|
||||||
|
private static String srtmFolder = "";
|
||||||
|
|
||||||
|
private static final Map<String, ShortBuffer> cache = new HashMap<>();
|
||||||
|
|
||||||
|
public HgtReader(String folder) {
|
||||||
|
srtmFolder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double getElevationFromHgt(double lat, double lon) {
|
||||||
|
try {
|
||||||
|
String file = getHgtFileName(lat, lon);
|
||||||
|
if (DEBUG) System.out.println("HGT buffer " + file + " for " + lat + " " + lon);
|
||||||
|
|
||||||
|
// given area in cache?
|
||||||
|
if (!cache.containsKey(file)) {
|
||||||
|
|
||||||
|
// fill initial cache value. If no file is found, then
|
||||||
|
// we use it as a marker to indicate 'file has been searched
|
||||||
|
// but is not there'
|
||||||
|
cache.put(file, null);
|
||||||
|
// Try all resource directories
|
||||||
|
//for (String location : Main.pref.getAllPossiblePreferenceDirs())
|
||||||
|
{
|
||||||
|
String fullPath = new File(srtmFolder, file + HGT_EXT).getPath();
|
||||||
|
File f = new File(fullPath);
|
||||||
|
if (f.exists()) {
|
||||||
|
// found something: read HGT file...
|
||||||
|
ShortBuffer data = readHgtFile(fullPath);
|
||||||
|
// ... and store result in cache
|
||||||
|
cache.put(file, data);
|
||||||
|
//break;
|
||||||
|
} else {
|
||||||
|
fullPath = new File(srtmFolder, file + ZIP_EXT).getPath();
|
||||||
|
f = new File(fullPath);
|
||||||
|
if (f.exists()) {
|
||||||
|
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(f)));
|
||||||
|
try {
|
||||||
|
for (; ; ) {
|
||||||
|
ZipEntry ze = zis.getNextEntry();
|
||||||
|
if (ze == null) break;
|
||||||
|
if (ze.getName().toLowerCase().endsWith(HGT_EXT)) {
|
||||||
|
// System.out.println("read zip " + ze.getName());
|
||||||
|
ShortBuffer data = readHgtStream(zis);
|
||||||
|
// ... and store result in cache
|
||||||
|
cache.put(file, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
zis.closeEntry();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("*** reading: " + f.getName() + " " + cache.get(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read elevation value
|
||||||
|
return readElevation(lat, lon);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.err.println("HGT Get elevation " + lat + ", " + lon + " failed: => " + e.getMessage());
|
||||||
|
// no problem... file not there
|
||||||
|
return NO_ELEVATION;
|
||||||
|
} catch (Exception ioe) {
|
||||||
|
// oops...
|
||||||
|
ioe.printStackTrace(System.err);
|
||||||
|
// fallback
|
||||||
|
return NO_ELEVATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static short[] getElevationDataFromHgt(double lat, double lon) {
|
||||||
|
try {
|
||||||
|
if (lon < 0) lon += 1;
|
||||||
|
if (lat < 0) lat += 1;
|
||||||
|
String file = getHgtFileName(lat, lon);
|
||||||
|
if (DEBUG) System.out.println("HGT buffer " + file + " for " + lat + " " + lon);
|
||||||
|
|
||||||
|
ShortBuffer data = null;
|
||||||
|
|
||||||
|
// Try all resource directories
|
||||||
|
//for (String location : Main.pref.getAllPossiblePreferenceDirs())
|
||||||
|
|
||||||
|
String fullPath = new File(srtmFolder, file + HGT_EXT).getPath();
|
||||||
|
File f = new File(fullPath);
|
||||||
|
if (f.exists()) {
|
||||||
|
// found something: read HGT file...
|
||||||
|
data = readHgtFile(fullPath);
|
||||||
|
} else {
|
||||||
|
fullPath = new File(srtmFolder, file + ZIP_EXT).getPath();
|
||||||
|
f = new File(fullPath);
|
||||||
|
if (f.exists()) {
|
||||||
|
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(f)));
|
||||||
|
try {
|
||||||
|
for (; ; ) {
|
||||||
|
ZipEntry ze = zis.getNextEntry();
|
||||||
|
if (ze == null) break;
|
||||||
|
if (ze.getName().toLowerCase().endsWith(HGT_EXT)) {
|
||||||
|
// System.out.println("read zip " + ze.getName());
|
||||||
|
data = readHgtStream(zis);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
zis.closeEntry();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
zis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
System.out.println("*** reading: " + f.getName() + " " + (data != null ? data.limit() : -1));
|
||||||
|
if (data != null) {
|
||||||
|
short[] array = new short[data.limit()];
|
||||||
|
data.get(array);
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.err.println("HGT Get elevation " + lat + ", " + lon + " failed: => " + e.getMessage());
|
||||||
|
// no problem... file not there
|
||||||
|
return null;
|
||||||
|
} catch (Exception ioe) {
|
||||||
|
// oops...
|
||||||
|
ioe.printStackTrace(System.err);
|
||||||
|
// fallback
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
private static ShortBuffer readHgtFile(String file) throws Exception {
|
||||||
|
if (file == null) throw new Exception("no hgt file " + file);
|
||||||
|
|
||||||
|
FileChannel fc = null;
|
||||||
|
ShortBuffer sb = null;
|
||||||
|
try {
|
||||||
|
// Eclipse complains here about resource leak on 'fc' - even with 'finally' clause???
|
||||||
|
fc = new FileInputStream(file).getChannel();
|
||||||
|
// choose the right endianness
|
||||||
|
|
||||||
|
ByteBuffer bb = ByteBuffer.allocateDirect((int) fc.size());
|
||||||
|
while (bb.remaining() > 0) fc.read(bb);
|
||||||
|
|
||||||
|
bb.flip();
|
||||||
|
//sb = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||||
|
sb = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer();
|
||||||
|
} finally {
|
||||||
|
if (fc != null) fc.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @SuppressWarnings("resource")
|
||||||
|
private static ShortBuffer readHgtStream(InputStream zis) throws Exception {
|
||||||
|
if (zis == null) throw new Exception("no hgt stream ");
|
||||||
|
|
||||||
|
ShortBuffer sb = null;
|
||||||
|
try {
|
||||||
|
// choose the right endianness
|
||||||
|
|
||||||
|
byte[] bytes = zis.readAllBytes();
|
||||||
|
ByteBuffer bb = ByteBuffer.allocate(bytes.length);
|
||||||
|
bb.put(bytes, 0, bytes.length);
|
||||||
|
//while (bb.remaining() > 0) zis.read(bb, 0, size);
|
||||||
|
|
||||||
|
//ByteBuffer bb = ByteBuffer.allocate(zis.available());
|
||||||
|
//Channels.newChannel(zis).read(bb);
|
||||||
|
bb.flip();
|
||||||
|
//sb = bb.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||||
|
sb = bb.order(ByteOrder.BIG_ENDIAN).asShortBuffer();
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the elevation value for the given coordinate.
|
||||||
|
* <p>
|
||||||
|
* See also <a href="http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file">stackexchange.com</a>
|
||||||
|
*
|
||||||
|
* @param lat, lon the coordinate to get the elevation data for
|
||||||
|
* @return the elevation value or <code>Double.NaN</code>, if no value is present
|
||||||
|
*/
|
||||||
|
public static double readElevation(double lat, double lon) {
|
||||||
|
String tag = getHgtFileName(lat, lon);
|
||||||
|
|
||||||
|
ShortBuffer sb = cache.get(tag);
|
||||||
|
|
||||||
|
if (sb == null) {
|
||||||
|
return NO_ELEVATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) System.out.println("HGT buffer size " + sb.capacity() + " limit " + sb.limit());
|
||||||
|
try {
|
||||||
|
int rowLength = HGT3_ROW_LENGTH;
|
||||||
|
int resolution = HGT3_RES;
|
||||||
|
if (sb.capacity() > (HGT3_ROW_LENGTH * HGT3_ROW_LENGTH)) {
|
||||||
|
rowLength = HGT1_ROW_LENGTH;
|
||||||
|
resolution = HGT1_RES;
|
||||||
|
}
|
||||||
|
// see http://gis.stackexchange.com/questions/43743/how-to-extract-elevation-from-hgt-file
|
||||||
|
double fLat = frac(lat) * SECONDS_PER_MINUTE;
|
||||||
|
double fLon = frac(lon) * SECONDS_PER_MINUTE;
|
||||||
|
|
||||||
|
// compute offset within HGT file
|
||||||
|
int row = (int) Math.round((fLat) * SECONDS_PER_MINUTE / resolution);
|
||||||
|
int col = (int) Math.round((fLon) * SECONDS_PER_MINUTE / resolution);
|
||||||
|
if (lon < 0) col = rowLength - col - 1;
|
||||||
|
if (lat > 0) row = rowLength - row - 1;
|
||||||
|
|
||||||
|
|
||||||
|
//row = rowLength - row;
|
||||||
|
int cell = (rowLength * (row)) + col;
|
||||||
|
//int cell = ((rowLength * (latitude)) + longitude);
|
||||||
|
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("Read HGT elevation data from row/col/cell " + row + "," + col + ", " + cell + ", " + sb.limit());
|
||||||
|
|
||||||
|
// valid position in buffer?
|
||||||
|
if (cell < sb.limit()) {
|
||||||
|
short ele = sb.get(cell);
|
||||||
|
// check for data voids
|
||||||
|
if (ele == HGT_VOID) {
|
||||||
|
return NO_ELEVATION;
|
||||||
|
} else {
|
||||||
|
return ele;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NO_ELEVATION;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("error at " + lon + " " + lat + " ");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return NO_ELEVATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the associated HGT file name for the given way point. Usually the
|
||||||
|
* format is <tt>[N|S]nn[W|E]mmm.hgt</tt> where <i>nn</i> is the integral latitude
|
||||||
|
* without decimals and <i>mmm</i> is the longitude.
|
||||||
|
*
|
||||||
|
* @param llat,llon the coordinate to get the filename for
|
||||||
|
* @return the file name of the HGT file
|
||||||
|
*/
|
||||||
|
public static String getHgtFileName(double llat, double llon) {
|
||||||
|
int lat = (int) llat;
|
||||||
|
int lon = (int) llon;
|
||||||
|
|
||||||
|
String latPref = "N";
|
||||||
|
if (lat < 0) {
|
||||||
|
latPref = "S";
|
||||||
|
lat = -lat + 1;
|
||||||
|
}
|
||||||
|
String lonPref = "E";
|
||||||
|
if (lon < 0) {
|
||||||
|
lonPref = "W";
|
||||||
|
lon = -lon + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("%s%02d%s%03d", latPref, lat, lonPref, lon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double frac(double d) {
|
||||||
|
long iPart;
|
||||||
|
double fPart;
|
||||||
|
|
||||||
|
// Get user input
|
||||||
|
iPart = (long) d;
|
||||||
|
fPart = d - iPart;
|
||||||
|
return Math.abs(fPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
if (cache != null) {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
System.out.println("*** HGT position values and enhance elevation");
|
||||||
|
if (args.length == 3) {
|
||||||
|
HgtReader elevReader = new HgtReader(args[0]);
|
||||||
|
double lon = Double.parseDouble(args[1]);
|
||||||
|
double lat = Double.parseDouble(args[2]);
|
||||||
|
// check hgt direct
|
||||||
|
double elev = elevReader.getElevationFromHgt(lat, lon);
|
||||||
|
System.out.println("-----> elv for hgt " + lat + ", " + lon + " = " + elev);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,39 +14,34 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import btools.util.DiffCoderDataOutputStream;
|
import btools.util.DiffCoderDataOutputStream;
|
||||||
|
|
||||||
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener
|
public abstract class MapCreatorBase implements WayListener, NodeListener, RelationListener {
|
||||||
{
|
|
||||||
private DiffCoderDataOutputStream[] tileOutStreams;
|
private DiffCoderDataOutputStream[] tileOutStreams;
|
||||||
protected File outTileDir;
|
protected File outTileDir;
|
||||||
|
|
||||||
protected HashMap<String,String> tags;
|
protected Map<String, String> tags;
|
||||||
|
|
||||||
public void putTag( String key, String value )
|
public void putTag(String key, String value) {
|
||||||
{
|
if (tags == null) tags = new HashMap<>();
|
||||||
if ( tags == null ) tags = new HashMap<String,String>();
|
|
||||||
tags.put(key, value);
|
tags.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTag( String key )
|
public String getTag(String key) {
|
||||||
{
|
|
||||||
return tags == null ? null : tags.get(key);
|
return tags == null ? null : tags.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashMap<String,String> getTagsOrNull()
|
public Map<String, String> getTagsOrNull() {
|
||||||
{
|
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTags( HashMap<String,String> tags )
|
public void setTags(Map<String, String> tags) {
|
||||||
{
|
|
||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static long readId( DataInputStream is) throws IOException
|
protected static long readId(DataInputStream is) throws IOException {
|
||||||
{
|
|
||||||
int offset = is.readByte();
|
int offset = is.readByte();
|
||||||
if (offset == 32) return -1;
|
if (offset == 32) return -1;
|
||||||
long i = is.readInt();
|
long i = is.readInt();
|
||||||
|
|
@ -54,10 +49,8 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
return i | offset;
|
return i | offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void writeId( DataOutputStream o, long id ) throws IOException
|
protected static void writeId(DataOutputStream o, long id) throws IOException {
|
||||||
{
|
if (id == -1) {
|
||||||
if ( id == -1 )
|
|
||||||
{
|
|
||||||
o.writeByte(32);
|
o.writeByte(32);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -68,20 +61,16 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static File[] sortBySizeAsc( File[] files )
|
protected static File[] sortBySizeAsc(File[] files) {
|
||||||
{
|
|
||||||
int n = files.length;
|
int n = files.length;
|
||||||
long[] sizes = new long[n];
|
long[] sizes = new long[n];
|
||||||
File[] sorted = new File[n];
|
File[] sorted = new File[n];
|
||||||
for (int i = 0; i < n; i++) sizes[i] = files[i].length();
|
for (int i = 0; i < n; i++) sizes[i] = files[i].length();
|
||||||
for(int nf=0; nf<n; nf++)
|
for (int nf = 0; nf < n; nf++) {
|
||||||
{
|
|
||||||
int idx = -1;
|
int idx = -1;
|
||||||
long min = -1;
|
long min = -1;
|
||||||
for( int i=0; i<n; i++ )
|
for (int i = 0; i < n; i++) {
|
||||||
{
|
if (sizes[i] != -1 && (idx == -1 || sizes[i] < min)) {
|
||||||
if ( sizes[i] != -1 && ( idx == -1 || sizes[i] < min ) )
|
|
||||||
{
|
|
||||||
min = sizes[i];
|
min = sizes[i];
|
||||||
idx = i;
|
idx = i;
|
||||||
}
|
}
|
||||||
|
|
@ -92,50 +81,40 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File fileFromTemplate( File template, File dir, String suffix )
|
protected File fileFromTemplate(File template, File dir, String suffix) {
|
||||||
{
|
|
||||||
String filename = template.getName();
|
String filename = template.getName();
|
||||||
filename = filename.substring(0, filename.length() - 3) + suffix;
|
filename = filename.substring(0, filename.length() - 3) + suffix;
|
||||||
return new File(dir, filename);
|
return new File(dir, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataInputStream createInStream( File inFile ) throws IOException
|
protected DataInputStream createInStream(File inFile) throws IOException {
|
||||||
{
|
|
||||||
return new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
|
return new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DiffCoderDataOutputStream createOutStream( File outFile ) throws IOException
|
protected DiffCoderDataOutputStream createOutStream(File outFile) throws IOException {
|
||||||
{
|
|
||||||
return new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
return new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DiffCoderDataOutputStream getOutStreamForTile( int tileIndex ) throws Exception
|
protected DiffCoderDataOutputStream getOutStreamForTile(int tileIndex) throws Exception {
|
||||||
{
|
if (tileOutStreams == null) {
|
||||||
if ( tileOutStreams == null )
|
|
||||||
{
|
|
||||||
tileOutStreams = new DiffCoderDataOutputStream[64];
|
tileOutStreams = new DiffCoderDataOutputStream[64];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( tileOutStreams[tileIndex] == null )
|
if (tileOutStreams[tileIndex] == null) {
|
||||||
{
|
|
||||||
tileOutStreams[tileIndex] = createOutStream(new File(outTileDir, getNameForTile(tileIndex)));
|
tileOutStreams[tileIndex] = createOutStream(new File(outTileDir, getNameForTile(tileIndex)));
|
||||||
}
|
}
|
||||||
return tileOutStreams[tileIndex];
|
return tileOutStreams[tileIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getNameForTile( int tileIndex )
|
protected String getNameForTile(int tileIndex) {
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("getNameForTile not implemented");
|
throw new IllegalArgumentException("getNameForTile not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void closeTileOutStreams() throws Exception
|
protected void closeTileOutStreams() throws Exception {
|
||||||
{
|
if (tileOutStreams == null) {
|
||||||
if ( tileOutStreams == null )
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for( int tileIndex=0; tileIndex<tileOutStreams.length; tileIndex++ )
|
for (int tileIndex = 0; tileIndex < tileOutStreams.length; tileIndex++) {
|
||||||
{
|
|
||||||
if (tileOutStreams[tileIndex] != null) tileOutStreams[tileIndex].close();
|
if (tileOutStreams[tileIndex] != null) tileOutStreams[tileIndex].close();
|
||||||
tileOutStreams[tileIndex] = null;
|
tileOutStreams[tileIndex] = null;
|
||||||
}
|
}
|
||||||
|
|
@ -145,27 +124,36 @@ public abstract class MapCreatorBase implements WayListener, NodeListener, Relat
|
||||||
// interface dummys
|
// interface dummys
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileStart( File nodefile ) throws Exception {}
|
public void nodeFileStart(File nodefile) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextNode( NodeData n ) throws Exception {}
|
public void nextNode(NodeData n) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileEnd( File nodefile ) throws Exception {}
|
public void nodeFileEnd(File nodefile) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean wayFileStart( File wayfile ) throws Exception { return true; }
|
public boolean wayFileStart(File wayfile) throws Exception {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextWay( WayData data ) throws Exception {}
|
public void nextWay(WayData data) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void wayFileEnd( File wayfile ) throws Exception {}
|
public void wayFileEnd(File wayfile) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextRelation( RelationData data ) throws Exception {}
|
public void nextRelation(RelationData data) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextRestriction( RelationData data, long fromWid, long toWid, long viaNid ) throws Exception {}
|
public void nextRestriction(RelationData data, long fromWid, long toWid, long viaNid) throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,60 +4,51 @@ import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NodeCutter does 1 step in map-processing:
|
* NodeCutter does 1 step in map-processing:
|
||||||
*
|
* <p>
|
||||||
* - cuts the 45*30 node tiles into 5*5 pieces
|
* - cuts the 45*30 node tiles into 5*5 pieces
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeCutter extends MapCreatorBase
|
public class NodeCutter extends MapCreatorBase {
|
||||||
{
|
|
||||||
private int lonoffset;
|
private int lonoffset;
|
||||||
private int latoffset;
|
private int latoffset;
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception
|
public static void main(String[] args) throws Exception {
|
||||||
{
|
|
||||||
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
|
System.out.println("*** NodeCutter: Cut big node-tiles into 5x5 tiles");
|
||||||
if (args.length != 2)
|
if (args.length != 2) {
|
||||||
{
|
|
||||||
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>");
|
System.out.println("usage: java NodeCutter <node-tiles-in> <node-tiles-out>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
new NodeCutter().process(new File(args[0]), new File(args[1]));
|
new NodeCutter().process(new File(args[0]), new File(args[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init( File nodeTilesOut )
|
public void init(File nodeTilesOut) {
|
||||||
{
|
|
||||||
this.outTileDir = nodeTilesOut;
|
this.outTileDir = nodeTilesOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process( File nodeTilesIn, File nodeTilesOut ) throws Exception
|
public void process(File nodeTilesIn, File nodeTilesOut) throws Exception {
|
||||||
{
|
|
||||||
init(nodeTilesOut);
|
init(nodeTilesOut);
|
||||||
|
|
||||||
new NodeIterator(this, true).processDir(nodeTilesIn, ".tlf");
|
new NodeIterator(this, true).processDir(nodeTilesIn, ".tlf");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileStart( File nodefile ) throws Exception
|
public void nodeFileStart(File nodefile) throws Exception {
|
||||||
{
|
|
||||||
lonoffset = -1;
|
lonoffset = -1;
|
||||||
latoffset = -1;
|
latoffset = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextNode( NodeData n ) throws Exception
|
public void nextNode(NodeData n) throws Exception {
|
||||||
{
|
|
||||||
n.writeTo(getOutStreamForTile(getTileIndex(n.ilon, n.ilat)));
|
n.writeTo(getOutStreamForTile(getTileIndex(n.ilon, n.ilat)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileEnd( File nodeFile ) throws Exception
|
public void nodeFileEnd(File nodeFile) throws Exception {
|
||||||
{
|
|
||||||
closeTileOutStreams();
|
closeTileOutStreams();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTileIndex( int ilon, int ilat )
|
private int getTileIndex(int ilon, int ilat) {
|
||||||
{
|
|
||||||
int lonoff = (ilon / 45000000) * 45;
|
int lonoff = (ilon / 45000000) * 45;
|
||||||
int latoff = (ilat / 30000000) * 30;
|
int latoff = (ilat / 30000000) * 30;
|
||||||
if (lonoffset == -1) lonoffset = lonoff;
|
if (lonoffset == -1) lonoffset = lonoff;
|
||||||
|
|
@ -67,13 +58,13 @@ public class NodeCutter extends MapCreatorBase
|
||||||
|
|
||||||
int lon = (ilon / 5000000) % 9;
|
int lon = (ilon / 5000000) % 9;
|
||||||
int lat = (ilat / 5000000) % 6;
|
int lat = (ilat / 5000000) % 6;
|
||||||
if ( lon < 0 || lon > 8 || lat < 0 || lat > 5 ) throw new IllegalArgumentException( "illegal pos: " + ilon + "," + ilat );
|
if (lon < 0 || lon > 8 || lat < 0 || lat > 5)
|
||||||
|
throw new IllegalArgumentException("illegal pos: " + ilon + "," + ilat);
|
||||||
return lon * 6 + lat;
|
return lon * 6 + lat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected String getNameForTile( int tileIndex )
|
protected String getNameForTile(int tileIndex) {
|
||||||
{
|
|
||||||
int lon = (tileIndex / 6) * 5 + lonoffset - 180;
|
int lon = (tileIndex / 6) * 5 + lonoffset - 180;
|
||||||
int lat = (tileIndex % 6) * 5 + latoffset - 90;
|
int lat = (tileIndex % 6) * 5 + latoffset - 90;
|
||||||
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
|
||||||
|
|
|
||||||
|
|
@ -8,39 +8,42 @@ import btools.util.DiffCoderDataOutputStream;
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeData extends MapCreatorBase
|
public class NodeData extends MapCreatorBase {
|
||||||
{
|
|
||||||
public long nid;
|
public long nid;
|
||||||
public int ilon;
|
public int ilon;
|
||||||
public int ilat;
|
public int ilat;
|
||||||
public byte[] description;
|
public byte[] description;
|
||||||
public short selev = Short.MIN_VALUE;
|
public short selev = Short.MIN_VALUE;
|
||||||
|
|
||||||
public NodeData( long id, double lon, double lat )
|
public NodeData(long id, double lon, double lat) {
|
||||||
{
|
|
||||||
nid = id;
|
nid = id;
|
||||||
ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||||
ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NodeData( DiffCoderDataInputStream dis ) throws Exception
|
public NodeData(DiffCoderDataInputStream dis) throws Exception {
|
||||||
{
|
|
||||||
nid = dis.readDiffed(0);
|
nid = dis.readDiffed(0);
|
||||||
ilon = (int) dis.readDiffed(1);
|
ilon = (int) dis.readDiffed(1);
|
||||||
ilat = (int) dis.readDiffed(2);
|
ilat = (int) dis.readDiffed(2);
|
||||||
int mode = dis.readByte();
|
int mode = dis.readByte();
|
||||||
if ( ( mode & 1 ) != 0 ) { int dlen = dis.readShort(); description = new byte[dlen]; dis.readFully( description ); }
|
if ((mode & 1) != 0) {
|
||||||
|
int dlen = dis.readShort();
|
||||||
|
description = new byte[dlen];
|
||||||
|
dis.readFully(description);
|
||||||
|
}
|
||||||
if ((mode & 2) != 0) selev = dis.readShort();
|
if ((mode & 2) != 0) selev = dis.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTo( DiffCoderDataOutputStream dos ) throws Exception
|
public void writeTo(DiffCoderDataOutputStream dos) throws Exception {
|
||||||
{
|
|
||||||
dos.writeDiffed(nid, 0);
|
dos.writeDiffed(nid, 0);
|
||||||
dos.writeDiffed(ilon, 1);
|
dos.writeDiffed(ilon, 1);
|
||||||
dos.writeDiffed(ilat, 2);
|
dos.writeDiffed(ilat, 2);
|
||||||
int mode = (description == null ? 0 : 1) | (selev == Short.MIN_VALUE ? 0 : 2);
|
int mode = (description == null ? 0 : 1) | (selev == Short.MIN_VALUE ? 0 : 2);
|
||||||
dos.writeByte((byte) mode);
|
dos.writeByte((byte) mode);
|
||||||
if ( ( mode & 1 ) != 0 ) { dos.writeShort( description.length ); dos.write( description ); }
|
if ((mode & 1) != 0) {
|
||||||
|
dos.writeShort(description.length);
|
||||||
|
dos.write(description);
|
||||||
|
}
|
||||||
if ((mode & 2) != 0) dos.writeShort(selev);
|
if ((mode & 2) != 0) dos.writeShort(selev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,19 @@ import btools.util.TinyDenseLongMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NodeFilter does 1 step in map-processing:
|
* NodeFilter does 1 step in map-processing:
|
||||||
*
|
* <p>
|
||||||
* - filters out unused nodes according to the way file
|
* - filters out unused nodes according to the way file
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeFilter extends MapCreatorBase
|
public class NodeFilter extends MapCreatorBase {
|
||||||
{
|
|
||||||
private DiffCoderDataOutputStream nodesOutStream;
|
private DiffCoderDataOutputStream nodesOutStream;
|
||||||
private File nodeTilesOut;
|
private File nodeTilesOut;
|
||||||
protected DenseLongMap nodebitmap;
|
protected DenseLongMap nodebitmap;
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception
|
public static void main(String[] args) throws Exception {
|
||||||
{
|
|
||||||
System.out.println("*** NodeFilter: Filter way related nodes");
|
System.out.println("*** NodeFilter: Filter way related nodes");
|
||||||
if (args.length != 3)
|
if (args.length != 3) {
|
||||||
{
|
|
||||||
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>");
|
System.out.println("usage: java NodeFilter <node-tiles-in> <way-file-in> <node-tiles-out>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -33,13 +30,11 @@ public class NodeFilter extends MapCreatorBase
|
||||||
new NodeFilter().process(new File(args[0]), new File(args[1]), new File(args[2]));
|
new NodeFilter().process(new File(args[0]), new File(args[1]), new File(args[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() throws Exception
|
public void init() throws Exception {
|
||||||
{
|
|
||||||
nodebitmap = Boolean.getBoolean("useDenseMaps") ? new DenseLongMap(512) : new TinyDenseLongMap();
|
nodebitmap = Boolean.getBoolean("useDenseMaps") ? new DenseLongMap(512) : new TinyDenseLongMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process( File nodeTilesIn, File wayFileIn, File nodeTilesOut ) throws Exception
|
public void process(File nodeTilesIn, File wayFileIn, File nodeTilesOut) throws Exception {
|
||||||
{
|
|
||||||
init();
|
init();
|
||||||
|
|
||||||
this.nodeTilesOut = nodeTilesOut;
|
this.nodeTilesOut = nodeTilesOut;
|
||||||
|
|
@ -52,41 +47,34 @@ public class NodeFilter extends MapCreatorBase
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextWay( WayData data ) throws Exception
|
public void nextWay(WayData data) throws Exception {
|
||||||
{
|
|
||||||
int nnodes = data.nodes.size();
|
int nnodes = data.nodes.size();
|
||||||
for (int i=0; i<nnodes; i++ )
|
for (int i = 0; i < nnodes; i++) {
|
||||||
{
|
|
||||||
nodebitmap.put(data.nodes.get(i), 0);
|
nodebitmap.put(data.nodes.get(i), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileStart( File nodefile ) throws Exception
|
public void nodeFileStart(File nodefile) throws Exception {
|
||||||
{
|
|
||||||
String filename = nodefile.getName();
|
String filename = nodefile.getName();
|
||||||
File outfile = new File(nodeTilesOut, filename);
|
File outfile = new File(nodeTilesOut, filename);
|
||||||
nodesOutStream = new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outfile)));
|
nodesOutStream = new DiffCoderDataOutputStream(new BufferedOutputStream(new FileOutputStream(outfile)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nextNode( NodeData n ) throws Exception
|
public void nextNode(NodeData n) throws Exception {
|
||||||
{
|
if (isRelevant(n)) {
|
||||||
if ( isRelevant( n ) )
|
|
||||||
{
|
|
||||||
n.writeTo(nodesOutStream);
|
n.writeTo(nodesOutStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRelevant( NodeData n )
|
public boolean isRelevant(NodeData n) {
|
||||||
{
|
|
||||||
// check if node passes bitmap
|
// check if node passes bitmap
|
||||||
return nodebitmap.getInt(n.nid) == 0; // 0 -> bit set, -1 -> unset
|
return nodebitmap.getInt(n.nid) == 0; // 0 -> bit set, -1 -> unset
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void nodeFileEnd( File nodeFile ) throws Exception
|
public void nodeFileEnd(File nodeFile) throws Exception {
|
||||||
{
|
|
||||||
nodesOutStream.close();
|
nodesOutStream.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,58 +13,46 @@ import btools.util.DiffCoderDataInputStream;
|
||||||
*
|
*
|
||||||
* @author ab
|
* @author ab
|
||||||
*/
|
*/
|
||||||
public class NodeIterator extends MapCreatorBase
|
public class NodeIterator extends MapCreatorBase {
|
||||||
{
|
|
||||||
private NodeListener listener;
|
private NodeListener listener;
|
||||||
private boolean delete;
|
private boolean delete;
|
||||||
|
|
||||||
public NodeIterator( NodeListener nodeListener, boolean deleteAfterReading )
|
public NodeIterator(NodeListener nodeListener, boolean deleteAfterReading) {
|
||||||
{
|
|
||||||
listener = nodeListener;
|
listener = nodeListener;
|
||||||
delete = deleteAfterReading;
|
delete = deleteAfterReading;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processDir( File indir, String inSuffix ) throws Exception
|
public void processDir(File indir, String inSuffix) throws Exception {
|
||||||
{
|
if (!indir.isDirectory()) {
|
||||||
if ( !indir.isDirectory() )
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("not a directory: " + indir);
|
throw new IllegalArgumentException("not a directory: " + indir);
|
||||||
}
|
}
|
||||||
|
|
||||||
File[] af = sortBySizeAsc(indir.listFiles());
|
File[] af = sortBySizeAsc(indir.listFiles());
|
||||||
for( int i=0; i<af.length; i++ )
|
for (int i = 0; i < af.length; i++) {
|
||||||
{
|
|
||||||
File nodefile = af[i];
|
File nodefile = af[i];
|
||||||
if ( nodefile.getName().endsWith( inSuffix ) )
|
if (nodefile.getName().endsWith(inSuffix)) {
|
||||||
{
|
|
||||||
processFile(nodefile);
|
processFile(nodefile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void processFile(File nodefile) throws Exception
|
public void processFile(File nodefile) throws Exception {
|
||||||
{
|
|
||||||
System.out.println("*** NodeIterator reading: " + nodefile);
|
System.out.println("*** NodeIterator reading: " + nodefile);
|
||||||
|
|
||||||
listener.nodeFileStart(nodefile);
|
listener.nodeFileStart(nodefile);
|
||||||
|
|
||||||
DiffCoderDataInputStream di = new DiffCoderDataInputStream(new BufferedInputStream(new FileInputStream(nodefile)));
|
DiffCoderDataInputStream di = new DiffCoderDataInputStream(new BufferedInputStream(new FileInputStream(nodefile)));
|
||||||
try
|
try {
|
||||||
{
|
for (; ; ) {
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
NodeData n = new NodeData(di);
|
NodeData n = new NodeData(di);
|
||||||
listener.nextNode(n);
|
listener.nextNode(n);
|
||||||
}
|
}
|
||||||
}
|
} catch (EOFException eof) {
|
||||||
catch( EOFException eof )
|
|
||||||
{
|
|
||||||
di.close();
|
di.close();
|
||||||
}
|
}
|
||||||
listener.nodeFileEnd(nodefile);
|
listener.nodeFileEnd(nodefile);
|
||||||
if ( delete && "true".equals( System.getProperty( "deletetmpfiles" ) ))
|
if (delete && "true".equals(System.getProperty("deletetmpfiles"))) {
|
||||||
{
|
|
||||||
nodefile.delete();
|
nodefile.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue