56 Commits

Author SHA1 Message Date
d69a72e90d Merge branch 'develop'
Some checks failed
FarmMaps/FarmMapsLib/pipeline/head There was a failure building this commit
# Conflicts:
#	package-lock.json
#	package.json
2026-02-16 22:57:18 +01:00
jenkins
3b2ea10fdc [ci skip] Updated packages #2616
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-03 09:01:03 +00:00
c20754e10a AW-7266 NG0956: The configured tracking expression (track by identity) caused re-creation of the entire collection
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-02-03 09:59:25 +01:00
jenkins
c4b8e9664c [ci skip] Updated packages #2615
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-01-30 12:55:46 +00:00
7822015f8c Revert "NG0956 Tracking expression caused re-creation of the DOM structure."
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
This reverts commit 48261c9657.
2026-01-30 13:54:24 +01:00
48261c9657 NG0956 Tracking expression caused re-creation of the DOM structure.
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-30 13:40:47 +01:00
jenkins
a6bd28ac45 [ci skip] Updated packages #2613
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-01-21 11:49:36 +00:00
jenkins
674a0f2a9e [ci skip] Updated packages #2612
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 11:32:36 +00:00
77800fe0f7 feature/Aw6526Angular21
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 12:30:47 +01:00
3251ba4022 Angular 20, oa. migrating from Karma to Vitest
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 11:53:59 +01:00
a758a38a0a Angular 20, oa. migrating from Karma to Vitest
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-21 11:32:10 +01:00
3c6a8e28c1 Angular 20, oa. migrating from Karma to Vitest
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-21 11:25:38 +01:00
b72e309fbe npm error ERESOLVE unable to resolve dependency tree
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-20 18:01:58 +01:00
d7b978c477 ERESOLVE unable to resolve dependency tree
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2026-01-20 17:53:50 +01:00
930d4fe0b7 Merge branch 'develop' into feature/AW6526Angular20
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
# Conflicts:
#	package-lock.json
2026-01-20 17:38:21 +01:00
642ea33c3c Angular v20 2026-01-20 17:36:35 +01:00
429ab24408 Angular v20 2026-01-20 17:11:03 +01:00
jenkins
eed70c449b [ci skip] Updated packages #2601
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-01-20 15:24:54 +00:00
4bef16aa2b Merge branch 'feature/AW6526Angular19' into develop
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2026-01-20 16:22:54 +01:00
05bc18c291 Angular v19 2026-01-20 16:22:33 +01:00
35ab2f7c90 Angular v19 2026-01-20 16:03:07 +01:00
2eca9b2562 Angular v19 2026-01-20 15:57:18 +01:00
jenkins
e0c89f4358 [ci skip] Updated packages #2600
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2026-01-20 14:36:58 +00:00
jenkins
fdfc1bc5c1 [ci skip] Updated packages #71
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2025-12-10 14:45:00 +00:00
e7b1842297 trigger build
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2025-12-10 15:13:05 +01:00
604c1176c3 Merge branch 'develop'
All checks were successful
FarmMaps.Develop/FarmMapsLib/pipeline/head This commit looks good
2025-12-10 14:20:39 +01:00
jenkins
0ff8992874 [ci skip] Updated packages #2585
Some checks reported errors
FarmMaps.Develop/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2025-11-13 12:46:51 +00:00
b5b84c2547 Merge branch 'develop' of https://git.akkerweb.nl/FarmMaps/FarmMapsLib into develop
Some checks failed
FarmMaps.Develop/FarmMapsLib/pipeline/head There was a failure building this commit
2025-11-13 13:43:49 +01:00
c2b016b2b9 AW-7096 Fix 2025-11-13 13:43:44 +01:00
f66187d3a2 Merge branch 'develop'
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
# Conflicts:
#	package-lock.json
#	package.json
2025-08-21 10:21:42 +02:00
jenkins
df0d13e5dd [ci skip] Updated packages #69
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2025-08-11 08:15:09 +00:00
Willem Dantuma
b347220dd4 Fix flag check
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2025-08-11 10:12:09 +02:00
jenkins
edeadef96f [ci skip] Updated packages #68
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2025-08-11 07:48:07 +00:00
Willem Dantuma
267a299589 Add hideShowLayerValues flag for fm-map-map
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2025-08-11 09:43:44 +02:00
jenkins
134220d75f [ci skip] Updated packages #67
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2025-04-05 06:32:27 +00:00
Willem Dantuma
80ef4ed6fe Merge branch 'develop'
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2025-04-05 08:30:19 +02:00
jenkins
d21f304081 [ci skip] Updated packages #66
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2024-12-10 07:49:03 +00:00
2bad3274fd 2024.12 from develop to master
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
Merge branch 'develop'

# Conflicts:
#	package-lock.json
#	package.json
2024-12-09 13:40:33 +01:00
jenkins
31d3e58062 [ci skip] Updated packages #65
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2024-10-22 14:16:00 +00:00
0231421cef Merge branch 'develop'
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
# Conflicts:
#	package-lock.json
2024-10-22 16:12:25 +02:00
Willem Dantuma
f2c133e9bd Merge branch 'develop'
Some checks failed
FarmMaps/FarmMapsLib/pipeline/head There was a failure building this commit
2024-09-24 20:10:52 +02:00
jenkins
1274d96f1d [ci skip] Updated packages #63
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2024-04-24 00:09:30 +00:00
35ccb4f4c5 Merge branch 'develop'
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2024-04-23 11:54:04 +02:00
jenkins
47fbc5eab9 [ci skip] Updated packages #62
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2024-03-20 12:00:40 +00:00
564d08b5b9 Fixed build
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2024-03-20 11:59:14 +00:00
7e17a49dc6 Merge branch 'develop'
Some checks failed
FarmMaps/FarmMapsLib/pipeline/head There was a failure building this commit
# Conflicts:
#	package-lock.json
#	package.json
2024-03-19 16:04:26 +01:00
jenkins
3f286ee042 [ci skip] Updated packages #58
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2024-02-29 13:56:31 +00:00
747499d009 Merge branch 'develop'
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
# Conflicts:
#	package-lock.json
#	package.json
2024-02-29 14:54:59 +01:00
jenkins
c4f4a4f61e [ci skip] Changed by Jenkins: 57
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2024-01-24 17:19:55 +00:00
6b095b7f49 Trigger build
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2024-01-24 17:18:09 +00:00
b2791eb80b Merge branch 'develop'
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
# Conflicts:
#	README.md
#	package-lock.json
#	package.json
2024-01-24 18:13:28 +01:00
jenkins
4c93a9f766 [ci skip] Changed by Jenkins: 56
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2023-11-16 17:02:44 +00:00
93554bc7c4 Update README.md
All checks were successful
FarmMaps/FarmMapsLib/pipeline/head This commit looks good
2023-11-16 16:40:14 +00:00
d074cd81cf Merge branch 'develop'
Some checks reported errors
FarmMaps/FarmMapsLib/pipeline/head Something is wrong with the build of this commit
2023-11-16 17:37:13 +01:00
87547aafcc Update Jenkinsfile
Some checks failed
FarmMaps/FarmMapsLib/pipeline/head There was a failure building this commit
2023-10-20 12:20:24 +00:00
6ab5ba8a56 Update Jenkinsfile.develop 2023-10-20 12:20:13 +00:00
232 changed files with 2527 additions and 3895 deletions

View File

@@ -52,6 +52,7 @@ ng serve
``` ```
*Go*` *Go*`
Point your browser to http://localhost:4200 Point your browser to http://localhost:4200
*ESLint* *ESLint*

View File

@@ -129,43 +129,14 @@
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"browserTarget": "farmmaps-lib-app:build" "buildTarget": "farmmaps-lib-app:build"
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular/build:unit-test",
"options": { "options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json", "tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js", "browsers": ["chromium"]
"styles": [
"src/styles.css"
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
]
}
}
}
},
"farmmaps-lib-app-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "farmmaps-lib-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "farmmaps-lib-app:serve:production"
}
} }
} }
} }
@@ -189,11 +160,10 @@
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular/build:unit-test",
"options": { "options": {
"main": "projects/common/src/test.ts",
"tsConfig": "projects/common/tsconfig.spec.json", "tsConfig": "projects/common/tsconfig.spec.json",
"karmaConfig": "projects/common/karma.conf.js" "browsers": ["chromium"]
} }
} }
} }
@@ -217,11 +187,10 @@
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular/build:unit-test",
"options": { "options": {
"main": "projects/common-map/src/test.ts",
"tsConfig": "projects/common-map/tsconfig.spec.json", "tsConfig": "projects/common-map/tsconfig.spec.json",
"karmaConfig": "projects/common-map/karma.conf.js" "browsers": ["chromium"]
} }
} }
} }
@@ -245,11 +214,10 @@
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular/build:unit-test",
"options": { "options": {
"main": "projects/common-map3d/src/test.ts",
"tsConfig": "projects/common-map3d/tsconfig.spec.json", "tsConfig": "projects/common-map3d/tsconfig.spec.json",
"karmaConfig": "projects/common-map3d/karma.conf.js" "browsers": ["chromium"]
} }
} }
} }
@@ -273,11 +241,10 @@
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular/build:unit-test",
"options": { "options": {
"main": "projects/ng-openlayers/src/test.ts",
"tsConfig": "projects/ng-openlayers/tsconfig.spec.json", "tsConfig": "projects/ng-openlayers/tsconfig.spec.json",
"karmaConfig": "projects/ng-openlayers/karma.conf.js" "browsers": ["chromium"]
} }
}, },
"lint": { "lint": {
@@ -294,5 +261,31 @@
}, },
"cli": { "cli": {
"analytics": false "analytics": false
},
"schematics": {
"@schematics/angular:component": {
"type": "component"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@schematics/angular:service": {
"type": "service"
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
}
} }
} }

View File

@@ -1,28 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@@ -1,23 +0,0 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to farmmaps-lib-app!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@@ -1,11 +0,0 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View File

