Compare commits
	
		
			190 Commits
		
	
	
		
			feature/Mi
			...
			9a338c04e6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 9a338c04e6 | ||
|  | 9cbc9f1293 | ||
|  | 34b75a4c18 | ||
|  | 1991e79ed2 | ||
|  | d661e5f3f9 | ||
|  | 0a384a9196 | ||
|  | 0e13b98b7f | ||
|  | 56a7544c1e | ||
|  | 61636b6e3d | ||
|  | ab835e904e | ||
|  | cd10165510 | ||
|  | 446d26607c | ||
|  | 8d204ce1cd | ||
|  | 2656a67fca | ||
|  | 46c5f74b49 | ||
|  | a5ece9b453 | ||
|  | 9fb5a9698c | ||
| fad2f19ae4 | |||
| 2ad04fc7c1 | |||
| f656ecd23f | |||
| d42aad87bd | |||
| 415ee8ef30 | |||
|  | 306065dcbd | ||
| 824ba97704 | |||
| ac8ab147bc | |||
|  | 640c99cde2 | ||
|  | da2492e017 | ||
|  | f93d235877 | ||
|  | fe798e3f60 | ||
|  | b8dcc4e9e0 | ||
|  | 4b6175b6c4 | ||
|  | 388a2faf84 | ||
|  | 0b0bb4d598 | ||
|  | 7696ddd8d2 | ||
|  | 1301a9d082 | ||
|  | e87affee08 | ||
|  | b00b21e6b8 | ||
|  | 835851350c | ||
|  | 7bdb48b644 | ||
|  | 84d6f44e9b | ||
|  | 2fd109d967 | ||
|  | f9934ccf27 | ||
|  | b93218e165 | ||
|  | 8e55308df9 | ||
|  | 5270739641 | ||
|  | ce8e8fa706 | ||
|  | d326a82c4e | ||
|  | c1683a058c | ||
|  | aa168bb3a1 | ||
|  | fe53955bea | ||
|  | ba3dad1365 | ||
|  | 6d2d259c01 | ||
|  | 92774647ff | ||
|  | 9ca047923f | ||
|  | c893a2820b | ||
|  | baa0166fc8 | ||
|  | 58c200aca0 | ||
|  | b8a4b3541d | ||
|  | 584a1514ac | ||
|  | 21af6ab2c4 | ||
|  | c606e02673 | ||
|  | ff4eb296ff | ||
|  | ea26cdd628 | ||
|  | 26e28bdda8 | ||
|  | 15d1f14ee1 | ||
|  | 22e007b70b | ||
|  | 9e4a538a07 | ||
|  | c44c771feb | ||
|  | 394bbab320 | ||
|  | 3008081ae8 | ||
|  | 7474c00498 | ||
|  | 964bf925e7 | ||
|  | fb8129148d | ||
|  | 73db03148a | ||
|  | 6367d3399d | ||
|  | ee4e7113d2 | ||
|  | ad86e41345 | ||
|  | e682b375e7 | ||
|  | 78425a621a | ||
| 1030b1c41a | |||
| 76bdad6eaf | |||
| ce56dd9e9f | |||
| e7a53ba7b5 | |||
| 9f1f6934ee | |||
| 773bf3f5e7 | |||
| cdc900d5fd | |||
| 80522a3a2f | |||
| 0157812357 | |||
| 329564d8f6 | |||
| ba2a067c8b | |||
| 4d67a656f5 | |||
| ad1d40cdd1 | |||
| 131e0eb371 | |||
| e563011099 | |||
| 7c072752c8 | |||
| 36ced002e0 | |||
| 3e1b892eef | |||
| f4004cf2be | |||
| d1d8723186 | |||
| a6fd052a4f | |||
| cede944538 | |||
| 3b3c2768d3 | |||
| 3dd19c702f | |||
| c028adfb77 | |||
| 1d2caa0fe3 | |||
|  | d7ca1512bd | ||
|  | ebe435027f | ||
|  | dfefa6746f | ||
|  | c90df5c972 | ||
| 514a91c2c7 | |||
|  | ee7d1cb9a2 | ||
| 309b320534 | |||
| fdfa7d3387 | |||
|  | 6785bfc3e0 | ||
|  | c682fb9184 | ||
|  | ca6ea96eae | ||
|  | 3ebc197fac | ||
| 52871efb09 | |||
| f014dc9f93 | |||
| e6cda406c9 | |||
| df04397b13 | |||
|  | e0b29c905c | ||
|  | 44ae806b30 | ||
|  | 78e3cf74b9 | ||
|  | 428353d379 | ||
| 9697e93915 | |||
|  | e057e463bd | ||
|  | d494a7c7d6 | ||
|  | e525227865 | ||
|  | 10bdd27608 | ||
|  | 2790ae74e7 | ||
|  | 31d01c75c5 | ||
|  | 0da8d3d46d | ||
|  | 60761e9f4a | ||
|  | ec9684b826 | ||
|  | 1073044f31 | ||
|  | 81126974f5 | ||
| 06f443a503 | |||
| eb560aaf33 | |||
|  | 464b82bfb9 | ||
|  | 1c75fb6f71 | ||
|  | 5f5d8a9712 | ||
|  | dc329a0e29 | ||
|  | 15a09e4b05 | ||
| 94f973406e | |||
| 6404e45d66 | |||
| c0f689331e | |||
| 0f5a5251db | |||
| 3352bfd805 | |||
|  | 60ed2afaa4 | ||
|  | 9ac534c171 | ||
|  | cecbdb2ded | ||
|  | 1092d5dcaf | ||
|  | 4af6b2937c | ||
| 263fb6c3d4 | |||
|  | 09fa8b4f1f | ||
|  | 214dc3ffa1 | ||
|  | fad04744cf | ||
|  | 033b74517f | ||
|  | 0b7564e874 | ||
|  | 0d247e6416 | ||
|  | b92cc7bc09 | ||
|  | eba18b0235 | ||
|  | 69f8184dfe | ||
|  | 31c1570ffb | ||
|  | fe2650c4fd | ||
|  | f89209c555 | ||
|  | b3d0a6f0c7 | ||
|  | 6bd1726fc9 | ||
|  | b0ff54d157 | ||
|  | 277dc7051e | ||
|  | ace4a6b364 | ||
|  | 146514d386 | ||
|  | 1187ee9fd6 | ||
| 4527276254 | |||
|  | 6f52302875 | ||
|  | cbe27c2add | ||
|  | cd156ab1bc | ||
|  | f9d0e2aee0 | ||
|  | 253b3d3c90 | ||
|  | 28d4adc571 | ||
|  | 925af1e645 | ||
|  | 4130e0a796 | ||
|  | 01933b1602 | ||
| 0482aa7124 | |||
| 75015f6d22 | |||
| aa3707aa56 | |||
|  | ff06b419f2 | ||
|  | c1c0bd2596 | ||
|  | 978cbdabfc | 
							
								
								
									
										1
									
								
								.npmrc
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								.npmrc
									
									
									
									
									
								
							| @@ -1 +0,0 @@ | ||||
