Compare commits
195 Commits
feature/Mi
...
03284d26dd
Author | SHA1 | Date | |
---|---|---|---|
|
03284d26dd | ||
946cbc6533 | |||
|
6277236993 | ||
|
20bda6b81f | ||
|
9a338c04e6 | ||
65c1643c95 | |||
|
9cbc9f1293 | ||
|
34b75a4c18 | ||
|
1991e79ed2 | ||
|
d661e5f3f9 | ||
|
0a384a9196 | ||
|
0e13b98b7f | ||
|
56a7544c1e | ||
|
61636b6e3d | ||
|
ab835e904e | ||
|
cd10165510 | ||
|
446d26607c | ||
|
8d204ce1cd | ||
|
2656a67fca | ||
|
46c5f74b49 | ||
|
a5ece9b453 | ||
|
9fb5a9698c | ||
fad2f19ae4 | |||
2ad04fc7c1 | |||
f656ecd23f | |||
d42aad87bd | |||
415ee8ef30 | |||
|
306065dcbd | ||
824ba97704 | |||
ac8ab147bc | |||
|
640c99cde2 | ||
|
da2492e017 | ||
|
f93d235877 | ||
|
fe798e3f60 | ||
|
b8dcc4e9e0 | ||
|
4b6175b6c4 | ||
|
388a2faf84 | ||
|
0b0bb4d598 | ||
|
7696ddd8d2 | ||
|
1301a9d082 | ||
|
e87affee08 | ||
|
b00b21e6b8 | ||
|
835851350c | ||
|
7bdb48b644 | ||
|
84d6f44e9b | ||
|
2fd109d967 | ||
|
f9934ccf27 | ||
|
b93218e165 | ||
|
8e55308df9 | ||
|
5270739641 | ||
|
ce8e8fa706 | ||
|
d326a82c4e | ||
|
c1683a058c | ||
|
aa168bb3a1 | ||
|
fe53955bea | ||
|
ba3dad1365 | ||
|
6d2d259c01 | ||
|
92774647ff | ||
|
9ca047923f | ||
|
c893a2820b | ||
|
baa0166fc8 | ||
|
58c200aca0 | ||
|
b8a4b3541d | ||
|
584a1514ac | ||
|
21af6ab2c4 | ||
|
c606e02673 | ||
|
ff4eb296ff | ||
|
ea26cdd628 | ||
|
26e28bdda8 | ||
|
15d1f14ee1 | ||
|
22e007b70b | ||
|
9e4a538a07 | ||
|
c44c771feb | ||
|
394bbab320 | ||
|
3008081ae8 | ||
|
7474c00498 | ||
|
964bf925e7 | ||
|
fb8129148d | ||
|
73db03148a | ||
|
6367d3399d | ||
|
ee4e7113d2 | ||
|
ad86e41345 | ||
|
e682b375e7 | ||
|
78425a621a | ||
1030b1c41a | |||
76bdad6eaf | |||
ce56dd9e9f | |||
e7a53ba7b5 | |||
9f1f6934ee | |||
773bf3f5e7 | |||
cdc900d5fd | |||
80522a3a2f | |||
0157812357 | |||
329564d8f6 | |||
ba2a067c8b | |||
4d67a656f5 | |||
ad1d40cdd1 | |||
131e0eb371 | |||
e563011099 | |||
7c072752c8 | |||
36ced002e0 | |||
3e1b892eef | |||
f4004cf2be | |||
d1d8723186 | |||
a6fd052a4f | |||
cede944538 | |||
3b3c2768d3 | |||
3dd19c702f | |||
c028adfb77 | |||
1d2caa0fe3 | |||
|
d7ca1512bd | ||
|
ebe435027f | ||
|
dfefa6746f | ||
|
c90df5c972 | ||
514a91c2c7 | |||
|
ee7d1cb9a2 | ||
309b320534 | |||
fdfa7d3387 | |||
|
6785bfc3e0 | ||
|
c682fb9184 | ||
|
ca6ea96eae | ||
|
3ebc197fac | ||
52871efb09 | |||
f014dc9f93 | |||
e6cda406c9 | |||
df04397b13 | |||
|
e0b29c905c | ||
|
44ae806b30 | ||
|
78e3cf74b9 | ||
|
428353d379 | ||
9697e93915 | |||
|
e057e463bd | ||
|
d494a7c7d6 | ||
|
e525227865 | ||
|
10bdd27608 | ||
|
2790ae74e7 | ||
|
31d01c75c5 | ||
|
0da8d3d46d | ||
|
60761e9f4a | ||
|
ec9684b826 | ||
|
1073044f31 | ||
|
81126974f5 | ||
06f443a503 | |||
eb560aaf33 | |||
|
464b82bfb9 | ||
|
1c75fb6f71 | ||
|
5f5d8a9712 | ||
|
dc329a0e29 | ||
|
15a09e4b05 | ||
94f973406e | |||
6404e45d66 | |||
c0f689331e | |||
0f5a5251db | |||
3352bfd805 | |||
|
60ed2afaa4 | ||
|
9ac534c171 | ||
|
cecbdb2ded | ||
|
1092d5dcaf | ||
|
4af6b2937c | ||
263fb6c3d4 | |||
|
09fa8b4f1f | ||
|
214dc3ffa1 | ||
|
fad04744cf | ||
|
033b74517f | ||
|
0b7564e874 | ||
|
0d247e6416 | ||
|
b92cc7bc09 | ||
|
eba18b0235 | ||
|
69f8184dfe | ||
|
31c1570ffb | ||
|
fe2650c4fd | ||
|
f89209c555 | ||
|
b3d0a6f0c7 | ||
|
6bd1726fc9 | ||
|
b0ff54d157 | ||
|
277dc7051e | ||
|
ace4a6b364 | ||
|
146514d386 | ||
|
1187ee9fd6 | ||
4527276254 | |||
|
6f52302875 | ||
|
cbe27c2add | ||
|
cd156ab1bc | ||
|
f9d0e2aee0 | ||
|
253b3d3c90 | ||
|
28d4adc571 | ||
|
925af1e645 | ||
|
4130e0a796 | ||
|
01933b1602 | ||
0482aa7124 | |||
75015f6d22 | |||
aa3707aa56 | |||
|
ff06b419f2 | ||
|
c1c0bd2596 | ||
|
978cbdabfc |
1
.npmrc
1
.npmrc
@@ -1 +0,0 @@
|
||||
@FarmMaps:registry=https://repository.akkerweb.nl/repository/npm-group/
|
@@ -7,18 +7,22 @@ pipeline {
|
||||
stages {
|
||||
stage('npm install'){
|
||||
steps {
|
||||
sh '''npm install
|
||||
sh '''rm -rf node_modules/
|
||||
npm install
|
||||
cd projects/common
|
||||
npm install
|
||||
cd ../common-map
|
||||
npm install
|
||||
cd ../common-map3d
|
||||
npm install
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('build'){
|
||||
steps {
|
||||
sh '''ng build common
|
||||
ng build common-map'''
|
||||
ng build common-map
|
||||
ng build common-map3d'''
|
||||
}
|
||||
}
|
||||
stage('npm publish'){
|
||||
@@ -28,6 +32,9 @@ pipeline {
|
||||
npm publish
|
||||
cd ../common-map
|
||||
npm version ${PACKAGE_VERSION}
|
||||
npm publish
|
||||
cd ../common-map3d
|
||||
npm version ${PACKAGE_VERSION}
|
||||
npm publish'''
|
||||
}
|
||||
}
|
||||
|
71
angular.json
71
angular.json
@@ -11,8 +11,11 @@
|
||||
"schematics": {},
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"builder": "@angular-builders/custom-webpack:browser",
|
||||
"options": {
|
||||
"customWebpackConfig": {
|
||||
"path": "./custom-webpack.config.js"
|
||||
},
|
||||
"aot": true,
|
||||
"outputPath": "dist/farmmaps-lib-app",
|
||||
"index": "src/index.html",
|
||||
@@ -22,18 +25,23 @@
|
||||
"assets": [
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input":"src/assets/images",
|
||||
"output":"/images"
|
||||
"input": "src/assets/images",
|
||||
"output": "/images"
|
||||
},
|
||||
{
|
||||
"glob": "silent-refresh.html",
|
||||
"input":"src/assets",
|
||||
"output":"/"
|
||||
"input": "src/assets",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"glob": "favicon.ico",
|
||||
"input":"src/assets",
|
||||
"output":"/"
|
||||
"input": "src/assets",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "node_modules/cesium/Build/Cesium",
|
||||
"output": "/assets/cesium"
|
||||
},
|
||||
"src/configuration.json"
|
||||
],
|
||||
@@ -41,10 +49,11 @@
|
||||
"src/styles.css",
|
||||
"node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"node_modules/font-awesome/css/font-awesome.min.css",
|
||||
"fonts/FMIconFont/style.css"
|
||||
"fonts/FMIconFont/style.css",
|
||||
"node_modules/cesium/Build/Cesium/Widgets/widgets.css",
|
||||
"node_modules/ol/ol.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"es5BrowserSupport": true
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
@@ -78,7 +87,7 @@
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"builder": "@angular-builders/custom-webpack:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "farmmaps-lib-app:build"
|
||||
},
|
||||
@@ -232,6 +241,46 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"common-map3d": {
|
||||
"projectType": "library",
|
||||
"root": "projects/common-map3d",
|
||||
"sourceRoot": "projects/common-map3d/src",
|
||||
"prefix": "fm-map3d",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr:build",
|
||||
"options": {
|
||||
"tsConfig": "projects/common-map3d/tsconfig.lib.json",
|
||||
"project": "projects/common-map3d/ng-package.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"tsConfig": "projects/common-map3d/tsconfig.lib.prod.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "projects/common-map3d/src/test.ts",
|
||||
"tsConfig": "projects/common-map3d/tsconfig.spec.json",
|
||||
"karmaConfig": "projects/common-map3d/karma.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"projects/common-map3d/tsconfig.lib.json",
|
||||
"projects/common-map3d/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "farmmaps-lib-app"
|
||||
|
10
custom-webpack.config.js
Normal file
10
custom-webpack.config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
node: {
|
||||
// Resolve node module use of fs
|
||||
fs: "empty",
|
||||
Buffer: false,
|
||||
http: "empty",
|
||||
https: "empty",
|
||||
zlib: "empty"
|
||||
}
|
||||
};
|
13074
package-lock.json
generated
13074
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
78
package.json
78
package.json
@@ -11,56 +11,62 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~9.1.0",
|
||||
"@angular/common": "~9.1.0",
|
||||
"@angular/compiler": "~9.1.0",
|
||||
"@angular/core": "~9.1.0",
|
||||
"@angular/forms": "~9.1.0",
|
||||
"@angular/platform-browser": "~9.1.0",
|
||||
"@angular/platform-browser-dynamic": "~9.1.0",
|
||||
"@angular/router": "~9.1.0",
|
||||
"@angular/animations": "~10.1.3",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/compiler": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@angular/platform-browser": "~10.1.3",
|
||||
"@angular/platform-browser-dynamic": "~10.1.3",
|
||||
"@angular/router": "~10.1.3",
|
||||
"@farmmaps/common": ">=0.0.1-prerelease.454 <0.0.1",
|
||||
"@farmmaps/common-map": ">=0.0.1-prerelease.454 <0.0.1",
|
||||
"@farmmaps/common-map3d": ">=0.0.1-prerelease.454 <0.0.1",
|
||||
"@microsoft/signalr": "^3.1.3",
|
||||
"@farmmaps/common": ">=0.0.1-prerelease.267 <0.0.1",
|
||||
"@farmmaps/common-map": ">=0.0.1-prerelease.267 <0.0.1",
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.0",
|
||||
"@ngrx/effects": "^9.0",
|
||||
"@ngrx/router-store": "^9.0",
|
||||
"@ngrx/store": "^9.0",
|
||||
"ngx-uploadx": "^3.3.4",
|
||||
"angular-oauth2-oidc": "^9.1",
|
||||
"@ng-bootstrap/ng-bootstrap": "^7.0",
|
||||
"@ngrx/effects": "^10.0",
|
||||
"@ngrx/router-store": "^10.0",
|
||||
"@ngrx/store": "^10.0",
|
||||
"angular-oauth2-oidc": "^10.0.3",
|
||||
"bootstrap": "^4.4.1",
|
||||
"cesium": "^1.73.0",
|
||||
"core-js": "^2.6.11",
|
||||
"font-awesome": "^4.7.0",
|
||||
"ngrx-store-localstorage": "^9.0",
|
||||
"ngrx-store-localstorage": "^10.0",
|
||||
"ngx-bootstrap": "^5.6.1",
|
||||
"ngx-openlayers": "1.0.0-next.16",
|
||||
"ngx-uploadx": "^3.5.1",
|
||||
"ol": "6.4.3",
|
||||
"ol-cesium": "^2.11.3",
|
||||
"resumablejs": "^1.1.0",
|
||||
"rxjs": "^6.5.4",
|
||||
"tassign": "^1.0.0",
|
||||
"tslib": "^1.10.0",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.2",
|
||||
"ngx-openlayers": "1.0.0-next.13",
|
||||
"ol": "6.1.1"
|
||||
"moment": "^2.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.901.0",
|
||||
"@angular-devkit/build-ng-packagr": "~0.901.0",
|
||||
"@angular/cli": "^9.1.0",
|
||||
"@angular/compiler-cli": "~9.1.0",
|
||||
"@angular/language-service": "~9.1.0",
|
||||
"@angular-builders/custom-webpack": "~10.0.1",
|
||||
"@angular-devkit/build-angular": "^0.1002.0",
|
||||
"@angular-devkit/build-ng-packagr": "~0.1001.3",
|
||||
"@angular/cli": "^10.1.3",
|
||||
"@angular/compiler-cli": "~10.1.3",
|
||||
"@angular/language-service": "~10.1.3",
|
||||
"@types/jasmine": "~2.8.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"@types/node": "^12.11.1",
|
||||
"codelyzer": "^5.1.2",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "^5.0.2",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~1.1.2",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"ng-packagr": "^9.0.0",
|
||||
"protractor": "^5.4.4",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"ng-packagr": "^10.1.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "^8.8.1",
|
||||
"tslint": "~5.11.0",
|
||||
"typescript": "~3.8.3"
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~4.0.3"
|
||||
}
|
||||
}
|
||||
|
75
projects/common-map/package-lock.json
generated
75
projects/common-map/package-lock.json
generated
@@ -4,79 +4,10 @@
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@openlayers/pepjs": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@openlayers/pepjs/-/pepjs-0.5.3.tgz",
|
||||
"integrity": "sha512-Bgvi5c14BS0FJWyYWWFstNEnXsB30nK8Jt8hkAAdqr7E0gDdBBWVDglF3Ub19wTxvgJ/CVHyTY6VuCtnyRzglg=="
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"ngx-openlayers": {
|
||||
"version": "1.0.0-next.13",
|
||||
"resolved": "https://registry.npmjs.org/ngx-openlayers/-/ngx-openlayers-1.0.0-next.13.tgz",
|
||||
"integrity": "sha512-6y724s5JV6n1oxEhryxP3wcbwzpierj9RLiMLXInfKOG3l5IO1AtPNkeK9+mKctVDUVm5URIfzzZH02Ld+bvSQ==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"ol": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ol/-/ol-6.1.1.tgz",
|
||||
"integrity": "sha512-0dL3i3eJqgOpqIjDKEY3grkeQnjAYfV5L/JCxhOu4SxiaizRwFrFgeas6LILRoxKa03jhQFbut2r2bbgcLGQeA==",
|
||||
"requires": {
|
||||
"@openlayers/pepjs": "^0.5.3",
|
||||
"pbf": "3.2.1",
|
||||
"pixelworks": "1.1.0",
|
||||
"rbush": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"pbf": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
|
||||
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
|
||||
"requires": {
|
||||
"ieee754": "^1.1.12",
|
||||
"resolve-protobuf-schema": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"pixelworks": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pixelworks/-/pixelworks-1.1.0.tgz",
|
||||
"integrity": "sha1-Hwla1I3Ki/ihyCWOAJIDGkTyLKU="
|
||||
},
|
||||
"protocol-buffers-schema": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz",
|
||||
"integrity": "sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA=="
|
||||
},
|
||||
"quickselect": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
|
||||
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
|
||||
},
|
||||
"rbush": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz",
|
||||
"integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==",
|
||||
"requires": {
|
||||
"quickselect": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"resolve-protobuf-schema": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
|
||||
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
|
||||
"requires": {
|
||||
"protocol-buffers-schema": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
|
||||
"integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,15 +4,18 @@
|
||||
"publishConfig": {
|
||||
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "^9.1.0",
|
||||
"ngrx-store-localstorage": "^9.0",
|
||||
"@ngrx/effects": "^9.0",
|
||||
"@ngrx/router-store": "^9.0",
|
||||
"@ngrx/store": "^9.0",
|
||||
"@angular/core": "^10.1.3",
|
||||
"ngrx-store-localstorage": "^10.0",
|
||||
"@ngrx/effects": "^10.0",
|
||||
"@ngrx/router-store": "^10.0",
|
||||
"@ngrx/store": "^10.0",
|
||||
"tassign": "^1.0.0",
|
||||
"@farmmaps/common": ">=0.0.1-prerelease.265 <0.0.1",
|
||||
"ngx-openlayers": "1.0.0-next.13",
|
||||
"@farmmaps/common": ">=0.0.1-prerelease.420 <0.0.1",
|
||||
"ngx-openlayers": "1.0.0-next.16",
|
||||
"ol": "6.1.1"
|
||||
}
|
||||
}
|
||||
|
@@ -87,7 +87,7 @@ export class StartSearch implements Action {
|
||||
export class StartSearchSuccess implements Action {
|
||||
readonly type = STARTSEARCHSUCCESS;
|
||||
|
||||
constructor(public features: Array<Feature>, public query:IQueryState,public setStateCount:number) { }
|
||||
constructor(public features: Array<Feature>, public query:IQueryState) { }
|
||||
}
|
||||
|
||||
export class SelectFeature implements Action {
|
||||
@@ -105,7 +105,7 @@ export class SelectItem implements Action {
|
||||
export class SelectItemSuccess implements Action {
|
||||
readonly type = SELECTITEMSUCCESS;
|
||||
|
||||
constructor(public item: IItem) { }
|
||||
constructor(public item: IItem, public parentItem: IItem) { }
|
||||
}
|
||||
|
||||
export class SelectTemporalItemsSuccess implements Action {
|
||||
|
@@ -5,21 +5,17 @@ import { AuthGuard } from '@farmmaps/common';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '', children: [
|
||||
{
|
||||
path: '',
|
||||
component: MapComponent
|
||||
}
|
||||
]
|
||||
path: '',
|
||||
component: MapComponent
|
||||
},
|
||||
{
|
||||
path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer/:queryState', children: [
|
||||
{
|
||||
path: '',
|
||||
component: MapComponent
|
||||
}
|
||||
]
|
||||
}
|
||||
path: ':xCenter/:yCenter/:zoom/:rotation/:baseLayer/:queryState',
|
||||
component: MapComponent
|
||||
},
|
||||
{
|
||||
path: ':queryState',
|
||||
component: MapComponent
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@@ -24,7 +24,6 @@ import { IPeriodState } from './models/period.state';
|
||||
|
||||
// components
|
||||
import { GpsLocation} from './components/aol/gps-location/gps-location.component';
|
||||
//import {Switch2D3DComponent } from './components/aol/switch2d3d/switch2d3d.component';
|
||||
import {FeatureListFeatureCropfieldComponent } from './components/feature-list-feature-cropfield/feature-list-feature-cropfield.component';
|
||||
import { FeatureListFeatureCroppingschemeComponent} from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.component';
|
||||
import { ItemWidgetListComponent} from './components/item-widget-list/item-widget-list.component';
|
||||
@@ -64,8 +63,11 @@ import { WidgetStatusComponent } from './components/widget-status/widget-status.
|
||||
import { ForChild} from './components/for-item/for-child.decorator';
|
||||
import {ForItemType } from './components/for-item/for-itemtype.decorator';
|
||||
import { ForSourceTask} from './components/for-item/for-sourcetask.decorator';
|
||||
import { ForPackage } from './components/for-item/for-package.decorator';
|
||||
import { PanToLocation} from './components/aol/pan-to-location/pan-to-location.component';
|
||||
import {LayerSwitcher} from './components/layer-switcher/layer-switcher.component';
|
||||
import {HistogramDetailsComponent} from './components/legend/histogram-details/histogram-details.component';
|
||||
import {StatisticsDetailsComponent} from './components/legend/statistics-details/statistics-details.component';
|
||||
|
||||
export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any> {
|
||||
const r = function(state, action) {
|
||||
@@ -143,10 +145,11 @@ export {
|
||||
IPeriodState,
|
||||
ForChild,
|
||||
ForItemType,
|
||||
ForSourceTask
|
||||
ForSourceTask,
|
||||
ForPackage
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
AngularOpenlayersModule,
|
||||
@@ -193,7 +196,9 @@ export {
|
||||
WidgetStatusComponent,
|
||||
GpsLocation,
|
||||
PanToLocation,
|
||||
LayerSwitcher
|
||||
LayerSwitcher,
|
||||
HistogramDetailsComponent,
|
||||
StatisticsDetailsComponent
|
||||
],
|
||||
entryComponents: [
|
||||
FeatureListComponent,
|
||||
@@ -246,31 +251,26 @@ export {
|
||||
FeatureListCropfieldComponent,
|
||||
FeatureListFeatureContainerComponent,
|
||||
ZoomToExtentComponent
|
||||
],
|
||||
providers: [
|
||||
FeatureIconService,
|
||||
GeolocationService,
|
||||
DeviceOrientationService,
|
||||
{ provide: AbstractFeatureListComponent, useClass: FeatureListCroppingschemeComponent, multi: true },
|
||||
{ provide: AbstractFeatureListComponent, useClass: FeatureListCropfieldComponent, multi: true },
|
||||
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureComponent, multi: true },
|
||||
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCroppingschemeComponent, multi: true },
|
||||
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCropfieldComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemCropfieldComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemGeotiffComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemTemporalComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemShapeComponent, multi: true },
|
||||
{ provide: AbstractItemListItemComponent, useClass: ItemListItemComponent, multi: true },
|
||||
{ provide: AbstractItemListComponent, useClass: ItemListComponent, multi: true }
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
export class AppCommonMapModule {
|
||||
static forRoot(): ModuleWithProviders<AppCommonMapModule> {
|
||||
return {
|
||||
ngModule: AppCommonMapModule,
|
||||
providers: [
|
||||
FeatureIconService,
|
||||
GeolocationService,
|
||||
DeviceOrientationService,
|
||||
{ provide: AbstractFeatureListComponent, useClass: FeatureListCroppingschemeComponent, multi: true },
|
||||
{ provide: AbstractFeatureListComponent, useClass: FeatureListCropfieldComponent, multi: true },
|
||||
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureComponent, multi: true },
|
||||
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCroppingschemeComponent, multi: true },
|
||||
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCropfieldComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemCropfieldComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemGeotiffComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemTemporalComponent, multi: true },
|
||||
{ provide: AbstractSelectedItemComponent, useClass: SelectedItemShapeComponent, multi: true },
|
||||
{ provide: AbstractItemListItemComponent, useClass: ItemListItemComponent, multi: true },
|
||||
{ provide: AbstractItemListComponent, useClass: ItemListComponent, multi: true }
|
||||
]
|
||||
};
|
||||
}
|
||||
export class AppCommonMapModule {
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, Input, ViewChild, ElementRef, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, OnInit, Input, ViewChild, ElementRef, OnChanges, SimpleChanges ,Host} from '@angular/core';
|
||||
import { MapComponent } from 'ngx-openlayers';
|
||||
import Overlay from 'ol/Overlay';
|
||||
import { fromLonLat, toLonLat } from 'ol/proj';
|
||||
@@ -23,9 +23,10 @@ export class GpsLocation implements OnInit,OnChanges{
|
||||
public path: string = "";
|
||||
public rotate: string = "";
|
||||
private resolution: number = 0;
|
||||
initialized:boolean = false;
|
||||
@ViewChild('location', { static: true }) locationElement: ElementRef;
|
||||
|
||||
constructor(private map: MapComponent) {
|
||||
constructor(@Host() private map: MapComponent) {
|
||||
|
||||
}
|
||||
|
||||
@@ -54,12 +55,13 @@ export class GpsLocation implements OnInit,OnChanges{
|
||||
this.resolution = evt.target.get('resolution');
|
||||
this.recalcLocationTolerance();
|
||||
});
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.position && this.instance) {
|
||||
var p = changes.position.currentValue as Position;
|
||||
if(p) {
|
||||
if(p && this.initialized) {
|
||||
this.instance.setPosition(fromLonLat([p.coords.longitude, p.coords.latitude]));
|
||||
this.locationTolerance = p.coords.accuracy;
|
||||
this.recalcLocationTolerance();
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import { Component, Host, Input, Output, EventEmitter, Optional, QueryList, OnInit, AfterViewInit, OnChanges, SimpleChanges, SkipSelf, forwardRef, Inject, InjectionToken } from '@angular/core';
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { LayerVectorComponent, LayerTileComponent, LayerGroupComponent, MapComponent } from 'ngx-openlayers';
|
||||
import { Component, Host, Input, Output, EventEmitter,OnDestroy, OnInit, OnChanges, SimpleChanges, forwardRef } from '@angular/core';
|
||||
import { LayerGroupComponent, MapComponent } from 'ngx-openlayers';
|
||||
import { ItemService,IItem,AppConfig } from '@farmmaps/common';
|
||||
import { IItemLayer,ItemLayer, ITemporalItemLayer} from '../../../models/item.layer';
|
||||
import { IItemLayer, ITemporalItemLayer} from '../../../models/item.layer';
|
||||
import { ILayerData} from '../../../models/layer.data';
|
||||
import { IRenderoutputTiles,IRenderoutputImage,IGradientstop,ILayer,IHistogram} from '../../../models/color.map';
|
||||
import {Extent} from 'ol/extent';
|
||||
@@ -11,13 +10,12 @@ import * as proj from 'ol/proj';
|
||||
import * as loadingstrategy from 'ol/loadingstrategy';
|
||||
import * as style from 'ol/style';
|
||||
import {Tile,Layer,Image} from 'ol/layer';
|
||||
import {XYZ,ImageStatic,OSM,BingMaps,TileWMS,TileArcGISRest} from 'ol/source';
|
||||
import {XYZ,ImageStatic,OSM,BingMaps,TileWMS,TileArcGISRest,TileJSON} from 'ol/source';
|
||||
import {Vector as VectorSource} from 'ol/source';
|
||||
import { Vector as VectorLayer } from 'ol/layer';
|
||||
import VectorTileSource from 'ol/source/VectorTile';
|
||||
import VectorTileLayer from 'ol/layer/VectorTile';
|
||||
import {GeoJSON,MVT} from 'ol/format';
|
||||
import { from } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'fm-map-item-layers',
|
||||
@@ -27,11 +25,17 @@ import { from } from 'rxjs';
|
||||
]
|
||||
})
|
||||
|
||||
export class ItemLayersComponent extends LayerGroupComponent implements OnChanges, OnInit {
|
||||
export class ItemLayersComponent extends LayerGroupComponent implements OnChanges, OnInit,OnDestroy {
|
||||
@Input() itemLayers: IItemLayer[];
|
||||
@Input() itemLayer: IItemLayer;
|
||||
@Output() onFeatureSelected: EventEmitter<string> = new EventEmitter<string>();
|
||||
@Output() onFeatureHover: EventEmitter<string> = new EventEmitter<string>();
|
||||
private _apiEndPoint: string;
|
||||
|
||||
private initialized:boolean = false;
|
||||
private mapEventHandlerInstalled = false;
|
||||
private selectedFeatures = {};
|
||||
private selectionLayer:Layer = null;
|
||||
|
||||
constructor(private itemService: ItemService, @Host() private map: MapComponent, public appConfig: AppConfig) {
|
||||
super(map);
|
||||
this._apiEndPoint = appConfig.getConfig("apiEndPoint");
|
||||
@@ -48,9 +52,11 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
return "#" + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b) + this.componentToHex(a);
|
||||
}
|
||||
|
||||
getColorFromGradient(layer: ILayer, feature): style.Style {
|
||||
var value = feature.get(layer.name);
|
||||
var gradient: IGradientstop[] = layer.renderer.colorMap.gradient;
|
||||
getColorFromGradient(item:IItem,layer: ILayer, feature): style.Style {
|
||||
var value = layer.indexKey ? feature.get(layer.indexKey): feature.get(layer.name);
|
||||
var key = item.code + "_" + value;
|
||||
if(!this.styleCache[key]) {
|
||||
var gradient: IGradientstop[] = layer.renderer.colorMap.gradient;
|
||||
var histogram: IHistogram = layer.renderer.band.histogram;
|
||||
var index = (value - histogram.min) / histogram.max;
|
||||
var min = gradient[0];
|
||||
@@ -67,7 +73,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
var green = Math.round(min.color.green + ((max.color.green - min.color.green) * i / size));
|
||||
var blue = Math.round(min.color.blue + ((max.color.blue - min.color.blue) * i / size));
|
||||
|
||||
return new style.Style(
|
||||
this.styleCache[key] = new style.Style(
|
||||
{
|
||||
image: new style.Circle({
|
||||
fill: new style.Fill({
|
||||
@@ -79,33 +85,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
color: this.rgbaToHex(red, green, blue, alpha)
|
||||
}),
|
||||
stroke: new style.Stroke({
|
||||
color: this.rgbaToHex(red, green, blue, alpha),
|
||||
color: this.rgbaToHex(red, green, blue, 255),
|
||||
width: 1.25
|
||||
}),
|
||||
});
|
||||
}
|
||||
return this.styleCache[key];
|
||||
}
|
||||
|
||||
createGeotiffLayer(item:IItem,itemLayer:IItemLayer):Layer {
|
||||
var layerIndex = -1;
|
||||
var layer: Layer = null;
|
||||
layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index;
|
||||
let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` });
|
||||
let source = new XYZ({ maxZoom: 19, minZoom: 1, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}` });
|
||||
layer = new Tile({ source: source });
|
||||
var data = item.data;
|
||||
var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null;
|
||||
if (l && l.rendering && l.rendering.renderoutputType == "Tiles") {
|
||||
var rt = l.rendering as IRenderoutputTiles;
|
||||
let source = new XYZ({ maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}` });
|
||||
let source = new XYZ({crossOrigin: 'use-credentials', maxZoom: rt.maxzoom, minZoom: rt.minzoom, url: `${this._apiEndPoint}/api/v1/items/${item.code}/tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}` });
|
||||
layer = new Tile({ source: source });
|
||||
}
|
||||
if (l && l.rendering && l.rendering.renderoutputType == "Image") {
|
||||
var ri = l.rendering as IRenderoutputImage;
|
||||
let projection = new Projection({
|
||||
code: 'image',
|
||||
units: 'pixels',
|
||||
extent: ri.extent
|
||||
});
|
||||
let source = new ImageStatic({ imageExtent: ri.extent, projection: projection, url: `${this._apiEndPoint}/api/v1/items/${item.code}/mapimage/${layerIndex}?v=${item.updated.getTime()}` });
|
||||
var ri = l.rendering as IRenderoutputImage;
|
||||
// convert to EPSG:4326 asworkaround for cesium
|
||||
var projectedExtent = proj.transformExtent( ri.extent, 'EPSG:3857','EPSG:4326');
|
||||
let source = new ImageStatic({ imageExtent:projectedExtent,projection:'EPSG:4326', crossOrigin: 'use-credentials', url: `${this._apiEndPoint}/api/v1/items/${item.code}/mapimage/${layerIndex}?v=${Date.parse(item.updated)}` });
|
||||
layer = new Image({ source: source });
|
||||
}
|
||||
return layer;
|
||||
@@ -116,19 +121,19 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
var layer: Layer = null;
|
||||
layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : item.data.layers[0].index;
|
||||
var data = item.data;
|
||||
var l = (data && data.layers && data.layers.length > 0) ? data.layers[0] : null;
|
||||
var l:ILayer = (data && data.layers && data.layers.length > 0) ? data.layers[layerIndex] : null;
|
||||
if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") {
|
||||
var rt = item.data.layers[layerIndex].rendering as IRenderoutputTiles;
|
||||
var rt = l.rendering as IRenderoutputTiles;
|
||||
layer = new VectorTileLayer({
|
||||
declutter: true,
|
||||
source: new VectorTileSource({
|
||||
maxZoom: rt.maxzoom,
|
||||
minZoom: rt.minzoom,
|
||||
format: new MVT(),
|
||||
url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${item.updated.getTime()}`
|
||||
url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/{z}/{x}/{y}.pbf?v=${Date.parse(item.updated)}`
|
||||
}),
|
||||
style: (feature) => {
|
||||
return this.getColorFromGradient(l, feature);
|
||||
return this.getColorFromGradient(item,l, feature);
|
||||
}
|
||||
})
|
||||
} else if (l && l.rendering && l.rendering.renderoutputType == "Tiles") {
|
||||
@@ -137,7 +142,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
source: new XYZ({
|
||||
maxZoom: rt.maxzoom,
|
||||
minZoom: rt.minzoom,
|
||||
url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${item.updated.getTime()}`
|
||||
url: `${this._apiEndPoint}/api/v1/items/${item.code}/vectortiles/image_tiles/${layerIndex}/{z}/{x}/{y}.png?v=${Date.parse(item.updated)}`
|
||||
})
|
||||
});
|
||||
} else {
|
||||
@@ -161,7 +166,7 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
layer = new VectorLayer({
|
||||
source: source,
|
||||
style: (feature) => {
|
||||
var key = feature.get("color");
|
||||
var key =feature.get("code") + "_" + feature.get("color");
|
||||
if (!this.styleCache[key]) {
|
||||
var color = feature.get("color");
|
||||
this.styleCache[key] = new style.Style(
|
||||
@@ -190,9 +195,48 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
}
|
||||
});
|
||||
}
|
||||
if(l.minzoom) {
|
||||
layer.setMinZoom(14);
|
||||
}
|
||||
if(l.maxzoom) {
|
||||
layer.setMaxZoom(l.maxzoom);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
createSelectionLayer(itemLayer:IItemLayer):Layer {
|
||||
var layerIndex = -1;
|
||||
var layer: Layer = null;
|
||||
layerIndex = itemLayer.layerIndex != -1 ? itemLayer.layerIndex : itemLayer.item.data.layers[0].index;
|
||||
var data = itemLayer.item.data;
|
||||
var l:ILayer = (data && data.layers && data.layers.length > 0) ? data.layers[layerIndex] : null;
|
||||
if (l && l.rendering && l.rendering.renderoutputType == "VectorTiles") {
|
||||
return new VectorTileLayer({
|
||||
renderMode: 'vector',
|
||||
source: itemLayer.layer.getSource(),
|
||||
style: (feature) => {
|
||||
if (feature.getId() in this.selectedFeatures) {
|
||||
|
||||
return new style.Style(
|
||||
{
|
||||
fill: new style.Fill({
|
||||
color: 'red'
|
||||
}),
|
||||
stroke: new style.Stroke({
|
||||
color: 'red',
|
||||
width: 1.25
|
||||
})
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
minZoom: itemLayer.layer.getMinZoom(),
|
||||
maxZoom: itemLayer.layer.getMaxZoom()
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
createExternalLayer(item:IItem,itemLayer:IItemLayer):Layer {
|
||||
let data = item.data as ILayerData;
|
||||
var layer: Layer = null;
|
||||
@@ -212,11 +256,32 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
layer = new Tile({ source: source });
|
||||
break;
|
||||
}
|
||||
case 'TileJSON': {
|
||||
let source = new TileJSON(data.options);
|
||||
layer = new Tile({ source: source });
|
||||
break;
|
||||
}
|
||||
case 'TileArcGISRest': {
|
||||
let source = new TileArcGISRest(data.options);
|
||||
layer = new Tile({ source: source });
|
||||
break;
|
||||
}
|
||||
case 'VectorWFSJson': {
|
||||
let source = new VectorSource({
|
||||
format: new GeoJSON(),
|
||||
url: function (extent) {
|
||||
return (
|
||||
data.options.url + '&srsname=' + data.projection +
|
||||
'&bbox=' +
|
||||
extent.join(',') +
|
||||
',EPSG:3857'
|
||||
);
|
||||
},
|
||||
strategy: loadingstrategy.bbox,
|
||||
});
|
||||
layer = new VectorLayer({ source: source });
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
@@ -245,7 +310,28 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
|
||||
ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.updateLayers(this.itemLayers);
|
||||
if(this.itemLayers) {
|
||||
this.updateLayers(this.itemLayers);
|
||||
} else if(this.itemLayer) {
|
||||
this.updateLayers([this.itemLayer])
|
||||
} else {
|
||||
this.updateLayers([]);
|
||||
}
|
||||
this.initialized=true;
|
||||
}
|
||||
|
||||
installMapEventHandler() {
|
||||
if(!this.mapEventHandlerInstalled) {
|
||||
this.map.instance.on(['click', 'pointermove'],this.mapEventHandler);
|
||||
this.mapEventHandlerInstalled=true;
|
||||
}
|
||||
}
|
||||
|
||||
unInstallMapEventHandler() {
|
||||
if(this.mapEventHandlerInstalled) {
|
||||
this.map.instance.un(['click', 'pointermove'],this.mapEventHandler);
|
||||
this.mapEventHandlerInstalled=false;
|
||||
}
|
||||
}
|
||||
|
||||
addOrUpdateOlLayer(itemLayer:IItemLayer,index:number):Layer {
|
||||
@@ -294,23 +380,52 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
|
||||
while(olLayers.getLength() > newLayers.length) {
|
||||
olLayers.removeAt(newLayers.length);
|
||||
}
|
||||
this.selectionLayer=null;
|
||||
if(this.mapEventHandlerInstalled && itemLayers.length==1 && itemLayers[0].item.itemType == 'vnd.farmmaps.itemtype.shape.processed') {
|
||||
this.selectionLayer = this.createSelectionLayer(itemLayers[0]);
|
||||
olLayers.push(this.selectionLayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mapEventHandler = (event) => {
|
||||
//if(event.type === 'click' && !this.onFeatureSelected.observers.length) return;
|
||||
//if(event.type === 'pointermode' && !this.onFeatureHover.observers.length) return;
|
||||
if(this.itemLayer && this.itemLayer.layer) {
|
||||
this.itemLayer.layer.getFeatures(event.pixel).then((features) => {
|
||||
this.selectedFeatures = {};
|
||||
if(!features.length) return;
|
||||
let fid = features[0].getId();
|
||||
this.selectedFeatures[fid] = features[0];
|
||||
console.debug(features[0]);
|
||||
if(this.selectionLayer) this.selectionLayer.changed();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this.instance) {
|
||||
if (this.instance && this.initialized) {
|
||||
if (changes['itemLayers']) {
|
||||
var itemLayers = changes['itemLayers'].currentValue as IItemLayer[];
|
||||
this.updateLayers(itemLayers);
|
||||
}
|
||||
if (changes['itemLayer']) {
|
||||
if (changes['itemLayer']) {
|
||||
var itemLayer = changes['itemLayer'].currentValue as IItemLayer;
|
||||
this.itemLayer = itemLayer
|
||||
if(itemLayer) {
|
||||
if(itemLayer.item.itemType == 'vnd.farmmaps.itemtype.shape.processed') {
|
||||
this.installMapEventHandler();
|
||||
}
|
||||
this.updateLayers([itemLayer]);
|
||||
} else {
|
||||
this.updateLayers([]);
|
||||
this.unInstallMapEventHandler();
|
||||
this.updateLayers([]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unInstallMapEventHandler();
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements
|
||||
@Input() selectedFeature: Feature;
|
||||
@Input() selectedItem: IItem;
|
||||
@Input() styles:IStyles;
|
||||
@Output() onFeaturesSelected: EventEmitter<Feature> = new EventEmitter<Feature>();
|
||||
@Output() onFeatureSelected: EventEmitter<Feature> = new EventEmitter<Feature>();
|
||||
@Output() onFeatureHover: EventEmitter<Feature> = new EventEmitter<Feature>();
|
||||
private stylesCache:IStyles = {};
|
||||
|
||||
@@ -96,9 +96,9 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements
|
||||
this.map.instance.addInteraction(this._hoverSelect);
|
||||
this._select.on('select', (e) => {
|
||||
if (e.selected.length > 0 && e.selected[0]) {
|
||||
this.onFeaturesSelected.emit(e.selected[0]);
|
||||
this.onFeatureSelected.emit(e.selected[0]);
|
||||
} else {
|
||||
this.onFeaturesSelected.emit(null);
|
||||
this.onFeatureSelected.emit(null);
|
||||
}
|
||||
});
|
||||
this._hoverSelect.on('select', (e) => {
|
||||
|
@@ -8,6 +8,7 @@
|
||||
border-radius:1.75em;
|
||||
padding:0;
|
||||
margin-top:0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#north {
|
||||
|
@@ -1,40 +0,0 @@
|
||||
// import { Component, OnInit,Input } from '@angular/core';
|
||||
//import { MapComponent } from 'ngx-openlayers';
|
||||
//import OLCesium from 'olcs/OLCesium.js';
|
||||
|
||||
|
||||
// @Component({
|
||||
// selector: 'fm-map-switch2d3d',
|
||||
// template: '<div (click)="handleClick($event)" class="rounded-circle twotreed">{{label}}</div>',
|
||||
// styles: [`.twotreed {
|
||||
// width:2.5em;
|
||||
// height:2.5em;
|
||||
// background-color: white;
|
||||
// text-align:center;
|
||||
// line-height:2.5em;
|
||||
// font-weight:bold;
|
||||
// cursor:default;}`]
|
||||
|
||||
// })
|
||||
// export class Switch2D3DComponent {
|
||||
|
||||
// @Input() enable:boolean;
|
||||
// public label: string = "3D";
|
||||
// private ol3d: OLCesium;
|
||||
|
||||
|
||||
// constructor(private map: MapComponent) {
|
||||
|
||||
// }
|
||||
|
||||
// ngOnInit() {
|
||||
// this.ol3d = new OLCesium({ map: this.map.instance }); // ol2dMap is the ol.Map instance
|
||||
// }
|
||||
|
||||
// handleClick(event) {
|
||||
// this.enable = !this.enable;
|
||||
// if (this.enable)
|
||||
// this.ol3d.setEnabled(this.enable);
|
||||
// this.label = this.enable?"2D":"3D";
|
||||
// }
|
||||
// }
|
@@ -25,10 +25,10 @@ export class ZoomToExtentComponent implements OnChanges {
|
||||
let threshold = 40 * rem;
|
||||
var left = 1 * rem;
|
||||
var right = 1 * rem;
|
||||
var bottom = Math.round(size[1] / 2);
|
||||
var bottom = Math.round((size[1] / 2) + (4*rem));
|
||||
var top = 1 * rem;
|
||||
if (size[0] > threshold) {
|
||||
bottom = 1 * rem;
|
||||
bottom = 5 * rem;
|
||||
left = 23 * rem;
|
||||
}
|
||||
options.padding = [top, right, bottom, left];
|
||||
|
@@ -2,7 +2,7 @@ import { Component, Input, OnInit, ComponentFactoryResolver, ViewChild, SimpleCh
|
||||
import { Feature } from 'ol';
|
||||
import { FeatureListComponent,AbstractFeatureListComponent } from '../feature-list/feature-list.component';
|
||||
import { WidgetHostDirective } from '../widget-host/widget-host.directive';
|
||||
import {IQueryState } from '@farmmaps/common';
|
||||
import {IQueryState,PackageService } from '@farmmaps/common';
|
||||
import * as mapReducers from '../../reducers/map.reducer';
|
||||
import * as mapActions from '../../actions/map.actions';
|
||||
import { Store } from '@ngrx/store';
|
||||
@@ -16,7 +16,8 @@ import { Observable } from 'rxjs';
|
||||
})
|
||||
export class FeatureListContainerComponent {
|
||||
|
||||
constructor(private store: Store<mapReducers.State>,private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListComponent) public featureLists: AbstractFeatureListComponent[] ) {
|
||||
constructor(private store: Store<mapReducers.State>,private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListComponent) public featureLists: AbstractFeatureListComponent[],private packageService:PackageService ) {
|
||||
this.featureLists = [...this.featureLists].reverse();
|
||||
}
|
||||
|
||||
@Input() features: Array<Feature>
|
||||
@@ -28,30 +29,44 @@ export class FeatureListContainerComponent {
|
||||
componentRef:any;
|
||||
|
||||
loadComponent(queryState:IQueryState) {
|
||||
var componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default
|
||||
var selected = -1;
|
||||
for (var i = 0; i < this.featureLists.length; i++) {
|
||||
if (this.featureLists[i]['forItemType'] == queryState.itemType && this.featureLists[i]['forChild'] && queryState.parentCode && queryState.parentCode != "") {
|
||||
selected = i;
|
||||
break;
|
||||
} else if (this.featureLists[i]['forItemType'] == queryState.itemType) {
|
||||
selected = i;
|
||||
break;
|
||||
let componentFactory: ComponentFactory<AbstractFeatureListComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListComponent); // default
|
||||
let selected = -1;
|
||||
let maxMatches =0;
|
||||
let showItem = true;
|
||||
for (let i = 0; i < this.featureLists.length; i++) {
|
||||
let matches=0;
|
||||
let criteria=0;
|
||||
if (this.featureLists[i]['forItemType']) {
|
||||
criteria++;
|
||||
if( this.featureLists[i]['forItemType'].indexOf(queryState.itemType) >= 0) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
if(this.featureLists[i]['forChild'] ) {
|
||||
criteria++;
|
||||
if(queryState.parentCode && queryState.parentCode != "") {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
if(criteria == matches && matches > maxMatches) {
|
||||
selected=i;
|
||||
maxMatches = matches;
|
||||
}
|
||||
}
|
||||
if (selected >= 0) {
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[i]['constructor'] as any);
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[selected]['constructor'] as any);
|
||||
if (this.featureLists[selected]['collapseSearch'] === true) {
|
||||
this.store.dispatch(new mapActions.CollapseSearch());
|
||||
}
|
||||
}
|
||||
}
|
||||
const viewContainerRef = this.widgetHost.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
this.componentRef = viewContainerRef.createComponent(componentFactory);
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).features = null;
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).queryState = queryState;
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = null;
|
||||
if(showItem) {
|
||||
this.componentRef = viewContainerRef.createComponent(componentFactory);
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).features = null;
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).queryState = queryState;
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = null;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -66,9 +81,11 @@ export class FeatureListContainerComponent {
|
||||
}
|
||||
if ((changes["features"] && changes["features"].currentValue)) {
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).features = changes["features"].currentValue;
|
||||
this.componentRef.changeDetectorRef.detectChanges();
|
||||
}
|
||||
if(changes["selectedFeature"]) {
|
||||
(<AbstractFeatureListComponent>this.componentRef.instance).selectedFeature = changes["selectedFeature"].currentValue;
|
||||
this.componentRef.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import { WidgetHostDirective } from '../widget-host/widget-host.directive';
|
||||
export class FeatureListFeatureContainerComponent {
|
||||
|
||||
constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractFeatureListFeatureComponent) public featureLists: AbstractFeatureListFeatureComponent[] ) {
|
||||
this.featureLists = [...this.featureLists].reverse();
|
||||
}
|
||||
|
||||
@Input() feature: Feature;
|
||||
@@ -23,11 +24,27 @@ export class FeatureListFeatureContainerComponent {
|
||||
|
||||
loadComponent() {
|
||||
var componentFactory: ComponentFactory<AbstractFeatureListFeatureComponent> = this.componentFactoryResolver.resolveComponentFactory(FeatureListFeatureComponent); // default
|
||||
for (var i = 0; i < this.featureLists.length; i++) {
|
||||
if (this.featureLists[i]['forItemType'] == this.feature.get("itemType")) {
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[i]['constructor'] as any);
|
||||
|
||||
let selected = -1;
|
||||
let maxMatches =0;
|
||||
for (let i = 0; i < this.featureLists.length; i++) {
|
||||
let matches=0;
|
||||
let criteria=0;
|
||||
if (this.featureLists[i]['forItemType']) {
|
||||
criteria++;
|
||||
if(this.featureLists[i]['forItemType'].indexOf(this.feature.get("itemType")) >= 0) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
if(criteria == matches && matches > maxMatches) {
|
||||
selected=i;
|
||||
maxMatches = matches;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (selected >= 0) {
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.featureLists[selected]['constructor'] as any);
|
||||
}
|
||||
|
||||
const viewContainerRef = this.widgetHost.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
|
@@ -0,0 +1,5 @@
|
||||
export function ForPackage(packageCode: string) {
|
||||
return function (constructor:Function) {
|
||||
constructor.prototype.forPackage = packageCode;
|
||||
};
|
||||
}
|
@@ -15,6 +15,7 @@ import { IItem, IListItem } from '@farmmaps/common';
|
||||
export class ItemListItemContainerComponent {
|
||||
|
||||
constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractItemListItemComponent) public itemComponentList: AbstractItemListItemComponent[] ) {
|
||||
this.itemComponentList = [...this.itemComponentList].reverse();
|
||||
}
|
||||
|
||||
@Input() item: IListItem;
|
||||
@@ -23,15 +24,35 @@ export class ItemListItemContainerComponent {
|
||||
|
||||
loadComponent() {
|
||||
var componentFactory: ComponentFactory<AbstractItemListItemComponent> = this.componentFactoryResolver.resolveComponentFactory(ItemListItemComponent); // default
|
||||
for (var i = 0; i < this.itemComponentList.length; i++) {
|
||||
if (this.itemComponentList[i]['forItemType'] &&
|
||||
this.itemComponentList[i]['forItemType'].indexOf(this.item.itemType) >= 0 &&
|
||||
this.itemComponentList[i]['forSourceTask'] &&
|
||||
this.itemComponentList[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0 )
|
||||
{
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.itemComponentList[i]['constructor'] as any);
|
||||
|
||||
let selected = -1;
|
||||
let maxMatches =0;
|
||||
let showItem = true;
|
||||
for (let i = 0; i < this.itemComponentList.length; i++) {
|
||||
let matches=0;
|
||||
let criteria=0;
|
||||
if (this.itemComponentList[i]['forItemType']) {
|
||||
criteria++;
|
||||
if(this.itemComponentList[i]['forItemType'].indexOf(this.item.itemType) >= 0) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.itemComponentList[i]['forSourceTask']) {
|
||||
criteria++;
|
||||
if(this.itemComponentList[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
if(criteria == matches && matches > maxMatches) {
|
||||
selected=i;
|
||||
maxMatches = matches;
|
||||
}
|
||||
}
|
||||
if (selected >= 0) {
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.itemComponentList[selected]['constructor'] as any);
|
||||
}
|
||||
|
||||
const viewContainerRef = this.widgetHost.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
|
@@ -36,10 +36,12 @@ export class LayerSwitcher implements OnInit,OnChanges{
|
||||
|
||||
|
||||
handleClick(event:Event) {
|
||||
event.stopPropagation();
|
||||
this.store.dispatch(new mapActions.ShowLayerSwitcher(true));
|
||||
}
|
||||
|
||||
close(event:Event) {
|
||||
event.stopPropagation();
|
||||
this.store.dispatch(new mapActions.ShowLayerSwitcher(false));
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,15 @@
|
||||
<div class="row pl-3">
|
||||
<div class="col-8 nopadding"><span i18n>Average</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{histogram.mean| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Standard deviation</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{histogram.stddev| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Min</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{histogram.min| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding" ><span i18n>Max</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{histogram.max| number:'1.0-2'}}</div>
|
||||
|
||||
<ng-container *ngIf="showConfidenceInterval()">
|
||||
<div class="col-8 nopadding"><span i18n>Confidence interval</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{histogram.confidence * 100| number:'1.0-0'}}%</div>
|
||||
</ng-container>
|
||||
</div>
|
@@ -0,0 +1,20 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {IHistogram, ILayer} from '../../../models/color.map';
|
||||
|
||||
@Component({
|
||||
selector: 'fm-map-histogram-details',
|
||||
templateUrl: './histogram-details.component.html',
|
||||
styles: ['.nopadding{\n' +
|
||||
' padding: 0 !important;\n' +
|
||||
' margin: 0 !important;\n' +
|
||||
'}']
|
||||
})
|
||||
export class HistogramDetailsComponent {
|
||||
|
||||
@Input()
|
||||
histogram: IHistogram;
|
||||
|
||||
showConfidenceInterval(): boolean {
|
||||
return this.histogram.confidence != null && this.histogram.confidence !== 1;
|
||||
}
|
||||
}
|
@@ -1,11 +1,14 @@
|
||||
<table class="container" *ngIf="showLegend()">
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<td colspan="3">
|
||||
<div class="title">
|
||||
<h4 *ngIf="showTitle">{{layer.name}}</h4>
|
||||
<b *ngIf="layer.unit">({{layer.unit}})</b>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a i18n-title title="more info"><i class="fa fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a>
|
||||
</td>
|
||||
<td colspan="2">
|
||||
<div class="title" *ngIf="histogramenabled">
|
||||
<h4>{{histogram}}</h4>
|
||||
@@ -14,26 +17,27 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngFor="let entry of layer.renderer.colorMap.entries; let i = index ">
|
||||
<td class="legend-items"><span [style.background-color]="getHex(entry.color)" class="color"></span></td>
|
||||
<td class="legend-items-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"><span [style.background-color]="getAlphaHex(entry.color)" [style.border-color]="getHex(entry.color)"class="color"></span></td>
|
||||
<td class="legend-items-text"><span *ngIf="!entry.label">{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span><span *ngIf="entry.label">{{entry.label}}</span></td>
|
||||
<td class="histogram-items">
|
||||
<div *ngIf="showHistogram()">
|
||||
<span class="bar" [style.background-color]="getHex(entry.color)" [style.width]="getPart(layer.renderer, i)">
|
||||
</span>
|
||||
<span class="bar-label">{{getLabel(layer.renderer,i)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
<tr *ngIf="showHistogram()">
|
||||
<td colspan="4" class="pb-2 pt-2">
|
||||
<a i18n-title title="more info"><i class="fa fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a>
|
||||
<div class="info pt-2" [ngbCollapse]="hideHistogramDetails">
|
||||
<div><span i18n>Average: </span>{{layer.renderer.band.histogram.mean| number:'1.0-3'}}</div>
|
||||
<div><span i18n>Standard deviation: </span>{{layer.renderer.band.histogram.stddev| number:'1.0-3'}}</div>
|
||||
<div><span i18n>Min: </span>{{layer.renderer.band.histogram.min| number:'1.0-3'}}</div>
|
||||
<div><span i18n>Max: </span>{{layer.renderer.band.histogram.max| number:'1.0-3'}}</div>
|
||||
<div *ngIf="showConfidenceInterval()"><span i18n>Confidence interval:</span> {{layer.renderer.band.histogram.confidence * 100| number:'1.0-0'}}%</div>
|
||||
</div>
|
||||
<td colspan="4" class="pb-1 pt-1">
|
||||
<div class="info" [ngbCollapse]="hideHistogramDetails">
|
||||
<ng-container *ngIf="bandContainsStatistics(); else histogram">
|
||||
<fm-map-statistics-details [statistics]="layer.renderer.band.statistics"></fm-map-statistics-details>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #histogram>
|
||||
<fm-map-histogram-details [histogram]="layer.renderer.band.histogram"></fm-map-histogram-details>
|
||||
</ng-template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@@ -20,6 +20,7 @@ span.color {
|
||||
display:inline-block;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
border: 2px solid;
|
||||
}
|
||||
|
||||
.legend-items {
|
||||
|
@@ -53,6 +53,10 @@ export class LegendComponent implements OnInit,AfterViewInit {
|
||||
return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue);
|
||||
}
|
||||
|
||||
public getAlphaHex(color: IColor): string {
|
||||
return '#' + this.componentToHex(color.red) + this.componentToHex(color.green) + this.componentToHex(color.blue)+ this.componentToHex(color.alpha);
|
||||
}
|
||||
|
||||
private componentToHex(c: number): string {
|
||||
const hex = c.toString(16);
|
||||
return hex.length === 1 ? `0${hex}` : hex;
|
||||
@@ -88,8 +92,8 @@ export class LegendComponent implements OnInit,AfterViewInit {
|
||||
return this.histogramenabled && this.layer.renderer.band.histogram.entries && this.layer.renderer.band.histogram.entries.length > 0 && this.layer.renderer.colorMap.colormapType == "minmax";
|
||||
}
|
||||
|
||||
showConfidenceInterval():boolean {
|
||||
return this.histogramenabled && this.layer.renderer.band.histogram.confidence != null && this.layer.renderer.band.histogram.confidence != 1;
|
||||
bandContainsStatistics(): boolean {
|
||||
return this.layer.renderer.band.statistics != null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,36 @@
|
||||
<div class="row pl-3">
|
||||
<div class="col-8 nopadding"><span i18n>Data points</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.populationCount}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Min</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.min| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Max</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.max| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Min+1</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.minPlus| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Max-1</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.maxMinus| number:'1.0-2'}}</div>
|
||||
|
||||
<div class="col-8 nopadding"><span i18n>Average</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.mean| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Standard deviation</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.stddev| number:'1.0-2'}}</div>
|
||||
|
||||
<div class="col-8 nopadding"><span i18n>Mode</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.mode| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Median</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.median| number:'1.0-2'}}</div>
|
||||
|
||||
<div class="col-8 nopadding"><span i18n>Kurtosis</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.curtosis| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Skewness</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.skewness| number:'1.0-2'}}</div>
|
||||
|
||||
<div class="col-8 nopadding"><span i18n>Variance</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.variance| number:'1.0-2'}}</div>
|
||||
<div class="col-8 nopadding"><span i18n>Coefficient of variation</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.variationCoefficient | number:'1.0-2'}}</div>
|
||||
<ng-container *ngIf="statistics.confidenceIntervalLow !== undefined">
|
||||
<div class="col-8 nopadding">90% <span i18n>Confidence interval</span>:</div>
|
||||
<div class="col-4 pull-left nopadding">{{statistics.confidenceIntervalLow | number:'1.0-2'}} - {{statistics.confidenceIntervalHigh | number:'1.0-2'}}</div>
|
||||
</ng-container>
|
||||
</div>
|
@@ -0,0 +1,16 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {IStatistics} from '../../../models/color.map';
|
||||
|
||||
@Component({
|
||||
selector: 'fm-map-statistics-details',
|
||||
templateUrl: './statistics-details.component.html',
|
||||
styles: ['.nopadding{\n' +
|
||||
' padding: 0 !important;\n' +
|
||||
' margin: 0 !important;\n' +
|
||||
'}']
|
||||
})
|
||||
export class StatisticsDetailsComponent {
|
||||
|
||||
@Input()
|
||||
statistics: IStatistics;
|
||||
}
|
@@ -12,6 +12,7 @@
|
||||
panelCollapsed:panelCollapsed$|async,
|
||||
searchMinified:searchMinified$|async,
|
||||
selectedItem:selectedItem$|async,
|
||||
parentItem:parentItem$|async,
|
||||
queryState:queryState$|async,
|
||||
searchCollapsed:searchCollapsed$|async,
|
||||
clearEnabled:clearEnabled$|async,
|
||||
@@ -20,9 +21,9 @@
|
||||
styles:styles$|async,
|
||||
selectedFeature:selectedFeature$|async
|
||||
} as state">
|
||||
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible}" class="map">
|
||||
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" [ngClass]="{'panel-visible':state.panelVisible}" class="map">
|
||||
<div>
|
||||
|
||||
|
||||
</div>
|
||||
<aol-view [zoom]="state.mapState.zoom" [rotation]="state.mapState.rotation">
|
||||
<aol-coordinate [x]="state.mapState.xCenter" [y]="state.mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate>
|
||||
@@ -34,11 +35,11 @@
|
||||
<fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers>
|
||||
<fm-map-item-layers [itemLayer]="state.selectedItemLayer"></fm-map-item-layers>
|
||||
<aol-layer-vector>
|
||||
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeaturesSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
|
||||
<fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
|
||||
</aol-layer-vector>
|
||||
<fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location>
|
||||
<!-- <fm-map-gps-location [position]="state.position" [headingTolerance]="20" [showHeading]="true" [heading]="state.compassHeading"></fm-map-gps-location> -->
|
||||
<div class="control-container">
|
||||
<!-- <switch2d3d></switch2d3d>-->
|
||||
<router-outlet name="map-controls"></router-outlet>
|
||||
<fm-map-layer-switcher></fm-map-layer-switcher>
|
||||
<fm-map-pan-to-location [position]="state.position" [mapState]="state.mapState" [animate]="true"></fm-map-pan-to-location>
|
||||
<fm-map-rotation-reset></fm-map-rotation-reset>
|
||||
@@ -51,13 +52,13 @@
|
||||
<div class="panel-top bg-secondary" *ngIf="!(state.searchMinified)">
|
||||
</div>
|
||||
<div class="panel-bottom">
|
||||
|
||||
|
||||
<div *ngIf="!(state.selectedItem)">
|
||||
<fm-map-feature-list-container [features]="state.features" [selectedFeature]="state.selectedFeature" [queryState]="state.queryState" [clickedFeature]="clickedFeature"></fm-map-feature-list-container>
|
||||
</div>
|
||||
|
||||
|
||||
<div *ngIf="state.selectedItem;let item">
|
||||
<fm-map-selected-item-container [item]="item" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container>
|
||||
<fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container>
|
||||
</div>
|
||||
<div *ngIf="state.features.length == 0" class="no-results m-2">
|
||||
<div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
||||
|
||||
aol-map { position:absolute;width:100%;height:100%;}
|
||||
aol-map { position:absolute;width:100%;height:calc(100vh + 4rem);}
|
||||
|
||||
.arrow {
|
||||
top: 3rem;
|
||||
@@ -49,7 +49,7 @@ aol-map { position:absolute;width:100%;height:100%;}
|
||||
.control-container {
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
bottom: 1em;
|
||||
bottom: 5em;
|
||||
}
|
||||
|
||||
switch2d3d {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { Observable, Subject, Subscription, from,of } from 'rxjs';
|
||||
import { Observable, Subject, Subscription, from,of ,EMPTY } from 'rxjs';
|
||||
import { withLatestFrom, switchMap,skip } from 'rxjs/operators';
|
||||
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { Store } from '@ngrx/store';
|
||||
@@ -12,7 +12,7 @@ import * as mapActions from '../../actions/map.actions';
|
||||
import { IMapState} from '../../models/map.state';
|
||||
import { ISelectedFeatures } from '../../models/selected.features';
|
||||
import { IItemLayer } from '../../models/item.layer';
|
||||
import { IQueryState } from '@farmmaps/common';
|
||||
import { IListItem, IQueryState } from '@farmmaps/common';
|
||||
import { IPeriodState } from '../../models/period.state';
|
||||
import {IStyles} from '../../models/style.cache';
|
||||
import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component';
|
||||
@@ -64,8 +64,9 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
public selectedFeature$: Observable<Feature> = this.store.select(mapReducers.selectGetSelectedFeature);
|
||||
public clickedFeature: Subject<Feature> = new Subject<Feature>();
|
||||
public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem);
|
||||
public parentItem$: Observable<IItem> =this.store.select(mapReducers.selectGetParentItem);
|
||||
public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState);
|
||||
public state$:Observable<{mapState:IMapState,queryState:IQueryState,setStateCount:number}> = this.store.select(mapReducers.selectGetState);
|
||||
public state$:Observable<{mapState:IMapState,queryState:IQueryState}> = this.store.select(mapReducers.selectGetState);
|
||||
public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod);
|
||||
public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled);
|
||||
public searchCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetSearchCollapsed);
|
||||
@@ -78,49 +79,55 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
public overlayLayersCollapsed: boolean = true;
|
||||
public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent);
|
||||
public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles);
|
||||
private setStateCount$:Observable<number> = this.store.select(mapReducers.selectgetSetStateCount);
|
||||
private lastUrl = "";
|
||||
private initialized: boolean = false;
|
||||
|
||||
@ViewChild('map') map;
|
||||
|
||||
constructor(private store: Store<mapReducers.State | commonReducers.State>,
|
||||
private route: ActivatedRoute,
|
||||
|
||||
constructor(private store: Store<mapReducers.State | commonReducers.State>,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private uploadService: ResumableFileUploadService,
|
||||
private serializeService: StateSerializerService,
|
||||
public itemTypeService: ItemTypeService,
|
||||
private location: Location,
|
||||
private uploadService: ResumableFileUploadService,
|
||||
private serializeService: StateSerializerService,
|
||||
public itemTypeService: ItemTypeService,
|
||||
private location: Location,
|
||||
private geolocationService: GeolocationService,
|
||||
private zone: NgZone,
|
||||
private deviceorientationService:DeviceOrientationService) {
|
||||
this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$),withLatestFrom(this.setStateCount$)).subscribe(([[queryState,mapState],setStateCount]) =>{
|
||||
this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$)).subscribe(([queryState,mapState]) =>{
|
||||
if(queryState) {
|
||||
let newQueryState = tassign(mapReducers.initialQueryState);
|
||||
console.debug(`Do Query ${setStateCount}`);
|
||||
console.debug(`Do Query`);
|
||||
let urlparts=[];
|
||||
if (queryState.itemCode && queryState.itemCode != "") {
|
||||
if (queryState.itemCode && queryState.itemCode != "") {
|
||||
if(queryState.itemType && queryState.itemType!= "") {
|
||||
let itemType = this.itemTypeService.itemTypes[queryState.itemType];
|
||||
if (itemType && itemType.viewer && itemType.viewer == "edit_in_editor" && itemType.editor) {
|
||||
urlparts.push('/editor');
|
||||
urlparts.push(itemType.editor);
|
||||
urlparts.push('item');
|
||||
urlparts.push(queryState.itemCode);
|
||||
}
|
||||
}
|
||||
urlparts.push(queryState.itemCode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newQueryState= queryState;
|
||||
}
|
||||
if(urlparts.length==0 ) {
|
||||
if(urlparts.length==0 ) {
|
||||
newQueryState.itemCode = queryState.itemCode;
|
||||
this.zone.run(() => {
|
||||
this.store.dispatch(new mapActions.SetQueryState(newQueryState,false));
|
||||
this.replaceUrl(mapState,newQueryState,false);
|
||||
})
|
||||
} else {
|
||||
this.router.navigate(urlparts);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
this.store.dispatch(new mapActions.Init());
|
||||
// this.store.select(commonReducers.getRootItems).subscribe((l) => {
|
||||
// if(l && l.length>0) {
|
||||
// this.store.dispatch(new mapActions.Init());
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
@HostListener('document:keyup', ['$event'])
|
||||
@@ -156,10 +163,11 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.initialized = false;
|
||||
console.debug("Init");
|
||||
this.store.dispatch(new mapActions.Clear());
|
||||
this.selectedFeatures$.next({x:0,y:0,features:[]});
|
||||
this.selectedFeatures$.next(null);
|
||||
this.selectedFeatures$.next(null);
|
||||
}
|
||||
|
||||
initCustomStyles() {
|
||||
@@ -203,8 +211,8 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
|
||||
normalizeMapState(mapState:IMapState):IMapState {
|
||||
if(!mapState) return null;
|
||||
return {zoom: this.round(mapState.zoom,0),
|
||||
rotation: this.round(mapState.rotation,2),
|
||||
return {zoom: this.round(mapState.zoom,0),
|
||||
rotation: this.round(mapState.rotation,2),
|
||||
xCenter: this.round(mapState.xCenter,5),
|
||||
yCenter: this.round(mapState.yCenter,5),
|
||||
baseLayerCode: mapState.baseLayerCode };
|
||||
@@ -219,7 +227,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
let queryState = params.get("queryState");
|
||||
var newQueryState = tassign(mapReducers.initialQueryState);
|
||||
if (queryState != "") {
|
||||
newQueryState = this.serializeService.deserialize(queryState);
|
||||
newQueryState = this.serializeService.deserialize(queryState);
|
||||
}
|
||||
return newQueryState;
|
||||
} else {
|
||||
@@ -227,66 +235,58 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
ngAfterViewInit() {
|
||||
console.debug("View init");
|
||||
this.initCustomStyles();
|
||||
this.initCustomStyles();
|
||||
|
||||
// url to state
|
||||
|
||||
this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => {
|
||||
var newMapState: IMapState = state.mapState;
|
||||
var newQueryState: IQueryState = state.queryState;
|
||||
|
||||
var queryStateChanged = false;
|
||||
var mapStateChanged = false;
|
||||
let urlMapState = this.getMapStateFromUrl(params);
|
||||
if(urlMapState) {
|
||||
newMapState = urlMapState;
|
||||
mapStateChanged = this.serializeMapState(state.mapState) != this.serializeMapState(newMapState);
|
||||
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));
|
||||
}
|
||||
|
||||
let urlQueryState = this.getQueryStateFromUrl(params);
|
||||
if(urlQueryState) {
|
||||
newQueryState = urlQueryState;
|
||||
queryStateChanged = this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState);
|
||||
}
|
||||
|
||||
if(queryStateChanged && mapStateChanged && state.setStateCount ==0) {
|
||||
return of(new mapActions.SetState(newMapState,newQueryState));
|
||||
window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(newMapState));
|
||||
} else if(queryStateChanged) {
|
||||
return of(new mapActions.SetQueryState(newQueryState));
|
||||
} return of(new mapActions.SetReplaceUrl(true));
|
||||
})).subscribe((action) => {
|
||||
if(action) {
|
||||
this.zone.run(() => {
|
||||
console.debug("Url to state");
|
||||
this.store.dispatch(action);
|
||||
});
|
||||
}
|
||||
});
|
||||
this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => {
|
||||
if(this.initialized) {
|
||||
let urlQueryState = this.getQueryStateFromUrl(params);
|
||||
if( this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState)) {
|
||||
return of(new mapActions.SetState(state.mapState,urlQueryState));
|
||||
}
|
||||
}
|
||||
return EMPTY;
|
||||
})).subscribe((action) => {
|
||||
if(action) {
|
||||
this.zone.run(() => {
|
||||
console.debug("Url to state");
|
||||
this.store.dispatch(action);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// state to url
|
||||
|
||||
|
||||
this.stateSub = this.state$.pipe(switchMap((state) => {
|
||||
let newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState);
|
||||
if(this.lastUrl!=newUrl && state.setStateCount>0) {
|
||||
if(this.lastUrl!=newUrl) {
|
||||
this.lastUrl=newUrl;
|
||||
return of(state);
|
||||
}
|
||||
else {
|
||||
return of(null);
|
||||
}
|
||||
}
|
||||
})).subscribe((newUrlState) =>{
|
||||
if(newUrlState) {
|
||||
console.debug(`State to url ${newUrlState.setStateCount}`);
|
||||
console.debug(`State to url`);
|
||||
this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.map.instance.updateSize();
|
||||
}, 500);
|
||||
});
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
handleSearchCollapse(event) {
|
||||
@@ -315,48 +315,58 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
event.preventDefault();
|
||||
var queryState = tassign(mapReducers.initialQueryState, query);
|
||||
this.store.dispatch(new mapActions.DoQuery(queryState));
|
||||
}
|
||||
|
||||
replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) {
|
||||
let parts =["."];
|
||||
parts.push(mapState.xCenter.toFixed(5));
|
||||
parts.push(mapState.yCenter.toFixed(5));
|
||||
parts.push( mapState.zoom.toFixed(0));
|
||||
parts.push( mapState.rotation.toFixed(2));
|
||||
if(mapState.baseLayerCode!="") {
|
||||
parts.push(mapState.baseLayerCode);
|
||||
parts.push( this.serializeService.serialize(queryState));
|
||||
console.debug("Replace url",parts);
|
||||
this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent });
|
||||
}
|
||||
}
|
||||
|
||||
handleOnMoveEnd(event) {
|
||||
console.debug("Move end");
|
||||
this.zone.run(() =>{
|
||||
var map = event.map;
|
||||
var view = map.getView();
|
||||
var rotation = view.getRotation();
|
||||
var zoom = view.getZoom();
|
||||
var center = transform(view.getCenter(), view.getProjection(), "EPSG:4326");
|
||||
var extent = view.calculateExtent(this.map.instance.getSize());
|
||||
let mapState: IMapState = { xCenter: center[0], yCenter: center[1], zoom: zoom, rotation: rotation, baseLayerCode: null };
|
||||
let state = { mapState: mapState, extent: extent };
|
||||
let source = from([state]);
|
||||
source.pipe(withLatestFrom(this.selectedBaseLayer$),withLatestFrom(this.setStateCount$)).subscribe(([[state, baselayer],setStateCount]) => {
|
||||
if (mapState && baselayer) { // do not react on first move
|
||||
let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code });
|
||||
this.store.dispatch(new mapActions.SetMapState(newMapState));
|
||||
this.store.dispatch(new mapActions.SetViewExtent(state.extent));
|
||||
}
|
||||
replaceUrl(mapState: IMapState, queryState: IQueryState, replace: boolean = true) {
|
||||
|
||||
let newMapState = this.serializeMapState(mapState);
|
||||
let newQueryState = this.serializeService.serialize(queryState);
|
||||
let currentMapState = this.serializeMapState(this.getMapStateFromUrl(this.route.snapshot.paramMap));
|
||||
let urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap);
|
||||
let currentQueryState = urlQueryState==null?"":this.serializeService.serialize(urlQueryState);
|
||||
if(mapState.baseLayerCode!="" && ((newMapState!= currentMapState) || (newQueryState!=currentQueryState))) {
|
||||
let parts =["."];
|
||||
parts.push(mapState.xCenter.toFixed(5));
|
||||
parts.push(mapState.yCenter.toFixed(5));
|
||||
parts.push( mapState.zoom.toFixed(0));
|
||||
parts.push( mapState.rotation.toFixed(2));
|
||||
parts.push(mapState.baseLayerCode);
|
||||
parts.push( this.serializeService.serialize(queryState));
|
||||
|
||||
console.debug("Replace url",parts);
|
||||
this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent });
|
||||
}
|
||||
}
|
||||
|
||||
handleOnMoveEnd(event) {
|
||||
if(this.initialized) {
|
||||
this.zone.run(() =>{
|
||||
console.debug("Move end");
|
||||
var map = event.map;
|
||||
var view = map.getView();
|
||||
var rotation = view.getRotation();
|
||||
var zoom = view.getZoom();
|
||||
var center = transform(view.getCenter(), view.getProjection(), "EPSG:4326");
|
||||
var viewExtent = view.calculateExtent(this.map.instance.getSize());
|
||||
let mapState: IMapState = { xCenter: center[0], yCenter: center[1], zoom: zoom, rotation: rotation, baseLayerCode: null };
|
||||
let state = { mapState: mapState, viewExtent: viewExtent };
|
||||
console.debug("Center: ",center[0],center[1] );
|
||||
let source = from([state]);
|
||||
source.pipe(withLatestFrom(this.selectedBaseLayer$)).subscribe(([state, baselayer]) => {
|
||||
if (mapState && baselayer) { // do not react on first move
|
||||
let newMapState = tassign(state.mapState, { baseLayerCode: baselayer.item.code });
|
||||
this.store.dispatch(new mapActions.SetMapState(newMapState));
|
||||
this.store.dispatch(new mapActions.SetViewExtent(state.viewExtent));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleOnMouseDown(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
this.zone.run(() =>{
|
||||
this.store.dispatch(new mapActions.CollapseSearch());
|
||||
this.store.dispatch(new commonActions.CloseAll());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -365,8 +375,8 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
}
|
||||
|
||||
handleClearSearch(event) {
|
||||
this.store.dispatch(new commonActions.Escape(true, false));
|
||||
}
|
||||
this.store.dispatch(new mapActions.Clear());
|
||||
}
|
||||
|
||||
handleOnDelete(itemLayer: IItemLayer) {
|
||||
this.store.dispatch(new mapActions.RemoveLayer(itemLayer));
|
||||
@@ -385,7 +395,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
extend(extent, itemLayer.layer.getExtent());
|
||||
if (extent) {
|
||||
this.store.dispatch(new mapActions.SetExtent(extent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectBaseLayer(itemLayer: IItemLayer) {
|
||||
@@ -400,7 +410,7 @@ export class MapComponent implements OnInit, OnDestroy,AfterViewInit {
|
||||
if (this.paramSub) this.paramSub.unsubscribe();
|
||||
if (this.itemTypeSub) this.itemTypeSub.unsubscribe();
|
||||
if (this.stateSub) this.stateSub.unsubscribe();
|
||||
if (this.queryStateSub) this.queryStateSub.unsubscribe();
|
||||
if (this.queryStateSub) this.queryStateSub.unsubscribe();
|
||||
if (this.querySub) this.querySub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,25 @@
|
||||
.custom-day {
|
||||
text-align: center;
|
||||
padding: 0.185rem 0.25rem;
|
||||
display: inline-block;
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.custom-day.focused {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.custom-day.range, .custom-day:hover {
|
||||
background-color: rgb(2, 117, 216);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.custom-day.faded {
|
||||
background-color: rgba(2, 117, 216, 0.5);
|
||||
}
|
||||
.custom-day {
|
||||
text-align: center;
|
||||
padding: 0.185rem 0.25rem;
|
||||
display: inline-block;
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
.custom-day.focused {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.custom-day.range, .custom-day:hover {
|
||||
background-color: rgb(2, 117, 216);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.custom-day.faded {
|
||||
background-color: rgba(2, 117, 216, 0.5);
|
||||
}
|
||||
|
||||
ngb-datepicker {
|
||||
overflow: hidden;
|
||||
max-width: calc(100vw - 3em);
|
||||
}
|
@@ -8,42 +8,58 @@ import { IItemLayer } from '../../models/item.layer';
|
||||
@Component({
|
||||
selector: 'fm-map-selected-item-container',
|
||||
templateUrl: './selected-item-container.component.html',
|
||||
styleUrls: ['./selected-item-container.component.scss']
|
||||
styleUrls: ['./selected-item-container.component.scss']
|
||||
})
|
||||
export class SelectedItemContainerComponent {
|
||||
|
||||
constructor(private componentFactoryResolver: ComponentFactoryResolver, @Inject(AbstractSelectedItemComponent) public selectedItemComponents: AbstractSelectedItemComponent[] ) {
|
||||
this.selectedItemComponents = [...this.selectedItemComponents].reverse();
|
||||
}
|
||||
|
||||
@Input() item: IItem;
|
||||
@Input() parentItem: IItem;
|
||||
@Input() itemLayer:IItemLayer;
|
||||
@Input() overlayLayers:Array<IItemLayer>;
|
||||
|
||||
@ViewChild(WidgetHostDirective, { static: true }) widgetHost: WidgetHostDirective;
|
||||
@ViewChild(WidgetHostDirective, { static: true }) widgetHost: WidgetHostDirective;
|
||||
|
||||
loadComponent() {
|
||||
let componentFactory: ComponentFactory<AbstractSelectedItemComponent> = this.componentFactoryResolver.resolveComponentFactory(SelectedItemComponent); // default
|
||||
let firstComponentWithTypeAndTask = this.selectedItemComponents
|
||||
.find(value => value['forSourceTask'] == this.item.sourceTask &&
|
||||
value['forItemType'] == this.item.itemType
|
||||
);
|
||||
|
||||
if (firstComponentWithTypeAndTask) {
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithTypeAndTask['constructor'] as any);
|
||||
} else {
|
||||
let firstComponentWithType = this.selectedItemComponents
|
||||
.find(value => value['forSourceTask'] == null &&
|
||||
value['forItemType'] == this.item.itemType);
|
||||
let selected = -1;
|
||||
let maxMatches =0;
|
||||
let showItem = true;
|
||||
for (let i = 0; i < this.selectedItemComponents.length; i++) {
|
||||
let matches=0;
|
||||
let criteria=0;
|
||||
if (this.selectedItemComponents[i]['forItemType'] ) {
|
||||
criteria++;
|
||||
if(this.selectedItemComponents[i]['forItemType'].indexOf(this.item.itemType) >= 0) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
if (this.selectedItemComponents[i]['forSourceTask']) {
|
||||
criteria++;
|
||||
if( this.selectedItemComponents[i]['forSourceTask'].indexOf(this.item.sourceTask) >= 0) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstComponentWithType) {
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(firstComponentWithType['constructor'] as any);
|
||||
if(criteria==matches && matches > maxMatches) {
|
||||
selected=i;
|
||||
maxMatches = matches;
|
||||
}
|
||||
}
|
||||
if (selected >= 0) {
|
||||
componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.selectedItemComponents[selected]['constructor'] as any);
|
||||
}
|
||||
|
||||
const viewContainerRef = this.widgetHost.viewContainerRef;
|
||||
viewContainerRef.clear();
|
||||
|
||||
const componentRef = viewContainerRef.createComponent(componentFactory);
|
||||
(<AbstractSelectedItemComponent>componentRef.instance).item = this.item;
|
||||
(<AbstractSelectedItemComponent>componentRef.instance).parentItem = this.parentItem;
|
||||
(<AbstractSelectedItemComponent>componentRef.instance).itemLayer = this.itemLayer;
|
||||
(<AbstractSelectedItemComponent>componentRef.instance).overlayLayers = this.overlayLayers;
|
||||
}
|
||||
|
@@ -7,9 +7,10 @@ import { commonReducers, ItemTypeService, IItem, Item, ItemService, FolderServic
|
||||
import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router';
|
||||
import { ForItemType } from '../for-item/for-itemtype.decorator';
|
||||
import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable,of } from 'rxjs';
|
||||
import {GeoJSON} from 'ol/format';
|
||||
import {getArea} from 'ol/sphere';
|
||||
import { withLatestFrom,switchMap,combineLatest } from 'rxjs/operators';
|
||||
|
||||
|
||||
@ForItemType("vnd.farmmaps.itemtype.cropfield")
|
||||
@@ -38,6 +39,25 @@ export class SelectedItemCropfieldComponent extends AbstractSelectedItemComponen
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this. items = this.folderService$.getItems(this.item.code, 0, 1000);
|
||||
var childItems = this.folderService$.getItems(this.item.code, 0, 1000);
|
||||
var atLocationItems = this.itemService$.getItemList(null,null,null,this.item.code,true);
|
||||
this.items = childItems.pipe(
|
||||
combineLatest(atLocationItems),
|
||||
switchMap(([ci,ali]) => {
|
||||
let retVal:IListItem[] = [];
|
||||
let codes = {};
|
||||
ci.forEach((listItem) => {
|
||||
retVal.push(listItem);
|
||||
codes[listItem.code]=listItem;
|
||||
});
|
||||
ali.forEach((atlocationitem) => {
|
||||
let listItem = atlocationitem as IListItem;
|
||||
let allowedItemTypes = "vnd.farmmaps.itemtype.device.senml"; // this is a hack
|
||||
if(!codes[listItem.code] && allowedItemTypes.indexOf(listItem.itemType) >= 0) {
|
||||
retVal.push(listItem);
|
||||
}
|
||||
});
|
||||
return of(retVal);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@
|
||||
<div class="card-body">
|
||||
<div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div>
|
||||
<div class="card menu-card">
|
||||
<h2 *ngIf="parentItem">{{parentItem.name}}</h2>
|
||||
<h1>{{item.name}}</h1>
|
||||
</div>
|
||||
<div class="legend-container" *ngIf="item?.data.layers;let layers">
|
||||
@@ -13,14 +14,15 @@
|
||||
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<fm-map-layer-legend [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
|
||||
<fm-map-layer-legend [showTitle]="layers.length == 1"
|
||||
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card menu-card">
|
||||
<ul class="p-0 mt-2">
|
||||
<li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i> <span i18n>Edit</span></a></li>
|
||||
<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)="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>
|
||||
</ul>
|
||||
|
@@ -4,6 +4,7 @@
|
||||
<div class="card-body">
|
||||
<div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div>
|
||||
<div class="card menu-card">
|
||||
<h2 *ngIf="parentItem">{{parentItem.name}}</h2>
|
||||
<h1>{{item.name}}</h1>
|
||||
</div>
|
||||
<div class="legend-container" *ngIf="item?.data.layers;let layers">
|
||||
@@ -20,7 +21,7 @@
|
||||
<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>
|
||||
<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)="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>
|
||||
</ul>
|
||||
|
@@ -4,6 +4,7 @@
|
||||
<div class="card-body">
|
||||
<div class="mb-2"><a href="#" (click)="handleBackToList($event)" i18n>Back</a></div>
|
||||
<div class="card menu-card">
|
||||
<h2 *ngIf="parentItem">{{parentItem.name}}</h2>
|
||||
<h1>{{item.name}}</h1>
|
||||
</div>
|
||||
<div class="legend-container" *ngIf="item?.data.layers;let layers">
|
||||
@@ -28,13 +29,17 @@
|
||||
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<fm-map-layer-legend [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
|
||||
<fm-map-layer-legend [showTitle]="layers.length == 1"
|
||||
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card menu-card">
|
||||
<ul class="p-0 mt-2">
|
||||
<li *ngIf="item.isEditable"><a href="#" class="mt-1 mr-1" (click)="handleOnEdit(item)"><i class="fa fa-pencil" aria-hidden="true" i18n-title title="Edit"></i> <span i18n>Edit</span></a></li>
|
||||
<li *ngIf="itemTypeService.isLayer(item)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" class="mt-1 mr-1"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i> <span i18n>Add as overlay</span></a></li>
|
||||
<ng-container *ngIf="itemTypeService.isLayer(item)">
|
||||
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Add as layer"></i> <span i18n>Add as overlay</span></a></li>
|
||||
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fa fa-eye" aria-hidden="true" i18n-title title="Remove overlay"></i> <span i18n>Remove overlay</span></a></li>
|
||||
</ng-container>
|
||||
<li><a href="#" (click)="handleGoToChart(item)"><i class="fa fa-line-chart" aria-hidden="true" i18m-title title="Show chart"></i> <span i18n>Show chart</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@@ -35,7 +35,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
|
||||
nextDate():Date {
|
||||
let temporalItemLayer = this.itemLayer as ITemporalItemLayer;
|
||||
if(temporalItemLayer.nextItemLayer.item)
|
||||
return temporalItemLayer.nextItemLayer.item.dataDate;
|
||||
return new Date(Date.parse(temporalItemLayer.nextItemLayer.item.dataDate)) ;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
|
||||
previousDate():Date {
|
||||
let temporalItemLayer = this.itemLayer as ITemporalItemLayer;
|
||||
if(temporalItemLayer.previousItemLayer.item)
|
||||
return temporalItemLayer.previousItemLayer.item.dataDate;
|
||||
return new Date(Date.parse(temporalItemLayer.previousItemLayer.item.dataDate));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
|
||||
}
|
||||
|
||||
handleGoToChart(item: IItem) {
|
||||
this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, item.dataDate.getUTCFullYear()]);
|
||||
this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, new Date(Date.parse(item.dataDate)).getUTCFullYear()]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<div *ngIf="item;let item">
|
||||
<div class="card border-0">
|
||||
<img *ngIf="item.thumbnail" class="card-img-top" [src]="config.getConfig('apiEndPoint') +'/api/v1/items/'+item.code+'/thumbnail?v='+item.updated.getTime()" />
|
||||
<img *ngIf="item.thumbnail" class="card-img-top" [src]="getThumbnailUrl(item)" />
|
||||
<div *ngIf="!item.thumbnail" class="big-icon" [style.background-color]="itemTypeService.getColor(item.itemType)">
|
||||
<i [ngClass]="itemTypeService.getIcon(item.itemType)"></i>
|
||||
</div>
|
||||
|
@@ -11,7 +11,8 @@ import { IItemLayer } from '../../models/item.layer';
|
||||
@Injectable()
|
||||
@Directive()
|
||||
export abstract class AbstractSelectedItemComponent {
|
||||
@Input() item: IItem
|
||||
@Input() item: IItem;
|
||||
@Input() parentItem: IItem;
|
||||
@Input() itemLayer: IItemLayer;
|
||||
@Input() overlayLayers: Array<IItemLayer>;
|
||||
constructor(public store: Store<mapReducers.State | commonReducers.State>, public itemTypeService: ItemTypeService, private location: Location, public router: Router) {
|
||||
@@ -60,6 +61,7 @@ export abstract class AbstractSelectedItemComponent {
|
||||
event.preventDefault();
|
||||
this.location.back();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -73,4 +75,8 @@ export class SelectedItemComponent extends AbstractSelectedItemComponent {
|
||||
constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, public config:AppConfig) {
|
||||
super(store, itemTypeService,location,router);
|
||||
}
|
||||
|
||||
getThumbnailUrl(item:IItem):string {
|
||||
return this.config.getConfig('apiEndPoint') +'/api/v1/items/'+item.code+'/thumbnail?v=' + Date.parse(item.updated);
|
||||
}
|
||||
}
|
||||
|
@@ -3,8 +3,8 @@ import { Injectable } from '@angular/core';
|
||||
import { Store, Action } from '@ngrx/store';
|
||||
import { Effect, Actions,ofType } from '@ngrx/effects';
|
||||
|
||||
import { Observable , of, interval } from 'rxjs';
|
||||
import { withLatestFrom, switchMap, map, catchError, mergeMap,delayWhen } from 'rxjs/operators';
|
||||
import { EMPTY, Observable , of } from 'rxjs';
|
||||
import { withLatestFrom, switchMap, map, catchError, mergeMap } from 'rxjs/operators';
|
||||
|
||||
import {GeoJSON,WKT} from 'ol/format';
|
||||
import {Feature} from 'ol';
|
||||
@@ -60,7 +60,7 @@ export class MapEffects {
|
||||
anchor: [0.5, 1],
|
||||
scale: 0.05,
|
||||
src: this.featureIconService$.getIconImageDataUrl("fa fa-file-o")
|
||||
}),
|
||||
}),
|
||||
stroke: new style.Stroke({
|
||||
color: 'red',
|
||||
width: 1
|
||||
@@ -82,7 +82,7 @@ export class MapEffects {
|
||||
fill: new style.Fill({
|
||||
color: 'rgba(0, 0, 255, 0.1)'
|
||||
})
|
||||
})));
|
||||
})));
|
||||
|
||||
return actions;
|
||||
}
|
||||
@@ -103,20 +103,13 @@ export class MapEffects {
|
||||
catchError(error => of(new commonActions.Fail(error))));
|
||||
}));
|
||||
|
||||
@Effect()
|
||||
initRootItems$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(commonActions.INITROOTSUCCESS),
|
||||
map((action) => new mapActions.Init()
|
||||
));
|
||||
|
||||
@Effect()
|
||||
startSearch$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(mapActions.STARTSEARCH),
|
||||
withLatestFrom(this.store$.select(mapReducers.selectgetSetStateCount)),
|
||||
switchMap(([action,setStateCount]) => {
|
||||
switchMap((action) => {
|
||||
let a = action as mapActions.StartSearch;
|
||||
var startDate = a.queryState.startDate;
|
||||
var endDate = a.queryState.endDate;
|
||||
var endDate = a.queryState.endDate;
|
||||
var newAction:Observable<Action>;
|
||||
if (a.queryState.itemCode || a.queryState.parentCode || a.queryState.itemType || a.queryState.query || a.queryState.tags) {
|
||||
newAction= this.itemService$.getFeatures(a.queryState.bbox, "EPSG:3857", a.queryState.query, a.queryState.tags, startDate, endDate, a.queryState.itemType, a.queryState.parentCode).pipe(
|
||||
@@ -126,13 +119,13 @@ export class MapEffects {
|
||||
f.id = f.properties["code"];
|
||||
}
|
||||
}
|
||||
return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState,setStateCount));
|
||||
return of(new mapActions.StartSearchSuccess(this._geojsonFormat.readFeatures(features), a.queryState));
|
||||
}
|
||||
),
|
||||
catchError(error => of(new commonActions.Fail(error))));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return newAction;
|
||||
}));
|
||||
|
||||
@@ -140,18 +133,19 @@ export class MapEffects {
|
||||
@Effect()
|
||||
zoomToExtent$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(mapActions.STARTSEARCHSUCCESS),
|
||||
delayWhen(action => (action as mapActions.StartSearchSuccess).setStateCount == 1 ? interval(500):interval(0)),
|
||||
mergeMap((action: mapActions.StartSearchSuccess) => {
|
||||
let actions =[];
|
||||
actions.push(new commonActions.SetMenuVisible(false));
|
||||
let extent = createEmpty();
|
||||
if (!action.query.bboxFilter) {
|
||||
if (!action.query.bboxFilter) {
|
||||
if (extent) {
|
||||
for (let f of action.features) {
|
||||
extend(extent, (f as Feature).getGeometry().getExtent());
|
||||
}
|
||||
}
|
||||
actions.push(new mapActions.SetExtent(extent));
|
||||
if(action.features && action.features.length >0) {
|
||||
actions.push(new mapActions.SetExtent(extent));
|
||||
}
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}));
|
||||
@@ -167,13 +161,19 @@ export class MapEffects {
|
||||
selectItem$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(mapActions.SELECTITEM),
|
||||
withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)),
|
||||
withLatestFrom(this.store$.select(mapReducers.getSetStateCount)),
|
||||
switchMap(([[action, selectedItem],setStateCount]) => {
|
||||
switchMap(([action, selectedItem]) => {
|
||||
let a = action as mapActions.SelectItem;
|
||||
let itemCode = selectedItem ? selectedItem.code : "";
|
||||
if (a.itemCode != itemCode || setStateCount == 1) {
|
||||
if (a.itemCode != itemCode) {
|
||||
return this.itemService$.getItem(a.itemCode).pipe(
|
||||
map((item: IItem) => new mapActions.SelectItemSuccess(item)),
|
||||
switchMap(child => {
|
||||
return this.itemService$.getItem(child.parentCode)
|
||||
.pipe(map(parent => {
|
||||
return {child, parent};
|
||||
}),catchError(() => { let parent:IItem = null;return of({child,parent})})
|
||||
);
|
||||
}),
|
||||
map(data => new mapActions.SelectItemSuccess(data.child, data.parent)),
|
||||
catchError(error => of(new commonActions.Fail(error))))
|
||||
} else {
|
||||
return [];
|
||||
@@ -202,14 +202,14 @@ export class MapEffects {
|
||||
if(action.item.itemType == "vnd.farmmaps.itemtype.temporal") {
|
||||
return this.itemService$.getChildItemList(action.item.code,null).pipe(
|
||||
map(items => new mapActions.SelectTemporalItemsSuccess(
|
||||
items.sort((a, b) =>
|
||||
-(b.dataDate.getTime() - a.dataDate.getTime())
|
||||
items.sort((a, b) =>
|
||||
-(Date.parse(b.dataDate) - Date.parse(a.dataDate))
|
||||
)
|
||||
)),
|
||||
catchError(error => of(new commonActions.Fail(error))));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@@ -231,9 +231,9 @@ export class MapEffects {
|
||||
feature = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (feature) {
|
||||
return of(new mapActions.UpdateFeatureSuccess(this.toPointFeature(deviceUpdateEventAction)));
|
||||
return of(new mapActions.UpdateFeatureSuccess(this.toPointFeature(deviceUpdateEventAction)));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
@@ -244,10 +244,17 @@ export class MapEffects {
|
||||
ofType(commonActions.ITEMCHANGEDEVENT),
|
||||
withLatestFrom(this.store$.select(mapReducers.selectGetSelectedItem)),
|
||||
mergeMap(([action, selectedItem]) => {
|
||||
let itemChangedAction = action as commonActions.ItemChangedEvent;
|
||||
let itemChangedAction = action as commonActions.ItemChangedEvent;
|
||||
if (selectedItem && selectedItem.code == itemChangedAction.itemCode) {
|
||||
return this.itemService$.getItem(itemChangedAction.itemCode).pipe(
|
||||
map((item: IItem) => new mapActions.SelectItemSuccess(item)),
|
||||
switchMap(child => {
|
||||
return this.itemService$.getItem(child.parentCode)
|
||||
.pipe(map(parent => {
|
||||
return {child, parent};
|
||||
})
|
||||
);
|
||||
}),
|
||||
map(data => new mapActions.SelectItemSuccess(data.child, data.parent)),
|
||||
catchError(error => of(new commonActions.Fail(error))));
|
||||
} else {
|
||||
return [];
|
||||
@@ -256,26 +263,18 @@ export class MapEffects {
|
||||
|
||||
getActionFromQueryState(queryState:IQueryState, inSearch:boolean):Observable<Action>|[] {
|
||||
if(!inSearch && (queryState.itemType || queryState.parentCode || queryState.itemCode || queryState.query || queryState.tags)) {
|
||||
var newAction:Action;
|
||||
if (queryState.itemCode && queryState.itemCode != "") {
|
||||
newAction= new mapActions.SelectItem(queryState.itemCode);
|
||||
var newAction:Action;
|
||||
if (queryState.itemCode && queryState.itemCode != "") {
|
||||
newAction= new mapActions.SelectItem(queryState.itemCode);
|
||||
} else {
|
||||
newAction= new mapActions.StartSearch(queryState);
|
||||
}
|
||||
return of(newAction);
|
||||
|
||||
} else {
|
||||
return of(new commonActions.Escape(true,false));
|
||||
newAction = new mapActions.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Effect()
|
||||
setQueryState$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(mapActions.SETQUERYSTATE),
|
||||
withLatestFrom(this.store$.select(mapReducers.selectGetInSearch)),
|
||||
switchMap(([action,inSearch]) => {
|
||||
let a = action as mapActions.SetQueryState;
|
||||
return this.getActionFromQueryState(a.queryState,inSearch);
|
||||
}));
|
||||
return of(newAction);
|
||||
}
|
||||
|
||||
@Effect()
|
||||
setState$: Observable<Action> = this.actions$.pipe(
|
||||
@@ -286,8 +285,20 @@ export class MapEffects {
|
||||
return this.getActionFromQueryState(a.queryState,inSearch);
|
||||
}));
|
||||
|
||||
@Effect()
|
||||
escape$:Observable<Action> = this.actions$.pipe(
|
||||
ofType(commonActions.ESCAPE),
|
||||
switchMap((action) => {
|
||||
let a = action as commonActions.Escape;
|
||||
if(a.escapeKey) {
|
||||
return of(new mapActions.Clear());
|
||||
} else {
|
||||
return EMPTY;
|
||||
}
|
||||
}));
|
||||
|
||||
constructor(private actions$: Actions, private store$: Store<mapReducers.State>, private folderService$: FolderService, private itemService$: ItemService,private featureIconService$:FeatureIconService,private itemTypeService$:ItemTypeService) {
|
||||
this._geojsonFormat = new GeoJSON();
|
||||
this._wktFormat = new WKT();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { IDataLayer } from '@farmmaps/common';
|
||||
|
||||
export interface IColor {
|
||||
red: number,
|
||||
green: number,
|
||||
@@ -26,6 +28,25 @@ export interface IHistogram {
|
||||
confidence?: number;
|
||||
}
|
||||
|
||||
export interface IStatistics {
|
||||
min: number;
|
||||
max: number;
|
||||
mean: number;
|
||||
stddev: number;
|
||||
populationCount: number;
|
||||
minPlus: number;
|
||||
maxMinus: number;
|
||||
mode: number;
|
||||
median: number;
|
||||
curtosis: number;
|
||||
skewness: number;
|
||||
variance: number;
|
||||
variationCoefficient: number;
|
||||
confidenceIntervalErrorMargin: number;
|
||||
confidenceIntervalLow: number;
|
||||
confidenceIntervalHigh: number;
|
||||
}
|
||||
|
||||
export interface IGradientstop {
|
||||
relativestop: number,
|
||||
color: IColor
|
||||
@@ -39,7 +60,8 @@ export interface IColorMap {
|
||||
}
|
||||
|
||||
export interface IBand {
|
||||
histogram: IHistogram
|
||||
histogram: IHistogram;
|
||||
statistics: IStatistics;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,11 +86,9 @@ export interface IRenderoutputImage {
|
||||
extent: [number,number,number,number]
|
||||
}
|
||||
|
||||
export interface ILayer {
|
||||
name: string,
|
||||
unit: string,
|
||||
index: number,
|
||||
scale: number,
|
||||
export interface ILayer extends IDataLayer {
|
||||
renderer: IRenderer,
|
||||
rendering: IRenderoutput
|
||||
rendering: IRenderoutput,
|
||||
minzoom: number,
|
||||
maxzoom: number
|
||||
}
|
||||
|
@@ -44,9 +44,10 @@ export interface State {
|
||||
panelCollapsed: boolean,
|
||||
selectedFeature: Feature,
|
||||
selectedItem:IItem,
|
||||
parentItem:IItem,
|
||||
clearEnabled: boolean,
|
||||
searchCollapsed: boolean,
|
||||
searchMinified: boolean,
|
||||
searchMinified: boolean,
|
||||
extent: number[],
|
||||
baseLayers: Array<IItemLayer>
|
||||
overlayLayers: Array<IItemLayer>,
|
||||
@@ -56,8 +57,8 @@ export interface State {
|
||||
selectedOverlayLayer: IItemLayer,
|
||||
styles:IStyles,
|
||||
showLayerSwitcher:boolean,
|
||||
setStateCount:number,
|
||||
inSearch:boolean,
|
||||
inZoom:boolean,
|
||||
replaceUrl:boolean
|
||||
}
|
||||
|
||||
@@ -81,7 +82,8 @@ export const initialState: State = {
|
||||
panelVisible: false,
|
||||
panelCollapsed: false,
|
||||
selectedFeature: null,
|
||||
selectedItem: null,
|
||||
selectedItem: null,
|
||||
parentItem: null,
|
||||
clearEnabled: false,
|
||||
searchCollapsed: true,
|
||||
searchMinified:false,
|
||||
@@ -94,12 +96,12 @@ export const initialState: State = {
|
||||
selectedItemLayer: null,
|
||||
styles: {},
|
||||
showLayerSwitcher: false,
|
||||
setStateCount: 0,
|
||||
inSearch:false,
|
||||
inZoom:false,
|
||||
replaceUrl:true
|
||||
}
|
||||
|
||||
export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State {
|
||||
export function reducer(state = initialState, action: mapActions.Actions | commonActions.Actions | RouterNavigationAction): State {
|
||||
switch (action.type) {
|
||||
case ROUTER_NAVIGATION: {
|
||||
let a = action as RouterNavigationAction;
|
||||
@@ -108,16 +110,16 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
case mapActions.SETMAPSTATE: {
|
||||
let a = action as mapActions.SetMapState;
|
||||
return tassign(state, {
|
||||
mapState: a.mapState,setStateCount: state.setStateCount+1
|
||||
mapState: a.mapState
|
||||
});
|
||||
}
|
||||
case mapActions.SETQUERYSTATE: {
|
||||
let a = action as mapActions.SetQueryState;
|
||||
return tassign(state, { queryState: tassign(a.queryState ),setStateCount: state.setStateCount+1,replaceUrl:a.replaceUrl});
|
||||
return tassign(state, { queryState: tassign(a.queryState ),replaceUrl:a.replaceUrl});
|
||||
}
|
||||
case mapActions.SETSTATE: {
|
||||
let a = action as mapActions.SetState;
|
||||
return tassign(state, { mapState: tassign(a.mapState), queryState: tassign(a.queryState),setStateCount: state.setStateCount+1});
|
||||
return tassign(state, { mapState: tassign(a.mapState), queryState: tassign(a.queryState)});
|
||||
}
|
||||
case mapActions.SETVIEWEXTENT: {
|
||||
let a = action as mapActions.SetViewExtent;
|
||||
@@ -125,13 +127,13 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
}
|
||||
case mapActions.SETPARENT: {
|
||||
let a = action as mapActions.SetParent;
|
||||
return tassign(state, {
|
||||
return tassign(state, {
|
||||
parentCode : a.parentCode
|
||||
});
|
||||
}
|
||||
case mapActions.STARTSEARCHSUCCESS: {
|
||||
let a = action as mapActions.StartSearchSuccess;
|
||||
return tassign(state, {
|
||||
return tassign(state, {
|
||||
features: a.features,
|
||||
inSearch:false
|
||||
});
|
||||
@@ -141,11 +143,11 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
return tassign(state, {
|
||||
selectedFeature: state.selectedItem?state.selectedFeature: a.feature
|
||||
});
|
||||
}
|
||||
case mapActions.SELECTITEM: {
|
||||
}
|
||||
case mapActions.SELECTITEM: {
|
||||
let a = action as mapActions.SelectItem;
|
||||
let itemCode = state.selectedItem ? state.selectedItem.code : "";
|
||||
let inSearch = (a.itemCode != itemCode || state.setStateCount == 1)
|
||||
let inSearch = a.itemCode != itemCode;
|
||||
return tassign(state, {
|
||||
selectedItem: null,
|
||||
selectedItemLayer: null,
|
||||
@@ -160,11 +162,12 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
itemLayer = new ItemLayer(a.item);
|
||||
itemLayer.layerIndex = a.item.data.layers?a.item.data.layers[0].index:-1;
|
||||
} else if (a.item && a.item.itemType == "vnd.farmmaps.itemtype.temporal") {
|
||||
itemLayer = new TemporalItemLayer(a.item);
|
||||
itemLayer = new TemporalItemLayer(a.item);
|
||||
}
|
||||
return tassign(state, {
|
||||
inSearch:false,
|
||||
selectedItem: a.item,
|
||||
parentItem: a.parentItem,
|
||||
selectedItemLayer: itemLayer,
|
||||
panelVisible: a.item != null,
|
||||
clearEnabled: a.item != null,
|
||||
@@ -181,7 +184,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
if(a.temporalItems.length>0) {
|
||||
let item = a.temporalItems[a.temporalItems.length-1];
|
||||
layerIndex = item.data.layers[0].index;
|
||||
selectedItemLayer.selectedItemLayer = new ItemLayer(item,1,true,layerIndex);
|
||||
selectedItemLayer.selectedItemLayer = new ItemLayer(item,1,true,layerIndex);
|
||||
} else {
|
||||
selectedItemLayer.selectedItemLayer = null;
|
||||
}
|
||||
@@ -189,9 +192,9 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
selectedItemLayer.nextItemLayer = null;
|
||||
if(selectedItemLayer.selectedItemLayer) {
|
||||
let layerIndex = selectedItemLayer.selectedItemLayer.item.data.layers[0].index;
|
||||
selectedItemLayer.layerIndex = layerIndex;
|
||||
selectedItemLayer.layerIndex = layerIndex;
|
||||
}
|
||||
|
||||
|
||||
return tassign(state,{selectedItemLayer:tassign(state.selectedItemLayer,selectedItemLayer as ItemLayer)});
|
||||
}
|
||||
case mapActions.NEXTTEMPORAL: {
|
||||
@@ -234,11 +237,11 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
temporalLayer.nextItemLayer.opacity=0;
|
||||
temporalLayer.nextItemLayer.layerIndex = temporalLayer.layerIndex;
|
||||
}
|
||||
temporalLayer.selectedItemLayer = temporalLayer.previousItemLayer;
|
||||
temporalLayer.selectedItemLayer = temporalLayer.previousItemLayer;
|
||||
if( temporalLayer.selectedItemLayer) {
|
||||
temporalLayer.selectedItemLayer.opacity=1;
|
||||
temporalLayer.selectedItemLayer.layerIndex = temporalLayer.layerIndex;
|
||||
}
|
||||
}
|
||||
temporalLayer.previousItemLayer = index-2 >=0? new ItemLayer(temporalLayer.temporalItems[index-2],0,true,temporalLayer.layerIndex):null;
|
||||
if( temporalLayer.previousItemLayer) {
|
||||
temporalLayer.previousItemLayer.opacity=0;
|
||||
@@ -289,7 +292,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
});
|
||||
}
|
||||
case mapActions.UPDATEFEATURESUCCESS: {
|
||||
let a = action as mapActions.UpdateFeatureSuccess;
|
||||
let a = action as mapActions.UpdateFeatureSuccess;
|
||||
let features: any[] = [];
|
||||
var index = -1;
|
||||
for (var i = 0; i < state.features.length; i++) {
|
||||
@@ -307,8 +310,11 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
case mapActions.COLLAPSESEARCH: {
|
||||
return tassign(state, { searchCollapsed: state.panelVisible ? false: true});
|
||||
}
|
||||
case commonActions.CLOSEALL: {
|
||||
return tassign(state, { searchCollapsed: state.panelVisible ? false: true,showLayerSwitcher:false});
|
||||
}
|
||||
case mapActions.SETEXTENT: {
|
||||
let a = action as mapActions.SetExtent;
|
||||
let a = action as mapActions.SetExtent;
|
||||
return tassign(state, { extent: a.extent });
|
||||
}
|
||||
case mapActions.ADDLAYER: {
|
||||
@@ -323,7 +329,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
case mapActions.REMOVELAYER: {
|
||||
let a = action as mapActions.RemoveLayer;
|
||||
@@ -353,7 +359,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
}
|
||||
case mapActions.SETLAYERINDEX: {
|
||||
let a = action as mapActions.SetLayerIndex;
|
||||
if (a.itemLayer == null) {
|
||||
if (a.itemLayer == null) {
|
||||
if(state.selectedItemLayer.item.itemType == "vnd.farmmaps.itemtype.temporal") {
|
||||
var newItemlayer = tassign(state.selectedItemLayer,{layerIndex:a.layerIndex});
|
||||
let tl = newItemlayer as ITemporalItemLayer;
|
||||
@@ -381,7 +387,7 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
} else {
|
||||
var newItemlayer = new ItemLayer(state.selectedItemLayer.item);
|
||||
newItemlayer.layerIndex = a.layerIndex;
|
||||
|
||||
|
||||
}
|
||||
return tassign(state, { selectedItemLayer: newItemlayer})
|
||||
} else {
|
||||
@@ -401,9 +407,13 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
}
|
||||
var selectedBaseLayer: IItemLayer = null;
|
||||
var mapState = tassign(state.mapState);
|
||||
let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode)[0];
|
||||
if (baseLayers.length > 0 && mapState.baseLayerCode != "" && sb) {
|
||||
selectedBaseLayer = sb;
|
||||
let sb = baseLayers.filter(layer => layer.item.code === mapState.baseLayerCode);
|
||||
let db = baseLayers.filter(layer => layer.item.data && layer.item.data.default === true);
|
||||
if (baseLayers.length > 0 && mapState.baseLayerCode != "" && sb.length>0) {
|
||||
selectedBaseLayer = sb[0];
|
||||
selectedBaseLayer.visible = true;
|
||||
} else if (baseLayers.length >0 && db.length>0){
|
||||
selectedBaseLayer = db[0];
|
||||
selectedBaseLayer.visible = true;
|
||||
} else if (baseLayers.length > 0) {
|
||||
selectedBaseLayer = baseLayers[0];
|
||||
@@ -426,27 +436,24 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
let a = action as mapActions.SelectOverlayLayer;
|
||||
return tassign(state, { selectedOverlayLayer: a.itemLayer });
|
||||
}
|
||||
case commonActions.ESCAPE: {
|
||||
let a = action as commonActions.Escape;
|
||||
|
||||
case mapActions.CLEAR: {
|
||||
let newQueryState = tassign(state.queryState, { query: null, tags: null, itemCode: null, parentCode: null, itemType: null });
|
||||
if (a.escapeKey) {
|
||||
return tassign(state, {
|
||||
panelVisible: false,
|
||||
panelCollapsed:false,
|
||||
selectedItem: null,
|
||||
selectedItemLayer: null,
|
||||
selectedFeature: null,
|
||||
queryState: newQueryState,
|
||||
clearEnabled: false,
|
||||
searchCollapsed: true,
|
||||
searchMinified: false,
|
||||
features: [],
|
||||
query:initialState.query,
|
||||
showLayerSwitcher: false
|
||||
});
|
||||
} else {
|
||||
return tassign(state, {});
|
||||
}
|
||||
return tassign(state, {
|
||||
panelVisible: false,
|
||||
panelCollapsed:false,
|
||||
selectedItem: null,
|
||||
selectedItemLayer: null,
|
||||
selectedFeature: null,
|
||||
queryState: newQueryState,
|
||||
clearEnabled: false,
|
||||
searchCollapsed: true,
|
||||
searchMinified: false,
|
||||
features: [],
|
||||
query:initialState.query,
|
||||
showLayerSwitcher: false,
|
||||
extent: null
|
||||
});
|
||||
}
|
||||
case mapActions.SETSTYLE:{
|
||||
let a = action as mapActions.SetStyle;
|
||||
@@ -457,14 +464,11 @@ export function reducer(state = initialState, action: mapActions.Actions | commo
|
||||
case mapActions.SHOWLAYERSWITCHER:{
|
||||
let a = action as mapActions.ShowLayerSwitcher;
|
||||
return tassign(state,{showLayerSwitcher:a.show});
|
||||
}
|
||||
}
|
||||
case mapActions.SETREPLACEURL: {
|
||||
let a= action as mapActions.SetReplaceUrl;
|
||||
return tassign(state,{replaceUrl:a.replaceUrl});
|
||||
}
|
||||
case mapActions.CLEAR: {
|
||||
return tassign(state,{setStateCount:0});
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
@@ -478,6 +482,7 @@ export const getPanelVisible = (state: State) => state.panelVisible;
|
||||
export const getPanelCollapsed = (state: State) => state.panelCollapsed;
|
||||
export const getSelectedFeature = (state: State) => state.selectedFeature;
|
||||
export const getSelectedItem = (state: State) => state.selectedItem;
|
||||
export const getParentItem = (state: State) => state.parentItem;
|
||||
export const getQueryState = (state: State) => state.queryState;
|
||||
export const getClearEnabled = (state: State) => state.clearEnabled;
|
||||
export const getSearchCollapsed = (state: State) => state.searchCollapsed;
|
||||
@@ -493,9 +498,8 @@ export const getSelectedItemLayer = (state: State) => state.selectedItemLayer;
|
||||
export const getPeriod = (state:State) => state.period;
|
||||
export const getStyles = (state:State) => state.styles;
|
||||
export const getShowLayerSwitcher = (state:State) => state.showLayerSwitcher;
|
||||
export const getSetStateCount = (state:State) => state.setStateCount;
|
||||
export const getInSearch = (state:State) => state.inSearch;
|
||||
export const getState = (state:State) => {return {mapState:state.mapState,queryState:state.queryState,setStateCount:state.setStateCount,replaceUrl:state.replaceUrl};}
|
||||
export const getState = (state:State) => {return {mapState:state.mapState,queryState:state.queryState,replaceUrl:state.replaceUrl};}
|
||||
|
||||
export const selectMapState = createFeatureSelector<State>(MODULE_NAME);
|
||||
export const selectGetMapState= createSelector(selectMapState, getMapState);
|
||||
@@ -505,6 +509,7 @@ export const selectGetPanelVisible = createSelector(selectMapState, getPanelVisi
|
||||
export const selectGetPanelCollapsed = createSelector(selectMapState, getPanelCollapsed);
|
||||
export const selectGetSelectedFeature = createSelector(selectMapState, getSelectedFeature);
|
||||
export const selectGetSelectedItem = createSelector(selectMapState, getSelectedItem);
|
||||
export const selectGetParentItem = createSelector(selectMapState, getParentItem);
|
||||
export const selectGetQueryState = createSelector(selectMapState, getQueryState);
|
||||
export const selectGetClearEnabled = createSelector(selectMapState, getClearEnabled);
|
||||
export const selectGetSearchCollapsed = createSelector(selectMapState, getSearchCollapsed);
|
||||
@@ -515,12 +520,11 @@ export const selectGetBaseLayers = createSelector(selectMapState, getBaseLayers)
|
||||
export const selectGetProjection = createSelector(selectMapState, getProjection);
|
||||
export const selectGetSelectedBaseLayer = createSelector(selectMapState, getSelectedBaseLayer);
|
||||
export const selectGetSelectedOverlayLayer = createSelector(selectMapState, getSelectedOverlayLayer);
|
||||
export const selectGetQuery = createSelector(selectMapState, getQuery);
|
||||
export const selectGetQuery = createSelector(selectMapState, getQuery);
|
||||
export const selectGetSelectedItemLayer = createSelector(selectMapState, getSelectedItemLayer);
|
||||
export const selectGetPeriod = createSelector(selectMapState, getPeriod);
|
||||
export const selectGetStyles = createSelector(selectMapState, getStyles);
|
||||
export const selectGetShowLayerSwitcher = createSelector(selectMapState,getShowLayerSwitcher);
|
||||
export const selectgetSetStateCount = createSelector(selectMapState,getSetStateCount);
|
||||
export const selectGetInSearch = createSelector(selectMapState,getInSearch);
|
||||
export const selectGetState = createSelector(selectMapState,getState);
|
||||
|
||||
|
24
projects/common-map3d/README.md
Normal file
24
projects/common-map3d/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# CommonMap3d
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.3.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project common-map3d` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project common-map3d`.
|
||||
> Note: Don't forget to add `--project common-map3d` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build common-map3d` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build common-map3d`, go to the dist folder `cd dist/common-map3d` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test common-map3d` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
32
projects/common-map3d/karma.conf.js
Normal file
32
projects/common-map3d/karma.conf.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../coverage/common-map3d'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
7
projects/common-map3d/ng-package.json
Normal file
7
projects/common-map3d/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/common-map3d",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
17
projects/common-map3d/package.json
Normal file
17
projects/common-map3d/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@farmmaps/common-map3d",
|
||||
"version": "0.0.1",
|
||||
"publishConfig": {
|
||||
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^10.1.3",
|
||||
"@angular/core": "^10.1.3",
|
||||
"ngx-openlayers": "1.0.0-next.16",
|
||||
"cesium": "^1.73.0",
|
||||
"ol-cesium": "^2.11.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0"
|
||||
}
|
||||
}
|
11
projects/common-map3d/src/fm-map3d/common-map3d.module.ts
Normal file
11
projects/common-map3d/src/fm-map3d/common-map3d.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Switch2D3DComponent } from './components/olcs/switch2d3d/switch2d3d.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [Switch2D3DComponent],
|
||||
imports: [
|
||||
],
|
||||
exports: [Switch2D3DComponent]
|
||||
})
|
||||
export class CommonMap3dModule { }
|
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CommonMap3dService } from './common-map3d.service';
|
||||
|
||||
describe('CommonMap3dService', () => {
|
||||
let service: CommonMap3dService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(CommonMap3dService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CommonMap3dService {
|
||||
|
||||
constructor() { }
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
||||
|
||||
.twotreed {
|
||||
display:block;
|
||||
width:2.5em;
|
||||
height:2.5em;
|
||||
background-color: $body-bg;
|
||||
background-size: contain;
|
||||
margin-top:0.5em;
|
||||
text-align: center;
|
||||
line-height: 2.5em;
|
||||
border-radius: 1.75em;
|
||||
padding: 0;
|
||||
color: $secondary;
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
import { Component, OnInit,Input,Host } from '@angular/core';
|
||||
import { MapComponent } from 'ngx-openlayers';
|
||||
import OLCesium from 'ol-cesium';
|
||||
|
||||
declare var olcs: any;
|
||||
|
||||
@Component({
|
||||
selector: 'fm-map3d-switch2d3d',
|
||||
template: '<div (click)="handleClick($event)" class="btn btn-outline-secondary twotreed">{{label}}</div>',
|
||||
styleUrls: ['./switch2d3d.component.scss']
|
||||
})
|
||||
export class Switch2D3DComponent {
|
||||
|
||||
@Input() enable:boolean;
|
||||
public label: string = "3D";
|
||||
private ol3d: OLCesium;
|
||||
private synchronizers:any[];
|
||||
|
||||
|
||||
constructor(@Host() private map: MapComponent) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
this.ol3d = new OLCesium({ map: this.map.instance, createSynchronizers: (map,scene) => {
|
||||
this.synchronizers = [
|
||||
new olcs.RasterSynchronizer(map,scene),
|
||||
new olcs.VectorSynchronizer(map,scene)
|
||||
];
|
||||
return this.synchronizers;
|
||||
}, stopOpenLayersEventsPropagation:true});
|
||||
}
|
||||
|
||||
synchronize() {
|
||||
this.synchronizers.forEach((synchronizer) => {
|
||||
synchronizer.synchronize();
|
||||
});
|
||||
}
|
||||
|
||||
handleClick(event) {
|
||||
this.enable = !this.enable;
|
||||
if(this.enable) {
|
||||
this.synchronize();
|
||||
}
|
||||
this.ol3d.setEnabled(this.enable);
|
||||
this.label = this.enable?"2D":"3D";
|
||||
}
|
||||
}
|
7
projects/common-map3d/src/public-api.ts
Normal file
7
projects/common-map3d/src/public-api.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Public API Surface of common-map3d
|
||||
*/
|
||||
|
||||
export * from './fm-map3d/common-map3d.service';
|
||||
export * from './fm-map3d/components/olcs/switch2d3d/switch2d3d.component';
|
||||
export * from './fm-map3d/common-map3d.module';
|
26
projects/common-map3d/src/test.ts
Normal file
26
projects/common-map3d/src/test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/dist/zone';
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(path: string, deep?: boolean, filter?: RegExp): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
32
projects/common-map3d/tsconfig.lib.json
Normal file
32
projects/common-map3d/tsconfig.lib.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"fullTemplateTypeCheck": true,
|
||||
"strictInjectionParameters": true,
|
||||
"enableResourceInlining": true,
|
||||
"enableIvy": false
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
10
projects/common-map3d/tsconfig.lib.prod.json
Normal file
10
projects/common-map3d/tsconfig.lib.prod.json
Normal file
@@ -0,0 +1,10 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"compilerOptions": {
|
||||
"declarationMap": false
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": false
|
||||
}
|
||||
}
|
17
projects/common-map3d/tsconfig.spec.json
Normal file
17
projects/common-map3d/tsconfig.spec.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
17
projects/common-map3d/tslint.json
Normal file
17
projects/common-map3d/tslint.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [
|
||||
true,
|
||||
"attribute",
|
||||
"lib",
|
||||
"camelCase"
|
||||
],
|
||||
"component-selector": [
|
||||
true,
|
||||
"element",
|
||||
"lib",
|
||||
"kebab-case"
|
||||
]
|
||||
}
|
||||
}
|
32
projects/common/package-lock.json
generated
32
projects/common/package-lock.json
generated
@@ -1,35 +1,5 @@
|
||||
{
|
||||
"name": "@farmmaps/common",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"angular-oauth2-oidc": {
|
||||
"version": "8.0.4",
|
||||
"resolved": "https://registry.npmjs.org/angular-oauth2-oidc/-/angular-oauth2-oidc-8.0.4.tgz",
|
||||
"integrity": "sha512-7/3niJBqD7rnElcW+SudE36g7zMWChW4gSq7NpJSzDA4aQadBgvg0hn317MfPm9tpYGrpE3G/z2NMnbzek4TMA==",
|
||||
"requires": {
|
||||
"jsrsasign": "^8.0.12",
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"jsrsasign": {
|
||||
"version": "8.0.12",
|
||||
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.12.tgz",
|
||||
"integrity": "sha1-Iqu5ZW00owuVMENnIINeicLlwxY="
|
||||
},
|
||||
"ngx-uploadx": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ngx-uploadx/-/ngx-uploadx-3.3.2.tgz",
|
||||
"integrity": "sha512-gRdXXq2cRU9HE6dj65qay9GV8NRC7n8y5LtMzJWqsfu2k3CHMQxo2TqZwA9/l/PqJ76RoO7sTPy1OenFQ+krkQ==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
||||
}
|
||||
}
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
|
@@ -3,20 +3,21 @@
|
||||
"version": "0.0.1",
|
||||
"publishConfig": {
|
||||
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
|
||||
},
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ng-bootstrap/ng-bootstrap": "^6.0",
|
||||
"@angular/common": "~9.1.0",
|
||||
"@angular/core": "~9.1.0",
|
||||
"@angular/forms": "~9.1.0",
|
||||
"@ngrx/effects": "^9.0",
|
||||
"@ngrx/router-store": "^9.0",
|
||||
"@ngrx/store": "^9.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^7.0",
|
||||
"@angular/common": "~10.1.3",
|
||||
"@angular/core": "~10.1.3",
|
||||
"@angular/forms": "~10.1.3",
|
||||
"@ngrx/effects": "^10.0",
|
||||
"@ngrx/router-store": "^10.0",
|
||||
"@ngrx/store": "^10.0",
|
||||
"tassign": "^1.0.0",
|
||||
"bootstrap": "^4.4.1",
|
||||
"@aspnet/signalr": "^1.1.4",
|
||||
"@microsoft/signalr": "^3.1.3",
|
||||
"font-awesome": "^4.7.0",
|
||||
"ngx-uploadx": "^3.3.4",
|
||||
"angular-oauth2-oidc": "^9.1"
|
||||
"angular-oauth2-oidc": "^10.0.3",
|
||||
"moment": "^2.27.0"
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import { IItemTypes } from '../models/item.types';
|
||||
import { IListItem } from '../models/list.item';
|
||||
import { IUser } from '../models/user';
|
||||
import { IItem } from '../models/item';
|
||||
import { UserInfo } from 'angular-oauth2-oidc';
|
||||
|
||||
export const INITUSER = '[AppCommon] InitUser';
|
||||
export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess';
|
||||
@@ -11,13 +12,18 @@ export const INITUSERSUCCESS = '[AppCommon] InitUserSuccess';
|
||||
export const INITUSERPACKAGES = '[AppCommon] InitUserPackages';
|
||||
export const INITUSERPACKAGESSUCCESS = '[AppCommon] InitUserPackagesSuccess';
|
||||
|
||||
export const INITUSERSETTINGSROOT = '[AppCommon] InitUserSettingsRoot';
|
||||
export const INITUSERSETTINGSROOTSUCCESS = '[AppCommon] InitUserSettingsRootSuccess';
|
||||
|
||||
export const INITROOT = '[Explorer] InitRoot';
|
||||
export const INITROOTSUCCESS = '[Explorer] InitRootSuccess';
|
||||
|
||||
export const OPENMODAL = '[AppCommon] OpenModal';
|
||||
export const CLOSEMODAL = '[AppCommon] CloseModal';
|
||||
export const LOGIN = '[AppCommon] Login';
|
||||
export const LOGOUT = '[AppCommon] Logout';
|
||||
export const ESCAPE = '[AppCommon] Escape';
|
||||
export const CLOSEALL = '[AppCommon] CloseAll';
|
||||
|
||||
export const LOADITEMTYPES = '[AppCommon] LoadItemTypes';
|
||||
export const LOADITEMTYPESSUCCESS = '[AppCommon] LoadItemTypesSuccess';
|
||||
@@ -28,7 +34,8 @@ export const ITEMDELETEDEVENT = '[AppCommon] ItemDeletedEvent';
|
||||
|
||||
export const TASKSTARTEVENT = '[AppCommon] TaskStartEvent';
|
||||
export const TASKENDEVENT = '[AppCommon] TaskEndEvent';
|
||||
export const TASKERRORTEVENT = '[AppCommon] TaskErrorEvent';
|
||||
export const TASKERROREVENT = '[AppCommon] TaskErrorEvent';
|
||||
export const TASKPROGRESSEVENT = '[AppCommon] TaskProgressEvent';
|
||||
|
||||
export const DEVICEUPDATEEVENT = '[AppCommon] DeviceUpdateEvent';
|
||||
|
||||
@@ -51,8 +58,16 @@ export const UPLOADEDFILECLICK = '[AppCommon] UploadedFileClick';
|
||||
|
||||
export const TOGGLEMENU = '[AppCommon] ToggleMenu';
|
||||
|
||||
export const TOGGLEACCOUNTMENU = '[AppCommon] ToggleAccountMenu';
|
||||
|
||||
export const SETMENUVISIBLE = '[AppCommon] SetMenuVisible';
|
||||
|
||||
export const ONLINE = '[AppCommon] Online';
|
||||
|
||||
export const OFFLINE = '[AppCommon] Offline';
|
||||
|
||||
export const SETPAGEMODE = '[AppCommon] SetPageMode';
|
||||
|
||||
export class InitUser implements Action {
|
||||
readonly type = INITUSER;
|
||||
|
||||
@@ -62,7 +77,13 @@ export class InitUser implements Action {
|
||||
export class InitUserSuccess implements Action {
|
||||
readonly type = INITUSERSUCCESS;
|
||||
|
||||
constructor(public user:IUser ) { }
|
||||
constructor(public user:IUser,public userinfo:UserInfo ) { }
|
||||
}
|
||||
|
||||
export class InitUserPackages implements Action {
|
||||
readonly type = INITUSERPACKAGES;
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
export class InitUserPackagesSuccess implements Action {
|
||||
@@ -71,6 +92,18 @@ export class InitUserPackagesSuccess implements Action {
|
||||
constructor(public items:IItem[] ) { }
|
||||
}
|
||||
|
||||
export class InitUserSettingsRoot implements Action {
|
||||
readonly type = INITUSERSETTINGSROOT;
|
||||
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
export class InitUserSettingsRootSuccess implements Action {
|
||||
readonly type = INITUSERSETTINGSROOTSUCCESS;
|
||||
|
||||
constructor(public item: IItem ) { }
|
||||
}
|
||||
|
||||
export class InitRoot implements Action {
|
||||
readonly type = INITROOT;
|
||||
|
||||
@@ -95,6 +128,12 @@ export class CloseModal implements Action {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class CloseAll implements Action {
|
||||
readonly type = CLOSEALL;
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class StartRouteLoading implements Action {
|
||||
readonly type = STARTROUTELOADING;
|
||||
|
||||
@@ -113,6 +152,12 @@ export class Login implements Action {
|
||||
constructor(public url: string) { }
|
||||
}
|
||||
|
||||
export class Logout implements Action {
|
||||
readonly type = LOGOUT;
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class Escape implements Action {
|
||||
readonly type = ESCAPE;
|
||||
|
||||
@@ -168,7 +213,13 @@ export class TaskEndEvent implements Action {
|
||||
}
|
||||
|
||||
export class TaskErrorEvent implements Action {
|
||||
readonly type = TASKERRORTEVENT;
|
||||
readonly type = TASKERROREVENT;
|
||||
|
||||
constructor(public itemCode: string, public attributes: any) { }
|
||||
}
|
||||
|
||||
export class TaskProgressEvent implements Action {
|
||||
readonly type = TASKPROGRESSEVENT;
|
||||
|
||||
constructor(public itemCode: string, public attributes: any) { }
|
||||
}
|
||||
@@ -226,11 +277,35 @@ export class ToggleMenu implements Action {
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class ToggleAccountMenu implements Action {
|
||||
readonly type = TOGGLEACCOUNTMENU;
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class SetMenuVisible implements Action {
|
||||
readonly type = SETMENUVISIBLE;
|
||||
|
||||
constructor(public visible:boolean) { }
|
||||
}
|
||||
|
||||
export class Online implements Action {
|
||||
readonly type = ONLINE;
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class Offline implements Action {
|
||||
readonly type = OFFLINE;
|
||||
|
||||
constructor() { }
|
||||
}
|
||||
|
||||
export class SetPageMode implements Action {
|
||||
readonly type = SETPAGEMODE;
|
||||
|
||||
constructor(public pageMode:boolean) {}
|
||||
}
|
||||
|
||||
|
||||
export type Actions = OpenModal
|
||||
@@ -238,6 +313,7 @@ export type Actions = OpenModal
|
||||
| InitRootSuccess
|
||||
| CloseModal
|
||||
| Login
|
||||
| Logout
|
||||
| ItemChangedEvent
|
||||
| ItemAddedEvent
|
||||
| ItemDeletedEvent
|
||||
@@ -258,8 +334,18 @@ export type Actions = OpenModal
|
||||
| TaskStartEvent
|
||||
| TaskEndEvent
|
||||
| TaskErrorEvent
|
||||
| TaskProgressEvent
|
||||
| DeviceUpdateEvent
|
||||
| ToggleMenu
|
||||
| SetMenuVisible
|
||||
| InitUserPackagesSuccess;
|
||||
| InitUserPackages
|
||||
| InitUserPackagesSuccess
|
||||
| InitUserSettingsRoot
|
||||
| InitUserSettingsRootSuccess
|
||||
| ToggleAccountMenu
|
||||
| CloseAll
|
||||
| Online
|
||||
| Offline
|
||||
| SetPageMode;
|
||||
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import { OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
|
||||
|
||||
//components
|
||||
import { ItemTypeService } from './services/itemtype.service';
|
||||
import { SchemaService } from './services/schema.service';
|
||||
import { FolderService } from './services/folder.service';
|
||||
import { TimespanService } from './services/timespan.service';
|
||||
import { ItemService } from './services/item.service';
|
||||
@@ -22,12 +23,14 @@ import { appConfigFactory } from "./shared/app.config.factory";
|
||||
import { AuthGuard } from './services/auth-guard.service';
|
||||
import { NavBarGuard } from './services/nav-bar-guard.service';
|
||||
import { FullScreenGuard } from './services/full-screen-guard.service';
|
||||
import { CodeListItemService } from './services/codelistitem.service';
|
||||
import { AuthCallbackGuard } from './components/auth-callback/auth-callback.guard';
|
||||
import { ResumableFileUploadService } from './components/resumable-file-upload/resumable-file-upload.service';
|
||||
import { NgbDateNativeAdapter } from './services/date-adapter.service'
|
||||
import { AuthConfigFactory } from './shared/authconfigFactory';
|
||||
import { StateSerializerService } from './services/state-serializer.service';
|
||||
import { PackageService } from './services/package.service';
|
||||
import { PackagePreloadStrategy } from './services/package-preload-strategy.service';
|
||||
import { SenmlService } from './services/senml-service';
|
||||
|
||||
export {
|
||||
FolderService,
|
||||
@@ -38,7 +41,6 @@ export {
|
||||
TypeaheadService,
|
||||
UserService,
|
||||
WeatherService,
|
||||
CodeListItemService,
|
||||
AppConfig,
|
||||
AccessTokenInterceptor,
|
||||
AuthGuard,
|
||||
@@ -47,7 +49,11 @@ export {
|
||||
AuthCallbackGuard,
|
||||
ResumableFileUploadService,
|
||||
NgbDateNativeAdapter,
|
||||
StateSerializerService
|
||||
StateSerializerService,
|
||||
SchemaService,
|
||||
PackageService,
|
||||
SenmlService,
|
||||
PackagePreloadStrategy
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
@@ -71,7 +77,7 @@ export class AppCommonServiceModule {
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: appConfigFactory,
|
||||
deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage,ItemTypeService],
|
||||
deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage, ItemTypeService],
|
||||
multi: true
|
||||
},
|
||||
{
|
||||
|
@@ -31,6 +31,8 @@ import { TimespanComponent } from './components/timespan/timespan.component';
|
||||
import { TagInputComponent } from './components/tag-input/tag-input.component';
|
||||
import { MenuBackgroundComponent } from './components/menu-background/menu-background.component';
|
||||
import { HasPackageDirective} from './components/has-package/has-package.directive';
|
||||
import { HasClaimDirective} from './components/has-claim/has-claim.directive';
|
||||
import { UserMenuComponent} from './components/user-menu/user-menu.component';
|
||||
import { Alert } from './enumerations/alert.enum';
|
||||
import { IEventMessage } from './models/event.message';
|
||||
import { IItem, Item } from './models/item';
|
||||
@@ -39,15 +41,20 @@ import { IItemType } from './models/item.type';
|
||||
import { IItemTypes } from './models/item.types';
|
||||
import { IItemTask, ItemTask } from './models/itemTask';
|
||||
import { IListItem } from './models/list.item';
|
||||
import { ITypeaheadItem } from './models/typeahead.item';
|
||||
import { ITypeaheadItem } from './models/typeahead.item'
|
||||
import { IJsonline } from './models/json-line';
|
||||
import { ISenMLItem } from './models/senml-item';
|
||||
import { IPackage,IPackages } from './models/package';
|
||||
import { IUser } from './models/user';
|
||||
import { IQueryState } from './models/query.state';
|
||||
import { ICodeListItem } from './models/code.list.item';
|
||||
import { IDataLayer } from './models/data.layer';
|
||||
import { IColor,IGradientstop} from './models/gradient';
|
||||
import * as commonActions from './actions/app-common.actions';
|
||||
import * as commonReducers from './reducers/app-common.reducer';
|
||||
import * as commonEffects from './effects/app-common.effects';
|
||||
import { SecureOAuthStorage} from './shared/secureOAuthStorage';
|
||||
import { GradientComponent } from './components/gradient/gradient.component';
|
||||
import { GradientSelectComponent } from './components/gradient-select/gradient-select.component';
|
||||
|
||||
export {
|
||||
SafePipe,
|
||||
@@ -60,7 +67,9 @@ export {
|
||||
SidePanelComponent,
|
||||
TimespanComponent,
|
||||
TagInputComponent,
|
||||
UserMenuComponent,
|
||||
HasPackageDirective,
|
||||
HasClaimDirective,
|
||||
Alert,
|
||||
IEventMessage,
|
||||
IItem,
|
||||
@@ -72,7 +81,6 @@ export {
|
||||
IListItem,
|
||||
ITypeaheadItem,
|
||||
IUser,
|
||||
ICodeListItem,
|
||||
IQueryState,
|
||||
IPackage,
|
||||
IPackages,
|
||||
@@ -82,7 +90,12 @@ export {
|
||||
AuthConfigFactory,
|
||||
MenuBackgroundComponent,
|
||||
SecureOAuthStorage,
|
||||
WeatherCurrentObservation
|
||||
WeatherCurrentObservation,
|
||||
IJsonline,
|
||||
ISenMLItem,
|
||||
IDataLayer,
|
||||
IColor,
|
||||
IGradientstop
|
||||
};
|
||||
|
||||
@NgModule({
|
||||
@@ -109,7 +122,11 @@ export {
|
||||
TagInputComponent,
|
||||
SessionClearedComponent,
|
||||
MenuBackgroundComponent,
|
||||
HasPackageDirective
|
||||
HasPackageDirective,
|
||||
HasClaimDirective,
|
||||
UserMenuComponent,
|
||||
GradientComponent,
|
||||
GradientSelectComponent
|
||||
],
|
||||
exports: [
|
||||
NgbModule,
|
||||
@@ -127,7 +144,11 @@ export {
|
||||
TagInputComponent,
|
||||
SessionClearedComponent,
|
||||
MenuBackgroundComponent,
|
||||
HasPackageDirective
|
||||
HasPackageDirective,
|
||||
HasClaimDirective,
|
||||
UserMenuComponent,
|
||||
GradientComponent,
|
||||
GradientSelectComponent
|
||||
]
|
||||
})
|
||||
export class AppCommonModule {
|
||||
|
@@ -1,7 +1,14 @@
|
||||
<div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async)}">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<button type="button" class="btn btn-outline-light" (click)="handleToggleMenu($event)"><i class="fa fa-bars" aria-hidden="true"></i></button>
|
||||
<div class="app fullscreen" (click)="handleClick($event)" [ngClass]="{'fullscreen' :(fullScreen|async),'pagemode':(isPageMode|async),'appmode':!(isPageMode|async)}">
|
||||
<nav class="navbar navbar-light navbar-expand bg-light navigation-clean">
|
||||
<div class="header-logo pageonly"><router-outlet name="header-logo"></router-outlet></div>
|
||||
<button type="button" class="btn btn-outline-secondary apponly" (click)="handleToggleMenu($event)"><i class="fa fa-bars" aria-hidden="true"></i></button>
|
||||
<router-outlet name="menu" class="ml-4"></router-outlet>
|
||||
<div class="collapse navbar-collapse pageonly">
|
||||
<a class="btn btn-primary ml-auto" role="button" [routerLink]="['/map']">
|
||||
<span *ngIf="(user|async)==null" i18n>Sign in</span>
|
||||
<span *ngIf="(user|async)!=null" i18n>To app</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="body">
|
||||
<router-outlet></router-outlet>
|
||||
@@ -11,7 +18,7 @@
|
||||
<div class="container-fluid">
|
||||
<div class="body">
|
||||
<div class="d-flex flex-row">
|
||||
<div class="mt-2 mb-2 flex-grow-1 logo"><router-outlet name="side-panel-logo"></router-outlet></div>
|
||||
<div class="mt-2 mb-2 flex-grow-1 logo" (click)="handleHome($event)"><router-outlet name="side-panel-logo"></router-outlet></div>
|
||||
<div class="mt-2 mb-2 ml-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fa fa-times" aria-hidden="true"></i></button></div>
|
||||
</div>
|
||||
<div class="d-flex flex-column cards">
|
||||
@@ -23,4 +30,12 @@
|
||||
<ng-container *ngIf="showUploadProgress">
|
||||
<fm-resumable-file-upload></fm-resumable-file-upload>
|
||||
</ng-container>
|
||||
<div class="user-menu apponly">
|
||||
<fm-user-menu [user]="user|async" [showMenu]="accountMenuVisible|async"></fm-user-menu>
|
||||
</div>
|
||||
<div class="healthstatus-container online apponly" [ngClass]="{'online' :(isOnline|async)}">
|
||||
<div class="healthstatus alert alert-danger m-0" >
|
||||
<span i18n>Not connected, make sure your device has an active internet connection</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -3,6 +3,7 @@
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
||||
|
||||
|
||||
|
||||
// custom styles
|
||||
|
||||
.btn:focus {
|
||||
@@ -25,7 +26,7 @@
|
||||
|
||||
*/
|
||||
|
||||
body { background: #f1f1f1; line-height: 18px; user-select:none;}
|
||||
body { background: #f1f1f1; line-height: 18px; user-select:none;font-family: Lato,Helvetica Neue,Helvetica,Arial,sans-serif;}
|
||||
|
||||
.navbar-brand {
|
||||
padding-top: .5rem;
|
||||
@@ -53,7 +54,20 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;}
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow:hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app.pagemode > .body {
|
||||
overflow: initial;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.app.pagemode .apponly {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.app.appmode .pageonly {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.app.fullscreen > .navbar {
|
||||
@@ -76,3 +90,37 @@ body { background: #f1f1f1; line-height: 18px; user-select:none;}
|
||||
.logo {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
height:2em;
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
transition: top 0.5s ease-out;
|
||||
position: absolute;
|
||||
top:0.25em;
|
||||
right:1em;
|
||||
}
|
||||
|
||||
.fullscreen > .user-menu {
|
||||
top:1em;
|
||||
}
|
||||
|
||||
.healthstatus-container {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.5s ease-out;
|
||||
max-height:5em;
|
||||
z-index: 2000;
|
||||
}
|
||||
|
||||
.healthstatus {
|
||||
width: 100%;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.online {
|
||||
max-height:0em;
|
||||
}
|
||||
|
@@ -1,14 +1,20 @@
|
||||
import { Component, OnInit, OnDestroy, Inject, ViewEncapsulation, RendererFactory2, PLATFORM_ID, ChangeDetectionStrategy, HostListener, Input } from '@angular/core';
|
||||
import { Router, NavigationStart, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router';
|
||||
import { Meta, Title, MetaDefinition } from '@angular/platform-browser';import { DOCUMENT } from "@angular/common";
|
||||
import { Subscription , Observable } from 'rxjs';
|
||||
import { Meta, Title, MetaDefinition } from '@angular/platform-browser'; import { DOCUMENT } from "@angular/common";
|
||||
import { Subscription, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
import { Store, Action } from '@ngrx/store';
|
||||
import { IUser } from '../../models/user';
|
||||
import { OAuthService, OAuthErrorEvent } from 'angular-oauth2-oidc';
|
||||
|
||||
//AppCommon
|
||||
import { IEventMessage } from '../../models/event.message';
|
||||
import { IListItem} from '../../models/list.item';
|
||||
import { IListItem } from '../../models/list.item';
|
||||
import { EventService } from '../../services/event.service';
|
||||
import * as commonActions from '../../actions/app-common.actions';
|
||||
import { ItemTypeService } from '../../services/itemtype.service';
|
||||
import * as commonActions from '../../actions/app-common.actions';
|
||||
import { HealthCheckService } from '../../services/healthcheck.service';
|
||||
import { AppConfig } from '../../shared/app.config';
|
||||
|
||||
import * as appReducers from '../../reducers/app-common.reducer';
|
||||
|
||||
@@ -31,22 +37,29 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
|
||||
public currentFolder: Observable<IListItem>;
|
||||
public folderParents: Observable<IListItem[]>;
|
||||
public fullScreen: Observable<boolean>;
|
||||
public routeLoading: Observable<boolean>;
|
||||
public menuVisible: Observable<boolean>;
|
||||
@Input() showUploadProgress: boolean =true;
|
||||
public fullScreen: Observable<boolean> = this.store$.select(appReducers.selectGetFullScreen);
|
||||
public isOnline: Observable<boolean> = this.store$.select(appReducers.SelectGetIsOnline);
|
||||
public routeLoading: Observable<boolean> = this.store$.select(appReducers.selectGetRouteLoading);
|
||||
public menuVisible: Observable<boolean> = this.store$.select(appReducers.SelectGetMenuVisible);
|
||||
public accountMenuVisible: Observable<boolean> = this.store$.select(appReducers.SelectGetAccountMenuVisible);
|
||||
public user: Observable<IUser> = this.store$.select(appReducers.SelectGetUser);
|
||||
public isPageMode: Observable<boolean> = this.store$.select(appReducers.SelectGetIsPageMode);
|
||||
@Input() showUploadProgress: boolean = true;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private title: Title,
|
||||
private meta: Meta,
|
||||
private store: Store<appReducers.State>,
|
||||
private eventService: EventService,
|
||||
) {
|
||||
public router: Router,
|
||||
private activatedRoute$: ActivatedRoute,
|
||||
private title$: Title,
|
||||
private meta$: Meta,
|
||||
private store$: Store<appReducers.State>,
|
||||
private eventService$: EventService,
|
||||
private healthCheckService$: HealthCheckService,
|
||||
private itemTypeService$: ItemTypeService,
|
||||
private oauthService$: OAuthService,
|
||||
private appConfig$: AppConfig
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
getActionFromEvent(event: IEventMessage): Action {
|
||||
var action: Action = null;
|
||||
@@ -76,6 +89,10 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
action = new commonActions.TaskErrorEvent(event.itemCode, event.attributes);
|
||||
break;
|
||||
}
|
||||
case "taskProgress": {
|
||||
action = new commonActions.TaskProgressEvent(event.itemCode, event.attributes);
|
||||
break;
|
||||
}
|
||||
case "deviceUpdate": {
|
||||
action = new commonActions.DeviceUpdateEvent(event.itemCode, event.attributes);
|
||||
break;
|
||||
@@ -85,61 +102,108 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.fullScreen = this.store.select(appReducers.selectGetFullScreen);
|
||||
this.routeLoading = this.store.select(appReducers.selectGetRouteLoading);
|
||||
this.menuVisible = this.store.select(appReducers.SelectGetMenuVisible);
|
||||
this.InstallRouteEventHandler();
|
||||
this.InstallEventServiceEventHandler();
|
||||
this.InstallAuthenticationEventHandler();
|
||||
this.InstallHealthCheck();
|
||||
|
||||
//load item types
|
||||
this.itemTypeService$.load(this.appConfig$)
|
||||
}
|
||||
|
||||
@HostListener('document:keyup', ['$event'])
|
||||
keyUp(event: KeyboardEvent) {
|
||||
onKeyUp(event: KeyboardEvent) {
|
||||
let x = event.keyCode;
|
||||
if (x === 27) {
|
||||
this.store.dispatch(new commonActions.Escape(true,false));
|
||||
}
|
||||
this.store$.dispatch(new commonActions.Escape(true, false));
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
// Subscription clean-up
|
||||
if(this.routerSub$) this.routerSub$.unsubscribe();
|
||||
if(this.eventSub$) this.eventSub$.unsubscribe();
|
||||
if (this.routerSub$) this.routerSub$.unsubscribe();
|
||||
if (this.eventSub$) this.eventSub$.unsubscribe();
|
||||
}
|
||||
|
||||
private InstallAuthenticationEventHandler() {
|
||||
// auth event handler
|
||||
this.oauthService$.events.subscribe((event) => {
|
||||
console.debug(event.type);
|
||||
if (event.type == 'token_error' || event.type == 'silent_refresh_timeout' || event.type == 'logout') {
|
||||
let e = event as OAuthErrorEvent;
|
||||
let p = e.params as any;
|
||||
if (event.type == 'silent_refresh_timeout' || event.type == 'logout' || (p.error && p.error == 'login_required')) {
|
||||
console.debug("Session expired");
|
||||
this.router.navigate(['loggedout'], { queryParams: { redirectTo: this.router.url } });
|
||||
}
|
||||
}
|
||||
if(event.type == 'token_received') {
|
||||
this.store$.dispatch(new commonActions.InitUser());
|
||||
}
|
||||
});
|
||||
if(this.oauthService$.hasValidAccessToken()) {
|
||||
this.store$.dispatch(new commonActions.InitUser());
|
||||
} else {
|
||||
if(this.oauthService$.getRefreshToken() !=null) {
|
||||
this.oauthService$.refreshToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InstallRouteEventHandler() {
|
||||
var other = this;
|
||||
this.routerSub$ = this.router.events.subscribe(event => {
|
||||
if (event instanceof RouteConfigLoadStart) {
|
||||
other.store.dispatch(new commonActions.StartRouteLoading());
|
||||
other.store$.dispatch(new commonActions.StartRouteLoading());
|
||||
}
|
||||
if (event instanceof NavigationEnd && (event.url == "/" || event.url.startsWith("/content") )) {
|
||||
other.store$.dispatch(new commonActions.SetPageMode(true));
|
||||
} else if (event instanceof NavigationEnd) {
|
||||
other.store$.dispatch(new commonActions.SetPageMode(false));
|
||||
}
|
||||
if (event instanceof RouteConfigLoadEnd) {
|
||||
other.store.dispatch(new commonActions.EndRouteLoading());
|
||||
}
|
||||
if(event instanceof NavigationStart) {
|
||||
other.store.dispatch(new commonActions.SetMenuVisible(false));
|
||||
other.store$.dispatch(new commonActions.EndRouteLoading());
|
||||
}
|
||||
});
|
||||
if (event instanceof NavigationStart) {
|
||||
other.store$.dispatch(new commonActions.SetMenuVisible(false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private InstallEventServiceEventHandler() {
|
||||
var other = this;
|
||||
this.eventSub$ = this.eventService.event.subscribe(event => {
|
||||
this.eventSub$ = this.eventService$.event.subscribe(event => {
|
||||
var action = other.getActionFromEvent(event);
|
||||
if (action) other.store.dispatch(action);
|
||||
if (action) other.store$.dispatch(action);
|
||||
});
|
||||
}
|
||||
|
||||
private InstallHealthCheck() {
|
||||
//check health every 30 seconds
|
||||
this.healthCheckService$.check(30000).pipe(distinctUntilChanged()).subscribe((online) => {
|
||||
if (online) {
|
||||
this.store$.dispatch(new commonActions.Online());
|
||||
} else {
|
||||
this.store$.dispatch(new commonActions.Offline());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleClick(event: MouseEvent) {
|
||||
this.store.dispatch(new commonActions.Escape(false,true));
|
||||
}
|
||||
this.store$.dispatch(new commonActions.Escape(false, true));
|
||||
}
|
||||
|
||||
handleStopBubble(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleMenu(event:MouseEvent) {
|
||||
handleToggleMenu(event: MouseEvent) {
|
||||
event.stopPropagation();
|
||||
this.store.dispatch(new commonActions.ToggleMenu());
|
||||
this.store$.dispatch(new commonActions.ToggleMenu());
|
||||
}
|
||||
|
||||
handleHome(event: MouseEvent) {
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,16 @@
|
||||
<div *ngIf="gradientItems" class="form-control gradient-select" (click)="handleToggleList()">
|
||||
<div *ngIf="selectedItem">
|
||||
<div *ngIf="showLabel">{{selectedItem?.name}}</div>
|
||||
<fm-gradient [gradientItem]="selectedItem"></fm-gradient>
|
||||
</div>
|
||||
<div class="gradient-list" [ngClass]="{'visible':listVisible}">
|
||||
<ul *ngIf="gradientItems">
|
||||
<li *ngFor="let item of gradientItems" (click)="handleSelect(item)" [ngClass]="{'bg-primary':isSelected(item),'text-white':isSelected(item)} ">
|
||||
<div>{{item?.name}}</div>
|
||||
<div><fm-gradient [gradientItem]="item"></fm-gradient></div>
|
||||
</li>
|
||||
</ul>
|
||||
<div *ngIf="showAdd" class="addGradient"><a href="#" i18n (click)="handleAdd($event)" >Add gradient</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -0,0 +1,55 @@
|
||||
.gradient-select {
|
||||
position: relative;
|
||||
width:30rem;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.gradient-list {
|
||||
position: absolute;
|
||||
top:100%;
|
||||
left:-0.15rem;
|
||||
width:100%;
|
||||
display: none;
|
||||
|
||||
margin-left: 1px;
|
||||
border-radius: 0.25rem;
|
||||
border: 1px solid #ced4da;
|
||||
}
|
||||
|
||||
.gradient-list.visible {
|
||||
display: block;
|
||||
background-color: white;
|
||||
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: 0.375rem .75rem;
|
||||
border-bottom: 1px solid #ced4da;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background-color: #ced4da;
|
||||
//color:white;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
max-height: 20rem;
|
||||
}
|
||||
|
||||
fm-gradient {
|
||||
display: block;
|
||||
height: 1.5rem;
|
||||
position: relative;
|
||||
border:1px solid white;
|
||||
}
|
||||
|
||||
.addGradient {
|
||||
padding: 0.375rem .75rem;
|
||||
border-top: 1px solid #ced4da;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GradientSelectComponent } from './gradient-select.component';
|
||||
|
||||
describe('GradientSelectComponent', () => {
|
||||
let component: GradientSelectComponent;
|
||||
let fixture: ComponentFixture<GradientSelectComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ GradientSelectComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GradientSelectComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,54 @@
|
||||
import { Component, ChangeDetectorRef, Input,SimpleChanges,OnChanges, Output,EventEmitter } from '@angular/core';
|
||||
import { IItem } from '../../models/item';
|
||||
|
||||
@Component({
|
||||
selector: 'fm-gradient-select',
|
||||
templateUrl: './gradient-select.component.html',
|
||||
styleUrls: ['./gradient-select.component.scss']
|
||||
})
|
||||
export class GradientSelectComponent implements OnChanges {
|
||||
|
||||
public listVisible:boolean = false;
|
||||
@Input() showLabel:boolean = true;
|
||||
@Input() gradientItems:IItem[];
|
||||
@Input() showAdd:boolean = false;
|
||||
@Input() selectedItem:IItem;
|
||||
@Output() onSelect:EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() onAdd:EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();
|
||||
|
||||
|
||||
constructor(private ref:ChangeDetectorRef) {
|
||||
}
|
||||
|
||||
ngOnChanges(changes:SimpleChanges) {
|
||||
if(changes["gradientItems"]) {
|
||||
this.gradientItems=changes["gradientItems"].currentValue;
|
||||
this.selectedItem=this.selectedItem == null && this.gradientItems && this.gradientItems.length>0?this.gradientItems[0]:null;
|
||||
this.ref.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
handleToggleList() {
|
||||
this.listVisible=!this.listVisible;
|
||||
}
|
||||
|
||||
handleSelect(item:IItem) {
|
||||
this.selectedItem=item;
|
||||
this.onSelect.emit(item);
|
||||
}
|
||||
|
||||
handleAdd(event:MouseEvent) {
|
||||
event.preventDefault();
|
||||
this.onAdd.emit(event);
|
||||
}
|
||||
|
||||
isSelected(item:IItem):boolean {
|
||||
if(this.selectedItem && this.selectedItem.data && this.selectedItem.data.gradient && item && item.data && item.data.gradient) {
|
||||
let sid = JSON.stringify(this.selectedItem.data.gradient)+this.selectedItem.name;
|
||||
let id = JSON.stringify( item.data.gradient) + item.name;
|
||||
return sid == id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<div class="gradient" [ngStyle]="gradientStyle"></div>
|
@@ -0,0 +1,4 @@
|
||||
.gradient {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GradientComponent } from './gradient.component';
|
||||
|
||||
describe('GradientComponent', () => {
|
||||
let component: GradientComponent;
|
||||
let fixture: ComponentFixture<GradientComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ GradientComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GradientComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,53 @@
|
||||
import { Component, Input, OnInit ,OnChanges, SimpleChanges,ChangeDetectorRef} from '@angular/core';
|
||||
import { IItem} from '../../models/item'
|
||||
import { IGradientstop } from '../../models/gradient';
|
||||
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'fm-gradient',
|
||||
templateUrl: './gradient.component.html',
|
||||
styleUrls: ['./gradient.component.scss']
|
||||
})
|
||||
export class GradientComponent implements OnInit,OnChanges {
|
||||
|
||||
@Input() gradientItem:IItem;
|
||||
public gradientStyle:any = {};
|
||||
|
||||
constructor(private ref: ChangeDetectorRef) { }
|
||||
|
||||
getGradientStyle(item:IItem):any {
|
||||
if(item.data && item.data.gradient) {
|
||||
let gradient = item.data.gradient as IGradientstop[];
|
||||
let gd = '{ "background": "linear-gradient(to right,';
|
||||
for(var i=0;i<gradient.length;i++) {
|
||||
let gs = gradient[i];
|
||||
if(i>0) gd+=",";
|
||||
gd += `rgba(${gs.color.red},${gs.color.green},${gs.color.blue},${gs.color.alpha/255})`;
|
||||
gd +=` ${gs.relativestop*100}%`
|
||||
}
|
||||
gradient.forEach((gs) => {
|
||||
});
|
||||
gd+=')"}';
|
||||
|
||||
return JSON.parse(gd);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if(this.gradientItem && this.gradientItem.itemType == "vnd.farmmaps.itemtype.gradient") {
|
||||
this.gradientStyle = this.getGradientStyle(this.gradientItem);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if(changes['gradientItem']) {
|
||||
let gradientItem = changes['gradientItem'].currentValue;
|
||||
if(gradientItem && gradientItem.itemType == "vnd.farmmaps.itemtype.gradient") {
|
||||
this.gradientStyle = this.getGradientStyle(changes['gradientItem'].currentValue);
|
||||
this.ref.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
import { Directive, ViewContainerRef,TemplateRef,OnInit,Input,OnDestroy } from '@angular/core';
|
||||
import { Store} from '@ngrx/store';
|
||||
import * as appCommonReducer from '../../reducers/app-common.reducer'
|
||||
import { IPackages } from '../../models/package';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { skip } from 'rxjs/operators';
|
||||
import {OAuthService } from 'angular-oauth2-oidc';
|
||||
import { IUser } from '../../models/user';
|
||||
|
||||
@Directive({
|
||||
selector: '[fm-hasclaim]',
|
||||
})
|
||||
export class HasClaimDirective implements OnInit{
|
||||
@Input('fm-hasclaim') claim:string;
|
||||
|
||||
constructor(private templateRef$: TemplateRef<any>,private viewContainerRef$: ViewContainerRef,private store$: Store<appCommonReducer.State>) { }
|
||||
private user$:Observable<IUser> = this.store$.select(appCommonReducer.SelectGetUser);
|
||||
private hasView = false;
|
||||
ngOnInit() {
|
||||
this.user$.subscribe((user) => {
|
||||
if (user && user.claims[this.claim]) {
|
||||
this.viewContainerRef$.createEmbeddedView(this.templateRef$);
|
||||
this.hasView=true;
|
||||
} else if (this.hasView) {
|
||||
this.viewContainerRef$.clear();
|
||||
this.hasView = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
bottom:0;
|
||||
opacity: 0;
|
||||
background-color: #000000;
|
||||
transition: opacity 0s ease-out 1s;
|
||||
transition: opacity 0.3s ease-out;
|
||||
pointer-events: none;
|
||||
z-index:99;
|
||||
}
|
||||
|
@@ -1,149 +1,153 @@
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { Subject , Subscription } from 'rxjs';
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { UploadxService, UploadState,UploadxOptions} from 'ngx-uploadx';
|
||||
import { AppConfig } from '../../shared/app.config';
|
||||
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ResumableFileUploadService implements OnDestroy{
|
||||
public files: Array<File> = new Array<File>();
|
||||
public isUploading = false;
|
||||
public totalProgress = 0;
|
||||
public isClosed = true;
|
||||
public isMinimized = false;
|
||||
public parentCode: string;
|
||||
public refresh: Subject<any> = new Subject<any>();
|
||||
private _eventSub:Subscription;
|
||||
private initialized = false;
|
||||
|
||||
constructor(private httpClient: HttpClient,private oauthService: OAuthService,private uploadService: UploadxService,public appConfig: AppConfig) {
|
||||
|
||||
}
|
||||
|
||||
endPoint() {
|
||||
return `${this.appConfig.getConfig("apiEndPoint")}/api/v1/file`;
|
||||
}
|
||||
|
||||
init() {
|
||||
if(!this.initialized) {
|
||||
this._eventSub=this.uploadService.init({
|
||||
endpoint:this.endPoint(),
|
||||
token:() => this.oauthService.getAccessToken(),
|
||||
chunkSize: 2097152}).subscribe((uploadState:UploadState) => {
|
||||
this.handleState(uploadState);
|
||||
} );
|
||||
this.initialized=true;
|
||||
}
|
||||
}
|
||||
|
||||
updatetotalprogress() {
|
||||
var totalProgress =0;
|
||||
var n=0;
|
||||
for(var i =0;i<this.files.length;i++) {
|
||||
if(!this.files[i].error) {
|
||||
totalProgress+=this.files[i].progress;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
this.totalProgress=totalProgress/this.files.length;
|
||||
if(this.totalProgress==100) this.isUploading=false;
|
||||
}
|
||||
|
||||
handleState(state:UploadState) {
|
||||
switch(state.status) {
|
||||
case "queue": {
|
||||
this.files.push(new File(state));
|
||||
this.isClosed=false;
|
||||
};break;
|
||||
case "uploading": {
|
||||
this.isUploading = true;
|
||||
var file =this.files.find((f) => f.identifier == state.uploadId )
|
||||
if(file) {
|
||||
file.progress = (state.progress?state.progress:0);
|
||||
}
|
||||
};break;
|
||||
case "complete": {
|
||||
var file =this.files.find((f) => f.identifier == state.uploadId )
|
||||
if(file) {
|
||||
var parts = state.url.split("/");
|
||||
file.itemCode = parts[parts.length-1];
|
||||
file.progress = (state.progress?state.progress:0);
|
||||
file.success=true;
|
||||
}
|
||||
};break;
|
||||
case "error": {
|
||||
var file =this.files.find((f) => f.identifier == state.uploadId )
|
||||
if(file) {
|
||||
file.error=true;
|
||||
file.errorMessage = state.response;
|
||||
}
|
||||
};break;
|
||||
}
|
||||
this.updatetotalprogress();
|
||||
this.refresh.next({});
|
||||
}
|
||||
|
||||
addFiles = (files: any[], event: any, metadata:any) => {
|
||||
for (let f of files) {
|
||||
var options:UploadxOptions = {metadata:metadata};
|
||||
this.uploadService.handleFile(f,options);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMinimize = function () {
|
||||
this.isMinimized = !this.isMinimized;
|
||||
};
|
||||
|
||||
cancelFile = function (file) {
|
||||
this.uploadService.control({action:'cancel',uploadId:file.identifier});
|
||||
var index = this.files.indexOf(file, 0);
|
||||
if (index > -1) {
|
||||
this.files.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
doClose = function () {
|
||||
this.uploadService.control({action:'cancelAll'});
|
||||
this.files = new Array<File>();
|
||||
this.isClosed = true;
|
||||
}
|
||||
|
||||
close = function () {
|
||||
let close = true;
|
||||
if (this.isUploading) {
|
||||
close = false;
|
||||
}
|
||||
if (close) {
|
||||
this.doClose();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if(this._eventSub) this._eventSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
export class File {
|
||||
private file: any;
|
||||
public fileName: string;
|
||||
public progress: number;
|
||||
public identifier: string;
|
||||
public itemCode: string;
|
||||
public success: boolean;
|
||||
public error: boolean;
|
||||
public errorMessage: string;
|
||||
|
||||
|
||||
constructor(state: UploadState) {
|
||||
this.file = state;
|
||||
this.fileName = state.file.name;
|
||||
this.progress = state.progress?state.progress:0;
|
||||
this.identifier = state.uploadId;
|
||||
this.success = false;
|
||||
this.error = false;
|
||||
this.errorMessage = "";
|
||||
this.itemCode = null;
|
||||
}
|
||||
}
|
||||
import { Injectable, OnDestroy } from '@angular/core';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { Subject , Subscription } from 'rxjs';
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { UploadxService, UploadState,UploadxOptions} from 'ngx-uploadx';
|
||||
import { AppConfig } from '../../shared/app.config';
|
||||
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ResumableFileUploadService implements OnDestroy{
|
||||
public files: Array<File> = new Array<File>();
|
||||
public isUploading = false;
|
||||
public totalProgress = 0;
|
||||
public isClosed = true;
|
||||
public isMinimized = false;
|
||||
public parentCode: string;
|
||||
public refresh: Subject<any> = new Subject<any>();
|
||||
public addedFiles: Subject<File> = new Subject<File>();
|
||||
private _eventSub:Subscription;
|
||||
private initialized = false;
|
||||
|
||||
constructor(private httpClient: HttpClient,private oauthService: OAuthService,private uploadService: UploadxService,public appConfig: AppConfig) {
|
||||
|
||||
}
|
||||
|
||||
endPoint() {
|
||||
return `${this.appConfig.getConfig("apiEndPoint")}/api/v1/file`;
|
||||
}
|
||||
|
||||
init() {
|
||||
if(!this.initialized) {
|
||||
this._eventSub=this.uploadService.init({
|
||||
endpoint:this.endPoint(),
|
||||
token:() => this.oauthService.getAccessToken(),
|
||||
chunkSize: 2097152}).subscribe((uploadState:UploadState) => {
|
||||
this.handleState(uploadState);
|
||||
} );
|
||||
this.initialized=true;
|
||||
}
|
||||
}
|
||||
|
||||
updatetotalprogress() {
|
||||
var totalProgress =0;
|
||||
var n=0;
|
||||
for(var i =0;i<this.files.length;i++) {
|
||||
if(!this.files[i].error) {
|
||||
totalProgress+=this.files[i].progress;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
this.totalProgress=totalProgress/this.files.length;
|
||||
if(this.totalProgress==100) this.isUploading=false;
|
||||
}
|
||||
|
||||
handleState(state:UploadState) {
|
||||
var file =this.files.find((f) => f.identifier == state.uploadId )
|
||||
if(state.status != "cancelled" && !file) {
|
||||
let file = new File(state);
|
||||
this.files.push(file);
|
||||
this.isClosed=false;
|
||||
this.addedFiles.next(file)
|
||||
}
|
||||
switch(state.status) {
|
||||
case "uploading": {
|
||||
if(file) {
|
||||
this.isUploading = true;
|
||||
file.progress = (state.progress?state.progress:0);
|
||||
}
|
||||
};break;
|
||||
case "complete": {
|
||||
if(file) {
|
||||
var parts = state.url.split("/");
|
||||
file.itemCode = parts[parts.length-1];
|
||||
file.progress = (state.progress?state.progress:0);
|
||||
file.success=true;
|
||||
}
|
||||
};break;
|
||||
case "error": {
|
||||
if(file) {
|
||||
file.error=true;
|
||||
file.errorMessage = state.response as string;
|
||||
}
|
||||
};break;
|
||||
}
|
||||
this.updatetotalprogress();
|
||||
this.refresh.next({});
|
||||
}
|
||||
|
||||
addFiles = (files: any[], event: any, metadata:any) => {
|
||||
for (let f of files) {
|
||||
var options:UploadxOptions = {metadata:metadata};
|
||||
this.uploadService.handleFile(f,options);
|
||||
}
|
||||
}
|
||||
|
||||
toggleMinimize = function () {
|
||||
this.isMinimized = !this.isMinimized;
|
||||
};
|
||||
|
||||
cancelFile = function (file) {
|
||||
this.uploadService.control({action:'cancel',uploadId:file.identifier});
|
||||
var index = this.files.indexOf(file, 0);
|
||||
if (index > -1) {
|
||||
this.files.splice(index, 1);
|
||||
}
|
||||
if(this.files.length==0) {
|
||||
this.isUploading = false;
|
||||
}
|
||||
};
|
||||
|
||||
doClose = function () {
|
||||
this.uploadService.control({action:'cancelAll'});
|
||||
this.files = new Array<File>();
|
||||
this.isClosed = true;
|
||||
}
|
||||
|
||||
close = function () {
|
||||
let close = true;
|
||||
if (this.isUploading) {
|
||||
close = false;
|
||||
}
|
||||
if (close) {
|
||||
this.doClose();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if(this._eventSub) this._eventSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
export class File {
|
||||
private file: any;
|
||||
public fileName: string;
|
||||
public progress: number;
|
||||
public identifier: string;
|
||||
public itemCode: string;
|
||||
public success: boolean;
|
||||
public error: boolean;
|
||||
public errorMessage: string;
|
||||
|
||||
|
||||
constructor(state: UploadState) {
|
||||
this.file = state;
|
||||
this.fileName = state.file.name;
|
||||
this.progress = state.progress?state.progress:0;
|
||||
this.identifier = state.uploadId;
|
||||
this.success = false;
|
||||
this.error = false;
|
||||
this.errorMessage = "";
|
||||
this.itemCode = null;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,14 @@
|
||||
<div>
|
||||
<div (click)="toggle($event)" class="rounded-circle menu-button hidden" [ngClass]="{'hidden':!user}">
|
||||
<span *ngIf="user"><span [title]="user.name">{{getLetter()}}</span></span>
|
||||
<div class="menu hidden" [ngClass]="{'hidden':!showMenu}">
|
||||
<div class="card" *ngIf="user">
|
||||
<div class="card-body">
|
||||
<div class="username">{{user.name}}</div>
|
||||
<div><a href="#" (click)="logout($event)" i18n>logout</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<router-outlet name="user-menu"></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@@ -0,0 +1,56 @@
|
||||
.menu-button {
|
||||
background-color: purple;
|
||||
display: inline-block;
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
line-height: 2.5em;
|
||||
text-align: center;
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.menu-button > span {
|
||||
color:white;
|
||||
}
|
||||
|
||||
.menu {
|
||||
max-height: 100vh;
|
||||
//transition: max-height 0.2s;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 20px rgba(0,0,0,.3);
|
||||
position: absolute;
|
||||
top: 3rem;
|
||||
right:0;
|
||||
background-color: #fff;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding:0.5rem;
|
||||
min-width: 10rem;
|
||||
}
|
||||
|
||||
.username {
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.menu.hidden {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-button.hidden {
|
||||
overflow: hidden;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserMenuComponent } from './user-menu.component';
|
||||
|
||||
describe('UserMenuComponent', () => {
|
||||
let component: UserMenuComponent;
|
||||
let fixture: ComponentFixture<UserMenuComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ UserMenuComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(UserMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@@ -0,0 +1,37 @@
|
||||
import { Component, OnInit,Input } from '@angular/core';
|
||||
import { OAuthService} from 'angular-oauth2-oidc'
|
||||
import { IUser } from '../../models/user';
|
||||
import {Store} from '@ngrx/store';
|
||||
import * as appReducers from '../../reducers/app-common.reducer';
|
||||
import * as appActions from '../../actions/app-common.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'fm-user-menu',
|
||||
templateUrl: './user-menu.component.html',
|
||||
styleUrls: ['./user-menu.component.scss']
|
||||
})
|
||||
export class UserMenuComponent implements OnInit {
|
||||
|
||||
@Input() user:IUser;
|
||||
@Input() showMenu:boolean;
|
||||
|
||||
constructor(private oauthService:OAuthService, private store: Store<appReducers.State>) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
getLetter():string {
|
||||
return this.user && this.user.name ? this.user.name.substr(0,1).toUpperCase():"";
|
||||
}
|
||||
|
||||
logout(event:MouseEvent) {
|
||||
event.preventDefault();
|
||||
this.store.dispatch(new appActions.Logout());
|
||||
}
|
||||
|
||||
|
||||
toggle(event:MouseEvent) {
|
||||
event.stopPropagation();
|
||||
this.store.dispatch(new appActions.ToggleAccountMenu());
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { OAuthService } from 'angular-oauth2-oidc';
|
||||
import { OAuthService,UserInfo } from 'angular-oauth2-oidc';
|
||||
import { Store, Action } from '@ngrx/store';
|
||||
import { Effect, Actions,ofType } from '@ngrx/effects';
|
||||
import { Observable , defer , of } from 'rxjs';
|
||||
import { withLatestFrom,mergeMap,switchMap,map,catchError} from 'rxjs/operators';
|
||||
import { Observable , defer , of,from,zip } from 'rxjs';
|
||||
import { withLatestFrom,mergeMap,switchMap,map,catchError,first} from 'rxjs/operators';
|
||||
import * as appCommonActions from '../actions/app-common.actions';
|
||||
import * as appCommonReducers from '../reducers/app-common.reducer';
|
||||
import { ItemService } from '../services/item.service';
|
||||
@@ -25,10 +25,18 @@ export class AppCommonEffects {
|
||||
withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)),
|
||||
mergeMap(([action, initialized]) => {
|
||||
var a = (action as appCommonActions.Login);
|
||||
this.oauthService$.initCodeFlow(a.url);
|
||||
this.oauthService$.initCodeFlow(a.url,{"prompt":"login"});
|
||||
return [];
|
||||
}));
|
||||
|
||||
@Effect({ dispatch: false })
|
||||
logout$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.LOGOUT),
|
||||
mergeMap((action) => {
|
||||
this.oauthService$.revokeTokenAndLogout();
|
||||
return [];
|
||||
}));
|
||||
|
||||
@Effect()
|
||||
loadItemTypes$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.LOADITEMTYPES),
|
||||
@@ -42,35 +50,67 @@ export class AppCommonEffects {
|
||||
@Effect()
|
||||
initUser$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.INITUSER),
|
||||
withLatestFrom(this.store$.select(appCommonReducers.selectGetInitialized)),
|
||||
switchMap(([action, initialized]) => {
|
||||
if(!initialized) {
|
||||
return this.userService$.getCurrentUser().pipe(
|
||||
map((user: IUser) => new appCommonActions.InitUserSuccess(user)),
|
||||
first(),
|
||||
switchMap((action) => {
|
||||
return zip(this.userService$.getCurrentUser(),from(this.oauthService$.loadUserProfile())).pipe(
|
||||
switchMap(([user,userInfo]) => {return of(new appCommonActions.InitUserSuccess(user,userInfo as UserInfo))} ),
|
||||
catchError(error => of(new appCommonActions.Fail(error))))
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
|
||||
@Effect()
|
||||
initUserPackages$:Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.INITUSERSUCCESS),
|
||||
switchMap((action) => {
|
||||
let a = action as appCommonActions.InitUserSuccess;
|
||||
return this.itemService$.getChildItemList(a.user.code+":USER_PACKAGES","vnd.farmmaps.itemtype.package").pipe(
|
||||
ofType(appCommonActions.INITUSERPACKAGES),
|
||||
switchMap(() => {
|
||||
return this.itemService$.getItemList('vnd.farmmaps.itemtype.package').pipe(
|
||||
switchMap((items) => of(new appCommonActions.InitUserPackagesSuccess(items))),
|
||||
catchError(error => of(new appCommonActions.Fail(error)))
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
@Effect()
|
||||
userPackagesChanged$:Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.ITEMCHANGEDEVENT),
|
||||
switchMap((action) => {
|
||||
let a = action as appCommonActions.ItemChangedEvent;
|
||||
if(a.itemCode.endsWith(":USER_PACKAGES"))
|
||||
return of(new appCommonActions.InitUserPackages());
|
||||
else
|
||||
return [];
|
||||
})
|
||||
);
|
||||
|
||||
@Effect()
|
||||
initUserSettingsRoot$:Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.INITUSERSETTINGSROOT),
|
||||
withLatestFrom(this.store$.select(appCommonReducers.SelectGetUser)),
|
||||
switchMap(([, user]) => {
|
||||
return this.itemService$.getItem(user.code + ':USER_SETTINGS').pipe(
|
||||
switchMap((item) => of(new appCommonActions.InitUserSettingsRootSuccess(item))),
|
||||
catchError(error => of(new appCommonActions.Fail(error)))
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
@Effect()
|
||||
initUserSettingsRootChanged$:Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.ITEMCHANGEDEVENT),
|
||||
switchMap((action) => {
|
||||
let a = action as appCommonActions.ItemChangedEvent;
|
||||
if(a.itemCode.endsWith(":USER_SETTINGS"))
|
||||
return of(new appCommonActions.InitUserSettingsRoot());
|
||||
else
|
||||
return [];
|
||||
})
|
||||
);
|
||||
|
||||
@Effect()
|
||||
initUserSuccess$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.INITUSERSUCCESS),
|
||||
switchMap(() => {
|
||||
return of(new appCommonActions.InitRoot());
|
||||
return [new appCommonActions.InitRoot(),new appCommonActions.InitUserPackages(),new appCommonActions.InitUserSettingsRoot()];
|
||||
}
|
||||
));
|
||||
|
||||
@@ -100,8 +140,11 @@ export class AppCommonEffects {
|
||||
withLatestFrom(this.store$.select(appCommonReducers.selectGetItemTypes)),
|
||||
switchMap(([action, itemtypes]) => {
|
||||
var a = action as appCommonActions.EditItem;
|
||||
var itemType = itemtypes[a.item.itemType];
|
||||
var editor = itemType.editor ? itemType.editor : "property";
|
||||
var editor = "property";
|
||||
if(a.item.itemType) {
|
||||
var itemType = itemtypes[a.item.itemType];
|
||||
var editor = itemType && itemType.editor ? itemType.editor : editor;
|
||||
}
|
||||
this.router$.navigate(['/editor',editor,'item', a.item.code])
|
||||
return [];
|
||||
}
|
||||
@@ -150,6 +193,23 @@ export class AppCommonEffects {
|
||||
return null;
|
||||
}));
|
||||
|
||||
@Effect({ dispatch: false })
|
||||
online$: Observable<Action> = this.actions$.pipe(
|
||||
ofType(appCommonActions.ONLINE),
|
||||
switchMap((action) => {
|
||||
console.debug("Online: Check token");
|
||||
if(!this.oauthService$.hasValidAccessToken()) {
|
||||
console.debug("No valid token, try to refresh");
|
||||
if(this.oauthService$.getRefreshToken() != null ) {
|
||||
console.debug("We have a refresh token");
|
||||
this.oauthService$.refreshToken();
|
||||
}
|
||||
}
|
||||
return of(undefined);
|
||||
}));
|
||||
|
||||
|
||||
|
||||
constructor(private actions$: Actions, private store$: Store<appCommonReducers.State>, private oauthService$: OAuthService, private itemService$: ItemService, private folderService$:FolderService, private userService$: UserService, private router$: Router, private stateSerializerService$:StateSerializerService) {
|
||||
store$.dispatch(new appCommonActions.LoadItemTypes());
|
||||
}
|
||||
|
22
projects/common/src/fm/models/WeatherData.ts
Normal file
22
projects/common/src/fm/models/WeatherData.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export interface WeatherData {
|
||||
date: Date;
|
||||
iconCode: number;
|
||||
minTemperature: number;
|
||||
maxTemperature: number;
|
||||
relHumidity: number;
|
||||
precipitation: number;
|
||||
wSpeed: number;
|
||||
wCardinal: string;
|
||||
wDir: number;
|
||||
}
|
||||
|
||||
export interface HourlyWeatherData {
|
||||
time: string;
|
||||
iconCode: number;
|
||||
temperature: number;
|
||||
relativeHumidity: number;
|
||||
rain: number;
|
||||
windSpeed: number;
|
||||
windDirectionCardinal: string;
|
||||
windDirection: number;
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
export interface ICodeListItem {
|
||||
codeList: string;
|
||||
code: string;
|
||||
codeGroup?: string;
|
||||
unit?: string;
|
||||
description?: string;
|
||||
validFrom?: Date;
|
||||
validTo?: Date;
|
||||
created?: Date;
|
||||
updated?: Date;
|
||||
data?:any
|
||||
}
|
||||
|
||||
export class CodeListItem implements ICodeListItem {
|
||||
public codeList: string;
|
||||
public code: string;
|
||||
public codeGroup?: string;
|
||||
public unit?: string;
|
||||
public description:string;
|
||||
public validFrom?: Date;
|
||||
public validTo?: Date;
|
||||
public created?: Date;
|
||||
public updated?: Date;
|
||||
public data?:any;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
}
|
7
projects/common/src/fm/models/data.layer.ts
Normal file
7
projects/common/src/fm/models/data.layer.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export interface IDataLayer {
|
||||
name: string,
|
||||
unit: string,
|
||||
index: number,
|
||||
indexKey?: string,
|
||||
scale: number,
|
||||
}
|
11
projects/common/src/fm/models/gradient.ts
Normal file
11
projects/common/src/fm/models/gradient.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export interface IColor {
|
||||
red: number,
|
||||
green: number,
|
||||
blue: number,
|
||||
alpha: number,
|
||||
}
|
||||
|
||||
export interface IGradientstop {
|
||||
relativestop: number,
|
||||
color: IColor
|
||||
}
|
@@ -15,9 +15,10 @@ export class Item implements IItem {
|
||||
public geometry?:any;
|
||||
public url?: string;
|
||||
public name?: string;
|
||||
public created?: Date;
|
||||
public updated?: Date;
|
||||
public dataDate?: Date;
|
||||
public created?: string;
|
||||
public updated?: string;
|
||||
public dataDate?: string;
|
||||
public dataEndDate?: string;
|
||||
public itemType?: string;
|
||||
public sourceTask?: string;
|
||||
public size?: number;
|
||||
|
@@ -4,5 +4,6 @@ export interface IItemType {
|
||||
editor?: string;
|
||||
isFolder?: boolean;
|
||||
iconColor?: string;
|
||||
schema?: string;
|
||||
extraAttributes?: string;
|
||||
}
|
||||
|
5
projects/common/src/fm/models/json-line.ts
Normal file
5
projects/common/src/fm/models/json-line.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface IJsonline {
|
||||
time: string;
|
||||
type: string;
|
||||
data: any;
|
||||
}
|
@@ -1,13 +1,14 @@
|
||||
export interface IListItem {
|
||||
url?: string;
|
||||
code?: string;
|
||||
name?: string;
|
||||
created?: Date;
|
||||
updated?: Date;
|
||||
dataDate?: Date;
|
||||
itemType?: string;
|
||||
sourceTask?: string;
|
||||
size?: number;
|
||||
state?: number;
|
||||
thumbnail?: boolean;
|
||||
}
|
||||
export interface IListItem {
|
||||
url?: string;
|
||||
code?: string;
|
||||
name?: string;
|
||||
created?: string;
|
||||
updated?: string;
|
||||
dataDate?: string;
|
||||
dataEndDate?: string;
|
||||
itemType?: string;
|
||||
sourceTask?: string;
|
||||
size?: number;
|
||||
state?: number;
|
||||
thumbnail?: boolean;
|
||||
}
|
||||
|
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;
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
export interface IUser {
|
||||
code?: string;
|
||||
name?: string;
|
||||
email?: string;
|
||||
}
|
||||
export interface IUser {
|
||||
code?: string;
|
||||
name?: string;
|
||||
email?: string;
|
||||
claims: any;
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import * as appCommonActions from '../actions/app-common.actions';
|
||||
import { createSelector, createFeatureSelector, ActionReducerMap } from '@ngrx/store';
|
||||
|
||||
import { MODULE_NAME } from '../module-name';
|
||||
import { IItem } from '../models/item';
|
||||
|
||||
export interface State {
|
||||
openedModalName: string,
|
||||
@@ -17,7 +18,11 @@ export interface State {
|
||||
fullScreen: boolean,
|
||||
routeLoading:boolean,
|
||||
menuVisible: boolean,
|
||||
userPackages: IPackages
|
||||
userPackages: IPackages,
|
||||
userSettingsRoot: IItem,
|
||||
accountMenuVisible: boolean,
|
||||
isOnline: boolean,
|
||||
isPageMode:boolean
|
||||
}
|
||||
|
||||
export const initialState: State = {
|
||||
@@ -29,14 +34,31 @@ export const initialState: State = {
|
||||
fullScreen: true,
|
||||
routeLoading: false,
|
||||
menuVisible: false,
|
||||
userPackages: {}
|
||||
userPackages: {},
|
||||
userSettingsRoot: null,
|
||||
accountMenuVisible: false,
|
||||
isOnline: true,
|
||||
isPageMode: true
|
||||
}
|
||||
|
||||
export function reducer(state = initialState, action: appCommonActions.Actions ): State {
|
||||
switch (action.type) {
|
||||
case appCommonActions.INITUSER: {
|
||||
return tassign(state,{initialized: true});
|
||||
}
|
||||
case appCommonActions.INITUSERSUCCESS: {
|
||||
let a = action as appCommonActions.InitUserSuccess;
|
||||
return tassign(state, { user: a.user,initialized: true });
|
||||
let claims = {}
|
||||
Object.getOwnPropertyNames(a.userinfo).forEach((k) => {
|
||||
claims[k] = a.userinfo[k];
|
||||
});
|
||||
var user:IUser = {
|
||||
code:a.user.code,
|
||||
email:a.userinfo["email"],
|
||||
name:a.userinfo["name"],
|
||||
claims:claims
|
||||
};
|
||||
return tassign(state, { user: user });
|
||||
}
|
||||
case appCommonActions.INITROOTSUCCESS: {
|
||||
let a = action as appCommonActions.InitRootSuccess;
|
||||
@@ -73,14 +95,17 @@ export function reducer(state = initialState, action: appCommonActions.Actions )
|
||||
});
|
||||
}
|
||||
case appCommonActions.TOGGLEMENU: {
|
||||
return tassign(state, { menuVisible: !state.menuVisible });
|
||||
return tassign(state, { menuVisible: !state.menuVisible,accountMenuVisible:!state.menuVisible?false:state.accountMenuVisible });
|
||||
}
|
||||
case appCommonActions.TOGGLEACCOUNTMENU: {
|
||||
return tassign(state, { accountMenuVisible: !state.accountMenuVisible });
|
||||
}
|
||||
case appCommonActions.ESCAPE: {
|
||||
return tassign(state, { menuVisible: false });
|
||||
return tassign(state, { menuVisible: false,accountMenuVisible:false });
|
||||
}
|
||||
case appCommonActions.SETMENUVISIBLE: {
|
||||
let a = action as appCommonActions.SetMenuVisible;
|
||||
return tassign(state, { menuVisible: a.visible });
|
||||
return tassign(state, { menuVisible: a.visible,accountMenuVisible:a.visible?false:state.accountMenuVisible });
|
||||
}
|
||||
case appCommonActions.INITUSERPACKAGESSUCCESS:{
|
||||
let a = action as appCommonActions.InitUserPackagesSuccess;
|
||||
@@ -91,6 +116,26 @@ export function reducer(state = initialState, action: appCommonActions.Actions )
|
||||
|
||||
return tassign(state,{userPackages:packages});
|
||||
}
|
||||
case appCommonActions.INITUSERSETTINGSROOTSUCCESS:{
|
||||
let a = action as appCommonActions.InitUserSettingsRootSuccess;
|
||||
return tassign(state, { userSettingsRoot : a.item });
|
||||
}
|
||||
case appCommonActions.LOGOUT:{
|
||||
return tassign(state,{user:null,initialized:false});
|
||||
}
|
||||
case appCommonActions.CLOSEALL: {
|
||||
return tassign(state,{accountMenuVisible:false,menuVisible:false });
|
||||
}
|
||||
case appCommonActions.ONLINE:{
|
||||
return tassign(state,{isOnline:true});
|
||||
}
|
||||
case appCommonActions.OFFLINE:{
|
||||
return tassign(state,{isOnline:false});
|
||||
}
|
||||
case appCommonActions.SETPAGEMODE: {
|
||||
let a = action as appCommonActions.SetPageMode;
|
||||
return tassign(state,{isPageMode:a.pageMode});
|
||||
}
|
||||
default: {
|
||||
return state;
|
||||
}
|
||||
@@ -106,6 +151,10 @@ export const getRouteLoading = (state: State) => state.routeLoading;
|
||||
export const getMenuVisible = (state: State) => state.menuVisible;
|
||||
export const getUser = (state: State) => state.user;
|
||||
export const getUserPackages = (state: State) => state.userPackages;
|
||||
export const getUserSettingsRoot = (state: State) => state.userSettingsRoot;
|
||||
export const getAccountMenuVisible = (state: State) => state.accountMenuVisible;
|
||||
export const getIsOnline = (state: State) => state.isOnline;
|
||||
export const getIsPageMode = (state: State) => state.isPageMode;
|
||||
|
||||
export const selectAppCommonState = createFeatureSelector<State>(MODULE_NAME);
|
||||
|
||||
@@ -118,4 +167,8 @@ export const selectGetRouteLoading = createSelector(selectAppCommonState, getRou
|
||||
export const SelectGetMenuVisible = createSelector(selectAppCommonState,getMenuVisible);
|
||||
export const SelectGetUser = createSelector(selectAppCommonState,getUser);
|
||||
export const SelectGetUserPackages = createSelector(selectAppCommonState,getUserPackages);
|
||||
export const SelectGetUserSettingsRoot = createSelector(selectAppCommonState,getUserSettingsRoot);
|
||||
export const SelectGetAccountMenuVisible = createSelector(selectAppCommonState,getAccountMenuVisible);
|
||||
export const SelectGetIsOnline = createSelector(selectAppCommonState,getIsOnline);
|
||||
export const SelectGetIsPageMode = createSelector(selectAppCommonState,getIsPageMode);
|
||||
|
||||
|
@@ -41,24 +41,9 @@ export class AuthGuard implements CanActivate, CanLoad, CanActivateChild {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
if (!this.oauthService.hasValidAccessToken()) {
|
||||
console.debug("No valid token");
|
||||
if(this.oauthService.getRefreshToken() != null ) {
|
||||
console.debug("We have a refresh token");
|
||||
this.oauthService.refreshToken().then(() => {
|
||||
this.store.dispatch(new appCommonActions.InitUser());
|
||||
resolve(true);
|
||||
}).catch(() => {
|
||||
console.debug("Error refreshing");
|
||||
this.oauthService.initCodeFlow(url);
|
||||
resolve(false);
|
||||
})
|
||||
} else {
|
||||
console.debug("No refresh token");
|
||||
this.oauthService.initCodeFlow(url);
|
||||
resolve(false);
|
||||
}
|
||||
} else {
|
||||
console.debug("Valid token init user");
|
||||
this.store.dispatch(new appCommonActions.InitUser());
|
||||
this.oauthService.initCodeFlow(url);
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
|
@@ -1,25 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { HttpClient, HttpParams } from "@angular/common/http";
|
||||
import { AppConfig } from "../shared/app.config";
|
||||
import { ICodeListItem } from '../models/code.list.item';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CodeListItemService {
|
||||
constructor(public httpClient: HttpClient, public appConfig: AppConfig) {
|
||||
}
|
||||
|
||||
ApiEndpoint() {
|
||||
return this.appConfig.getConfig("apiEndPoint");
|
||||
}
|
||||
|
||||
getCodeListItems(codelist: string[]): Observable<ICodeListItem[]> {
|
||||
var params = new HttpParams();
|
||||
for (const cl of codelist) {
|
||||
params = params.append('cl', cl);
|
||||
}
|
||||
return this.httpClient.get<ICodeListItem[]>(`${this.ApiEndpoint()}/api/v1/codelistitems/`, { params: params });
|
||||
}
|
||||
}
|
@@ -42,8 +42,8 @@ export class EventService {
|
||||
}
|
||||
|
||||
private Authenticate() {
|
||||
var accessToken = this.oauthService.getAccessToken();
|
||||
if (accessToken) {
|
||||
var accessToken = this.oauthService.getAccessToken();
|
||||
if (this.oauthService.hasValidAccessToken()) {
|
||||
this._connection.send('authenticate', this.oauthService.getAccessToken());
|
||||
this.authenticated=true;
|
||||
} else {
|
||||
|
@@ -1,56 +1,49 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable , Observer } from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import { IListItem } from '../models/list.item';
|
||||
import { IItem } from '../models/item';
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { AppConfig } from "../shared/app.config";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class FolderService {
|
||||
|
||||
constructor(public httpClient: HttpClient, public appConfig: AppConfig) {
|
||||
}
|
||||
|
||||
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> {
|
||||
return this.httpClient.get<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/${code}`).pipe(map(i => this.parseDates(i)));
|
||||
}
|
||||
|
||||
getMyRoots(): Observable<IListItem[]> {
|
||||
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/my_roots`).pipe(map(ia => ia.map(i => this.parseDates(i))));
|
||||
}
|
||||
|
||||
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))));
|
||||
}
|
||||
|
||||
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))));
|
||||
}
|
||||
|
||||
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))));
|
||||
}
|
||||
|
||||
moveItem(itemCode: string, newParentCode: string): Observable<IListItem> {
|
||||
const body = { itemCode: itemCode,newParentCode: newParentCode };
|
||||
return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/items/move`, body).pipe(map(i => this.parseDates(i)));
|
||||
}
|
||||
|
||||
createFolder(folder: IItem): Observable<IListItem> {
|
||||
return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/`, folder).pipe(map(i => this.parseDates(i)));
|
||||
}
|
||||
}
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable , Observer } from 'rxjs';
|
||||
import {map} from 'rxjs/operators';
|
||||
import { IListItem } from '../models/list.item';
|
||||
import { IItem } from '../models/item';
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { AppConfig } from "../shared/app.config";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class FolderService {
|
||||
|
||||
constructor(public httpClient: HttpClient, public appConfig: AppConfig) {
|
||||
}
|
||||
|
||||
ApiEndpoint() {
|
||||
return this.appConfig.getConfig("apiEndPoint");
|
||||
}
|
||||
|
||||
getFolder(code: string): Observable<IListItem> {
|
||||
return this.httpClient.get<IListItem>(`${this.ApiEndpoint()}/api/v1/folders/${code}`);
|
||||
}
|
||||
|
||||
getMyRoots(): Observable<IListItem[]> {
|
||||
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/my_roots`);
|
||||
}
|
||||
|
||||
getFolderParents(code: string): Observable<IListItem[]> {
|
||||
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/parents`);
|
||||
}
|
||||
|
||||
getChildFolders(code: string): Observable<IListItem[]> {
|
||||
return this.httpClient.get<IListItem[]>(`${this.ApiEndpoint()}/api/v1/folders/${code}/listfolders`);
|
||||
}
|
||||
|
||||
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}`);
|
||||
}
|
||||
|
||||
moveItem(itemCode: string, newParentCode: string): Observable<IListItem> {
|
||||
const body = { itemCode: itemCode,newParentCode: newParentCode };
|
||||
return this.httpClient.post<IListItem>(`${this.ApiEndpoint()}/api/v1/items/move`, body);
|
||||
}
|
||||
|
||||
createFolder(folder: IItem): Observable<IListItem> {
|
||||
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