170 Commits

Author SHA1 Message Date
d42aad87bd Merge branch 'develop' of https://git.akkerweb.nl/FarmMaps/FarmMapsLib into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-10 12:02:34 +02:00
415ee8ef30 something weird, codeList item in url seems empty. But item is not empty 2020-10-10 12:00:05 +02:00
Willem Dantuma
306065dcbd Fix logout issue
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-09 16:35:14 +02:00
824ba97704 try to fix deleteCodeListItem
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-09 07:47:47 +02:00
ac8ab147bc added codelist service
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-08 14:25:33 +02:00
Willem Dantuma
640c99cde2 Add test component
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-06 09:07:40 +02:00
Willem Dantuma
da2492e017 Add fm-gradient-select
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-06 08:22:10 +02:00
Willem Dantuma
f93d235877 add fm-gradient component
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-05 14:58:47 +02:00
Willem Dantuma
fe798e3f60 Fix AW-1508
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-05 11:08:36 +02:00
Willem Dantuma
b8dcc4e9e0 Add 3D example to test app
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-02 17:48:34 +02:00
Willem Dantuma
4b6175b6c4 Add npm scope
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-02 17:37:36 +02:00
Willem Dantuma
388a2faf84 Fix build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-02 17:33:25 +02:00
Willem Dantuma
0b0bb4d598 Fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-10-02 17:26:18 +02:00
Willem Dantuma
7696ddd8d2 Add common-map3d to jenkins build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-10-02 17:23:11 +02:00
Willem Dantuma
1301a9d082 Merge branch 'feature/Resurect_3D' into develop 2020-10-02 17:21:01 +02:00
Willem Dantuma
e87affee08 Refactor 3d to own library 2020-10-02 17:20:00 +02:00
Willem Dantuma
b00b21e6b8 Merge branch 'develop' into feature/Resurect_3D 2020-10-02 16:44:11 +02:00
Willem Dantuma
835851350c Fix i18n tags
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-10-02 07:46:59 +02:00
Willem Dantuma
7bdb48b644 Fix signalr authentication
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-30 18:30:36 +02:00
Willem Dantuma
84d6f44e9b Fix transition
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-29 20:57:14 +02:00
Willem Dantuma
2fd109d967 Fix AW-1414
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-29 15:27:21 +02:00
Willem Dantuma
f9934ccf27 Fix AW-1355
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-29 14:56:23 +02:00
Willem Dantuma
b93218e165 Merge branch 'develop' into feature/Resurect_3D
# Conflicts:
#	angular.json
#	package-lock.json
#	package.json
#	projects/common-map/package.json
2020-09-26 12:17:58 +02:00
Willem Dantuma
8e55308df9 First working angular 10 version
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-26 10:10:38 +02:00
Willem Dantuma
5270739641 Fix versions
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-26 10:03:15 +02:00
Willem Dantuma
ce8e8fa706 Update common
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-25 21:24:54 +02:00
Willem Dantuma
d326a82c4e Fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-25 21:18:04 +02:00
Willem Dantuma
c1683a058c Fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-25 21:16:38 +02:00
Willem Dantuma
aa168bb3a1 Fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-25 19:55:08 +02:00
Willem Dantuma
fe53955bea Try to fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-25 19:47:39 +02:00
Willem Dantuma
ba3dad1365 Fix dep
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-25 19:38:47 +02:00
Willem Dantuma
6d2d259c01 Merge branch 'feature/angular_upgrade_10' into develop
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-25 19:28:18 +02:00
Willem Dantuma
92774647ff Upgrade to angular 10 2020-09-25 19:27:53 +02:00
Willem Dantuma
9ca047923f npm auit fix 2020-09-25 17:10:17 +02:00
Willem Dantuma
c893a2820b Fix responsetype
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-22 19:29:44 +02:00
Willem Dantuma
baa0166fc8 Stupid bug :-(
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-22 15:13:00 +02:00
Willem Dantuma
58c200aca0 Another attempt to fix parameter
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-22 15:03:23 +02:00
Willem Dantuma
b8a4b3541d Fix params
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-22 14:46:51 +02:00
Willem Dantuma
584a1514ac Add getItemData
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-22 13:58:33 +02:00
Willem Dantuma
21af6ab2c4 Some styling fixes 2020-09-18 17:19:29 +02:00
Willem Dantuma
c606e02673 Fix static image layer 2020-09-18 16:05:44 +02:00
Willem Dantuma
ff4eb296ff Update libs
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-18 14:50:17 +02:00
Willem Dantuma
ea26cdd628 Update libs
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-18 14:30:20 +02:00
Willem Dantuma
26e28bdda8 Revert changes 2020-09-18 14:29:50 +02:00
Willem Dantuma
15d1f14ee1 More or less working now 2020-09-18 14:20:33 +02:00
Willem Dantuma
22e007b70b First attempt to bring back 3D view in map 2020-09-18 08:38:05 +02:00
Willem Dantuma
9e4a538a07 Implement min/maxzoom on vectorlayer
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-16 20:09:52 +02:00
Willem Dantuma
c44c771feb Use indexKey if specified
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-16 15:02:42 +02:00
Willem Dantuma
394bbab320 Add support for alpha in legend
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-16 14:40:03 +02:00
Willem Dantuma
3008081ae8 Implement stylecache for vectorlayer
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-16 13:38:06 +02:00
Willem Dantuma
7474c00498 Fix layerindex
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-16 10:34:04 +02:00
Willem Dantuma
964bf925e7 Fix typo
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-15 15:01:20 +02:00
Willem Dantuma
fb8129148d Add to module
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-15 14:01:24 +02:00
Willem Dantuma
73db03148a First attempt package preload strategy 2020-09-15 14:00:18 +02:00
Willem Dantuma
6367d3399d Implement default baselayer option
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-09 09:54:50 +02:00
Willem Dantuma
ee4e7113d2 Add TileJSON interface 2020-09-09 09:47:32 +02:00
Willem Dantuma
ad86e41345 Add background-color padding and border
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-04 10:40:34 +02:00
Willem Dantuma
e682b375e7 Add user-menu router outlet
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-04 10:10:01 +02:00
Willem Dantuma
78425a621a Fix fm-hasclaim
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-03 12:43:55 +02:00
1030b1c41a removed logging in weather service.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
changed moment to peerdependency.
2020-09-03 10:37:24 +02:00
76bdad6eaf fixed build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-03 10:25:39 +02:00
ce56dd9e9f updated weather service with correct date manipulation.
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-03 10:19:12 +02:00
e7a53ba7b5 HourlyWeatherData time changed to string.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-02 21:32:47 +02:00
9f1f6934ee made hourly weather data static typed.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-02 16:21:49 +02:00
773bf3f5e7 fix build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-09-02 15:28:12 +02:00
cdc900d5fd fixed weather service
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-09-02 15:23:54 +02:00
80522a3a2f Revert "AW-1435 Add links to package manager and widget manager"
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
This reverts commit 0157812357.
2020-08-17 15:53:02 +02:00
0157812357 AW-1435 Add links to package manager and widget manager
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-17 14:19:38 +02:00
329564d8f6 AW-1431 Oops
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-13 15:47:04 +02:00
ba2a067c8b AW-1431 Expose new property
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-13 15:28:30 +02:00
4d67a656f5 AW-1431 Add InitUserSettingsRoot
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-13 13:06:28 +02:00
ad1d40cdd1 AW-1426 Add extra check.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-11 19:13:57 +02:00
131e0eb371 Cleanup
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-11 19:03:05 +02:00
e563011099 New try
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 18:58:50 +02:00
7c072752c8 Build fixed
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 18:52:39 +02:00
36ced002e0 Fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 18:50:08 +02:00
3e1b892eef Fix build?
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 18:38:15 +02:00
f4004cf2be Revert "AW-1426 Add extra check"
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
This reverts commit a6fd052a4f.
2020-08-11 14:42:45 +02:00
d1d8723186 Rollback c028adfb77
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 14:40:04 +02:00
a6fd052a4f AW-1426 Add extra check
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 14:35:04 +02:00
cede944538 Revert "AW-1426 Remove default component if none found"
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
This reverts commit 3b3c2768d3.
2020-08-11 14:24:12 +02:00
3b3c2768d3 AW-1426 Remove default component if none found
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 14:11:51 +02:00
3dd19c702f Remove Trijntje
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-08-11 13:32:05 +02:00
c028adfb77 Updated statistics confidence interval.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-06 12:04:56 +02:00
1d2caa0fe3 Updated statistics labels.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-06 09:15:31 +02:00
Willem Dantuma
d7ca1512bd Add TaskProgressEvent
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-05 20:05:44 +02:00
Willem Dantuma
ebe435027f Add addedfiles stream
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-05 15:49:02 +02:00
Willem Dantuma
dfefa6746f Merge branch 'develop' of https://git.akkerweb.nl/FarmMaps/FarmMapsLib into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-05 10:34:04 +02:00
Willem Dantuma
c90df5c972 Fix userPackagesChanged 2020-08-05 10:33:55 +02:00
514a91c2c7 Improved implementation to retrieve user packages.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-05 09:02:28 +02:00
Willem Dantuma
ee7d1cb9a2 AW-1419
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-04 16:09:22 +02:00
309b320534 AW-1415 Add postItemPackageTask
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-04 14:08:50 +02:00
fdfa7d3387 Typo
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-03 08:48:11 +02:00
Willem Dantuma
6785bfc3e0 Fix when not having READ rights on the parent
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-08-01 09:44:32 +02:00
Willem Dantuma
c682fb9184 Fix z-index
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-31 09:51:28 +02:00
Willem Dantuma
ca6ea96eae Add interfacetype VectorWFSJson
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-30 09:33:32 +02:00
Willem Dantuma
3ebc197fac Make extent nullable
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-29 08:43:17 +02:00
52871efb09 fix for statistics
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-28 20:02:23 +02:00
f014dc9f93 now use statistics if available in band.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-28 19:05:38 +02:00
e6cda406c9 improved alignment of legend. fixed rain in weatherservice.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-25 09:57:24 +02:00
df04397b13 I info icon now placed in the top right.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-23 19:39:06 +02:00
Willem Dantuma
e0b29c905c Fix height
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-23 11:48:31 +02:00
Willem Dantuma
44ae806b30 Add offline alert
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-23 11:28:07 +02:00
Willem Dantuma
78e3cf74b9 Merge branch 'develop' of https://git.akkerweb.nl/FarmMaps/FarmMapsLib into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-23 09:43:03 +02:00
Willem Dantuma
428353d379 Fix inituser flow 2020-07-23 09:42:44 +02:00
9697e93915 convert hourly to daily with wilco's new changes for weather2.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-22 23:04:06 +02:00
Willem Dantuma
e057e463bd Only on change
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-22 21:16:38 +02:00
Willem Dantuma
d494a7c7d6 Implement healthcheck service
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-22 20:31:12 +02:00
Willem Dantuma
e525227865 another attempt to implement on /offline
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-22 18:49:15 +02:00
Willem Dantuma
10bdd27608 Check token when going online
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-22 09:05:02 +02:00
Willem Dantuma
2790ae74e7 Mirror navigator online flag in observable state 2020-07-22 08:52:41 +02:00
Willem Dantuma
31d01c75c5 And another potential issue
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-22 08:23:53 +02:00
Willem Dantuma
0da8d3d46d Fix editing unidentified type
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-22 08:22:35 +02:00
Willem Dantuma
60761e9f4a Fix typo
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-21 19:28:14 +02:00
Willem Dantuma
ec9684b826 Set upload flag only when file is found
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-21 19:11:30 +02:00
Willem Dantuma
1073044f31 Merge branch 'develop' of https://git.akkerweb.nl/FarmMaps/FarmMapsLib into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-21 18:40:10 +02:00
Willem Dantuma
81126974f5 After cancel a single file close the upload dialog 2020-07-21 18:39:51 +02:00
06f443a503 fix circular dependency error.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-21 17:06:58 +02:00
eb560aaf33 added weather method do receive historical + forecast weatherdata.
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-07-21 16:57:11 +02:00
Willem Dantuma
464b82bfb9 Fix colspan
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-21 11:21:34 +02:00
Willem Dantuma
1c75fb6f71 Fix upload dialog
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-20 15:07:25 +02:00
Willem Dantuma
5f5d8a9712 Do not add when cancelled
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-20 14:59:27 +02:00
Willem Dantuma
dc329a0e29 Try to fix file count
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-20 14:50:00 +02:00
Willem Dantuma
15a09e4b05 Attempt to fix multiple queue events for file upload
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-20 14:13:30 +02:00
94f973406e fixed parent name size.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-15 23:35:22 +02:00
6404e45d66 fix bug.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-15 23:28:17 +02:00
c0f689331e Added parentitem title to geotiff, shape and temporal selected-item components.
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-07-15 23:15:31 +02:00
0f5a5251db only show legend title if one layer
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-15 13:41:25 +02:00
3352bfd805 Added schema layout api to schemaService
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-09 13:45:24 +02:00
Willem Dantuma
60ed2afaa4 Fix build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-07-07 09:45:13 +02:00
Willem Dantuma
9ac534c171 Fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-07-07 09:42:30 +02:00
Willem Dantuma
cecbdb2ded Fix dates
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-07-07 09:37:17 +02:00
Willem Dantuma
1092d5dcaf Fix build
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-07-07 09:31:47 +02:00
Willem Dantuma
4af6b2937c Refactor Date to string
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-07-07 09:24:45 +02:00
263fb6c3d4 Changed schema service to return any for json object.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-30 16:30:33 +02:00
Willem Dantuma
09fa8b4f1f Fix template selection
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-30 09:11:46 +02:00
Willem Dantuma
214dc3ffa1 Refactor temporallast
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-29 16:09:19 +02:00
Willem Dantuma
fad04744cf Refactor ILayer
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-29 16:00:53 +02:00
Willem Dantuma
033b74517f Export IDataLayer
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-29 15:51:08 +02:00
Willem Dantuma
0b7564e874 Add data layer
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-29 15:44:23 +02:00
Willem Dantuma
0d247e6416 Fix layerswitcher not opening
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-26 09:18:07 +02:00
Willem Dantuma
b92cc7bc09 Fix build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-26 09:02:33 +02:00
Willem Dantuma
eba18b0235 Close menu's on map click
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-06-26 08:49:35 +02:00
Willem Dantuma
69f8184dfe add closeall action
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-26 08:22:31 +02:00
Willem Dantuma
31c1570ffb Fix relogin issue on mobile
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-25 19:04:40 +02:00
Willem Dantuma
fe2650c4fd Refactored logout
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-25 18:52:13 +02:00
Willem Dantuma
f89209c555 Add logout event handler
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-25 17:21:12 +02:00
Willem Dantuma
b3d0a6f0c7 parse dataenddate
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-25 10:34:25 +02:00
Willem Dantuma
6bd1726fc9 Add dataenddate
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-25 10:01:54 +02:00
Willem Dantuma
b0ff54d157 Add dataenddate
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-25 09:47:53 +02:00
Willem Dantuma
277dc7051e Add shadow and transition
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-24 15:49:57 +02:00
Willem Dantuma
ace4a6b364 Implement toggleaccountmenu action
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-24 15:07:11 +02:00
Willem Dantuma
146514d386 Merge branch 'develop' of https://git.akkerweb.nl/FarmMaps/FarmMapsLib into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-24 13:08:27 +02:00
Willem Dantuma
1187ee9fd6 Implement logout 2020-06-24 13:08:13 +02:00
4527276254 Changed schema service.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-24 11:52:05 +02:00
Willem Dantuma
6f52302875 Fix build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-24 08:40:28 +02:00
Willem Dantuma
cbe27c2add Refactor template selection logic add forpackage decorator
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-06-24 08:35:34 +02:00
Willem Dantuma
cd156ab1bc Export packageservice
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-24 07:56:28 +02:00
Willem Dantuma
f9d0e2aee0 Implemented hasclaim directive
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-23 17:51:47 +02:00
Willem Dantuma
253b3d3c90 Fix build
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-23 15:44:14 +02:00
Willem Dantuma
28d4adc571 AW-1330
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2020-06-23 15:39:30 +02:00
Willem Dantuma
925af1e645 Update dependency
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-23 14:18:49 +02:00
Willem Dantuma
4130e0a796 Merge branch 'develop' of https://git.akkerweb.nl/FarmMaps/FarmMapsLib into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-23 13:36:29 +02:00
Willem Dantuma
01933b1602 AW-1330 add atitemlocationitem parameter 2020-06-23 13:36:03 +02:00
0482aa7124 Added provided in root for schema service
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-22 13:37:38 +02:00
75015f6d22 Added schema service
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-22 13:12:02 +02:00
aa3707aa56 Added getSchema method to itemtypes service.
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-22 12:20:02 +02:00
Willem Dantuma
ff06b419f2 Fix top in fullscreen mode
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-12 12:53:43 +02:00
Willem Dantuma
c1c0bd2596 Add start of user/account menu
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-12 12:24:08 +02:00
Willem Dantuma
978cbdabfc Read both farmmaps currentuser and authenticate userinfo
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2020-06-10 21:53:54 +02:00
114 changed files with 7879 additions and 8298 deletions

1
.npmrc
View File

@@ -1 +0,0 @@
@FarmMaps:registry=https://repository.akkerweb.nl/repository/npm-group/

View File

@@ -7,18 +7,22 @@ pipeline {
stages { stages {
stage('npm install'){ stage('npm install'){
steps { steps {
sh '''npm install sh '''rm -rf node_modules/
npm install
cd projects/common cd projects/common
npm install npm install
cd ../common-map cd ../common-map
npm install npm install
cd ../common-map3d
npm install
''' '''
} }
} }
stage('build'){ stage('build'){
steps { steps {
sh '''ng build common sh '''ng build common
ng build common-map''' ng build common-map
ng build common-map3d'''
} }
} }
stage('npm publish'){ stage('npm publish'){
@@ -28,6 +32,9 @@ pipeline {
npm publish npm publish
cd ../common-map cd ../common-map
npm version ${PACKAGE_VERSION} npm version ${PACKAGE_VERSION}
npm publish
cd ../common-map3d
npm version ${PACKAGE_VERSION}
npm publish''' npm publish'''
} }
} }

View File

@@ -11,8 +11,11 @@
"schematics": {}, "schematics": {},
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular-builders/custom-webpack:browser",
"options": { "options": {
"customWebpackConfig": {
"path": "./custom-webpack.config.js"
},
"aot": true, "aot": true,
"outputPath": "dist/farmmaps-lib-app", "outputPath": "dist/farmmaps-lib-app",
"index": "src/index.html", "index": "src/index.html",
@@ -22,18 +25,23 @@
"assets": [ "assets": [
{ {
"glob": "**/*", "glob": "**/*",
"input":"src/assets/images", "input": "src/assets/images",
"output":"/images" "output": "/images"
}, },
{ {
"glob": "silent-refresh.html", "glob": "silent-refresh.html",
"input":"src/assets", "input": "src/assets",
"output":"/" "output": "/"
}, },
{ {
"glob": "favicon.ico", "glob": "favicon.ico",
"input":"src/assets", "input": "src/assets",
"output":"/" "output": "/"
},
{
"glob": "**/*",
"input": "node_modules/cesium/Build/Cesium",
"output": "/assets/cesium"
}, },
"src/configuration.json" "src/configuration.json"
], ],
@@ -41,10 +49,11 @@
"src/styles.css", "src/styles.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/bootstrap/dist/css/bootstrap.min.css",
"node_modules/font-awesome/css/font-awesome.min.css", "node_modules/font-awesome/css/font-awesome.min.css",
"fonts/FMIconFont/style.css" "fonts/FMIconFont/style.css",
"node_modules/cesium/Build/Cesium/Widgets/widgets.css",
"node_modules/ol/ol.css"
], ],
"scripts": [], "scripts": []
"es5BrowserSupport": true
}, },
"configurations": { "configurations": {
"production": { "production": {
@@ -78,7 +87,7 @@
} }
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular-builders/custom-webpack:dev-server",
"options": { "options": {
"browserTarget": "farmmaps-lib-app:build" "browserTarget": "farmmaps-lib-app:build"
}, },
@@ -232,6 +241,46 @@
} }
} }
} }
},
"common-map3d": {
"projectType": "library",
"root": "projects/common-map3d",
"sourceRoot": "projects/common-map3d/src",
"prefix": "fm-map3d",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr:build",
"options": {
"tsConfig": "projects/common-map3d/tsconfig.lib.json",
"project": "projects/common-map3d/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "projects/common-map3d/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/common-map3d/src/test.ts",
"tsConfig": "projects/common-map3d/tsconfig.spec.json",
"karmaConfig": "projects/common-map3d/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/common-map3d/tsconfig.lib.json",
"projects/common-map3d/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
} }
}, },
"defaultProject": "farmmaps-lib-app" "defaultProject": "farmmaps-lib-app"

10
custom-webpack.config.js Normal file
View File

@@ -0,0 +1,10 @@
module.exports = {
node: {
// Resolve node module use of fs
fs: "empty",
Buffer: false,
http: "empty",
https: "empty",
zlib: "empty"
}
};

12937
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,56 +11,62 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "~9.1.0", "@angular/animations": "~10.1.3",
"@angular/common": "~9.1.0", "@angular/common": "~10.1.3",
"@angular/compiler": "~9.1.0", "@angular/compiler": "~10.1.3",
"@angular/core": "~9.1.0", "@angular/core": "~10.1.3",
"@angular/forms": "~9.1.0", "@angular/forms": "~10.1.3",
"@angular/platform-browser": "~9.1.0", "@angular/platform-browser": "~10.1.3",
"@angular/platform-browser-dynamic": "~9.1.0", "@angular/platform-browser-dynamic": "~10.1.3",
"@angular/router": "~9.1.0", "@angular/router": "~10.1.3",
"@farmmaps/common": ">=0.0.1-prerelease.434 <0.0.1",
"@farmmaps/common-map": ">=0.0.1-prerelease.434 <0.0.1",
"@farmmaps/common-map3d": ">=0.0.1-prerelease.434 <0.0.1",
"@microsoft/signalr": "^3.1.3", "@microsoft/signalr": "^3.1.3",
"@farmmaps/common": ">=0.0.1-prerelease.267 <0.0.1", "@ng-bootstrap/ng-bootstrap": "^7.0",
"@farmmaps/common-map": ">=0.0.1-prerelease.267 <0.0.1", "@ngrx/effects": "^10.0",
"@ng-bootstrap/ng-bootstrap": "^6.0", "@ngrx/router-store": "^10.0",
"@ngrx/effects": "^9.0", "@ngrx/store": "^10.0",
"@ngrx/router-store": "^9.0", "angular-oauth2-oidc": "^10.0.3",
"@ngrx/store": "^9.0",
"ngx-uploadx": "^3.3.4",
"angular-oauth2-oidc": "^9.1",
"bootstrap": "^4.4.1", "bootstrap": "^4.4.1",
"cesium": "^1.73.0",
"core-js": "^2.6.11", "core-js": "^2.6.11",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"ngrx-store-localstorage": "^9.0", "ngrx-store-localstorage": "^10.0",
"ngx-bootstrap": "^5.6.1",
"ngx-openlayers": "1.0.0-next.16",
"ngx-uploadx": "^3.5.1",
"ol": "6.1.1",
"ol-cesium": "^2.11.3",
"resumablejs": "^1.1.0", "resumablejs": "^1.1.0",
"rxjs": "^6.5.4", "rxjs": "^6.5.4",
"tassign": "^1.0.0", "tassign": "^1.0.0",
"tslib": "^1.10.0", "tslib": "^2.0.0",
"zone.js": "~0.10.2", "zone.js": "~0.10.2",
"ngx-openlayers": "1.0.0-next.13", "moment": "^2.27.0"
"ol": "6.1.1"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.901.0", "@angular-devkit/build-angular": "^0.1001.3",
"@angular-devkit/build-ng-packagr": "~0.901.0", "@angular-devkit/build-ng-packagr": "~0.1001.3",
"@angular/cli": "^9.1.0", "@angular/cli": "^10.1.3",
"@angular/compiler-cli": "~9.1.0", "@angular/compiler-cli": "~10.1.3",
"@angular/language-service": "~9.1.0", "@angular/language-service": "~10.1.3",
"@angular-builders/custom-webpack": "~10.0.1",
"@types/jasmine": "~2.8.8", "@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3", "@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1", "@types/node": "^12.11.1",
"codelyzer": "^5.1.2", "codelyzer": "^5.1.2",
"jasmine-core": "~2.99.1", "jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~4.2.1", "jasmine-spec-reporter": "~5.0.0",
"karma": "^5.0.2", "karma": "~5.0.0",
"karma-chrome-launcher": "~2.2.0", "karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.0.1", "karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~1.1.2", "karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^0.2.2", "karma-jasmine-html-reporter": "^1.5.0",
"ng-packagr": "^9.0.0", "ng-packagr": "^10.1.0",
"protractor": "^5.4.4", "protractor": "~7.0.0",
"ts-node": "^8.8.1", "ts-node": "^8.8.1",
"tslint": "~5.11.0", "tslint": "~6.1.0",
"typescript": "~3.8.3" "typescript": "~4.0.3"
} }
} }

