5 Commits

302 changed files with 32705 additions and 26605 deletions

1
.gitignore vendored
View File

@@ -51,5 +51,4 @@ projects/common-map/node_modules/
.angular/* .angular/*
projects/common-map3d/node_modules/ projects/common-map3d/node_modules/
projects/common-map/node_modules/ projects/common-map/node_modules/
projects/ng-openlayers/node_modules/
projects/common/node_modules/ projects/common/node_modules/

View File

@@ -2,6 +2,7 @@
This is a sample FarmMaps client in Angular 7.x. This is a sample FarmMaps client in Angular 7.x.
## Quick start ## Quick start
Use one of the two options below to get started. Use one of the two options below to get started.
@@ -50,8 +51,7 @@ npm install -g @angular/cli
npm install npm install
ng serve ng serve
``` ```
*Go*` *Go*
Point your browser to http://localhost:4200 Point your browser to http://localhost:4200

View File

@@ -129,14 +129,43 @@
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular-devkit/build-angular:extract-i18n",
"options": { "options": {
"buildTarget": "farmmaps-lib-app:build" "browserTarget": "farmmaps-lib-app:build"
} }
}, },
"test": { "test": {
"builder": "@angular/build:unit-test", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json", "tsConfig": "src/tsconfig.spec.json",
"browsers": ["chromium"] "karmaConfig": "src/karma.conf.js",
"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"
}
} }
} }
} }
@@ -160,10 +189,11 @@
} }
}, },
"test": { "test": {
"builder": "@angular/build:unit-test", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"main": "projects/common/src/test.ts",
"tsConfig": "projects/common/tsconfig.spec.json", "tsConfig": "projects/common/tsconfig.spec.json",
"browsers": ["chromium"] "karmaConfig": "projects/common/karma.conf.js"
} }
} }
} }
@@ -179,18 +209,19 @@
"options": { "options": {
"tsConfig": "projects/common-map/tsconfig.lib.json", "tsConfig": "projects/common-map/tsconfig.lib.json",
"project": "projects/common-map/ng-package.json" "project": "projects/common-map/ng-package.json"
}, }
"configurations": { , "configurations": {
"production": { "production": {
"tsConfig": "projects/common-map/tsconfig.lib.prod.json" "tsConfig": "projects/common-map/tsconfig.lib.prod.json"
} }
} }
}, },
"test": { "test": {
"builder": "@angular/build:unit-test", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"main": "projects/common-map/src/test.ts",
"tsConfig": "projects/common-map/tsconfig.spec.json", "tsConfig": "projects/common-map/tsconfig.spec.json",
"browsers": ["chromium"] "karmaConfig": "projects/common-map/karma.conf.js"
} }
} }
} }
@@ -214,46 +245,11 @@
} }
}, },
"test": { "test": {
"builder": "@angular/build:unit-test", "builder": "@angular-devkit/build-angular:karma",
"options": { "options": {
"main": "projects/common-map3d/src/test.ts",
"tsConfig": "projects/common-map3d/tsconfig.spec.json", "tsConfig": "projects/common-map3d/tsconfig.spec.json",
"browsers": ["chromium"] "karmaConfig": "projects/common-map3d/karma.conf.js"
}
}
}
},
"ng-openlayers": {
"projectType": "library",
"root": "projects/ng-openlayers",
"sourceRoot": "projects/ng-openlayers/src",
"prefix": "ng-openlayers",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:ng-packagr",
"options": {
"tsConfig": "projects/ng-openlayers/tsconfig.lib.json",
"project": "projects/ng-openlayers/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "projects/ng-openlayers/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "projects/ng-openlayers/tsconfig.spec.json",
"browsers": ["chromium"]
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"projects/ng-openlayers/**/*.ts",
"projects/ng-openlayers/**/*.html"
]
} }
} }
} }
@@ -261,31 +257,5 @@
}, },
"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": "."
}
} }
} }

28
e2e/protractor.conf.js Normal file
View File

@@ -0,0 +1,28 @@
// 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 } }));
}
};

23
e2e/src/app.e2e-spec.ts Normal file
View File

@@ -0,0 +1,23 @@
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));
});
});

11
e2e/src/app.po.ts Normal file
View File

@@ -0,0 +1,11 @@
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>;
}
}

13
e2e/tsconfig.e2e.json Normal file
View File

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

46642
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "farmmaps-lib-app", "name": "farmmaps-lib-app",
"version": "4.21.1", "version": "4.2.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
@@ -11,40 +11,40 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular-eslint/eslint-plugin": "20.7.0", "@angular-eslint/eslint-plugin": "^15.2.1",
"@angular/animations": "21.1.0", "@angular/animations": "^15.2.10",
"@angular/common": "21.1.0", "@angular/common": "^15.2.10",
"@angular/compiler": "21.1.0", "@angular/compiler": "^15.2.10",
"@angular/core": "21.1.0", "@angular/core": "^15.2.10",
"@angular/forms": "21.1.0", "@angular/forms": "^15.2.10",
"@angular/platform-browser": "21.1.0", "@angular/platform-browser": "^15.2.10",
"@angular/platform-browser-dynamic": "21.1.0", "@angular/platform-browser-dynamic": "^15.2.10",
"@angular/router": "21.1.0", "@angular/router": "^15.2.10",
"@farmmaps/common": "file:dist/common", "@farmmaps/common": "file:dist/common",
"@farmmaps/common-map": "file:dist/common-map", "@farmmaps/common-map": "file:dist/common-map",
"@farmmaps/common-map3d": "file:dist/common-map3d", "@farmmaps/common-map3d": "file:dist/common-map3d",
"@farmmaps/ng-openlayers": "file:dist/ng-openlayers", "@microsoft/signalr": "^3.1.16",
"@microsoft/signalr": "10.0.0", "@ng-bootstrap/ng-bootstrap": "^13.0.0",
"@ng-bootstrap/ng-bootstrap": "20.0.0", "@ngrx/effects": "^14",
"@ngrx/effects": "21.0.1", "@ngrx/router-store": "^14",
"@ngrx/router-store": "21.0.1", "@ngrx/store": "^14",
"@ngrx/store": "21.0.1", "@popperjs/core": "^2.11.6",
"@popperjs/core": "2.11.8", "angular-oauth2-oidc": "^13",
"angular-oauth2-oidc": "20.0.2", "assert": "^2.0.0",
"assert": "2.1.0", "bootstrap": "^5.2.0",
"bootstrap": "5.3.8",
"browserify-zlib": "^0.2.0", "browserify-zlib": "^0.2.0",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"cesium": "^1.97.0", "cesium": "^1.97.0",
"core-js": "^2.6.12", "core-js": "^2.6.12",
"https-browserify": "^1.0.0", "https-browserify": "^1.0.0",
"moment": "^2.29.4", "moment": "^2.29.4",
"ngrx-store-localstorage": "20.1.0", "ngrx-store-localstorage": "^14",
"ngx-avatars": "1.10.1", "ngx-avatar": "^4.1.0",
"ngx-clipboard": "^16.0.0", "ngx-clipboard": "^14.0.1",
"ngx-image-cropper": "^7.0.0", "ngx-image-cropper": "^3.3.5",
"ngx-uploadx": "7.0.1", "ngx-openlayers": "1.0.0-next.19",
"ol": "^8.2.0", "ngx-uploadx": "^5.2.0",
"ol": "6.14.1",
"olcs": "^2.13.1", "olcs": "^2.13.1",
"resumablejs": "^1.1.0", "resumablejs": "^1.1.0",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
@@ -54,47 +54,35 @@
"tslib": "^2.4.0", "tslib": "^2.4.0",
"url": "^0.11.0", "url": "^0.11.0",
"util": "^0.12.4", "util": "^0.12.4",
"zone.js": "~0.15.1" "zone.js": "~0.11.4"
},
"optionalDependencies": {
"@lmdb/lmdb-linux-x64": "^3.1.0",
"@rollup/rollup-linux-x64-gnu": "^4.21.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "21.0.3", "@angular-builders/custom-webpack": "^14",
"@angular-devkit/build-angular": "21.1.0", "@angular-devkit/build-angular": "^15.2.10",
"@angular/build": "21.1.0", "@angular/cli": "^15.2.10",
"@angular/cli": "21.1.0", "@angular/compiler-cli": "^15.2.10",
"@angular/compiler-cli": "21.1.0", "@angular/language-service": "^15.2.10",
"@angular/language-service": "21.1.0", "@angular/localize": "^15.2.10",
"@angular/localize": "21.1.0",
"@types/arcgis-rest-api": "^10.4.5", "@types/arcgis-rest-api": "^10.4.5",
"@types/node": "^22.5.4", "@types/jasmine": "~2.8.8",
"@typescript-eslint/eslint-plugin": "8.53.1", "@types/jasminewd2": "^2.0.9",
"@typescript-eslint/parser": "8.53.1", "@types/node": "^12.20.15",
"@vitest/browser": "4.0.17", "@typescript-eslint/eslint-plugin": "^5.54.0",
"@vitest/browser-playwright": "^4.0.17", "@typescript-eslint/eslint-plugin-tslint": "^5.54.0",
"eslint": "9.39.2", "@typescript-eslint/parser": "^5.54.0",
"eslint-config-prettier": "10.1.8", "eslint": "^8.35.0",
"eslint-plugin-import": "2.32.0", "eslint-config-prettier": "^8.6.0",
"jsdom": "^27.4.0", "eslint-plugin-import": "^2.27.5",
"ng-packagr": "21.1.0", "jasmine-core": "^4.3.0",
"jasmine-spec-reporter": "^7.0.0",
"karma": "^6.3.20",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "^3.0.3",
"karma-jasmine": "^5.1.0",
"karma-jasmine-html-reporter": "^2.0.0",
"ng-packagr": "^15.2.2",
"protractor": "~7.0.0",
"ts-node": "^8.8.1", "ts-node": "^8.8.1",
"typescript": "~5.9.3", "typescript": "~4.8.0"
"vitest": "4.0.17"
},
"overrides": {
"ngrx-store-localstorage": {
"@angular/common": "$@angular/common",
"@angular/core": "$@angular/core"
},
"ngx-avatars": {
"@angular/common": "$@angular/common",
"@angular/core": "$@angular/core"
},
"ngx-uploadx": {
"@angular/common": "$@angular/common",
"@angular/core": "$@angular/core"
}
} }
} }

View File

@@ -11,205 +11,20 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "20.3.16", "@angular/core": ">=14.0.0",
"@ngrx/effects": "20.1.0", "@ngrx/effects": ">=14.0.0",
"@ngrx/router-store": "20.1.0", "@ngrx/router-store": ">=14.0.0",
"@ngrx/store": "20.1.0", "@ngrx/store": ">=14.0.0",
"ngrx-store-localstorage": "20.1.0", "ngrx-store-localstorage": ">=14.0.0",
"tassign": "^1.0.0" "ngx-openlayers": ">=1.0.0-next.19",
"ol": ">=6.8.1",
"tassign": ">=1.0.0"
} }
}, },
"../../dist/libs/ng-openlayers": {
"version": "18.0.0",
"extraneous": true,
"license": "MPL-2.0",
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": "^18.2.3",
"@angular/core": "^18.2.3",
"ol": "^8.2.0"
}
},
"dist/ng-openlayers": {
"extraneous": true
},
"node_modules/@angular/common": {
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.16.tgz",
"integrity": "sha512-GRAziNlntwdnJy3F+8zCOvDdy7id0gITjDnM6P9+n2lXvtDuBLGJKU3DWBbvxcCjtD6JK/g/rEX5fbCxbUHkQQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/core": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@angular/core": {
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.16.tgz",
"integrity": "sha512-KSFPKvOmWWLCJBbEO+CuRUXfecX2FRuO0jNi9c54ptXMOPHlK1lIojUnyXmMNzjdHgRug8ci9qDuftvC2B7MKg==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/compiler": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0",
"zone.js": "~0.15.0"
},
"peerDependenciesMeta": {
"@angular/compiler": {
"optional": true
},
"zone.js": {
"optional": true
}
}
},
"node_modules/@angular/platform-browser": {
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.16.tgz",
"integrity": "sha512-YsrLS6vyS77i4pVHg4gdSBW74qvzHjpQRTVQ5Lv/OxIjJdYYYkMmjNalCNgy1ZuyY6CaLIB11ccxhrNnxfKGOQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/animations": "20.3.16",
"@angular/common": "20.3.16",
"@angular/core": "20.3.16"
},
"peerDependenciesMeta": {
"@angular/animations": {
"optional": true
}
}
},
"node_modules/@angular/router": {
"version": "20.3.16",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.16.tgz",
"integrity": "sha512-e1LiQFZaajKqc00cY5FboIrWJZSMnZ64GDp5R0UejritYrqorQQQNOqP1W85BMuY2owibMmxVfX+dJg/Mc8PuQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.3.0"
},
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"peerDependencies": {
"@angular/common": "20.3.16",
"@angular/core": "20.3.16",
"@angular/platform-browser": "20.3.16",
"rxjs": "^6.5.3 || ^7.4.0"
}
},
"node_modules/@ngrx/effects": {
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-20.1.0.tgz",
"integrity": "sha512-wkIu0QnTarBd1zUswk643H5SILO9Be1OeswMe7g4tXlkNLFRPkyu0BUzXT80PsUPP5p0VrdFz3akrn3HHjjTjA==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^20.0.0",
"@ngrx/store": "20.1.0",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
"node_modules/@ngrx/router-store": {
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-20.1.0.tgz",
"integrity": "sha512-XlpQsgmEc1E9Ogiv6QbJ8g8JzjrJOlr7JX9FngHIDLyXQFCMn5wcXsqhjiBjG9JgJLwlzV+v14i7EM/nqwQCGA==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/common": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/router": "^20.0.0",
"@ngrx/store": "20.1.0",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
"node_modules/@ngrx/store": {
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/@ngrx/store/-/store-20.1.0.tgz",
"integrity": "sha512-o8j3CGAGedm+BIb+QDhNXrVaU//n9uF0wH0HZWtXHmW1mjRBaQiUA+ZPMUkDwAeN8KuOcoIEC+2QUXxXGVI7ow==",
"license": "MIT",
"peer": true,
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"@angular/core": "^20.0.0",
"rxjs": "^6.5.3 || ^7.5.0"
}
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ngrx-store-localstorage": {
"version": "20.1.0",
"resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-20.1.0.tgz",
"integrity": "sha512-/5+i5qTxZdE8Q5qdSmj7+9JvriAnHwW7RsXzh1rrQ/UHA9vf12q6mJ6wYTTehUO4Qcl2t/K5MRkooN2eG2ZEvw==",
"license": "MIT",
"peer": true,
"dependencies": {
"deepmerge": "^4.2.2",
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/common": ">=20.0.0",
"@angular/core": ">=20.0.0",
"@ngrx/store": ">=20.0.0"
}
},
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/tassign": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/tassign/-/tassign-1.0.0.tgz",
"integrity": "sha512-k0Ti9f+A1R0BRdArEbiUHldd+A40kZ5qsiNSNk4czx61wkAoSZYjqkaQs0jC63AfgPdCXVPstDq2ZW5ZLRTCgw==",
"peer": true
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.7.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
} }
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@farmmaps/common-map", "name": "@farmmaps/common-map",
"version": "2.0.0", "version": "2.1.0",
"publishConfig": { "publishConfig": {
"registry": "https://repository.akkerweb.nl/repository/npm-hosted/" "registry": "https://repository.akkerweb.nl/repository/npm-hosted/"
}, },
@@ -8,16 +8,13 @@
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "21.1.0", "@angular/core": ">=15.0.0",
"ngrx-store-localstorage": "20.1.0", "ngrx-store-localstorage": ">=15.0.0",
"@ngrx/effects": "21.0.1", "@ngrx/effects": ">=15.0.0",
"@ngrx/router-store": "21.0.1", "@ngrx/router-store":">=15.0.0",
"@ngrx/store": "21.0.1", "@ngrx/store":">=15.0.0",
"tassign": "^1.0.0" "tassign": ">=1.0.0",
}, "ngx-openlayers": ">=1.0.0-next.19",
"overrides": { "ol": ">=6.8.1"
"ngrx-store-localstorage": {
"@angular/core": "$@angular/core"
}
} }
} }

