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 { |   stages { | ||||||
|     stage('npm install'){ |     stage('npm install'){ | ||||||
|       steps { |       steps { | ||||||
|         sh '''npm install |         sh '''rm -rf node_modules/ | ||||||
|  |         npm install | ||||||
|         cd projects/common |         cd projects/common | ||||||
|         npm install |         npm install | ||||||
|         cd ../common-map |         cd ../common-map | ||||||
|         npm install |         npm install | ||||||
|  |         cd ../common-map3d | ||||||
|  |         npm install | ||||||
|         ''' |         ''' | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     stage('build'){ |     stage('build'){ | ||||||
|       steps { |       steps { | ||||||
|         sh '''ng build common |         sh '''ng build common | ||||||
|         ng build common-map''' |         ng build common-map | ||||||
|  |         ng build common-map3d''' | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     stage('npm publish'){ |     stage('npm publish'){ | ||||||
| @@ -28,6 +32,9 @@ pipeline { | |||||||
|         npm publish |         npm publish | ||||||
|         cd ../common-map |         cd ../common-map | ||||||
|         npm version ${PACKAGE_VERSION} |         npm version ${PACKAGE_VERSION} | ||||||
|  |         npm publish | ||||||
|  |         cd ../common-map3d | ||||||
|  |         npm version ${PACKAGE_VERSION} | ||||||
|         npm publish''' |         npm publish''' | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										71
									
								
								angular.json
									
									
									
									
									
								
							
							
						
						
									
										71
									
								
								angular.json
									
									
									
									
									
								
							| @@ -11,8 +11,11 @@ | |||||||
|       "schematics": {}, |       "schematics": {}, | ||||||
|       "architect": { |       "architect": { | ||||||
|         "build": { |         "build": { | ||||||
|           "builder": "@angular-devkit/build-angular:browser", |           "builder": "@angular-builders/custom-webpack:browser", | ||||||
|           "options": { |           "options": { | ||||||
|  |             "customWebpackConfig": { | ||||||
|  |               "path": "./custom-webpack.config.js" | ||||||
|  |             }, | ||||||
|             "aot": true, |             "aot": true, | ||||||
|             "outputPath": "dist/farmmaps-lib-app", |             "outputPath": "dist/farmmaps-lib-app", | ||||||
|             "index": "src/index.html", |             "index": "src/index.html", | ||||||
| @@ -22,18 +25,23 @@ | |||||||
|             "assets": [ |             "assets": [ | ||||||
|               { |               { | ||||||
|                 "glob": "**/*", |                 "glob": "**/*", | ||||||
|                 "input":"src/assets/images", |                 "input": "src/assets/images", | ||||||
|                 "output":"/images" |                 "output": "/images" | ||||||
|               }, |               }, | ||||||
|               { |               { | ||||||
|                 "glob": "silent-refresh.html", |                 "glob": "silent-refresh.html", | ||||||
|                 "input":"src/assets", |                 "input": "src/assets", | ||||||
|                 "output":"/" |                 "output": "/" | ||||||
|               }, |               }, | ||||||
|               { |               { | ||||||
|                 "glob": "favicon.ico", |                 "glob": "favicon.ico", | ||||||
|                 "input":"src/assets", |                 "input": "src/assets", | ||||||
|                 "output":"/" |                 "output": "/" | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 "glob": "**/*", | ||||||
|  |                 "input": "node_modules/cesium/Build/Cesium", | ||||||
|  |                 "output": "/assets/cesium" | ||||||
|               }, |               }, | ||||||
|               "src/configuration.json" |               "src/configuration.json" | ||||||
|             ], |             ], | ||||||
| @@ -41,10 +49,11 @@ | |||||||
|               "src/styles.css", |               "src/styles.css", | ||||||
|               "node_modules/bootstrap/dist/css/bootstrap.min.css", |               "node_modules/bootstrap/dist/css/bootstrap.min.css", | ||||||
|               "node_modules/font-awesome/css/font-awesome.min.css",               |               "node_modules/font-awesome/css/font-awesome.min.css",               | ||||||
|               "fonts/FMIconFont/style.css" |               "fonts/FMIconFont/style.css", | ||||||
|  |               "node_modules/cesium/Build/Cesium/Widgets/widgets.css", | ||||||
|  |               "node_modules/ol/ol.css" | ||||||
|             ], |             ], | ||||||
|             "scripts": [], |             "scripts": [] | ||||||
|             "es5BrowserSupport": true |  | ||||||
|           }, |           }, | ||||||
|           "configurations": { |           "configurations": { | ||||||
|             "production": { |             "production": { | ||||||
| @@ -78,7 +87,7 @@ | |||||||
|           } |           } | ||||||
|         }, |         }, | ||||||
|         "serve": { |         "serve": { | ||||||
|           "builder": "@angular-devkit/build-angular:dev-server", |           "builder": "@angular-builders/custom-webpack:dev-server", | ||||||
|           "options": { |           "options": { | ||||||
|             "browserTarget": "farmmaps-lib-app:build" |             "browserTarget": "farmmaps-lib-app:build" | ||||||
|           }, |           }, | ||||||
| @@ -232,6 +241,46 @@ | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |     }, | ||||||
|  |     "common-map3d": { | ||||||
|  |       "projectType": "library", | ||||||
|  |       "root": "projects/common-map3d", | ||||||
|  |       "sourceRoot": "projects/common-map3d/src", | ||||||
|  |       "prefix": "fm-map3d", | ||||||
|  |       "architect": { | ||||||
|  |         "build": { | ||||||
|  |           "builder": "@angular-devkit/build-angular:ng-packagr:build", | ||||||
|  |           "options": { | ||||||
|  |             "tsConfig": "projects/common-map3d/tsconfig.lib.json", | ||||||
|  |             "project": "projects/common-map3d/ng-package.json" | ||||||
|  |           }, | ||||||
|  |           "configurations": { | ||||||
|  |             "production": { | ||||||
|  |               "tsConfig": "projects/common-map3d/tsconfig.lib.prod.json" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "test": { | ||||||
|  |           "builder": "@angular-devkit/build-angular:karma", | ||||||
|  |           "options": { | ||||||
|  |             "main": "projects/common-map3d/src/test.ts", | ||||||
|  |             "tsConfig": "projects/common-map3d/tsconfig.spec.json", | ||||||
|  |             "karmaConfig": "projects/common-map3d/karma.conf.js" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "lint": { | ||||||
|  |           "builder": "@angular-devkit/build-angular:tslint", | ||||||
|  |           "options": { | ||||||
|  |             "tsConfig": [ | ||||||
|  |               "projects/common-map3d/tsconfig.lib.json", | ||||||
|  |               "projects/common-map3d/tsconfig.spec.json" | ||||||
|  |             ], | ||||||
|  |             "exclude": [ | ||||||
|  |               "**/node_modules/**" | ||||||
|  |             ] | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "defaultProject": "farmmaps-lib-app" |   "defaultProject": "farmmaps-lib-app" | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								custom-webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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, |   "private": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@angular/animations": "~9.1.0", |     "@angular/animations": "~10.1.3", | ||||||
|     "@angular/common": "~9.1.0", |     "@angular/common": "~10.1.3", | ||||||
|     "@angular/compiler": "~9.1.0", |     "@angular/compiler": "~10.1.3", | ||||||
|     "@angular/core": "~9.1.0", |     "@angular/core": "~10.1.3", | ||||||
|     "@angular/forms": "~9.1.0", |     "@angular/forms": "~10.1.3", | ||||||
|     "@angular/platform-browser": "~9.1.0", |     "@angular/platform-browser": "~10.1.3", | ||||||
|     "@angular/platform-browser-dynamic": "~9.1.0", |     "@angular/platform-browser-dynamic": "~10.1.3", | ||||||
|     "@angular/router": "~9.1.0", |     "@angular/router": "~10.1.3", | ||||||
|  |     "@farmmaps/common": ">=0.0.1-prerelease.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", |     "@microsoft/signalr": "^3.1.3", | ||||||
|     "@farmmaps/common": ">=0.0.1-prerelease.267 <0.0.1", |     "@ng-bootstrap/ng-bootstrap": "^7.0", | ||||||
|     "@farmmaps/common-map": ">=0.0.1-prerelease.267 <0.0.1", |     "@ngrx/effects": "^10.0", | ||||||
|     "@ng-bootstrap/ng-bootstrap": "^6.0", |     "@ngrx/router-store": "^10.0", | ||||||
|     "@ngrx/effects": "^9.0", |     "@ngrx/store": "^10.0", | ||||||
|     "@ngrx/router-store": "^9.0", |     "angular-oauth2-oidc": "^10.0.3", | ||||||
|     "@ngrx/store": "^9.0", |  | ||||||
|     "ngx-uploadx": "^3.3.4", |  | ||||||
|     "angular-oauth2-oidc": "^9.1", |  | ||||||
|     "bootstrap": "^4.4.1", |     "bootstrap": "^4.4.1", | ||||||
|  |     "cesium": "^1.73.0", | ||||||
|     "core-js": "^2.6.11", |     "core-js": "^2.6.11", | ||||||
|     "font-awesome": "^4.7.0", |     "font-awesome": "^4.7.0", | ||||||
|     "ngrx-store-localstorage": "^9.0", |     "ngrx-store-localstorage": "^10.0", | ||||||
|  |     "ngx-bootstrap": "^5.6.1", | ||||||
|  |     "ngx-openlayers": "1.0.0-next.16", | ||||||
|  |     "ngx-uploadx": "^3.5.1", | ||||||
|  |     "ol": "6.4.3", | ||||||
|  |     "ol-cesium": "^2.11.3", | ||||||
|     "resumablejs": "^1.1.0", |     "resumablejs": "^1.1.0", | ||||||
|     "rxjs": "^6.5.4", |     "rxjs": "^6.5.4", | ||||||
|     "tassign": "^1.0.0", |     "tassign": "^1.0.0", | ||||||
|     "tslib": "^1.10.0", |     "tslib": "^2.0.0", | ||||||
|     "zone.js": "~0.10.2", |     "zone.js": "~0.10.2", | ||||||
|     "ngx-openlayers": "1.0.0-next.13", |     "moment": "^2.27.0" | ||||||
|     "ol": "6.1.1" |  | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@angular-devkit/build-angular": "~0.901.0", |     "@angular-builders/custom-webpack": "~10.0.1", | ||||||
|     "@angular-devkit/build-ng-packagr": "~0.901.0", |     "@angular-devkit/build-angular": "^0.1002.0", | ||||||
|     "@angular/cli": "^9.1.0", |     "@angular-devkit/build-ng-packagr": "~0.1001.3", | ||||||
|     "@angular/compiler-cli": "~9.1.0", |     "@angular/cli": "^10.1.3", | ||||||
|     "@angular/language-service": "~9.1.0", |     "@angular/compiler-cli": "~10.1.3", | ||||||
|  |     "@angular/language-service": "~10.1.3", | ||||||
|     "@types/jasmine": "~2.8.8", |     "@types/jasmine": "~2.8.8", | ||||||
|     "@types/jasminewd2": "~2.0.3", |     "@types/jasminewd2": "~2.0.3", | ||||||
|     "@types/node": "^12.11.1", |     "@types/node": "^12.11.1", | ||||||
|     "codelyzer": "^5.1.2", |     "codelyzer": "^5.1.2", | ||||||
|     "jasmine-core": "~2.99.1", |     "jasmine-core": "~3.5.0", | ||||||
|     "jasmine-spec-reporter": "~4.2.1", |     "jasmine-spec-reporter": "~5.0.0", | ||||||
|     "karma": "^5.0.2", |     "karma": "~5.0.0", | ||||||
|     "karma-chrome-launcher": "~2.2.0", |     "karma-chrome-launcher": "~3.1.0", | ||||||
|     "karma-coverage-istanbul-reporter": "~2.0.1", |     "karma-coverage-istanbul-reporter": "~3.0.2", | ||||||
|     "karma-jasmine": "~1.1.2", |     "karma-jasmine": "~4.0.0", | ||||||
|     "karma-jasmine-html-reporter": "^0.2.2", |     "karma-jasmine-html-reporter": "^1.5.0", | ||||||
|     "ng-packagr": "^9.0.0", |     "ng-packagr": "^10.1.0", | ||||||
|     "protractor": "^5.4.4", |     "protractor": "~7.0.0", | ||||||
|     "ts-node": "^8.8.1", |     "ts-node": "^8.8.1", | ||||||
|     "tslint": "~5.11.0", |     "tslint": "~6.1.0", | ||||||
|     "typescript": "~3.8.3" |     "typescript": "~4.0.3" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										75
									
								
								projects/common-map/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										75
									
								
								projects/common-map/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -4,79 +4,10 @@ | |||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 1, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@openlayers/pepjs": { |  | ||||||
|       "version": "0.5.3", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@openlayers/pepjs/-/pepjs-0.5.3.tgz", |  | ||||||
|       "integrity": "sha512-Bgvi5c14BS0FJWyYWWFstNEnXsB30nK8Jt8hkAAdqr7E0gDdBBWVDglF3Ub19wTxvgJ/CVHyTY6VuCtnyRzglg==" |  | ||||||
|     }, |  | ||||||
|     "ieee754": { |  | ||||||
|       "version": "1.1.13", |  | ||||||
|       "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", |  | ||||||
|       "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" |  | ||||||
|     }, |  | ||||||
|     "ngx-openlayers": { |  | ||||||
|       "version": "1.0.0-next.13", |  | ||||||
|       "resolved": "https://registry.npmjs.org/ngx-openlayers/-/ngx-openlayers-1.0.0-next.13.tgz", |  | ||||||
|       "integrity": "sha512-6y724s5JV6n1oxEhryxP3wcbwzpierj9RLiMLXInfKOG3l5IO1AtPNkeK9+mKctVDUVm5URIfzzZH02Ld+bvSQ==", |  | ||||||
|       "requires": { |  | ||||||
|         "tslib": "^1.9.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "ol": { |  | ||||||
|       "version": "6.1.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/ol/-/ol-6.1.1.tgz", |  | ||||||
|       "integrity": "sha512-0dL3i3eJqgOpqIjDKEY3grkeQnjAYfV5L/JCxhOu4SxiaizRwFrFgeas6LILRoxKa03jhQFbut2r2bbgcLGQeA==", |  | ||||||
|       "requires": { |  | ||||||
|         "@openlayers/pepjs": "^0.5.3", |  | ||||||
|         "pbf": "3.2.1", |  | ||||||
|         "pixelworks": "1.1.0", |  | ||||||
|         "rbush": "^3.0.1" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "pbf": { |  | ||||||
|       "version": "3.2.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", |  | ||||||
|       "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", |  | ||||||
|       "requires": { |  | ||||||
|         "ieee754": "^1.1.12", |  | ||||||
|         "resolve-protobuf-schema": "^2.1.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "pixelworks": { |  | ||||||
|       "version": "1.1.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/pixelworks/-/pixelworks-1.1.0.tgz", |  | ||||||
|       "integrity": "sha1-Hwla1I3Ki/ihyCWOAJIDGkTyLKU=" |  | ||||||
|     }, |  | ||||||
|     "protocol-buffers-schema": { |  | ||||||
|       "version": "3.4.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz", |  | ||||||
|       "integrity": "sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA==" |  | ||||||
|     }, |  | ||||||
|     "quickselect": { |  | ||||||
|       "version": "2.0.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", |  | ||||||
|       "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" |  | ||||||
|     }, |  | ||||||
|     "rbush": { |  | ||||||
|       "version": "3.0.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", |  | ||||||
|       "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", |  | ||||||
|       "requires": { |  | ||||||
|         "quickselect": "^2.0.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "resolve-protobuf-schema": { |  | ||||||
|       "version": "2.1.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", |  | ||||||
|       "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", |  | ||||||
|       "requires": { |  | ||||||
|         "protocol-buffers-schema": "^3.3.1" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "tslib": { |     "tslib": { | ||||||
|       "version": "1.10.0", |       "version": "2.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", |       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", | ||||||
|       "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" |       "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,15 +4,18 @@ | |||||||
|   "publishConfig": { |   "publishConfig": { | ||||||
|     "registry": "https://repository.akkerweb.nl/repository/npm-hosted/" |     "registry": "https://repository.akkerweb.nl/repository/npm-hosted/" | ||||||
|   }, |   }, | ||||||
|  |   "dependencies": { | ||||||
|  |     "tslib": "^2.0.0" | ||||||
|  |   }, | ||||||
|   "peerDependencies": { |   "peerDependencies": { | ||||||
|     "@angular/core": "^9.1.0", |     "@angular/core": "^10.1.3", | ||||||
|     "ngrx-store-localstorage": "^9.0", |     "ngrx-store-localstorage": "^10.0", | ||||||
|     "@ngrx/effects": "^9.0", |     "@ngrx/effects": "^10.0", | ||||||
|     "@ngrx/router-store": "^9.0", |     "@ngrx/router-store": "^10.0", | ||||||
|     "@ngrx/store": "^9.0", |     "@ngrx/store": "^10.0", | ||||||
|     "tassign": "^1.0.0", |     "tassign": "^1.0.0", | ||||||
|     "@farmmaps/common": ">=0.0.1-prerelease.265 <0.0.1", |     "@farmmaps/common": ">=0.0.1-prerelease.420 <0.0.1", | ||||||
|     "ngx-openlayers": "1.0.0-next.13", |     "ngx-openlayers": "1.0.0-next.16", | ||||||
|     "ol": "6.1.1" |     "ol": "6.1.1" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -87,7 +87,7 @@ export class StartSearch implements Action { | |||||||
| export class StartSearchSuccess implements Action { | export class StartSearchSuccess implements Action { | ||||||
|   readonly type = STARTSEARCHSUCCESS; |   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 { | export class SelectFeature implements Action { | ||||||
| @@ -105,7 +105,7 @@ export class SelectItem implements Action { | |||||||
| export class SelectItemSuccess implements Action { | export class SelectItemSuccess implements Action { | ||||||
|   readonly type = SELECTITEMSUCCESS; |   readonly type = SELECTITEMSUCCESS; | ||||||
|  |  | ||||||
|   constructor(public item: IItem) { } |   constructor(public item: IItem, public parentItem: IItem) { } | ||||||
| } | } | ||||||
|  |  | ||||||
| export class SelectTemporalItemsSuccess implements Action { | export class SelectTemporalItemsSuccess implements Action { | ||||||
|   | |||||||
| @@ -4,21 +4,17 @@ import { MapComponent } from './components/map/map.component'; | |||||||
| import { AuthGuard } from '@farmmaps/common'; | import { AuthGuard } from '@farmmaps/common'; | ||||||
|  |  | ||||||
| const routes = [ | const routes = [ | ||||||
|     { |  | ||||||
|     path: '', children: [ |  | ||||||
|     { |     { | ||||||
|         path: '', |         path: '', | ||||||
|         component: MapComponent    |         component: MapComponent    | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer/:queryState', children: [ |        path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer/:queryState', | ||||||
|             { |        component: MapComponent | ||||||
|                 path: '', |     }, | ||||||
|  |     { | ||||||
|  |        path: ':queryState',  | ||||||
|        component: MapComponent |        component: MapComponent | ||||||
|             } |  | ||||||
|         ] |  | ||||||
|     } |     } | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,7 +24,6 @@ import { IPeriodState } from './models/period.state'; | |||||||
|  |  | ||||||
| // components | // components | ||||||
| import { GpsLocation} from './components/aol/gps-location/gps-location.component'; | import { GpsLocation} from './components/aol/gps-location/gps-location.component'; | ||||||
| //import {Switch2D3DComponent } from './components/aol/switch2d3d/switch2d3d.component'; |  | ||||||
| import {FeatureListFeatureCropfieldComponent } from './components/feature-list-feature-cropfield/feature-list-feature-cropfield.component'; | import {FeatureListFeatureCropfieldComponent } from './components/feature-list-feature-cropfield/feature-list-feature-cropfield.component'; | ||||||
| import { FeatureListFeatureCroppingschemeComponent} from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.component'; | import { FeatureListFeatureCroppingschemeComponent} from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.component'; | ||||||
| import { ItemWidgetListComponent} from './components/item-widget-list/item-widget-list.component'; | import { ItemWidgetListComponent} from './components/item-widget-list/item-widget-list.component'; | ||||||
| @@ -64,8 +63,11 @@ import { WidgetStatusComponent } from './components/widget-status/widget-status. | |||||||
| import { ForChild} from './components/for-item/for-child.decorator'; | import { ForChild} from './components/for-item/for-child.decorator'; | ||||||
| import {ForItemType } from './components/for-item/for-itemtype.decorator'; | import {ForItemType } from './components/for-item/for-itemtype.decorator'; | ||||||
| import { ForSourceTask} from './components/for-item/for-sourcetask.decorator'; | import { ForSourceTask} from './components/for-item/for-sourcetask.decorator'; | ||||||
|  | import { ForPackage } from './components/for-item/for-package.decorator'; | ||||||
| import { PanToLocation} from './components/aol/pan-to-location/pan-to-location.component'; | import { PanToLocation} from './components/aol/pan-to-location/pan-to-location.component'; | ||||||
| import {LayerSwitcher} from './components/layer-switcher/layer-switcher.component'; | import {LayerSwitcher} from './components/layer-switcher/layer-switcher.component'; | ||||||
|  | import {HistogramDetailsComponent} from './components/legend/histogram-details/histogram-details.component'; | ||||||
|  | import {StatisticsDetailsComponent} from './components/legend/statistics-details/statistics-details.component'; | ||||||
|  |  | ||||||
| export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any> { | export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any> { | ||||||
|   const r = function(state, action) { |   const r = function(state, action) { | ||||||
| @@ -143,7 +145,8 @@ export { | |||||||
|   IPeriodState, |   IPeriodState, | ||||||
|   ForChild, |   ForChild, | ||||||
|   ForItemType, |   ForItemType, | ||||||
|   ForSourceTask |   ForSourceTask, | ||||||
|  |   ForPackage | ||||||
| } | } | ||||||
|  |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
| @@ -193,7 +196,9 @@ export { | |||||||
|     WidgetStatusComponent, |     WidgetStatusComponent, | ||||||
|     GpsLocation, |     GpsLocation, | ||||||
|     PanToLocation, |     PanToLocation, | ||||||
|     LayerSwitcher |     LayerSwitcher, | ||||||
|  |     HistogramDetailsComponent, | ||||||
|  |     StatisticsDetailsComponent | ||||||
|   ], |   ], | ||||||
|   entryComponents: [ |   entryComponents: [ | ||||||
|     FeatureListComponent, |     FeatureListComponent, | ||||||
| @@ -246,14 +251,7 @@ export { | |||||||
|        FeatureListCropfieldComponent, |        FeatureListCropfieldComponent, | ||||||
|        FeatureListFeatureContainerComponent, |        FeatureListFeatureContainerComponent, | ||||||
|        ZoomToExtentComponent |        ZoomToExtentComponent | ||||||
|   ] |   ], | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| export class AppCommonMapModule { |  | ||||||
|   static forRoot(): ModuleWithProviders<AppCommonMapModule> { |  | ||||||
|     return { |  | ||||||
|       ngModule: AppCommonMapModule,       |  | ||||||
|   providers: [ |   providers: [ | ||||||
|     FeatureIconService, |     FeatureIconService, | ||||||
|     GeolocationService, |     GeolocationService, | ||||||
| @@ -271,6 +269,8 @@ export class AppCommonMapModule { | |||||||
|     { provide: AbstractItemListItemComponent, useClass: ItemListItemComponent, multi: true }, |     { provide: AbstractItemListItemComponent, useClass: ItemListItemComponent, multi: true }, | ||||||
|     { provide: AbstractItemListComponent, useClass: ItemListComponent, 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 { MapComponent } from 'ngx-openlayers'; | ||||||
| import Overlay from 'ol/Overlay'; | import Overlay from 'ol/Overlay'; | ||||||
| import { fromLonLat, toLonLat } from 'ol/proj'; | import { fromLonLat, toLonLat } from 'ol/proj'; | ||||||
| @@ -23,9 +23,10 @@ export class GpsLocation  implements OnInit,OnChanges{ | |||||||
|   public path: string = ""; |   public path: string = ""; | ||||||
|   public rotate: string = ""; |   public rotate: string = ""; | ||||||
|   private resolution: number = 0; |   private resolution: number = 0; | ||||||
|  |   initialized:boolean = false; | ||||||
|   @ViewChild('location', { static: true }) locationElement: ElementRef; |   @ViewChild('location', { static: true }) locationElement: ElementRef; | ||||||
|  |  | ||||||
|   constructor(private map: MapComponent) { |   constructor(@Host() private map: MapComponent) { | ||||||
|      |      | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -54,12 +55,13 @@ export class GpsLocation  implements OnInit,OnChanges{ | |||||||
|       this.resolution = evt.target.get('resolution'); |       this.resolution = evt.target.get('resolution'); | ||||||
|       this.recalcLocationTolerance(); |       this.recalcLocationTolerance(); | ||||||
|     }); |     }); | ||||||
|  |     this.initialized = true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ngOnChanges(changes: SimpleChanges) { |   ngOnChanges(changes: SimpleChanges) { | ||||||
|     if (changes.position && this.instance) { |     if (changes.position && this.instance) { | ||||||
|       var p = changes.position.currentValue as Position; |       var p = changes.position.currentValue as Position; | ||||||
|       if(p) { |       if(p && this.initialized) { | ||||||
|         this.instance.setPosition(fromLonLat([p.coords.longitude, p.coords.latitude])); |         this.instance.setPosition(fromLonLat([p.coords.longitude, p.coords.latitude])); | ||||||
|         this.locationTolerance = p.coords.accuracy; |         this.locationTolerance = p.coords.accuracy; | ||||||
|         this.recalcLocationTolerance();   |         this.recalcLocationTolerance();   | ||||||
|   | |||||||
| @@ -1,8 +1,7 @@ | |||||||
| import { Component, Host, Input, Output, EventEmitter, Optional, QueryList, OnInit, AfterViewInit, OnChanges, SimpleChanges, SkipSelf, forwardRef, Inject, InjectionToken } from '@angular/core'; | import { Component, Host, Input, Output, EventEmitter,OnDestroy, OnInit, OnChanges, SimpleChanges, forwardRef } from '@angular/core'; | ||||||
| import { HttpClient } from "@angular/common/http"; | import { LayerGroupComponent, MapComponent } from 'ngx-openlayers'; | ||||||
| import { LayerVectorComponent, LayerTileComponent, LayerGroupComponent, MapComponent } from 'ngx-openlayers'; |  | ||||||
| import { ItemService,IItem,AppConfig } from '@farmmaps/common'; | 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 { ILayerData} from '../../../models/layer.data'; | ||||||
| import { IRenderoutputTiles,IRenderoutputImage,IGradientstop,ILayer,IHistogram} from '../../../models/color.map'; | import { IRenderoutputTiles,IRenderoutputImage,IGradientstop,ILayer,IHistogram} from '../../../models/color.map'; | ||||||
| import {Extent} from 'ol/extent'; | import {Extent} from 'ol/extent'; | ||||||
| @@ -11,13 +10,12 @@ import * as proj from 'ol/proj'; | |||||||
| import * as loadingstrategy from 'ol/loadingstrategy'; | import * as loadingstrategy from 'ol/loadingstrategy'; | ||||||
| import * as style from 'ol/style'; | import * as style from 'ol/style'; | ||||||
| import {Tile,Layer,Image} from 'ol/layer'; | import {Tile,Layer,Image} from 'ol/layer'; | ||||||
| import {XYZ,ImageStatic,OSM,BingMaps,TileWMS,TileArcGISRest} from 'ol/source'; | import {XYZ,ImageStatic,OSM,BingMaps,TileWMS,TileArcGISRest,TileJSON} from 'ol/source'; | ||||||
| import {Vector as VectorSource} from 'ol/source'; | import {Vector as VectorSource} from 'ol/source'; | ||||||
| import { Vector as VectorLayer } from 'ol/layer'; | import { Vector as VectorLayer } from 'ol/layer'; | ||||||
| import VectorTileSource from 'ol/source/VectorTile'; | import VectorTileSource from 'ol/source/VectorTile'; | ||||||
| import VectorTileLayer from 'ol/layer/VectorTile'; | import VectorTileLayer from 'ol/layer/VectorTile'; | ||||||
| import {GeoJSON,MVT} from 'ol/format'; | import {GeoJSON,MVT} from 'ol/format'; | ||||||
| import { from } from 'rxjs'; |  | ||||||
|  |  | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'fm-map-item-layers', |   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() itemLayers: IItemLayer[]; | ||||||
|   @Input() itemLayer: IItemLayer; |   @Input() itemLayer: IItemLayer; | ||||||
|  |   @Output() onFeatureSelected: EventEmitter<string> = new EventEmitter<string>(); | ||||||
|  |   @Output() onFeatureHover: EventEmitter<string> = new EventEmitter<string>(); | ||||||
|   private _apiEndPoint: 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) { |   constructor(private itemService: ItemService, @Host() private map: MapComponent, public appConfig: AppConfig) { | ||||||
|     super(map); |     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); |      return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b) + this.componentToHex(a); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getColorFromGradient(layer: ILayer,  feature): style.Style { |   getColorFromGradient(item:IItem,layer: ILayer,  feature): style.Style { | ||||||
|     var value = feature.get(layer.name); |     var value = layer.indexKey ? feature.get(layer.indexKey): feature.get(layer.name); | ||||||
|  |     var key = item.code + "_" + value; | ||||||
|  |     if(!this.styleCache[key]) { | ||||||
|       var gradient: IGradientstop[] = layer.renderer.colorMap.gradient; |       var gradient: IGradientstop[] = layer.renderer.colorMap.gradient; | ||||||
|     var histogram: IHistogram = layer.renderer.band.histogram; |     var histogram: IHistogram = layer.renderer.band.histogram; | ||||||
|     var index = (value - histogram.min) / histogram.max; |     var index = (value - histogram.min) / histogram.max; | ||||||
| @@ -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 green = Math.round(min.color.green + ((max.color.green - min.color.green) * i / size)); | ||||||
|     var blue = Math.round(min.color.blue + ((max.color.blue - min.color.blue) * i / size)); |     var blue = Math.round(min.color.blue + ((max.color.blue - min.color.blue) * i / size)); | ||||||
|  |  | ||||||
|     return new style.Style( |     this.styleCache[key] = new style.Style( | ||||||
|       { |       { | ||||||
|         image: new style.Circle({ |         image: new style.Circle({ | ||||||
|           fill: new style.Fill({ |           fill: new style.Fill({ | ||||||
| @@ -79,33 +85,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | |||||||
|           color: this.rgbaToHex(red, green, blue, alpha) |           color: this.rgbaToHex(red, green, blue, alpha) | ||||||
|         }), |         }), | ||||||
|         stroke: new style.Stroke({ |         stroke: new style.Stroke({ | ||||||
|           color: this.rgbaToHex(red, green, blue, alpha), |           color: this.rgbaToHex(red, green, blue, 255), | ||||||
|           width: 1.25 |           width: 1.25 | ||||||
|         }), |         }), | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|  |     return this.styleCache[key]; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   createGeotiffLayer(item:IItem,itemLayer:IItemLayer):Layer { |   createGeotiffLayer(item:IItem,itemLayer:IItemLayer):Layer { | ||||||
|     var layerIndex = -1; |     var layerIndex = -1; | ||||||
|     var layer: Layer = null;   |     var layer: Layer = null;   | ||||||
|     layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; |     layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; | ||||||
|     let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` }); |     let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}` }); | ||||||
|     layer = new Tile({ source: source }); |     layer = new Tile({ source: source }); | ||||||
|     var data = item.data; |     var data = item.data; | ||||||
|     var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; |     var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; | ||||||
|     if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { |     if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { | ||||||
|       var rt = l.rendering as IRenderoutputTiles; |       var rt = l.rendering as IRenderoutputTiles; | ||||||
|       let source = new XYZ({ maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` }); |       let source = new XYZ({crossOrigin: 'use-credentials', maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}` }); | ||||||
|       layer = new Tile({ source: source }); |       layer = new Tile({ source: source }); | ||||||
|     } |     } | ||||||
|     if (l && l.rendering && l.rendering.renderoutputType == "Image") { |     if (l && l.rendering && l.rendering.renderoutputType == "Image") { | ||||||
|       var ri = l.rendering as IRenderoutputImage;   |       var ri = l.rendering as IRenderoutputImage;   | ||||||
|       let projection = new Projection({ |       // convert to EPSG:4326 asworkaround for cesium    | ||||||
|         code: 'image', |       var projectedExtent = proj.transformExtent( ri.extent, 'EPSG:3857','EPSG:4326'); | ||||||
|         units: 'pixels', |       let source = new ImageStatic({ imageExtent:projectedExtent,projection:'EPSG:4326',  crossOrigin: 'use-credentials', url: `${this._apiEndPoint}/api/v1/items/${item.code}/mapimage/${layerIndex}?v=${Date.parse(item.updated)}` }); | ||||||
|         extent: ri.extent |  | ||||||
|       }); |  | ||||||
|       let source = new ImageStatic({ imageExtent: ri.extent, projection: projection, url: `${this._apiEndPoint}/api/v1/items/${item.code}/mapimage/${layerIndex}?v=${item.updated.getTime()}` }); |  | ||||||
|       layer = new Image({ source: source }); |       layer = new Image({ source: source }); | ||||||
|     } |     } | ||||||
|     return layer; |     return layer; | ||||||
| @@ -116,19 +121,19 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | |||||||
|     var layer: Layer = null;  |     var layer: Layer = null;  | ||||||
|     layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; |     layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index; | ||||||
|     var data = item.data; |     var data = item.data; | ||||||
|     var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null; |     var l:ILayer = (data && data.layers && data.layers.length > 0) ? data.layers[layerIndex] : null; | ||||||
|     if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") { |     if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") { | ||||||
|       var rt = item.data.layers[layerIndex].rendering as IRenderoutputTiles; |       var rt = l.rendering as IRenderoutputTiles; | ||||||
|       layer = new VectorTileLayer({ |       layer = new VectorTileLayer({ | ||||||
|         declutter: true, |         declutter: true, | ||||||
|         source: new VectorTileSource({ |         source: new VectorTileSource({ | ||||||
|           maxZoom: rt.maxzoom, |           maxZoom: rt.maxzoom, | ||||||
|           minZoom: rt.minzoom, |           minZoom: rt.minzoom, | ||||||
|           format: new MVT(), |           format: new MVT(), | ||||||
|           url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${item.updated.getTime()}` |           url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${Date.parse(item.updated)}` | ||||||
|         }), |         }), | ||||||
|         style: (feature) => { |         style: (feature) => { | ||||||
|           return this.getColorFromGradient(l, feature); |           return this.getColorFromGradient(item,l, feature); | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|     } else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { |     } else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") { | ||||||
| @@ -137,7 +142,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | |||||||
|         source: new XYZ({ |         source: new XYZ({ | ||||||
|           maxZoom: rt.maxzoom, |           maxZoom: rt.maxzoom, | ||||||
|           minZoom: rt.minzoom, |           minZoom: rt.minzoom, | ||||||
|           url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` |           url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}` | ||||||
|         }) |         }) | ||||||
|       }); |       }); | ||||||
|     } else { |     } else { | ||||||
| @@ -161,7 +166,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | |||||||
|       layer = new VectorLayer({ |       layer = new VectorLayer({ | ||||||
|         source: source, |         source: source, | ||||||
|         style: (feature) => { |         style: (feature) => { | ||||||
|           var key = feature.get("color"); |           var key =feature.get("code") + "_" + feature.get("color"); | ||||||
|           if (!this.styleCache[key]) { |           if (!this.styleCache[key]) { | ||||||
|             var color = feature.get("color"); |             var color = feature.get("color"); | ||||||
|             this.styleCache[key] = new style.Style( |             this.styleCache[key] = new style.Style( | ||||||
| @@ -190,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; |     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 { |   createExternalLayer(item:IItem,itemLayer:IItemLayer):Layer { | ||||||
|     let data = item.data as ILayerData; |     let data = item.data as ILayerData; | ||||||
|     var layer: Layer = null;  |     var layer: Layer = null;  | ||||||
| @@ -212,11 +256,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | |||||||
|         layer = new Tile({ source: source }); |         layer = new Tile({ source: source }); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|  |       case 'TileJSON': { | ||||||
|  |         let source = new TileJSON(data.options); | ||||||
|  |         layer = new Tile({ source: source }); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|       case 'TileArcGISRest': { |       case 'TileArcGISRest': { | ||||||
|         let source = new TileArcGISRest(data.options); |         let source = new TileArcGISRest(data.options); | ||||||
|         layer = new Tile({ source: source }); |         layer = new Tile({ source: source }); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|  |       case 'VectorWFSJson': { | ||||||
|  |         let source = new VectorSource({ | ||||||
|  |           format: new GeoJSON(), | ||||||
|  |           url: function (extent) { | ||||||
|  |             return ( | ||||||
|  |               data.options.url + '&srsname=' + data.projection + | ||||||
|  |               '&bbox=' + | ||||||
|  |               extent.join(',') + | ||||||
|  |               ',EPSG:3857' | ||||||
|  |             ); | ||||||
|  |           }, | ||||||
|  |           strategy: loadingstrategy.bbox, | ||||||
|  |         }); | ||||||
|  |         layer = new VectorLayer({ source: source }); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|       default: { |       default: { | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
| @@ -245,7 +310,28 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | |||||||
|  |  | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     super.ngOnInit(); |     super.ngOnInit(); | ||||||
|  |     if(this.itemLayers) { | ||||||
|       this.updateLayers(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 { |   addOrUpdateOlLayer(itemLayer:IItemLayer,index:number):Layer { | ||||||
| @@ -294,23 +380,52 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange | |||||||
|       while(olLayers.getLength() > newLayers.length) { |       while(olLayers.getLength() > newLayers.length) { | ||||||
|         olLayers.removeAt(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) { |   ngOnChanges(changes: SimpleChanges) { | ||||||
|     if (this.instance) { |     if (this.instance && this.initialized) { | ||||||
|       if (changes['itemLayers']) { |       if (changes['itemLayers']) { | ||||||
|         var itemLayers = changes['itemLayers'].currentValue as IItemLayer[]; |         var itemLayers = changes['itemLayers'].currentValue as IItemLayer[]; | ||||||
|         this.updateLayers(itemLayers); |         this.updateLayers(itemLayers); | ||||||
|       } |       } | ||||||
|       if (changes['itemLayer']) {       |       if (changes['itemLayer']) {       | ||||||
|         var itemLayer = changes['itemLayer'].currentValue as IItemLayer; |         var itemLayer = changes['itemLayer'].currentValue as IItemLayer; | ||||||
|  |         this.itemLayer = itemLayer | ||||||
|         if(itemLayer) { |         if(itemLayer) { | ||||||
|  |           if(itemLayer.item.itemType == 'vnd.farmmaps.itemtype.shape.processed') { | ||||||
|  |             this.installMapEventHandler(); | ||||||
|  |           } | ||||||
|           this.updateLayers([itemLayer]); |           this.updateLayers([itemLayer]); | ||||||
|         } else { |         } else { | ||||||
|  |           this.unInstallMapEventHandler(); | ||||||
|           this.updateLayers([]);           |           this.updateLayers([]);           | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   ngOnDestroy() { | ||||||
|  |     this.unInstallMapEventHandler(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements | |||||||
|   @Input() selectedFeature: Feature; |   @Input() selectedFeature: Feature; | ||||||
|   @Input() selectedItem: IItem; |   @Input() selectedItem: IItem; | ||||||
|   @Input() styles:IStyles; |   @Input() styles:IStyles; | ||||||
|   @Output() onFeaturesSelected: EventEmitter<Feature> = new EventEmitter<Feature>(); |   @Output() onFeatureSelected: EventEmitter<Feature> = new EventEmitter<Feature>(); | ||||||
|   @Output() onFeatureHover: EventEmitter<Feature> = new EventEmitter<Feature>(); |   @Output() onFeatureHover: EventEmitter<Feature> = new EventEmitter<Feature>(); | ||||||
|   private stylesCache:IStyles = {}; |   private stylesCache:IStyles = {}; | ||||||
|  |  | ||||||
| @@ -96,9 +96,9 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements | |||||||
|     this.map.instance.addInteraction(this._hoverSelect);       |     this.map.instance.addInteraction(this._hoverSelect);       | ||||||
|     this._select.on('select', (e) => { |     this._select.on('select', (e) => { | ||||||
|       if (e.selected.length > 0 && e.selected[0]) { |       if (e.selected.length > 0 && e.selected[0]) { | ||||||
|         this.onFeaturesSelected.emit(e.selected[0]); |         this.onFeatureSelected.emit(e.selected[0]); | ||||||
|       } else { |       } else { | ||||||
|         this.onFeaturesSelected.emit(null); |         this.onFeatureSelected.emit(null); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|     this._hoverSelect.on('select', (e) => { |     this._hoverSelect.on('select', (e) => { | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
|   border-radius:1.75em; |   border-radius:1.75em; | ||||||
|   padding:0; |   padding:0; | ||||||
|   margin-top:0.5em; |   margin-top:0.5em; | ||||||
|  |   display: block; | ||||||
| } | } | ||||||
|  |  | ||||||
| #north { | #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; |       let threshold = 40 * rem; | ||||||
|       var left = 1 * rem; |       var left = 1 * rem; | ||||||
|       var right = 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; |       var top = 1 * rem; | ||||||
|       if (size[0] > threshold) { |       if (size[0] > threshold) { | ||||||
|         bottom = 1 * rem; |         bottom = 5 * rem; | ||||||
|         left = 23 * rem; |         left = 23 * rem; | ||||||
|       } |       } | ||||||
|       options.padding = [top, right, bottom, left]; |       options.padding = [top, right, bottom, left]; | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import { Component, Input, OnInit, ComponentFactoryResolver, ViewChild, SimpleCh | |||||||
| import { Feature } from 'ol'; | import { Feature } from 'ol'; | ||||||
| import { FeatureListComponent,AbstractFeatureListComponent } from '../feature-list/feature-list.component'; | import { FeatureListComponent,AbstractFeatureListComponent } from '../feature-list/feature-list.component'; | ||||||
| import { WidgetHostDirective } from '../widget-host/widget-host.directive'; | import { WidgetHostDirective } from '../widget-host/widget-host.directive'; | ||||||
| import  {IQueryState } from '@farmmaps/common'; | import  {IQueryState,PackageService } from '@farmmaps/common'; | ||||||
| import * as mapReducers from '../../reducers/map.reducer'; | import * as mapReducers from '../../reducers/map.reducer'; | ||||||
| import * as mapActions from '../../actions/map.actions'; | import * as mapActions from '../../actions/map.actions'; | ||||||
| import { Store } from '@ngrx/store'; | import { Store } from '@ngrx/store'; | ||||||
| @@ -16,7 +16,8 @@ import { Observable } from 'rxjs'; | |||||||
| }) | }) | ||||||
| export class FeatureListContainerComponent { | export class FeatureListContainerComponent { | ||||||
|  |  | ||||||
|   constructor(private store: Store<mapReducers.State>,private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListComponent) public featureLists: AbstractFeatureListComponent[] ) { |   constructor(private store: Store<mapReducers.State>,private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListComponent) public featureLists: AbstractFeatureListComponent[],private packageService:PackageService ) { | ||||||
|  |     this.featureLists = [...this.featureLists].reverse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Input() features: Array<Feature> |   @Input() features: Array<Feature> | ||||||
| @@ -28,31 +29,45 @@ export class FeatureListContainerComponent { | |||||||
|   componentRef:any; |   componentRef:any; | ||||||
|  |  | ||||||
|   loadComponent(queryState:IQueryState) { |   loadComponent(queryState:IQueryState) { | ||||||
|     var componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default |     let componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default | ||||||
|     var selected = -1; |     let selected = -1; | ||||||
|     for (var i = 0; i < this.featureLists.length; i++) { |     let maxMatches =0; | ||||||
|       if (this.featureLists[i]['forItemType'] == queryState.itemType && this.featureLists[i]['forChild'] && queryState.parentCode && queryState.parentCode != "") { |     let showItem = true; | ||||||
|         selected = i; |     for (let i = 0; i < this.featureLists.length; i++) { | ||||||
|         break; |       let matches=0; | ||||||
|       } else if (this.featureLists[i]['forItemType'] == queryState.itemType) { |       let criteria=0; | ||||||
|         selected = i;         |       if (this.featureLists[i]['forItemType']) { | ||||||
|         break; |         criteria++; | ||||||
|  |         if( this.featureLists[i]['forItemType'].indexOf(queryState.itemType) >= 0) { | ||||||
|  |           matches++; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       if(this.featureLists[i]['forChild'] ) { | ||||||
|  |         criteria++; | ||||||
|  |         if(queryState.parentCode && queryState.parentCode != "") { | ||||||
|  |           matches++; | ||||||
|  |         } | ||||||
|  |       }      | ||||||
|  |       if(criteria == matches && matches > maxMatches) { | ||||||
|  |         selected=i; | ||||||
|  |         maxMatches = matches; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     if (selected >= 0) { |     if (selected >= 0) { | ||||||
|       componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[i]['constructor'] as any); |       componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[selected]['constructor'] as any); | ||||||
|       if (this.featureLists[selected]['collapseSearch'] === true) { |       if (this.featureLists[selected]['collapseSearch'] === true) { | ||||||
|         this.store.dispatch(new mapActions.CollapseSearch()); |         this.store.dispatch(new mapActions.CollapseSearch()); | ||||||
|       }      |       }      | ||||||
|     } |     } | ||||||
|     const viewContainerRef = this.widgetHost.viewContainerRef; |     const viewContainerRef = this.widgetHost.viewContainerRef; | ||||||
|     viewContainerRef.clear(); |     viewContainerRef.clear(); | ||||||
|  |     if(showItem) { | ||||||
|       this.componentRef = viewContainerRef.createComponent(componentFactory); |       this.componentRef = viewContainerRef.createComponent(componentFactory); | ||||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).features = null; |       (<AbstractFeatureListComponent>this.componentRef.instance).features = null; | ||||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).queryState = queryState; |       (<AbstractFeatureListComponent>this.componentRef.instance).queryState = queryState; | ||||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = null; |       (<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = null; | ||||||
|     }     |     }     | ||||||
|  |   } | ||||||
|  |  | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this.clickedFeature.subscribe((feature => { |     this.clickedFeature.subscribe((feature => { | ||||||
| @@ -66,9 +81,11 @@ export class FeatureListContainerComponent { | |||||||
|     } |     } | ||||||
|     if ((changes["features"] && changes["features"].currentValue)) { |     if ((changes["features"] && changes["features"].currentValue)) { | ||||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).features = changes["features"].currentValue; |       (<AbstractFeatureListComponent>this.componentRef.instance).features = changes["features"].currentValue; | ||||||
|  |       this.componentRef.changeDetectorRef.detectChanges(); | ||||||
|     } |     } | ||||||
|     if(changes["selectedFeature"]) { |     if(changes["selectedFeature"]) { | ||||||
|       (<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = changes["selectedFeature"].currentValue; |       (<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 { | export class FeatureListFeatureContainerComponent { | ||||||
|  |  | ||||||
|   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListFeatureComponent) public featureLists: AbstractFeatureListFeatureComponent[] ) { |   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListFeatureComponent) public featureLists: AbstractFeatureListFeatureComponent[] ) { | ||||||
|  |     this.featureLists = [...this.featureLists].reverse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Input() feature: Feature; |   @Input() feature: Feature; | ||||||
| @@ -23,11 +24,27 @@ export class FeatureListFeatureContainerComponent { | |||||||
|  |  | ||||||
|   loadComponent() { |   loadComponent() { | ||||||
|     var componentFactory: ComponentFactory<AbstractFeatureListFeatureComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListFeatureComponent); // default |     var componentFactory: ComponentFactory<AbstractFeatureListFeatureComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListFeatureComponent); // default | ||||||
|     for (var i = 0; i < this.featureLists.length; i++) { |     | ||||||
|       if (this.featureLists[i]['forItemType'] == this.feature.get("itemType")) { |     let selected = -1; | ||||||
|         componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[i]['constructor'] as any); |     let maxMatches =0; | ||||||
|  |     for (let i = 0; i < this.featureLists.length; i++) { | ||||||
|  |       let matches=0; | ||||||
|  |       let criteria=0; | ||||||
|  |       if (this.featureLists[i]['forItemType']) { | ||||||
|  |         criteria++; | ||||||
|  |         if(this.featureLists[i]['forItemType'].indexOf(this.feature.get("itemType")) >= 0) { | ||||||
|  |           matches++; | ||||||
|         } |         } | ||||||
|       }          |       }          | ||||||
|  |       if(criteria == matches && matches > maxMatches) { | ||||||
|  |         selected=i; | ||||||
|  |         maxMatches = matches; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (selected >= 0) { | ||||||
|  |       componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[selected]['constructor'] as any);         | ||||||
|  |     }      | ||||||
|  |  | ||||||
|     const viewContainerRef = this.widgetHost.viewContainerRef; |     const viewContainerRef = this.widgetHost.viewContainerRef; | ||||||
|     viewContainerRef.clear(); |     viewContainerRef.clear(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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 { | export class ItemListItemContainerComponent { | ||||||
|  |  | ||||||
|   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractItemListItemComponent) public itemComponentList: AbstractItemListItemComponent[] ) { |   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractItemListItemComponent) public itemComponentList: AbstractItemListItemComponent[] ) { | ||||||
|  |     this.itemComponentList = [...this.itemComponentList].reverse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Input() item: IListItem; |   @Input() item: IListItem; | ||||||
| @@ -23,15 +24,35 @@ export class ItemListItemContainerComponent { | |||||||
|  |  | ||||||
|   loadComponent() { |   loadComponent() { | ||||||
|     var componentFactory: ComponentFactory<AbstractItemListItemComponent> = this.componentFactoryResolver.resolveComponentFactory(ItemListItemComponent); // default |     var componentFactory: ComponentFactory<AbstractItemListItemComponent> = this.componentFactoryResolver.resolveComponentFactory(ItemListItemComponent); // default | ||||||
|     for (var i = 0; i < this.itemComponentList.length; i++) { |  | ||||||
|       if (this.itemComponentList[i]['forItemType'] && |     let selected = -1; | ||||||
|         this.itemComponentList[i]['forItemType'].indexOf(this.item.itemType) >= 0 && |     let maxMatches =0; | ||||||
|         this.itemComponentList[i]['forSourceTask'] && |     let showItem = true; | ||||||
|         this.itemComponentList[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0 ) |     for (let i = 0; i < this.itemComponentList.length; i++) { | ||||||
|       { |       let matches=0; | ||||||
|         componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.itemComponentList[i]['constructor'] as any); |       let criteria=0; | ||||||
|  |       if (this.itemComponentList[i]['forItemType']) { | ||||||
|  |         criteria++; | ||||||
|  |         if(this.itemComponentList[i]['forItemType'].indexOf(this.item.itemType) >= 0) { | ||||||
|  |           matches++; | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |       if (this.itemComponentList[i]['forSourceTask']) { | ||||||
|  |         criteria++; | ||||||
|  |         if(this.itemComponentList[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0) { | ||||||
|  |           matches++; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |          | ||||||
|  |       if(criteria == matches && matches > maxMatches) { | ||||||
|  |         selected=i; | ||||||
|  |         maxMatches = matches; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (selected >= 0) { | ||||||
|  |       componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.itemComponentList[selected]['constructor'] as any);          | ||||||
|  |     } | ||||||
|  |      | ||||||
|     const viewContainerRef = this.widgetHost.viewContainerRef; |     const viewContainerRef = this.widgetHost.viewContainerRef; | ||||||
|     viewContainerRef.clear(); |     viewContainerRef.clear(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -36,10 +36,12 @@ export class LayerSwitcher  implements OnInit,OnChanges{ | |||||||
|  |  | ||||||
|  |  | ||||||
|   handleClick(event:Event) { |   handleClick(event:Event) { | ||||||
|  |     event.stopPropagation(); | ||||||
|     this.store.dispatch(new mapActions.ShowLayerSwitcher(true)); |     this.store.dispatch(new mapActions.ShowLayerSwitcher(true)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   close(event:Event) { |   close(event:Event) { | ||||||
|  |     event.stopPropagation(); | ||||||
|     this.store.dispatch(new mapActions.ShowLayerSwitcher(false)); |     this.store.dispatch(new mapActions.ShowLayerSwitcher(false)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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()"> | <table class="container" *ngIf="showLegend()"> | ||||||
|   <tr> |   <tr> | ||||||
|     <td colspan="2"> |     <td colspan="3"> | ||||||
|       <div class="title"> |       <div class="title"> | ||||||
|         <h4 *ngIf="showTitle">{{layer.name}}</h4> |         <h4 *ngIf="showTitle">{{layer.name}}</h4> | ||||||
|         <b *ngIf="layer.unit">({{layer.unit}})</b> |         <b *ngIf="layer.unit">({{layer.unit}})</b> | ||||||
|       </div> |       </div> | ||||||
|     </td> |     </td> | ||||||
|  |     <td> | ||||||
|  |       <a i18n-title title="more info"><i class="fa fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a> | ||||||
|  |     </td> | ||||||
|     <td colspan="2"> |     <td colspan="2"> | ||||||
|       <div class="title" *ngIf="histogramenabled"> |       <div class="title" *ngIf="histogramenabled"> | ||||||
|         <h4>{{histogram}}</h4> |         <h4>{{histogram}}</h4> | ||||||
| @@ -14,7 +17,7 @@ | |||||||
|     </td> |     </td> | ||||||
|   </tr> |   </tr> | ||||||
|   <tr *ngFor="let entry of layer.renderer.colorMap.entries; let i = index "> |   <tr *ngFor="let entry of layer.renderer.colorMap.entries; let i = index "> | ||||||
|     <td class="legend-items"><span [style.background-color]="getHex(entry.color)" class="color"></span></td> |     <td class="legend-items"><span [style.background-color]="getAlphaHex(entry.color)" [style.border-color]="getHex(entry.color)"class="color"></span></td> | ||||||
|     <td class="legend-items-text"><span *ngIf="!entry.label">{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span><span *ngIf="entry.label">{{entry.label}}</span></td> |     <td class="legend-items-text"><span *ngIf="!entry.label">{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span><span *ngIf="entry.label">{{entry.label}}</span></td> | ||||||
|     <td class="histogram-items"> |     <td class="histogram-items"> | ||||||
|       <div *ngIf="showHistogram()"> |       <div *ngIf="showHistogram()"> | ||||||
| @@ -25,14 +28,15 @@ | |||||||
|     </td> |     </td> | ||||||
|   </tr> |   </tr> | ||||||
|   <tr *ngIf="showHistogram()"> |   <tr *ngIf="showHistogram()"> | ||||||
|     <td colspan="4" class="pb-2 pt-2"> |     <td colspan="4" class="pb-1 pt-1"> | ||||||
|       <a i18n-title title="more info"><i class="fa fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a> |       <div class="info" [ngbCollapse]="hideHistogramDetails"> | ||||||
|       <div class="info pt-2"  [ngbCollapse]="hideHistogramDetails"> |         <ng-container *ngIf="bandContainsStatistics(); else histogram"> | ||||||
|         <div><span i18n>Average: </span>{{layer.renderer.band.histogram.mean| number:'1.0-3'}}</div> |           <fm-map-statistics-details [statistics]="layer.renderer.band.statistics"></fm-map-statistics-details> | ||||||
|         <div><span i18n>Standard deviation: </span>{{layer.renderer.band.histogram.stddev| number:'1.0-3'}}</div> |         </ng-container> | ||||||
|         <div><span i18n>Min: </span>{{layer.renderer.band.histogram.min| number:'1.0-3'}}</div> |  | ||||||
|         <div><span i18n>Max: </span>{{layer.renderer.band.histogram.max| number:'1.0-3'}}</div> |         <ng-template #histogram> | ||||||
|         <div *ngIf="showConfidenceInterval()"><span i18n>Confidence interval:</span> {{layer.renderer.band.histogram.confidence * 100| number:'1.0-0'}}%</div>   |           <fm-map-histogram-details [histogram]="layer.renderer.band.histogram"></fm-map-histogram-details> | ||||||
|  |         </ng-template> | ||||||
|       </div> |       </div> | ||||||
|     </td> |     </td> | ||||||
|   </tr> |   </tr> | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ span.color { | |||||||
|   display:inline-block; |   display:inline-block; | ||||||
|   width: 1.5em; |   width: 1.5em; | ||||||
|   height: 1.5em; |   height: 1.5em; | ||||||
|  |   border: 2px solid; | ||||||
| } | } | ||||||
|  |  | ||||||
| .legend-items { | .legend-items { | ||||||
|   | |||||||
| @@ -53,6 +53,10 @@ export class LegendComponent implements OnInit,AfterViewInit { | |||||||
|     return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue); |     return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public getAlphaHex(color: IColor): string { | ||||||
|  |     return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue)+ this.componentToHex(color.alpha); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   private componentToHex(c: number): string { |   private componentToHex(c: number): string { | ||||||
|     const hex = c.toString(16); |     const hex = c.toString(16); | ||||||
|     return hex.length === 1 ? `0${hex}` : hex; |     return hex.length === 1 ? `0${hex}` : hex; | ||||||
| @@ -88,8 +92,8 @@ export class LegendComponent implements OnInit,AfterViewInit { | |||||||
|     return this.histogramenabled && this.layer.renderer.band.histogram.entries && this.layer.renderer.band.histogram.entries.length > 0 && this.layer.renderer.colorMap.colormapType == "minmax"; |     return this.histogramenabled && this.layer.renderer.band.histogram.entries && this.layer.renderer.band.histogram.entries.length > 0 && this.layer.renderer.colorMap.colormapType == "minmax"; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   showConfidenceInterval():boolean { |   bandContainsStatistics(): boolean { | ||||||
|     return this.histogramenabled && this.layer.renderer.band.histogram.confidence != null && this.layer.renderer.band.histogram.confidence != 1; |     return this.layer.renderer.band.statistics != null; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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, |   panelCollapsed:panelCollapsed$|async, | ||||||
|   searchMinified:searchMinified$|async, |   searchMinified:searchMinified$|async, | ||||||
|   selectedItem:selectedItem$|async, |   selectedItem:selectedItem$|async, | ||||||
|  |   parentItem:parentItem$|async, | ||||||
|   queryState:queryState$|async, |   queryState:queryState$|async, | ||||||
|   searchCollapsed:searchCollapsed$|async, |   searchCollapsed:searchCollapsed$|async, | ||||||
|   clearEnabled:clearEnabled$|async, |   clearEnabled:clearEnabled$|async, | ||||||
| @@ -34,11 +35,11 @@ | |||||||
|     <fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers> |     <fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers> | ||||||
|     <fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers> |     <fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers> | ||||||
|     <aol-layer-vector> |     <aol-layer-vector> | ||||||
|       <fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeaturesSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector> |       <fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector> | ||||||
|     </aol-layer-vector> |     </aol-layer-vector> | ||||||
|     <fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> |     <!-- <fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> --> | ||||||
|     <div class="control-container"> |     <div class="control-container"> | ||||||
|   <!--    <switch2d3d></switch2d3d>--> |       <router-outlet name="map-controls"></router-outlet> | ||||||
|       <fm-map-layer-switcher></fm-map-layer-switcher> |       <fm-map-layer-switcher></fm-map-layer-switcher> | ||||||
|       <fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location> |       <fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location> | ||||||
|       <fm-map-rotation-reset></fm-map-rotation-reset> |       <fm-map-rotation-reset></fm-map-rotation-reset> | ||||||
| @@ -57,7 +58,7 @@ | |||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <div *ngIf="state.selectedItem;let item"> |         <div *ngIf="state.selectedItem;let item"> | ||||||
|           <fm-map-selected-item-container [item]="item" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container> |           <fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container> | ||||||
|         </div> |         </div> | ||||||
|         <div *ngIf="state.features.length == 0" class="no-results  m-2"> |         <div *ngIf="state.features.length == 0" class="no-results  m-2"> | ||||||
|           <div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div> |           <div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div> | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| @import "~bootstrap/scss/bootstrap.scss"; | @import "~bootstrap/scss/bootstrap.scss"; | ||||||
|  |  | ||||||
| aol-map { position:absolute;width:100%;height:100%;} | aol-map { position:absolute;width:100%;height:calc(100vh + 4rem);} | ||||||
|  |  | ||||||
| .arrow { | .arrow { | ||||||
|   top: 3rem; |   top: 3rem; | ||||||
| @@ -49,7 +49,7 @@ aol-map { position:absolute;width:100%;height:100%;} | |||||||
| .control-container { | .control-container { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   right: 1em; |   right: 1em; | ||||||
|   bottom: 1em; |   bottom: 5em; | ||||||
| } | } | ||||||
|  |  | ||||||
| switch2d3d { | switch2d3d { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone } from '@angular/core'; | import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone } from '@angular/core'; | ||||||
| import { Location } from '@angular/common'; | 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 { withLatestFrom, switchMap,skip  } from 'rxjs/operators'; | ||||||
| import { Router, ActivatedRoute, ParamMap } from '@angular/router'; | import { Router, ActivatedRoute, ParamMap } from '@angular/router'; | ||||||
| import { Store } from '@ngrx/store'; | import { Store } from '@ngrx/store'; | ||||||
| @@ -12,7 +12,7 @@ import * as mapActions from '../../actions/map.actions'; | |||||||
| import { IMapState} from '../../models/map.state'; | import { IMapState} from '../../models/map.state'; | ||||||
| import { ISelectedFeatures } from '../../models/selected.features'; | import { ISelectedFeatures } from '../../models/selected.features'; | ||||||
| import { IItemLayer } from '../../models/item.layer'; | import { IItemLayer } from '../../models/item.layer'; | ||||||
| import { IQueryState } from '@farmmaps/common'; | import { IListItem, IQueryState } from '@farmmaps/common'; | ||||||
| import { IPeriodState } from '../../models/period.state'; | import { IPeriodState } from '../../models/period.state'; | ||||||
| import {IStyles} from '../../models/style.cache'; | import {IStyles} from '../../models/style.cache'; | ||||||
| import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component'; | 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 selectedFeature$: Observable<Feature> = this.store.select(mapReducers.selectGetSelectedFeature); | ||||||
|   public clickedFeature: Subject<Feature> = new Subject<Feature>(); |   public clickedFeature: Subject<Feature> = new Subject<Feature>(); | ||||||
|   public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem); |   public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem); | ||||||
|  |   public parentItem$: Observable<IItem> =this.store.select(mapReducers.selectGetParentItem); | ||||||
|   public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState); |   public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState); | ||||||
|   public state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState); |   public state$:Observable<{mapState:IMapState,queryState:IQueryState}> = this.store.select(mapReducers.selectGetState); | ||||||
|   public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod); |   public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod); | ||||||
|   public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled); |   public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled); | ||||||
|   public searchCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetSearchCollapsed); |   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 overlayLayersCollapsed: boolean = true; | ||||||
|   public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent); |   public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent); | ||||||
|   public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles); |   public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles); | ||||||
|   private setStateCount$:Observable<number> =  this.store.select(mapReducers.selectgetSetStateCount); |  | ||||||
|   private lastUrl = ""; |   private lastUrl = ""; | ||||||
|  |   private initialized: boolean = false; | ||||||
|  |  | ||||||
|   @ViewChild('map') map; |   @ViewChild('map') map; | ||||||
|  |  | ||||||
| @@ -93,10 +94,10 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|     private geolocationService: GeolocationService, |     private geolocationService: GeolocationService, | ||||||
|     private zone: NgZone, |     private zone: NgZone, | ||||||
|     private deviceorientationService:DeviceOrientationService) { |     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) { |         if(queryState) { | ||||||
|           let newQueryState = tassign(mapReducers.initialQueryState); |           let newQueryState = tassign(mapReducers.initialQueryState); | ||||||
|           console.debug(`Do Query ${setStateCount}`); |           console.debug(`Do Query`); | ||||||
|           let urlparts=[]; |           let urlparts=[]; | ||||||
|           if (queryState.itemCode && queryState.itemCode != "") { |           if (queryState.itemCode && queryState.itemCode != "") { | ||||||
|             if(queryState.itemType && queryState.itemType!= "") { |             if(queryState.itemType && queryState.itemType!= "") { | ||||||
| @@ -114,13 +115,19 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|           if(urlparts.length==0 ) { |           if(urlparts.length==0 ) { | ||||||
|             newQueryState.itemCode = queryState.itemCode; |             newQueryState.itemCode = queryState.itemCode; | ||||||
|             this.zone.run(() => { |             this.zone.run(() => { | ||||||
|               this.store.dispatch(new mapActions.SetQueryState(newQueryState,false)); |               this.replaceUrl(mapState,newQueryState,false);              | ||||||
|             }) |             }) | ||||||
|           } else { |           } else { | ||||||
|             this.router.navigate(urlparts); |             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']) |   @HostListener('document:keyup', ['$event']) | ||||||
| @@ -156,6 +163,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|  |     this.initialized = false; | ||||||
|     console.debug("Init"); |     console.debug("Init"); | ||||||
|     this.store.dispatch(new mapActions.Clear()); |     this.store.dispatch(new mapActions.Clear()); | ||||||
|     this.selectedFeatures$.next({x:0,y:0,features:[]}); |     this.selectedFeatures$.next({x:0,y:0,features:[]}); | ||||||
| @@ -233,30 +241,25 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|  |  | ||||||
|     // url to state |     // 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]) => { |       this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => { | ||||||
|       var newMapState: IMapState = state.mapState; |          if(this.initialized) { | ||||||
|       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); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|           let urlQueryState = this.getQueryStateFromUrl(params); |           let urlQueryState = this.getQueryStateFromUrl(params); | ||||||
|       if(urlQueryState) { |           if( this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState)) { | ||||||
|         newQueryState = urlQueryState; |             return of(new mapActions.SetState(state.mapState,urlQueryState)); | ||||||
|         queryStateChanged = this.serializeService.serialize(state.queryState) !=  this.serializeService.serialize(urlQueryState); |  | ||||||
|           }           |           }           | ||||||
|    |          }  | ||||||
|       if(queryStateChanged && mapStateChanged && state.setStateCount ==0) { |          return EMPTY;         | ||||||
|         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)); |  | ||||||
|       })).subscribe((action) => { |       })).subscribe((action) => { | ||||||
|         if(action) { |         if(action) { | ||||||
|           this.zone.run(() => { |           this.zone.run(() => { | ||||||
| @@ -270,7 +273,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|  |  | ||||||
|     this.stateSub = this.state$.pipe(switchMap((state) => { |     this.stateSub = this.state$.pipe(switchMap((state) => { | ||||||
|       let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState); |       let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState); | ||||||
|       if(this.lastUrl!=newUrl && state.setStateCount>0) { |       if(this.lastUrl!=newUrl) { | ||||||
|         this.lastUrl=newUrl; |         this.lastUrl=newUrl; | ||||||
|         return of(state); |         return of(state); | ||||||
|       } |       } | ||||||
| @@ -279,14 +282,11 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|       } |       } | ||||||
|       })).subscribe((newUrlState) =>{ |       })).subscribe((newUrlState) =>{ | ||||||
|         if(newUrlState) { |         if(newUrlState) { | ||||||
|           console.debug(`State to url ${newUrlState.setStateCount}`); |           console.debug(`State to url`); | ||||||
|           this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl); |           this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl); | ||||||
|         } |         } | ||||||
|       }); |       }); | ||||||
|  |     this.initialized = true;    | ||||||
|     setTimeout(() => { |  | ||||||
|       this.map.instance.updateSize(); |  | ||||||
|     }, 500); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleSearchCollapse(event) { |   handleSearchCollapse(event) { | ||||||
| @@ -318,45 +318,55 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) { |   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 =["."]; |         let parts =["."]; | ||||||
|         parts.push(mapState.xCenter.toFixed(5)); |         parts.push(mapState.xCenter.toFixed(5)); | ||||||
|         parts.push(mapState.yCenter.toFixed(5)); |         parts.push(mapState.yCenter.toFixed(5)); | ||||||
|         parts.push( mapState.zoom.toFixed(0)); |         parts.push( mapState.zoom.toFixed(0)); | ||||||
|         parts.push( mapState.rotation.toFixed(2));         |         parts.push( mapState.rotation.toFixed(2));         | ||||||
|       if(mapState.baseLayerCode!="") { |  | ||||||
|         parts.push(mapState.baseLayerCode); |         parts.push(mapState.baseLayerCode); | ||||||
|         parts.push( this.serializeService.serialize(queryState)); |         parts.push( this.serializeService.serialize(queryState)); | ||||||
|  |          | ||||||
|         console.debug("Replace url",parts); |         console.debug("Replace url",parts); | ||||||
|         this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent }); |         this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent }); | ||||||
|       } |       } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleOnMoveEnd(event) {     |   handleOnMoveEnd(event) {     | ||||||
|     console.debug("Move end"); |     if(this.initialized) { | ||||||
|       this.zone.run(() =>{ |       this.zone.run(() =>{ | ||||||
|  |         console.debug("Move end"); | ||||||
|         var map = event.map; |         var map = event.map; | ||||||
|         var view = map.getView(); |         var view = map.getView(); | ||||||
|         var rotation = view.getRotation(); |         var rotation = view.getRotation(); | ||||||
|         var zoom = view.getZoom(); |         var zoom = view.getZoom(); | ||||||
|         var center = transform(view.getCenter(), view.getProjection(), "EPSG:4326"); |         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 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]); |         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 |           if (mapState && baselayer) { // do not react on first move | ||||||
|             let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code }); |             let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code }); | ||||||
|             this.store.dispatch(new mapActions.SetMapState(newMapState)); |             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) { |   handleOnMouseDown(event: MouseEvent) { | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|     this.zone.run(() =>{ |     this.zone.run(() =>{ | ||||||
|       this.store.dispatch(new mapActions.CollapseSearch()); |       this.store.dispatch(new commonActions.CloseAll()); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -365,7 +375,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleClearSearch(event) { |   handleClearSearch(event) { | ||||||
|     this.store.dispatch(new commonActions.Escape(true, false)); |     this.store.dispatch(new mapActions.Clear()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleOnDelete(itemLayer: IItemLayer) { |   handleOnDelete(itemLayer: IItemLayer) { | ||||||
|   | |||||||
| @@ -18,3 +18,8 @@ | |||||||
| .custom-day.faded { | .custom-day.faded { | ||||||
|   background-color: rgba(2, 117, 216, 0.5); |   background-color: rgba(2, 117, 216, 0.5); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | ngb-datepicker { | ||||||
|  |   overflow: hidden; | ||||||
|  |   max-width: calc(100vw - 3em); | ||||||
|  | } | ||||||
| @@ -13,9 +13,11 @@ import { IItemLayer } from '../../models/item.layer'; | |||||||
| export class SelectedItemContainerComponent { | export class SelectedItemContainerComponent { | ||||||
|  |  | ||||||
|   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractSelectedItemComponent) public selectedItemComponents: AbstractSelectedItemComponent[] ) { |   constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractSelectedItemComponent) public selectedItemComponents: AbstractSelectedItemComponent[] ) { | ||||||
|  |     this.selectedItemComponents = [...this.selectedItemComponents].reverse(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @Input() item: IItem; |   @Input() item: IItem; | ||||||
|  |   @Input() parentItem: IItem; | ||||||
|   @Input() itemLayer:IItemLayer; |   @Input() itemLayer:IItemLayer; | ||||||
|   @Input() overlayLayers:Array<IItemLayer>; |   @Input() overlayLayers:Array<IItemLayer>; | ||||||
|  |  | ||||||
| @@ -23,27 +25,41 @@ export class SelectedItemContainerComponent { | |||||||
|  |  | ||||||
|   loadComponent() { |   loadComponent() { | ||||||
|     let componentFactory: ComponentFactory<AbstractSelectedItemComponent> = this.componentFactoryResolver.resolveComponentFactory(SelectedItemComponent); // default |     let componentFactory: ComponentFactory<AbstractSelectedItemComponent> = this.componentFactoryResolver.resolveComponentFactory(SelectedItemComponent); // default | ||||||
|     let firstComponentWithTypeAndTask = this.selectedItemComponents |  | ||||||
|       .find(value => value['forSourceTask'] == this.item.sourceTask && |  | ||||||
|         value['forItemType'] == this.item.itemType |  | ||||||
|       ); |  | ||||||
|  |  | ||||||
|     if (firstComponentWithTypeAndTask) { |     let selected = -1; | ||||||
|       componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithTypeAndTask['constructor'] as any); |     let maxMatches =0; | ||||||
|     } else { |     let showItem = true; | ||||||
|       let firstComponentWithType = this.selectedItemComponents |     for (let i = 0; i < this.selectedItemComponents.length; i++) { | ||||||
|         .find(value => value['forSourceTask'] == null && |       let matches=0; | ||||||
|           value['forItemType'] == this.item.itemType); |       let criteria=0; | ||||||
|  |       if (this.selectedItemComponents[i]['forItemType'] ) { | ||||||
|       if (firstComponentWithType) { |         criteria++; | ||||||
|         componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithType['constructor'] as any); |         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; |     const viewContainerRef = this.widgetHost.viewContainerRef; | ||||||
|     viewContainerRef.clear(); |     viewContainerRef.clear(); | ||||||
|  |  | ||||||
|     const componentRef = viewContainerRef.createComponent(componentFactory); |     const componentRef = viewContainerRef.createComponent(componentFactory); | ||||||
|     (<AbstractSelectedItemComponent>componentRef.instance).item = this.item; |     (<AbstractSelectedItemComponent>componentRef.instance).item = this.item; | ||||||
|  |     (<AbstractSelectedItemComponent>componentRef.instance).parentItem = this.parentItem; | ||||||
|     (<AbstractSelectedItemComponent>componentRef.instance).itemLayer = this.itemLayer; |     (<AbstractSelectedItemComponent>componentRef.instance).itemLayer = this.itemLayer; | ||||||
|     (<AbstractSelectedItemComponent>componentRef.instance).overlayLayers = this.overlayLayers; |     (<AbstractSelectedItemComponent>componentRef.instance).overlayLayers = this.overlayLayers; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -7,9 +7,10 @@ import { commonReducers, ItemTypeService, IItem, Item, ItemService, FolderServic | |||||||
| import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router'; | import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router'; | ||||||
| import { ForItemType } from '../for-item/for-itemtype.decorator'; | import { ForItemType } from '../for-item/for-itemtype.decorator'; | ||||||
| import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; | import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; | ||||||
| import { Observable } from 'rxjs'; | import { Observable,of } from 'rxjs'; | ||||||
| import {GeoJSON} from 'ol/format'; | import {GeoJSON} from 'ol/format'; | ||||||
| import {getArea} from 'ol/sphere'; | import {getArea} from 'ol/sphere'; | ||||||
|  | import { withLatestFrom,switchMap,combineLatest } from 'rxjs/operators'; | ||||||
|  |  | ||||||
|  |  | ||||||
| @ForItemType("vnd.farmmaps.itemtype.cropfield") | @ForItemType("vnd.farmmaps.itemtype.cropfield") | ||||||
| @@ -38,6 +39,25 @@ export class SelectedItemCropfieldComponent extends AbstractSelectedItemComponen | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   ngOnInit() { |   ngOnInit() { | ||||||
|     this. items = this.folderService$.getItems(this.item.code, 0, 1000); |     var childItems = this.folderService$.getItems(this.item.code, 0, 1000); | ||||||
|  |     var atLocationItems = this.itemService$.getItemList(null,null,null,this.item.code,true); | ||||||
|  |     this.items =  childItems.pipe( | ||||||
|  |       combineLatest(atLocationItems), | ||||||
|  |       switchMap(([ci,ali]) => { | ||||||
|  |           let retVal:IListItem[] = []; | ||||||
|  |           let codes = {}; | ||||||
|  |           ci.forEach((listItem) => { | ||||||
|  |              retVal.push(listItem); | ||||||
|  |              codes[listItem.code]=listItem; | ||||||
|  |           }); | ||||||
|  |           ali.forEach((atlocationitem) => { | ||||||
|  |              let listItem = atlocationitem as IListItem; | ||||||
|  |              let allowedItemTypes = "vnd.farmmaps.itemtype.device.senml"; // this is a hack | ||||||
|  |              if(!codes[listItem.code] && allowedItemTypes.indexOf(listItem.itemType) >= 0) { | ||||||
|  |                retVal.push(listItem); | ||||||
|  |              } | ||||||
|  |           }); | ||||||
|  |           return of(retVal); | ||||||
|  |       })); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
|     <div class="card-body"> |     <div class="card-body"> | ||||||
|       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> |       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> | ||||||
|       <div class="card menu-card"> |       <div class="card menu-card"> | ||||||
|  |         <h2 *ngIf="parentItem">{{parentItem.name}}</h2> | ||||||
|         <h1>{{item.name}}</h1> |         <h1>{{item.name}}</h1> | ||||||
|       </div> |       </div> | ||||||
|       <div class="legend-container" *ngIf="item?.data.layers;let layers"> |       <div class="legend-container" *ngIf="item?.data.layers;let layers"> | ||||||
| @@ -13,7 +14,8 @@ | |||||||
|               <option *ngFor="let l of layers;" [value]="l.index"  [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option> |               <option *ngFor="let l of layers;" [value]="l.index"  [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option> | ||||||
|             </select> |             </select> | ||||||
|           </div> |           </div> | ||||||
|           <fm-map-layer-legend [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> |           <fm-map-layer-legend [showTitle]="layers.length == 1" | ||||||
|  |             [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div class="card menu-card"> |       <div class="card menu-card"> | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
|     <div class="card-body"> |     <div class="card-body"> | ||||||
|       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> |       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> | ||||||
|       <div class="card menu-card"> |       <div class="card menu-card"> | ||||||
|  |         <h2 *ngIf="parentItem">{{parentItem.name}}</h2> | ||||||
|         <h1>{{item.name}}</h1> |         <h1>{{item.name}}</h1> | ||||||
|       </div> |       </div> | ||||||
|       <div class="legend-container" *ngIf="item?.data.layers;let layers"> |       <div class="legend-container" *ngIf="item?.data.layers;let layers"> | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
|     <div class="card-body"> |     <div class="card-body"> | ||||||
|       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> |       <div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div> | ||||||
|       <div class="card menu-card"> |       <div class="card menu-card"> | ||||||
|  |         <h2 *ngIf="parentItem">{{parentItem.name}}</h2> | ||||||
|         <h1>{{item.name}}</h1> |         <h1>{{item.name}}</h1> | ||||||
|       </div> |       </div> | ||||||
|       <div class="legend-container" *ngIf="item?.data.layers;let layers"> |       <div class="legend-container" *ngIf="item?.data.layers;let layers"> | ||||||
| @@ -28,13 +29,17 @@ | |||||||
|               <option *ngFor="let l of layers;" [value]="l.index"  [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option> |               <option *ngFor="let l of layers;" [value]="l.index"  [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option> | ||||||
|             </select> |             </select> | ||||||
|           </div> |           </div> | ||||||
|           <fm-map-layer-legend [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> |           <fm-map-layer-legend [showTitle]="layers.length == 1" | ||||||
|  |             [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div class="card menu-card"> |       <div class="card menu-card"> | ||||||
|         <ul class="p-0 mt-2"> |         <ul class="p-0 mt-2"> | ||||||
|           <li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i> <span i18n>Edit</span></a></li> |           <li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i> <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> |           <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> |         </ul> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent | |||||||
|   nextDate():Date { |   nextDate():Date { | ||||||
|     let temporalItemLayer = this.itemLayer as  ITemporalItemLayer; |     let temporalItemLayer = this.itemLayer as  ITemporalItemLayer; | ||||||
|     if(temporalItemLayer.nextItemLayer.item)  |     if(temporalItemLayer.nextItemLayer.item)  | ||||||
|       return temporalItemLayer.nextItemLayer.item.dataDate; |       return new Date(Date.parse(temporalItemLayer.nextItemLayer.item.dataDate)) ; | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -47,7 +47,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent | |||||||
|   previousDate():Date { |   previousDate():Date { | ||||||
|     let temporalItemLayer = this.itemLayer as  ITemporalItemLayer; |     let temporalItemLayer = this.itemLayer as  ITemporalItemLayer; | ||||||
|     if(temporalItemLayer.previousItemLayer.item)  |     if(temporalItemLayer.previousItemLayer.item)  | ||||||
|       return temporalItemLayer.previousItemLayer.item.dataDate; |       return new Date(Date.parse(temporalItemLayer.previousItemLayer.item.dataDate)); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -74,7 +74,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleGoToChart(item: IItem) {   |   handleGoToChart(item: IItem) {   | ||||||
|     this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, item.dataDate.getUTCFullYear()]); |     this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, new Date(Date.parse(item.dataDate)).getUTCFullYear()]); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| <div *ngIf="item;let item"> | <div *ngIf="item;let item"> | ||||||
|   <div class="card border-0"> |   <div class="card border-0"> | ||||||
|     <img *ngIf="item.thumbnail" class="card-img-top" [src]="config.getConfig('apiEndPoint') +'/api/v1/items/'+item.code+'/thumbnail?v='+item.updated.getTime()" /> |     <img *ngIf="item.thumbnail" class="card-img-top" [src]="getThumbnailUrl(item)" /> | ||||||
|     <div *ngIf="!item.thumbnail" class="big-icon" [style.background-color]="itemTypeService.getColor(item.itemType)"> |     <div *ngIf="!item.thumbnail" class="big-icon" [style.background-color]="itemTypeService.getColor(item.itemType)"> | ||||||
|       <i [ngClass]="itemTypeService.getIcon(item.itemType)"></i> |       <i [ngClass]="itemTypeService.getIcon(item.itemType)"></i> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -11,7 +11,8 @@ import { IItemLayer } from '../../models/item.layer'; | |||||||
| @Injectable() | @Injectable() | ||||||
| @Directive() | @Directive() | ||||||
| export abstract class AbstractSelectedItemComponent { | export abstract class AbstractSelectedItemComponent { | ||||||
|   @Input() item: IItem |   @Input() item: IItem; | ||||||
|  |   @Input() parentItem: IItem; | ||||||
|   @Input() itemLayer: IItemLayer; |   @Input() itemLayer: IItemLayer; | ||||||
|   @Input() overlayLayers: Array<IItemLayer>; |   @Input() overlayLayers: Array<IItemLayer>; | ||||||
|   constructor(public store: Store<mapReducers.State | commonReducers.State>, public itemTypeService: ItemTypeService, private location: Location, public router: Router) { |   constructor(public store: Store<mapReducers.State | commonReducers.State>, public itemTypeService: ItemTypeService, private location: Location, public router: Router) { | ||||||
| @@ -60,6 +61,7 @@ export abstract class AbstractSelectedItemComponent { | |||||||
|     event.preventDefault(); |     event.preventDefault(); | ||||||
|     this.location.back(); |     this.location.back(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| @Injectable() | @Injectable() | ||||||
| @@ -73,4 +75,8 @@ export class SelectedItemComponent extends AbstractSelectedItemComponent { | |||||||
|   constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, public config:AppConfig) { |   constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, public config:AppConfig) { | ||||||
|     super(store, itemTypeService,location,router); |     super(store, itemTypeService,location,router); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   getThumbnailUrl(item:IItem):string { | ||||||
|  |     return this.config.getConfig('apiEndPoint') +'/api/v1/items/'+item.code+'/thumbnail?v=' + Date.parse(item.updated); | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,8 +3,8 @@ import { Injectable } from '@angular/core'; | |||||||
| import { Store, Action } from '@ngrx/store'; | import { Store, Action } from '@ngrx/store'; | ||||||
| import { Effect, Actions,ofType } from '@ngrx/effects'; | import { Effect, Actions,ofType } from '@ngrx/effects'; | ||||||
|  |  | ||||||
| import { Observable , of, interval } from 'rxjs'; | import { EMPTY, Observable , of } from 'rxjs'; | ||||||
| import { withLatestFrom, switchMap, map, catchError, mergeMap,delayWhen } from 'rxjs/operators'; | import { withLatestFrom, switchMap, map, catchError, mergeMap } from 'rxjs/operators'; | ||||||
|  |  | ||||||
| import {GeoJSON,WKT} from 'ol/format'; | import {GeoJSON,WKT} from 'ol/format'; | ||||||
| import {Feature} from 'ol'; | import {Feature} from 'ol'; | ||||||
| @@ -103,17 +103,10 @@ export class MapEffects { | |||||||
|         catchError(error => of(new commonActions.Fail(error)))); |         catchError(error => of(new commonActions.Fail(error)))); | ||||||
|     })); |     })); | ||||||
|  |  | ||||||
|   @Effect() |  | ||||||
|   initRootItems$: Observable<Action> = this.actions$.pipe( |  | ||||||
|     ofType(commonActions.INITROOTSUCCESS), |  | ||||||
|     map((action) => new mapActions.Init() |  | ||||||
|     )); |  | ||||||
|  |  | ||||||
|   @Effect() |   @Effect() | ||||||
|   startSearch$: Observable<Action> = this.actions$.pipe( |   startSearch$: Observable<Action> = this.actions$.pipe( | ||||||
|     ofType(mapActions.STARTSEARCH), |     ofType(mapActions.STARTSEARCH), | ||||||
|     withLatestFrom(this.store$.select(mapReducers.selectgetSetStateCount)), |     switchMap((action) => { | ||||||
|     switchMap(([action,setStateCount]) => { |  | ||||||
|       let a = action as mapActions.StartSearch; |       let a = action as mapActions.StartSearch; | ||||||
|       var startDate = a.queryState.startDate; |       var startDate = a.queryState.startDate; | ||||||
|       var endDate = a.queryState.endDate; |       var endDate = a.queryState.endDate; | ||||||
| @@ -126,7 +119,7 @@ export class MapEffects { | |||||||
|                 f.id = f.properties["code"]; |                 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)))); |           catchError(error => of(new commonActions.Fail(error)))); | ||||||
| @@ -140,7 +133,6 @@ export class MapEffects { | |||||||
|   @Effect() |   @Effect() | ||||||
|   zoomToExtent$: Observable<Action> = this.actions$.pipe( |   zoomToExtent$: Observable<Action> = this.actions$.pipe( | ||||||
|     ofType(mapActions.STARTSEARCHSUCCESS), |     ofType(mapActions.STARTSEARCHSUCCESS), | ||||||
|     delayWhen(action => (action as mapActions.StartSearchSuccess).setStateCount == 1 ? interval(500):interval(0)), |  | ||||||
|     mergeMap((action: mapActions.StartSearchSuccess) => { |     mergeMap((action: mapActions.StartSearchSuccess) => { | ||||||
|       let actions =[]; |       let actions =[]; | ||||||
|       actions.push(new commonActions.SetMenuVisible(false)); |       actions.push(new commonActions.SetMenuVisible(false)); | ||||||
| @@ -150,9 +142,11 @@ export class MapEffects { | |||||||
|           for (let f of action.features) { |           for (let f of action.features) { | ||||||
|             extend(extent, (f as Feature).getGeometry().getExtent()); |             extend(extent, (f as Feature).getGeometry().getExtent()); | ||||||
|           } |           } | ||||||
|         }   |           if(action.features && action.features.length >0) { | ||||||
|             actions.push(new mapActions.SetExtent(extent)); |             actions.push(new mapActions.SetExtent(extent)); | ||||||
|           }   |           }   | ||||||
|  |         } | ||||||
|  |       } | ||||||
|       return actions; |       return actions; | ||||||
|     })); |     })); | ||||||
|  |  | ||||||
| @@ -167,13 +161,19 @@ export class MapEffects { | |||||||
|   selectItem$: Observable<Action> = this.actions$.pipe( |   selectItem$: Observable<Action> = this.actions$.pipe( | ||||||
|     ofType(mapActions.SELECTITEM), |     ofType(mapActions.SELECTITEM), | ||||||
|     withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)), |     withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)), | ||||||
|     withLatestFrom(this.store$.select(mapReducers.getSetStateCount)), |       switchMap(([action, selectedItem]) => { | ||||||
|       switchMap(([[action, selectedItem],setStateCount]) => { |  | ||||||
|         let a = action as mapActions.SelectItem; |         let a = action as mapActions.SelectItem; | ||||||
|         let itemCode = selectedItem ? selectedItem.code : ""; |         let itemCode = selectedItem ? selectedItem.code : ""; | ||||||
|         if (a.itemCode != itemCode || setStateCount == 1) { |         if (a.itemCode != itemCode) { | ||||||
|           return this.itemService$.getItem(a.itemCode).pipe( |           return this.itemService$.getItem(a.itemCode).pipe( | ||||||
|             map((item: IItem) => new mapActions.SelectItemSuccess(item)), |             switchMap(child => { | ||||||
|  |               return this.itemService$.getItem(child.parentCode) | ||||||
|  |                 .pipe(map(parent => { | ||||||
|  |                     return {child, parent}; | ||||||
|  |                   }),catchError(() => { let parent:IItem = null;return of({child,parent})}) | ||||||
|  |                 ); | ||||||
|  |             }), | ||||||
|  |             map(data => new mapActions.SelectItemSuccess(data.child, data.parent)), | ||||||
|             catchError(error => of(new commonActions.Fail(error)))) |             catchError(error => of(new commonActions.Fail(error)))) | ||||||
|         } else { |         } else { | ||||||
|           return []; |           return []; | ||||||
| @@ -203,7 +203,7 @@ export class MapEffects { | |||||||
|           return this.itemService$.getChildItemList(action.item.code,null).pipe( |           return this.itemService$.getChildItemList(action.item.code,null).pipe( | ||||||
|             map(items => new mapActions.SelectTemporalItemsSuccess( |             map(items => new mapActions.SelectTemporalItemsSuccess( | ||||||
|               items.sort((a, b) => |               items.sort((a, b) => | ||||||
|                  -(b.dataDate.getTime() - a.dataDate.getTime()) |                  -(Date.parse(b.dataDate) - Date.parse(a.dataDate)) | ||||||
|               ) |               ) | ||||||
|             )), |             )), | ||||||
|             catchError(error => of(new commonActions.Fail(error)))); |             catchError(error => of(new commonActions.Fail(error)))); | ||||||
| @@ -247,7 +247,14 @@ export class MapEffects { | |||||||
|       let itemChangedAction = action as commonActions.ItemChangedEvent; |       let itemChangedAction = action as commonActions.ItemChangedEvent; | ||||||
|       if (selectedItem && selectedItem.code == itemChangedAction.itemCode) { |       if (selectedItem && selectedItem.code == itemChangedAction.itemCode) { | ||||||
|         return this.itemService$.getItem(itemChangedAction.itemCode).pipe( |         return this.itemService$.getItem(itemChangedAction.itemCode).pipe( | ||||||
|           map((item: IItem) => new mapActions.SelectItemSuccess(item)), |           switchMap(child => { | ||||||
|  |             return this.itemService$.getItem(child.parentCode) | ||||||
|  |               .pipe(map(parent => { | ||||||
|  |                   return {child, parent}; | ||||||
|  |                 }) | ||||||
|  |               ); | ||||||
|  |           }), | ||||||
|  |           map(data => new mapActions.SelectItemSuccess(data.child, data.parent)), | ||||||
|           catchError(error => of(new commonActions.Fail(error)))); |           catchError(error => of(new commonActions.Fail(error)))); | ||||||
|       } else { |       } else { | ||||||
|         return []; |         return []; | ||||||
| @@ -262,20 +269,12 @@ export class MapEffects { | |||||||
|       } else { |       } else { | ||||||
|         newAction= new mapActions.StartSearch(queryState); |         newAction= new mapActions.StartSearch(queryState); | ||||||
|       } |       } | ||||||
|       return of(newAction);   |  | ||||||
|     } else { |  | ||||||
|       return of(new commonActions.Escape(true,false)); |  | ||||||
|     } |  | ||||||
|   }  |  | ||||||
|        |        | ||||||
|   @Effect() |     } else { | ||||||
|   setQueryState$: Observable<Action> = this.actions$.pipe( |       newAction = new mapActions.Clear(); | ||||||
|     ofType(mapActions.SETQUERYSTATE), |     } | ||||||
|     withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)), |     return of(newAction); | ||||||
|     switchMap(([action,inSearch]) => { |   } | ||||||
|       let a = action as mapActions.SetQueryState; |  | ||||||
|        return this.getActionFromQueryState(a.queryState,inSearch); |  | ||||||
|       })); |  | ||||||
|  |  | ||||||
|   @Effect() |   @Effect() | ||||||
|   setState$: Observable<Action> = this.actions$.pipe( |   setState$: Observable<Action> = this.actions$.pipe( | ||||||
| @@ -286,6 +285,18 @@ export class MapEffects { | |||||||
|       return this.getActionFromQueryState(a.queryState,inSearch); |       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) { |     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._geojsonFormat = new GeoJSON(); | ||||||
|     this._wktFormat = new WKT(); |     this._wktFormat = new WKT(); | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | import { IDataLayer } from '@farmmaps/common'; | ||||||
|  |  | ||||||
| export interface IColor { | export interface IColor { | ||||||
|   red: number, |   red: number, | ||||||
|   green: number, |   green: number, | ||||||
| @@ -26,6 +28,25 @@ export interface IHistogram { | |||||||
|   confidence?: number; |   confidence?: number; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export interface IStatistics { | ||||||
|  |   min: number; | ||||||
|  |   max: number; | ||||||
|  |   mean: number; | ||||||
|  |   stddev: number; | ||||||
|  |   populationCount: number; | ||||||
|  |   minPlus: number; | ||||||
|  |   maxMinus: number; | ||||||
|  |   mode: number; | ||||||
|  |   median: number; | ||||||
|  |   curtosis: number; | ||||||
|  |   skewness: number; | ||||||
|  |   variance: number; | ||||||
|  |   variationCoefficient: number; | ||||||
|  |   confidenceIntervalErrorMargin: number; | ||||||
|  |   confidenceIntervalLow: number; | ||||||
|  |   confidenceIntervalHigh: number; | ||||||
|  | } | ||||||
|  |  | ||||||
| export interface IGradientstop { | export interface IGradientstop { | ||||||
|   relativestop: number, |   relativestop: number, | ||||||
|   color: IColor |   color: IColor | ||||||
| @@ -39,7 +60,8 @@ export interface IColorMap { | |||||||
| } | } | ||||||
|  |  | ||||||
| export interface IBand { | export interface IBand { | ||||||
|   histogram: IHistogram |   histogram: IHistogram; | ||||||
|  |   statistics: IStatistics; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -64,11 +86,9 @@ export interface IRenderoutputImage { | |||||||
|   extent: [number,number,number,number] |   extent: [number,number,number,number] | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface ILayer { | export interface ILayer extends IDataLayer { | ||||||
|   name: string, |  | ||||||
|   unit: string, |  | ||||||
|   index: number, |  | ||||||
|   scale: number, |  | ||||||
|   renderer: IRenderer, |   renderer: IRenderer, | ||||||
|   rendering: IRenderoutput |   rendering: IRenderoutput, | ||||||
|  |   minzoom: number, | ||||||
|  |   maxzoom: number | ||||||
| } | } | ||||||
|   | |||||||
| @@ -44,6 +44,7 @@ export interface State { | |||||||
|   panelCollapsed: boolean, |   panelCollapsed: boolean, | ||||||
|   selectedFeature: Feature, |   selectedFeature: Feature, | ||||||
|   selectedItem:IItem, |   selectedItem:IItem, | ||||||
|  |   parentItem:IItem, | ||||||
|   clearEnabled: boolean, |   clearEnabled: boolean, | ||||||
|   searchCollapsed: boolean, |   searchCollapsed: boolean, | ||||||
|   searchMinified: boolean, |   searchMinified: boolean, | ||||||
| @@ -56,8 +57,8 @@ export interface State { | |||||||
|   selectedOverlayLayer: IItemLayer, |   selectedOverlayLayer: IItemLayer, | ||||||
|   styles:IStyles, |   styles:IStyles, | ||||||
|   showLayerSwitcher:boolean, |   showLayerSwitcher:boolean, | ||||||
|   setStateCount:number, |  | ||||||
|   inSearch:boolean, |   inSearch:boolean, | ||||||
|  |   inZoom:boolean, | ||||||
|   replaceUrl:boolean |   replaceUrl:boolean | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -82,6 +83,7 @@ export const initialState: State = { | |||||||
|   panelCollapsed: false, |   panelCollapsed: false, | ||||||
|   selectedFeature: null, |   selectedFeature: null, | ||||||
|   selectedItem: null, |   selectedItem: null, | ||||||
|  |   parentItem: null, | ||||||
|   clearEnabled: false, |   clearEnabled: false, | ||||||
|   searchCollapsed: true, |   searchCollapsed: true, | ||||||
|   searchMinified:false, |   searchMinified:false, | ||||||
| @@ -94,8 +96,8 @@ export const initialState: State = { | |||||||
|   selectedItemLayer: null, |   selectedItemLayer: null, | ||||||
|   styles: {}, |   styles: {}, | ||||||
|   showLayerSwitcher: false, |   showLayerSwitcher: false, | ||||||
|   setStateCount: 0, |  | ||||||
|   inSearch:false, |   inSearch:false, | ||||||
|  |   inZoom:false, | ||||||
|   replaceUrl:true |   replaceUrl:true | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -108,16 +110,16 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | |||||||
|     case mapActions.SETMAPSTATE: { |     case mapActions.SETMAPSTATE: { | ||||||
|       let a = action as mapActions.SetMapState; |       let a = action as mapActions.SetMapState; | ||||||
|       return tassign(state, { |       return tassign(state, { | ||||||
|         mapState: a.mapState,setStateCount: state.setStateCount+1 |         mapState: a.mapState | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|     case mapActions.SETQUERYSTATE: { |     case mapActions.SETQUERYSTATE: { | ||||||
|       let a = action as 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: { |     case mapActions.SETSTATE: { | ||||||
|       let a = action as 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: { |     case mapActions.SETVIEWEXTENT: { | ||||||
|       let a = action as mapActions.SetViewExtent; |       let a = action as mapActions.SetViewExtent; | ||||||
| @@ -145,7 +147,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | |||||||
|     case mapActions.SELECTITEM: { |     case mapActions.SELECTITEM: { | ||||||
|       let a = action as mapActions.SelectItem; |       let a = action as mapActions.SelectItem; | ||||||
|       let itemCode = state.selectedItem ? state.selectedItem.code : ""; |       let itemCode = state.selectedItem ? state.selectedItem.code : ""; | ||||||
|       let inSearch = (a.itemCode != itemCode || state.setStateCount == 1) |       let inSearch = a.itemCode != itemCode; | ||||||
|       return tassign(state, { |       return tassign(state, { | ||||||
|         selectedItem: null, |         selectedItem: null, | ||||||
|         selectedItemLayer: null, |         selectedItemLayer: null, | ||||||
| @@ -165,6 +167,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | |||||||
|       return tassign(state, { |       return tassign(state, { | ||||||
|         inSearch:false, |         inSearch:false, | ||||||
|         selectedItem: a.item, |         selectedItem: a.item, | ||||||
|  |         parentItem: a.parentItem, | ||||||
|         selectedItemLayer: itemLayer, |         selectedItemLayer: itemLayer, | ||||||
|         panelVisible: a.item != null, |         panelVisible: a.item != null, | ||||||
|         clearEnabled: a.item != null, |         clearEnabled: a.item != null, | ||||||
| @@ -307,6 +310,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | |||||||
|     case mapActions.COLLAPSESEARCH: { |     case mapActions.COLLAPSESEARCH: { | ||||||
|       return tassign(state, { searchCollapsed: state.panelVisible ? false: true}); |       return tassign(state, { searchCollapsed: state.panelVisible ? false: true}); | ||||||
|     } |     } | ||||||
|  |     case commonActions.CLOSEALL: { | ||||||
|  |       return tassign(state, { searchCollapsed: state.panelVisible ? false: true,showLayerSwitcher:false}); | ||||||
|  |     } | ||||||
|     case mapActions.SETEXTENT: { |     case mapActions.SETEXTENT: { | ||||||
|       let a = action as mapActions.SetExtent; |       let a = action as mapActions.SetExtent; | ||||||
|       return tassign(state, { extent: a.extent }); |       return tassign(state, { extent: a.extent }); | ||||||
| @@ -401,9 +407,13 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | |||||||
|       } |       } | ||||||
|       var selectedBaseLayer: IItemLayer = null; |       var selectedBaseLayer: IItemLayer = null; | ||||||
|       var mapState = tassign(state.mapState); |       var mapState = tassign(state.mapState); | ||||||
|       let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode)[0];  |       let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode); | ||||||
|       if (baseLayers.length > 0 && mapState.baseLayerCode != "" && sb) { |       let db = baseLayers.filter(layer => layer.item.data && layer.item.data.default === true); | ||||||
|         selectedBaseLayer = sb; |       if (baseLayers.length > 0 && mapState.baseLayerCode != "" && sb.length>0) { | ||||||
|  |         selectedBaseLayer = sb[0]; | ||||||
|  |         selectedBaseLayer.visible = true; | ||||||
|  |       } else if (baseLayers.length >0 && db.length>0){ | ||||||
|  |         selectedBaseLayer = db[0]; | ||||||
|         selectedBaseLayer.visible = true; |         selectedBaseLayer.visible = true; | ||||||
|       } else if (baseLayers.length > 0) { |       } else if (baseLayers.length > 0) { | ||||||
|         selectedBaseLayer = baseLayers[0]; |         selectedBaseLayer = baseLayers[0]; | ||||||
| @@ -426,10 +436,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | |||||||
|       let a = action as mapActions.SelectOverlayLayer; |       let a = action as mapActions.SelectOverlayLayer; | ||||||
|       return tassign(state, { selectedOverlayLayer: a.itemLayer }); |       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 }); |       let newQueryState = tassign(state.queryState, { query: null, tags: null, itemCode: null, parentCode: null, itemType: null }); | ||||||
|       if (a.escapeKey) { |  | ||||||
|       return tassign(state, { |       return tassign(state, { | ||||||
|         panelVisible: false, |         panelVisible: false, | ||||||
|         panelCollapsed:false, |         panelCollapsed:false, | ||||||
| @@ -442,11 +451,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo | |||||||
|         searchMinified: false, |         searchMinified: false, | ||||||
|         features: [], |         features: [], | ||||||
|         query:initialState.query, |         query:initialState.query, | ||||||
|           showLayerSwitcher: false |         showLayerSwitcher: false, | ||||||
|  |         extent: null | ||||||
|       }); |       }); | ||||||
|       } else { |  | ||||||
|         return tassign(state, {}); |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|     case mapActions.SETSTYLE:{ |     case mapActions.SETSTYLE:{ | ||||||
|       let a = action as 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; |       let a= action as mapActions.SetReplaceUrl; | ||||||
|       return tassign(state,{replaceUrl:a.replaceUrl}); |       return tassign(state,{replaceUrl:a.replaceUrl}); | ||||||
|     }    |     }    | ||||||
|     case mapActions.CLEAR: { |  | ||||||
|       return tassign(state,{setStateCount:0}); |  | ||||||
|     } |  | ||||||
|     default: { |     default: { | ||||||
|       return state; |       return state; | ||||||
|     } |     } | ||||||
| @@ -478,6 +482,7 @@ export const getPanelVisible = (state: State) => state.panelVisible; | |||||||
| export const getPanelCollapsed = (state: State) => state.panelCollapsed; | export const getPanelCollapsed = (state: State) => state.panelCollapsed; | ||||||
| export const getSelectedFeature = (state: State) => state.selectedFeature; | export const getSelectedFeature = (state: State) => state.selectedFeature; | ||||||
| export const getSelectedItem = (state: State) => state.selectedItem; | export const getSelectedItem = (state: State) => state.selectedItem; | ||||||
|  | export const getParentItem = (state: State) => state.parentItem; | ||||||
| export const getQueryState = (state: State) => state.queryState; | export const getQueryState = (state: State) => state.queryState; | ||||||
| export const getClearEnabled = (state: State) => state.clearEnabled; | export const getClearEnabled = (state: State) => state.clearEnabled; | ||||||
| export const getSearchCollapsed = (state: State) => state.searchCollapsed; | export const getSearchCollapsed = (state: State) => state.searchCollapsed; | ||||||
| @@ -493,9 +498,8 @@ export const getSelectedItemLayer = (state: State) => state.selectedItemLayer; | |||||||
| export const getPeriod = (state:State) => state.period; | export const getPeriod = (state:State) => state.period; | ||||||
| export const getStyles = (state:State) => state.styles; | export const getStyles = (state:State) => state.styles; | ||||||
| export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher; | export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher; | ||||||
| export const getSetStateCount = (state:State) => state.setStateCount; |  | ||||||
| export const getInSearch = (state:State) => state.inSearch; | 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 selectMapState = createFeatureSelector<State>(MODULE_NAME); | ||||||
| export const selectGetMapState= createSelector(selectMapState, getMapState); | export const selectGetMapState= createSelector(selectMapState, getMapState); | ||||||
| @@ -505,6 +509,7 @@ export const selectGetPanelVisible = createSelector(selectMapState, getPanelVisi | |||||||
| export const selectGetPanelCollapsed = createSelector(selectMapState, getPanelCollapsed); | export const selectGetPanelCollapsed = createSelector(selectMapState, getPanelCollapsed); | ||||||
| export const selectGetSelectedFeature = createSelector(selectMapState, getSelectedFeature); | export const selectGetSelectedFeature = createSelector(selectMapState, getSelectedFeature); | ||||||
| export const selectGetSelectedItem = createSelector(selectMapState, getSelectedItem); | export const selectGetSelectedItem = createSelector(selectMapState, getSelectedItem); | ||||||
|  | export const selectGetParentItem = createSelector(selectMapState, getParentItem); | ||||||
| export const selectGetQueryState = createSelector(selectMapState, getQueryState); | export const selectGetQueryState = createSelector(selectMapState, getQueryState); | ||||||
| export const selectGetClearEnabled = createSelector(selectMapState, getClearEnabled); | export const selectGetClearEnabled = createSelector(selectMapState, getClearEnabled); | ||||||
| export const selectGetSearchCollapsed = createSelector(selectMapState, getSearchCollapsed); | export const selectGetSearchCollapsed = createSelector(selectMapState, getSearchCollapsed); | ||||||
| @@ -520,7 +525,6 @@ export const selectGetSelectedItemLayer = createSelector(selectMapState, getSele | |||||||
| export const selectGetPeriod = createSelector(selectMapState, getPeriod); | export const selectGetPeriod = createSelector(selectMapState, getPeriod); | ||||||
| export const selectGetStyles = createSelector(selectMapState, getStyles); | export const selectGetStyles = createSelector(selectMapState, getStyles); | ||||||
| export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher); | export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher); | ||||||
| export const selectgetSetStateCount = createSelector(selectMapState,getSetStateCount); |  | ||||||
| export const selectGetInSearch = createSelector(selectMapState,getInSearch); | export const selectGetInSearch = createSelector(selectMapState,getInSearch); | ||||||
| export const selectGetState = createSelector(selectMapState,getState); | 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", |   "name": "@farmmaps/common", | ||||||
|   "version": "0.0.1", |   "version": "0.0.1", | ||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 1 | ||||||
|   "requires": true, |  | ||||||
|   "dependencies": { |  | ||||||
|     "angular-oauth2-oidc": { |  | ||||||
|       "version": "8.0.4", |  | ||||||
|       "resolved": "https://registry.npmjs.org/angular-oauth2-oidc/-/angular-oauth2-oidc-8.0.4.tgz", |  | ||||||
|       "integrity": "sha512-7/3niJBqD7rnElcW+SudE36g7zMWChW4gSq7NpJSzDA4aQadBgvg0hn317MfPm9tpYGrpE3G/z2NMnbzek4TMA==", |  | ||||||
|       "requires": { |  | ||||||
|         "jsrsasign": "^8.0.12", |  | ||||||
|         "tslib": "^1.9.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "jsrsasign": { |  | ||||||
|       "version": "8.0.12", |  | ||||||
|       "resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz", |  | ||||||
|       "integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY=" |  | ||||||
|     }, |  | ||||||
|     "ngx-uploadx": { |  | ||||||
|       "version": "3.3.2", |  | ||||||
|       "resolved": "https://registry.npmjs.org/ngx-uploadx/-/ngx-uploadx-3.3.2.tgz", |  | ||||||
|       "integrity": "sha512-gRdXXq2cRU9HE6dj65qay9GV8NRC7n8y5LtMzJWqsfu2k3CHMQxo2TqZwA9/l/PqJ76RoO7sTPy1OenFQ+krkQ==", |  | ||||||
|       "requires": { |  | ||||||
|         "tslib": "^1.9.0" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "tslib": { |  | ||||||
|       "version": "1.10.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", |  | ||||||
|       "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,18 +5,19 @@ | |||||||
|     "registry": "https://repository.akkerweb.nl/repository/npm-hosted/" |     "registry": "https://repository.akkerweb.nl/repository/npm-hosted/" | ||||||
|   }, |   }, | ||||||
|   "peerDependencies": { |   "peerDependencies": { | ||||||
|     "@ng-bootstrap/ng-bootstrap": "^6.0", |     "@ng-bootstrap/ng-bootstrap": "^7.0", | ||||||
|     "@angular/common": "~9.1.0", |     "@angular/common": "~10.1.3", | ||||||
|     "@angular/core": "~9.1.0", |     "@angular/core": "~10.1.3", | ||||||
|     "@angular/forms": "~9.1.0", |     "@angular/forms": "~10.1.3", | ||||||
|     "@ngrx/effects": "^9.0", |     "@ngrx/effects": "^10.0", | ||||||
|     "@ngrx/router-store": "^9.0", |     "@ngrx/router-store": "^10.0", | ||||||
|     "@ngrx/store": "^9.0", |     "@ngrx/store": "^10.0", | ||||||
|     "tassign": "^1.0.0", |     "tassign": "^1.0.0", | ||||||
|     "bootstrap": "^4.4.1", |     "bootstrap": "^4.4.1", | ||||||
|     "@aspnet/signalr": "^1.1.4", |     "@microsoft/signalr": "^3.1.3", | ||||||
|     "font-awesome": "^4.7.0", |     "font-awesome": "^4.7.0", | ||||||
|     "ngx-uploadx": "^3.3.4", |     "ngx-uploadx": "^3.3.4", | ||||||
|     "angular-oauth2-oidc": "^9.1" |     "angular-oauth2-oidc": "^10.0.3", | ||||||
|  |     "moment": "^2.27.0" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import { IItemTypes } from '../models/item.types'; | |||||||
| import { IListItem } from '../models/list.item'; | import { IListItem } from '../models/list.item'; | ||||||
| import { IUser } from '../models/user'; | import { IUser } from '../models/user'; | ||||||
| import { IItem } from '../models/item'; | import { IItem } from '../models/item'; | ||||||
|  | import { UserInfo } from 'angular-oauth2-oidc'; | ||||||
|  |  | ||||||
| export const INITUSER = '[AppCommon] InitUser'; | export const INITUSER = '[AppCommon] InitUser'; | ||||||
| export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess'; | export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess'; | ||||||
| @@ -11,13 +12,18 @@ export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess'; | |||||||
| export const INITUSERPACKAGES = '[AppCommon] InitUserPackages'; | export const INITUSERPACKAGES = '[AppCommon] InitUserPackages'; | ||||||
| export const INITUSERPACKAGESSUCCESS = '[AppCommon] InitUserPackagesSuccess'; | export const INITUSERPACKAGESSUCCESS = '[AppCommon] InitUserPackagesSuccess'; | ||||||
|  |  | ||||||
|  | export const INITUSERSETTINGSROOT = '[AppCommon] InitUserSettingsRoot'; | ||||||
|  | export const INITUSERSETTINGSROOTSUCCESS = '[AppCommon] InitUserSettingsRootSuccess'; | ||||||
|  |  | ||||||
| export const INITROOT = '[Explorer] InitRoot'; | export const INITROOT = '[Explorer] InitRoot'; | ||||||
| export const INITROOTSUCCESS = '[Explorer] InitRootSuccess'; | export const INITROOTSUCCESS = '[Explorer] InitRootSuccess'; | ||||||
|  |  | ||||||
| export const OPENMODAL = '[AppCommon] OpenModal'; | export const OPENMODAL = '[AppCommon] OpenModal'; | ||||||
| export const CLOSEMODAL = '[AppCommon] CloseModal'; | export const CLOSEMODAL = '[AppCommon] CloseModal'; | ||||||
| export const LOGIN = '[AppCommon] Login'; | export const LOGIN = '[AppCommon] Login'; | ||||||
|  | export const LOGOUT = '[AppCommon] Logout'; | ||||||
| export const ESCAPE = '[AppCommon] Escape'; | export const ESCAPE = '[AppCommon] Escape'; | ||||||
|  | export const CLOSEALL = '[AppCommon] CloseAll'; | ||||||
|  |  | ||||||
| export const LOADITEMTYPES = '[AppCommon] LoadItemTypes'; | export const LOADITEMTYPES = '[AppCommon] LoadItemTypes'; | ||||||
| export const LOADITEMTYPESSUCCESS = '[AppCommon] LoadItemTypesSuccess'; | export const LOADITEMTYPESSUCCESS = '[AppCommon] LoadItemTypesSuccess'; | ||||||
| @@ -28,7 +34,8 @@ export const ITEMDELETEDEVENT = '[AppCommon] ItemDeletedEvent'; | |||||||
|  |  | ||||||
| export const TASKSTARTEVENT = '[AppCommon] TaskStartEvent'; | export const TASKSTARTEVENT = '[AppCommon] TaskStartEvent'; | ||||||
| export const TASKENDEVENT = '[AppCommon] TaskEndEvent'; | export const TASKENDEVENT = '[AppCommon] TaskEndEvent'; | ||||||
| export const TASKERRORTEVENT = '[AppCommon] TaskErrorEvent'; | export const TASKERROREVENT = '[AppCommon] TaskErrorEvent'; | ||||||
|  | export const TASKPROGRESSEVENT = '[AppCommon] TaskProgressEvent'; | ||||||
|  |  | ||||||
| export const DEVICEUPDATEEVENT = '[AppCommon] DeviceUpdateEvent'; | export const DEVICEUPDATEEVENT = '[AppCommon] DeviceUpdateEvent'; | ||||||
|  |  | ||||||
| @@ -51,8 +58,16 @@ export const UPLOADEDFILECLICK = '[AppCommon] UploadedFileClick'; | |||||||
|  |  | ||||||
| export const TOGGLEMENU = '[AppCommon] ToggleMenu'; | export const TOGGLEMENU = '[AppCommon] ToggleMenu'; | ||||||
|  |  | ||||||
|  | export const TOGGLEACCOUNTMENU = '[AppCommon] ToggleAccountMenu'; | ||||||
|  |  | ||||||
| export const SETMENUVISIBLE = '[AppCommon] SetMenuVisible'; | export const SETMENUVISIBLE = '[AppCommon] SetMenuVisible'; | ||||||
|  |  | ||||||
|  | export const ONLINE = '[AppCommon] Online'; | ||||||
|  |  | ||||||
|  | export const OFFLINE = '[AppCommon] Offline'; | ||||||
|  |  | ||||||
|  | export const SETPAGEMODE = '[AppCommon] SetPageMode'; | ||||||
|  |  | ||||||
| export class InitUser implements Action { | export class InitUser implements Action { | ||||||
|   readonly type = INITUSER; |   readonly type = INITUSER; | ||||||
|  |  | ||||||
| @@ -62,7 +77,13 @@ export class InitUser implements Action { | |||||||
| export class InitUserSuccess implements Action { | export class InitUserSuccess implements Action { | ||||||
|   readonly type = INITUSERSUCCESS; |   readonly type = INITUSERSUCCESS; | ||||||
|  |  | ||||||
|   constructor(public user:IUser ) { } |   constructor(public user:IUser,public userinfo:UserInfo ) { } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class InitUserPackages implements Action { | ||||||
|  |   readonly type = INITUSERPACKAGES; | ||||||
|  |  | ||||||
|  |   constructor() {} | ||||||
| } | } | ||||||
|  |  | ||||||
| export class InitUserPackagesSuccess implements Action { | export class InitUserPackagesSuccess implements Action { | ||||||
| @@ -71,6 +92,18 @@ export class InitUserPackagesSuccess implements Action { | |||||||
|   constructor(public items:IItem[] ) { } |   constructor(public items:IItem[] ) { } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export class InitUserSettingsRoot implements Action { | ||||||
|  |   readonly type = INITUSERSETTINGSROOT; | ||||||
|  |  | ||||||
|  |   constructor() {} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class InitUserSettingsRootSuccess implements Action { | ||||||
|  |   readonly type = INITUSERSETTINGSROOTSUCCESS; | ||||||
|  |  | ||||||
|  |   constructor(public item: IItem ) { } | ||||||
|  | } | ||||||
|  |  | ||||||
| export class InitRoot implements Action { | export class InitRoot implements Action { | ||||||
|   readonly type = INITROOT; |   readonly type = INITROOT; | ||||||
|  |  | ||||||
| @@ -95,6 +128,12 @@ export class CloseModal implements Action { | |||||||
|   constructor() { } |   constructor() { } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export class CloseAll implements Action { | ||||||
|  |   readonly type = CLOSEALL; | ||||||
|  |  | ||||||
|  |   constructor() { } | ||||||
|  | } | ||||||
|  |  | ||||||
| export class StartRouteLoading implements Action { | export class StartRouteLoading implements Action { | ||||||
|   readonly type = STARTROUTELOADING; |   readonly type = STARTROUTELOADING; | ||||||
|  |  | ||||||
| @@ -113,6 +152,12 @@ export class Login implements Action { | |||||||
|   constructor(public url: string) { } |   constructor(public url: string) { } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export class Logout implements Action { | ||||||
|  |   readonly type = LOGOUT; | ||||||
|  |  | ||||||
|  |   constructor() { } | ||||||
|  | } | ||||||
|  |  | ||||||
| export class Escape implements Action { | export class Escape implements Action { | ||||||
|   readonly type = ESCAPE; |   readonly type = ESCAPE; | ||||||
|  |  | ||||||
| @@ -168,7 +213,13 @@ export class TaskEndEvent implements Action { | |||||||
| } | } | ||||||
|  |  | ||||||
| export class TaskErrorEvent implements Action { | export class TaskErrorEvent implements Action { | ||||||
|   readonly type = TASKERRORTEVENT; |   readonly type = TASKERROREVENT; | ||||||
|  |  | ||||||
|  |   constructor(public itemCode: string, public attributes: any) { } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class TaskProgressEvent implements Action { | ||||||
|  |   readonly type = TASKPROGRESSEVENT; | ||||||
|  |  | ||||||
|   constructor(public itemCode: string, public attributes: any) { } |   constructor(public itemCode: string, public attributes: any) { } | ||||||
| } | } | ||||||
| @@ -226,18 +277,43 @@ export class ToggleMenu implements Action { | |||||||
|   constructor() { } |   constructor() { } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export class ToggleAccountMenu implements Action { | ||||||
|  |   readonly type = TOGGLEACCOUNTMENU; | ||||||
|  |  | ||||||
|  |   constructor() { } | ||||||
|  | } | ||||||
|  |  | ||||||
| export class SetMenuVisible implements Action { | export class SetMenuVisible implements Action { | ||||||
|   readonly type = SETMENUVISIBLE; |   readonly type = SETMENUVISIBLE; | ||||||
|  |  | ||||||
|   constructor(public visible:boolean) { } |   constructor(public visible:boolean) { } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export class Online implements Action { | ||||||
|  |   readonly type = ONLINE; | ||||||
|  |  | ||||||
|  |   constructor() { } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class Offline implements Action { | ||||||
|  |   readonly type = OFFLINE; | ||||||
|  |  | ||||||
|  |   constructor() { } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export class SetPageMode implements Action { | ||||||
|  |   readonly type = SETPAGEMODE; | ||||||
|  |  | ||||||
|  |   constructor(public pageMode:boolean) {} | ||||||
|  | } | ||||||
|  |    | ||||||
|  |  | ||||||
| export type Actions = OpenModal | export type Actions = OpenModal | ||||||
|   | InitRoot |   | InitRoot | ||||||
|   | InitRootSuccess |   | InitRootSuccess | ||||||
|   | CloseModal |   | CloseModal | ||||||
|   | Login |   | Login | ||||||
|  |   | Logout | ||||||
|   | ItemChangedEvent |   | ItemChangedEvent | ||||||
|   | ItemAddedEvent |   | ItemAddedEvent | ||||||
|   | ItemDeletedEvent |   | ItemDeletedEvent | ||||||
| @@ -258,8 +334,18 @@ export type Actions = OpenModal | |||||||
|   | TaskStartEvent |   | TaskStartEvent | ||||||
|   | TaskEndEvent |   | TaskEndEvent | ||||||
|   | TaskErrorEvent |   | TaskErrorEvent | ||||||
|  |   | TaskProgressEvent | ||||||
|   | DeviceUpdateEvent |   | DeviceUpdateEvent | ||||||
|   | ToggleMenu |   | ToggleMenu | ||||||
|   | SetMenuVisible |   | SetMenuVisible | ||||||
|   | InitUserPackagesSuccess; |   | InitUserPackages | ||||||
|  |   | InitUserPackagesSuccess | ||||||
|  |   | InitUserSettingsRoot | ||||||
|  |   | InitUserSettingsRootSuccess | ||||||
|  |   | ToggleAccountMenu | ||||||
|  |   | CloseAll | ||||||
|  |   | Online | ||||||
|  |   | Offline | ||||||
|  |   | SetPageMode; | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc'; | |||||||
|  |  | ||||||
| //components | //components | ||||||
| import { ItemTypeService } from './services/itemtype.service'; | import { ItemTypeService } from './services/itemtype.service'; | ||||||
|  | import { SchemaService } from './services/schema.service'; | ||||||
| import { FolderService } from './services/folder.service'; | import { FolderService } from './services/folder.service'; | ||||||
| import { TimespanService } from './services/timespan.service'; | import { TimespanService } from './services/timespan.service'; | ||||||
| import { ItemService } from './services/item.service'; | import { ItemService } from './services/item.service'; | ||||||
| @@ -28,6 +29,9 @@ import { ResumableFileUploadService } from './components/resumable-file-upload/r | |||||||
| import { NgbDateNativeAdapter } from './services/date-adapter.service' | import { NgbDateNativeAdapter } from './services/date-adapter.service' | ||||||
| import { AuthConfigFactory } from './shared/authconfigFactory'; | import { AuthConfigFactory } from './shared/authconfigFactory'; | ||||||
| import { StateSerializerService } from './services/state-serializer.service'; | import { StateSerializerService } from './services/state-serializer.service'; | ||||||
|  | import { PackageService } from './services/package.service'; | ||||||
|  | import { PackagePreloadStrategy } from './services/package-preload-strategy.service'; | ||||||
|  | import { SenmlService } from './services/senml-service'; | ||||||
|  |  | ||||||
| export { | export { | ||||||
|   FolderService, |   FolderService, | ||||||
| @@ -47,7 +51,11 @@ export { | |||||||
|   AuthCallbackGuard, |   AuthCallbackGuard, | ||||||
|   ResumableFileUploadService, |   ResumableFileUploadService, | ||||||
|   NgbDateNativeAdapter, |   NgbDateNativeAdapter, | ||||||
|   StateSerializerService |   StateSerializerService, | ||||||
|  |   SchemaService, | ||||||
|  |   PackageService, | ||||||
|  |   SenmlService, | ||||||
|  |   PackagePreloadStrategy | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
| @@ -71,7 +79,7 @@ export class AppCommonServiceModule { | |||||||
|         { |         { | ||||||
|           provide: APP_INITIALIZER, |           provide: APP_INITIALIZER, | ||||||
|           useFactory: appConfigFactory, |           useFactory: appConfigFactory, | ||||||
|           deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage,ItemTypeService], |           deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage, ItemTypeService], | ||||||
|           multi: true |           multi: true | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -31,6 +31,8 @@ import { TimespanComponent } from './components/timespan/timespan.component'; | |||||||
| import { TagInputComponent } from './components/tag-input/tag-input.component'; | import { TagInputComponent } from './components/tag-input/tag-input.component'; | ||||||
| import { MenuBackgroundComponent } from './components/menu-background/menu-background.component'; | import { MenuBackgroundComponent } from './components/menu-background/menu-background.component'; | ||||||
| import { HasPackageDirective} from './components/has-package/has-package.directive'; | import { HasPackageDirective} from './components/has-package/has-package.directive'; | ||||||
|  | import { HasClaimDirective} from './components/has-claim/has-claim.directive'; | ||||||
|  | import { UserMenuComponent} from './components/user-menu/user-menu.component'; | ||||||
| import { Alert } from './enumerations/alert.enum'; | import { Alert } from './enumerations/alert.enum'; | ||||||
| import { IEventMessage } from './models/event.message'; | import { IEventMessage } from './models/event.message'; | ||||||
| import { IItem, Item } from './models/item'; | import { IItem, Item } from './models/item'; | ||||||
| @@ -39,15 +41,21 @@ import { IItemType } from './models/item.type'; | |||||||
| import { IItemTypes } from './models/item.types'; | import { IItemTypes } from './models/item.types'; | ||||||
| import { IItemTask, ItemTask } from './models/itemTask'; | import { IItemTask, ItemTask } from './models/itemTask'; | ||||||
| import { IListItem } from './models/list.item'; | import { IListItem } from './models/list.item'; | ||||||
| import { ITypeaheadItem } from './models/typeahead.item'; | import { ITypeaheadItem } from './models/typeahead.item' | ||||||
|  | import { IJsonline } from './models/json-line'; | ||||||
|  | import { ISenMLItem } from './models/senml-item'; | ||||||
| import { IPackage,IPackages } from './models/package'; | import { IPackage,IPackages } from './models/package'; | ||||||
| import { IUser } from './models/user'; | import { IUser } from './models/user'; | ||||||
| import { IQueryState } from './models/query.state'; | import { IQueryState } from './models/query.state'; | ||||||
| import { ICodeListItem } from './models/code.list.item'; | import { ICodeListItem } from './models/code.list.item'; | ||||||
|  | import { IDataLayer } from './models/data.layer'; | ||||||
|  | import { IColor,IGradientstop} from './models/gradient'; | ||||||
| import * as commonActions from './actions/app-common.actions'; | import * as commonActions from './actions/app-common.actions'; | ||||||
| import * as commonReducers from './reducers/app-common.reducer'; | import * as commonReducers from './reducers/app-common.reducer'; | ||||||
| import * as commonEffects from './effects/app-common.effects'; | import * as commonEffects from './effects/app-common.effects'; | ||||||
| import { SecureOAuthStorage} from './shared/secureOAuthStorage'; | import { SecureOAuthStorage} from './shared/secureOAuthStorage'; | ||||||
|  | import { GradientComponent } from './components/gradient/gradient.component'; | ||||||
|  | import { GradientSelectComponent } from './components/gradient-select/gradient-select.component'; | ||||||
|  |  | ||||||
| export { | export { | ||||||
|   SafePipe, |   SafePipe, | ||||||
| @@ -60,7 +68,9 @@ export { | |||||||
|   SidePanelComponent, |   SidePanelComponent, | ||||||
|   TimespanComponent, |   TimespanComponent, | ||||||
|   TagInputComponent, |   TagInputComponent, | ||||||
|  |   UserMenuComponent, | ||||||
|   HasPackageDirective, |   HasPackageDirective, | ||||||
|  |   HasClaimDirective, | ||||||
|   Alert, |   Alert, | ||||||
|   IEventMessage, |   IEventMessage, | ||||||
|   IItem, |   IItem, | ||||||
| @@ -82,7 +92,12 @@ export { | |||||||
|   AuthConfigFactory, |   AuthConfigFactory, | ||||||
|   MenuBackgroundComponent, |   MenuBackgroundComponent, | ||||||
|   SecureOAuthStorage, |   SecureOAuthStorage, | ||||||
|   WeatherCurrentObservation |   WeatherCurrentObservation, | ||||||
|  |   IJsonline, | ||||||
|  |   ISenMLItem, | ||||||
|  |   IDataLayer, | ||||||
|  |   IColor, | ||||||
|  |   IGradientstop | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
| @@ -109,7 +124,11 @@ export { | |||||||
|     TagInputComponent, |     TagInputComponent, | ||||||
|     SessionClearedComponent, |     SessionClearedComponent, | ||||||
|     MenuBackgroundComponent, |     MenuBackgroundComponent, | ||||||
|     HasPackageDirective |     HasPackageDirective, | ||||||
|  |     HasClaimDirective, | ||||||
|  |     UserMenuComponent, | ||||||
|  |     GradientComponent, | ||||||
|  |     GradientSelectComponent | ||||||
|   ], |   ], | ||||||
|   exports: [ |   exports: [ | ||||||
|     NgbModule, |     NgbModule, | ||||||
| @@ -127,7 +146,11 @@ export { | |||||||
|     TagInputComponent, |     TagInputComponent, | ||||||
|     SessionClearedComponent, |     SessionClearedComponent, | ||||||
|     MenuBackgroundComponent, |     MenuBackgroundComponent, | ||||||
|     HasPackageDirective |     HasPackageDirective, | ||||||
|  |     HasClaimDirective, | ||||||
|  |     UserMenuComponent, | ||||||
|  |     GradientComponent, | ||||||
|  |     GradientSelectComponent | ||||||
|   ] |   ] | ||||||
| }) | }) | ||||||
| export class AppCommonModule { | export class AppCommonModule { | ||||||
|   | |||||||
| @@ -1,7 +1,14 @@ | |||||||
| <div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async)}"> | <div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async),'pagemode':(isPageMode|async),'appmode':!(isPageMode|async)}"> | ||||||
|   <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> |   <nav class="navbar navbar-light navbar-expand bg-light navigation-clean"> | ||||||
|     <button type="button" class="btn btn-outline-light" (click)="handleToggleMenu($event)"><i class="fa fa-bars" aria-hidden="true"></i></button> |     <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> |     <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> |   </nav> | ||||||
|   <div class="body"> |   <div class="body"> | ||||||
|     <router-outlet></router-outlet> |     <router-outlet></router-outlet> | ||||||
| @@ -11,7 +18,7 @@ | |||||||
|     <div class="container-fluid"> |     <div class="container-fluid"> | ||||||
|       <div class="body"> |       <div class="body"> | ||||||
|         <div class="d-flex flex-row"> |         <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 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> | ||||||
|         <div class="d-flex flex-column cards">  |         <div class="d-flex flex-column cards">  | ||||||
| @@ -23,4 +30,12 @@ | |||||||
|   <ng-container *ngIf="showUploadProgress"> |   <ng-container *ngIf="showUploadProgress"> | ||||||
|     <fm-resumable-file-upload></fm-resumable-file-upload> |     <fm-resumable-file-upload></fm-resumable-file-upload> | ||||||
|   </ng-container>   |   </ng-container>   | ||||||
|  |   <div class="user-menu 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> | </div> | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| @import "~bootstrap/scss/bootstrap.scss"; | @import "~bootstrap/scss/bootstrap.scss"; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // custom styles | // custom styles | ||||||
|  |  | ||||||
| .btn:focus { | .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 { | .navbar-brand { | ||||||
|   padding-top: .5rem; |   padding-top: .5rem; | ||||||
| @@ -53,7 +54,20 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;} | |||||||
|   bottom: 0; |   bottom: 0; | ||||||
|   left: 0; |   left: 0; | ||||||
|   right: 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 { | .app.fullscreen > .navbar { | ||||||
| @@ -76,3 +90,37 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;} | |||||||
| .logo { | .logo { | ||||||
|   margin-left: 1rem; |   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 { 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 { Router, NavigationStart, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router'; | ||||||
| import { Meta, Title, MetaDefinition } from '@angular/platform-browser';import { DOCUMENT } from "@angular/common"; | import { Meta, Title, MetaDefinition } from '@angular/platform-browser'; import { DOCUMENT } from "@angular/common"; | ||||||
| import { Subscription ,  Observable } from 'rxjs'; | import { Subscription, Observable } from 'rxjs'; | ||||||
|  | import { distinctUntilChanged } from 'rxjs/operators'; | ||||||
| import { Store, Action } from '@ngrx/store'; | import { Store, Action } from '@ngrx/store'; | ||||||
|  | import { IUser } from '../../models/user'; | ||||||
|  | import { OAuthService, OAuthErrorEvent } from 'angular-oauth2-oidc'; | ||||||
|  |  | ||||||
| //AppCommon | //AppCommon | ||||||
| import { IEventMessage } from '../../models/event.message'; | import { IEventMessage } from '../../models/event.message'; | ||||||
| import { IListItem} from '../../models/list.item'; | import { IListItem } from '../../models/list.item'; | ||||||
| import { EventService } from '../../services/event.service'; | import { EventService } from '../../services/event.service'; | ||||||
|  | import { ItemTypeService } from '../../services/itemtype.service'; | ||||||
| import * as  commonActions from '../../actions/app-common.actions'; | 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'; | import * as appReducers from '../../reducers/app-common.reducer'; | ||||||
|  |  | ||||||
| @@ -31,23 +37,30 @@ export class AppComponent implements OnInit, OnDestroy { | |||||||
|  |  | ||||||
|   public currentFolder: Observable<IListItem>; |   public currentFolder: Observable<IListItem>; | ||||||
|   public folderParents: Observable<IListItem[]>; |   public folderParents: Observable<IListItem[]>; | ||||||
|   public fullScreen: Observable<boolean>; |   public fullScreen: Observable<boolean> = this.store$.select(appReducers.selectGetFullScreen); | ||||||
|   public routeLoading: Observable<boolean>; |   public isOnline: Observable<boolean> = this.store$.select(appReducers.SelectGetIsOnline); | ||||||
|   public menuVisible: Observable<boolean>; |   public routeLoading: Observable<boolean> = this.store$.select(appReducers.selectGetRouteLoading); | ||||||
|   @Input() showUploadProgress: boolean =true; |   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( |   constructor( | ||||||
|     private router: Router, |     public router: Router, | ||||||
|     private activatedRoute: ActivatedRoute, |     private activatedRoute$: ActivatedRoute, | ||||||
|     private title: Title, |     private title$: Title, | ||||||
|     private meta: Meta, |     private meta$: Meta, | ||||||
|     private store: Store<appReducers.State>, |     private store$: Store<appReducers.State>, | ||||||
|     private eventService: EventService, |     private eventService$: EventService, | ||||||
|  |     private healthCheckService$: HealthCheckService, | ||||||
|  |     private itemTypeService$: ItemTypeService, | ||||||
|  |     private oauthService$: OAuthService, | ||||||
|  |     private appConfig$: AppConfig | ||||||
|   ) { |   ) { | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   getActionFromEvent(event: IEventMessage): Action { |   getActionFromEvent(event: IEventMessage): Action { | ||||||
|     var action: Action = null; |     var action: Action = null; | ||||||
|     console.debug(`${event.eventType} Event received`); |     console.debug(`${event.eventType} Event received`); | ||||||
| @@ -76,6 +89,10 @@ export class AppComponent implements OnInit, OnDestroy { | |||||||
|         action = new commonActions.TaskErrorEvent(event.itemCode, event.attributes); |         action = new commonActions.TaskErrorEvent(event.itemCode, event.attributes); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|  |       case "taskProgress": { | ||||||
|  |         action = new commonActions.TaskProgressEvent(event.itemCode, event.attributes); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|       case "deviceUpdate": { |       case "deviceUpdate": { | ||||||
|         action = new commonActions.DeviceUpdateEvent(event.itemCode, event.attributes); |         action = new commonActions.DeviceUpdateEvent(event.itemCode, event.attributes); | ||||||
|         break; |         break; | ||||||
| @@ -85,61 +102,108 @@ export class AppComponent implements OnInit, OnDestroy { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   ngOnInit() { |   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.InstallRouteEventHandler(); | ||||||
|     this.InstallEventServiceEventHandler(); |     this.InstallEventServiceEventHandler(); | ||||||
|  |     this.InstallAuthenticationEventHandler(); | ||||||
|  |     this.InstallHealthCheck(); | ||||||
|  |  | ||||||
|  |     //load item types | ||||||
|  |     this.itemTypeService$.load(this.appConfig$) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @HostListener('document:keyup', ['$event']) |   @HostListener('document:keyup', ['$event']) | ||||||
|   keyUp(event: KeyboardEvent) { |   onKeyUp(event: KeyboardEvent) { | ||||||
|     let x = event.keyCode; |     let x = event.keyCode; | ||||||
|     if (x === 27) { |     if (x === 27) { | ||||||
|       this.store.dispatch(new commonActions.Escape(true,false)); |       this.store$.dispatch(new commonActions.Escape(true, false)); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ngOnDestroy() { |   ngOnDestroy() { | ||||||
|     // Subscription clean-up |     // Subscription clean-up | ||||||
|     if(this.routerSub$) this.routerSub$.unsubscribe(); |     if (this.routerSub$) this.routerSub$.unsubscribe(); | ||||||
|     if(this.eventSub$) this.eventSub$.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() { |   private InstallRouteEventHandler() { | ||||||
|     var other = this; |     var other = this; | ||||||
|     this.routerSub$ = this.router.events.subscribe(event => { |     this.routerSub$ = this.router.events.subscribe(event => { | ||||||
|       if (event instanceof RouteConfigLoadStart) { |       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) { |       if (event instanceof RouteConfigLoadEnd) { | ||||||
|         other.store.dispatch(new commonActions.EndRouteLoading()); |         other.store$.dispatch(new commonActions.EndRouteLoading()); | ||||||
|       } |       } | ||||||
|       if(event instanceof NavigationStart) { |       if (event instanceof NavigationStart) { | ||||||
|         other.store.dispatch(new commonActions.SetMenuVisible(false)); |         other.store$.dispatch(new commonActions.SetMenuVisible(false)); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   private InstallEventServiceEventHandler() { |   private InstallEventServiceEventHandler() { | ||||||
|     var other = this; |     var other = this; | ||||||
|     this.eventSub$ = this.eventService.event.subscribe(event => { |     this.eventSub$ = this.eventService$.event.subscribe(event => { | ||||||
|       var action = other.getActionFromEvent(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) { |   handleClick(event: MouseEvent) { | ||||||
|     this.store.dispatch(new commonActions.Escape(false,true)); |     this.store$.dispatch(new commonActions.Escape(false, true)); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleStopBubble(event: MouseEvent) { |   handleStopBubble(event: MouseEvent) { | ||||||
|     event.stopPropagation(); |     event.stopPropagation(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleToggleMenu(event:MouseEvent) { |   handleToggleMenu(event: MouseEvent) { | ||||||
|     event.stopPropagation(); |     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; |     bottom:0; | ||||||
|     opacity: 0; |     opacity: 0; | ||||||
|     background-color: #000000; |     background-color: #000000; | ||||||
|     transition: opacity 0s ease-out 1s; |     transition: opacity 0.3s ease-out; | ||||||
|     pointer-events: none; |     pointer-events: none; | ||||||
|     z-index:99; |     z-index:99; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ export class ResumableFileUploadService implements OnDestroy{ | |||||||
|   public isMinimized = false; |   public isMinimized = false; | ||||||
|   public parentCode: string; |   public parentCode: string; | ||||||
|   public refresh: Subject<any> = new Subject<any>(); |   public refresh: Subject<any> = new Subject<any>(); | ||||||
|  |   public addedFiles: Subject<File> = new Subject<File>(); | ||||||
|   private _eventSub:Subscription; |   private _eventSub:Subscription; | ||||||
|   private initialized = false; |   private initialized = false; | ||||||
|  |  | ||||||
| @@ -52,20 +53,21 @@ export class ResumableFileUploadService implements OnDestroy{ | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleState(state:UploadState) { |   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 ) |       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) { |           if(file) { | ||||||
|  |             this.isUploading = true; | ||||||
|             file.progress = (state.progress?state.progress:0); |             file.progress = (state.progress?state.progress:0); | ||||||
|           } |           } | ||||||
|         };break; |         };break; | ||||||
|         case "complete": { |         case "complete": { | ||||||
|             var file =this.files.find((f) => f.identifier == state.uploadId ) |  | ||||||
|             if(file) { |             if(file) { | ||||||
|               var parts = state.url.split("/"); |               var parts = state.url.split("/"); | ||||||
|               file.itemCode = parts[parts.length-1]; |               file.itemCode = parts[parts.length-1]; | ||||||
| @@ -74,10 +76,9 @@ export class ResumableFileUploadService implements OnDestroy{ | |||||||
|             } |             } | ||||||
|         };break; |         };break; | ||||||
|         case "error": { |         case "error": { | ||||||
|           var file =this.files.find((f) => f.identifier == state.uploadId ) |  | ||||||
|           if(file) { |           if(file) { | ||||||
|             file.error=true; |             file.error=true; | ||||||
|             file.errorMessage = state.response; |             file.errorMessage = state.response as string; | ||||||
|           } |           } | ||||||
|         };break; |         };break; | ||||||
|       } |       } | ||||||
| @@ -102,6 +103,9 @@ export class ResumableFileUploadService implements OnDestroy{ | |||||||
|     if (index > -1) { |     if (index > -1) { | ||||||
|       this.files.splice(index, 1); |       this.files.splice(index, 1); | ||||||
|     } |     } | ||||||
|  |     if(this.files.length==0) { | ||||||
|  |       this.isUploading = false; | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   doClose = function () { |   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 { Injectable } from '@angular/core'; | ||||||
| import { Router } from '@angular/router'; | import { Router } from '@angular/router'; | ||||||
| import { OAuthService } from 'angular-oauth2-oidc'; | import { OAuthService,UserInfo } from 'angular-oauth2-oidc'; | ||||||
| import { Store, Action } from '@ngrx/store'; | import { Store, Action } from '@ngrx/store'; | ||||||
| import { Effect, Actions,ofType } from '@ngrx/effects'; | import { Effect, Actions,ofType } from '@ngrx/effects'; | ||||||
| import { Observable ,  defer ,  of } from 'rxjs'; | import { Observable ,  defer ,  of,from,zip } from 'rxjs'; | ||||||
| import { withLatestFrom,mergeMap,switchMap,map,catchError} from 'rxjs/operators'; | import { withLatestFrom,mergeMap,switchMap,map,catchError,first} from 'rxjs/operators'; | ||||||
| import * as appCommonActions from '../actions/app-common.actions'; | import * as appCommonActions from '../actions/app-common.actions'; | ||||||
| import * as appCommonReducers from '../reducers/app-common.reducer'; | import * as appCommonReducers from '../reducers/app-common.reducer'; | ||||||
| import { ItemService } from '../services/item.service'; | import { ItemService } from '../services/item.service'; | ||||||
| @@ -25,7 +25,15 @@ export class AppCommonEffects { | |||||||
|     withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)), |     withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)), | ||||||
|     mergeMap(([action, initialized]) => { |     mergeMap(([action, initialized]) => { | ||||||
|       var a = (action as appCommonActions.Login); |       var a = (action as appCommonActions.Login); | ||||||
|       this.oauthService$.initCodeFlow(a.url); |       this.oauthService$.initCodeFlow(a.url,{"prompt":"login"}); | ||||||
|  |       return []; | ||||||
|  |     })); | ||||||
|  |  | ||||||
|  |     @Effect({ dispatch: false }) | ||||||
|  |     logout$: Observable<Action> = this.actions$.pipe( | ||||||
|  |       ofType(appCommonActions.LOGOUT), | ||||||
|  |       mergeMap((action) => { | ||||||
|  |         this.oauthService$.revokeTokenAndLogout(); | ||||||
|         return []; |         return []; | ||||||
|       })); |       })); | ||||||
|  |  | ||||||
| @@ -42,35 +50,67 @@ export class AppCommonEffects { | |||||||
|   @Effect() |   @Effect() | ||||||
|   initUser$: Observable<Action> = this.actions$.pipe( |   initUser$: Observable<Action> = this.actions$.pipe( | ||||||
|     ofType(appCommonActions.INITUSER), |     ofType(appCommonActions.INITUSER), | ||||||
|     withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)), |     first(), | ||||||
|     switchMap(([action, initialized]) => { |     switchMap((action) => { | ||||||
|       if(!initialized) { |         return zip(this.userService$.getCurrentUser(),from(this.oauthService$.loadUserProfile())).pipe( | ||||||
|       return this.userService$.getCurrentUser().pipe( |         switchMap(([user,userInfo]) => {return of(new appCommonActions.InitUserSuccess(user,userInfo as UserInfo))} ), | ||||||
|         map((user: IUser) => new appCommonActions.InitUserSuccess(user)), |  | ||||||
|         catchError(error => of(new appCommonActions.Fail(error)))) |         catchError(error => of(new appCommonActions.Fail(error)))) | ||||||
|       } else { |  | ||||||
|         return []; |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|   )); |   )); | ||||||
|      |      | ||||||
|  |  | ||||||
|   @Effect() |   @Effect() | ||||||
|   initUserPackages$:Observable<Action> = this.actions$.pipe( |   initUserPackages$:Observable<Action> = this.actions$.pipe( | ||||||
|     ofType(appCommonActions.INITUSERSUCCESS), |     ofType(appCommonActions.INITUSERPACKAGES), | ||||||
|     switchMap((action) => { |     switchMap(() => { | ||||||
|       let a = action as appCommonActions.InitUserSuccess; |       return this.itemService$.getItemList('vnd.farmmaps.itemtype.package').pipe( | ||||||
|       return this.itemService$.getChildItemList(a.user.code+":USER_PACKAGES","vnd.farmmaps.itemtype.package").pipe( |  | ||||||
|         switchMap((items) => of(new appCommonActions.InitUserPackagesSuccess(items))), |         switchMap((items) => of(new appCommonActions.InitUserPackagesSuccess(items))), | ||||||
|         catchError(error =>  of(new appCommonActions.Fail(error))) |         catchError(error =>  of(new appCommonActions.Fail(error))) | ||||||
|       ) |       ) | ||||||
|     }) |     }) | ||||||
|   ); |   ); | ||||||
|  |  | ||||||
|  |   @Effect() | ||||||
|  |   userPackagesChanged$:Observable<Action> = this.actions$.pipe( | ||||||
|  |     ofType(appCommonActions.ITEMCHANGEDEVENT), | ||||||
|  |     switchMap((action) => { | ||||||
|  |       let a = action as appCommonActions.ItemChangedEvent; | ||||||
|  |       if(a.itemCode.endsWith(":USER_PACKAGES"))  | ||||||
|  |         return of(new appCommonActions.InitUserPackages()); | ||||||
|  |       else | ||||||
|  |         return []; | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  |    | ||||||
|  |   @Effect() | ||||||
|  |   initUserSettingsRoot$:Observable<Action> = this.actions$.pipe( | ||||||
|  |     ofType(appCommonActions.INITUSERSETTINGSROOT), | ||||||
|  |     withLatestFrom(this.store$.select(appCommonReducers.SelectGetUser)), | ||||||
|  |     switchMap(([, user]) => { | ||||||
|  |       return this.itemService$.getItem(user.code + ':USER_SETTINGS').pipe( | ||||||
|  |         switchMap((item) => of(new appCommonActions.InitUserSettingsRootSuccess(item))), | ||||||
|  |         catchError(error =>  of(new appCommonActions.Fail(error))) | ||||||
|  |       ) | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  |  | ||||||
|  |   @Effect() | ||||||
|  |   initUserSettingsRootChanged$:Observable<Action> = this.actions$.pipe( | ||||||
|  |     ofType(appCommonActions.ITEMCHANGEDEVENT), | ||||||
|  |     switchMap((action) => { | ||||||
|  |       let a = action as appCommonActions.ItemChangedEvent; | ||||||
|  |       if(a.itemCode.endsWith(":USER_SETTINGS"))  | ||||||
|  |         return of(new appCommonActions.InitUserSettingsRoot()); | ||||||
|  |       else | ||||||
|  |         return []; | ||||||
|  |     }) | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   @Effect() |   @Effect() | ||||||
|   initUserSuccess$: Observable<Action> = this.actions$.pipe( |   initUserSuccess$: Observable<Action> = this.actions$.pipe( | ||||||
|     ofType(appCommonActions.INITUSERSUCCESS), |     ofType(appCommonActions.INITUSERSUCCESS), | ||||||
|     switchMap(() => { |     switchMap(() => { | ||||||
|       return of(new appCommonActions.InitRoot()); |       return [new appCommonActions.InitRoot(),new appCommonActions.InitUserPackages(),new appCommonActions.InitUserSettingsRoot()]; | ||||||
|     } |     } | ||||||
|     )); |     )); | ||||||
|  |  | ||||||
| @@ -100,8 +140,11 @@ export class AppCommonEffects { | |||||||
|       withLatestFrom(this.store$.select(appCommonReducers.selectGetItemTypes)), |       withLatestFrom(this.store$.select(appCommonReducers.selectGetItemTypes)), | ||||||
|       switchMap(([action, itemtypes]) => { |       switchMap(([action, itemtypes]) => { | ||||||
|         var a = action as appCommonActions.EditItem; |         var a = action as appCommonActions.EditItem; | ||||||
|  |         var editor =  "property"; | ||||||
|  |         if(a.item.itemType) { | ||||||
|           var itemType = itemtypes[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]) |       this.router$.navigate(['/editor',editor,'item', a.item.code]) | ||||||
|       return []; |       return []; | ||||||
|     } |     } | ||||||
| @@ -150,6 +193,23 @@ export class AppCommonEffects { | |||||||
|       return null; |       return null; | ||||||
|     })); |     })); | ||||||
|  |  | ||||||
|  |     @Effect({ dispatch: false }) | ||||||
|  |     online$: Observable<Action> = this.actions$.pipe( | ||||||
|  |       ofType(appCommonActions.ONLINE), | ||||||
|  |       switchMap((action) => { | ||||||
|  |           console.debug("Online: Check token"); | ||||||
|  |           if(!this.oauthService$.hasValidAccessToken()) { | ||||||
|  |             console.debug("No valid token, try to refresh"); | ||||||
|  |             if(this.oauthService$.getRefreshToken() != null ) { | ||||||
|  |               console.debug("We have a refresh token"); | ||||||
|  |               this.oauthService$.refreshToken();               | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           return of(undefined); | ||||||
|  |       })); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private oauthService$: OAuthService, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService) { |   constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private oauthService$: OAuthService, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService) { | ||||||
|     store$.dispatch(new appCommonActions.LoadItemTypes()); |     store$.dispatch(new appCommonActions.LoadItemTypes()); | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										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; |     validTo?: Date; | ||||||
|     created?: Date; |     created?: Date; | ||||||
|     updated?: Date; |     updated?: Date; | ||||||
|     data?:any |     data?:any; | ||||||
|  |     readWrite?: boolean; | ||||||
| } | } | ||||||
|  |  | ||||||
| export class CodeListItem implements ICodeListItem { | export class CodeListItem implements ICodeListItem { | ||||||
| @@ -22,6 +23,7 @@ export class CodeListItem implements ICodeListItem { | |||||||
|   public created?: Date; |   public created?: Date; | ||||||
|   public updated?: Date; |   public updated?: Date; | ||||||
|   public data?:any; |   public data?:any; | ||||||
|  |   public readWrite?: boolean; | ||||||
|  |  | ||||||
|   constructor() {   |   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 geometry?:any; | ||||||
|   public url?: string; |   public url?: string; | ||||||
|   public name?: string; |   public name?: string; | ||||||
|   public created?: Date; |   public created?: string; | ||||||
|   public updated?: Date; |   public updated?: string; | ||||||
|   public dataDate?: Date; |   public dataDate?: string; | ||||||
|  |   public dataEndDate?: string; | ||||||
|   public itemType?: string; |   public itemType?: string; | ||||||
|   public sourceTask?: string; |   public sourceTask?: string; | ||||||
|   public size?: number; |   public size?: number; | ||||||
|   | |||||||
| @@ -4,5 +4,6 @@ export interface IItemType { | |||||||
|   editor?: string; |   editor?: string; | ||||||
|   isFolder?: boolean; |   isFolder?: boolean; | ||||||
|   iconColor?: string; |   iconColor?: string; | ||||||
|  |   schema?: string; | ||||||
|   extraAttributes?: string; |   extraAttributes?: string; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										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; |     url?: string; | ||||||
|     code?: string; |     code?: string; | ||||||
|     name?: string; |     name?: string; | ||||||
|     created?: Date; |     created?: string; | ||||||
|     updated?: Date; |     updated?: string; | ||||||
|     dataDate?: Date; |     dataDate?: string; | ||||||
|  |     dataEndDate?: string; | ||||||
|     itemType?: string; |     itemType?: string; | ||||||
|     sourceTask?: string; |     sourceTask?: string; | ||||||
|     size?: number; |     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; |   code?: string; | ||||||
|   name?: string; |   name?: string; | ||||||
|   email?: 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 { createSelector, createFeatureSelector, ActionReducerMap } from '@ngrx/store'; | ||||||
|  |  | ||||||
| import { MODULE_NAME } from '../module-name'; | import { MODULE_NAME } from '../module-name'; | ||||||
|  | import { IItem } from '../models/item'; | ||||||
|  |  | ||||||
| export interface State { | export interface State { | ||||||
|   openedModalName: string, |   openedModalName: string, | ||||||
| @@ -17,7 +18,11 @@ export interface State { | |||||||
|   fullScreen: boolean, |   fullScreen: boolean, | ||||||
|   routeLoading:boolean, |   routeLoading:boolean, | ||||||
|   menuVisible: boolean, |   menuVisible: boolean, | ||||||
|   userPackages: IPackages |   userPackages: IPackages, | ||||||
|  |   userSettingsRoot: IItem, | ||||||
|  |   accountMenuVisible: boolean, | ||||||
|  |   isOnline: boolean, | ||||||
|  |   isPageMode:boolean | ||||||
| } | } | ||||||
|  |  | ||||||
| export const initialState: State = { | export const initialState: State = { | ||||||
| @@ -29,14 +34,31 @@ export const initialState: State = { | |||||||
|   fullScreen: true, |   fullScreen: true, | ||||||
|   routeLoading: false, |   routeLoading: false, | ||||||
|   menuVisible: false, |   menuVisible: false, | ||||||
|   userPackages: {} |   userPackages: {}, | ||||||
|  |   userSettingsRoot: null, | ||||||
|  |   accountMenuVisible: false, | ||||||
|  |   isOnline: true, | ||||||
|  |   isPageMode: true | ||||||
| } | } | ||||||
|  |  | ||||||
| export function reducer(state = initialState, action: appCommonActions.Actions ): State { | export function reducer(state = initialState, action: appCommonActions.Actions ): State { | ||||||
|   switch (action.type) { |   switch (action.type) { | ||||||
|  |     case appCommonActions.INITUSER: { | ||||||
|  |       return tassign(state,{initialized: true}); | ||||||
|  |     } | ||||||
|     case appCommonActions.INITUSERSUCCESS: { |     case appCommonActions.INITUSERSUCCESS: { | ||||||
|       let a = action as appCommonActions.InitUserSuccess; |       let a = action as appCommonActions.InitUserSuccess; | ||||||
|       return tassign(state, { user: a.user,initialized: true }); |       let claims = {} | ||||||
|  |       Object.getOwnPropertyNames(a.userinfo).forEach((k) => { | ||||||
|  |          claims[k] = a.userinfo[k]; | ||||||
|  |       }); | ||||||
|  |       var user:IUser = { | ||||||
|  |         code:a.user.code, | ||||||
|  |         email:a.userinfo["email"], | ||||||
|  |         name:a.userinfo["name"], | ||||||
|  |         claims:claims | ||||||
|  |       };      | ||||||
|  |       return tassign(state, { user: user }); | ||||||
|     } |     } | ||||||
|     case appCommonActions.INITROOTSUCCESS: { |     case appCommonActions.INITROOTSUCCESS: { | ||||||
|       let a = action as appCommonActions.InitRootSuccess; |       let a = action as appCommonActions.InitRootSuccess; | ||||||
| @@ -73,14 +95,17 @@ export function reducer(state = initialState, action: appCommonActions.Actions ) | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|     case appCommonActions.TOGGLEMENU: { |     case appCommonActions.TOGGLEMENU: { | ||||||
|       return tassign(state, { menuVisible: !state.menuVisible }); |       return tassign(state, { menuVisible: !state.menuVisible,accountMenuVisible:!state.menuVisible?false:state.accountMenuVisible }); | ||||||
|  |     } | ||||||
|  |     case appCommonActions.TOGGLEACCOUNTMENU: { | ||||||
|  |       return tassign(state, { accountMenuVisible: !state.accountMenuVisible }); | ||||||
|     } |     } | ||||||
|     case appCommonActions.ESCAPE: { |     case appCommonActions.ESCAPE: { | ||||||
|       return tassign(state, { menuVisible: false }); |       return tassign(state, { menuVisible: false,accountMenuVisible:false }); | ||||||
|     } |     } | ||||||
|     case appCommonActions.SETMENUVISIBLE: { |     case appCommonActions.SETMENUVISIBLE: { | ||||||
|       let a = action as appCommonActions.SetMenuVisible; |       let a = action as appCommonActions.SetMenuVisible; | ||||||
|       return tassign(state, { menuVisible: a.visible }); |       return tassign(state, { menuVisible: a.visible,accountMenuVisible:a.visible?false:state.accountMenuVisible }); | ||||||
|     } |     } | ||||||
|     case appCommonActions.INITUSERPACKAGESSUCCESS:{ |     case appCommonActions.INITUSERPACKAGESSUCCESS:{ | ||||||
|       let a = action as appCommonActions.InitUserPackagesSuccess; |       let a = action as appCommonActions.InitUserPackagesSuccess; | ||||||
| @@ -91,6 +116,26 @@ export function reducer(state = initialState, action: appCommonActions.Actions ) | |||||||
|  |  | ||||||
|       return tassign(state,{userPackages:packages}); |       return tassign(state,{userPackages:packages}); | ||||||
|     } |     } | ||||||
|  |     case appCommonActions.INITUSERSETTINGSROOTSUCCESS:{ | ||||||
|  |       let a = action as appCommonActions.InitUserSettingsRootSuccess; | ||||||
|  |       return tassign(state, { userSettingsRoot : a.item }); | ||||||
|  |     } | ||||||
|  |     case appCommonActions.LOGOUT:{ | ||||||
|  |       return tassign(state,{user:null,initialized:false}); | ||||||
|  |     } | ||||||
|  |     case appCommonActions.CLOSEALL: { | ||||||
|  |       return tassign(state,{accountMenuVisible:false,menuVisible:false }); | ||||||
|  |     } | ||||||
|  |     case appCommonActions.ONLINE:{ | ||||||
|  |       return tassign(state,{isOnline:true}); | ||||||
|  |     } | ||||||
|  |     case appCommonActions.OFFLINE:{ | ||||||
|  |       return tassign(state,{isOnline:false}); | ||||||
|  |     } | ||||||
|  |     case appCommonActions.SETPAGEMODE: { | ||||||
|  |       let a = action as appCommonActions.SetPageMode; | ||||||
|  |       return tassign(state,{isPageMode:a.pageMode}); | ||||||
|  |     } | ||||||
|     default: { |     default: { | ||||||
|       return state; |       return state; | ||||||
|     } |     } | ||||||
| @@ -106,6 +151,10 @@ export const getRouteLoading = (state: State) => state.routeLoading; | |||||||
| export const getMenuVisible = (state: State) => state.menuVisible; | export const getMenuVisible = (state: State) => state.menuVisible; | ||||||
| export const getUser = (state: State) => state.user; | export const getUser = (state: State) => state.user; | ||||||
| export const getUserPackages = (state: State) => state.userPackages; | export const getUserPackages = (state: State) => state.userPackages; | ||||||
|  | export const getUserSettingsRoot = (state: State) => state.userSettingsRoot; | ||||||
|  | export const getAccountMenuVisible = (state: State) => state.accountMenuVisible; | ||||||
|  | export const getIsOnline = (state: State) => state.isOnline; | ||||||
|  | export const getIsPageMode = (state: State) => state.isPageMode; | ||||||
|  |  | ||||||
| export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME); | 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 SelectGetMenuVisible = createSelector(selectAppCommonState,getMenuVisible); | ||||||
| export const SelectGetUser = createSelector(selectAppCommonState,getUser); | export const SelectGetUser = createSelector(selectAppCommonState,getUser); | ||||||
| export const SelectGetUserPackages = createSelector(selectAppCommonState,getUserPackages); | export const SelectGetUserPackages = createSelector(selectAppCommonState,getUserPackages); | ||||||
|  | export const SelectGetUserSettingsRoot = createSelector(selectAppCommonState,getUserSettingsRoot); | ||||||
|  | export const SelectGetAccountMenuVisible = createSelector(selectAppCommonState,getAccountMenuVisible); | ||||||
|  | export const SelectGetIsOnline = createSelector(selectAppCommonState,getIsOnline); | ||||||
|  | export const SelectGetIsPageMode = createSelector(selectAppCommonState,getIsPageMode); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -41,24 +41,9 @@ export class AuthGuard implements CanActivate, CanLoad, CanActivateChild  { | |||||||
|     return new Promise<boolean>((resolve) => { |     return new Promise<boolean>((resolve) => { | ||||||
|       if (!this.oauthService.hasValidAccessToken()) { |       if (!this.oauthService.hasValidAccessToken()) { | ||||||
|         console.debug("No valid token"); |         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); |         this.oauthService.initCodeFlow(url); | ||||||
|         resolve(false); |         resolve(false); | ||||||
|           }) |  | ||||||
|       } else {         |       } 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); |         resolve(true); | ||||||
|       } |       } | ||||||
|     }); |     }); | ||||||
|   | |||||||
| @@ -22,4 +22,20 @@ export class CodeListItemService { | |||||||
|     } |     } | ||||||
|     return this.httpClient.get<ICodeListItem[]>(`${this.ApiEndpoint()}/api/v1/codelistitems/`, { params: params }); |     return this.httpClient.get<ICodeListItem[]>(`${this.ApiEndpoint()}/api/v1/codelistitems/`, { params: params }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   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() { |   private Authenticate() { | ||||||
|     var accessToken = this.oauthService.getAccessToken();    |     var accessToken = this.oauthService.getAccessToken();    | ||||||
|     if (accessToken) { |     if (this.oauthService.hasValidAccessToken()) { | ||||||
|       this._connection.send('authenticate', this.oauthService.getAccessToken()); |       this._connection.send('authenticate', this.oauthService.getAccessToken()); | ||||||
|       this.authenticated=true; |       this.authenticated=true; | ||||||
|     } else { |     } else { | ||||||
|   | |||||||
| @@ -18,39 +18,32 @@ export class FolderService { | |||||||
|     return this.appConfig.getConfig("apiEndPoint"); |     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> { |      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[]> { |     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[]> { |     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[]> { |     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[]> { |     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> { |     moveItem(itemCode: string, newParentCode: string): Observable<IListItem> { | ||||||
|         const body = { itemCode: itemCode,newParentCode: newParentCode }; |         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> { |     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