View File

@@ -4,79 +4,10 @@
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@openlayers/pepjs": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@openlayers/pepjs/-/pepjs-0.5.3.tgz",
"integrity": "sha512-Bgvi5c14BS0FJWyYWWFstNEnXsB30nK8Jt8hkAAdqr7E0gDdBBWVDglF3Ub19wTxvgJ/CVHyTY6VuCtnyRzglg=="
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"ngx-openlayers": {
"version": "1.0.0-next.13",
"resolved": "https://registry.npmjs.org/ngx-openlayers/-/ngx-openlayers-1.0.0-next.13.tgz",
"integrity": "sha512-6y724s5JV6n1oxEhryxP3wcbwzpierj9RLiMLXInfKOG3l5IO1AtPNkeK9+mKctVDUVm5URIfzzZH02Ld+bvSQ==",
"requires": {
"tslib": "^1.9.0"
}
},
"ol": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/ol/-/ol-6.1.1.tgz",
"integrity": "sha512-0dL3i3eJqgOpqIjDKEY3grkeQnjAYfV5L/JCxhOu4SxiaizRwFrFgeas6LILRoxKa03jhQFbut2r2bbgcLGQeA==",
"requires": {
"@openlayers/pepjs": "^0.5.3",
"pbf": "3.2.1",
"pixelworks": "1.1.0",
"rbush": "^3.0.1"
}
},
"pbf": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
"requires": {
"ieee754": "^1.1.12",
"resolve-protobuf-schema": "^2.1.0"
}
},
"pixelworks": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pixelworks/-/pixelworks-1.1.0.tgz",
"integrity": "sha1-Hwla1I3Ki/ihyCWOAJIDGkTyLKU="
},
"protocol-buffers-schema": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz",
"integrity": "sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA=="
},
"quickselect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
},
"rbush": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
"integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
"requires": {
"quickselect": "^2.0.0"
}
},
"resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"requires": {
"protocol-buffers-schema": "^3.3.1"
}
},
"tslib": { "tslib": {
"version": "1.10.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
} }
} }
} }

View File

@@ -4,15 +4,18 @@
"publishConfig": { "publishConfig": {
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/" "registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
}, },
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": { "peerDependencies": {
"@angular/core": "^9.1.0", "@angular/core": "^10.1.3",
"ngrx-store-localstorage": "^9.0", "ngrx-store-localstorage": "^10.0",
"@ngrx/effects": "^9.0", "@ngrx/effects": "^10.0",
"@ngrx/router-store": "^9.0", "@ngrx/router-store": "^10.0",
"@ngrx/store": "^9.0", "@ngrx/store": "^10.0",
"tassign": "^1.0.0", "tassign": "^1.0.0",
"@farmmaps/common": ">=0.0.1-prerelease.265 <0.0.1", "@farmmaps/common": ">=0.0.1-prerelease.420 <0.0.1",
"ngx-openlayers": "1.0.0-next.13", "ngx-openlayers": "1.0.0-next.16",
"ol": "6.1.1" "ol": "6.1.1"
} }
} }

View File