@@ -1,13 +0,0 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "farmmaps-lib-app", "name": "farmmaps-lib-app",
"version": "4.19.0", "version": "4.19.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "farmmaps-lib-app", "name": "farmmaps-lib-app",
"version": "4.19.0", "version": "4.19.1",
"dependencies": { "dependencies": {
"@angular-eslint/eslint-plugin": "^18.2.0", "@angular-eslint/eslint-plugin": "^18.2.0",
"@angular/animations": "18.2.3", "@angular/animations": "18.2.3",
@@ -90,7 +90,7 @@
}, },
"dist/common": { "dist/common": {
"name": "@farmmaps/common", "name": "@farmmaps/common",
"version": "4.19.0-prerelease.2583", "version": "4.19.0-prerelease.2584",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@@ -115,7 +115,7 @@
}, },
"dist/common-map": { "dist/common-map": {
"name": "@farmmaps/common-map", "name": "@farmmaps/common-map",
"version": "4.19.0-prerelease.2583", "version": "4.19.0-prerelease.2584",
"dependencies": { "dependencies": {
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
@@ -130,7 +130,7 @@
}, },
"dist/common-map3d": { "dist/common-map3d": {
"name": "@farmmaps/common-map3d", "name": "@farmmaps/common-map3d",
"version": "4.19.0-prerelease.2583", "version": "4.19.0-prerelease.2584",
"dependencies": { "dependencies": {
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
@@ -143,7 +143,7 @@
}, },
"dist/ng-openlayers": { "dist/ng-openlayers": {
"name": "@farmmaps/ng-openlayers", "name": "@farmmaps/ng-openlayers",
"version": "4.19.0-prerelease.2583", "version": "4.19.0-prerelease.2584",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"

View File

@@ -1,6 +1,6 @@
{ {
"name": "farmmaps-lib-app", "name": "farmmaps-lib-app",
"version": "4.19.0", "version": "4.19.1",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",

View File

@@ -11,11 +11,11 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "^18.2.3", "@angular/core": "20.3.16",
"@ngrx/effects": "^18.0.2", "@ngrx/effects": "20.1.0",
"@ngrx/router-store": "^18.0.2", "@ngrx/router-store": "20.1.0",
"@ngrx/store": "^18.0.2", "@ngrx/store": "20.1.0",
"ngrx-store-localstorage": "^18", "ngrx-store-localstorage": "20.1.0",
"tassign": "^1.0.0" "tassign": "^1.0.0"
} }
}, },
@@ -36,52 +36,64 @@
"extraneous": true "extraneous": true
}, },
"node_modules/@angular/common": { "node_modules/@angular/common": {
"version": "18.2.3", "version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz", "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.16.tgz",
"integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==", "integrity": "sha512-GRAziNlntwdnJy3F+8zCOvDdy7id0gITjDnM6P9+n2lXvtDuBLGJKU3DWBbvxcCjtD6JK/g/rEX5fbCxbUHkQQ==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"engines": { "engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "18.2.3", "@angular/core": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/core": { "node_modules/@angular/core": {
"version": "18.2.3", "version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz", "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.16.tgz",
"integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==", "integrity": "sha512-KSFPKvOmWWLCJBbEO+CuRUXfecX2FRuO0jNi9c54ptXMOPHlK1lIojUnyXmMNzjdHgRug8ci9qDuftvC2B7MKg==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"engines": { "engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/compiler": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0", "rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.14.10" "zone.js": "~0.15.0"
},
"peerDependenciesMeta": {
"@angular/compiler": {
"optional": true
},
"zone.js": {
"optional": true
}
} }
}, },
"node_modules/@angular/platform-browser": { "node_modules/@angular/platform-browser": {
"version": "18.2.3", "version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.3.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.16.tgz",
"integrity": "sha512-M2ob4zN7tAcL2mx7U6KnZNqNFPFl9MlPBE0FrjQjIzAjU0wSYPIJXmaPu9aMUp9niyo+He5iX98I+URi2Yc99g==", "integrity": "sha512-YsrLS6vyS77i4pVHg4gdSBW74qvzHjpQRTVQ5Lv/OxIjJdYYYkMmjNalCNgy1ZuyY6CaLIB11ccxhrNnxfKGOQ==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"engines": { "engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/animations": "18.2.3", "@angular/animations": "20.3.16",
"@angular/common": "18.2.3", "@angular/common": "20.3.16",
"@angular/core": "18.2.3" "@angular/core": "20.3.16"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@angular/animations": { "@angular/animations": {
@@ -90,76 +102,67 @@
} }
}, },
"node_modules/@angular/router": { "node_modules/@angular/router": {
"version": "18.2.3", "version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.3.tgz", "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.16.tgz",
"integrity": "sha512-fvD9eSDIiIbeYoUokoWkXzu7/ZaxlzKPUHFqX1JuKuH5ciQDeT/d7lp4mj31Bxammhohzi3+z12THJYsCkj/iQ==", "integrity": "sha512-e1LiQFZaajKqc00cY5FboIrWJZSMnZ64GDp5R0UejritYrqorQQQNOqP1W85BMuY2owibMmxVfX+dJg/Mc8PuQ==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"engines": { "engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "18.2.3", "@angular/common": "20.3.16",
"@angular/core": "18.2.3", "@angular/core": "20.3.16",
"@angular/platform-browser": "18.2.3", "@angular/platform-browser": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@ngrx/effects": { "node_modules/@ngrx/effects": {
"version": "18.0.2", "version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-18.0.2.tgz", "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-20.1.0.tgz",
"integrity": "sha512-YojXcOD9Lsq4kl2HCjENccyUM/mOlgBdtddsg9j/ojzSUgu3ZuBVKLN3atrL2TJYkbMX1MN0RzafSkL3TPGFIA==", "integrity": "sha512-wkIu0QnTarBd1zUswk643H5SILO9Be1OeswMe7g4tXlkNLFRPkyu0BUzXT80PsUPP5p0VrdFz3akrn3HHjjTjA==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"@ngrx/operators": "18.0.1",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "^18.0.0", "@angular/core": "^20.0.0",
"@ngrx/store": "18.0.2", "@ngrx/store": "20.1.0",
"rxjs": "^6.5.3 || ^7.5.0" "rxjs": "^6.5.3 || ^7.5.0"
} }
}, },
"node_modules/@ngrx/operators": {
"version": "18.0.1",
"resolved": "https://registry.npmjs.org/@ngrx/operators/-/operators-18.0.1.tgz",
"integrity": "sha512-M+QMrHNKgcuiLaRGZxJ4aQi5/OCRfKC4+T/63dsHyLFZ53/FFpF6a/ytSO1Q+tzOplZ5o99S+i8FVaZqNQ3LmQ==",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@ngrx/router-store": { "node_modules/@ngrx/router-store": {
"version": "18.0.2", "version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-18.0.2.tgz", "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-20.1.0.tgz",
"integrity": "sha512-jUrQ/uJJ53x8O1XbN2YxH2GpRREZlwS5gRxlCoc4fWL4Us/uS1/K6+QfRmKBPtpTKBIixqsOb+dIUV5iwBrivA==", "integrity": "sha512-XlpQsgmEc1E9Ogiv6QbJ8g8JzjrJOlr7JX9FngHIDLyXQFCMn5wcXsqhjiBjG9JgJLwlzV+v14i7EM/nqwQCGA==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "^18.0.0", "@angular/common": "^20.0.0",
"@angular/core": "^18.0.0", "@angular/core": "^20.0.0",
"@angular/router": "^18.0.0", "@angular/router": "^20.0.0",
"@ngrx/store": "18.0.2", "@ngrx/store": "20.1.0",
"rxjs": "^6.5.3 || ^7.5.0" "rxjs": "^6.5.3 || ^7.5.0"
} }
}, },
"node_modules/@ngrx/store": { "node_modules/@ngrx/store": {
"version": "18.0.2", "version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-18.0.2.tgz", "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-20.1.0.tgz",
"integrity": "sha512-ajwv0+njsO4vzArp9esnFvs1wyUb1U1W8E8LSCKrcW2hWWo9o1Pezj+JRsdQwatxHfrrPFuTDyajsl6GQM/JSA==", "integrity": "sha512-o8j3CGAGedm+BIb+QDhNXrVaU//n9uF0wH0HZWtXHmW1mjRBaQiUA+ZPMUkDwAeN8KuOcoIEC+2QUXxXGVI7ow==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "^18.0.0", "@angular/core": "^20.0.0",
"rxjs": "^6.5.3 || ^7.5.0" "rxjs": "^6.5.3 || ^7.5.0"
} }
}, },
@@ -173,18 +176,19 @@
} }
}, },
"node_modules/ngrx-store-localstorage": { "node_modules/ngrx-store-localstorage": {
"version": "18.0.0", "version": "20.1.0",
"resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-18.0.0.tgz", "resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-20.1.0.tgz",
"integrity": "sha512-WoDePvMWiWF9LQHe+dTqbpm8lxoKCPoIvA0/1enIPTmdLQsOpdDKhMSD5YgwuqDusNfEik3QslProTFGyXZwtw==", "integrity": "sha512-/5+i5qTxZdE8Q5qdSmj7+9JvriAnHwW7RsXzh1rrQ/UHA9vf12q6mJ6wYTTehUO4Qcl2t/K5MRkooN2eG2ZEvw==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"deepmerge": "^4.2.2", "deepmerge": "^4.2.2",
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "^18.0.1", "@angular/common": ">=20.0.0",
"@angular/core": "^18.0.1", "@angular/core": ">=20.0.0",
"@ngrx/store": "^18.0.0" "@ngrx/store": ">=20.0.0"
} }
}, },
"node_modules/rxjs": { "node_modules/rxjs": {
@@ -206,12 +210,6 @@
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
},
"node_modules/zone.js": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
"peer": true
} }
} }
} }

View File

@@ -8,11 +8,16 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "18.2.3", "@angular/core": "21.1.0",
"ngrx-store-localstorage": "^18.0.0", "ngrx-store-localstorage": "20.1.0",
"@ngrx/effects": "^18.0.2", "@ngrx/effects": "21.0.1",
"@ngrx/router-store": "^18.0.2", "@ngrx/router-store": "21.0.1",
"@ngrx/store": "^18.0.2", "@ngrx/store": "21.0.1",
"tassign": "^1.0.0" "tassign": "^1.0.0"
},
"overrides": {
"ngrx-store-localstorage": {
"@angular/core": "$@angular/core"
}
} }
} }

View File