View File

@@ -58,9 +58,7 @@ export const GETLAYERVALUESUCCESS = '[Map] GetLayerValueSuccess'
export const TOGGLESHOWDATALAYERSLIDE = '[Map] ToggleShowDataLayerSlide' export const TOGGLESHOWDATALAYERSLIDE = '[Map] ToggleShowDataLayerSlide'
export const SETVIEWSTATE = '[Map] SetViewState' export const SETVIEWSTATE = '[Map] SetViewState'
export const CLEARFEATURES = '[Map] ClearFeatures'; export const CLEARFEATURES = '[Map] ClearFeatures';
export const SETPANELEXTRAWIDE = '[Map] SetPanelExtraWide';
export const BACKUPFEATURES = '[Map] BackupFeatures';
export const RESTOREFEATURES = '[Map] RestoreFeatures';
export class Clear implements Action { export class Clear implements Action {
readonly type = CLEAR; readonly type = CLEAR;
@@ -302,7 +300,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>>, public zoomToExtent: boolean = true) { } constructor(public features: Array<Feature<Geometry>>) { }
} }
export class SetLayerValuesLocation implements Action { export class SetLayerValuesLocation implements Action {
@@ -344,21 +342,6 @@ export class ClearFeatures implements Action {
constructor() {} constructor() {}
} }
export class SetPanelExtraWide implements Action {
readonly type = SETPANELEXTRAWIDE;
constructor(public panelExtraWide:boolean) {}
}
export class BackupFeatures implements Action {
readonly type = BACKUPFEATURES;
constructor() {}
}
export class RestoreFeatures implements Action {
readonly type = RESTOREFEATURES;
constructor() {}
}
export type Actions = SetMapState export type Actions = SetMapState
| Init | Init
| Clear | Clear
@@ -406,8 +389,5 @@ export type Actions = SetMapState
| SetPeriod | SetPeriod
| ToggleShowDataLayerSlide | ToggleShowDataLayerSlide
| SetViewState | SetViewState
| ClearFeatures | ClearFeatures;
| SetPanelExtraWide
| BackupFeatures
| RestoreFeatures;

View File

@@ -1,79 +1,78 @@
import { NgModule ,ModuleWithProviders} from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
//external modules //external modules
import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; import { AngularOpenlayersModule } from 'ngx-openlayers';
import { StoreModule, ActionReducer, MetaReducer } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects'; import { EffectsModule } from '@ngrx/effects';
import { ActionReducer, MetaReducer, StoreModule } from '@ngrx/store'; import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { AngularOpenlayersModule } from '@farmmaps/ng-openlayers';
//common modules //common modules
import { AppCommonModule } from '@farmmaps/common'; import { AppCommonModule } from '@farmmaps/common';
import * as mapActions from './actions/map.actions';
import * as mapEffects from './effects/map.effects';
import { MODULE_NAME } from './module-name'; import { MODULE_NAME } from './module-name';
import * as mapReducers from './reducers/map.reducer'; import * as mapReducers from './reducers/map.reducer';
import * as mapActions from './actions/map.actions';
import * as mapEffects from './effects/map.effects';
import { IClickedFeature } from './models/clicked.feature'; import { IMapState} from './models/map.state';
import { IItemLayer, ItemLayer, ITemporalItemLayer, TemporalItemLayer } from './models/item.layer';
import { IMapState } from './models/map.state';
import { IPeriodState } from './models/period.state';
import { ISelectedFeatures } from './models/selected.features'; import { ISelectedFeatures } from './models/selected.features';
import { IItemLayer,ItemLayer,ITemporalItemLayer,TemporalItemLayer } from './models/item.layer';
import { IClickedFeature } from './models/clicked.feature';
import { IPeriodState } from './models/period.state';
// components // components
import { MapRoutingModule } from './common-map-routing.module'; import { GpsLocation} from './components/aol/gps-location/gps-location.component';
import { FileDropTargetComponent } from './components/aol/file-drop-target/file-drop-target.component'; import {FeatureListFeatureCropfieldComponent } from './components/feature-list-feature-cropfield/feature-list-feature-cropfield.component';
import { GpsLocation } from './components/aol/gps-location/gps-location.component'; import { FeatureListFeatureCroppingschemeComponent} from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.component';
import { ItemLayersComponent } from './components/aol/item-layers/item-layers.component'; import { ItemWidgetListComponent} from './components/item-widget-list/item-widget-list.component';
import { ItemVectorSourceComponent } from './components/aol/item-vector-source/item-vector-source.component'; import { AbstractItemListItemComponent, ItemListItemComponent, AbstractItemWidgetComponent } from './components/item-list-item/item-list-item.component';
import { LayerListComponent } from './components/aol/layer-list/layer-list.component';
import { LayerValuesComponent } from './components/aol/layer-values/layer-values.component';
import { LayerVectorImageComponent } from './components/aol/layer-vector-image/layer-vector-image.component';
import { PanToLocation } from './components/aol/pan-to-location/pan-to-location.component';
import { RotationResetComponent } from './components/aol/rotation-reset/rotation-reset.component';
import { ZoomToExtentComponent } from './components/aol/zoom-to-extent/zoom-to-extent.component';
import { FeatureListContainerComponent } from './components/feature-list-container/feature-list-container.component';
import { FeatureListCropfieldComponent } from './components/feature-list-cropfield/feature-list-cropfield.component';
import { FeatureListObservationComponent } from './components/feature-list-observation/feature-list-observation.component';
import { FeatureListCroppingschemeComponent } from './components/feature-list-croppingscheme/feature-list-croppingscheme.component';
import { FeatureListFeatureContainerComponent } from './components/feature-list-feature-container/feature-list-feature-container.component';
import { FeatureListFeatureCropfieldComponent } from './components/feature-list-feature-cropfield/feature-list-feature-cropfield.component';
import { FeatureListFeatureCroppingschemeComponent } from './components/feature-list-feature-croppingscheme/feature-list-feature-croppingscheme.component';
import { AbstractFeatureListFeatureComponent, FeatureListFeatureComponent } from './components/feature-list-feature/feature-list-feature.component';
import { AbstractFeatureListComponent, FeatureListComponent } from './components/feature-list/feature-list.component';
import { GeometryThumbnailComponent } from './components/feature-thumbnail/feature-thumbnail.component';
import { ForChild } from './components/for-item/for-child.decorator';
import { ForItemType } from './components/for-item/for-itemtype.decorator';
import { ForPackage } from './components/for-item/for-package.decorator';
import { ForSourceTask } from './components/for-item/for-sourcetask.decorator';
import { ifZoomToShowDirective } from './components/if-zoom-to-show/if-zoom-to-show.directive';
import { ItemListItemContainerComponent } from './components/item-list-item-container/item-list-item-container.component'; import { ItemListItemContainerComponent } from './components/item-list-item-container/item-list-item-container.component';
import { AbstractItemListItemComponent, AbstractItemWidgetComponent, ItemListItemComponent } from './components/item-list-item/item-list-item.component'; import { AbstractItemListComponent,ItemListComponent} from './components/item-list/item-list.component';
import { AbstractItemListComponent, ItemListComponent } from './components/item-list/item-list.component'; import { AbstractSelectedItemComponent, SelectedItemComponent } from './components/selected-item/selected-item.component';
import { ItemWidgetListComponent } from './components/item-widget-list/item-widget-list.component';
import { LayerSwitcher } from './components/layer-switcher/layer-switcher.component';
import { HistogramDetailsComponent } from './components/legend/histogram-details/histogram-details.component';
import { LegendComponent } from './components/legend/legend.component';
import { StatisticsDetailsComponent } from './components/legend/statistics-details/statistics-details.component';
import { MapSearchComponent } from './components/map-search/map-search.component';
import { MapComponent } from './components/map/map.component';
import { MetaDataModalComponent } from './components/meta-data-modal/meta-data-modal.component';
import { SelectPeriodModalComponent } from './components/select-period-modal/select-period-modal.component';
import { SelectedItemContainerComponent } from './components/selected-item-container/selected-item-container.component';
import { SelectedItemCropfieldComponent } from './components/selected-item-cropfield/selected-item-cropfield.component'; import { SelectedItemCropfieldComponent } from './components/selected-item-cropfield/selected-item-cropfield.component';
import { SelectedItemGeotiffComponent } from './components/selected-item-geotiff/selected-item-geotiff.component'; import { SelectedItemGeotiffComponent } from './components/selected-item-geotiff/selected-item-geotiff.component';
import { SelectedItemShapeComponent } from './components/selected-item-shape/selected-item-shape.component'; import { SelectedItemTemporalComponent} from './components/selected-item-temporal/selected-item-temporal.component';
import { SelectedItemTemporalComponent } from './components/selected-item-temporal/selected-item-temporal.component'; import {SelectedItemShapeComponent } from './components/selected-item-shape/selected-item-shape.component';
import { AbstractSelectedItemComponent, SelectedItemComponent } from './components/selected-item/selected-item.component'; import { SelectedItemContainerComponent } from './components/selected-item-container/selected-item-container.component';
import { WidgetHostDirective } from './components/widget-host/widget-host.directive'; import { AbstractFeatureListFeatureComponent, FeatureListFeatureComponent } from './components/feature-list-feature/feature-list-feature.component';
import { WidgetStatusComponent } from './components/widget-status/widget-status.component'; import {FeatureListFeatureContainerComponent } from './components/feature-list-feature-container/feature-list-feature-container.component';
import { ZoomToShowAlert } from './components/zoom-to-show-alert/zoom-to-show-alert.component'; import { FeatureListCroppingschemeComponent } from './components/feature-list-croppingscheme/feature-list-croppingscheme.component';
import { DeviceOrientationService } from './services/device-orientation.service'; import {FeatureListCropfieldComponent } from './components/feature-list-cropfield/feature-list-cropfield.component';
import { FeatureIconService } from './services/feature-icon.service'; import {FeatureListContainerComponent } from './components/feature-list-container/feature-list-container.component';
import { WidgetHostDirective} from './components/widget-host/widget-host.directive';
import { FeatureListComponent,AbstractFeatureListComponent} from './components/feature-list/feature-list.component';
import { FileDropTargetComponent } from './components/aol/file-drop-target/file-drop-target.component';
import { ItemVectorSourceComponent } from './components/aol/item-vector-source/item-vector-source.component';
import { ItemLayersComponent } from './components/aol/item-layers/item-layers.component';
import { ZoomToExtentComponent } from './components/aol/zoom-to-extent/zoom-to-extent.component';
import { RotationResetComponent } from './components/aol/rotation-reset/rotation-reset.component';
import { LayerListComponent } from './components/aol/layer-list/layer-list.component';
import { MetaDataModalComponent } from './components/meta-data-modal/meta-data-modal.component';
import { SelectPeriodModalComponent } from './components/select-period-modal/select-period-modal.component';
import { MapComponent } from './components/map/map.component';
import { MapSearchComponent } from './components/map-search/map-search.component';
import { MapRoutingModule } from './common-map-routing.module';
import { LegendComponent } from './components/legend/legend.component';
import { LayerVectorImageComponent } from './components/aol/layer-vector-image/layer-vector-image.component';
import {FeatureIconService} from './services/feature-icon.service';
import { GeolocationService } from './services/geolocation.service'; import { GeolocationService } from './services/geolocation.service';
import { TemporalService } from './services/temporal.service'; import {DeviceOrientationService} from './services/device-orientation.service';
import { TemporalService} from './services/temporal.service';
import { WidgetStatusComponent } from './components/widget-status/widget-status.component';
import { ForChild} from './components/for-item/for-child.decorator';
import {ForItemType } from './components/for-item/for-itemtype.decorator';
import { ForSourceTask} from './components/for-item/for-sourcetask.decorator';
import { ForPackage } from './components/for-item/for-package.decorator';
import { PanToLocation} from './components/aol/pan-to-location/pan-to-location.component';
import {LayerSwitcher} from './components/layer-switcher/layer-switcher.component';
import {HistogramDetailsComponent} from './components/legend/histogram-details/histogram-details.component';
import {StatisticsDetailsComponent} from './components/legend/statistics-details/statistics-details.component';
import { ifZoomToShowDirective} from './components/if-zoom-to-show/if-zoom-to-show.directive';
import { ZoomToShowAlert} from './components/zoom-to-show-alert/zoom-to-show-alert.component';
import { LayerValuesComponent } from './components/aol/layer-values/layer-values.component';
import { GeometryThumbnailComponent } from './components/feature-thumbnail/feature-thumbnail.component';
export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any> { export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any> {
const r = function(state, action) { const r = function(state, action) {
@@ -105,13 +104,70 @@ export function LocalStorageSync(reducer: ActionReducer<any>): ActionReducer<any
const metaReducers: Array<MetaReducer<any, any>> = [LocalStorageSync]; const metaReducers: Array<MetaReducer<any, any>> = [LocalStorageSync];
export { export {
mapEffects,
mapReducers,
mapActions,
ZoomToExtentComponent,
ItemVectorSourceComponent,
ItemLayersComponent,
FileDropTargetComponent,
MapComponent,
MetaDataModalComponent,
RotationResetComponent,
MapSearchComponent,
SelectPeriodModalComponent,
LayerListComponent,
LegendComponent,
LayerVectorImageComponent,
FeatureListComponent,
WidgetHostDirective,
FeatureListContainerComponent,
FeatureListCroppingschemeComponent,
FeatureListCropfieldComponent,
FeatureListFeatureContainerComponent,
FeatureListFeatureComponent,
FeatureListFeatureCroppingschemeComponent,
FeatureListFeatureCropfieldComponent,
SelectedItemContainerComponent,
SelectedItemComponent,
SelectedItemCropfieldComponent,
SelectedItemGeotiffComponent,
SelectedItemTemporalComponent,
SelectedItemShapeComponent,
ItemListItemComponent,
ItemListItemContainerComponent,
ItemListComponent,
ItemWidgetListComponent,
WidgetStatusComponent,
GpsLocation,
PanToLocation,
LayerSwitcher,
AbstractFeatureListComponent, AbstractFeatureListComponent,
AbstractFeatureListFeatureComponent, AbstractItemListComponent, AbstractItemListItemComponent, AbstractItemWidgetComponent, AbstractSelectedItemComponent, DeviceOrientationService, FeatureIconService, FeatureListComponent, FeatureListContainerComponent, FeatureListCropfieldComponent, FeatureListObservationComponent, FeatureListCroppingschemeComponent, FeatureListFeatureComponent, FeatureListFeatureContainerComponent, FeatureListFeatureCropfieldComponent, FeatureListFeatureCroppingschemeComponent, FileDropTargetComponent, ForChild, AbstractFeatureListFeatureComponent,
ForItemType, ForPackage, ForSourceTask, GeolocationService, GeometryThumbnailComponent, GpsLocation, IClickedFeature, ifZoomToShowDirective, IItemLayer, IMapState, IPeriodState, ISelectedFeatures, ItemLayer, ItemLayersComponent, ItemListComponent, ItemListItemComponent, AbstractSelectedItemComponent,
ItemListItemContainerComponent, ITemporalItemLayer, ItemVectorSourceComponent, ItemWidgetListComponent, LayerListComponent, LayerSwitcher, LayerVectorImageComponent, LegendComponent, mapActions, MapComponent, mapEffects, AbstractItemWidgetComponent,
mapReducers, MapSearchComponent, MetaDataModalComponent, PanToLocation, RotationResetComponent, SelectedItemComponent, SelectedItemContainerComponent, SelectedItemCropfieldComponent, AbstractItemListItemComponent,
SelectedItemGeotiffComponent, SelectedItemShapeComponent, SelectedItemTemporalComponent, SelectPeriodModalComponent, TemporalItemLayer, TemporalService, WidgetHostDirective, WidgetStatusComponent, ZoomToExtentComponent, ZoomToShowAlert AbstractItemListComponent,
}; FeatureIconService,
GeolocationService,
DeviceOrientationService,
TemporalService,
IMapState,
ISelectedFeatures,
IItemLayer,
ItemLayer,
IPeriodState,
ForChild,
ForItemType,
ForSourceTask,
ForPackage ,
ITemporalItemLayer,
TemporalItemLayer,
ifZoomToShowDirective,
ZoomToShowAlert,
IClickedFeature,
GeometryThumbnailComponent
}
@NgModule({ @NgModule({
imports: [ imports: [
@@ -143,7 +199,6 @@ export {
FeatureListContainerComponent, FeatureListContainerComponent,
FeatureListCroppingschemeComponent, FeatureListCroppingschemeComponent,
FeatureListCropfieldComponent, FeatureListCropfieldComponent,
FeatureListObservationComponent,
FeatureListFeatureContainerComponent, FeatureListFeatureContainerComponent,
FeatureListFeatureComponent, FeatureListFeatureComponent,
FeatureListFeatureCroppingschemeComponent, FeatureListFeatureCroppingschemeComponent,
@@ -203,7 +258,6 @@ export {
FeatureListContainerComponent, FeatureListContainerComponent,
FeatureListCroppingschemeComponent, FeatureListCroppingschemeComponent,
FeatureListCropfieldComponent, FeatureListCropfieldComponent,
FeatureListObservationComponent,
FeatureListFeatureContainerComponent, FeatureListFeatureContainerComponent,
ZoomToExtentComponent, ZoomToExtentComponent,
ifZoomToShowDirective, ifZoomToShowDirective,
@@ -217,7 +271,6 @@ export {
TemporalService, TemporalService,
{ provide: AbstractFeatureListComponent, useClass: FeatureListCroppingschemeComponent, multi: true }, { provide: AbstractFeatureListComponent, useClass: FeatureListCroppingschemeComponent, multi: true },
{ provide: AbstractFeatureListComponent, useClass: FeatureListCropfieldComponent, multi: true }, { provide: AbstractFeatureListComponent, useClass: FeatureListCropfieldComponent, multi: true },
{ provide: AbstractFeatureListComponent, useClass: FeatureListObservationComponent, multi: true },
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureComponent, multi: true }, { provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureComponent, multi: true },
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCroppingschemeComponent, multi: true }, { provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCroppingschemeComponent, multi: true },
{ provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCropfieldComponent, multi: true }, { provide: AbstractFeatureListFeatureComponent, useClass: FeatureListFeatureCropfieldComponent, multi: true },

View File

@@ -1,5 +1,5 @@
import { Component, Input, OnDestroy, OnInit, EventEmitter, Output, Inject } from '@angular/core'; import { Component, Input, OnDestroy, OnInit, EventEmitter, Output, Inject } from '@angular/core';
import { MapComponent } from '@farmmaps/ng-openlayers'; import { MapComponent } from 'ngx-openlayers';
import * as proj from 'ol/proj'; import * as proj from 'ol/proj';
import {Point,Geometry} from 'ol/geom'; import {Point,Geometry} from 'ol/geom';
@@ -15,8 +15,7 @@ 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

@@ -1,3 +1,6 @@
@import "~bootstrap/scss/bootstrap.scss";
.gps-location { .gps-location {
display:none; display:none;
} }
@@ -11,7 +14,7 @@
} }
.tolerance { .tolerance {
fill: var(--bs-primary); fill: $primary;
fill-opacity:0.4; fill-opacity:0.4;
} }
@@ -20,20 +23,20 @@
} }
.border { .border {
fill: var(--bs-white); fill: $white;
} }
.center { .center {
fill: var(--bs-primary); fill: $primary;
} }
.stop1 { .stop1 {
stop-color: var(--bs-primary); stop-color: $primary;
stop-opacity:1; stop-opacity:1;
} }
.stop2 { .stop2 {
stop-color:var(--bs-primary); stop-color:$primary;
stop-opacity: 0; stop-opacity: 0;
} }

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input, ViewChild, ElementRef, OnChanges, SimpleChanges ,Host} from '@angular/core'; import { Component, OnInit, Input, ViewChild, ElementRef, OnChanges, SimpleChanges ,Host} from '@angular/core';
import { MapComponent } from '@farmmaps/ng-openlayers'; import { MapComponent } from 'ngx-openlayers';
import Overlay from 'ol/Overlay'; import Overlay from 'ol/Overlay';
import { fromLonLat, toLonLat } from 'ol/proj'; import { fromLonLat, toLonLat } from 'ol/proj';
@@ -7,8 +7,7 @@ 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

@@ -1,5 +1,5 @@
import { Component, Host, Input, Output, EventEmitter,OnDestroy, OnInit, OnChanges, SimpleChanges, forwardRef } from '@angular/core'; import { Component, Host, Input, Output, EventEmitter,OnDestroy, OnInit, OnChanges, SimpleChanges, forwardRef } from '@angular/core';
import { LayerGroupComponent, MapComponent } from '@farmmaps/ng-openlayers'; import { LayerGroupComponent, MapComponent } from 'ngx-openlayers';
import { ItemService,IItem,AppConfig } from '@farmmaps/common'; import { ItemService,IItem,AppConfig } from '@farmmaps/common';
import { IItemLayer, ITemporalItemLayer} from '../../../models/item.layer'; import { IItemLayer, ITemporalItemLayer} from '../../../models/item.layer';
import { ILayerData} from '../../../models/layer.data'; import { ILayerData} from '../../../models/layer.data';
@@ -19,15 +19,13 @@ import VectorTileLayer from 'ol/layer/VectorTile';
import {GeoJSON,MVT} from 'ol/format'; import {GeoJSON,MVT} from 'ol/format';
import { Geometry } from 'ol/geom'; import { Geometry } from 'ol/geom';
import BaseLayer from 'ol/layer/Base'; import BaseLayer from 'ol/layer/Base';
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 {
@@ -182,9 +180,9 @@ export class ItemLayersComponent extends LayerGroupComponent implements OnChange
const source = new VectorSource({ const source = new VectorSource({
strategy: loadingstrategy.bbox, strategy: loadingstrategy.bbox,
loader: function (extent: Extent, resolution: number, projection: Projection) { loader: function (extent: Extent, resolution: number, projection: Projection) {
const source = this as VectorSource<Feature<Geometry>>; const source = this as VectorSource<Geometry>;
__this.itemService.getItemFeatures(item.code, extent, projection.getCode(), layerIndex).subscribe(function (data) { __this.itemService.getItemFeatures(item.code, extent, projection.getCode(), layerIndex).subscribe(function (data) {
const features = format.readFeatures(data).filter(feature => feature instanceof Feature) as Feature[]; const features = format.readFeatures(data);
for (const f of features) { for (const f of features) {
if (f.get("code")) { if (f.get("code")) {
f.setId(f.get("code")); f.setId(f.get("code"));

View File

@@ -1,5 +1,5 @@
import { Component, Host, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, forwardRef, Inject, InjectionToken, OnDestroy, LOCALE_ID } from '@angular/core'; import { Component, Host, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, forwardRef, Inject, InjectionToken, OnDestroy, LOCALE_ID } from '@angular/core';
import { LayerVectorComponent, SourceVectorComponent, MapComponent } from '@farmmaps/ng-openlayers'; import { LayerVectorComponent, SourceVectorComponent, MapComponent } from 'ngx-openlayers';
import { ItemService, ItemTypeService, IItem, IItemType, FolderService } from '@farmmaps/common'; import { ItemService, ItemTypeService, IItem, IItemType, FolderService } from '@farmmaps/common';
import { Feature } from 'ol'; import { Feature } from 'ol';
@@ -26,11 +26,10 @@ import { formatNumber } from '@angular/common';
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<Geometry>;
private _format: GeoJSON; private _format: GeoJSON;
private _select: Select; private _select: Select;
private _hoverSelect: Select; private _hoverSelect: Select;
@@ -84,7 +83,6 @@ export class ItemVectorSourceComponent extends SourceVectorComponent implements
ngOnInit() { ngOnInit() {
this.sub = this.folderService.getFolder('my_settings').subscribe( this.sub = this.folderService.getFolder('my_settings').subscribe(
userSettingsRoot => { userSettingsRoot => {
if (userSettingsRoot == undefined) return;
this.itemService.getChildItemList(userSettingsRoot.code, 'vnd.farmmaps.itemtype.settings.general').subscribe( this.itemService.getChildItemList(userSettingsRoot.code, 'vnd.farmmaps.itemtype.settings.general').subscribe(
items => { items => {
if (items && items.length > 0 && items[0].data?.displayMapFeatureSettings) { if (items && items.length > 0 && items[0].data?.displayMapFeatureSettings) {

View File

@@ -1,50 +1,31 @@
<div> <div>
@if (itemLayers.length > 0) { <div class="layerlist" *ngIf="itemLayers.length > 0;else noLayers">
<div class="layerlist">
<div class="list-group"> <div class="list-group">
@for (itemLayer of itemLayers; track itemLayer.item.code) { <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-item list-group-item-action p-2 text-truncate" [ngClass]="{'active' : selectedLayer==itemLayer}">
<div (click)="handleSelectLayer($event,itemLayer)" [title]="itemLayer.item.name">{{itemLayer.item.name}}</div> <div (click)="handleSelectLayer($event,itemLayer)" [title]="itemLayer.item.name">{{itemLayer.item.name}}</div>
@if (selectedLayer==itemLayer && !baseLayers) { <div class="mt-1" *ngIf="selectedLayer==itemLayer && !baseLayers">
<div class="mt-1">
<span class="btn-group"> <span class="btn-group">
<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="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 (itemLayer.visible) { <a title="Transparency 25%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.25)">25%</a>
<a title="Transparency 25%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.25)">25%</a> <a title="Transparency 50%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.5)">50%</a>
} <a title="Transparency 75%" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.75)">75%</a>
@if (itemLayer.visible) { <a title="No transparency" *ngIf="itemLayer.visible" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,1)">100%</a>
<a title="Transparency 50%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.5)">50%</a>
}
@if (itemLayer.visible) {
<a title="Transparency 75%" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,0.75)">75%</a>
}
@if (itemLayer.visible) {
<a title="No transparency" href="#" class="btn btn-light btn-sm" (click)="handleSetOpacity($event,itemLayer,1)">100%</a>
}
</span> </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> <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 *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>
<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> <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>
}
@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> </div>
} <div *ngIf="itemLayer.legendVisible">
@if (itemLayer.legendVisible) {
<div>
<div class="card legend"> <div class="card legend">
<fm-map-layer-legend [layer]="firstLayer(itemLayer)" (click)="handleLegendClick($event,itemLayer);" [histogramenabled]="true"></fm-map-layer-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>
</div>
<ng-template #noLayers>
<div class="list-group"> <div class="list-group">
<div class="list-group-item" i18n>No layers</div> <div class="list-group-item" i18n>No layers</div>
</div> </div>
} </ng-template>
</div>

View File

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

View File

@@ -3,7 +3,7 @@ import { IItemLayer } from '../../../models/item.layer';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import * as mapReducers from '../../../reducers/map.reducer'; import * as mapReducers from '../../../reducers/map.reducer';
import * as mapActions from '../../../actions/map.actions'; import * as mapActions from '../../../actions/map.actions';
import { MapComponent } from '@farmmaps/ng-openlayers'; import { MapComponent } from 'ngx-openlayers';
import { ILayervalue } from '../../../models/layer.value'; import { ILayervalue } from '../../../models/layer.value';
import { Observable, interval, Subject } from 'rxjs'; import { Observable, interval, Subject } from 'rxjs';
import { debounce, throttle } from 'rxjs/operators'; import { debounce, throttle } from 'rxjs/operators';
@@ -16,8 +16,7 @@ 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

@@ -1,22 +1,20 @@
import { Component, OnDestroy, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core'; import { Component, OnDestroy, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core';
import { LayerVectorComponent, MapComponent } from '@farmmaps/ng-openlayers'; import { LayerVectorComponent, MapComponent } from 'ngx-openlayers';
import RenderType from 'ol/layer/Vector'; import RenderType from 'ol/layer/Vector';
import { Vector as VectorSource } from 'ol/source'; import { Vector as VectorSource } from 'ol/source';
import { Geometry } from 'ol/geom'; import { Geometry } from 'ol/geom';
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;
@Input() @Input()
renderMode: RenderType<VectorSource<Feature<Geometry>>> | string = "image"; renderMode: RenderType<VectorSource<Geometry>> | string = "image";
constructor(map: MapComponent) { constructor(map: MapComponent) {
super(map); super(map);

View File

@@ -1,8 +1,11 @@
@import "~bootstrap/scss/bootstrap.scss";
.gps-location { .gps-location {
display:block; display:block;
width:2.5em; width:2.5em;
height:2.5em; height:2.5em;
background-color: var(--bs-body-bg); background-color: $body-bg;
background-size: contain; background-size: contain;
margin-top:0.5em; margin-top:0.5em;
border-radius: 1.75em; border-radius: 1.75em;
@@ -14,15 +17,15 @@
} }
.pan-to { .pan-to {
fill: var(--bs-secondary); fill: $secondary;
} }
div.gps-location:hover .pan-to { div.gps-location:hover .pan-to {
fill: var(--bs-white); fill: $white;
} }
.pan-to-centered { .pan-to-centered {
fill: var(--bs-primary); fill: $primary;
} }
div.gps-location:hover .pan-to-centered { div.gps-location:hover .pan-to-centered {
@@ -30,5 +33,5 @@ div.gps-location:hover .pan-to-centered {
} }
.pan-to.pan-to-disabled { .pan-to.pan-to-disabled {
fill: var(--bs-gray-300); fill: $gray-300;
} }

View File

@@ -1,5 +1,5 @@
import { Component, OnInit, Input, Host, OnChanges, SimpleChanges,ChangeDetectorRef } from '@angular/core'; import { Component, OnInit, Input, Host, OnChanges, SimpleChanges,ChangeDetectorRef } from '@angular/core';
import { MapComponent } from '@farmmaps/ng-openlayers'; import { MapComponent } from 'ngx-openlayers';
import {IMapState} from '../../../models/map.state' import {IMapState} from '../../../models/map.state'
import {View} from 'ol'; import {View} from 'ol';
import { fromLonLat } from 'ol/proj'; import { fromLonLat } from 'ol/proj';
@@ -8,8 +8,7 @@ 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

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.compass { .compass {
width:2.5em; width:2.5em;
height:2.5em; height:2.5em;
@@ -11,7 +13,7 @@
#north { #north {
display: none; display: none;
fill:var(--bs-black); fill:$black;
} }
#top-l { #top-l {
@@ -31,23 +33,23 @@
} }
div.compass:hover #top-l { div.compass:hover #top-l {
fill:var(--bs-white); fill:$white;
} }
div.compass:hover #top-r { div.compass:hover #top-r {
fill:var(--bs-gray-300); fill:$gray-300;
} }
div.compass:hover #bottom-l { div.compass:hover #bottom-l {
fill:var(--bs-gray-300); fill:$gray-300;
} }
div.compass:hover #bottom-r { div.compass:hover #bottom-r {
fill:var(--bs-white); fill:$white;
} }
div.compass:hover #north { div.compass:hover #north {
fill:var(--bs-white); fill:$white;
} }
.compass-n { .compass-n {

View File

@@ -1,5 +1,5 @@
import { Component, Host, Input, OnInit, ChangeDetectorRef } from '@angular/core'; import { Component, Host, Input, OnInit, ChangeDetectorRef } from '@angular/core';
import { ViewComponent, MapComponent } from '@farmmaps/ng-openlayers'; import { ViewComponent, MapComponent } from 'ngx-openlayers';
import {View} from 'ol'; import {View} from 'ol';
@@ -8,8 +8,7 @@ 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

@@ -1,12 +1,11 @@
import { Component, Host, Input, OnInit, OnChanges, SimpleChanges, forwardRef } from '@angular/core'; import { Component, Host, Input, OnInit, OnChanges, SimpleChanges, forwardRef } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { ViewComponent, MapComponent } from '@farmmaps/ng-openlayers'; import { ViewComponent, MapComponent } from 'ngx-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

@@ -1,10 +1,14 @@
@import "~bootstrap/scss/bootstrap.scss";
.row { .row {
border-bottom: 1px solid var(--bs-gray-500); border-bottom: 1px solid $gray-500;
user-select: none; user-select: none;
} }
.row.selected { .row.selected {
background-color: var(--bs-gray-100); background-color: $gray-100;
} }
@media screen and (min-width: 44rem) { @media screen and (min-width: 44rem) {

View File

@@ -13,8 +13,7 @@ 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,20 +1,14 @@
<div class="card border-0"> <div class="card border-0">
@if ((schemeItem|async); as schemeItem) { <div class="card-body" *ngIf="(schemeItem|async);let schemeItem">
<div class="card-body">
<fm-back-button></fm-back-button> <fm-back-button></fm-back-button>
<h4 i18n>Farm</h4> <h4 i18n>Farm</h4>
<h3>{{schemeItem.name}}</h3> <h3>{{schemeItem.name}}</h3>
@if (features; as features) { <div *ngIf="features;let features">
<div>
<div class="cropfields"> <div class="cropfields">
@for (feature of features; track $index) { <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="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> <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

@@ -1,20 +1,22 @@
@import "~bootstrap/scss/bootstrap.scss";
fm-map-feature-list-feature-container { fm-map-feature-list-feature-container {
width:100%; width:100%;
pointer-events:none; pointer-events:none;
} }
.row { .row {
border-bottom: 1px solid var(--bs-gray-500); border-bottom: 1px solid $gray-500;
user-select: none; user-select: none;
padding-left:1.5rem; padding-left:1.5rem;
} }
.row.selected { .row.selected {
background-color: var(--bs-gray-100); background-color: $gray-100;
} }
.cropfields { .cropfields {
border-top: 1px solid var(--bs-gray-500); border-top: 1px solid $gray-500;
margin-left: -1.25rem; margin-left: -1.25rem;
margin-right: -1.25rem; margin-right: -1.25rem;
} }

View File

@@ -14,8 +14,7 @@ import { Observable } from 'rxjs';
@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

@@ -2,16 +2,12 @@
<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>
@if (features; as features) { <div *ngIf="features;let features">
<div>
<div class="farms"> <div class="farms">
@for (feature of features; track $index) { <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="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> <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

@@ -1,20 +1,22 @@
@import "~bootstrap/scss/bootstrap.scss";
fm-map-feature-list-feature-container { fm-map-feature-list-feature-container {
width:100%; width:100%;
pointer-events:none; pointer-events:none;
} }
.row { .row {
border-bottom: 1px solid var(--bs-gray-500); border-bottom: 1px solid $gray-500;
user-select: none; user-select: none;
padding-left:1.5rem; padding-left:1.5rem;
} }
.row.selected { .row.selected {
background-color: var(--bs-gray-100); background-color: $gray-100;
} }
.farms { .farms {
border-top: 1px solid var(--bs-gray-500); border-top: 1px solid $gray-500;
margin-left: -1.25rem; margin-left: -1.25rem;
margin-right: -1.25rem; margin-right: -1.25rem;
} }

View File

@@ -16,8 +16,7 @@ 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

@@ -11,8 +11,7 @@ import { WidgetHostDirective } from '../widget-host/widget-host.directive';
<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,5 +1,4 @@
@if (feature; as feature) { <div *ngIf="feature;let feature" class="d-flex m-0">
<div class="d-flex m-0">
<div class="p-2"> <div class="p-2">
<div class="thumbnail"> <div class="thumbnail">
<fm-map-feature-thumbnail [feature]="feature"></fm-map-feature-thumbnail> <fm-map-feature-thumbnail [feature]="feature"></fm-map-feature-thumbnail>
@@ -12,5 +11,4 @@
<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

@@ -1,3 +1,7 @@
@import "~bootstrap/scss/bootstrap.scss";
.card-title { .card-title {
font-size: 1rem; font-size: 1rem;
white-space: nowrap; white-space: nowrap;

View File

@@ -15,8 +15,7 @@ import {getArea} from 'ol/sphere';
@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,8 +1,6 @@
@if (feature; as feature) { <div *ngIf="feature;let feature" class="row m-0">
<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

@@ -1,3 +1,7 @@
@import "~bootstrap/scss/bootstrap.scss";
.card-title { .card-title {
font-size: 1rem; font-size: 1rem;
white-space: nowrap; white-space: nowrap;

View File

@@ -12,8 +12,7 @@ import { ForItemType } from '../for-item/for-itemtype.decorator';
@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,18 +1,12 @@
@if (feature; as feature) { <div *ngIf="feature;let feature" class="row m-0">
<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">
@if (feature.get('thumbnail')) { <img *ngIf="feature.get('thumbnail')" [src]="config.getConfig('apiEndPoint') + '/api/v1/items/'+feature.get('code')+'/thumbnail'" />
<img [src]="config.getConfig('apiEndPoint') + '/api/v1/items/'+feature.get('code')+'/thumbnail'" /> <div *ngIf="!feature.get('thumbnail')" [style.background-color]="itemTypeService.getColor(feature.get('itemType'))">
}
@if (!feature.get('thumbnail')) {
<div [style.background-color]="itemTypeService.getColor(feature.get('itemType'))">
<i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))"></i> <i [ngClass]="itemTypeService.getIcon(feature.get('itemType'))"></i>
</div> </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

@@ -1,3 +1,7 @@
@import "~bootstrap/scss/bootstrap.scss";
.card-title { .card-title {
font-size: 1rem; font-size: 1rem;
white-space: nowrap; white-space: nowrap;

View File

@@ -20,8 +20,7 @@ export abstract class AbstractFeatureListFeatureComponent {
@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,20 +0,0 @@
<div class="card border-0">
@if ((schemeItem|async); as schemeItem) {
<div class="card-body">
<fm-back-button></fm-back-button>
<h4 i18n>Farm</h4>
<h3>{{schemeItem.name}}</h3>
@if (features; as features) {
<div>
<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>

View File

@@ -1,20 +0,0 @@
fm-map-feature-list-feature-container {
width:100%;
pointer-events:none;
}
.row {
border-bottom: 1px solid var(--bs-gray-500);
user-select: none;
padding-left:1.5rem;
}
.row.selected {
background-color: var(--bs-gray-100);
}
.cropfields {
border-top: 1px solid var(--bs-gray-500);
margin-left: -1.25rem;
margin-right: -1.25rem;
}

View File

@@ -1,31 +0,0 @@
import { Component, Injectable,AfterViewInit, OnInit,SimpleChanges, ChangeDetectorRef} from '@angular/core';
import { Location } from '@angular/common';
import { AbstractFeatureListComponent } from '../feature-list/feature-list.component';
import {ForItemType } from '../for-item/for-itemtype.decorator';
import {ForChild } from '../for-item/for-child.decorator';
import { Store } from '@ngrx/store';
import * as mapReducers from '../../reducers/map.reducer';
import { commonReducers, ItemTypeService, IItem,ItemService } from '@farmmaps/common';
import { Observable } from 'rxjs';
@ForChild()
@ForItemType("vnd.farmmaps.itemtype.observation")
@Injectable()
@Component({
selector: 'fm-map-feature-list-observation',
templateUrl: './feature-list-observation.component.html',
styleUrls: ['./feature-list-observation.component.scss'],
standalone: false
})
export class FeatureListObservationComponent extends AbstractFeatureListComponent implements OnInit {
constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, private itemService: ItemService) {
super(store, itemTypeService,location);
}
public schemeItem: Observable<IItem>
ngOnInit() {
this.schemeItem = this.itemService.getItem(this.queryState.parentCode);
}
}

View File

@@ -1,10 +1,6 @@
@if (features; as features) { <div *ngIf="features;let features">
<div>
<fm-back-button></fm-back-button> <fm-back-button></fm-back-button>
@for (feature of features; track $index) { <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="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> <fm-map-feature-list-feature-container [feature]="feature"></fm-map-feature-list-feature-container>
</div> </div>
} </div>
</div>
}

View File

@@ -1,13 +1,15 @@
@import "~bootstrap/scss/bootstrap.scss";
fm-map-feature-list-feature-container { fm-map-feature-list-feature-container {
width: 100%; width: 100%;
pointer-events: none; pointer-events: none;
} }
.row { .row {
border-bottom: 1px solid var(--bs-gray-500); border-bottom: 1px solid $gray-500;
user-select: none; user-select: none;
} }
.row.selected { .row.selected {
background-color: var(--bs-gray-100); background-color: $gray-100;
} }

View File

@@ -60,8 +60,7 @@ export abstract class AbstractFeatureListComponent {
@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

@@ -8,8 +8,7 @@ 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

@@ -1,11 +1,10 @@
import { Directive, ViewContainerRef,TemplateRef,OnInit,Input, OnChanges } from '@angular/core'; import { Directive, ViewContainerRef,TemplateRef,OnInit,Input, OnChanges } from '@angular/core';
import { Layer } from 'ol/layer'; import { Layer } from 'ol/layer';
import { Source } from 'ol/source'; import { Source } from 'ol/source';
import { MapComponent } from '@farmmaps/ng-openlayers'; import { MapComponent } from 'ngx-openlayers';
@Directive({ @Directive({
selector: '[fmMapIfZoomToShow]', selector: '[fmMapIfZoomToShow]',
standalone: false
}) })
export class ifZoomToShowDirective implements OnInit { export class ifZoomToShowDirective implements OnInit {
@Input() @Input()

View File

@@ -10,8 +10,7 @@ import { IItem, IListItem } from '@farmmaps/common';
<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,6 +1,4 @@
@if (item; as item) { <div *ngIf="item;let item" class="widget" [style.background-color]="itemTypeService.getColor(item.itemType)">
<div class="widget" [style.background-color]="itemTypeService.getColor(item.itemType)">
<div class="align-middle icon" [ngClass]="itemTypeService.getIcon(item.itemType)"></div> <div class="align-middle icon" [ngClass]="itemTypeService.getIcon(item.itemType)"></div>
<div class="title">{{item.name}}</div> <div class="title">{{item.name}}</div>
</div> </div>
}

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.widget { .widget {
padding:0.8rem; padding:0.8rem;
height:100%; height:100%;

View File

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

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.widget-container { .widget-container {
overflow:auto; overflow:auto;
margin-bottom:1rem; margin-bottom:1rem;
@@ -5,7 +7,7 @@
.widget { .widget {
position:relative; position:relative;
border: 1px solid var(--bs-gray-500); border: 1px solid $gray-500;
user-select: none; user-select: none;
display:inline-block; display:inline-block;
width:50%; width:50%;
@@ -26,7 +28,7 @@
} }
.widget:hover { .widget:hover {
background-color: var(--bs-gray-100); background-color: $gray-100;
} }
.widget-container { .widget-container {

View File

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

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.widget-container { .widget-container {
overflow:auto; overflow:auto;
margin-bottom:1rem; margin-bottom:1rem;
@@ -5,7 +7,7 @@
.widget { .widget {
position:relative; position:relative;
border: 1px solid var(--bs-gray-500); border: 1px solid $gray-500;
user-select: none; user-select: none;
display:inline-block; display:inline-block;
width:50%; width:50%;
@@ -26,7 +28,7 @@
} }
.widget:hover { .widget:hover {
background-color: var(--bs-gray-100); background-color: $gray-100;
} }
.widget-container { .widget-container {

View File

@@ -10,8 +10,7 @@ import { AbstractItemWidgetComponent } from '../item-list-item/item-list-item.co
@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

@@ -10,7 +10,7 @@
<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>Base maps</span></span> <span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Base maps</span></span>
<div class="mb-4"> <div class="mb-4">
<fm-map-layer-list [baseLayers]="true" [itemLayers]="baseMaps|async" [selectedLayer]="selectedBaseLayer|async" (onSelectLayer)="handleSelectBaseLayer($event)"></fm-map-layer-list> <fm-map-layer-list [baseLayers]="true" [itemLayers]="baseLayers|async" [selectedLayer]="selectedBaseLayer|async" (onSelectLayer)="handleSelectBaseLayer($event)"></fm-map-layer-list>
</div> </div>
</li> </li>
<li class="nav-item py-0"> <li class="nav-item py-0">
@@ -19,20 +19,13 @@
<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>
@if (selectedItemLayer$ | async; as selectedItemLayer) { <li class="nav-item py-0" *ngIf="selectedItemLayer$ | async as selectedItemLayer">
<li class="nav-item py-0"> <span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Data</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>
<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>
<div class="mb-4"> <div class="mb-4">
<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> <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> </div>
</li> </li>
}
<li class="nav-item py-0">
<span><i class="fal fa-layer-group" aria-hidden="true"></i>&nbsp;<span i18n>Aerial photos</span></span>
<div class="mb-4">
<fm-map-layer-list [baseLayers]="true" [itemLayers]="aerialMaps|async" [selectedLayer]="selectedBaseLayer|async" (onSelectLayer)="handleSelectBaseLayer($event)"></fm-map-layer-list>
</div>
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -1,15 +1,18 @@
@import "~bootstrap/scss/bootstrap.scss";
.layer-switcher { .layer-switcher {
display:block; display:block;
width:2.5em; width:2.5em;
height:2.5em; height:2.5em;
background-color: var(--bs-body-bg); background-color: $body-bg;
background-size: contain; background-size: contain;
margin-top:0.5em; margin-top:0.5em;
text-align: center; text-align: center;
line-height: 2.5em; line-height: 2.5em;
border-radius: 1.75em; border-radius: 1.75em;
padding: 0; padding: 0;
color: var(--bs-secondary); color: $secondary;
} }
.layer-switcher i { .layer-switcher i {
@@ -21,7 +24,7 @@
} }
.layers { .layers {
color:var(--bs-secondary); color:$secondary;
position: absolute; position: absolute;
overflow: hidden; overflow: hidden;
bottom: -1em; bottom: -1em;

View File

@@ -4,13 +4,12 @@ import { Store } from '@ngrx/store';
import * as mapReducers from '../../reducers/map.reducer'; import * as mapReducers from '../../reducers/map.reducer';
import * as mapActions from '../../actions/map.actions'; import * as mapActions from '../../actions/map.actions';
import {createEmpty,extend } from 'ol/extent'; import {createEmpty,extend } from 'ol/extent';
import { filter, map, Observable } from 'rxjs'; import { 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{
@@ -18,8 +17,7 @@ export class LayerSwitcher implements OnInit,OnChanges{
public overlayLayers: Observable<Array<IItemLayer>>; public overlayLayers: Observable<Array<IItemLayer>>;
public selectedOverlayLayer: Observable<IItemLayer>; public selectedOverlayLayer: Observable<IItemLayer>;
public selectedItemLayer$: Observable<IItemLayer>; public selectedItemLayer$: Observable<IItemLayer>;
public baseMaps: Observable<Array<IItemLayer>>; public baseLayers: Observable<Array<IItemLayer>>;
public aerialMaps: Observable<Array<IItemLayer>>;
public selectedBaseLayer: Observable<IItemLayer>; public selectedBaseLayer: Observable<IItemLayer>;
constructor( private store: Store<mapReducers.State>) { constructor( private store: Store<mapReducers.State>) {
@@ -28,8 +26,7 @@ export class LayerSwitcher implements OnInit,OnChanges{
ngOnInit() { ngOnInit() {
this.overlayLayers = this.store.select(mapReducers.selectGetOverlayLayers); this.overlayLayers = this.store.select(mapReducers.selectGetOverlayLayers);
this.selectedOverlayLayer = this.store.select(mapReducers.selectGetSelectedOverlayLayer); this.selectedOverlayLayer = this.store.select(mapReducers.selectGetSelectedOverlayLayer);
this.baseMaps = this.store.select(mapReducers.selectGetBaseMaps); this.baseLayers = this.store.select(mapReducers.selectGetBaseLayers);
this.aerialMaps = this.store.select(mapReducers.selectGetArealMaps);
this.selectedBaseLayer = this.store.select(mapReducers.selectGetSelectedBaseLayer); this.selectedBaseLayer = this.store.select(mapReducers.selectGetSelectedBaseLayer);
this.selectedItemLayer$ = this.store.select(mapReducers.selectGetSelectedItemLayer) this.selectedItemLayer$ = this.store.select(mapReducers.selectGetSelectedItemLayer)
this.showLayerSwitcher = this.store.select(mapReducers.selectGetShowLayerSwitcher); this.showLayerSwitcher = this.store.select(mapReducers.selectGetShowLayerSwitcher);
@@ -86,4 +83,3 @@ export class LayerSwitcher implements OnInit,OnChanges{
event.preventDefault(); event.preventDefault();
} }
} }

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>
@if (showConfidenceInterval()) { <ng-container *ngIf="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

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

View File

@@ -5,8 +5,7 @@ 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>
@if (statistics.confidenceIntervalLow !== undefined) { <ng-container *ngIf="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

@@ -7,8 +7,7 @@ import { IStatistics } from '../../../models/color.map';
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

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
div.map-search { div.map-search {
position: absolute; position: absolute;
top: 0.5rem; top: 0.5rem;
@@ -14,7 +16,7 @@ div.map-search {
} }
.disabled { .disabled {
color:adjust(#000000,80%); color:lighten(#000000,80%);
} }
:host ::ng-deep ngb-typeahead-window.dropdown-menu { :host ::ng-deep ngb-typeahead-window.dropdown-menu {
@@ -104,7 +106,7 @@ div.map-search.searchcollapsed button[type="submit"] {
} }
.form-control, .form-control:focus { .form-control, .form-control:focus {
border-color: var(--bs-secondary); border-color: $secondary;
} }

View File

@@ -9,8 +9,7 @@ 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 @@
@if ({ <ng-container *ngIf="{
mapState:mapState$|async, mapState:mapState$|async,
extent:extent$|async, extent:extent$|async,
baseLayers:baseLayers$|async, baseLayers:baseLayers$|async,
@@ -10,7 +10,6 @@
panelVisible:panelVisible$|async, panelVisible:panelVisible$|async,
openedModalName:openedModalName$|async, openedModalName:openedModalName$|async,
panelCollapsed:panelCollapsed$|async, panelCollapsed:panelCollapsed$|async,
panelExtraWide:panelExtraWide$|async,
searchMinified:searchMinified$|async, searchMinified:searchMinified$|async,
selectedItem:selectedItem$|async, selectedItem:selectedItem$|async,
parentItem:parentItem$|async, parentItem:parentItem$|async,
@@ -22,25 +21,23 @@
styles:styles$|async, styles:styles$|async,
selectedFeature:selectedFeature$|async, selectedFeature:selectedFeature$|async,
fullscreen:fullscreen$|async, fullscreen:fullscreen$|async,
showDataLayerSlide:showDataLayerSlide$|async, showDataLayerSlide:showDataLayerSlide$|async
menuVisible:menuVisible$|async, } as state">
}; 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>
@if (!overrideOverlayLayers) { <fm-map-item-layers *ngIf="!overrideOverlayLayers" [itemLayers]="state.overlayLayers"></fm-map-item-layers>
<fm-map-item-layers [itemLayers]="state.overlayLayers"></fm-map-item-layers> <fm-map-item-layers *ngIf="!overrideSelectedItemLayer" [itemLayer]="state.selectedItemLayer" (onPrerender)="handlePrerender($event)"></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>
@@ -61,49 +58,37 @@
</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> <div *ngIf="noContent">
<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">
@if (noContent) { <fm-side-panel [resizeable]="true" (onResize)="handlePanelResize($event)" [visible]="state.panelVisible && noContent" [collapsed]="state.panelCollapsed" [collapsable]="false">
<div class="panel-wrapper"> <div class="panel-wrapper" *ngIf="noContent">
@if (!(state.searchMinified)) { <div class="panel-top bg-secondary" *ngIf="!(state.searchMinified)">
<div class="panel-top bg-secondary">
</div> </div>
}
<div class="panel-bottom"> <div class="panel-bottom">
@if (!(state.selectedItem)) {
<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> <fm-map-feature-list-container [features]="state.features" [selectedFeature]="state.selectedFeature" [queryState]="state.queryState" [clickedFeature]="clickedFeature"></fm-map-feature-list-container>
</div> </div>
}
@if (state.selectedItem; as item) { <div *ngIf="state.selectedItem;let item">
<div>
<fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container> <fm-map-selected-item-container [item]="item" [parentItem]="state.parentItem" [itemLayer]="state.selectedItemLayer" [overlayLayers]="state.overlayLayers"></fm-map-selected-item-container>
</div> </div>
} <div *ngIf="state.features.length == 0" class="no-results m-2">
@if (state.features.length == 0) { <div *ngIf="state.queryState.query">Cannot find <span>{{state.queryState?.query}}</span></div>
<div class="no-results m-2"> <div *ngIf="state.queryState?.tags">Cannot find tag <span>{{state.queryState?.tags}}</span></div>
@if (state.queryState.query) { </div>
<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>
}
</fm-side-panel> </fm-side-panel>
<fm-side-panel [resizeable]="true" [visible]="!noContent" [extrawide]="state.panelExtraWide"> <fm-side-panel [resizeable]="true" [visible]="!noContent">
<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

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
aol-map { position:absolute;width:100%;height:calc( 100vh );} aol-map { position:absolute;width:100%;height:calc( 100vh );}
.arrow { .arrow {
@@ -52,20 +54,11 @@ aol-map { position:absolute;width:100%;height:calc( 100vh );}
pointer-events: none; pointer-events: none;
} }
@media screen and (max-width: 768px) {
.control-container {
margin-left: 1em;
margin-right: 1em;
pointer-events: all;
}
}
@media screen and (min-width: 768px) { .control-container {
.control-container { float:right;
float: right;
margin-right: 1em; margin-right: 1em;
pointer-events: all; pointer-events: all;
}
} }
.fullscreen .viewport-container { .fullscreen .viewport-container {
@@ -202,7 +195,7 @@ timespan.menuVisible {
width: 4rem; width: 4rem;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
color: var(--bs-primary); color: $primary;
} }
.shortcut-icon > .farm-icon { .shortcut-icon > .farm-icon {

View File

@@ -1,39 +1,39 @@
import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit, NgZone, ElementRef } from '@angular/core'; import { Component, OnInit, OnDestroy, HostListener, ViewChild, AfterViewInit,NgZone,ElementRef } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Observable, Subject, Subscription, from, of, EMPTY } from 'rxjs'; import { Observable, Subject, Subscription, from,of ,EMPTY } from 'rxjs';
import { withLatestFrom, switchMap, skip } from 'rxjs/operators'; import { withLatestFrom, switchMap,skip } from 'rxjs/operators';
import { Router, ActivatedRoute, ParamMap } from '@angular/router'; import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { DeviceService } from '@farmmaps/common'; import { DeviceService } from '@farmmaps/common';
import { getRenderPixel } from 'ol/render'; import {getRenderPixel} from 'ol/render';
// Map // Map
import * as mapReducers from '../../reducers/map.reducer'; import * as mapReducers from '../../reducers/map.reducer';
import * as mapActions from '../../actions/map.actions'; import * as mapActions from '../../actions/map.actions';
import { IMapState } from '../../models/map.state'; import { IMapState} from '../../models/map.state';
import { IClickedFeature } from '../../models/clicked.feature'; import { IClickedFeature} from '../../models/clicked.feature';
import { IQuery } from '../../reducers/map.reducer' import { IQuery } from '../../reducers/map.reducer'
import { ISelectedFeatures } from '../../models/selected.features'; import { ISelectedFeatures } from '../../models/selected.features';
import { IItemLayer } from '../../models/item.layer'; import { IItemLayer } from '../../models/item.layer';
import { IListItem, IQueryState } from '@farmmaps/common'; import { IListItem, IQueryState } from '@farmmaps/common';
import { IPeriodState } from '../../models/period.state'; import { IPeriodState } from '../../models/period.state';
import { IStyles } from '../../models/style.cache'; import {IStyles} from '../../models/style.cache';
import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component'; import { IDroppedFile } from '../aol/file-drop-target/file-drop-target.component';
import { StateSerializerService } from '@farmmaps/common'; import { StateSerializerService } from '@farmmaps/common';
import { GeolocationService } from '../../services/geolocation.service'; import { GeolocationService} from '../../services/geolocation.service';
import { GeolocatorService } from '@farmmaps/common'; import { GeolocatorService } from '@farmmaps/common';
import { DeviceOrientationService } from '../../services/device-orientation.service'; import {DeviceOrientationService} from '../../services/device-orientation.service';
// AppCommon // AppCommon
import { ResumableFileUploadService, ItemTypeService } from '@farmmaps/common'; import { ResumableFileUploadService, ItemTypeService } from '@farmmaps/common';
import { IItemType, IItem } from '@farmmaps/common'; import { IItemType, IItem } from '@farmmaps/common';
import { commonReducers } from '@farmmaps/common'; import {commonReducers} from '@farmmaps/common';
import { commonActions } from '@farmmaps/common'; import {commonActions} from '@farmmaps/common';
import { Feature } from 'ol'; import {Feature} from 'ol';
import { Geometry, Point, Circle } from 'ol/geom'; import {Geometry,Point,Circle} from 'ol/geom';
import { Extent, createEmpty, extend } from 'ol/extent'; import {Extent,createEmpty,extend } from 'ol/extent';
import { transform } from 'ol/proj'; import {transform} from 'ol/proj';
import { tassign } from 'tassign'; import { tassign } from 'tassign';
import * as style from 'ol/style'; import * as style from 'ol/style';
@@ -41,11 +41,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 {
title = 'Map'; title = 'Map';
public openedModalName$: Observable<string> = this.store.select(commonReducers.selectOpenedModalName); public openedModalName$: Observable<string> = this.store.select(commonReducers.selectOpenedModalName);
public itemTypes$: Observable<{ [id: string]: IItemType }>; public itemTypes$: Observable<{ [id: string]: IItemType }>;
@@ -64,16 +63,15 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
private stateSub: Subscription; private stateSub: Subscription;
private queryStateSub: Subscription; private queryStateSub: Subscription;
private querySub: Subscription; private querySub: Subscription;
public parentCode$: Observable<string> = this.store.select(mapReducers.selectGetParentCode); public parentCode$: Observable<string> =this.store.select(mapReducers.selectGetParentCode);
public panelVisible$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelVisible); public panelVisible$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelVisible);
public panelCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelCollapsed); public panelCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelCollapsed);
public panelExtraWide$: Observable<boolean> = this.store.select(mapReducers.selectGetPanelExtraWide);
public selectedFeature$: Observable<Feature<Geometry>> = this.store.select(mapReducers.selectGetSelectedFeature); public selectedFeature$: Observable<Feature<Geometry>> = this.store.select(mapReducers.selectGetSelectedFeature);
public clickedFeature: Subject<Feature<Geometry>> = new Subject<Feature<Geometry>>(); public clickedFeature: Subject<Feature<Geometry>> = new Subject<Feature<Geometry>>();
public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem); public selectedItem$: Observable<IItem> = this.store.select(mapReducers.selectGetSelectedItem);
public parentItem$: Observable<IItem> = this.store.select(mapReducers.selectGetParentItem); public parentItem$: Observable<IItem> =this.store.select(mapReducers.selectGetParentItem);
public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState); public queryState$: Observable<IQueryState> = this.store.select(mapReducers.selectGetQueryState);
public state$: Observable<{ mapState: IMapState, queryState: IQueryState }> = this.store.select(mapReducers.selectGetState); public state$:Observable<{mapState:IMapState,queryState:IQueryState}> = this.store.select(mapReducers.selectGetState);
public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod); public period$: Observable<IPeriodState> = this.store.select(mapReducers.selectGetPeriod);
public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled); public clearEnabled$: Observable<boolean> = this.store.select(mapReducers.selectGetClearEnabled);
public searchCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetSearchCollapsed); public searchCollapsed$: Observable<boolean> = this.store.select(mapReducers.selectGetSearchCollapsed);
@@ -86,15 +84,13 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
public baseLayersCollapsed = true; public baseLayersCollapsed = true;
public overlayLayersCollapsed = true; public overlayLayersCollapsed = true;
public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent); public extent$: Observable<Extent> = this.store.select(mapReducers.selectGetExtent);
public styles$: Observable<IStyles> = this.store.select(mapReducers.selectGetStyles); public styles$:Observable<IStyles> = this.store.select(mapReducers.selectGetStyles);
public fullscreen$: Observable<boolean> = this.store.select(commonReducers.selectGetFullScreen); public fullscreen$: Observable<boolean> = this.store.select(commonReducers.selectGetFullScreen);
private lastUrl = ""; private lastUrl = "";
private initialized = false; private initialized = false;
public noContent = false; public noContent = false;
public overrideSelectedItemLayer = false; public overrideSelectedItemLayer = false;
public overrideOverlayLayers = false; public overrideOverlayLayers = false;
public hideShowLayerValues = false;
public const
public dataLayerSlideValue = 50; public dataLayerSlideValue = 50;
public dataLayerSlideEnabled = false; public dataLayerSlideEnabled = false;
private visibleAreaBottom = 0; private visibleAreaBottom = 0;
@@ -113,21 +109,20 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
private geolocationService: GeolocationService, private geolocationService: GeolocationService,
private geolocaterService: GeolocatorService, private geolocaterService: GeolocatorService,
private zone: NgZone, private zone: NgZone,
private deviceorientationService: DeviceOrientationService, private deviceorientationService:DeviceOrientationService,
public devicesService: DeviceService) { public devicesService:DeviceService) {
if (route && route.snapshot && route.snapshot.data && route.snapshot.data["fm-map-map"]) { if(route && route.snapshot && route.snapshot.data && route.snapshot.data["fm-map-map"]) {
const params = route.snapshot.data["fm-map-map"]; const params = route.snapshot.data["fm-map-map"];
this.overrideSelectedItemLayer = params["overrideSelectedItemlayer"] ? params["overrideSelectedItemlayer"] : false; this.overrideSelectedItemLayer = params["overrideSelectedItemlayer"] ? params["overrideSelectedItemlayer"] : false;
this.overrideOverlayLayers = params["overrideOverlayLayers"] ? params["overrideOverlayLayers"] : false; this.overrideOverlayLayers = params["overrideOverlayLayers"] ? params["overrideOverlayLayers"] : false;
this.hideShowLayerValues = params["hideShowLayerValues"] ? params["hideShowLayerValues"] : false;
} }
this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$)).subscribe(([query, mapState]) => { this.querySub = this.query$.pipe(skip(1), withLatestFrom(this.mapState$)).subscribe(([query,mapState]) =>{
if (query && query.querystate) { if(query && query.querystate) {
let newQueryState = tassign(mapReducers.initialQueryState); let newQueryState = tassign(mapReducers.initialQueryState);
//console.debug(`Do Query`); //console.debug(`Do Query`);
const urlparts = []; const urlparts=[];
if (query.querystate.itemCode && query.querystate.itemCode != "") { if (query.querystate.itemCode && query.querystate.itemCode != "") {
if (query.querystate.itemType && query.querystate.itemType != "") { if(query.querystate.itemType && query.querystate.itemType!= "") {
const itemType = this.itemTypeService.itemTypes[query.querystate.itemType]; const itemType = this.itemTypeService.itemTypes[query.querystate.itemType];
if (itemType && itemType.viewer && itemType.viewer == "edit_in_editor" && itemType.editor) { if (itemType && itemType.viewer && itemType.viewer == "edit_in_editor" && itemType.editor) {
urlparts.push('/editor'); urlparts.push('/editor');
@@ -137,12 +132,12 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
} }
} }
} else { } else {
newQueryState = query.querystate; newQueryState= query.querystate;
} }
if (urlparts.length == 0) { if(urlparts.length==0 ) {
newQueryState.itemCode = query.querystate.itemCode; newQueryState.itemCode = query.querystate.itemCode;
this.zone.run(() => { this.zone.run(() => {
this.replaceUrl(mapState, newQueryState, query.replace); this.replaceUrl(mapState,newQueryState,query.replace);
}) })
} else { } else {
this.router.navigate(urlparts); this.router.navigate(urlparts);
@@ -165,19 +160,19 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
} }
} }
handlePanelResize(resizeTop: number) { handlePanelResize(resizeTop:number) {
if (resizeTop == 100 || !this.devicesService.IsMobile()) { if(resizeTop==100 || !this.devicesService.IsMobile() ) {
this.visibleAreaBottom = 0; this.visibleAreaBottom=0;
} else { } else {
this.visibleAreaBottom = 100 - resizeTop; this.visibleAreaBottom=100-resizeTop;
if (this.visibleAreaBottom > 60) { if(this.visibleAreaBottom>60) {
this.visibleAreaBottom = 60; this.visibleAreaBottom=60;
} }
} }
} }
bottom(panelVisible: boolean) { bottom(panelVisible:boolean) {
if (panelVisible) { if(panelVisible) {
return this.visibleAreaBottom + '%'; return this.visibleAreaBottom + '%';
} else { } else {
return "0%"; return "0%";
@@ -193,7 +188,7 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
} }
handleFileDropped(droppedFile: IDroppedFile) { handleFileDropped(droppedFile: IDroppedFile) {
this.uploadService.addFiles(droppedFile.files, droppedFile.event, { parentCode: droppedFile.parentCode, geometry: droppedFile.geometry }); this.uploadService.addFiles(droppedFile.files, droppedFile.event, { parentCode:droppedFile.parentCode, geometry:droppedFile.geometry });
} }
handleFeatureClick(feature: Feature<Geometry>) { handleFeatureClick(feature: Feature<Geometry>) {
@@ -209,26 +204,20 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
this.store.dispatch(new mapActions.DoQuery(queryState)); this.store.dispatch(new mapActions.DoQuery(queryState));
} }
handleSidepaneloutletActivate(component: any) { handleSidepaneloutletActivate(component:any) {
if (component && component.hasOwnProperty('clickedFeature')) { if(component && component.hasOwnProperty('clickedFeature')) {
(component as IClickedFeature).clickedFeature = this.clickedFeature; (component as IClickedFeature).clickedFeature = this.clickedFeature;
} }
if (component && component.hasOwnProperty('extrawide')) {
this.store.dispatch(new mapActions.SetPanelExtraWide(true));
}
} }
handleSidepaneloutletDeactivate(component: any) { handleSidepaneloutletDeactivate(component:any) {
if (component && component.hasOwnProperty('clickedFeature')) { if(component && component.hasOwnProperty('clickedFeature')) {
(component as IClickedFeature).clickedFeature = null; (component as IClickedFeature).clickedFeature = null;
} }
if (component && component.hasOwnProperty('extrawide')) {
this.store.dispatch(new mapActions.SetPanelExtraWide(false));
}
} }
handlePrerender(event: any) { handlePrerender(event:any) {
if (!this.dataLayerSlideEnabled) return; if(!this.dataLayerSlideEnabled) return;
const ctx = event.context; const ctx = event.context;
const mapSize = this.map.instance.getSize(); const mapSize = this.map.instance.getSize();
const width = mapSize[0] * (this.dataLayerSlideValue / 100); const width = mapSize[0] * (this.dataLayerSlideValue / 100);
@@ -247,7 +236,7 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
ctx.clip(); ctx.clip();
} }
handleSlideChange(event: any) { handleSlideChange(event:any) {
this.dataLayerSlideValue = event.target.value; this.dataLayerSlideValue = event.target.value;
this.map.instance.render(); this.map.instance.render();
} }
@@ -256,65 +245,63 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
this.initialized = false; this.initialized = false;
//console.debug("Init"); //console.debug("Init");
this.store.dispatch(new mapActions.Clear()); this.store.dispatch(new mapActions.Clear());
this.selectedFeatures$.next({ x: 0, y: 0, features: [] }); this.selectedFeatures$.next({x:0,y:0,features:[]});
this.selectedFeatures$.next(null); this.selectedFeatures$.next(null);
} }
initCustomStyles() { initCustomStyles() {
this.store.dispatch(new mapActions.SetStyle('vnd.farmmaps.itemtype.layer', new style.Style({ this.store.dispatch(new mapActions.SetStyle('vnd.farmmaps.itemtype.layer',new style.Style({
stroke: new style.Stroke({ stroke: new style.Stroke({
color: 'red', color: 'red',
lineDash: [5, 5], lineDash: [ 5,5],
width: 1 width: 1
}), }),
geometry: (feature) => feature.getGeometry() geometry:(feature) =>feature.getGeometry()
}))); })));
this.store.dispatch(new mapActions.SetStyle('vnd.farmmaps.itemtype.layer_selected', new style.Style({ this.store.dispatch(new mapActions.SetStyle('vnd.farmmaps.itemtype.layer_selected',new style.Style({
stroke: new style.Stroke({ stroke: new style.Stroke({
color: 'red', color: 'red',
lineDash: [5, 5], lineDash: [ 5,5],
width: 3 width: 3
}), }),
geometry: (feature) => feature.getGeometry() geometry:(feature) =>feature.getGeometry()
}))); })));
} }
round(value: number, decimals: number): number { round(value:number,decimals:number):number {
const d = Math.pow(10, decimals); const d = Math.pow(10, decimals);
return Math.round((value + Number.EPSILON) * d) / d; return Math.round((value + Number.EPSILON)*d)/d;
} }
getMapStateFromUrl(params: ParamMap): IMapState { getMapStateFromUrl(params:ParamMap):IMapState {
const hasUrlmapState = params.has("xCenter") && params.has("yCenter"); const hasUrlmapState = params.has("xCenter") && params.has("yCenter");
if (hasUrlmapState) { if (hasUrlmapState) {
const xCenter = parseFloat(params.get("xCenter")); const xCenter = parseFloat(params.get("xCenter"));
const yCenter = parseFloat(params.get("yCenter")); const yCenter = parseFloat(params.get("yCenter"));
const zoom = parseFloat(params.get("zoom")); const zoom = parseFloat(params.get("zoom"));
const rotation = parseFloat(params.get("rotation")); const rotation = parseFloat(params.get("rotation"));
const baseLayer = params.get("baseLayer") ? params.get("baseLayer") : ""; const baseLayer = params.get("baseLayer")?params.get("baseLayer"):"";
const newMapState = { zoom: zoom, rotation: rotation, xCenter: xCenter, yCenter: yCenter, baseLayerCode: baseLayer }; const newMapState = {zoom: zoom, rotation: rotation, xCenter: xCenter, yCenter: yCenter, baseLayerCode: baseLayer };
return newMapState; return newMapState;
} else { } else {
return null; return null;
} }
} }
normalizeMapState(mapState: IMapState): IMapState { normalizeMapState(mapState:IMapState):IMapState {
if (!mapState) return null; if(!mapState) return null;
return { return {zoom: this.round(mapState.zoom,0),
zoom: this.round(mapState.zoom, 0), rotation: this.round(mapState.rotation,2),
rotation: this.round(mapState.rotation, 2), xCenter: this.round(mapState.xCenter,5),
xCenter: this.round(mapState.xCenter, 5), yCenter: this.round(mapState.yCenter,5),
yCenter: this.round(mapState.yCenter, 5), baseLayerCode: mapState.baseLayerCode };
baseLayerCode: mapState.baseLayerCode
};
} }
serializeMapState(mapState: IMapState): string { serializeMapState(mapState:IMapState):string {
return JSON.stringify(this.normalizeMapState(mapState)); return JSON.stringify(this.normalizeMapState(mapState));
} }
getQueryStateFromUrl(params: ParamMap): IQueryState { getQueryStateFromUrl(params:ParamMap):IQueryState {
if (params.has("queryState")) { if (params.has("queryState")) {
const queryState = params.get("queryState"); const queryState = params.get("queryState");
let newQueryState = tassign(mapReducers.initialQueryState); let newQueryState = tassign(mapReducers.initialQueryState);
@@ -329,10 +316,10 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
ngAfterViewInit() { ngAfterViewInit() {
//console.debug("View init"); //console.debug("View init");
this.noContent = true; this.noContent=true;
this.route.children.forEach((entry) => { this.route.children.forEach((entry) => {
if (entry.outlet == "primary") { if(entry.outlet=="primary") {
this.noContent = false; this.noContent=false;
} }
}); });
@@ -342,25 +329,25 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
const urlMapState = this.getMapStateFromUrl(this.route.snapshot.paramMap); const urlMapState = this.getMapStateFromUrl(this.route.snapshot.paramMap);
const urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap); const urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap);
if (urlQueryState && urlMapState && this.noContent) { if(urlQueryState && urlMapState && this.noContent) {
this.store.dispatch(new mapActions.SetState(urlMapState, urlQueryState)); this.store.dispatch(new mapActions.SetState(urlMapState,urlQueryState));
window.localStorage.setItem("FarmMapsCommonMap_mapState", this.serializeMapState(urlMapState)); window.localStorage.setItem("FarmMapsCommonMap_mapState",this.serializeMapState(urlMapState));
} else if (urlQueryState && this.noContent) { } else if(urlQueryState && this.noContent) {
this.store.dispatch(new mapActions.SetQueryState(urlQueryState)); this.store.dispatch(new mapActions.SetQueryState(urlQueryState));
} else { } else {
this.store.dispatch(new mapActions.SetReplaceUrl(true)); this.store.dispatch(new mapActions.SetReplaceUrl(true));
} }
this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$), switchMap(([params, state]) => { this.paramSub = this.route.paramMap.pipe(withLatestFrom(this.state$),switchMap(([params,state]) => {
if (this.initialized && this.noContent) { if(this.initialized && this.noContent) {
const urlQueryState = this.getQueryStateFromUrl(params); const urlQueryState = this.getQueryStateFromUrl(params);
if (this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState)) { if( this.serializeService.serialize(state.queryState) != this.serializeService.serialize(urlQueryState)) {
return of(new mapActions.SetState(state.mapState, urlQueryState)); return of(new mapActions.SetState(state.mapState,urlQueryState));
} }
} }
return EMPTY; return EMPTY;
})).subscribe((action) => { })).subscribe((action) => {
if (action) { if(action) {
this.zone.run(() => { this.zone.run(() => {
//console.debug("Url to state"); //console.debug("Url to state");
this.store.dispatch(action); this.store.dispatch(action);
@@ -372,22 +359,22 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
this.stateSub = this.state$.pipe(switchMap((state) => { this.stateSub = this.state$.pipe(switchMap((state) => {
const newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState); const newUrl = this.serializeMapState(state.mapState) + "_" + this.serializeService.serialize(state.queryState);
if (this.lastUrl != newUrl) { if(this.lastUrl!=newUrl) {
this.lastUrl = newUrl; this.lastUrl=newUrl;
return of(state); return of(state);
} }
else { else {
return of(null); return of(null);
} }
})).subscribe((newUrlState: any) => { })).subscribe((newUrlState: any) => {
if (newUrlState) { if(newUrlState) {
//console.debug(`State to url`); //console.debug(`State to url`);
this.replaceUrl(newUrlState.mapState, newUrlState.queryState, newUrlState.replaceUrl); this.replaceUrl(newUrlState.mapState,newUrlState.queryState,newUrlState.replaceUrl);
} }
}); });
this.initialized = true; this.initialized = true;
this.showDataLayerSlide$.subscribe((v) => { this.showDataLayerSlide$.subscribe((v) => {
this.dataLayerSlideEnabled = v; this.dataLayerSlideEnabled=v;
this.map.instance.render(); this.map.instance.render();
}); });
this.store.select(mapReducers.selectGetViewEnabled).subscribe((v) => { this.store.select(mapReducers.selectGetViewEnabled).subscribe((v) => {
@@ -407,7 +394,7 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
this.store.dispatch(new commonActions.ToggleMenu()); this.store.dispatch(new commonActions.ToggleMenu());
} }
handleToggleBaseLayers(event: MouseEvent) { handleToggleBaseLayers(event:MouseEvent) {
this.baseLayersCollapsed = !this.baseLayersCollapsed; this.baseLayersCollapsed = !this.baseLayersCollapsed;
event.preventDefault(); event.preventDefault();
} }
@@ -424,30 +411,30 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
} }
replaceUrl(mapState: IMapState, queryState: IQueryState, replace = true) { replaceUrl(mapState: IMapState, queryState: IQueryState, replace = true) {
if (this.noContent) { if(this.noContent) {
const newMapState = this.serializeMapState(mapState); const newMapState = this.serializeMapState(mapState);
const newQueryState = this.serializeService.serialize(queryState); const newQueryState = this.serializeService.serialize(queryState);
const currentMapState = this.serializeMapState(this.getMapStateFromUrl(this.route.snapshot.paramMap)); const currentMapState = this.serializeMapState(this.getMapStateFromUrl(this.route.snapshot.paramMap));
const urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap); const urlQueryState = this.getQueryStateFromUrl(this.route.snapshot.paramMap);
const currentQueryState = urlQueryState == null ? "" : this.serializeService.serialize(urlQueryState); const currentQueryState = urlQueryState==null?"":this.serializeService.serialize(urlQueryState);
if (mapState.baseLayerCode != "" && ((newMapState != currentMapState) || (newQueryState != currentQueryState))) { if(mapState.baseLayerCode!="" && ((newMapState!= currentMapState) || (newQueryState!=currentQueryState))) {
const parts = ["."]; const parts =["."];
parts.push(mapState.xCenter.toFixed(5)); parts.push(mapState.xCenter.toFixed(5));
parts.push(mapState.yCenter.toFixed(5)); parts.push(mapState.yCenter.toFixed(5));
parts.push(mapState.zoom.toFixed(0)); parts.push( mapState.zoom.toFixed(0));
parts.push(mapState.rotation.toFixed(2)); parts.push( mapState.rotation.toFixed(2));
parts.push(mapState.baseLayerCode); parts.push(mapState.baseLayerCode);
parts.push(this.serializeService.serialize(queryState)); parts.push( this.serializeService.serialize(queryState));
//console.debug("Replace url",parts); //console.debug("Replace url",parts);
this.router.navigate(parts, { replaceUrl: replace, relativeTo: this.route.parent }); this.router.navigate(parts, { replaceUrl: replace,relativeTo:this.route.parent });
} }
} }
} }
handleOnMoveEnd(event) { handleOnMoveEnd(event) {
if (this.initialized && this.viewEnabled) { if(this.initialized && this.viewEnabled) {
this.zone.run(() => { this.zone.run(() =>{
//console.debug("Move end"); //console.debug("Move end");
const map = event.map; const map = event.map;
const view = map.getView(); const view = map.getView();
@@ -472,19 +459,17 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
handleOnMouseDown(event: MouseEvent) { handleOnMouseDown(event: MouseEvent) {
event.stopPropagation(); event.stopPropagation();
this.zone.run(() => { this.zone.run(() =>{
this.store.dispatch(new commonActions.CloseAll()); this.store.dispatch(new commonActions.CloseAll());
}); });
} }
handleShowLayerValues(event: MouseEvent) { handleShowLayerValues(event: MouseEvent) {
if (!this.hideShowLayerValues) {
event.stopPropagation(); event.stopPropagation();
this.zone.run(() => { this.zone.run(() =>{
this.store.dispatch(new mapActions.ToggleLayerValuesEnabled()); this.store.dispatch(new mapActions.ToggleLayerValuesEnabled());
}); });
} }
}
handleOnDownload(event) { handleOnDownload(event) {
@@ -499,10 +484,10 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
} }
handleOnToggleVisibility(itemLayer: IItemLayer) { handleOnToggleVisibility(itemLayer: IItemLayer) {
this.store.dispatch(new mapActions.SetVisibility(itemLayer, !itemLayer.visible)); this.store.dispatch(new mapActions.SetVisibility(itemLayer,!itemLayer.visible));
} }
handleOnSetOpacity(event: { layer: IItemLayer, opacity: number }) { handleOnSetOpacity(event:{ layer: IItemLayer,opacity:number }) {
this.store.dispatch(new mapActions.SetOpacity(event.layer, event.opacity)); this.store.dispatch(new mapActions.SetOpacity(event.layer, event.opacity));
} }
@@ -522,16 +507,16 @@ export class MapComponent implements OnInit, OnDestroy, AfterViewInit {
this.store.dispatch(new mapActions.SelectOverlayLayer(itemLayer)); this.store.dispatch(new mapActions.SelectOverlayLayer(itemLayer));
} }
handlePeriodChange(period: IPeriodState) { handlePeriodChange(period:IPeriodState) {
this.store.dispatch(new mapActions.SetPeriod(period)); this.store.dispatch(new mapActions.SetPeriod(period));
} }
handleCitySearch(location: string) { handleCitySearch(location:string) {
this.geolocaterService.geocode(location).subscribe(locations => { this.geolocaterService.geocode(location).subscribe(locations => {
if (locations.length > 0) { if( locations.length > 0) {
const point = new Point([locations[0].coordinates.lon, locations[0].coordinates.lat]); const point = new Point([locations[0].coordinates.lon,locations[0].coordinates.lat]);
point.transform('EPSG:4326', 'EPSG:3857'); point.transform('EPSG:4326', 'EPSG:3857');
const circle = new Circle(point.getCoordinates(), 5000);// const circle = new Circle(point.getCoordinates(),5000);//
const extent = createEmpty(); const extent = createEmpty();
extend(extent, circle.getExtent()); extend(extent, circle.getExtent());
this.store.dispatch(new mapActions.SetExtent(extent)) this.store.dispatch(new mapActions.SetExtent(extent))

View File

@@ -11,8 +11,7 @@ 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

@@ -18,8 +18,7 @@ const after = (one: NgbDateStruct, two: NgbDateStruct) =>
@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

@@ -1,9 +1,13 @@
@import "~bootstrap/scss/bootstrap.scss";
.row { .row {
border-bottom: 1px solid var(--bs-gray-500); border-bottom: 1px solid $gray-500;
user-select: none; user-select: none;
} }
.row:hover { .row:hover {
background-color: var(--bs-gray-100); background-color: $gray-100;
} }

View File

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

View File

@@ -1,6 +1,5 @@
<div class="spacer"></div> <div class="spacer"></div>
@if (item; as item) { <div *ngIf="item;let item">
<div>
<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>
@@ -10,9 +9,7 @@
<div class="card-text"><span>{{areaInHa(item)| number:'1.2-2'}} ha</span>&nbsp;<span>{{item.data.cropTypeName}}</span></div> <div class="card-text"><span>{{areaInHa(item)| number:'1.2-2'}} ha</span>&nbsp;<span>{{item.data.cropTypeName}}</span></div>
<div class="card-text"><span>{{item.data.startDate|date}}</span> - <span>{{item.data.endDate|date}}</span> </div> <div class="card-text"><span>{{item.data.startDate|date}}</span> - <span>{{item.data.endDate|date}}</span> </div>
<ul class="p-0 mt-2"> <ul class="p-0 mt-2">
@if (item.isEditable) { <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>
<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> </ul>
</div> </div>
</div> </div>
@@ -20,4 +17,3 @@
<fm-map-item-widget-list [item]="item"></fm-map-item-widget-list> <fm-map-item-widget-list [item]="item"></fm-map-item-widget-list>
</div> </div>
</div> </div>
}

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.big-icon { .big-icon {
width: 100%; width: 100%;
color: white; color: white;

View File

@@ -18,15 +18,14 @@ import { withLatestFrom,switchMap,combineLatest } from 'rxjs/operators';
@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{
public items: Observable<IListItem[]>; public items: Observable<IListItem[]>;
constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, private itemService$: ItemService,private folderService$: FolderService) { constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, private itemService$: ItemService,private folderService$: FolderService) {
super(store, itemTypeService, itemService$, location,router); super(store, itemTypeService,location,router);
} }
areaInHa(item:IItem):number { areaInHa(item:IItem):number {

View File

@@ -1,51 +1,34 @@
<div class="spacer"></div> <div class="spacer"></div>
@if (item; as item) { <div *ngIf="item;let item">
<div>
<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>
<div class="card menu-card"> <div class="card menu-card">
@if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) { <h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h2>{{parentItem.name}}</h2>
}
<h1>{{item.name}}</h1> <h1>{{item.name}}</h1>
</div> </div>
@if (item?.data.layers; as layers) { <div class="legend-container" *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card"> <div class="card menu-card">
@if (layers.length>1) { <div *ngIf="layers.length>1">
<div>
<select (change)="onLayerChanged($event.target.value)"> <select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) { <option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select> </select>
</div> </div>
}
<fm-map-layer-legend [showTitle]="layers.length == 1" <fm-map-layer-legend [showTitle]="layers.length == 1"
[layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div> </div>
</div> </div>
<div class="card menu-card"> <div class="card menu-card">
<ul class="p-0 mt-2"> <ul class="p-0 mt-2">
@if (item.isEditable) { <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>
<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> <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>
@if (itemTypeService.isLayer(item)) { <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>
@if (!getItemLayer(item,itemLayer.layerIndex)) { </ng-container>
<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><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> </ul>
</div> </div>
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert> <fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
}
</div> </div>
</div> </div>
</div> </div>
}

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.big-icon { .big-icon {
width: 100%; width: 100%;
color: white; color: white;

View File

@@ -1,13 +1,12 @@
import { Component, Injectable } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, Injectable, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { commonReducers, FolderService, IItem, IItemLinkType, ItemService, ItemTypeService, IUrlType } from '@farmmaps/common';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs';
import * as mapActions from '../../actions/map.actions';
import * as mapReducers from '../../reducers/map.reducer'; import * as mapReducers from '../../reducers/map.reducer';
import { commonReducers, ItemTypeService, ItemService, FolderService } from '@farmmaps/common';
import { Router } from '@angular/router';
import { ForItemType } from '../for-item/for-itemtype.decorator'; import { ForItemType } from '../for-item/for-itemtype.decorator';
import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component';
import * as mapActions from '../../actions/map.actions';
@ForItemType("vnd.farmmaps.itemtype.geotiff.processed") @ForItemType("vnd.farmmaps.itemtype.geotiff.processed")
@@ -15,18 +14,12 @@ import { AbstractSelectedItemComponent } from '../selected-item/selected-item.co
@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 {
sub: Subscription;
constructor(store: Store<mapReducers.State | commonReducers.State>, public itemService: ItemService, itemTypeService: ItemTypeService, location: Location, router: Router, private itemService$: ItemService,private folderService$: FolderService) { constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, private itemService$: ItemService,private folderService$: FolderService) {
super(store, itemTypeService,itemService,location,router); super(store, itemTypeService,location,router);
}
ngOnDestroy(): void {
if (this.sub) this.sub.unsubscribe();
} }
onLayerChanged(layerIndex: number) { onLayerChanged(layerIndex: number) {
@@ -36,13 +29,4 @@ export class SelectedItemGeotiffComponent extends AbstractSelectedItemComponent
layer(layers:any,layerIndex:number) { layer(layers:any,layerIndex:number) {
return layers.find(l => l.index == layerIndex); return layers.find(l => l.index == layerIndex);
} }
download(event:MouseEvent,item:IItem,layers:any,layerIndex:number) {
event.stopPropagation();
event.preventDefault();
const itemLink : IItemLinkType = {itemcode:item.code,query:`layer=${this.layer(layers,layerIndex).name}`,pathsuffix:"download", validminutes:10}
this.sub = this.itemService.getItemLink(itemLink).subscribe((itemLinkUrl:IUrlType) => {
window.location.href = itemLinkUrl.url;
})
}
} }

View File

@@ -1,49 +1,32 @@
<div class="spacer"></div> <div class="spacer"></div>
@if (item; as item) { <div *ngIf="item;let item">
<div>
<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>
<div class="card menu-card"> <div class="card menu-card">
@if (parentOfItemType('vnd.farmmaps.itemtype.cropfield')) { <h2 *ngIf="parentOfItemType('vnd.farmmaps.itemtype.cropfield')">{{parentItem.name}}</h2>
<h2>{{parentItem.name}}</h2>
}
<h1>{{item.name}}</h1> <h1>{{item.name}}</h1>
</div> </div>
@if (item?.data.layers; as layers) { <div class="legend-container" *ngIf="item?.data.layers;let layers">
<div class="legend-container">
<div class="card menu-card"> <div class="card menu-card">
@if (layers.length>1) { <div *ngIf="layers.length>1">
<div>
<select (change)="onLayerChanged($event.target.value)"> <select (change)="onLayerChanged($event.target.value)">
@for (l of layers; track l.index) { <option *ngFor="let l of layers;" [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
<option [value]="l.index" [selected]="itemLayer.layerIndex == l.index">{{l.name}}</option>
}
</select> </select>
</div> </div>
}
<fm-map-layer-legend [showTitle]="layers.length==1" [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend> <fm-map-layer-legend [showTitle]="layers.length==1" [layer]="layer(layers,itemLayer.layerIndex)" [histogramenabled]="true"></fm-map-layer-legend>
</div> </div>
</div> </div>
<div class="card menu-card"> <div class="card menu-card">
<ul class="p-0 mt-2"> <ul class="p-0 mt-2">
@if (item.isEditable) { <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>
<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> <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>
@if (itemTypeService.isLayer(item)) { <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>
@if (!getItemLayer(item,itemLayer.layerIndex)) { </ng-container>
<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> </ul>
</div> </div>
}
<fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert> <fm-map-zoom-to-show-alert [layer]="itemLayer?.layer"></fm-map-zoom-to-show-alert>
</div> </div>
</div> </div>
</div> </div>
}

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.big-icon { .big-icon {
width: 100%; width: 100%;
color: white; color: white;

View File

@@ -1,13 +1,14 @@
import { Component, Input, Injectable, OnInit } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, Injectable, OnDestroy } from '@angular/core'; import { Feature } from 'ol';
import { Router } from '@angular/router';
import { commonReducers, FolderService, IItem, IItemLinkType, ItemService, ItemTypeService, IUrlType } from '@farmmaps/common';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import * as mapActions from '../../actions/map.actions';
import * as mapReducers from '../../reducers/map.reducer'; import * as mapReducers from '../../reducers/map.reducer';
import { commonReducers, ItemTypeService, IItem, Item, ItemService, FolderService, IListItem} from '@farmmaps/common';
import * as mapActions from '../../actions/map.actions';
import { Router, ActivatedRoute, ParamMap, Event } from '@angular/router';
import { ForItemType } from '../for-item/for-itemtype.decorator'; import { ForItemType } from '../for-item/for-itemtype.decorator';
import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component';
import { Subscription } from 'rxjs'; import { Observable } from 'rxjs';
@ForItemType("vnd.farmmaps.itemtype.shape.processed") @ForItemType("vnd.farmmaps.itemtype.shape.processed")
@@ -15,20 +16,14 @@ import { Subscription } from 'rxjs';
@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 {
constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, private itemService$: ItemService,private folderService$: FolderService) {
super(store, itemTypeService,location,router);
}
public selectedLayer = 0; public selectedLayer = 0;
sub: Subscription;
constructor(store: Store<mapReducers.State | commonReducers.State>, public itemService: ItemService, itemTypeService: ItemTypeService, location: Location, router: Router, private itemService$: ItemService,private folderService$: FolderService) {
super(store, itemTypeService,itemService,location,router);
}
ngOnDestroy(): void {
if (this.sub) this.sub.unsubscribe();
}
onLayerChanged(layerIndex: number) { onLayerChanged(layerIndex: number) {
this.store.dispatch(new mapActions.SetLayerIndex(layerIndex)); this.store.dispatch(new mapActions.SetLayerIndex(layerIndex));
@@ -37,13 +32,4 @@ export class SelectedItemShapeComponent extends AbstractSelectedItemComponent im
layer(layers:any,layerIndex:number) { layer(layers:any,layerIndex:number) {
return layers.find(l => l.index == layerIndex); return layers.find(l => l.index == layerIndex);
} }
download(event:MouseEvent,item:IItem,layers:any,layerIndex:number) {
event.stopPropagation();
event.preventDefault();
const itemLink : IItemLinkType = {itemcode:item.code,query:`layer=${this.layer(layers,layerIndex).name}`,pathsuffix:"download", validminutes:10}
this.sub = this.itemService.getItemLink(itemLink).subscribe((itemLinkUrl:IUrlType) => {
window.location.href = itemLinkUrl.url;
})
}
} }

View File

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

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.big-icon { .big-icon {
width: 100%; width: 100%;
color: white; color: white;

View File

@@ -1,33 +1,28 @@
import { Component, Injectable } from '@angular/core';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { Component, Injectable, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { commonReducers, IItem, IItemLinkType, ItemService, ItemTypeService, IUrlType } from '@farmmaps/common';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs';
import * as mapActions from '../../actions/map.actions';
import { IItemLayer, ITemporalItemLayer } from '../../models/item.layer';
import * as mapReducers from '../../reducers/map.reducer'; import * as mapReducers from '../../reducers/map.reducer';
import { TemporalService } from '../../services/temporal.service'; import { commonReducers, ItemTypeService, IItem } from '@farmmaps/common';
import { Router } from '@angular/router';
import { ForItemType } from '../for-item/for-itemtype.decorator'; import { ForItemType } from '../for-item/for-itemtype.decorator';
import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component'; import { AbstractSelectedItemComponent } from '../selected-item/selected-item.component';
import { ITemporalItemLayer} from '../../models/item.layer';
import * as mapActions from '../../actions/map.actions';
import { IItemLayer } from '../../models/item.layer';
import {TemporalService} from '../../services/temporal.service';
@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 {
sub: Subscription;
constructor(store: Store<mapReducers.State | commonReducers.State>, public itemService: ItemService, itemTypeService: ItemTypeService, location: Location, router: Router,public temporalService:TemporalService) { constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router,public temporalService:TemporalService) {
super(store, itemTypeService,itemService,location,router); super(store, itemTypeService,location,router);
}
ngOnDestroy(): void {
if (this.sub) this.sub.unsubscribe();
} }
onLayerChanged(layerIndex: number) { onLayerChanged(layerIndex: number) {
@@ -38,6 +33,8 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
return (this.itemLayer as ITemporalItemLayer)?.selectedItemLayer return (this.itemLayer as ITemporalItemLayer)?.selectedItemLayer
} }
handleNextTemporal(event:MouseEvent) { handleNextTemporal(event:MouseEvent) {
this.store.dispatch(new mapActions.NextTemporal()); this.store.dispatch(new mapActions.NextTemporal());
event.preventDefault(); event.preventDefault();
@@ -64,13 +61,4 @@ export class SelectedItemTemporalComponent extends AbstractSelectedItemComponent
this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, new Date(Date.parse(item.dataDate)).getUTCFullYear()]); this.router.navigate(['/viewer', 'temporal', 'item', item.parentCode, new Date(Date.parse(item.dataDate)).getUTCFullYear()]);
return false; return false;
} }
download(event:MouseEvent,item:IItem,layers:any,layerIndex:number) {
event.stopPropagation();
event.preventDefault();
const itemLink : IItemLinkType = {itemcode:item.code,query:`layer=${this.layer(layers,layerIndex).name}`,pathsuffix:"download", validminutes:10}
this.sub = this.itemService.getItemLink(itemLink).subscribe((itemLinkUrl:IUrlType) => {
window.location.href = itemLinkUrl.url;
})
}
} }

View File

@@ -1,5 +1,4 @@
@if (item; as item) { <div *ngIf="item;let item">
<div>
<div class="card border-0"> <div class="card border-0">
<fm-thumbnail [item]="item" [edit]="item.isEditable"></fm-thumbnail> <fm-thumbnail [item]="item" [edit]="item.isEditable"></fm-thumbnail>
</div> </div>
@@ -9,23 +8,14 @@
<h1 class="card-title">{{item.name}}</h1> <h1 class="card-title">{{item.name}}</h1>
<div class="card menu-card"> <div class="card menu-card">
<ul class="p-0 mt-2"> <ul class="p-0 mt-2">
@if (itemTypeService.hasViewer(item)) { <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>
<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="item.isEditable"><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" title="Edit"></i> <span i18n>Edit</span></a></li>
} <ng-container *ngIf="itemTypeService.isLayer(item)">
@if (item.isEditable) { <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><a href="#" (click)="handleOnEdit(item)"><i class="fal fa-pencil" aria-hidden="true" title="Edit"></i> <span i18n>Edit</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>
} </ng-container>
@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> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
}

View File

@@ -1,3 +1,5 @@
@import "~bootstrap/scss/bootstrap.scss";
.big-icon { .big-icon {
width: 100%; width: 100%;
color: white; color: white;

View File

@@ -2,7 +2,7 @@ import {Component, Injectable, Input, Directive} from '@angular/core';
import {Location} from '@angular/common'; import {Location} from '@angular/common';
import {Store} from '@ngrx/store'; import {Store} from '@ngrx/store';
import * as mapReducers from '../../reducers/map.reducer'; import * as mapReducers from '../../reducers/map.reducer';
import {AppConfig, commonReducers, IItem, ItemService, ItemTypeService} from '@farmmaps/common'; import {AppConfig, commonReducers, IItem, ItemTypeService} from '@farmmaps/common';
import * as mapActions from '../../actions/map.actions'; import * as mapActions from '../../actions/map.actions';
import {Router} from '@angular/router'; import {Router} from '@angular/router';
import { IItemLayer } from '../../models/item.layer'; import { IItemLayer } from '../../models/item.layer';
@@ -15,7 +15,7 @@ export abstract class AbstractSelectedItemComponent {
@Input() parentItem: IItem; @Input() parentItem: IItem;
@Input() itemLayer: IItemLayer; @Input() itemLayer: IItemLayer;
@Input() overlayLayers: Array<IItemLayer>; @Input() overlayLayers: Array<IItemLayer>;
constructor(public store: Store<mapReducers.State | commonReducers.State>, public itemTypeService: ItemTypeService, public itemService: ItemService, private location: Location, public router: Router) { constructor(public store: Store<mapReducers.State | commonReducers.State>, public itemTypeService: ItemTypeService, private location: Location, public router: Router) {
} }
handleOnView(item: IItem) { handleOnView(item: IItem) {
@@ -37,11 +37,6 @@ export abstract class AbstractSelectedItemComponent {
return false; return false;
} }
handleOnDelete(item: IItem) {
this.itemService.deleteItem(item.code);
return false;
}
handleAddAsLayer(item: IItem,layerIndex = -1) { handleAddAsLayer(item: IItem,layerIndex = -1) {
this.store.dispatch(new mapActions.AddLayer(item,layerIndex)); this.store.dispatch(new mapActions.AddLayer(item,layerIndex));
return false; return false;
@@ -77,13 +72,12 @@ export abstract class AbstractSelectedItemComponent {
@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 {
constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, itemService: ItemService, location: Location, router: Router, public config:AppConfig) { constructor(store: Store<mapReducers.State | commonReducers.State>, itemTypeService: ItemTypeService, location: Location, router: Router, public config:AppConfig) {
super(store, itemTypeService,itemService,location,router); super(store, itemTypeService,location,router);
} }
getThumbnailUrl(item:IItem):string { getThumbnailUrl(item:IItem):string {

View File

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

@@ -3,8 +3,7 @@ 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;

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