@@ -105,7 +105,7 @@ export class SelectItem implements Action {
export class SelectItemSuccess implements Action { export class SelectItemSuccess implements Action {
readonly type = SELECTITEMSUCCESS; readonly type = SELECTITEMSUCCESS;
constructor(public item: IItem) { } constructor(public item: IItem, public parentItem: IItem) { }
} }
export class SelectTemporalItemsSuccess implements Action { export class SelectTemporalItemsSuccess implements Action {

View File

@@ -24,7 +24,6 @@ import { IPeriodState } from './models/period.state';
// components // components
import { GpsLocation} from './components/aol/gps-location/gps-location.component'; import { GpsLocation} from './components/aol/gps-location/gps-location.component';
//import {Switch2D3DComponent } from './components/aol/switch2d3d/switch2d3d.component';
import {FeatureListFeatureCropfieldComponent } from './components/feature-list-feature-cropfield/feature-list-feature-cropfield.component'; import {FeatureListFeatureCropfieldComponent } from './components/feature-list-feature-cropfield/feature-list-feature-cropfield.component';
import { FeatureListFeatureCroppingschemeComponent} from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.component'; import { FeatureListFeatureCroppingschemeComponent} from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.component';
import { ItemWidgetListComponent} from './components/item-widget-list/item-widget-list.component'; import { ItemWidgetListComponent} from './components/item-widget-list/item-widget-list.component';
@@ -64,8 +63,11 @@ import { WidgetStatusComponent } from './components/widget-status/widget-status.
import { ForChild} from './components/for-item/for-child.decorator'; import { ForChild} from './components/for-item/for-child.decorator';
import {ForItemType } from './components/for-item/for-itemtype.decorator'; import {ForItemType } from './components/for-item/for-itemtype.decorator';
import { ForSourceTask} from './components/for-item/for-sourcetask.decorator'; import { ForSourceTask} from './components/for-item/for-sourcetask.decorator';
import { ForPackage } from './components/for-item/for-package.decorator';
import { PanToLocation} from './components/aol/pan-to-location/pan-to-location.component'; import { PanToLocation} from './components/aol/pan-to-location/pan-to-location.component';
import {LayerSwitcher} from './components/layer-switcher/layer-switcher.component'; import {LayerSwitcher} from './components/layer-switcher/layer-switcher.component';
import {HistogramDetailsComponent} from './components/legend/histogram-details/histogram-details.component';
import {StatisticsDetailsComponent} from './components/legend/statistics-details/statistics-details.component';
export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any> { export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any> {
const r = function(state, action) { const r = function(state, action) {
@@ -143,10 +145,11 @@ export {
IPeriodState, IPeriodState,
ForChild, ForChild,
ForItemType, ForItemType,
ForSourceTask ForSourceTask,
ForPackage
} }
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
AngularOpenlayersModule, AngularOpenlayersModule,
@@ -193,7 +196,9 @@ export {
WidgetStatusComponent, WidgetStatusComponent,
GpsLocation, GpsLocation,
PanToLocation, PanToLocation,
LayerSwitcher LayerSwitcher,
HistogramDetailsComponent,
StatisticsDetailsComponent
], ],
entryComponents: [ entryComponents: [
FeatureListComponent, FeatureListComponent,
@@ -253,7 +258,7 @@ export {
export class AppCommonMapModule { export class AppCommonMapModule {
static forRoot(): ModuleWithProviders<AppCommonMapModule> { static forRoot(): ModuleWithProviders<AppCommonMapModule> {
return { return {
ngModule: AppCommonMapModule, ngModule: AppCommonMapModule,
providers: [ providers: [
FeatureIconService, FeatureIconService,
GeolocationService, GeolocationService,

View File

@@ -1,4 +1,4 @@
import { Component, OnInit, Input, ViewChild, ElementRef, OnChanges, SimpleChanges } from '@angular/core'; import { Component, OnInit, Input, ViewChild, ElementRef, OnChanges, SimpleChanges ,Host} from '@angular/core';
import { MapComponent } from 'ngx-openlayers'; import { MapComponent } from 'ngx-openlayers';
import Overlay from 'ol/Overlay'; import Overlay from 'ol/Overlay';
import { fromLonLat, toLonLat } from 'ol/proj'; import { fromLonLat, toLonLat } from 'ol/proj';
@@ -23,9 +23,10 @@ export class GpsLocation implements OnInit,OnChanges{
public path: string = ""; public path: string = "";
public rotate: string = ""; public rotate: string = "";
private resolution: number = 0; private resolution: number = 0;
initialized:boolean = false;
@ViewChild('location', { static: true }) locationElement: ElementRef; @ViewChild('location', { static: true }) locationElement: ElementRef;
constructor(private map: MapComponent) { constructor(@Host() private map: MapComponent) {
} }
@@ -54,12 +55,13 @@ export class GpsLocation implements OnInit,OnChanges{
this.resolution = evt.target.get('resolution'); this.resolution = evt.target.get('resolution');
this.recalcLocationTolerance(); this.recalcLocationTolerance();
}); });
this.initialized = true;
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (changes.position && this.instance) { if (changes.position && this.instance) {
var p = changes.position.currentValue as Position; var p = changes.position.currentValue as Position;
if(p) { if(p && this.initialized) {
this.instance.setPosition(fromLonLat([p.coords.longitude, p.coords.latitude])); this.instance.setPosition(fromLonLat([p.coords.longitude, p.coords.latitude]));
this.locationTolerance = p.coords.accuracy; this.locationTolerance = p.coords.accuracy;
this.recalcLocationTolerance(); this.recalcLocationTolerance();

View File

@@ -11,7 +11,7 @@ import * as proj from 'ol/proj';
import * as loadingstrategy from 'ol/loadingstrategy'; import * as loadingstrategy from 'ol/loadingstrategy';
import * as style from 'ol/style'; import * as style from 'ol/style';
import {Tile,Layer,Image} from 'ol/layer'; import {Tile,Layer,Image} from 'ol/layer';
import {XYZ,ImageStatic,OSM,BingMaps,TileWMS,TileArcGISRest} from 'ol/source'; import {XYZ,ImageStatic,OSM,BingMaps,TileWMS,TileArcGISRest,TileJSON} from 'ol/source';
import {Vector as VectorSource} from 'ol/source'; import {Vector as VectorSource} from 'ol/source';
import { Vector as VectorLayer } from 'ol/layer'; import { Vector as VectorLayer } from 'ol/layer';
import VectorTileSource from 'ol/source/VectorTile'; import VectorTileSource from 'ol/source/VectorTile';
@@ -31,6 +31,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
@Input() itemLayers: IItemLayer[]; @Input() itemLayers: IItemLayer[];
@Input() itemLayer: IItemLayer; @Input() itemLayer: IItemLayer;
private _apiEndPoint: string; private _apiEndPoint: string;
private initialized:boolean = false;
constructor(private itemService: ItemService, @Host() private map: MapComponent, public appConfig: AppConfig) { constructor(private itemService: ItemService, @Host() private map: MapComponent, public appConfig: AppConfig) {
super(map); super(map);
@@ -48,9 +49,11 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b) + this.componentToHex(a); return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b) + this.componentToHex(a);
} }
getColorFromGradient(layer: ILayer, feature): style.Style { getColorFromGradient(item:IItem,layer: ILayer, feature): style.Style {
var value = feature.get(layer.name); var value = layer.indexKey ? feature.get(layer.indexKey): feature.get(layer.name);
var gradient: IGradientstop[] = layer.renderer.colorMap.gradient; var key = item.code + "_" + value;
if(!this.styleCache[key]) {
var gradient: IGradientstop[] = layer.renderer.colorMap.gradient;
var histogram: IHistogram = layer.renderer.band.histogram; var histogram: IHistogram = layer.renderer.band.histogram;
var index = (value - histogram.min) / histogram.max; var index = (value - histogram.min) / histogram.max;
var min = gradient[0]; var min = gradient[0];
@@ -67,7 +70,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
var green = Math.round(min.color.green + ((max.color.green - min.color.green) * i / size)); var green = Math.round(min.color.green + ((max.color.green - min.color.green) * i / size));
var blue = Math.round(min.color.blue + ((max.color.blue - min.color.blue) * i / size)); var blue = Math.round(min.color.blue + ((max.color.blue - min.color.blue) * i / size));
return new style.Style( this.styleCache[key] = new style.Style(
{ {
image: new style.Circle({ image: new style.Circle({
fill: new style.Fill({ fill: new style.Fill({
@@ -79,33 +82,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
color: this.rgbaToHex(red, green, blue, alpha) color: this.rgbaToHex(red, green, blue, alpha)
}), }),
stroke: new style.Stroke({ stroke: new style.Stroke({
color: this.rgbaToHex(red, green, blue, alpha), color: this.rgbaToHex(red, green, blue, 255),
width: 1.25 width: 1.25
}), }),
}); });
}
return this.styleCache[key];
} }
createGeotiffLayer(item:IItem,itemLayer:IItemLayer):Layer { createGeotiffLayer(item:IItem,itemLayer:IItemLayer):Layer {
var layerIndex = -1; var layerIndex = -1;
var layer: Layer = null; var layer: Layer = null;
layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index;
let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` }); let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}` });
layer = new Tile({ source: source }); layer = new Tile({ source: source });
var data = item.data; var data = item.data;
var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null;
if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { if (l && l.rendering && l.rendering.renderoutputType == "Tiles") {
var rt = l.rendering as IRenderoutputTiles; var rt = l.rendering as IRenderoutputTiles;
let source = new XYZ({ maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` }); let source = new XYZ({crossOrigin: 'use-credentials', maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}` });
layer = new Tile({ source: source }); layer = new Tile({ source: source });
} }
if (l && l.rendering && l.rendering.renderoutputType == "Image") { if (l && l.rendering && l.rendering.renderoutputType == "Image") {
var ri = l.rendering as IRenderoutputImage; var ri = l.rendering as IRenderoutputImage;
let projection = new Projection({ // convert to EPSG:4326 asworkaround for cesium
code: 'image', var projectedExtent = proj.transformExtent( ri.extent, 'EPSG:3857','EPSG:4326');
units: 'pixels', let source = new ImageStatic({ imageExtent:projectedExtent,projection:'EPSG:4326', crossOrigin: 'use-credentials', url: `${this._apiEndPoint}/api/v1/items/${item.code}/mapimage/${layerIndex}?v=${Date.parse(item.updated)}` });
extent: ri.extent
});
let source = new ImageStatic({ imageExtent: ri.extent, projection: projection, url: `${this._apiEndPoint}/api/v1/items/${item.code}/mapimage/${layerIndex}?v=${item.updated.getTime()}` });
layer = new Image({ source: source }); layer = new Image({ source: source });
} }
return layer; return layer;
@@ -116,19 +118,19 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
var layer: Layer = null; var layer: Layer = null;
layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index;
var data = item.data; var data = item.data;
var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; var l:ILayer = (data && data.layers && data.layers.length > 0) ? data.layers[layerIndex] : null;
if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") { if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") {
var rt = item.data.layers[layerIndex].rendering as IRenderoutputTiles; var rt = l.rendering as IRenderoutputTiles;
layer = new VectorTileLayer({ layer = new VectorTileLayer({
declutter: true, declutter: true,
source: new VectorTileSource({ source: new VectorTileSource({
maxZoom: rt.maxzoom, maxZoom: rt.maxzoom,
minZoom: rt.minzoom, minZoom: rt.minzoom,
format: new MVT(), format: new MVT(),
url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${item.updated.getTime()}` url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${Date.parse(item.updated)}`
}), }),
style: (feature) => { style: (feature) => {
return this.getColorFromGradient(l, feature); return this.getColorFromGradient(item,l, feature);
} }
}) })
} else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { } else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") {
@@ -137,7 +139,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
source: new XYZ({ source: new XYZ({
maxZoom: rt.maxzoom, maxZoom: rt.maxzoom,
minZoom: rt.minzoom, minZoom: rt.minzoom,
url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}`
}) })
}); });
} else { } else {
@@ -161,7 +163,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
layer = new VectorLayer({ layer = new VectorLayer({
source: source, source: source,
style: (feature) => { style: (feature) => {
var key = feature.get("color"); var key =feature.get("code") + "_" + feature.get("color");
if (!this.styleCache[key]) { if (!this.styleCache[key]) {
var color = feature.get("color"); var color = feature.get("color");
this.styleCache[key] = new style.Style( this.styleCache[key] = new style.Style(
@@ -190,6 +192,12 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
} }
}); });
} }
if(l.minzoom) {
layer.setMinZoom(l.minzoom);
}
if(l.maxzoom) {
layer.setMaxZoom(l.maxzoom);
}
return layer; return layer;
} }
@@ -212,11 +220,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
layer = new Tile({ source: source }); layer = new Tile({ source: source });
break; break;
} }
case 'TileJSON': {
let source = new TileJSON(data.options);
layer = new Tile({ source: source });
break;
}
case 'TileArcGISRest': { case 'TileArcGISRest': {
let source = new TileArcGISRest(data.options); let source = new TileArcGISRest(data.options);
layer = new Tile({ source: source }); layer = new Tile({ source: source });
break; break;
} }
case 'VectorWFSJson': {
let source = new VectorSource({
format: new GeoJSON(),
url: function (extent) {
return (
data.options.url + '&srsname=' + data.projection +
'&bbox=' +
extent.join(',') +
',EPSG:3857'
);
},
strategy: loadingstrategy.bbox,
});
layer = new VectorLayer({ source: source });
break;
}
default: { default: {
break; break;
} }
@@ -245,7 +274,14 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
ngOnInit() { ngOnInit() {
super.ngOnInit(); super.ngOnInit();
this.updateLayers(this.itemLayers); if(this.itemLayers) {
this.updateLayers(this.itemLayers);
} else if(this.itemLayer) {
this.updateLayers([this.itemLayer])
} else {
this.updateLayers([]);
}
this.initialized=true;
} }
addOrUpdateOlLayer(itemLayer:IItemLayer,index:number):Layer { addOrUpdateOlLayer(itemLayer:IItemLayer,index:number):Layer {
@@ -298,7 +334,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
} }
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (this.instance) { if (this.instance && this.initialized) {
if (changes['itemLayers']) { if (changes['itemLayers']) {
var itemLayers = changes['itemLayers'].currentValue as IItemLayer[]; var itemLayers = changes['itemLayers'].currentValue as IItemLayer[];
this.updateLayers(itemLayers); this.updateLayers(itemLayers);

View File

@@ -1,40 +0,0 @@
// import { Component, OnInit,Input } from '@angular/core';
//import { MapComponent } from 'ngx-openlayers';
//import OLCesium from 'olcs/OLCesium.js';
// @Component({
// selector: 'fm-map-switch2d3d',
// template: '<div (click)="handleClick($event)" class="rounded-circle twotreed">{{label}}</div>',
// styles: [`.twotreed {
// width:2.5em;
// height:2.5em;
// background-color: white;
// text-align:center;
// line-height:2.5em;
// font-weight:bold;
// cursor:default;}`]
// })
// export class Switch2D3DComponent {
// @Input() enable:boolean;
// public label: string = "3D";
// private ol3d: OLCesium;
// constructor(private map: MapComponent) {
// }
// ngOnInit() {
// this.ol3d = new OLCesium({ map: this.map.instance }); // ol2dMap is the ol.Map instance
// }
// handleClick(event) {
// this.enable = !this.enable;
// if (this.enable)
// this.ol3d.setEnabled(this.enable);
// this.label = this.enable?"2D":"3D";
// }
// }

View File

@@ -2,7 +2,7 @@ import { Component, Input, OnInit, ComponentFactoryResolver, ViewChild, SimpleCh
import { Feature } from 'ol'; import { Feature } from 'ol';
import { FeatureListComponent,AbstractFeatureListComponent } from '../feature-list/feature-list.component'; import { FeatureListComponent,AbstractFeatureListComponent } from '../feature-list/feature-list.component';
import { WidgetHostDirective } from '../widget-host/widget-host.directive'; import { WidgetHostDirective } from '../widget-host/widget-host.directive';
import {IQueryState } from '@farmmaps/common'; import {IQueryState,PackageService } from '@farmmaps/common';
import * as mapReducers from '../../reducers/map.reducer'; import * as mapReducers from '../../reducers/map.reducer';
import * as mapActions from '../../actions/map.actions'; import * as mapActions from '../../actions/map.actions';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@@ -16,7 +16,7 @@ import { Observable } from 'rxjs';
}) })
export class FeatureListContainerComponent { export class FeatureListContainerComponent {
constructor(private store: Store<mapReducers.State>,private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListComponent) public featureLists: AbstractFeatureListComponent[] ) { constructor(private store: Store<mapReducers.State>,private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListComponent) public featureLists: AbstractFeatureListComponent[],private packageService:PackageService ) {
} }
@Input() features: Array<Feature> @Input() features: Array<Feature>
@@ -28,30 +28,44 @@ export class FeatureListContainerComponent {
componentRef:any; componentRef:any;
loadComponent(queryState:IQueryState) { loadComponent(queryState:IQueryState) {
var componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default let componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default
var selected = -1; let selected = -1;
for (var i = 0; i < this.featureLists.length; i++) { let maxMatches =0;
if (this.featureLists[i]['forItemType'] == queryState.itemType && this.featureLists[i]['forChild'] && queryState.parentCode && queryState.parentCode != "") { let showItem = true;
selected = i; for (let i = 0; i < this.featureLists.length; i++) {
break; let matches=0;
} else if (this.featureLists[i]['forItemType'] == queryState.itemType) { let criteria=0;
selected = i; if (this.featureLists[i]['forItemType']) {
break; criteria++;
if( this.featureLists[i]['forItemType'].indexOf(queryState.itemType) >= 0) {
matches++;
}
}
if(this.featureLists[i]['forChild'] ) {
criteria++;
if(queryState.parentCode && queryState.parentCode != "") {
matches++;
}
}
if(criteria == matches && matches > maxMatches) {
selected=i;
maxMatches = matches;
} }
} }
if (selected >= 0) { if (selected >= 0) {
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[i]['constructor'] as any); componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[selected]['constructor'] as any);
if (this.featureLists[selected]['collapseSearch'] === true) { if (this.featureLists[selected]['collapseSearch'] === true) {
this.store.dispatch(new mapActions.CollapseSearch()); this.store.dispatch(new mapActions.CollapseSearch());
} }
} }
const viewContainerRef = this.widgetHost.viewContainerRef; const viewContainerRef = this.widgetHost.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
if(showItem) {
this.componentRef = viewContainerRef.createComponent(componentFactory); this.componentRef = viewContainerRef.createComponent(componentFactory);
(<AbstractFeatureListComponent>this.componentRef.instance).features = null; (<AbstractFeatureListComponent>this.componentRef.instance).features = null;
(<AbstractFeatureListComponent>this.componentRef.instance).queryState = queryState; (<AbstractFeatureListComponent>this.componentRef.instance).queryState = queryState;
(<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = null; (<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = null;
}
} }
ngOnInit() { ngOnInit() {

View File

@@ -23,11 +23,27 @@ export class FeatureListFeatureContainerComponent {
loadComponent() { loadComponent() {
var componentFactory: ComponentFactory<AbstractFeatureListFeatureComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListFeatureComponent); // default var componentFactory: ComponentFactory<AbstractFeatureListFeatureComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListFeatureComponent); // default
for (var i = 0; i < this.featureLists.length; i++) {
if (this.featureLists[i]['forItemType'] == this.feature.get("itemType")) { let selected = -1;
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[i]['constructor'] as any); let maxMatches =0;
for (let i = 0; i < this.featureLists.length; i++) {
let matches=0;
let criteria=0;
if (this.featureLists[i]['forItemType']) {
criteria++;
if(this.featureLists[i]['forItemType'].indexOf(this.feature.get("itemType")) >= 0) {
matches++;
}
}
if(criteria == matches && matches > maxMatches) {
selected=i;
maxMatches = matches;
} }
} }
if (selected >= 0) {
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[selected]['constructor'] as any);
}
const viewContainerRef = this.widgetHost.viewContainerRef; const viewContainerRef = this.widgetHost.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();

View File

@@ -0,0 +1,5 @@
export function ForPackage(packageCode: string) {
return function (constructor:Function) {
constructor.prototype.forPackage = packageCode;
};
}

View File

@@ -23,15 +23,35 @@ export class ItemListItemContainerComponent {
loadComponent() { loadComponent() {
var componentFactory: ComponentFactory<AbstractItemListItemComponent> = this.componentFactoryResolver.resolveComponentFactory(ItemListItemComponent); // default var componentFactory: ComponentFactory<AbstractItemListItemComponent> = this.componentFactoryResolver.resolveComponentFactory(ItemListItemComponent); // default
for (var i = 0; i < this.itemComponentList.length; i++) {
if (this.itemComponentList[i]['forItemType'] && let selected = -1;
this.itemComponentList[i]['forItemType'].indexOf(this.item.itemType) >= 0 && let maxMatches =0;
this.itemComponentList[i]['forSourceTask'] && let showItem = true;
this.itemComponentList[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0 ) for (let i = 0; i < this.itemComponentList.length; i++) {
{ let matches=0;
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.itemComponentList[i]['constructor'] as any); let criteria=0;
if (this.itemComponentList[i]['forItemType']) {
criteria++;
if(this.itemComponentList[i]['forItemType'].indexOf(this.item.itemType) >= 0) {
matches++;
}
} }
} if (this.itemComponentList[i]['forSourceTask']) {
criteria++;
if(this.itemComponentList[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0) {
matches++;
}
}
if(criteria == matches && matches > maxMatches) {
selected=i;
maxMatches = matches;
}
}
if (selected >= 0) {
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.itemComponentList[selected]['constructor'] as any);
}
const viewContainerRef = this.widgetHost.viewContainerRef; const viewContainerRef = this.widgetHost.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();

View File

@@ -36,10 +36,12 @@ export class LayerSwitcher implements OnInit,OnChanges{
handleClick(event:Event) { handleClick(event:Event) {
event.stopPropagation();
this.store.dispatch(new mapActions.ShowLayerSwitcher(true)); this.store.dispatch(new mapActions.ShowLayerSwitcher(true));
} }
close(event:Event) { close(event:Event) {
event.stopPropagation();
this.store.dispatch(new mapActions.ShowLayerSwitcher(false)); this.store.dispatch(new mapActions.ShowLayerSwitcher(false));
} }

View File

@@ -0,0 +1,15 @@
<div class="row pl-3">
<div class="col-8 nopadding"><span i18n>Average</span>:</div>
<div class="col-4 pull-left nopadding">{{histogram.mean| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Standard deviation</span>:</div>
<div class="col-4 pull-left nopadding">{{histogram.stddev| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Min</span>:</div>
<div class="col-4 pull-left nopadding">{{histogram.min| number:'1.0-2'}}</div>
<div class="col-8 nopadding" ><span i18n>Max</span>:</div>
<div class="col-4 pull-left nopadding">{{histogram.max| number:'1.0-2'}}</div>
<ng-container *ngIf="showConfidenceInterval()">
<div class="col-8 nopadding"><span i18n>Confidence interval</span>:</div>
<div class="col-4 pull-left nopadding">{{histogram.confidence * 100| number:'1.0-0'}}%</div>
</ng-container>
</div>

View File

@@ -0,0 +1,20 @@
import {Component, Input} from '@angular/core';
import {IHistogram, ILayer} from '../../../models/color.map';
@Component({
selector: 'fm-map-histogram-details',
templateUrl: './histogram-details.component.html',
styles: ['.nopadding{\n' +
' padding: 0 !important;\n' +
' margin: 0 !important;\n' +
'}']
})
export class HistogramDetailsComponent {
@Input()
histogram: IHistogram;
showConfidenceInterval(): boolean {
return this.histogram.confidence != null && this.histogram.confidence !== 1;
}
}

View File

@@ -1,11 +1,14 @@
<table class="container" *ngIf="showLegend()"> <table class="container" *ngIf="showLegend()">
<tr> <tr>
<td colspan="2"> <td colspan="3">
<div class="title"> <div class="title">
<h4 *ngIf="showTitle">{{layer.name}}</h4> <h4 *ngIf="showTitle">{{layer.name}}</h4>
<b *ngIf="layer.unit">({{layer.unit}})</b> <b *ngIf="layer.unit">({{layer.unit}})</b>
</div> </div>
</td> </td>
<td>
<a i18n-title title="more info"><i class="fa fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a>
</td>
<td colspan="2"> <td colspan="2">
<div class="title" *ngIf="histogramenabled"> <div class="title" *ngIf="histogramenabled">
<h4>{{histogram}}</h4> <h4>{{histogram}}</h4>
@@ -14,26 +17,27 @@
</td> </td>
</tr> </tr>
<tr *ngFor="let entry of layer.renderer.colorMap.entries; let i = index "> <tr *ngFor="let entry of layer.renderer.colorMap.entries; let i = index ">
<td class="legend-items"><span [style.background-color]="getHex(entry.color)" class="color"></span></td> <td class="legend-items"><span [style.background-color]="getAlphaHex(entry.color)" [style.border-color]="getHex(entry.color)"class="color"></span></td>
<td class="legend-items-text"><span *ngIf="!entry.label">{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span><span *ngIf="entry.label">{{entry.label}}</span></td> <td class="legend-items-text"><span *ngIf="!entry.label">{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span><span *ngIf="entry.label">{{entry.label}}</span></td>
<td class="histogram-items"> <td class="histogram-items">
<div *ngIf="showHistogram()"> <div *ngIf="showHistogram()">
<span class="bar" [style.background-color]="getHex(entry.color)" [style.width]="getPart(layer.renderer, i)"> <span class="bar" [style.background-color]="getHex(entry.color)" [style.width]="getPart(layer.renderer, i)">
</span> </span>
<span class="bar-label">{{getLabel(layer.renderer,i)}}</span> <span class="bar-label">{{getLabel(layer.renderer,i)}}</span>
</div> </div>
</td> </td>
</tr> </tr>
<tr *ngIf="showHistogram()"> <tr *ngIf="showHistogram()">
<td colspan="4" class="pb-2 pt-2"> <td colspan="4" class="pb-1 pt-1">
<a i18n-title title="more info"><i class="fa fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a> <div class="info" [ngbCollapse]="hideHistogramDetails">
<div class="info pt-2" [ngbCollapse]="hideHistogramDetails"> <ng-container *ngIf="bandContainsStatistics(); else histogram">
<div><span i18n>Average: </span>{{layer.renderer.band.histogram.mean| number:'1.0-3'}}</div> <fm-map-statistics-details [statistics]="layer.renderer.band.statistics"></fm-map-statistics-details>
<div><span i18n>Standard deviation: </span>{{layer.renderer.band.histogram.stddev| number:'1.0-3'}}</div> </ng-container>
<div><span i18n>Min: </span>{{layer.renderer.band.histogram.min| number:'1.0-3'}}</div>
<div><span i18n>Max: </span>{{layer.renderer.band.histogram.max| number:'1.0-3'}}</div> <ng-template #histogram>
<div *ngIf="showConfidenceInterval()"><span i18n>Confidence interval:</span> {{layer.renderer.band.histogram.confidence * 100| number:'1.0-0'}}%</div> <fm-map-histogram-details [histogram]="layer.renderer.band.histogram"></fm-map-histogram-details>
</div> </ng-template>
</div>
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -20,6 +20,7 @@ span.color {
display:inline-block; display:inline-block;
width: 1.5em; width: 1.5em;
height: 1.5em; height: 1.5em;
border: 2px solid;
} }
.legend-items { .legend-items {

View File

@@ -53,6 +53,10 @@ export class LegendComponent implements OnInit,AfterViewInit {
return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue); return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue);
} }
public getAlphaHex(color: IColor): string {
return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue)+ this.componentToHex(color.alpha);
}
private componentToHex(c: number): string { private componentToHex(c: number): string {
const hex = c.toString(16); const hex = c.toString(16);
return hex.length === 1 ? `0${hex}` : hex; return hex.length === 1 ? `0${hex}` : hex;
@@ -88,8 +92,8 @@ export class LegendComponent implements OnInit,AfterViewInit {
return this.histogramenabled && this.layer.renderer.band.histogram.entries && this.layer.renderer.band.histogram.entries.length > 0 && this.layer.renderer.colorMap.colormapType == "minmax"; return this.histogramenabled && this.layer.renderer.band.histogram.entries && this.layer.renderer.band.histogram.entries.length > 0 && this.layer.renderer.colorMap.colormapType == "minmax";
} }
showConfidenceInterval():boolean { bandContainsStatistics(): boolean {
return this.histogramenabled && this.layer.renderer.band.histogram.confidence != null && this.layer.renderer.band.histogram.confidence != 1; return this.layer.renderer.band.statistics != null;
} }
} }

View File

@@ -0,0 +1,36 @@
<div class="row pl-3">
<div class="col-8 nopadding"><span i18n>Data points</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.populationCount}}</div>
<div class="col-8 nopadding"><span i18n>Min</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.min| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Max</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.max| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Min+1</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.minPlus| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Max-1</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.maxMinus| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Average</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.mean| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Standard deviation</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.stddev| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Mode</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.mode| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Median</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.median| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Kurtosis</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.curtosis| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Skewness</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.skewness| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Variance</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.variance| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Coefficient of variation</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.variationCoefficient | number:'1.0-2'}}</div>
<ng-container *ngIf="statistics.confidenceIntervalLow !== undefined">
<div class="col-8 nopadding">90% <span i18n>Confidence interval</span>:</div>
<div class="col-4 pull-left nopadding">{{statistics.confidenceIntervalLow | number:'1.0-2'}} - {{statistics.confidenceIntervalHigh | number:'1.0-2'}}</div>
</ng-container>
</div>

View File

@@ -0,0 +1,16 @@
import {Component, Input} from '@angular/core';
import {IStatistics} from '../../../models/color.map';
@Component({
selector: 'fm-map-statistics-details',
templateUrl: './statistics-details.component.html',
styles: ['.nopadding{\n' +
' padding: 0 !important;\n' +
' margin: 0 !important;\n' +
'}']
})
export class StatisticsDetailsComponent {
@Input()
statistics: IStatistics;
}

View File

@@ -12,6 +12,7 @@
panelCollapsed:panelCollapsed$|async, panelCollapsed:panelCollapsed$|async,
searchMinified:searchMinified$|async, searchMinified:searchMinified$|async,
selectedItem:selectedItem$|async, selectedItem:selectedItem$|async,
parentItem:parentItem$|async,
queryState:queryState$|async, queryState:queryState$|async,
searchCollapsed:searchCollapsed$|async, searchCollapsed:searchCollapsed$|async,
clearEnabled:clearEnabled$|async, clearEnabled:clearEnabled$|async,
@@ -20,9 +21,9 @@
styles:styles$|async, styles:styles$|async,
selectedFeature:selectedFeature$|async selectedFeature:selectedFeature$|async
} as state"> } as state">
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible}" class="map"> <aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible}" class="map">
<div> <div>
</div> </div>
<aol-view [zoom]="state.mapState.zoom" [rotation]="state.mapState.rotation"> <aol-view [zoom]="state.mapState.zoom" [rotation]="state.mapState.rotation">
<aol-coordinate [x]="state.mapState.xCenter" [y]="state.mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate> <aol-coordinate [x]="state.mapState.xCenter" [y]="state.mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate>
@@ -36,9 +37,9 @@
<aol-layer-vector> <aol-layer-vector>
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeaturesSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector> <fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeaturesSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
</aol-layer-vector> </aol-layer-vector>
<fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> <!-- <fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> -->
<div class="control-container"> <div class="control-container">
<!-- <switch2d3d></switch2d3d>--> <router-outlet name="map-controls"></router-outlet>
<fm-map-layer-switcher></fm-map-layer-switcher> <fm-map-layer-switcher></fm-map-layer-switcher>
<fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location> <fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location>
<fm-map-rotation-reset></fm-map-rotation-reset> <fm-map-rotation-reset></fm-map-rotation-reset>
@@ -51,13 +52,13 @@
<div class="panel-top bg-secondary" *ngIf="!(state.searchMinified)"> <div class="panel-top bg-secondary" *ngIf="!(state.searchMinified)">
</div> </div>
<div class="panel-bottom"> <div class="panel-bottom">
<div *ngIf="!(state.selectedItem)"> <div *ngIf="!(state.selectedItem)">
<fm-map-feature-list-container [features]="state.features" [selectedFeature]="state.selectedFeature" [queryState]="state.queryState" [clickedFeature]="clickedFeature"></fm-map-feature-list-container> <fm-map-feature-list-container [features]="state.features" [selectedFeature]="state.selectedFeature" [queryState]="state.queryState" [clickedFeature]="clickedFeature"></fm-map-feature-list-container>
</div> </div>
<div *ngIf="state.selectedItem;let item"> <div *ngIf="state.selectedItem;let item">
<fm-map-selected-item-container [item]="item" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container> <fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container>
</div> </div>
<div *ngIf="state.features.length == 0" class="no-results m-2"> <div *ngIf="state.features.length == 0" class="no-results m-2">
<div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div> <div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div>

View File

@@ -64,6 +64,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
public selectedFeature$: Observable<Feature> = this.store.select(mapReducers.selectGetSelectedFeature); public selectedFeature$: Observable<Feature> = this.store.select(mapReducers.selectGetSelectedFeature);
public clickedFeature: Subject<Feature> = new Subject<Feature>(); public clickedFeature: Subject<Feature> = new Subject<Feature>();
public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem); public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem);
public parentItem$: Observable<IItem> =this.store.select(mapReducers.selectGetParentItem);
public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState); public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState);
public state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState); public state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState);
public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod); public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod);
@@ -82,14 +83,14 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
private lastUrl = ""; private lastUrl = "";
@ViewChild('map') map; @ViewChild('map') map;
constructor(private store: Store<mapReducers.State | commonReducers.State>, constructor(private store: Store<mapReducers.State | commonReducers.State>,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private uploadService: ResumableFileUploadService, private uploadService: ResumableFileUploadService,
private serializeService: StateSerializerService, private serializeService: StateSerializerService,
public itemTypeService: ItemTypeService, public itemTypeService: ItemTypeService,
private location: Location, private location: Location,
private geolocationService: GeolocationService, private geolocationService: GeolocationService,
private zone: NgZone, private zone: NgZone,
private deviceorientationService:DeviceOrientationService) { private deviceorientationService:DeviceOrientationService) {
@@ -98,29 +99,29 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
let newQueryState = tassign(mapReducers.initialQueryState); let newQueryState = tassign(mapReducers.initialQueryState);
console.debug(`Do Query ${setStateCount}`); console.debug(`Do Query ${setStateCount}`);
let urlparts=[]; let urlparts=[];
if (queryState.itemCode && queryState.itemCode != "") { if (queryState.itemCode && queryState.itemCode != "") {
if(queryState.itemType && queryState.itemType!= "") { if(queryState.itemType && queryState.itemType!= "") {
let itemType = this.itemTypeService.itemTypes[queryState.itemType]; let itemType = this.itemTypeService.itemTypes[queryState.itemType];
if (itemType && itemType.viewer && itemType.viewer == "edit_in_editor" && itemType.editor) { if (itemType && itemType.viewer && itemType.viewer == "edit_in_editor" && itemType.editor) {
urlparts.push('/editor'); urlparts.push('/editor');
urlparts.push(itemType.editor); urlparts.push(itemType.editor);
urlparts.push('item'); urlparts.push('item');
urlparts.push(queryState.itemCode); urlparts.push(queryState.itemCode);
} }
} }
} else { } else {
newQueryState= queryState; newQueryState= queryState;
} }
if(urlparts.length==0 ) { if(urlparts.length==0 ) {
newQueryState.itemCode = queryState.itemCode; newQueryState.itemCode = queryState.itemCode;
this.zone.run(() => { this.zone.run(() => {
this.store.dispatch(new mapActions.SetQueryState(newQueryState,false)); this.store.dispatch(new mapActions.SetQueryState(newQueryState,false));
}) })
} else { } else {
this.router.navigate(urlparts); this.router.navigate(urlparts);
} }
} }
}); });
} }
@HostListener('document:keyup', ['$event']) @HostListener('document:keyup', ['$event'])
@@ -159,7 +160,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
console.debug("Init"); console.debug("Init");
this.store.dispatch(new mapActions.Clear()); this.store.dispatch(new mapActions.Clear());
this.selectedFeatures$.next({x:0,y:0,features:[]}); this.selectedFeatures$.next({x:0,y:0,features:[]});
this.selectedFeatures$.next(null); this.selectedFeatures$.next(null);
} }
initCustomStyles() { initCustomStyles() {
@@ -203,8 +204,8 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
normalizeMapState(mapState:IMapState):IMapState { normalizeMapState(mapState:IMapState):IMapState {
if(!mapState) return null; if(!mapState) return null;
return {zoom: this.round(mapState.zoom,0), return {zoom: this.round(mapState.zoom,0),
rotation: this.round(mapState.rotation,2), rotation: this.round(mapState.rotation,2),
xCenter: this.round(mapState.xCenter,5), xCenter: this.round(mapState.xCenter,5),
yCenter: this.round(mapState.yCenter,5), yCenter: this.round(mapState.yCenter,5),
baseLayerCode: mapState.baseLayerCode }; baseLayerCode: mapState.baseLayerCode };
@@ -219,7 +220,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
let queryState = params.get("queryState"); let queryState = params.get("queryState");
var newQueryState = tassign(mapReducers.initialQueryState); var newQueryState = tassign(mapReducers.initialQueryState);
if (queryState != "") { if (queryState != "") {
newQueryState = this.serializeService.deserialize(queryState); newQueryState = this.serializeService.deserialize(queryState);
} }
return newQueryState; return newQueryState;
} else { } else {
@@ -227,16 +228,16 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
} }
} }
ngAfterViewInit() { ngAfterViewInit() {
console.debug("View init"); console.debug("View init");
this.initCustomStyles(); this.initCustomStyles();
// url to state // url to state
this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => { this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => {
var newMapState: IMapState = state.mapState; var newMapState: IMapState = state.mapState;
var newQueryState: IQueryState = state.queryState; var newQueryState: IQueryState = state.queryState;
var queryStateChanged = false; var queryStateChanged = false;
var mapStateChanged = false; var mapStateChanged = false;
let urlMapState = this.getMapStateFromUrl(params); let urlMapState = this.getMapStateFromUrl(params);
@@ -250,7 +251,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
newQueryState = urlQueryState; newQueryState = urlQueryState;
queryStateChanged = this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState); queryStateChanged = this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState);
} }
if(queryStateChanged && mapStateChanged && state.setStateCount ==0) { if(queryStateChanged && mapStateChanged && state.setStateCount ==0) {
return of(new mapActions.SetState(newMapState,newQueryState)); return of(new mapActions.SetState(newMapState,newQueryState));
window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(newMapState)); window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(newMapState));
@@ -258,16 +259,16 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
return of(new mapActions.SetQueryState(newQueryState)); return of(new mapActions.SetQueryState(newQueryState));
} return of(new mapActions.SetReplaceUrl(true)); } return of(new mapActions.SetReplaceUrl(true));
})).subscribe((action) => { })).subscribe((action) => {
if(action) { if(action) {
this.zone.run(() => { this.zone.run(() => {
console.debug("Url to state"); console.debug("Url to state");
this.store.dispatch(action); this.store.dispatch(action);
}); });
} }
}); });
// state to url // state to url
this.stateSub = this.state$.pipe(switchMap((state) => { this.stateSub = this.state$.pipe(switchMap((state) => {
let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState); let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState);
if(this.lastUrl!=newUrl && state.setStateCount>0) { if(this.lastUrl!=newUrl && state.setStateCount>0) {
@@ -276,13 +277,13 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
} }
else { else {
return of(null); return of(null);
} }
})).subscribe((newUrlState) =>{ })).subscribe((newUrlState) =>{
if(newUrlState) { if(newUrlState) {
console.debug(`State to url ${newUrlState.setStateCount}`); console.debug(`State to url ${newUrlState.setStateCount}`);
this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl); this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl);
} }
}); });
setTimeout(() => { setTimeout(() => {
this.map.instance.updateSize(); this.map.instance.updateSize();
@@ -315,7 +316,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
event.preventDefault(); event.preventDefault();
var queryState = tassign(mapReducers.initialQueryState, query); var queryState = tassign(mapReducers.initialQueryState, query);
this.store.dispatch(new mapActions.DoQuery(queryState)); this.store.dispatch(new mapActions.DoQuery(queryState));
} }
replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) { replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) {
let parts =["."]; let parts =["."];
@@ -328,7 +329,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
parts.push( this.serializeService.serialize(queryState)); parts.push( this.serializeService.serialize(queryState));
console.debug("Replace url",parts); console.debug("Replace url",parts);
this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent }); this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent });
} }
} }
handleOnMoveEnd(event) { handleOnMoveEnd(event) {
@@ -350,13 +351,13 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
this.store.dispatch(new mapActions.SetViewExtent(state.extent)); this.store.dispatch(new mapActions.SetViewExtent(state.extent));
} }
}); });
}); });
} }
handleOnMouseDown(event: MouseEvent) { handleOnMouseDown(event: MouseEvent) {
event.stopPropagation(); event.stopPropagation();
this.zone.run(() =>{ this.zone.run(() =>{
this.store.dispatch(new mapActions.CollapseSearch()); this.store.dispatch(new commonActions.CloseAll());
}); });
} }
@@ -366,7 +367,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
handleClearSearch(event) { handleClearSearch(event) {
this.store.dispatch(new commonActions.Escape(true, false)); this.store.dispatch(new commonActions.Escape(true, false));
} }
handleOnDelete(itemLayer: IItemLayer) { handleOnDelete(itemLayer: IItemLayer) {
this.store.dispatch(new mapActions.RemoveLayer(itemLayer)); this.store.dispatch(new mapActions.RemoveLayer(itemLayer));
@@ -385,7 +386,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
extend(extent, itemLayer.layer.getExtent()); extend(extent, itemLayer.layer.getExtent());
if (extent) { if (extent) {
this.store.dispatch(new mapActions.SetExtent(extent)); this.store.dispatch(new mapActions.SetExtent(extent));
} }
} }
handleSelectBaseLayer(itemLayer: IItemLayer) { handleSelectBaseLayer(itemLayer: IItemLayer) {
@@ -400,7 +401,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
if (this.paramSub) this.paramSub.unsubscribe(); if (this.paramSub) this.paramSub.unsubscribe();
if (this.itemTypeSub) this.itemTypeSub.unsubscribe(); if (this.itemTypeSub) this.itemTypeSub.unsubscribe();
if (this.stateSub) this.stateSub.unsubscribe(); if (this.stateSub) this.stateSub.unsubscribe();
if (this.queryStateSub) this.queryStateSub.unsubscribe(); if (this.queryStateSub) this.queryStateSub.unsubscribe();
if (this.querySub) this.querySub.unsubscribe(); if (this.querySub) this.querySub.unsubscribe();
} }
} }