@@ -302,7 +302,7 @@ export class SetReplaceUrl implements Action {
export class SetFeatures implements Action { export class SetFeatures implements Action {
readonly type = SETFEATURES; readonly type = SETFEATURES;
constructor(public features: Array<Feature<Geometry>>) { } constructor(public features: Array<Feature<Geometry>>, public zoomToExtent: boolean = true) { }
} }
export class SetLayerValuesLocation implements Action { export class SetLayerValuesLocation implements Action {

View File

@@ -14,8 +14,9 @@ export interface IDroppedFile {
} }
@Component({ @Component({
selector: 'fm-map-file-drop-target', selector: 'fm-map-file-drop-target',
template: '' template: '',
standalone: false
}) })
export class FileDropTargetComponent implements OnInit, OnDestroy { export class FileDropTargetComponent implements OnInit, OnDestroy {
element: Element; element: Element;

View File

@@ -5,9 +5,10 @@ import { fromLonLat, toLonLat } from 'ol/proj';
@Component({ @Component({
selector: 'fm-map-gps-location', selector: 'fm-map-gps-location',
templateUrl: './gps-location.component.html', templateUrl: './gps-location.component.html',
styleUrls: ['./gps-location.component.scss'] styleUrls: ['./gps-location.component.scss'],
standalone: false
}) })
export class GpsLocation implements OnInit,OnChanges{ export class GpsLocation implements OnInit,OnChanges{

View File

@@ -22,11 +22,12 @@ import BaseLayer from 'ol/layer/Base';
import Feature from 'ol/Feature'; import Feature from 'ol/Feature';
@Component({ @Component({
selector: 'fm-map-item-layers', selector: 'fm-map-item-layers',
template: `<ng-content></ng-content>`, template: `<ng-content></ng-content>`,
providers: [ providers: [
{ provide: LayerGroupComponent, useExisting: forwardRef(() => ItemLayersComponent) } { provide: LayerGroupComponent, useExisting: forwardRef(() => ItemLayersComponent) }
] ],
standalone: false
}) })
export class ItemLayersComponent extends LayerGroupComponent implements OnChanges, OnInit,OnDestroy { export class ItemLayersComponent extends LayerGroupComponent implements OnChanges, OnInit,OnDestroy {

View File

@@ -22,11 +22,12 @@ import { getCenter } from 'ol/extent';
import { formatNumber } from '@angular/common'; import { formatNumber } from '@angular/common';
@Component({ @Component({
selector: 'fm-map-item-source-vector', selector: 'fm-map-item-source-vector',
template: `<ng-content></ng-content>`, template: `<ng-content></ng-content>`,
providers: [ providers: [
{ provide: SourceVectorComponent, useExisting: forwardRef(() => ItemVectorSourceComponent) } { provide: SourceVectorComponent, useExisting: forwardRef(() => ItemVectorSourceComponent) }
] ],
standalone: false
}) })
export class ItemVectorSourceComponent extends SourceVectorComponent implements OnInit, OnDestroy, OnChanges { export class ItemVectorSourceComponent extends SourceVectorComponent implements OnInit, OnDestroy, OnChanges {
instance: Vector<Feature<Geometry>>; instance: Vector<Feature<Geometry>>;

View File

@@ -1,31 +1,50 @@
<div> <div>
<div class="layerlist" *ngIf="itemLayers.length > 0;else noLayers"> @if (itemLayers.length > 0) {
<div class="list-group"> <div class="layerlist">
<div *ngFor="let itemLayer of itemLayers" class="list-group-item list-group-item-action p-2 text-truncate" [ngClass]="{'active' : selectedLayer==itemLayer}"> <div class="list-group">
<div (click)="handleSelectLayer($event,itemLayer)" [title]="itemLayer.item.name">{{itemLayer.item.name}}</div> @for (itemLayer of itemLayers; track itemLayer.item.code) {
<div class="mt-1" *ngIf="selectedLayer==itemLayer && !baseLayers"> <div class="list-group-item list-group-item-action p-2 text-truncate" [ngClass]="{'active' : selectedLayer==itemLayer}">
<span class="btn-group"> <div (click)="handleSelectLayer($event,itemLayer)" [title]="itemLayer.item.name">{{itemLayer.item.name}}</div>
<a title="Toggle visibility"href="#" class="btn btn-light btn-sm" (click)="handleToggleVisibility($event,itemLayer)"><i class="fa" aria-hidden="true" [ngClass]="{'fa-eye':!itemLayer.visible,'fa-eye-slash':itemLayer.visible}"></i></a> @if (selectedLayer==itemLayer && !baseLayers) {
<a title="Transparency 25%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.25)">25%</a> <div class="mt-1">
<a title="Transparency 50%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.5)">50%</a> <span class="btn-group">
<a title="Transparency 75%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.75)">75%</a> <a title="Toggle visibility"href="#" class="btn btn-light btn-sm" (click)="handleToggleVisibility($event,itemLayer)"><i class="fa" aria-hidden="true" [ngClass]="{'fa-eye':!itemLayer.visible,'fa-eye-slash':itemLayer.visible}"></i></a>
<a title="No transparency" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,1)">100%</a> @if (itemLayer.visible) {
</span> <a title="Transparency 25%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.25)">25%</a>
<a href="#" title="Zoom to extent" class="btn btn-light btn-sm" (click)="handleZoomToExtent($event,itemLayer)"><i class="far fa-search-plus" aria-hidden="true"></i></a> }
<span *ngIf="firstLayer(itemLayer)"><a href="#" title="Toggle legend" class="btn btn-light btn-sm" (click)="itemLayer.legendVisible=toggleLegend($event,itemLayer.legendVisible)"><i class="far fa-chart-bar" aria-hidden="true"></i></a></span> @if (itemLayer.visible) {
<span *ngIf="!dataLayers" class="float-end"><a href="#" title="Remove overlay" class="btn btn-light btn-sm" (click)="handleDelete($event,itemLayer)"><i class="fas fa-layer-minus" aria-hidden="true"></i></a></span> <a title="Transparency 50%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.5)">50%</a>
</div> }
<div *ngIf="itemLayer.legendVisible"> @if (itemLayer.visible) {
<div class="card legend"> <a title="Transparency 75%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.75)">75%</a>
<fm-map-layer-legend [layer]="firstLayer(itemLayer)" (click)="handleLegendClick($event,itemLayer);" [histogramenabled]="true"></fm-map-layer-legend> }
</div> @if (itemLayer.visible) {
<a title="No transparency" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,1)">100%</a>
}
</span>
<a href="#" title="Zoom to extent" class="btn btn-light btn-sm" (click)="handleZoomToExtent($event,itemLayer)"><i class="far fa-search-plus" aria-hidden="true"></i></a>
@if (firstLayer(itemLayer)) {
<span><a href="#" title="Toggle legend" class="btn btn-light btn-sm" (click)="itemLayer.legendVisible=toggleLegend($event,itemLayer.legendVisible)"><i class="far fa-chart-bar" aria-hidden="true"></i></a></span>
}
@if (!dataLayers) {
<span class="float-end"><a href="#" title="Remove overlay" class="btn btn-light btn-sm" (click)="handleDelete($event,itemLayer)"><i class="fas fa-layer-minus" aria-hidden="true"></i></a></span>
}
</div>
}
@if (itemLayer.legendVisible) {
<div>
<div class="card legend">
<fm-map-layer-legend [layer]="firstLayer(itemLayer)" (click)="handleLegendClick($event,itemLayer);" [histogramenabled]="true"></fm-map-layer-legend>
</div>
</div>
}
</div> </div>
}
</div> </div>
</div> </div>
</div> } @else {
<div class="list-group">
<div class="list-group-item" i18n>No layers</div>
</div>
}
</div> </div>
<ng-template #noLayers>
<div class="list-group">
<div class="list-group-item" i18n>No layers</div>
</div>
</ng-template>

View File

@@ -2,9 +2,10 @@ import { Component,Input,Output,EventEmitter } from '@angular/core';
import { IItemLayer } from '../../../models/item.layer'; import { IItemLayer } from '../../../models/item.layer';
@Component({ @Component({
selector: 'fm-map-layer-list', selector: 'fm-map-layer-list',
templateUrl: './layer-list.component.html', templateUrl: './layer-list.component.html',
styleUrls: ['./layer-list.component.scss'] styleUrls: ['./layer-list.component.scss'],
standalone: false
}) })
export class LayerListComponent { export class LayerListComponent {

View File

@@ -1,18 +1,27 @@
<div #layerValues class="layer-values"> <div #layerValues class="layer-values">
<div class="cross" *ngIf="enabled$ | async"> @if (enabled$ | async) {
<div class="pointer"></div> <div class="cross" >
<div class="values-container border border-dark rounded p-2" *ngIf="(layerValues$ | async ) as layers"> <div class="pointer"></div>
<div class="lonlat pb-2 "><span class="font-weight-bold">{{lonlat$}}</span><i class="ms-2 fal fa-copy" (click)="copyToClipboard()"></i> </div> @if ((layerValues$ | async ); as layers) {
<ul class="value-list p-0 mb-0" *ngIf="layers.length>0 ;else no_data"> <div class="values-container border border-dark rounded p-2">
<li class="border-top pt-1 pb-1 value" *ngFor="let layerValue of layers"> <div class="lonlat pb-2 "><span class="font-weight-bold">{{lonlat$}}</span><i class="ms-2 fal fa-copy" (click)="copyToClipboard()"></i> </div>
<div>{{layerValue.layerName}}</div> @if (layers.length>0 ) {
<div>{{layerValue.date|date}}</div> <ul class="value-list p-0 mb-0">
<div><span *ngIf="layerValue.quantity"><span class="me-1">{{layerValue.quantity}}</span> </span><span class="me-1 font-weight-bold">{{getScaledValue(layerValue)}}</span><span>{{layerValue.unit}}</span></div> @for (layerValue of layers; track $index) {
</li> <li class="border-top pt-1 pb-1 value">
</ul> <div>{{layerValue.layerName}}</div>
<ng-template #no_data> <div>{{layerValue.date|date}}</div>
<div i18n class="border-top pt-1 pb-1">No data at location</div> <div>@if (layerValue.quantity) {
</ng-template> <span><span class="me-1">{{layerValue.quantity}}</span> </span>
</div> }<span class="me-1 font-weight-bold">{{getScaledValue(layerValue)}}</span><span>{{layerValue.unit}}</span></div>
</div> </li>
</div> }
</ul>
} @else {
<div i18n class="border-top pt-1 pb-1">No data at location</div>
}
</div>
}
</div>
}
</div>

View File

@@ -14,9 +14,10 @@ import { GeoJSON, WKT } from 'ol/format';
import { Point } from 'ol/geom'; import { Point } from 'ol/geom';
@Component({ @Component({
selector: 'fm-map-layer-values', selector: 'fm-map-layer-values',
templateUrl: './layer-values.component.html', templateUrl: './layer-values.component.html',
styleUrls: ['./layer-values.component.scss'] styleUrls: ['./layer-values.component.scss'],
standalone: false
}) })
export class LayerValuesComponent implements OnInit, AfterViewInit { export class LayerValuesComponent implements OnInit, AfterViewInit {

View File

@@ -6,10 +6,11 @@ import { Geometry } from 'ol/geom';
import Feature from 'ol/Feature'; import Feature from 'ol/Feature';
@Component({ @Component({
selector: 'fm-map-aol-layer-vector-image', selector: 'fm-map-aol-layer-vector-image',
template: ` template: `
<ng-content></ng-content> <ng-content></ng-content>
`, `,
standalone: false
}) })
export class LayerVectorImageComponent extends LayerVectorComponent implements OnInit, OnDestroy, OnChanges { export class LayerVectorImageComponent extends LayerVectorComponent implements OnInit, OnDestroy, OnChanges {
//public source: Vector; //public source: Vector;

View File

@@ -6,9 +6,10 @@ import { fromLonLat } from 'ol/proj';
@Component({ @Component({
selector: 'fm-map-pan-to-location', selector: 'fm-map-pan-to-location',
templateUrl: './pan-to-location.component.html', templateUrl: './pan-to-location.component.html',
styleUrls: ['./pan-to-location.component.scss'] styleUrls: ['./pan-to-location.component.scss'],
standalone: false
}) })
export class PanToLocation implements OnInit,OnChanges{ export class PanToLocation implements OnInit,OnChanges{

View File

@@ -6,9 +6,10 @@ import {View} from 'ol';
@Component({ @Component({
selector: 'fm-map-rotation-reset', selector: 'fm-map-rotation-reset',
templateUrl: './rotation-reset.component.html', templateUrl: './rotation-reset.component.html',
styleUrls: ['./rotation-reset.component.scss'] styleUrls: ['./rotation-reset.component.scss'],
standalone: false
}) })
export class RotationResetComponent implements OnInit { export class RotationResetComponent implements OnInit {
view: View; view: View;

View File

@@ -4,8 +4,9 @@ import { ViewComponent, MapComponent } from '@farmmaps/ng-openlayers';
@Component({ @Component({
selector: 'fm-map-zoom-to-extent', selector: 'fm-map-zoom-to-extent',
template: `<ng-content></ng-content>` template: `<ng-content></ng-content>`,
standalone: false
}) })
export class ZoomToExtentComponent implements OnChanges { export class ZoomToExtentComponent implements OnChanges {
view: ViewComponent; view: ViewComponent;

View File

@@ -11,9 +11,10 @@ import { Observable } from 'rxjs';
@Component({ @Component({
selector: 'fm-map-feature-list-container', selector: 'fm-map-feature-list-container',
templateUrl: './feature-list-container.component.html', templateUrl: './feature-list-container.component.html',
styleUrls: ['./feature-list-container.component.scss'] styleUrls: ['./feature-list-container.component.scss'],
standalone: false
}) })
export class FeatureListContainerComponent { export class FeatureListContainerComponent {

View File

@@ -1,14 +1,20 @@
<div class="card border-0"> <div class="card border-0">
<div class="card-body" *ngIf="(schemeItem|async);let schemeItem"> @if ((schemeItem|async); as schemeItem) {
<fm-back-button></fm-back-button> <div class="card-body">
<h4 i18n>Farm</h4> <fm-back-button></fm-back-button>
<h3>{{schemeItem.name}}</h3> <h4 i18n>Farm</h4>
<div *ngIf="features;let features"> <h3>{{schemeItem.name}}</h3>
<div class="cropfields"> @if (features; as features) {
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)"> <div>
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container> <div class="cropfields">
@for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div>
</div> </div>
</div> }
</div> </div>
</div> }
</div> </div>

View File

@@ -12,9 +12,10 @@ import { Observable } from 'rxjs';
@ForItemType("vnd.farmmaps.itemtype.cropfield") @ForItemType("vnd.farmmaps.itemtype.cropfield")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-feature-list-cropfield', selector: 'fm-map-feature-list-cropfield',
templateUrl: './feature-list-cropfield.component.html', templateUrl: './feature-list-cropfield.component.html',
styleUrls: ['./feature-list-cropfield.component.scss'] styleUrls: ['./feature-list-cropfield.component.scss'],
standalone: false
}) })
export class FeatureListCropfieldComponent extends AbstractFeatureListComponent implements OnInit { export class FeatureListCropfieldComponent extends AbstractFeatureListComponent implements OnInit {

View File

@@ -1,13 +1,17 @@
<div class="card border-0"> <div class="card border-0">
<div class="card-body"> <div class="card-body">
<fm-back-button></fm-back-button> <fm-back-button></fm-back-button>
<h3><i class="far fa-farm"></i>&nbsp;<span i18n>Farms</span></h3> <h3><i class="far fa-farm"></i>&nbsp;<span i18n>Farms</span></h3>
<div *ngIf="features;let features"> @if (features; as features) {
<div class="farms"> <div>
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features"[ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)"> <div class="farms">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container> @for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3"[ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div> </div>
</div> </div>
</div> }
</div> </div>
</div> </div>

View File

@@ -16,7 +16,8 @@ import { Geometry} from 'ol/geom';
@Component({ @Component({
selector: 'fm-map-feature-list-croppingscheme', selector: 'fm-map-feature-list-croppingscheme',
templateUrl: './feature-list-croppingscheme.component.html', templateUrl: './feature-list-croppingscheme.component.html',
styleUrls: ['./feature-list-croppingscheme.component.scss'] styleUrls: ['./feature-list-croppingscheme.component.scss'],
standalone: false
}) })
export class FeatureListCroppingschemeComponent extends AbstractFeatureListComponent { export class FeatureListCroppingschemeComponent extends AbstractFeatureListComponent {

View File

@@ -6,12 +6,13 @@ import { WidgetHostDirective } from '../widget-host/widget-host.directive';
@Component({ @Component({
selector: 'fm-map-feature-list-feature-container', selector: 'fm-map-feature-list-feature-container',
template: ` template: `
<div> <div>
<ng-template fm-map-widget-host></ng-template> <ng-template fm-map-widget-host></ng-template>
</div> </div>
` `,
standalone: false
}) })
export class FeatureListFeatureContainerComponent { export class FeatureListFeatureContainerComponent {

View File

@@ -1,14 +1,16 @@
<div *ngIf="feature;let feature" class="d-flex m-0"> @if (feature; as feature) {
<div class="p-2"> <div class="d-flex m-0">
<div class="thumbnail"> <div class="p-2">
<fm-map-feature-thumbnail [feature]="feature"></fm-map-feature-thumbnail> <div class="thumbnail">
<fm-map-feature-thumbnail [feature]="feature"></fm-map-feature-thumbnail>
</div>
</div> </div>
</div> <div class="flex-grow p-2 overflow-hidden">
<div class="flex-grow p-2 overflow-hidden"> <h1 class="card-title" title="{{feature.get('name')}}">{{feature.get('name')}}</h1>
<h1 class="card-title" title="{{feature.get('name')}}">{{feature.get('name')}}</h1> <div class="card-text"><span>{{areaInHa(feature)| number:'1.2-2'}}
<div class="card-text"><span>{{areaInHa(feature)| number:'1.2-2'}}
ha</span>&nbsp;<span>{{feature.get('cropTypeName')}}</span> </div> ha</span>&nbsp;<span>{{feature.get('cropTypeName')}}</span> </div>
<div class="card-text"><span>{{feature.get('datadate')|date}}</span> - <div class="card-text"><span>{{feature.get('datadate')|date}}</span> -
<span>{{feature.get('dataenddate')|date}}</span> </div> <span>{{feature.get('dataenddate')|date}}</span> </div>
</div> </div>
</div> </div>
}

View File

@@ -13,9 +13,10 @@ import {getArea} from 'ol/sphere';
@ForItemType("vnd.farmmaps.itemtype.cropfield") @ForItemType("vnd.farmmaps.itemtype.cropfield")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-feature-list-feature-cropfield', selector: 'fm-map-feature-list-feature-cropfield',
templateUrl: './feature-list-feature-cropfield.component.html', templateUrl: './feature-list-feature-cropfield.component.html',
styleUrls: ['./feature-list-feature-cropfield.component.scss'] styleUrls: ['./feature-list-feature-cropfield.component.scss'],
standalone: false
}) })
export class FeatureListFeatureCropfieldComponent extends AbstractFeatureListFeatureComponent { export class FeatureListFeatureCropfieldComponent extends AbstractFeatureListFeatureComponent {

View File

@@ -1,6 +1,8 @@
<div *ngIf="feature;let feature" class="row m-0"> @if (feature; as feature) {
<div class="row m-0">
<div class="col p-2"> <div class="col p-2">
<h1 class="card-title" title="{{feature.get('name')}}">{{feature.get('name')}}</h1> <h1 class="card-title" title="{{feature.get('name')}}">{{feature.get('name')}}</h1>
<div class="card-text">{{feature.get('datadate')|date:'shortDate'}}</div> <div class="card-text">{{feature.get('datadate')|date:'shortDate'}}</div>
</div> </div>
</div> </div>
}

View File

@@ -10,9 +10,10 @@ import { ForItemType } from '../for-item/for-itemtype.decorator';
@ForItemType("vnd.farmmaps.itemtype.croppingscheme") @ForItemType("vnd.farmmaps.itemtype.croppingscheme")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-feature-list-feature-croppingscheme', selector: 'fm-map-feature-list-feature-croppingscheme',
templateUrl: './feature-list-feature-croppingscheme.component.html', templateUrl: './feature-list-feature-croppingscheme.component.html',
styleUrls: ['./feature-list-feature-croppingscheme.component.scss'] styleUrls: ['./feature-list-feature-croppingscheme.component.scss'],
standalone: false
}) })
export class FeatureListFeatureCroppingschemeComponent extends AbstractFeatureListFeatureComponent { export class FeatureListFeatureCroppingschemeComponent extends AbstractFeatureListFeatureComponent {

View File

@@ -1,12 +1,18 @@
<div *ngIf="feature;let feature" class="row m-0"> @if (feature; as feature) {
<div class="row m-0">
<div class="col-3 w-25 m-0 p-2 thumbnail"> <div class="col-3 w-25 m-0 p-2 thumbnail">
<img *ngIf="feature.get('thumbnail')" [src]="config.getConfig('apiEndPoint') + '/api/v1/items/'+feature.get('code')+'/thumbnail'" /> @if (feature.get('thumbnail')) {
<div *ngIf="!feature.get('thumbnail')" [style.background-color]="itemTypeService.getColor(feature.get('itemType'))"> <img [src]="config.getConfig('apiEndPoint') + '/api/v1/items/'+feature.get('code')+'/thumbnail'" />
<i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))"></i> }
</div> @if (!feature.get('thumbnail')) {
<div [style.background-color]="itemTypeService.getColor(feature.get('itemType'))">
<i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))"></i>
</div>
}
</div> </div>
<div class="col p-2"> <div class="col p-2">
<h1 class="card-title" title="{{feature.get('name')}}"><i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))" [style.color]="itemTypeService.getColor(feature.get('itemType'))"></i>&nbsp;{{feature.get('name')}}</h1> <h1 class="card-title" title="{{feature.get('name')}}"><i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))" [style.color]="itemTypeService.getColor(feature.get('itemType'))"></i>&nbsp;{{feature.get('name')}}</h1>
<div class="card-text">{{feature.get('datadate')|date:'shortDate'}}</div> <div class="card-text">{{feature.get('datadate')|date:'shortDate'}}</div>
</div> </div>
</div> </div>
}

View File

@@ -18,9 +18,10 @@ export abstract class AbstractFeatureListFeatureComponent {
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-feature-list-feature', selector: 'fm-map-feature-list-feature',
templateUrl: './feature-list-feature.component.html', templateUrl: './feature-list-feature.component.html',
styleUrls: ['./feature-list-feature.component.scss'] styleUrls: ['./feature-list-feature.component.scss'],
standalone: false
}) })
export class FeatureListFeatureComponent extends AbstractFeatureListFeatureComponent { export class FeatureListFeatureComponent extends AbstractFeatureListFeatureComponent {

View File

@@ -1,14 +1,20 @@
<div class="card border-0"> <div class="card border-0">
<div class="card-body" *ngIf="(schemeItem|async);let schemeItem"> @if ((schemeItem|async); as schemeItem) {
<fm-back-button></fm-back-button> <div class="card-body">
<h4 i18n>Farm</h4> <fm-back-button></fm-back-button>
<h3>{{schemeItem.name}}</h3> <h4 i18n>Farm</h4>
<div *ngIf="features;let features"> <h3>{{schemeItem.name}}</h3>
<div class="cropfields"> @if (features; as features) {
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)"> <div>
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container> <div class="cropfields">
@for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div>
</div> </div>
</div> }
</div> </div>
</div> }
</div> </div>

View File

@@ -12,9 +12,10 @@ import { Observable } from 'rxjs';
@ForItemType("vnd.farmmaps.itemtype.observation") @ForItemType("vnd.farmmaps.itemtype.observation")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-feature-list-observation', selector: 'fm-map-feature-list-observation',
templateUrl: './feature-list-observation.component.html', templateUrl: './feature-list-observation.component.html',
styleUrls: ['./feature-list-observation.component.scss'] styleUrls: ['./feature-list-observation.component.scss'],
standalone: false
}) })
export class FeatureListObservationComponent extends AbstractFeatureListComponent implements OnInit { export class FeatureListObservationComponent extends AbstractFeatureListComponent implements OnInit {

View File

@@ -1,6 +1,10 @@
<div *ngIf="features;let features"> @if (features; as features) {
<fm-back-button></fm-back-button> <div>
<div class="row m-0 ps-3 pe-3" *ngFor="let feature of features" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)"> <fm-back-button></fm-back-button>
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container> @for (feature of features; track $index) {
<div class="row m-0 ps-3 pe-3" [ngClass]="{'selected':isFeatureSelected(feature)}" (click)="handleFeatureClick(feature)" (mouseenter)="handleFeatureMouseEnter(feature)" (mouseleave)="handleFeatureMouseLeave(feature)">
<fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div>
}
</div> </div>
</div> }

View File

@@ -58,9 +58,10 @@ export abstract class AbstractFeatureListComponent {
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-feature-list', selector: 'fm-map-feature-list',
templateUrl: './feature-list.component.html', templateUrl: './feature-list.component.html',
styleUrls: ['./feature-list.component.scss'] styleUrls: ['./feature-list.component.scss'],
standalone: false
}) })
export class FeatureListComponent extends AbstractFeatureListComponent { export class FeatureListComponent extends AbstractFeatureListComponent {

View File

@@ -6,9 +6,10 @@ import * as render from 'ol/render';
import * as style from 'ol/style'; import * as style from 'ol/style';
@Component({ @Component({
selector: 'fm-map-feature-thumbnail', selector: 'fm-map-feature-thumbnail',
templateUrl: './feature-thumbnail.component.html', templateUrl: './feature-thumbnail.component.html',
styleUrls: ['./feature-thumbnail.component.scss'] styleUrls: ['./feature-thumbnail.component.scss'],
standalone: false
}) })
export class GeometryThumbnailComponent implements AfterViewInit { export class GeometryThumbnailComponent implements AfterViewInit {

View File

@@ -4,7 +4,8 @@ import { Source } from 'ol/source';
import { MapComponent } from '@farmmaps/ng-openlayers'; import { MapComponent } from '@farmmaps/ng-openlayers';
@Directive({ @Directive({
selector: '[fmMapIfZoomToShow]', selector: '[fmMapIfZoomToShow]',
standalone: false
}) })
export class ifZoomToShowDirective implements OnInit { export class ifZoomToShowDirective implements OnInit {
@Input() @Input()

View File

@@ -5,12 +5,13 @@ import { IItem, IListItem } from '@farmmaps/common';
@Component({ @Component({
selector: 'fm-map-item-list-item-container', selector: 'fm-map-item-list-item-container',
template: ` template: `
<div style="height:100%"> <div style="height:100%">
<ng-template fm-map-widget-host></ng-template> <ng-template fm-map-widget-host></ng-template>
</div> </div>
` `,
standalone: false
}) })
export class ItemListItemContainerComponent { export class ItemListItemContainerComponent {

View File

@@ -1,4 +1,6 @@
<div *ngIf="item;let item" class="widget" [style.background-color]="itemTypeService.getColor(item.itemType)"> @if (item; as item) {
<div class="align-middle icon" [ngClass]="itemTypeService.getIcon(item.itemType)"></div> <div class="widget" [style.background-color]="itemTypeService.getColor(item.itemType)">
<div class="title">{{item.name}}</div> <div class="align-middle icon" [ngClass]="itemTypeService.getIcon(item.itemType)"></div>
</div> <div class="title">{{item.name}}</div>
</div>
}

View File

@@ -42,8 +42,9 @@ export abstract class AbstractItemWidgetComponent {
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-item-list-item', selector: 'fm-map-item-list-item',
templateUrl: './item-list-item.component.html', templateUrl: './item-list-item.component.html',
styleUrls: ['./item-list-item.component.scss'] styleUrls: ['./item-list-item.component.scss'],
standalone: false
}) })
export class ItemListItemComponent extends AbstractItemListItemComponent { export class ItemListItemComponent extends AbstractItemListItemComponent {

View File

@@ -1,7 +1,11 @@
<div *ngIf="items;let items" class="widget-container pt-0"> @if (items; as items) {
<div class="widget" *ngFor="let item of items" (click)="handleItemClick(item)"> <div class="widget-container pt-0">
<div class="content"> @for (item of items; track item.code) {
<fm-map-item-list-item-container [item]="item" class="item-container"></fm-map-item-list-item-container> <div class="widget" (click)="handleItemClick(item)">
</div> <div class="content">
<fm-map-item-list-item-container [item]="item" class="item-container"></fm-map-item-list-item-container>
</div>
</div>
}
</div> </div>
</div> }

View File

@@ -28,9 +28,10 @@ export abstract class AbstractItemListComponent {
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-item-list', selector: 'fm-map-item-list',
templateUrl: './item-list.component.html', templateUrl: './item-list.component.html',
styleUrls: ['./item-list.component.scss'] styleUrls: ['./item-list.component.scss'],
standalone: false
}) })
export class ItemListComponent extends AbstractItemListComponent { export class ItemListComponent extends AbstractItemListComponent {

View File

@@ -1,7 +1,11 @@
<div *ngIf="widgets;let widgets" class="widget-container pt-0"> @if (widgets; as widgets) {
<div class="widget" *ngFor="let widget of widgets"> <div class="widget-container pt-0">
<div class="content"> @for (widget of widgets; track widget.item.code) {
<ng-template #widgetTemplate></ng-template> <div class="widget">
</div> <div class="content">
<ng-template #widgetTemplate></ng-template>
</div>
</div>
}
</div> </div>
</div> }

View File

@@ -8,9 +8,10 @@ import { AbstractItemWidgetComponent } from '../item-list-item/item-list-item.co
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-item-widget-list', selector: 'fm-map-item-widget-list',
templateUrl: './item-widget-list.component.html', templateUrl: './item-widget-list.component.html',
styleUrls: ['./item-widget-list.component.scss'] styleUrls: ['./item-widget-list.component.scss'],
standalone: false
}) })
export class ItemWidgetListComponent implements AfterViewInit { export class ItemWidgetListComponent implements AfterViewInit {

View File

@@ -19,12 +19,14 @@
<fm-map-layer-list [itemLayers]="overlayLayers|async" [selectedLayer]="selectedOverlayLayer|async" (onDelete)="handleOnDelete($event)" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list> <fm-map-layer-list [itemLayers]="overlayLayers|async" [selectedLayer]="selectedOverlayLayer|async" (onDelete)="handleOnDelete($event)" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list>
</div> </div>
</li> </li>
<li class="nav-item py-0" *ngIf="selectedItemLayer$ | async as selectedItemLayer"> @if (selectedItemLayer$ | async; as selectedItemLayer) {
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Overlay slider</span><span class="slideButton"><a href="#" title="Compare" class="btn btn-light btn-sm" (click)="handleToggleShowDatalayerSlide($event)"><i class="fal fa-sliders-h-square"></i></a></span></span> <li class="nav-item py-0">
<div class="mb-4"> <span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Overlay slider</span><span class="slideButton"><a href="#" title="Compare" class="btn btn-light btn-sm" (click)="handleToggleShowDatalayerSlide($event)"><i class="fal fa-sliders-h-square"></i></a></span></span>
<fm-map-layer-list [dataLayers]="true" [itemLayers]="[selectedItemLayer]" [selectedLayer]="selectedItemLayer" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list> <div class="mb-4">
</div> <fm-map-layer-list [dataLayers]="true" [itemLayers]="[selectedItemLayer]" [selectedLayer]="selectedItemLayer" (onToggleVisibility)="handleOnToggleVisibility($event)" (onSetOpacity)="handleOnSetOpacity($event)" (onZoomToExtent)="handleZoomToExtent($event)" (onSelectLayer)="handleSelectOverlayLayer($event)"></fm-map-layer-list>
</li> </div>
</li>
}
<li class="nav-item py-0"> <li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Aerial photos</span></span> <span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Aerial photos</span></span>
<div class="mb-4"> <div class="mb-4">
@@ -32,6 +34,6 @@
</div> </div>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -7,9 +7,10 @@ import {createEmpty,extend } from 'ol/extent';
import { filter, map, Observable } from 'rxjs'; import { filter, map, Observable } from 'rxjs';
@Component({ @Component({
selector: 'fm-map-layer-switcher', selector: 'fm-map-layer-switcher',
templateUrl: './layer-switcher.component.html', templateUrl: './layer-switcher.component.html',
styleUrls: ['./layer-switcher.component.scss'] styleUrls: ['./layer-switcher.component.scss'],
standalone: false
}) })
export class LayerSwitcher implements OnInit,OnChanges{ export class LayerSwitcher implements OnInit,OnChanges{

View File

@@ -8,8 +8,8 @@
<div class="col-8 nopadding" ><span i18n>Max</span>:</div> <div class="col-8 nopadding" ><span i18n>Max</span>:</div>
<div class="col-4 pull-left nopadding">{{ getScaledValue(histogram.max)| number:'1.0-2'}}</div> <div class="col-4 pull-left nopadding">{{ getScaledValue(histogram.max)| number:'1.0-2'}}</div>
<ng-container *ngIf="showConfidenceInterval()"> @if (showConfidenceInterval()) {
<div class="col-8 nopadding"><span i18n>Confidence interval</span>:</div> <div class="col-8 nopadding"><span i18n>Confidence interval</span>:</div>
<div class="col-4 pull-left nopadding">{{ getScaledValue(histogram.confidence * 100)| number:'1.0-0'}}%</div> <div class="col-4 pull-left nopadding">{{ getScaledValue(histogram.confidence * 100)| number:'1.0-0'}}%</div>
</ng-container> }
</div> </div>

View File

@@ -2,12 +2,13 @@ import {Component, Input} from '@angular/core';
import {IHistogram, ILayer} from '../../../models/color.map'; import {IHistogram, ILayer} from '../../../models/color.map';
@Component({ @Component({
selector: 'fm-map-histogram-details', selector: 'fm-map-histogram-details',
templateUrl: './histogram-details.component.html', templateUrl: './histogram-details.component.html',
styles: ['.nopadding{\n' + styles: ['.nopadding{\n' +
' padding: 0 !important;\n' + ' padding: 0 !important;\n' +
' margin: 0 !important;\n' + ' margin: 0 !important;\n' +
'}'] '}'],
standalone: false
}) })
export class HistogramDetailsComponent { export class HistogramDetailsComponent {

View File

@@ -1,46 +1,66 @@
<table class="container" *ngIf="showLegend()"> @if (showLegend()) {
<tr> <table class="container">
<td colspan="3"> <tr>
<div class="title"> <td colspan="3">
<h4 *ngIf="showTitle">{{layer.name}}</h4> <div class="title">
<b *ngIf="layer.unit">({{layer.unit}})</b> @if (showTitle) {
</div> <h4>{{layer.name}}</h4>
</td> }
<td> @if (layer.unit) {
<a i18n-title title="more info"><i class="fal fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a> <b>({{layer.unit}})</b>
</td> }
<td colspan="2"> </div>
<div class="title" *ngIf="histogramenabled"> </td>
<h4>{{histogram}}</h4> <td>
<b *ngIf="histogramunit">({{histogramunit}})</b> <a i18n-title title="more info"><i class="fal fa-info-circle text-primary" (click)="hideHistogramDetails = !hideHistogramDetails"></i></a>
</div> </td>
</td> <td colspan="2">
</tr> @if (histogramenabled) {
<tr *ngFor="let entry of layer.renderer.colorMap.entries; let i = index "> <div class="title">
<td class="legend-items"><span [style.background-color]="getAlphaHex(entry.color)" [style.border-color]="getHex(entry.color)"class="color"></span></td> <h4>{{histogram}}</h4>
<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> @if (histogramunit) {
<td class="histogram-items"> <b>({{histogramunit}})</b>
<div *ngIf="showHistogram()"> }
<span class="bar" [style.background-color]="getHex(entry.color)" [style.width]="getPart(layer.renderer, i)"> </div>
</span> }
<span *ngIf="getPercentage(layer.renderer,i) as percentage" class="bar-label">{{percentage | number:'1.0-2'}} %</span> </td>
</div> </tr>
</td> @for (entry of layer.renderer.colorMap.entries; track $index; let i = $index) {
</tr> <tr>
<tr *ngIf="showHistogram()"> <td class="legend-items"><span [style.background-color]="getAlphaHex(entry.color)" [style.border-color]="getHex(entry.color)"class="color"></span></td>
<td colspan="4" class="pb-1 pt-1"> <td class="legend-items-text">@if (!entry.label) {
<div class="info" [ngbCollapse]="hideHistogramDetails"> <span>{{getScaledValue(entry.value,layer.scale) | number:'1.0-2'}} {{legendunit}}</span>
<ng-container *ngIf="bandContainsStatistics(); else histogram"> }@if (entry.label) {
<fm-map-statistics-details [statistics]="layer.renderer.band.statistics" [scale]="layer.scale"></fm-map-statistics-details> <span>{{entry.label}}</span>
</ng-container> }</td>
<td class="histogram-items">
<ng-template #histogram> @if (showHistogram()) {
<fm-map-histogram-details [histogram]="layer.renderer.band.histogram" [scale]="layer.scale"></fm-map-histogram-details> <div>
</ng-template> <span class="bar" [style.background-color]="getHex(entry.color)" [style.width]="getPart(layer.renderer, i)">
</div> </span>
</td> @if (getPercentage(layer.renderer,i); as percentage) {
</tr> <span class="bar-label">{{percentage | number:'1.0-2'}} %</span>
</table> }
</div>
}
</td>
</tr>
}
@if (showHistogram()) {
<tr>
<td colspan="4" class="pb-1 pt-1">
<div class="info" [ngbCollapse]="hideHistogramDetails">
@if (bandContainsStatistics()) {
<fm-map-statistics-details [statistics]="layer.renderer.band.statistics" [scale]="layer.scale"></fm-map-statistics-details>
} @else {
<fm-map-histogram-details [histogram]="layer.renderer.band.histogram" [scale]="layer.scale"></fm-map-histogram-details>
}
</div>
</td>
</tr>
}
</table>
}

View File

@@ -3,9 +3,10 @@ import { IColorMap, IColor, IColorEntry,ILayer, IRenderer } from '../../models/c
@Component({ @Component({
selector: 'fm-map-layer-legend', selector: 'fm-map-layer-legend',
templateUrl: './legend.component.html', templateUrl: './legend.component.html',
styleUrls: ['./legend.component.scss'] styleUrls: ['./legend.component.scss'],
standalone: false
}) })
export class LegendComponent implements OnInit,AfterViewInit { export class LegendComponent implements OnInit,AfterViewInit {

View File

@@ -29,8 +29,8 @@
<div class="col-4 pull-left nopadding">{{getSquaredScaledValue(statistics.variance)| number:'1.0-2'}}</div> <div class="col-4 pull-left nopadding">{{getSquaredScaledValue(statistics.variance)| number:'1.0-2'}}</div>
<div class="col-8 nopadding"><span i18n>Coefficient of variation</span>:</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> <div class="col-4 pull-left nopadding">{{statistics.variationCoefficient | number:'1.0-2'}}</div>
<ng-container *ngIf="statistics.confidenceIntervalLow !== undefined"> @if (statistics.confidenceIntervalLow !== undefined) {
<div class="col-8 nopadding">90% <span i18n>Confidence interval</span>:</div> <div class="col-8 nopadding">90% <span i18n>Confidence interval</span>:</div>
<div class="col-4 pull-left nopadding">{{getScaledValue(statistics.confidenceIntervalLow) | number:'1.0-2'}} - {{getScaledValue(statistics.confidenceIntervalHigh) | number:'1.0-2'}}</div> <div class="col-4 pull-left nopadding">{{getScaledValue(statistics.confidenceIntervalLow) | number:'1.0-2'}} - {{getScaledValue(statistics.confidenceIntervalHigh) | number:'1.0-2'}}</div>
</ng-container> }
</div> </div>

View File

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

View File

@@ -14,7 +14,7 @@ div.map-search {
} }
.disabled { .disabled {
color:lighten(#000000,80%); color:adjust(#000000,80%);
} }
:host ::ng-deep ngb-typeahead-window.dropdown-menu { :host ::ng-deep ngb-typeahead-window.dropdown-menu {

View File

@@ -7,9 +7,10 @@ import { IPeriodState } from '../../models/period.state';
import { tassign } from 'tassign'; import { tassign } from 'tassign';
@Component({ @Component({
selector: 'fm-map-map-search', selector: 'fm-map-map-search',
templateUrl: './map-search.component.html', templateUrl: './map-search.component.html',
styleUrls: ['./map-search.component.scss'] styleUrls: ['./map-search.component.scss'],
standalone: false
}) })
export class MapSearchComponent { export class MapSearchComponent {

View File

@@ -1,4 +1,4 @@
<ng-container *ngIf="{ @if ({
mapState:mapState$|async, mapState:mapState$|async,
extent:extent$|async, extent:extent$|async,
baseLayers:baseLayers$|async, baseLayers:baseLayers$|async,
@@ -22,23 +22,25 @@
styles:styles$|async, styles:styles$|async,
selectedFeature:selectedFeature$|async, selectedFeature:selectedFeature$|async,
fullscreen:fullscreen$|async, fullscreen:fullscreen$|async,
showDataLayerSlide:showDataLayerSlide$|async showDataLayerSlide:showDataLayerSlide$|async,
} as state"> menuVisible:menuVisible$|async,
}; as state) {
<aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" (dblClick)="handleShowLayerValues($event)" [ngClass]="{'panel-visible':state.panelVisible,'fullscreen':state.fullscreen }" class="map"> <aol-map #map (moveEnd)="handleOnMoveEnd($event)" (click)="handleOnMouseDown($event)" (dblClick)="handleShowLayerValues($event)" [ngClass]="{'panel-visible':state.panelVisible,'fullscreen':state.fullscreen }" class="map">
<div> <div>
</div> </div>
<aol-view [zoom]="state.mapState.zoom" [rotation]="state.mapState.rotation">
<aol-view [zoom]="state.mapState.zoom" [rotation]="state.mapState.rotation"> <aol-coordinate [x]="state.mapState.xCenter" [y]="state.mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate>
<aol-coordinate [x]="state.mapState.xCenter" [y]="state.mapState.yCenter" [srid]="'EPSG:4326'"></aol-coordinate> <fm-map-zoom-to-extent [extent]="state.extent" [animate]="true"></fm-map-zoom-to-extent>
<fm-map-zoom-to-extent [extent]="state.extent" [animate]="true"></fm-map-zoom-to-extent> </aol-view>
</aol-view>
<aol-interaction-default></aol-interaction-default> <aol-interaction-default></aol-interaction-default>
<aol-interaction-dragrotateandzoom></aol-interaction-dragrotateandzoom> <aol-interaction-dragrotateandzoom></aol-interaction-dragrotateandzoom>
<fm-map-item-layers [itemLayers]="state.baseLayers"></fm-map-item-layers> <fm-map-item-layers [itemLayers]="state.baseLayers"></fm-map-item-layers>
<fm-map-item-layers *ngIf="!overrideOverlayLayers" [itemLayers]="state.overlayLayers"></fm-map-item-layers> @if (!overrideOverlayLayers) {
<fm-map-item-layers *ngIf="!overrideSelectedItemLayer" [itemLayer]="state.selectedItemLayer" (onPrerender)="handlePrerender($event)"></fm-map-item-layers> <fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers>
}
@if (!overrideSelectedItemLayer) {
<fm-map-item-layers [itemLayer]="state.selectedItemLayer" (onPrerender)="handlePrerender($event)"></fm-map-item-layers>
}
<aol-layer-vector> <aol-layer-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> <fm-map-item-source-vector [styles]="state.styles" [features]="state.features" (onFeatureSelected)="handleFeatureClick($event)" (onFeatureHover)="handleFeatureHover($event)" [selectedFeature]="state.selectedFeature" [selectedItem]="state.selectedItem"></fm-map-item-source-vector>
</aol-layer-vector> </aol-layer-vector>
@@ -59,37 +61,49 @@
</div> </div>
</div> </div>
<fm-map-file-drop-target [parentCode]="state.parentCode" (onFileDropped)="handleFileDropped($event)"></fm-map-file-drop-target> <fm-map-file-drop-target [parentCode]="state.parentCode" (onFileDropped)="handleFileDropped($event)"></fm-map-file-drop-target>
@if (noContent) {
<div *ngIf="noContent"> <div>
<fm-map-map-search #mapSearch [openedModalName]="state.openedModalName" (onOpenModal)="handleOpenModal($event)" (onCloseModal)="handleCloseModal()" [ngClass]="{'menuVisible':state.menuVisible}" (onToggleMenu)="handleToggleMenu($event)" (onSearchCollapse)="handleSearchCollapse($event)" (onSearchExpand)="handleSearchExpand($event)" [collapsed]="state.searchCollapsed" [searchMinified]="state.searchMinified" (onSearch)="handleSearch($event)" (onClear)="handleClearSearch($event)" [filterOptions]="state.queryState" [clearEnabled]="state.clearEnabled" [period]="state.period" (onPeriodChange)="handlePeriodChange($event)" (onCitySearch)="handleCitySearch($event)"></fm-map-map-search> <fm-map-map-search #mapSearch [openedModalName]="state.openedModalName" (onOpenModal)="handleOpenModal($event)" (onCloseModal)="handleCloseModal()" [ngClass]="{'menuVisible':state.menuVisible}" (onToggleMenu)="handleToggleMenu($event)" (onSearchCollapse)="handleSearchCollapse($event)" (onSearchExpand)="handleSearchExpand($event)" [collapsed]="state.searchCollapsed" [searchMinified]="state.searchMinified" (onSearch)="handleSearch($event)" (onClear)="handleClearSearch($event)" [filterOptions]="state.queryState" [clearEnabled]="state.clearEnabled" [period]="state.period" (onPeriodChange)="handlePeriodChange($event)" (onCitySearch)="handleCitySearch($event)"></fm-map-map-search>
</div> </div>
}
<div class="side-panel-container"> <div class="side-panel-container">
<fm-side-panel [resizeable]="true" (onResize)="handlePanelResize($event)" [visible]="state.panelVisible && noContent" [collapsed]="state.panelCollapsed" [collapsable]="false" [extrawide]="state.panelExtraWide"> <fm-side-panel [resizeable]="true" (onResize)="handlePanelResize($event)" [visible]="state.panelVisible && noContent" [collapsed]="state.panelCollapsed" [collapsable]="false" [extrawide]="state.panelExtraWide">
<div class="panel-wrapper" *ngIf="noContent"> @if (noContent) {
<div class="panel-top bg-secondary" *ngIf="!(state.searchMinified)"> <div class="panel-wrapper">
</div> @if (!(state.searchMinified)) {
<div class="panel-bottom"> <div class="panel-top bg-secondary">
</div>
<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 class="panel-bottom">
</div> @if (!(state.selectedItem)) {
<div>
<div *ngIf="state.selectedItem;let item"> <fm-map-feature-list-container [features]="state.features" [selectedFeature]="state.selectedFeature" [queryState]="state.queryState" [clickedFeature]="clickedFeature"></fm-map-feature-list-container>
<fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container> </div>
</div> }
<div *ngIf="state.features.length == 0" class="no-results m-2"> @if (state.selectedItem; as item) {
<div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div> <div>
<div *ngIf="state.queryState?.tags">Cannot find tag <span>{{state.queryState?.tags}}</span></div> <fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container>
</div>
}
@if (state.features.length == 0) {
<div class="no-results m-2">
@if (state.queryState.query) {
<div>Cannot find <span>{{state.queryState?.query}}</span></div>
}
@if (state.queryState?.tags) {
<div>Cannot find tag <span>{{state.queryState?.tags}}</span></div>
}
</div>
}
</div> </div>
</div> </div>
</div> }
</fm-side-panel> </fm-side-panel>
<fm-side-panel [resizeable]="true" [visible]="!noContent" [extrawide]="state.panelExtraWide"> <fm-side-panel [resizeable]="true" [visible]="!noContent" [extrawide]="state.panelExtraWide">
<router-outlet (activate)="handleSidepaneloutletActivate($event)" (deactivate)="handleSidepaneloutletDeactivate($event)"></router-outlet> <router-outlet (activate)="handleSidepaneloutletActivate($event)" (deactivate)="handleSidepaneloutletDeactivate($event)"></router-outlet>
</fm-side-panel> </fm-side-panel>
</div> </div>
</aol-map> </aol-map>
</ng-container> }

View File

@@ -39,9 +39,10 @@ import * as style from 'ol/style';
@Component({ @Component({
selector: 'fm-map-map', selector: 'fm-map-map',
templateUrl: './map.component.html', templateUrl: './map.component.html',
styleUrls: ['./map.component.scss'] styleUrls: ['./map.component.scss'],
standalone: false
}) })
export class MapComponent implements OnInit, OnDestroy, AfterViewInit { export class MapComponent implements OnInit, OnDestroy, AfterViewInit {

View File

@@ -10,8 +10,9 @@ export interface IMetaData {
} }
@Component({ @Component({
selector: 'fm-map-meta-data-modal', selector: 'fm-map-meta-data-modal',
templateUrl: 'meta-data-modal.component.html' templateUrl: 'meta-data-modal.component.html',
standalone: false
}) })
export class MetaDataModalComponent { export class MetaDataModalComponent {

View File

@@ -16,9 +16,10 @@ const after = (one: NgbDateStruct, two: NgbDateStruct) =>
? false : one.day > two.day : one.month > two.month : one.year > two.year; ? false : one.day > two.day : one.month > two.month : one.year > two.year;
@Component({ @Component({
selector: 'fm-map-select-period-modal', selector: 'fm-map-select-period-modal',
templateUrl: 'select-period-modal.component.html', templateUrl: 'select-period-modal.component.html',
styleUrls: ['select-period-modal.component.scss'] styleUrls: ['select-period-modal.component.scss'],
standalone: false
}) })
export class SelectPeriodModalComponent { export class SelectPeriodModalComponent {

View File

@@ -6,9 +6,10 @@ import { IItemLayer } from '../../models/item.layer';
@Component({ @Component({
selector: 'fm-map-selected-item-container', selector: 'fm-map-selected-item-container',
templateUrl: './selected-item-container.component.html', templateUrl: './selected-item-container.component.html',
styleUrls: ['./selected-item-container.component.scss'] styleUrls: ['./selected-item-container.component.scss'],
standalone: false
}) })
export class SelectedItemContainerComponent { export class SelectedItemContainerComponent {

View File

@@ -1,19 +1,23 @@
<div class="spacer"></div> <div class="spacer"></div>
<div *ngIf="item;let item"> @if (item; as item) {
<div class="card border-0"> <div>
<div class="card-body"> <div class="card border-0">
<fm-back-button></fm-back-button> <div class="card-body">
<div class="card menu-card"> <fm-back-button></fm-back-button>
<h1>Cropfield</h1> <div class="card menu-card">
<h4>{{item.name}}</h4> <h1>Cropfield</h1>
<div class="card-text"><span>{{areaInHa(item)| number:'1.2-2'}} ha</span>&nbsp;<span>{{item.data.cropTypeName}}</span></div> <h4>{{item.name}}</h4>
<div class="card-text"><span>{{item.data.startDate|date}}</span> - <span>{{item.data.endDate|date}}</span> </div> <div class="card-text"><span>{{areaInHa(item)| number:'1.2-2'}} ha</span>&nbsp;<span>{{item.data.cropTypeName}}</span></div>
<ul class="p-0 mt-2"> <div class="card-text"><span>{{item.data.startDate|date}}</span> - <span>{{item.data.endDate|date}}</span> </div>
<li *ngIf="item.isEditable"><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li> <ul class="p-0 mt-2">
</ul> @if (item.isEditable) {
<li><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
</ul>
</div>
</div> </div>
<fm-map-item-list [items]="(items|async)"></fm-map-item-list>
<fm-map-item-widget-list [item]="item"></fm-map-item-widget-list>
</div> </div>
<fm-map-item-list [items]="(items|async)"></fm-map-item-list>
<fm-map-item-widget-list [item]="item"></fm-map-item-widget-list>
</div> </div>
</div> }

View File

@@ -16,9 +16,10 @@ import { withLatestFrom,switchMap,combineLatest } from 'rxjs/operators';
@ForItemType("vnd.farmmaps.itemtype.cropfield") @ForItemType("vnd.farmmaps.itemtype.cropfield")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-selected-item-cropfield', selector: 'fm-map-selected-item-cropfield',
templateUrl: './selected-item-cropfield.component.html', templateUrl: './selected-item-cropfield.component.html',
styleUrls: ['./selected-item-cropfield.component.scss'] styleUrls: ['./selected-item-cropfield.component.scss'],
standalone: false
}) })
export class SelectedItemCropfieldComponent extends AbstractSelectedItemComponent implements OnInit{ export class SelectedItemCropfieldComponent extends AbstractSelectedItemComponent implements OnInit{

View File

@@ -1,37 +1,51 @@
<div class="spacer"></div> <div class="spacer"></div>
<div *ngIf="item;let item"> @if (item; as item) {
<div class="card border-0"> <div>
<div class="card-body"> <div class="card border-0">
<fm-back-button></fm-back-button> <div class="card-body">
<div class="card menu-card"> <fm-back-button></fm-back-button>
<h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1>
</div>
<ng-container *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card">
<div *ngIf="layers.length>1">
<select (change)="onLayerChanged($event.target.value)">
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select>
</div>
<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"> <div class="card menu-card">
<ul class="p-0 mt-2"> @if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) {
<li *ngIf="item.isEditable"><a href="#" class="ms-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li> <h2>{{parentItem.name}}</h2>
<ng-container *ngIf="itemTypeService.isLayer(item)"> }
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li> <h1>{{item.name}}</h1>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
<li><fm-item-link class="text-primary p-0" [itemCode]="item.code" pathSuffix="data" [showText]="true"></fm-item-link></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div> </div>
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert> @if (item?.data.layers; as layers) {
</ng-container> <div class="legend-container">
<div class="card menu-card">
@if (layers.length>1) {
<div>
<select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) {
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select>
</div>
}
<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">
@if (item.isEditable) {
<li><a href="#" class="ms-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
<li><fm-item-link class="text-primary p-0" [itemCode]="item.code" pathSuffix="data" [showText]="true"></fm-item-link></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
}
</div>
</div> </div>
</div> </div>
</div> }

View File

@@ -13,9 +13,10 @@ import { AbstractSelectedItemComponent } from '../selected-item/selected-item.co
@ForItemType("vnd.farmmaps.itemtype.geotiff.processed") @ForItemType("vnd.farmmaps.itemtype.geotiff.processed")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-selected-item-geotiff', selector: 'fm-map-selected-item-geotiff',
templateUrl: './selected-item-geotiff.component.html', templateUrl: './selected-item-geotiff.component.html',
styleUrls: ['./selected-item-geotiff.component.scss'] styleUrls: ['./selected-item-geotiff.component.scss'],
standalone: false
}) })
export class SelectedItemGeotiffComponent extends AbstractSelectedItemComponent implements OnDestroy { export class SelectedItemGeotiffComponent extends AbstractSelectedItemComponent implements OnDestroy {
sub: Subscription; sub: Subscription;

View File

@@ -1,35 +1,49 @@
<div class="spacer"></div> <div class="spacer"></div>
<div *ngIf="item;let item"> @if (item; as item) {
<div class="card border-0"> <div>
<div class="card-body"> <div class="card border-0">
<fm-back-button></fm-back-button> <div class="card-body">
<div class="card menu-card"> <fm-back-button></fm-back-button>
<h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1>
</div>
<ng-container *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card">
<div *ngIf="layers.length>1">
<select (change)="onLayerChanged($event.target.value)">
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select>
</div>
<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"> <div class="card menu-card">
<ul class="p-0 mt-2"> @if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) {
<li *ngIf="item.isEditable"><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)" ><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li> <h2>{{parentItem.name}}</h2>
<ng-container *ngIf="itemTypeService.isLayer(item)"> }
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-olus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li> <h1>{{item.name}}</h1>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div> </div>
</ng-container> @if (item?.data.layers; as layers) {
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert> <div class="legend-container">
<div class="card menu-card">
@if (layers.length>1) {
<div>
<select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) {
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select>
</div>
}
<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">
@if (item.isEditable) {
<li><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)" ><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-olus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)" ><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
}
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
</div>
</div> </div>
</div> </div>
</div> }

View File

@@ -13,9 +13,10 @@ import { Subscription } from 'rxjs';
@ForItemType("vnd.farmmaps.itemtype.shape.processed") @ForItemType("vnd.farmmaps.itemtype.shape.processed")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-selected-item-shape', selector: 'fm-map-selected-item-shape',
templateUrl: './selected-item-shape.component.html', templateUrl: './selected-item-shape.component.html',
styleUrls: ['./selected-item-shape.component.scss'] styleUrls: ['./selected-item-shape.component.scss'],
standalone: false
}) })
export class SelectedItemShapeComponent extends AbstractSelectedItemComponent implements OnDestroy { export class SelectedItemShapeComponent extends AbstractSelectedItemComponent implements OnDestroy {
public selectedLayer = 0; public selectedLayer = 0;

View File

@@ -1,52 +1,70 @@
<div class="spacer"></div> <div class="spacer"></div>
<div *ngIf="selectedItem();let item"> @if (selectedItem(); as item) {
<div class="card border-0"> <div>
<div class="card-body"> <div class="card border-0">
<fm-back-button></fm-back-button> <div class="card-body">
<div class="card menu-card"> <fm-back-button></fm-back-button>
<h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h1>{{item.name}}</h1>
</div>
<ng-container *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card">
<h5><span i18n>Date</span>: {{temporalService.selectedDate(itemLayer)}}</h5>
<div class="d-flex justify-content-between">
<div>
<button *ngIf="temporalService.hasPrevious(itemLayer)" type="button" class="btn btn-link p-0" (click)="handlePreviousTemporal($event)">
<i class="fal fa-arrow-circle-left" aria-hidden="true" i18n-title title="Previous"></i>&nbsp;{{ temporalService.previousDate(itemLayer) }}
</button>
</div>
<div>
<button *ngIf="temporalService.hasNext(itemLayer)" type="button" class="btn btn-link p-0" (click)="handleNextTemporal($event)">
{{ temporalService.nextDate(itemLayer)}}&nbsp;<i class="fal fa-arrow-circle-right" aria-hidden="true" i18n-title title="Next"></i>
</button>
</div>
</div>
<fm-map-zoom-to-show-alert [layer]="currentItemLayer()?.layer"></fm-map-zoom-to-show-alert>
</div>
<div class="card menu-card pt-2">
<div *ngIf="layers.length>1">
<select (change)="onLayerChanged($event.target.value)">
<option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
</select>
</div>
<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"> <div class="card menu-card">
<ul class="p-0 mt-2"> @if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) {
<li *ngIf="item.isEditable"><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li> <h2>{{parentItem.name}}</h2>
<ng-container *ngIf="itemTypeService.isLayer(item)"> }
<li *ngIf="!getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li> <h1>{{item.name}}</h1>
<li *ngIf="getItemLayer(item,itemLayer.layerIndex)"><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
</ng-container>
<li><a href="#" (click)="handleGoToChart(item)"><i class="fal fa-line-chart" aria-hidden="true" i18m-title title="Show chart"></i>&nbsp;<span i18n>Show chart</span></a></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div> </div>
</ng-container> @if (item?.data.layers; as layers) {
<div class="legend-container">
<div class="card menu-card">
<h5><span i18n>Date</span>: {{temporalService.selectedDate(itemLayer)}}</h5>
<div class="d-flex justify-content-between">
<div>
@if (temporalService.hasPrevious(itemLayer)) {
<button type="button" class="btn btn-link p-0" (click)="handlePreviousTemporal($event)">
<i class="fal fa-arrow-circle-left" aria-hidden="true" i18n-title title="Previous"></i>&nbsp;{{ temporalService.previousDate(itemLayer) }}
</button>
}
</div>
<div>
@if (temporalService.hasNext(itemLayer)) {
<button type="button" class="btn btn-link p-0" (click)="handleNextTemporal($event)">
{{ temporalService.nextDate(itemLayer)}}&nbsp;<i class="fal fa-arrow-circle-right" aria-hidden="true" i18n-title title="Next"></i>
</button>
}
</div>
</div>
<fm-map-zoom-to-show-alert [layer]="currentItemLayer()?.layer"></fm-map-zoom-to-show-alert>
</div>
<div class="card menu-card pt-2">
@if (layers.length>1) {
<div>
<select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) {
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select>
</div>
}
<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">
@if (item.isEditable) {
<li><a href="#" class="mt-1 me-1" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" i18n-title title="Edit"></i>&nbsp;<span i18n>Edit</span></a></li>
}
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleAddAsLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item,itemLayer.layerIndex)) {
<li><a href="#" (click)="handleRemoveLayer(item,itemLayer.layerIndex)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
<li><a href="#" (click)="handleGoToChart(item)"><i class="fal fa-line-chart" aria-hidden="true" i18m-title title="Show chart"></i>&nbsp;<span i18n>Show chart</span></a></li>
<li><a href="#" (click)="download($event,item,layers,itemLayer.layerIndex)"><i class="fal fa-download" aria-hidden="true" i18n-title title="Download"></i>&nbsp;<span i18n>Download</span></a></li>
</ul>
</div>
}
</div>
</div> </div>
</div> </div>
</div> }

View File

@@ -14,9 +14,10 @@ import { AbstractSelectedItemComponent } from '../selected-item/selected-item.co
@ForItemType("vnd.farmmaps.itemtype.temporal") @ForItemType("vnd.farmmaps.itemtype.temporal")
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-selected-item-temporal', selector: 'fm-map-selected-item-temporal',
templateUrl: './selected-item-temporal.component.html', templateUrl: './selected-item-temporal.component.html',
styleUrls: ['./selected-item-temporal.component.scss'] styleUrls: ['./selected-item-temporal.component.scss'],
standalone: false
}) })
export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent implements OnDestroy { export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent implements OnDestroy {
sub: Subscription; sub: Subscription;

View File

@@ -1,21 +1,31 @@
<div *ngIf="item;let item"> @if (item; as item) {
<div class="card border-0"> <div>
<fm-thumbnail [item]="item" [edit]="item.isEditable"></fm-thumbnail> <div class="card border-0">
</div> <fm-thumbnail [item]="item" [edit]="item.isEditable"></fm-thumbnail>
<div class="card"> </div>
<div class="card-body"> <div class="card">
<fm-back-button></fm-back-button> <div class="card-body">
<h1 class="card-title">{{item.name}}</h1> <fm-back-button></fm-back-button>
<div class="card menu-card"> <h1 class="card-title">{{item.name}}</h1>
<ul class="p-0 mt-2"> <div class="card menu-card">
<li *ngIf="itemTypeService.hasViewer(item)"><a href="#" (click)="handleOnView(item)" class="btn btn-outline-secondary"><i class="fal fa-eye" aria-hidden="true" title="View"></i>&nbsp;<span i18n>View</span></a></li> <ul class="p-0 mt-2">
<li *ngIf="item.isEditable"><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" title="Edit"></i> <span i18n>Edit</span></a></li> @if (itemTypeService.hasViewer(item)) {
<ng-container *ngIf="itemTypeService.isLayer(item)"> <li><a href="#" (click)="handleOnView(item)" class="btn btn-outline-secondary"><i class="fal fa-eye" aria-hidden="true" title="View"></i>&nbsp;<span i18n>View</span></a></li>
<li *ngIf="!getItemLayer(item)"><a href="#" (click)="handleAddAsLayer(item)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li> }
<li *ngIf="getItemLayer(item)"><a href="#" (click)="handleRemoveLayer(item)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li> @if (item.isEditable) {
</ng-container> <li><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" title="Edit"></i> <span i18n>Edit</span></a></li>
</ul> }
@if (itemTypeService.isLayer(item)) {
@if (!getItemLayer(item)) {
<li><a href="#" (click)="handleAddAsLayer(item)"><i class="fas fa-layer-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Add as overlay</span></a></li>
}
@if (getItemLayer(item)) {
<li ><a href="#" (click)="handleRemoveLayer(item)"><i class="fas fa-layer-minus" aria-hidden="true" i18n-title title="Remove overlay"></i>&nbsp;<span i18n>Remove overlay</span></a></li>
}
}
</ul>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> }

View File

@@ -75,9 +75,10 @@ export abstract class AbstractSelectedItemComponent {
@Injectable() @Injectable()
@Component({ @Component({
selector: 'fm-map-selected-item', selector: 'fm-map-selected-item',
templateUrl: './selected-item.component.html', templateUrl: './selected-item.component.html',
styleUrls: ['./selected-item.component.scss'] styleUrls: ['./selected-item.component.scss'],
standalone: false
}) })
export class SelectedItemComponent extends AbstractSelectedItemComponent { export class SelectedItemComponent extends AbstractSelectedItemComponent {

View File

@@ -1,7 +1,8 @@
import { Directive, ViewContainerRef } from '@angular/core'; import { Directive, ViewContainerRef } from '@angular/core';
@Directive({ @Directive({
selector: '[fm-map-widget-host]', selector: '[fm-map-widget-host]',
standalone: false
}) })
export class WidgetHostDirective { export class WidgetHostDirective {
constructor(public viewContainerRef: ViewContainerRef) { } constructor(public viewContainerRef: ViewContainerRef) { }

View File

@@ -1,6 +1,16 @@
<div [ngSwitch]="stage"> <div>
<h6 *ngSwitchCase="StageEnum.DevelopmentPreAlpha" style="color:darkred" [ngbTooltip]="info"><b><span i18n>Stage:Pre-alpha</span></b></h6> @switch (stage) {
<h6 *ngSwitchCase="StageEnum.DevelopmentAlpha" style="color:red" [ngbTooltip]="info"><b><span i18n>Stage:Alpha</span></b></h6> @case (StageEnum.DevelopmentPreAlpha) {
<h6 *ngSwitchCase="StageEnum.DevelopmentBeta" style="color:orange" [ngbTooltip]="info" ><b><span i18n>Stage:Beta</span></b></h6> <h6 style="color:darkred" [ngbTooltip]="info"><b><span i18n>Stage:Pre-alpha</span></b></h6>
<h6 *ngSwitchCase="StageEnum.ReleaseCandidate" style="color:green" [ngbTooltip]="info" ><b><span i18n>Stage:RC</span></b></h6> }
@case (StageEnum.DevelopmentAlpha) {
<h6 style="color:red" [ngbTooltip]="info"><b><span i18n>Stage:Alpha</span></b></h6>
}
@case (StageEnum.DevelopmentBeta) {
<h6 style="color:orange" [ngbTooltip]="info" ><b><span i18n>Stage:Beta</span></b></h6>
}
@case (StageEnum.ReleaseCandidate) {
<h6 style="color:green" [ngbTooltip]="info" ><b><span i18n>Stage:RC</span></b></h6>
}
}
</div> </div>

View File

@@ -1,9 +1,10 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
@Component({ @Component({
selector: 'fm-map-widget-status', selector: 'fm-map-widget-status',
templateUrl: './widget-status.component.html', templateUrl: './widget-status.component.html',
styleUrls: ['./widget-status.component.css'] styleUrls: ['./widget-status.component.css'],
standalone: false
}) })
export class WidgetStatusComponent implements OnInit { export class WidgetStatusComponent implements OnInit {
@Input() stage: Stage; @Input() stage: Stage;

View File

@@ -3,8 +3,9 @@ import { Layer } from 'ol/layer';
import { Source } from 'ol/source'; import { Source } from 'ol/source';
@Component({ @Component({
selector: 'fm-map-zoom-to-show-alert', selector: 'fm-map-zoom-to-show-alert',
template: '<div *fmMapIfZoomToShow="layer$" class="alert alert-info"><i class="fas fa-search-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Zoom in to show layer</span></div>' template: '<div *fmMapIfZoomToShow="layer$" class="alert alert-info"><i class="fas fa-search-plus" aria-hidden="true" i18n-title title="Add as layer"></i>&nbsp;<span i18n>Zoom in to show layer</span></div>',
standalone: false
}) })
export class ZoomToShowAlert { export class ZoomToShowAlert {
public layer$: Layer<Source>; public layer$: Layer<Source>;

View File

@@ -166,12 +166,14 @@ export class MapEffects {
zoomToExtent2$ = createEffect(() => this.actions$.pipe( zoomToExtent2$ = createEffect(() => this.actions$.pipe(
ofType(mapActions.SETFEATURES), ofType(mapActions.SETFEATURES),
switchMap((action: mapActions.SetFeatures) => { switchMap((action: mapActions.SetFeatures) => {
const extent = createEmpty(); if (action.zoomToExtent) {
if (extent) { const extent = createEmpty();
for (const f of action.features) { if (extent) {
extend(extent, (f as Feature<Geometry>).getGeometry().getExtent()); for (const f of action.features) {
extend(extent, (f as Feature<Geometry>).getGeometry().getExtent());
}
if (action.features.length > 0) return of(new mapActions.SetExtent(extent));
} }
if (action.features.length > 0) return of(new mapActions.SetExtent(extent));
} }
return EMPTY; return EMPTY;
}))); })));

View File

@@ -1,18 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
}
);

View File

@@ -3,7 +3,7 @@
"compilerOptions": { "compilerOptions": {
"outDir": "../../out-tsc/lib", "outDir": "../../out-tsc/lib",
"module": "es2015", "module": "es2015",
"moduleResolution": "node", "moduleResolution": "bundler",
"declaration": true, "declaration": true,
"sourceMap": true, "sourceMap": true,
"inlineSources": true, "inlineSources": true,
@@ -12,15 +12,9 @@
"importHelpers": true, "importHelpers": true,
"esModuleInterop": true, "esModuleInterop": true,
"types": [], "types": [],
"lib": [
"dom",
"es2018"
],
"paths": { "paths": {
"@angular/*": [ "@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
] ]
} }
}, },
@@ -33,7 +27,6 @@
"compilationMode": "partial" "compilationMode": "partial"
}, },
"exclude": [ "exclude": [
"src/test.ts",
"**/*.spec.ts" "**/*.spec.ts"
] ]
} }

View File

@@ -3,7 +3,7 @@
"compilerOptions": { "compilerOptions": {
"outDir": "../../out-tsc/lib", "outDir": "../../out-tsc/lib",
"module": "es2015", "module": "es2015",
"moduleResolution": "node", "moduleResolution": "bundler",
"declaration": true, "declaration": true,
"sourceMap": true, "sourceMap": true,
"inlineSources": true, "inlineSources": true,
@@ -12,15 +12,9 @@
"importHelpers": true, "importHelpers": true,
"esModuleInterop": true, "esModuleInterop": true,
"types": [], "types": [],
"lib": [
"dom",
"es2018"
],
"paths": { "paths": {
"@angular/*": [ "@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
] ]
} }
}, },
@@ -33,7 +27,6 @@
"compilationMode": "partial" "compilationMode": "partial"
}, },
"exclude": [ "exclude": [
"src/test.ts",
"**/*.spec.ts" "**/*.spec.ts"
] ]
} }

View File

@@ -3,12 +3,10 @@
"compilerOptions": { "compilerOptions": {
"outDir": "../../out-tsc/spec", "outDir": "../../out-tsc/spec",
"types": [ "types": [
"jasmine",
"node" "node"
] ]
}, },
"files": [ "files": [
"src/test.ts"
], ],
"include": [ "include": [
"**/*.spec.ts", "**/*.spec.ts",

View File

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

View File

@@ -11,8 +11,8 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "^18.2.3", "@angular/common": "20.3.16",
"@angular/core": "^18.2.3", "@angular/core": "20.3.16",
"cesium": "^1.97.0", "cesium": "^1.97.0",
"ol-cesium": ">=2.13.0" "ol-cesium": ">=2.13.0"
} }
@@ -34,35 +34,46 @@
"extraneous": true "extraneous": true
}, },
"node_modules/@angular/common": { "node_modules/@angular/common": {
"version": "18.2.3", "version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.3.tgz", "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.16.tgz",
"integrity": "sha512-NFL4yXXImSCH7i1xnHykUjHa9vl9827fGiwSV2mnf7LjSUsyDzFD8/54dNuYN9OY8AUD+PnK0YdNro6cczVyIA==", "integrity": "sha512-GRAziNlntwdnJy3F+8zCOvDdy7id0gITjDnM6P9+n2lXvtDuBLGJKU3DWBbvxcCjtD6JK/g/rEX5fbCxbUHkQQ==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"engines": { "engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "18.2.3", "@angular/core": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/core": { "node_modules/@angular/core": {
"version": "18.2.3", "version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.3.tgz", "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.16.tgz",
"integrity": "sha512-VGhMJxj7d0rYpqVfQrcGRB7EE/BCziotft/I/YPl6bOMPSAvMukG7DXQuJdYpNrr62ks78mlzHlZX/cdmB9Prw==", "integrity": "sha512-KSFPKvOmWWLCJBbEO+CuRUXfecX2FRuO0jNi9c54ptXMOPHlK1lIojUnyXmMNzjdHgRug8ci9qDuftvC2B7MKg==",
"license": "MIT",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"engines": { "engines": {
"node": "^18.19.1 || ^20.11.1 || >=22.0.0" "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/compiler": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0", "rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.14.10" "zone.js": "~0.15.0"
},
"peerDependenciesMeta": {
"@angular/compiler": {
"optional": true
},
"zone.js": {
"optional": true
}
} }
}, },
"node_modules/@cesium/engine": { "node_modules/@cesium/engine": {
@@ -498,9 +509,10 @@
} }
}, },
"node_modules/rxjs": { "node_modules/rxjs": {
"version": "7.8.1", "version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"license": "Apache-2.0",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.1.0" "tslib": "^2.1.0"
@@ -549,12 +561,6 @@
"integrity": "sha512-Dn6vJ1Z9v1tepSjvnCpwk5QqwIPcEFKdgnjqfYOABv1ngSofuAhtlugcUC3ehS1OHdgDWSG6C5mvj+Qm15udTQ==", "integrity": "sha512-Dn6vJ1Z9v1tepSjvnCpwk5QqwIPcEFKdgnjqfYOABv1ngSofuAhtlugcUC3ehS1OHdgDWSG6C5mvj+Qm15udTQ==",
"peer": true "peer": true
}, },
"node_modules/zone.js": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz",
"integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==",
"peer": true
},
"node_modules/zstddec": { "node_modules/zstddec": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz",

View File

@@ -8,8 +8,8 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "18.2.3", "@angular/common": "21.1.0",
"@angular/core": "18.2.3", "@angular/core": "21.1.0",
"cesium": "^1.97.0", "cesium": "^1.97.0",
"ol-cesium": ">=2.13.0" "ol-cesium": ">=2.13.0"
} }

View File

@@ -8,9 +8,10 @@ import { mapReducers,mapActions } from '@farmmaps/common-map';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@Component({ @Component({
selector: 'fm-map3d-switch2d3d', selector: 'fm-map3d-switch2d3d',
templateUrl: './switch2d3d.component.html', templateUrl: './switch2d3d.component.html',
styleUrls: ['./switch2d3d.component.scss'] styleUrls: ['./switch2d3d.component.scss'],
standalone: false
}) })
export class Switch2D3DComponent { export class Switch2D3DComponent {

View File

@@ -1,17 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js';
import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(), {
teardown: { destroyAfterEach: false }
}
);

View File

@@ -4,7 +4,7 @@
"outDir": "../../out-tsc/lib", "outDir": "../../out-tsc/lib",
"target": "es2015", "target": "es2015",
"module": "es2015", "module": "es2015",
"moduleResolution": "node", "moduleResolution": "bundler",
"declaration": true, "declaration": true,
"sourceMap": true, "sourceMap": true,
"inlineSources": true, "inlineSources": true,
@@ -13,15 +13,9 @@
"importHelpers": true, "importHelpers": true,
"esModuleInterop": true, "esModuleInterop": true,
"types": [], "types": [],
"lib": [
"dom",
"es2018"
],
"paths": { "paths": {
"@angular/*": [ "@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
] ]
} }
}, },
@@ -34,7 +28,6 @@
"compilationMode": "partial" "compilationMode": "partial"
}, },
"exclude": [ "exclude": [
"src/test.ts",
"**/*.spec.ts" "**/*.spec.ts"
] ]
} }

