Compare commits

...

390 Commits
0.1 ... master

Author SHA1 Message Date
2378cd5eaa 更新 'src/renderer/components/FlightgearMap.vue' 2022-11-25 18:23:10 +08:00
portree_kid
4fff310d76 API Key 2021-06-29 08:17:30 +02:00
portree_kid
09b0cba5b0 Auth Key for com not org 2021-06-29 07:07:34 +02:00
portree_kid
f5930e2039 API Token 2021-06-28 17:04:17 +02:00
portree_kid
cc80726ec7 Merge branch 'master' of https://github.com/Portree-Kid/flightgear-airports 2021-06-28 13:56:05 +02:00
portree_kid
b73f0bb1a1 New token 2021-06-28 13:38:47 +02:00
Keith Paterson
1ac67e1e18 Bump to 0.0.34 2021-06-25 08:54:43 +02:00
Keith Paterson
7a58b4bde1 Change Type via multiselect 2021-06-24 08:00:05 +02:00
Keith Paterson
ba533e8942 #142 2021-06-23 16:47:55 +02:00
Keith Paterson
8b0afa9fcb #141 2021-06-23 15:59:25 +02:00
Keith Paterson
8a4820d23c Version 0.0.33 2021-06-11 13:55:28 +02:00
Keith Paterson
da597f80e4 #139 2021-06-09 16:29:20 +02:00
Keith Paterson
1bd3b3b2e4 #139 2021-06-09 16:27:35 +02:00
Keith Paterson
b9b6574614 #140 2021-06-07 14:40:04 +02:00
Keith Paterson
089ce029ec Check for double edges 2021-06-04 09:16:58 +02:00
Keith Paterson
9e7f41a5c9 Check for double edges 2021-06-04 09:16:15 +02:00
Keith Paterson
60881eaf3e Logfile in user dir 2021-06-03 16:00:46 +02:00
Keith Paterson
0f4b82807e #133 2021-06-03 15:35:44 +02:00
Keith Paterson
f9d0469466 Unique Trafficlist 2021-06-03 15:03:22 +02:00
Keith Paterson
f56d8ad926 Check 2021-06-03 11:32:48 +02:00
Keith Paterson
b2ddb08374 #130 2021-06-02 12:12:52 +02:00
Keith Paterson
091e7853eb #138 2021-06-01 14:44:43 +02:00
Keith Paterson
e52fcf9572 #137 2021-05-31 17:21:14 +02:00
Keith Paterson
055897c0f6 Bump to 0.0.32 2021-05-28 16:55:00 +02:00
Keith Paterson
cf2e5150e8 Tooltip for Airports in overview 2021-05-28 16:54:18 +02:00
Keith Paterson
d8417c7fd9 #134 2021-05-28 12:58:54 +02:00
Keith Paterson
ed25672448 NPE when editing 2021-05-28 10:22:30 +02:00
Keith Paterson
5e0e2c9ea5 try catch in upload 2021-05-28 10:22:08 +02:00
Keith Paterson
d7ffe7bcec Version 0.0.31 2021-04-21 10:54:16 +02:00
Keith Paterson
cf1c829c11 Improved Tooltips 2021-04-21 10:53:11 +02:00
Keith Paterson
cfa7a1bf3b Rollback Electron upgrade 2021-04-16 14:45:28 +02:00
Keith Paterson
fd40bc76aa Logging 2021-04-16 14:44:43 +02:00
Keith Paterson
281e4b1339 Merge branch 'dependabot/npm_and_yarn/electron-9.4.0' of https://github.com/Portree-Kid/flightgear-airports.git
# Conflicts:
#	package-lock.json
2021-04-15 11:31:43 +02:00
Keith Paterson
77b8d81378 Updated element ui 2021-04-15 11:29:30 +02:00
Keith Paterson
21e27bf913 #124 Editable Tower height 2021-04-14 16:56:30 +02:00
Keith Paterson
92a39591b5 Fixed #128 2021-03-02 13:53:20 +01:00
Keith Paterson
ec700c2ed7 osx-binaries 2021-02-15 09:08:41 +01:00
Keith Paterson
c161b38664 Removed unpacking of osx tgz 2021-02-05 07:36:37 +01:00
Keith Paterson
fd2017624b v 0.30 2021-02-04 11:08:43 +01:00
Keith Paterson
41b7b0498f #126 2021-02-04 08:13:43 +01:00
Keith Paterson
737f9a7b56 #76 2021-01-29 22:38:40 +01:00
Keith Paterson
bd4bdd6b51 Traffic Generator 2021-01-29 17:03:11 +01:00
dependabot[bot]
b95a468cdc build(deps-dev): bump electron from 7.2.4 to 9.4.0
Bumps [electron](https://github.com/electron/electron) from 7.2.4 to 9.4.0.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v7.2.4...v9.4.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-28 20:49:16 +00:00
Keith Paterson
9f6a9a0943 Add ICAO even if one find 2021-01-27 15:35:10 +01:00
Keith Paterson
bf56736ab9 Fix gaps in IDs 2021-01-21 07:55:37 +01:00
Keith Paterson
8732819261 Removed GIT url 2021-01-19 22:05:54 +01:00
Keith Paterson
092d0f2a24 v0.0.29 2021-01-19 21:44:25 +01:00
Keith Paterson
1ad43169cc v0.0.29 2021-01-19 21:29:00 +01:00
Keith Paterson
828e84e5c1 New Tower icon (Ian Tolosa) 2021-01-17 18:20:00 +01:00
Keith Paterson
3ed45ee87e #121 2021-01-17 14:55:29 +01:00
Keith Paterson
6ed5da1a99 #122 2021-01-17 14:48:21 +01:00
Keith Paterson
2936559873 #119 2021-01-17 14:45:00 +01:00
Keith Paterson
d3daa87244 #117 2021-01-17 14:44:28 +01:00
Keith Paterson
ba1f5ef2e9 #120 2021-01-15 20:54:15 +01:00
Keith Paterson
bcc5bbb335 #114 / #117 Save history 2021-01-14 22:27:42 +01:00
Keith Paterson
fbf0e5cbab #118 2021-01-13 13:22:49 +01:00
Keith Paterson
ab32e6849b Merge branch 'dependabot/npm_and_yarn/axios-0.21.1' 2021-01-13 10:08:36 +01:00
Keith Paterson
ee5a6a5484 #116 Click on Toollayer closes it 2021-01-13 09:35:36 +01:00
Keith Paterson
44e289f3ae v0.0.28 2021-01-12 20:39:15 +01:00
Keith Paterson
7625ea1cf9 Small fixes 2021-01-12 19:39:50 +01:00
Keith Paterson
ac53005c0d Non interactive layers 2021-01-11 22:00:22 +01:00
Keith Paterson
25e603d15f #115 Nose Wheel 2021-01-11 21:59:52 +01:00
Keith Paterson
618346d1b1 #115 Better edit of NoseWheel position 2021-01-11 13:25:06 +01:00
Keith Paterson
51d114e6d2 #113 hack adding points 2021-01-10 20:01:28 +01:00
Keith Paterson
d317bd049c #109 Fixed 2021-01-08 09:13:56 +01:00
Keith Paterson
2d94425de5 #110 2021-01-07 21:42:12 +01:00
Keith Paterson
1bba5e2157 #108 2021-01-07 16:51:23 +01:00
dependabot[bot]
9b757150d5 build(deps): bump axios from 0.18.1 to 0.21.1
Bumps [axios](https://github.com/axios/axios) from 0.18.1 to 0.21.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.21.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.18.1...v0.21.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-05 21:41:11 +00:00
Keith Paterson
4dd17ad726 #107 Tower position editable 2021-01-04 22:18:38 +01:00
Keith Paterson
e6d473c73e #73 pushback above non pushback 2021-01-02 22:21:09 +01:00
Keith Paterson
84fd39c3b5 #90 add multiple airlines 2021-01-02 19:51:22 +01:00
Keith Paterson
67b2220bbe API_KEY 2021-01-01 22:41:36 +01:00
Keith Paterson
6391ecf65d API_KEY 2021-01-01 22:00:15 +01:00
Keith Paterson
03c3956d94 #75 more decimals 2020-12-31 17:17:58 +01:00
Keith Paterson
4c0f09e8d9 Fix Route Edit #40 2020-12-31 16:01:00 +01:00
Keith Paterson
af03c5fe12 Not interactive when not editing 2020-12-30 18:15:47 +01:00
Keith Paterson
b7954d15df API_KEY 2020-12-29 22:08:23 +01:00
Keith Paterson
3b55747ec6 Build Fix 2020-12-29 21:08:23 +01:00
Keith Paterson
6bb3ec531d 0.0.27 2020-12-29 20:58:09 +01:00
Keith Paterson
c5891498e7 API_KEY 2020-12-29 20:56:11 +01:00
Keith Paterson
e4363dd2a6 V 0.0.28 2020-12-29 19:38:37 +01:00
Keith Paterson
92009bfce0 API_KEY 2020-12-28 22:33:38 +01:00
Keith Paterson
98b92ef308 API_KEY 2020-12-28 22:02:40 +01:00
Keith Paterson
5fc343a7c1 API_KEY 2020-12-27 21:57:42 +01:00
Keith Paterson
92f0b72456 V0.0.27 2020-12-27 19:21:27 +01:00
Keith Paterson
5c01f2a656 Remove Menu 2020-12-26 20:50:55 +01:00
Keith Paterson
956e347517 Panes 2020-12-22 21:15:55 +01:00
Keith Paterson
5aa33c6814 Fix pushback create 2020-12-22 19:51:43 +01:00
Keith Paterson
a9b2558cf2 Fix position of Tower Icon 2020-12-21 16:36:07 +01:00
Keith Paterson
0bb3021e3c Undefined layer if not found 2020-12-21 16:35:31 +01:00
Paterson
8274fb882e Merge branch 'master' of C:\Users\dikpater\flightgear-airports\flightgear-airports\.git into NodeJS 2020-12-18 21:50:51 +01:00
Paterson
9e82084e52 Path cleaned 2020-12-18 21:44:03 +01:00
Paterson
edf0e23f8b Migrate to Node v14.15.1 2020-12-15 18:01:07 +01:00
portree_kid
f4bc2f47ce Threshold icons 2020-12-08 21:52:35 +01:00
portree_kid
91c1d93fd3 Towerlayer 2020-12-07 22:19:27 +01:00
portree_kid
3a9c1cd3c2 #66 2020-11-23 07:05:09 +01:00
portree_kid
a43a6c476b Multiline Edit 2020-11-22 14:20:03 +01:00
portree_kid
56284283bf #51 Add Version 2020-11-21 20:19:53 +01:00
portree_kid
41376bb130 #61 Layercontrol fixed 2020-11-19 22:29:50 +01:00
portree_kid
e13127de4b #101 2020-11-18 22:03:35 +01:00
portree_kid
60903eb3e5 MultiLine Edit 2020-11-18 20:26:40 +01:00
portree_kid
e8b0c3c923 https://github.com/Portree-Kid/flightgear-airports/issues/98 2020-10-20 21:12:32 +02:00
portree_kid
d0361b0e76 Filesettings 2020-10-20 21:11:50 +02:00
portree_kid
9c5f7f58f7 https://github.com/Portree-Kid/flightgear-airports/issues/1 2020-10-14 22:49:48 +02:00
portree_kid
062dec2303 v0.0.26 2020-10-05 22:44:57 +02:00
portree_kid
5130503153 https://github.com/Portree-Kid/flightgear-airports/issues/95 2020-10-05 20:26:28 +02:00
portree_kid
7c57323daf https://github.com/Portree-Kid/flightgear-airports/issues/93 2020-10-04 22:18:51 +02:00
portree_kid
71c8a6f8e0 https://github.com/Portree-Kid/flightgear-airports/issues/96 2020-10-04 13:11:13 +02:00
portree_kid
eeaaca03f4 Version 0.0.25 2020-09-30 20:54:18 +02:00
portree_kid
0f9f95c09d Sorting of parking on load 2020-09-30 20:45:52 +02:00
portree_kid
300d243ef8 Upload 2020-09-28 22:39:19 +02:00
portree_kid
bf8f7002ae Problem storing pushback hold points 2020-09-28 22:38:55 +02:00
portree_kid
dbb60162a7 Snap To 2020-08-28 22:02:36 +02:00
portree_kid
715f3c968e Snap to 2020-08-28 22:02:13 +02:00
portree_kid
0146ee8623 https://github.com/Portree-Kid/flightgear-airports/issues/89 2020-08-26 11:16:15 +02:00
portree_kid
4d5a4f4ff7 Disable AI Layer 2020-08-25 21:48:48 +02:00
portree_kid
217d3d7a8e Group heading broken 2020-08-24 14:52:08 +02:00
portree_kid
939322471d https://github.com/Portree-Kid/flightgear-airports/issues/14 2020-08-24 14:29:48 +02:00
portree_kid
2ff284df5c Remove Arc when removing node 2020-08-17 19:22:12 +02:00
portree_kid
67c8814691 Move Projectlevel Buttons 2020-08-16 17:17:35 +02:00
portree_kid
a1fca579af Logging 2020-08-13 09:56:04 +02:00
portree_kid
63e08cacbf Better group edit 2020-08-08 15:25:22 +02:00
portree_kid
54fc153feb Update tooltip in box select 2020-08-06 22:45:21 +02:00
portree_kid
dc480ad25d Box not displayed on new Parking 2020-08-06 20:59:30 +02:00
portree_kid
7c8cc0e705 https://github.com/Portree-Kid/flightgear-airports/issues/88 2020-08-03 22:37:37 +02:00
portree_kid
00132e46fb Restore Min Zoom after Edit 2020-08-02 21:36:37 +02:00
portree_kid
0172b49450 0.0.24 2020-08-02 16:13:51 +02:00
portree_kid
ec718a54ff https://github.com/Portree-Kid/flightgear-airports/issues/87 2020-08-02 15:44:15 +02:00
portree_kid
07b9d3efee https://github.com/Portree-Kid/flightgear-airports/issues/86 2020-08-02 14:43:03 +02:00
portree_kid
a2bd53637b https://github.com/Portree-Kid/flightgear-airports/issues/25 2020-08-02 14:19:15 +02:00
portree_kid
8a328a1707 0.0.23 2020-08-01 20:32:57 +02:00
portree_kid
02433ac77c https://github.com/Portree-Kid/flightgear-
airports/issues/85
2020-08-01 09:08:15 +02:00
portree_kid
0499ee82c2 https://github.com/Portree-Kid/flightgear-airports/issues/83 2020-08-01 07:23:26 +02:00
portree_kid
2178061b55 Msgs 2020-07-29 13:56:28 +02:00
portree_kid
c569e45a6e https://github.com/Portree-Kid/flightgear-airports/issues/72 2020-07-28 21:39:40 +02:00
portree_kid
1075e8d466 https://github.com/Portree-Kid/flightgear-airports/issues/77 2020-07-28 12:22:47 +02:00
portree_kid
ed35aab63b https://github.com/Portree-Kid/flightgear-airports/issues/57 2020-07-27 21:28:52 +02:00
portree_kid
febd720588 https://github.com/Portree-Kid/flightgear-airports/issues/82 2020-07-27 07:04:53 +02:00
portree_kid
93a5c954e5 https://github.com/Portree-Kid/flightgear-airports/issues/84 2020-07-26 21:12:50 +02:00
portree_kid
d6acedb384 https://github.com/Portree-Kid/flightgear-airports/issues/73 2020-07-26 21:11:22 +02:00
portree_kid
6770b52f89 https://github.com/Portree-Kid/flightgear-airports/issues/69 2020-07-26 19:44:21 +02:00
portree_kid
9fe7322c7b Checkmsg & Popover 2020-07-25 21:40:13 +02:00
portree_kid
c26c123088 Hint for link 2020-07-23 21:41:32 +02:00
portree_kid
d3d4d91faf 0.0.22 2020-07-23 19:40:59 +02:00
portree_kid
30d6bce2d8 https://github.com/Portree-Kid/flightgear-airports/issues/71 2020-07-22 22:20:18 +02:00
portree_kid
3ecace714e https://github.com/Portree-Kid/flightgear-airports/issues/59 2020-07-22 21:28:31 +02:00
portree_kid
a52b3c3b67 https://github.com/Portree-Kid/flightgear-airports/issues/63 2020-07-20 20:36:51 +02:00
portree_kid
a4f00b8a22 https://github.com/Portree-Kid/flightgear-airports/issues/78 2020-07-20 10:49:14 +02:00
portree_kid
9f1420309b Iligemate ends 2020-07-17 20:52:27 +02:00
portree_kid
a28c7cdddf https://github.com/Portree-Kid/flightgear-airports/issues/54 2020-07-17 16:38:44 +02:00
portree_kid
df28183232 https://github.com/Portree-Kid/flightgear-airports/issues/58 2020-07-17 09:08:43 +02:00
portree_kid
ec03754c95 https://github.com/Portree-Kid/flightgear-airports/issues/58 2020-07-16 21:51:01 +02:00
portree_kid
4fac546e90 https://github.com/Portree-Kid/flightgear-airports/issues/42 2020-07-15 16:16:07 +02:00
portree_kid
6f3b1dd936 https://github.com/Portree-Kid/flightgear-airports/issues/55 2020-07-13 21:05:55 +02:00
portree_kid
9865ce0e9e 0.0.20 2020-07-13 11:49:22 +02:00
portree_kid
a7fb534c2e https://github.com/Portree-Kid/flightgear-airports/issues/56 2020-07-12 22:59:56 +02:00
portree_kid
82f88d159b https://github.com/Portree-Kid/flightgear-airports/issues/49 2020-07-12 22:18:05 +02:00
portree_kid
d61dc7c955 https://github.com/Portree-Kid/flightgear-airports/issues/47 2020-07-12 20:58:12 +02:00
portree_kid
d372170fe9 0.0.20 2020-07-10 22:11:10 +02:00
portree_kid
7e032e73b2 https://github.com/Portree-Kid/flightgear-airports/issues/46 2020-07-10 21:44:51 +02:00
portree_kid
d4aa4b09a0 Selection err 2020-07-10 21:42:44 +02:00
portree_kid
79631004f8 Add Airline 2020-07-10 16:21:24 +02:00
portree_kid
29355c63da 0.0.19 2020-07-10 12:06:59 +02:00
portree_kid
176fbeed08 Merge remote-tracking branch 'remotes/origin/dependabot/npm_and_yarn/electron-7.2.4' into electron_update 2020-07-10 08:25:58 +02:00
portree_kid
6c052c16e2 https://github.com/Portree-Kid/flightgear-airports/issues/33 2020-07-09 22:03:46 +02:00
portree_kid
d6f1204dd1 https://github.com/Portree-Kid/flightgear-airports/issues/37 2020-07-09 21:47:10 +02:00
portree_kid
9a8e03f9cf radius 10 parking pos 2020-07-09 17:03:11 +02:00
portree_kid
8b360beaf1 Dragging of parking 2020-07-09 13:13:21 +02:00
portree_kid
9dfd4320fe Dragging of parking 2020-07-09 13:11:41 +02:00
portree_kid
dc874ff9f5 Sort WIP 2020-07-08 12:41:32 +02:00
portree_kid
bed38277ce Layer visibility 2020-07-08 11:33:31 +02:00
portree_kid
4c01775ccf 0.1.18 2020-07-08 08:35:23 +02:00
portree_kid
8a167dc991 Layer Control 2020-07-07 22:36:25 +02:00
dependabot[bot]
d9ab69a29e Bump electron from 4.2.12 to 7.2.4
Bumps [electron](https://github.com/electron/electron) from 4.2.12 to 7.2.4.
- [Release notes](https://github.com/electron/electron/releases)
- [Changelog](https://github.com/electron/electron/blob/master/docs/breaking-changes.md)
- [Commits](https://github.com/electron/electron/compare/v4.2.12...v7.2.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-07 10:17:39 +00:00
portree_kid
a71d95cb74 Wider column 2020-07-06 22:38:02 +02:00
portree_kid
760c7e4fd0 File Logger 2020-07-06 22:06:23 +02:00
portree_kid
70926ed2ff Link lines 2020-07-06 20:59:21 +02:00
portree_kid
4b7183c428 Backwards line 2020-07-05 21:39:29 +02:00
portree_kid
8fcd1f64fb Update Stats on Save 2020-07-05 21:18:14 +02:00
portree_kid
64685aa1d1 https://github.com/Portree-Kid/flightgear-airports/issues/28 2020-07-04 22:37:32 +02:00
portree_kid
fad6b3de84 Scanname 2020-07-04 22:37:00 +02:00
portree_kid
3c315c4f0c 0.0.17 2020-07-04 20:30:04 +02:00
portree_kid
3cc8f1e34a Select on parking box click 2020-07-04 19:57:38 +02:00
portree_kid
3416333b42 Box check 2020-07-04 08:24:58 +02:00
portree_kid
6de8d22381 Parking Box 2020-07-03 12:49:03 +02:00
portree_kid
87750d0826 Fixes 2020-07-02 21:29:37 +02:00
portree_kid
ba1fa6cb4f Check only for real runway ends 2020-07-02 21:29:27 +02:00
portree_kid
8a646490c8 Push to back 2020-07-02 13:01:28 +02:00
portree_kid
921321d3b0 https://github.com/Portree-Kid/flightgear-airports/issues/21 2020-07-01 22:37:25 +02:00
portree_kid
7a75e4f392 https://github.com/Portree-Kid/flightgear-airports/issues/18 2020-07-01 22:03:23 +02:00
portree_kid
af4d3b7143 0.0.16 2020-07-01 22:02:23 +02:00
portree_kid
463c6d9feb rmdir icons 2020-07-01 21:09:06 +02:00
portree_kid
05a54a2d2f Check for unconnected pushback nodes 2020-07-01 20:57:18 +02:00
portree_kid
233362b2fa - rmdir icons 2020-07-01 16:30:08 +02:00
portree_kid
ec9b5be879 Use is required only unpack fails 2020-07-01 15:49:22 +02:00
portree_kid
234ec5eb69 MacOSx 2020-07-01 15:37:33 +02:00
portree_kid
d9cc35f2c2 Travis 2020-07-01 15:17:03 +02:00
portree_kid
78f86d41ee untar 2020-07-01 14:54:35 +02:00
portree_kid
b4003c3c06 Display box direction 2020-07-01 13:45:05 +02:00
portree_kid
72dc611764 Parking group selection 2020-06-30 21:45:00 +02:00
portree_kid
57b83daa51 Multiediting for Parkings 2020-06-30 16:34:14 +02:00
portree_kid
94779a1686 Guide drawing 2020-06-29 09:08:53 +02:00
portree_kid
b9a34df117 Check Pushback Routes 2020-06-28 14:31:03 +02:00
portree_kid
7b7afb357b NPE 2020-06-28 14:30:44 +02:00
portree_kid
1d048c0d28 Vertex deselect 2020-06-26 21:31:18 +02:00
portree_kid
98ff3a92aa Parking number not a number 2020-06-26 20:07:10 +02:00
portree_kid
594b4f5411 Message for upload started 2020-06-26 20:06:14 +02:00
portree_kid
71432255f5 f 2020-06-26 11:56:46 +02:00
portree_kid
38c380a433 find 2020-06-26 11:18:04 +02:00
portree_kid
1463427b97 f 2020-06-26 11:03:11 +02:00
portree_kid
952d2834b0 mv -v 2020-06-26 10:55:15 +02:00
portree_kid
eea409a087 find 2020-06-26 10:34:44 +02:00
portree_kid
afc0790b81 f 2020-06-26 10:21:03 +02:00
portree_kid
a05c96be61 list dire 2020-06-26 10:02:25 +02:00
portree_kid
d5071c4333 merge 2020-06-26 09:44:13 +02:00
portree_kid
cb5e641bbc f 2020-06-26 09:30:09 +02:00
portree_kid
7191b41dc6 pwd 2020-06-26 08:31:30 +02:00
portree_kid
d18cfed7e9 Before Workspaces 2020-06-26 08:11:10 +02:00
portree_kid
29d02be3ad Stages 2020-06-26 07:49:59 +02:00
portree_kid
484d0673c1 Workspaces 2020-06-26 07:40:38 +02:00
portree_kid
5287576c9c workspaces 2020-06-26 07:20:34 +02:00
portree_kid
0387719195 Stages 16 2020-06-26 07:03:56 +02:00
portree_kid
df6501d06e Stages 15 2020-06-25 23:01:51 +02:00
portree_kid
a28f7d685d Stages 14 2020-06-25 22:45:07 +02:00
portree_kid
4503bf3dba Stages 13 2020-06-25 22:29:14 +02:00
portree_kid
70dddc2299 Stages 12 2020-06-25 22:21:26 +02:00
portree_kid
c8fdec8cb5 Stages paths: build 2020-06-25 21:49:05 +02:00
portree_kid
3a22487fb9 Stages 11 2020-06-25 21:42:27 +02:00
portree_kid
e4f2a62224 Steps 10 2020-06-25 21:25:37 +02:00
portree_kid
0acc30fa18 Stages 9 2020-06-25 21:13:20 +02:00
portree_kid
5058cc705b Stages 8 2020-06-25 20:57:55 +02:00
portree_kid
399b567497 Stages 7 2020-06-25 20:50:01 +02:00
portree_kid
bc8a99a996 Stages 6 2020-06-25 20:44:25 +02:00
portree_kid
589d7c28d8 Stage 5 2020-06-25 20:37:19 +02:00
portree_kid
fe7fbd433e Stages 4 2020-06-25 20:25:46 +02:00
portree_kid
8394e46add Stages 3 2020-06-25 17:05:38 +02:00
portree_kid
d8438c331a Stages 2 2020-06-25 17:01:15 +02:00
portree_kid
2515649b55 Stages 2020-06-25 16:55:42 +02:00
portree_kid
01f3db55dd Two stage build 2020-06-25 16:14:36 +02:00
portree_kid
55df912131 Version 0.0.15 2020-06-25 08:12:06 +02:00
portree_kid
a9019340a4 Status for groundweb upload 2020-06-24 20:57:21 +02:00
portree_kid
c221699b16 Username, Debugger off by default 2020-06-23 21:19:12 +02:00
portree_kid
4b1ee70da5 Styling 2020-06-22 17:04:50 +02:00
portree_kid
063fd049f0 Check 2020-06-21 22:55:39 +02:00
portree_kid
b841b8560d Parking defaul gate 2020-06-21 22:55:25 +02:00
portree_kid
50e3806d12 0.0.14 2020-06-21 22:54:29 +02:00
portree_kid
2f50bf1782 center 2020-06-21 22:53:42 +02:00
portree_kid
4923b467f4 Move Parking to AirportTab and make Airport always visible 2020-06-19 21:04:44 +02:00
portree_kid
e122bbbb08 Bug with FrontWheel pos 2020-06-19 21:03:46 +02:00
portree_kid
474e8bf113 DEV_MODE 2020-06-19 21:03:07 +02:00
portree_kid
77bf06f764 Crappy H1 2020-06-19 21:02:33 +02:00
portree_kid
33421e5aab Check for parking type 2020-06-18 22:10:44 +02:00
portree_kid
bd7530a9a1 Missing reference to dijikstra 2020-06-16 09:42:19 +02:00
portree_kid
fb4d518c6c Deselect 2020-06-15 21:52:42 +02:00
portree_kid
ac0879b7b8 None type parking disabled 2020-06-15 21:52:32 +02:00
portree_kid
dea6a9bf19 Select new Parking 2020-06-15 07:09:17 +02:00
portree_kid
d4c0e59779 Selection 2020-06-11 22:18:02 +02:00
portree_kid
3f9183439f Remove Threshold Layer 2020-06-10 19:28:32 +02:00
portree_kid
228f16994b APT Dat 715 Segments (Line code 10) 2020-06-10 07:09:39 +02:00
portree_kid
d9eed59de7 Node edit 2020-06-08 22:54:01 +02:00
portree_kid
32c0cbd33b Better point selection 2020-06-07 20:46:22 +02:00
portree_kid
46091aa7db Cancel events on buttons 2020-06-03 16:31:27 +02:00
portree_kid
743b5c6e8d Stop click reaching anything below the buttons 2020-06-03 15:22:47 +02:00
portree_kid
0f890d1593 Bi Directional Wheelpos 2020-06-02 23:15:48 +02:00
portree_kid
3aea131727 Bidirectional Binding Parking/Heading 2020-06-02 12:02:37 +02:00
portree_kid
66687bb35f Edit node coordinates bidirectional 2020-06-01 13:37:12 +02:00
portree_kid
23cc742c93 Remove vertex 2020-05-31 20:53:38 +02:00
portree_kid
2a7c8a735f Bugfix geo-coordinate-parser 2020-05-26 20:40:17 +02:00
portree_kid
93d6328dea Fix bug with 0.000 coords 2020-05-26 15:29:00 +02:00
portree_kid
7c643b209a Show versionnumber 2020-05-25 22:00:01 +02:00
portree_kid
9a3f473949 WIP Buttons only when not editing 2020-05-25 21:13:13 +02:00
portree_kid
37e97e84e2 Bugs 2020-05-25 16:55:21 +02:00
portree_kid
4090519e51 Step presision 2020-05-25 16:54:45 +02:00
portree_kid
9f381dea12 Bug in upload 2020-05-24 22:50:29 +02:00
portree_kid
e3da808f5c Lookup error 2020-05-24 22:45:31 +02:00
portree_kid
d80a308f4d Removed check for end 2020-05-24 22:07:24 +02:00
portree_kid
c144aab56c Fix scanning 2020-05-24 20:54:56 +02:00
portree_kid
294abffcc4 Fix mapmoving 2020-05-24 20:54:42 +02:00
portree_kid
3ee9ec60e3 Remove center storing 2020-05-24 14:57:50 +02:00
portree_kid
7142373c2a Heading Edit 2020-05-24 11:15:25 +02:00
portree_kid
1f395e2157 Tooltips for WorkInProgress 2020-05-24 09:04:07 +02:00
portree_kid
315cb8b1f0 WIP 2020-05-23 23:22:10 +02:00
portree_kid
7f9c5b5457 Consistant editing 2020-05-22 14:43:28 +02:00
portree_kid
eb69b0cdb2 Bug in Upload 2020-05-22 09:05:13 +02:00
portree_kid
49165227af Pushback ArcEdit 2020-05-21 18:41:40 +02:00
portree_kid
0938ab8780 Wrong method call 2020-05-21 18:16:25 +02:00
portree_kid
f3703d1ef8 Open Sidebar 2020-05-21 18:16:03 +02:00
portree_kid
26726df251 Check for looooong routes 2020-05-21 10:44:07 +02:00
portree_kid
fc25b62737 Check of RunwayNodes 2020-05-16 10:47:18 +02:00
portree_kid
503dacd54a Deselection 2020-05-16 10:46:34 +02:00
portree_kid
07042d92e7 Remove App Version 2020-05-16 10:46:17 +02:00
portree_kid
f9f9256ffe Deselection 2020-05-16 10:45:56 +02:00
portree_kid
3b9ab26035 Mapmove improvement 2020-05-16 09:17:13 +02:00
portree_kid
00bdd6c558 Check parking number, number with letter (9L) 2020-05-15 16:33:33 +02:00
portree_kid
de0267ac7d Bug with lookup 2020-05-13 19:34:03 +02:00
portree_kid
079a56abfd V 0.0.10 2020-05-13 18:53:44 +02:00
portree_kid
a4d048c116 Name check 2020-05-13 18:53:23 +02:00
portree_kid
6e87c3428b Styling and radii 2020-05-13 15:14:14 +02:00
portree_kid
b06b1b6a03 Upload check 2020-05-13 11:01:28 +02:00
portree_kid
9ce9e54c26 styling 2020-05-11 22:08:19 +02:00
portree_kid
d8093c323c Performance APT scan 2020-05-11 20:24:08 +02:00
portree_kid
8d13cf3d35 Marker for front wheel 2020-05-05 22:31:04 +02:00
portree_kid
be078ecced Search performance 2020-05-05 14:28:33 +02:00
portree_kid
d8ad1ffcc4 Remove tagging 2020-05-04 19:28:36 +02:00
portree_kid
c7fae2427a 0.0.8 2020-05-04 19:03:45 +02:00
portree_kid
ffc7109ea5 Removed draft 2020-05-04 18:50:45 +02:00
portree_kid
2ec5ab4512 overwrite: true 2020-05-04 18:28:36 +02:00
portree_kid
f312dc5998 Release name 2020-05-04 18:19:31 +02:00
portree_kid
ad8bc5d3f3 draft/tagname 2020-05-04 16:41:03 +02:00
portree_kid
94b3e8995e Directory select in install 2020-05-04 16:40:49 +02:00
portree_kid
b4ba826fd3 Removed docker 2020-05-03 22:43:54 +02:00
portree_kid
2713d2cbfa linux build 2020-05-03 21:32:43 +02:00
portree_kid
0a112fe76a Message after Test copy 2020-05-03 21:28:17 +02:00
portree_kid
ead320bdb5 NaN in ParkingItem 2020-05-03 21:05:29 +02:00
portree_kid
da9c89d762 Backupfile 2020-05-03 09:48:53 +02:00
portree_kid
59d58b05ab WIP 2020-05-03 09:48:38 +02:00
portree_kid
cb2f9c69ab Version 0.0.8 2020-05-03 09:46:28 +02:00
portree_kid
eaae16c291 Debugger 2020-04-28 20:38:22 +02:00
portree_kid
0756f377b3 Remove new TaxiwayNodes 2020-04-28 20:37:34 +02:00
portree_kid
fb72f8d760 Remove frequency 2020-04-27 15:23:05 +02:00
portree_kid
90740751dd Error pushback for real pushback routes 2020-04-26 20:23:38 +02:00
portree_kid
e08238c64d logging 2020-04-25 21:45:51 +02:00
portree_kid
4cc5e1913d logging to debug 2020-04-25 21:36:05 +02:00
portree_kid
bd0e45440c show selected airport 2020-04-25 14:56:26 +02:00
portree_kid
4d87ac16b8 Limit zoom 2020-04-24 22:05:06 +02:00
portree_kid
9065ae75f9 Parking List 2020-04-24 21:19:05 +02:00
portree_kid
af2578cc36 Tooltip changed 2020-04-24 21:18:21 +02:00
portree_kid
98c29b8b62 Pushback Route create 2020-04-18 22:35:24 +02:00
portree_kid
4b908f57ce Check for empty names 2020-04-16 20:54:50 +02:00
portree_kid
826781647e Debugger 2020-04-16 08:35:43 +02:00
portree_kid
304269b996 try(Catch) 2020-04-12 14:05:54 +02:00
portree_kid
05318a7782 NPE 2020-04-11 21:52:44 +02:00
portree_kid
7092441d3b Webservice response 2020-04-11 21:52:30 +02:00
portree_kid
677695d0ba Centerlines 2020-04-11 21:51:24 +02:00
portree_kid
4c76d30010 Test Directory copy 2020-04-11 08:10:46 +02:00
portree_kid
41214581fd Set icao on click 2020-04-10 16:04:59 +02:00
portree_kid
a4baeee2ea Work in progress 2020-04-10 13:37:49 +02:00
portree_kid
70a0f5a7de branches: release 2020-04-09 07:42:07 +02:00
portree_kid
2956bfbddb file_glob: true 2020-04-09 06:52:26 +02:00
portree_kid
3a6fdab354 remove on tags 2020-04-08 23:24:13 +02:00
portree_kid
fd15052c1c skip_cleanup: true 2020-04-08 23:20:21 +02:00
portree_kid
28dd9a1b63 Tags deploy 2020-04-08 23:16:48 +02:00
portree_kid
1af687fd2c 0.0.5 2020-04-08 22:57:29 +02:00
portree_kid
69a0e849a6 Bezier lines 2020-04-08 22:38:57 +02:00
portree_kid
cb8a49fc9d Check for holdpoint end 2020-04-03 21:08:03 +02:00
portree_kid
61d9145dd8 Coordinate Setting 2020-04-02 21:36:33 +02:00
portree_kid
1650b44628 Gate type 2020-04-02 21:35:59 +02:00
portree_kid
26a935997b snap 5 2020-04-02 21:34:22 +02:00
portree_kid
ffc8d76d1f Styling 2020-03-31 13:58:52 +02:00
portree_kid
5be00f012f Typo 2020-03-30 23:04:51 +02:00
portree_kid
2d5f2f0063 Issues glueing parking 2020-03-30 22:39:02 +02:00
portree_kid
f515d6249f Load Taxilines 2020-03-30 21:40:00 +02:00
portree_kid
bf59d025f6 Parking List 2020-03-30 17:30:05 +02:00
portree_kid
d5f68613a1 Parking List 2020-03-30 14:17:38 +02:00
portree_kid
4b564798ab Parking Number 2020-03-29 18:21:40 +02:00
portree_kid
1e43d973b2 Thresholds 2020-03-29 18:10:51 +02:00
portree_kid
0870422875 Remove null arcs (begin==end) 2020-03-29 14:59:00 +02:00
portree_kid
c7ebaccb3f Close worker on crash 2020-03-29 14:58:25 +02:00
portree_kid
41463dedff removed index copy 2020-03-29 14:57:55 +02:00
portree_kid
ebf82c09e9 NPE 2020-03-29 14:55:34 +02:00
portree_kid
a4239d385b Handle HTTP 500 2020-03-29 14:55:18 +02:00
portree_kid
821da07c4e Save popup 2020-03-25 14:51:01 +01:00
portree_kid
3c98a0720b Airlines, Parking 2020-03-24 21:26:51 +01:00
portree_kid
686cabb635 Upload Error 500 2020-03-24 21:26:26 +01:00
portree_kid
4ed58fc7fa pushbackRoute 2020-03-24 07:56:51 +01:00
portree_kid
62f24201b2 Help 2020-03-22 16:14:14 +01:00
portree_kid
918cfd750c Check for wrong pushback ends 2020-03-22 12:18:14 +01:00
portree_kid
6d6c065793 Remove arcs 2020-03-22 12:17:58 +01:00
portree_kid
ec629a7ef4 Check 2020-03-20 17:30:53 +01:00
portree_kid
ffca8da053 Check in built version 2020-03-20 06:50:54 +01:00
portree_kid
c2b0fe2a7b Empty frequency list 2020-03-18 20:59:43 +01:00
portree_kid
1f6fc221f9 Frequency Editing and Upload 2020-03-18 16:48:18 +01:00
portree_kid
84989714e5 Zoom large/small 2020-03-14 08:05:37 +01:00
portree_kid
45980c3abe Author name in XML 2020-03-13 22:45:26 +01:00
portree_kid
43f8aeb9d6 Store Frequencies 2020-03-13 08:36:36 +01:00
portree_kid
7d6be33150 Edit Frequencies 2020-03-12 11:44:50 +01:00
portree_kid
56e23da410 New arcs weren't exported direction missing 2020-03-10 14:58:41 +01:00
portree_kid
fe1db57414 Wrong orientation of Parkings 2020-03-10 08:38:29 +01:00
portree_kid
62823feedd Forward/Backward/Bidirection 2020-03-09 19:58:40 +01:00
portree_kid
aba6ac6df5 Build on branches 2020-03-08 22:14:10 +01:00
portree_kid
5f60c46dc9 Writing start node 0 2020-03-08 21:20:55 +01:00
portree_kid
06f630afab ICAO Control 2020-03-08 15:34:19 +01:00
portree_kid
76db33a686 Manual add 2020-03-07 22:12:52 +01:00
portree_kid
2f86bed0bf Check Intersection 2020-03-07 21:35:41 +01:00
portree_kid
9eb92fb13d Single ICAO search 2020-03-06 15:29:46 +01:00
portree_kid
ae680eb994 Remove Linux for time being 2020-03-06 09:46:31 +01:00
portree_kid
034c132347 NPE in editArc 2020-03-06 07:58:04 +01:00
portree_kid
8fccc23009 New middle vertex fixed 2020-03-05 22:05:20 +01:00
portree_kid
a76f7d6209 Remove snap 2020-03-05 20:16:11 +01:00
portree_kid
b4206d3a53 Snapcraft 2020-03-05 19:59:10 +01:00
117 changed files with 12522 additions and 2206 deletions

View File

@@ -125,6 +125,18 @@ let rendererConfig = {
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options
},
process,
};
},
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,

View File

@@ -97,7 +97,18 @@ let webConfig = {
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
minify: {
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options
},
process,
};
}, minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true

View File

@@ -1,32 +1,82 @@
osx_image: xcode8.3
sudo: required
dist: trusty
dist: xenial
language: node_js
node_js:
- 10.15.3
matrix:
include:
- os: osx
- os: windows
- os: linux
workspaces:
use:
- binaries
cache:
directories:
- node_modules
- "$HOME/.electron"
- "$HOME/.cache"
addons:
before_install:
addons:
before_install:
install:
- source ~/.bashrc
- npm install -g xvfb-maybe
- npm install
script:
- npm run build
deploy:
provider: releases
api_key:
secure: Xm3z86gQmpSajvlXSVgG7mxcaS5K7GfE4HrARIDR4pQ4UguQ3/fFOenRyKAQImmH0kLCSIbVE21NMAQ3YI2F9El9I6GU7Wirccsg14TZZiBQAzKOTXZ+JsBloeBxuTOsR84SkO6pV8OT/pRnQd9frqyc1W7wzoX+0sQykMztXCNfI+1MXXGOAgMzlKuDMO1PVENz0V63mWipL/Mae/SyrHa1Bws+4LJKvv42m0HMKdN7ekt41vxcSUvLmN+YLlqUEVr/eUhQXUNCaWlmM0KxmfuzTedca5/1yzLvN8smEt2I2b0DFRM3Mi/QdI22fVRHs6XGpm8yqZRKmhySDXFspXKWBiuCF6AezX6NiIe/ZUh10gIukjkyLtrNk/o29qhj2WE9HJz4xhsGMAx3632cLSMPku5ALN7jv5scWjwCGNHs4ZeZyyePMeGM6Y6pje1uJPbxWGjwoV6iI4Y4esP+z3KcXvZdnzkpYMy2mzdT4gf7A7zieax1YwB6U/MEanXYKFBH4yzYK9+hx3ck2eLT3LcV/ChHN2bKmERJdf18h+zI8e5YKTzd3/kHLiUUv19N2a4TUYloGOyzrLwn2VD1Xm4jJVVLTIQvWbH5YK7cNkHYa4+PnsB+JppSoby7HiBcothw4OLpJO8eCMqXcPRU1n+cRAZTB7VOS7pWh8e8LFE=
file: build/flightgear-airports*.*
draft: true
on:
repo: Portree-Kid/flightgear-airports
node_js:
- 10.15.3
jobs:
include:
- stage: Build binaries
os: windows
script:
- npm run build
- mkdir windows-binaries
- mv -v build/flightgear-airports* windows-binaries/
- ls -l windows-binaries
workspaces:
create:
name: windows-binaries
paths: windows-binaries
- os: linux
script:
- npm run build
- mkdir linux-binaries
- mv -v build/flightgear-airports* linux-binaries/
- ls -l linux-binaries
workspaces:
create:
name: linux-binaries
paths: linux-binaries
- os: osx
script:
- npm run build
- mkdir osx-binaries
- mv -v build/flightgear-airports* osx-binaries/
- ls -l osx-binaries
workspaces:
create:
name: osx-binaries
paths: osx-binaries
- stage: Deploy
install:
- pwd
- find -name *osx-binaries*
- tar -xzf ${CASHER_DIR}/osx-binaries-fetch.tgz
- find -name flightgear-airports*
- cp ./Users/travis/build/Portree-Kid/flightgear-airports/osx-binaries/*.dmg build/
- cp ./C:/Users/travis/build/Portree-Kid/flightgear-airports/windows-binaries/*.exe build/
- cp ./linux-binaries/*.AppImage build/
- ls -l "build/"
script: skip
workspaces:
use:
- windows-binaries
- linux-binaries
- osx-binaries
deploy:
skip_cleanup: true
overwrite: true
draft: true
name: '0.0.33'
provider: releases
api_key:
secure: FElxoWQZKIcc9gHFY4yp1CsqvJr76BWdDrp7VsgPN7PuEoZht5Boic3fWbxKyT1fKGr8Yem6CJBXQjaqIZiWWMxuF3KYmsdOK0YRpj6WCePpl0AdZcnWDMsAGuC3ornb1CB3R6x8fx7SMMl8Ghc9/JFDUdjduvT9A0KZ1JrTZLYpP1iOq+aAW0H3byu+tNfTDvee9MzMAtjKriDz0L5z94VQ1lEmuc79qUmNvv9gIJUoNOI58QCDUuOEz2eZPooMz5cOdaupzBK130V7ki/UWbP3meXmzRKN6/iSA9iXiIrBerFtA4wG6hVvKMvKP7d7NalWAtpzOElFivlo7tp+Z1SfjSfZHmZG/PMyndVh1VPmMzX6a76qLyJFGIvOZKOkzzmVyGSVGlUHm8GCkw5wd0S3m6f1eA/YJO5c8aduC7M+qZSuEVdzaAW5xf8Fb1/rs7jELZPxuzio9i+T7vRQ1OFrCBtExQAuO9EyfaIvaVQAImvhOPiza6zkfEkF622LIQZIBL9HZ1OeKKrdWaWG98nUGWDIFXb+B4IBM1cJIPRYmaAbnWVVCBzIGxcb1pMb/zzSocu9poEWCY/CVHuOG7Xg45pghTunPS54E9qxvBx8jDIGz4s3bRwWnBI0DJ5b6aBNqMpghmpKYMCoviT+RwCIKOENlFdrPWH5pk9CmRc=wYHBe6BJtqhkjodvabd6fSX5blecd/y9sSfHV9prk7PRFxkpkp4Fw/sp086SFlunTGmTn359T1mhgr0MD/VDRXu34PEdn47Jpu8CTPfnihiK/3iRaGqW0qLqhmpd6GYc1ecm9sahxYwXgWyCtGZQbdHvrCIiS/Zm1hQtg3Q0O3eYeF+zQXrfCCTIhLLy2egID4Z23RbK620y3dqcSfbWfLzPpXr+2MYVrNZ3gukKiVauf2kBYPNzMTKEtIBfAGHiwQwlCm/fRN5NcL9bwMGmMGktJkIHgpAa2A9nnyw7EadKEKqVw4tI/j1813/FDWlJZBg89RNsZJ0QGSMQcMB0h1XkWZFPV02gqMN2z2eNXsvTJ882g8UkCBzPWYsRQGNKEx9FUFtZO9HAjOnnpAsdTcaQYTow7BiVmPBePtCFKsPyqMgJUsuz6WhUmfJ5FMbMDKn1N6ymyBYmTic5KrT3FRR3s7vFyTNdfNfwd7aNMnZSGb2uHLZyGjrC2QFK3AE/F+jP2bMu/9rcuLcp/CSLpxpN5F6RpVVfqY8X9joEUeXOSd4kO7OPBsg4lxDCqzVmER/X+MEwM57cmyutTtaREZEDTKCoEhWtcMHqLpCojPWbMsKnMh7NRARNWETe76tcwO88K2LqQFvhhILeTXKlWDpNcbGwqSinF62RXG4DxUQ=
file_glob: true
file: build/*
on:
branches: '0.0.33'
repo: Portree-Kid/flightgear-airports

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"C_Cpp.default.configurationProvider": "go2sh.cmake-integration"
}

13
I.txt Normal file
View File

@@ -0,0 +1,13 @@
I
1050 Generated by WorldEditor 2.0.0r4
1 0 1 0 ABCD BezierTest
120 Linear Feature 4
111 58.21662712 -005.33381019
116 58.21536335 -005.33388344 58.21539229 -005.33536672
120 Linear Feature 2
111 58.21649206 -005.33161294
112 58.21619300 -005.33166788 58.21620265 -005.33296805
112 58.21572029 -005.33166789 58.21573959 -005.33263844
115 58.21535371 -005.33172282
99

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

8
build/installer.nsh Normal file
View File

@@ -0,0 +1,8 @@
!macro preInit
SetRegView 64
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$PROGRAMFILES64\flightgear-airports"
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$PROGRAMFILES64\flightgear-airports"
SetRegView 32
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "$PROGRAMFILES32\flightgear-airports"
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "$PROGRAMFILES32\flightgear-airports"
!macroend

2354
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,15 @@
{
"name": "flightgear-airports",
"version": "0.0.1",
"version": "0.0.34",
"author": "portree_kid <keith.paterson@gmx.de>",
"description": "An software to design Flightgear groundnets",
"license": null,
"license": "GPL-3.0",
"main": "./dist/electron/main.js",
"scripts": {
"build": "node .electron-vue/build.js && electron-builder",
"build:dir": "node .electron-vue/build.js && electron-builder --dir",
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
"dev": "node .electron-vue/dev-runner.js",
"dev": "SET NODE_ENV=development&& node .electron-vue/dev-runner.js",
"e2e": "npm run pack && mocha test/e2e",
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test",
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test",
@@ -18,6 +17,7 @@
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
"test": "npm run unit && npm run e2e",
"mocha": "SET NODE_ENV=mocha&& mocha test/mocha/**/*.js",
"unit": "karma start test/unit/karma.conf.js",
"postinstall": ""
},
@@ -35,7 +35,22 @@
"filter": [
"**/*"
]
}],
},
{
"from": "node_modules/dijkstrajs",
"to": "workers",
"filter": [
"**/dijkstra.js"
]
},
{
"from": "node_modules/@turf",
"to": "workers/node_modules/@turf",
"filter": [
"**"
]
}
],
"files": [
"dist/electron/**/*"
],
@@ -55,35 +70,50 @@
]
},
"mac": {
"icon": "build/icons/icon.icns"
"icon": "build/icons/icon.icns",
"publish": []
},
"win": {
"icon": "build/icons/icon.ico"
"icon": "build/icons/icon.ico",
"publish": []
},
"nsis": {
"oneClick": false,
"perMachine": true,
"allowToChangeInstallationDirectory": true
},
"linux": {
"icon": "build/icons"
"icon": "build/icons",
"target": "AppImage",
"publish": []
}
},
"dependencies": {
"@turf/intersect": "^6.1.3",
"@turf/turf": "^5.1.6",
"axios": "^0.18.0",
"axios": "^0.21.1",
"coordinate-parser": "^1.0.3",
"dijkstrajs": "^1.0.1",
"electron-debug": "^3.0.1",
"element-ui": "^2.12.0",
"element-ui": "^2.15.1",
"file-url": "^3.0.0",
"fs": "0.0.1-security",
"geo-coordinates-parser": "^1.2.3",
"fs-extra": "^9.0.1",
"geo-coordinates-parser": "^1.2.4",
"geodesy": "^2.2.0",
"idb": "^4.0.5",
"jquery": "^3.4.1",
"leaflet": "^1.5.1",
"leaflet-editable": "^1.2.0",
"leaflet-polylinedecorator": "^1.6.0",
"leaflet-search": "^2.9.9",
"leaflet-sidebar-v2": "^3.2.1",
"leaflet-textpath": "^1.2.0",
"leaflet.pattern": "^0.1.0",
"lokijs": "^1.5.8",
"mathjs": "^6.2.5",
"path": "^0.12.7",
"tiny-worker": "^2.3.0",
"vue": "^2.5.16",
"vue-electron": "^1.0.6",
"vue-idb": "^0.2.0",
@@ -112,15 +142,15 @@
"babel-register": "^6.26.0",
"babili-webpack-plugin": "^0.1.2",
"cfonts": "^2.1.2",
"chai": "^4.1.2",
"chai": "^4.2.0",
"chalk": "^2.4.1",
"copy-webpack-plugin": "^4.5.1",
"cross-env": "^5.1.6",
"css-loader": "^0.28.11",
"del": "^3.0.0",
"devtron": "^1.4.0",
"electron": "^4.2.12",
"electron-builder": "^21.2.0",
"electron": "^7.2.4",
"electron-builder": "^22.10.4",
"electron-devtools-installer": "^2.2.4",
"eslint": "^4.19.1",
"eslint-config-standard": "^11.0.0",
@@ -146,7 +176,7 @@
"mocha": "^5.2.0",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"node-sass": "^4.9.2",
"node-sass": "^4.14.1",
"require-dir": "^1.0.0",
"sass-loader": "^7.0.3",
"spectron": "^3.8.0",

View File

@@ -6,7 +6,7 @@
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
<script>
require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, "\\\\") %>')
</script>
<% } %>
</head>

View File

@@ -1,7 +1,11 @@
'use strict'
import { app, BrowserWindow } from 'electron'
import { app, BrowserWindow, Menu } from 'electron'
const { ipcMain } = require('electron')
ipcMain.on('OpenDebugger', (event, arg) => {
mainWindow.webContents.openDevTools()
})
/**
* Set `__static` path to static files in production
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
@@ -16,6 +20,7 @@ const winURL = process.env.NODE_ENV === 'development'
: `file://${__dirname}/index.html`
function createWindow () {
Menu.setApplicationMenu(null)
/**
* Initial window options
*/
@@ -26,10 +31,10 @@ function createWindow () {
nodeIntegration: true,
nodeIntegrationInWorker: true
},
closable: true,
width: 1000
})
mainWindow.loadURL(winURL)
mainWindow.webContents.openDevTools()
mainWindow.onerror = function (message, source, lineno, colno, error) {
console.error(error)

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="150mm" height="150mm" version="1.1" viewBox="0 0 150 150" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-1.6394e-5 -147)"><g transform="translate(-512.5 -153)"><path d="m587.5 347.75c-0.32736 0-0.75863 2.023-0.97757 3.043 0 0-8.7023-0.47356-8.7023 0.14107 0 0.61461 8.733 0.36873 8.733 0.36873 0.0812 0.14151 0.22163 0.15914 0.368 0.18528v0.21306c-0.57176 0.16009-1.4739 0.31231-2.5099 0.41186-1.036 0.0995-1.1332 0.93371-1.1332 0.93371-0.70455 4.8918-0.96276 10.026-1.2071 14.632-0.012 0.0118-0.0165 1.6e-4 -0.0369 9e-3l-20.112 2e-3 -27.221 1.9869c-2.1208 0.16639-2.2014 1.5529-2.2014 3.2272v8.0906l29.858 3.5196 19.67 0.0146c1.2567 9.6974 3.6713 28.109 3.6713 28.109l-12.803 2.1075c-0.75926 0.18181-1.8746 0.23703-2.1181 1.0313-0.3756 1.9795-0.35641 4.2182-0.24741 6.0605 0.0369 0.68858 0.16248 1.2114 0.8672 1.354l13.439 2.401c0.39078 0.0865 0.49893-0.13326 0.58873-0.48422l1.3408-4.2669s-0.081 7.3888 0.73272 9.0111c0.81369-1.6224 0.73271-9.0111 0.73271-9.0111l1.3412 4.2669c0.0898 0.35096 0.19795 0.57077 0.58874 0.48422l13.439-2.401c0.70472-0.14253 0.83032-0.6654 0.8672-1.354 0.10899-1.8424 0.12819-4.081-0.24741-6.0605-0.24351-0.79425-1.3589-0.84947-2.1181-1.0313l-12.803-2.1075s2.4149-18.412 3.6716-28.109l19.67-0.0146 29.859-3.5196v-8.0906c5.8e-4 -1.6743-0.081-3.0608-2.2018-3.2272l-27.221-1.9869-20.112-2e-3c-0.0204-9e-3 -0.0249 3e-3 -0.0369-9e-3 -0.2443-4.6057-0.50251-9.7399-1.2071-14.632 0 0-0.0972-0.83417-1.1332-0.93371-1.036-0.0996-1.9381-0.25177-2.5099-0.41186v-0.21307c0.14636-0.0262 0.28682-0.0438 0.368-0.18528 0 0 8.733 0.24589 8.733-0.36873s-8.7023-0.14106-8.7023-0.14106c-0.21894-1.0201-0.65058-3.0431-0.97793-3.0431zm0 9.0766c0.34605 0 0.71407-1.3e-4 0.71408 0.5394v2.36c0 0.54212-0.36803 0.54196-0.71408 0.54196-0.34606 0-0.7192 1.6e-4 -0.7192-0.54196v-2.36c-1e-5 -0.53953 0.37314-0.5394 0.7192-0.5394zm-12.73 16.475c0.34605 0 0.71407-1.3e-4 0.71407 0.5394v2.36c0 0.54211-0.36802 0.54196-0.71407 0.54196-0.34606 0-0.7192 1.5e-4 -0.7192-0.54196v-2.36c0-0.53953 0.37314-0.5394 0.7192-0.5394zm25.474 0c0.34606 0 0.71408-1.3e-4 0.71408 0.5394v2.36c0 0.54211-0.36802 0.54196-0.71408 0.54196-0.34605 0-0.71919 1.5e-4 -0.71919-0.54196v-2.36c-1e-5 -0.53953 0.37314-0.5394 0.71919-0.5394z" fill="#3296ff"/><circle cx="587.5" cy="375" r="74.595" fill="none" stroke="#3296ff" stroke-linecap="square" stroke-linejoin="round" stroke-width=".811" style="paint-order:normal"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="280mm" height="282.66mm" version="1.1" viewBox="0 0 280 282.66" xmlns="http://www.w3.org/2000/svg"><g transform="translate(35 -7.171)"><g transform="translate(-7.6294e-6 7.3444)"><g transform="matrix(4.5516 0 0 4.5516 -372.53 -527.39)" fill="#3296ff"><path transform="matrix(.26458 0 0 .26458 -7.9725e-5 4.7925e-5)" d="m396.53 449.99c-6.703 0-12.091 16.285-12.119 29.002l-0.17578 54.354-2.8828 7.209-14.512-0.18945c0.6477-4.764 1.2976-11.043-1.4473-15.457l-0.0469-1.3477 14.395 0.83985 0.16797-1.9668-14.578 0.97461c0-1.4239-1.0254-4.1602-2.3457-4.1602-1.4366 0-2.2472 2.4804-2.4375 4.1211l-14.088-1.1582-4e-3 2.4609 14.047-0.9668-0.1914 1.3164c-2.3109 4.8272-1.8613 10.199-0.92774 15.412-46.681 3.307-70.422 5.1355-71.252 5.2051-0.82972 0.0695-1.8463 0.90433-1.9707 2.293-0.0556 0.59954-0.16605 0.90065-0.33008 0.90039-0.16479-1e-3 -0.35633 0.0276-0.57422 0.082-0.21808 0.0533-0.43767 0.32575-0.6582 0.81641-0.67616 2.9741-0.27076 5.9302-0.0977 8.7734 0.0552 0.10885 12.16 0.73137 36.314 1.8672 24.156 1.1349 36.316 1.7029 36.48 1.7031 0.16403 3.7e-4 6.0384 0.17301 24.84 0.20703l2.123 5.0859c-0.0302 21.998 0.0965 58.62 7.0059 86.129-15.285 2.3628-23.055 3.6526-23.314 3.8594-2.0877 2.7596-2.3172 6.879-1.8203 9.998 0.0556 1e-3 4.4001 0.33632 13.033 1.0059l13.115 0.92578c0.10923-0.0544 1.4785-2.0469 1.4785-2.0469 0.21947 1.7517 0.2277 5.2639 2.7734 5.2676h2e-3c2.5457-4e-3 2.552-3.5159 2.7715-5.2676 0 0 1.3693 1.9924 1.4785 2.0469l13.115-0.92578c8.6331-0.66954 12.978-1.0047 13.033-1.0059 0.49693-3.119 0.26734-7.2384-1.8203-9.998-0.25928-0.20674-8.0293-1.4966-23.314-3.8594 6.9093-27.509 7.0361-64.131 7.0059-86.129l2.123-5.0859c18.801-0.034 24.678-0.20666 24.842-0.20703 0.16404-2.3e-4 12.323-0.56825 36.479-1.7031 24.155-1.1358 36.259-1.7583 36.314-1.8672 0.17311-2.8432 0.5785-5.7993-0.0976-8.7734-0.22035-0.49066-0.44013-0.76312-0.65821-0.81641-0.21807-0.0544-0.40943-0.0832-0.57421-0.082-0.16404 2.6e-4 -0.27257-0.30085-0.32813-0.90039-0.12435-1.3886-1.141-2.2234-1.9707-2.293-0.82972-0.0695-24.573-1.8981-71.254-5.2051 0.93358-5.2134 1.3832-10.585-0.92773-15.412l-0.19141-1.3164 14.047 0.9668-4e-3 -2.4609-14.088 1.1582c-0.19049-1.6407-1.0009-4.1211-2.4375-4.1211-1.3203 0-2.3457 2.7363-2.3457 4.1602l-14.576-0.97461 0.16602 1.9668 14.395-0.83985-0.0469 1.3477c-2.7449 4.4137-2.095 10.693-1.4473 15.457l-14.512 0.18945-2.8809-7.209-0.17578-54.354c0-12.782-5.4181-29.002-12.121-29.002zm-2.207 19.031h1.6133v4.8184h-1.6133zm2.8008 0h1.6133v4.8184h-1.6133zm-20.578 81.467h2.7774v7.1152h-2.7774zm3.8789 0h2.7773v7.1152h-2.7773zm29.434 0h2.7774v7.1152h-2.7774zm3.8789 0h2.7773v7.1152h-2.7773z" fill="#3296ff"/></g><circle cx="105" cy="139.83" r="139.18" fill="none" stroke="#3296ff" stroke-linecap="square" stroke-linejoin="round" stroke-width="1.6438" style="paint-order:normal"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="280mm" height="311.54mm" version="1.1" viewBox="0 0 280 311.54" xmlns="http://www.w3.org/2000/svg"><g transform="translate(-6.4969e-6 14.543)"><circle cx="140" cy="141.28" r="139.36" fill="none" stroke="#3296ff" stroke-linecap="square" stroke-linejoin="round" stroke-miterlimit="4.3333" stroke-width="1.2759" style="paint-order:normal"/><path d="m152.63 24.911c-0.0267-25.744-10.66-39.454-12.634-39.454h1e-5c-1.9741 0-12.608 13.71-12.634 39.454l0.0182 79.957-1.6403 1.3481s-84.993 49.186-89.398 51.752-4.5756 5.1873-4.5756 5.1873-7.5179 14.841-7.7484 15.302c-0.23052 0.4615-0.27144 1.1507-0.27144 1.1507l0.1078 2.3443s6.6825-7.4591 6.8393-7.6432c0.1568-0.18415 0.32861-0.34095 0.7442-0.33769 0.41558 3e-3 1.0215 0.0362 1.4196-0.32729 0.93562-1.1932 2.1371-2.6676 3.9093-3.1924 1.7722-0.52482 39.634-11.689 59.608-17.581l31.015-1.8624v47.86l-0.95849 1.5429h-1.3832c-0.24979-1.7755-0.85068-1.9326-0.85068-1.9326l-11.633 0.12468s-1.2585 0.79803-1.2585 6.6315c0 10.609 1.3118 13.973 1.3118 13.973s0.30131 0.16754 1.2416 0.16754l2.5625 10.064s0.46106 0.0701 0.9494 0.0701l1.7637 5.2665 0.71822-0.0351 1.3923-5.3548c0.55067 0 0.99225-0.16884 0.99225-0.16884l0.0714-0.44288 9.2212 6.0198 5.3548 27.175s-33.731 22.555-34.623 23.158c-0.89108 0.60283-1.4159 2.6639-1.565 4.3768l-0.65199 7.4952 38.872-12.307 1.0494 5.3873 1.1104-5.3873 38.871 12.307-0.65198-7.4952c-0.14909-1.7129-0.67393-3.774-1.565-4.3768-0.8911-0.60285-34.623-23.158-34.623-23.158l5.3561-27.175 9.2199-6.0198 0.0714 0.44288s0.4429 0.16884 0.99356 0.16884l1.391 5.3548 0.71822 0.0351 1.765-5.2665c0.48833 0 0.9481-0.0701 0.9481-0.0701l2.5625-10.064c0.9403 0 1.2416-0.16754 1.2416-0.16754s1.3118-3.3645 1.3118-13.973c0-5.8335-1.2585-6.6315-1.2585-6.6315l-11.633-0.12468s-0.6009 0.15707-0.8507 1.9326h-1.3832l-0.95719-1.5429v-47.86l31.013 1.8624c19.974 5.8921 57.836 17.057 59.608 17.581 1.7722 0.5248 2.9749 1.9992 3.9106 3.1924 0.39808 0.36352 1.0027 0.33031 1.4182 0.32729 0.4156-3e-3 0.58741 0.15355 0.74421 0.33769 0.1568 0.18415 6.8393 7.6432 6.8393 7.6432l0.10781-2.3443s-0.0397-0.68921-0.27015-1.1507c-0.23051-0.4615-7.7497-15.302-7.7497-15.302s-0.16886-2.6209-4.5743-5.1873c-4.4054-2.5664-89.399-51.752-89.399-51.752l-1.639-1.3481 0.0169-79.957m-34.454 111.65c0.78911 0 1.6055 0.23981 1.6053 1.2949v6.738c2.3e-4 0.93982-0.81617 1.2949-1.6053 1.2949-0.78909 0-1.6053-0.35505-1.6053-1.2949v-6.738c0-1.0551 0.81618-1.2949 1.6053-1.2949zm5.0002 0c0.78909 0 1.6055 0.23981 1.6053 1.2949v6.738c2.4e-4 0.93982-0.81619 1.2949-1.6053 1.2949s-1.6053-0.35505-1.6053-1.2949v-6.738c0-1.0551 0.81618-1.2949 1.6053-1.2949zm33.577 0c0.78909 0 1.6053 0.23981 1.6053 1.2949v6.738c0 0.93982-0.81618 1.2949-1.6053 1.2949-0.7891 0-1.6055-0.35505-1.6053-1.2949v-6.738c-2.4e-4 -1.055 0.81618-1.2949 1.6053-1.2949zm5.0002 0c0.78909 1e-5 1.6053 0.2398 1.6053 1.2949v6.738c0 0.93982-0.81618 1.2949-1.6053 1.2949s-1.6055-0.35505-1.6053-1.2949v-6.738c-2.4e-4 -1.0551 0.81619-1.2949 1.6053-1.2949z" fill="#3296ff"/></g></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="360mm" height="369.09mm" version="1.1" viewBox="0 0 360 369.09" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><g transform="translate(75 36.046)"><g transform="translate(-65.5 -28.331)"><path d="m170.5-0.026086c-7.0846 9.35e-6 -19.026 24.031-19.062 58.205l0.0362 54.388-1.9176 10.818s-0.10598 1.4555-0.99653 1.9124-25.868 13.39-25.868 13.39c0.89235-6.9871 2.094-27.847-0.25947-27.847h-15.911c-3.162 1e-5 -2.337 31.287-0.34733 31.287h1.0188l1.163 3.968-94.45 48.717c-4.8538 2.6647-11.085 6.7435-12.179 12.611l-1.7264 14.181v2.7208l1.8819-8.6075c0.26207-1.101 1.0968-1.7912 2.1833-2.0856l48.418-14.553 1.5222 5.4184 1.2943-6.2934 32.906-10.202 1.2591 4.6033 1.4023-5.4184-0.02378-0.0238 17.489-5.4184h13.63v0.0238l1.1754 4.6751 1.1268-4.6989 27.248-0.014 0.0594 84.392c0.23612 14.268 3.6729 28.282 6.066 42.28 0 0-0.48489 3.4026-4.915 6.2216 0 0-36.324 23.321-38.756 24.91-2.4318 1.5884-3.5142 3.8978-3.5142 8.6897l0.0961 5.1067 52.194-11.616c1.3933 6.3589 5.2092 17.812 5.3703 18.305 0.16114 0.49226 0.88696 1.043 0.88696 1.043s0.44043 0.31323 0.75567 0.31323h1.486c0.31524 0 0.75567-0.31323 0.75567-0.31323s0.72582-0.55079 0.88695-1.043c0.16114-0.49225 3.977-11.946 5.3703-18.305l52.194 11.616 0.0956-5.1067c0-4.7918-1.0819-7.1013-3.5137-8.6897s-38.756-24.91-38.756-24.91c-4.4301-2.8189-4.915-6.2216-4.915-6.2216 2.3931-13.998 5.8299-28.012 6.066-42.28l0.06-84.392 27.248 0.014 1.1273 4.6989 1.1748-4.6751v-0.0238h13.63l17.49 5.4184-0.0238 0.0238 1.4023 5.4184 1.2586-4.6033 32.906 10.202 1.2948 6.2934 1.5222-5.4184 48.418 14.553c1.0865 0.29433 1.9212 0.98458 2.1833 2.0856l1.8824 8.6075v-2.7208l-1.7263-14.181c-1.0936-5.8671-7.3253-9.946-12.179-12.611l-94.45-48.717 1.163-3.968h1.0188c1.9896 0 2.8147-31.287-0.34734-31.287h-15.911c-2.3535 0-1.1518 20.86-0.25947 27.847 0 0-24.977-12.933-25.867-13.39-0.89055-0.45697-0.99705-1.9124-0.99705-1.9124l-1.9176-10.818 0.0362-54.388c-0.03593-34.174-11.977-58.205-19.062-58.205zm-2.373 45.661c0.8389 4.8e-5 1.0544 0.39604 1.0544 1.0673v3.692c0 0.68329-0.21552 1.0792-1.0544 1.0792-0.8389-6e-6 -1.0441-0.39594-1.0441-1.0792v-3.692c0-0.6713 0.20519-1.0673 1.0441-1.0673zm4.7454 0c0.8389 4.8e-5 1.0446 0.39604 1.0446 1.0673v3.692c0 0.68329-0.2057 1.0792-1.0446 1.0792-0.8389-6e-6 -1.0539-0.39594-1.0539-1.0792v-3.692c0-0.6713 0.215-1.0673 1.0539-1.0673zm-46.369 122.62c0.87915-2e-3 1.44 0.52759 1.44 1.4746v5.0349c0 0.82604-0.55805 1.4757-1.44 1.4757-0.88197 0-1.4379-0.64964-1.4379-1.4757v-5.0349c-2e-5 -0.93514 0.55878-1.4724 1.4379-1.4746zm87.992 0c0.87916 2e-3 1.4385 0.5395 1.4385 1.4746v5.0349c0 0.82604-0.55649 1.4757-1.4385 1.4757s-1.44-0.64964-1.44-1.4757v-5.0349c5e-5 -0.94705 0.56085-1.4766 1.44-1.4746zm-81.29 0.0476c0.87915-2e-3 1.4394 0.52759 1.4395 1.4746v5.0349c0 0.82603-0.55752 1.4757-1.4395 1.4757s-1.4385-0.64964-1.4385-1.4757v-5.0349c-2e-5 -0.93514 0.5593-1.4724 1.4385-1.4746zm74.589 0c0.87915 2e-3 1.4385 0.5395 1.4384 1.4746v5.0349c0 0.82603-0.55649 1.4757-1.4384 1.4757-0.88197 0-1.44-0.64964-1.44-1.4757v-5.0349c5e-5 -0.94705 0.56085-1.4766 1.44-1.4746z" fill="#3296ff" fill-rule="evenodd"/><circle cx="170.5" cy="172.29" r="179.31" fill="none" stroke="#3296ff" stroke-linecap="square" stroke-linejoin="round" stroke-miterlimit="3" stroke-width="1.371" style="paint-order:normal"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="5203mm" height="5463.5mm" version="1.1" viewBox="0 0 5203 5463.5" xmlns="http://www.w3.org/2000/svg"><g transform="translate(2496.5 2583.2)"><path d="m103.62-2580.1c-130.08 0-255.75 434.93-255.75 588.23l-0.125 1240.4s-55.621 87.016-114.96 127.88c-59.342 40.86-294.12 203.78-294.12 203.78l12.788-24.751s2e-4 -204.6 0-255.75c-1e-4 -51.151-12.788-63.938-25.575-63.938-12.787 1e-4 -204.6 0-204.6 0-25.575 0-38.363 12.788-38.363 63.938 0 0-0.399 257.75 0 281.33 0.399 23.577 12.788 51.151 12.788 51.151h25.575l-0.62501 98.405s-1405.1 961.43-1444.4 988.54c-39.261 27.115-51.15 42.459-51.15 63.938v217.39l575.44-242.96 869.56-306.9 179.03-51.151 498.74-62.464s-0.736 1167.2-0.02 1290.1c0.706 122.92 63.489 432.48 63.489 432.48l-702.87 539.38-38.363 51.15v166.24l869.78-212.97 12.563 46.73 25.575 89.513 12.788 102.3 12.788 38.363 12.788-38.363 12.788-102.3 25.575-89.513 12.563-46.73 869.78 212.97v-166.24l-38.363-51.15-702.87-539.38s62.783-309.56 63.488-432.48c0.711-122.92-0.05-1290.1-0.05-1290.1l498.77 62.464 179.03 51.151 869.56 306.9 575.44 242.96v-217.39c0-21.479-11.889-36.823-51.151-63.938-39.261-27.115-1444.4-988.54-1444.4-988.54l-0.60001-98.405h25.575s12.389-27.573 12.788-51.151c0.399-23.577 0-281.33 0-281.33 0-51.15-12.788-63.938-38.363-63.938 0 0-191.81 1e-4 -204.6 0-12.788-1e-4 -25.575 12.788-25.575 63.938-2e-4 51.151 0 255.75 0 255.75l12.788 24.751s-234.77-162.92-294.12-203.78c-59.342-40.86-114.99-127.88-114.99-127.88l-0.104-1240.4c-1e-4 -153.3-125.68-588.23-255.75-588.23zm-38.363 409.2c6.347 2e-4 12.787 5.5342 12.787 11.064v67.809c0 5.4289-6.4405 10.865-12.787 10.864-6.3906 2e-4 -12.788-5.4355-12.788-10.864v-67.809c1e-4 -5.5302 6.3971-11.013 12.788-11.064zm76.726 0c6.3906 0.05 12.788 5.5342 12.788 11.064v67.809c-1e-4 5.4289-6.397 10.865-12.788 10.864-6.347 2e-4 -12.788-5.4355-12.788-10.864v-67.809c0-5.5302 6.4406-11.064 12.788-11.064zm-575.44 2186.7h25.575c6.347 2.01e-4 12.788 6.393 12.788 12.788v76.726c0 6.3929-6.4406 12.788-12.788 12.788h-25.575c-6.3906 1e-4 -12.787-6.3947-12.788-12.788v-76.726c1e-4 -6.3947 6.397-12.736 12.788-12.788zm115.09 0h25.575c6.347 2.01e-4 12.788 6.393 12.788 12.788v76.726c0 6.3929-6.4406 12.788-12.788 12.788h-25.575c-6.3906 1e-4 -12.787-6.3947-12.788-12.788v-76.726c2e-4 -6.3947 6.3971-12.736 12.788-12.788zm818.41 0h25.575c6.3907 0.05 12.788 6.393 12.788 12.788v76.726c-2e-4 6.3929-6.397 12.788-12.788 12.788h-25.575c-6.347 1e-4 -12.788-6.3947-12.788-12.788v-76.726c0-6.3947 6.4406-12.787 12.788-12.788zm115.09 0h25.575c6.3906 0.05 12.788 6.393 12.788 12.788v76.726c-1e-4 6.3929-6.3971 12.788-12.788 12.788h-25.575c-6.347 1e-4 -12.788-6.3947-12.788-12.788v-76.726c0-6.3947 6.4406-12.787 12.788-12.788zm-1048.6 140.66h25.575c6.347 1e-4 12.788 6.3929 12.788 12.787v76.726c0 6.3929-6.4406 12.788-12.788 12.788h-25.575c-6.3906 1e-4 -12.787-6.3947-12.788-12.788v-76.726c1e-4 -6.3946 6.397-12.736 12.788-12.787zm115.09 0h25.575c6.347 1e-4 12.788 6.3929 12.788 12.787v76.726c0 6.3929-6.4406 12.788-12.788 12.788h-25.575c-6.3906 1e-4 -12.787-6.3947-12.788-12.788v-76.726c2e-4 -6.3946 6.3971-12.736 12.788-12.787zm818.41 0h25.575c6.3907 0.05 12.788 6.3929 12.788 12.787v76.726c-2e-4 6.3929-6.397 12.788-12.788 12.788h-25.575c-6.347 1e-4 -12.788-6.3947-12.788-12.788v-76.726c0-6.3946 6.4406-12.787 12.788-12.787zm115.09 0h25.575c6.3906 0.05 12.788 6.3929 12.788 12.787v76.726c-1e-4 6.3929-6.3971 12.788-12.788 12.788h-25.575c-6.347 1e-4 -12.788-6.3947-12.788-12.788v-76.726c0-6.3946 6.4406-12.787 12.788-12.787z" fill="#3296ff" fill-rule="evenodd"/><circle cx="105" cy="135.8" r="2582.6" fill="none" stroke="#3296ff" stroke-linecap="square" stroke-linejoin="round" stroke-width="37.746" style="paint-order:normal"/></g></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="660mm" height="735.62mm" version="1.1" viewBox="0 0 660 735.62" xmlns="http://www.w3.org/2000/svg"><g transform="translate(227.07 219.31)"><g transform="translate(-220.16 -219.35)"><path d="m323.98 0.041596c-12.621 1.1404-32.35 58.556-32.35 95.304v172.91l-46.402 31.844c1.7124-7.7226 3.623-25.137-0.66208-43.984h-33.315c-3.6679 12.285-5.2993 33.094 0.0391 51.47l3.3026 0.0898 3.0428 11.261c-67.538 46.949-135.82 92.856-204.08 138.75-2.0153 1.3847-2.119 1.8573-2.119 1.8573l-11.437 22.93v8.1344l26.342-17.935 71.254-28.565 34-11.466 0.69527 3.201 1.5605-3.992 51.247-17.319 1.0976 3.6346 1.041-4.3845 29.414-9.9624 21.011-3.2655 0.82027 6e-3 1.3632 4.7322 1.7772-4.7341 50.009-0.0273s-0.0109 126.27 0 148.94c0.0156 32.451 7.5281 73.581 8.5445 79.039s3.3484 11.282-3.1483 16.589c-6.4967 5.3071-71.763 58.406-77.1 62.77-5.3362 4.364-4.9704 10.253-4.7556 11.913s1.9276 15.884 1.9276 15.884l98.173-36.949 7.8414 28.559 1.7675 4e-3 7.8414-28.559 98.171 36.949s1.7128-14.223 1.9276-15.884 0.58251-7.5514-4.7536-11.915c-5.3362-4.3639-70.605-57.461-77.102-62.768-6.4967-5.3071-4.1647-11.131-3.1483-16.589s8.5308-46.587 8.5464-79.039c0.0109-22.668 0-148.94 0-148.94l50.007 0.0273 1.7773 4.7341 1.3632-4.7322 0.82222-8e-3 21.011 3.2674 29.413 9.9624 1.041 4.3826 1.0976-3.6326 51.249 17.319 1.5605 3.992 0.69331-3.201 34.002 11.464 71.252 28.567 26.344 17.935-2e-3 -8.1344-11.435-22.93s-0.1057-0.47265-2.121-1.8573c-68.258-45.89-136.54-91.8-204.08-138.75l3.0409-11.259 3.3045-0.0918c5.3383-18.376 3.705-39.185 0.0371-51.47h-33.315c-4.2851 18.847-2.3726 36.264-0.66013 43.986l-46.402-31.846v-172.91c0-36.748-19.762-94.167-32.383-95.308zm-4.3806 54.566h2.0546c0.8995-7e-6 1.2851 0.38391 1.2851 1.2812v7.5582c-4e-5 0.89669-0.38558 1.2812-1.2851 1.2812h-2.0546c-0.89949-9e-6 -1.2246-0.38449-1.2206-1.2812v-7.5582c-4e-3 -0.89728 0.32117-1.2812 1.2206-1.2812zm6.6422 0h2.0546c0.8995-7e-6 1.2851 0.38391 1.2851 1.2812v7.5582c-4e-5 0.89669-0.38558 1.2812-1.2851 1.2812h-2.0546c-0.89948-9e-6 -1.2246-0.38449-1.2206-1.2812v-7.5582c-4e-3 -0.89728 0.32117-1.2812 1.2206-1.2812zm43.543 294.97h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89668-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89676 0.38559-1.2813 1.2851-1.2812zm13.968 0h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89668-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89958-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89676 0.38559-1.2813 1.2851-1.2812zm-122 0.0488h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89667-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-8e-5 -0.89675 0.38559-1.2813 1.2851-1.2812zm13.97 0h2.5057c0.89955-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89667-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89675 0.38558-1.2813 1.2851-1.2812zm94.048 13.818h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89668-0.38552 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89675 0.38559-1.2813 1.2851-1.2812zm13.97 0h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89668-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89675 0.38559-1.2813 1.2851-1.2812zm-122 0.0488h2.5057c0.89955-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89667-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89675 0.38559-1.2813 1.2851-1.2812zm13.97 0h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89667-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89675 0.38558-1.2813 1.2851-1.2812zm97.866 6.2614h8.8667zm-108.03 0.0488h8.8667zm104.23 7.5406h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89667-0.38552 1.2792-1.2851 1.2792h-2.5057c-0.89957-5e-5 -1.2852-0.38261-1.2851-1.2792v-8.8394c-7e-5 -0.89675 0.38559-1.2813 1.2851-1.2812zm13.97 0h2.5057c0.89954-6e-5 1.2831 0.38451 1.2831 1.2812v8.8394c2e-5 0.89667-0.38357 1.2792-1.2831 1.2792h-2.5057c-0.89957-5e-5 -1.2852-0.38261-1.2851-1.2792v-8.8394c-7e-5 -0.89675 0.38559-1.2813 1.2851-1.2812zm-122 0.0488h2.5057c0.89955-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c1e-5 0.89667-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89675 0.38559-1.2813 1.2851-1.2812zm13.97 0h2.5057c0.89954-6e-5 1.2851 0.38451 1.2851 1.2812v8.8394c2e-5 0.89667-0.38553 1.2812-1.2851 1.2812h-2.5057c-0.89957-5e-5 -1.2852-0.38456-1.2851-1.2812v-8.8394c-7e-5 -0.89675 0.38558-1.2813 1.2851-1.2812z" fill="#3296ff"/><circle cx="323.1" cy="368.73" r="328.11" fill="none" stroke="#3296ff" stroke-width="3.7896" style="paint-order:normal"/></g></g></svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg width="211.67mm" height="211.67mm" version="1.1" viewBox="0 0 211.67 211.67" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(953.33 -995.17)">
<path d="m-742.19 1101c0 27.929-11.095 54.714-30.844 74.463-19.749 19.749-46.534 30.844-74.463 30.844-27.929 0-54.714-11.095-74.463-30.844-19.749-19.749-30.844-46.534-30.844-74.463 0-27.929 11.095-54.714 30.844-74.463 19.749-19.749 46.534-30.844 74.463-30.844 27.929 0 54.714 11.095 74.463 30.844 19.749 19.749 30.844 46.534 30.844 74.463z" fill="none" stroke="#3296ff" stroke-linejoin="round" stroke-width="1.0531"/>
<path d="m-847.5 1004.7c-7.4097 0-9.4794 27.242-9.4556 35.842v13.103l-1.2407 2.0809-2.6718 3.1116-5.7373 4.7856-15.217 12.445c0.26327-2.4217 0.81772-10.57-0.18346-11.848l-8.6782 0.072c-0.70678 0.8906-1.6381 10.501 0.13788 15.728h1.1029l0.38829 1.823-21.816 16.071c0.5494-2.3946 1.2496-11.314 0.25043-13.182l-8.8583 0.014c-1.0492 4.8589-1.1533 10.88-0.0459 15.696l1.2043 0.04 0.50012 2.5271s-28.608 21.118-31.045 22.91c-2.4373 1.7913-3.8489 5.5589-3.8729 8.2254-0.0241 2.6665-0.0401 5.2116-0.0401 5.2116l35.659-15.492 0.65645 4.4375 1.1948-5.2253 9.3938-3.722 0.4792 5.7045 1.5361-6.4857 9.1575-3.7024 0.83374 5.6914 1.1619-6.4791 8.8095-3.6302 0.53827 2.4748 1.0438-3.1115 9.1247-2.9869 0.64327 2.4748 0.93875-2.9147 12.853-3.4791 0.32163 5.0153 1.4704 10.352 0.033 19.858c0.17362 4.5575 0.64186 9.1069 1.287 13.621l1.477 6.7877c0.23946 1.6304-0.74861 3.3195-1.8528 4.424 0 0-27.318 20.921-28.298 21.678-0.98046 0.7565-1.2339 2.0863-1.2339 2.0863l-2.2479 10.338 25.84-9.5902 11.73-4.0193 2.1403 6.7961 0.58694 6.9848 0.58694-6.9848 2.1403-6.7961 11.73 4.0193 25.84 9.5902-2.2479-10.338s-0.25346-1.3298-1.2339-2.0863c-0.98047-0.7566-28.298-21.678-28.298-21.678-1.1042-1.1045-2.0923-2.7936-1.8528-4.4239l1.477-6.7877c0.64515-4.5145 1.1134-9.0639 1.287-13.621l0.033-19.858 1.4705-10.352 0.32163-5.0153 12.853 3.4791 0.93876 2.9147 0.64326-2.4748 9.1247 2.9869 1.0438 3.1115 0.53826-2.4748 8.8095 3.6302 1.1619 6.4791 0.83375-5.6914 9.1575 3.7024 1.5361 6.4857 0.4792-5.7045 9.3938 3.722 1.1948 5.2253 0.65644-4.4375 35.659 15.492s-0.016-2.5452-0.0401-5.2117-1.4357-6.4341-3.8729-8.2254c-2.4373-1.7912-31.045-22.91-31.045-22.91l0.50013-2.5271 1.2043-0.04c1.1074-4.816 1.0033-10.837-0.0459-15.696l-8.8583-0.014c-0.99921 1.8687-0.29898 10.788 0.25042 13.182l-21.816-16.071 0.3883-1.8229h1.1029c1.776-5.2277 0.84466-14.838 0.13787-15.728l-8.6782-0.072c-1.0012 1.2776-0.44672 9.4259-0.18345 11.848l-15.217-12.445-5.7373-4.7856-2.6718-3.1116-1.2407-2.0809v-13.103c0.0237-8.6006-2.046-35.842-9.4556-35.842zm-2.0804 12.256h1.3916v3.5317h-1.3916zm2.8227 0h1.4048v3.5317h-1.4048zm-19.766 73.142h1.3982v3.5317h-1.3982zm3.5777 0h1.3917v3.5317h-1.3917zm29.501 0h1.3916v3.5317h-1.3916zm3.5645 0h1.4048v3.5317h-1.4048zm-36.643 4.2932h1.3982v3.5251h-1.3982zm3.5777 0h1.3917v3.5251h-1.3917zm29.501 0h1.3916v3.5251h-1.3916zm3.5645 0h1.4048v3.5251h-1.4048zm-27.426 2.0481h1.3983v3.5186h-1.3983zm14.015 0h1.4048v3.5186h-1.4048zm-9.919 0.01h1.3983v3.5251h-1.3983zm14.015 0h1.4048v3.5251h-1.4048zm-18.112 4.4901h1.3983v3.5251h-1.3983zm4.0962 0h1.3983v3.5251h-1.3983zm9.919 0h1.4048v3.5251h-1.4048zm4.0963 0h1.4048v3.5251h-1.4048zm-18.112 4.5033h1.3983v3.5316h-1.3983zm4.0962 0h1.3983v3.5316h-1.3983zm9.919 0h1.4048v3.5316h-1.4048zm4.0963 0h1.4048v3.5316h-1.4048z" fill="#3296ff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,8 @@
.leaflet-touch .leaflet-control-layers-toggle {
width: 30px;
height: 30px;
color:black;
background-image: none;
}

View File

@@ -0,0 +1,67 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
var L = require('leaflet');
export function checkMapper(o) {
if (o instanceof L.ParkingSpot) {
/*
if( o.box === undefined ) {
debugger;
} */
return {
'index': Number(o['id']),
'_leaflet_id': o._leaflet_id,
'type': 'parking',
'parkingType': o.options.attributes.type,
'name': o.options.attributes.name,
'radius': String(o.options.attributes.radius),
'lat': o._latlng.lat,
'lng': o._latlng.lng,
'box': o.box !== undefined ? o.box.getLatLngs() : null
};
} else if (o instanceof L.RunwayNode) {
console.log(o)
return { 'index': Number(o['glueindex']), '_leaflet_id': o._leaflet_id, 'lat': o._latlng.lat, 'lng': o._latlng.lng, 'type': 'runway' };
} else if (o instanceof L.HoldNode) {
console.log(o)
return { 'index': Number(o['glueindex']), '_leaflet_id': o._leaflet_id, 'type': o.holdPointType };
} else if (o instanceof L.RunwayPolygon) {
return {
'type': 'runway_poly',
'pavement': o.getLatLngs()
}
} else if (o instanceof L.TakeoffPolygon) {
return {
'type': 'takeoffpad_poly',
'pavement': o.getLatLngs()
}
} else if (o instanceof L.Polyline) {
console.log(o)
var latLngs = o.getLatLngs().map(l => ({ lat: l.lat, lng: l.lng, index: l.glueindex }));
if (o.options.attributes===undefined) {
return null;
}
return { 'start': Number(o['begin']), 'end': Number(o['end']), '_leaflet_id': o._leaflet_id, 'type': 'poly', 'direction': o.options.attributes.direction, 'isPushBackRoute': o.options.attributes.isPushBackRoute, latLngs: latLngs };
}
else {
console.log('Unknown Type ')
console.log(typeof o)
}
}
export function groMapper(o) {
if (o instanceof L.Polygon) {
}
}

View File

@@ -0,0 +1,31 @@
<template>
</template>
<script lang="js">
import {aiLayer} from '../phi/AILayer'
export default {
/* eslint-disable */
name: 'ai-layer',
props: [],
mounted () {
this.aiLayer = aiLayer({url: this.$store.state.Settings.settings.phi_url})
if(this.aiLayer) {
this.aiLayer.addTo(this.$parent.mapObject)
}
},
data () {
return {
}
},
methods: {
},
computed: {
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,100 @@
<!--
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div :key="airline.label + '-div'">
<div v-for="item in traffic" v-bind:key="airline.label + '-' + item.id + '-innerdiv'">
<div :key="item.id + '-dep'" v-if="direction == 0">{{ item.departure.time }} {{ item.callsign }} {{ item.departure.port }} --> {{ item.arrival.port }} {{ item['required-aircraft'] }} {{ item.flighttype }}</div>
<div :key="item.id + '-arr'" v-if="direction == 1">{{ item.arrival.time }} {{ item.callsign }} {{ item.departure.port }} --> {{ item.arrival.port }} {{ item['required-aircraft'] }} {{ item.flighttype }}</div>
</div>
</div>
</template>
<script lang="js">
import {readTrafficXML} from '../loaders/traffic_loader'
import ParkingItem from './ParkingItem'
const fs = require('fs')
const path = require('path')
export default {
name: 'airline-traffic',
components: {ParkingItem},
props: {airline: Object},
data () {
return {}
},
methods: {
traverseDir (dir, airline) {
var result = []
if (!fs.existsSync(dir)) {
return result
}
var iaco = airline.label
fs.readdirSync(dir).forEach(file => {
let fullPath = path.join(dir, file)
if (fs.lstatSync(fullPath).isDirectory()) {
var children = this.traverseDir(fullPath, airline)
result = result.concat(children)
} else {
if (file.match(new RegExp(`${iaco}.xml`, 'i'))) {
result.push(fullPath)
}
}
})
return result
}
},
computed: {
direction: function () {
return this.$parent.$parent.$parent.$data.direction
},
filename: function () {
var ret = this.traverseDir(this.$store.state.Settings.settings.flightgearDirectory_traffic, this.airline)
if (ret.length > 0) {
return ret[0]
}
},
trafficFile: function () {
return readTrafficXML(this.filename)
},
traffic: function () {
if (this.filename) {
var aircraftLookup = this.trafficFile.filter(a => a['required-aircraft'])
.reduce((req, acc) => {
req[acc['required-aircraft']] = acc
return req
}, {})
var ret = this.trafficFile.filter(f => f.callsign).filter(f =>
(f.departure.port === this.$store.state.Airports.currentAirport.icao && this.direction === 0) ||
(f.arrival.port === this.$store.state.Airports.currentAirport.icao && this.direction === 1))
.map(obj => ({ ...obj, flighttype: aircraftLookup[obj['required-aircraft']].flighttype }))
.filter((v, i, a) => a.findIndex(t => (t.id === v.id)) === i)
return ret
}
},
aircraft: function () {
if (this.filename) {
return this.trafficFile.filter(f => f.registration)
}
console.debug(this.filename)
}
}
}
</script>
<style>
div.row.div {
display: flex;
justify-content: space-between;
}
</style>

View File

@@ -0,0 +1,129 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div>
<el-row>
<el-col :span="3" class="text">{{airport.icao}}</el-col>
<el-col :span="6" class="text">{{date}}</el-col>
<el-col :span="6" class="text">{{upload_date}}</el-col>
<el-col :span="9">
<el-popover
placement="top-start"
title="Goto"
width="200"
trigger="hover"
content="Center to airport"
>
<el-button @click="goto" class="button" slot="reference" :disabled="editing">
<i class="fas fa-bullseye"></i>
</el-button>
</el-popover>
<el-popover
placement="top-start"
title="Remove"
width="200"
trigger="hover"
content="Remove this Work in progress file"
>
<el-button @click="remove" tooltip="Remove wip file" class="button" slot="reference" :disabled="editing">
<i class="fas fa-trash-alt"></i>
</el-button>
</el-popover>
<el-popover
placement="top-start"
title="Upload"
width="200"
trigger="hover"
content="Upload work in progress"
>
<el-button @click="upload" tooltip="Upload to groundweb" class="button" slot="reference" :disabled="editing">
<i class="fas fa-upload"></i>
</el-button>
</el-popover>
</el-col>
</el-row>
</div>
</template>
<script lang="js">
/* eslint-disable */
const $ = require('jquery');
import 'element-ui/lib/theme-chalk/index.css'
import {removeWip} from '../loaders/groundnet_functions'
import Vue from 'vue'
import { EventBus } from './event-bus.js';
export default {
name: 'airport',
components: { },
props: {airport: Object, editing: Boolean},
mounted () {
this.$forceUpdate();
},
data () {
return {
ok: true
}
},
methods: {
goto() {
let airports = this.$store.state.Airports.airports
.filter(a => a.properties.icao.match(this.airport.icao))
if (airports.length > 0) {
this.$store.commit('CENTER', [airports[0].geometry.coordinates[1], airports[0].geometry.coordinates[0]])
}
},
remove() {
removeWip(this.$store.state.Settings.settings.airportsDirectory, this.airport.icao)
this.$store.dispatch('removeWip', this.airport.icao);
},
upload() {
let airports = this.$store.state.Airports.airports
.filter(a => a.properties.icao.match(this.airport.icao))
if (airports.length > 0) {
this.$store.commit('CENTER', [airports[0].geometry.coordinates[1], airports[0].geometry.coordinates[0]])
}
Vue.set(this.$parent.$parent.$parent, 'uploadVisible', true)
this.$parent.$parent.$parent.$refs.upload.status()
this.$parent.$parent.$parent.$refs.upload.check()
}
},
computed: {
date: function () {
var d = new Date(this.airport.time)
return d.toLocaleDateString() + ' ' + d.toLocaleTimeString()
},
upload_date: function () {
if( this.airport.upload !== undefined ) {
var d = new Date(this.airport.upload)
return d.toLocaleDateString() + ' ' + d.toLocaleTimeString()
} else {
return "-"
}
}
}
}
</script>
<style scoped lang="scss">
.text {
padding: 10px;
}
.button {
padding-left: 10px;
padding-right: 10px;
}
.el-popover--plain {
padding: 5px 5px;
}
</style>

View File

@@ -1,64 +1,333 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div width="100%" v-if="airport">
<div>
<el-row>
<el-col :span="7">ICAO :</el-col>
<el-col :span="15">{{ icao }}</el-col>
</el-row>
<el-row>
<el-col :span="7">Name :</el-col>
<el-col :span="15">{{ name }}</el-col>
</el-row>
<el-row>
<el-col :span="7">Flights :</el-col>
<el-col :span="15">{{ flights }}</el-col>
</el-row>
<el-row>
<el-col :span="7">Airlines :</el-col>
<el-col :span="15">{{ airlines }}</el-col>
</el-row>
<el-row>
<el-col :span="7">Groundnet :</el-col>
<el-col :span="15">{{groundnet}}</el-col>
</el-row>
<el-row>
<el-col :span="7">Parking Positions :</el-col>
<el-col :span="15">{{ parking }}</el-col>
</el-row>
</div>
<div v-if="airport">
<Upload :visible.sync="uploadVisible" ref="upload"></Upload>
<el-dialog
title="Add Airline"
:visible.sync="dialogVisible"
width="40%"
:before-close="handleClose"
>
<span>Add an selectable airline to {{ icao }} {{ name }}</span>
<el-input
placeholder="Please input airline(s)"
v-model="airlineCode"
></el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" @click="addAirline">Confirm</el-button>
</span>
</el-dialog>
<el-dialog
title="Import File"
:visible.sync="showImportFile"
width="20%"
:before-close="handleClose"
>
<span>Beware wip will be overwritten</span>
<el-row>
<el-col :span="20">
<el-input placeholder="Please input file" v-model="fileImportName">
</el-input>
</el-col>
<el-col :span="4">
<file-select @input="fileImportFileName"></file-select>
</el-col>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="showImportFile = false">Cancel</el-button>
<el-button type="primary" @click="importFile">Confirm</el-button>
</span>
</el-dialog>
<h1 class="leaflet-sidebar-header">{{ icao }} {{ name }}</h1>
<div width="100%">
<el-row>
<el-popover
placement="top-start"
title="Description"
width="50"
trigger="hover"
content="Edit"
>
<el-button @click="edit" v-if="!editing" slot="reference"
><i class="fas fa-edit"></i
></el-button>
</el-popover>
<el-popover
placement="top-start"
title="Description"
width="200"
trigger="hover"
content="Import groundnet"
>
<el-button
@click="showImportFile = true"
v-if="!editing"
slot="reference"
><i class="fas fa-file-import"></i
></el-button>
</el-popover>
<el-popover
placement="top-start"
title="Description"
width="220"
trigger="hover"
content="Export groundnet to export directory"
>
<el-button @click="test" v-if="!editing" slot="reference"
><i class="fas fa-file-export"></i
></el-button>
</el-popover>
<el-popover
placement="top-start"
title="Description"
width="200"
trigger="hover"
content="Upload Airport"
>
<el-button @click="upload" v-if="!editing" slot="reference"
><i class="fas fa-upload"></i
></el-button>
</el-popover>
</el-row>
<el-row>
<el-col :span="7"><span class="label"> Airlines :</span></el-col>
<el-col :span="15">
<el-tag v-for="item in airlines" :key="item.value">{{
item.value
}}</el-tag>
</el-col>
<el-col :span="2">
<el-button @click="dialogVisible = true" v-if="editing"
><i class="fas fa-plus"></i
></el-button>
</el-col>
</el-row>
</div>
<el-tabs v-model="activeTab">
<el-tab-pane label="Frequencies" name="first">
<div>
<el-row v-for="f in frequencyList" :key="f.index">
<Frequency :frequency="f"></Frequency>
</el-row>
<el-button @click="addFrequency" v-if="editing">Add</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="Parkings" name="second">
<ParkingList></ParkingList>
</el-tab-pane>
<el-tab-pane label="Statistics" name="third">
<el-row
><el-col :span="8"
><span class="label">Traffic :</span></el-col
></el-row
>
<el-row>
<el-col :span="8">Flights :</el-col>
<el-col :span="4">{{ flights }}</el-col>
<el-col :span="8"></el-col>
<el-col :span="4"></el-col>
</el-row>
<el-row
><el-col :span="16"
><span class="label">GIT/Terrasync :</span></el-col
></el-row
>
<el-row>
<el-col :span="8">Parking Positions :</el-col>
<el-col :span="4">{{ parking }}</el-col>
<el-col :span="8">Groundnet Nodes :</el-col>
<el-col :span="4">{{ groundnet }}</el-col>
</el-row>
<el-row
><el-col :span="8"><span class="label">Work :</span></el-col></el-row
>
<el-row v-if="wip">
<el-col :span="8">Work Parking Positions :</el-col>
<el-col :span="4">{{ wipparking }}</el-col>
<el-col :span="8">Work Groundnet Nodes :</el-col>
<el-col :span="4">{{ wipgroundnet }}</el-col>
</el-row>
<el-row v-if="wip">
<el-col :span="4">Saved :</el-col>
<el-col :span="8" class="text">{{ date }}</el-col>
<el-col :span="4">Uploaded :</el-col>
<el-col :span="8" class="text">{{ upload_date }}</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="Traffic" name="fourth">
<TrafficList></TrafficList>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script lang="js">
export default {
import EditButton from './EditButton'
import FileSelect from './FileSelect'
import Frequency from './Frequency'
import ParkingList from './ParkingList'
import TrafficList from './TrafficList'
import Upload from './Upload'
const fs = require('fs')
const path = require('path')
export default {
data () {
return {showImportFile: false, activeTab: 'first', editing: false, uploadVisible: false, dialogVisible: false, airlineCode: '', fileImport: null}
},
components: {
EditButton, FileSelect, Frequency, ParkingList, TrafficList, Upload
},
methods: {
fileImportFileName (f) {
this.fileImport = f
},
edit () {
this.isEditing = true
this.$emit('edit', true)
},
upload () {
this.uploadVisible = true
this.$refs.upload.status()
this.$refs.upload.check()
},
test () {
this.$parent.$parent.$parent.$refs.editLayer.test()
},
importFile () {
this.showImportFile = false
var fDir = this.$store.state.Settings.settings.airportsDirectory
var fNew = path.join(fDir, this.icao[0], this.icao[1], this.icao[2], this.icao + '.groundnet.new.xml')
var editLayer = this.$parent.$parent.$parent.$refs.editLayer
fs.copyFile(this.fileImport.path, fNew, () => {
editLayer.reload(false)
})
},
setEditing (editing) {
this.editing = editing
},
addAirline () {
this.dialogVisible = false
this.airlineCode.split(/[ ,]/).forEach(element => {
if (element.length === 3) {
this.$store.dispatch('addAirline', element)
}
})
},
addFrequency () {
this.$store.dispatch('addFrequency', {type: 'AWOS', value: 0})
},
initLayer () {
var parent = this.$parent
while (parent !== undefined && parent.$refs.editLayer === undefined) {
parent = parent.$parent
}
if (parent === undefined) {
return
}
this.editLayer = parent.$refs.editLayer
},
handleClose () {
}
},
computed: {
fileImportName: function () {
if (this.fileImport !== null) {
console.log(this.fileImport)
return this.fileImport.path
}
return 'Please select'
},
icao: function () {
return this.$store.state.Editable.data.airport.icao
return this.$store.state.Airports.currentAirport.icao
},
name: function () {
return this.$store.state.Editable.data.airport.name
return this.$store.state.Airports.currentAirport.name
},
flights: function () {
return this.$store.state.Editable.data.airport.flights
return this.$store.state.Airports.currentAirport.flights
},
airlines: function () {
return this.$store.state.Editable.data.airport.airlines
var airlineCodes = []
if (this.$store.state.Airports.currentAirport !== undefined && this.$store.state.Airports.currentAirport.airlines) {
var storedairlineCodes = this.$store.state.Airports.currentAirport.airlines
storedairlineCodes.forEach(element => {
airlineCodes.push({value: element, label: element})
})
}
return airlineCodes
},
groundnet: function () {
return this.$store.state.Editable.data.airport.groundnet
return this.$store.state.Airports.currentAirport.groundnet
},
parking: function () {
return this.$store.state.Editable.data.airport.parking
return this.$store.state.Airports.currentAirport.parking
},
airport: function () {
return this.$store.state.Editable.type === 'airport'
wipgroundnet: function () {
return this.$store.state.Airports.currentAirport.wipgroundnet
},
wipparking: function () {
return this.$store.state.Airports.currentAirport.wipparking
},
wip: function () {
// .icao
var wip = this.$store.state.Settings.wip.filter(w => w.icao === this.$store.state.Airports.currentAirport.icao)
if (wip.length > 0) {
return true
}
return false
},
date: function () {
var wip = this.$store.state.Settings.wip.filter(w => w.icao === this.$store.state.Airports.currentAirport.icao)
var d = new Date(wip[0].time)
return d.toLocaleDateString() + ' ' + d.toLocaleTimeString()
},
upload_date: function () {
var wip = this.$store.state.Settings.wip.filter(w => w.icao === this.$store.state.Airports.currentAirport.icao)
if (wip[0].upload !== undefined) {
var d = new Date(wip[0].upload)
return d.toLocaleDateString() + ' ' + d.toLocaleTimeString()
} else {
return '-'
}
},
airport: {
get: function () {
return this.$store.state.Airports.currentAirport !== undefined
}
},
frequencyList: {
// getter
get: function () {
return this.$store.state.Frequencies.items
}
}
}
}
</script>
<style lang="scss" scoped>
.el-row {
padding: 0em
}
.el-row {
padding: 0em;
margin-bottom: 5px;
}
.label {
display: flex;
justify-content: left;
align-items: center;
font-weight: bold;
}
</style>

View File

@@ -1,13 +1,10 @@
<template>
<section class="edit-layer">
<h1>edit-layer Component</h1>
</section>
</template>
<script lang="js">
import L from 'leaflet'
export default {
name: 'edit-layer',
name: 'airport-layer',
props: [],
mounted () {
this.add()

View File

@@ -27,6 +27,21 @@
<el-switch v-model="isPushback"></el-switch>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Direction :</span>
</el-col>
<el-col :span="15">
<el-select v-model="direction" placeholder="Select">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
></el-option>
</el-select>
</el-col>
</el-row>
</div>
</template>
@@ -36,6 +51,14 @@
arc: function () {
return this.$store.state.Editable.type === 'arc'
},
// ga (general aviation), cargo (cargo), gate (commercial passenger traffic),
// mil-fighter (military fighter), mil-cargo (military transport)
options: function () {
return [{value: 'bi-directional', label: 'bi-directional'},
{value: 'forward', label: 'forward'},
{value: 'backward', label: 'backward'}
]
},
name: {
// getter
get: function () {
@@ -49,12 +72,23 @@
isPushback: {
// getter
get: function () {
return this.$store.state.Editable.data.arc.isPushBackRoute === '1'
return this.$store.state.Editable.data.arc.isPushBackRoute === '1' ||
this.$store.state.Editable.data.arc.isPushBackRoute === 1
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_PUSHBACK', newValue ? '1' : '0')
}
},
direction: {
// getter
get: function () {
return this.$store.state.Editable.data.arc.direction
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_DIRECTION', newValue)
}
}
}
}

View File

@@ -0,0 +1,106 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div width="100%" v-if="multiarc">
<!--
airlineCodes: 0
heading: 341.34
index: 13
lat: "N59 52.610885"
lon: "W1 17.855144"
name: "Western_Apron_Hanger"
pushBackRoute: 27
radius: 18
type: "gate"
-->
<el-row>
<el-col :span="7">
<span class="demo-input-label">Name :</span>
</el-col>
<el-col :span="15">
<el-input placeholder="Please input" v-model="name"></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Pushback :</span>
</el-col>
<el-col :span="15">
<el-switch v-model="isPushback"></el-switch>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Direction :</span>
</el-col>
<el-col :span="15">
<el-select v-model="direction" placeholder="Select">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
></el-option>
</el-select>
</el-col>
</el-row>
</div>
</template>
<script lang="js">
export default {
computed: {
multiarc: function () {
return this.$store.state.Editable.type === 'multiarc'
},
// ga (general aviation), cargo (cargo), gate (commercial passenger traffic),
// mil-fighter (military fighter), mil-cargo (military transport)
options: function () {
return [{value: 'bi-directional', label: 'bi-directional'},
{value: 'forward', label: 'forward'},
{value: 'backward', label: 'backward'}
]
},
name: {
// getter
get: function () {
return this.$store.state.Editable.data.multiarc.name
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_ARC_NAME', newValue)
}
},
isPushback: {
// getter
get: function () {
return this.$store.state.Editable.data.multiarc.isPushBackRoute === '1' ||
Number(this.$store.state.Editable.data.multiarc.isPushBackRoute) === Number(1)
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_PUSHBACK', newValue ? '1' : '0')
}
},
direction: {
// getter
get: function () {
return this.$store.state.Editable.data.multiarc.direction
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_DIRECTION', newValue)
}
}
}
}
</script>

View File

@@ -6,11 +6,25 @@
<i class="fa fa-caret-left"></i>
</div>
</h1>
<div id="panel" width="100%">
<div id="panel" width="100%">
<el-row v-if="!results || results.length === 0 "><h3>Check not run</h3></el-row>
<el-row v-for="(result,idx) in results" :key="idx">
<el-col :span="15">{{ result.message }}</el-col>
<el-col :span="7"><el-button v-on:click="show(result.id)">Show</el-button></el-col>
<el-col :span="2" v-if="result.id==-1"><span class="label"><i class="far fa-check-circle"></i></span></el-col>
<el-col :span="2" v-if="result.id>=0"><span class="label"><i class="fas fa-exclamation-triangle"></i></span></el-col>
<el-col :span="2" v-if="result.id==-2"><span class="label"><i class="fas fa-exclamation-triangle"></i></span></el-col>
<el-col :span="15">
<el-popover
placement="top-start"
title="Description"
width="200"
trigger="hover"
v-if="result.message"
:content=result.message[1]
>
<span class="label" slot="reference">{{ result.message[0] }}</span>
</el-popover>
</el-col>
<el-col :span="4" v-if="result.id>=0"><el-button v-on:click="show(result.id)" class="button"><i class="fas fa-bullseye"></i></el-button></el-col>
</el-row>
</div>
</div>
@@ -46,5 +60,19 @@
}
.el-col {
border-radius: 1px;
padding: 0pt;
}
.button {
padding-left: 10px;
padding-right: 10px;
}
h3 {
text-align: center;
}
.label {
display: flex;
justify-content: left;
align-items: center;
font-weight: normal;
}
</style>

View File

@@ -3,19 +3,32 @@
<div class="select-button">
...
</div>
<input type="file" @change="handleFileChange" webkitdirectory directory/>
<input name="hiddenDir" type="file" v-on:change="handleFileChange($event)" webkitdirectory directory tabindex="-1"/>
</label>
</template>
<script>
export default {
/* eslint-disable */
const path = require('path');
const fs = require('fs');
export default {
props: {
value: File
},
methods: {
handleFileChange (e) {
this.$emit('input', e.target.files[0])
try {
if (e.target.files && e.target.files.length>0) {
var first = e.target.files[0].webkitRelativePath.split("/")[0];
var webkitdirectoryPath = e.target.files[0].path.split(first)[0] + first;
this.$emit('input', webkitdirectoryPath)
}
} catch (error) {
console.error(error)
}
}
}
}
@@ -32,6 +45,8 @@ export default {
text-align: center;
font-weight: bold;
width: 28px;
height: 28px;
}
.directory-select > input[type="file"] {

View File

@@ -1,29 +1,111 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div id="EditBar">
<EditButton icon="fas fa-edit" v-on:click="edit" :show="!editing" tooltip="Edit"></EditButton>
<EditButton
icon="fas fa-undo"
v-on:click="centerDialogVisible = true"
:show="editing"
tooltip="Undo"
></EditButton>
<el-dialog title="Reload" :visible.sync="centerDialogVisible" width="30%" center>
<span style="center">Reload from last save? You will lose the current edits.</span>
<el-dialog
title="Checking"
width="30%"
center
:visible.sync="checkDialogVisible"
>
<el-container direction="vertical">
<el-progress
:percentage="Number(((progress / max) * 100).toPrecision(3))"
v-if="max > 0"
></el-progress>
</el-container>
</el-dialog>
<el-dialog
title="Revert"
:visible.sync="centerDialogVisible"
width="550px"
center
>
<span>
Please select the Version to revert to.
<el-row v-for="item in saves" :key="item.file">
<el-button @click="revert(item.file)">{{item.mtime}}</el-button>
</el-row>
</span>
<span slot="footer" class="dialog-footer">
<el-button @click="undoFirst">Base version (GIT)</el-button>
<el-button type="primary" @click="undoLast">Last save</el-button>
<el-button type="primary" @click="cancel">Cancel</el-button>
</span>
</el-dialog>
<el-dialog title="Saving" :visible.sync="saveDialogVisible" width="30%" center>
<el-dialog
title="Saving"
:visible.sync="saveDialogVisible"
width="30%"
center
>
<span style="center">Saving..</span>
</el-dialog>
<EditButton icon="fas fa-save" v-on:click="save" :show="editing" tooltip="Save"></EditButton>
<ZoomButton
icon="fas fa-th"
v-on:click="zoomin"
:show="true"
tooltip="Zoomin"
></ZoomButton>
<ZoomButton
icon="fas fa-th-large"
v-on:click="zoomout"
:show="!editing"
tooltip="Zoomout"
></ZoomButton>
<EditButton
icon="fa fa-window-close"
v-on:click="close"
:show="editing"
tooltip="Close/Save Editing"
></EditButton>
<EditButton
icon="fas fa-undo"
v-on:click="openReload"
:show="editing"
tooltip="Revert to Savepoint"
></EditButton>
<EditButton
icon="fas fa-save"
v-on:click="save"
:show="editing"
tooltip="Save"
></EditButton>
<EditButton
icon="far fa-check-square"
v-on:click="showCheck"
:show="editing"
tooltip="Check"
></EditButton>
<EditButton
icon="fas fa-draw-polygon"
v-on:click="drawPolyline"
:show="editing"
tooltip="Draw Taxiline"
tooltip="Draw Bi-Directional Taxiline"
></EditButton>
<EditButton
icon="fas fa-long-arrow-alt-right"
v-on:click="drawForwardPolyline"
:show="editing"
tooltip="Draw Forward Taxiline"
></EditButton>
<EditButton
icon="fas fa-arrows-alt-h"
v-on:click="drawPushbackPolyline"
:show="editing"
tooltip="Draw Pushback"
></EditButton>
<EditButton
icon="fas fa-parking"
@@ -31,58 +113,137 @@
:show="editing"
tooltip="Draw Parking"
></EditButton>
<EditButton icon="fas fa-trash-alt" v-on:click="deleteFeature" :show="editing" tooltip="Remove"></EditButton>
<EditButton icon="far fa-check-square" v-on:click="showCheck" :show="editing" tooltip="Check"></EditButton>
<el-dialog title="Checking" width="30%" center :visible.sync="checkDialogVisible">
<el-container direction="vertical">
<el-progress :percentage="Number(((progress / max)*100).toPrecision(3))" v-if="max>0"></el-progress>
</el-container>
</el-dialog>
<EditButton
icon="fas fa-trash-alt"
v-on:click="deleteFeature"
:show="editing"
tooltip="Remove"
></EditButton>
</div>
</template>
<script lang="js">
/* eslint-disable */
const path = require('path')
const fs = require('fs');
const mapper = require('../check/mapper');
import {listSaves} from '../loaders/groundnet_loader'
import EditButton from './EditButton'
import ZoomButton from './ZoomButton';
import Vue from 'vue'
import fileUrl from 'file-url'
const path = require('path')
export default {
components: { EditButton },
components: { EditButton, ZoomButton },
data () {
return {isEditing: false, centerDialogVisible: false, saveDialogVisible: false, checkDialogVisible: false, checking: false, progress: 0, max: 0}
return {isEditing: false, uploadVisible: false, centerDialogVisible: false, saveDialogVisible: false, checkDialogVisible: false, checking: false, progress: 0, max: 0, pavementLayerVisible: true, saves: [] }
},
created () {
},
methods: {
cancel () {
this.centerDialogVisible = false
},
zoomout() {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.zoomUpdated(9)
},
zoomin() {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.zoomUpdated(14)
},
hideAPT() {
this.pavementLayerVisible = !this.pavementLayerVisible
this.$parent.$parent.$refs.pavementLayer.setVisible(this.pavementLayerVisible)
},
edit () {
this.editing = true
this.$parent.$parent.$refs.editLayer.enableEdit()
this.isEditing = true
this.$emit('edit', true)
},
undoFirst () {
this.editing = false
this.centerDialogVisible = false
this.$parent.$parent.$refs.editLayer.disableEdit()
this.$parent.$parent.$refs.editLayer.reload(true)
setEditing (editing) {
this.isEditing = editing
},
undoLast () {
this.editing = false
revert (file) {
this.isEditing = false
this.$emit('edit', false)
this.centerDialogVisible = false
this.$parent.$parent.$refs.map.mapObject.options.minZoom = 1;
this.$parent.$parent.$refs.editLayer.disableEdit()
this.$parent.$parent.$refs.editLayer.reload(false)
this.$parent.$parent.$refs.towerLayer.disableEdit()
this.$parent.$parent.$refs.thresholdLayer.disableEdit()
this.$parent.$parent.$refs.editLayer.reload(file)
},
close () {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.isEditing = false
this.$emit('edit', false)
this.$parent.$parent.$refs.map.mapObject.options.minZoom = 1;
Vue.set(this, 'saveDialogVisible', true)
this.$emit('edit', false)
Vue.nextTick( function () {
setTimeout( this.closeDefered.bind(this), 100);
}, this)
},
closeDefered () {
this.$parent.$parent.$refs.editLayer.save()
this.$parent.$parent.$refs.towerLayer.save()
this.$parent.$parent.$refs.thresholdLayer.save()
this.$parent.$parent.$refs.editLayer.disableEdit()
this.$parent.$parent.$refs.towerLayer.disableEdit()
this.$parent.$parent.$refs.thresholdLayer.disableEdit()
this.rescanCurrentGroundnet()
Vue.set(this, 'saveDialogVisible', false)
},
save () {
this.editing = false
Vue.set(this, 'saveDialogVisible', true)
this.editing = false
Vue.nextTick( () => {
this.$parent.$parent.$refs.editLayer.save()
this.$parent.$parent.$refs.editLayer.disableEdit()
Vue.set(this, 'saveDialogVisible', false)
this.$parent.$parent.$refs.editLayer.stopDrawing()
Vue.nextTick( function () {
setTimeout( this.saveDefered.bind(this), 100);
}, this)
},
saveDefered () {
this.$parent.$parent.$refs.editLayer.save()
this.$parent.$parent.$refs.towerLayer.save()
this.$parent.$parent.$refs.thresholdLayer.save()
this.rescanCurrentGroundnet()
Vue.set(this, 'saveDialogVisible', false)
},
rescanCurrentGroundnet () {
try {
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
: `file://${process.resourcesPath}/workers/worker.js`
console.log('make a worker: ', path.resolve(__dirname, 'worker.js'))
var icao = this.$parent.$parent.$refs.editLayer.icao
const worker = new Worker(winURL)
var aptDir = path.join(this.$store.state.Settings.settings.airportsDirectory, icao[0], icao[1], icao[2]);
worker.postMessage(['scan', aptDir ])
// the reply
var store = this.$store
worker.onmessage = function (e) {
if (e.data === 'scanStarted') {
this.progress = 0
this.max = 0
} else if (e.data === 'DONE') {
console.log('DONE')
store.dispatch('getAirports')
worker.terminate()
} else if (e.data.length > 0) {
if (e.data[0] === 'max') {
}
if (e.data[0] === 'progress') {
}
}
}
} catch (err) {
console.error(err)
}
},
pollData () {
var workery = this.worker
var view = this
@@ -93,18 +254,34 @@
view.scanning = Boolean(workery.checking)
workery.view = view
}
}, 1000)
}, 500)
},
check () {
try {
this.scanning = true
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/check.js`
: `file://${__dirname}/check.js`
: `file://${process.resourcesPath}/workers/check.js`
console.log('make a check worker: ', path.resolve(__dirname, 'check.js'))
if(!this.$parent.$parent.$refs.pavementLayer.pavement) {
this.max = 0
this.checkDialogVisible = false
this.$message({
type: 'Error',
showClose: true,
message: `Check can't run without pavementlayer since runways aren't known. Is the APT file set correctly?`
})
return
}
const worker = new Worker(winURL)
worker.onerror = function(e) {
worker.terminate()
worker.view.max = 0
worker.view.checkDialogVisible = false
e.preventDefault(); // <-- "Hey browser, I handled it!"
}
console.log(fileUrl('src/renderer/utils/check.js'))
worker.checking = this.checking
@@ -114,15 +291,27 @@
worker.progress = 0
// var worker = new Worker(fileUrl('src/renderer/utils/worker.js'))
this.worker = worker
var xml = []
var groundnet = []
this.$parent.$parent.$refs.editLayer.groundnetLayerGroup.eachLayer(l => {
console.log(l)
xml.push(l)
groundnet.push(l)
})
var features = groundnet.map(mapper.checkMapper).filter(n => n)
var pavement = []
this.$parent.$parent.$refs.pavementLayer.pavement.eachLayer(l => {
console.log(l)
pavement.push(l)
})
var thresholds = []
this.$parent.$parent.$refs.thresholdLayer.getLayer().eachLayer(l => {
console.log(l)
thresholds.push(l)
})
var pavementFeatures = pavement.map(mapper.checkMapper).filter(n => n)
//TODO
var thresholdFeatures = thresholds.map(mapper.checkMapper).filter(n => n)
var features = xml.map(this.featuresMapper).filter(n => n)
worker.postMessage(['check', features ] )
worker.postMessage(['check', features.concat(pavementFeatures).concat(thresholdFeatures) ] )
this.pollData()
// the reply
var store = this.$store
@@ -153,30 +342,35 @@
}
},
drawPolyline () {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.$refs.editLayer.drawPolyline()
},
drawForwardPolyline () {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.$refs.editLayer.drawForwardPolyline()
},
drawPushbackPolyline () {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.$refs.editLayer.drawPushbackPolyline()
},
drawParking () {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.$refs.editLayer.drawParking()
},
deleteFeature () {
this.$parent.$parent.$refs.editLayer.stopDrawing()
this.$parent.$parent.$refs.editLayer.deleteFeature()
},
showCheck() {
this.$parent.$parent.$refs.editLayer.stopDrawing()
Vue.set(this, 'checkDialogVisible', true)
this.check()
},
featuresMapper(o) {
if (o instanceof L.ParkingSpot) {
return { 'index': Number(o['id']), '_leaflet_id': o._leaflet_id, 'type': 'parking', 'name': o.options.attributes.name, 'radius': String(o.options.attributes.radius) };
} else if (o instanceof L.RunwayNode) {
console.log(o)
return { 'index': Number(o['glueindex']), '_leaflet_id': o._leaflet_id, 'type': 'runway' };
} else if (o instanceof L.Polyline) {
console.log(o)
return { 'start': Number(o['begin']), 'end': Number(o['end']), '_leaflet_id': o._leaflet_id, 'type': 'poly' };
} else {
console.log('Unknown Type ')
console.log(typeof o)
openReload: function() {
this.centerDialogVisible = true
var icao = this.$parent.$parent.$refs.editLayer.icao
if (icao !== undefined && icao !== '') {
this.saves = listSaves(this.$store.state.Settings.settings.airportsDirectory, icao).sort((a, b) => a.mtimeMs - b.mtimeMs)
}
}
},

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,11 @@
<template>
<label class="file-select">
<div class="select-button">
<span v-if="value">Selected File: {{value.name}}</span>
<span v-else>Select File</span>
...
</div>
<input type="file" @change="handleFileChange"/>
</label>
</template>
<script>
export default {
props: {

View File

@@ -1,47 +1,77 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<l-map
:zoom="zoom"
:center="center"
:options="options"
@ready="ready"
@update:zoom="zoomUpdated"
@update:center="centerUpdated"
@update:bounds="boundsUpdated"
ref="map"
>
<!--The backgroundmap-->
<l-tile-layer :url="url" :attribution="attribution">
</l-tile-layer>
<l-tile-layer :url="url" :attribution="attribution" :options="{maxZoom: 22, maxNativeZoom: 17}"></l-tile-layer>
<!--
<l-control position="topright" >
<el-button @click="editAirport()">{{ icao }}</el-button>
</l-control>
-->
<!--<l-marker :lat-lng="marker"></l-marker>-->
<LeafletSidebar></LeafletSidebar>
<EditBar></EditBar>
<PavementLayer ref="pavementLayer"></PavementLayer>
<LeafletSidebar ref="sidebar" @editParking="onEditSidebar" @edit="onEdit($event)"></LeafletSidebar>
<AiLayer ref="aiLayer"></AiLayer>
<l-layer-group layerType="overlay" name="airports" ref="airportLayer">
<l-circle
v-for="(item, index) in this.$store.state.Airports.airports"
:key="index"
:lat-lng="[item.geometry.coordinates[1], item.geometry.coordinates[0]]"
:radius="((item.properties.flights+5)*20)"
:color='item.color'
:color="item.color"
@add="addEvent($event, item)"
@click="onClick(item)"
@click="onClick($event, item)"
></l-circle>
</l-layer-group>
<EditLayer ref="editLayer"></EditLayer>
<PavementLayer ref="pavementLayer"></PavementLayer>
<ThresholdLayer ref="thresholdLayer"></ThresholdLayer>
<TowerLayer ref="towerLayer"></TowerLayer>
<ToolLayer ref="toolLayer" @select-poly="onSelectedPolygon"></ToolLayer>
<EditBar ref="editBar" @edit="onEdit($event)"></EditBar>
<ToolBar ref="toolBar"></ToolBar>
</l-map>
</template>
<script lang="js">
import 'leaflet/dist/leaflet.css'
import { LMap, LTileLayer, LMarker, LCircle, LLayerGroup } from 'vue2-leaflet'
import 'leaflet-search/dist/leaflet-search.src.css'
import '@/assets/button.css'
import { LMap, LTileLayer, LMarker, LCircle, LLayerGroup, LControl, LTooltip } from 'vue2-leaflet'
import LeafletSidebar from './LeafletSidebar'
import AiLayer from './AiLayer'
import EditBar from './EditBar'
import ToolBar from './ToolBar'
import EditLayer from './EditLayer'
import ToolLayer from './ToolLayer'
import TowerLayer from './TowerLayer'
import PavementLayer from './PavementLayer'
import ThresholdLayer from './ThresholdLayer'
import { Loading } from 'element-ui'
import L from 'leaflet'
import { LeafletSearch } from 'leaflet-search'
// https://github.com/KoRiGaN/Vue2Leaflet/issues/103
delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
@@ -49,46 +79,225 @@
})
export default {
name: 'flightgear-map',
components: { LMap, LTileLayer, LMarker, LCircle, LeafletSidebar, EditBar, EditLayer, PavementLayer, LLayerGroup },
components: { LMap, LTileLayer, LMarker, LCircle, LTooltip, LeafletSidebar, AiLayer, EditBar, ToolBar, EditLayer, TowerLayer, PavementLayer, LLayerGroup, LControl, ThresholdLayer, ToolLayer, LeafletSearch },
props: [],
created () {
this.loadingInstance = null
this.$store.watch(
function (state) {
return state.Loading.icao
},
(newValue, oldValue) => {
// debugger
console.log('setIcaoLoading ' + oldValue + ' => ' + newValue + ' groundnetLoaded ' + this.groundnetLoaded + ' pavementLoaded ' + this.pavementLoaded + ' ' + this.loadingInstance)
if (newValue !== oldValue && newValue !== '') {
this.groundnetLoaded = newValue
if ((this.loadingInstance === null || this.loadingInstance === undefined)) {
this.loadingInstance = Loading.service({ fullscreen: false })
}
}
}
,
{
deep: false,
immediate: true
}
)
this.$store.watch(
function (state) {
return state.Loading.groundnetLoaded
},
(newValue, oldValue) => {
// debugger
console.log('groundnetLoaded ' + oldValue + ' => ' + newValue + ' groundnetLoaded ' + this.groundnetLoaded + ' pavementLoaded ' + this.pavementLoaded + ' ' + this.loadingInstance)
if (newValue !== oldValue) {
this.groundnetLoaded = newValue
if (this.groundnetLoaded &&
this.pavementLoaded &&
this.loadingInstance !== null) {
this.loadingInstance.close()
this.loadingInstance = null
}
}
}
,
{
deep: false,
immediate: true
}
)
this.$store.watch(
function (state) {
return state.Loading.pavementLoaded
},
(newValue, oldValue) => {
console.log('pavementLoaded ' + oldValue + ' => ' + newValue + ' ' + this.groundnetLoaded + ' ' + this.pavementLoaded + ' ' + this.loadingInstance)
if (newValue !== oldValue) {
this.pavementLoaded = newValue
if (this.groundnetLoaded &&
this.pavementLoaded &&
this.loadingInstance !== null) {
this.loadingInstance.close()
this.loadingInstance = null
}
}
}
,
{
deep: false,
immediate: true
}
)
},
mounted () {
this.$store.dispatch('getAirports')
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'BOUNDS' || mutation.type === 'SET_AIRPORTS') {
if (mutation.type === 'CENTER' || mutation.type === 'SET_AIRPORTS' || mutation.type === 'ZOOM') {
// console.log(this.$parent)
// console.log(this.$store.state.Settings.bounds)
let airportsToLoad = this.$store.state.Airports.airports
.filter(feature => this.visible(feature))
.map(feature => feature.properties.icao)
if (airportsToLoad.length > 0 && airportsToLoad[0] !== this.editingAirport && this.zoom > 12) {
this.$refs.editLayer.load(airportsToLoad[0])
this.$refs.pavementLayer.load(airportsToLoad[0])
this.editingAirport = airportsToLoad[0]
this.$store.dispatch('setIcaoLoading', airportsToLoad[0])
this.$nextTick(() => { // Loading should be closed asynchronously
this.$refs.pavementLayer.load(airportsToLoad[0])
this.$refs.editLayer.load(airportsToLoad[0])
this.$refs.thresholdLayer.load(airportsToLoad[0])
if (this.$refs.towerLayer) {
this.$refs.towerLayer.load(airportsToLoad[0])
}
this.editingAirport = airportsToLoad[0]
})
}
if (this.$refs.editLayer) {
this.$refs.editLayer.setVisible(this.zoom >= 12)
}
if (this.$refs.airportLayer) {
this.$refs.airportLayer.setVisible(this.zoom < 12)
}
this.$refs.editLayer.setVisible(this.zoom >= 12)
this.$refs.airportLayer.setVisible(this.zoom < 12)
// console.log(this.groundnet)
}
})
},
data () {
return {
// 'http://{s}.tile.osm.org/{z}/{x}/{y}.png'
url: 'https://a.tile.openstreetmap.de/{z}/{x}/{y}.png',
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
marker: L.latLng(47.413220, -1.219482),
loadingInstance: Object,
groundnetLoaded: false,
pavementLoaded: false,
url: 'http://xjsjc.kongjian.xin:4200/tile/{z}/{x}/{y}.png',
attribution: '<A href="https://github.com/Portree-Kid/flightgear-airports" target="_blank">Flightgear Airports ' + require('electron').remote.app.getVersion() +
'</A> <A href="https://www.electronjs.org/" target="_blank">Electron</A> ' +
' <A href="https://element.eleme.io/#/en-US/" target="_blank">element.io</A> ' +
' &copy; <a href="http://osm.org/copyright" target="_blank">OpenStreetMap</a> contributors',
airports: this.$store.state.Airports.airports,
options: {editable: true}
options: {editable: true},
layersControl: null,
icao: 'TEST'
}
},
methods: {
visible (feature) {
let bounds = this.$store.state.Settings.bounds
if (!bounds.hasOwnProperty('getNorthEast')) {
bounds = this.$refs.map.mapObject.getBounds()
ready (e) {
e.on('layeradd', this.onLayerAdd)
},
onLayerAdd (e) {
if (this.layersControl === null) {
this.layersControl = L.control.layers({}, {}, {position: 'topleft'})
this.layersControl.addTo(this.$refs.map.mapObject)
var icon = this.layersControl._container.ownerDocument.createElement('I')
icon.className = 'fas fa-layer-group'
icon.style = 'padding-top: 9px; height: 30px; width: 30px; text-align: center; vertical-align: sub;'
this.layersControl._container.children[0].appendChild(icon)
// this.layersControl.addOverlay(this.$refs.thresholdLayer, 'Threshold Layer')
}
let width = bounds.getNorthWest().distanceTo(bounds.getSouthEast())
if (this.$refs.pavementLayer.getLayer() === e.layer) {
// debugger
var l = this.layersControl._layers.filter(l => l.name === 'APT Layer')
if (l.length > 0 && l[0].layer !== this.$refs.pavementLayer.getLayer()) {
this.layersControl.removeLayer(l[0].layer)
this.layersControl.addOverlay(this.$refs.pavementLayer.getLayer(), 'APT Layer')
}
if (l.length === 0) {
this.layersControl.addOverlay(this.$refs.pavementLayer.getLayer(), 'APT Layer')
}
}
if (this.$refs.thresholdLayer !== undefined && this.$refs.thresholdLayer.getLayer() === e.layer) {
l = this.layersControl._layers.filter(l => l.name === 'Threshold Layer')
if (l.length > 0 && l[0].layer !== this.$refs.thresholdLayer.getLayer()) {
this.layersControl.removeLayer(l[0].layer)
this.layersControl.addOverlay(this.$refs.thresholdLayer.getLayer(), 'Threshold Layer')
}
if (l.length === 0) {
this.layersControl.addOverlay(this.$refs.thresholdLayer.getLayer(), 'Threshold Layer')
}
this.$refs.thresholdLayer.zoomUpdated()
}
if (this.$refs.towerLayer !== undefined && this.$refs.towerLayer.getLayer() === e.layer) {
l = this.layersControl._layers.filter(l => l.name === 'Tower Layer')
if (l.length > 0 && l[0].layer !== this.$refs.towerLayer.getLayer()) {
this.layersControl.removeLayer(l[0].layer)
this.layersControl.addOverlay(this.$refs.towerLayer.getLayer(), 'Tower Layer')
}
if (l.length === 0) {
this.layersControl.addOverlay(this.$refs.towerLayer.getLayer(), 'Tower Layer')
}
this.$refs.towerLayer.zoomUpdated()
}
if (this.$refs.editLayer !== undefined && this.searchControl === undefined && this.$refs.editLayer.getLayer() === e.layer) {
this.searchControl = new L.Control.Search({
layer: this.$refs.editLayer.getLayer(),
position: 'topleft',
propertyName: 'searchTerm',
marker: {animate: false},
initial: false
})
this.searchControl.addTo(this.$refs.map.mapObject)
}
},
onSelectedPolygon (ring) {
var parkings = this.$refs.editLayer.getParkings(ring)
console.debug(ring)
console.debug(parkings)
this.$store.commit('SET_EDIT_TYPE', 'parking-group')
this.$refs.sidebar.setData(parkings)
},
onEdit (event) {
if (event) {
this.$refs.map.mapObject.options.minZoom = 13
} else {
this.$refs.map.mapObject.options.minZoom = 1
}
this.$refs.editLayer.enableEdit()
this.$refs.towerLayer.enableEdit()
this.$refs.thresholdLayer.enableEdit()
this.$refs.editBar.setEditing(event)
this.$refs.toolBar.setEditing(event)
this.$refs.sidebar.setEditing(event)
},
onEditSidebar (event) {
this.$refs.editLayer.onEdit(event)
},
editAirport () {
if (this.editingAirport) {
let airportsToLoad = this.$store.state.Airports.airports
.filter(feature => feature.properties.icao === this.icao)
let properties = Object.assign({}, airportsToLoad[0].properties)
this.$store.commit('SET_EDIT_AIRPORT', properties)
}
},
setIcao (icao) {
this.icao = icao
this.$store.dispatch('setCurrentAirport', icao)
},
visible (feature) {
let bounds
if (this.$refs.map) {
bounds = this.$refs.map.mapObject.getBounds()
} else {
return false
}
let width = L.latLng(bounds._northEast).distanceTo(L.latLng(bounds._southWest))
// Load all airports in a minimum 5 km box
if (width < 5000) {
let rest = 5000 - width
@@ -97,49 +306,107 @@
}
let coordinates = feature.geometry.coordinates
let ret = bounds.getNorthEast().lat > Number(coordinates[1]) &&
bounds.getNorthEast().lng > Number(coordinates[0])
let ret2 = bounds.getSouthWest().lat < Number(coordinates[1]) &&
bounds.getSouthWest().lng < Number(coordinates[0])
let ret = bounds._northEast.lat > Number(coordinates[1]) &&
bounds._northEast.lng > Number(coordinates[0])
let ret2 = bounds._southWest.lat < Number(coordinates[1]) &&
bounds._southWest.lng < Number(coordinates[0])
return ret && ret2
},
normalStyle (target) {
if (target.airport.properties.groundnet === 0) {
target.setStyle({color: 'red', fillColor: 'red', weight: '2'})
} else if ((target.airport.properties.flights / target.airport.properties.parking) > 40) {
target.setStyle({color: 'yellow', fillColor: 'yellow', weight: '2'})
} else {
target.setStyle({color: '#3388ff', fillColor: '#3388ff', weight: '2'})
}
},
highlightStyle (target) {
if (target.airport.properties.groundnet === 0) {
target.setStyle({color: 'red', fillColor: 'red', weight: '5'})
} else if ((target.airport.properties.flights / target.airport.properties.parking) > 40) {
target.setStyle({color: 'yellow', fillColor: 'yellow', weight: '5'})
} else {
target.setStyle({color: '#3388ff', fillColor: '#3388ff', weight: '5'})
}
},
// Triggered by adding airports to the map
addEvent (event, item) {
this.selected = null
event.target.airport = item
// console.log(event, item)
if (item.properties.groundnet === 0) {
event.target.setStyle({color: 'red', fillColor: 'red'})
} else if ((item.properties.flights / item.properties.parking) > 40) {
event.target.setStyle({color: 'yellow', fillColor: 'yellow'})
}
this.normalStyle(event.target)
event.target.bindTooltip(event.target.airport.properties.icao + ' ' + event.target.airport.properties.name)
},
onClick (item) {
onClick (event, item) {
console.log(item)
if (this.selected !== null) {
this.normalStyle(this.selected)
}
this.selected = event.target
this.highlightStyle(this.selected)
event.target.bringToBack()
this.setIcao(item.properties.icao)
this.$store.commit('SET_EDIT_AIRPORT', item.properties)
let newCenter = L.latLng(item.geometry.coordinates[1], item.geometry.coordinates[0])
this.$refs.map.setCenter(newCenter)
},
zoomUpdated (zoom) {
if (zoom !== this.$store.state.Settings.zoom) {
this.$store.dispatch('setZoom', zoom)
this.$refs.airportLayer.setVisible(zoom < 12)
if (this.$refs.towerLayer) {
this.$refs.towerLayer.setVisible(this.zoom >= 12)
}
if (this.$refs.thresholdLayer) {
this.$refs.thresholdLayer.setVisible(this.zoom >= 12)
}
this.$refs.pavementLayer.setVisible(zoom >= 12)
}
if (this.$refs.editLayer.groundnetLayerGroup) {
this.$refs.editLayer.groundnetLayerGroup.eachLayer(function (layer) {
if (layer.updateArrows !== undefined) {
layer.updateArrows(zoom)
}
})
}
if (this.$refs.thresholdLayer) {
this.$refs.thresholdLayer.zoomUpdated()
}
if (this.$refs.towerLayer) {
this.$refs.towerLayer.zoomUpdated()
}
},
async centerUpdated (center) {
if (center !== this.$store.state.Settings.center) {
this.$store.dispatch('setCenter', center)
this.$store.dispatch('setCenter', {lat: Number(center.lat), lng: Number(center.lng)})
this.$refs.airportLayer.setVisible(this.zoom < 12)
if (this.$refs.thresholdLayer) {
this.$refs.thresholdLayer.setVisible(this.zoom >= 12)
}
if (this.$refs.towerLayer) {
this.$refs.towerLayer.setVisible(this.zoom >= 12)
}
this.$refs.pavementLayer.setVisible(this.zoom >= 12)
}
},
async boundsUpdated (bounds) {
/*
if (bounds !== this.$store.state.Settings.bounds) {
this.$store.dispatch('setBounds', bounds)
this.$refs.airportLayer.setVisible(this.zoom < 12)
this.$refs.pavementLayer.setVisible(this.zoom < 12)
}
*/
}
},
computed: {
zoom: function () {
return this.$store.state.Settings.zoom
},
isEditing: function () {
return this.$refs.editLayer !== undefined && this.$refs.editLayer.editing
},
center: function () {
return this.$store.state.Settings.center
}
@@ -157,4 +424,17 @@
.flightgear-map {
color: aqua;
}
.item {
padding: 18px 0;
}
.l-control.el-card {
padding-left: 2px;
padding-top: 0px;
padding-right: 2px;
padding-bottom: 0px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 30px;
height: 30px;
}
</style>

View File

@@ -0,0 +1,124 @@
<template>
<div>
<span>
<el-row>
<el-col :span="7">
<el-select v-model="type" placeholder="Select" :disabled="!editing">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
></el-option>
</el-select>
</el-col>
<el-col :span="13">
<el-input
placeholder="Please input frequency"
v-model="value"
:disabled="!editing"
v-bind:class="{ invalid: !ok && editing}"
></el-input>
</el-col>
<el-col :span="2">
<el-button @click="remove" v-if="editing" ><i class="fas fa-trash-alt"></i></el-button>
</el-col>
</el-row>
</span>
</div>
</template>
<script lang="js">
import EditButton from './EditButton'
/* eslint-disable */
export default {
name: 'frequency',
components: { EditButton },
props: {frequency: Object},
mounted () {
},
data () {
return {
ok: true, editLayer: null
}
},
methods: {
remove () {
this.$store.dispatch('removeFrequency', this.frequency)
},
isValid (frequency) {
let ok = frequency >= 11800 && frequency <= 13700
if (!ok) {
return false
}
let fractions = (frequency - (Math.trunc(frequency/100)*100))
let subFraction = Math.round(fractions % 25)
switch (subFraction) {
case 0:
break
case 5:
break
case 10:
break
case 15:
break
case 25:
break
default:
return false
}
return true
},
initLayer() {
var parent = this.$parent
while (parent.$refs.editLayer===undefined) {
parent = parent.$parent
}
this.editLayer = parent.$refs.editLayer
}
},
computed: {
editing: function () {
if(this.editLayer=== null)
this.initLayer()
return this.editLayer.editing
},
options: function () {
return [
{value: 'AWOS', label: 'AWOS'},
{value: 'GROUND', label: 'GROUND'},
{value: 'TOWER', label: 'TOWER'},
{value: 'APPROACH', label: 'APPROACH'},
{value: 'DEPARTURE', label: 'DEPARTURE'}
]
},
type: {
// getter
get: function () {
return this.frequency.type;
},
// setter
set: function (newValue) {
this.frequency.type = newValue;
}
},
value: {
// getter
get: function () {
return this.frequency.value;
},
// setter
set: function (newValue) {
this.frequency.value = newValue;
this.ok = this.isValid(newValue);
}
}
}
}
</script>
<style scoped lang="scss">
.invalid {
padding: 1px;
background-color: red;
}
</style>

View File

@@ -0,0 +1,71 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div class="leaflet-sidebar-pane" id="home">
<h1 class="leaflet-sidebar-header">
{{version}}
<div class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></div>
</h1>
<h2>Setup</h2>
<ol>
<li>Set directories in settings (bottom left)</li>
<li>Start scans one after the other in sidebar with magnifying glass.</li>
</ol>
<h2>Search</h2>
You can search for airports with ICAO or parts of the name. Only airports with flights are shown.
If you want to view another one it will be added when you type the full ICAO.
<h2>World view</h2>
<p>
When zoomed out you will see circles. Their size corresponds with the number of flights.
Blue means Ok. Yellow to little parking in comparison to the number of flights. Red no groundnet.
</p>
<h2>Edit view</h2>
<p>
</p>
<ul>
<li>Undo undos all changes or all changes during session</li>
<li>Save, saves the groundnet</li>
<li>Check triggers the groundnet check.</li>
<li>Draw bi directional taxiline</li>
<li>Draw uni directional taxiline</li>
<li>Draw pushback.</li>
<li>Add parking</li>
<li>Remove element, removes the currently selected element</li>
</ul>
</div>
</template>
<script lang="js">
export default {
name: 'help',
props: [],
mounted () {
},
data () {
return {
}
},
methods: {
},
computed: {
version: function () {
return ' Flightgear Airports ' + require('electron').remote.app.getVersion()
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -1,128 +0,0 @@
<template>
<div id="wrapper">
<img id="logo" src="~@/assets/logo.png" alt="electron-vue">
<main>
<div class="left-side">
<span class="title">
Welcome to your new project!
</span>
<system-information></system-information>
</div>
<div class="right-side">
<div class="doc">
<div class="title">Getting Started</div>
<p>
electron-vue comes packed with detailed documentation that covers everything from
internal configurations, using the project structure, building your application,
and so much more.
</p>
<button @click="open('https://simulatedgreg.gitbooks.io/electron-vue/content/')">Read the Docs</button><br><br>
</div>
<div class="doc">
<div class="title alt">Other Documentation</div>
<button class="alt" @click="open('https://electron.atom.io/docs/')">Electron</button>
<button class="alt" @click="open('https://vuejs.org/v2/guide/')">Vue.js</button>
</div>
</div>
</main>
</div>
</template>
<script>
import SystemInformation from './LandingPage/SystemInformation'
export default {
name: 'landing-page',
components: { SystemInformation },
methods: {
open (link) {
this.$electron.shell.openExternal(link)
}
}
}
</script>
<style>
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro');
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body { font-family: 'Source Sans Pro', sans-serif; }
#wrapper {
background:
radial-gradient(
ellipse at top left,
rgba(255, 255, 255, 1) 40%,
rgba(229, 229, 229, .9) 100%
);
height: 100vh;
padding: 60px 80px;
width: 100vw;
}
#logo {
height: auto;
margin-bottom: 20px;
width: 420px;
}
main {
display: flex;
justify-content: space-between;
}
main > div { flex-basis: 50%; }
.left-side {
display: flex;
flex-direction: column;
}
.welcome {
color: #555;
font-size: 23px;
margin-bottom: 10px;
}
.title {
color: #2c3e50;
font-size: 20px;
font-weight: bold;
margin-bottom: 6px;
}
.title.alt {
font-size: 18px;
margin-bottom: 10px;
}
.doc p {
color: black;
margin-bottom: 10px;
}
.doc button {
font-size: .8em;
cursor: pointer;
outline: none;
padding: 0.75em 2em;
border-radius: 2em;
display: inline-block;
color: #fff;
background-color: #4fc08d;
transition: all 0.15s ease;
box-sizing: border-box;
border: 1px solid #4fc08d;
}
.doc button.alt {
color: #42b983;
background-color: transparent;
}
</style>

View File

@@ -1,39 +1,56 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div id="sidebar" class="leaflet-sidebar collapsed">
<Upload :visible.sync="uploadVisible" ref="upload"></Upload>
<!-- Nav tabs -->
<div class="leaflet-sidebar-tabs">
<ul role="tablist"> <!-- top aligned tabs -->
<li><a href="#home" role="tab"><i class="fa fa-bars"></i></a></li>
<li><a href="#edit" role="tab"><i class="fas fa-edit"></i></a></li>
<!--<li><a href="#parking" role="tab"><i class="fas fa-parking"></i></a></li>-->
<li :v-if="results"><a href="#check" role="tab"><i class="far fa-check-square"></i></a></li>
<li><a href="#search" role="tab"><i class="fa fa-search"></i></a></li>
<li><a href="#scan" role="tab"><i class="fa fa-sync"></i></a></li>
<li><a href="#check" role="tab"><i class="far fa-check-square"></i></a></li>
<li><a href="#wip" role="tab"><i class="fas fa-wrench"></i></a></li>
</ul>
<ul role="tablist"> <!-- bottom aligned tabs -->
<li><a href="#scan" role="tab"><i class="fa fa-sync"></i></a></li>
<li><a href="#settings" role="tab"><i class="fas fa-cog"></i></a></li>
</ul>
</div>
<!-- Tab panes -->
<div class="leaflet-sidebar-content">
<div class="leaflet-sidebar-pane" id="home">
<h1 class="leaflet-sidebar-header">
Help
<div class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></div>
</h1>
<p>A responsive sidebar for mapping libraries</p>
</div>
<Help/>
<div class="leaflet-sidebar-pane" id="edit">
<h1 class="leaflet-sidebar-header">
Properties
<div class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></div>
</h1>
<AirportEdit></AirportEdit>
<ParkingEdit></ParkingEdit>
<ArcEditMulti></ArcEditMulti>
<ArcEdit></ArcEdit>
<NodeEdit></NodeEdit>
<ThresholdEdit></ThresholdEdit>
<TowerEdit></TowerEdit>
<NodeEdit></NodeEdit>
<ParkingGroupEdit ref="parkingGroupEdit" @editParking="(msg) => $emit('editParking', msg)"></ParkingGroupEdit>
<AirportEdit ref="airportEdit" @edit="$emit('edit', $event)"></AirportEdit>
</div>
<!--
<div class="leaflet-sidebar-pane" id="parking">
<ParkingList></ParkingList>
</div>
-->
<div class="leaflet-sidebar-pane" id="search">
<Search></Search>
</div>
@@ -46,6 +63,9 @@
<div class="leaflet-sidebar-pane" id="check">
<CheckPanel></CheckPanel>
</div>
<div class="leaflet-sidebar-pane" id="wip">
<WorkInProgress></WorkInProgress>
</div>
</div>
</div>
</template>
@@ -56,19 +76,33 @@
import {} from 'leaflet-sidebar-v2'
import L from 'leaflet'
import AirportEdit from './AirportEdit'
import CheckPanel from './CheckPanel'
import ParkingEdit from './ParkingEdit'
import ArcEdit from './ArcEdit'
import TowerEdit from './TowerEdit'
import ArcEditMulti from './ArcEditMulti'
import CheckPanel from './CheckPanel'
import FileSelect from './FileSelect'
import Help from './Help'
import NodeEdit from './NodeEdit'
import ThresholdEdit from './ThresholdEdit'
import ParkingEdit from './ParkingEdit'
import ParkingGroupEdit from './ParkingGroupEdit'
// import ParkingList from './ParkingList'
import RunScan from './RunScan'
import SettingsPanel from './SettingsPanel'
import Search from './Search'
import Upload from './Upload'
import WorkInProgress from './WorkInProgress'
export default {
name: 'leaflet-sidebar',
components: { AirportEdit, ArcEdit, CheckPanel, NodeEdit, ParkingEdit, SettingsPanel, RunScan, FileSelect, Search },
components: { Help, AirportEdit, ArcEdit, ArcEditMulti, CheckPanel, NodeEdit, ParkingEdit, ParkingGroupEdit, RunScan, TowerEdit, ThresholdEdit, FileSelect, SettingsPanel, Search, Upload, WorkInProgress },
props: [],
created () {
window.addEventListener('keydown', this.doCommand)
},
destroyed () {
window.removeEventListener('keydown', this.doCommand)
},
mounted () {
this.add()
},
@@ -76,18 +110,41 @@
this.remove()
},
data () {
return {
return { uploadVisible: false
}
},
methods: {
doCommand (e) {
let cmd = String.fromCharCode(e.keyCode).toLowerCase()
if (e.keyCode === 46 /** DEL */ && e.target.type !== 'text') {
this.$parent.$parent.$refs.editLayer.deleteFeature()
}
console.log(cmd)
},
deferredMountedTo (parent) {
this.sidebar = L.control.sidebar({
autopan: false, // whether to maintain the centered map point when opening the sidebar
closeButton: true, // whether t add a close button to the panes
closeButton: true, // whether to add a close button to the panes
container: 'sidebar', // the DOM container or #ID of a predefined sidebar container that should be used
position: 'left' // left or right
})
parent.addControl(this.sidebar)
this.$store.subscribe((mutation, state) => {
switch (mutation.type) {
case 'SET_EDIT_AIRPORT':
case 'SET_EDIT_PARKING':
case 'SET_EDIT_NODE':
case 'SET_EDIT_RUNWAY':
case 'SET_EDIT_ARC':
this.sidebar.open('edit')
break
case 'CHECK_RESULTS':
this.sidebar.open('check')
break
default:
break
}
})
},
remove () {
if (this.sidebar) {
@@ -99,14 +156,35 @@
this.deferredMountedTo(this.$parent.mapObject)
}
},
setEditing (editing) {
this.$refs.parkingGroupEdit.setEditing(editing)
this.$refs.airportEdit.setEditing(editing)
},
setData (data) {
if (data.length > 0) {
this.sidebar.open('edit')
this.$refs.parkingGroupEdit.setData(data)
}
},
scan () {
}
},
computed: {
results: function () {
return this.$store.state.Check.results.length > 0
}
}
}
</script>
<style scoped lang="scss">
</style>
<style>
/* global styles */
.el-popover--plain {
padding: 10px 10px;
}
.el-popover__title {
display: none;
}
</style>

View File

@@ -1,34 +1,60 @@
<template>
<div width="100%" v-if="node">
<div>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Is on runway :</span>
</el-col>
<el-col :span="15">
<el-switch v-model="isOnRunway"></el-switch>
</el-col>
<div width="100%" v-if="node">
<div>
<el-row>
<el-col :span="7">
<span class="label">Coordinates :</span>
</el-col>
<el-col :span="17">
<el-input placeholder="Please input" v-model="coordinates" :disabled="!editing"
@focus="coordFocussed = true"
@blur="coordFocussed = false"></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Is on runway :</span>
</el-col>
<el-col :span="15">
<el-switch v-model="isOnRunway" :disabled="!editing" @focus="runwayFocussed = true"
@blur="runwayFocussed = false"></el-switch>
</el-col>
</el-row>
<el-row>
<el-col :span="9">
<span class="label">Holdpoint Type :</span>
</el-col>
<el-col :span="15">
<el-select v-model="holdPointType" placeholder="Select" :disabled="!editing" >
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
:disabled="type.disabled"
></el-option>
</el-select>
</el-col>
</el-row>
<el-row>
<el-popover
placement="top-start"
title="Goto"
width="200"
trigger="hover"
content="Weld/Link nodes"
>
<el-button @click="link" slot="reference"><i class="fas fa-link"></i></el-button>
</el-popover>
</el-row>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Holdpoint Type :</span>
</el-col>
<el-col :span="15">
<el-select v-model="holdPointType" placeholder="Select">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
></el-option>
</el-select>
</el-col>
</el-row>
</div>
</div>
</div>
</template>
<script lang="js">
/* eslint-disable */
const Coordinates = require('coordinate-parser');
export default {
/*
methods: {
@@ -37,14 +63,43 @@
}
},
*/
data () {
return {
coordFocussed: false, runwayFocussed: false, holdFocussed: false
}
},
methods: {
link: function () {
this.$parent.$parent.$parent.$refs.editLayer.link(this.$store.state.Editable.index)
}
},
computed: {
editing: {
get: function () {
return this.$parent.$parent.$parent.$refs.editLayer.editing
}
},
options: function () {
return [{value: 'none', label: 'none'}, {value: 'PushBack', label: 'PushBack'}, {value: 'normal', label: 'normal'}, {value: 'CAT II/III', label: 'CAT II/III'}]
return [{value: 'none', label: 'none', disabled: false }, {value: 'PushBack', label: 'PushBack'}, {value: 'normal', label: 'normal'}, {value: 'CAT II/III', label: 'CAT II/III'}]
},
node: function () {
return this.$store.state.Editable.type === 'node'
return this.$store.state.Editable.type === 'node' || this.$store.state.Editable.type === 'runway'
},
// {index: 39, lat: "N58 27.343", lon: "W03 5.153", isOnRunway: 0, holdPointType: "none"}
coordinates: {
// getter
get: function () {
if(this.$store.state.Editable.index!==undefined) {
return this.$store.state.Editable.data.node.coords;
}
},
// setter
set: function (newValue) {
if (this.coordFocussed) {
this.$store.commit('SET_EDIT_NODE_COORDS', newValue)
}
}
},
isOnRunway: {
// getter
get: function () {
@@ -52,7 +107,9 @@
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_ISONRUNWAY', newValue ? 1 : 0)
if(!this.runwayFocussed) {
this.$store.commit('SET_EDIT_ISONRUNWAY', newValue ? 1 : 0)
}
}
},
holdPointType: {

View File

@@ -12,72 +12,381 @@
type: "gate"
-->
<el-row>
<el-col :span="7">
<span class="demo-input-label">Name :</span>
<el-col :span="4">
<span class="label">Name :</span>
</el-col>
<el-col :span="15">
<el-input placeholder="Please input" v-model="name"></el-input>
<el-col :span="8">
<el-input
placeholder="Name"
v-model="name"
:disabled="!editing"
></el-input>
</el-col>
<el-col :span="5">
<span class="label">Number :</span>
</el-col>
<el-col :span="7">
<el-input
placeholder="Number"
v-model="number"
:disabled="!editing"
></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Size :</span>
<span class="label">Size :</span>
</el-col>
<el-col :span="15">
<el-radio-group v-model="wingspan">
<el-tooltip content="PIPER PA-31/CESSNA 404 Titan" placement="top" effect="light">
<el-radio :label="15">A</el-radio>
<el-col :span="17">
<!--
* Cat Models FG Radii N2M Radii
* B Small Regionals ERJ CRJ ATR 14 6
* C A319 A320 A321 B737 18 10
* D B757, B767 26 15
* E B777 B787 A330 A340 A360 33 24
* F A380 40 24
-->
<el-radio-group v-model="wingspan" :disabled="!editing">
<el-tooltip
content="PIPER PA-31/CESSNA 404 Titan"
placement="top"
effect="light"
>
<el-radio :label="15">A (7.5)</el-radio>
</el-tooltip>
<el-tooltip content="BOMBARDIER Regional Jet CRJ-200/DE HAVILLAND CANADA DHC-6" placement="top" effect="light">
<el-radio :label="24">B</el-radio>
<el-tooltip
content="Beech 200 / Cessna 425"
placement="top"
effect="light"
>
<el-radio :label="20">- (10)</el-radio>
</el-tooltip>
<el-tooltip content="BOEING 737-700/AIRBUS A-320/EMBRAER ERJ 190-100" placement="top" effect="light">
<el-radio :label="36">C</el-radio>
<el-tooltip
content="BOMBARDIER Regional Jet CRJ-200/DE HAVILLAND CANADA DHC-6"
placement="top"
effect="light"
>
<el-radio :label="28">B (14)</el-radio>
</el-tooltip>
<el-tooltip content="B767 Series/AIRBUS A-310" placement="top" effect="light">
<el-radio :label="52">D</el-radio>
<el-tooltip
content="BOEING 737-700/AIRBUS A-320/EMBRAER ERJ 190-100"
placement="top"
effect="light"
>
<el-radio :label="36">C (18)</el-radio>
</el-tooltip>
<el-tooltip content="B777 Series/B787 Series/A330 Family" placement="top" effect="light">
<el-radio :label="65">E</el-radio>
<el-tooltip
content="B767 Series/AIRBUS A-310"
placement="top"
effect="light"
>
<el-radio :label="52">D (26)</el-radio>
</el-tooltip>
<el-tooltip content="BOEING 747-8/AIRBUS A-380-800" placement="top" effect="light">
<el-radio :label="80">F</el-radio>
<el-tooltip
content="B777 Series/B787 Series/A330 Family"
placement="top"
effect="light"
>
<el-radio :label="66">E (33)</el-radio>
</el-tooltip>
<el-tooltip
content="BOEING 747-8/AIRBUS A-380-800"
placement="top"
effect="light"
>
<el-radio :label="80">F (40)</el-radio>
</el-tooltip>
</el-radio-group>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Aircraft :</span>
<span class="label">Aircraft :</span>
</el-col>
<el-col :span="15">
{{type}}
<el-col :span="17">{{ type }}</el-col>
</el-row>
<el-row v-if="editing">
<el-col :span="7">
<span class="label">Calculate :</span>
</el-col>
<el-col :span="17">
<el-radio-group v-model="calculate" size="small">
<el-radio-button label="Nose Wheel"></el-radio-button>
<el-radio-button label="Center"></el-radio-button>
</el-radio-group>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="demo-input-label">Parking Type :</span>
</el-col>
<el-col :span="15">
<el-select v-model="parking_type" placeholder="Select">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
></el-option>
</el-select>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Coordinates :</span>
</el-col>
<el-col :span="17">
<el-popover
placement="bottom-start"
title="E-Mail"
width="200"
trigger="hover"
content="D.DDD, DMS, DM supported"
:disabled="!editing || calculate === 'Center'"
>
<el-input
placeholder="Please input"
v-model="coordinates"
slot="reference"
:disabled="!editing || calculate === 'Center'"
@focus="coordFocussed = true"
@blur="coordFocussed = false"
></el-input>
</el-popover>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Nosewheel Coordinates :</span>
</el-col>
<el-col :span="17">
<el-popover
placement="top-start"
title="E-Mail"
width="200"
trigger="hover"
content="D.DDD, DMS, DM supported"
:disabled="!editing || calculate === 'Nose Wheel'"
>
<el-input
placeholder="Please input"
v-model="noseCoordinates"
slot="reference"
:disabled="!editing || calculate === 'Nose Wheel'"
@focus="noseCoordFocussed = true"
@blur="noseCoordFocussed = false"
></el-input>
</el-popover>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Heading :</span>
</el-col>
<el-col :span="13">
<el-input-number
v-model="heading"
:min="-361"
:max="720"
:step="0.1"
:precision="1"
:disabled="!editing || calculate === 'Heading'"
@change="headingChange"
></el-input-number>
</el-col>
<el-col :span="4">
<el-button @click="rotate" class="button">
<i class="fas fa-ruler-combined"></i>
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Parking Type :</span>
</el-col>
<el-col :span="17">
<el-select
v-model="parking_type"
placeholder="Select"
:disabled="!editing"
>
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
:disabled="type.disabled"
></el-option>
</el-select>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Airline :</span>
</el-col>
<el-col :span="17">
<el-select
v-model="airlineCodes"
multiple
placeholder="Select"
:disabled="!editing"
>
<el-option
v-for="item in airlines"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Pushback Route End :</span>
</el-col>
<el-col :span="17">{{ pushbackEnd }}</el-col>
</el-row>
</div>
</template>
<script lang="js">
/* eslint-disable */
const convert = require('geo-coordinates-parser');
const turf = require('@turf/turf');
const turfOptions = { units: 'kilometers' };
export default {
mounted() {
this.$store.watch(
function (state) {
return state.Editable.data.parking;
},
() => { this.editedParking() }
,
{
deep: true //add this if u need to watch object properties change etc.
}
);
},
methods: {
editedParking() {
this.externalChange = true
this.heading = Number(this.$store.state.Editable.data.parking.heading);
this.externalChange = false
},
rotate() {
var heading = this.$store.state.Editable.data.parking.heading + 90;
while (heading>=360) {
heading -= 360
}
while (heading<0) {
heading += 360
}
this.headingChange(heading);
},
headingChange( newValue ) {
while (newValue>=360) {
newValue -= 360
}
while (newValue<0) {
newValue += 360
}
if ( ( Math.abs( this.$store.state.Editable.data.parking.heading - newValue ) >= 0 && Math.abs( this.$store.state.Editable.data.parking.heading - newValue ) <= 0.1 )
|| !this.externalChange) {
if (Number(this.$store.state.Editable.data.parking.heading) !== newValue) {
this.$store.commit('SET_EDIT_PARKING_HEADING', newValue)
}
if(this.calculate === 'Center') {
// we change center
const noseWheelLatLng = convert(this.$store.state.Editable.data.parking.nosecoords);
const parkingSize = this.validRadii.indexOf(this.$store.state.Editable.data.parking.radius);
if (parkingSize>=0) {
var reverseHeading = this.normalizeAngle(this.$store.state.Editable.data.parking.heading+180);
var newCenter = turf.destination(this.latToTurf(noseWheelLatLng), this.validN2M[parkingSize]/1000, reverseHeading, turfOptions);
this.$store.commit('SET_EDIT_PARKING_COORDS', this.turfToLatLng(newCenter));
}
}
}
},
show (idx) {
this.$parent.$parent.$parent.$refs.editLayer.show(idx)
},
normalizeAngle( angle ) {
if(angle >= 180) {
return angle - 360;
}
if(angle <= -180) {
return angle + 360;
}
return angle;
},
latToTurf (turfPoint) {
return [turfPoint.decimalLongitude, turfPoint.decimalLatitude];
},
turfToLatLng (turfPoint) {
return '' + turfPoint.geometry.coordinates[1].toFixed(6) + ',' + turfPoint.geometry.coordinates[0].toFixed(6);
},
beautify (coordString) {
var a = coordString.split(' ');
if (a.length === 2) {
return '' + Number(a[0]).toFixed(6) + ' ' + Number(a[1]).toFixed(6);
} else {
return coordString;
}
},
calcWheel () {
if(this.calculate === 'Nose Wheel') {
// we change nosewheel
const centerLatLng = convert(this.beautify(this.$store.state.Editable.data.parking.coords));
const parkingSize = this.validRadii.indexOf(this.$store.state.Editable.data.parking.radius);
if (parkingSize>=0) {
var newNoseWheel = turf.destination(this.latToTurf(centerLatLng), this.validN2M[parkingSize]/1000, this.$store.state.Editable.data.parking.heading, turfOptions);
this.$store.commit('SET_EDIT_PARKING_NOSE_COORDS', this.turfToLatLng(newNoseWheel));
}
}
},
calcCenter () {
if (this.calculate === 'Center') {
// we change center
const noseWheelLatLng = convert(this.beautify(this.$store.state.Editable.data.parking.nosecoords));
const parkingSize = this.validRadii.indexOf(this.$store.state.Editable.data.parking.radius);
if (parkingSize>=0) {
var newCenter = turf.destination(this.latToTurf(noseWheelLatLng), this.validN2M[parkingSize]/1000, this.$store.state.Editable.data.parking.heading - 180, turfOptions);
this.$store.commit('SET_EDIT_PARKING_COORDS', this.turfToLatLng(newCenter));
}
}
}
},
data () { return {rotateFocussed: false, externalChange: false, coordFocussed: false, noseCoordFocussed: false, calculateState: 'Nose Wheel', noseWheel: '', validRadii: [7.5, 10, 14, 18, 26, 33, 40], validN2M: [5, 5, 6, 10, 15, 24, 24], heading: 0 }},
computed: {
editing: {
get: function () {
return this.$parent.$parent.$parent.$refs.editLayer.editing
}
},
parking: function () {
return this.$store.state.Editable.type === 'parking'
},
airlines: function () {
var airlineCodes = [];
var storedairlineCodes = this.$store.state.Airports.currentAirport.airlines;
// return [{value: 'bi-directional', label: 'bi-directional'},
// {value: 'forward', label: 'forward'},
// {value: 'backward', label: 'backward'}
// ]
if(storedairlineCodes) {
storedairlineCodes.forEach(element => {
airlineCodes.push({value: element, label: element});
});
}
return airlineCodes
},
airlineCodes: {
// getter
get: function () {
var codes = this.$store.state.Editable.data.parking.airlineCodes
if (Array.isArray(codes)) {
return codes
} else if (codes !== undefined && typeof codes === 'string') {
return codes.split(',')
} else {
return []
}
return
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_PARKING_AIRLINES', newValue)
}
},
name: {
// getter
get: function () {
@@ -88,6 +397,74 @@
this.$store.commit('SET_EDIT_PARKING_NAME', newValue)
}
},
number: {
// getter
get: function () {
return this.$store.state.Editable.data.parking.number
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_PARKING_NUMBER', newValue)
}
},
calculate: {
get: function () {
return this.calculateState;
},
set: function (newValue) {
this.calculateState = newValue;
if (newValue==='Center') {
this.calcCenter();
} else {
this.calcWheel();
}
}
},
coordinates: {
// getter
get: function () {
if(this.$store.state.Editable.index!==undefined) {
return this.$store.state.Editable.data.parking.coords;
}
},
// setter
set: function (newValue) {
if (newValue==='unknown') {
}
if( newValue.match(/,/g) !== null && newValue.match(/,/g).length === 3) {
newValue = newValue.replace(', ', ' ').replace(/,/g, '.').replace(' ', ', ');
}
if (this.coordFocussed) {
this.$store.commit('SET_EDIT_PARKING_COORDS', newValue)
}
this.calcWheel();
}
},
noseCoordinates: {
// getter
get: function () {
if(this.$store.state.Editable.index!==undefined) {
if(!this.$store.state.Editable.data.parking.nosecoords && this.calculate === 'Nose Wheel') {
this.calcWheel();
}
return this.$store.state.Editable.data.parking.nosecoords;
}
},
// setter
set: function (newValue) {
if (newValue==='unknown') {
}
if( newValue.match(/,/g) !== null && newValue.match(/,/g).length === 3) {
newValue = newValue.replace(', ', ' ').replace(/,/g, '.').replace(' ', ', ');
}
if (this.noseCoordFocussed) {
this.$store.commit('SET_EDIT_PARKING_NOSE_COORDS', newValue);
}
this.calcCenter();
}
},
wingspan: {
// getter
get: function () {
@@ -98,17 +475,31 @@
this.$store.commit('SET_EDIT_PARKING_RADIUS', newValue / 2)
}
},
pushbackEnd: function () {
return this.$parent.$parent.$parent.$refs.editLayer.findRouteToPushback(this.$store.state.Editable.index)
},
type: function () {
/**
* Cat Models FG Radii N2M Radii
* B Small Regionals ERJ CRJ ATR 14 6
* C A319 A320 A321 B737 18 10
* D B757, B767 26 15
* E B777 B787 A330 A340 A360 33 24
* F A380 40 24
*/
switch (this.$store.state.Editable.data.parking.radius * 2) {
case 15:
return 'PIPER PA-31/CESSNA 404 Titan'
case 24:
return 'Piper J-3 Cub/Cessna 172'
case 20:
return 'Beech 200/Cessna 425'
case 28:
return 'BOMBARDIER Regional Jet CRJ-200/DE HAVILLAND CANADA DHC-6'
case 36:
return 'BOEING 737-700/AIRBUS A-320/EMBRAER ERJ 190-100'
case 52:
return 'B767 Series/AIRBUS A-310'
case 65:
case 66:
return 'B777 Series/B787 Series/A330 Family'
case 80:
return 'BOEING 747-8/AIRBUS A-380-800'
@@ -119,12 +510,12 @@
// ga (general aviation), cargo (cargo), gate (commercial passenger traffic),
// mil-fighter (military fighter), mil-cargo (military transport)
options: function () {
return [{value: 'none', label: 'none'},
return [{value: 'none', label: 'none', disabled: true},
{value: 'ga', label: 'general aviation'},
{value: 'cargo', label: 'cargo'},
{value: 'gate', label: 'commercial passenger traffic'},
{value: 'mil-fighter', label: 'military fighter'},
{value: 'mil-cargo', label: 'commercial passenger traffic'}
{value: 'mil-cargo', label: 'military cargo'}
]
},
parking_type: {
@@ -143,3 +534,24 @@
}
}
</script>
<style>
.el-row {
margin: 1px;
}
.el-col {
padding-left: 2pt;
}
.label {
display: flex;
justify-content: left;
align-items: center;
font-weight: bold;
}
.el-popover--plain {
padding: 10px 10px;
}
.el-popover__title {
display: none;
}
</style>

View File

@@ -0,0 +1,320 @@
<template>
<div v-if="parking">
<!--
airlineCodes: 0
heading: 341.34
index: 13
lat: "N59 52.610885"
lon: "W1 17.855144"
name: "Western_Apron_Hanger"
pushBackRoute: 27
radius: 18
type: "gate"
-->
<!--
<el-row>
<el-col :span="4">
<span class="label">Name :</span>
</el-col>
<el-col :span="8">
<el-input placeholder="Name" v-model="name" :disabled="!editing"></el-input>
</el-col>
</el-row>
-->
<el-row>
<el-col :span="7">
<span class="label">Size :</span>
</el-col>
<el-col :span="17">
<!--
* Cat Models FG Radii N2M Radii
* B Small Regionals ERJ CRJ ATR 14 6
* C A319 A320 A321 B737 18 10
* D B757, B767 26 15
* E B777 B787 A330 A340 A360 33 24
* F A380 40 24
-->
<el-radio-group v-model="wingspan" :disabled="!editing" @change="wingspanChange">
<el-tooltip content="PIPER PA-31/CESSNA 404 Titan" placement="top" effect="light">
<el-radio :label="15">A</el-radio>
</el-tooltip>
<el-tooltip
content="BOMBARDIER Regional Jet CRJ-200/DE HAVILLAND CANADA DHC-6"
placement="top"
effect="light"
>
<el-radio :label="28">B</el-radio>
</el-tooltip>
<el-tooltip
content="BOEING 737-700/AIRBUS A-320/EMBRAER ERJ 190-100"
placement="top"
effect="light"
>
<el-radio :label="36">C</el-radio>
</el-tooltip>
<el-tooltip content="B767 Series/AIRBUS A-310" placement="top" effect="light">
<el-radio :label="52">D</el-radio>
</el-tooltip>
<el-tooltip content="B777 Series/B787 Series/A330 Family" placement="top" effect="light">
<el-radio :label="66">E</el-radio>
</el-tooltip>
<el-tooltip content="BOEING 747-8/AIRBUS A-380-800" placement="top" effect="light">
<el-radio :label="80">F</el-radio>
</el-tooltip>
</el-radio-group>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Aircraft :</span>
</el-col>
<el-col :span="17" class="value">{{type}}</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Heading :</span>
</el-col>
<el-col :span="13">
<el-input-number
v-model="avgHeading" @change="headingChange"
:min="-361"
:max="720"
:step="0.1"
:precision="1"
:disabled="!editing"
></el-input-number>
</el-col>
<el-col :span="4">
<el-button @click="rotate" class="button">
<i class="fas fa-ruler-combined"></i>
</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Parking Type :</span>
</el-col>
<el-col :span="17">
<el-select v-model="parking_type" @change="typeChange" placeholder="Select" :disabled="!editing">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
:disabled="type.disabled"
></el-option>
</el-select>
</el-col>
</el-row>
<!--
<el-row>
<el-col :span="7">
<span class="label">Airline :</span>
</el-col>
<el-col :span="17">
<el-select v-model="airlineCodes" multiple placeholder="Select" :disabled="!editing">
<el-option
v-for="item in airlines"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-col>
</el-row>
-->
</div>
</template>
<script lang="js">
/* eslint-disable */
import Vue from 'vue'
const convert = require('geo-coordinates-parser');
const Coordinates = require('coordinate-parser');
export default {
data () {
return {
data: Object, avgHeading: 5, editing: Boolean, wingspan: 0, parking_type: ''
}
},
methods: {
rotate() {
this.avgHeading = this.avgHeading + 90;
while (this.avgHeading>=360) {
this.avgHeading -= 360
}
while (this.avgHeading<0) {
this.avgHeading += 360
}
this.headingChange(this.avgHeading);
},
show (idx) {
this.$parent.$parent.$parent.$refs.editLayer.show(idx)
},
setData (data) {
this.data = data;
this.setAvgHeading();
this.setAvgType();
},
setEditing(editing) {
this.editing = editing
},
setAvgHeading() {
if( this.data === null || this.data === undefined) {
return 0
}
this.avgHeading = Number( this.data.reduce(function (r, parking) {
r.sum = r.sum + parking.options.attributes.heading;
r.avg = r.sum / ++r.count;
return r;
}, { sum: 0, count: 0, avg: 0 }).avg);
},
setAvgType() {
if( this.data === null || this.data === undefined) {
return 0
}
var types = this.data.map(parking => parking.options.attributes.type).filter((v, i, a) => a.indexOf(v) === i);
if (types.length == 1) {
this.parking_type = types[0];
} else {
this.parking_type = '';
}
},
wingspanChange( newValue ) {
if ( newValue ) {
this.$emit('editParking', {type: 'parking-group-wingspan', wingspan: newValue} )
}
},
headingChange( newValue ) {
while (newValue>=360) {
newValue -= 360
}
while (newValue<0) {
newValue += 360
}
if ( newValue ) {
this.$emit('editParking', {type: 'parking-group-angle', angle: newValue} )
}
},
typeChange( newValue ) {
if ( newValue ) {
this.$emit('editParking', {type: 'parking-group-type', parking_type: newValue} )
}
}
},
computed: {
parking: function () {
return this.data !== null && this.$store.state.Editable.type === 'parking-group'
},
airlines: function () {
var airlineCodes = [];
var storedairlineCodes = this.$store.state.Airports.currentAirport.airlines;
// return [{value: 'bi-directional', label: 'bi-directional'},
// {value: 'forward', label: 'forward'},
// {value: 'backward', label: 'backward'}
// ]
if(storedairlineCodes) {
storedairlineCodes.forEach(element => {
airlineCodes.push({value: element, label: element});
});
}
return airlineCodes
},
airlineCodes: {
// getter
get: function () {
},
// setter
set: function (newValue) {
}
},
name: {
// getter
get: function () {
},
// setter
set: function (newValue) {
}
},
number: {
// getter
get: function () {
},
// setter
set: function (newValue) {
}
},
pushbackEnd: function () {
return this.$parent.$parent.$parent.$refs.editLayer.findRouteToPushback(this.$store.state.Editable.index)
},
type: function () {
/**
* Cat Models FG Radii N2M Radii
* B Small Regionals ERJ CRJ ATR 14 6
* C A319 A320 A321 B737 18 10
* D B757, B767 26 15
* E B777 B787 A330 A340 A360 33 24
* F A380 40 24
*/
switch (this.wingspan) {
case 15:
return 'PIPER PA-31/CESSNA 404 Titan'
case 28:
return 'BOMBARDIER Regional Jet CRJ-200/DE HAVILLAND CANADA DHC-6'
case 36:
return 'BOEING 737-700/AIRBUS A-320/EMBRAER ERJ 190-100'
case 52:
return 'B767 Series/AIRBUS A-310'
case 66:
return 'B777 Series/B787 Series/A330 Family'
case 80:
return 'BOEING 747-8/AIRBUS A-380-800'
default:
return 'Unknown radius : ' + this.wingspan
}
},
// ga (general aviation), cargo (cargo), gate (commercial passenger traffic),
// mil-fighter (military fighter), mil-cargo (military transport)
options: function () {
return [{value: 'none', label: 'none', disabled: true},
{value: 'ga', label: 'general aviation'},
{value: 'cargo', label: 'cargo'},
{value: 'gate', label: 'commercial passenger traffic'},
{value: 'mil-fighter', label: 'military fighter'},
{value: 'mil-cargo', label: 'military cargo'}
]
}
}
}
</script>
<style>
.el-row {
margin: 1px;
}
.el-col {
padding-left: 2pt;
}
.label {
display: flex;
justify-content: left;
align-items: center;
font-weight: bold;
}
.value {
display: flex;
justify-content: left;
align-items: center;
}
.el-popover--plain {
padding: 10px 10px;
}
.el-popover__title {
display: none;
}
</style>

View File

@@ -0,0 +1,156 @@
<!--
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div>
<el-link v-if="!editing" type="primary" @click="show(parking.index)">{{parking.name}} {{number}} {{parking.type}} {{type}}</el-link>
<el-input @focus="show(parking.index)" v-if="editing" placeholder="Name" v-model="name" class="wide"></el-input>
<el-input @focus="show(parking.index)" v-if="editing" placeholder="Number" v-model="number" class="narrow"></el-input>
<el-select @focus="show(parking.index)" v-if="editing" v-model="parking_type" placeholder="Select">
<el-option
v-for="type in options"
:key="type.value"
:label="type.label"
:value="type.value"
:disabled="type.disabled"
></el-option>
</el-select>
</div>
</template>
<script lang="js">
export default {
name: 'parking-item',
props: {parking: Object},
mounted () {
},
data () {
return { editLayer: null
}
},
methods: {
show (idx) {
if (this.editLayer === null) {
this.initLayer()
}
if (this.editLayer) {
return this.editLayer.show(idx)
}
},
initLayer () {
var parent = this.$parent
while (parent && !parent.$refs.editLayer) {
parent = parent.$parent
}
if (parent) {
this.editLayer = parent.$refs.editLayer
}
}
},
computed: {
options: function () {
return [{value: 'none', label: 'none', disabled: true},
{value: 'ga', label: 'general aviation'},
{value: 'cargo', label: 'cargo'},
{value: 'gate', label: 'commercial passenger traffic'},
{value: 'mil-fighter', label: 'military fighter'},
{value: 'mil-cargo', label: 'military cargo'}
]
},
editing: function () {
if (this.editLayer === null) {
this.initLayer()
}
return this.editLayer.editing
},
name: {
// getter
get: function () {
return this.parking.name
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_PARKING_ITEM_NAME', [this.parking.index, newValue])
}
},
number: {
// getter
get: function () {
if (this.parking.number && this.parking.number !== 'undefined') {
return this.parking.number
} else {
return ''
}
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_PARKING_ITEM_NUMBER', [this.parking.index, newValue])
}
},
parking_type: {
// getter
get: function () {
if (this.parking.type === undefined) {
return 'none'
}
return this.parking.type
},
// setter
set: function (newValue) {
this.$store.commit('SET_EDIT_PARKING_ITEM_TYPE', [this.parking.index, newValue])
}
},
type: function () {
/**
* Cat Models FG Radii N2M Radii
* B Small Regionals ERJ CRJ ATR 14 6
* C A319 A320 A321 B737 18 10
* D B757, B767 26 15
* E B777 B787 A330 A340 A360 33 24
* F A380 40 24
*/
switch (this.parking.radius * 2) {
case 15:
return 'Piper J-3 Cub/Cessna 172'
case 20:
return 'Beech 200/Cessna 425'
case 28:
return 'BOMBARDIER Regional Jet CRJ-200/DE HAVILLAND CANADA DHC-6'
case 36:
return 'BOEING 737-700/AIRBUS A-320/EMBRAER ERJ 190-100'
case 52:
return 'B767 Series/AIRBUS A-310'
case 66:
return 'B777 Series/B787 Series/A330 Family'
case 80:
return 'BOEING 747-8/AIRBUS A-380-800'
default:
return 'Unknown radius : ' + this.parking.radius
}
}
}
}
</script>
<style scoped lang="scss">
div {
display: flex;
justify-content: space-between;
}
div.wide {
width: 40%;
}
div.narrow {
width: 20%;
}
</style>

View File

@@ -0,0 +1,53 @@
<template>
<div>
<!--
<h1 class="leaflet-sidebar-header">
Parking List
<div class="leaflet-sidebar-close">
<i class="fa fa-caret-left"></i>
</div>
</h1>
-->
<el-container direction="vertical">
<div v-for="p in parkings" v-bind:key="p.index" class="row">
<ParkingItem :parking="p"></ParkingItem>
</div>
</el-container>
</div>
</template>
<script lang="js">
import ParkingItem from './ParkingItem'
export default {
name: 'parking-list',
components: {ParkingItem},
props: [],
data () {
return {
}
},
computed: {
parkings: {
// getter
get: function () {
if (this.$store.state.Parkings.items !== undefined) {
return this.$store.state.Parkings.items
}
},
// setter
set: function (newValue) {
console.log(newValue)
}
}
}
}
</script>
<style>
div.row.div {
display: flex;
justify-content: space-between;
}
</style>

View File

@@ -1,14 +1,11 @@
<template>
<section class="edit-layer">
<h1>edit-layer Component</h1>
</section>
</template>
<template></template>
<script lang="js">
import { LMap, LMarker } from 'vue2-leaflet'
import L from 'leaflet'
import LEdit from 'leaflet-editable/src/Leaflet.Editable.js'
import {readPavement} from '../loaders/pavement_loader'
import * as turf from '@turf/turf'
export default {
name: 'edit-layer',
@@ -25,20 +22,33 @@
this.remove()
},
data () {
return {
}
return {}
},
methods: {
getLayer () {
return this.pavement
},
load (icao) {
// Callback for add
readPavement(this.$store.state.Settings.settings.flightgearDirectory_apt, icao, this.read)
},
// Callback called when pavement read
read (layer) {
this.groundnet = layer
if (this.groundnet) {
this.groundnet.addTo(this.$parent.mapObject)
this.pavement = layer
if (this.pavement) {
this.pavement.on('add', this.onAdd)
this.pavement.addTo(this.$parent.mapObject)
this.visible = true
} else {
this.$message({
type: 'Error',
showClose: true,
message: `Couldn't load pavement from ${this.$store.state.Settings.settings.flightgearDirectory_apt}`
})
}
this.groundnet.eachLayer(l => {
},
onAdd () {
this.pavement.eachLayer(l => {
if (l) {
l.bringToBack()
}
@@ -57,7 +67,7 @@
this.layerGroup = L.layerGroup()
},
remove () {
if (this.layerGroup) {
if (this.pavement) {
this.$parent.removeLayer(this.layerGroup)
}
},
@@ -66,12 +76,35 @@
this.deferredMountedTo(this.$parent.mapObject)
}
},
isOnRunway (latlng) {
var ret = false
this.pavement.eachLayer(l => {
if (l instanceof L.RunwayPolygon) {
console.debug(l)
if (turf.booleanContains(l.turfyRunway, this.latToTurf(latlng))) {
ret = true
}
}
})
return ret
},
latToTurf (turfPoint) {
return turf.point([turfPoint.lng, turfPoint.lat])
},
setVisible (visible) {
if (this.layerGroup !== undefined) {
if (visible) {
this.layerGroup.addTo(this.$parent.mapObject)
} else {
this.layerGroup.removeFrom(this.$parent.mapObject)
if (this.pavement !== undefined) {
if (visible !== this.visible) {
if (visible) {
this.pavement.addTo(this.$parent.mapObject)
this.pavement.eachLayer(l => {
if (l) {
l.bringToBack()
}
})
} else {
this.pavement.removeFrom(this.$parent.mapObject)
}
this.visible = visible
}
}
}

View File

@@ -7,6 +7,7 @@
</div>
</h1>
<el-container direction="vertical">
<span v-if="scanning" class="center">{{scanName}}</span>
<el-progress :percentage="Number(((progress / max)*100).toPrecision(3))" v-if="max>0"></el-progress>
<!--<el-progress :percentage="progress / max"></el-progress>-->
<!--{{max}}&nbsp;{{progress}}-->
@@ -45,7 +46,8 @@
progress: 0,
scanning: false,
polling: null,
worker: null
worker: null,
scanName: 'unknown'
}
},
methods: {
@@ -64,6 +66,7 @@
scanAPT () {
try {
this.scanning = true
this.scanName = 'Scanning APT'
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
: `file://${process.resourcesPath}/workers/worker.js`
@@ -71,13 +74,23 @@
const worker = new Worker(winURL)
console.log(fileUrl('src/renderer/utils/worker.js'))
worker.onerror = function (e) {
console.error(e)
worker.terminate()
worker.scanning = false
worker.view.progress = 0
worker.view.max = 0
worker.view.worker = null
clearInterval(this.polling)
e.preventDefault()
}
worker.scanning = this.scanning
worker.max = this.max
worker.progress = 0
// var worker = new Worker(fileUrl('src/renderer/utils/worker.js'))
this.worker = worker
worker.postMessage(['scanapt', this.$store.state.Settings.settings.flightgearDirectory_apt])
worker.postMessage(['scanapt', this.$store.state.Settings.settings.flightgearDirectory_apt, this.$store.state.Settings.settings.scanLogging])
this.pollData()
// the reply
var store = this.$store
@@ -110,6 +123,7 @@
scanGroundnets () {
try {
this.scanning = true
this.scanName = 'Scanning Groundnets'
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
: `file://${process.resourcesPath}/workers/worker.js`
@@ -117,13 +131,23 @@
const worker = new Worker(winURL)
console.log(fileUrl('src/renderer/utils/worker.js'))
worker.onerror = function (e) {
console.error(e)
worker.terminate()
worker.scanning = false
worker.view.progress = 0
worker.view.max = 0
worker.view.worker = null
clearInterval(this.polling)
e.preventDefault()
}
worker.scanning = this.scanning
worker.max = this.max
worker.progress = 0
this.worker = worker
worker.postMessage(['scan', this.$store.state.Settings.settings.airportsDirectory])
worker.postMessage(['scan', this.$store.state.Settings.settings.airportsDirectory, this.$store.state.Settings.settings.scanLogging])
this.pollData()
// the reply
var store = this.$store
@@ -144,7 +168,6 @@
this.max = e.data[1]
}
if (e.data[0] === 'progress') {
this.scanning = false
this.progress += e.data[1]
}
}
@@ -158,6 +181,7 @@
// let flightgearDirectory = this.$store.state.settings.flightgearDirectory
try {
this.scanning = true
this.scanName = 'Scanning Traffic'
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
: `file://${process.resourcesPath}/workers/worker.js`
@@ -165,12 +189,22 @@
const worker = new Worker(winURL)
console.log(fileUrl('src/renderer/utils/worker.js'))
worker.onerror = function (e) {
console.error(e)
worker.terminate()
worker.scanning = false
worker.view.progress = 0
worker.view.max = 0
worker.view.worker = null
clearInterval(this.polling)
e.preventDefault()
}
this.scanning = true
worker.scanning = this.scanning
worker.max = this.max
worker.progress = this.progress
this.worker = worker
worker.postMessage(['scanai', this.$store.state.Settings.settings.flightgearDirectory_traffic])
worker.postMessage(['scanai', this.$store.state.Settings.settings.flightgearDirectory_traffic, this.$store.state.Settings.settings.scanLogging])
this.pollData()
// the reply
var store = this.$store
@@ -182,9 +216,12 @@
this.scanning = false
console.log('DONE')
store.dispatch('getAirports')
worker.view.max = 0
worker.view.scanning = false
worker.terminate()
if (worker.view.max !== undefined) {
worker.view.max = 0
}
if (worker.view.scanning !== undefined) {
worker.view.scanning = false
}
clearInterval(this.polling)
} else if (e.data.length > 0) {
if (e.data[0] === 'max') {
@@ -209,4 +246,9 @@
.el-button+.el-button {
margin-left: 0px;
}
.center {
font-weight: bold;
font-size: 12pt;
align-self: center;
}
</style>

View File

@@ -16,11 +16,8 @@
<script lang="js">
// import scanner from '../utils/scan'
import fileUrl from 'file-url'
import {Table, TableColumn} from 'element-ui'
const path = require('path')
export default {
name: 'search',
components: { [Table.name]: Table,
@@ -32,11 +29,11 @@
},
data () {
// this.$store.dispatch('getAirportsUnfiltered')
return {searchterm: this.searchterm}
return {searchterm: this.searchterm, lastSearchTerm: '', lastResult: {}}
},
methods: {
goto (icao) {
console.log(icao)
console.debug('Goto : ' + icao)
let airports = this.$store.state.Airports.airports
.filter(a => a.properties.icao.match(icao))
if (airports.length > 0) {
@@ -46,92 +43,35 @@
formatter (row, column) {
console.log('Row ' + row)
return row
},
scanAPT () {
try {
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
: `file://${__dirname}/worker.js`
console.log('make a worker: ', path.resolve(__dirname, 'worker.js'))
const worker = new Worker(winURL)
console.log(fileUrl('src/renderer/utils/worker.js'))
// var worker = new Worker(fileUrl('src/renderer/utils/worker.js'))
worker.postMessage(['scanapt'])
// the reply
var store = this.$store
worker.onmessage = function (e) {
if (e.data === 'DONE') {
console.log('DONE')
store.dispatch('getAirports')
worker.terminate()
}
console.log(e.data)
}
} catch (err) {
console.error(err)
}
},
scanGroundnets () {
try {
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
: `file://${__dirname}/worker.js`
console.log('make a worker: ', path.resolve(__dirname, 'worker.js'))
const worker = new Worker(winURL)
console.log(fileUrl('src/renderer/utils/worker.js'))
worker.postMessage(['scan', this.$store.state.Settings.settings.airportsDirectory])
// the reply
var store = this.$store
worker.onmessage = function (e) {
if (e.data === 'DONE') {
console.log('DONE')
store.dispatch('getAirports')
worker.terminate()
}
console.log(e.data)
}
} catch (err) {
console.error(err)
}
},
scanTraffic () {
// let flightgearDirectory = this.$store.state.settings.flightgearDirectory
try {
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/worker.js`
: `file://${__dirname}/worker.js`
console.log('make a worker: ', path.resolve(__dirname, 'worker.js'))
const worker = new Worker(winURL)
console.log(fileUrl('src/renderer/utils/worker.js'))
worker.postMessage(['scanai', this.$store.state.Settings.settings.flightgearDirectory])
// the reply
var store = this.$store
worker.onmessage = function (e) {
if (e.data === 'DONE') {
console.log('DONE')
store.dispatch('getAirports')
worker.terminate()
}
console.log(e.data)
}
} catch (err) {
console.error(err)
}
}
},
computed: {
searched: function () {
console.log(this.searchterm)
let searchRegex = new RegExp(this.searchterm, 'i')
return this.$store.state.Airports.airports
.filter(a => searchRegex.test(a.properties.icao) || searchRegex.test(a.properties.name))
// .map(a => console.log(a.properties))
.map(a => ({ icao: a.properties.icao, name: a.properties.name }))
console.debug('Search : ' + this.searchterm)
if (this.searchterm !== this.lastSearchTerm) {
let searchRegex = new RegExp(this.searchterm, 'i')
let result = this.$store.state.Airports.airports
.filter(point => point.geometry !== undefined)
.filter(point => point.geometry.coordinates !== undefined)
.filter(a => a.properties !== undefined)
.filter(a => searchRegex.test(a.properties.icao) || searchRegex.test(a.properties.name))
// .map(a => console.log(a.properties))
.map(a => ({ icao: a.properties.icao, name: a.properties.name }))
.filter((v, i, a) => a.findIndex(t => (t.icao === v.icao)) === i)
let icaoResult = result.filter(a => a.icao === this.searchterm).filter((v, i, a) => a.findIndex(t => (t.icao === v.icao)) === i)
if (result !== undefined &&
icaoResult.length === 0 &&
this.searchterm !== undefined &&
this.searchterm.length >= 3 &&
this.searchterm.length <= 4) {
// Not found so it might have been excluded due to no traffic
this.$store.dispatch('getAirport', this.searchterm)
}
this.lastResult = result
this.lastSearchTerm = this.searchterm
return result
}
return this.lastResult
}
}
}

View File

@@ -6,82 +6,362 @@
<i class="fa fa-caret-left"></i>
</div>
</h1>
<div id="panel" width="100%">
<el-row>
<el-col :span="7">Airports Directory</el-col>
<el-col :span="15">{{ airports_directory }}</el-col>
<el-col :span="2">
<directory-select @input="airportsDirectorySelect"></directory-select>
</el-col>
</el-row>
<el-row>
<el-col :span="7">Flightgear Directory</el-col>
<el-col :span="15">{{ flightgear_directory }}</el-col>
<el-col :span="2">
<directory-select @input="flightgearDirectorySelect"></directory-select>
</el-col>
</el-row>
<el-row>
<el-col :span="7">AI Directory</el-col>
<el-col :span="15">{{ AI_directory }}</el-col>
<el-col :span="2">
</el-col>
</el-row>
<el-row>
<el-col :span="7">Traffic Directory</el-col>
<el-col :span="15">{{ Traffic_directory }}</el-col>
<el-col :span="2">
</el-col>
</el-row>
<el-row>
<el-col :span="7">APT File</el-col>
<el-col :span="15">{{ apt_file }}</el-col>
<el-col :span="2">
</el-col>
</el-row>
</div>
<el-collapse v-model="activeName" accordion>
<el-collapse-item title="General" name="1">
<el-row>
<el-col :span="12" class="label">Number of saves : </el-col>
<el-col :span="12">
<el-popover
placement="top-start"
title="Saves"
width="200"
trigger="hover"
content="How many previous versions should be kept."
>
<el-input
placeholder="Number of versions"
slot="reference"
v-model="numberOfSaves"
></el-input>
</el-popover>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item title="Directories" name="2">
<el-row>
<el-col :span="22" class="label">Airports Directory</el-col>
</el-row>
<el-row>
<el-col
:span="22"
v-bind:class="{
invalid: !airports_directory_ok,
file_label: airports_directory_ok,
}"
>{{ airports_directory }}</el-col
>
<el-col :span="2">
<el-popover
placement="top-start"
title="E-Mail"
width="200"
trigger="hover"
content="The work directory. Best is a copy from groundweb"
>
<directory-select
@input="airportsDirectorySelect"
slot="reference"
></directory-select>
</el-popover>
</el-col>
</el-row>
<el-row>
<el-col :span="22" class="label">Flightgear Data Directory</el-col>
</el-row>
<el-row>
<el-col
:span="22"
v-bind:class="{
invalid: !flightgear_directory_ok,
file_label: flightgear_directory_ok,
}"
>{{ flightgear_directory }}</el-col
>
<el-col :span="2">
<el-popover
placement="top-start"
title="E-Mail"
width="200"
trigger="hover"
content="The FGDATA directory."
>
<directory-select
@input="flightgearDirectorySelect"
slot="reference"
></directory-select>
</el-popover>
</el-col>
</el-row>
<el-row>
<el-col :span="22" class="label">Traffic Directory</el-col>
</el-row>
<el-row>
<el-col
:span="22"
v-bind:class="{
invalid: !Traffic_directory_ok,
file_label: Traffic_directory_ok,
}"
>{{ Traffic_directory }}</el-col
>
<el-col :span="2"> </el-col>
</el-row>
<el-row>
<el-col :span="22" class="label">APT File</el-col>
</el-row>
<el-row>
<el-col :span="22" v-bind:class="{ invalid: !apt_file_ok }">{{
apt_file
}}</el-col>
<el-col :span="2"> </el-col>
</el-row>
<el-row>
<el-col :span="7" class="label">Export Directory : </el-col>
<el-col
:span="15"
v-bind:class="{
invalid: !test_directory_ok,
file_label: test_directory_ok,
}"
>{{ test_directory }}</el-col
>
<el-col :span="2">
<directory-select @input="testDirectorySelect"></directory-select>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item title="User" name="3">
<el-row>
<el-col :span="7" class="label">Author E-Mail : </el-col>
<el-col :span="17">
<el-popover
placement="top-start"
title="E-Mail"
width="200"
trigger="hover"
content="Only used as a committer/author for Github. This e-mail is only visible via GIT."
>
<el-input
placeholder="Please input your email"
slot="reference"
v-model="email"
></el-input>
</el-popover>
</el-col>
</el-row>
<el-row>
<el-col :span="7" class="label">Author Name : </el-col>
<el-col :span="17">
<el-popover
placement="top-start"
title="Goto"
width="200"
trigger="hover"
content="This is saved to the file and is therefore distributed via Terrasync."
>
<el-input
placeholder="Please input your Name"
slot="reference"
v-model="name"
></el-input>
</el-popover>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item title="Troubleshooting" name="4">
<el-row>
<el-col :span="7">
<span class="label">Scan logging :</span>
</el-col>
<el-col :span="15">
<el-popover
placement="top-start"
title="Logging"
width="200"
trigger="hover"
content="Switch on logging for scan. Big performance hit"
>
<el-switch v-model="scanLogging" slot="reference"></el-switch>
</el-popover>
</el-col>
</el-row>
<el-row>
<el-col :span="7" class="label"></el-col>
<el-col :span="17">
<el-popover
placement="top-start"
title="Debug"
width="200"
trigger="hover"
content="Opens the JavaScript Debugger for troubleshooting"
>
<el-button @click="debug" class="button" slot="reference">
<i class="fas fa-bug"></i> Debugger
</el-button>
</el-popover>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item title="Flightgear" name="5">
<el-row>
<el-col :span="22" class="label">Phi Host Url</el-col>
</el-row>
<el-row>
<el-col :span="24" class="label">
<el-input
placeholder="Please input a valid Phi URL"
v-model="phi_url"
></el-input>
</el-col>
</el-row>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script lang="js">
import FileSelect from './FileSelect'
import DirectorySelect from './DirectorySelect'
const { ipcRenderer } = require('electron')
const fs = require('fs')
export default {
name: 'settings-panel',
components: { DirectorySelect, FileSelect },
props: [],
mounted () {
},
data () {
return {
}
return { ok: true, activeName: '0', scanStoreLogging: false }
},
mounted () {
this.$store.watch(
function (state) {
return state.Settings.settings
},
() => { this.loggingChanged() }
,
{
deep: true // add this if u need to watch object properties change etc.
}
)
},
methods: {
loggingChanged () {
this.scanStoreLogging = this.$store.state.Settings.settings.scanLogging === 1
},
flightgearDirectorySelect: function (flightgearDirectory) {
console.log(flightgearDirectory)
this.$store.commit('FLIGHTGEAR_DIRECTORY', flightgearDirectory.path)
this.$store.commit('FLIGHTGEAR_DIRECTORY', flightgearDirectory)
},
airportsDirectorySelect: function (flightgearDirectory) {
console.log(flightgearDirectory)
this.$store.commit('AIPORTS_DIRECTORY', flightgearDirectory.path)
this.$store.commit('AIPORTS_DIRECTORY', flightgearDirectory)
},
testDirectorySelect: function (testDirectory) {
console.log(testDirectory)
this.$store.commit('TEST_DIRECTORY', testDirectory)
},
debug: function () {
ipcRenderer.send('OpenDebugger', 'ping')
}
},
computed: {
numberOfSaves: {
// getter
get: function () {
return this.$store.state.Settings.settings.numberOfSaves
},
// setter
set: function (newValue) {
this.$store.commit('SET_NUMBER_OF_SAVES', newValue)
}
},
email: {
// getter
get: function () {
return this.$store.state.Settings.settings.email
},
// setter
set: function (newValue) {
this.$store.commit('SET_EMAIL', newValue)
}
},
name: {
// getter
get: function () {
return this.$store.state.Settings.settings.name
},
// setter
set: function (newValue) {
this.$store.commit('SET_NAME', newValue)
}
},
phi_url: {
// getter
get: function () {
return this.$store.state.Settings.settings.phi_url
},
// setter
set: function (newValue) {
this.$store.commit('SET_PHI_URL', newValue)
}
},
flightgear_directory: function () {
return this.$store.state.Settings.settings.flightgearDirectory
},
flightgear_directory_ok: function () {
try {
fs.accessSync(this.$store.state.Settings.settings.flightgearDirectory)
return true
} catch (error) {
return false
}
},
AI_directory: function () {
return this.$store.state.Settings.settings.flightgearDirectory_ai
},
Traffic_directory: function () {
return this.$store.state.Settings.settings.flightgearDirectory_traffic
},
Traffic_directory_ok: function () {
try {
fs.accessSync(this.$store.state.Settings.settings.flightgearDirectory_traffic)
return true
} catch (error) {
return false
}
},
apt_file: function () {
return this.$store.state.Settings.settings.flightgearDirectory_apt
},
apt_file_ok: function () {
try {
fs.accessSync(this.$store.state.Settings.settings.flightgearDirectory_apt)
return true
} catch (error) {
return false
}
},
airports_directory: function () {
return this.$store.state.Settings.settings.airportsDirectory
},
airports_directory_ok: function () {
try {
fs.accessSync(this.$store.state.Settings.settings.airportsDirectory)
return true
} catch (error) {
return false
}
},
test_directory: function () {
return this.$store.state.Settings.settings.testDirectory
},
test_directory_ok: function () {
try {
fs.accessSync(this.$store.state.Settings.settings.testDirectory)
return true
} catch (error) {
return false
}
},
scanLogging: {
// getter
get: function () {
return this.scanStoreLogging
},
// setter
set: function (newValue) {
this.$store.commit('SET_SCAN_LOGGING', newValue ? 1 : 0)
}
}
}
}
@@ -89,9 +369,20 @@
<style>
.el-row {
margin-bottom: 20px;
margin-bottom: 10px;
}
.el-col {
border-radius: 4px;
}
.label {
padding: 5px;
font-weight: bold;
}
.file_label {
padding: 5px;
}
.invalid {
padding: 5px;
background-color: red;
}
</style>

View File

@@ -0,0 +1,95 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div width="100%" v-if="threshold">
<div>
<el-row>
<el-col :span="7">
<span class="label">Runway :</span>
</el-col>
<el-col :span="17">
<el-input
placeholder="Please input"
v-model="runway"
:disabled="true"
></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Displacement :</span>
</el-col>
<el-col :span="17">
<el-input-number
v-model="displacement"
:disabled="!editing"
></el-input-number>
</el-col>
</el-row>
</div>
</div>
</template>
<script lang="js">
/* eslint-disable */
const Coordinates = require('coordinate-parser');
import {writeTowerXML} from '../loaders/tower_writer'
export default {
/*
methods: {
updateIsOnRunway (value) {
this.$store.commit('SET_EDIT_ISONRUNWAY', value)
}
},
*/
data () {
return {
coordFocussed: false
}
},
methods: {
save () {
var o = {latitude: this.latitude, longitude: this.longitude, height: this.height};
writeTowerXML(this.$store.state.Settings.settings.airportsDirectory, this.$parent.$parent.$parent.icao, o)
}
},
computed: {
editing: {
get: function () {
return this.$parent.$parent.$parent.$refs.editLayer.editing
}
},
threshold: function () {
return this.$store.state.Editable.type === 'threshold'
},
//<rwy>07L</rwy>
//<hdg-deg>68.77</hdg-deg>
//<displ-m>0.0</displ-m>
//<stopw-m>160.0</stopw-m>
runway: function () {
return this.$store.state.Editable.data.threshold.runway;
},
displacement: {
set: function (newValue) {
this.$store.dispatch('setDisplacement', newValue);
},
get: function () {
return this.$store.state.Editable.data.threshold.displacement;
}
}
}
}
</script>

View File

@@ -0,0 +1,158 @@
<template></template>
<script lang="js">
import { LMap, LMarker } from 'vue2-leaflet'
import L from 'leaflet'
import leafletPattern from 'leaflet.pattern'
import LEdit from 'leaflet-editable/src/Leaflet.Editable.js'
import {readThresholdXML} from '../loaders/threshold_loader'
import {writeThresholdXML} from '../loaders/threshold_writer'
export default {
name: 'edit-layer',
props: [],
created () {
},
mounted () {
console.debug(LMap, LMarker, L, LEdit, leafletPattern)
this.$store.watch(
function (state) {
return state.Editable.data.threshold
},
() => { this.editedThreshold() }
,
{
deep: true
}
)
var stripes = new L.StripePattern({color: 'yellow'})
stripes.addTo(this.$parent.mapObject)
},
beforeDestroy () {
this.remove()
},
data () {
return {
}
},
methods: {
editedThreshold () {
if (this.$store.state.Editable.data.threshold) {
var rwy = this.$store.state.Editable.data.threshold.runway
var displacement = this.$store.state.Editable.data.threshold.displacement
this.layerGroup.eachLayer(l => {
if (l instanceof L.Threshold) {
if (l.rwy === rwy) {
l.setDisplacement(displacement)
}
}
})
}
},
getLayer () {
return this.layerGroup
},
load (icao) {
this.$parent.mapObject.createPane('threshold-pane')
this.$parent.mapObject.getPane('threshold-pane').style.zIndex = 550
if (this.layerGroup) {
this.layerGroup.removeFrom(this.$parent.mapObject)
}
var stripes = new L.StripePattern({color: 'yellow'})
stripes.addTo(this.$parent.mapObject)
// Callback for add
this.layerGroup = readThresholdXML(this.$store.state.Settings.settings.airportsDirectory, icao, this.read, stripes)
if (!this.layerGroup) {
console.warn('Threshold for ICAO not loaded ' + icao)
return
}
this.layerGroup.addTo(this.$parent.mapObject)
this.visible = true
this.icao = icao
},
deferredMountedTo (parent) {
},
remove () {
if (this.layerGroup) {
this.$parent.removeLayer(this.layerGroup)
}
},
add () {
if (this.$parent._isMounted) {
this.deferredMountedTo(this.$parent.mapObject)
}
},
enableEdit () {
if (this.layerGroup) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.Threshold) {
l.setInteractive(true)
}
})
}
},
disableEdit () {
if (this.layerGroup) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.Threshold) {
l.setInteractive(false)
}
})
}
},
setVisible (visible) {
if (this.layerGroup !== undefined) {
if (visible !== this.visible) {
if (visible) {
this.layerGroup.addTo(this.$parent.mapObject)
} else {
this.layerGroup.removeFrom(this.$parent.mapObject)
}
this.visible = visible
}
}
},
save () {
if (this.layerGroup) {
var list = {}
this.layerGroup.eachLayer(l => {
if (l instanceof L.Threshold) {
var latitude = l.originLatLng[0].toFixed(6)
var longitude = l.originLatLng[1].toFixed(6)
if (list[l.index] === undefined) {
list[l.index] = []
}
var o = {latitude: latitude, longitude: longitude, index: l.index, rwy: l.rwy, heading: l.heading, displacement: l.displacement, stopw_m: l.stopw_m}
list[l.index].push(o)
}
})
writeThresholdXML(this.$store.state.Settings.settings.airportsDirectory, this.icao, list)
}
},
zoomUpdated () {
if (this.layerGroup) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.Threshold) {
l.updateIcon(this.$parent.mapObject)
}
})
}
}
},
computed: {
edit: function () {
console.log('Zoom : ' + this.$store.state.Settings.zoom)
if (this.$store.state.Settings.zoom > 12) {
console.log('Zoom above 12')
}
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,76 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div id="ToolBar">
<ToolButton
icon="far fa-eye"
v-on:click="showTooltips"
:show="editing"
tooltip="Show Tooltips"
></ToolButton>
<ToolButton
icon="fas fa-draw-polygon"
v-on:click="drawPolyline"
:show="editing"
tooltip="Draw Guideline"
></ToolButton>
</div>
</template>
<script lang="js">
/* eslint-disable */
import ToolButton from './ToolButton'
import Vue from 'vue'
import fileUrl from 'file-url'
const path = require('path')
const fs = require('fs')
export default {
components: { ToolButton },
data () {
return {isEditing: false}
},
created () {
},
methods: {
drawPolyline () {
this.$parent.$parent.$refs.toolLayer.stopDrawing()
this.$parent.$parent.$refs.toolLayer.drawPolyline()
},
showTooltips () {
this.$parent.$parent.$refs.editLayer.showTooltips()
},
setEditing (edit) {
this.isEditing = edit;
if(!this.isEditing) {
this.$parent.$parent.$refs.toolLayer.stopDrawing()
}
}
},
computed: {
editing: {
// getter
get: function () {
console.log(`Getting Visible : ${this.isEditing}`)
return this.isEditing
},
// setter
set: function (newValue) {
this.isEditing = newValue
console.log(`Setting Visible : ${this.isEditing}`)
}
}
}
}
</script>

View File

@@ -0,0 +1,63 @@
<template></template>
<script lang="js">
import L from 'leaflet'
import '@fortawesome/fontawesome-free/css/all.css'
import ToolControl from '../leaflet/ToolControl.js'
export default {
name: 'edit-bar',
components: { ToolControl, L },
props: {
icon: String,
show: Boolean,
tooltip: String
},
watch: {
show: function (newVal, oldVal) { // watch it
console.log('Prop changed: ', newVal, ' | was: ', oldVal)
if (newVal) {
this.add()
} else {
this.remove()
}
}
},
mounted () {
this.add()
},
beforeDestroy () {
this.remove()
},
data () {
return {
}
},
methods: {
deferredMountedTo (parent) {
this.editbutton = new L.ToolControl({html: `<i class="${this.icon}"></i>`, callback: this.click, tooltip: this.tooltip})
if (this.show) {
parent.addControl(this.editbutton)
}
},
click () {
// console.log(parent)
this.$emit('click')
},
remove () {
if (this.editbutton) {
this.$parent.$parent.mapObject.removeControl(this.editbutton)
}
},
add () {
if (this.$parent.$parent._isMounted) {
this.deferredMountedTo(this.$parent.$parent.mapObject)
}
}
},
computed: {
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,127 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template></template>
<script lang="js">
/* eslint-disable */
import {LMap, LMarker} from 'vue2-leaflet'
import L from 'leaflet'
import LEdit from 'leaflet-editable/src/Leaflet.Editable.js'
const turf = require('@turf/turf')
export default {
name: 'tool-layer',
props: [],
mounted () {
this.add()
},
beforeDestroy () {
this.remove()
},
data () {
return {
}
},
methods: {
deferredMountedTo (parent) {
this.toolLayerGroup = L.layerGroup();
this.toolLayerGroup.addTo(this.$parent.mapObject)
},
remove () {
if (this.sidebar) {
this.$parent.removeLayer(this.sidebar)
}
},
add () {
if (this.$parent._isMounted) {
this.deferredMountedTo(this.$parent.mapObject)
}
},
stopDrawing () {
this.$parent.mapObject.editTools.stopDrawing()
this.toolLayerGroup.eachLayer((layer) => {
layer.removeFrom(this.toolLayerGroup);
});
},
drawPolyline () {
var polyLine = this.$parent.mapObject.editTools.startPolygon(undefined, {color: 'green'})
var layerGroup = this.toolLayerGroup;
polyLine.addTo(this.toolLayerGroup)
polyLine.on('click', event => {
polyLine.removeFrom(layerGroup);
});
polyLine.on('editable:drawing:end', event => {
console.debug('editable:drawing:end', event)
var latLngs = event.target.getLatLngs()[0].map( latLng => ([latLng.lat, latLng.lng]));
// turf rings must start/end with the same point
latLngs.push(latLngs[0]);
var longest = 0;
var angleLongest = 0;
latLngs.forEach((item, index, arr) => {
if (index > 0) {
var angle = turf.bearing(turf.point([arr[index-1][1], arr[index-1][0]]), turf.point([arr[index][1], arr[index][0]])) + 180;
var dist = turf.distance( turf.point(arr[index-1]), turf.point(arr[index]));
if (dist>longest) {
longest = dist;
angleLongest = angle;
}
}
});
event.target.bindTooltip(angleLongest.toFixed(2) + '°', {permanent: true})
var ring = [latLngs];
this.$emit('select-poly', ring);
})
polyLine.on('editable:vertex:dragend', event => {
console.debug(event)
var latLngs = event.target.getLatLngs()[0].map( latLng => ([latLng.lat, latLng.lng]));
// turf rings must start/end with the same point
latLngs.push(latLngs[0]);
var longest = 0;
var angleLongest = 0;
latLngs.forEach((item, index, arr) => {
if (index > 0) {
var angle = turf.bearing(turf.point([arr[index-1][1], arr[index-1][0]]), turf.point([arr[index][1], arr[index][0]])) + 180;
var dist = turf.distance( turf.point(arr[index-1]), turf.point(arr[index]));
if (dist>longest) {
longest = dist;
angleLongest = angle;
}
}
});
event.target.setTooltipContent(angleLongest.toFixed(2) + '°')
var ring = [latLngs];
this.$emit('select-poly', ring);
})
},
},
computed: {
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,93 @@
<template>
<div width="100%" v-if="tower">
<div>
<el-row>
<el-col :span="7">
<span class="label">Latitude :</span>
</el-col>
<el-col :span="17">
<el-input placeholder="Please input" v-model="latitude" :disabled="true"
@focus="coordFocussed = true"
@blur="coordFocussed = false"></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Longitude :</span>
</el-col>
<el-col :span="17">
<el-input placeholder="Please input" v-model="longitude" :disabled="true"
@focus="coordFocussed = true"
@blur="coordFocussed = false"></el-input>
</el-col>
</el-row>
<el-row>
<el-col :span="7">
<span class="label">Height :</span>
</el-col>
<el-col :span="17">
<el-input-number placeholder="Please input" @change="handleChange" v-model="height" :disabled="!editing" :step="0.01"
@focus="coordFocussed = true"
@blur="coordFocussed = false"></el-input-number>
</el-col>
</el-row>
</div>
</div>
</template>
<script lang="js">
/* eslint-disable */
const Coordinates = require('coordinate-parser');
import {writeTowerXML} from '../loaders/tower_writer'
export default {
/*
methods: {
updateIsOnRunway (value) {
this.$store.commit('SET_EDIT_ISONRUNWAY', value)
}
},
*/
data () {
return {
coordFocussed: false
}
},
methods: {
save () {
var o = {latitude: this.latitude, longitude: this.longitude, height: this.height};
writeTowerXML(this.$store.state.Settings.settings.airportsDirectory, this.$parent.$parent.$parent.icao, o)
},
handleChange (newValue) {
this.$store.dispatch('setTowerHeight', newValue);
}
},
computed: {
editing: {
get: function () {
return this.$parent.$parent.$parent.$refs.editLayer.editing
}
},
tower: function () {
return this.$store.state.Editable.type === 'tower'
},
// {index: 39, lat: "N58 27.343", lon: "W03 5.153", isOnRunway: 0, holdPointType: "none"}
latitude: function () {
return this.$store.state.Editable.data.tower.coords.latitude;
},
longitude: function () {
return this.$store.state.Editable.data.tower.coords.longitude;
},
height: {
get: function () {
return this.$store.state.Editable.data.tower.height;
},
set: function (newValue) {
this.$store.dispatch('setTowerHeight', newValue);
}
}
}
}
</script>

View File

@@ -0,0 +1,154 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template></template>
<script lang="js">
import { LMap, LMarker } from 'vue2-leaflet'
import L from 'leaflet'
import LEdit from 'leaflet-editable/src/Leaflet.Editable.js'
import {readTowerXML} from '../loaders/tower_loader'
import {writeTowerXML} from '../loaders/tower_writer'
export default {
name: 'tower-layer',
props: [],
created () {
console.debug([LMap, LMarker, L, LEdit])
},
mounted () {
this.$store.watch(
function (state) {
return state.Editable.data.tower
},
() => { this.editedTower() }
,
{
deep: true
}
)
},
beforeDestroy () {
this.remove()
},
data () {
return {
}
},
methods: {
editedTower () {
if (this.$store.state.Editable.data.tower) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.TowerMarker) {
l.setTowerHeight(this.$store.state.Editable.data.tower.height)
}
})
}
},
getLayer () {
return this.layerGroup
},
load (icao) {
this.$parent.mapObject.createPane('tower-pane')
this.$parent.mapObject.getPane('tower-pane').style.zIndex = 550
if (this.layerGroup !== undefined) {
this.layerGroup.removeFrom(this.$parent.mapObject)
}
// Callback for add
this.layerGroup = readTowerXML(this.$store.state.Settings.settings.airportsDirectory, icao, this.read)
if (!this.layerGroup) {
console.warn('Tower for ICAO not loaded ' + icao)
return
}
this.layerGroup.addTo(this.$parent.mapObject)
this.visible = true
this.icao = icao
},
deferredMountedTo (parent) {
},
remove () {
if (this.layerGroup) {
this.$parent.removeLayer(this.layerGroup)
}
},
add () {
if (this.$parent._isMounted) {
this.deferredMountedTo(this.$parent.mapObject)
}
},
enableEdit () {
if (this.layerGroup) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.TowerMarker) {
l.setInteractive(true)
}
})
}
},
disableEdit () {
if (this.layerGroup) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.TowerMarker) {
l.setInteractive(false)
}
})
}
},
save () {
if (this.layerGroup) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.TowerMarker) {
var latitude = l.getLatLng().lat.toFixed(6)
var longitude = l.getLatLng().lng.toFixed(6)
var height = l.elev_m
var o = {latitude: latitude, longitude: longitude, height: height}
writeTowerXML(this.$store.state.Settings.settings.airportsDirectory, l.icao, o)
}
})
}
},
setVisible (visible) {
if (this.layerGroup !== undefined) {
if (visible !== this.visible) {
if (visible) {
this.layerGroup.addTo(this.$parent.mapObject)
} else {
this.layerGroup.removeFrom(this.$parent.mapObject)
}
this.visible = visible
}
}
},
zoomUpdated () {
if (this.layerGroup) {
this.layerGroup.eachLayer(l => {
if (l instanceof L.TowerMarker) {
l.updateIcon(this.$parent.mapObject)
}
})
}
}
},
computed: {
edit: function () {
console.log('Zoom : ' + this.$store.state.Settings.zoom)
if (this.$store.state.Settings.zoom > 12) {
console.log()
}
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,97 @@
<!--
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<div>
<el-radio-group v-model="direction">
<el-radio :label="departure"
><i class="fas fa-plane-departure"></i
></el-radio>
<el-radio :label="arrival"><i class="fas fa-plane-arrival"></i></el-radio>
</el-radio-group>
<el-collapse v-model="activeName" accordion ref="collapse">
<el-collapse-item
v-for="a in airlines"
v-bind:key="a.index"
class="row"
:title="a.label"
:name="a.label"
>
<AirlineItem :airline="a" ref="airline"></AirlineItem>
</el-collapse-item>
</el-collapse>
<el-popover
placement="top-start"
title="Add Test Traffic"
width="200"
trigger="hover"
content="Generate Testtraffic"
>
<el-button @click="generate" class="button" slot="reference">
<i class="fas fa-notes-medical"></i>
</el-button>
</el-popover>
</div>
</template>
<script lang="js">
import AirlineItem from './AirlineItem'
import {writeTrafficXML} from '../loaders/traffic_writer'
export default {
name: 'traffic-list',
components: {AirlineItem},
props: [],
data () {
return {
activeName: '',
departure: 0,
arrival: 1,
direction: 0,
activeIndex: '1',
activeIndex2: '1'
}
},
methods: {
generate () {
// .filter((f) => f.$children[0])
var aircraft = this.$refs.airline.flatMap((f) => {
console.debug(f.aircraft)
return f.aircraft
}
).filter(f => f)
writeTrafficXML(this.$store.state.Settings.settings.flightgearDirectory_traffic, this.$store.state.Parkings.items, aircraft)
}
},
computed: {
airlines: function () {
var airlineCodes = []
if (this.$store.state.Airports.currentAirport !== undefined && this.$store.state.Airports.currentAirport.airlines) {
var storedairlineCodes = this.$store.state.Airports.currentAirport.airlines
storedairlineCodes.forEach(element => {
airlineCodes.push({value: element, label: element})
})
}
return airlineCodes.filter((v, i, a) => a.indexOf(v) === i)
}
}
}
</script>
<style>
div.row.div {
display: flex;
justify-content: space-between;
}
</style>

View File

@@ -0,0 +1,365 @@
<!--
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
-->
<template>
<el-dialog :title.sync="title" :visible.sync="visible" width="30%" center>
<span v-if="max>0">
<el-progress :percentage="Number(((progress / max)*100).toPrecision(3))" v-if="max>0"></el-progress>
</span>
<span v-if="results.length>0" style="color: red">{{results.length}} Errors please correct first</span><br/>
<span class="center">E-Mail : {{this.$store.state.Settings.settings.email}}</span><br/>
<span class="center"><el-checkbox v-model="gplv2" class="center">I agree to release the groundnet under GPL v2</el-checkbox></span><br/>
<span :class="textClass" v-if="message">{{message}}</span><br/>
<el-button @click="handleOkClicked('twr')" :disabled="!tower_comittable" >Upload Tower</el-button>
<el-button @click="handleOkClicked('groundnet')" :disabled="!groundnet_comittable" >Upload Groundnet</el-button>
<el-button @click="handleOkClicked('threshold')" :disabled="!threshold_comittable" >Upload Threshold</el-button>
<span slot="footer" class="dialog-footer">
<el-button @click="closeClicked">{{buttonText}}</el-button>
</span>
</el-dialog>
</template>
<script lang="js">
/* eslint-disable */
import Vue from 'vue'
import fileUrl from 'file-url'
import axios from 'axios'
const fs = require('fs')
const path = require('path')
const mapper = require('../check/mapper');
export default {
name: 'upload',
props: [],
mounted () {
this.$store.watch(
function (state) {
return state.Loading.groundnetLoaded;
},
() => { if(this.$store.state.Loading.groundnetLoaded &&
this.$store.state.Loading.pavementLoaded &&
this.visible) this.check() }
,
{
deep: false
}
);
this.$store.watch(
function (state) {
return state.Loading.pavementLoaded;
},
() => { if(this.$store.state.Loading.groundnetLoaded &&
this.$store.state.Loading.pavementLoaded &&
this.visible) this.check() }
,
{
deep: false
}
);
},
data () {
return {
gplv2: false, message: null, error: false, progress: 0, max: 0, azure: false, success: false, uploading: false, buttonText: 'Ok'
}
},
methods: {
reqListener(e) {
try {
if(JSON.parse(e.srcElement.response).status==='OK') {
this.message = null;
this.azure = true;
this.error = false;
} else {
this.message = 'Azure down';
}
} catch (error) {
console.error(error);
}
},
status () {
this.azure = false;
var xhr = new XMLHttpRequest();
var parent = this.$parent;
this.message = 'Checking for Groundweb health'
xhr.open('GET', 'http://groundweb.azurewebsites.net/groundnets/status', true);
xhr.onreadystatechange = function () {
if (xhr.status !== 200){
parent.$refs.upload.message = 'Azure down';
parent.$refs.upload.error = true;
console.error(xhr);
}
}
xhr.addEventListener("load", this.reqListener);
try {
xhr.send();
} catch (err) {
console.error(err);
this.error = true;
}
},
closeClicked () {
Vue.set(this.$parent, 'uploadVisible', false)
return;
},
handleOkClicked (type) {
this.uploading = true;
var f = path.join(this.$store.state.Settings.settings.airportsDirectory,
this.icao[0],
this.icao[1],
this.icao[2],
this.icao + `.${type}.new.xml`);
if (f == null || !fs.existsSync(f)) {
this.message = 'File doesn\'t exist';
return;
}
var data = fs.readFileSync(f, 'utf8').toString();
var blob = new Blob([data]);
var url = URL.createObjectURL(blob);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://groundweb.azurewebsites.net/groundnets/upload', true);
// define new form
var formData = new FormData();
formData.append("gpl", this.gplv2 )
formData.append("user_email", this.$store.state.Settings.settings.email)
formData.append('groundnet', blob, this.icao + `.${type}.xml`);
var parent = this.$parent;
var messageField = this.message;
// action after uploading happens
xhr.onreadystatechange = function () {
if (xhr.status !== 200){
parent.$refs.upload.message = 'Upload Error'
parent.$refs.upload.error = true;
console.error(xhr);
}
}
xhr.onload = function(e) {
console.log("File uploading completed! ");
console.log(e);
parent.$refs.upload.uploading = false
if (e.srcElement.status===500) {
parent.$refs.upload.message == e.srcElement.statusText
} else if(JSON.parse(e.srcElement.response).message.match('[A-Z0-9]* Imported Successfully')) {
parent.$refs.upload.success = true
parent.$refs.upload.message = `${type} Uploaded Successfully`
parent.$store.commit('UPLOAD_WIP', parent.$store.state.Airports.currentAirport.icao)
} else if(JSON.parse(e.srcElement.response).message === 'XML Errors') {
var response = JSON.parse(e.srcElement.response);
if (response.validationErrors) {
parent.$refs.upload.message = 'XML Errors : \n';
response.validationErrors.forEach(element => {
parent.$refs.upload.message += element.message + '\n';
});
}
} else if(JSON.parse(e.srcElement.response) !== undefined) {
var response = JSON.parse(e.srcElement.response);
parent.$refs.upload.message = response.err;
} else {
parent.$refs.upload.message = response.message;
}
};
this.message = "File uploading started!"
// do the uploading
console.log("File uploading started!");
xhr.send(formData);
},
pollData () {
var workery = this.worker
var view = this
workery.polling = setInterval(() => {
if (workery != null) {
view.max = Number(workery.max)
view.progress = Number(workery.progress)
view.scanning = Boolean(workery.checking)
workery.view = view
}
}, 500)
},
check () {
try {
if(!(this.$store.state.Loading.groundnetLoaded &&
this.$store.state.Loading.pavementLoaded)) {
return
}
this.scanning = true
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/check.js`
: `file://${process.resourcesPath}/workers/check.js`
console.debug('make a check worker: ', path.resolve(__dirname, 'check.js'))
const worker = new Worker(winURL)
console.debug(fileUrl('src/renderer/utils/check.js'))
worker.checking = this.checking
worker.max = this.max
worker.view = this
worker.editLayer = this.$parent.$parent.$refs.editLayer
worker.progress = 0
// var worker = new Worker(fileUrl('src/renderer/utils/worker.js'))
this.worker = worker
var groundnet = []
if (!this.editLayer().groundnetLayerGroup) {
this.message = 'Groundnet not visible'
}
if (!this.pavementLayer().pavement) {
this.message = 'Pavement not visible'
}
this.editLayer().groundnetLayerGroup.eachLayer(l => {
console.log(l)
if (l instanceof L.Polyline) {
l._latlngs[0].glueindex = this.begin;
l._latlngs.slice(-1)[0].glueindex = this.end;
l.extensions(this)
}
groundnet.push(l)
})
var features = groundnet.map(mapper.checkMapper).filter(n => n)
var pavement = []
this.pavementLayer().pavement.eachLayer(l => {
console.log(l)
pavement.push(l)
})
var features2 = pavement.map(mapper.checkMapper).filter(n => n)
worker.postMessage(['check', features.concat(features2) ] )
this.pollData()
// the reply
var store = this.$store
worker.onmessage = function (e) {
if (e.data === 'checkStarted') {
this.progress = 0
this.max = 4
} else if (e.data[0] === 'DONE') {
console.log('DONE')
worker.terminate()
worker.view.max = 0
worker.view.checkDialogVisible = false
clearInterval(this.polling)
this.checking = false
} else if (e.data.length > 0) {
if (e.data[0] === 'max') {
this.max = e.data[1]
}
if (e.data[0] === 'progress') {
this.progress += e.data[1]
}
}
// console.log(e.data)
}
} catch (err) {
console.error(err)
}
},
editLayer () {
var parent = this.$parent;
while (!parent.icao||parent.$refs.editLayer==undefined) {
parent = parent.$parent;
if (parent.icao&&parent.$refs.editLayer!==undefined) {
return parent.$refs.editLayer;
}
}
},
pavementLayer () {
var parent = this.$parent;
while (!parent.icao||parent.$refs.pavementLayer==undefined) {
parent = parent.$parent;
if (parent.icao&&parent.$refs.pavementLayer!==undefined) {
return parent.$refs.pavementLayer;
}
}
}
},
computed: {
visible: {
// getter
get: function () {
return this.$attrs.visible
},
// setter
set: function (newValue) {
Vue.set(this.$parent, 'uploadVisible', newValue)
}
},
textClass: function () {
return !this.error?'centermessage':'error'
},
title: function () {
return `Upload ${this.icao} to groundweb.`
},
icao: {
get: function () {
var parent = this.$parent;
while (!parent.icao) {
parent = parent.$parent;
if (parent.icao) {
return parent.icao;
}
}
return this.$store.state.Airports.currentAirport.icao
},
set: function (newValue) {
console.error('ICAO being set ' + newValue);
}
},
tower_comittable: function () {
var f = path.join(this.$store.state.Settings.settings.airportsDirectory,
this.icao[0],
this.icao[1],
this.icao[2],
this.icao + '.twr.new.xml');
return fs.existsSync(f) && this.gplv2 && this.max === 0 && this.azure && !this.uploading;
},
groundnet_comittable: function () {
var f = path.join(this.$store.state.Settings.settings.airportsDirectory,
this.icao[0],
this.icao[1],
this.icao[2],
this.icao + '.groundnet.new.xml');
return fs.existsSync(f) && this.$store.state.Check.results.filter(a => a.id>=0).length === 0 && this.gplv2 && this.max === 0 && this.azure && !this.uploading
},
threshold_comittable: function () {
var f = path.join(this.$store.state.Settings.settings.airportsDirectory,
this.icao[0],
this.icao[1],
this.icao[2],
this.icao + '.threshold.new.xml');
return fs.existsSync(f) && this.gplv2 && this.max === 0 && this.azure && !this.uploading
},
comittable: function () {
return this.$store.state.Check.results.filter(a => a.id>=0).length === 0 && this.gplv2 && this.max === 0 && this.azure && !this.uploading
},
results: function () {
return this.$store.state.Check.results.filter(a => a.id>=0)
}
},
}
</script>
<style scoped lang="scss">
.el-dialog__body {padding: 10px;}
.center { text-align: center; vertical-align: middle; padding: 5px; font-size: 12pt; font-weight: normal}
.centermessage { text-align: left; vertical-align: middle; padding: 5px; font-size: 12pt; font-weight: normal; white-space: pre-line;}
.error { text-align: center; color: red; padding: 5px; font-size: 12pt; font-weight: normal;}
.el-dialog--center .el-dialog__body { padding: 5px;}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<section class="work-in-progress">
<h1 class="leaflet-sidebar-header">
Work in progress
<div class="leaflet-sidebar-close"><i class="fa fa-caret-left"></i></div>
</h1>
<div>
<el-row>
<el-col :span="3" class="text">
ICAO
</el-col>
<el-col :span="7" class="text">
Edited
</el-col>
<el-col :span="8" class="text">
Uploaded
</el-col>
<el-col :span="6" class="text">
Actions
</el-col>
</el-row>
<el-row v-for="w in wip" :key="w.icao">
<Airport :airport="w" :editing="editing"></Airport>
</el-row>
</div>
</section>
</template>
<script lang="js">
import Airport from './Airport'
export default {
name: 'work-in-progress',
props: [],
components: {Airport},
mounted () {
},
data () {
return {
}
},
methods: {
},
computed: {
editing: {
get: function () {
return this.$store.state.Editable.editing
}
},
wip: function () {
return this.$store.state.Settings.wip
}
}
}
</script>
<style scoped lang="scss">
.text {
padding: 10px;
font-weight: bold;
}
.el-row {
margin-bottom: 0px;
}
</style>

View File

@@ -0,0 +1,64 @@
<template></template>
<script lang="js">
import L from 'leaflet'
import '@fortawesome/fontawesome-free/css/all.css'
import EditBar from '../leaflet/ZoomControl.js'
export default {
name: 'edit-bar',
props: {
icon: String,
show: Boolean,
tooltip: String
},
watch: {
show: function (newVal, oldVal) { // watch it
console.log('Prop changed: ', newVal, ' | was: ', oldVal)
if (newVal) {
this.add()
} else {
this.remove()
}
}
},
mounted () {
console.debug(L)
console.debug(EditBar)
this.add()
},
beforeDestroy () {
this.remove()
},
data () {
return {
}
},
methods: {
deferredMountedTo (parent) {
this.editbutton = new L.ZoomControl({html: `<i class="${this.icon}"></i>`, callback: this.click, tooltip: this.tooltip})
if (this.show) {
parent.addControl(this.editbutton)
}
},
click () {
// console.log(parent)
this.$emit('click')
},
remove () {
if (this.editbutton) {
this.$parent.$parent.mapObject.removeControl(this.editbutton)
}
},
add () {
if (this.$parent.$parent._isMounted) {
this.deferredMountedTo(this.$parent.$parent.mapObject)
}
}
},
computed: {
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,14 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
import Vue from 'vue'
export const EventBus = new Vue()

View File

@@ -2,7 +2,7 @@
L.EditControl = L.Control.extend({
options: {
position: 'topleft',
position: 'topright',
callback: null,
kind: '',
html: '<i class="fas fa-draw-polygon"></i>',
@@ -16,11 +16,23 @@ L.EditControl = L.Control.extend({
link.href = '#';
link.title = this.options.tooltip;
link.innerHTML = this.options.html;
L.DomEvent.on(link, 'click', L.DomEvent.stop)
.on(link, 'click', function () {
window.LAYER = this.options.callback.call(map.editTools);
}, this);
link.callback = this.options.callback;
link.addEventListener('click',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
link.addEventListener('mousedown',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
window.LAYER = this.callback.call(map.editTools);
}, false);
link.addEventListener('mouseup',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
return container;
}
});
@@ -32,6 +44,33 @@ L.NewMarkerControl = L.EditControl.extend({
callback: null,
kind: 'marker',
html: '🖈'
}
},
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'),
link = L.DomUtil.create('a', '', container);
link.href = '#';
link.title = this.options.tooltip;
link.innerHTML = this.options.html;
link.callback = this.options.callback;
link.addEventListener('click',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
link.addEventListener('mousedown',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
window.LAYER = this.callback.call(map.editTools);
}, false);
link.addEventListener('mouseup',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
return container;
}
});

View File

@@ -0,0 +1,41 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const turf = require('@turf/turf')
L.RunwayPolygon = L.Polygon.extend({
turfyRunway: [],
setTurfy: function (runwayPoints) {
var latLngs = runwayPoints.map(this.turfToLatLng);
latLngs.push(latLngs[0]);
this.turfyRunway = turf.polygon([latLngs]);
},
turfToLatLng: function (turfPoint) {
return [turfPoint.lng, turfPoint.lat];
}
});
var runwayPoly = function (runwayPoints) {
var runwayPoly = new L.RunwayPolygon(runwayPoints);
runwayPoly.setStyle({ color: 'grey', fillColor: 'grey', opacity: 0.5, fillOpacity: 0.5, interactive: false });
runwayPoly.setTurfy(runwayPoints);
console.debug(runwayPoints);
return runwayPoly;
}
module.exports = runwayPoly;

View File

@@ -0,0 +1,41 @@
/*
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const turf = require('@turf/turf')
L.TakeoffPolygon = L.Polygon.extend({
turfyRunway: [],
setTurfy: function (padPoints) {
var latLngs = padPoints.map(this.turfToLatLng);
latLngs.push(latLngs[0]);
this.turfyRunway = turf.polygon([latLngs]);
},
turfToLatLng: function (turfPoint) {
return [turfPoint.lng, turfPoint.lat];
}
});
var takeoffPadPoly = function (padPoints) {
var takeoffPadPoly = new L.TakeoffPolygon(padPoints);
takeoffPadPoly.setStyle({ color: 'black', fillColor: '', opacity: 1.0, fillOpacity: 0.0, interactive: false });
takeoffPadPoly.setTurfy(padPoints);
console.debug(padPoints);
return takeoffPadPoly;
}
module.exports = takeoffPadPoly;

View File

@@ -0,0 +1,25 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
L.TaxiwayPolygon = L.Polygon.extend({
});
var taxiwayPoly = function (runwayPoints) {
var taxiwayPoly = new L.TaxiwayPolygon(runwayPoints);
taxiwayPoly.setStyle({ color: 'grey', fillColor: 'grey', opacity: 0.3, fillOpacity: 0.3, interactive: false });
return taxiwayPoly;
}
module.exports = taxiwayPoly;

View File

@@ -0,0 +1,36 @@
/* eslint-disable */
L.ToolControl = L.Control.extend({
options: {
position: 'bottomright',
callback: null,
kind: 'marker',
html: '🖈'
},
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'),
link = L.DomUtil.create('a', '', container);
link.href = '#';
link.title = this.options.tooltip;
link.innerHTML = this.options.html;
link.callback = this.options.callback;
link.addEventListener('click',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
link.addEventListener('mousedown',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
window.LAYER = this.callback.call(map.editTools);
}, false);
link.addEventListener('mouseup',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
return container;
}
});

View File

@@ -0,0 +1,76 @@
/* eslint-disable */
L.ZoomControl = L.Control.extend({
options: {
position: 'topleft',
callback: null,
kind: '',
html: '<i class="fas fa-draw-polygon"></i>',
tooltip: 'tooltip'
},
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'),
link = L.DomUtil.create('a', '', container);
link.href = '#';
link.title = this.options.tooltip;
link.innerHTML = this.options.html;
link.callback = this.options.callback;
link.addEventListener('click',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
link.addEventListener('mousedown',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
window.LAYER = this.callback.call(map.editTools);
}, false);
link.addEventListener('mouseup',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
return container;
}
});
L.NewMarkerControl = L.EditControl.extend({
options: {
position: 'topright',
callback: null,
kind: 'marker',
html: '🖈'
},
onAdd: function (map) {
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar'),
link = L.DomUtil.create('a', '', container);
link.href = '#';
link.title = this.options.tooltip;
link.innerHTML = this.options.html;
link.callback = this.options.callback;
link.addEventListener('click',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
link.addEventListener('mousedown',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
window.LAYER = this.callback.call(map.editTools);
}, false);
link.addEventListener('mouseup',function (event) {
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}, false);
return container;
}
});

View File

@@ -7,6 +7,16 @@ const store = require('../store');
var $ = require('jquery');
L.HoldNode = L.Marker.extend({
select() {
if(this._icon!==null) {
this._icon.childNodes[0].style['background-color'] = 'red';
}
},
deselect() {
if(this._icon!==null) {
this._icon.childNodes[0].style['background-color'] = '#4838cc';
}
},
addListeners: function () {
this.on('editable:drawing:move', function (event) {
console.log("Move : ", event);
@@ -21,7 +31,7 @@ L.HoldNode = L.Marker.extend({
this.featureLookup[this.glueindex].push(this);
},
/**
*
*
*/
follow(dragIndex, event) {
@@ -51,18 +61,18 @@ L.HoldNode = L.Marker.extend({
element.updateEndVertex(event.latlng);
element.updateMiddle();
}
} else if (element instanceof L.Editable.VertexMarker) {
} else if (element instanceof L.Editable.VertexMarker) {
console.log(element);
element.setLatLng(event.latlng);
element.latlngs.forEach((latlng, index) => {
console.log(latlng);
console.log(latlng);
if(latlng.__vertex === element) {
latlng.update(event.latlng);
}
});
element.editor.feature.setLatLngs(element.latlngs);
element.editor.feature.updateMiddle();
}
}
}
})
}
@@ -70,7 +80,7 @@ L.HoldNode = L.Marker.extend({
var holdNode = function (n, layerGroup) {
//console.log(n.attr('lat') + " " + n.attr('lon'));
var latlon = convert(n.attr('lat') + " " + n.attr('lon'));
var latlon = convert(n.attr('lat') + " " + n.attr('lon'));
var fa_icon = null;
if (n.attr('holdPointType') === 'PushBack') {
fa_icon = "<div style='background-color:#4838cc;' class='marker-pin'></div><i class='fas fa-arrows-alt-h'></i>";
@@ -83,8 +93,9 @@ var holdNode = function (n, layerGroup) {
iconSize: [30, 42],
iconAnchor: [15, 42]
});
const node = new L.HoldNode([latlon.decimalLatitude, latlon.decimalLongitude], { icon: icon });
const node = new L.HoldNode([latlon.decimalLatitude, latlon.decimalLongitude], { icon: icon });
node.glueindex = n.attr('index');
node.feature = { properties: { searchTerm: n.attr('index')}};
node.holdPointType = n.attr('holdPointType');
node.addTo(layerGroup);
node.addListeners();

View File

@@ -0,0 +1,93 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
/**http://wiki.openstreetmap.org/wiki/Zoom_levels*/
var metersPerPixel = function (latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI / 180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel + 8);
};
var pixelValue = function (latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
};
function stripSVG(fName) {
var rx = /<\s*svg[^>]*>([\s\S]*)<\s*\/svg[^>]*>/gm;
var svg = fs.readFileSync(path.join(__static, '/', fName), 'utf8');
var svg2 = rx.exec(svg);
return svg2[0];
}
const airLinerSVGs = {
A: stripSVG('FGA_ACT_A_GA.svg'),
B: stripSVG('FGA_ACT_B_PROP.svg'),
C: stripSVG('FGA_ACT_C.svg'),
D: stripSVG('FGA_ACT_D.svg'),
E: stripSVG('FGA_ACT_E.svg'),
F: stripSVG('FGA_ACT_F.svg')
};
L.ParkingAircraftMarker = L.Marker.extend({
options: {
zIndexOffset: 10000,
},
initialize: function (latlng, options) {
L.Marker.prototype.initialize(latlng, options);
L.Util.setOptions(this, options);
this.heading = options.heading;
this.updateIcon();
this.isDragging = false;
},
updateProperties: function(properties) {
this.heading = properties.heading;
this.updateIcon();
},
updateIcon : function() {
if(this._map !== undefined && this._map !== null) {
var metersPP = metersPerPixel(this._map.getCenter().lat, this._map.getZoom());
console.log(metersPP);
var scale = 0.07 / metersPP;
this.setIcon(L.divIcon({
iconSize: null,
className: 'aircraft-marker-icon',
html: `<div style=\'transform: translateX(-10px) translateY(-10px); height: 20px; width: 20px; border: 1px red\'>${airLinerSVG}</div>`,
}));
this.getElement().children.item(0).children.item(0).style = `transform: translateX(-200px) translateY(-200px) scale(${scale},${scale}) rotate(${(this.heading)-45}deg)`;
}
else {
this.setIcon(L.divIcon({
iconSize: null,
className: 'aircraft-marker-icon',
html: `<div style=\'transform: rotate(${this.heading}deg) scale(0.001,0.001) \'>${airLinerSVG}</div>`,
}));
}
},
onAdd : function(map) {
var metersPP = metersPerPixel(map.getCenter().lat, map.getZoom());
console.log(metersPP);
console.log(this);
this.updateIcon();
},
});
//Builds a marker for a ai or multiplayer aircraft
module.exports.default = function (latlng, options) {
return new L.ParkingAircraftMarker(latlng, options);
}
/*
var l1 = feature.properties.callsign,
l2 = feature.properties.heading + 'T ' + feature.properties.speed + 'KTAS ' +
formatFL(feature.geometry.coordinates[2]),
l3 = feature.properties.departureAirportId + ' -> ' + feature.properties.arrivalAirportId;
*/

View File

@@ -5,41 +5,66 @@ const turf = require('@turf/turf');
const util = require('util');
const store = require('../store');
/**
* Cat Models FG Radii N2M Radii
* B Small Regionals ERJ CRJ ATR 14 6
* C A319 A320 A321 B737 18 10
* D B757, B767 26 15
* E B777 B787 A330 A340 A360 33 24
* F A380 40 24
*/
// ratchet to known radii
const validRadii = [7.5, 10, 14, 18, 26, 33, 40];
const validN2M = [5, 5, 6, 10, 15, 24, 24];
var $ = require('jquery');
L.ParkingSpot = L.Circle.extend({
createDirection: function () {
if (this.direction === undefined ) {
var start = this._latlng;
var center = this._latlng;
var options = { units: 'kilometers' };
var end = turf.destination([start.lng, start.lat], this.options.attributes.radius / 1000, this.options.attributes.heading - 180, options);
// Resize, since leaflet is wrong
var rad2 = start.distanceTo(this.turfToLatLng(end), options);
console.debug('Dist ', start, [start.lng, start.lat], end.geometry.coordinates, this.options.attributes.radius, rad2);
var start = turf.destination([center.lng, center.lat], this.options.attributes.radius / 1000, this.normalizeAngle(this.options.attributes.heading+180), options);
var end = turf.destination([center.lng, center.lat], this.options.attributes.radius / 1000, this.normalizeAngle(this.options.attributes.heading), options);
// Resize, since leaflet is wrong
var rad2 = center.distanceTo(this.turfToLatLng(end), options);
console.debug('Dist ', center, [center.lng, center.lat], end.geometry.coordinates, this.options.attributes.radius, rad2);
this.setRadius(rad2);
// console.log(util.inspect(this.editor));
if(this.editor._resizeLatLng.__vertex !== undefined){
this.editor._resizeLatLng.__vertex.setLatLng(this.turfToLatLng(end));
}
this.direction = L.polyline([start, this.turfToLatLng(end)]);
this.direction = L.polyline([this.turfToLatLng(start), this.turfToLatLng(end)]);
this.direction.addTo(this.editor.editLayer);
this.frontWheel = L.circleMarker(center, {radius:4, weight: 2 });
this.frontWheel.addTo(this.editor.editLayer);
this.updateWheelPos();
this.updateBox();
const parkingSize = validRadii.indexOf(this.options.attributes.radius);
if(parkingSize>=0) {
this.setStyle({ opacity: 0, fill: false });
}
}
},
updateMiddleMarker: function() {
if (this.editEnabled()) {
try {
console.log("Update Middle ", this.editor.editLayer._layers[0]);
console.debug("Update Middle ", this.editor.editLayer._layers[0]);
var o = this.editor.editLayer._layers;
console.log(o);
console.debug(o);
for (var key in o) {
if (o.hasOwnProperty(key)) {
console.log(key, o[key]);
console.log(this.editor._resizeLatLng.__vertex==o[key]);
// console.log(this.editor._resizeLatLng.__vertex._icon);
console.log(o[key] == this.direction);
console.debug(key, o[key]);
console.debug(this.editor._resizeLatLng.__vertex==o[key]);
// console.debug(this.editor._resizeLatLng.__vertex._icon);
console.debug(o[key] == this.direction);
if (this.editor._resizeLatLng.__vertex!=o[key] &&
o[key] != this.direction) {
o[key] != this.direction &&
o[key] != this.frontWheel &&
o[key] != this.box) {
o[key].setLatLng(this.getLatLng());
}
}
@@ -47,135 +72,321 @@ L.ParkingSpot = L.Circle.extend({
//Object.values(o);
/*
.forEach(vertex => {
console.log(this.editor._resizeLatLng.__vertex==vertex);
console.debug(this.editor._resizeLatLng.__vertex==vertex);
});
*/
*/
} catch (error) {
console.log(error);
console.error(error);
}
}
},
removeDirection: function () {
removeDirection() {
this.direction = undefined;
},
//
updateVertexFromDirection: function () {
updateHeading(heading) {
this.options.attributes.heading = heading;
this.updateVertexFromDirection();
this.updateWheelPos();
this.updateBox();
},
updateRadius(radius) {
this._mRadius = radius;
this.updateDirectionFromVertex();
this.updateVertexFromDirection();
this.updateWheelPos();
this.updateBox();
},
updateType(type) {
this.options.attributes.type = type;
this.deselect();
},
// Update the direction vertex from the direction
updateVertexFromDirection() {
if (this.editEnabled()) {
var start = this._latlng;
var center = this._latlng;
var options = { units: 'kilometers' };
var end = turf.destination([start.lng, start.lat], this.options.attributes.radius / 1000, this.options.attributes.heading - 180, options);
var start = turf.destination([center.lng, center.lat], this.options.attributes.radius / 1000, this.normalizeAngle(this.options.attributes.heading+180), options);
var end = turf.destination([center.lng, center.lat], this.options.attributes.radius / 1000, this.normalizeAngle(this.options.attributes.heading), options);
// Resize, since leaflet is wrong
var rad2 = start.distanceTo(this.turfToLatLng(end), options);
var rad2 = center.distanceTo(this.turfToLatLng(end), options);
this.setRadius(rad2);
if(this.editor._resizeLatLng.__vertex!== undefined){
this.editor._resizeLatLng.__vertex.setLatLng(this.turfToLatLng(end));
}
this.direction.setLatLngs([start, this.turfToLatLng(end)]);
this.direction.setLatLngs([this.turfToLatLng(start), this.turfToLatLng(end)]);
}
},
updateDirectionFromVertex: function () {
// Update the direction from the moved direction vertex
updateDirectionFromVertex() {
if (this.editEnabled()) {
var start = this._latlng;
var end = this.editor._resizeLatLng.__vertex.getLatLng();
var heading = turf.bearing([start.lng, start.lat], [end.lng, end.lat]);
this.options.attributes.heading = heading + 180;
const counts = [7.5, 12, 18, 26, 32.5, 40];
this.options.attributes.heading = heading;
const output = validRadii.reduce((prev, curr) => Math.abs(curr - this._mRadius) < Math.abs(prev - this._mRadius) ? curr : prev);
const output = counts.reduce((prev, curr) => Math.abs(curr - this._mRadius) < Math.abs(prev - this._mRadius) ? curr : prev);
console.debug('Found radius ' + output);
console.log(output);
this._mRadius = output;
this.options.attributes.radius = this._mRadius;
this.direction.setLatLngs([start, end]);
}
},
highlight() {
updateWheelPos() {
var start = this._latlng;
var options = { units: 'kilometers' };
const parkingSize = validRadii.indexOf(this.options.attributes.radius);
if (parkingSize>=0) {
var frontWheelEnd = turf.destination([start.lng, start.lat], validN2M[parkingSize] / 1000, this.options.attributes.heading, options);
if(this.frontWheel!==undefined) {
this.frontWheel.setLatLng(this.turfToLatLng(frontWheelEnd));
return this.turfToLatLng(frontWheelEnd);
}
}
},
updateBox() {
var start = [this._latlng.lng, this._latlng.lat];
var options = { units: 'kilometers' };
const parkingSize = validRadii.indexOf(this.options.attributes.radius);
var backwards = this.normalizeAngle(this.options.attributes.heading + 180);
var left = this.normalizeAngle(this.options.attributes.heading - 90);
var right = this.normalizeAngle(this.options.attributes.heading + 90);
var radiusKM = this.options.attributes.radius / 1000;
var halfRadiusKM = radiusKM/2;
var thirdRadiusKM = radiusKM/3;
if (parkingSize>=0) {
this.setStyle({ opacity: 0, fill: false });
var frontWheelEnd = turf.destination(start, validN2M[parkingSize] / 1000, this.options.attributes.heading, options);
var front = turf.destination(start, radiusKM, this.options.attributes.heading, options);
var back = turf.destination(start, radiusKM, backwards, options);
var leftBack = turf.destination(back, radiusKM, left, options);
var rightBack = turf.destination(back, radiusKM, right, options);
var leftMiddle = turf.destination(start, radiusKM, left, options);
var rightMiddle = turf.destination(start, radiusKM, right, options);
var leftFront = turf.destination(front, thirdRadiusKM, left, options);
var rightFront = turf.destination(front, thirdRadiusKM, right, options);
var leftIntermediate = turf.destination(leftFront, halfRadiusKM, backwards, options);
var rightIntermediate = turf.destination(rightFront, halfRadiusKM, backwards, options);
if(this.box === undefined) {
var latlngs = [leftBack, rightBack, rightMiddle, rightIntermediate, rightFront, leftFront, leftIntermediate, leftMiddle].map(l => this.turfToLatLng(l));
this.box = L.polygon(latlngs);
//this.box.addTo(this.editor.editLayer);
this.box._parkingSpot = this;
this.box.on('click', function (event) {
console.debug("Click Parking Box : " + event.target);
if (Number(store.default.state.Editable.index) >= 0 &&
event.target._parkingSpot.featureLookup !== undefined &&
event.target._parkingSpot.featureLookup[store.default.state.Editable.index]!==undefined) {
event.target._parkingSpot.featureLookup[store.default.state.Editable.index].forEach(element => {
if(element.deselect !== undefined) {
element.deselect();
}
});
}
event.target._parkingSpot.select();
});
if(this.editor && this.editor.editLayer) {
this.box.addTo(this.editor.editLayer);
}
}
if(this.box!==undefined) {
var latlngs = [leftBack, rightBack, rightMiddle, rightIntermediate, rightFront, leftFront, leftIntermediate, leftMiddle].map(l => this.turfToLatLng(l));
console.debug(latlngs);
this.box.setLatLngs(latlngs);
}
}
},
normalizeAngle( angle ) {
if(angle >= 180) {
return angle - 360;
}
if(angle <= -180) {
return angle + 360;
}
return angle;
},
select() {
store.default.dispatch('setParking', this.options.attributes);
store.default.dispatch('setParkingCoords', this.getLatLng().lat.toFixed(6) + ' ' + this.getLatLng().lng.toFixed(6));
var style = {};
style['color'] = 'red';
this.setStyle(style);
},
if(this.direction) {
this.direction.setStyle(style);
this.frontWheel.setStyle(style);
}
var wheelPos = this.updateWheelPos();
if(wheelPos) {
store.default.dispatch('setParkingNoseCoords', wheelPos.lat.toFixed(6) + ' ' + wheelPos.lng.toFixed(6));
}
this.updateBox();
if(this.box) {
this.box.setStyle(style);
}
},
deselect() {
var style = {};
if(this.options.attributes.type == 'ga') {
style['color'] = 'green';
} else if(this.options.attributes.type == 'cargo') {
style['color'] = 'yellow';
} else if(this.options.attributes.type == 'gate') {
style['color'] = '#3388ff';
} else if(this.options.attributes.type == 'mil-fighter') {
style['color'] = 'red';
} else if(this.options.attributes.type == 'mil-cargo') {
style['color'] = 'DarkRed';
} else {
style['color'] = '#3388ff';
}
this.setStyle(style);
if(this.direction) {
this.direction.setStyle(style);
this.frontWheel.setStyle(style);
}
this.updateWheelPos();
this.updateBox();
if(this.box) {
this.box.setStyle(style);
}
},
setInteractive(interactive) {
if (interactive) {
if(this.direction&&this.direction._path) {
L.DomUtil.addClass(this.direction._path, 'leaflet-interactive');
}
if(this.box&&this.box._path) {
L.DomUtil.addClass(this.box._path, 'leaflet-interactive');
}
} else {
if(this.direction&&this.direction._path) {
L.DomUtil.removeClass(this.direction._path, 'leaflet-interactive');
}
if(this.box&&this.box._path) {
L.DomUtil.removeClass(this.box._path, 'leaflet-interactive');
}
}
},
addListeners: function () {
this.on('editable:drawing:move', function (event) {
console.log("Move : ", event);
console.log("Move : ", event.latlng);
console.debug("Move Parking Spot: ", event);
console.debug("Move Parking Spot : ", event.latlng);
// Is it the edit vertex (Middle) moving?
if(event.target.editor._resizeLatLng.__vertex._icon !== event.sourceTarget._element){
event.target.setLatLng(event.latlng);
event.target.updateVertexFromDirection();
this.follow(event.target.id, event);
event.target.updateWheelPos();
event.target.updateBox();
this.follow(event.target.id, event);
}
else if(event.target.editor._resizeLatLng.__vertex._icon === event.sourceTarget._element) {
event.target.updateDirectionFromVertex();
event.target.updateVertexFromDirection();
event.target.updateDirectionFromVertex();
event.target.updateVertexFromDirection();
event.target.updateWheelPos();
event.target.updateBox();
}
});
/*
this.on('editable:vertex:drag', function (event) {
console.log("Drag : ", event);
this.on('add', function (event) {
console.debug(event);
event.target.updateBox();
if(event.target.box !== undefined) {
event.target.box.addTo(event.target._map);
}
event.target.setInteractive(false);
});
*/
this.on('click', function (event) {
console.log("Click : " + event.target);
this.on('remove', function (event) {
console.debug(event);
if(event.target.box !== undefined) {
event.target.box.removeFrom(event.target._map);
}
});
this.on('editable:vertex:drag', function (event) {
console.debug("Drag Parking : ", event);
});
this.on('editable:vertex:dragend', function (event) {
console.debug("DragEnd Parking : ", event);
store.default.dispatch('setParking', event.target.options.attributes);
this.highlight();
this.unwatch = store.default.watch(
function (state) {
return state.Editable.data.parking;
},
() => {
if (event.target instanceof L.ParkingSpot) {
event.target.setStyle({color : '#3388ff'});
this.unwatch();
}
}
,
{
deep: true //add this if u need to watch object properties change etc.
}
);
});
store.default.dispatch('setParkingCoords', event.target.getLatLng().lat.toFixed(6) + ' ' + event.target.getLatLng().lng.toFixed(6));
var wheelPos = event.target.updateWheelPos();
store.default.dispatch('setParkingNoseCoords', wheelPos.lat.toFixed(6) + ' ' + wheelPos.lng.toFixed(6));
event.target.updateBox();
/*
store.default.dispatch('setParkingHeading', this.options.attributes.heading)
store.default.dispatch('setParkingRadius', this.options.attributes.radius)
*/
});
this.on('click', function (event) {
console.debug("Click Parking : " + event.target);
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index]!==undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
if(element.deselect !== undefined) {
element.deselect();
}
});
}
event.target.select();
});
this.on('editable:vertex:clicked', function (event) {
console.log(this.featureLookup[event.vertex.glueindex]);
console.debug(this.featureLookup[event.vertex.glueindex]);
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index]!==undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
if(element.deselect !== undefined) {
element.deselect();
}
});
}
if(event.target.editor._resizeLatLng.__vertex._icon !== event.sourceTarget._element){
event.vertex._icon.style['background-color'] = 'red';
store.default.dispatch('setParking', event.target.options.attributes);
this.unwatch = store.default.watch(
function (state) {
return state.Editable.data.parking;
},
() => {
event.target.setStyle({color : '#3388ff'});
this.unwatch();
}
,
{
deep: true //add this if u need to watch object properties change etc.
}
);
this.select();
}
});
this.on('editable:disable', function (event) {
event.target.removeDirection();
});
});
},
updateStyle: function () {
},
selectParking() {
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index]!==undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
if(element.deselect !== undefined) {
element.deselect();
}
});
}
this.select();
},
turfToLatLng: function (turfPoint) {
return {lat: turfPoint.geometry.coordinates[1], lng: turfPoint.geometry.coordinates[0]};
},
extensions: function (editLayer) {
this.createDirection();
this.createDirection();
if (typeof this.featureLookup[this.id] === 'undefined') {
this.featureLookup[this.id] = [];
}
this.featureLookup[this.id].push(this);
},
/**
*
*
*/
follow (dragIndex, event) {
@@ -194,6 +405,8 @@ L.ParkingSpot = L.Circle.extend({
// element.extensions();
element.updateMiddleMarker();
element.updateVertexFromDirection();
element.updateWheelPos();
element.updateBox();
}
else if (element instanceof L.TaxiwaySegment) {
if (element.begin === dragIndex) {
@@ -209,17 +422,17 @@ L.ParkingSpot = L.Circle.extend({
element.updateMiddle();
}
} else if (element instanceof L.Editable.VertexMarker) {
console.log(element);
console.debug(element);
element.setLatLng(event.latlng);
element.latlngs.forEach((latlng, index) => {
console.log(latlng);
console.debug(latlng);
if (latlng.__vertex === element) {
latlng.update(event.latlng);
}
});
element.editor.feature.setLatLngs(element.latlngs);
element.editor.feature.updateMiddle();
}
}
}
})
},
@@ -239,12 +452,14 @@ var parkingSpot = function (n, layerGroup) {
var latlon = convert(n.attr('lat') + " " + n.attr('lon'));
//console.log(latlon.decimalLatitude);
//console.log(convert(n.attr('lat') + " " + n.attr('lon')).decimalLongitude);
const circle = new L.ParkingSpot([latlon.decimalLatitude, latlon.decimalLongitude], { radius: n.attr('radius'), attributes: {} });
circle.on('editable:enable', function (event) {
const parking = new L.ParkingSpot([latlon.decimalLatitude, latlon.decimalLongitude], { radius: n.attr('radius'), attributes: {} });
parking.on('editable:enable', function (event) {
// event.target.createDirection();
});
circle.id = n.attr('index');
circle.glueindex = n.attr('index');
parking.id = n.attr('index');
parking.glueindex = n.attr('index');
parking.feature = { properties: { searchTerm: n.attr('index') + ' ' + n.attr('name')}};
/*
<Parking index="2"
type="gate"
@@ -254,23 +469,24 @@ lat="N44 52.799"
lon="W93 11.947"
heading="-147.51"
radius="18"
pushBackRoute="541"
pushBackRoute="541"
airlineCodes="VIR,KAL,DAL,KLM" />
*/
//circle.attributes = { type: n.attr('type'), name: n.attr('name'), radius: Number(n.attr('radius')), airlineCodes: n.attr('airlineCodes'), heading: Number(n.attr('heading')) };
$.each( n.attrs, function( key, value ) {
console.log( '$', circle.id, key , value);
console.debug( '$', parking.id, key , value);
if(isNaN(value))
circle.options.attributes[ key ] = value;
parking.options.attributes[ key ] = value;
else
circle.options.attributes[ key ] = Number( value);
parking.options.attributes[ key ] = Number( value);
});
circle.addListeners();
circle.addTo(layerGroup);
return circle;
parking.addListeners();
parking.addTo(layerGroup);
parking.deselect();
return parking;
}
module.exports = parkingSpot;

View File

@@ -15,38 +15,46 @@ L.RunwayNode = L.Marker.extend({
});
this.on('click', function (event) {
console.log("Click Runway : ", event);
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index]!==undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
element.deselect();
});
}
event.target.options.attributes.selected = true;
if (store.default.state.Editable.index !== event.target.options.attributes.index) {
store.default.dispatch('setRunway', event.target.options.attributes);
}
this.unwatch = store.default.watch(
function (state) {
return state.Editable.data.runway;
},
() => {
// Reset colour
if(event.target instanceof L.RunwayNode &&
event.target.options.attributes &&
event.target.options.attributes.selected) {
event.target.options.attributes.selected = false;
event.target.updateStyle();
this.unwatch();
}
}
,
{
deep: true //add this if u need to watch object properties change etc.
}
);
});
this.on('add', function (event) {
event.target.setInteractive(false);
});
},
highlight() {
var style = {};
style['color'] = 'red';
this.setStyle(style);
},
setInteractive(interactive) {
if (interactive) {
if(this._icon) {
L.DomUtil.addClass(this._icon, 'leaflet-interactive');
}
} else {
if(this._icon) {
L.DomUtil.removeClass(this._icon, 'leaflet-interactive');
}
}
},
select() {
try {
this._icon.style['color'] = 'red';
} catch (error) {
console.error(error);
}
},
deselect() {
try {
this._icon.style['color'] = 'black';
} catch (error) {
console.error(error);
}
},
extensions: function (editLayer) {
this.options.attributes = {};
if (typeof this.featureLookup[this.glueindex] === 'undefined') {
@@ -55,7 +63,7 @@ L.RunwayNode = L.Marker.extend({
this.featureLookup[this.glueindex].push(this);
},
/**
*
*
*/
follow(dragIndex, event) {
@@ -85,18 +93,18 @@ L.RunwayNode = L.Marker.extend({
element.updateEndVertex(event.latlng);
element.updateMiddle();
}
} else if (element instanceof L.Editable.VertexMarker) {
} else if (element instanceof L.Editable.VertexMarker) {
console.log(element);
element.setLatLng(event.latlng);
element.latlngs.forEach((latlng, index) => {
console.log(latlng);
console.log(latlng);
if(latlng.__vertex === element) {
latlng.update(event.latlng);
}
});
element.editor.feature.setLatLngs(element.latlngs);
element.editor.feature.updateMiddle();
}
}
}
})
}
@@ -112,7 +120,9 @@ var runwayNode = function (n, layerGroup) {
});
var node = new L.RunwayNode([latlon.decimalLatitude, latlon.decimalLongitude], { icon: icon, attributes: {} });
node.glueindex = n.attr('index');
$.each( n.attrs, function( key, value ) {
node.feature = n.attr('index');
$.each( n.attrs, function( key, value ) {
if(isNaN(value))
node.options.attributes[ key ] = value;
else

View File

@@ -1,152 +0,0 @@
/* eslint-disable */
var L = require('leaflet');
const store = require('../store');
L.TaxiwaySegment = L.Polyline.extend({
begin: String,
end: String,
bidirectional: Boolean,
updateBeginVertex: function (latlng) {
if (this._latlngs[0].__vertex) {
this._latlngs[0].__vertex.setLatLng(latlng);
}
},
updateEndVertex: function (latlng) {
if (this._latlngs[1].__vertex) {
this._latlngs[1].__vertex.setLatLng(latlng);
}
},
updateMiddle: function () {
this._latlngs.forEach(element => {
if (element.__vertex.middleMarker) {
element.__vertex.middleMarker.updateLatLng();
}
});
},
extensions: function (editLayer) {
this._latlngs[0].__vertex.glueindex = this.begin;
this._latlngs.slice(-1)[0].__vertex.glueindex = this.end;
if (typeof this.featureLookup[this.begin] === 'undefined') {
this.featureLookup[this.begin] = new Array();
}
if (typeof this.featureLookup[this.end] === 'undefined') {
this.featureLookup[this.end] = new Array();
}
this.featureLookup[this.begin].push(this);
this.featureLookup[this.end].push(this);
},
addListeners: function () {
this.on('click', function (event) {
console.log("Click : " + event.target);
store.default.dispatch('setArc', event.target.options.attributes);
store.default.watch( function (state) {
return state.Editable.data.arc;
},
() => {
console.log(this)
}
,
{
deep: true //add this if u need to watch object properties change etc.
});
});
this.on('editable:drawing:move', function (event) {
console.log(event.target);
if (dragIndex >= 0) {
this.follow(dragIndex, event);
}
});
this.on('editable:vertex:clicked', function (event) {
console.log(this.featureLookup[event.vertex.glueindex]);
store.default.dispatch('setNode', event.vertex.latlng.attributes)
event.vertex._icon.style['background-color'] = 'red';
});
var dragIndex = -1;
this.on('editable:vertex:dragstart', function (event) {
console.log("Event Target : ", event.target);
console.log("Middle Marker : ", event.vertex == event.vertex.middleMarker);
console.log("Middle Marker : ", event.vertex.glueindex == undefined);
if (event.vertex.glueindex == undefined)
return;
dragIndex = event.vertex.glueindex;
});
this.on('editable:vertex:dragend', function (event) {
console.log("Dragend : ", event.vertex);
if (dragIndex > 0) {
this.featureLookup[dragIndex].forEach(element => {
if (element instanceof L.ParkingSpot) {
//element.setLatLng(event);
console.log(element);
}
});
}
dragIndex = -1;
});
},
/**
*
*/
follow(dragIndex, event) {
this.featureLookup[dragIndex].forEach(element => {
if (element !== event.target) {
if (element instanceof L.RunwayNode) {
element.setLatLng(event.latlng);
}
else if (element instanceof L.HoldNode) {
element.setLatLng(event.latlng);
}
else if (element instanceof L.ParkingSpot) {
// element.disableEdit();
element.setLatLng(event.latlng);
// element.enableEdit();
// element.extensions();
element.updateMiddleMarker();
element.updateVertexFromDirection();
}
else if (element instanceof L.TaxiwaySegment) {
if (element.begin === dragIndex) {
element.getLatLngs()[0].update(event.latlng);
element.setLatLngs(element.getLatLngs());
element.updateBeginVertex(event.latlng);
element.updateMiddle();
}
if (element.end === dragIndex) {
element.getLatLngs()[element.getLatLngs().length - 1].update(event.latlng);
element.setLatLngs(element.getLatLngs());
element.updateEndVertex(event.latlng);
element.updateMiddle();
}
} else if (element instanceof L.Editable.VertexMarker) {
console.log(element);
element.setLatLng(event.latlng);
element.latlngs.forEach((latlng, index) => {
console.log(latlng);
if (latlng.__vertex === element) {
latlng.update(event.latlng);
}
});
element.editor.feature.setLatLngs(element.latlngs);
element.editor.feature.updateMiddle();
}
}
})
},
updateStyle() {
var style = {};
if (this.options.attributes.isPushBackRoute) {
style.color = 'magenta';
}
console.log("isPushBackRoute ", this.options.attributes.isPushBackRoute);
this.setStyle(style);
if (!this.bidirectional) {
this.setText(' ► ', { repeat: true, attributes: { fill: 'red', size: 20 } })
}
}
});

View File

@@ -1,25 +1,33 @@
/* eslint-disable */
const Vue = require('vue');
var L = require('leaflet');
const store = require('../store');
const util = require('util');
const assign = require('core-js/fn/object/assign');
exports.extendTaxiSegment = function (taxiwaySegment) {
const extendTaxiSegment = function (taxiwaySegment) {
taxiwaySegment.__proto__.begin;
taxiwaySegment.__proto__.end;
taxiwaySegment.__proto__.bidirectional;
taxiwaySegment.__proto__.updateBeginVertex = function (latlng) {
if (this._latlngs[0].__vertex) {
this._latlngs[0].__vertex.setLatLng(latlng);
this._latlngs[0].__vertex.setLatLng(latlng);
this.selectVertex(Number(this._latlngs[0].glueindex));
}
};
taxiwaySegment.__proto__.updateEndVertex = function (latlng) {
if(this._latlngs[1].__vertex){
this._latlngs[1].__vertex.setLatLng(latlng);
if (this._latlngs[1].__vertex) {
this._latlngs[1].__vertex.setLatLng(latlng);
this.selectVertex(Number(this._latlngs[1].glueindex));
}
};
taxiwaySegment.__proto__.updateMiddle = function () {
this._latlngs.forEach(element => {
if(element.__vertex.middleMarker){
if (element.__vertex && element.__vertex.middleMarker) {
element.__vertex.middleMarker.updateLatLng();
}
});
@@ -29,149 +37,443 @@ exports.extendTaxiSegment = function (taxiwaySegment) {
};
taxiwaySegment.__proto__.extensions = function (editLayer) {
this.editLayer = editLayer;
this._latlngs[0].__vertex.glueindex = this.begin;
this._latlngs.slice(-1)[0].__vertex.glueindex = this.end;
if (typeof this.featureLookup[this.begin] === 'undefined') {
this.featureLookup[this.begin] = new Array();
this._latlngs[0].glueindex = this.begin;
this._latlngs.slice(-1)[0].glueindex = this.end;
if (this.featureLookup) {
if (typeof this.featureLookup[this.begin] === 'undefined') {
this.featureLookup[this.begin] = new Array();
}
if (typeof this.featureLookup[this.end] === 'undefined') {
this.featureLookup[this.end] = new Array();
}
this.featureLookup[this.begin].push(this);
this.featureLookup[this.end].push(this);
this.bidirectional = true;
}
if (typeof this.featureLookup[this.end] === 'undefined') {
this.featureLookup[this.end] = new Array();
}
this.featureLookup[this.begin].push(this);
this.featureLookup[this.end].push(this);
this.bidirectional = true;
};
taxiwaySegment.__proto__.select = function () {
this.options.attributes.selected = true;
this.updateStyle();
};
taxiwaySegment.__proto__.selectVertex = function (index) {
this.getLatLngs().forEach(element => {
if (Number(element.glueindex) === index) {
if (element.__vertex !== undefined && element.__vertex._icon != null) {
element.__vertex.__proto__.deselect = function () {
if (this._icon != null) {
this._icon.style.setProperty('background-color', 'white');
this._icon.style.setProperty('color', 'white');
} else if (this.icon != null) {
if (this.icon.style != null) {
this.icon.style['background-color'] = 'white';
} else {
this.setStyle({ color: 'white' })
}
} else if (this.options.icon != null) {
if (this.options.icon.style != null) {
this.options.icon.style['background-color'] = 'white';
} else {
this.options.icon._setIconStyles({ color: 'white' })
}
}
}
element.__vertex._icon.style.setProperty('background-color', 'red');
element.__vertex._icon.style.setProperty('color', 'red');
} else if (element.__vertex !== undefined && element.__vertex.icon != null) {
if (element.__vertex.icon.style != null) {
element.__vertex.icon.style['background-color'] = 'red';
} else {
element.__vertex.setStyle({ color: 'red' })
}
} else if (element.__vertex.options.icon != null) {
if (element.__vertex.options.icon.style != null) {
element.__vertex.options.icon.style['background-color'] = 'red';
} else {
element.__vertex.options.icon._setIconStyles({ color: 'red' })
}
}
}
});
};
taxiwaySegment.__proto__.deselect = function () {
this.options.attributes.selected = false;
this.updateStyle();
this.getLatLngs().forEach(element => {
if (element.__vertex !== undefined) {
if (element.__vertex._icon != null) {
element.__vertex._icon.style['background-color'] = 'white';
} else if (element.__vertex.icon != null) {
if (element.__vertex.icon.style != null) {
element.__vertex.icon.style['background-color'] = 'white';
} else {
element.__vertex.setStyle({ color: 'white' })
}
} else if (element.__vertex.options.icon != null) {
if (element.__vertex.options.icon.style != null) {
element.__vertex.options.icon.style['background-color'] = 'white';
} else {
element.__vertex.options.icon._setIconStyles({ color: 'white' })
}
}
}
});
};
taxiwaySegment.__proto__.addListeners = function () {
this.on('click', function (event) {
event.target.setStyle({color : 'red'});
console.log("Click : " + event.target);
if (store.default.state.Editable.data.arc === undefined ||
store.default.state.Editable.data.arc !== event.target.options.attributes) {
if(event.target.options.attributes === undefined) {
event.target.options.attributes = {};
}
event.target.options.attributes.index = event.target._leaflet_id;
event.target.options.attributes.selected = true;
store.default.dispatch('setArc', event.target.options.attributes);
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index] !== undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
element.deselect();
});
}
this.unwatch = store.default.watch(
function (state) {
return state.Editable.data.arc;
},
() => {
// Reset colour
if(event.target instanceof L.Polyline &&
event.target.options.attributes &&
event.target.options.attributes.selected) {
event.target.options.attributes.selected = false;
event.target.updateStyle();
this.unwatch();
}
}
,
{
deep: true //add this if u need to watch object properties change etc.
event.target.select();
console.debug("Click : " + util.inspect(event.originalEvent));
if (!event.originalEvent.ctrlKey) {
if (store.default.state.Editable.data.arc === undefined ||
store.default.state.Editable.data.arc !== event.target.options.attributes) {
if (event.target.options.attributes === undefined) {
event.target.options.attributes = {};
}
event.target.options.attributes.index = event.target._leaflet_id;
this.editLayer.featureLookup[event.target._leaflet_id] = [];
this.featureLookup[event.target._leaflet_id].push(this);
event.target.options.attributes.selected = true;
store.default.dispatch('setArc', event.target.options.attributes);
}
);
} else {
var arcs = event.target.expandArc(event.target.options.attributes);
var multiarc = { name: '', index: 900719925474099, ids: [], isPushBackRoute: null, direction: null };
if (store.default.state.Editable.data.multiarc === undefined ||
store.default.state.Editable.data.multiarc !== event.target.options.attributes) {
if (event.target.options.attributes === undefined) {
event.target.options.attributes = {};
}
event.target.options.attributes.index = event.target._leaflet_id;
this.editLayer.featureLookup[event.target._leaflet_id] = [];
this.featureLookup[event.target._leaflet_id].push(this);
event.target.options.attributes.selected = true;
//multiarc.name = JSON.parse(JSON.stringify(event.target.options.attributes.name));
//multiarc.isPushBackRoute = JSON.parse(JSON.stringify(event.target.options.attributes.isPushBackRoute));
//multiarc.direction = JSON.parse(JSON.stringify(event.target.options.attributes.direction));
if (event.target.options.attributes.name !== undefined) {
multiarc.name = assign(event.target.options.attributes.name);
}
if (event.target.options.attributes.isPushBackRoute) {
multiarc.isPushBackRoute = assign(event.target.options.attributes.isPushBackRoute);
} else {
multiarc.isPushBackRoute = false;
}
multiarc.direction = assign(event.target.options.attributes.direction);
this.editLayer.featureLookup[900719925474099] = [];
multiarc.ids = [];
//TODO
store.default.dispatch('setMultiArc', multiarc);
}
var editLayer = this.editLayer;
arcs.forEach(id => {
console.debug(id);
var arc = editLayer.groundnetLayerGroup.getLayer(id);
if (arc && arc instanceof L.Polyline) {
editLayer.featureLookup[900719925474099].push(arc);
arc.select();
}
});
store.default.dispatch('setMultiArcIds', arcs);
}
});
this.on('editable:drawing:move', function (event) {
console.log(event.target);
if (dragIndex >= 0) {
this.selectVertex(dragIndex);
console.log('GlueDrag : ' + dragIndex + '\t' + event.target.dragIndex);
this.follow(dragIndex, event);
}
});
this.on('editable:middlemarker:mousedown', event => {
console.debug('editable:middlemarker:mousedown');
});
this.on('editable:vertex:new', event => {
console.log(event)
let closest = this.editLayer.closestLayerSnap(event.latlng, 10)
if (closest) {
event.latlng.__vertex['glueindex'] = Number(closest.glueindex);
event.latlng.__vertex.setLatLng(closest.latlng);
this.editLayer.featureLookup[event.latlng.__vertex.glueindex].push(event.latlng.__vertex);
console.log(closest)
} else {
event.latlng.__vertex['glueindex'] = ++this.editLayer.groundnetLayerGroup.maxId;
this.editLayer.featureLookup[event.latlng.__vertex.glueindex] = [];
this.editLayer.featureLookup[event.latlng.__vertex.glueindex].push(event.latlng.__vertex);
console.debug('editable:vertex:new ' + event.vertex.getIndex() + '\t' + event.vertex.getLastIndex() + '\t');
// Find nearest node
let isOnRunway = this.editLayer.isOnRunway(event.latlng)
let closest = this.editLayer.closestLayerSnap(event.latlng, 5)
let taxiwaySegment = event.vertex.editor.feature;
if (taxiwaySegment.options.attributes === undefined) {
taxiwaySegment.options.attributes = { direction: 'bi-directional' };
}
event.latlng.attributes = {index: event.latlng.__vertex.glueindex, isOnRunway: 0};
})
this.on('editable:vertex:clicked', function (event) {
console.log(this.featureLookup[event.vertex.glueindex]);
var isOnRunwayNum = 0;
if (isOnRunway) {
isOnRunwayNum = 1;
}
taxiwaySegment.updateStyle();
if (event.vertex.getIndex() !== 0 && event.vertex.getIndex() !== event.vertex.getLastIndex()) {
var x = taxiwaySegment.getLatLngs().filter(l => l === event.vertex.latlng);
// Somehow the latlng is not in our Segment!?
if (taxiwaySegment.getLatLngs().length < 3 && x.length === 0) {
var fixed = taxiwaySegment.getLatLngs();
fixed.splice(1, 0, event.vertex.latlng);
taxiwaySegment.setLatLngs(fixed);
}
var nextIndex = ++taxiwaySegment.editLayer.groundnetLayerGroup.maxId;
var splitOffNodes = taxiwaySegment.getLatLngs().splice(-1);
var remainingNodes = taxiwaySegment.getLatLngs();
if (remainingNodes.length <= 1 || !remainingNodes[1]) {
console.error('Not enough remaining nodes', remainingNodes);
}
splitOffNodes.unshift(L.latLng(remainingNodes[1].lat, remainingNodes[1].lng, remainingNodes[1].alt));
remainingNodes[1]['glueindex'] = nextIndex;
remainingNodes[1].attributes = { index: nextIndex, isOnRunway: isOnRunwayNum };
taxiwaySegment.options.attributes.end = nextIndex;
splitOffNodes[0]['glueindex'] = nextIndex;
splitOffNodes[0].attributes = { index: nextIndex, isOnRunway: isOnRunwayNum };
taxiwaySegment.setLatLngs(remainingNodes);
//taxiwaySegment.editor.refresh();
//taxiwaySegment.editor.reset();
if (splitOffNodes.length > 1) {
var polyline = new L.Polyline(splitOffNodes, { attributes: {} });
polyline.addTo(taxiwaySegment.editLayer.$parent.$parent.$refs.map.mapObject);
polyline.addTo(taxiwaySegment.editLayer.groundnetLayerGroup);
extendTaxiSegment(polyline);
polyline.addListeners();
polyline.setEditlayer(taxiwaySegment.editLayer);
polyline.enableEdit(taxiwaySegment.editLayer.$parent.$parent.$refs.map.mapObject);
polyline.editor.refresh();
//polyline.editor.reset();
polyline.featureLookup = this.featureLookup;
polyline.feature = { properties: {searchTerm: 'Arc ' + nextIndex + '-' + taxiwaySegment.end}};
store.default.dispatch('setNode', event.vertex.latlng.attributes)
if(event.vertex._icon!=null) {
event.vertex._icon.style['background-color'] = 'red';
} else if(event.vertex.icon!=null ) {
if(event.vertex.icon.style!=null) {
event.vertex.icon.style['background-color'] = 'red';
polyline.options.attributes.name = taxiwaySegment.options.attributes.name;
polyline.options.attributes.direction = taxiwaySegment.options.attributes.direction;
polyline.options.attributes.isPushBackRoute = taxiwaySegment.options.attributes.isPushBackRoute;
polyline.options.attributes.begin = nextIndex;
polyline.options.attributes.end = taxiwaySegment.end;
polyline.updateStyle();
polyline.begin = nextIndex;
polyline.end = taxiwaySegment.end;
taxiwaySegment.end = nextIndex;
this.editLayer.featureLookup[nextIndex] = [];
this.featureLookup[nextIndex].push(taxiwaySegment);
this.featureLookup[nextIndex].push(polyline);
} else {
event.vertex.setStyle({color : 'red'})
console.error('SplitoffNodes Short ', splitOffNodes);
}
} else if(event.vertex.options.icon!=null ) {
if(event.vertex.options.icon.style!=null) {
event.vertex.options.icon.style['background-color'] = 'red';
} else {
// Glue to another node
if (closest) {
event.latlng['glueindex'] = Number(closest.glueindex);
event.latlng.__vertex.setLatLng(closest.latlng);
event.latlng.attributes = { index: event.latlng.glueindex, isOnRunway: isOnRunwayNum };
// Push Vertex to lookup
this.editLayer.featureLookup[event.latlng.glueindex].push(event.latlng.__vertex);
if (isOnRunwayNum == 1) {
this.editLayer.addRunwayNode(event.latlng, event.latlng['glueindex'])
}
if (taxiwaySegment.options.attributes.begin === undefined) {
taxiwaySegment.options.attributes.begin = event.latlng['glueindex']
} else {
taxiwaySegment.options.attributes.end = event.latlng['glueindex']
}
if (taxiwaySegment.getLatLngs().length === 1) {
taxiwaySegment.begin = closest.glueindex;
}
taxiwaySegment.end = closest.glueindex;
console.debug(`Closest : ${closest}`)
} else {
event.vertex.options.icon._setIconStyles({color : 'red'})
event.vertex.latlng['glueindex'] = ++this.editLayer.groundnetLayerGroup.maxId;
event.vertex.latlng.attributes = { index: event.vertex.latlng.glueindex, isOnRunway: isOnRunwayNum };
this.editLayer.featureLookup[event.vertex.latlng.glueindex] = [];
this.editLayer.featureLookup[event.vertex.latlng.glueindex].push(event.vertex);
this.editLayer.featureLookup[event.vertex.latlng.glueindex].push(taxiwaySegment);
if (isOnRunwayNum == 1) {
this.editLayer.addRunwayNode(event.latlng, event.vertex.latlng['glueindex'])
}
// taxiwaySegment.editor.refresh();
//taxiwaySegment.editor.reset();
if (taxiwaySegment.options.attributes.begin === undefined) {
taxiwaySegment.options.attributes.begin = event.vertex.latlng['glueindex']
taxiwaySegment.begin = event.vertex.latlng.glueindex;
} else if (taxiwaySegment.options.attributes.end === undefined ||
(taxiwaySegment.getLatLngs()[taxiwaySegment.getLatLngs().length - 1].glueindex &&
Number(taxiwaySegment.getLatLngs()[taxiwaySegment.getLatLngs().length - 1].glueindex) !== taxiwaySegment.options.attributes.end)) {
taxiwaySegment.options.attributes.end = event.vertex.latlng['glueindex']
taxiwaySegment.end = Number(event.vertex.latlng.glueindex);
}
}
}
this.unwatch = store.default.watch(
function (state) {
return state.Editable.data.node;
},
() => {
if(event.vertex._icon!=null) {
event.vertex._icon.style['background-color'] = 'white';
} else if(event.vertex.icon!=null ) {
if(event.vertex.icon.style!=null) {
event.vertex.icon.style['background-color'] = 'white';
} else {
event.vertex.setStyle({color : 'white'})
}
} else if(event.vertex.options.icon!=null ) {
if(event.vertex.options.icon.style!=null) {
event.vertex.options.icon.style['background-color'] = 'white';
} else {
event.vertex.options.icon._setIconStyles({color : 'white'})
}
}
this.unwatch();
}
,
{
deep: true //add this if u need to watch object properties change etc.
}
);
}
//this.splitShape(taxiwaySegment.getLatLngs(), )
});
var dragIndex = -1;
this.on('editable:vertex:dragstart', function (event) {
console.log("Event Target : ", event.target);
console.log("Middle Marker : ", event.vertex == event.vertex.middleMarker);
console.log("Middle Marker : ", event.vertex.glueindex == undefined);
if(event.vertex.glueindex == undefined)
return;
dragIndex = event.vertex.glueindex;
this.on('editable:vertex:deleted', event => {
console.debug('editable:vertex:deleted')
});
this.on('editable:vertex:dragend', function (event) {
console.log("Dragend : ", event.vertex);
if (dragIndex > 0) {
this.featureLookup[dragIndex].forEach(element => {
if (element instanceof L.ParkingSpot) {
//element.setLatLng(event);
console.log(element);
this.on('editable:vertex:mousedown', event => {
console.debug('editable:vertex:mousedown')
event.layer.editor.map.fire('mousedown', event);
});
this.on('editable:vertex:click', event => {
console.debug('editable:vertex:click')
});
this.on('editable:vertex:rawclick', event => {
console.debug('editable:vertex:rawclick')
event.cancel()
});
this.on('editable:vertex:clicked', function (event) {
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index] !== undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
if (element.deselect !== undefined) {
element.deselect();
}
});
}
dragIndex = -1;
if (!this.editor.map.editTools.drawing()) {
var hold = this.featureLookup[event.vertex.latlng.glueindex].filter(n => n instanceof L.HoldNode);
if (hold.length > 0) {
hold[0].select();
}
var parking = this.featureLookup[event.vertex.latlng.glueindex].filter(n => n instanceof L.ParkingSpot);
if (parking.length > 0) {
parking[0].selectParking();
} else {
this.editLayer.featureLookup[event.vertex.latlng.glueindex].forEach
store.default.dispatch('setNode', event.vertex.latlng)
this.selectVertex(store.default.state.Editable.index)
}
}
});
var dragIndex = -1;
this.on('editable:vertex:dragstart', function (event) {
console.log("Drag Start : ", event.target);
console.log("Middle Marker : ", event.vertex == event.vertex.middleMarker, event.vertex.latlng.glueindex == undefined);
if (event.vertex.latlng.glueindex == undefined)
return;
console.log("Drag Start : ", event.vertex.latlng.glueindex);
dragIndex = event.vertex.latlng.glueindex;
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index] !== undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
if (element.deselect !== undefined) {
element.deselect();
}
});
}
if (!this.editor.map.editTools.drawing()) {
var hold = this.featureLookup[event.vertex.latlng.glueindex].filter(n => n instanceof L.HoldNode);
if (hold.length > 0) {
hold[0].select();
}
var parking = this.featureLookup[event.vertex.latlng.glueindex].filter(n => n instanceof L.ParkingSpot);
if (parking.length > 0) {
parking[0].selectParking();
} else {
this.selectVertex(Number(dragIndex))
}
}
});
this.on('editable:vertex:dragend', function (event) {
console.log("Dragend : ", event.vertex);
try {
if (dragIndex > 0) {
event.target.featureLookup[dragIndex].forEach(element => {
if (element instanceof L.ParkingSpot) {
//element.setLatLng(event);
console.log(element);
}
});
}
dragIndex = -1;
if (!event.vertex.latlng.glueindex) {
console.error('GlueIndex not found : ', event.vertex);
}
var parking = this.featureLookup[event.vertex.latlng.glueindex].filter(n => n instanceof L.ParkingSpot);
if (parking.length > 0) {
parking[0].selectParking();
} else {
if (Number(event.vertex.latlng.glueindex) !== store.default.state.Editable.index) {
if (Number(store.default.state.Editable.index) >= 0 &&
this.featureLookup[store.default.state.Editable.index] !== undefined) {
this.featureLookup[store.default.state.Editable.index].forEach(element => {
if (element.deselect !== undefined) {
element.deselect();
}
});
}
store.default.dispatch('setNode', event.vertex.latlng)
}
var lines = this.featureLookup[event.vertex.latlng.glueindex].filter(n => n instanceof L.Polyline);
Vue.default.nextTick(function () {
lines.forEach(line => {
line.selectVertex(store.default.state.Editable.index)
});
})
}
} catch (error) {
console.error(error);
console.error(event.vertex.latlng.glueindex);
}
});
};
/**
*
*/
taxiwaySegment.__proto__.follow = function (dragIndex, event) {
taxiwaySegment.__proto__.expandArc = function (attributes) {
var isPushBackRoute = attributes.isPushBackRoute;
var ids = [];
var walkedNodes = [];
var segmentIds = [];
console.debug('start Walk');
ids = ids.concat(this.walkPushbackRoute(attributes.begin, walkedNodes, isPushBackRoute));
ids = ids.concat(this.walkPushbackRoute(attributes.end, walkedNodes, isPushBackRoute));
return ids;
}
taxiwaySegment.__proto__.walkPushbackRoute = function (index, walkedNodes, isPushBackRoute) {
console.debug('Walk Level');
walkedNodes.push(index)
var segmentIds = [];
var polyLines = this.featureLookup[index].filter(n => n instanceof L.Polyline);
if (polyLines === undefined || polyLines.length > 2) {
console.debug('Walk ' + index + '\t' + polyLines.length);
return;
}
polyLines.forEach(l => {
segmentIds.push(l._leaflet_id);
console.debug('Walk Next ' + index + '\t' +
(walkedNodes.indexOf(index) < 0) + '\t' +
l.begin + '\t' +
(walkedNodes.indexOf(Number(l.begin)) < 0) + '\t' +
l.end + '\t' +
(walkedNodes.indexOf(Number(l.end)) < 0) + '\t' +
l.options.attributes.direction + '\t' +
l.options.attributes.begin + '\t' +
l.options.attributes.end);
console.debug('Walk isPushBackRoute ' + l.options.attributes.isPushBackRoute + '\t' + isPushBackRoute + '\t' + (l.options.attributes.isPushBackRoute === isPushBackRoute));
if (l.options.attributes.isPushBackRoute === isPushBackRoute) {
console.debug(Number(l.begin) === index && walkedNodes.indexOf(Number(l.end)) < 0);
if (Number(l.begin) === index && walkedNodes.indexOf(Number(l.end)) < 0) {
console.debug('Walk forward ' + l.options.attributes.direction);
segmentIds = segmentIds.concat(this.walkPushbackRoute(Number(l.end), walkedNodes, isPushBackRoute))
}
console.debug(Number(l.end) === index && walkedNodes.indexOf(Number(l.begin)) < 0);
if (Number(l.end) === index && walkedNodes.indexOf(Number(l.begin)) < 0) {
console.debug('Walk backward ' + l.options.attributes.direction);
segmentIds = segmentIds.concat(this.walkPushbackRoute(Number(l.begin), walkedNodes, isPushBackRoute))
}
}
});
return segmentIds;
}
/**
*
*/
taxiwaySegment.__proto__.follow = function (dragIndex, event) {
this.featureLookup[dragIndex].forEach(element => {
if(element !== event.target){
if (element !== event.target) {
if (element instanceof L.RunwayNode) {
element.setLatLng(event.latlng);
}
@@ -185,6 +487,8 @@ exports.extendTaxiSegment = function (taxiwaySegment) {
// element.extensions();
element.updateMiddleMarker();
element.updateVertexFromDirection();
element.updateWheelPos();
element.updateBox();
}
else if (element instanceof L.Polyline) {
if (Number(element.begin) === Number(dragIndex)) {
@@ -199,7 +503,8 @@ exports.extendTaxiSegment = function (taxiwaySegment) {
element.updateEndVertex(event.latlng);
element.updateMiddle();
}
} else if (element instanceof L.Editable.VertexMarker) {
} else if (element instanceof L.Editable.VertexMarker &&
element !== event.vertex) {
console.log(element);
element.setLatLng(event.latlng);
element.latlngs.forEach((latlng, index) => {
@@ -210,26 +515,90 @@ exports.extendTaxiSegment = function (taxiwaySegment) {
});
element.editor.feature.setLatLngs(element.latlngs);
element.editor.feature.updateMiddle();
}
}
}
})
};
taxiwaySegment.__proto__.updateStyle = function() {
taxiwaySegment.__proto__.updateArrows = function (zoom) {
if (this._map === null) {
return;
}
if (this.options.attributes.direction === 'forward') {
this.setText(null);
if (zoom <= 16) {
this.setText(' >', { repeat: true, offset: 6, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 15px serif;' } })
} else if (zoom <= 19) {
this.setText(' > ', { repeat: true, offset: 7, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 20px serif;' } })
} else {
this.setText(' > ', { repeat: true, offset: 10, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 30px serif;' } })
}
} else if (this.options.attributes.direction === 'backward') {
this.setText(null);
if (zoom <= 16) {
this.setText(' <', { repeat: true, offset: 6, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 15px serif;' } })
} else if (zoom <= 19) {
this.setText(' < ', { repeat: true, offset: 7, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 20px serif;' } })
} else {
this.setText(' < ', { repeat: true, offset: 10, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 30px serif;' } })
}
} else {
this.setText(null);
}
}
taxiwaySegment.__proto__.setInteractive = function (interactive) {
if (this.getLayers) {
this.getLayers().forEach(layer => {
layer.setInteractive(interactive);
});
return;
}
if (!this._path) {
return;
}
this.options.interactive = interactive;
if (interactive) {
L.DomUtil.addClass(this._path, 'leaflet-interactive');
} else {
L.DomUtil.removeClass(this._path, 'leaflet-interactive');
}
};
taxiwaySegment.__proto__.updateStyle = function () {
var style = {};
if(this.options.attributes.selected){
style.color = 'red';
} else if (this.options.attributes.isPushBackRoute) {
style.color = 'magenta';
if (!this.options.attributes) {
return;
}
if (this.options.attributes.selected) {
style.color = 'red';
} else if (this.options.attributes.isPushBackRoute) {
style.color = 'magenta';
}
else {
style.color = '#3388ff';
style.color = '#3388ff';
}
if (this.editEnabled()) {
style.interactive = true;
} else {
style.interactive = false;
}
this.setStyle(style);
if (!this.bidirectional) {
this.setText(' ► ', {repeat: true, attributes: {fill: 'red', size: 20}})
} else {
this.setText('')
}
if (this._map !== null) {
if (this.options.attributes.direction === 'forward') {
this.setText(null);
this.setText(' > ', { repeat: true, offset: 10, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 30px serif;' } })
} else if (this.options.attributes.direction === 'backward') {
this.setText(null);
this.setText(' < ', { repeat: true, offset: 10, attributes: { fill: 'red', style: 'vertical-align: bottom; vertical-align: bottom; font-weight: bold; font: bold 30px serif;' } })
} else {
this.setText(null);
}
}
};
};
exports.extendTaxiSegment = extendTaxiSegment;

View File

@@ -0,0 +1,164 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const convert = require('geo-coordinates-parser');
const fs = require('fs');
const path = require('path');
const store = require('../store');
const turf = require('@turf/turf');
/**http://wiki.openstreetmap.org/wiki/Zoom_levels*/
L.Threshold = L.Marker.extend({
heading: 0,
displacement: 0,
stopw_m: 0,
originLatLng: null,
rwy: '',
interactive: false,
stripSVG: function(fName) {
var rx = /<\s*svg[^>]*>([\s\S]*)<\s*\/svg[^>]*>/gm;
var svg = fs.readFileSync(path.join(__static, '/', fName), 'utf8');
var svg2 = rx.exec(svg);
return svg2[0];
},
updateIcon : function(map) {
console.debug(`Lat Lng Threshold ${this.getLatLng()}`);
if(map !== null) {
var metersPP = this.metersPerPixel(map.getCenter().lat, map.getZoom());
console.debug('Old Meters per pixel ' + this._metersPP);
console.debug('New Meters per pixel ' + metersPP);
if(this._metersPP != metersPP) {
var pixelSize = (this.iconSize/2) / metersPP;
var scale = pixelSize/this.iconSize;
var offset = 0;//-(this.iconSize/2);
this.setIcon(L.divIcon({
iconSize: 64,
className: 'threshold-marker-icon',
html: `<div style=\'transform: translateX(${offset}px) translateY(${offset}px) scale(${scale}) rotate(${this.heading}deg); border: 1px red\'>${this.svg}</div>`,
}));
this.setInteractive(this.interactive);
this.update(this.getLatLng());
this.setLatLng(this.getLatLng());
this._metersPP = metersPP;
}
}
},
setInteractive(interactive) {
if (interactive) {
if(this._icon) {
L.DomUtil.addClass(this._icon, 'leaflet-interactive');
}
} else {
if(this._icon) {
L.DomUtil.removeClass(this._icon, 'leaflet-interactive');
}
}
this.interactive = interactive;
},
metersPerPixel: function (latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI / 180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel + 8);
},
pixelValue: function (latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
},
setOrigin (originLatLng) {
this.originLatLng = originLatLng;
},
setHeading (heading) {
this.heading = Number(heading);
},
setRunway (rwy) {
this.rwy = Number(rwy);
},
setStopW (stopw_m) {
this.stopw_m = stopw_m;
},
setDisplacement(displacement) {
const turfOptions = { units: 'kilometers' };
this.displacement = Number(displacement);
var newPos = turf.destination([this.originLatLng[1], this.originLatLng[0]], displacement/1000, this.normalizeAngle(this.heading), turfOptions);
var newValue = {lat: newPos.geometry.coordinates[1].toFixed(6),
lng: newPos.geometry.coordinates[0].toFixed(6) };
console.debug(`Threshold Old : ${this.originLatLng} -> ${this.turfToLatLng(newPos)}`);
this.setLatLng(newValue);
},
normalizeAngle( angle ) {
if(angle >= 180) {
return angle - 360;
}
if(angle <= -180) {
return angle + 360;
}
return angle;
},
latToTurf (turfPoint) {
return [turfPoint.lng, turfPoint.lat];
},
latLngToTurf (turfPoint) {
return [turfPoint.decimalLongitude, turfPoint.decimalLatitude];
},
turfToLatLng: function (turfPoint) {
return '' + turfPoint.geometry.coordinates[1].toFixed(6) + ',' + turfPoint.geometry.coordinates[0].toFixed(6);
}
});
L.Threshold.addInitHook(function(){
this.svg = this.stripSVG('FGA_THR.svg');
this.iconSize = 500;
this.on('click', function (event) {
console.debug("Click Threshold : ", event);
store.default.dispatch('setThreshold', {rwy: event.target.rwy, displacement: event.target.displacement});
});
this.on('add', function (event) {
event.target.setInteractive(false);
});
});
//Builds a marker for a threshold
/*
<threshold>
<lon>13.517142</lon>
<lat>52.380125</lat>
<rwy>07L</rwy>
<hdg-deg>68.77</hdg-deg>
<displ-m>0.0</displ-m>
<stopw-m>160.0</stopw-m>
</threshold>
*/
var threshold = function (n, options) {
var latlon = convert(n.find('lat/text()').text() + " " + n.find('lon/text()').text());
var rwy = n.find('rwy/text()').text();
var heading = n.find('hdg-deg/text()').text();
var displ_m = n.find('displ-m/text()').text();
var stopw_m = n.find('stopw-m/text()').text();
var marker = new L.Threshold([latlon.decimalLatitude, latlon.decimalLongitude],
{pane: 'threshold-pane'});
marker.setOrigin([latlon.decimalLatitude, latlon.decimalLongitude]);
marker.setHeading(heading);
marker.setDisplacement(displ_m);
marker.setRunway(rwy);
marker.setStopW(stopw_m);
return marker;
}
module.exports = threshold;

View File

@@ -0,0 +1,116 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const convert = require('geo-coordinates-parser');
const fs = require('fs');
const path = require('path');
const store = require('../store');
/**http://wiki.openstreetmap.org/wiki/Zoom_levels*/
L.TowerMarker = L.Marker.extend({
options: {
zIndexOffset: 10000, draggable: 'true'
},
stripSVG: function(fName) {
var rx = /<\s*svg[^>]*>([\s\S]*)<\s*\/svg[^>]*>/gm;
var svg = fs.readFileSync(path.join(__static, '/', fName), 'utf8');
var svg2 = rx.exec(svg);
return svg2[0];
},
updateIcon : function(map) {
console.debug(`Lat Lng Tower ${this.getLatLng()}`);
if(map !== null) {
var metersPP = this.metersPerPixel(map.getCenter().lat, map.getZoom());
console.debug('Old Meters per pixel ' + this._metersPP);
console.debug('New Meters per pixel ' + metersPP);
if(this._metersPP != metersPP) {
var pixelSize = this.iconSize / metersPP;
var scale = pixelSize/this.iconSize;
var offset = 0;//-(this.iconSize/2);
this.setIcon(L.divIcon({
iconSize: 32,
className: 'threshold-marker-icon',
html: `<div style=\'transform: translateX(${offset}px) translateY(${offset}px) scale(${scale}); border: 1px red\'>${this.svg}</div>`,
}));
this.setInteractive(this.interactive);
this.update(this.getLatLng());
console.debug();
this.setLatLng(this.getLatLng());
this._metersPP = metersPP;
}
}
},
setInteractive(interactive) {
if (interactive) {
if(this._icon) {
L.DomUtil.addClass(this._icon, 'leaflet-interactive');
}
} else {
if(this._icon) {
L.DomUtil.removeClass(this._icon, 'leaflet-interactive');
}
}
this.interactive = interactive;
},
metersPerPixel: function (latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI / 180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel + 8);
},
pixelValue: function (latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
},
setTowerHeight: function (height) {
this.elev_m = height;
}
});
L.TowerMarker.addInitHook(function(){
this.svg = this.stripSVG('tower.svg');
this.iconSize = 32;
this.on('click', function (event) {
store.default.dispatch('setTowerCoords',
event.target.getLatLng().lat.toFixed(6) + ' ' +
event.target.getLatLng().lng.toFixed(6) + ' ' +
event.target.elev_m);
store.default.dispatch('setTowerHeight', event.target.elev_m );
});
this.on('dragstart', function (event) {
console.debug("Drag Tower : ", event);
});
this.on('dragend', function (event) {
console.debug("DragEnd Tower : ", event);
store.default.dispatch('setTowerCoords',
event.target.getLatLng().lat.toFixed(6) + ' ' +
event.target.getLatLng().lng.toFixed(6) + ' ' +
event.target.elev_m);
store.default.dispatch('setTowerHeight', event.target.elev_m );
});
this.on('add', function (event) {
event.target.setInteractive(false);
});
});
//Builds a marker for a ai or multiplayer aircraft
var tower = function (n, options) {
var latlon = convert(n.find('lat/text()').text() + " " + n.find('lon/text()').text());
var marker = new L.TowerMarker([latlon.decimalLatitude, latlon.decimalLongitude], {pane: 'tower-pane'});
marker.elev_m = n.find('elev-m/text()').text();
return marker;
}
module.exports = tower;

View File

@@ -0,0 +1,10 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
exports.removeWip = function (fDir, icao) {
var fNew = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.new.xml');
if( fs.existsSync(fNew) ) {
fs.unlinkSync(fNew);
}
}

View File

@@ -17,22 +17,50 @@ var $ = require('jquery');
var featureLookup = {};
exports.addFeature = function (feature) {
featureLookup[feature.id] = new Array();
var frequencies = [];
function addFrequencies (type, value) {
value.split(' ').forEach(frequencyValue => {
if( value.length > 0) {
var frequency = {type: type, value: frequencyValue};
frequencies.push(frequency);
}
})
}
exports.readGroundnetXML = function (fDir, icao, force) {
try {
layerGroup = L.layerGroup();
layerGroup.maxId = 0;
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.xml');
var fNew = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.new.xml');
exports.addFeature = function (feature) {
featureLookup[feature.id] = [];
}
if (f == null || !fs.existsSync(f))
return;
if (fNew != null && fs.existsSync(fNew) && !force) {
f = fNew;
exports.listSaves = function (fDir, icao) {
var directory = path.join(fDir, icao[0], icao[1], icao[2]);
var files = fs.readdirSync(directory);
var ret = files
.filter(f => f.includes(icao) )
.filter(f => f.includes('groundnet') )
.map(f => {
try {
var fileDate = fs.lstatSync(path.join(directory, f));
return {file: f, mtime: `${fileDate.mtime}`, mtimeMs: `${fileDate.mtimeMs}`};
} catch (error) {
console.error(error);
}
});
ret.forEach( f => {
console.debug(f);
});
return ret;
}
exports.readGroundnetXML = function (fDir, icao, f) {
try {
store.default.dispatch('setGroundnetLoaded', false);
var layerGroup = L.layerGroup();
layerGroup.minId = 9999999999;
layerGroup.maxId = 0;
if (f == null || (!fs.existsSync(f) ))
return layerGroup;
var features = new Array();
@@ -47,30 +75,75 @@ exports.readGroundnetXML = function (fDir, icao, force) {
console.error("Error in " + airline);
throw err;
}
// <frequencies>
// <AWOS>13135</AWOS>
// <GROUND>12175</GROUND>
// <TOWER>11870</TOWER>
// <APPROACH>12120</APPROACH>
// </frequencies>
frequencies = [];
var approach = xml.find('groundnet/frequencies/APPROACH/text()').text();
addFrequencies('APPROACH', approach);
var awos = xml.find('groundnet/frequencies/AWOS/text()').text();
addFrequencies('AWOS', awos);
var clearance = xml.find('groundnet/frequencies/CLEARANCE/text()').text();
addFrequencies('CLEARANCE', clearance);
var departure = xml.find('groundnet/frequencies/DEPARTURE/text()').text();
addFrequencies('DEPARTURE', departure);
var ground = xml.find('groundnet/frequencies/GROUND/text()').text();
addFrequencies('GROUND', ground);
var tower = xml.find('groundnet/frequencies/TOWER/text()').text();
addFrequencies('TOWER', tower);
var unicom = xml.find('groundnet/frequencies/UNICOM/text()').text();
addFrequencies('UNICOM', unicom);
store.default.dispatch('setFrequencies', frequencies);
var parkingNodes = xml.find('groundnet/parkingList/Parking');
console.log("Parking Nodes" + parkingNodes.length);
console.debug("Parking Nodes length" + parkingNodes.length);
var merged = new Array();
var nodesLookup = {};
featureLookup = [];
parkingNodes.map(n => {
var circle = parkingSpot(n, layerGroup);
nodesLookup[n.attr('index')] = n;
featureLookup[n.attr('index')] = new Array();
featureLookup[n.attr('index')].push(circle);
layerGroup.minId = Math.min(layerGroup.minId, Number(n.attr('index')))
layerGroup.maxId = Math.max(layerGroup.maxId, Number(n.attr('index')))
features.push(circle);
}).sort();
store.default.dispatch('setParkings', parkingNodes.map(
p => ({index: Number(p.attrs.index), radius: Number(p.attrs.radius), name: String(p.attrs.name), number: String(p.attrs.number), type: String(p.attrs.type)}
)).sort((p1, p2) => {
if (p1.name === p2.name) {
return p1.number?p1.number.localeCompare(p2.number):-1;
} else {
return p1.name.localeCompare(p2.name)
}}));
// Get all nodes into the lookup
var taxiNodes = xml.find('groundnet/TaxiNodes/node');
taxiNodes.map(n => {
//attrs.lat
//console.log(n.attr('lat') + " " + n.attr('lon'));
var latlon = convert(n.attr('lat') + " " + n.attr('lon'));
try {
var latlon = convert(n.attr('lat') + " " + n.attr('lon'));
} catch (error) {
console.warn(n.attr('lat') + " " + n.attr('lon'));
convert(n.attr('lat') + " " + n.attr('lon'));
}
//console.log(latlon.decimalLatitude);
layerGroup.minId = Math.min(layerGroup.minId, Number(n.attr('index')))
layerGroup.maxId = Math.max(layerGroup.maxId, Number(n.attr('index')))
console.debug(`Min Id : ${layerGroup.minId} Max Id : ${layerGroup.maxId} `);
nodesLookup[n.attr('index')] = n;
if (n.attr('isOnRunway') === '1') {
@@ -102,6 +175,7 @@ exports.readGroundnetXML = function (fDir, icao, force) {
featureLookup[n.attr('begin')].forEach(element => {
if (element instanceof L.Polyline && element.end === n.attr('begin') && element.begin === n.attr('end')) {
element.bidirectional = true;
element.options.attributes.direction = 'bi-directional'
bidirectional = true;
element.updateStyle();
}
@@ -111,6 +185,7 @@ exports.readGroundnetXML = function (fDir, icao, force) {
featureLookup[n.attr('end')].forEach(element => {
if (element instanceof L.Polyline && element.end === n.attr('begin') && element.begin === n.attr('end')) {
element.bidirectional = true;
element.options.attributes.direction = 'bi-directional'
bidirectional = true;
element.updateStyle();
}
@@ -119,12 +194,18 @@ exports.readGroundnetXML = function (fDir, icao, force) {
if (!bidirectional) {
var beginlatlon = convert(beginNode.attr('lat') + " " + beginNode.attr('lon'));
var endlatlon = convert(endNode.attr('lat') + " " + endNode.attr('lon'));
var polyline = new L.Polyline([[beginlatlon.decimalLatitude, beginlatlon.decimalLongitude], [endlatlon.decimalLatitude, endlatlon.decimalLongitude]], { attributes: {} }).addTo(layerGroup);
var pane = 'route-pane';
if(n.attr('isPushBackRoute') === '1') {
pane = 'pushback-pane';
}
var polyline = new L.Polyline([[beginlatlon.decimalLatitude, beginlatlon.decimalLongitude], [endlatlon.decimalLatitude, endlatlon.decimalLongitude]], { pane: pane, attributes: {} }).addTo(layerGroup);
extendTaxiSegment(polyline);
polyline.addListeners();
polyline._latlngs[0].attributes = {};
$.each(beginNode.attrs, function (key, value) {
console.log(key + "\t" + value);
console.debug(key + "\t" + value);
if (isNaN(value))
polyline._latlngs[0].attributes[key] = value;
@@ -133,7 +214,7 @@ exports.readGroundnetXML = function (fDir, icao, force) {
});
polyline._latlngs[1].attributes = {};
$.each(endNode.attrs, function (key, value) {
console.log(key + "\t" + value);
console.debug(key + "\t" + value);
if (isNaN(value))
polyline._latlngs[1].attributes[key] = value;
@@ -141,17 +222,20 @@ exports.readGroundnetXML = function (fDir, icao, force) {
polyline._latlngs[1].attributes[key] = Number(value);
});
$.each(n.attrs, function (key, value) {
console.log(key + "\t" + value);
console.debug(key + "\t" + value);
if (isNaN(value))
polyline.options.attributes[key] = value;
else
polyline.options.attributes[key] = Number(value);
});
polyline.options.attributes.direction = 'forward'
polyline.updateStyle();
polyline.begin = beginNode.attr('index');
polyline.end = endNode.attr('index');
polyline.feature = { properties: { searchTerm: 'Arc ' + beginNode.attr('index') + '-' + endNode.attr('index')}};
// polyline.enableEdit();
// polyline.on('dblclick', function (event) { L.DomEvent.stop; polyline.toggleEdit; });
@@ -168,7 +252,7 @@ exports.readGroundnetXML = function (fDir, icao, force) {
}
}
}).sort();
store.default.dispatch('setGroundnetLoaded', true);
return layerGroup;
});

View File

@@ -3,7 +3,6 @@ const fs = require('fs');
const path = require('path');
const markers = require('./MagneticVertex');
const TaxiwaySegment = require('./TaxiwaySegment');
const parkingSpot = require('./ParkingSpot.js');
@@ -15,111 +14,309 @@ const mathjs = require('mathjs');
var builder = require('xmlbuilder');
var featureLookup = [];
var parkings = [];
var pushBackNodeLookup = [];
/**
* Walk nodes until the pushback node is found.
* @param {*} index
*/
function findRouteToPushback (index) {
if (featureLookup===undefined) {
return
}
var walkedNodes = [index]
var pushBackNodes = []
walkPushbackRoute(index, walkedNodes, pushBackNodes)
return pushBackNodes
}
function walkPushbackRoute (index, walkedNodes, pushBackNodes) {
var polyLines = featureLookup[index];
if (polyLines===undefined) {
return;
}
if (pushBackNodeLookup[index]!==undefined) {
pushBackNodes.push(pushBackNodeLookup[index]['@index']);
}
polyLines.forEach(l => {
if( l['@isPushBackRoute'] === '1' ) {
if (Number(l['@begin']) === index && walkedNodes.indexOf(Number(l['@end'])) < 0) {
walkedNodes.push(Number(l['@end']))
pushBackNodes.concat(walkPushbackRoute(Number(l['@end']), walkedNodes, pushBackNodes))
}
if (Number(l['@end']) === index && walkedNodes.indexOf(Number(l['@begin'])) < 0 ) {
walkedNodes.push(Number(l['@begin']))
pushBackNodes.concat(walkPushbackRoute(Number(l['@begin']), walkedNodes, pushBackNodes))
}
}
});
}
/**
*
* @param {*} fDir The directory
* @param {*} icao
* @param {*} featureList
*/
exports.writeGroundnetXML = function (fDir, icao, featureList) {
try {
try { fs.mkdirSync(path.join(fDir), { recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0]),{ recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1]), { recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1], icao[2]), { recursive: true })} catch (err) { }
var fileNames = [];
for (let index = 1; index <= store.default.state.Settings.settings.numberOfSaves; index++) {
fileNames.push(path.join(fDir, icao[0], icao[1], icao[2], icao + `.groundnet.bak.${index}.xml`));
}
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.groundnet.new.xml');
if( fs.existsSync(f) ) {
var previous = '';
fileNames.reverse().forEach(fBak => {
if (fs.existsSync(fBak) && previous !== '') {
console.debug( `Copy ${fBak} to ${previous}`);
fs.copyFileSync(fBak, previous);
}
previous = fBak;
});
fs.copyFileSync(f, previous);
}
if (f == null)
return;
pushBackNodeLookup = [];
console.log(featureList);
console.debug(featureList);
var parkings = featureList.map(mapParkings).filter(n => n);
var runwayNodes = featureList.map(mapRunwayNodes).filter(n => n);
var holdNodes = featureList.map(mapHoldPoint).filter(n => n);
holdNodes.forEach(n => {
pushBackNodeLookup[n['@index']] = n;
});
var nodes = [];
var arcList = [];
var frequencies = [];
var featureLookup = [];
var version = new Date().toUTCString() + ' by FlightgearAirports ' + require('electron').remote.app.getVersion();
var name = store.default.state.Settings.settings.name;
featureLookup = [];
// Loaded segments
featureList.filter(o => o instanceof L.TaxiwaySegment).filter(n => n).forEach(element => {
var begin = mapBeginNode(element);
if(begin['@index']==="")
console.warn("Begin missing");
nodes[begin['@index']] = begin;
var end = mapEndNode(element);
if(end['@index']==="")
console.warn("End missing");
nodes[end['@index']] = end;
});
// New segments
// New segments
featureList.filter(o => o instanceof L.Polyline).filter(n => n).forEach(arcElement => {
// element._latlngs.forEach(latlng => { nodes[latlng.__vertex.glueindex] = mapVertexNode(latlng) });
// element._latlngs.forEach(latlng => { nodes[latlng.glueindex] = mapVertexNode(latlng) });
var startIndex = -1;
console.log(arcElement.options.attributes);
console.debug(arcElement.options.attributes);
var currentArc = arcElement.options.attributes;
arcElement._latlngs.forEach( latlng => {
if (latlng.__vertex !== undefined && latlng.__vertex.glueindex !== undefined) {
nodes[latlng.__vertex.glueindex] = mapVertexNode(latlng);
if (startIndex > 0) {
if (latlng !== undefined && latlng.glueindex !== undefined) {
nodes[latlng.glueindex] = mapVertexNode(latlng);
if (startIndex >= 0) {
if (featureLookup[startIndex] == undefined) {
featureLookup[startIndex] = [];
}
if (featureLookup[latlng.__vertex.glueindex] == undefined) {
featureLookup[latlng.__vertex.glueindex] = [];
if (featureLookup[latlng.glueindex] == undefined) {
featureLookup[latlng.glueindex] = [];
}
if( currentArc.direction === 'bi-directional' || currentArc.direction === 'forward' ){
arc = { '@begin': startIndex, '@end': String(latlng.glueindex) };
styleArc(currentArc, arc);
arcList.push(arc);
featureLookup[startIndex][latlng.glueindex] = arc;
}
if( currentArc.direction === 'bi-directional' || currentArc.direction === 'backward' ){
arc = { '@begin': String(latlng.glueindex), '@end': startIndex };
styleArc(currentArc, arc);
arcList.push(arc);
featureLookup[latlng.glueindex][startIndex] = arc;
}
if (currentArc.direction === '' || !currentArc.direction) {
console.error( "Arc without direction " + util.inspect(currentArc) );
}
arc = { '@begin': startIndex, '@end': String(latlng.__vertex.glueindex) };
styleArc(currentArc, arc);
arcList.push(arc);
featureLookup[startIndex][latlng.__vertex.glueindex] = arc;
arc = { '@begin': String(latlng.__vertex.glueindex), '@end': startIndex };
styleArc(currentArc, arc);
arcList.push(arc);
featureLookup[latlng.__vertex.glueindex][startIndex] = arc;
}
startIndex = latlng.__vertex.glueindex;
startIndex = latlng.glueindex;
} else {
console.error( "LatLng without glueindex " + util.inspect(latlng) );
}
});
});
runwayNodes.forEach(element => {
if (nodes[element['@index']] != undefined) {
nodes[element['@index']]['@isOnRunway'] = "1";
nodes[element['@index']]['@isOnRunway'] = "1";
}
});
// delete the parkings
// Find the index of the pushback node
parkings.forEach(n => {
nodes[n['@index']] = null;
var pushBackNode = findRouteToPushback(Number(n['@index']))[0];
if (pushBackNode!==undefined) {
n['@pushBackRoute'] = pushBackNode;
}
});
nodes = nodes.filter(n => n);
arcList = arcList.filter(a => a['@begin'] !== a['@end']);
nodes.sort((p, p2) => { return p['@index'] - p2['@index'] });
//console.debug(util.inspect(nodes));
var uniqueNodes = nodes.filter((v, i, a) => a.indexOf(v) === i);
var maxId = uniqueNodes.slice(-1)[0]['@index'];
var approachList = store.default.state.Frequencies.items.filter(f => f.type === 'APPROACH').map(mapFrequency);
var xmlObj = { groundnet: { version: 1, parkingList: { Parking: parkings }, TaxiNodes: { node: uniqueNodes }, TaxiWaySegments: { arc: arcList } } };
var awosList = store.default.state.Frequencies.items.filter(f => f.type === 'AWOS').map(mapFrequency);
var clearanceList = store.default.state.Frequencies.items.filter(f => f.type === 'CLEARANCE').map(mapFrequency);
var departureList = store.default.state.Frequencies.items.filter(f => f.type === 'DEPARTURE').map(mapFrequency);
var groundList = store.default.state.Frequencies.items.filter(f => f.type === 'GROUND').map(mapFrequency);
var towerList = store.default.state.Frequencies.items.filter(f => f.type === 'TOWER').map(mapFrequency);
var unicomList = store.default.state.Frequencies.items.filter(f => f.type === 'UNICOM').map(mapFrequency);
var gapStart = -1;
var gapEnd = -1;
do {
gapStart = -1;
gapEnd = -1;
var allIds = parkings.map(n => Number(n['@index']))
.concat(uniqueNodes.map(n => Number(n['@index'])))
.sort((a, b) => a - b);
allIds.forEach((element, index, array) => {
if (index > 0 && array[index-1] + 1 != element && gapStart == -1 ) {
gapStart = array[index-1];
gapEnd = element;
}
});
var gap = gapEnd - gapStart -1;
if ( gap >= 0 ) {
parkings = parkings.map(n => {
if (n['@index']>gapStart) {
n['@index'] = String(n['@index'] - gap);
}
if (n['@pushbackRoute']>gapStart) {
n['@pushbackRoute'] = String(n['@pushbackRoute'] - gap);
}
return n;
});
uniqueNodes = uniqueNodes.map(n => {
if (n['@index']>gapStart) {
n['@index'] = String(n['@index'] - gap);
}
return n;
});
arcList = arcList.map(n => {
if (n['@begin']>gapStart) {
n['@begin'] = String(n['@begin'] - gap);
}
if (n['@end']>gapStart) {
n['@end'] = String(n['@end'] - gap);
}
return n;
});
}
} while( gapStart > 0 && gapEnd > 0);
var xmlObj = { groundnet: { version: 1, fgaversion: version, name: name,
'frequencies': { APPROACH: approachList, DEPARTURE: departureList, AWOS: awosList, CLEARANCE: clearanceList, GROUND: groundList, TOWER: towerList, UNICOM: unicomList },
parkingList: { Parking: parkings }, TaxiNodes: { node: uniqueNodes }, TaxiWaySegments: { arc: arcList } } };
xmlString = builder.create(xmlObj).end({ pretty: true });
fs.writeFileSync(f, xmlString);
console.log(xmlString);
console.debug(xmlString);
} catch (error) {
console.error(error);
}
return layerGroup;
return;
}
var mapFrequency = function (o) {
return mathjs.round(o.value, 0);
}
var mapParkings = function (o) {
console.log(o);
console.debug(o);
if (o instanceof L.ParkingSpot) {
var lat = convertLat(o.getLatLng());
var lon = convertLon(o.getLatLng());
// <Parking index="0" type="gate" name="GA_Parking" lat="S9 25.739923" lon="E160 2.927602" heading="67" radius="44" airlineCodes="" />
return { '@index': String(o['id']), '@type': o.options.attributes.type, '@name': o.options.attributes.name, '@lat': lat, '@lon': lon, '@heading': Number(o.options.attributes.heading), '@radius': String(o.options.attributes.radius) };
var parking = { '@index': String(o['id']), '@type': o.options.attributes.type, '@lat': lat, '@lon': lon, '@heading': Number(o.options.attributes.heading%360).toFixed(1), '@radius': String(o.options.attributes.radius) };
if (o.options.attributes.name !== '' && o.options.attributes.name !== 'undefined' && o.options.attributes.name !== undefined) {
parking['@name'] = o.options.attributes.name;
}
if( o.options.attributes.airlineCodes) {
console.debug(o.options.attributes.airlineCodes);
parking['@airlineCodes'] = o.options.attributes.airlineCodes;
}
if(o.options.attributes.number !== undefined &&
typeof o.options.attributes.number === 'number' || (
typeof o.options.attributes.number === 'string' &&
o.options.attributes.number.trim() !== ''
)
) {
console.debug(o.options.attributes.number);
parking['@number'] = o.options.attributes.number;
}
return parking;
}
}
var mapRunwayNodes = function (o) {
console.log(o);
console.debug(o);
if (o instanceof L.RunwayNode) {
return { '@index': String(o['glueindex']), '@lat': convertLat(o._latlng), '@lon': convertLon(o._latlng), '@isOnRunway': '1', '@holdPointType': 'none' };
var runwayNode = { '@index': String(o['glueindex']),
'@lat': convertLat(o._latlng),
'@lon': convertLon(o._latlng),
'@isOnRunway': '1',
'@holdPointType': 'none' };
return runwayNode;
}
if (o instanceof L.HoldNode) {
return { '@index': String(o['glueindex']), '@lat': convertLat(o._latlng), '@lon': convertLon(o._latlng), '@isOnRunway': '0', '@holdPointType': o['holdPointType'] };
// return { '@index': String(o['glueindex']), '@lat': convertLat(o._latlng), '@lon': convertLon(o._latlng), '@isOnRunway': '0', '@holdPointType': o['holdPointType'] };
}
}
var mapHoldPoint = function (o) {
if (o instanceof L.HoldNode) {
if( o['holdPointType'] === undefined )
{
console.error('Oh dear ' + o);
}
return { '@index': String(o['glueindex']), '@holdPointType': o['holdPointType'] };
}
}
var mapBeginNode = function (o) {
if (o instanceof L.TaxiwaySegment) {
console.log(o);
console.debug('Map Begin : ', o['begin']);
// <Parking index="0" type="gate" name="GA_Parking" lat="S9 25.739923" lon="E160 2.927602" heading="67" radius="44" airlineCodes="" />
return { '@index': String(o['begin']), '@lat': convertLat(o._latlngs[0]), '@lon': convertLon(o._latlngs[0]), '@isOnRunway': '0', '@type': 'begin' };
}
@@ -127,7 +324,7 @@ var mapBeginNode = function (o) {
var mapEndNode = function (o) {
if (o instanceof L.TaxiwaySegment) {
console.log(o);
console.debug('Map End : ', o['end']);
// <Parking index="0" type="gate" name="GA_Parking" lat="S9 25.739923" lon="E160 2.927602" heading="67" radius="44" airlineCodes="" />
return { '@index': String(o['end']), '@lat': convertLat(o._latlngs[1]), '@lon': convertLon(o._latlngs[1]), '@isOnRunway': '0', '@type': 'end' };
}
@@ -135,38 +332,40 @@ var mapEndNode = function (o) {
var mapVertexNode = function (l) {
if (l instanceof L.LatLng) {
console.log(l);
console.debug(l);
// <Parking index="0" type="gate" name="GA_Parking" lat="S9 25.739923" lon="E160 2.927602" heading="67" radius="44" airlineCodes="" />
return { '@index': String(l.__vertex.glueindex), '@lat': convertLat(l), '@lon': convertLon(l), '@isOnRunway': '0', '@holdPointType': l.attributes['holdPointType'] };
return { '@index': String(l.glueindex), '@lat': convertLat(l), '@lon': convertLon(l), '@isOnRunway': '0', '@holdPointType': l.attributes['holdPointType'] };
}
}
var convertLat = function (latlng) {
console.log(latlng.lat);
//console.debug(latlng.lat);
var NS = latlng.lat > 0 ? 'N' : 'S';
var deg = mathjs.floor(mathjs.abs(latlng.lat));
var min = (mathjs.abs(latlng.lat) - deg) * 60;
// console.log(NS + deg + " " + min);
return NS + String(deg).padStart(2, '0') + " " + mathjs.round(min, 3);
// console.debug(NS + deg + " " + min);
return NS + String(deg).padStart(2, '0') + " " + mathjs.round(min, 6);
}
var convertLon = function (latlng) {
console.log(latlng.lng);
//console.debug(latlng.lng);
var NS = latlng.lng > 0 ? 'E' : 'W';
var deg = mathjs.floor(mathjs.abs(latlng.lng));
var min = (mathjs.abs(latlng.lng) - deg) * 60;
// console.log(NS + deg + " " + min);
return NS + String(deg).padStart(2, '0') + " " + mathjs.round(min, 3);
// console.debug(NS + deg + " " + min);
return NS + String(deg).padStart(2, '0') + " " + mathjs.round(min, 6);
}
var styleArc = function (attributes, arc) {
console.log(attributes);
//console.debug(attributes);
if(attributes !== undefined){
if (attributes.isPushBackRoute !== undefined && Number(attributes.isPushBackRoute) === 1 ) {
arc['@isPushBackRoute'] = "1";
} else {
arc['@isPushBackRoute'] = "0";
}
arc['@name'] = attributes.name;
if ( attributes.name !== '' && attributes.name !== 'undefined') {
arc['@name'] = attributes.name;
}
}
}

View File

@@ -1,155 +1,511 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const lineReader = require('readline');
const zlib = require('zlib');
// const geodesy = require('geodesy');
const LatLonEllipsoidal = require( 'geodesy/latlon-ellipsoidal-vincenty.js').default;
const LatLonEllipsoidal = require('geodesy/latlon-ellipsoidal-vincenty.js').default;
const fs = require('fs');
module.exports.readPavement = function (f, icao, cb) {
console.log(f);
layerGroup = L.layerGroup();
var currentFeature;
const store = require('../store');
lineReader.createInterface({
input: fs.createReadStream(f).pipe(zlib.createGunzip())
}).on('line', function (line) {
var fields = line.split(/[ ]+/);
// var fields = line.match('([0-9]+)');
if (fields == null)
return;
var scanMethod = scanMethods[fields[0]];
if (scanMethod != null) {
currentFeature = scanMethod(fields, icao, layerGroup, currentFeature);
const buildRunwayPoly = require('../leaflet/Runway.js');
const buildTaxiwayPoly = require('../leaflet/Taxiway.js');
/**
*
* @param {*} line
* @param {*} icao
* @param {*} layerGroup
* @param {*} currentFeature
*/
function bezier(line, icao, layerGroup, currentFeature) {
var previous = currentFeature.slice(-1)[0].slice(-1)[0];
if (previous !== undefined &&
Number(previous[0]) !== Number(line[1]) &&
Number(previous[1]) !== Number(line[2])) {
var midpoint = calcMidpoint([Number(previous[0]), Number(previous[1])],
[Number(line[1]), Number(line[2])]);
var startPoint = [Number(previous[0]), Number(previous[1])];
var endPoint = [Number(line[1]), Number(line[2])];
if (module.exports.debug) {
L.polyline([startPoint, endPoint], { color: 'fuchsia' }).addTo(layerGroup);
var marker = new L.marker(endPoint, { title: 'endpoint', color: 'fuchsia' });
marker.bindTooltip(String(currentFeature.slice(-1)[0].length + ' ' + line), { className: "my-label", offset: [0, 0] });
marker.addTo(layerGroup);
}
else {
if (fields[0] == '99') {
lineReader.close();
var control = [Number(line[3]), Number(line[4])];
if (!isNaN(control[0]) && control[0] !== undefined && !isNaN(control[1]) && control[1] !== undefined) {
var controlReflected = pointReflection([Number(line[3]), Number(line[4])],
[Number(line[1]), Number(line[2])]);
// L.marker(control, { title: 'control' }).addTo(layerGroup);
// L.marker(controlPointReflected, { title: 'controlReflected' }).addTo(layerGroup);
// L.polyline([controlPointReflected, control], { color: 'green' }).addTo(layerGroup);
// L.polyline([controlReflected, control], { color: 'red' }).addTo(layerGroup);
// L.polyline([controlReflected, midpoint], { color: 'aqua' }).addTo(layerGroup);
var modifiedControlPoint = calcMidpoint(controlReflected, midpoint);
// var modifiedControlPoint = controlReflected;
var points = null;
if (exports.bezierPoint === null) {
if (module.exports.debug) {
L.polyline([startPoint, controlReflected, endPoint], { color: 'purple' }).addTo(layerGroup);
var marker = new L.marker(controlReflected, { title: 'control First' });
marker.bindTooltip(String(currentFeature.slice(-1)[0].length + ' ' + line), { className: "my-label", offset: [0, 0] });
marker.addTo(layerGroup);
}
points = deCasteljau([
[Number(previous[0]), Number(previous[1])],
controlReflected, [Number(line[1]), Number(line[2])]]);
exports.bezierPoint = control;
} else {
if (module.exports.debug) {
L.polyline([startPoint, exports.bezierPoint, controlReflected, endPoint], { color: 'purple' }).addTo(layerGroup);
var marker = new L.marker(exports.bezierPoint, { title: 'exports.bezierPoint' }).addTo(layerGroup);
marker.bindTooltip(String(currentFeature.slice(-1)[0].length + ' ' + line), { className: "my-label", offset: [0, 10] });
marker.addTo(layerGroup);
var marker1 = new L.marker(controlReflected, { title: 'controlReflected' }).addTo(layerGroup);
marker1.bindTooltip(String(currentFeature.slice(-1)[0].length + ' ' + line), { className: "my-label", offset: [0, -10] });
marker1.addTo(layerGroup);
}
points = deCasteljau([
[Number(previous[0]), Number(previous[1])],
exports.bezierPoint,
controlReflected,
[Number(line[1]), Number(line[2])]]);
exports.bezierPoint = control;
}
// L.polyline([previous, control, [Number(line[1]), Number(line[2])]]).addTo(layerGroup);
for (let index = 0; index < points.length; index++) {
const element = points[index];
if (element[0] === "NaN") {
console.debug(control);
}
currentFeature.slice(-1)[0].push(element);
}
} else {
var points = null;
if (module.exports.debug) {
L.polyline([startPoint, exports.bezierPoint, endPoint], { color: 'purple' }).addTo(layerGroup);
L.marker(exports.bezierPoint, { title: 'exports.bezierPoint' }).addTo(layerGroup);
}
points = deCasteljau([
[Number(previous[0]), Number(previous[1])],
exports.bezierPoint,
[Number(line[1]), Number(line[2])]]);
for (let index = 0; index < points.length; index++) {
const element = points[index];
if (element[0] === "NaN") {
console.debug(control);
}
currentFeature.slice(-1)[0].push(element);
}
// console.log('Ignored:', line);
}
}).on('error', function (err) {
console.log(err);
lr.close();
}).on('close', function () {
console.log("End");
cb(layerGroup);
});
}
//L.marker([Number(line[3]), Number(line[4])]).addTo(layerGroup);
currentFeature.slice(-1)[0].push([line[1], line[2]]);
return currentFeature;
}
/**
* @brief Reflect point p on midpoint
*/
var pointReflectionMidpoint = function (p, p0, p1) {
var dx, dy, a, b, x, y;
dx = (p0[0] + p1[0]) / 2 - p[0];
dy = (p0[1] + p1[1]) / 2 - p[1];
return [(p0[0] + p1[0]) / 2 + dx, (p0[1] + p1[1]) / 2 + dy];
}
/**
* @brief Reflect point p on midpoint
*/
var pointReflection = function (p, p0) {
var dx, dy;
dx = p0[0] - p[0];
dy = p0[1] - p[1];
return [p0[0] + dx, p0[1] + dy];
}
/**
* @brief Reflect point p along line through points p0 and p1
*
* @author Balint Morvai <balint@morvai.de>
* @license http://en.wikipedia.org/wiki/MIT_License MIT License
* @param p point to reflect
* @param p0 first point for reflection line
* @param p1 second point for reflection line
* @return object
*/
var reflect = function (p, p0, p1) {
var dx, dy, a, b, x, y;
dx = p1[0] - p0[0];
dy = p1[1] - p0[1];
a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
b = 2 * dx * dy / (dx * dx + dy * dy);
x = a * (p[0] - p0[0]) + b * (p[1] - p0[1]) + p0[0];
y = b * (p[0] - p0[0]) - a * (p[1] - p0[1]) + p0[1];
return [x, y];
}
function calcMidpoint(p1, p2) {
return [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2];
}
function lerp(p1, p2, t) {
const s = 1 - t;
return [p1[0] * s + p2[0] * t, p1[1] * s + p2[1] * t];
}
function deCasteljau(p1, p2, control) {
const numSegments = 10;
var intermediates = [];
for (let index = 0; index <= numSegments; index++) {
p3 = lerp(p1, control, index / numSegments);
p4 = lerp(control, p2, index / numSegments);
var intermediate = lerp(p3, p4, index / numSegments);
intermediates.push([String(intermediate[0]), String(intermediate[1])]);
}
intermediates.push(p2);
return intermediates;
}
function deCasteljauSegment(pointArray, len) {
var subPoints = [];
if (pointArray.length == 1) {
return pointArray[0];
}
for (let index = 1; index < pointArray.length; index++) {
subPoints.push(lerp(pointArray[index - 1], pointArray[index], len));
}
return deCasteljauSegment(subPoints, len);
}
function deCasteljau(pointArray) {
const numSegments = 10;
var intermediates = [];
for (let index = 0; index < numSegments; index++) {
intermediates.push(deCasteljauSegment(pointArray, index / numSegments));
}
return intermediates;
}
function createPoly(currentFeature, layerGroup) {
switch (module.exports.type) {
case 110:
var taxiwayPoly = new L.Polygon(currentFeature);
taxiwayPoly.setStyle({ color: module.exports.colour, interactive: false });
taxiwayPoly.addTo(layerGroup);
break;
case 120:
var taxiwayPoly = new L.Polygon(currentFeature);
taxiwayPoly.setStyle({ color: module.exports.colour, interactive: false });
taxiwayPoly.addTo(layerGroup);
break;
case 130:
var taxiwayPoly = new L.Polygon(currentFeature);
taxiwayPoly.setStyle({ color: module.exports.colour, fillOpacity: .0, interactive: false });
taxiwayPoly.addTo(layerGroup);
break;
default:
break;
}
}
function createLineString(currentFeature, layerGroup) {
switch (module.exports.type) {
case 110:
var taxiwayPoly = new L.Polyline(currentFeature);
taxiwayPoly.setStyle({ color: module.exports.colour, interactive: false });
taxiwayPoly.addTo(layerGroup);
break;
case 120:
var taxiwayPoly = new L.Polyline(currentFeature);
taxiwayPoly.setStyle({ color: module.exports.colour, interactive: false });
taxiwayPoly.addTo(layerGroup);
break;
case 130:
var taxiwayPoly = new L.Polyline(currentFeature);
taxiwayPoly.setStyle({ color: module.exports.colour, interactive: false });
taxiwayPoly.addTo(layerGroup);
break;
default:
break;
}
}
module.exports.readPavement = function (f, icao, callback) {
console.log(f);
var pavementLayerGroup = L.layerGroup();
var currentFeature;
store.default.dispatch('setPavementLoaded', false);
if (!fs.existsSync(f)) {
store.default.dispatch('setPavementLoaded', true);
callback();
return;
}
try {
fs.accessSync(f, fs.constants.R_OK);
lineReader.createInterface({
input: fs.createReadStream(f).pipe(zlib.createGunzip())
}).on('line', function (line) {
try {
var fields = line.split(/[ ]+/);
// var fields = line.match('([0-9]+)');
if (fields == null)
return;
var scanMethod = scanMethods[fields[0]];
if (scanMethod != null) {
currentFeature = scanMethod(fields, icao, pavementLayerGroup, currentFeature);
}
else {
if (fields[0] == '99') {
lineReader.close();
}
// console.log('Ignored:', line);
}
} catch (error) {
console.error('Error reading : ' + line + error);
}
}).on('error', function (err) {
store.default.dispatch('setPavementLoaded', true);
console.error(err);
lr.close();
callback();
}).on('close', function () {
store.default.dispatch('setPavementLoaded', true);
console.log("End");
callback(pavementLayerGroup);
});
} catch (err) {
console.error('no access!');
store.default.dispatch('setPavementLoaded', true);
callback();
return;
}
}
module.exports.debug = false;
module.exports.isScanning = false;
module.exports.colour = 'grey';
module.exports.type = 0;
module.exports.bezierPoint = null;
var scanMethods = {
1: (line, icao) => {
// console.log('Airport:', line);
if (line[4]===icao){
console.log('Airport:', line);
if (line[4] === icao) {
console.debug('Airport:', line);
module.exports.isScanning = true;
} else {
module.exports.isScanning = false;
}
},
// APTDAT 715 Segment
10: (line, icao, layerGroup) => {
if (module.exports.isScanning) {
var pointMiddle = new LatLonEllipsoidal(Number(line[1]), Number(line[2]));
var point1 = pointMiddle.destinationPoint(line[5] / 6.562, line[4]);
var point2 = pointMiddle.destinationPoint(line[5] / 6.562, line[4] - 180);
var runwayPoints = [];
var bearing = point1.initialBearingTo(point2);
// Width in ft
var runwayWidth = Number(line[8]) / 3.281;
runwayPoints.push(point1.destinationPoint(runwayWidth / 2, (bearing + 90)));
runwayPoints.push(point2.destinationPoint(runwayWidth / 2, (bearing + 90)));
runwayPoints.push(point2.destinationPoint(runwayWidth / 2, (bearing - 90)));
runwayPoints.push(point1.destinationPoint(runwayWidth / 2, (bearing - 90)));
var runwayPoly = buildTaxiwayPoly(runwayPoints);
runwayPoly.addTo(layerGroup);
}
},
// Runway
100: (line, icao, layerGroup) => {
if (module.exports.isScanning) {
console.log('Runway ', line );
var point1 = new LatLonEllipsoidal(Number(line[9]), Number(line[10]));
var point2 = new LatLonEllipsoidal(Number(line[18]), Number(line[19]));
console.debug('Runway ', line);
var point1 = new LatLonEllipsoidal(Number(line[9]), Number(line[10]));
var point2 = new LatLonEllipsoidal(Number(line[18]), Number(line[19]));
var runwayPoints = [];
var runwayPoints = [];
var bearing = point1.initialBearingTo(point2);
var runwayWidth = Number(line[1]);
var destination = point1.destinationPoint( 1000, bearing);
var bearing = point1.initialBearingTo(point2);
var runwayWidth = Number(line[1]);
var destination = point1.destinationPoint(1000, bearing);
runwayPoints.push(point1.destinationPoint( runwayWidth/2, (bearing+90)));
runwayPoints.push(point2.destinationPoint( runwayWidth/2, (bearing+90)));
runwayPoints.push(point2.destinationPoint( runwayWidth/2, (bearing-90)));
runwayPoints.push(point1.destinationPoint( runwayWidth/2, (bearing-90)));
runwayPoints.push(point1.destinationPoint(runwayWidth / 2, (bearing + 90)));
runwayPoints.push(point2.destinationPoint(runwayWidth / 2, (bearing + 90)));
runwayPoints.push(point2.destinationPoint(runwayWidth / 2, (bearing - 90)));
runwayPoints.push(point1.destinationPoint(runwayWidth / 2, (bearing - 90)));
var runwayPoly = new L.Polygon(runwayPoints);
runwayPoly.setStyle({color: 'grey', interactive: false});
runwayPoly.addTo(layerGroup);
var runwayPoly = buildRunwayPoly(runwayPoints);
runwayPoly.addTo(layerGroup);
var runwayLine = new L.Polyline([point1,point2]);
runwayLine.setStyle({color: 'red'});
// runwayLine.addTo(layerGroup);
var runwayLine1 = new L.Polyline([point1 ,destination]);
runwayLine1.setStyle({color: 'red'});
// runwayLine1.addTo(layerGroup);
var displacedEnd1 = point1.destinationPoint(Number(line[20]), bearing)
var displacedEnd2 = point2.destinationPoint(Number(line[20]), bearing - 180)
var runwayLine = new L.Polyline([displacedEnd1, displacedEnd2]);
runwayLine.setStyle({ color: 'yellow', stroke: true, dashArray: [50, 50] });
runwayLine.addTo(layerGroup);
var t1 = new L.Polyline([displacedEnd1.destinationPoint(runwayWidth / 2, (bearing - 90)),
displacedEnd1.destinationPoint(runwayWidth / 2, (bearing + 90))]);
t1.setStyle({ color: 'yellow' });
t1.addTo(layerGroup);
var t2 = new L.Polyline([displacedEnd2.destinationPoint(runwayWidth / 2, (bearing - 90)),
displacedEnd2.destinationPoint(runwayWidth / 2, (bearing + 90))]);
t2.setStyle({ color: 'yellow' });
t2.addTo(layerGroup);
var runwayLine1 = new L.Polyline([point1, destination]);
runwayLine1.setStyle({ color: 'red' });
// runwayLine1.addTo(layerGroup);
}
},
// Pavement
110: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return undefined;
if(typeof currentFeature !== 'undefined')
{
var taxiwayPoly = new L.Polygon(currentFeature);
taxiwayPoly.setStyle({color: 'grey', interactive: false});
taxiwayPoly.addTo(layerGroup);
return undefined;
if (typeof currentFeature !== 'undefined') {
createPoly(currentFeature, layerGroup);
}
module.exports.colour = 'grey';
module.exports.type = 110;
return [[]];
},
120: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return undefined;
return undefined;
if (typeof currentFeature !== 'undefined') {
createPoly(currentFeature, layerGroup);
}
module.exports.colour = 'yellow';
module.exports.type = 120;
return [[]];
},
130: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return undefined;
return undefined;
if (typeof currentFeature !== 'undefined') {
createPoly(currentFeature, layerGroup);
}
module.exports.colour = 'black';
module.exports.type = 130;
return [[]];
},
111: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return;
console.log(line);
currentFeature.slice(-1)[0].push([line[1], line[2]]);
return;
console.debug(line);
var previous = currentFeature.slice(-1)[0].slice(-1)[0];
if (previous !== undefined &&
previous !== null &&
previous[0] === line[1] &&
previous[1] === line[2]) {
module.exports.bezierPoint = null;
} else {
currentFeature.slice(-1)[0].push([line[1], line[2]]);
module.exports.bezierPoint = null;
}
return currentFeature;
},
// Bezier
112: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return;
console.log(line);
currentFeature.slice(-1)[0].push([line[1], line[2]]);
return currentFeature;
if (!module.exports.isScanning) {
return;
}
console.debug(line);
return bezier(line, icao, layerGroup, currentFeature);
},
113: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return;
if (!module.exports.isScanning) {
return;
}
console.debug(line);
currentFeature.slice(-1)[0].push([line[1], line[2]]);
currentFeature.push([]);
exports.bezierPoint = null;
return currentFeature;
},
// Bezier
114: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return;
currentFeature.slice(-1)[0].push([line[1], line[2]]);
if (!module.exports.isScanning) {
return;
}
console.debug(line);
bezier(line, icao, layerGroup, currentFeature);
currentFeature.push([]);
exports.bezierPoint = null;
// currentFeature.slice(-1)[0].push([line[1], line[2]]);
return currentFeature;
},
115: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return;
currentFeature.slice(-1)[0].push([line[1], line[2]]);
var taxiwayLine = new L.Polyline(currentFeature);
taxiwayLine.setStyle({color: 'red'});
// taxiwayLine.addTo(layerGroup);
return;
console.debug(line);
if (exports.bezierPoint !== null) {
bezier(line, icao, layerGroup, currentFeature);
}
else {
currentFeature.slice(-1)[0].push([line[1], line[2]]);
}
createLineString(currentFeature, layerGroup);
exports.bezierPoint = null;
},
// End with Bezier
116: (line, icao, layerGroup, currentFeature) => {
if (!module.exports.isScanning)
return;
currentFeature.slice(-1)[0].push([line[1], line[2]]);
var taxiwayLine = new L.Polyline(currentFeature);
taxiwayLine.setStyle({color: 'red'});
return;
console.debug(line);
currentFeature = bezier(line, icao, layerGroup, currentFeature);
createLineString(currentFeature, layerGroup);
exports.bezierPoint = null;
// taxiwayLine.addTo(layerGroup);
},
99: (l) => {
console.log('Finished');
}
},
}

View File

@@ -0,0 +1,89 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const xamel = require('xamel');
const convert = require('geo-coordinates-parser');
const LatLonEllipsoidal = require('geodesy/latlon-ellipsoidal-vincenty.js').default;
const store = require('../store');
const util = require('util');
const takeoffPadPoly = require('../leaflet/TakeoffPad.js');
const threshold = require('./Threshold.js');
var $ = require('jquery');
exports.readThresholdXML = function (fDir, icao, force, stripes) {
try {
var layerGroup = L.layerGroup();
layerGroup.maxId = 0;
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.threshold.xml');
var fNew = path.join(fDir, icao[0], icao[1], icao[2], icao + '.threshold.new.xml');
if (f == null || !fs.existsSync(f))
return;
if (fNew != null && fs.existsSync(fNew) && !force) {
f = fNew;
}
var features = new Array();
// map.on("editable:vertex:new", function (event) {
// log.console("Add vertex " + event);
// });
var xmlGroundnet = fs.readFileSync(f, 'utf8').toString();
xamel.parse(xmlGroundnet, function (err, xml) {
console.debug("parsed " + path.basename(f));
if (err !== null) {
console.error("Error in " + airline);
throw err;
}
var runwayNodes = xml.find('PropertyList/runway');
console.log("Threshold Nodes" + runwayNodes.length);
featureLookup = [];
var index = 0;
runwayNodes.map(r => {
var thresholds = r.find('threshold');
thresholds.map(n => {
var icon = threshold(n);
icon.index = index;
icon.addTo(layerGroup);
// Width in m
var runwayWidth = 20;
var latlon = convert(n.find('lat/text()').text() + " " + n.find('lon/text()').text());
var displ_m = Number(n.find('displ-m/text()').text());
var pointMiddle = new LatLonEllipsoidal(latlon.decimalLatitude, latlon.decimalLongitude);
var heading = Number(n.find('hdg-deg/text()').text());
var point1 = pointMiddle.destinationPoint(displ_m, heading);
var point2 = pointMiddle.destinationPoint(displ_m + 80, heading);
var runwayPoints = [];
runwayPoints.push(point1.destinationPoint(runwayWidth / 2, (heading + 90)));
runwayPoints.push(point2.destinationPoint(runwayWidth / 2, (heading + 90)));
runwayPoints.push(point2.destinationPoint(runwayWidth / 2, (heading - 90)));
runwayPoints.push(point1.destinationPoint(runwayWidth / 2, (heading - 90)));
var runwayPoly = takeoffPadPoly(runwayPoints);
runwayPoly.addTo(layerGroup);
}
)
index+=1;
}).sort();
return layerGroup;
});
// var jsonAirports = JSON.parse(geoJSON);
// return jsonAirports;
} catch (error) {
console.error(error);
}
return layerGroup;
};

View File

@@ -0,0 +1,120 @@
/*
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const store = require('../store');
const util = require('util');
const mathjs = require('mathjs');
var builder = require('xmlbuilder');
var writeThresholdXML = function (fDir, icao, coordinates) {
try {
try { fs.mkdirSync(path.join(fDir), { recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0]),{ recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1]), { recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1], icao[2]), { recursive: true })} catch (err) { }
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.threshold.new.xml');
var fBak = path.join(fDir, icao[0], icao[1], icao[2], icao + '.threshold.bak.xml');
if( fs.existsSync(f) ) {
fs.copyFileSync(f, fBak);
}
if (f == null)
return;
var xmlObj = { PropertyList: { runway: map(coordinates) } };
xmlString = builder.create(xmlObj).end({ pretty: true });
fs.writeFileSync(f, xmlString);
console.debug(xmlString);
} catch (error) {
console.error(error);
}
return;
}
var map = function (o) {
console.debug(o);
/**
<?xml version='1.0' encoding='ISO-8859-1'?>
<PropertyList>
<runway>
<threshold>
<lon>13.485025</lon>
<lat>52.367689</lat>
<rwy>07</rwy>
<hdg-deg>68.75</hdg-deg>
<displ-m>0.0</displ-m>
<stopw-m>300.0</stopw-m>
</threshold>
<threshold>
<lon>13.526097</lon>
<lat>52.377431</lat>
<rwy>25</rwy>
<hdg-deg>248.78</hdg-deg>
<displ-m>0.0</displ-m>
<stopw-m>300.0</stopw-m>
</threshold>
</runway>
<runway>
<threshold>
<lon>13.517142</lon>
<lat>52.380125</lat>
<rwy>07L</rwy>
<hdg-deg>68.77</hdg-deg>
<displ-m>0.0</displ-m>
<stopw-m>160.0</stopw-m>
</threshold>
<threshold>
<lon>13.554253</lon>
<lat>52.388919</lat>
<rwy>25R</rwy>
<hdg-deg>248.80</hdg-deg>
<displ-m>0.0</displ-m>
<stopw-m>300.0</stopw-m>
</threshold>
</runway>
</PropertyList>
*/
var ret = [];
Object.keys(o).forEach(key => {
ret.push({ threshold: mapThreshold(o[key])});
});
return ret;
}
/**
* <latitude>58.106924</latitude>
<longitude>6.610419</longitude>
<index>0</index>
<rwy>14</rwy>
<heading>131.16</heading>
<displacement>273</displacement>
<stopw_m>0.0</stopw_m>
* @param {*} t
*/
var mapThreshold = function (t) {
return t.map( t => {return {lon: t.longitude, lat: t.latitude, rwy: t.rwy, 'hdg-deg': t.heading, 'displ-m': t.displacement, 'stopw-m': t.stopw_m}});
}
export { writeThresholdXML };

View File

@@ -0,0 +1,78 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const fs = require('fs');
const path = require('path');
var xamel = require('xamel');
const convert = require('geo-coordinates-parser');
const store = require('../store');
const util = require('util');
const tower = require('./Tower.js');
var $ = require('jquery');
exports.readTowerXML = function (fDir, icao, force) {
try {
var layerGroup = L.layerGroup();
layerGroup.maxId = 0;
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.twr.xml');
var fNew = path.join(fDir, icao[0], icao[1], icao[2], icao + '.twr.new.xml');
if (f == null || !fs.existsSync(f))
return;
if (fNew != null && fs.existsSync(fNew) && !force) {
f = fNew;
}
var features = new Array();
// map.on("editable:vertex:new", function (event) {
// log.console("Add vertex " + event);
// });
var xmlGroundnet = fs.readFileSync(f, 'utf8').toString();
xamel.parse(xmlGroundnet, function (err, xml) {
console.debug("parsed " + path.basename(f));
if (err !== null) {
console.error("Error in " + airline);
throw err;
}
var towerNodes = xml.find('PropertyList/tower/twr');
console.log("Towers " + towerNodes.length);
towerNodes.map(n => {
var towerIcon = tower(n, layerGroup);
towerIcon['icao'] = icao
towerIcon.addTo(layerGroup);
/*
//DEBUG Code
var latlon = convert(n.find('lat/text()').text() + " " + n.find('lon/text()').text());
var marker = new L.Circle([latlon.decimalLatitude, latlon.decimalLongitude], 5);
marker.addTo(layerGroup);
*/
features.push(towerIcon);
}).sort();
return layerGroup;
});
// var jsonAirports = JSON.parse(geoJSON);
// return jsonAirports;
} catch (error) {
console.error(error);
}
return layerGroup;
};

View File

@@ -0,0 +1,63 @@
/*
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const store = require('../store');
const util = require('util');
const mathjs = require('mathjs');
var builder = require('xmlbuilder');
var featureLookup = [];
var parkings = [];
var pushBackNodeLookup = [];
exports.writeTowerXML = function (fDir, icao, coordinates) {
try {
try { fs.mkdirSync(path.join(fDir), { recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0]),{ recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1]), { recursive: true })} catch (err) { }
try { fs.mkdirSync(path.join(fDir, icao[0], icao[1], icao[2]), { recursive: true })} catch (err) { }
var f = path.join(fDir, icao[0], icao[1], icao[2], icao + '.twr.new.xml');
var fBak = path.join(fDir, icao[0], icao[1], icao[2], icao + '.twr.bak.xml');
if( fs.existsSync(f) ) {
fs.copyFileSync(f, fBak);
}
if (f == null)
return;
var xmlObj = { PropertyList: { tower: { twr: map(coordinates)} } };
xmlString = builder.create(xmlObj).end({ pretty: true });
fs.writeFileSync(f, xmlString);
console.debug(xmlString);
} catch (error) {
console.error(error);
}
return;
}
var map = function (o) {
console.debug(o);
/**
<lon>-1.6286902</lon>
<lat>59.53396633</lat>
<elev-m>3.05</elev-m>
*/
return { lon: o.longitude, lat: o.latitude, 'elev-m': o.height};
}

View File

@@ -0,0 +1,123 @@
/*
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const fs = require('fs');
const path = require('path');
var xamel = require('xamel');
const store = require('../store');
const util = require('util');
exports.readTrafficXML = function (f) {
try {
var ret = [];
var xmlTraffic = fs.readFileSync(f, 'utf8').toString();
xamel.parse(xmlTraffic, function (err, xml) {
console.debug("parsed " + path.basename(f));
if (err !== null) {
console.error("Error in " + airline);
throw err;
}
var requiredAircraft = xml.find('trafficlist/aircraft');
console.log("Aircraft " + requiredAircraft.length);
ret.concat(requiredAircraft);
var flights = xml.find('trafficlist/flight');
console.log("Flights " + flights.length);
ret.concat(flights);
console.log(ret.length);
ret = ret.concat(flights.map(flightMapper)).concat(requiredAircraft.map(aircraftMapper))
return ret;
});
return ret;
} catch (error) {
console.error(error);
}
};
/*
* <flight>
<callsign>Hebridean_1047</callsign>
<required-aircraft>HBR_BN_2</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>EGPU</port>
<time>4/14:50:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>EGEO</port>
<time>4/15:50:00</time>
</arrival>
<repeat>WEEK</repeat>
</flight>
*/
function flightMapper(params) {
return {
id: `${btoa(buildId(params))}`,
callsign: params.find('callsign').text(),
'required-aircraft': params.find('required-aircraft').text(),
arrival: {
port: params.find('arrival/port').text(),
time: params.find('arrival/time').text()
},
departure: {
port: params.find('departure/port').text(),
time: params.find('departure/time').text()
}
};
}
function buildId(params) {
return `${params.find('callsign').text()}_`+
`${params.find('arrival/port').text()}_`+
`${params.find('arrival/time').text()}_`+
`${params.find('departure/port').text()}_` +
`${params.find('departure/time').text()}`;
}
/*
<aircraft>
<model>Aircraft/BN-2/BN-2-Hebridean.xml</model>
<livery>HBR</livery>
<airline>HBR</airline>
<home-port>EGEO</home-port>
<required-aircraft>HBR_BN_2</required-aircraft>
<actype>BN2</actype>
<offset>0</offset>
<radius>8</radius>
<flighttype>gate</flighttype>
<performance-class>turboprop_transport</performance-class>
<registration>G-HEBO</registration>
<heavy>false</heavy>
</aircraft>
*/
function aircraftMapper(params) {
return {
model: params.find('model').text(),
livery: params.find('livery').text(),
airline: params.find('airline').text(),
'home-port': params.find('home-port').text(),
'required-aircraft': params.find('required-aircraft').text(),
actype: params.find('actype').text(),
offset: params.find('offset').text(),
radius: params.find('radius').text(),
flighttype: params.find('flighttype').text(),
'performance-class': params.find('performance-class').text(),
'registration': params.find('registration').text(),
'heavy': params.find('heavy').text(),
};
}

View File

@@ -0,0 +1,180 @@
/*
Copyright 2021 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
const fs = require('fs');
const path = require('path');
const store = require('../store');
const util = require('util');
const mathjs = require('mathjs');
var builder = require('xmlbuilder');
var parkingStats = (acc, cur) => {
if (!acc[cur.radius]) {
acc[cur.radius] = { count: 0, radius: cur.radius }
}
acc[cur.radius].count += 1
return acc
};
/**
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<trafficlist xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="traffic.xsd">
<aircraft>
<model>Aircraft/BN-2/BN-2-Hebridean.xml</model>
<livery>HBR</livery>
<airline>HBR</airline>
<home-port>EGEO</home-port>
<required-aircraft>HBR_BN_2</required-aircraft>
<actype>BN2</actype>
<offset>0</offset>
<radius>8</radius>
<flighttype>gate</flighttype>
<performance-class>turboprop_transport</performance-class>
<registration>G-HEBS</registration>
<heavy>false</heavy>
</aircraft>
<flight>
<callsign>Hebridean_1047</callsign>
<required-aircraft>HBR_BN_2</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>EGPU</port>
<time>4/14:50:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>EGEO</port>
<time>4/15:50:00</time>
</arrival>
<repeat>WEEK</repeat>
</flight>
</trafficlist>
*/
var writeTrafficXML = function (fDir, parkings, aircraft) {
try {
var icao = store.default.state.Airports.currentAirport.icao;
var aircraftList = aircraft;
try { fs.mkdirSync(path.join(fDir), { recursive: true }) } catch (err) { }
try { fs.mkdirSync(path.join(fDir, 'TST'), { recursive: true }) } catch (err) { }
var f = path.join(fDir, 'TST', icao + '.xml');
var parkingData = parkings.reduce(parkingStats, {});
/*
*
<flight>
<callsign>Hebridean_1001</callsign>
<required-aircraft>HBR_BN_2</required-aircraft>
<fltrules>VFR</fltrules>
<departure>
<port>EGEO</port>
<time>2/14:10:00</time>
</departure>
<cruise-alt>50</cruise-alt>
<arrival>
<port>EGEY</port>
<time>2/15:20:00</time>
</arrival>
<repeat>WEEK</repeat>
</flight>
*/
var flightMapper = function (pStat) {
var ret = [];
var blockSize = Math.min( pStat[1].count/6, 6);
for (let index = 0; index < pStat[1].count; index++) {
var aircraft = this[index];
var minutes = `${Math.floor(index/blockSize)}`.padStart(2, '0');
var seconds = `${index}`.padStart(2, '0');
for (let weekday = 0; weekday < 7; weekday++) {
ret.push({
callsign: `Test_${index}_${weekday}`,
'required-aircraft': aircraft['required-aircraft'],
fltrules: 'VFR',
departure: {
port: icao,
time: `${weekday}/12:${minutes}:${seconds}`
},
'cruise-alt': 50,
arrival: {
port: icao,
time: `${weekday}/13:${minutes}:${seconds}`
},
repeat: 'WEEK'
});
}
}
return ret;
}
/*
<aircraft>
<model>Aircraft/BN-2/BN-2-Hebridean.xml</model>
<livery>HBR</livery>
<airline>HBR</airline>
<home-port>EGEO</home-port>
<required-aircraft>HBR_BN_2</required-aircraft>
<actype>BN2</actype>
<offset>0</offset>
<radius>8</radius>
<flighttype>gate</flighttype>
<performance-class>turboprop_transport</performance-class>
<registration>G-HEBO</registration>
<heavy>false</heavy>
</aircraft>
*/
var aircraftMapper = function (pStat) {
var ret = [];
if (typeof this === 'undefined') {
return;
}
var possibleAircraft = this.filter(a => a.radius <= pStat[1].radius);
for (let index = 0; index < pStat[1].count; index++) {
var aircraft = possibleAircraft[Math.floor(Math.random() * possibleAircraft.length)];
aircraft['required-aircraft'] = `GG-${index}`;
aircraft.registration = `GG-${index}`;
aircraft['home-port'] = icao;
ret.push(aircraft);
}
return ret;
}
var aircraftList = Object.entries(parkingData).flatMap(aircraftMapper, aircraft).sort();
var flightList = Object.entries(parkingData).flatMap(flightMapper, aircraftList).sort();
var xmlObj = { trafficList: { aircraft: aircraftList, flight: flightList } };
var xmlString = builder.create(xmlObj).end({ pretty: true });
fs.writeFileSync(f, xmlString);
console.debug(xmlString);
} catch (error) {
console.error(error);
}
return;
}
export { writeTrafficXML as writeTrafficXML };

View File

@@ -14,6 +14,10 @@ Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.config.errorHandler = (err, vm, info) => {
console.error(err)
}
/* eslint-disable no-new */
new Vue({
components: { App },

268
src/renderer/phi/AILayer.js Normal file
View File

@@ -0,0 +1,268 @@
/* eslint-disable */
const $ = require('jquery');
const aiAircraftMarker = require('./AircraftMarker').default;
var aiLayerLookup = {};
/**http://wiki.openstreetmap.org/wiki/Zoom_levels*/
var metersPerPixel = function (latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI / 180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel + 8);
};
var pixelValue = function (latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
};
function mapSGPropertyNode(node) {
var o = {};
node.children.forEach(child => {
if (child.nChildren > 0) {
o[child['name']] = mapSGPropertyNode(child);
} else {
switch (child.type) {
case "string":
o[child['name']] = child.value;
break;
case "bool":
o[child['name']] = Boolean(child.value);
break;
case "double":
case "int":
o[child['name']] = child.value;
break;
default:
break;
}
}
});
o.name = node.name;
o.index = node.index;
o.path = node.path;
return o;
}
(function (factory) {
if (typeof define === "function" && define.amd) {
// AMD. Register as an anonymous module.
define([
'knockout', 'leaflet', 'props'
], factory);
} else {
// Browser globals
factory();
}
}(function (ko, leaflet, SGPropertyNode) {
var AITypeToCssClassMap = {
aircraft: "ai-aircraft-marker-icon",
multiplayer: "mp-aircraft-marker-icon"
}
function formatFL(num) {
return "F" + ("000" + (num / 100).toFixed(0)).substr(-3, 3);
}
function ViewModel(h, l) {
var self = this;
self.heading = h;
self.labelLines = l;
}
L.AILayer = L.GeoJSON.extend({
options: {
pointToLayer: function (feature, latlng) {
var options = {
title: feature.properties.callsign,
alt: feature.properties.callsign,
riseOnHover: true,
draggable: true,
};
var aiMarker = null;
if (feature.properties.type == "aircraft") {
if (aiLayerLookup[feature.id] === undefined) {
aiMarker = aiAircraftMarker(latlng, feature.properties);
aiMarker.on('add', function (e) {
});
aiMarker.options.draggable = true;
//We can't drag multiplayer
if (feature.properties.type == "aircraft") {
aiMarker.on('dragstart', function (evt) {
evt.target.isDragging = true;
});
aiMarker.on('dragend', function (evt) {
if (evt.target !== this)
return;
var pos = evt.target.getLatLng();
var props = {
name: "position",
children: [
{
name: "latitude-deg",
value: pos.lat,
}, {
name: "longitude-deg",
value: pos.lng,
},
],
};
$.post("json" + feature.properties.path, JSON.stringify(props));
evt.target.isDragging = false;
});
}
aiLayerLookup[feature.id] = aiMarker;
return aiMarker;
} else {
var aiMarker = aiLayerLookup[feature.id];
aiMarker.setLatLng({lat: feature.geometry.coordinates[1],
lng: feature.geometry.coordinates[0]});
aiMarker.updateProperties(feature.properties);
return aiMarker;
}
}
},
// onEachFeature : function(feature, layer) {
// },
},
onAdd: function (map) {
L.GeoJSON.prototype.onAdd.call(this, map);
this.update(++this.updateId);
},
onRemove: function (map) {
this.updateId++;
L.GeoJSON.prototype.onRemove.call(this, map);
},
stop: function () {
this.updateId++;
},
// Refresh method called every 10s to reload other aircraft
updateId: 0,
update: function (id) {
var self = this;
if (self.updateId != id)
return;
var url = this.options.url + "/json/ai/models?d=99";
var jqxhr = $.get(url).done(function (data) {
try {
self.clearLayers();
self.addData(self.aiPropsToGeoJson(data, [
"aircraft", "multiplayer", "carrier"
], self._map.getBounds()));
} catch (error) {
console.error(error);
}
}).fail(function (a, b) {
self.updateId++;
// alert('failed to load AI data');
}).always(function () {
});
if (self.updateId == id) {
setTimeout(function () {
self.update(id)
}, 10000);
}
},
// Builds the GeoJSON representation of AI, Multiplayer and Carriers
aiPropsToGeoJson: function (props, types, bounds) {
var geoJSON = {
type: "FeatureCollection",
features: [],
};
types.forEach(function (type) {
props.children.filter(childObject => childObject.path.includes(type))
.map(mapSGPropertyNode)
.forEach(function (child) {
if (!child["valid"])
return;
var path = child.path;
var position = child.position;
var orientation = child.orientation;
var velocities = child.velocities;
var lon = position["longitude-deg"];
var lat = position["latitude-deg"];
if (false == bounds.contains(L.latLng(lat, lon))) {
return;
}
var alt = position["altitude-ft"];
var heading = orientation["true-heading-deg"];
var id = child.id;
var callsign = "";
var name = "";
var speed = 0;
var departureAirportId = "";
var arrivalAirportId = "";
if (type == "multiplayer") {
name = child["sim"]["model"]["path"];
}
if (type == "carrier") {
callsign = child["sign"];
name = child["name"];
speed = velocities["speed-kts"];
} else {
callsign = child["callsign"];
speed = velocities["true-airspeed-kt"];
departureAirportId = child["departure-airport-id"];
arrivalAirportId = child["arrival-airport-id"];
}
geoJSON.features.push({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
lon, lat, alt.toFixed(0)
],
},
"id": id,
"properties": {
"path": path,
"type": type,
"heading": heading.toFixed(0),
"speed": speed.toFixed(0),
"callsign": callsign,
"name": name,
"departureAirportId": departureAirportId,
"arrivalAirportId": arrivalAirportId,
},
});
});
});
return geoJSON;
},
});
L.aiLayer = function (options) {
return new L.AILayer(null, options);
}
}));
export function aiLayer(options) {
return undefined //new L.AILayer(null, options);
}

View File

@@ -0,0 +1,87 @@
/* eslint-disable */
const fs = require('fs');
const path = require('path');
/**http://wiki.openstreetmap.org/wiki/Zoom_levels*/
var metersPerPixel = function (latitude, zoomLevel) {
var earthCircumference = 40075017;
var latitudeRadians = latitude * (Math.PI / 180);
return earthCircumference * Math.cos(latitudeRadians) / Math.pow(2, zoomLevel + 8);
};
var pixelValue = function (latitude, meters, zoomLevel) {
return meters / metersPerPixel(latitude, zoomLevel);
};
function stripSVG(fName) {
var rx = /<\s*svg[^>]*>([\s\S]*)<\s*\/svg[^>]*>/gm;
var svg = fs.readFileSync(path.join(__static, '/', fName), 'utf8');
var svg2 = rx.exec(svg);
return svg2[0];
}
const airLinerSVG = stripSVG('Airplane_silhouette.svg');
const gaSVG = stripSVG('Black_aircraft_icon.svg');
L.ParkingAircraftMarker = L.Marker.extend({
options: {
zIndexOffset: 10000,
},
initialize: function (latlng, options) {
L.Marker.prototype.initialize(latlng, options);
L.Util.setOptions(this, options);
this.heading = options.heading;
this.updateIcon();
this.isDragging = false;
},
updateProperties: function(properties) {
this.heading = properties.heading;
this.updateIcon();
},
updateIcon : function() {
if(this._map !== undefined && this._map !== null) {
var metersPP = metersPerPixel(this._map.getCenter().lat, this._map.getZoom());
console.log(metersPP);
var scale = 0.07 / metersPP;
this.setIcon(L.divIcon({
iconSize: null,
className: 'aircraft-marker-icon',
html: `<div style=\'transform: translateX(-10px) translateY(-10px); height: 20px; width: 20px; border: 1px red\'>${airLinerSVG}</div>`,
}));
this.getElement().children.item(0).children.item(0).style = `transform: translateX(-200px) translateY(-200px) scale(${scale},${scale}) rotate(${(this.heading)-45}deg)`;
}
else {
this.setIcon(L.divIcon({
iconSize: null,
className: 'aircraft-marker-icon',
html: `<div style=\'transform: rotate(${this.heading}deg) scale(0.001,0.001) \'>${airLinerSVG}</div>`,
}));
}
},
onAdd : function(map) {
var metersPP = metersPerPixel(map.getCenter().lat, map.getZoom());
console.log(metersPP);
console.log(this);
this.updateIcon();
},
});
//Builds a marker for a ai or multiplayer aircraft
module.exports.default = function (latlng, options) {
return new L.ParkingAircraftMarker(latlng, options);
}
/*
var l1 = feature.properties.callsign,
l2 = feature.properties.heading + 'T ' + feature.properties.speed + 'KTAS ' +
formatFL(feature.geometry.coordinates[2]),
l3 = feature.properties.departureAirportId + ' -> ' + feature.properties.arrivalAirportId;
*/

View File

@@ -1,26 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
import L from 'leaflet'
import { createPersistedState, createSharedMutations } from 'vuex-electron'
import modules from './modules'
Vue.use(Vuex)
//https://a.tile.openstreetmap.de
//'http://{s}.tile.osm.org/{z}/{x}/{y}.png'
export default new Vuex.Store({
modules,
plugins: [
createPersistedState(),
createSharedMutations()
],
strict: process.env.NODE_ENV !== 'production',
state: { zoom: 13,
center: L.latLng(47.413220, -1.219482),
url: 'http://{s}.tile.openstreetmap.de/{z}/{x}/{y}.png',
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
marker: L.latLng(47.413220, -1.219482)}
})

View File

@@ -6,19 +6,32 @@ const ADD_AIRPORT = 'ADD_AIRPORT';
const SET_AIRPORTS = 'SET_AIRPORTS';
const SET_UNFILTERED_AIRPORTS = 'SET_UNFILTERED_AIRPORTS';
const RESET_AIRPORTS = 'RESET_AIRPORTS';
const SET_CURRENT_AIRPORT = 'SET_CURRENT_AIRPORT';
const UPDATE_CURRENT_AIRPORT = 'UPDATE_CURRENT_AIRPORT';
const state = {
airports: [], unfilteredairports:[]
airports: [], unfilteredairports:[], currentAirport: {}
}
const mutations = {
'DELETE_INDEXED_DB' () { },
ADD_AIRPORT (state, airports) {
state.airports.push(airports);
airports.forEach(airport => {
airport.properties.manual = true;
idb.saveAirport(airport);
});
Vue.set(state, 'airports', state.airports.concat(airports));
},
'UPDATE_CURRENT_AIRPORT' (state, airport) {
Vue.set(state, 'currentAirport', airport.properties);
idb.saveAirport(airport);
},
SET_AIRPORTS (state, airports) {
Vue.set(state, 'airports', airports);
},
SET_CURRENT_AIRPORT (state, airport) {
Vue.set(state, 'currentAirport', airport);
},
SET_UNFILTERED_AIRPORTS (state, airports) {
state.unfilteredairports = airports;
},
@@ -38,7 +51,48 @@ const actions = {
let airports = await idb.getAirports();
context.commit(SET_AIRPORTS, airports
.filter(point => typeof point.geometry.coordinates !== "undefined" )
.filter(point => point.properties.flights > 0 ));
.filter(point => (point.properties.flights > 0 || point.properties.manual)));
} catch (error) {
console.error(error);
}
},
async addAirline(context, airlineCode) {
try {
airlineCode = airlineCode.toUpperCase()
let airports = await idb.getAirports();
let searchRegex = new RegExp(this.state.Airports.currentAirport.icao, 'i');
let airport = airports
.filter(point => typeof point.geometry.coordinates !== "undefined" )
.filter(a => searchRegex.test(a.properties.icao));
if (airport[0] !== undefined && !airport[0].properties.airlines.includes(airlineCode)) {
airport[0].properties.airlines.push(airlineCode);
airport[0].properties.airlines.sort();
}
context.commit(UPDATE_CURRENT_AIRPORT, airport[0]);
} catch (error) {
console.error(error);
}
},
async setCurrentAirport(context, icao) {
try {
let airports = await idb.getAirports();
let searchRegex = new RegExp(icao, 'i');
let airport = airports
.filter(point => typeof point.geometry.coordinates !== "undefined" )
.filter(a => searchRegex.test(a.properties.icao));
context.commit(SET_CURRENT_AIRPORT, airport[0].properties);
} catch (error) {
console.error(error);
}
},
async getAirport(context, icao) {
try {
let airports = await idb.getAirports();
let searchRegex = new RegExp(icao, 'i');
let airport = airports
.filter(point => typeof point.geometry.coordinates !== "undefined" )
.filter(a => searchRegex.test(a.properties.icao));
context.commit(ADD_AIRPORT, airport);
} catch (error) {
console.error(error);
}

View File

@@ -1,24 +1,43 @@
/**
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
import Vue from 'vue'
const state = {
type: 'none',
index: 'none',
data: {airports: {}, parking: {}, arc: {}, node: {}, runway: {}}
editing: false,
data: {airports: {}, parking: {}, arc: {}, multiarc: {}, node: {}, runway: {}, threshold: {}, tower: {}}
}
const SET_EDIT_AIRPORT = 'SET_EDIT_AIRPORT'
const SET_EDIT_PARKING = 'SET_EDIT_PARKING'
const SET_EDIT_ARC = 'SET_EDIT_ARC'
const SET_EDIT_MULTI_ARC = 'SET_EDIT_MULTI_ARC'
const SET_EDIT_RUNWAY = 'SET_EDIT_RUNWAY'
const mutations = {
SET_EDIT_TYPE (state, type) {
state.type = type
},
SET_EDIT (state, editing) {
state.editing = editing
},
SET_EDIT_AIRPORT (state, airport) {
Vue.set(state.data, 'airport', airport)
state.index = airport.icao
state.type = 'airport'
},
SET_EDIT_PARKING (state, parking) {
Vue.set(state, 'data', {})
var p = Object.assign({}, parking)
Vue.set(state.data, 'parking', p)
Vue.set(state, 'index', p.index)
@@ -28,19 +47,19 @@ const mutations = {
if (node === undefined) {
return
}
Vue.set(state, 'data', {})
Vue.set(state.data, 'node', node)
Vue.set(state, 'index', node.index)
Vue.set(state, 'type', 'node')
},
SET_EDIT_RUNWAY (state, runway) {
Vue.set(state, 'data', {})
Vue.set(state.data, 'runway', runway)
Vue.set(state.data, 'node', runway)
Vue.set(state, 'index', runway.index)
Vue.set(state, 'type', 'runway')
},
SET_EDIT_ARC (state, arc) {
Vue.set(state, 'data', {})
if (arc === undefined) {
return
}
Vue.set(state.data, 'arc', arc)
if (state.data.arc.name === undefined) {
Vue.set(state.data.arc, 'name', '')
@@ -48,24 +67,111 @@ const mutations = {
Vue.set(state, 'index', arc.index)
Vue.set(state, 'type', 'arc')
},
SET_EDIT_MULTI_ARC (state, arc) {
if (arc === undefined) {
return
}
Vue.set(state.data.multiarc, 'isPushBackRoute', arc.isPushBackRoute)
Vue.set(state.data.multiarc, 'direction', arc.direction)
if (state.data.multiarc.name === undefined) {
Vue.set(state.data.multiarc, 'name', '')
}
Vue.set(state, 'index', arc.index)
Vue.set(state, 'type', 'multiarc')
},
'SET_EDIT_MULTI_ARC_IDS' (state, arcs) {
if (arcs === undefined) {
return
}
if (!state.data || state.type !== 'multiarc') {
return
}
if (state.data.multiarc.ids === undefined) {
state.data.multiarc.ids = []
}
state.data.multiarc.ids = state.data.multiarc.ids.concat(arcs.filter(n => n).filter((v, i, a) => a.indexOf(v) === i))
},
'SET_EDIT_PARKING_NAME' (state, parkingName) {
Vue.set(state.data.parking, 'name', parkingName)
},
'SET_EDIT_PARKING_NUMBER' (state, parkingName) {
Vue.set(state.data.parking, 'number', parkingName)
},
'SET_EDIT_PARKING_HEADING' (state, heading) {
while (heading >= 360) {
heading -= 360
}
while (heading < 0) {
heading += 360
}
Vue.set(state.data.parking, 'heading', heading)
},
'SET_EDIT_PARKING_AIRLINES' (state, airlineCodes) {
Vue.set(state.data.parking, 'airlineCodes', airlineCodes)
},
'SET_EDIT_PARKING_TYPE' (state, type) {
Vue.set(state.data.parking, 'type', type)
},
'SET_EDIT_PARKING_RADIUS' (state, radius) {
Vue.set(state.data.parking, 'radius', radius)
},
'SET_EDIT_PARKING_COORDS' (state, coords) {
Vue.set(state.data.parking, 'coords', coords)
},
'SET_EDIT_PARKING_NOSE_COORDS' (state, coords) {
Vue.set(state.data.parking, 'nosecoords', coords)
},
'SET_EDIT_ARC_NAME' (state, arcName) {
Vue.set(state.data.arc, 'name', arcName)
if (state.type === 'arc') {
Vue.set(state.data.arc, 'name', arcName)
} else {
Vue.set(state.data.multiarc, 'name', arcName)
}
},
'SET_EDIT_PUSHBACK' (state, isPushBackRoute) {
Vue.set(state.data.arc, 'isPushBackRoute', isPushBackRoute)
if (state.type === 'arc') {
Vue.set(state.data.arc, 'isPushBackRoute', Number(isPushBackRoute))
} else {
Vue.set(state.data.multiarc, 'isPushBackRoute', Number(isPushBackRoute))
}
},
'SET_EDIT_DIRECTION' (state, direction) {
if (state.type === 'arc') {
Vue.set(state.data.arc, 'direction', direction)
} else {
Vue.set(state.data.multiarc, 'direction', direction)
}
},
'SET_EDIT_HOLDPOINTTYPE' (state, holdPointType) {
Vue.set(state.data.node, 'holdPointType', holdPointType)
},
'SET_EDIT_NODE_COORDS' (state, coords) {
Vue.set(state.data.node, 'coords', coords)
},
'SET_EDIT_TOWER_COORDS' (state, coords) {
state.type = 'tower'
if (!state.data.tower) {
state.data.tower = {}
}
if (!state.data.tower.coords) {
state.data.tower.coords = {}
}
Vue.set(state.data.tower.coords, 'latitude', coords.split(' ')[0])
Vue.set(state.data.tower.coords, 'longitude', coords.split(' ')[1])
},
'SET_EDIT_TOWER_HEIGHT' (state, height) {
Vue.set(state.data.tower, 'height', height)
},
'SET_EDIT_THRESHOLD_COORDS' (state, threshold) {
state.type = 'threshold'
Vue.set(state.data.threshold, 'runway', threshold.rwy)
Vue.set(state.data.threshold, 'displacement', threshold.displacement)
},
'SET_EDIT_THRESHOLD_DISPLACEMENT' (state, displacement) {
Vue.set(state.data.threshold, 'displacement', displacement)
},
'SET_EDIT_ISONRUNWAY' (state, isOnRunway) {
Vue.set(state.data.node, 'isOnRunway', isOnRunway)
}
@@ -81,11 +187,42 @@ const actions = {
async setParking (context, parking) {
context.commit(SET_EDIT_PARKING, parking)
},
async setParkingRadius (context, radius) {
context.commit('SET_EDIT_PARKING_RADIUS', radius)
},
async setParkingHeading (context, heading) {
context.commit('SET_EDIT_PARKING_HEADING', heading)
},
async setParkingCoords (context, coords) {
context.commit('SET_EDIT_PARKING_COORDS', coords)
},
async setParkingNoseCoords (context, coords) {
context.commit('SET_EDIT_PARKING_NOSE_COORDS', coords)
},
async setArc (context, arc) {
context.commit(SET_EDIT_ARC, arc)
},
async setMultiArc (context, arc) {
context.commit(SET_EDIT_MULTI_ARC, arc)
},
async setMultiArcIds (context, arc) {
context.commit('SET_EDIT_MULTI_ARC_IDS', arc)
},
async setNode (context, node) {
context.commit('SET_EDIT_NODE', node)
context.commit('SET_EDIT_NODE', node.attributes)
context.commit('SET_EDIT_NODE_COORDS', node.lat.toFixed(6) + ' ' + node.lng.toFixed(6))
},
async setTowerCoords (context, node) {
context.commit('SET_EDIT_TOWER_COORDS', node)
},
async setTowerHeight (context, height) {
context.commit('SET_EDIT_TOWER_HEIGHT', height)
},
async setThreshold (context, node) {
context.commit('SET_EDIT_THRESHOLD_COORDS', node)
},
async setDisplacement (context, displacement) {
context.commit('SET_EDIT_THRESHOLD_DISPLACEMENT', displacement)
}
}

View File

@@ -0,0 +1,43 @@
import Vue from 'vue'
const state = { items: [] }
const mutations = {
ADD_FREQUENCY: (state, item) => {
state.items.push(item)
},
UPDATE_FREQUENCY: (state, item) => {
const existingItem = state.items.find((i) => i.id === item.id)
Object.assign(existingItem, item)
},
REMOVE_FREQUENCY: (state, item) => {
const index = state.items.indexOf(item)
if (index > -1) {
state.items.splice(index, 1)
}
},
SET_FREQUENCIES (state, frequencies) {
Vue.set(state, 'items', frequencies)
}
}
const actions = {
async addFrequency (context, frequency) {
context.commit('ADD_FREQUENCY', frequency)
},
async updateFrequency (context, frequency) {
context.commit('SET_GROUND', frequency)
},
async removeFrequency (context, frequency) {
context.commit('REMOVE_FREQUENCY', frequency)
},
async setFrequencies (context, frequencies) {
context.commit('SET_FREQUENCIES', frequencies)
}
}
export default {
state,
mutations,
actions
}

View File

@@ -0,0 +1,51 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
import Vue from 'vue'
const state = { icao: '', groundnetLoaded: false, pavementLoaded: false }
const mutations = {
SET_ICAO_LOADING (state, icao) {
Vue.set(state, 'icao', icao)
},
SET_GROUNDNET_LOADED (state, loaded) {
Vue.set(state, 'groundnetLoaded', loaded)
},
SET_PAVEMENT_LOADED (state, loaded) {
Vue.set(state, 'pavementLoaded', loaded)
}
}
const actions = {
async setIcaoLoading (context, p) {
context.commit('SET_ICAO_LOADING', p)
},
async setGroundnetLoaded (context, p) {
if (typeof p !== 'boolean') {
console.error('Not Boolean')
}
context.commit('SET_GROUNDNET_LOADED', p)
},
async setPavementLoaded (context, p) {
if (typeof p !== 'boolean') {
console.error('Not Boolean')
}
context.commit('SET_PAVEMENT_LOADED', p)
}
}
export default {
state,
mutations,
actions
}

View File

@@ -0,0 +1,60 @@
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
import Vue from 'vue'
const state = { items: [] }
const mutations = {
ADD_PARKING: (state, item) => {
state.items.push(item)
},
UPDATE_PARKING: (state, item) => {
const existingItem = state.items.find((i) => i.index === item.index)
if (existingItem !== undefined) {
Object.assign(existingItem, item)
}
},
SET_PARKINGS (state, parkings) {
Vue.set(state, 'items', parkings)
},
'SET_EDIT_PARKING_ITEM_NAME' (state, value) {
const existingItem = state.items.find((i) => i.index === value[0])
Vue.set(existingItem, 'name', value[1])
},
'SET_EDIT_PARKING_ITEM_NUMBER' (state, value) {
const existingItem = state.items.find((i) => i.index === value[0])
Vue.set(existingItem, 'number', value[1])
},
'SET_EDIT_PARKING_ITEM_TYPE' (state, value) {
const existingItem = state.items.find((i) => i.index === value[0])
Vue.set(existingItem, 'type', value[1])
}
}
const actions = {
async addParking (context, p) {
context.commit('ADD_PARKING', p)
},
async updatedParking (context, p) {
context.commit('UPDATE_PARKING', p)
},
async setParkings (context, parkings) {
context.commit('SET_PARKINGS', parkings)
}
}
export default {
state,
mutations,
actions
}

View File

@@ -1,43 +1,107 @@
/* eslint-disable */
const path = require('path');
const fs = require('fs');
const state = {
settings: { flightgearDirectory: '.' },
settings: { numberOfSaves: 1, flightgearDirectory: '.', testDirectory: '.', email: 'flightgearairports@example.org', name: 'unknown', phi_url: 'http://localhost:8080' },
zoom: 14,
center: [47.413220, -1.219482],
bounds: undefined
bounds: undefined,
wip: []
}
const mutations = {
'DELETE_INDEXED_DB' () { },
'FLIGHTGEAR_DIRECTORY' (state, flightgearDirectory) {
state.settings.flightgearDirectory = flightgearDirectory
state.settings.flightgearDirectory_ai = flightgearDirectory + '/data/AI'
state.settings.flightgearDirectory_traffic = flightgearDirectory + '/data/AI/Traffic'
state.settings.flightgearDirectory_apt = flightgearDirectory + '/data/Airports/apt.dat.gz'
'DELETE_INDEXED_DB'() { },
'FLIGHTGEAR_DIRECTORY'(state, flightgearDirectory) {
try {
fs.accessSync(flightgearDirectory)
state.settings.flightgearDirectory = flightgearDirectory
} catch (err) {
try {
fs.accessSync(flightgearDirectory.replace(/\.App/, ''))
state.settings.flightgearDirectory = flightgearDirectory.replace(/\.App/, '')
} catch (error) {
console.warn(error)
}
}
state.settings.flightgearDirectory_ai = flightgearDirectory + path.sep + 'AI'
state.settings.flightgearDirectory_traffic = path.join(flightgearDirectory, 'AI', 'Traffic');
state.settings.flightgearDirectory_apt = path.join(flightgearDirectory, 'Airports', 'apt.dat.gz');
},
'AIPORTS_DIRECTORY' (state, airportsDirectory) {
'AIPORTS_DIRECTORY'(state, airportsDirectory) {
state.settings.airportsDirectory = airportsDirectory
},
'ZOOM' (state, zoom) {
'TEST_DIRECTORY'(state, testDirectory) {
state.settings.testDirectory = testDirectory
},
'ZOOM'(state, zoom) {
state.zoom = zoom
},
'CENTER' (state, center) {
'CENTER'(state, center) {
state.center = center
},
'BOUNDS' (state, bounds) {
'BOUNDS'(state, bounds) {
state.bounds = bounds
},
'SET_EMAIL'(state, email) {
state.settings.email = email
},
'SET_NAME'(state, name) {
state.settings.name = name
},
'SET_NUMBER_OF_SAVES'(state, numberOfSaves) {
state.settings.numberOfSaves = numberOfSaves
},
'SET_PHI_URL'(state, phi_url) {
state.settings.phi_url = phi_url
},
'SET_SCAN_LOGGING'(state, scanLogging) {
state.settings.scanLogging = scanLogging
},
'ADD_WIP'(state, airport) {
const item = state.wip.find((e) => e.icao === airport.icao)
airport.time = Date.now()
if (item === null || item === undefined) {
state.wip.push(airport)
} else {
Object.assign(item, airport)
}
state.wip.sort((w1, w2) => w1.time - w2.time)
},
'UPLOAD_WIP'(state, icao) {
const item = state.wip.find((e) => e.icao === icao)
item.upload = Date.now()
state.wip.sort((p, p2) => { return p.time - p2.time })
},
'REMOVE_WIP'(state, icao) {
const item = state.wip.find((e) => e.icao === icao)
const index = state.wip.indexOf(item)
if (index > -1) {
state.wip.splice(index, 1)
}
}
}
const plugins = []
const actions = {
async setZoom (context, zoom) {
async setZoom(context, zoom) {
context.commit('ZOOM', zoom)
},
async setCenter (context, center) {
context.commit('CENTER', center)
async setCenter(context, center) {
if (center.lat !== context.state.center.lat || center.lng !== context.state.center.lng) {
context.commit('CENTER', center)
}
},
async setBounds (context, bounds) {
async setBounds(context, bounds) {
context.commit('BOUNDS', bounds)
},
async addWip(context, airport) {
context.commit('ADD_WIP', airport)
},
async removeWip(context, icao) {
context.commit('REMOVE_WIP', icao)
}
}

View File

@@ -1,50 +0,0 @@
/* eslint-disable */
const lineReader = require('readline');
const fs = require('fs');
const airports = require('./airports');
var scanMethods = {
1: (l, apts) => {
console.log('Airport:', l);
var airportFeature = airports.getAirport(apts, l[4]);
console.log(JSON.stringify(airportFeature));
airportFeature.properties.name = l.slice(5).join(' ').replace('\t', ' ');
console.debug(airportFeature.properties.name);
// apts.update(airportFeature);
},
99: (l) => {
console.log('Finished');
}
};
function scan (f, apts) {
console.log(f);
lineReader.createInterface({
input: fs.createReadStream(f)
}).on('line', function (line) {
var fields = line.split(/[ ]+/);
// var fields = line.match('([0-9]+)');
if (fields == null)
return;
var scanMethod = scanMethods[fields[0]];
if (scanMethod != null) {
scanMethod(fields, apts);
}
else {
if (fields[0] == '99') {
lineReader.close();
}
// console.log('Ignored:', line);
}
}).on('error', function (err) {
console.log(err);
lr.close();
}).on('close', function () {
console.log("End");
airports.save();
});
}
// export default { scan, name }

View File

@@ -1,46 +1,178 @@
/* eslint-disable */
const lineReader = require('readline');
const zlib = require('zlib');
var icao;
async function asyncForEach(array, callback) {
function aptForEach(array, apt, features, callback) {
logger('info', "AsyncForEach Len " + array.length);
for (let index = 0; index < array.length; index++) {
try {
var res = await callback(array[index], index, array);
logger('info', "Index " + index + " " + res);
apt = callback(array[index], index, apt);
logger('info', "Index : " + index + " Result : " + apt);
} catch (error) {
logger('error', error);
}
}
saveAirport(features, apt).then(t => {
postMessage(['progress', array.length]);
logger('info', `Stored : ${apt.icao}` );
if( apt.last ) {
postMessage('DONE');
}
});
}
this.boundingBox = null;
var scanMethods = {
1: async (l, apts) => {
var promise = new Promise(function (resolve, reject) {
1: (l, apts, apt) => {
try {
logger('info', 'Airport:', l);
icao = l[4];
saveName(apts, l[4], l.slice(5).join(' ').replace('\t', ' '))
.then(result => {
resolve(result)
}).catch( err => {reject(err)});
});
return promise;
apt.icao = l[4];
apt.name = l.slice(5).join(' ').replace('\t', ' ');
apt.type = 'airport'
return apt;
} catch (error) {
reject(error);
}
},
14: async (l, apts) => {
var promise = new Promise(function (resolve, reject) {
logger('info','Viewport:', l);
saveCoordinates(apts, icao, l[1], l[2]).then(result => {
resolve(result)
}).catch( err => {reject(err)});;
});
return promise;
14: (l, apts, apt) => {
logger('info', 'Viewport:', l);
try {
updateBoundingBox(apt, l[1], l[2]);
return apt;
} catch (error) {
reject(error);
}
},
99: async (l) => {
logger('info','Finished');
16: (l, apts, apt) => {
logger('info', 'Seaplane:', l);
apt.icao = l[4];
apt.name = l.slice(5).join(' ').replace('\t', ' ');
apt.type = 'seaplane'
resolve(apt);
},
17: (l, apts, apt) => {
logger('info', 'Heliport:', l);
apt.icao = l[4];
apt.name = l.slice(5).join(' ').replace('\t', ' ');
apt.type = 'heliport'
},
18: (l, apts, apt) => {
logger('info', 'Airport light beacon:', l);
try {
updateBoundingBox(apt, l[1], l[2]);
return apt;
} catch (error) {
reject(error);
}
},
19: (l, apts, apt) => {
logger('info', 'Windsock:', l);
try {
updateBoundingBox(apt, l[1], l[2]);
return apt;
} catch (error) {
reject(error);
}
},
111: (l, apts, apt) => {
logger('info', 'Node :', l);
try {
updateBoundingBox(apt, l[1], l[2]);
return apt;
} catch (error) {
reject(error);
}
},
112: (l, apts, apt) => {
logger('info', 'Node :', l);
try {
updateBoundingBox(apt, l[1], l[2]);
return apt;
} catch (error) {
reject(error);
}
},
113: (l, apts, apt) => {
logger('info', 'Node :', l);
try {
updateBoundingBox(apt, l[1], l[2]);
return apt;
} catch (error) {
reject(error);
}
},
99: (l) => {
logger('info', 'Finished');
}
};
function updateBoundingBox(apt, lat, lon) {
if (lat > 90 || lat < -90) {
logger('debug', `${lat} out of bounds`);
}
if (!apt.boundingBoxCenter || isNaN(apt.boundingBoxCenter[0]) || isNaN(apt.boundingBoxCenter[1])) {
apt.boundingBoxCenter = [lon, lat];
} else {
var avgLon = (Number(lon) + Number(apt.boundingBoxCenter[0])) / 2;
var avgLat = (Number(lat) + Number(apt.boundingBoxCenter[1])) / 2;
apt.boundingBoxCenter = [avgLon, avgLat];
}
}
async function saveAirport(features, apt) {
var promise = new Promise(function (resolve, reject) {
var transaction = features.transaction("airports", "readwrite");
var objectStore = transaction.objectStore("airports");
var index = objectStore.index('icaoIndex');
if (!apt || !apt.icao) {
return;
}
var objectGetRequest = index.get(apt.icao);
objectGetRequest.onsuccess = function (event) {
logger('info', 'objectGetRequest', event);
var feature = event.target.result;
if (!feature) {
feature = {
"type": "Feature",
'properties': { 'icao': icao, 'name': '', 'twr': false, 'threshold': false, 'flights': 0, airlines: [] },
'geometry': {
"type": "Point"
}
};
}
feature.properties.name = apt.name;
feature.properties.icao = apt.icao;
feature.properties.type = apt.type;
feature.geometry.coordinates = apt.boundingBoxCenter;
logger('info', "Storing Airport : " + feature.properties.icao + " Name : " + feature.properties.name);
// Create another request that inserts the item back into the database
var updateAirportRequest = objectStore.put(feature);
// Log the transaction that originated this request
logger('info', "The transaction that originated this request is " + updateAirportRequest);
// When this new request succeeds, run the displayData() function again to update the display
updateAirportRequest.onsuccess = function (event) {
logger('info', "Stored Name : " + event.target.result);
resolve(event.target.result);
};
updateAirportRequest.onerror = function (event) {
logger('info', "Error ", event);
reject(event);
};
};
objectGetRequest.onerror = function (event) {
logger('info', "Error ", event);
reject(event);
};
});
return promise;
}
async function saveName(features, icao, name) {
var promise = new Promise(function (resolve, reject) {
var transaction = features.transaction("airports", "readwrite");
@@ -62,32 +194,32 @@ async function saveName(features, icao, name) {
}
feature.properties.name = name;
feature.properties.icao = icao;
logger('info',"Storing ICAO : " + feature.properties.icao + " Name : " + name );
logger('info', "Storing ICAO : " + feature.properties.icao + " Name : " + name);
// Create another request that inserts the item back into the database
var updateAirportRequest = objectStore.put(feature);
// Log the transaction that originated this request
logger('info',"The transaction that originated this request is " + updateAirportRequest);
logger('info', "The transaction that originated this request is " + updateAirportRequest);
// When this new request succeeds, run the displayData() function again to update the display
updateAirportRequest.onsuccess = function (event) {
logger('info',"Stored Name : " + event.target.result);
logger('info', "Stored Name : " + event.target.result);
resolve(event.target.result);
};
updateAirportRequest.onerror = function (event) {
logger('info',"Error ", event);
logger('info', "Error ", event);
reject(event);
};
};
objectGetRequest.onerror = function (event) {
logger('info',"Error ", event);
logger('info', "Error ", event);
reject(event);
};
});
return promise;
}
async function saveCoordinates(features, icao, lat, lon) {
async function saveCoordinates(features, icao, boundingBoxCenter) {
var promise = new Promise(function (resolve, reject) {
var transaction = features.transaction("airports", "readwrite");
@@ -96,7 +228,7 @@ async function saveCoordinates(features, icao, lat, lon) {
var objectStoreRequest = index.get(icao);
objectStoreRequest.onsuccess = function (event) {
logger('info', 'objectStoreRequest',event);
logger('info', 'objectStoreRequest', event);
var feature = event.target.result;
if (!feature) {
feature = {
@@ -107,26 +239,27 @@ async function saveCoordinates(features, icao, lat, lon) {
}
};
}
feature.geometry.coordinates = [lon, lat];
logger('info',"ICAO : " + feature.properties.icao);
feature.geometry.coordinates = boundingBoxCenter;
logger('info', "ICAO : " + feature.properties.icao);
// Create another request that inserts the item back into the database
var updateAirportRequest = objectStore.put(feature);
// Log the transaction that originated this request
logger('info',"The transaction that originated this request is " + updateAirportRequest);
logger('info', "The transaction that originated this request is " + updateAirportRequest);
// When this new request succeeds, run the displayData() function again to update the display
updateAirportRequest.onsuccess = function (event) {
logger('info',"Stored Position : " + event.target.result);
logger('info', "Stored Position : " + event.target.result);
resolve("Stored Position : " + event.target.result);
};
updateAirportRequest.onerror = function (event) {
logger('info',"Error ", event);
logger('info', "Error ", event);
reject(event);
};
};
objectStoreRequest.onerror = function (event) {
logger('info',"Error ", event);
logger('info', "Error ", event);
reject(event);
};
});
@@ -134,63 +267,76 @@ async function saveCoordinates(features, icao, lat, lon) {
}
async function scanAPTIntoDB(f, features) {
var promise = new Promise(function (resolve, reject) {
var promise = new Promise(function (resolve, reject) {
try {
var i;
var count = 0;
var postMessage = this.postMessage;
var lines = [];
require('fs').createReadStream(f).pipe(zlib.createGunzip())
.on('data', function(chunk) {
for (i=0; i < chunk.length; ++i)
.on('data', function (chunk) {
for (i = 0; i < chunk.length; ++i)
if (chunk[i] == 10) count++;
})
.on('end', function() {
postMessage(['max', count]);
console.log('Line Count',count);
.on('end', function () {
postMessage(['max', count*2]);
console.log('Line Count', count);
lineReader.createInterface({
input: fs.createReadStream(f).pipe(zlib.createGunzip())
}).on('line', function (line) {
lines.push(line);
var fields = line.split(/[ ]+/);
if (fields[0] == '99') {
logger('info',"End Reading");
logger('info', "End Reading");
}
}).on('error', function (err) {
logger('info',err);
logger('info', err);
lr.close();
}).on('close', async function () {
logger('info',"End File Read");
asyncForEach(lines, async line => {
//await waitFor(5000);
try {
postMessage(['progress', 1]);
logger('info', "End File Read");
var currentAirport = [];
var currentIcao = null;
try {
lines.forEach((line, index) => {
var fields = line.split(/[ ]+/);
// var fields = line.match('([0-9]+)');
if (fields != null) {
var scanMethod = scanMethods[fields[0]];
if (scanMethod != null) {
var text = await scanMethod(fields, features);
logger('info', 'Scanned', text);
resolve(text);
} else {
resolve('Ignored ' + line);
}
if ([1, 16, 17, 99].indexOf(Number(fields[0])) >= 0) {
var apt = { icao: currentIcao, last: Number(fields[0])===99 }
var bla = aptForEach(currentAirport, apt, features, (line, index, apt) => {
//await waitFor(5000);
try {
var fields = line.split(/[ ]+/);
// var fields = line.match('([0-9]+)');
if (fields != null) {
var scanMethod = scanMethods[fields[0]];
if (scanMethod != null) {
var text = scanMethod(fields, features, apt);
logger('info', `Scanned ${fields[0]}`, text);
resolve(text);
} else {
resolve('Ignored ' + line);
}
}
return apt;
} catch (error) {
logger('error', error);
reject(error);
}
});
postMessage(['progress', currentAirport.length]);
currentIcao = fields[4]
currentAirport = [];
}
} catch (error) {
logger('error', error);
reject(error);
}
}).then(t => {
currentAirport.push(line);
});
logger('info', "Finished");
postMessage('DONE');
}).catch(reason => {
// postMessage('DONE');
} catch (error) {
logger('error', "Crashed");
logger('error', reason);
postMessage('DONE');
});
logger('error', error);
postMessage('DONE');
}
});
});
});
} catch (error) {
console.error(error);
reject(error);

View File

@@ -1,16 +1,30 @@
/* eslint-disable */
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080/src/renderer/utils/`
: `file://D:/GIT/flightgear-airports/src/renderer/utils/`
: `file://${process.resourcesPath}/workers/`
var path = require('path');
const fs = require('fs');
//debugger;
var turf;
importScripts('../../../node_modules/dijkstrajs/dijkstra.js');
var check_msg;
if (process.env.NODE_ENV === 'development') {
importScripts('../../../node_modules/dijkstrajs/dijkstra.js');
turf = require('./node_modules/@turf/turf');
} else if (process.env.NODE_ENV === 'mocha') {
importScripts('../../../node_modules/dijkstrajs/dijkstra.js');
turf = require('../../../node_modules/@turf/turf')
} else {
importScripts('dijkstra.js');
turf = require('@turf/turf')
}
const homedir = require('os').homedir();
importScripts('logger.js');
importScripts('haversine.js');
function errorReceiver(event) {
throw event.data;
@@ -18,7 +32,7 @@ function errorReceiver(event) {
onmessage = function (event) {
postMessage('checkStarted');
logger('info', 'Check Started');
logger('info', 'Check Started');
console.log(event.data);
if (event.data[0] === 'check') {
checkGroundnet(event.data[1]).then(result => {
@@ -27,79 +41,404 @@ onmessage = function (event) {
// event.origin.webContents.send('scanFinished');
}
).catch(result => {
console.log('Crashed');
console.log(result);
postMessage('DONE');
console.error('Crashed');
console.error(result);
postMessage(['DONE', [{ id: -1, message: ['Crashed', result] }]]);
});
}
};
/**
* Implements the checks of the groundnet
* @param {*} data
*/
async function checkGroundnet(data) {
var promise = new Promise(function (resolve, reject) {
try {
// debugger;
const fName = process.env.NODE_ENV === 'development'
? './src/renderer/utils/check_msg.json'
: path.join(`${process.resourcesPath}`, 'workers', 'check_msg.json');
check_msg = JSON.parse(fs.readFileSync(fName, 'utf8').toString());
//debugger;
var parkings = data.map(mapParkings).filter(n => n !== undefined);
var runwayNodes = data.map(mapRunwayNodes).filter(n => n !== undefined);
var runwayNodeIDs = data.map(mapRunwayNodeId).filter(n => n !== undefined);
var runwayNodes = data.map(mapRunwayNode).filter(n => n !== undefined);
var pushbackNodes = data.map(mapPushbackNodes).filter(n => n !== undefined);
var edges = data.map(mapEdges).filter(n => n !== undefined);
this.max = parkings.length * runwayNodes.length;
var normalNodes = data.map(mapEdges).filter(n => n !== undefined)
.flatMap(m => m.latLngs).filter(n => runwayNodeIDs.indexOf(Number(n.index)) < 0);
var takeoffPads = data.map(mapTakeoffPads).filter(n => n !== undefined);
this.max = 30;
this.postMessage(['max', this.max]);
var graph = {};
var boxes = {};
//debugger;
data.forEach(element => {
//debugger;
if (element.box !== undefined && element.box !== null) {
boxes[element.index] = element.box[0].map(latlng => [latlng.lat, latlng.lng]);
boxes[element.index].push(boxes[element.index][0]);
}
});
var directionalGraph = {};
var bidirectionalGraph = {};
console.debug(parkings);
parkings.forEach(element => {
graph[element] = {};
directionalGraph[element] = {};
bidirectionalGraph[element] = {};
});
runwayNodes.forEach(element => {
graph[element] = {};
runwayNodeIDs.forEach(element => {
directionalGraph[element] = {};
bidirectionalGraph[element] = {};
});
var notOkNodes = [];
//debugger;
console.debug(edges);
if (edges === undefined) {
resolve([{ id: -1, message: check_msg.NO_EDGES }]);
}
this.postMessage(['progress', 1]);
//debugger;
if (takeoffPads.length === 0) {
resolve([{ id: -1, message: check_msg.NO_RUNWAYS }]);
}
this.postMessage(['progress', 1]);
edges.forEach(edge => {
directionalGraph[edge.start] = {};
bidirectionalGraph[edge.start] = {};
directionalGraph[edge.end] = {};
bidirectionalGraph[edge.end] = {};
if (edge.latLngs !== undefined) {
// Check if there are segments > 2km
edge.latLngs.forEach((latLng, index, arr) => {
if (index > 0) {
var d = distance([arr[index - 1].lng, arr[index - 1].lat], [latLng.lng, latLng.lat]);
if (d > 2000) {
notOkNodes.push({ id: Number(arr[index - 1].index), message: check_msg.LONG_ROUTE_START });
notOkNodes.push({ id: Number(arr[index].index), message: check_msg.LONG_ROUTE_END });
}
//console.log(d);
}
});
}
});
this.postMessage(['progress', 1]);
this.postMessage(['progress', 1]);
// Add edges to graphs
edges.forEach(element => {
graph[element.start] = {};
graph[element.end] = {};
});
edges.forEach(element => {
var node1 = graph[element.start];
node1[Number(element.end)] = 1;
var node2 = graph[element.end];
node2[Number(element.start)] = 1;
var node1 = directionalGraph[element.start];
var node2 = directionalGraph[element.end];
if (element.direction === undefined) {
notOkNodes.push({ id: Number(element._leaflet_id), message: check_msg.EDGE_MISSING_DIRECTION });
}
if (element.direction === 'bi-directional' || element.direction === 'forward') {
node1[Number(element.end)] = 1;
}
if (element.direction === 'bi-directional' || element.direction === 'backward') {
node2[Number(element.start)] = 1;
}
var node3 = bidirectionalGraph[element.start];
var node4 = bidirectionalGraph[element.end];
node3[Number(element.end)] = 1;
node4[Number(element.start)] = 1;
});
this.postMessage(['progress', 1]);
var isLegitEnd = function (v) {
if (bidirectionalGraph[v] === undefined) {
debugger;
}
if (Object.keys(bidirectionalGraph[v]).length <= 1) {
return true;
}
return Object.keys(bidirectionalGraph[v]).filter(v => runwayNodeIDs[v]).length === 0;
}
runwayNodeIDs = runwayNodeIDs.filter(
(v, i) => isLegitEnd(v)
);
// Check if there is a route from every parking to every runway node
var okNodes = [];
logger('info', graph);
logger('info', directionalGraph);
parkings.forEach(parkingNode => {
runwayNodes.forEach(runwayNode => {
var ok = checkRoute(graph, parkingNode, runwayNode);
if(ok) {
okNodes.push(parkingNode);
okNodes.push(runwayNode);
}
this.postMessage(['progress', 1]);
runwayNodeIDs.forEach(runwayNode => {
var ok = checkRoute(directionalGraph, parkingNode, runwayNode);
if (ok) {
okNodes.push(parkingNode);
okNodes.push(runwayNode);
} else {
console.log(`No route from Parking ${parkingNode} to Runwaynode ${runwayNode}`);
}
});
});
okNodes = okNodes.filter((v,i) => okNodes.indexOf(v) === i);
var allLegitimateEndNodes = parkings.concat(runwayNodes);
var notOkNodes = allLegitimateEndNodes.filter(
(v,i) => okNodes.indexOf(v) < 0
).map(
id => {return {id:id, message:'Node not connected'}}
);
// Build pushback directionalGraph
var noPushbackGraph = {};
parkings.forEach(element => {
noPushbackGraph[element] = {};
});
pushbackNodes.forEach(element => {
noPushbackGraph[element] = {};
});
edges.filter(element => element.isPushBackRoute).forEach(element => {
noPushbackGraph[element.start] = {};
noPushbackGraph[element.end] = {};
});
// add all pushback edges
edges.filter(element => element.isPushBackRoute).forEach(element => {
var node1 = noPushbackGraph[Number(element.start)];
node1[Number(element.end)] = 1;
var node2 = noPushbackGraph[Number(element.end)];
node2[Number(element.start)] = 1;
});
var okPushbacks = [];
// Check pushback
var multiplePushbackRoutes = {};
parkings.forEach(parkingNode => {
pushbackNodes.forEach(pushbackNode => {
var numRoutes = checkRoute(noPushbackGraph, parkingNode, pushbackNode);
if (numRoutes === 0) {
if (multiplePushbackRoutes[parkingNode] === undefined &&
Object.keys(noPushbackGraph[parkingNode]) > 0) {
// Only when there is a edge leaving
multiplePushbackRoutes[parkingNode] = [];
}
} else if (numRoutes === 1) {
if (multiplePushbackRoutes[parkingNode] === undefined) {
multiplePushbackRoutes[parkingNode] = [pushbackNode];
} else {
multiplePushbackRoutes[parkingNode].push(pushbackNode);
}
} else if (numRoutes > 1) {
if (multiplePushbackRoutes[parkingNode] === undefined) {
multiplePushbackRoutes[parkingNode] = [pushbackNode];
} else {
multiplePushbackRoutes[parkingNode].push(pushbackNode);
}
}
});
});
var notConnectedToPushback = pushbackNodes.map(
id => {
var normalRoutes = bidirectionalGraph[id];
var pushbackRoutes = noPushbackGraph[id];
if (Object.keys(pushbackRoutes).length < 1)
return { id: id, message: check_msg.PUSHBACK_NOT_CONNECTED }
}).filter(n => n !== undefined);
this.postMessage(['progress', 1]);
var multipleTaxiRoutes = pushbackNodes.map(
id => {
var normalRoutes = bidirectionalGraph[id];
var pushbackRoutes = noPushbackGraph[id];
if (normalRoutes !== undefined) {
var nonPushbackRoutes = Object.keys(normalRoutes).filter(r => pushbackRoutes[r] === undefined);
if (nonPushbackRoutes.length > 1)
return { id: id, message: check_msg.TO_MANY_PUSHBACK_TAXI_ROUTES }
}
}).filter(n => n !== undefined);
this.postMessage(['progress', 1]);
var pushbackExitNotBidirectional = pushbackNodes.map(
id => {
var normalRoutes = bidirectionalGraph[id];
var pushbackRoutes = noPushbackGraph[id];
if (normalRoutes !== undefined) {
var nonPushbackRoutes = Object.keys(normalRoutes).filter(r => pushbackRoutes[r] === undefined);
if(nonPushbackRoutes.length > 0) {
var returnRoute = Object.keys(bidirectionalGraph[nonPushbackRoutes[0]]).map(id => Number(id)).filter(retId =>id === retId);
if (returnRoute.length === 0)
return { id: id, message: check_msg.PUSHBACK_EXIT_NOT_BIDRECTIONAL }
}
}
}).filter(n => n !== undefined);
this.postMessage(['progress', 1]);
var rogueHoldPoints = pushbackNodes.map(
id => {
var routes = noPushbackGraph[id];
if (Object.keys(routes).length < 1)
return { id: id, message: check_msg.UNCONNECTED_PUSHBACK }
/*
else if(Object.keys(routes).length>1)
return { id: id, message: 'Multiple connected pushback node' }
*/
}
).filter(n => n !== undefined);
this.postMessage(['progress', 1]);
var wrongPushbackRoutes = parkings.filter(
function (e) {
return this[e] != undefined && this[e].length != 1;
}
, multiplePushbackRoutes).map(
id => {
var endPoints = multiplePushbackRoutes[id];
if (endPoints.length < 1)
return { id: id, message: check_msg.NO_WAY_TO_HOLDPOINT }
else
return { id: id, message: check_msg.MULTIPLE_PUSHBACK }
}
);
this.postMessage(['progress', 1]);
okNodes = okNodes.filter((v, i) => okNodes.indexOf(v) === i);
var notOkNodesParkings = parkings.filter(
(v, i) => okNodes.indexOf(v) < 0
).map(
id => { return { id: id, message: check_msg.NO_RUNWAY_ROUTE } }
);
this.postMessage(['progress', 1]);
var notOkNodesRunways = runwayNodeIDs.filter(
(v, i) => okNodes.indexOf(v) < 0
).map(
id => { return { id: id, message: check_msg.NO_PARKING_ROUTE } }
);
this.postMessage(['progress', 1]);
if (parkings.length === 0) {
notOkNodes.push({id:0, message:'No parkings'});
notOkNodes.push({ id: -2, message: check_msg.NO_PARKINGS });
}
if (runwayNodes.length === 0) {
notOkNodes.push({id:0, message:'No Runwaynodes'});
this.postMessage(['progress', 1]);
if (runwayNodeIDs.length === 0) {
notOkNodes.push({ id: -2, message: check_msg.NO_RUNWAY_NODES });
}
this.postMessage(['progress', 1]);
var allEnds = Object.entries(bidirectionalGraph).filter(
(v, i) => Object.keys(v[1]).length <= 1
);
// Ends that are not on Runway and not a Parking or Pushback
var allLegitimateEndNodes = parkings.concat(runwayNodeIDs).concat(pushbackNodes);
var danglingEnds = allEnds.filter(
(v, i) => allLegitimateEndNodes.indexOf(Number(v[0])) < 0
).map(
v => { return { id: Number(v[0]), message: check_msg.NOT_LEGIT_END } }
);
this.postMessage(['progress', 1]);
var parkingNodes = data.map(mapParkingNode).filter(n => n !== undefined);
var overlappingParkings = [];
parkingNodes.forEach(parkingNode => {
if (boxes[parkingNode.index] === undefined) {
overlappingParkings.push({ id: parkingNode.index, message: check_msg.UNKNOWN_RADIUS });
}
});
// Check for intersecting radii
parkingNodes.forEach(parkingNode => {
parkingNodes.forEach(parkingNode1 => {
console.log(parkingNode, parkingNode1);
if (parkingNode.index !== parkingNode1.index) {
var d = distance([parkingNode.lng, parkingNode.lat],
[parkingNode1.lng, parkingNode1.lat]);
if (d < parkingNode.radius + parkingNode1.radius + 10) {
// If bigger circles intersect we should check the boxes
if (boxes[parkingNode.index] !== null && boxes[parkingNode1.index] !== null &&
boxes[parkingNode.index] !== undefined && boxes[parkingNode1.index] !== undefined) {
var poly1 = turf.polygon([boxes[parkingNode.index]]);
var poly2 = turf.polygon([boxes[parkingNode1.index]]);
var intersection = turf.intersect(poly1, poly2);
if (intersection !== null) {
overlappingParkings.push({ id: parkingNode.index, message: check_msg.OVERLAPPING_PARKINGS });
}
}
}
}
});
});
this.postMessage(['progress', 1]);
var invalidParkings = [];
// Check for name
parkingNodes.forEach(parkingNode => {
if (!parkingNode.name || /^\s*$/.test(parkingNode.name)) {
invalidParkings.push({ id: parkingNode.index, message: check_msg.NAME_EMPTY });
}
if (!parkingNode.type) {
invalidParkings.push({ id: parkingNode.index, message: check_msg.TYPE_EMPTY });
}
if (['ga', 'cargo', 'gate', 'mil-fighter', 'mil-cargo'].indexOf(parkingNode.parkingType) < 0) {
invalidParkings.push({ id: parkingNode.index, message: check_msg.PARKING_TYPE_INVALID });
}
});
this.postMessage(['progress', 1]);
this.postMessage(['progress', 1]);
//Check for dual pushback/runway nodes
runwayNodeIDs.forEach(runwayNode => {
if (pushbackNodes.indexOf(runwayNode) >= 0) {
notOkNodes.push({ id: runwayNode, message: check_msg.DUAL_PUSHBACK });
}
});
this.postMessage(['progress', 1]);
//Check if runwaynodes are on runway
runwayNodes.forEach(runwayNode => {
// debugger;
if (takeoffPads.filter(r => turf.booleanContains(r, latToTurf(runwayNode))).length === 0) {
notOkNodes.push({ id: runwayNode.index, message: check_msg.RUNWAY_NODE_NOT_ON_RUNWAY });
}
});
this.postMessage(['progress', 1]);
//Check if nodes no normal nodes are on runway
normalNodes.forEach(normalNode => {
//debugger;
if (takeoffPads.filter(r => turf.booleanContains(r, latToTurf(normalNode))).length > 0) {
notOkNodes.push({ id: normalNode.index, message: check_msg.NON_RUNWAYNODE_ON_RUNWAY });
}
});
this.postMessage(['progress', 1]);
var doubleEdges = edges.filter((v, i, a) => a.findIndex(t => (t.start === v.start && t.end === v.end) ) !== i);
doubleEdges.forEach(e => {
notOkNodes.push({ id: e.id, message: check_msg.DOUBLE_EDGE });
});
// debugger;
notOkNodes = notOkNodes.concat(invalidParkings);
if (invalidParkings.length === 0) {
notOkNodes.push({ id: -1, message: check_msg.PARKINGS_VALID });
}
notOkNodes = notOkNodes.concat(overlappingParkings);
if (overlappingParkings.length === 0) {
notOkNodes.push({ id: -1, message: check_msg.NO_OVERLAPPING_PARKINGS });
}
var danglingEnds = Object.entries(graph).filter(
(v,i) => Object.keys(v[1]).length <= 1
).filter(
(v,i) => allLegitimateEndNodes.indexOf(Number(v[0])) < 0
).map(
v => {return {id:Number(v[0]), message:'Node not a legimate end'}}
);
notOkNodes = notOkNodes.concat(danglingEnds);
// check1(graph);
// check2();
// this.postMessage(['progress', 1]);
if (danglingEnds.length === 0) {
notOkNodes.push({ id: -1, message: check_msg.NO_INVALID_ENDS });
}
notOkNodes = notOkNodes.concat(notOkNodesParkings).concat(rogueHoldPoints);
if (notOkNodesParkings.length === 0 && rogueHoldPoints === 0) {
notOkNodes.push({ id: -1, message: check_msg.ROUTES_FROM_PARKINGS_OK });
}
notOkNodes = notOkNodes.concat(notOkNodesRunways);
if (notOkNodesRunways.length === 0) {
notOkNodes.push({ id: -1, message: check_msg.ROUTES_FROM_RUNWAYS_OK });
}
notOkNodes = notOkNodes.concat(wrongPushbackRoutes);
notOkNodes = notOkNodes.concat(notConnectedToPushback);
notOkNodes = notOkNodes.concat(multipleTaxiRoutes);
notOkNodes = notOkNodes.concat(pushbackExitNotBidirectional);
if (wrongPushbackRoutes.length === 0 &&
notConnectedToPushback.length === 0 &&
multipleTaxiRoutes.length === 0 &&
pushbackExitNotBidirectional.length === 0
) {
notOkNodes.push({ id: -1, message: check_msg.PUSHBACK_ROUTES_OK });
}
resolve(notOkNodes);
} catch (error) {
reject(error);
}
@@ -107,53 +446,102 @@ async function checkGroundnet(data) {
return promise;
}
function checkRoute(graph, from, to) {
function checkRoute(directionalGraph, from, to) {
try {
var pathD = this.dijkstra.find_path(graph, from, to);
if (pathD.length>0) {
console.log(pathD);
var pathD = this.dijkstra.find_path(directionalGraph, from, to);
if (pathD.length > 0) {
console.log(pathD);
return pathD.length;
}
return true;
return 0;
} catch (error) {
console.log('No route from ' + from + ' to ' + to);
return false;
// console.error(error);
return 0;
}
}
function check1(graph) {
var graph1 = {
a: {b: 1, d: 1},
b: {a: 1, c: 1, e: 1},
c: {b: 1, f: 1},
d: {a: 1, e: 1, g: 1},
e: {b: 1, d: 1, f: 1, h: 1},
f: {c: 1, e: 1, i: 1},
g: {d: 1, h: 1},
h: {e: 1, g: 1, i: 1},
i: {f: 1, h: 1}
function check1(directionalGraph) {
var directionalGraph1 = {
a: { b: 1, d: 1 },
b: { a: 1, c: 1, e: 1 },
c: { b: 1, f: 1 },
d: { a: 1, e: 1, g: 1 },
e: { b: 1, d: 1, f: 1, h: 1 },
f: { c: 1, e: 1, i: 1 },
g: { d: 1, h: 1 },
h: { e: 1, g: 1, i: 1 },
i: { f: 1, h: 1 }
};
var path = this.dijkstra.find_path(graph, 'a', 'i');
var path = this.dijkstra.find_path(directionalGraph, 'a', 'i');
console.log(path);
}
function check2(params) {
}
var mapPushbackNodes = function (o) {
if (o.type === 'PushBack') {
return o.index;
}
}
var mapParkings = function (o) {
if(o.type === 'parking')
return o.index;
console.log(o);
if (o.type === 'parking')
return o.index;
}
var mapRunwayNodes = function (o) {
if(o.type === 'runway')
return o.index;
console.log(o);
var mapParkingNode = function (o) {
// debugger;
if (o.type === 'parking')
return { index: o.index, lat: o.lat, lng: o.lng, name: o.name, radius: Number(o.radius), type: o.type, parkingType: o.parkingType };
console.debug(o);
}
var mapBoxes = function (o) {
// debugger;
if (o.type === 'parking')
return { index: o.index };
}
var mapRunwayNodeId = function (o) {
if (o.type === 'runway')
return o.index;
console.debug(o);
}
var mapRunwayNode = function (o) {
if (o.type === 'runway') {
return { index: o.index, lat: o.lat, lng: o.lng };
}
}
var mapTakeoffPads = function (o) {
if (o.type === 'takeoffpad_poly') {
var pts = o.pavement[0].map(latLngToArray);
pts.push(pts[0]);
return turf.polygon([pts]);
}
}
var mapEdges = function (o) {
if(o.type === 'poly')
return {start: o.start, end: o.end};
console.log(o);
if (o.type === 'poly')
return {
id: o._leaflet_id, start: o.start, end: o.end, isPushBackRoute: o.isPushBackRoute !== undefined &&
o.isPushBackRoute !== 0, direction: o.direction, latLngs: o.latLngs
};
}
var latToTurf = function (turfPoint) {
return turf.point([turfPoint.lng, turfPoint.lat]);
};
var latLngToArray = function (turfPoint) {
//debugger;
return [turfPoint.lng, turfPoint.lat];
};
var turfToLatLng = function (turfPoint) {
return '' + turfPoint.geometry.coordinates[1].toFixed(6) + ',' + turfPoint.geometry.coordinates[0].toFixed(6);
};

View File

@@ -0,0 +1,33 @@
{
"LONG_ROUTE_START" : ["Start of long route", "Route segments of >2km are Taxidraw artefacts"],
"LONG_ROUTE_END": ["End of long route", "Route segments of >2km are Taxidraw artefacts"],
"EDGE_MISSING_DIRECTION": ["Edge missing direction", "Each edge must have a direction (forward, backward, bi-directional)"],
"NO_RUNWAY_ROUTE": ["No way from parking to each runway", "There must be a route from each parking to each runway."],
"NO_PARKING_ROUTE": ["No way from runway to each parking", "There must be a route from each runway to each parking."],
"NO_PARKINGS": ["No parkings", ""],
"NO_RUNWAY_NODES": ["No Runwaynodes", "Fine for parking only"],
"NOT_LEGIT_END": ["Node not a legimate end", "Taxiroutes must end either at a parking or on a runway"],
"UNKNOWN_RADIUS" :["Unknown radius", "Radii must be one from the list"],
"OVERLAPPING_PARKINGS" :["Overlapping parkings", "Parkings must not overlap"],
"NAME_EMPTY" :["Name empty", "Name of parking must not be empty"],
"TYPE_EMPTY" :["Parking type empty", "The parking type must not be empty"],
"PARKING_TYPE_INVALID" :["Parking type not valid", "The type of parking must be one of ()"],
"DUAL_PUSHBACK": ["Dual runway/ pushback node", "A runway node can not be a hold node at the same time"],
"RUNWAY_NODE_NOT_ON_RUNWAY" : ["Runwaynode not in takeoff pad", ""],
"NON_RUNWAYNODE_ON_RUNWAY": ["Non Runwaynode in takeoff pad", ""],
"PARKINGS_VALID": ["Parkings valid", "All Ok"],
"NO_OVERLAPPING_PARKINGS": ["No parkings overlapping", "Parking positions may not overlap. Reduce the radius or move."],
"NO_INVALID_ENDS": ["No invalid ends", ""],
"ROUTES_FROM_PARKINGS_OK": ["Routes from parkings OK", ""],
"ROUTES_FROM_RUNWAYS_OK": ["Routes from runways OK", ""],
"PUSHBACK_ROUTES_OK": ["Pushback routes OK", ""],
"UNCONNECTED_PUSHBACK": ["Unconnected Pushbacknode", ""],
"NO_WAY_TO_HOLDPOINT":["No way to pushback holdpoint", "There is no route from the parking to the pushback hold point-"],
"MULTIPLE_PUSHBACK": ["Multiple connected pushback points", "There are more than one possible pushback holdpoint routes."],
"PUSHBACK_NOT_CONNECTED": ["Pushback Holding Point not Connected to Pushback Route", ""],
"TO_MANY_PUSHBACK_TAXI_ROUTES": ["Too many Taxi routes from Pushback Holding Point", "There must be only one pushback point reachable from the parking."],
"PUSHBACK_EXIT_NOT_BIDRECTIONAL": ["Pushback Holding Point Exit route is not bidirectional", ""],
"NO_EDGES": ["No Edges", "No checks are run if there are no edges present"],
"NO_RUNWAYS": ["No Runways", "No checks are run if there are no runways present. APT layer visible?"],
"DOUBLE_EDGE": ["No Double Edges", "This edge is doubled"]
}

View File

@@ -0,0 +1,21 @@
var earthRadius = 6371008.8;
function distance(point1, point2) {
var dLat = degrees_to_radians((point2[1] - point1[1]));
var dLon = degrees_to_radians((point2[0] - point1[0]));
var lat1 = degrees_to_radians(point1[1]);
var lat2 = degrees_to_radians(point2[1]);
var a = Math.pow(Math.sin(dLat / 2), 2) +
Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2);
return earthRadius * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
function degrees_to_radians(degrees)
{
var pi = Math.PI;
return degrees * (pi/180);
}

View File

@@ -1,21 +1,40 @@
var util = require("util")
/* eslint-disable no-unused-vars */
var util = require('util');
const d = new Date();
const fName = 'scan_' + d.getFullYear()
+ d.getMonth()
+ d.getDay()
+ d.getHours()
+ d.getMinutes()
+ d.getSeconds()
+ d.getMilliseconds() + '.log';
const fName = 'scan_' + d.getFullYear() +
d.getMonth() +
d.getDay() +
d.getHours() +
d.getMinutes() +
d.getSeconds() +
d.getMilliseconds() + '.log';
var logStream = require('fs').createWriteStream( fName, {autoClose: true});
var logStream = null;
var loggerInit = function (logging) {
if (logging) {
try {
const homedir = require('os').homedir();
const logFileName = require('path').join(homedir, fName);
logStream = require('fs').createWriteStream( logFileName, {autoClose: true});
} catch (error) {
console.error('Logging not possible ' + error);
}
}
}
var logger = function (level, msg, o) {
var d = new Date();
logStream.write(d.toUTCString() + '|' + level + ' | ' + msg + '\r\n');
if (o != undefined) {
var d = new Date();
try {
if (logStream !== null) {
logStream.write(d.toUTCString() + '|' + level + ' | ' + msg + '\r\n');
}
if (o != undefined && logStream!==null) {
logStream.write( util.inspect(o,{depth: 2}) + '\r\n');
}
} catch (error) {
console.error('Logging not possible ' + error);
}
}

View File

@@ -1,18 +1,23 @@
/* eslint-disable */
// const fs = require('fs');
// const path = require('path');
// const math = require('mathjs');
// const util = require('util');
// const airports = require('./airports.js');
// const homedir = require('os').homedir();
// const apt = require('apt.js');
/*
Copyright 2020 Keith Paterson
This file is part of FG Airports.
FG Airports is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FG Airports is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with FG Airports. If not, see http://www.gnu.org/licenses/.
*/
/* eslint-disable */
/**
* Iterates over an array with a async function and await
* @param {*} array The array being iterated over
* @param {*} callback
*/
const { Debugger } = require("electron");
async function asyncForEach(array, callback) {
logger('info', "AsyncForEach Len " + array.length);
for (let index = 0; index < array.length; index++) {
@@ -43,7 +48,7 @@ async function scanGroundnetFiles(p, features) {
try {
logger('info', 'Start Groundnets ' + p);
var files = traverseDir(p);
this.postMessage(['max', files.length]);
this.postMessage(['max', files.length*2]);
logger('info', files);
asyncForEach(files, async f => {
@@ -181,6 +186,9 @@ function scanTrafficIntoDB(p, features) {
function traverseDir(dir) {
var result = [];
if(!fs.existsSync(dir)) {
return result;
}
fs.readdirSync(dir).forEach(file => {
let fullPath = path.join(dir, file);
if (fs.lstatSync(fullPath).isDirectory()) {
@@ -236,26 +244,57 @@ function readAI(f, apts) {
resolve();
return;
}
const aircraftLookup = {};
dat.trafficlist.aircraft.map(n => {
try {
if(aircraftLookup[n['required-aircraft']] === undefined) {
aircraftLookup[n['required-aircraft']] = [];
}
aircraftLookup[n['required-aircraft']].push(n.airline);
aircraftLookup[n['required-aircraft']] = aircraftLookup[n['required-aircraft']].filter((v, i, a) => a.indexOf(v) === i);
} catch (error) {
reject(error);
}
//debugger;
});
logger('info', 'Traffic', dat.trafficlist.flight);
logger('info', "Departure flights " + dat.trafficlist.flight.length);
var merged = new Array();
// Flat list. Each flight departing or landing counts as one.
var merged = [];
var airports = {};
dat.trafficlist.flight.map(n => {
merged.push(n.departure.port);
merged.push(n.departure.port);
merged.push(n.arrival.port);
if(airports[n.departure.port] === undefined) {
airports[n.departure.port] = [];
}
if(airports[n.arrival.port] === undefined) {
airports[n.arrival.port] = [];
}
airports[n.departure.port] = airports[n.departure.port].concat(aircraftLookup[n['required-aircraft']]);
airports[n.departure.port] = airports[n.departure.port].filter((v, i, a) => a.indexOf(v) === i)
airports[n.arrival.port] = airports[n.arrival.port].concat(aircraftLookup[n['required-aircraft']]);
airports[n.arrival.port] = airports[n.arrival.port].filter((v, i, a) => a.indexOf(v) === i)
}).sort();
//debugger;
var counts = {};
for (var i = 0; i < merged.length; i++) {
counts[merged[i]] = 1 + (counts[merged[i]] || 0);
}
asyncForEach(Object.keys(counts), async key => {
logger('info', key);
await store(key, airline[1], counts[key]);
asyncForEach(Object.keys(counts), async icao => {
logger('info', icao);
await store(icao, airports[icao], counts[icao]);
}).then(t => {
logger('info', "Finished");
resolve();
@@ -287,7 +326,7 @@ function readAI(f, apts) {
* @param {*} value
*/
function store(icao, airline, value) {
function store(icao, airlines, value) {
var promise = new Promise(function (resolve, reject) {
logger('info', "Airport " + icao + " has " + value + " new flights");
// Make a request to get a record by key from the object store
@@ -297,18 +336,18 @@ function store(icao, airline, value) {
var objectStoreRequest = index.get(icao);
objectStoreRequest.onsuccess = function (event) {
logger('info', 'Stored ', event);
logger('info', 'Store Request', event);
var feature = objectStoreRequest.result;
if (!feature) {
feature = createFeature(icao);
}
feature.properties.flights += value;
logger('info', "Airline : ", airline);
if (!feature.properties.airlines.includes(airline)) {
feature.properties.airlines.push(airline);
feature.properties.airlines.sort();
}
logger('info', "ICAO : " + feature.properties.icao + " Flights : " + feature.properties.flights);
logger('info', "Airlines : ", JSON.stringify(airlines));
//debugger;
feature.properties.airlines = feature.properties.airlines.concat(airlines);
feature.properties.airlines = feature.properties.airlines.filter((v, i, a) => a.indexOf(v) === i)
feature.properties.airlines.sort()
// Create another request that inserts the item back into the database
var updateAirportRequest = objectStore.put(feature);
@@ -317,16 +356,16 @@ function store(icao, airline, value) {
// When this new request succeeds, run the displayData() function again to update the display
updateAirportRequest.onsuccess = function (event) {
logger('info', "Stored", event);
logger('info', "Updated Success", event);
resolve();
};
updateAirportRequest.onerror = function (event) {
logger('info', "Error storing ", event);
logger('info', "Error updating ", event);
reject(event);
};
};
objectStoreRequest.onerror = function (event) {
logger('info', "Error " + event);
logger('info', "Error reading" + event);
reject(event);
};
});
@@ -342,7 +381,8 @@ function store(icao, airline, value) {
async function readGroundnet(f, features) {
var promise = new Promise(function (resolve, reject) {
try {
var filename = path.basename(f).match('^([^.]+)\\.([^.]+)\\.([^.]+)');
var thisPostMessage = this.postMessage;
var filename = path.basename(f).match('^([^.]+)\\.([^.]+)(\\.new)?\\.([^.]+)');
if (filename == null) {
resolve("File didn't match");
}
@@ -430,12 +470,18 @@ async function readGroundnet(f, features) {
logger('info', 'groundnet : ' + filename[1]);
if (dat['?xml'].groundnet) {
var nodes = dat['?xml'].groundnet.TaxiNodes;
var parkingnodes = dat['?xml'].groundnet.parkingList;
if (nodes && nodes.node) {
logger('info', nodes);
}
feature['properties']['groundnet'] = nodes && nodes.node ? nodes.node.length : 0;
var nodes = dat['?xml'].groundnet.parkingList;
feature['properties']['parking'] = nodes && nodes.Parking ? nodes.Parking.length : 0;
if(filename [3] === '.new') {
feature['properties']['wipgroundnet'] = nodes && nodes.node ? nodes.node.length : 0;
feature['properties']['wipparking'] = parkingnodes && parkingnodes.Parking ? parkingnodes.Parking.length : 0;
} else {
feature['properties']['groundnet'] = nodes && nodes.node ? nodes.node.length : 0;
feature['properties']['parking'] = parkingnodes && parkingnodes.Parking ? parkingnodes.Parking.length : 0;
}
}
} else if (filename[2] == 'ils') {
logger('info', 'ils : ' + filename[1]);
@@ -448,6 +494,7 @@ async function readGroundnet(f, features) {
// report on the success of the transaction completing, when everything is done
transaction.oncomplete = function (event) {
logger('info', 'Write Transaction complete ' + event);
thisPostMessage(['progress', 1]);
resolve("Stored " + filename[1]);
};
@@ -470,7 +517,7 @@ async function readGroundnet(f, features) {
};
}
objectStoreRequest.onerror = function (event) {
logger('info', "Read Errpr : " + event);
logger('info', "Read Error : " + event);
resolve(event);
}
}

Some files were not shown because too many files have changed in this diff Show More