View File

@@ -1,20 +1,25 @@
.custom-day { .custom-day {
text-align: center; text-align: center;
padding: 0.185rem 0.25rem; padding: 0.185rem 0.25rem;
display: inline-block; display: inline-block;
height: 2rem; height: 2rem;
width: 2rem; width: 2rem;
} }
.custom-day.focused { .custom-day.focused {
background-color: #e6e6e6; background-color: #e6e6e6;
} }
.custom-day.range, .custom-day:hover { .custom-day.range, .custom-day:hover {
background-color: rgb(2, 117, 216); background-color: rgb(2, 117, 216);
color: white; color: white;
} }
.custom-day.faded { .custom-day.faded {
background-color: rgba(2, 117, 216, 0.5); background-color: rgba(2, 117, 216, 0.5);
} }
ngb-datepicker {
overflow: hidden;
max-width: calc(100vw - 3em);
}

View File

@@ -8,7 +8,7 @@ import { IItemLayer } from '../../models/item.layer';
@Component({ @Component({
selector: 'fm-map-selected-item-container', selector: 'fm-map-selected-item-container',
templateUrl: './selected-item-container.component.html', templateUrl: './selected-item-container.component.html',
styleUrls: ['./selected-item-container.component.scss'] styleUrls: ['./selected-item-container.component.scss']
}) })
export class SelectedItemContainerComponent { export class SelectedItemContainerComponent {
@@ -16,34 +16,49 @@ export class SelectedItemContainerComponent {
} }
@Input() item: IItem; @Input() item: IItem;
@Input() parentItem: IItem;
@Input() itemLayer:IItemLayer; @Input() itemLayer:IItemLayer;
@Input() overlayLayers:Array<IItemLayer>; @Input() overlayLayers:Array<IItemLayer>;
@ViewChild(WidgetHostDirective, { static: true }) widgetHost: WidgetHostDirective; @ViewChild(WidgetHostDirective, { static: true }) widgetHost: WidgetHostDirective;
loadComponent() { loadComponent() {
let componentFactory: ComponentFactory<AbstractSelectedItemComponent> = this.componentFactoryResolver.resolveComponentFactory(SelectedItemComponent); // default let componentFactory: ComponentFactory<AbstractSelectedItemComponent> = this.componentFactoryResolver.resolveComponentFactory(SelectedItemComponent); // default
let firstComponentWithTypeAndTask = this.selectedItemComponents
.find(value => value['forSourceTask'] == this.item.sourceTask &&
value['forItemType'] == this.item.itemType
);
if (firstComponentWithTypeAndTask) { let selected = -1;
componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithTypeAndTask['constructor'] as any); let maxMatches =0;
} else { let showItem = true;
let firstComponentWithType = this.selectedItemComponents for (let i = 0; i < this.selectedItemComponents.length; i++) {
.find(value => value['forSourceTask'] == null && let matches=0;
value['forItemType'] == this.item.itemType); let criteria=0;
if (this.selectedItemComponents[i]['forItemType'] ) {
criteria++;
if(this.selectedItemComponents[i]['forItemType'].indexOf(this.item.itemType) >= 0) {
matches++;
}
}
if (this.selectedItemComponents[i]['forSourceTask']) {
criteria++;
if( this.selectedItemComponents[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0) {
matches++;
}
}
if (firstComponentWithType) { if(criteria==matches && matches > maxMatches) {
componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithType['constructor'] as any); selected=i;
maxMatches = matches;
} }
} }
if (selected >= 0) {
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.selectedItemComponents[selected]['constructor'] as any);
}
const viewContainerRef = this.widgetHost.viewContainerRef; const viewContainerRef = this.widgetHost.viewContainerRef;
viewContainerRef.clear(); viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent(componentFactory); const componentRef = viewContainerRef.createComponent(componentFactory);
(<AbstractSelectedItemComponent>componentRef.instance).item = this.item; (<AbstractSelectedItemComponent>componentRef.instance).item = this.item;
(<AbstractSelectedItemComponent>componentRef.instance).parentItem = this.parentItem;
(<AbstractSelectedItemComponent>componentRef.instance).itemLayer = this.itemLayer; (<AbstractSelectedItemComponent>componentRef.instance).itemLayer = this.itemLayer;
(<AbstractSelectedItemComponent>componentRef.instance).overlayLayers = this.overlayLayers; (<AbstractSelectedItemComponent>componentRef.instance).overlayLayers = this.overlayLayers;
} }

View File

@@ -7,9 +7,10 @@ import { commonReducers, ItemTypeService, IItem, Item, ItemService, FolderServic
import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router'; import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router';
import { ForItemType } from '../for-item/for-itemtype.decorator'; import { ForItemType } from '../for-item/for-itemtype.decorator';
import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component';
import { Observable } from 'rxjs'; import { Observable,of } from 'rxjs';
import {GeoJSON} from 'ol/format'; import {GeoJSON} from 'ol/format';
import {getArea} from 'ol/sphere'; import {getArea} from 'ol/sphere';
import { withLatestFrom,switchMap,combineLatest } from 'rxjs/operators';
@ForItemType("vnd.farmmaps.itemtype.cropfield") @ForItemType("vnd.farmmaps.itemtype.cropfield")
@@ -38,6 +39,25 @@ export class SelectedItemCropfieldComponent extends AbstractSelectedItemComponen
} }
ngOnInit() { ngOnInit() {
this. items = this.folderService$.getItems(this.item.code, 0, 1000); var childItems = this.folderService$.getItems(this.item.code, 0, 1000);
var atLocationItems = this.itemService$.getItemList(null,null,null,this.item.code,true);
this.items = childItems.pipe(
combineLatest(atLocationItems),
switchMap(([ci,ali]) => {
let retVal:IListItem[] = [];
let codes = {};
ci.forEach((listItem) => {
retVal.push(listItem);
codes[listItem.code]=listItem;
});
ali.forEach((atlocationitem) => {
let listItem = atlocationitem as IListItem;
let allowedItemTypes = "vnd.farmmaps.itemtype.device.senml"; // this is a hack
if(!codes[listItem.code] && allowedItemTypes.indexOf(listItem.itemType) >= 0) {
retVal.push(listItem);
}
});
return of(retVal);
}));
} }
} }

View File

@@ -4,6 +4,7 @@
<div class="card-body"> <div class="card-body">
<div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div>
<div class="card menu-card"> <div class="card menu-card">
<h2 *ngIf="parentItem">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1> <h1>{{item.name}}</h1>
</div> </div>
<div class="legend-container" *ngIf="item?.data.layers;let layers"> <div class="legend-container" *ngIf="item?.data.layers;let layers">
@@ -13,14 +14,15 @@
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option> <option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select> </select>
</div> </div>
<fm-map-layer-legend [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> <fm-map-layer-legend [showTitle]="layers.length == 1"
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div> </div>
</div> </div>
<div class="card menu-card"> <div class="card menu-card">
<ul class="p-0 mt-2"> <ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li> <li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
<ng-container *ngIf="itemTypeService.isLayer(item)"> <ng-container *ngIf="itemTypeService.isLayer(item)">
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li> <li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li> <li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container> </ng-container>
</ul> </ul>

View File

@@ -4,6 +4,7 @@
<div class="card-body"> <div class="card-body">
<div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div>
<div class="card menu-card"> <div class="card menu-card">
<h2 *ngIf="parentItem">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1> <h1>{{item.name}}</h1>
</div> </div>
<div class="legend-container" *ngIf="item?.data.layers;let layers"> <div class="legend-container" *ngIf="item?.data.layers;let layers">
@@ -20,7 +21,7 @@
<ul class="p-0 mt-2"> <ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)" ><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li> <li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)" ><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
<ng-container *ngIf="itemTypeService.isLayer(item)"> <ng-container *ngIf="itemTypeService.isLayer(item)">
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" ><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li> <li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" ><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)" ><i class="fa fa-eye" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li> <li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)" ><i class="fa fa-eye" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container> </ng-container>
</ul> </ul>

View File

@@ -4,6 +4,7 @@
<div class="card-body"> <div class="card-body">
<div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div>
<div class="card menu-card"> <div class="card menu-card">
<h2 *ngIf="parentItem">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1> <h1>{{item.name}}</h1>
</div> </div>
<div class="legend-container" *ngIf="item?.data.layers;let layers"> <div class="legend-container" *ngIf="item?.data.layers;let layers">
@@ -28,13 +29,17 @@
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option> <option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select> </select>
</div> </div>
<fm-map-layer-legend [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> <fm-map-layer-legend [showTitle]="layers.length == 1"
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div> </div>
</div> </div>
<div class="card menu-card"> <div class="card menu-card">
<ul class="p-0 mt-2"> <ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li> <li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
<li *ngIf="itemTypeService.isLayer(item)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" class="mt-1 mr-1"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li> <ng-container *ngIf="itemTypeService.isLayer(item)">
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
<li><a href="#" (click)="handleGoToChart(item)"><i class="fa fa-line-chart" aria-hidden="true" i18m-title title="Show chart"></i>&nbsp;<span i18n>Show chart</span></a></li> <li><a href="#" (click)="handleGoToChart(item)"><i class="fa fa-line-chart" aria-hidden="true" i18m-title title="Show chart"></i>&nbsp;<span i18n>Show chart</span></a></li>
</ul> </ul>
</div> </div>

View File

@@ -35,7 +35,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
nextDate():Date { nextDate():Date {
let temporalItemLayer = this.itemLayer as ITemporalItemLayer; let temporalItemLayer = this.itemLayer as ITemporalItemLayer;
if(temporalItemLayer.nextItemLayer.item) if(temporalItemLayer.nextItemLayer.item)
return temporalItemLayer.nextItemLayer.item.dataDate; return new Date(Date.parse(temporalItemLayer.nextItemLayer.item.dataDate)) ;
return null; return null;
} }
@@ -47,7 +47,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
previousDate():Date { previousDate():Date {
let temporalItemLayer = this.itemLayer as ITemporalItemLayer; let temporalItemLayer = this.itemLayer as ITemporalItemLayer;
if(temporalItemLayer.previousItemLayer.item) if(temporalItemLayer.previousItemLayer.item)
return temporalItemLayer.previousItemLayer.item.dataDate; return new Date(Date.parse(temporalItemLayer.previousItemLayer.item.dataDate));
return null; return null;
} }
@@ -74,7 +74,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
} }
handleGoToChart(item: IItem) { handleGoToChart(item: IItem) {
this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, item.dataDate.getUTCFullYear()]); this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, new Date(Date.parse(item.dataDate)).getUTCFullYear()]);
return false; return false;
} }
} }

View File

@@ -1,6 +1,6 @@
<div *ngIf="item;let item"> <div *ngIf="item;let item">
<div class="card border-0"> <div class="card border-0">
<img *ngIf="item.thumbnail" class="card-img-top" [src]="config.getConfig('apiEndPoint') +'/api/v1/items/'+item.code+'/thumbnail?v='+item.updated.getTime()" /> <img *ngIf="item.thumbnail" class="card-img-top" [src]="getThumbnailUrl(item)" />
<div *ngIf="!item.thumbnail" class="big-icon" [style.background-color]="itemTypeService.getColor(item.itemType)"> <div *ngIf="!item.thumbnail" class="big-icon" [style.background-color]="itemTypeService.getColor(item.itemType)">
<i [ngClass]="itemTypeService.getIcon(item.itemType)"></i> <i [ngClass]="itemTypeService.getIcon(item.itemType)"></i>
</div> </div>

View File

@@ -11,7 +11,8 @@ import { IItemLayer } from '../../models/item.layer';
@Injectable() @Injectable()
@Directive() @Directive()
export abstract class AbstractSelectedItemComponent { export abstract class AbstractSelectedItemComponent {
@Input() item: IItem @Input() item: IItem;
@Input() parentItem: IItem;
@Input() itemLayer: IItemLayer; @Input() itemLayer: IItemLayer;
@Input() overlayLayers: Array<IItemLayer>; @Input() overlayLayers: Array<IItemLayer>;
constructor(public store: Store<mapReducers.State | commonReducers.State>, public itemTypeService: ItemTypeService, private location: Location, public router: Router) { constructor(public store: Store<mapReducers.State | commonReducers.State>, public itemTypeService: ItemTypeService, private location: Location, public router: Router) {
@@ -60,6 +61,7 @@ export abstract class AbstractSelectedItemComponent {
event.preventDefault(); event.preventDefault();
this.location.back(); this.location.back();
} }
} }
@Injectable() @Injectable()
@@ -73,4 +75,8 @@ export class SelectedItemComponent extends AbstractSelectedItemComponent {
constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, public config:AppConfig) { constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, public config:AppConfig) {
super(store, itemTypeService,location,router); super(store, itemTypeService,location,router);
} }
getThumbnailUrl(item:IItem):string {
return this.config.getConfig('apiEndPoint') +'/api/v1/items/'+item.code+'/thumbnail?v=' + Date.parse(item.updated);
}
} }

View File