View File

@@ -4,7 +4,7 @@
"outDir": "../../out-tsc/lib", "outDir": "../../out-tsc/lib",
"target": "es2015", "target": "es2015",
"module": "es2015", "module": "es2015",
"moduleResolution": "node", "moduleResolution": "bundler",
"declaration": true, "declaration": true,
"sourceMap": true, "sourceMap": true,
"inlineSources": true, "inlineSources": true,
@@ -13,15 +13,9 @@
"importHelpers": true, "importHelpers": true,
"esModuleInterop": true, "esModuleInterop": true,
"types": [], "types": [],
"lib": [
"dom",
"es2018"
],
"paths": { "paths": {
"@angular/*": [ "@angular/*": [
"node_modules/@angular/*"
"node_modules/@angular/*"
] ]
} }
}, },
@@ -34,7 +28,6 @@
"compilationMode": "partial" "compilationMode": "partial"
}, },
"exclude": [ "exclude": [
"src/test.ts",
"**/*.spec.ts" "**/*.spec.ts"
] ]
} }

View File

@@ -3,12 +3,10 @@
"compilerOptions": { "compilerOptions": {
"outDir": "../../out-tsc/spec", "outDir": "../../out-tsc/spec",
"types": [ "types": [
"jasmine",
"node" "node"
] ]
}, },
"files": [ "files": [
"src/test.ts"
], ],
"include": [ "include": [
"**/*.spec.ts", "**/*.spec.ts",

View File

@@ -1,32 +0,0 @@
// 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'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

File diff suppressed because it is too large Load Diff

View File

@@ -5,20 +5,20 @@
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/" "registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
}, },
"peerDependencies": { "peerDependencies": {
"@ng-bootstrap/ng-bootstrap": "^17.0.1", "@ng-bootstrap/ng-bootstrap": "^20.0.0",
"@angular/common": "18.2.3", "@angular/common": "21.1.0",
"@angular/core": "18.2.3", "@angular/core": "21.1.0",
"@angular/forms": "18.2.3", "@angular/forms": "21.1.0",
"@ngrx/effects": "^18.0.2", "@ngrx/effects": "21.0.1",
"@ngrx/router-store": "^18.0.2", "@ngrx/router-store": "21.0.1",
"@ngrx/store": "^18.0.2", "@ngrx/store": "21.0.1",
"tassign": "^1.0.0", "tassign": "^1.0.0",
"bootstrap": "^5.3.3", "bootstrap": "^5.3.3",
"@microsoft/signalr": "^3.1.16", "@microsoft/signalr": "10.0.0",
"ngx-uploadx": "^6.2.0", "ngx-uploadx": "7.0.1",
"angular-oauth2-oidc": "^17.0.2", "angular-oauth2-oidc": "20.0.2",
"moment": "^2.29.4", "moment": "^2.29.4",
"ngx-avatars": "^1.8.0", "ngx-avatars": "1.10.1",
"ngx-image-cropper": "^7.0.0", "ngx-image-cropper": "^7.0.0",
"ngx-clipboard": "^16.0.0" "ngx-clipboard": "^16.0.0"
} }