| @FarmMaps:registry=https://repository.akkerweb.nl/repository/npm-group/ | ||||
| @@ -7,18 +7,22 @@ pipeline { | ||||
|   stages { | ||||
|     stage('npm install'){ | ||||
|       steps { | ||||
|         sh '''npm install | ||||
|         sh '''rm -rf node_modules/ | ||||
|         npm install | ||||
|         cd projects/common | ||||
|         npm install | ||||
|         cd ../common-map | ||||
|         npm install | ||||
|         cd ../common-map3d | ||||
|         npm install | ||||
|         ''' | ||||
|       } | ||||
|     } | ||||
|     stage('build'){ | ||||
|       steps { | ||||
|         sh '''ng build common | ||||
|         ng build common-map''' | ||||
|         ng build common-map | ||||
|         ng build common-map3d''' | ||||
|       } | ||||
|     } | ||||
|     stage('npm publish'){ | ||||
| @@ -28,6 +32,9 @@ pipeline { | ||||
|         npm publish | ||||
|         cd ../common-map | ||||
|         npm version ${PACKAGE_VERSION} | ||||
|         npm publish | ||||
|         cd ../common-map3d | ||||
|         npm version ${PACKAGE_VERSION} | ||||
|         npm publish''' | ||||
|       } | ||||
|     } | ||||
|   | ||||
							
								
								
									
										71
									
								
								angular.json
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								angular.json
									
									
									
									
									
								
							| @@ -11,8 +11,11 @@ | ||||
|       "schematics": {}, | ||||
|       "architect": { | ||||
|         "build": { | ||||
|           "builder": "@angular-devkit/build-angular:browser", | ||||
|           "builder": "@angular-builders/custom-webpack:browser", | ||||
|           "options": { | ||||
|             "customWebpackConfig": { | ||||
|               "path": "./custom-webpack.config.js" | ||||
|             }, | ||||
|             "aot": true, | ||||
|             "outputPath": "dist/farmmaps-lib-app", | ||||
|             "index": "src/index.html", | ||||
| @@ -22,18 +25,23 @@ | ||||
|             "assets": [ | ||||
|               { | ||||
|                 "glob": "**/*", | ||||
|                 "input":"src/assets/images", | ||||
|                 "output":"/images" | ||||
|                 "input": "src/assets/images", | ||||
|                 "output": "/images" | ||||
|               }, | ||||
|               { | ||||
|                 "glob": "silent-refresh.html", | ||||
|                 "input":"src/assets", | ||||
|                 "output":"/" | ||||
|                 "input": "src/assets", | ||||
|                 "output": "/" | ||||
|               }, | ||||
|               { | ||||
|                 "glob": "favicon.ico", | ||||
|                 "input":"src/assets", | ||||
|                 "output":"/" | ||||
|                 "input": "src/assets", | ||||
|                 "output": "/" | ||||
|               }, | ||||
|               { | ||||
|                 "glob": "**/*", | ||||
|                 "input": "node_modules/cesium/Build/Cesium", | ||||
|                 "output": "/assets/cesium" | ||||
|               }, | ||||
|               "src/configuration.json" | ||||
|             ], | ||||
| @@ -41,10 +49,11 @@ | ||||
|               "src/styles.css", | ||||
|               "node_modules/bootstrap/dist/css/bootstrap.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": [], | ||||
|             "es5BrowserSupport": true | ||||
|             "scripts": [] | ||||
|           }, | ||||
|           "configurations": { | ||||
|             "production": { | ||||
| @@ -78,7 +87,7 @@ | ||||
|           } | ||||
|         }, | ||||
|         "serve": { | ||||
|           "builder": "@angular-devkit/build-angular:dev-server", | ||||
|           "builder": "@angular-builders/custom-webpack:dev-server", | ||||
|           "options": { | ||||
|             "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" | ||||
|   | ||||
							
								
								
									
										10
									
								
								custom-webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								custom-webpack.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| module.exports = { | ||||
|     node: { | ||||
|       // Resolve node module use of fs | ||||
|       fs: "empty", | ||||
|       Buffer: false, | ||||
|       http: "empty", | ||||
|       https: "empty", | ||||
|       zlib: "empty" | ||||
|     } | ||||
|   }; | ||||
							
								
								
									
										13002
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										13002
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										78
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								package.json
									
									
									
									
									
								
							| @@ -11,56 +11,62 @@ | ||||
|   }, | ||||
|   "private": true, | ||||
|   "dependencies": { | ||||
|     "@angular/animations": "~9.1.0", | ||||
|     "@angular/common": "~9.1.0", | ||||
|     "@angular/compiler": "~9.1.0", | ||||
|     "@angular/core": "~9.1.0", | ||||
|     "@angular/forms": "~9.1.0", | ||||
|     "@angular/platform-browser": "~9.1.0", | ||||
|     "@angular/platform-browser-dynamic": "~9.1.0", | ||||
|     "@angular/router": "~9.1.0", | ||||
|     "@angular/animations": "~10.1.3", | ||||
|     "@angular/common": "~10.1.3", | ||||
|     "@angular/compiler": "~10.1.3", | ||||
|     "@angular/core": "~10.1.3", | ||||
|     "@angular/forms": "~10.1.3", | ||||
|     "@angular/platform-browser": "~10.1.3", | ||||
|     "@angular/platform-browser-dynamic": "~10.1.3", | ||||
|     "@angular/router": "~10.1.3", | ||||
|     "@farmmaps/common": ">=0.0.1-prerelease.454 <0.0.1", | ||||
|     "@farmmaps/common-map": ">=0.0.1-prerelease.454 <0.0.1", | ||||
|     "@farmmaps/common-map3d": ">=0.0.1-prerelease.454 <0.0.1", | ||||
|     "@microsoft/signalr": "^3.1.3", | ||||
|     "@farmmaps/common": ">=0.0.1-prerelease.267 <0.0.1", | ||||
|     "@farmmaps/common-map": ">=0.0.1-prerelease.267 <0.0.1", | ||||
|     "@ng-bootstrap/ng-bootstrap": "^6.0", | ||||
|     "@ngrx/effects": "^9.0", | ||||
|     "@ngrx/router-store": "^9.0", | ||||
|     "@ngrx/store": "^9.0", | ||||
|     "ngx-uploadx": "^3.3.4", | ||||
|     "angular-oauth2-oidc": "^9.1", | ||||
|     "@ng-bootstrap/ng-bootstrap": "^7.0", | ||||
|     "@ngrx/effects": "^10.0", | ||||
|     "@ngrx/router-store": "^10.0", | ||||
|     "@ngrx/store": "^10.0", | ||||
|     "angular-oauth2-oidc": "^10.0.3", | ||||
|     "bootstrap": "^4.4.1", | ||||
|     "cesium": "^1.73.0", | ||||
|     "core-js": "^2.6.11", | ||||
|     "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.4.3", | ||||
|     "ol-cesium": "^2.11.3", | ||||
|     "resumablejs": "^1.1.0", | ||||
|     "rxjs": "^6.5.4", | ||||
|     "tassign": "^1.0.0", | ||||
|     "tslib": "^1.10.0", | ||||
|     "tslib": "^2.0.0", | ||||
|     "zone.js": "~0.10.2", | ||||
|     "ngx-openlayers": "1.0.0-next.13", | ||||
|     "ol": "6.1.1" | ||||
|     "moment": "^2.27.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@angular-devkit/build-angular": "~0.901.0", | ||||
|     "@angular-devkit/build-ng-packagr": "~0.901.0", | ||||
|     "@angular/cli": "^9.1.0", | ||||
|     "@angular/compiler-cli": "~9.1.0", | ||||
|     "@angular/language-service": "~9.1.0", | ||||
|     "@angular-builders/custom-webpack": "~10.0.1", | ||||
|     "@angular-devkit/build-angular": "^0.1002.0", | ||||
|     "@angular-devkit/build-ng-packagr": "~0.1001.3", | ||||
|     "@angular/cli": "^10.1.3", | ||||
|     "@angular/compiler-cli": "~10.1.3", | ||||
|     "@angular/language-service": "~10.1.3", | ||||
|     "@types/jasmine": "~2.8.8", | ||||
|     "@types/jasminewd2": "~2.0.3", | ||||
|     "@types/node": "^12.11.1", | ||||
|     "codelyzer": "^5.1.2", | ||||
|     "jasmine-core": "~2.99.1", | ||||
|     "jasmine-spec-reporter": "~4.2.1", | ||||
|     "karma": "^5.0.2", | ||||
|     "karma-chrome-launcher": "~2.2.0", | ||||
|     "karma-coverage-istanbul-reporter": "~2.0.1", | ||||
|     "karma-jasmine": "~1.1.2", | ||||
|     "karma-jasmine-html-reporter": "^0.2.2", | ||||
|     "ng-packagr": "^9.0.0", | ||||
|     "protractor": "^5.4.4", | ||||
|     "jasmine-core": "~3.5.0", | ||||
|     "jasmine-spec-reporter": "~5.0.0", | ||||
|     "karma": "~5.0.0", | ||||
|     "karma-chrome-launcher": "~3.1.0", | ||||
|     "karma-coverage-istanbul-reporter": "~3.0.2", | ||||
|     "karma-jasmine": "~4.0.0", | ||||
|     "karma-jasmine-html-reporter": "^1.5.0", | ||||
|     "ng-packagr": "^10.1.0", | ||||
|     "protractor": "~7.0.0", | ||||
|     "ts-node": "^8.8.1", | ||||
|     "tslint": "~5.11.0", | ||||
|     "typescript": "~3.8.3" | ||||
|     "tslint": "~6.1.0", | ||||
|     "typescript": "~4.0.3" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										75
									
								
								projects/common-map/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										75
									
								
								projects/common-map/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -4,79 +4,10 @@ | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "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": { | ||||
|       "version": "1.10.0", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", | ||||
|       "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", | ||||
|       "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,15 +4,18 @@ | ||||
|   "publishConfig": { | ||||
|     "registry": "https://repository.akkerweb.nl/repository/npm-hosted/" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "tslib": "^2.0.0" | ||||
|   }, | ||||
|   "peerDependencies": { | ||||
|     "@angular/core": "^9.1.0", | ||||
|     "ngrx-store-localstorage": "^9.0", | ||||
|     "@ngrx/effects": "^9.0", | ||||
|     "@ngrx/router-store": "^9.0", | ||||
|     "@ngrx/store": "^9.0", | ||||
|     "@angular/core": "^10.1.3", | ||||
|     "ngrx-store-localstorage": "^10.0", | ||||
|     "@ngrx/effects": "^10.0", | ||||
|     "@ngrx/router-store": "^10.0", | ||||
|     "@ngrx/store": "^10.0", | ||||
|     "tassign": "^1.0.0", | ||||
|     "@farmmaps/common": ">=0.0.1-prerelease.265 <0.0.1", | ||||
|     "ngx-openlayers": "1.0.0-next.13", | ||||
|     "@farmmaps/common": ">=0.0.1-prerelease.420 <0.0.1", | ||||
|     "ngx-openlayers": "1.0.0-next.16", | ||||
|     "ol": "6.1.1" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -87,7 +87,7 @@ export class StartSearch implements Action { | ||||
| export class StartSearchSuccess implements Action { | ||||
|   readonly type = STARTSEARCHSUCCESS; | ||||
|  | ||||
|   constructor(public features: Array<Feature>, public query:IQueryState,public setStateCount:number) { } | ||||
|   constructor(public features: Array<Feature>, public query:IQueryState) { } | ||||
| } | ||||
|  | ||||
| export class SelectFeature implements Action { | ||||
| @@ -105,7 +105,7 @@ export class SelectItem implements Action { | ||||
| export class SelectItemSuccess implements Action { | ||||
|   readonly type = SELECTITEMSUCCESS; | ||||
|  | ||||
|   constructor(public item: IItem) { } | ||||
|   constructor(public item: IItem, public parentItem: IItem) { } | ||||
| } | ||||
|  | ||||
| export class SelectTemporalItemsSuccess implements Action { | ||||
|   | ||||
| @@ -4,21 +4,17 @@ import { MapComponent } from './components/map/map.component'; | ||||
| import { AuthGuard } from '@farmmaps/common'; | ||||
|  | ||||
| const routes = [ | ||||
|     { | ||||
|     path: '', children: [ | ||||
|     { | ||||
|         path: '', | ||||
|         component: MapComponent    | ||||
|             } | ||||
|         ] | ||||
|     }, | ||||
|     { | ||||
|       path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer/:queryState', children: [ | ||||
|             { | ||||
|                 path: '', | ||||
|        path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer/:queryState', | ||||
|        component: MapComponent | ||||
|     }, | ||||
|     { | ||||
|        path: ':queryState',  | ||||
|        component: MapComponent | ||||
|             } | ||||
|         ] | ||||
|     } | ||||
| ]; | ||||
|  | ||||
|   | ||||
| @@ -24,7 +24,6 @@ import { IPeriodState } from './models/period.state'; | ||||
|  | ||||
| // components | ||||
| 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 { FeatureListFeatureCroppingschemeComponent} from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.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 {ForItemType } from './components/for-item/for-itemtype.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 {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> { | ||||
|   const r = function(state, action) { | ||||
| @@ -143,7 +145,8 @@ export { | ||||
|   IPeriodState, | ||||
|   ForChild, | ||||
|   ForItemType, | ||||
|   ForSourceTask | ||||
|   ForSourceTask, | ||||
|   ForPackage | ||||
| } | ||||
|  | ||||
| @NgModule({ | ||||
| @@ -193,7 +196,9 @@ export { | ||||
|     WidgetStatusComponent, | ||||
|     GpsLocation, | ||||
|     PanToLocation, | ||||
|     LayerSwitcher | ||||
|     LayerSwitcher, | ||||
|     HistogramDetailsComponent, | ||||
|     StatisticsDetailsComponent | ||||
|   ], | ||||
|   entryComponents: [ | ||||
|     FeatureListComponent, | ||||
| @@ -246,14 +251,7 @@ export { | ||||
|        FeatureListCropfieldComponent, | ||||
|        FeatureListFeatureContainerComponent, | ||||
|        ZoomToExtentComponent | ||||
|   ] | ||||
| }) | ||||
|  | ||||
|  | ||||
| export class AppCommonMapModule { | ||||
|   static forRoot(): ModuleWithProviders<AppCommonMapModule> { | ||||
|     return { | ||||
|       ngModule: AppCommonMapModule,       | ||||
|   ], | ||||
|   providers: [ | ||||
|     FeatureIconService, | ||||
|     GeolocationService, | ||||
| @@ -271,6 +269,8 @@ export class AppCommonMapModule { | ||||
|     { provide: AbstractItemListItemComponent, useClass: ItemListItemComponent, multi: true }, | ||||
|     { provide: AbstractItemListComponent, useClass: ItemListComponent, multi: true } | ||||
|   ] | ||||
|     }; | ||||
|   } | ||||
| }) | ||||
|  | ||||
|  | ||||
| export class AppCommonMapModule {   | ||||
| } | ||||
|   | ||||
| @@ -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 Overlay from 'ol/Overlay'; | ||||
| import { fromLonLat, toLonLat } from 'ol/proj'; | ||||
| @@ -23,9 +23,10 @@ export class GpsLocation  implements OnInit,OnChanges{ | ||||
|   public path: string = ""; | ||||
|   public rotate: string = ""; | ||||
|   private resolution: number = 0; | ||||
|   initialized:boolean = false; | ||||
|   @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.recalcLocationTolerance(); | ||||
|     }); | ||||
|     this.initialized = true; | ||||
|   } | ||||
|  | ||||
|   ngOnChanges(changes: SimpleChanges) { | ||||
|     if (changes.position && this.instance) { | ||||
|       var p = changes.position.currentValue as Position; | ||||
|       if(p) { | ||||
|       if(p && this.initialized) { | ||||
|         this.instance.setPosition(fromLonLat([p.coords.longitude, p.coords.latitude])); | ||||
|         this.locationTolerance = p.coords.accuracy; | ||||
|         this.recalcLocationTolerance();   | ||||
|   | ||||
| @@ -1,8 +1,7 @@ | ||||
| import { Component, Host, Input, Output, EventEmitter, Optional, QueryList, OnInit, AfterViewInit, OnChanges, SimpleChanges, SkipSelf, forwardRef, Inject, InjectionToken } from '@angular/core'; | ||||
| import { HttpClient } from "@angular/common/http"; | ||||
| import { LayerVectorComponent, LayerTileComponent, LayerGroupComponent, MapComponent } from 'ngx-openlayers'; | ||||
| import { Component, Host, Input, Output, EventEmitter,OnDestroy, OnInit, OnChanges, SimpleChanges, forwardRef } from '@angular/core'; | ||||
| import { LayerGroupComponent, MapComponent } from 'ngx-openlayers'; | ||||
| import { ItemService,IItem,AppConfig } from '@farmmaps/common'; | ||||
| import { IItemLayer,ItemLayer, ITemporalItemLayer} from '../../../models/item.layer'; | ||||
| import { IItemLayer, ITemporalItemLayer} from '../../../models/item.layer'; | ||||
| import { ILayerData} from '../../../models/layer.data'; | ||||
| import { IRenderoutputTiles,IRenderoutputImage,IGradientstop,ILayer,IHistogram} from '../../../models/color.map'; | ||||
| import {Extent} from 'ol/extent'; | ||||
| @@ -11,13 +10,12 @@ import * as proj from 'ol/proj'; | ||||
| import * as loadingstrategy from 'ol/loadingstrategy'; | ||||
| import * as style from 'ol/style'; | ||||
| 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 VectorLayer } from 'ol/layer'; | ||||
| import VectorTileSource from 'ol/source/VectorTile'; | ||||
| import VectorTileLayer from 'ol/layer/VectorTile'; | ||||
| import {GeoJSON,MVT} from 'ol/format'; | ||||
| import { from } from 'rxjs'; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'fm-map-item-layers', | ||||
| @@ -27,10 +25,16 @@ import { from } from 'rxjs'; | ||||
|   ] | ||||
| }) | ||||
|  | ||||
| export class ItemLayersComponent extends LayerGroupComponent implements OnChanges, OnInit { | ||||
| export class ItemLayersComponent extends LayerGroupComponent implements OnChanges, OnInit,OnDestroy { | ||||
|   @Input() itemLayers: IItemLayer[]; | ||||
|   @Input() itemLayer: IItemLayer; | ||||
|   @Output() onFeatureSelected: EventEmitter<string> = new EventEmitter<string>(); | ||||
|   @Output() onFeatureHover: EventEmitter<string> = new EventEmitter<string>(); | ||||
|   private _apiEndPoint: string; | ||||
|   private initialized:boolean = false; | ||||
|   private mapEventHandlerInstalled = false; | ||||
|   private selectedFeatures = {}; | ||||
|   private selectionLayer:Layer = null; | ||||
|    | ||||
|   constructor(private itemService: ItemService, @Host() private map: MapComponent, public appConfig: AppConfig) { | ||||
|     super(map); | ||||
| @@ -48,8 +52,10 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|      return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b) + this.componentToHex(a); | ||||
|   } | ||||
|  | ||||
|   getColorFromGradient(layer: ILayer,  feature): style.Style { | ||||
|     var value = feature.get(layer.name); | ||||
|   getColorFromGradient(item:IItem,layer: ILayer,  feature): style.Style { | ||||
|     var value = layer.indexKey ? feature.get(layer.indexKey): feature.get(layer.name); | ||||
|     var key = item.code + "_" + value; | ||||
|     if(!this.styleCache[key]) { | ||||
|       var gradient: IGradientstop[] = layer.renderer.colorMap.gradient; | ||||
|     var histogram: IHistogram = layer.renderer.band.histogram; | ||||
|     var index = (value - histogram.min) / histogram.max; | ||||
| @@ -67,7 +73,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|     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)); | ||||
|  | ||||
|     return new style.Style( | ||||
|     this.styleCache[key] = new style.Style( | ||||
|       { | ||||
|         image: new style.Circle({ | ||||
|           fill: new style.Fill({ | ||||
| @@ -79,33 +85,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|           color: this.rgbaToHex(red, green, blue, alpha) | ||||
|         }), | ||||
|         stroke: new style.Stroke({ | ||||
|           color: this.rgbaToHex(red, green, blue, alpha), | ||||
|           color: this.rgbaToHex(red, green, blue, 255), | ||||
|           width: 1.25 | ||||
|         }), | ||||
|       }); | ||||
|     } | ||||
|     return this.styleCache[key]; | ||||
|   } | ||||
|  | ||||
|   createGeotiffLayer(item:IItem,itemLayer:IItemLayer):Layer { | ||||
|     var layerIndex = -1; | ||||
|     var layer: Layer = null;   | ||||
|     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 }); | ||||
|     var data = item.data; | ||||
|     var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; | ||||
|     if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { | ||||
|       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 }); | ||||
|     } | ||||
|     if (l && l.rendering && l.rendering.renderoutputType == "Image") { | ||||
|       var ri = l.rendering as IRenderoutputImage;   | ||||
|       let projection = new Projection({ | ||||
|         code: 'image', | ||||
|         units: 'pixels', | ||||
|         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()}` }); | ||||
|       // convert to EPSG:4326 asworkaround for cesium    | ||||
|       var projectedExtent = proj.transformExtent( ri.extent, 'EPSG:3857','EPSG:4326'); | ||||
|       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)}` }); | ||||
|       layer = new Image({ source: source }); | ||||
|     } | ||||
|     return layer; | ||||
| @@ -116,19 +121,19 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|     var layer: Layer = null;  | ||||
|     layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; | ||||
|     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") { | ||||
|       var rt = item.data.layers[layerIndex].rendering as IRenderoutputTiles; | ||||
|       var rt = l.rendering as IRenderoutputTiles; | ||||
|       layer = new VectorTileLayer({ | ||||
|         declutter: true, | ||||
|         source: new VectorTileSource({ | ||||
|           maxZoom: rt.maxzoom, | ||||
|           minZoom: rt.minzoom, | ||||
|           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) => { | ||||
|           return this.getColorFromGradient(l, feature); | ||||
|           return this.getColorFromGradient(item,l, feature); | ||||
|         } | ||||
|       }) | ||||
|     } else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { | ||||
| @@ -137,7 +142,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|         source: new XYZ({ | ||||
|           maxZoom: rt.maxzoom, | ||||
|           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 { | ||||
| @@ -161,7 +166,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|       layer = new VectorLayer({ | ||||
|         source: source, | ||||
|         style: (feature) => { | ||||
|           var key = feature.get("color"); | ||||
|           var key =feature.get("code") + "_" + feature.get("color"); | ||||
|           if (!this.styleCache[key]) { | ||||
|             var color = feature.get("color"); | ||||
|             this.styleCache[key] = new style.Style( | ||||
| @@ -190,9 +195,48 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|     if(l.minzoom) { | ||||
|       layer.setMinZoom(14); | ||||
|     } | ||||
|     if(l.maxzoom) { | ||||
|       layer.setMaxZoom(l.maxzoom); | ||||
|     } | ||||
|     return layer; | ||||
|   } | ||||
|  | ||||
|   createSelectionLayer(itemLayer:IItemLayer):Layer { | ||||
|     var layerIndex = -1; | ||||
|     var layer: Layer = null;  | ||||
|     layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : itemLayer.item.data.layers[0].index; | ||||
|     var data = itemLayer.item.data; | ||||
|     var l:ILayer = (data && data.layers && data.layers.length > 0) ? data.layers[layerIndex] : null; | ||||
|     if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") { | ||||
|       return new VectorTileLayer({ | ||||
|         renderMode: 'vector', | ||||
|         source: itemLayer.layer.getSource(), | ||||
|         style: (feature) => { | ||||
|           if (feature.getId() in this.selectedFeatures) { | ||||
|  | ||||
|             return new style.Style( | ||||
|               { | ||||
|                 fill: new style.Fill({ | ||||
|                   color: 'red' | ||||
|                 }), | ||||
|                 stroke: new style.Stroke({ | ||||
|                   color: 'red', | ||||
|                   width: 1.25 | ||||
|                 }) | ||||
|               } | ||||
|             ); | ||||
|           } | ||||
|         }, | ||||
|         minZoom: itemLayer.layer.getMinZoom(), | ||||
|         maxZoom: itemLayer.layer.getMaxZoom()         | ||||
|       }); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   createExternalLayer(item:IItem,itemLayer:IItemLayer):Layer { | ||||
|     let data = item.data as ILayerData; | ||||
|     var layer: Layer = null;  | ||||
| @@ -212,11 +256,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|         layer = new Tile({ source: source }); | ||||
|         break; | ||||
|       } | ||||
|       case 'TileJSON': { | ||||
|         let source = new TileJSON(data.options); | ||||
|         layer = new Tile({ source: source }); | ||||
|         break; | ||||
|       } | ||||
|       case 'TileArcGISRest': { | ||||
|         let source = new TileArcGISRest(data.options); | ||||
|         layer = new Tile({ source: source }); | ||||
|         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: { | ||||
|         break; | ||||
|       } | ||||
| @@ -245,7 +310,28 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|  | ||||
|   ngOnInit() { | ||||
|     super.ngOnInit(); | ||||
|     if(this.itemLayers) { | ||||
|       this.updateLayers(this.itemLayers); | ||||
|     } else if(this.itemLayer) { | ||||
|       this.updateLayers([this.itemLayer]) | ||||
|     } else { | ||||
|       this.updateLayers([]); | ||||
|     } | ||||
|     this.initialized=true; | ||||
|   } | ||||
|  | ||||
|   installMapEventHandler() { | ||||
|     if(!this.mapEventHandlerInstalled) { | ||||
|       this.map.instance.on(['click', 'pointermove'],this.mapEventHandler); | ||||
|       this.mapEventHandlerInstalled=true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   unInstallMapEventHandler() { | ||||
|     if(this.mapEventHandlerInstalled) { | ||||
|       this.map.instance.un(['click', 'pointermove'],this.mapEventHandler); | ||||
|       this.mapEventHandlerInstalled=false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   addOrUpdateOlLayer(itemLayer:IItemLayer,index:number):Layer { | ||||
| @@ -294,23 +380,52 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | ||||
|       while(olLayers.getLength() > newLayers.length) { | ||||
|         olLayers.removeAt(newLayers.length); | ||||
|       } | ||||
|       this.selectionLayer=null; | ||||
|       if(this.mapEventHandlerInstalled && itemLayers.length==1 && itemLayers[0].item.itemType == 'vnd.farmmaps.itemtype.shape.processed') { | ||||
|         this.selectionLayer = this.createSelectionLayer(itemLayers[0]); | ||||
|         olLayers.push(this.selectionLayer) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   mapEventHandler = (event) => { | ||||
|     //if(event.type === 'click' && !this.onFeatureSelected.observers.length) return; | ||||
|     //if(event.type === 'pointermode' && !this.onFeatureHover.observers.length) return; | ||||
|     if(this.itemLayer && this.itemLayer.layer) { | ||||
|        this.itemLayer.layer.getFeatures(event.pixel).then((features) => { | ||||
|          this.selectedFeatures = {}; | ||||
|          if(!features.length) return; | ||||
|         let fid = features[0].getId(); | ||||
|         this.selectedFeatures[fid] = features[0];     | ||||
|         console.debug(features[0]); | ||||
|         if(this.selectionLayer) this.selectionLayer.changed(); | ||||
|        }) | ||||
|     }     | ||||
|   } | ||||
|  | ||||
|   ngOnChanges(changes: SimpleChanges) { | ||||
|     if (this.instance) { | ||||
|     if (this.instance && this.initialized) { | ||||
|       if (changes['itemLayers']) { | ||||
|         var itemLayers = changes['itemLayers'].currentValue as IItemLayer[]; | ||||
|         this.updateLayers(itemLayers); | ||||
|       } | ||||
|       if (changes['itemLayer']) {       | ||||
|         var itemLayer = changes['itemLayer'].currentValue as IItemLayer; | ||||
|         this.itemLayer = itemLayer | ||||
|         if(itemLayer) { | ||||
|           if(itemLayer.item.itemType == 'vnd.farmmaps.itemtype.shape.processed') { | ||||
|             this.installMapEventHandler(); | ||||
|           } | ||||
|           this.updateLayers([itemLayer]); | ||||
|         } else { | ||||
|           this.unInstallMapEventHandler(); | ||||
|           this.updateLayers([]);           | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
|     this.unInstallMapEventHandler(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -34,7 +34,7 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements | ||||
|   @Input() selectedFeature: Feature; | ||||
|   @Input() selectedItem: IItem; | ||||
|   @Input() styles:IStyles; | ||||
|   @Output() onFeaturesSelected: EventEmitter<Feature> = new EventEmitter<Feature>(); | ||||
|   @Output() onFeatureSelected: EventEmitter<Feature> = new EventEmitter<Feature>(); | ||||
|   @Output() onFeatureHover: EventEmitter<Feature> = new EventEmitter<Feature>(); | ||||
|   private stylesCache:IStyles = {}; | ||||
|  | ||||
| @@ -96,9 +96,9 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements | ||||
|     this.map.instance.addInteraction(this._hoverSelect);       | ||||
|     this._select.on('select', (e) => { | ||||
|       if (e.selected.length > 0 && e.selected[0]) { | ||||
|         this.onFeaturesSelected.emit(e.selected[0]); | ||||
|         this.onFeatureSelected.emit(e.selected[0]); | ||||
|       } else { | ||||
|         this.onFeaturesSelected.emit(null); | ||||
|         this.onFeatureSelected.emit(null); | ||||
|       } | ||||
|     }); | ||||
|     this._hoverSelect.on('select', (e) => { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
|   border-radius:1.75em; | ||||
|   padding:0; | ||||
|   margin-top:0.5em; | ||||
|   display: block; | ||||
| } | ||||
|  | ||||
| #north { | ||||
|   | ||||
| @@ -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"; | ||||
| //   } | ||||
| // } | ||||
| @@ -25,10 +25,10 @@ export class ZoomToExtentComponent implements OnChanges { | ||||
|       let threshold = 40 * rem; | ||||
|       var left = 1 * rem; | ||||
|       var right = 1 * rem; | ||||
|       var bottom = Math.round(size[1] / 2); | ||||
|       var bottom = Math.round((size[1] / 2) + (4*rem)); | ||||
|       var top = 1 * rem; | ||||
|       if (size[0] > threshold) { | ||||
|         bottom = 1 * rem; | ||||
|         bottom = 5 * rem; | ||||
|         left = 23 * rem; | ||||
|       } | ||||
|       options.padding = [top, right, bottom, left]; | ||||
|   | ||||
| @@ -2,7 +2,7 @@ import { Component, Input, OnInit, ComponentFactoryResolver, ViewChild, SimpleCh | ||||
| import { Feature } from 'ol'; | ||||
| import { FeatureListComponent,AbstractFeatureListComponent } from '../feature-list/feature-list.component'; | ||||
| 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 mapActions from '../../actions/map.actions'; | ||||
| import { Store } from '@ngrx/store'; | ||||
| @@ -16,7 +16,8 @@ import { Observable } from 'rxjs'; | ||||
| }) | ||||
| 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 ) { | ||||
|     this.featureLists = [...this.featureLists].reverse(); | ||||
|   } | ||||
|  | ||||
|   @Input() features: Array<Feature> | ||||
| @@ -28,31 +29,45 @@ export class FeatureListContainerComponent { | ||||
|   componentRef:any; | ||||
|  | ||||
|   loadComponent(queryState:IQueryState) { | ||||
|     var componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default | ||||
|     var selected = -1; | ||||
|     for (var i = 0; i < this.featureLists.length; i++) { | ||||
|       if (this.featureLists[i]['forItemType'] == queryState.itemType && this.featureLists[i]['forChild'] && queryState.parentCode && queryState.parentCode != "") { | ||||
|         selected = i; | ||||
|         break; | ||||
|       } else if (this.featureLists[i]['forItemType'] == queryState.itemType) { | ||||
|         selected = i;         | ||||
|         break; | ||||
|     let componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default | ||||
|     let selected = -1; | ||||
|     let maxMatches =0; | ||||
|     let showItem = true; | ||||
|     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(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) { | ||||
|       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) { | ||||
|         this.store.dispatch(new mapActions.CollapseSearch()); | ||||
|       }      | ||||
|     } | ||||
|     const viewContainerRef = this.widgetHost.viewContainerRef; | ||||
|     viewContainerRef.clear(); | ||||
|  | ||||
|     if(showItem) { | ||||
|       this.componentRef = viewContainerRef.createComponent(componentFactory); | ||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).features = null; | ||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).queryState = queryState; | ||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = null; | ||||
|     }     | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|     this.clickedFeature.subscribe((feature => { | ||||
| @@ -66,9 +81,11 @@ export class FeatureListContainerComponent { | ||||
|     } | ||||
|     if ((changes["features"] && changes["features"].currentValue)) { | ||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).features = changes["features"].currentValue; | ||||
|       this.componentRef.changeDetectorRef.detectChanges(); | ||||
|     } | ||||
|     if(changes["selectedFeature"]) { | ||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = changes["selectedFeature"].currentValue; | ||||
|       this.componentRef.changeDetectorRef.detectChanges(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,7 @@ import { WidgetHostDirective } from '../widget-host/widget-host.directive'; | ||||
| export class FeatureListFeatureContainerComponent { | ||||
|  | ||||
|   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListFeatureComponent) public featureLists: AbstractFeatureListFeatureComponent[] ) { | ||||
|     this.featureLists = [...this.featureLists].reverse(); | ||||
|   } | ||||
|  | ||||
|   @Input() feature: Feature; | ||||
| @@ -23,11 +24,27 @@ export class FeatureListFeatureContainerComponent { | ||||
|  | ||||
|   loadComponent() { | ||||
|     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")) { | ||||
|         componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[i]['constructor'] as any); | ||||
|     | ||||
|     let selected = -1; | ||||
|     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; | ||||
|     viewContainerRef.clear(); | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,5 @@ | ||||
| export function ForPackage(packageCode: string) { | ||||
|   return function (constructor:Function) { | ||||
|     constructor.prototype.forPackage = packageCode; | ||||
|   }; | ||||
| } | ||||
| @@ -15,6 +15,7 @@ import { IItem, IListItem } from '@farmmaps/common'; | ||||
| export class ItemListItemContainerComponent { | ||||
|  | ||||
|   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractItemListItemComponent) public itemComponentList: AbstractItemListItemComponent[] ) { | ||||
|     this.itemComponentList = [...this.itemComponentList].reverse(); | ||||
|   } | ||||
|  | ||||
|   @Input() item: IListItem; | ||||
| @@ -23,15 +24,35 @@ export class ItemListItemContainerComponent { | ||||
|  | ||||
|   loadComponent() { | ||||
|     var componentFactory: ComponentFactory<AbstractItemListItemComponent> = this.componentFactoryResolver.resolveComponentFactory(ItemListItemComponent); // default | ||||
|     for (var i = 0; i < this.itemComponentList.length; i++) { | ||||
|       if (this.itemComponentList[i]['forItemType'] && | ||||
|         this.itemComponentList[i]['forItemType'].indexOf(this.item.itemType) >= 0 && | ||||
|         this.itemComponentList[i]['forSourceTask'] && | ||||
|         this.itemComponentList[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0 ) | ||||
|       { | ||||
|         componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.itemComponentList[i]['constructor'] as any); | ||||
|  | ||||
|     let selected = -1; | ||||
|     let maxMatches =0; | ||||
|     let showItem = true; | ||||
|     for (let i = 0; i < this.itemComponentList.length; i++) { | ||||
|       let matches=0; | ||||
|       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; | ||||
|     viewContainerRef.clear(); | ||||
|  | ||||
|   | ||||
| @@ -36,10 +36,12 @@ export class LayerSwitcher  implements OnInit,OnChanges{ | ||||
|  | ||||
|  | ||||
|   handleClick(event:Event) { | ||||
|     event.stopPropagation(); | ||||
|     this.store.dispatch(new mapActions.ShowLayerSwitcher(true)); | ||||
|   } | ||||
|  | ||||
|   close(event:Event) { | ||||
|     event.stopPropagation(); | ||||
|     this.store.dispatch(new mapActions.ShowLayerSwitcher(false)); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -1,11 +1,14 @@ | ||||
| <table class="container" *ngIf="showLegend()"> | ||||
|   <tr> | ||||
|     <td colspan="2"> | ||||
|     <td colspan="3"> | ||||
|       <div class="title"> | ||||
|         <h4 *ngIf="showTitle">{{layer.name}}</h4> | ||||
|         <b *ngIf="layer.unit">({{layer.unit}})</b> | ||||
|       </div> | ||||
|     </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"> | ||||
|       <div class="title" *ngIf="histogramenabled"> | ||||
|         <h4>{{histogram}}</h4> | ||||
| @@ -14,7 +17,7 @@ | ||||
|     </td> | ||||
|   </tr> | ||||
|   <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="histogram-items"> | ||||
|       <div *ngIf="showHistogram()"> | ||||
| @@ -25,14 +28,15 @@ | ||||
|     </td> | ||||
|   </tr> | ||||
|   <tr *ngIf="showHistogram()"> | ||||
|     <td colspan="4" class="pb-2 pt-2"> | ||||
|       <a i18n-title title="more info"><i class="fa fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a> | ||||
|       <div class="info pt-2"  [ngbCollapse]="hideHistogramDetails"> | ||||
|         <div><span i18n>Average: </span>{{layer.renderer.band.histogram.mean| number:'1.0-3'}}</div> | ||||
|         <div><span i18n>Standard deviation: </span>{{layer.renderer.band.histogram.stddev| number:'1.0-3'}}</div> | ||||
|         <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> | ||||
|         <div *ngIf="showConfidenceInterval()"><span i18n>Confidence interval:</span> {{layer.renderer.band.histogram.confidence * 100| number:'1.0-0'}}%</div>   | ||||
|     <td colspan="4" class="pb-1 pt-1"> | ||||
|       <div class="info" [ngbCollapse]="hideHistogramDetails"> | ||||
|         <ng-container *ngIf="bandContainsStatistics(); else histogram"> | ||||
|           <fm-map-statistics-details [statistics]="layer.renderer.band.statistics"></fm-map-statistics-details> | ||||
|         </ng-container> | ||||
|  | ||||
|         <ng-template #histogram> | ||||
|           <fm-map-histogram-details [histogram]="layer.renderer.band.histogram"></fm-map-histogram-details> | ||||
|         </ng-template> | ||||
|       </div> | ||||
|     </td> | ||||
|   </tr> | ||||
|   | ||||
| @@ -20,6 +20,7 @@ span.color { | ||||
|   display:inline-block; | ||||
|   width: 1.5em; | ||||
|   height: 1.5em; | ||||
|   border: 2px solid; | ||||
| } | ||||
|  | ||||
| .legend-items { | ||||
|   | ||||
| @@ -53,6 +53,10 @@ export class LegendComponent implements OnInit,AfterViewInit { | ||||
|     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 { | ||||
|     const hex = c.toString(16); | ||||
|     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"; | ||||
|   } | ||||
|  | ||||
|   showConfidenceInterval():boolean { | ||||
|     return this.histogramenabled && this.layer.renderer.band.histogram.confidence != null && this.layer.renderer.band.histogram.confidence != 1; | ||||
|   bandContainsStatistics(): boolean { | ||||
|     return this.layer.renderer.band.statistics != null; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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; | ||||
| } | ||||
| @@ -12,6 +12,7 @@ | ||||
|   panelCollapsed:panelCollapsed$|async, | ||||
|   searchMinified:searchMinified$|async, | ||||
|   selectedItem:selectedItem$|async, | ||||
|   parentItem:parentItem$|async, | ||||
|   queryState:queryState$|async, | ||||
|   searchCollapsed:searchCollapsed$|async, | ||||
|   clearEnabled:clearEnabled$|async, | ||||
| @@ -34,11 +35,11 @@ | ||||
|     <fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers> | ||||
|     <fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers> | ||||
|     <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" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-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"> | ||||
|   <!--    <switch2d3d></switch2d3d>--> | ||||
|       <router-outlet name="map-controls"></router-outlet> | ||||
|       <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-rotation-reset></fm-map-rotation-reset> | ||||
| @@ -57,7 +58,7 @@ | ||||
|         </div> | ||||
|  | ||||
|         <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 *ngIf="state.features.length == 0" class="no-results  m-2"> | ||||
|           <div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| @import "~bootstrap/scss/bootstrap.scss"; | ||||
|  | ||||
| aol-map { position:absolute;width:100%;height:100%;} | ||||
| aol-map { position:absolute;width:100%;height:calc(100vh + 4rem);} | ||||
|  | ||||
| .arrow { | ||||
|   top: 3rem; | ||||
| @@ -49,7 +49,7 @@ aol-map { position:absolute;width:100%;height:100%;} | ||||
| .control-container { | ||||
|   position: absolute; | ||||
|   right: 1em; | ||||
|   bottom: 1em; | ||||
|   bottom: 5em; | ||||
| } | ||||
|  | ||||
| switch2d3d { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone } from '@angular/core'; | ||||
| import { Location } from '@angular/common'; | ||||
| import { Observable, Subject, Subscription, from,of  } from 'rxjs'; | ||||
| import { Observable, Subject, Subscription, from,of ,EMPTY } from 'rxjs'; | ||||
| import { withLatestFrom, switchMap,skip  } from 'rxjs/operators'; | ||||
| import { Router, ActivatedRoute, ParamMap } from '@angular/router'; | ||||
| import { Store } from '@ngrx/store'; | ||||
| @@ -12,7 +12,7 @@ import * as mapActions from '../../actions/map.actions'; | ||||
| import { IMapState} from '../../models/map.state'; | ||||
| import { ISelectedFeatures } from '../../models/selected.features'; | ||||
| import { IItemLayer } from '../../models/item.layer'; | ||||
| import { IQueryState } from '@farmmaps/common'; | ||||
| import { IListItem, IQueryState } from '@farmmaps/common'; | ||||
| import { IPeriodState } from '../../models/period.state'; | ||||
| import {IStyles} from '../../models/style.cache'; | ||||
| import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component'; | ||||
| @@ -64,8 +64,9 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   public selectedFeature$: Observable<Feature> = this.store.select(mapReducers.selectGetSelectedFeature); | ||||
|   public clickedFeature: Subject<Feature> = new Subject<Feature>(); | ||||
|   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 state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState); | ||||
|   public state$:Observable<{mapState:IMapState,queryState:IQueryState}> = this.store.select(mapReducers.selectGetState); | ||||
|   public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod); | ||||
|   public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled); | ||||
|   public searchCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetSearchCollapsed); | ||||
| @@ -78,8 +79,8 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   public overlayLayersCollapsed: boolean = true; | ||||
|   public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent); | ||||
|   public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles); | ||||
|   private setStateCount$:Observable<number> =  this.store.select(mapReducers.selectgetSetStateCount); | ||||
|   private lastUrl = ""; | ||||
|   private initialized: boolean = false; | ||||
|  | ||||
|   @ViewChild('map') map; | ||||
|  | ||||
| @@ -93,10 +94,10 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|     private geolocationService: GeolocationService, | ||||
|     private zone: NgZone, | ||||
|     private deviceorientationService:DeviceOrientationService) { | ||||
|       this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$),withLatestFrom(this.setStateCount$)).subscribe(([[queryState,mapState],setStateCount]) =>{ | ||||
|       this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$)).subscribe(([queryState,mapState]) =>{ | ||||
|         if(queryState) { | ||||
|           let newQueryState = tassign(mapReducers.initialQueryState); | ||||
|           console.debug(`Do Query ${setStateCount}`); | ||||
|           console.debug(`Do Query`); | ||||
|           let urlparts=[]; | ||||
|           if (queryState.itemCode && queryState.itemCode != "") { | ||||
|             if(queryState.itemType && queryState.itemType!= "") { | ||||
| @@ -114,13 +115,19 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|           if(urlparts.length==0 ) { | ||||
|             newQueryState.itemCode = queryState.itemCode; | ||||
|             this.zone.run(() => { | ||||
|               this.store.dispatch(new mapActions.SetQueryState(newQueryState,false)); | ||||
|               this.replaceUrl(mapState,newQueryState,false);              | ||||
|             }) | ||||
|           } else { | ||||
|             this.router.navigate(urlparts); | ||||
|           } | ||||
|         } | ||||
|       }); | ||||
|       this.store.dispatch(new mapActions.Init()); | ||||
|       // this.store.select(commonReducers.getRootItems).subscribe((l) => { | ||||
|       //    if(l && l.length>0) { | ||||
|       //      this.store.dispatch(new mapActions.Init()); | ||||
|       //    } | ||||
|       // }); | ||||
|   } | ||||
|  | ||||
|   @HostListener('document:keyup', ['$event']) | ||||
| @@ -156,6 +163,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|     this.initialized = false; | ||||
|     console.debug("Init"); | ||||
|     this.store.dispatch(new mapActions.Clear()); | ||||
|     this.selectedFeatures$.next({x:0,y:0,features:[]}); | ||||
| @@ -233,30 +241,25 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|  | ||||
|     // url to state | ||||
|  | ||||
|       let urlMapState = this.getMapStateFromUrl(this.route.snapshot.paramMap); | ||||
|       let urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap); | ||||
|       if(urlQueryState && urlMapState) { | ||||
|         this.store.dispatch(new mapActions.SetState(urlMapState,urlQueryState)); | ||||
|         window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(urlMapState)); | ||||
|       } else if(urlQueryState) { | ||||
|         this.store.dispatch(new mapActions.SetQueryState(urlQueryState)); | ||||
|       }  else { | ||||
|         this.store.dispatch(new mapActions.SetReplaceUrl(true)); | ||||
|       } | ||||
|  | ||||
|       this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => { | ||||
|       var newMapState: IMapState = state.mapState; | ||||
|       var newQueryState: IQueryState = state.queryState; | ||||
|       | ||||
|       var queryStateChanged = false; | ||||
|       var mapStateChanged = false; | ||||
|       let urlMapState = this.getMapStateFromUrl(params); | ||||
|       if(urlMapState) { | ||||
|         newMapState = urlMapState; | ||||
|         mapStateChanged = this.serializeMapState(state.mapState) != this.serializeMapState(newMapState); | ||||
|       } | ||||
|  | ||||
|          if(this.initialized) { | ||||
|           let urlQueryState = this.getQueryStateFromUrl(params); | ||||
|       if(urlQueryState) { | ||||
|         newQueryState = urlQueryState; | ||||
|         queryStateChanged = this.serializeService.serialize(state.queryState) !=  this.serializeService.serialize(urlQueryState); | ||||
|           if( this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState)) { | ||||
|             return of(new mapActions.SetState(state.mapState,urlQueryState)); | ||||
|           }           | ||||
|    | ||||
|       if(queryStateChanged && mapStateChanged && state.setStateCount ==0) { | ||||
|         return of(new mapActions.SetState(newMapState,newQueryState)); | ||||
|         window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(newMapState)); | ||||
|       } else if(queryStateChanged) { | ||||
|         return of(new mapActions.SetQueryState(newQueryState)); | ||||
|       } return of(new mapActions.SetReplaceUrl(true)); | ||||
|          }  | ||||
|          return EMPTY;         | ||||
|       })).subscribe((action) => { | ||||
|         if(action) { | ||||
|           this.zone.run(() => { | ||||
| @@ -270,7 +273,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|  | ||||
|     this.stateSub = this.state$.pipe(switchMap((state) => { | ||||
|       let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState); | ||||
|       if(this.lastUrl!=newUrl && state.setStateCount>0) { | ||||
|       if(this.lastUrl!=newUrl) { | ||||
|         this.lastUrl=newUrl; | ||||
|         return of(state); | ||||
|       } | ||||
| @@ -279,14 +282,11 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|       } | ||||
|       })).subscribe((newUrlState) =>{ | ||||
|         if(newUrlState) { | ||||
|           console.debug(`State to url ${newUrlState.setStateCount}`); | ||||
|           console.debug(`State to url`); | ||||
|           this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl); | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|     setTimeout(() => { | ||||
|       this.map.instance.updateSize(); | ||||
|     }, 500); | ||||
|     this.initialized = true;    | ||||
|   } | ||||
|  | ||||
|   handleSearchCollapse(event) { | ||||
| @@ -318,45 +318,55 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   } | ||||
|  | ||||
|   replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) { | ||||
|       | ||||
|       let newMapState = this.serializeMapState(mapState); | ||||
|       let newQueryState = this.serializeService.serialize(queryState); | ||||
|       let currentMapState = this.serializeMapState(this.getMapStateFromUrl(this.route.snapshot.paramMap)); | ||||
|       let urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap); | ||||
|       let currentQueryState = urlQueryState==null?"":this.serializeService.serialize(urlQueryState); | ||||
|       if(mapState.baseLayerCode!="" && ((newMapState!= currentMapState)  || (newQueryState!=currentQueryState))) { | ||||
|         let parts =["."]; | ||||
|         parts.push(mapState.xCenter.toFixed(5)); | ||||
|         parts.push(mapState.yCenter.toFixed(5)); | ||||
|         parts.push( mapState.zoom.toFixed(0)); | ||||
|         parts.push( mapState.rotation.toFixed(2));         | ||||
|       if(mapState.baseLayerCode!="") { | ||||
|         parts.push(mapState.baseLayerCode); | ||||
|         parts.push( this.serializeService.serialize(queryState)); | ||||
|          | ||||
|         console.debug("Replace url",parts); | ||||
|         this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent }); | ||||
|       } | ||||
|   } | ||||
|  | ||||
|   handleOnMoveEnd(event) {     | ||||
|     console.debug("Move end"); | ||||
|     if(this.initialized) { | ||||
|       this.zone.run(() =>{ | ||||
|         console.debug("Move end"); | ||||
|         var map = event.map; | ||||
|         var view = map.getView(); | ||||
|         var rotation = view.getRotation(); | ||||
|         var zoom = view.getZoom(); | ||||
|         var center = transform(view.getCenter(), view.getProjection(), "EPSG:4326"); | ||||
|       var extent = view.calculateExtent(this.map.instance.getSize()); | ||||
|         var viewExtent = view.calculateExtent(this.map.instance.getSize()); | ||||
|         let mapState: IMapState = { xCenter: center[0], yCenter: center[1], zoom: zoom, rotation: rotation, baseLayerCode: null }; | ||||
|       let state = { mapState: mapState, extent: extent }; | ||||
|         let state = { mapState: mapState, viewExtent: viewExtent }; | ||||
|         console.debug("Center: ",center[0],center[1] ); | ||||
|         let source = from([state]); | ||||
|       source.pipe(withLatestFrom(this.selectedBaseLayer$),withLatestFrom(this.setStateCount$)).subscribe(([[state, baselayer],setStateCount]) => { | ||||
|         source.pipe(withLatestFrom(this.selectedBaseLayer$)).subscribe(([state, baselayer]) => { | ||||
|           if (mapState && baselayer) { // do not react on first move | ||||
|             let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code }); | ||||
|             this.store.dispatch(new mapActions.SetMapState(newMapState)); | ||||
|           this.store.dispatch(new mapActions.SetViewExtent(state.extent)); | ||||
|             this.store.dispatch(new mapActions.SetViewExtent(state.viewExtent)); | ||||
|           } | ||||
|         }); | ||||
|       }); | ||||
|     }    | ||||
|   } | ||||
|  | ||||
|   handleOnMouseDown(event: MouseEvent) { | ||||
|     event.stopPropagation(); | ||||
|     this.zone.run(() =>{ | ||||
|       this.store.dispatch(new mapActions.CollapseSearch()); | ||||
|       this.store.dispatch(new commonActions.CloseAll()); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
| @@ -365,7 +375,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | ||||
|   } | ||||
|  | ||||
|   handleClearSearch(event) { | ||||
|     this.store.dispatch(new commonActions.Escape(true, false)); | ||||
|     this.store.dispatch(new mapActions.Clear()); | ||||
|   } | ||||
|  | ||||
|   handleOnDelete(itemLayer: IItemLayer) { | ||||
|   | ||||
| @@ -18,3 +18,8 @@ | ||||
| .custom-day.faded { | ||||
|   background-color: rgba(2, 117, 216, 0.5); | ||||
| } | ||||
|  | ||||
| ngb-datepicker { | ||||
|   overflow: hidden; | ||||
|   max-width: calc(100vw - 3em); | ||||
| } | ||||
| @@ -13,9 +13,11 @@ import { IItemLayer } from '../../models/item.layer'; | ||||
| export class SelectedItemContainerComponent { | ||||
|  | ||||
|   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractSelectedItemComponent) public selectedItemComponents: AbstractSelectedItemComponent[] ) { | ||||
|     this.selectedItemComponents = [...this.selectedItemComponents].reverse(); | ||||
|   } | ||||
|  | ||||
|   @Input() item: IItem; | ||||
|   @Input() parentItem: IItem; | ||||
|   @Input() itemLayer:IItemLayer; | ||||
|   @Input() overlayLayers:Array<IItemLayer>; | ||||
|  | ||||
| @@ -23,27 +25,41 @@ export class SelectedItemContainerComponent { | ||||
|  | ||||
|   loadComponent() { | ||||
|     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) { | ||||
|       componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithTypeAndTask['constructor'] as any); | ||||
|     } else { | ||||
|       let firstComponentWithType = this.selectedItemComponents | ||||
|         .find(value => value['forSourceTask'] == null && | ||||
|           value['forItemType'] == this.item.itemType); | ||||
|  | ||||
|       if (firstComponentWithType) { | ||||
|         componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithType['constructor'] as any); | ||||
|     let selected = -1; | ||||
|     let maxMatches =0; | ||||
|     let showItem = true; | ||||
|     for (let i = 0; i < this.selectedItemComponents.length; i++) { | ||||
|       let matches=0; | ||||
|       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(criteria==matches && matches > maxMatches) { | ||||
|         selected=i; | ||||
|         maxMatches = matches; | ||||
|       } | ||||
|     } | ||||
|     if (selected >= 0) { | ||||
|       componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.selectedItemComponents[selected]['constructor'] as any); | ||||
|     } | ||||
|  | ||||
|     const viewContainerRef = this.widgetHost.viewContainerRef; | ||||
|     viewContainerRef.clear(); | ||||
|  | ||||
|     const componentRef = viewContainerRef.createComponent(componentFactory); | ||||
|     (<AbstractSelectedItemComponent>componentRef.instance).item = this.item; | ||||
|     (<AbstractSelectedItemComponent>componentRef.instance).parentItem = this.parentItem; | ||||
|     (<AbstractSelectedItemComponent>componentRef.instance).itemLayer = this.itemLayer; | ||||
|     (<AbstractSelectedItemComponent>componentRef.instance).overlayLayers = this.overlayLayers; | ||||
|   } | ||||
|   | ||||
| @@ -7,9 +7,10 @@ import { commonReducers, ItemTypeService, IItem, Item, ItemService, FolderServic | ||||
| import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router'; | ||||
| import { ForItemType } from '../for-item/for-itemtype.decorator'; | ||||
| import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; | ||||
| import { Observable } from 'rxjs'; | ||||
| import { Observable,of } from 'rxjs'; | ||||
| import {GeoJSON} from 'ol/format'; | ||||
| import {getArea} from 'ol/sphere'; | ||||
| import { withLatestFrom,switchMap,combineLatest } from 'rxjs/operators'; | ||||
|  | ||||
|  | ||||
| @ForItemType("vnd.farmmaps.itemtype.cropfield") | ||||
| @@ -38,6 +39,25 @@ export class SelectedItemCropfieldComponent extends AbstractSelectedItemComponen | ||||
|   } | ||||
|  | ||||
|   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); | ||||
|       })); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|     <div class="card-body"> | ||||
|       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> | ||||
|       <div class="card menu-card"> | ||||
|         <h2 *ngIf="parentItem">{{parentItem.name}}</h2> | ||||
|         <h1>{{item.name}}</h1> | ||||
|       </div> | ||||
|       <div class="legend-container" *ngIf="item?.data.layers;let layers"> | ||||
| @@ -13,7 +14,8 @@ | ||||
|               <option *ngFor="let l of layers;" [value]="l.index"  [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option> | ||||
|             </select> | ||||
|           </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 class="card menu-card"> | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|     <div class="card-body"> | ||||
|       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> | ||||
|       <div class="card menu-card"> | ||||
|         <h2 *ngIf="parentItem">{{parentItem.name}}</h2> | ||||
|         <h1>{{item.name}}</h1> | ||||
|       </div> | ||||
|       <div class="legend-container" *ngIf="item?.data.layers;let layers"> | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
|     <div class="card-body"> | ||||
|       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> | ||||
|       <div class="card menu-card"> | ||||
|         <h2 *ngIf="parentItem">{{parentItem.name}}</h2> | ||||
|         <h1>{{item.name}}</h1> | ||||
|       </div> | ||||
|       <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> | ||||
|             </select> | ||||
|           </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 class="card menu-card"> | ||||
|         <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> <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> <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> <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> <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> <span i18n>Show chart</span></a></li> | ||||
|         </ul> | ||||
|       </div> | ||||
|   | ||||
| @@ -35,7 +35,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent | ||||
|   nextDate():Date { | ||||
|     let temporalItemLayer = this.itemLayer as  ITemporalItemLayer; | ||||
|     if(temporalItemLayer.nextItemLayer.item)  | ||||
|       return temporalItemLayer.nextItemLayer.item.dataDate; | ||||
|       return new Date(Date.parse(temporalItemLayer.nextItemLayer.item.dataDate)) ; | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
| @@ -47,7 +47,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent | ||||
|   previousDate():Date { | ||||
|     let temporalItemLayer = this.itemLayer as  ITemporalItemLayer; | ||||
|     if(temporalItemLayer.previousItemLayer.item)  | ||||
|       return temporalItemLayer.previousItemLayer.item.dataDate; | ||||
|       return new Date(Date.parse(temporalItemLayer.previousItemLayer.item.dataDate)); | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
| @@ -74,7 +74,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent | ||||
|   } | ||||
|  | ||||
|   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; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| <div *ngIf="item;let item"> | ||||
|   <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)"> | ||||
|       <i [ngClass]="itemTypeService.getIcon(item.itemType)"></i> | ||||
|     </div> | ||||
|   | ||||
| @@ -11,7 +11,8 @@ import { IItemLayer } from '../../models/item.layer'; | ||||
| @Injectable() | ||||
| @Directive() | ||||
| export abstract class AbstractSelectedItemComponent { | ||||
|   @Input() item: IItem | ||||
|   @Input() item: IItem; | ||||
|   @Input() parentItem: IItem; | ||||
|   @Input() itemLayer: IItemLayer; | ||||
|   @Input() overlayLayers: Array<IItemLayer>; | ||||
|   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(); | ||||
|     this.location.back(); | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
| @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) { | ||||
|     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); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -3,8 +3,8 @@ import { Injectable } from '@angular/core'; | ||||
| import { Store, Action } from '@ngrx/store'; | ||||
| import { Effect, Actions,ofType } from '@ngrx/effects'; | ||||
|  | ||||
| import { Observable , of, interval } from 'rxjs'; | ||||
| import { withLatestFrom, switchMap, map, catchError, mergeMap,delayWhen } from 'rxjs/operators'; | ||||
| import { EMPTY, Observable , of } from 'rxjs'; | ||||
| import { withLatestFrom, switchMap, map, catchError, mergeMap } from 'rxjs/operators'; | ||||
|  | ||||
| import {GeoJSON,WKT} from 'ol/format'; | ||||
| import {Feature} from 'ol'; | ||||
| @@ -103,17 +103,10 @@ export class MapEffects { | ||||
|         catchError(error => of(new commonActions.Fail(error)))); | ||||
|     })); | ||||
|  | ||||
|   @Effect() | ||||
|   initRootItems$: Observable<Action> = this.actions$.pipe( | ||||
|     ofType(commonActions.INITROOTSUCCESS), | ||||
|     map((action) => new mapActions.Init() | ||||
|     )); | ||||
|  | ||||
|   @Effect() | ||||
|   startSearch$: Observable<Action> = this.actions$.pipe( | ||||
|     ofType(mapActions.STARTSEARCH), | ||||
|     withLatestFrom(this.store$.select(mapReducers.selectgetSetStateCount)), | ||||
|     switchMap(([action,setStateCount]) => { | ||||
|     switchMap((action) => { | ||||
|       let a = action as mapActions.StartSearch; | ||||
|       var startDate = a.queryState.startDate; | ||||
|       var endDate = a.queryState.endDate; | ||||
| @@ -126,7 +119,7 @@ export class MapEffects { | ||||
|                 f.id = f.properties["code"]; | ||||
|               } | ||||
|             } | ||||
|             return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState,setStateCount)); | ||||
|             return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState)); | ||||
|           } | ||||
|           ), | ||||
|           catchError(error => of(new commonActions.Fail(error)))); | ||||
| @@ -140,7 +133,6 @@ export class MapEffects { | ||||
|   @Effect() | ||||
|   zoomToExtent$: Observable<Action> = this.actions$.pipe( | ||||
|     ofType(mapActions.STARTSEARCHSUCCESS), | ||||
|     delayWhen(action => (action as mapActions.StartSearchSuccess).setStateCount == 1 ? interval(500):interval(0)), | ||||
|     mergeMap((action: mapActions.StartSearchSuccess) => { | ||||
|       let actions =[]; | ||||
|       actions.push(new commonActions.SetMenuVisible(false)); | ||||
| @@ -150,9 +142,11 @@ export class MapEffects { | ||||
|           for (let f of action.features) { | ||||
|             extend(extent, (f as Feature).getGeometry().getExtent()); | ||||
|           } | ||||
|         }   | ||||
|           if(action.features && action.features.length >0) { | ||||
|             actions.push(new mapActions.SetExtent(extent)); | ||||
|           }   | ||||
|         } | ||||
|       } | ||||
|       return actions; | ||||
|     })); | ||||
|  | ||||
| @@ -167,13 +161,19 @@ export class MapEffects { | ||||
|   selectItem$: Observable<Action> = this.actions$.pipe( | ||||
|     ofType(mapActions.SELECTITEM), | ||||
|     withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)), | ||||
|     withLatestFrom(this.store$.select(mapReducers.getSetStateCount)), | ||||
|       switchMap(([[action, selectedItem],setStateCount]) => { | ||||
|       switchMap(([action, selectedItem]) => { | ||||
|         let a = action as mapActions.SelectItem; | ||||
|         let itemCode = selectedItem ? selectedItem.code : ""; | ||||
|         if (a.itemCode != itemCode || setStateCount == 1) { | ||||
|         if (a.itemCode != itemCode) { | ||||
|           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)))) | ||||
|         } else { | ||||
|           return []; | ||||
| @@ -203,7 +203,7 @@ export class MapEffects { | ||||
|           return this.itemService$.getChildItemList(action.item.code,null).pipe( | ||||
|             map(items => new mapActions.SelectTemporalItemsSuccess( | ||||
|               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)))); | ||||
| @@ -247,7 +247,14 @@ export class MapEffects { | ||||
|       let itemChangedAction = action as commonActions.ItemChangedEvent; | ||||
|       if (selectedItem && selectedItem.code == itemChangedAction.itemCode) { | ||||
|         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)))); | ||||
|       } else { | ||||
|         return []; | ||||
| @@ -262,20 +269,12 @@ export class MapEffects { | ||||
|       } else { | ||||
|         newAction= new mapActions.StartSearch(queryState); | ||||
|       } | ||||
|       return of(newAction);   | ||||
|     } else { | ||||
|       return of(new commonActions.Escape(true,false)); | ||||
|     } | ||||
|   }  | ||||
|        | ||||
|   @Effect() | ||||
|   setQueryState$: Observable<Action> = this.actions$.pipe( | ||||
|     ofType(mapActions.SETQUERYSTATE), | ||||
|     withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)), | ||||
|     switchMap(([action,inSearch]) => { | ||||
|       let a = action as mapActions.SetQueryState; | ||||
|        return this.getActionFromQueryState(a.queryState,inSearch); | ||||
|       })); | ||||
|     } else { | ||||
|       newAction = new mapActions.Clear(); | ||||
|     } | ||||
|     return of(newAction); | ||||
|   } | ||||
|  | ||||
|   @Effect() | ||||
|   setState$: Observable<Action> = this.actions$.pipe( | ||||
| @@ -286,6 +285,18 @@ export class MapEffects { | ||||
|       return this.getActionFromQueryState(a.queryState,inSearch); | ||||
|     })); | ||||
|  | ||||
|     @Effect() | ||||
|     escape$:Observable<Action> = this.actions$.pipe( | ||||
|       ofType(commonActions.ESCAPE), | ||||
|       switchMap((action) => { | ||||
|         let a = action as commonActions.Escape; | ||||
|         if(a.escapeKey) { | ||||
|           return of(new mapActions.Clear()); | ||||
|         } else { | ||||
|           return EMPTY; | ||||
|         }        | ||||
|       })); | ||||
|  | ||||
|     constructor(private actions$: Actions, private store$: Store<mapReducers.State>, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService,private itemTypeService$:ItemTypeService) { | ||||
|     this._geojsonFormat = new GeoJSON(); | ||||
|     this._wktFormat = new WKT(); | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import { IDataLayer } from '@farmmaps/common'; | ||||
|  | ||||
| export interface IColor { | ||||
|   red: number, | ||||
|   green: number, | ||||
| @@ -26,6 +28,25 @@ export interface IHistogram { | ||||
|   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 { | ||||
|   relativestop: number, | ||||
|   color: IColor | ||||
| @@ -39,7 +60,8 @@ export interface IColorMap { | ||||
| } | ||||
|  | ||||
| export interface IBand { | ||||
|   histogram: IHistogram | ||||
|   histogram: IHistogram; | ||||
|   statistics: IStatistics; | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -64,11 +86,9 @@ export interface IRenderoutputImage { | ||||
|   extent: [number,number,number,number] | ||||
| } | ||||
|  | ||||
| export interface ILayer { | ||||
|   name: string, | ||||
|   unit: string, | ||||
|   index: number, | ||||
|   scale: number, | ||||
| export interface ILayer extends IDataLayer { | ||||
|   renderer: IRenderer, | ||||
|   rendering: IRenderoutput | ||||
|   rendering: IRenderoutput, | ||||
|   minzoom: number, | ||||
|   maxzoom: number | ||||
| } | ||||
|   | ||||
| @@ -44,6 +44,7 @@ export interface State { | ||||
|   panelCollapsed: boolean, | ||||
|   selectedFeature: Feature, | ||||
|   selectedItem:IItem, | ||||
|   parentItem:IItem, | ||||
|   clearEnabled: boolean, | ||||
|   searchCollapsed: boolean, | ||||
|   searchMinified: boolean, | ||||
| @@ -56,8 +57,8 @@ export interface State { | ||||
|   selectedOverlayLayer: IItemLayer, | ||||
|   styles:IStyles, | ||||
|   showLayerSwitcher:boolean, | ||||
|   setStateCount:number, | ||||
|   inSearch:boolean, | ||||
|   inZoom:boolean, | ||||
|   replaceUrl:boolean | ||||
| } | ||||
|  | ||||
| @@ -82,6 +83,7 @@ export const initialState: State = { | ||||
|   panelCollapsed: false, | ||||
|   selectedFeature: null, | ||||
|   selectedItem: null, | ||||
|   parentItem: null, | ||||
|   clearEnabled: false, | ||||
|   searchCollapsed: true, | ||||
|   searchMinified:false, | ||||
| @@ -94,8 +96,8 @@ export const initialState: State = { | ||||
|   selectedItemLayer: null, | ||||
|   styles: {}, | ||||
|   showLayerSwitcher: false, | ||||
|   setStateCount: 0, | ||||
|   inSearch:false, | ||||
|   inZoom:false, | ||||
|   replaceUrl:true | ||||
| } | ||||
|  | ||||
| @@ -108,16 +110,16 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|     case mapActions.SETMAPSTATE: { | ||||
|       let a = action as mapActions.SetMapState; | ||||
|       return tassign(state, { | ||||
|         mapState: a.mapState,setStateCount: state.setStateCount+1 | ||||
|         mapState: a.mapState | ||||
|       }); | ||||
|     } | ||||
|     case mapActions.SETQUERYSTATE: { | ||||
|       let a = action as mapActions.SetQueryState; | ||||
|       return tassign(state, { queryState: tassign(a.queryState ),setStateCount: state.setStateCount+1,replaceUrl:a.replaceUrl}); | ||||
|       return tassign(state, { queryState: tassign(a.queryState ),replaceUrl:a.replaceUrl}); | ||||
|     } | ||||
|     case mapActions.SETSTATE: { | ||||
|       let a = action as mapActions.SetState; | ||||
|       return tassign(state, { mapState: tassign(a.mapState), queryState: tassign(a.queryState),setStateCount: state.setStateCount+1}); | ||||
|       return tassign(state, { mapState: tassign(a.mapState), queryState: tassign(a.queryState)}); | ||||
|     } | ||||
|     case mapActions.SETVIEWEXTENT: { | ||||
|       let a = action as mapActions.SetViewExtent; | ||||
| @@ -145,7 +147,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|     case mapActions.SELECTITEM: { | ||||
|       let a = action as mapActions.SelectItem; | ||||
|       let itemCode = state.selectedItem ? state.selectedItem.code : ""; | ||||
|       let inSearch = (a.itemCode != itemCode || state.setStateCount == 1) | ||||
|       let inSearch = a.itemCode != itemCode; | ||||
|       return tassign(state, { | ||||
|         selectedItem: null, | ||||
|         selectedItemLayer: null, | ||||
| @@ -165,6 +167,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|       return tassign(state, { | ||||
|         inSearch:false, | ||||
|         selectedItem: a.item, | ||||
|         parentItem: a.parentItem, | ||||
|         selectedItemLayer: itemLayer, | ||||
|         panelVisible: a.item != null, | ||||
|         clearEnabled: a.item != null, | ||||
| @@ -307,6 +310,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|     case mapActions.COLLAPSESEARCH: { | ||||
|       return tassign(state, { searchCollapsed: state.panelVisible ? false: true}); | ||||
|     } | ||||
|     case commonActions.CLOSEALL: { | ||||
|       return tassign(state, { searchCollapsed: state.panelVisible ? false: true,showLayerSwitcher:false}); | ||||
|     } | ||||
|     case mapActions.SETEXTENT: { | ||||
|       let a = action as mapActions.SetExtent; | ||||
|       return tassign(state, { extent: a.extent }); | ||||
| @@ -401,9 +407,13 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|       } | ||||
|       var selectedBaseLayer: IItemLayer = null; | ||||
|       var mapState = tassign(state.mapState); | ||||
|       let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode)[0];  | ||||
|       if (baseLayers.length > 0 && mapState.baseLayerCode != "" && sb) { | ||||
|         selectedBaseLayer = sb; | ||||
|       let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode); | ||||
|       let db = baseLayers.filter(layer => layer.item.data && layer.item.data.default === true); | ||||
|       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; | ||||
|       } else if (baseLayers.length > 0) { | ||||
|         selectedBaseLayer = baseLayers[0]; | ||||
| @@ -426,10 +436,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|       let a = action as mapActions.SelectOverlayLayer; | ||||
|       return tassign(state, { selectedOverlayLayer: a.itemLayer }); | ||||
|     } | ||||
|     case commonActions.ESCAPE: { | ||||
|       let a = action as commonActions.Escape; | ||||
|    | ||||
|     case mapActions.CLEAR: { | ||||
|       let newQueryState = tassign(state.queryState, { query: null, tags: null, itemCode: null, parentCode: null, itemType: null }); | ||||
|       if (a.escapeKey) { | ||||
|       return tassign(state, { | ||||
|         panelVisible: false, | ||||
|         panelCollapsed:false, | ||||
| @@ -442,11 +451,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|         searchMinified: false, | ||||
|         features: [], | ||||
|         query:initialState.query, | ||||
|           showLayerSwitcher: false | ||||
|         showLayerSwitcher: false, | ||||
|         extent: null | ||||
|       }); | ||||
|       } else { | ||||
|         return tassign(state, {}); | ||||
|       } | ||||
|     } | ||||
|     case mapActions.SETSTYLE:{ | ||||
|       let a = action as mapActions.SetStyle; | ||||
| @@ -462,9 +469,6 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | ||||
|       let a= action as mapActions.SetReplaceUrl; | ||||
|       return tassign(state,{replaceUrl:a.replaceUrl}); | ||||
|     }    | ||||
|     case mapActions.CLEAR: { | ||||
|       return tassign(state,{setStateCount:0}); | ||||
|     } | ||||
|     default: { | ||||
|       return state; | ||||
|     } | ||||
| @@ -478,6 +482,7 @@ export const getPanelVisible = (state: State) => state.panelVisible; | ||||
| export const getPanelCollapsed = (state: State) => state.panelCollapsed; | ||||
| export const getSelectedFeature = (state: State) => state.selectedFeature; | ||||
| export const getSelectedItem = (state: State) => state.selectedItem; | ||||
| export const getParentItem = (state: State) => state.parentItem; | ||||
| export const getQueryState = (state: State) => state.queryState; | ||||
| export const getClearEnabled = (state: State) => state.clearEnabled; | ||||
| export const getSearchCollapsed = (state: State) => state.searchCollapsed; | ||||
| @@ -493,9 +498,8 @@ export const getSelectedItemLayer = (state: State) => state.selectedItemLayer; | ||||
| export const getPeriod = (state:State) => state.period; | ||||
| export const getStyles = (state:State) => state.styles; | ||||
| export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher; | ||||
| export const getSetStateCount = (state:State) => state.setStateCount; | ||||
| export const getInSearch = (state:State) => state.inSearch; | ||||
| export const getState = (state:State) => {return {mapState:state.mapState,queryState:state.queryState,setStateCount:state.setStateCount,replaceUrl:state.replaceUrl};} | ||||
| export const getState = (state:State) => {return {mapState:state.mapState,queryState:state.queryState,replaceUrl:state.replaceUrl};} | ||||
|  | ||||
| export const selectMapState = createFeatureSelector<State>(MODULE_NAME); | ||||
| export const selectGetMapState= createSelector(selectMapState, getMapState); | ||||
| @@ -505,6 +509,7 @@ export const selectGetPanelVisible = createSelector(selectMapState, getPanelVisi | ||||
| export const selectGetPanelCollapsed = createSelector(selectMapState, getPanelCollapsed); | ||||
| export const selectGetSelectedFeature = createSelector(selectMapState, getSelectedFeature); | ||||
| export const selectGetSelectedItem = createSelector(selectMapState, getSelectedItem); | ||||
| export const selectGetParentItem = createSelector(selectMapState, getParentItem); | ||||
| export const selectGetQueryState = createSelector(selectMapState, getQueryState); | ||||
| export const selectGetClearEnabled = createSelector(selectMapState, getClearEnabled); | ||||
| export const selectGetSearchCollapsed = createSelector(selectMapState, getSearchCollapsed); | ||||
| @@ -520,7 +525,6 @@ export const selectGetSelectedItemLayer = createSelector(selectMapState, getSele | ||||
| export const selectGetPeriod = createSelector(selectMapState, getPeriod); | ||||
| export const selectGetStyles = createSelector(selectMapState, getStyles); | ||||
| export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher); | ||||
| export const selectgetSetStateCount = createSelector(selectMapState,getSetStateCount); | ||||
| export const selectGetInSearch = createSelector(selectMapState,getInSearch); | ||||
| export const selectGetState = createSelector(selectMapState,getState); | ||||
|  | ||||
|   | ||||
							
								
								
									
										24
									
								
								projects/common-map3d/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								projects/common-map3d/README.md
									
									
									
									
									
										Normal 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). | ||||
							
								
								
									
										32
									
								
								projects/common-map3d/karma.conf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								projects/common-map3d/karma.conf.js
									
									
									
									
									
										Normal 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 | ||||
|   }); | ||||
| }; | ||||
							
								
								
									
										7
									
								
								projects/common-map3d/ng-package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								projects/common-map3d/ng-package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| { | ||||
|   "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", | ||||
|   "dest": "../../dist/common-map3d", | ||||
|   "lib": { | ||||
|     "entryFile": "src/public-api.ts" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										17
									
								
								projects/common-map3d/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								projects/common-map3d/package.json
									
									
									
									
									
										Normal 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" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										11
									
								
								projects/common-map3d/src/fm-map3d/common-map3d.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								projects/common-map3d/src/fm-map3d/common-map3d.module.ts
									
									
									
									
									
										Normal 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 { } | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -0,0 +1,9 @@ | ||||
| import { Injectable } from '@angular/core'; | ||||
|  | ||||
| @Injectable({ | ||||
|   providedIn: 'root' | ||||
| }) | ||||
| export class CommonMap3dService { | ||||
|  | ||||
|   constructor() { } | ||||
| } | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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";     | ||||
|   } | ||||
| } | ||||
							
								
								
									
										7
									
								
								projects/common-map3d/src/public-api.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								projects/common-map3d/src/public-api.ts
									
									
									
									
									
										Normal 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'; | ||||
							
								
								
									
										26
									
								
								projects/common-map3d/src/test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								projects/common-map3d/src/test.ts
									
									
									
									
									
										Normal 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); | ||||
							
								
								
									
										32
									
								
								projects/common-map3d/tsconfig.lib.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								projects/common-map3d/tsconfig.lib.json
									
									
									
									
									
										Normal 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" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										10
									
								
								projects/common-map3d/tsconfig.lib.prod.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								projects/common-map3d/tsconfig.lib.prod.json
									
									
									
									
									
										Normal 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 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										17
									
								
								projects/common-map3d/tsconfig.spec.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								projects/common-map3d/tsconfig.spec.json
									
									
									
									
									
										Normal 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" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										17
									
								
								projects/common-map3d/tslint.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								projects/common-map3d/tslint.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| { | ||||
|   "extends": "../../tslint.json", | ||||
|   "rules": { | ||||
|     "directive-selector": [ | ||||
|       true, | ||||
|       "attribute", | ||||
|       "lib", | ||||
|       "camelCase" | ||||
|     ], | ||||
|     "component-selector": [ | ||||
|       true, | ||||
|       "element", | ||||
|       "lib", | ||||
|       "kebab-case" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
							
								
								
									
										32
									
								
								projects/common/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								projects/common/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,35 +1,5 @@ | ||||
| { | ||||
|   "name": "@farmmaps/common", | ||||
|   "version": "0.0.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==" | ||||
|     } | ||||
|   } | ||||
|   "lockfileVersion": 1 | ||||
| } | ||||
|   | ||||
| @@ -5,18 +5,19 @@ | ||||
|     "registry": "https://repository.akkerweb.nl/repository/npm-hosted/" | ||||
|   }, | ||||
|   "peerDependencies": { | ||||
|     "@ng-bootstrap/ng-bootstrap": "^6.0", | ||||
|     "@angular/common": "~9.1.0", | ||||
|     "@angular/core": "~9.1.0", | ||||
|     "@angular/forms": "~9.1.0", | ||||
|     "@ngrx/effects": "^9.0", | ||||
|     "@ngrx/router-store": "^9.0", | ||||
|     "@ngrx/store": "^9.0", | ||||
|     "@ng-bootstrap/ng-bootstrap": "^7.0", | ||||
|     "@angular/common": "~10.1.3", | ||||
|     "@angular/core": "~10.1.3", | ||||
|     "@angular/forms": "~10.1.3", | ||||
|     "@ngrx/effects": "^10.0", | ||||
|     "@ngrx/router-store": "^10.0", | ||||
|     "@ngrx/store": "^10.0", | ||||
|     "tassign": "^1.0.0", | ||||
|     "bootstrap": "^4.4.1", | ||||
|     "@aspnet/signalr": "^1.1.4", | ||||
|     "@microsoft/signalr": "^3.1.3", | ||||
|     "font-awesome": "^4.7.0", | ||||
|     "ngx-uploadx": "^3.3.4", | ||||
|     "angular-oauth2-oidc": "^9.1" | ||||
|     "angular-oauth2-oidc": "^10.0.3", | ||||
|     "moment": "^2.27.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { IItemTypes } from '../models/item.types'; | ||||
| import { IListItem } from '../models/list.item'; | ||||
| import { IUser } from '../models/user'; | ||||
| import { IItem } from '../models/item'; | ||||
| import { UserInfo } from 'angular-oauth2-oidc'; | ||||
|  | ||||
| export const INITUSER = '[AppCommon] InitUser'; | ||||
| export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess'; | ||||
| @@ -11,13 +12,18 @@ export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess'; | ||||
| export const INITUSERPACKAGES = '[AppCommon] InitUserPackages'; | ||||
| export const INITUSERPACKAGESSUCCESS = '[AppCommon] InitUserPackagesSuccess'; | ||||
|  | ||||
| export const INITUSERSETTINGSROOT = '[AppCommon] InitUserSettingsRoot'; | ||||
| export const INITUSERSETTINGSROOTSUCCESS = '[AppCommon] InitUserSettingsRootSuccess'; | ||||
|  | ||||
| export const INITROOT = '[Explorer] InitRoot'; | ||||
| export const INITROOTSUCCESS = '[Explorer] InitRootSuccess'; | ||||
|  | ||||
| export const OPENMODAL = '[AppCommon] OpenModal'; | ||||
| export const CLOSEMODAL = '[AppCommon] CloseModal'; | ||||
| export const LOGIN = '[AppCommon] Login'; | ||||
| export const LOGOUT = '[AppCommon] Logout'; | ||||
| export const ESCAPE = '[AppCommon] Escape'; | ||||
| export const CLOSEALL = '[AppCommon] CloseAll'; | ||||
|  | ||||
| export const LOADITEMTYPES = '[AppCommon] LoadItemTypes'; | ||||
| export const LOADITEMTYPESSUCCESS = '[AppCommon] LoadItemTypesSuccess'; | ||||
| @@ -28,7 +34,8 @@ export const ITEMDELETEDEVENT = '[AppCommon] ItemDeletedEvent'; | ||||
|  | ||||
| export const TASKSTARTEVENT = '[AppCommon] TaskStartEvent'; | ||||
| 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'; | ||||
|  | ||||
| @@ -51,8 +58,16 @@ export const UPLOADEDFILECLICK = '[AppCommon] UploadedFileClick'; | ||||
|  | ||||
| export const TOGGLEMENU = '[AppCommon] ToggleMenu'; | ||||
|  | ||||
| export const TOGGLEACCOUNTMENU = '[AppCommon] ToggleAccountMenu'; | ||||
|  | ||||
| export const SETMENUVISIBLE = '[AppCommon] SetMenuVisible'; | ||||
|  | ||||
| export const ONLINE = '[AppCommon] Online'; | ||||
|  | ||||
| export const OFFLINE = '[AppCommon] Offline'; | ||||
|  | ||||
| export const SETPAGEMODE = '[AppCommon] SetPageMode'; | ||||
|  | ||||
| export class InitUser implements Action { | ||||
|   readonly type = INITUSER; | ||||
|  | ||||
| @@ -62,7 +77,13 @@ export class InitUser implements Action { | ||||
| export class InitUserSuccess implements Action { | ||||
|   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 { | ||||
| @@ -71,6 +92,18 @@ export class InitUserPackagesSuccess implements Action { | ||||
|   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 { | ||||
|   readonly type = INITROOT; | ||||
|  | ||||
| @@ -95,6 +128,12 @@ export class CloseModal implements Action { | ||||
|   constructor() { } | ||||
| } | ||||
|  | ||||
| export class CloseAll implements Action { | ||||
|   readonly type = CLOSEALL; | ||||
|  | ||||
|   constructor() { } | ||||
| } | ||||
|  | ||||
| export class StartRouteLoading implements Action { | ||||
|   readonly type = STARTROUTELOADING; | ||||
|  | ||||
| @@ -113,6 +152,12 @@ export class Login implements Action { | ||||
|   constructor(public url: string) { } | ||||
| } | ||||
|  | ||||
| export class Logout implements Action { | ||||
|   readonly type = LOGOUT; | ||||
|  | ||||
|   constructor() { } | ||||
| } | ||||
|  | ||||
| export class Escape implements Action { | ||||
|   readonly type = ESCAPE; | ||||
|  | ||||
| @@ -168,7 +213,13 @@ export class TaskEndEvent 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) { } | ||||
| } | ||||
| @@ -226,18 +277,43 @@ export class ToggleMenu implements Action { | ||||
|   constructor() { } | ||||
| } | ||||
|  | ||||
| export class ToggleAccountMenu implements Action { | ||||
|   readonly type = TOGGLEACCOUNTMENU; | ||||
|  | ||||
|   constructor() { } | ||||
| } | ||||
|  | ||||
| export class SetMenuVisible implements Action { | ||||
|   readonly type = SETMENUVISIBLE; | ||||
|  | ||||
|   constructor(public visible:boolean) { } | ||||
| } | ||||
|  | ||||
| export class Online implements Action { | ||||
|   readonly type = ONLINE; | ||||
|  | ||||
|   constructor() { } | ||||
| } | ||||
|  | ||||
| export class Offline implements Action { | ||||
|   readonly type = OFFLINE; | ||||
|  | ||||
|   constructor() { } | ||||
| } | ||||
|  | ||||
| export class SetPageMode implements Action { | ||||
|   readonly type = SETPAGEMODE; | ||||
|  | ||||
|   constructor(public pageMode:boolean) {} | ||||
| } | ||||
|    | ||||
|  | ||||
| export type Actions = OpenModal | ||||
|   | InitRoot | ||||
|   | InitRootSuccess | ||||
|   | CloseModal | ||||
|   | Login | ||||
|   | Logout | ||||
|   | ItemChangedEvent | ||||
|   | ItemAddedEvent | ||||
|   | ItemDeletedEvent | ||||
| @@ -258,8 +334,18 @@ export type Actions = OpenModal | ||||
|   | TaskStartEvent | ||||
|   | TaskEndEvent | ||||
|   | TaskErrorEvent | ||||
|   | TaskProgressEvent | ||||
|   | DeviceUpdateEvent | ||||
|   | ToggleMenu | ||||
|   | SetMenuVisible | ||||
|   | InitUserPackagesSuccess; | ||||
|   | InitUserPackages | ||||
|   | InitUserPackagesSuccess | ||||
|   | InitUserSettingsRoot | ||||
|   | InitUserSettingsRootSuccess | ||||
|   | ToggleAccountMenu | ||||
|   | CloseAll | ||||
|   | Online | ||||
|   | Offline | ||||
|   | SetPageMode; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; | ||||
|  | ||||
| //components | ||||
| import { ItemTypeService } from './services/itemtype.service'; | ||||
| import { SchemaService } from './services/schema.service'; | ||||
| import { FolderService } from './services/folder.service'; | ||||
| import { TimespanService } from './services/timespan.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 { AuthConfigFactory } from './shared/authconfigFactory'; | ||||
| 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 { | ||||
|   FolderService, | ||||
| @@ -47,7 +51,11 @@ export { | ||||
|   AuthCallbackGuard, | ||||
|   ResumableFileUploadService, | ||||
|   NgbDateNativeAdapter, | ||||
|   StateSerializerService | ||||
|   StateSerializerService, | ||||
|   SchemaService, | ||||
|   PackageService, | ||||
|   SenmlService, | ||||
|   PackagePreloadStrategy | ||||
| }; | ||||
|  | ||||
| @NgModule({ | ||||
| @@ -71,7 +79,7 @@ export class AppCommonServiceModule { | ||||
|         { | ||||
|           provide: APP_INITIALIZER, | ||||
|           useFactory: appConfigFactory, | ||||
|           deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage,ItemTypeService], | ||||
|           deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage, ItemTypeService], | ||||
|           multi: true | ||||
|         }, | ||||
|         { | ||||
|   | ||||
| @@ -31,6 +31,8 @@ import { TimespanComponent } from './components/timespan/timespan.component'; | ||||
| import { TagInputComponent } from './components/tag-input/tag-input.component'; | ||||
| import { MenuBackgroundComponent } from './components/menu-background/menu-background.component'; | ||||
| 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 { IEventMessage } from './models/event.message'; | ||||
| import { IItem, Item } from './models/item'; | ||||
| @@ -39,15 +41,21 @@ import { IItemType } from './models/item.type'; | ||||
| import { IItemTypes } from './models/item.types'; | ||||
| import { IItemTask, ItemTask } from './models/itemTask'; | ||||
| 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 { IUser } from './models/user'; | ||||
| import { IQueryState } from './models/query.state'; | ||||
| 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 commonReducers from './reducers/app-common.reducer'; | ||||
| import * as commonEffects from './effects/app-common.effects'; | ||||
| import { SecureOAuthStorage} from './shared/secureOAuthStorage'; | ||||
| import { GradientComponent } from './components/gradient/gradient.component'; | ||||
| import { GradientSelectComponent } from './components/gradient-select/gradient-select.component'; | ||||
|  | ||||
| export { | ||||
|   SafePipe, | ||||
| @@ -60,7 +68,9 @@ export { | ||||
|   SidePanelComponent, | ||||
|   TimespanComponent, | ||||
|   TagInputComponent, | ||||
|   UserMenuComponent, | ||||
|   HasPackageDirective, | ||||
|   HasClaimDirective, | ||||
|   Alert, | ||||
|   IEventMessage, | ||||
|   IItem, | ||||
| @@ -82,7 +92,12 @@ export { | ||||
|   AuthConfigFactory, | ||||
|   MenuBackgroundComponent, | ||||
|   SecureOAuthStorage, | ||||
|   WeatherCurrentObservation | ||||
|   WeatherCurrentObservation, | ||||
|   IJsonline, | ||||
|   ISenMLItem, | ||||
|   IDataLayer, | ||||
|   IColor, | ||||
|   IGradientstop | ||||
| }; | ||||
|  | ||||
| @NgModule({ | ||||
| @@ -109,7 +124,11 @@ export { | ||||
|     TagInputComponent, | ||||
|     SessionClearedComponent, | ||||
|     MenuBackgroundComponent, | ||||
|     HasPackageDirective | ||||
|     HasPackageDirective, | ||||
|     HasClaimDirective, | ||||
|     UserMenuComponent, | ||||
|     GradientComponent, | ||||
|     GradientSelectComponent | ||||
|   ], | ||||
|   exports: [ | ||||
|     NgbModule, | ||||
| @@ -127,7 +146,11 @@ export { | ||||
|     TagInputComponent, | ||||
|     SessionClearedComponent, | ||||
|     MenuBackgroundComponent, | ||||
|     HasPackageDirective | ||||
|     HasPackageDirective, | ||||
|     HasClaimDirective, | ||||
|     UserMenuComponent, | ||||
|     GradientComponent, | ||||
|     GradientSelectComponent | ||||
|   ] | ||||
| }) | ||||
| export class AppCommonModule { | ||||
|   | ||||
| @@ -1,7 +1,14 @@ | ||||
| <div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async)}"> | ||||
|   <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> | ||||
|     <button type="button" class="btn btn-outline-light" (click)="handleToggleMenu($event)"><i class="fa fa-bars" aria-hidden="true"></i></button> | ||||
| <div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async),'pagemode':(isPageMode|async),'appmode':!(isPageMode|async)}"> | ||||
|   <nav class="navbar navbar-light navbar-expand bg-light navigation-clean"> | ||||
|     <div class="header-logo pageonly"><router-outlet name="header-logo"></router-outlet></div>  | ||||
|     <button type="button" class="btn btn-outline-secondary apponly" (click)="handleToggleMenu($event)"><i class="fa fa-bars" aria-hidden="true"></i></button> | ||||
|     <router-outlet name="menu" class="ml-4"></router-outlet> | ||||
|     <div class="collapse navbar-collapse pageonly">       | ||||
|       <a class="btn btn-primary ml-auto" role="button" [routerLink]="['/map']"> | ||||
|         <span *ngIf="(user|async)==null" i18n>Sign in</span> | ||||
|         <span *ngIf="(user|async)!=null" i18n>To app</span> | ||||
|       </a> | ||||
|     </div> | ||||
|   </nav> | ||||
|   <div class="body"> | ||||
|     <router-outlet></router-outlet> | ||||
| @@ -11,7 +18,7 @@ | ||||
|     <div class="container-fluid"> | ||||
|       <div class="body"> | ||||
|         <div class="d-flex flex-row"> | ||||
|           <div class="mt-2 mb-2 flex-grow-1 logo"><router-outlet name="side-panel-logo"></router-outlet></div> | ||||
|           <div class="mt-2 mb-2 flex-grow-1 logo" (click)="handleHome($event)"><router-outlet name="side-panel-logo"></router-outlet></div> | ||||
|           <div class="mt-2 mb-2 ml-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fa fa-times" aria-hidden="true"></i></button></div> | ||||
|         </div> | ||||
|         <div class="d-flex flex-column cards">  | ||||
| @@ -23,4 +30,12 @@ | ||||
|   <ng-container *ngIf="showUploadProgress"> | ||||
|     <fm-resumable-file-upload></fm-resumable-file-upload> | ||||
|   </ng-container>   | ||||
|   <div class="user-menu apponly"> | ||||
|     <fm-user-menu [user]="user|async" [showMenu]="accountMenuVisible|async"></fm-user-menu> | ||||
|   </div> | ||||
|   <div class="healthstatus-container online apponly" [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> | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| @import "~bootstrap/scss/bootstrap.scss"; | ||||
|  | ||||
|  | ||||
|  | ||||
| // custom styles | ||||
|  | ||||
| .btn:focus { | ||||
| @@ -25,7 +26,7 @@ | ||||
|  | ||||
| */ | ||||
|  | ||||
| body { background: #f1f1f1; line-height: 18px; user-select:none;} | ||||
| body { background: #f1f1f1; line-height: 18px; user-select:none;font-family: Lato,Helvetica Neue,Helvetica,Arial,sans-serif;} | ||||
|  | ||||
| .navbar-brand { | ||||
|   padding-top: .5rem; | ||||
| @@ -53,7 +54,20 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;} | ||||
|   bottom: 0; | ||||
|   left: 0; | ||||
|   right: 0; | ||||
|   overflow:hidden; | ||||
|   overflow: hidden;   | ||||
| } | ||||
|  | ||||
| .app.pagemode > .body { | ||||
|   overflow: initial; | ||||
|   position: relative; | ||||
| } | ||||
|  | ||||
| .app.pagemode .apponly { | ||||
|   display: none !important; | ||||
| } | ||||
|  | ||||
| .app.appmode .pageonly { | ||||
|   display: none !important; | ||||
| } | ||||
|  | ||||
| .app.fullscreen > .navbar { | ||||
| @@ -76,3 +90,37 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;} | ||||
| .logo { | ||||
|   margin-left: 1rem; | ||||
| } | ||||
|  | ||||
| .header-logo { | ||||
|   height:2em; | ||||
| } | ||||
|  | ||||
| .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; | ||||
| } | ||||
|   | ||||
| @@ -1,14 +1,20 @@ | ||||
| import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactory2, PLATFORM_ID, ChangeDetectionStrategy, HostListener, Input } from '@angular/core'; | ||||
| 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 { Subscription ,  Observable } from 'rxjs'; | ||||
| import { Meta, Title, MetaDefinition } from '@angular/platform-browser'; import { DOCUMENT } from "@angular/common"; | ||||
| import { Subscription, Observable } from 'rxjs'; | ||||
| import { distinctUntilChanged } from 'rxjs/operators'; | ||||
| import { Store, Action } from '@ngrx/store'; | ||||
| import { IUser } from '../../models/user'; | ||||
| import { OAuthService, OAuthErrorEvent } from 'angular-oauth2-oidc'; | ||||
|  | ||||
| //AppCommon | ||||
| 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 { ItemTypeService } from '../../services/itemtype.service'; | ||||
| import * as  commonActions from '../../actions/app-common.actions'; | ||||
| import { HealthCheckService } from '../../services/healthcheck.service'; | ||||
| import { AppConfig } from '../../shared/app.config'; | ||||
|  | ||||
| import * as appReducers from '../../reducers/app-common.reducer'; | ||||
|  | ||||
| @@ -31,23 +37,30 @@ export class AppComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|   public currentFolder: Observable<IListItem>; | ||||
|   public folderParents: Observable<IListItem[]>; | ||||
|   public fullScreen: Observable<boolean>; | ||||
|   public routeLoading: Observable<boolean>; | ||||
|   public menuVisible: Observable<boolean>; | ||||
|   @Input() showUploadProgress: boolean =true; | ||||
|   public fullScreen: Observable<boolean> = this.store$.select(appReducers.selectGetFullScreen); | ||||
|   public isOnline: Observable<boolean> = this.store$.select(appReducers.SelectGetIsOnline); | ||||
|   public routeLoading: Observable<boolean> = this.store$.select(appReducers.selectGetRouteLoading); | ||||
|   public menuVisible: Observable<boolean> = this.store$.select(appReducers.SelectGetMenuVisible); | ||||
|   public accountMenuVisible: Observable<boolean> = this.store$.select(appReducers.SelectGetAccountMenuVisible); | ||||
|   public user: Observable<IUser> = this.store$.select(appReducers.SelectGetUser); | ||||
|   public isPageMode: Observable<boolean> =  this.store$.select(appReducers.SelectGetIsPageMode); | ||||
|   @Input() showUploadProgress: boolean = true; | ||||
|  | ||||
|   constructor( | ||||
|     private router: Router, | ||||
|     private activatedRoute: ActivatedRoute, | ||||
|     private title: Title, | ||||
|     private meta: Meta, | ||||
|     private store: Store<appReducers.State>, | ||||
|     private eventService: EventService, | ||||
|     public router: Router, | ||||
|     private activatedRoute$: ActivatedRoute, | ||||
|     private title$: Title, | ||||
|     private meta$: Meta, | ||||
|     private store$: Store<appReducers.State>, | ||||
|     private eventService$: EventService, | ||||
|     private healthCheckService$: HealthCheckService, | ||||
|     private itemTypeService$: ItemTypeService, | ||||
|     private oauthService$: OAuthService, | ||||
|     private appConfig$: AppConfig | ||||
|   ) { | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
|   getActionFromEvent(event: IEventMessage): Action { | ||||
|     var action: Action = null; | ||||
|     console.debug(`${event.eventType} Event received`); | ||||
| @@ -76,6 +89,10 @@ export class AppComponent implements OnInit, OnDestroy { | ||||
|         action = new commonActions.TaskErrorEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|       case "taskProgress": { | ||||
|         action = new commonActions.TaskProgressEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
|       } | ||||
|       case "deviceUpdate": { | ||||
|         action = new commonActions.DeviceUpdateEvent(event.itemCode, event.attributes); | ||||
|         break; | ||||
| @@ -85,61 +102,108 @@ export class AppComponent implements OnInit, OnDestroy { | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|     this.fullScreen = this.store.select(appReducers.selectGetFullScreen); | ||||
|     this.routeLoading = this.store.select(appReducers.selectGetRouteLoading); | ||||
|     this.menuVisible = this.store.select(appReducers.SelectGetMenuVisible); | ||||
|     this.InstallRouteEventHandler(); | ||||
|     this.InstallEventServiceEventHandler(); | ||||
|     this.InstallAuthenticationEventHandler(); | ||||
|     this.InstallHealthCheck(); | ||||
|  | ||||
|     //load item types | ||||
|     this.itemTypeService$.load(this.appConfig$) | ||||
|   } | ||||
|  | ||||
|   @HostListener('document:keyup', ['$event']) | ||||
|   keyUp(event: KeyboardEvent) { | ||||
|   onKeyUp(event: KeyboardEvent) { | ||||
|     let x = event.keyCode; | ||||
|     if (x === 27) { | ||||
|       this.store.dispatch(new commonActions.Escape(true,false)); | ||||
|       this.store$.dispatch(new commonActions.Escape(true, false)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
|     // Subscription clean-up | ||||
|     if(this.routerSub$) this.routerSub$.unsubscribe(); | ||||
|     if(this.eventSub$) this.eventSub$.unsubscribe(); | ||||
|     if (this.routerSub$) this.routerSub$.unsubscribe(); | ||||
|     if (this.eventSub$) this.eventSub$.unsubscribe(); | ||||
|   } | ||||
|  | ||||
|   private InstallAuthenticationEventHandler() { | ||||
|     // auth event handler | ||||
|     this.oauthService$.events.subscribe((event) => { | ||||
|       console.debug(event.type); | ||||
|       if (event.type == 'token_error' || event.type == 'silent_refresh_timeout' || event.type == 'logout') { | ||||
|         let e = event as OAuthErrorEvent; | ||||
|         let p = e.params as any; | ||||
|         if (event.type == 'silent_refresh_timeout' || event.type == 'logout' || (p.error && p.error == 'login_required')) { | ||||
|           console.debug("Session expired"); | ||||
|           this.router.navigate(['loggedout'], { queryParams: { redirectTo: this.router.url } }); | ||||
|         } | ||||
|       } | ||||
|       if(event.type == 'token_received') { | ||||
|         this.store$.dispatch(new commonActions.InitUser()); | ||||
|       } | ||||
|     }); | ||||
|     if(this.oauthService$.hasValidAccessToken()) { | ||||
|        this.store$.dispatch(new commonActions.InitUser()); | ||||
|     } else { | ||||
|       if(this.oauthService$.getRefreshToken() !=null) { | ||||
|         this.oauthService$.refreshToken(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private InstallRouteEventHandler() { | ||||
|     var other = this; | ||||
|     this.routerSub$ = this.router.events.subscribe(event => { | ||||
|       if (event instanceof RouteConfigLoadStart) { | ||||
|         other.store.dispatch(new commonActions.StartRouteLoading()); | ||||
|         other.store$.dispatch(new commonActions.StartRouteLoading()); | ||||
|       } | ||||
|       if (event instanceof NavigationEnd && (event.url == "/" || event.url.startsWith("/content") )) { | ||||
|         other.store$.dispatch(new commonActions.SetPageMode(true)); | ||||
|       } else if (event instanceof NavigationEnd) { | ||||
|         other.store$.dispatch(new commonActions.SetPageMode(false)); | ||||
|       } | ||||
|       if (event instanceof RouteConfigLoadEnd) { | ||||
|         other.store.dispatch(new commonActions.EndRouteLoading()); | ||||
|         other.store$.dispatch(new commonActions.EndRouteLoading()); | ||||
|       } | ||||
|       if(event instanceof NavigationStart) { | ||||
|         other.store.dispatch(new commonActions.SetMenuVisible(false)); | ||||
|       if (event instanceof NavigationStart) { | ||||
|         other.store$.dispatch(new commonActions.SetMenuVisible(false)); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private InstallEventServiceEventHandler() { | ||||
|     var other = this; | ||||
|     this.eventSub$ = this.eventService.event.subscribe(event => { | ||||
|     this.eventSub$ = this.eventService$.event.subscribe(event => { | ||||
|       var action = other.getActionFromEvent(event); | ||||
|       if (action) other.store.dispatch(action); | ||||
|       if (action) other.store$.dispatch(action); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private InstallHealthCheck() { | ||||
|      //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()); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   handleClick(event: MouseEvent) { | ||||
|     this.store.dispatch(new commonActions.Escape(false,true)); | ||||
|     this.store$.dispatch(new commonActions.Escape(false, true)); | ||||
|   } | ||||
|  | ||||
|   handleStopBubble(event: MouseEvent) { | ||||
|     event.stopPropagation(); | ||||
|   } | ||||
|  | ||||
|   handleToggleMenu(event:MouseEvent) { | ||||
|   handleToggleMenu(event: MouseEvent) { | ||||
|     event.stopPropagation(); | ||||
|     this.store.dispatch(new commonActions.ToggleMenu()); | ||||
|     this.store$.dispatch(new commonActions.ToggleMenu()); | ||||
|   } | ||||
|  | ||||
|   handleHome(event: MouseEvent) { | ||||
|     this.router.navigate(['/']); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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; | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| <div class="gradient" [ngStyle]="gradientStyle"></div> | ||||
| @@ -0,0 +1,4 @@ | ||||
| .gradient { | ||||
|     height: 100%; | ||||
|     width: 100%; | ||||
| } | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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(); | ||||
|       }       | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @@ -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;      | ||||
|       }   | ||||
|     });     | ||||
|   } | ||||
| } | ||||
| @@ -7,7 +7,7 @@ | ||||
|     bottom:0; | ||||
|     opacity: 0; | ||||
|     background-color: #000000; | ||||
|     transition: opacity 0s ease-out 1s; | ||||
|     transition: opacity 0.3s ease-out; | ||||
|     pointer-events: none; | ||||
|     z-index:99; | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,7 @@ export class ResumableFileUploadService implements OnDestroy{ | ||||
|   public isMinimized = false; | ||||
|   public parentCode: string; | ||||
|   public refresh: Subject<any> = new Subject<any>(); | ||||
|   public addedFiles: Subject<File> = new Subject<File>(); | ||||
|   private _eventSub:Subscription; | ||||
|   private initialized = false; | ||||
|  | ||||
| @@ -52,20 +53,21 @@ export class ResumableFileUploadService implements OnDestroy{ | ||||
|   } | ||||
|  | ||||
|   handleState(state:UploadState) { | ||||
|       switch(state.status) { | ||||
|         case "queue": { | ||||
|             this.files.push(new File(state)); | ||||
|             this.isClosed=false; | ||||
|         };break; | ||||
|         case "uploading": { | ||||
|           this.isUploading = true; | ||||
|       var file =this.files.find((f) => f.identifier == state.uploadId ) | ||||
|       if(state.status != "cancelled" && !file) { | ||||
|         let file = new File(state); | ||||
|         this.files.push(file); | ||||
|         this.isClosed=false;   | ||||
|         this.addedFiles.next(file) | ||||
|       } | ||||
|       switch(state.status) { | ||||
|         case "uploading": {          | ||||
|           if(file) { | ||||
|             this.isUploading = true; | ||||
|             file.progress = (state.progress?state.progress:0); | ||||
|           } | ||||
|         };break; | ||||
|         case "complete": { | ||||
|             var file =this.files.find((f) => f.identifier == state.uploadId ) | ||||
|             if(file) { | ||||
|               var parts = state.url.split("/"); | ||||
|               file.itemCode = parts[parts.length-1]; | ||||
| @@ -74,10 +76,9 @@ export class ResumableFileUploadService implements OnDestroy{ | ||||
|             } | ||||
|         };break; | ||||
|         case "error": { | ||||
|           var file =this.files.find((f) => f.identifier == state.uploadId ) | ||||
|           if(file) { | ||||
|             file.error=true; | ||||
|             file.errorMessage = state.response; | ||||
|             file.errorMessage = state.response as string; | ||||
|           } | ||||
|         };break; | ||||
|       } | ||||
| @@ -102,6 +103,9 @@ export class ResumableFileUploadService implements OnDestroy{ | ||||
|     if (index > -1) { | ||||
|       this.files.splice(index, 1); | ||||
|     } | ||||
|     if(this.files.length==0) { | ||||
|       this.isUploading = false; | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   doClose = function () { | ||||
|   | ||||
| @@ -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> | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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(); | ||||
|   }); | ||||
| }); | ||||
| @@ -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()); | ||||
|   }  | ||||
| } | ||||
| @@ -1,10 +1,10 @@ | ||||
| import { Injectable } from '@angular/core'; | ||||
| 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 { Effect, Actions,ofType } from '@ngrx/effects'; | ||||
| import { Observable ,  defer ,  of } from 'rxjs'; | ||||
| import { withLatestFrom,mergeMap,switchMap,map,catchError} from 'rxjs/operators'; | ||||
| import { Observable ,  defer ,  of,from,zip } from 'rxjs'; | ||||
| import { withLatestFrom,mergeMap,switchMap,map,catchError,first} from 'rxjs/operators'; | ||||
| import * as appCommonActions from '../actions/app-common.actions'; | ||||
| import * as appCommonReducers from '../reducers/app-common.reducer'; | ||||
| import { ItemService } from '../services/item.service'; | ||||
| @@ -25,7 +25,15 @@ export class AppCommonEffects { | ||||
|     withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)), | ||||
|     mergeMap(([action, initialized]) => { | ||||
|       var a = (action as appCommonActions.Login); | ||||
|       this.oauthService$.initCodeFlow(a.url); | ||||
|       this.oauthService$.initCodeFlow(a.url,{"prompt":"login"}); | ||||
|       return []; | ||||
|     })); | ||||
|  | ||||
|     @Effect({ dispatch: false }) | ||||
|     logout$: Observable<Action> = this.actions$.pipe( | ||||
|       ofType(appCommonActions.LOGOUT), | ||||
|       mergeMap((action) => { | ||||
|         this.oauthService$.revokeTokenAndLogout(); | ||||
|         return []; | ||||
|       })); | ||||
|  | ||||
| @@ -42,35 +50,67 @@ export class AppCommonEffects { | ||||
|   @Effect() | ||||
|   initUser$: Observable<Action> = this.actions$.pipe( | ||||
|     ofType(appCommonActions.INITUSER), | ||||
|     withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)), | ||||
|     switchMap(([action, initialized]) => { | ||||
|       if(!initialized) { | ||||
|       return this.userService$.getCurrentUser().pipe( | ||||
|         map((user: IUser) => new appCommonActions.InitUserSuccess(user)), | ||||
|     first(), | ||||
|     switchMap((action) => { | ||||
|         return zip(this.userService$.getCurrentUser(),from(this.oauthService$.loadUserProfile())).pipe( | ||||
|         switchMap(([user,userInfo]) => {return of(new appCommonActions.InitUserSuccess(user,userInfo as UserInfo))} ), | ||||
|         catchError(error => of(new appCommonActions.Fail(error)))) | ||||
|       } else { | ||||
|         return []; | ||||
|       } | ||||
|     } | ||||
|   )); | ||||
|      | ||||
|  | ||||
|   @Effect() | ||||
|   initUserPackages$:Observable<Action> = this.actions$.pipe( | ||||
|     ofType(appCommonActions.INITUSERSUCCESS), | ||||
|     switchMap((action) => { | ||||
|       let a = action as appCommonActions.InitUserSuccess; | ||||
|       return this.itemService$.getChildItemList(a.user.code+":USER_PACKAGES","vnd.farmmaps.itemtype.package").pipe( | ||||
|     ofType(appCommonActions.INITUSERPACKAGES), | ||||
|     switchMap(() => { | ||||
|       return this.itemService$.getItemList('vnd.farmmaps.itemtype.package').pipe( | ||||
|         switchMap((items) => of(new appCommonActions.InitUserPackagesSuccess(items))), | ||||
|         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() | ||||
|   initUserSuccess$: Observable<Action> = this.actions$.pipe( | ||||
|     ofType(appCommonActions.INITUSERSUCCESS), | ||||
|     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)), | ||||
|       switchMap(([action, itemtypes]) => { | ||||
|         var a = action as appCommonActions.EditItem; | ||||
|         var editor =  "property"; | ||||
|         if(a.item.itemType) { | ||||
|           var itemType = itemtypes[a.item.itemType]; | ||||
|         var editor = itemType.editor ? itemType.editor : "property"; | ||||
|           var editor = itemType && itemType.editor ? itemType.editor : editor;   | ||||
|         } | ||||
|       this.router$.navigate(['/editor',editor,'item', a.item.code]) | ||||
|       return []; | ||||
|     } | ||||
| @@ -150,6 +193,23 @@ export class AppCommonEffects { | ||||
|       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();               | ||||
|             } | ||||
|           } | ||||
|           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) { | ||||
|     store$.dispatch(new appCommonActions.LoadItemTypes()); | ||||
|   } | ||||
|   | ||||
							
								
								
									
										22
									
								
								projects/common/src/fm/models/WeatherData.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								projects/common/src/fm/models/WeatherData.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
| @@ -8,7 +8,8 @@ export interface ICodeListItem { | ||||
|     validTo?: Date; | ||||
|     created?: Date; | ||||
|     updated?: Date; | ||||
|     data?:any | ||||
|     data?:any; | ||||
|     readWrite?: boolean; | ||||
| } | ||||
|  | ||||
| export class CodeListItem implements ICodeListItem { | ||||
| @@ -22,6 +23,7 @@ export class CodeListItem implements ICodeListItem { | ||||
|   public created?: Date; | ||||
|   public updated?: Date; | ||||
|   public data?:any; | ||||
|   public readWrite?: boolean; | ||||
|  | ||||
|   constructor() {   | ||||
|   } | ||||
|   | ||||
							
								
								
									
										7
									
								
								projects/common/src/fm/models/data.layer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								projects/common/src/fm/models/data.layer.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| export interface IDataLayer { | ||||
|     name: string, | ||||
|     unit: string, | ||||
|     index: number, | ||||
|     indexKey?: string, | ||||
|     scale: number, | ||||
|   } | ||||
							
								
								
									
										11
									
								
								projects/common/src/fm/models/gradient.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								projects/common/src/fm/models/gradient.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| export interface IColor { | ||||
|   red: number, | ||||
|   green: number, | ||||
|   blue: number, | ||||
|   alpha: number, | ||||
|  } | ||||
|    | ||||
| export interface IGradientstop { | ||||
|   relativestop: number, | ||||
|   color: IColor | ||||
|  } | ||||
| @@ -15,9 +15,10 @@ export class Item implements IItem { | ||||
|   public geometry?:any; | ||||
|   public url?: string; | ||||
|   public name?: string; | ||||
|   public created?: Date; | ||||
|   public updated?: Date; | ||||
|   public dataDate?: Date; | ||||
|   public created?: string; | ||||
|   public updated?: string; | ||||
|   public dataDate?: string; | ||||
|   public dataEndDate?: string; | ||||
|   public itemType?: string; | ||||
|   public sourceTask?: string; | ||||
|   public size?: number; | ||||
|   | ||||
| @@ -4,5 +4,6 @@ export interface IItemType { | ||||
|   editor?: string; | ||||
|   isFolder?: boolean; | ||||
|   iconColor?: string; | ||||
|   schema?: string; | ||||
|   extraAttributes?: string; | ||||
| } | ||||
|   | ||||
							
								
								
									
										5
									
								
								projects/common/src/fm/models/json-line.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								projects/common/src/fm/models/json-line.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export interface IJsonline { | ||||
|   time: string; | ||||
|   type: string; | ||||
|   data: any; | ||||
| } | ||||
| @@ -2,9 +2,10 @@ export interface IListItem { | ||||
|     url?: string; | ||||
|     code?: string; | ||||
|     name?: string; | ||||
|     created?: Date; | ||||
|     updated?: Date; | ||||
|     dataDate?: Date; | ||||
|     created?: string; | ||||
|     updated?: string; | ||||
|     dataDate?: string; | ||||
|     dataEndDate?: string; | ||||
|     itemType?: string; | ||||
|     sourceTask?: string; | ||||
|     size?: number; | ||||
|   | ||||
							
								
								
									
										5
									
								
								projects/common/src/fm/models/senml-item.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								projects/common/src/fm/models/senml-item.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| export interface ISenMLItem {     | ||||
|     u?: string; | ||||
|     v?: string; | ||||
|     n?: string; | ||||
| } | ||||
| @@ -2,4 +2,5 @@ export interface IUser { | ||||
|   code?: string; | ||||
|   name?: string; | ||||
|   email?: string; | ||||
|   claims: any; | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import * as appCommonActions from '../actions/app-common.actions'; | ||||
| import { createSelector, createFeatureSelector, ActionReducerMap } from '@ngrx/store'; | ||||
|  | ||||
| import { MODULE_NAME } from '../module-name'; | ||||
| import { IItem } from '../models/item'; | ||||
|  | ||||
| export interface State { | ||||
|   openedModalName: string, | ||||
| @@ -17,7 +18,11 @@ export interface State { | ||||
|   fullScreen: boolean, | ||||
|   routeLoading:boolean, | ||||
|   menuVisible: boolean, | ||||
|   userPackages: IPackages | ||||
|   userPackages: IPackages, | ||||
|   userSettingsRoot: IItem, | ||||
|   accountMenuVisible: boolean, | ||||
|   isOnline: boolean, | ||||
|   isPageMode:boolean | ||||
| } | ||||
|  | ||||
| export const initialState: State = { | ||||
| @@ -29,14 +34,31 @@ export const initialState: State = { | ||||
|   fullScreen: true, | ||||
|   routeLoading: false, | ||||
|   menuVisible: false, | ||||
|   userPackages: {} | ||||
|   userPackages: {}, | ||||
|   userSettingsRoot: null, | ||||
|   accountMenuVisible: false, | ||||
|   isOnline: true, | ||||
|   isPageMode: true | ||||
| } | ||||
|  | ||||
| export function reducer(state = initialState, action: appCommonActions.Actions ): State { | ||||
|   switch (action.type) { | ||||
|     case appCommonActions.INITUSER: { | ||||
|       return tassign(state,{initialized: true}); | ||||
|     } | ||||
|     case 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: { | ||||
|       let a = action as appCommonActions.InitRootSuccess; | ||||
| @@ -73,14 +95,17 @@ export function reducer(state = initialState, action: appCommonActions.Actions ) | ||||
|       }); | ||||
|     } | ||||
|     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: { | ||||
|       return tassign(state, { menuVisible: false }); | ||||
|       return tassign(state, { menuVisible: false,accountMenuVisible:false }); | ||||
|     } | ||||
|     case 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:{ | ||||
|       let a = action as appCommonActions.InitUserPackagesSuccess; | ||||
| @@ -91,6 +116,26 @@ export function reducer(state = initialState, action: appCommonActions.Actions ) | ||||
|  | ||||
|       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}); | ||||
|     } | ||||
|     case appCommonActions.SETPAGEMODE: { | ||||
|       let a = action as appCommonActions.SetPageMode; | ||||
|       return tassign(state,{isPageMode:a.pageMode}); | ||||
|     } | ||||
|     default: { | ||||
|       return state; | ||||
|     } | ||||
| @@ -106,6 +151,10 @@ export const getRouteLoading = (state: State) => state.routeLoading; | ||||
| export const getMenuVisible = (state: State) => state.menuVisible; | ||||
| export const getUser = (state: State) => state.user; | ||||
| 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 getIsPageMode = (state: State) => state.isPageMode; | ||||
|  | ||||
| export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME); | ||||
|  | ||||
| @@ -118,4 +167,8 @@ export const selectGetRouteLoading = createSelector(selectAppCommonState, getRou | ||||
| export const SelectGetMenuVisible = createSelector(selectAppCommonState,getMenuVisible); | ||||
| export const SelectGetUser = createSelector(selectAppCommonState,getUser); | ||||
| export const SelectGetUserPackages = createSelector(selectAppCommonState,getUserPackages); | ||||
| export const SelectGetUserSettingsRoot = createSelector(selectAppCommonState,getUserSettingsRoot); | ||||
| export const SelectGetAccountMenuVisible = createSelector(selectAppCommonState,getAccountMenuVisible); | ||||
| export const SelectGetIsOnline = createSelector(selectAppCommonState,getIsOnline); | ||||
| export const SelectGetIsPageMode = createSelector(selectAppCommonState,getIsPageMode); | ||||
|  | ||||
|   | ||||
| @@ -41,24 +41,9 @@ export class AuthGuard implements CanActivate, CanLoad, CanActivateChild  { | ||||
|     return new Promise<boolean>((resolve) => { | ||||
|       if (!this.oauthService.hasValidAccessToken()) { | ||||
|         console.debug("No valid token"); | ||||
|         if(this.oauthService.getRefreshToken() != null ) { | ||||
|           console.debug("We have a refresh token"); | ||||
|           this.oauthService.refreshToken().then(() => { | ||||
|             this.store.dispatch(new appCommonActions.InitUser()); | ||||
|             resolve(true); | ||||
|           }).catch(() => { | ||||
|             console.debug("Error refreshing"); | ||||
|         this.oauthService.initCodeFlow(url); | ||||
|         resolve(false); | ||||
|           }) | ||||
|       } else {         | ||||
|           console.debug("No refresh token"); | ||||
|           this.oauthService.initCodeFlow(url); | ||||
|           resolve(false); | ||||
|         }                    | ||||
|       } else { | ||||
|         console.debug("Valid token init user"); | ||||
|         this.store.dispatch(new appCommonActions.InitUser()); | ||||
|         resolve(true); | ||||
|       } | ||||
|     }); | ||||
|   | ||||
| @@ -22,4 +22,20 @@ export class CodeListItemService { | ||||
|     } | ||||
|     return this.httpClient.get<ICodeListItem[]>(`${this.ApiEndpoint()}/api/v1/codelistitems/`, { params: params }); | ||||
|   } | ||||
|  | ||||
|   getCodeListItem(codeList: string, code: string): Observable<ICodeListItem> { | ||||
|     return this.httpClient.get<ICodeListItem>(`${this.ApiEndpoint()}/api/v1/codelistitems/${codeList}/${code}`); | ||||
|   } | ||||
|  | ||||
|   postCodeListItem(codeListItem: ICodeListItem): Observable<ICodeListItem> { | ||||
|     return this.httpClient.post<ICodeListItem>(`${this.ApiEndpoint()}/api/v1/codelistitems/${codeListItem.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}`); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -43,7 +43,7 @@ export class EventService { | ||||
|  | ||||
|   private Authenticate() { | ||||
|     var accessToken = this.oauthService.getAccessToken();    | ||||
|     if (accessToken) { | ||||
|     if (this.oauthService.hasValidAccessToken()) { | ||||
|       this._connection.send('authenticate', this.oauthService.getAccessToken()); | ||||
|       this.authenticated=true; | ||||
|     } else { | ||||
|   | ||||
| @@ -18,39 +18,32 @@ export class FolderService { | ||||
|     return this.appConfig.getConfig("apiEndPoint"); | ||||
|   } | ||||
|  | ||||
|   parseDates(item: any): IListItem { | ||||
|     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; | ||||
|   } | ||||
|  | ||||
|      getFolder(code: string): Observable<IListItem> { | ||||
|       return this.httpClient.get<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/${code}`).pipe(map(i => this.parseDates(i)));             | ||||
|       return this.httpClient.get<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/${code}`);             | ||||
|     } | ||||
|  | ||||
|     getMyRoots(): Observable<IListItem[]> { | ||||
|       return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/my_roots`).pipe(map(ia => ia.map(i => this.parseDates(i)))); | ||||
|       return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/my_roots`); | ||||
|     }     | ||||
|  | ||||
|     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)))); | ||||
|       return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/parents`); | ||||
|     } | ||||
|  | ||||
|     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)))); | ||||
|       return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/listfolders`); | ||||
|     } | ||||
|  | ||||
|     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)))); | ||||
|       return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/list?skip=${skip}&take=${take}`); | ||||
|     } | ||||
|  | ||||
|     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))); | ||||
|       return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/items/move`, body); | ||||
|     } | ||||
|  | ||||
|     createFolder(folder: IItem): Observable<IListItem> { | ||||
|       return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/`, folder).pipe(map(i => this.parseDates(i))); | ||||
|       return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/`, folder); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										30
									
								
								projects/common/src/fm/services/healthcheck.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								projects/common/src/fm/services/healthcheck.service.ts
									
									
									
									
									
										Normal 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; | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user