@@ -60,7 +60,7 @@ export class MapEffects {
anchor: [0.5, 1], anchor: [0.5, 1],
scale: 0.05, scale: 0.05,
src: this.featureIconService$.getIconImageDataUrl("fa fa-file-o") src: this.featureIconService$.getIconImageDataUrl("fa fa-file-o")
}), }),
stroke: new style.Stroke({ stroke: new style.Stroke({
color: 'red', color: 'red',
width: 1 width: 1
@@ -82,7 +82,7 @@ export class MapEffects {
fill: new style.Fill({ fill: new style.Fill({
color: 'rgba(0, 0, 255, 0.1)' color: 'rgba(0, 0, 255, 0.1)'
}) })
}))); })));
return actions; return actions;
} }
@@ -116,7 +116,7 @@ export class MapEffects {
switchMap(([action,setStateCount]) => { switchMap(([action,setStateCount]) => {
let a = action as mapActions.StartSearch; let a = action as mapActions.StartSearch;
var startDate = a.queryState.startDate; var startDate = a.queryState.startDate;
var endDate = a.queryState.endDate; var endDate = a.queryState.endDate;
var newAction:Observable<Action>; var newAction:Observable<Action>;
if (a.queryState.itemCode || a.queryState.parentCode || a.queryState.itemType || a.queryState.query || a.queryState.tags) { if (a.queryState.itemCode || a.queryState.parentCode || a.queryState.itemType || a.queryState.query || a.queryState.tags) {
newAction= this.itemService$.getFeatures(a.queryState.bbox, "EPSG:3857", a.queryState.query, a.queryState.tags, startDate, endDate, a.queryState.itemType, a.queryState.parentCode).pipe( newAction= this.itemService$.getFeatures(a.queryState.bbox, "EPSG:3857", a.queryState.query, a.queryState.tags, startDate, endDate, a.queryState.itemType, a.queryState.parentCode).pipe(
@@ -132,7 +132,7 @@ export class MapEffects {
catchError(error => of(new commonActions.Fail(error)))); catchError(error => of(new commonActions.Fail(error))));
} else { } else {
return []; return [];
} }
return newAction; return newAction;
})); }));
@@ -145,13 +145,15 @@ export class MapEffects {
let actions =[]; let actions =[];
actions.push(new commonActions.SetMenuVisible(false)); actions.push(new commonActions.SetMenuVisible(false));
let extent = createEmpty(); let extent = createEmpty();
if (!action.query.bboxFilter) { if (!action.query.bboxFilter) {
if (extent) { if (extent) {
for (let f of action.features) { for (let f of action.features) {
extend(extent, (f as Feature).getGeometry().getExtent()); extend(extent, (f as Feature).getGeometry().getExtent());
} }
} if(action.features && action.features.length >0) {
actions.push(new mapActions.SetExtent(extent)); actions.push(new mapActions.SetExtent(extent));
}
}
} }
return actions; return actions;
})); }));
@@ -173,7 +175,14 @@ export class MapEffects {
let itemCode = selectedItem ? selectedItem.code : ""; let itemCode = selectedItem ? selectedItem.code : "";
if (a.itemCode != itemCode || setStateCount == 1) { if (a.itemCode != itemCode || setStateCount == 1) {
return this.itemService$.getItem(a.itemCode).pipe( return this.itemService$.getItem(a.itemCode).pipe(
map((item: IItem) => new mapActions.SelectItemSuccess(item)), switchMap(child => {
return this.itemService$.getItem(child.parentCode)
.pipe(map(parent => {
return {child, parent};
}),catchError(() => { let parent:IItem = null;return of({child,parent})})
);
}),
map(data => new mapActions.SelectItemSuccess(data.child, data.parent)),
catchError(error => of(new commonActions.Fail(error)))) catchError(error => of(new commonActions.Fail(error))))
} else { } else {
return []; return [];
@@ -202,14 +211,14 @@ export class MapEffects {
if(action.item.itemType == "vnd.farmmaps.itemtype.temporal") { if(action.item.itemType == "vnd.farmmaps.itemtype.temporal") {
return this.itemService$.getChildItemList(action.item.code,null).pipe( return this.itemService$.getChildItemList(action.item.code,null).pipe(
map(items => new mapActions.SelectTemporalItemsSuccess( map(items => new mapActions.SelectTemporalItemsSuccess(
items.sort((a, b) => items.sort((a, b) =>
-(b.dataDate.getTime() - a.dataDate.getTime()) -(Date.parse(b.dataDate) - Date.parse(a.dataDate))
) )
)), )),
catchError(error => of(new commonActions.Fail(error)))); catchError(error => of(new commonActions.Fail(error))));
} else { } else {
return []; return [];
} }
} }
)); ));
@@ -231,9 +240,9 @@ export class MapEffects {
feature = f; feature = f;
break; break;
} }
} }
if (feature) { if (feature) {
return of(new mapActions.UpdateFeatureSuccess(this.toPointFeature(deviceUpdateEventAction))); return of(new mapActions.UpdateFeatureSuccess(this.toPointFeature(deviceUpdateEventAction)));
} else { } else {
return []; return [];
} }
@@ -244,10 +253,17 @@ export class MapEffects {
ofType(commonActions.ITEMCHANGEDEVENT), ofType(commonActions.ITEMCHANGEDEVENT),
withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)), withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)),
mergeMap(([action, selectedItem]) => { mergeMap(([action, selectedItem]) => {
let itemChangedAction = action as commonActions.ItemChangedEvent; let itemChangedAction = action as commonActions.ItemChangedEvent;
if (selectedItem && selectedItem.code == itemChangedAction.itemCode) { if (selectedItem && selectedItem.code == itemChangedAction.itemCode) {
return this.itemService$.getItem(itemChangedAction.itemCode).pipe( return this.itemService$.getItem(itemChangedAction.itemCode).pipe(
map((item: IItem) => new mapActions.SelectItemSuccess(item)), switchMap(child => {
return this.itemService$.getItem(child.parentCode)
.pipe(map(parent => {
return {child, parent};
})
);
}),
map(data => new mapActions.SelectItemSuccess(data.child, data.parent)),
catchError(error => of(new commonActions.Fail(error)))); catchError(error => of(new commonActions.Fail(error))));
} else { } else {
return []; return [];
@@ -256,17 +272,17 @@ export class MapEffects {
getActionFromQueryState(queryState:IQueryState, inSearch:boolean):Observable<Action>|[] { getActionFromQueryState(queryState:IQueryState, inSearch:boolean):Observable<Action>|[] {
if(!inSearch && (queryState.itemType || queryState.parentCode || queryState.itemCode || queryState.query || queryState.tags)) { if(!inSearch && (queryState.itemType || queryState.parentCode || queryState.itemCode || queryState.query || queryState.tags)) {
var newAction:Action; var newAction:Action;
if (queryState.itemCode && queryState.itemCode != "") { if (queryState.itemCode && queryState.itemCode != "") {
newAction= new mapActions.SelectItem(queryState.itemCode); newAction= new mapActions.SelectItem(queryState.itemCode);
} else { } else {
newAction= new mapActions.StartSearch(queryState); newAction= new mapActions.StartSearch(queryState);
} }
return of(newAction); return of(newAction);
} else { } else {
return of(new commonActions.Escape(true,false)); return of(new commonActions.Escape(true,false));
} }
} }
@Effect() @Effect()
setQueryState$: Observable<Action> = this.actions$.pipe( setQueryState$: Observable<Action> = this.actions$.pipe(

View File

@@ -1,3 +1,5 @@
import { IDataLayer } from '@farmmaps/common';
export interface IColor { export interface IColor {
red: number, red: number,
green: number, green: number,
@@ -26,6 +28,25 @@ export interface IHistogram {
confidence?: number; confidence?: number;
} }
export interface IStatistics {
min: number;
max: number;
mean: number;
stddev: number;
populationCount: number;
minPlus: number;
maxMinus: number;
mode: number;
median: number;
curtosis: number;
skewness: number;
variance: number;
variationCoefficient: number;
confidenceIntervalErrorMargin: number;
confidenceIntervalLow: number;
confidenceIntervalHigh: number;
}
export interface IGradientstop { export interface IGradientstop {
relativestop: number, relativestop: number,
color: IColor color: IColor
@@ -39,7 +60,8 @@ export interface IColorMap {
} }
export interface IBand { export interface IBand {
histogram: IHistogram histogram: IHistogram;
statistics: IStatistics;
} }
@@ -64,11 +86,9 @@ export interface IRenderoutputImage {
extent: [number,number,number,number] extent: [number,number,number,number]
} }
export interface ILayer { export interface ILayer extends IDataLayer {
name: string,
unit: string,
index: number,
scale: number,
renderer: IRenderer, renderer: IRenderer,
rendering: IRenderoutput rendering: IRenderoutput,
minzoom: number,
maxzoom: number
} }

View File

@@ -44,9 +44,10 @@ export interface State {
panelCollapsed: boolean, panelCollapsed: boolean,
selectedFeature: Feature, selectedFeature: Feature,
selectedItem:IItem, selectedItem:IItem,
parentItem:IItem,
clearEnabled: boolean, clearEnabled: boolean,
searchCollapsed: boolean, searchCollapsed: boolean,
searchMinified: boolean, searchMinified: boolean,
extent: number[], extent: number[],
baseLayers: Array<IItemLayer> baseLayers: Array<IItemLayer>
overlayLayers: Array<IItemLayer>, overlayLayers: Array<IItemLayer>,
@@ -81,7 +82,8 @@ export const initialState: State = {
panelVisible: false, panelVisible: false,
panelCollapsed: false, panelCollapsed: false,
selectedFeature: null, selectedFeature: null,
selectedItem: null, selectedItem: null,
parentItem: null,
clearEnabled: false, clearEnabled: false,
searchCollapsed: true, searchCollapsed: true,
searchMinified:false, searchMinified:false,
@@ -99,7 +101,7 @@ export const initialState: State = {
replaceUrl:true replaceUrl:true
} }
export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State { export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State {
switch (action.type) { switch (action.type) {
case ROUTER_NAVIGATION: { case ROUTER_NAVIGATION: {
let a = action as RouterNavigationAction; let a = action as RouterNavigationAction;
@@ -125,13 +127,13 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} }
case mapActions.SETPARENT: { case mapActions.SETPARENT: {
let a = action as mapActions.SetParent; let a = action as mapActions.SetParent;
return tassign(state, { return tassign(state, {
parentCode : a.parentCode parentCode : a.parentCode
}); });
} }
case mapActions.STARTSEARCHSUCCESS: { case mapActions.STARTSEARCHSUCCESS: {
let a = action as mapActions.StartSearchSuccess; let a = action as mapActions.StartSearchSuccess;
return tassign(state, { return tassign(state, {
features: a.features, features: a.features,
inSearch:false inSearch:false
}); });
@@ -141,8 +143,8 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
return tassign(state, { return tassign(state, {
selectedFeature: state.selectedItem?state.selectedFeature: a.feature selectedFeature: state.selectedItem?state.selectedFeature: a.feature
}); });
} }
case mapActions.SELECTITEM: { case mapActions.SELECTITEM: {
let a = action as mapActions.SelectItem; let a = action as mapActions.SelectItem;
let itemCode = state.selectedItem ? state.selectedItem.code : ""; let itemCode = state.selectedItem ? state.selectedItem.code : "";
let inSearch = (a.itemCode != itemCode || state.setStateCount == 1) let inSearch = (a.itemCode != itemCode || state.setStateCount == 1)
@@ -160,11 +162,12 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
itemLayer = new ItemLayer(a.item); itemLayer = new ItemLayer(a.item);
itemLayer.layerIndex = a.item.data.layers?a.item.data.layers[0].index:-1; itemLayer.layerIndex = a.item.data.layers?a.item.data.layers[0].index:-1;
} else if (a.item && a.item.itemType == "vnd.farmmaps.itemtype.temporal") { } else if (a.item && a.item.itemType == "vnd.farmmaps.itemtype.temporal") {
itemLayer = new TemporalItemLayer(a.item); itemLayer = new TemporalItemLayer(a.item);
} }
return tassign(state, { return tassign(state, {
inSearch:false, inSearch:false,
selectedItem: a.item, selectedItem: a.item,
parentItem: a.parentItem,
selectedItemLayer: itemLayer, selectedItemLayer: itemLayer,
panelVisible: a.item != null, panelVisible: a.item != null,
clearEnabled: a.item != null, clearEnabled: a.item != null,
@@ -181,7 +184,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
if(a.temporalItems.length>0) { if(a.temporalItems.length>0) {
let item = a.temporalItems[a.temporalItems.length-1]; let item = a.temporalItems[a.temporalItems.length-1];
layerIndex = item.data.layers[0].index; layerIndex = item.data.layers[0].index;
selectedItemLayer.selectedItemLayer = new ItemLayer(item,1,true,layerIndex); selectedItemLayer.selectedItemLayer = new ItemLayer(item,1,true,layerIndex);
} else { } else {
selectedItemLayer.selectedItemLayer = null; selectedItemLayer.selectedItemLayer = null;
} }
@@ -189,9 +192,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
selectedItemLayer.nextItemLayer = null; selectedItemLayer.nextItemLayer = null;
if(selectedItemLayer.selectedItemLayer) { if(selectedItemLayer.selectedItemLayer) {
let layerIndex = selectedItemLayer.selectedItemLayer.item.data.layers[0].index; let layerIndex = selectedItemLayer.selectedItemLayer.item.data.layers[0].index;
selectedItemLayer.layerIndex = layerIndex; selectedItemLayer.layerIndex = layerIndex;
} }
return tassign(state,{selectedItemLayer:tassign(state.selectedItemLayer,selectedItemLayer as ItemLayer)}); return tassign(state,{selectedItemLayer:tassign(state.selectedItemLayer,selectedItemLayer as ItemLayer)});
} }
case mapActions.NEXTTEMPORAL: { case mapActions.NEXTTEMPORAL: {
@@ -234,11 +237,11 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
temporalLayer.nextItemLayer.opacity=0; temporalLayer.nextItemLayer.opacity=0;
temporalLayer.nextItemLayer.layerIndex = temporalLayer.layerIndex; temporalLayer.nextItemLayer.layerIndex = temporalLayer.layerIndex;
} }
temporalLayer.selectedItemLayer = temporalLayer.previousItemLayer; temporalLayer.selectedItemLayer = temporalLayer.previousItemLayer;
if( temporalLayer.selectedItemLayer) { if( temporalLayer.selectedItemLayer) {
temporalLayer.selectedItemLayer.opacity=1; temporalLayer.selectedItemLayer.opacity=1;
temporalLayer.selectedItemLayer.layerIndex = temporalLayer.layerIndex; temporalLayer.selectedItemLayer.layerIndex = temporalLayer.layerIndex;
} }
temporalLayer.previousItemLayer = index-2 >=0? new ItemLayer(temporalLayer.temporalItems[index-2],0,true,temporalLayer.layerIndex):null; temporalLayer.previousItemLayer = index-2 >=0? new ItemLayer(temporalLayer.temporalItems[index-2],0,true,temporalLayer.layerIndex):null;
if( temporalLayer.previousItemLayer) { if( temporalLayer.previousItemLayer) {
temporalLayer.previousItemLayer.opacity=0; temporalLayer.previousItemLayer.opacity=0;
@@ -289,7 +292,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
}); });
} }
case mapActions.UPDATEFEATURESUCCESS: { case mapActions.UPDATEFEATURESUCCESS: {
let a = action as mapActions.UpdateFeatureSuccess; let a = action as mapActions.UpdateFeatureSuccess;
let features: any[] = []; let features: any[] = [];
var index = -1; var index = -1;
for (var i = 0; i < state.features.length; i++) { for (var i = 0; i < state.features.length; i++) {
@@ -307,10 +310,13 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
case mapActions.COLLAPSESEARCH: { case mapActions.COLLAPSESEARCH: {
return tassign(state, { searchCollapsed: state.panelVisible ? false: true}); return tassign(state, { searchCollapsed: state.panelVisible ? false: true});
} }
case commonActions.CLOSEALL: {
return tassign(state, { searchCollapsed: state.panelVisible ? false: true,showLayerSwitcher:false});
}
case mapActions.SETEXTENT: { case mapActions.SETEXTENT: {
let a = action as mapActions.SetExtent; let a = action as mapActions.SetExtent;
return tassign(state, { extent: a.extent }); return tassign(state, { extent: a.extent });
} }
case mapActions.ADDLAYER: { case mapActions.ADDLAYER: {
let a = action as mapActions.AddLayer; let a = action as mapActions.AddLayer;
let itemLayers = state.overlayLayers.slice(0); let itemLayers = state.overlayLayers.slice(0);
@@ -323,7 +329,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} else { } else {
return state; return state;
} }
} }
case mapActions.REMOVELAYER: { case mapActions.REMOVELAYER: {
let a = action as mapActions.RemoveLayer; let a = action as mapActions.RemoveLayer;
@@ -353,7 +359,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} }
case mapActions.SETLAYERINDEX: { case mapActions.SETLAYERINDEX: {
let a = action as mapActions.SetLayerIndex; let a = action as mapActions.SetLayerIndex;
if (a.itemLayer == null) { if (a.itemLayer == null) {
if(state.selectedItemLayer.item.itemType == "vnd.farmmaps.itemtype.temporal") { if(state.selectedItemLayer.item.itemType == "vnd.farmmaps.itemtype.temporal") {
var newItemlayer = tassign(state.selectedItemLayer,{layerIndex:a.layerIndex}); var newItemlayer = tassign(state.selectedItemLayer,{layerIndex:a.layerIndex});
let tl = newItemlayer as ITemporalItemLayer; let tl = newItemlayer as ITemporalItemLayer;
@@ -381,7 +387,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} else { } else {
var newItemlayer = new ItemLayer(state.selectedItemLayer.item); var newItemlayer = new ItemLayer(state.selectedItemLayer.item);
newItemlayer.layerIndex = a.layerIndex; newItemlayer.layerIndex = a.layerIndex;
} }
return tassign(state, { selectedItemLayer: newItemlayer}) return tassign(state, { selectedItemLayer: newItemlayer})
} else { } else {
@@ -401,9 +407,13 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
} }
var selectedBaseLayer: IItemLayer = null; var selectedBaseLayer: IItemLayer = null;
var mapState = tassign(state.mapState); var mapState = tassign(state.mapState);
let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode)[0]; let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode);
if (baseLayers.length > 0 && mapState.baseLayerCode != "" && sb) { let db = baseLayers.filter(layer => layer.item.data && layer.item.data.default === true);
selectedBaseLayer = sb; if (baseLayers.length > 0 && mapState.baseLayerCode != "" && sb.length>0) {
selectedBaseLayer = sb[0];
selectedBaseLayer.visible = true;
} else if (baseLayers.length >0 && db.length>0){
selectedBaseLayer = db[0];
selectedBaseLayer.visible = true; selectedBaseLayer.visible = true;
} else if (baseLayers.length > 0) { } else if (baseLayers.length > 0) {
selectedBaseLayer = baseLayers[0]; selectedBaseLayer = baseLayers[0];
@@ -457,7 +467,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
case mapActions.SHOWLAYERSWITCHER:{ case mapActions.SHOWLAYERSWITCHER:{
let a = action as mapActions.ShowLayerSwitcher; let a = action as mapActions.ShowLayerSwitcher;
return tassign(state,{showLayerSwitcher:a.show}); return tassign(state,{showLayerSwitcher:a.show});
} }
case mapActions.SETREPLACEURL: { case mapActions.SETREPLACEURL: {
let a= action as mapActions.SetReplaceUrl; let a= action as mapActions.SetReplaceUrl;
return tassign(state,{replaceUrl:a.replaceUrl}); return tassign(state,{replaceUrl:a.replaceUrl});
@@ -478,6 +488,7 @@ export const getPanelVisible = (state: State) => state.panelVisible;
export const getPanelCollapsed = (state: State) => state.panelCollapsed; export const getPanelCollapsed = (state: State) => state.panelCollapsed;
export const getSelectedFeature = (state: State) => state.selectedFeature; export const getSelectedFeature = (state: State) => state.selectedFeature;
export const getSelectedItem = (state: State) => state.selectedItem; export const getSelectedItem = (state: State) => state.selectedItem;
export const getParentItem = (state: State) => state.parentItem;
export const getQueryState = (state: State) => state.queryState; export const getQueryState = (state: State) => state.queryState;
export const getClearEnabled = (state: State) => state.clearEnabled; export const getClearEnabled = (state: State) => state.clearEnabled;
export const getSearchCollapsed = (state: State) => state.searchCollapsed; export const getSearchCollapsed = (state: State) => state.searchCollapsed;
@@ -505,6 +516,7 @@ export const selectGetPanelVisible = createSelector(selectMapState, getPanelVisi
export const selectGetPanelCollapsed = createSelector(selectMapState, getPanelCollapsed); export const selectGetPanelCollapsed = createSelector(selectMapState, getPanelCollapsed);
export const selectGetSelectedFeature = createSelector(selectMapState, getSelectedFeature); export const selectGetSelectedFeature = createSelector(selectMapState, getSelectedFeature);
export const selectGetSelectedItem = createSelector(selectMapState, getSelectedItem); export const selectGetSelectedItem = createSelector(selectMapState, getSelectedItem);
export const selectGetParentItem = createSelector(selectMapState, getParentItem);
export const selectGetQueryState = createSelector(selectMapState, getQueryState); export const selectGetQueryState = createSelector(selectMapState, getQueryState);
export const selectGetClearEnabled = createSelector(selectMapState, getClearEnabled); export const selectGetClearEnabled = createSelector(selectMapState, getClearEnabled);
export const selectGetSearchCollapsed = createSelector(selectMapState, getSearchCollapsed); export const selectGetSearchCollapsed = createSelector(selectMapState, getSearchCollapsed);
@@ -515,7 +527,7 @@ export const selectGetBaseLayers = createSelector(selectMapState, getBaseLayers)
export const selectGetProjection = createSelector(selectMapState, getProjection); export const selectGetProjection = createSelector(selectMapState, getProjection);
export const selectGetSelectedBaseLayer = createSelector(selectMapState, getSelectedBaseLayer); export const selectGetSelectedBaseLayer = createSelector(selectMapState, getSelectedBaseLayer);
export const selectGetSelectedOverlayLayer = createSelector(selectMapState, getSelectedOverlayLayer); export const selectGetSelectedOverlayLayer = createSelector(selectMapState, getSelectedOverlayLayer);
export const selectGetQuery = createSelector(selectMapState, getQuery); export const selectGetQuery = createSelector(selectMapState, getQuery);
export const selectGetSelectedItemLayer = createSelector(selectMapState, getSelectedItemLayer); export const selectGetSelectedItemLayer = createSelector(selectMapState, getSelectedItemLayer);
export const selectGetPeriod = createSelector(selectMapState, getPeriod); export const selectGetPeriod = createSelector(selectMapState, getPeriod);
export const selectGetStyles = createSelector(selectMapState, getStyles); export const selectGetStyles = createSelector(selectMapState, getStyles);

View File

@@ -0,0 +1,24 @@
# CommonMap3d
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.3.
## Code scaffolding
Run `ng generate component component-name --project common-map3d` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project common-map3d`.
> Note: Don't forget to add `--project common-map3d` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build common-map3d` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build common-map3d`, go to the dist folder `cd dist/common-map3d` and run `npm publish`.
## Running unit tests
Run `ng test common-map3d` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/common-map3d'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/common-map3d",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,17 @@
{
"name": "@farmmaps/common-map3d",
"version": "0.0.1",
"publishConfig": {
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
},
"peerDependencies": {
"@angular/common": "^10.1.3",
"@angular/core": "^10.1.3",
"ngx-openlayers": "1.0.0-next.16",
"cesium": "^1.73.0",
"ol-cesium": "^2.11.3"
},
"dependencies": {
"tslib": "^2.0.0"
}
}

View File

@@ -0,0 +1,11 @@
import { NgModule } from '@angular/core';
import { Switch2D3DComponent } from './components/olcs/switch2d3d/switch2d3d.component';
@NgModule({
declarations: [Switch2D3DComponent],
imports: [
],
exports: [Switch2D3DComponent]
})
export class CommonMap3dModule { }

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { CommonMap3dService } from './common-map3d.service';
describe('CommonMap3dService', () => {
let service: CommonMap3dService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(CommonMap3dService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,9 @@
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CommonMap3dService {
constructor() { }
}

View File

@@ -0,0 +1,15 @@
@import "~bootstrap/scss/bootstrap.scss";
.twotreed {
display:block;
width:2.5em;
height:2.5em;
background-color: $body-bg;
background-size: contain;
margin-top:0.5em;
text-align: center;
line-height: 2.5em;
border-radius: 1.75em;
padding: 0;
color: $secondary;
}

View File

@@ -0,0 +1,49 @@
import { Component, OnInit,Input,Host } from '@angular/core';
import { MapComponent } from 'ngx-openlayers';
import OLCesium from 'ol-cesium';
declare var olcs: any;
@Component({
selector: 'fm-map3d-switch2d3d',
template: '<div (click)="handleClick($event)" class="btn btn-outline-secondary twotreed">{{label}}</div>',
styleUrls: ['./switch2d3d.component.scss']
})
export class Switch2D3DComponent {
@Input() enable:boolean;
public label: string = "3D";
private ol3d: OLCesium;
private synchronizers:any[];
constructor(@Host() private map: MapComponent) {
}
ngOnInit() {
this.ol3d = new OLCesium({ map: this.map.instance, createSynchronizers: (map,scene) => {
this.synchronizers = [
new olcs.RasterSynchronizer(map,scene),
new olcs.VectorSynchronizer(map,scene)
];
return this.synchronizers;
}, stopOpenLayersEventsPropagation:true});
}
synchronize() {
this.synchronizers.forEach((synchronizer) => {
synchronizer.synchronize();
});
}
handleClick(event) {
this.enable = !this.enable;
if(this.enable) {
this.synchronize();
}
this.ol3d.setEnabled(this.enable);
this.label = this.enable?"2D":"3D";
}
}

View File

@@ -0,0 +1,7 @@
/*
* Public API Surface of common-map3d
*/
export * from './fm-map3d/common-map3d.service';
export * from './fm-map3d/components/olcs/switch2d3d/switch2d3d.component';
export * from './fm-map3d/common-map3d.module';

View File

@@ -0,0 +1,26 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,32 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true,
"enableIvy": false
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,10 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"enableIvy": false
}
}

View File

@@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

View File

@@ -1,35 +1,5 @@
{ {
"name": "@farmmaps/common", "name": "@farmmaps/common",
"version": "0.0.1", "version": "0.0.1",
"lockfileVersion": 1, "lockfileVersion": 1
"requires": true,
"dependencies": {
"angular-oauth2-oidc": {
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/angular-oauth2-oidc/-/angular-oauth2-oidc-8.0.4.tgz",
"integrity": "sha512-7/3niJBqD7rnElcW+SudE36g7zMWChW4gSq7NpJSzDA4aQadBgvg0hn317MfPm9tpYGrpE3G/z2NMnbzek4TMA==",
"requires": {
"jsrsasign": "^8.0.12",
"tslib": "^1.9.0"
}
},
"jsrsasign": {
"version": "8.0.12",
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz",
"integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY="
},
"ngx-uploadx": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/ngx-uploadx/-/ngx-uploadx-3.3.2.tgz",
"integrity": "sha512-gRdXXq2cRU9HE6dj65qay9GV8NRC7n8y5LtMzJWqsfu2k3CHMQxo2TqZwA9/l/PqJ76RoO7sTPy1OenFQ+krkQ==",
"requires": {
"tslib": "^1.9.0"
}
},
"tslib": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
} }

View File

@@ -3,20 +3,21 @@
"version": "0.0.1", "version": "0.0.1",
"publishConfig": { "publishConfig": {
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/" "registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
}, },
"peerDependencies": { "peerDependencies": {
"@ng-bootstrap/ng-bootstrap": "^6.0", "@ng-bootstrap/ng-bootstrap": "^7.0",
"@angular/common": "~9.1.0", "@angular/common": "~10.1.3",
"@angular/core": "~9.1.0", "@angular/core": "~10.1.3",
"@angular/forms": "~9.1.0", "@angular/forms": "~10.1.3",
"@ngrx/effects": "^9.0", "@ngrx/effects": "^10.0",
"@ngrx/router-store": "^9.0", "@ngrx/router-store": "^10.0",
"@ngrx/store": "^9.0", "@ngrx/store": "^10.0",
"tassign": "^1.0.0", "tassign": "^1.0.0",
"bootstrap": "^4.4.1", "bootstrap": "^4.4.1",
"@aspnet/signalr": "^1.1.4", "@microsoft/signalr": "^3.1.3",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"ngx-uploadx": "^3.3.4", "ngx-uploadx": "^3.3.4",
"angular-oauth2-oidc": "^9.1" "angular-oauth2-oidc": "^10.0.3",
"moment": "^2.27.0"
} }
} }

View File

@@ -4,6 +4,7 @@ import { IItemTypes } from '../models/item.types';
import { IListItem } from '../models/list.item'; import { IListItem } from '../models/list.item';
import { IUser } from '../models/user'; import { IUser } from '../models/user';
import { IItem } from '../models/item'; import { IItem } from '../models/item';
import { UserInfo } from 'angular-oauth2-oidc';
export const INITUSER = '[AppCommon] InitUser'; export const INITUSER = '[AppCommon] InitUser';
export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess'; export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess';
@@ -11,13 +12,18 @@ export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess';
export const INITUSERPACKAGES = '[AppCommon] InitUserPackages'; export const INITUSERPACKAGES = '[AppCommon] InitUserPackages';
export const INITUSERPACKAGESSUCCESS = '[AppCommon] InitUserPackagesSuccess'; export const INITUSERPACKAGESSUCCESS = '[AppCommon] InitUserPackagesSuccess';
export const INITUSERSETTINGSROOT = '[AppCommon] InitUserSettingsRoot';
export const INITUSERSETTINGSROOTSUCCESS = '[AppCommon] InitUserSettingsRootSuccess';
export const INITROOT = '[Explorer] InitRoot'; export const INITROOT = '[Explorer] InitRoot';
export const INITROOTSUCCESS = '[Explorer] InitRootSuccess'; export const INITROOTSUCCESS = '[Explorer] InitRootSuccess';
export const OPENMODAL = '[AppCommon] OpenModal'; export const OPENMODAL = '[AppCommon] OpenModal';
export const CLOSEMODAL = '[AppCommon] CloseModal'; export const CLOSEMODAL = '[AppCommon] CloseModal';
export const LOGIN = '[AppCommon] Login'; export const LOGIN = '[AppCommon] Login';
export const LOGOUT = '[AppCommon] Logout';
export const ESCAPE = '[AppCommon] Escape'; export const ESCAPE = '[AppCommon] Escape';
export const CLOSEALL = '[AppCommon] CloseAll';
export const LOADITEMTYPES = '[AppCommon] LoadItemTypes'; export const LOADITEMTYPES = '[AppCommon] LoadItemTypes';
export const LOADITEMTYPESSUCCESS = '[AppCommon] LoadItemTypesSuccess'; export const LOADITEMTYPESSUCCESS = '[AppCommon] LoadItemTypesSuccess';
@@ -28,7 +34,8 @@ export const ITEMDELETEDEVENT = '[AppCommon] ItemDeletedEvent';
export const TASKSTARTEVENT = '[AppCommon] TaskStartEvent'; export const TASKSTARTEVENT = '[AppCommon] TaskStartEvent';
export const TASKENDEVENT = '[AppCommon] TaskEndEvent'; export const TASKENDEVENT = '[AppCommon] TaskEndEvent';
export const TASKERRORTEVENT = '[AppCommon] TaskErrorEvent'; export const TASKERROREVENT = '[AppCommon] TaskErrorEvent';
export const TASKPROGRESSEVENT = '[AppCommon] TaskProgressEvent';
export const DEVICEUPDATEEVENT = '[AppCommon] DeviceUpdateEvent'; export const DEVICEUPDATEEVENT = '[AppCommon] DeviceUpdateEvent';
@@ -51,8 +58,14 @@ export const UPLOADEDFILECLICK = '[AppCommon] UploadedFileClick';
export const TOGGLEMENU = '[AppCommon] ToggleMenu'; export const TOGGLEMENU = '[AppCommon] ToggleMenu';
export const TOGGLEACCOUNTMENU = '[AppCommon] ToggleAccountMenu';
export const SETMENUVISIBLE = '[AppCommon] SetMenuVisible'; export const SETMENUVISIBLE = '[AppCommon] SetMenuVisible';
export const ONLINE = '[AppCommon] Online';
export const OFFLINE = '[AppCommon] Offline';
export class InitUser implements Action { export class InitUser implements Action {
readonly type = INITUSER; readonly type = INITUSER;
@@ -62,7 +75,13 @@ export class InitUser implements Action {
export class InitUserSuccess implements Action { export class InitUserSuccess implements Action {
readonly type = INITUSERSUCCESS; readonly type = INITUSERSUCCESS;
constructor(public user:IUser ) { } constructor(public user:IUser,public userinfo:UserInfo ) { }
}
export class InitUserPackages implements Action {
readonly type = INITUSERPACKAGES;
constructor() {}
} }
export class InitUserPackagesSuccess implements Action { export class InitUserPackagesSuccess implements Action {
@@ -71,6 +90,18 @@ export class InitUserPackagesSuccess implements Action {
constructor(public items:IItem[] ) { } constructor(public items:IItem[] ) { }
} }
export class InitUserSettingsRoot implements Action {
readonly type = INITUSERSETTINGSROOT;
constructor() {}
}
export class InitUserSettingsRootSuccess implements Action {
readonly type = INITUSERSETTINGSROOTSUCCESS;
constructor(public item: IItem ) { }
}
export class InitRoot implements Action { export class InitRoot implements Action {
readonly type = INITROOT; readonly type = INITROOT;
@@ -95,6 +126,12 @@ export class CloseModal implements Action {
constructor() { } constructor() { }
} }
export class CloseAll implements Action {
readonly type = CLOSEALL;
constructor() { }
}
export class StartRouteLoading implements Action { export class StartRouteLoading implements Action {
readonly type = STARTROUTELOADING; readonly type = STARTROUTELOADING;
@@ -113,6 +150,12 @@ export class Login implements Action {
constructor(public url: string) { } constructor(public url: string) { }
} }
export class Logout implements Action {
readonly type = LOGOUT;
constructor() { }
}
export class Escape implements Action { export class Escape implements Action {
readonly type = ESCAPE; readonly type = ESCAPE;
@@ -168,7 +211,13 @@ export class TaskEndEvent implements Action {
} }
export class TaskErrorEvent implements Action { export class TaskErrorEvent implements Action {
readonly type = TASKERRORTEVENT; readonly type = TASKERROREVENT;
constructor(public itemCode: string, public attributes: any) { }
}
export class TaskProgressEvent implements Action {
readonly type = TASKPROGRESSEVENT;
constructor(public itemCode: string, public attributes: any) { } constructor(public itemCode: string, public attributes: any) { }
} }
@@ -226,11 +275,29 @@ export class ToggleMenu implements Action {
constructor() { } constructor() { }
} }
export class ToggleAccountMenu implements Action {
readonly type = TOGGLEACCOUNTMENU;
constructor() { }
}
export class SetMenuVisible implements Action { export class SetMenuVisible implements Action {
readonly type = SETMENUVISIBLE; readonly type = SETMENUVISIBLE;
constructor(public visible:boolean) { } constructor(public visible:boolean) { }
} }
export class Online implements Action {
readonly type = ONLINE;
constructor() { }
}
export class Offline implements Action {
readonly type = OFFLINE;
constructor() { }
}
export type Actions = OpenModal export type Actions = OpenModal
@@ -238,6 +305,7 @@ export type Actions = OpenModal
| InitRootSuccess | InitRootSuccess
| CloseModal | CloseModal
| Login | Login
| Logout
| ItemChangedEvent | ItemChangedEvent
| ItemAddedEvent | ItemAddedEvent
| ItemDeletedEvent | ItemDeletedEvent
@@ -258,8 +326,17 @@ export type Actions = OpenModal
| TaskStartEvent | TaskStartEvent
| TaskEndEvent | TaskEndEvent
| TaskErrorEvent | TaskErrorEvent
| TaskProgressEvent
| DeviceUpdateEvent | DeviceUpdateEvent
| ToggleMenu | ToggleMenu
| SetMenuVisible | SetMenuVisible
| InitUserPackagesSuccess; | InitUserPackages
| InitUserPackagesSuccess
| InitUserSettingsRoot
| InitUserSettingsRootSuccess
| ToggleAccountMenu
| CloseAll
| Online
| Offline;

View File

@@ -9,6 +9,7 @@ import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
//components //components
import { ItemTypeService } from './services/itemtype.service'; import { ItemTypeService } from './services/itemtype.service';
import { SchemaService } from './services/schema.service';
import { FolderService } from './services/folder.service'; import { FolderService } from './services/folder.service';
import { TimespanService } from './services/timespan.service'; import { TimespanService } from './services/timespan.service';
import { ItemService } from './services/item.service'; import { ItemService } from './services/item.service';
@@ -28,6 +29,9 @@ import { ResumableFileUploadService } from './components/resumable-file-upload/r
import { NgbDateNativeAdapter } from './services/date-adapter.service' import { NgbDateNativeAdapter } from './services/date-adapter.service'
import { AuthConfigFactory } from './shared/authconfigFactory'; import { AuthConfigFactory } from './shared/authconfigFactory';
import { StateSerializerService } from './services/state-serializer.service'; import { StateSerializerService } from './services/state-serializer.service';
import { PackageService } from './services/package.service';
import { PackagePreloadStrategy } from './services/package-preload-strategy.service';
import { SenmlService } from './services/senml-service';
export { export {
FolderService, FolderService,
@@ -47,7 +51,11 @@ export {
AuthCallbackGuard, AuthCallbackGuard,
ResumableFileUploadService, ResumableFileUploadService,
NgbDateNativeAdapter, NgbDateNativeAdapter,
StateSerializerService StateSerializerService,
SchemaService,
PackageService,
SenmlService,
PackagePreloadStrategy
}; };
@NgModule({ @NgModule({
@@ -71,7 +79,7 @@ export class AppCommonServiceModule {
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,
useFactory: appConfigFactory, useFactory: appConfigFactory,
deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage,ItemTypeService], deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage, ItemTypeService],
multi: true multi: true
}, },
{ {

View File

@@ -31,6 +31,8 @@ import { TimespanComponent } from './components/timespan/timespan.component';
import { TagInputComponent } from './components/tag-input/tag-input.component'; import { TagInputComponent } from './components/tag-input/tag-input.component';
import { MenuBackgroundComponent } from './components/menu-background/menu-background.component'; import { MenuBackgroundComponent } from './components/menu-background/menu-background.component';
import { HasPackageDirective} from './components/has-package/has-package.directive'; import { HasPackageDirective} from './components/has-package/has-package.directive';
import { HasClaimDirective} from './components/has-claim/has-claim.directive';
import { UserMenuComponent} from './components/user-menu/user-menu.component';
import { Alert } from './enumerations/alert.enum'; import { Alert } from './enumerations/alert.enum';
import { IEventMessage } from './models/event.message'; import { IEventMessage } from './models/event.message';
import { IItem, Item } from './models/item'; import { IItem, Item } from './models/item';
@@ -39,15 +41,21 @@ import { IItemType } from './models/item.type';
import { IItemTypes } from './models/item.types'; import { IItemTypes } from './models/item.types';
import { IItemTask, ItemTask } from './models/itemTask'; import { IItemTask, ItemTask } from './models/itemTask';
import { IListItem } from './models/list.item'; import { IListItem } from './models/list.item';
import { ITypeaheadItem } from './models/typeahead.item'; import { ITypeaheadItem } from './models/typeahead.item'
import { IJsonline } from './models/json-line';
import { ISenMLItem } from './models/senml-item';
import { IPackage,IPackages } from './models/package'; import { IPackage,IPackages } from './models/package';
import { IUser } from './models/user'; import { IUser } from './models/user';
import { IQueryState } from './models/query.state'; import { IQueryState } from './models/query.state';
import { ICodeListItem } from './models/code.list.item'; import { ICodeListItem } from './models/code.list.item';
import { IDataLayer } from './models/data.layer';
import { IColor,IGradientstop} from './models/gradient';
import * as commonActions from './actions/app-common.actions'; import * as commonActions from './actions/app-common.actions';
import * as commonReducers from './reducers/app-common.reducer'; import * as commonReducers from './reducers/app-common.reducer';
import * as commonEffects from './effects/app-common.effects'; import * as commonEffects from './effects/app-common.effects';
import { SecureOAuthStorage} from './shared/secureOAuthStorage'; import { SecureOAuthStorage} from './shared/secureOAuthStorage';
import { GradientComponent } from './components/gradient/gradient.component';
import { GradientSelectComponent } from './components/gradient-select/gradient-select.component';
export { export {
SafePipe, SafePipe,
@@ -60,7 +68,9 @@ export {
SidePanelComponent, SidePanelComponent,
TimespanComponent, TimespanComponent,
TagInputComponent, TagInputComponent,
UserMenuComponent,
HasPackageDirective, HasPackageDirective,
HasClaimDirective,
Alert, Alert,
IEventMessage, IEventMessage,
IItem, IItem,
@@ -82,7 +92,12 @@ export {
AuthConfigFactory, AuthConfigFactory,
MenuBackgroundComponent, MenuBackgroundComponent,
SecureOAuthStorage, SecureOAuthStorage,
WeatherCurrentObservation WeatherCurrentObservation,
IJsonline,
ISenMLItem,
IDataLayer,
IColor,
IGradientstop
}; };
@NgModule({ @NgModule({
@@ -109,7 +124,11 @@ export {
TagInputComponent, TagInputComponent,
SessionClearedComponent, SessionClearedComponent,
MenuBackgroundComponent, MenuBackgroundComponent,
HasPackageDirective HasPackageDirective,
HasClaimDirective,
UserMenuComponent,
GradientComponent,
GradientSelectComponent
], ],
exports: [ exports: [
NgbModule, NgbModule,
@@ -127,7 +146,11 @@ export {
TagInputComponent, TagInputComponent,
SessionClearedComponent, SessionClearedComponent,
MenuBackgroundComponent, MenuBackgroundComponent,
HasPackageDirective HasPackageDirective,
HasClaimDirective,
UserMenuComponent,
GradientComponent,
GradientSelectComponent
] ]
}) })
export class AppCommonModule { export class AppCommonModule {

View File

@@ -23,4 +23,12 @@
<ng-container *ngIf="showUploadProgress"> <ng-container *ngIf="showUploadProgress">
<fm-resumable-file-upload></fm-resumable-file-upload> <fm-resumable-file-upload></fm-resumable-file-upload>
</ng-container> </ng-container>
<div class="user-menu">
<fm-user-menu [user]="user|async" [showMenu]="accountMenuVisible|async"></fm-user-menu>
</div>
<div class="healthstatus-container online" [ngClass]="{'online' :(isOnline|async)}">
<div class="healthstatus alert alert-danger m-0" >
<span i18n>Not connected, make sure your device has an active internet connection</span>
</div>
</div>
</div> </div>

View File

@@ -76,3 +76,33 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;}
.logo { .logo {
margin-left: 1rem; margin-left: 1rem;
} }
.user-menu {
transition: top 0.5s ease-out;
position: absolute;
top:0.25em;
right:1em;
}
.fullscreen > .user-menu {
top:1em;
}
.healthstatus-container {
width: 100%;
position: absolute;
bottom: 0px;
overflow: hidden;
transition: max-height 0.5s ease-out;
max-height:5em;
z-index: 2000;
}
.healthstatus {
width: 100%;
padding: 1em;
}
.online {
max-height:0em;
}

View File

@@ -2,13 +2,16 @@ import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactor
import { Router, NavigationStart, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router'; import { Router, NavigationStart, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router';
import { Meta, Title, MetaDefinition } from '@angular/platform-browser';import { DOCUMENT } from "@angular/common"; import { Meta, Title, MetaDefinition } from '@angular/platform-browser';import { DOCUMENT } from "@angular/common";
import { Subscription , Observable } from 'rxjs'; import { Subscription , Observable } from 'rxjs';
import { distinctUntilChanged} from 'rxjs/operators';
import { Store, Action } from '@ngrx/store'; import { Store, Action } from '@ngrx/store';
import { IUser } from '../../models/user';
//AppCommon //AppCommon
import { IEventMessage } from '../../models/event.message'; import { IEventMessage } from '../../models/event.message';
import { IListItem} from '../../models/list.item'; import { IListItem} from '../../models/list.item';
import { EventService } from '../../services/event.service'; import { EventService } from '../../services/event.service';
import * as commonActions from '../../actions/app-common.actions'; import * as commonActions from '../../actions/app-common.actions';
import { HealthCheckService } from '../../services/healthcheck.service';
import * as appReducers from '../../reducers/app-common.reducer'; import * as appReducers from '../../reducers/app-common.reducer';
@@ -32,8 +35,11 @@ export class AppComponent implements OnInit, OnDestroy {
public currentFolder: Observable<IListItem>; public currentFolder: Observable<IListItem>;
public folderParents: Observable<IListItem[]>; public folderParents: Observable<IListItem[]>;
public fullScreen: Observable<boolean>; public fullScreen: Observable<boolean>;
public isOnline: Observable<boolean>;
public routeLoading: Observable<boolean>; public routeLoading: Observable<boolean>;
public menuVisible: Observable<boolean>; public menuVisible: Observable<boolean>;
public accountMenuVisible: Observable<boolean>;
public user:Observable<IUser>;
@Input() showUploadProgress: boolean =true; @Input() showUploadProgress: boolean =true;
constructor( constructor(
@@ -43,9 +49,18 @@ export class AppComponent implements OnInit, OnDestroy {
private meta: Meta, private meta: Meta,
private store: Store<appReducers.State>, private store: Store<appReducers.State>,
private eventService: EventService, private eventService: EventService,
) { private healthCheckService: HealthCheckService
) {
//check health every 30 seconds
this.healthCheckService.check(30000).pipe(distinctUntilChanged()).subscribe((online) => {
if(online) {
this.store.dispatch(new commonActions.Online());
} else {
this.store.dispatch(new commonActions.Offline());
}
});
} }
getActionFromEvent(event: IEventMessage): Action { getActionFromEvent(event: IEventMessage): Action {
@@ -76,6 +91,10 @@ export class AppComponent implements OnInit, OnDestroy {
action = new commonActions.TaskErrorEvent(event.itemCode, event.attributes); action = new commonActions.TaskErrorEvent(event.itemCode, event.attributes);
break; break;
} }
case "taskProgress": {
action = new commonActions.TaskProgressEvent(event.itemCode, event.attributes);
break;
}
case "deviceUpdate": { case "deviceUpdate": {
action = new commonActions.DeviceUpdateEvent(event.itemCode, event.attributes); action = new commonActions.DeviceUpdateEvent(event.itemCode, event.attributes);
break; break;
@@ -86,19 +105,22 @@ export class AppComponent implements OnInit, OnDestroy {
ngOnInit() { ngOnInit() {
this.fullScreen = this.store.select(appReducers.selectGetFullScreen); this.fullScreen = this.store.select(appReducers.selectGetFullScreen);
this.isOnline = this.store.select(appReducers.SelectGetIsOnline);
this.routeLoading = this.store.select(appReducers.selectGetRouteLoading); this.routeLoading = this.store.select(appReducers.selectGetRouteLoading);
this.menuVisible = this.store.select(appReducers.SelectGetMenuVisible); this.menuVisible = this.store.select(appReducers.SelectGetMenuVisible);
this.accountMenuVisible = this.store.select(appReducers.SelectGetAccountMenuVisible);
this.user = this.store.select(appReducers.SelectGetUser);
this.InstallRouteEventHandler(); this.InstallRouteEventHandler();
this.InstallEventServiceEventHandler(); this.InstallEventServiceEventHandler();
} }
@HostListener('document:keyup', ['$event']) @HostListener('document:keyup', ['$event'])
keyUp(event: KeyboardEvent) { onKeyUp(event: KeyboardEvent) {
let x = event.keyCode; let x = event.keyCode;
if (x === 27) { if (x === 27) {
this.store.dispatch(new commonActions.Escape(true,false)); this.store.dispatch(new commonActions.Escape(true,false));
} }
} }
ngOnDestroy() { ngOnDestroy() {
// Subscription clean-up // Subscription clean-up

View File

@@ -0,0 +1,16 @@
<div *ngIf="gradientItems" class="form-control gradient-select" (click)="handleToggleList()">
<div *ngIf="selectedItem">
<div *ngIf="showLabel">{{selectedItem?.name}}</div>
<fm-gradient [gradientItem]="selectedItem"></fm-gradient>
</div>
<div class="gradient-list" [ngClass]="{'visible':listVisible}">
<ul *ngIf="gradientItems">
<li *ngFor="let item of gradientItems" (click)="handleSelect(item)" [ngClass]="{'bg-primary':isSelected(item),'text-white':isSelected(item)} ">
<div>{{item?.name}}</div>
<div><fm-gradient [gradientItem]="item"></fm-gradient></div>
</li>
</ul>
<div *ngIf="showAdd" class="addGradient"><a href="#" i18n (click)="handleAdd($event)" >Add gradient</a></div>
</div>
</div>

View File

@@ -0,0 +1,55 @@
.gradient-select {
position: relative;
width:30rem;
height: auto;
}
.gradient-list {
position: absolute;
top:100%;
left:-0.15rem;
width:100%;
display: none;
margin-left: 1px;
border-radius: 0.25rem;
border: 1px solid #ced4da;
}
.gradient-list.visible {
display: block;
background-color: white;
}
li {
display: block;
overflow: hidden;
padding: 0.375rem .75rem;
border-bottom: 1px solid #ced4da;
}
li:hover {
background-color: #ced4da;
//color:white;
}
ul {
list-style: none;
padding: 0;
overflow: hidden;
overflow-y: auto;
max-height: 20rem;
}
fm-gradient {
display: block;
height: 1.5rem;
position: relative;
border:1px solid white;
}
.addGradient {
padding: 0.375rem .75rem;
border-top: 1px solid #ced4da;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GradientSelectComponent } from './gradient-select.component';
describe('GradientSelectComponent', () => {
let component: GradientSelectComponent;
let fixture: ComponentFixture<GradientSelectComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ GradientSelectComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(GradientSelectComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,54 @@
import { Component, ChangeDetectorRef, Input,SimpleChanges,OnChanges, Output,EventEmitter } from '@angular/core';
import { IItem } from '../../models/item';
@Component({
selector: 'fm-gradient-select',
templateUrl: './gradient-select.component.html',
styleUrls: ['./gradient-select.component.scss']
})
export class GradientSelectComponent implements OnChanges {
public listVisible:boolean = false;
@Input() showLabel:boolean = true;
@Input() gradientItems:IItem[];
@Input() showAdd:boolean = false;
@Input() selectedItem:IItem;
@Output() onSelect:EventEmitter<any> = new EventEmitter<any>();
@Output() onAdd:EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();
constructor(private ref:ChangeDetectorRef) {
}
ngOnChanges(changes:SimpleChanges) {
if(changes["gradientItems"]) {
this.gradientItems=changes["gradientItems"].currentValue;
this.selectedItem=this.selectedItem == null && this.gradientItems && this.gradientItems.length>0?this.gradientItems[0]:null;
this.ref.detectChanges();
}
}
handleToggleList() {
this.listVisible=!this.listVisible;
}
handleSelect(item:IItem) {
this.selectedItem=item;
this.onSelect.emit(item);
}
handleAdd(event:MouseEvent) {
event.preventDefault();
this.onAdd.emit(event);
}
isSelected(item:IItem):boolean {
if(this.selectedItem && this.selectedItem.data && this.selectedItem.data.gradient && item && item.data && item.data.gradient) {
let sid = JSON.stringify(this.selectedItem.data.gradient)+this.selectedItem.name;
let id = JSON.stringify( item.data.gradient) + item.name;
return sid == id;
}
return false;
}
}

View File

@@ -0,0 +1 @@
<div class="gradient" [ngStyle]="gradientStyle"></div>

View File

@@ -0,0 +1,4 @@
.gradient {
height: 100%;
width: 100%;
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GradientComponent } from './gradient.component';
describe('GradientComponent', () => {
let component: GradientComponent;
let fixture: ComponentFixture<GradientComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ GradientComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(GradientComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,53 @@
import { Component, Input, OnInit ,OnChanges, SimpleChanges,ChangeDetectorRef} from '@angular/core';
import { IItem} from '../../models/item'
import { IGradientstop } from '../../models/gradient';
@Component({
selector: 'fm-gradient',
templateUrl: './gradient.component.html',
styleUrls: ['./gradient.component.scss']
})
export class GradientComponent implements OnInit,OnChanges {
@Input() gradientItem:IItem;
public gradientStyle:any = {};
constructor(private ref: ChangeDetectorRef) { }
getGradientStyle(item:IItem):any {
if(item.data && item.data.gradient) {
let gradient = item.data.gradient as IGradientstop[];
let gd = '{ "background": "linear-gradient(to right,';
for(var i=0;i<gradient.length;i++) {
let gs = gradient[i];
if(i>0) gd+=",";
gd += `rgba(${gs.color.red},${gs.color.green},${gs.color.blue},${gs.color.alpha/255})`;
gd +=` ${gs.relativestop*100}%`
}
gradient.forEach((gs) => {
});
gd+=')"}';
return JSON.parse(gd);
}
}
ngOnInit(): void {
if(this.gradientItem && this.gradientItem.itemType == "vnd.farmmaps.itemtype.gradient") {
this.gradientStyle = this.getGradientStyle(this.gradientItem);
}
}
ngOnChanges(changes: SimpleChanges) {
if(changes['gradientItem']) {
let gradientItem = changes['gradientItem'].currentValue;
if(gradientItem && gradientItem.itemType == "vnd.farmmaps.itemtype.gradient") {
this.gradientStyle = this.getGradientStyle(changes['gradientItem'].currentValue);
this.ref.markForCheck();
}
}
}
}

View File

@@ -0,0 +1,30 @@
import { Directive, ViewContainerRef,TemplateRef,OnInit,Input,OnDestroy } from '@angular/core';
import { Store} from '@ngrx/store';
import * as appCommonReducer from '../../reducers/app-common.reducer'
import { IPackages } from '../../models/package';
import { Observable, Subscription } from 'rxjs';
import { skip } from 'rxjs/operators';
import {OAuthService } from 'angular-oauth2-oidc';
import { IUser } from '../../models/user';
@Directive({
selector: '[fm-hasclaim]',
})
export class HasClaimDirective implements OnInit{
@Input('fm-hasclaim') claim:string;
constructor(private templateRef$: TemplateRef<any>,private viewContainerRef$: ViewContainerRef,private store$: Store<appCommonReducer.State>) { }
private user$:Observable<IUser> = this.store$.select(appCommonReducer.SelectGetUser);
private hasView = false;
ngOnInit() {
this.user$.subscribe((user) => {
if (user && user.claims[this.claim]) {
this.viewContainerRef$.createEmbeddedView(this.templateRef$);
this.hasView=true;
} else if (this.hasView) {
this.viewContainerRef$.clear();
this.hasView = false;
}
});
}
}

View File

@@ -7,7 +7,7 @@
bottom:0; bottom:0;
opacity: 0; opacity: 0;
background-color: #000000; background-color: #000000;
transition: opacity 0s ease-out 1s; transition: opacity 0.3s ease-out;
pointer-events: none; pointer-events: none;
z-index:99; z-index:99;
} }

View File

@@ -1,149 +1,153 @@
import { Injectable, OnDestroy } from '@angular/core'; import { Injectable, OnDestroy } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc'; import { OAuthService } from 'angular-oauth2-oidc';
import { Subject , Subscription } from 'rxjs'; import { Subject , Subscription } from 'rxjs';
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { UploadxService, UploadState,UploadxOptions} from 'ngx-uploadx'; import { UploadxService, UploadState,UploadxOptions} from 'ngx-uploadx';
import { AppConfig } from '../../shared/app.config'; import { AppConfig } from '../../shared/app.config';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class ResumableFileUploadService implements OnDestroy{ export class ResumableFileUploadService implements OnDestroy{
public files: Array<File> = new Array<File>(); public files: Array<File> = new Array<File>();
public isUploading = false; public isUploading = false;
public totalProgress = 0; public totalProgress = 0;
public isClosed = true; public isClosed = true;
public isMinimized = false; public isMinimized = false;
public parentCode: string; public parentCode: string;
public refresh: Subject<any> = new Subject<any>(); public refresh: Subject<any> = new Subject<any>();
private _eventSub:Subscription; public addedFiles: Subject<File> = new Subject<File>();
private initialized = false; private _eventSub:Subscription;
private initialized = false;
constructor(private httpClient: HttpClient,private oauthService: OAuthService,private uploadService: UploadxService,public appConfig: AppConfig) {
constructor(private httpClient: HttpClient,private oauthService: OAuthService,private uploadService: UploadxService,public appConfig: AppConfig) {
}
}
endPoint() {
return `${this.appConfig.getConfig("apiEndPoint")}/api/v1/file`; endPoint() {
} return `${this.appConfig.getConfig("apiEndPoint")}/api/v1/file`;
}
init() {
if(!this.initialized) { init() {
this._eventSub=this.uploadService.init({ if(!this.initialized) {
endpoint:this.endPoint(), this._eventSub=this.uploadService.init({
token:() => this.oauthService.getAccessToken(), endpoint:this.endPoint(),
chunkSize: 2097152}).subscribe((uploadState:UploadState) => { token:() => this.oauthService.getAccessToken(),
this.handleState(uploadState); chunkSize: 2097152}).subscribe((uploadState:UploadState) => {
} ); this.handleState(uploadState);
this.initialized=true; } );
} this.initialized=true;
} }
}
updatetotalprogress() {
var totalProgress =0; updatetotalprogress() {
var n=0; var totalProgress =0;
for(var i =0;i<this.files.length;i++) { var n=0;
if(!this.files[i].error) { for(var i =0;i<this.files.length;i++) {
totalProgress+=this.files[i].progress; if(!this.files[i].error) {
n++; totalProgress+=this.files[i].progress;
} n++;
} }
this.totalProgress=totalProgress/this.files.length; }
if(this.totalProgress==100) this.isUploading=false; this.totalProgress=totalProgress/this.files.length;
} if(this.totalProgress==100) this.isUploading=false;
}
handleState(state:UploadState) {
switch(state.status) { handleState(state:UploadState) {
case "queue": { var file =this.files.find((f) => f.identifier == state.uploadId )
this.files.push(new File(state)); if(state.status != "cancelled" && !file) {
this.isClosed=false; let file = new File(state);
};break; this.files.push(file);
case "uploading": { this.isClosed=false;
this.isUploading = true; this.addedFiles.next(file)
var file =this.files.find((f) => f.identifier == state.uploadId ) }
if(file) { switch(state.status) {
file.progress = (state.progress?state.progress:0); case "uploading": {
} if(file) {
};break; this.isUploading = true;
case "complete": { file.progress = (state.progress?state.progress:0);
var file =this.files.find((f) => f.identifier == state.uploadId ) }
if(file) { };break;
var parts = state.url.split("/"); case "complete": {
file.itemCode = parts[parts.length-1]; if(file) {
file.progress = (state.progress?state.progress:0); var parts = state.url.split("/");
file.success=true; file.itemCode = parts[parts.length-1];
} file.progress = (state.progress?state.progress:0);
};break; file.success=true;
case "error": { }
var file =this.files.find((f) => f.identifier == state.uploadId ) };break;
if(file) { case "error": {
file.error=true; if(file) {
file.errorMessage = state.response; file.error=true;
} file.errorMessage = state.response as string;
};break; }
} };break;
this.updatetotalprogress(); }
this.refresh.next({}); this.updatetotalprogress();
} this.refresh.next({});
}
addFiles = (files: any[], event: any, metadata:any) => {
for (let f of files) { addFiles = (files: any[], event: any, metadata:any) => {
var options:UploadxOptions = {metadata:metadata}; for (let f of files) {
this.uploadService.handleFile(f,options); var options:UploadxOptions = {metadata:metadata};
} this.uploadService.handleFile(f,options);
} }
}
toggleMinimize = function () {
this.isMinimized = !this.isMinimized; toggleMinimize = function () {
}; this.isMinimized = !this.isMinimized;
};
cancelFile = function (file) {
this.uploadService.control({action:'cancel',uploadId:file.identifier}); cancelFile = function (file) {
var index = this.files.indexOf(file, 0); this.uploadService.control({action:'cancel',uploadId:file.identifier});
if (index > -1) { var index = this.files.indexOf(file, 0);
this.files.splice(index, 1); if (index > -1) {
} this.files.splice(index, 1);
}; }
if(this.files.length==0) {
doClose = function () { this.isUploading = false;
this.uploadService.control({action:'cancelAll'}); }
this.files = new Array<File>(); };
this.isClosed = true;
} doClose = function () {
this.uploadService.control({action:'cancelAll'});
close = function () { this.files = new Array<File>();
let close = true; this.isClosed = true;
if (this.isUploading) { }
close = false;
} close = function () {
if (close) { let close = true;
this.doClose(); if (this.isUploading) {
} close = false;
} }
if (close) {
ngOnDestroy() { this.doClose();
if(this._eventSub) this._eventSub.unsubscribe(); }
} }
}
ngOnDestroy() {
export class File { if(this._eventSub) this._eventSub.unsubscribe();
private file: any; }
public fileName: string; }
public progress: number;
public identifier: string; export class File {
public itemCode: string; private file: any;
public success: boolean; public fileName: string;
public error: boolean; public progress: number;
public errorMessage: string; public identifier: string;
public itemCode: string;
public success: boolean;
constructor(state: UploadState) { public error: boolean;
this.file = state; public errorMessage: string;
this.fileName = state.file.name;
this.progress = state.progress?state.progress:0;
this.identifier = state.uploadId; constructor(state: UploadState) {
this.success = false; this.file = state;
this.error = false; this.fileName = state.file.name;
this.errorMessage = ""; this.progress = state.progress?state.progress:0;
this.itemCode = null; this.identifier = state.uploadId;
} this.success = false;
} this.error = false;
this.errorMessage = "";
this.itemCode = null;
}
}

View File

@@ -0,0 +1,14 @@
<div>
<div (click)="toggle($event)" class="rounded-circle menu-button hidden" [ngClass]="{'hidden':!user}">
<span *ngIf="user"><span [title]="user.name">{{getLetter()}}</span></span>
<div class="menu hidden" [ngClass]="{'hidden':!showMenu}">
<div class="card" *ngIf="user">
<div class="card-body">
<div class="username">{{user.name}}</div>
<div><a href="#" (click)="logout($event)" i18n>logout</a></div>
</div>
</div>
<router-outlet name="user-menu"></router-outlet>
</div>
</div>
</div>

View File

@@ -0,0 +1,56 @@
.menu-button {
background-color: purple;
display: inline-block;
width: 2.5em;
height: 2.5em;
line-height: 2.5em;
text-align: center;
font-size: 1rem;
position: relative;
display: inline-block;
}
div.menu-button > span {
color:white;
}
.menu {
max-height: 100vh;
//transition: max-height 0.2s;
overflow: hidden;
box-shadow: 0 0 20px rgba(0,0,0,.3);
position: absolute;
top: 3rem;
right:0;
background-color: #fff;
border-radius: 0.25rem;
padding: 0.5rem;
}
.card {
padding:0.5rem;
min-width: 10rem;
}
.username {
white-space: nowrap;
font-weight: 500;
font-size: 1.2rem;
line-height: 1.2rem;
}
.card-body {
text-align: left;
}
.hidden {
max-height: 0;
}
.menu.hidden {
padding: 0;
}
.menu-button.hidden {
overflow: hidden;
}

View File

@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UserMenuComponent } from './user-menu.component';
describe('UserMenuComponent', () => {
let component: UserMenuComponent;
let fixture: ComponentFixture<UserMenuComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ UserMenuComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UserMenuComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
import { Component, OnInit,Input } from '@angular/core';
import { OAuthService} from 'angular-oauth2-oidc'
import { IUser } from '../../models/user';
import {Store} from '@ngrx/store';
import * as appReducers from '../../reducers/app-common.reducer';
import * as appActions from '../../actions/app-common.actions';
@Component({
selector: 'fm-user-menu',
templateUrl: './user-menu.component.html',
styleUrls: ['./user-menu.component.scss']
})
export class UserMenuComponent implements OnInit {
@Input() user:IUser;
@Input() showMenu:boolean;
constructor(private oauthService:OAuthService, private store: Store<appReducers.State>) { }
ngOnInit(): void {
}
getLetter():string {
return this.user && this.user.name ? this.user.name.substr(0,1).toUpperCase():"";
}
logout(event:MouseEvent) {
event.preventDefault();
this.store.dispatch(new appActions.Logout());
}
toggle(event:MouseEvent) {
event.stopPropagation();
this.store.dispatch(new appActions.ToggleAccountMenu());
}
}

View File

@@ -1,10 +1,10 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc'; import { OAuthService,UserInfo } from 'angular-oauth2-oidc';
import { Store, Action } from '@ngrx/store'; import { Store, Action } from '@ngrx/store';
import { Effect, Actions,ofType } from '@ngrx/effects'; import { Effect, Actions,ofType } from '@ngrx/effects';
import { Observable , defer , of } from 'rxjs'; import { Observable , defer , of,from,zip } from 'rxjs';
import { withLatestFrom,mergeMap,switchMap,map,catchError} from 'rxjs/operators'; import { withLatestFrom,mergeMap,switchMap,map,catchError,first} from 'rxjs/operators';
import * as appCommonActions from '../actions/app-common.actions'; import * as appCommonActions from '../actions/app-common.actions';
import * as appCommonReducers from '../reducers/app-common.reducer'; import * as appCommonReducers from '../reducers/app-common.reducer';
import { ItemService } from '../services/item.service'; import { ItemService } from '../services/item.service';
@@ -25,10 +25,18 @@ export class AppCommonEffects {
withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)), withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)),
mergeMap(([action, initialized]) => { mergeMap(([action, initialized]) => {
var a = (action as appCommonActions.Login); var a = (action as appCommonActions.Login);
this.oauthService$.initCodeFlow(a.url); this.oauthService$.initCodeFlow(a.url,{"prompt":"login"});
return []; return [];
})); }));
@Effect({ dispatch: false })
logout$: Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.LOGOUT),
mergeMap((action) => {
this.oauthService$.revokeTokenAndLogout();
return [];
}));
@Effect() @Effect()
loadItemTypes$: Observable<Action> = this.actions$.pipe( loadItemTypes$: Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.LOADITEMTYPES), ofType(appCommonActions.LOADITEMTYPES),
@@ -42,35 +50,67 @@ export class AppCommonEffects {
@Effect() @Effect()
initUser$: Observable<Action> = this.actions$.pipe( initUser$: Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.INITUSER), ofType(appCommonActions.INITUSER),
withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)), first(),
switchMap(([action, initialized]) => { switchMap((action) => {
if(!initialized) { return zip(this.userService$.getCurrentUser(),from(this.oauthService$.loadUserProfile())).pipe(
return this.userService$.getCurrentUser().pipe( switchMap(([user,userInfo]) => {return of(new appCommonActions.InitUserSuccess(user,userInfo as UserInfo))} ),
map((user: IUser) => new appCommonActions.InitUserSuccess(user)),
catchError(error => of(new appCommonActions.Fail(error)))) catchError(error => of(new appCommonActions.Fail(error))))
} else {
return [];
}
} }
)); ));
@Effect() @Effect()
initUserPackages$:Observable<Action> = this.actions$.pipe( initUserPackages$:Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.INITUSERSUCCESS), ofType(appCommonActions.INITUSERPACKAGES),
switchMap((action) => { switchMap(() => {
let a = action as appCommonActions.InitUserSuccess; return this.itemService$.getItemList('vnd.farmmaps.itemtype.package').pipe(
return this.itemService$.getChildItemList(a.user.code+":USER_PACKAGES","vnd.farmmaps.itemtype.package").pipe(
switchMap((items) => of(new appCommonActions.InitUserPackagesSuccess(items))), switchMap((items) => of(new appCommonActions.InitUserPackagesSuccess(items))),
catchError(error => of(new appCommonActions.Fail(error))) catchError(error => of(new appCommonActions.Fail(error)))
) )
}) })
); );
@Effect()
userPackagesChanged$:Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.ITEMCHANGEDEVENT),
switchMap((action) => {
let a = action as appCommonActions.ItemChangedEvent;
if(a.itemCode.endsWith(":USER_PACKAGES"))
return of(new appCommonActions.InitUserPackages());
else
return [];
})
);
@Effect()
initUserSettingsRoot$:Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.INITUSERSETTINGSROOT),
withLatestFrom(this.store$.select(appCommonReducers.SelectGetUser)),
switchMap(([, user]) => {
return this.itemService$.getItem(user.code + ':USER_SETTINGS').pipe(
switchMap((item) => of(new appCommonActions.InitUserSettingsRootSuccess(item))),
catchError(error => of(new appCommonActions.Fail(error)))
)
})
);
@Effect()
initUserSettingsRootChanged$:Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.ITEMCHANGEDEVENT),
switchMap((action) => {
let a = action as appCommonActions.ItemChangedEvent;
if(a.itemCode.endsWith(":USER_SETTINGS"))
return of(new appCommonActions.InitUserSettingsRoot());
else
return [];
})
);
@Effect() @Effect()
initUserSuccess$: Observable<Action> = this.actions$.pipe( initUserSuccess$: Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.INITUSERSUCCESS), ofType(appCommonActions.INITUSERSUCCESS),
switchMap(() => { switchMap(() => {
return of(new appCommonActions.InitRoot()); return [new appCommonActions.InitRoot(),new appCommonActions.InitUserPackages(),new appCommonActions.InitUserSettingsRoot()];
} }
)); ));
@@ -100,8 +140,11 @@ export class AppCommonEffects {
withLatestFrom(this.store$.select(appCommonReducers.selectGetItemTypes)), withLatestFrom(this.store$.select(appCommonReducers.selectGetItemTypes)),
switchMap(([action, itemtypes]) => { switchMap(([action, itemtypes]) => {
var a = action as appCommonActions.EditItem; var a = action as appCommonActions.EditItem;
var itemType = itemtypes[a.item.itemType]; var editor = "property";
var editor = itemType.editor ? itemType.editor : "property"; if(a.item.itemType) {
var itemType = itemtypes[a.item.itemType];
var editor = itemType && itemType.editor ? itemType.editor : editor;
}
this.router$.navigate(['/editor',editor,'item', a.item.code]) this.router$.navigate(['/editor',editor,'item', a.item.code])
return []; return [];
} }
@@ -150,6 +193,25 @@ export class AppCommonEffects {
return null; return null;
})); }));
@Effect({ dispatch: false })
online$: Observable<Action> = this.actions$.pipe(
ofType(appCommonActions.ONLINE),
switchMap((action) => {
console.debug("Online: Check token");
if(!this.oauthService$.hasValidAccessToken()) {
console.debug("No valid token, try to refresh");
if(this.oauthService$.getRefreshToken() != null ) {
console.debug("We have a refresh token");
this.oauthService$.refreshToken().then(() => {
this.store$.dispatch(new appCommonActions.InitUser());
});
}
}
return of(undefined);
}));
constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private oauthService$: OAuthService, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService) { constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private oauthService$: OAuthService, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService) {
store$.dispatch(new appCommonActions.LoadItemTypes()); store$.dispatch(new appCommonActions.LoadItemTypes());
} }

View File

@@ -0,0 +1,22 @@
export interface WeatherData {
date: Date;
iconCode: number;
minTemperature: number;
maxTemperature: number;
relHumidity: number;
precipitation: number;
wSpeed: number;
wCardinal: string;
wDir: number;
}
export interface HourlyWeatherData {
time: string;
iconCode: number;
temperature: number;
relativeHumidity: number;
rain: number;
windSpeed: number;
windDirectionCardinal: string;
windDirection: number;
}

View File

@@ -0,0 +1,7 @@
export interface IDataLayer {
name: string,
unit: string,
index: number,
indexKey?: string,
scale: number,
}

View File

@@ -0,0 +1,11 @@
export interface IColor {
red: number,
green: number,
blue: number,
alpha: number,
}
export interface IGradientstop {
relativestop: number,
color: IColor
}

View File

@@ -15,9 +15,10 @@ export class Item implements IItem {
public geometry?:any; public geometry?:any;
public url?: string; public url?: string;
public name?: string; public name?: string;
public created?: Date; public created?: string;
public updated?: Date; public updated?: string;
public dataDate?: Date; public dataDate?: string;
public dataEndDate?: string;
public itemType?: string; public itemType?: string;
public sourceTask?: string; public sourceTask?: string;
public size?: number; public size?: number;

View File

@@ -4,5 +4,6 @@ export interface IItemType {
editor?: string; editor?: string;
isFolder?: boolean; isFolder?: boolean;
iconColor?: string; iconColor?: string;
schema?: string;
extraAttributes?: string; extraAttributes?: string;
} }

View File

@@ -0,0 +1,5 @@
export interface IJsonline {
time: string;
type: string;
data: any;
}

View File

@@ -1,13 +1,14 @@
export interface IListItem { export interface IListItem {
url?: string; url?: string;
code?: string; code?: string;
name?: string; name?: string;
created?: Date; created?: string;
updated?: Date; updated?: string;
dataDate?: Date; dataDate?: string;
itemType?: string; dataEndDate?: string;
sourceTask?: string; itemType?: string;
size?: number; sourceTask?: string;
state?: number; size?: number;
thumbnail?: boolean; state?: number;
} thumbnail?: boolean;
}

View File

@@ -0,0 +1,5 @@
export interface ISenMLItem {
u?: string;
v?: string;
n?: string;
}

View File

@@ -1,5 +1,6 @@
export interface IUser { export interface IUser {
code?: string; code?: string;
name?: string; name?: string;
email?: string; email?: string;
} claims: any;
}

View File

@@ -7,6 +7,7 @@ import * as appCommonActions from '../actions/app-common.actions';
import { createSelector, createFeatureSelector, ActionReducerMap } from '@ngrx/store'; import { createSelector, createFeatureSelector, ActionReducerMap } from '@ngrx/store';
import { MODULE_NAME } from '../module-name'; import { MODULE_NAME } from '../module-name';
import { IItem } from '../models/item';
export interface State { export interface State {
openedModalName: string, openedModalName: string,
@@ -17,7 +18,10 @@ export interface State {
fullScreen: boolean, fullScreen: boolean,
routeLoading:boolean, routeLoading:boolean,
menuVisible: boolean, menuVisible: boolean,
userPackages: IPackages userPackages: IPackages,
userSettingsRoot: IItem,
accountMenuVisible: boolean,
isOnline: boolean
} }
export const initialState: State = { export const initialState: State = {
@@ -29,14 +33,30 @@ export const initialState: State = {
fullScreen: true, fullScreen: true,
routeLoading: false, routeLoading: false,
menuVisible: false, menuVisible: false,
userPackages: {} userPackages: {},
userSettingsRoot: null,
accountMenuVisible: false,
isOnline: true
} }
export function reducer(state = initialState, action: appCommonActions.Actions ): State { export function reducer(state = initialState, action: appCommonActions.Actions ): State {
switch (action.type) { switch (action.type) {
case appCommonActions.INITUSER: {
return tassign(state,{initialized: true});
}
case appCommonActions.INITUSERSUCCESS: { case appCommonActions.INITUSERSUCCESS: {
let a = action as appCommonActions.InitUserSuccess; let a = action as appCommonActions.InitUserSuccess;
return tassign(state, { user: a.user,initialized: true }); let claims = {}
Object.getOwnPropertyNames(a.userinfo).forEach((k) => {
claims[k] = a.userinfo[k];
});
var user:IUser = {
code:a.user.code,
email:a.userinfo["email"],
name:a.userinfo["name"],
claims:claims
};
return tassign(state, { user: user });
} }
case appCommonActions.INITROOTSUCCESS: { case appCommonActions.INITROOTSUCCESS: {
let a = action as appCommonActions.InitRootSuccess; let a = action as appCommonActions.InitRootSuccess;
@@ -73,14 +93,17 @@ export function reducer(state = initialState, action: appCommonActions.Actions )
}); });
} }
case appCommonActions.TOGGLEMENU: { case appCommonActions.TOGGLEMENU: {
return tassign(state, { menuVisible: !state.menuVisible }); return tassign(state, { menuVisible: !state.menuVisible,accountMenuVisible:!state.menuVisible?false:state.accountMenuVisible });
}
case appCommonActions.TOGGLEACCOUNTMENU: {
return tassign(state, { accountMenuVisible: !state.accountMenuVisible });
} }
case appCommonActions.ESCAPE: { case appCommonActions.ESCAPE: {
return tassign(state, { menuVisible: false }); return tassign(state, { menuVisible: false,accountMenuVisible:false });
} }
case appCommonActions.SETMENUVISIBLE: { case appCommonActions.SETMENUVISIBLE: {
let a = action as appCommonActions.SetMenuVisible; let a = action as appCommonActions.SetMenuVisible;
return tassign(state, { menuVisible: a.visible }); return tassign(state, { menuVisible: a.visible,accountMenuVisible:a.visible?false:state.accountMenuVisible });
} }
case appCommonActions.INITUSERPACKAGESSUCCESS:{ case appCommonActions.INITUSERPACKAGESSUCCESS:{
let a = action as appCommonActions.InitUserPackagesSuccess; let a = action as appCommonActions.InitUserPackagesSuccess;
@@ -91,6 +114,22 @@ export function reducer(state = initialState, action: appCommonActions.Actions )
return tassign(state,{userPackages:packages}); return tassign(state,{userPackages:packages});
} }
case appCommonActions.INITUSERSETTINGSROOTSUCCESS:{
let a = action as appCommonActions.InitUserSettingsRootSuccess;
return tassign(state, { userSettingsRoot : a.item });
}
case appCommonActions.LOGOUT:{
return tassign(state,{user:null,initialized:false});
}
case appCommonActions.CLOSEALL: {
return tassign(state,{accountMenuVisible:false,menuVisible:false });
}
case appCommonActions.ONLINE:{
return tassign(state,{isOnline:true});
}
case appCommonActions.OFFLINE:{
return tassign(state,{isOnline:false});
}
default: { default: {
return state; return state;
} }
@@ -106,6 +145,9 @@ export const getRouteLoading = (state: State) => state.routeLoading;
export const getMenuVisible = (state: State) => state.menuVisible; export const getMenuVisible = (state: State) => state.menuVisible;
export const getUser = (state: State) => state.user; export const getUser = (state: State) => state.user;
export const getUserPackages = (state: State) => state.userPackages; export const getUserPackages = (state: State) => state.userPackages;
export const getUserSettingsRoot = (state: State) => state.userSettingsRoot;
export const getAccountMenuVisible = (state: State) => state.accountMenuVisible;
export const getIsOnline = (state: State) => state.isOnline;
export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME); export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME);
@@ -118,4 +160,7 @@ export const selectGetRouteLoading = createSelector(selectAppCommonState, getRou
export const SelectGetMenuVisible = createSelector(selectAppCommonState,getMenuVisible); export const SelectGetMenuVisible = createSelector(selectAppCommonState,getMenuVisible);
export const SelectGetUser = createSelector(selectAppCommonState,getUser); export const SelectGetUser = createSelector(selectAppCommonState,getUser);
export const SelectGetUserPackages = createSelector(selectAppCommonState,getUserPackages); export const SelectGetUserPackages = createSelector(selectAppCommonState,getUserPackages);
export const SelectGetUserSettingsRoot = createSelector(selectAppCommonState,getUserSettingsRoot);
export const SelectGetAccountMenuVisible = createSelector(selectAppCommonState,getAccountMenuVisible);
export const SelectGetIsOnline = createSelector(selectAppCommonState,getIsOnline);

View File

@@ -22,4 +22,16 @@ export class CodeListItemService {
} }
return this.httpClient.get<ICodeListItem[]>(`${this.ApiEndpoint()}/api/v1/codelistitems/`, { params: params }); return this.httpClient.get<ICodeListItem[]>(`${this.ApiEndpoint()}/api/v1/codelistitems/`, { params: params });
} }
postCodeListItem(codeList: string, codeListItem: ICodeListItem): Observable<ICodeListItem> {
return this.httpClient.post<ICodeListItem>(`${this.ApiEndpoint()}/api/v1/codelistitems/${codeList}`, codeListItem);
}
putCodeListItem(codeListItem: ICodeListItem): Observable<ICodeListItem> {
return this.httpClient.put<ICodeListItem>(`${this.ApiEndpoint()}/api/v1/codelistitems/${codeListItem.codeList}`, codeListItem);
}
deleteCodeListItem(codelist: string, code: string): Observable<any> {
return this.httpClient.delete(`${this.ApiEndpoint()}/api/v1/codelistitems/${codelist}/${code}`);
}
} }

View File

@@ -42,8 +42,8 @@ export class EventService {
} }
private Authenticate() { private Authenticate() {
var accessToken = this.oauthService.getAccessToken(); var accessToken = this.oauthService.getAccessToken();
if (accessToken) { if (this.oauthService.hasValidAccessToken()) {
this._connection.send('authenticate', this.oauthService.getAccessToken()); this._connection.send('authenticate', this.oauthService.getAccessToken());
this.authenticated=true; this.authenticated=true;
} else { } else {

View File

@@ -1,56 +1,49 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable , Observer } from 'rxjs'; import { Observable , Observer } from 'rxjs';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
import { IListItem } from '../models/list.item'; import { IListItem } from '../models/list.item';
import { IItem } from '../models/item'; import { IItem } from '../models/item';
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { AppConfig } from "../shared/app.config"; import { AppConfig } from "../shared/app.config";
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class FolderService { export class FolderService {
constructor(public httpClient: HttpClient, public appConfig: AppConfig) { constructor(public httpClient: HttpClient, public appConfig: AppConfig) {
} }
ApiEndpoint() { ApiEndpoint() {
return this.appConfig.getConfig("apiEndPoint"); return this.appConfig.getConfig("apiEndPoint");
} }
parseDates(item: any): IListItem { getFolder(code: string): Observable<IListItem> {
item.created = new Date(Date.parse(item.created)); return this.httpClient.get<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/${code}`);
item.updated = new Date(Date.parse(item.updated)); }
item.dataDate = new Date(Date.parse(item.dataDate));
return item; getMyRoots(): Observable<IListItem[]> {
} return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/my_roots`);
}
getFolder(code: string): Observable<IListItem> {
return this.httpClient.get<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/${code}`).pipe(map(i => this.parseDates(i))); getFolderParents(code: string): Observable<IListItem[]> {
} return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/parents`);
}
getMyRoots(): Observable<IListItem[]> {
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/my_roots`).pipe(map(ia => ia.map(i => this.parseDates(i)))); getChildFolders(code: string): Observable<IListItem[]> {
} return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/listfolders`);
}
getFolderParents(code: string): Observable<IListItem[]> {
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/parents`).pipe(map(ia => ia.map(i => this.parseDates(i)))); getItems(code: string,skip:number, take:number): Observable<IListItem[]> {
} return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/list?skip=${skip}&take=${take}`);
}
getChildFolders(code: string): Observable<IListItem[]> {
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/listfolders`).pipe(map(ia => ia.map(i => this.parseDates(i)))); moveItem(itemCode: string, newParentCode: string): Observable<IListItem> {
} const body = { itemCode: itemCode,newParentCode: newParentCode };
return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/items/move`, body);
getItems(code: string,skip:number, take:number): Observable<IListItem[]> { }
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/list?skip=${skip}&take=${take}`).pipe(map(ia => ia.map(i => this.parseDates(i))));
} createFolder(folder: IItem): Observable<IListItem> {
return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/`, folder);
moveItem(itemCode: string, newParentCode: string): Observable<IListItem> { }
const body = { itemCode: itemCode,newParentCode: newParentCode }; }
return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/items/move`, body).pipe(map(i => this.parseDates(i)));
}
createFolder(folder: IItem): Observable<IListItem> {
return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/`, folder).pipe(map(i => this.parseDates(i)));
}
}

View File

@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { Observable,BehaviorSubject,of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { HttpClient, HttpXhrBackend } from "@angular/common/http";
import { AppConfig } from "../shared/app.config";
@Injectable({
providedIn: 'root',
})
export class HealthCheckService {
private httpClient: HttpClient;
constructor(xhrBackend: HttpXhrBackend, public appConfig: AppConfig) {
this.httpClient = new HttpClient(xhrBackend);
}
ApiEndpoint() {
return this.appConfig.getConfig("apiEndPoint");
}
check(interval:number): Observable<boolean> {
let retval = new BehaviorSubject<boolean>(true);
setInterval(() => {
this.httpClient.get(`${this.ApiEndpoint()}/api/v1/healthcheck`).pipe(map(() => true),catchError((error) => of(false))).toPromise().then((status) => {
retval.next(status);
});
},interval);
return retval;
}
}

View File

@@ -3,6 +3,7 @@ import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { IItemType } from '../models/item.type'; import { IItemType } from '../models/item.type';
import { IItem } from '../models/item'; import { IItem } from '../models/item';
import { IJsonline } from '../models/json-line';
import { IItemTask } from '../models/itemTask'; import { IItemTask } from '../models/itemTask';
import { HttpClient, HttpParams } from "@angular/common/http"; import { HttpClient, HttpParams } from "@angular/common/http";
import { AppConfig } from "../shared/app.config"; import { AppConfig } from "../shared/app.config";
@@ -19,21 +20,14 @@ export class ItemService {
return this.appConfig.getConfig("apiEndPoint"); return this.appConfig.getConfig("apiEndPoint");
} }
parseDates(item: any): IItem {
item.created = new Date(Date.parse(item.created));
item.updated = new Date(Date.parse(item.updated));
item.dataDate = new Date(Date.parse(item.dataDate));
return item;
}
getItemTypes(): Observable<{ [id: string]: IItemType }> { getItemTypes(): Observable<{ [id: string]: IItemType }> {
return this.httpClient.get<{ [id: string]: IItemType }>(`${this.ApiEndpoint()}/api/v1/itemtypes/`); return this.httpClient.get<{ [id: string]: IItemType }>(`${this.ApiEndpoint()}/api/v1/itemtypes/`);
} }
getFeatures(extent: number[], crs: string, searchText?: string, searchTags?:string,startDate?:Date,endDate?:Date,itemType?:string,parentCode?:string,dataFilter?:string,level?:number): Observable<any> { getFeatures(extent: number[], crs: string, searchText?: string, searchTags?:string,startDate?:Date,endDate?:Date,itemType?:string,parentCode?:string,dataFilter?:string,level?:number): Observable<any> {
var params = new HttpParams(); var params = new HttpParams();
params = params.append("bbox", extent.join(","));
params = params.append("crs", crs); params = params.append("crs", crs);
if (extent) params =params.append("bbox", extent.join(","));
if (searchText) params = params.append("q", searchText); if (searchText) params = params.append("q", searchText);
if (searchTags) params = params.append("t", searchTags); if (searchTags) params = params.append("t", searchTags);
if (startDate) params = params.append("sd", startDate.toISOString()); if (startDate) params = params.append("sd", startDate.toISOString());
@@ -58,21 +52,27 @@ export class ItemService {
} }
getItem(code: string): Observable<IItem> { getItem(code: string): Observable<IItem> {
return this.httpClient.get<IItem>(`${this.ApiEndpoint()}/api/v1/items/${code}`).pipe(map(i => this.parseDates(i))); return this.httpClient.get<IItem>(`${this.ApiEndpoint()}/api/v1/items/${code}`);
}
getItemData(code: string,limit?:number): Observable<ArrayBuffer> {
var params = new HttpParams();
if(limit) params=params.append("limit", limit.toString());
return this.httpClient.get(`${this.ApiEndpoint()}/api/v1/items/${code}/data`,{ params: params,responseType: 'arraybuffer' });
} }
getItemByCodeAndType(code: string, itemType: string): Observable<IItem> { getItemByCodeAndType(code: string, itemType: string): Observable<IItem> {
return this.httpClient.get<IItem>(`${this.ApiEndpoint()}/api/v1/items/${code}/${itemType}`); return this.httpClient.get<IItem>(`${this.ApiEndpoint()}/api/v1/items/${code}/${itemType}`);
} }
getItemList(itemType: string, dataFilter?: any, level: number = 1): Observable<IItem[]> { getItemList(itemType?: string, dataFilter?: any, level?:number ,atItemLocationItemCode?:string,indexed?:boolean): Observable<IItem[]> {
var params = new HttpParams(); var params = new HttpParams();
params = params.append("it", itemType); if(itemType) params = params.append("it", itemType);
if(dataFilter != null){ if(dataFilter) params = params.append("df", JSON.stringify(dataFilter));
params = params.append("df", JSON.stringify(dataFilter)); if(atItemLocationItemCode) params = params.append("ail",atItemLocationItemCode);
} if(indexed) params = params.append("ind",indexed?"true":"false");
params = params.append("lvl", itemType); if(level) params = params.append("lvl", level.toFixed());
return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/`, { params: params }).pipe(map(ia => ia.map(i => this.parseDates(i)))); return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/`, { params: params });
} }
getChildItemList(parentcode: string, itemType: string, dataFilter?: any, level: number = 1, deep: boolean = true): Observable<IItem[]> { getChildItemList(parentcode: string, itemType: string, dataFilter?: any, level: number = 1, deep: boolean = true): Observable<IItem[]> {
@@ -85,7 +85,7 @@ export class ItemService {
} }
params = params.append("lvl", level.toString()); params = params.append("lvl", level.toString());
params = params.append("deep", deep.toString()); params = params.append("deep", deep.toString());
return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/${parentcode}/children`, { params: params }).pipe(map(ia => ia.map(i => this.parseDates(i)))); return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/${parentcode}/children`, { params: params });
} }
getChildItemListCount(parentcode: string, itemType: string): Observable<Number> { getChildItemListCount(parentcode: string, itemType: string): Observable<Number> {
@@ -103,7 +103,7 @@ export class ItemService {
params = params.append("df", JSON.stringify(dataFilter)); params = params.append("df", JSON.stringify(dataFilter));
} }
params = params.append("lvl", level.toString()); params = params.append("lvl", level.toString());
return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/${parentcode}/children`, { params: params }).pipe(map(ia => ia.map(i => this.parseDates(i)))); return this.httpClient.get<IItem[]>(`${this.ApiEndpoint()}/api/v1/items/${parentcode}/children`, { params: params });
} }
getItemFeatures(code: string, extent: number[], crs: string, layerIndex?:number): Observable<any> { getItemFeatures(code: string, extent: number[], crs: string, layerIndex?:number): Observable<any> {
@@ -134,7 +134,7 @@ export class ItemService {
return this.httpClient.post<any>(`${this.ApiEndpoint()}/api/v1/items/delete`, itemCodes); return this.httpClient.post<any>(`${this.ApiEndpoint()}/api/v1/items/delete`, itemCodes);
} }
getTemporalLast(code: string): Observable<any> { getTemporalLast(code: string): Observable<IJsonline> {
return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/temporal/last`); return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/items/${code}/temporal/last`);
} }
@@ -152,6 +152,6 @@ export class ItemService {
getItemTaskList(itemcode: string, unfinishedOnly?: boolean): Observable<IItemTask[]> { getItemTaskList(itemcode: string, unfinishedOnly?: boolean): Observable<IItemTask[]> {
var params = new HttpParams(); var params = new HttpParams();
if (unfinishedOnly) params = params.append("unfinishedOnly", unfinishedOnly.toString()); if (unfinishedOnly) params = params.append("unfinishedOnly", unfinishedOnly.toString());
return this.httpClient.get<IItemTask[]>(`${this.ApiEndpoint()}/api/v1/items/${itemcode}/tasks`, { params: params }).pipe(map(ia => ia.map(i => this.parseDates(i)))); return this.httpClient.get<IItemTask[]>(`${this.ApiEndpoint()}/api/v1/items/${itemcode}/tasks`, { params: params });
} }
} }

View File

@@ -9,9 +9,9 @@ export class ItemTypeService {
public itemTypes: IItemTypes; public itemTypes: IItemTypes;
private httpClient: HttpClient; private httpClient: HttpClient;
constructor(xhrBackend: HttpXhrBackend) { constructor(xhrBackend: HttpXhrBackend) {
this.httpClient = new HttpClient(xhrBackend); this.httpClient = new HttpClient(xhrBackend);
} }
getIcon(itemType: string) { getIcon(itemType: string) {
var icon = "fa fa-file-o"; var icon = "fa fa-file-o";
@@ -31,6 +31,12 @@ export class ItemTypeService {
return extraAttributes; return extraAttributes;
} }
getSchema(itemType: string): string {
let schema = null;
if (this.itemTypes[itemType]) schema = this.itemTypes[itemType].schema;
return schema;
}
hasViewer(item: IItem) { hasViewer(item: IItem) {
let itemType: string = item.itemType; let itemType: string = item.itemType;
if (this.itemTypes[itemType]) return this.itemTypes[itemType].viewer !== undefined; if (this.itemTypes[itemType]) return this.itemTypes[itemType].viewer !== undefined;
@@ -49,7 +55,7 @@ export class ItemTypeService {
} }
public load(config:AppConfig): Promise<any> { public load(config:AppConfig): Promise<any> {
var url = `${ config.getConfig("apiEndPoint")}/api/v1/itemtypes/` var url = `${ config.getConfig("apiEndPoint")}/api/v1/itemtypes/`
return this.httpClient.get(url) return this.httpClient.get(url)
.toPromise() .toPromise()
@@ -59,4 +65,4 @@ export class ItemTypeService {
}) })
.catch(error => this.itemTypes = null); .catch(error => this.itemTypes = null);
}; };
} }

View File

@@ -0,0 +1,31 @@
import { Injectable} from '@angular/core';
import { PreloadingStrategy,Route } from '@angular/router'
import { Observable, EMPTY } from 'rxjs';
import { PackageService} from './package.service';
@Injectable({
providedIn: 'root',
})
export class PackagePreloadStrategy extends PreloadingStrategy {
loading = new Set<Route>();
constructor(private packageService$:PackageService) {
super();
}
preload(route: Route, load: Function): Observable<any> {
if (this.loading.has(route)) {
// Don't preload the same route twice
return EMPTY;
}
if(route.data && route.data.package) {
if(this.packageService$.hasPackage(route.data.package)) {
console.debug(`Load module for package ${route.data.package}`)
this.loading.add(route);
return load()
}
}
return EMPTY;
}
}

View File

@@ -3,6 +3,11 @@ import { Store} from '@ngrx/store';
import * as appCommonReducer from '../reducers/app-common.reducer' import * as appCommonReducer from '../reducers/app-common.reducer'
import { IPackages } from '../models/package'; import { IPackages } from '../models/package';
import { IItem } from '../models/item';
import { IItemTask } from '../models/itemTask';
import { HttpClient } from "@angular/common/http";
import { AppConfig } from "../shared/app.config";
import { Observable } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@@ -11,14 +16,22 @@ import { IPackages } from '../models/package';
export class PackageService { export class PackageService {
private packages$:IPackages = {}; private packages$:IPackages = {};
constructor(private store$: Store<appCommonReducer.State>) { constructor(private store$: Store<appCommonReducer.State>, public httpClient: HttpClient, public appConfig: AppConfig) {
store$.select(appCommonReducer.SelectGetUserPackages).subscribe((packages) => { store$.select(appCommonReducer.SelectGetUserPackages).subscribe((packages) => {
this.packages$ = packages; this.packages$ = packages;
}) })
} }
ApiEndpoint() {
return this.appConfig.getConfig("apiEndPoint");
}
hasPackage(id:string):boolean { hasPackage(id:string):boolean {
if(!this.packages$[id]) return false; if(!this.packages$[id]) return false;
return this.packages$[id].enabled == true; return this.packages$[id].enabled == true;
} }
postItemPackageTask(item: IItem, task: IItemTask): Observable<IItemTask> {
return this.httpClient.post<IItemTask>(`${this.ApiEndpoint()}/api/v1/items/${item.code}/packagetasks`, task);
}
} }

View File

@@ -0,0 +1,43 @@
import {Inject, Injectable, LOCALE_ID} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {AppConfig} from '../shared/app.config';
import {Observable, of} from 'rxjs';
import {catchError, switchMap} from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class SchemaService {
constructor(private httpClient: HttpClient, private appConfig: AppConfig,
@Inject(LOCALE_ID) private locale) {
}
ApiEndpoint() {
return this.appConfig.getConfig('apiEndPoint');
}
/** Takes decoded schema url id */
public getSchema(id): Observable<any> {
const encodedId = encodeURIComponent(id);
return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/schema/${encodedId}`);
}
public getSchemaLayout(schemaUrl: string, locale: string): Observable<any> {
const encodedId = encodeURIComponent(schemaUrl);
return this.httpClient.get<any>(`${this.ApiEndpoint()}/api/v1/schema/${encodedId}/layout/${locale}`);
}
public getSchemaAndLayout(schemaUrl: string, locale: string = this.locale):
Observable<{schemaJson: any, schemaLayout: any}> {
return this.getSchema(schemaUrl).pipe(
switchMap(
schemaJson => this.getSchemaLayout(schemaUrl, locale)
.pipe(catchError(err => {
return of(undefined);
})),
(schemaJson, schemaLayout) => {
return ({schemaJson, schemaLayout});
}
));
}
}

View File

@@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import { IJsonline } from '../models/json-line';
import { ISenMLItem } from '../models/senml-item';
import { IDataLayer } from '../models/data.layer';
import { IItem } from '../models/item';
@Injectable({
providedIn: 'root',
})
export class SenmlService {
constructor() {
}
getSenMLItem(layer:IDataLayer,jsonLine:IJsonline): ISenMLItem {
if (jsonLine) {
var senmlPack = jsonLine.data as ISenMLItem[];
var temp = senmlPack.filter((i) => i.u == layer.indexKey);
if (temp.length == 1) return temp[0];
return null;
}
}
getFirstLayer(item:IItem):IDataLayer {
if(item && item.data && item.data["layers"] && item.data["layers"].length > 0 ) {
return item.data["layers"][0] as IDataLayer;
} else {
let retVal:IDataLayer = { name:"Soil moisture",index:0,scale:1,unit:"%",indexKey:"%vol" };
return retVal;
}
}
}

View File

@@ -1,23 +1,122 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {AppConfig} from '../shared/app.config';
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {GeoJSON} from 'ol/format';
import {map, switchMap} from 'rxjs/operators';
import {getCenter} from 'ol/extent';
import moment from 'moment';
import {AppConfig} from '../shared/app.config';
import {WeatherCurrentObservation} from '../models/weatherCurrentObservation'; import {WeatherCurrentObservation} from '../models/weatherCurrentObservation';
import {IItem} from '../models/item';
import {HourlyWeatherData, WeatherData} from '../models/WeatherData';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class WeatherService { export class WeatherService {
private apiUrl = '/api/v1/weather/currentobservation'; private apiCurrentObservationUrl = 'currentobservation';
private apiObservation = 'observation';
private apiForecast = 'forecast';
private format: GeoJSON;
constructor(public httpClient: HttpClient, public appConfig: AppConfig) { constructor(public httpClient: HttpClient, public appConfig: AppConfig) {
this.format = new GeoJSON();
} }
public GetCurrentObservation(centroid: number[]): Observable<WeatherCurrentObservation> { public GetCurrentObservation(centroid: number[]): Observable<WeatherCurrentObservation> {
const endpoint = this.appConfig.getConfig('weatherApiEndPoint'); const endpoint = this.appConfig.getConfig('weatherApiEndPoint');
const apiKey = this.appConfig.getConfig('weatherApiKey'); const apiKey = this.appConfig.getConfig('weatherApiKey');
const observationUrl = `${endpoint}${this.apiUrl}/?c=${centroid[0]},${centroid[1]}&key=${apiKey}`; const observationUrl = `${endpoint}${this.apiCurrentObservationUrl}/?c=${centroid[0]},${centroid[1]}&key=${apiKey}`;
return this.httpClient.get<WeatherCurrentObservation>(observationUrl); return this.httpClient.get<WeatherCurrentObservation>(observationUrl);
} }
public getWeatherRangeForItem(item: IItem, daysBefore = 10, daysAfter = 10): Observable<HourlyWeatherData[]> {
const geometry = this.format.readGeometry(item.geometry);
const centroid = getCenter(geometry.getExtent());
const startDate = new Date();
startDate.setDate(startDate.getDate() - daysBefore);
startDate.setHours(0, 0, 0, 0);
const startDateString = moment(startDate).local().format('YYYY-MM-DD[T]HH:mm:ss');
const endDate = new Date();
endDate.setDate(endDate.getDate() + daysAfter);
endDate.setHours(23, 0, 0, 0);
const endDateString = moment(endDate).local().format('YYYY-MM-DD[T]HH:mm:ss');
return this.getWeatherRange(centroid, startDateString, endDateString);
}
public getWeatherRange(centroid: number[], startDate: string, endDate: string): Observable<HourlyWeatherData[]> {
const endpoint = this.appConfig.getConfig('weatherApiEndPoint');
const apiKey = this.appConfig.getConfig('weatherApiKey');
// weather does not support UTC format, also remove Z
const sd = encodeURIComponent(startDate);
const edHistoricalDate = new Date();
edHistoricalDate.setHours(edHistoricalDate.getHours(), 0, 0, 0);
const edHistorical = moment(edHistoricalDate).local().format('YYYY-MM-DD[T]HH:mm:ss');
const historical = `${endpoint}${this.apiObservation}/?c=${centroid[0]},${centroid[1]}&sd=${sd}&ed=${edHistorical}&t=observation&interval=hourly&key=${apiKey}`;
const forecast = `${endpoint}${this.apiForecast}/?c=${centroid[0]},${centroid[1]}&interval=hourly&key=${apiKey}`;
return this.httpClient.get<any[]>(historical).pipe(
map(h => h.map(d => ({...d, rain: d.rainPastHour}))),
switchMap(h => {
return this.httpClient.get<any[]>(forecast)
.pipe(
map(f => [...h, ...f.filter(fd => fd.time <= endDate)] ));
})
);
}
public hourlyToDaily(hourlyWeatherData: HourlyWeatherData[]): WeatherData[] {
const days = this.groupBy(hourlyWeatherData, hi => hi.time.split('T')[0]);
return Object.entries(days)
.reduce ( (result, entry) => {
const wData = entry[1];
const sortData = wData.sort(d => new Date(d.time).getHours() - 12);
const closestToTwelveOClockData = sortData[0];
if (closestToTwelveOClockData.length === 0) {
return result;
}
const bestCardinal = closestToTwelveOClockData.windDirectionCardinal;
const bestWindDirection = closestToTwelveOClockData.windDirection;
const bestIconCode = closestToTwelveOClockData.iconCode;
return [
...result,
{
date: entry[0],
iconCode: bestIconCode,
minTemperature: Math.min(...wData.map(d => d.temperature)),
maxTemperature: Math.max(...wData.map(d => d.temperature)),
relHumidity: wData.reduce( (r, e) => r + e.relativeHumidity, 0) / wData.length,
precipitation: wData.reduce( (r, e) => r + e.rain, 0),
wSpeed: wData.reduce( (r, e) => r + e.windSpeed, 0) / wData.length,
wDir: bestWindDirection,
wCardinal: bestCardinal
}
];
}, []);
}
private groupBy(items: any[], selector: (item) => any): any[] {
return items.reduce(
(result, item) => {
const group = selector(item);
return ({
...result,
[group]: [
...(result[group] || []),
item,
],
}); },
{},
);
}
} }

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