View File

@@ -1,5 +1,5 @@
// angular modules // angular modules
import { NgModule, APP_INITIALIZER, ModuleWithProviders, Injector, Optional, SkipSelf } from '@angular/core'; import { NgModule, ModuleWithProviders, Injector, Optional, SkipSelf, inject, provideAppInitializer } from '@angular/core';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { HTTP_INTERCEPTORS } from '@angular/common/http';
@@ -90,12 +90,10 @@ export class AppCommonServiceModule {
providers: [ providers: [
AppConfig, AppConfig,
ItemTypeService, ItemTypeService,
{ provideAppInitializer(() => {
provide: APP_INITIALIZER, const initializerFn = (appConfigFactory)(inject(Injector), inject(AppConfig), inject(OAuthService), inject(AuthConfigFactory), inject(OAuthStorage), inject(ItemTypeService));
useFactory: appConfigFactory, return initializerFn();
deps: [Injector, AppConfig, OAuthService, AuthConfigFactory, OAuthStorage, ItemTypeService], }),
multi: true
},
{ {
provide: HTTP_INTERCEPTORS, provide: HTTP_INTERCEPTORS,
useClass: AccessTokenInterceptor, useClass: AccessTokenInterceptor,

View File

@@ -6,9 +6,10 @@ import * as appReducers from '../../reducers/app-common.reducer';
import * as appActions from '../../actions/app-common.actions'; import * as appActions from '../../actions/app-common.actions';
@Component({ @Component({
selector: 'fm-app-menu', selector: 'fm-app-menu',
templateUrl: './app-menu.component.html', templateUrl: './app-menu.component.html',
styleUrls: ['./app-menu.component.scss'] styleUrls: ['./app-menu.component.scss'],
standalone: false
}) })
export class AppMenuComponent implements OnInit { export class AppMenuComponent implements OnInit {

View File

@@ -6,8 +6,12 @@
<router-outlet name="menu" class="ms-4"></router-outlet> <router-outlet name="menu" class="ms-4"></router-outlet>
<div class="collapse navbar-collapse pageonly"> <div class="collapse navbar-collapse pageonly">
<a class="btn btn-primary ms-auto" role="button" [routerLink]="[ startPage == null?'/map':startPage]"> <a class="btn btn-primary ms-auto" role="button" [routerLink]="[ startPage == null?'/map':startPage]">
<span *ngIf="(user|async)==null" i18n>Sign in</span> @if ((user|async)==null) {
<span *ngIf="(user|async)!=null" i18n>To app</span> <span i18n>Sign in</span>
}
@if ((user|async)!=null) {
<span i18n>To app</span>
}
</a> </a>
</div> </div>
</div> </div>
@@ -24,14 +28,14 @@
<div class="mt-2 mb-2 ms-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fal fa-times" aria-hidden="true"></i></button></div> <div class="mt-2 mb-2 ms-2"><button type="button" class="btn btn-outline-secondary" (click)="handleToggleMenu($event)"><i class="fal fa-times" aria-hidden="true"></i></button></div>
</div> </div>
<div class="d-flex flex-column cards"> <div class="d-flex flex-column cards">
<router-outlet name="side-panel-menu"></router-outlet> <router-outlet name="side-panel-menu"></router-outlet>
</div> </div>
</div> </div>
</div> </div>
</fm-side-panel> </fm-side-panel>
<ng-container *ngIf="showUploadProgress"> @if (showUploadProgress) {
<fm-resumable-file-upload></fm-resumable-file-upload> <fm-resumable-file-upload></fm-resumable-file-upload>
</ng-container> }
<div class="user-menu apponly"> <div class="user-menu apponly">
<fm-setting-menu [user]="user|async" [showMenu]="settingMenuVisible|async" [backgroundColor]="settingMenuBackgroundColor|async"></fm-setting-menu> <fm-setting-menu [user]="user|async" [showMenu]="settingMenuVisible|async" [backgroundColor]="settingMenuBackgroundColor|async"></fm-setting-menu>
<fm-help-menu [user]="user|async" [showMenu]="helpMenuVisible|async"></fm-help-menu> <fm-help-menu [user]="user|async" [showMenu]="helpMenuVisible|async"></fm-help-menu>

View File

@@ -1,6 +1,6 @@
import { Component, OnInit, OnDestroy, Inject, Optional, ViewEncapsulation, RendererFactory2, PLATFORM_ID, ChangeDetectionStrategy, HostListener, Input } from '@angular/core'; import { Component, OnInit, OnDestroy, Inject, Optional, ViewEncapsulation, RendererFactory2, PLATFORM_ID, ChangeDetectionStrategy, HostListener, Input, DOCUMENT } from '@angular/core';
import { Router, NavigationStart, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router'; import { Router, NavigationStart, NavigationEnd, RouteConfigLoadStart, RouteConfigLoadEnd, ActivatedRoute, PRIMARY_OUTLET } from '@angular/router';
import { Meta, Title, MetaDefinition } from '@angular/platform-browser'; import { DOCUMENT } from "@angular/common"; import { Meta, Title, MetaDefinition } from '@angular/platform-browser';
import { Subscription, Observable } from 'rxjs'; import { Subscription, Observable } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators'; import { distinctUntilChanged } from 'rxjs/operators';
import { Store, Action } from '@ngrx/store'; import { Store, Action } from '@ngrx/store';
@@ -20,11 +20,12 @@ import { AppConfig } from '../../shared/app.config';
import * as appReducers from '../../reducers/app-common.reducer'; import * as appReducers from '../../reducers/app-common.reducer';
@Component({ @Component({
selector: 'fm-app', selector: 'fm-app',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'], styleUrls: ['./app.component.scss'],
encapsulation: ViewEncapsulation.None, encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false
}) })
export class AppComponent implements OnInit, OnDestroy { export class AppComponent implements OnInit, OnDestroy {

View File

@@ -5,7 +5,8 @@ import { Location} from '@angular/common';
@Component({ @Component({
selector: 'fm-auth-callback', selector: 'fm-auth-callback',
template:'<div></div>' template: '<div></div>',
standalone: false
}) })
export class AuthCallbackComponent { export class AuthCallbackComponent {

View File

@@ -3,9 +3,10 @@ import { IUser } from '../../models/user';
import { AppConfig } from '../../shared/app.config'; import { AppConfig } from '../../shared/app.config';
@Component({ @Component({
selector: 'fm-avatar', selector: 'fm-avatar',
templateUrl: './avatar.component.html', templateUrl: './avatar.component.html',
styleUrls: ['./avatar.component.css'] styleUrls: ['./avatar.component.css'],
standalone: false
}) })
export class AvatarComponent implements OnInit { export class AvatarComponent implements OnInit {

View File

@@ -1,4 +1,5 @@
<div *ngIf="show()" class="back-button mb-2"> @if (show()) {
<div class="back-button mb-2">
<i class="fal fa-arrow-left"></i>&nbsp;<span i18n="@FmBackButton">Back</span> <i class="fal fa-arrow-left"></i>&nbsp;<span i18n="@FmBackButton">Back</span>
</div> </div>
}

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