diff --git a/angular.json b/angular.json
index adf2628..948b96a 100644
--- a/angular.json
+++ b/angular.json
@@ -253,6 +253,43 @@
}
}
}
+ },
+ "ng-openlayers": {
+ "projectType": "library",
+ "root": "projects/ng-openlayers",
+ "sourceRoot": "projects/ng-openlayers/src",
+ "prefix": "lib",
+ "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-devkit/build-angular:karma",
+ "options": {
+ "main": "projects/ng-openlayers/src/test.ts",
+ "tsConfig": "projects/ng-openlayers/tsconfig.spec.json",
+ "karmaConfig": "projects/ng-openlayers/karma.conf.js"
+ }
+ },
+ "lint": {
+ "builder": "@angular-eslint/builder:lint",
+ "options": {
+ "lintFilePatterns": [
+ "projects/ng-openlayers/**/*.ts",
+ "projects/ng-openlayers/**/*.html"
+ ]
+ }
+ }
+ }
}
},
"cli": {
diff --git a/package.json b/package.json
index 80a467b..092018c 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"@farmmaps/common": "file:dist/common",
"@farmmaps/common-map": "file:dist/common-map",
"@farmmaps/common-map3d": "file:dist/common-map3d",
- "ng-openlayers": "17.1.3",
+ "ng-openlayers": "file:dist/ng-openlayers",
"@microsoft/signalr": "^3.1.16",
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
"@ngrx/effects": "^17",
diff --git a/projects/ng-openlayers/.eslintrc.json b/projects/ng-openlayers/.eslintrc.json
new file mode 100644
index 0000000..9863079
--- /dev/null
+++ b/projects/ng-openlayers/.eslintrc.json
@@ -0,0 +1,54 @@
+{
+ "extends": "../../.eslintrc.json",
+ "ignorePatterns": [
+ "!**/*"
+ ],
+ "overrides": [
+ {
+ "files": [
+ "*.ts"
+ ],
+ "parserOptions": {
+ "project": [
+ "libs/ng-openlayers/tsconfig.*?.json"
+ ],
+ "createDefaultProgram": true
+ },
+ "rules": {
+ "@angular-eslint/component-selector": [
+ "error",
+ {
+ "type": "element",
+ "prefix": "aol",
+ "style": "kebab-case"
+ }
+ ],
+ "@angular-eslint/directive-selector": [
+ "error",
+ {
+ "type": "attribute",
+ "prefix": "aol",
+ "style": "camelCase"
+ }
+ ],
+ "@typescript-eslint/explicit-member-accessibility": [
+ "off",
+ {
+ "accessibility": "explicit"
+ }
+ ],
+ "arrow-parens": [
+ "off",
+ "always"
+ ],
+ "import/order": "off"
+ }
+ },
+ {
+ "files": [
+ "*.html"
+ ],
+ "rules": {}
+ }
+ ]
+}
diff --git a/projects/ng-openlayers/.gitignore b/projects/ng-openlayers/.gitignore
new file mode 100644
index 0000000..4d64059
--- /dev/null
+++ b/projects/ng-openlayers/.gitignore
@@ -0,0 +1,2 @@
+/node_modules
+
diff --git a/projects/ng-openlayers/karma.conf.js b/projects/ng-openlayers/karma.conf.js
new file mode 100644
index 0000000..ce04701
--- /dev/null
+++ b/projects/ng-openlayers/karma.conf.js
@@ -0,0 +1,32 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-coverage-istanbul-reporter'),
+ require('@angular-devkit/build-angular/plugins/karma'),
+ ],
+ client: {
+ clearContext: false, // leave Jasmine Spec Runner output visible in browser
+ },
+ coverageIstanbulReporter: {
+ dir: require('path').join(__dirname, '../../coverage/ng-openlayers'),
+ reports: ['html', 'lcovonly', 'text-summary'],
+ fixWebpackSourcePaths: true,
+ },
+ reporters: ['progress', 'kjhtml'],
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['Chrome'],
+ singleRun: false,
+ restartOnFileChange: true,
+ });
+};
diff --git a/projects/ng-openlayers/ng-package.json b/projects/ng-openlayers/ng-package.json
new file mode 100644
index 0000000..bcf2327
--- /dev/null
+++ b/projects/ng-openlayers/ng-package.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
+ "dest": "../../dist/libs/ng-openlayers",
+ "assets": [
+ "ngcc.config.js"
+ ],
+ "lib": {
+ "entryFile": "src/public-api.ts"
+ }
+}
diff --git a/projects/ng-openlayers/ngcc.config.js b/projects/ng-openlayers/ngcc.config.js
new file mode 100644
index 0000000..81d297a
--- /dev/null
+++ b/projects/ng-openlayers/ngcc.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ ignorableDeepImportMatchers: [/ol\//],
+};
diff --git a/projects/ng-openlayers/package.json b/projects/ng-openlayers/package.json
new file mode 100644
index 0000000..90aa737
--- /dev/null
+++ b/projects/ng-openlayers/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "ng-openlayers",
+ "version": "17.1.3",
+ "description": "OpenLayers library for Angular",
+ "author": "Kamil Furtak (kamil.furtak@gmail.com)",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/kamilfurtak/ng-openlayers.git"
+ },
+ "bugs": {
+ "url": "https://github.com/kamilfurtak/ng-openlayers/issues"
+ },
+ "homepage": "https://github.com/kamilfurtak/ng-openlayers",
+ "keywords": [
+ "ngx-openlayers",
+ "angular",
+ "angular17",
+ "openlayers",
+ "openlayers8",
+ "ol8"
+ ],
+ "license": "MPL-2.0",
+ "private": false,
+ "scripts": {
+ "release": "standard-version -m \"chore(release): version %s\" -t \"\"",
+ "prepublishOnly": "cp projects/ng-openlayers/README.md dist/ng-openlayers/README.md"
+ },
+ "standard-version": {
+ "postchangelog": "cp projects/ng-openlayers/CHANGELOG.md dist/ng-openlayers/CHANGELOG.md"
+ },
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@angular/common": ">=17.0.0",
+ "@angular/core": ">=17.0.0",
+ "ol": "^8.2.0"
+ }
+}
diff --git a/projects/ng-openlayers/project.json b/projects/ng-openlayers/project.json
new file mode 100644
index 0000000..e88a908
--- /dev/null
+++ b/projects/ng-openlayers/project.json
@@ -0,0 +1,38 @@
+{
+ "name": "ng-openlayers",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "sourceRoot": "libs/ng-openlayers/src",
+ "prefix": "lib",
+ "targets": {
+ "build": {
+ "executor": "@nx/angular:package",
+ "options": {
+ "tsConfig": "libs/ng-openlayers/tsconfig.lib.json",
+ "project": "libs/ng-openlayers/ng-package.json"
+ },
+ "configurations": {
+ "production": {
+ "tsConfig": "libs/ng-openlayers/tsconfig.lib.prod.json"
+ }
+ }
+ },
+ "test": {
+ "executor": "@angular-devkit/build-angular:karma",
+ "options": {
+ "main": "libs/ng-openlayers/src/test.ts",
+ "tsConfig": "libs/ng-openlayers/tsconfig.spec.json",
+ "karmaConfig": "libs/ng-openlayers/karma.conf.js"
+ }
+ },
+ "lint": {
+ "executor": "@nx/eslint:lint",
+ "options": {
+ "lintFilePatterns": [
+ "libs/ng-openlayers/**/*.ts",
+ "libs/ng-openlayers/**/*.html"
+ ]
+ }
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/attribution.component.ts b/projects/ng-openlayers/src/lib/attribution.component.ts
new file mode 100644
index 0000000..28b0c99
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/attribution.component.ts
@@ -0,0 +1,15 @@
+import { Component, ElementRef, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'aol-attribution',
+ template: '',
+})
+export class AttributionComponent implements OnInit {
+ label: string;
+
+ constructor(private elementRef: ElementRef) {}
+
+ ngOnInit() {
+ this.label = this.elementRef.nativeElement.innerHTML;
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/attributions.component.ts b/projects/ng-openlayers/src/lib/attributions.component.ts
new file mode 100644
index 0000000..47ead99
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/attributions.component.ts
@@ -0,0 +1,25 @@
+import { AfterViewInit, Component, ContentChildren, Host, QueryList } from '@angular/core';
+import { SourceComponent } from './sources/source.component';
+import { AttributionComponent } from './attribution.component';
+
+@Component({
+ selector: 'aol-attributions',
+ template: '',
+})
+export class AttributionsComponent implements AfterViewInit {
+ @ContentChildren(AttributionComponent)
+ attributions: QueryList;
+
+ instance: Array;
+
+ constructor(@Host() private source: SourceComponent) {}
+
+ /* we can do this at the very end */
+ ngAfterViewInit() {
+ if (this.attributions.length) {
+ this.instance = this.attributions.map((cmp) => cmp.label);
+ // console.log('setting attributions:', this.instance);
+ this.source.instance.setAttributions(this.instance);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/collectioncoordinates.component.ts b/projects/ng-openlayers/src/lib/collectioncoordinates.component.ts
new file mode 100644
index 0000000..94565b1
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/collectioncoordinates.component.ts
@@ -0,0 +1,89 @@
+import { Component, Input, OnChanges, OnInit, Optional, SimpleChanges } from '@angular/core';
+import { MapComponent } from './map.component';
+import { GeometryLinestringComponent } from './geom/geometrylinestring.component';
+import { GeometryPolygonComponent } from './geom/geometrypolygon.component';
+import { GeometryMultiPointComponent } from './geom/geometrymultipoint.component';
+import { GeometryMultiLinestringComponent } from './geom/geometrymultilinestring.component';
+import { GeometryMultiPolygonComponent } from './geom/geometrymultipolygon.component';
+import { Coordinate } from 'ol/coordinate';
+import { transform } from 'ol/proj';
+
+@Component({
+ selector: 'aol-collection-coordinates',
+ template: ` `,
+})
+export class CollectionCoordinatesComponent implements OnChanges, OnInit {
+ @Input()
+ coordinates: Coordinate[] | Coordinate[][] | Coordinate[][][];
+ @Input()
+ srid = 'EPSG:3857';
+
+ private host: any;
+ private mapSrid = 'EPSG:3857';
+
+ constructor(
+ private map: MapComponent,
+ @Optional() geometryLinestring: GeometryLinestringComponent,
+ @Optional() geometryPolygon: GeometryPolygonComponent,
+ @Optional() geometryMultipoint: GeometryMultiPointComponent,
+ @Optional() geometryMultilinestring: GeometryMultiLinestringComponent,
+ @Optional() geometryMultipolygon: GeometryMultiPolygonComponent
+ ) {
+ if (!!geometryLinestring) {
+ this.host = geometryLinestring;
+ } else if (!!geometryPolygon) {
+ this.host = geometryPolygon;
+ } else if (!!geometryMultipoint) {
+ this.host = geometryMultipoint;
+ } else if (!!geometryMultilinestring) {
+ this.host = geometryMultilinestring;
+ } else if (!!geometryMultipolygon) {
+ this.host = geometryMultipolygon;
+ } else {
+ throw new Error('aol-collection-coordinates must be a child of a geometry component');
+ }
+ }
+
+ ngOnInit() {
+ this.map.instance.on('change:view', (e) => this.onMapViewChanged(e));
+ this.mapSrid = this.map.instance.getView().getProjection().getCode();
+ this.transformCoordinates();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ this.transformCoordinates();
+ }
+
+ private onMapViewChanged(event) {
+ this.mapSrid = event.target.get(event.key).getProjection().getCode();
+ this.transformCoordinates();
+ }
+
+ private transformCoordinates() {
+ let transformedCoordinates: Coordinate[] | Coordinate[][] | Coordinate[][][];
+
+ if (this.srid === this.mapSrid) {
+ transformedCoordinates = this.coordinates;
+ } else {
+ switch (this.host.componentType) {
+ case 'geometry-linestring':
+ case 'geometry-multipoint':
+ transformedCoordinates = (this.coordinates as Coordinate[]).map((c) => transform(c, this.srid, this.mapSrid));
+ break;
+ case 'geometry-polygon':
+ case 'geometry-multilinestring':
+ transformedCoordinates = (this.coordinates as Coordinate[][]).map((cc) =>
+ cc.map((c) => transform(c, this.srid, this.mapSrid))
+ );
+ break;
+ case 'geometry-multipolygon':
+ transformedCoordinates = (this.coordinates as Coordinate[][][]).map((ccc) =>
+ ccc.map((cc) => cc.map((c) => transform(c, this.srid, this.mapSrid)))
+ );
+ break;
+ }
+ }
+
+ this.host.instance.setCoordinates(transformedCoordinates);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/content.component.ts b/projects/ng-openlayers/src/lib/content.component.ts
new file mode 100644
index 0000000..d03c05f
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/content.component.ts
@@ -0,0 +1,9 @@
+import { Component, ElementRef } from '@angular/core';
+
+@Component({
+ selector: 'aol-content',
+ template: '',
+})
+export class ContentComponent {
+ constructor(public elementRef: ElementRef) {}
+}
diff --git a/projects/ng-openlayers/src/lib/controls/attribution.component.ts b/projects/ng-openlayers/src/lib/controls/attribution.component.ts
new file mode 100644
index 0000000..33fbce9
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/attribution.component.ts
@@ -0,0 +1,33 @@
+import { Component, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
+import { Attribution } from 'ol/control';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-control-attribution',
+ template: ``,
+})
+export class ControlAttributionComponent implements OnInit, OnDestroy {
+ @Input()
+ collapsible: boolean;
+
+ public componentType = 'control';
+ instance: Attribution;
+ target: HTMLElement;
+
+ constructor(
+ private map: MapComponent,
+ private element: ElementRef
+ ) {}
+
+ ngOnInit() {
+ this.target = this.element.nativeElement;
+ // console.log('ol.control.Attribution init: ', this);
+ this.instance = new Attribution(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-attribution');
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/control.component.ts b/projects/ng-openlayers/src/lib/controls/control.component.ts
new file mode 100644
index 0000000..2ff6489
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/control.component.ts
@@ -0,0 +1,33 @@
+import { Component, ContentChild, OnDestroy, OnInit } from '@angular/core';
+import { Control } from 'ol/control';
+import { MapComponent } from '../map.component';
+import { ContentComponent } from '../content.component';
+
+@Component({
+ selector: 'aol-control',
+ template: ` `,
+})
+export class ControlComponent implements OnInit, OnDestroy {
+ @ContentChild(ContentComponent, { static: true })
+ content: ContentComponent;
+
+ public componentType = 'control';
+ instance: Control;
+ element: HTMLElement;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ if (this.content) {
+ this.element = this.content.elementRef.nativeElement;
+ this.instance = new Control(this);
+ this.map.instance.addControl(this.instance);
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.instance) {
+ this.map.instance.removeControl(this.instance);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/default.component.ts b/projects/ng-openlayers/src/lib/controls/default.component.ts
new file mode 100644
index 0000000..ebb9ebd
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/default.component.ts
@@ -0,0 +1,42 @@
+import { Component, OnDestroy, OnInit, Input } from '@angular/core';
+import { Control, defaults } from 'ol/control';
+import { Collection } from 'ol';
+import { Options as AttributionOptions } from 'ol/control/Attribution';
+import { Options as RotateOptions } from 'ol/control/Rotate';
+import { Options as ZoomOptions } from 'ol/control/Zoom';
+
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-control-defaults',
+ template: '',
+})
+export class DefaultControlComponent implements OnInit, OnDestroy {
+ @Input()
+ attribution: boolean;
+ @Input()
+ attributionOptions: AttributionOptions;
+ @Input()
+ rotate: boolean;
+ @Input()
+ rotateOptions: RotateOptions;
+ @Input()
+ zoom: boolean;
+ @Input()
+ zoomOptions: ZoomOptions;
+
+ instance: Collection;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ // console.log('ol.control.defaults init: ', this);
+ this.instance = defaults(this);
+ this.instance.forEach((c) => this.map.instance.addControl(c));
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-defaults');
+ this.instance.forEach((c) => this.map.instance.removeControl(c));
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/fullscreen.component.ts b/projects/ng-openlayers/src/lib/controls/fullscreen.component.ts
new file mode 100644
index 0000000..b4dca7f
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/fullscreen.component.ts
@@ -0,0 +1,36 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { FullScreen } from 'ol/control';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-control-fullscreen',
+ template: ` `,
+})
+export class ControlFullScreenComponent implements OnInit, OnDestroy {
+ @Input()
+ className: string;
+ @Input()
+ label: string;
+ @Input()
+ labelActive: string;
+ @Input()
+ tipLabel: string;
+ @Input()
+ keys: boolean;
+
+ instance: FullScreen;
+
+ constructor(private map: MapComponent) {
+ // console.log('instancing aol-control-fullscreen');
+ }
+
+ ngOnInit() {
+ this.instance = new FullScreen(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-fullscreen');
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/mouseposition.component.ts b/projects/ng-openlayers/src/lib/controls/mouseposition.component.ts
new file mode 100644
index 0000000..35479a4
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/mouseposition.component.ts
@@ -0,0 +1,36 @@
+import { Component, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
+import MousePosition from 'ol/control/MousePosition';
+import { MapComponent } from '../map.component';
+import { CoordinateFormat } from 'ol/coordinate';
+import { ProjectionLike } from 'ol/proj';
+
+@Component({
+ selector: 'aol-control-mouseposition',
+ template: ``,
+})
+export class ControlMousePositionComponent implements OnInit, OnDestroy {
+ @Input()
+ coordinateFormat: CoordinateFormat;
+ @Input()
+ projection: ProjectionLike;
+
+ instance: MousePosition;
+ target: HTMLElement;
+
+ constructor(
+ private map: MapComponent,
+ private element: ElementRef
+ ) {}
+
+ ngOnInit() {
+ this.target = this.element.nativeElement;
+ // console.log('ol.control.MousePosition init: ', this);
+ this.instance = new MousePosition(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-mouseposition');
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/overviewmap.component.ts b/projects/ng-openlayers/src/lib/controls/overviewmap.component.ts
new file mode 100644
index 0000000..42ef072
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/overviewmap.component.ts
@@ -0,0 +1,53 @@
+import { Component, Input, OnDestroy, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { Layer } from 'ol/layer';
+import { View } from 'ol';
+import { OverviewMap } from 'ol/control';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-control-overviewmap',
+ template: ` `,
+})
+export class ControlOverviewMapComponent implements OnInit, OnChanges, OnDestroy {
+ @Input()
+ collapsed: boolean;
+ @Input()
+ collapseLabel: string;
+ @Input()
+ collapsible: boolean;
+ @Input()
+ label: string;
+ @Input()
+ layers: Layer[];
+ @Input()
+ target: HTMLElement;
+ @Input()
+ tipLabel: string;
+ @Input()
+ view: View;
+
+ instance: OverviewMap;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new OverviewMap(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeControl(this.instance);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.instance != null && changes.hasOwnProperty('view')) {
+ this.reloadInstance();
+ }
+ }
+
+ private reloadInstance() {
+ this.map.instance.removeControl(this.instance);
+ this.instance = new OverviewMap(this);
+ this.map.instance.addControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/rotate.component.ts b/projects/ng-openlayers/src/lib/controls/rotate.component.ts
new file mode 100644
index 0000000..775866f
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/rotate.component.ts
@@ -0,0 +1,36 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Rotate } from 'ol/control';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-control-rotate',
+ template: ` `,
+})
+export class ControlRotateComponent implements OnInit, OnDestroy {
+ @Input()
+ className: string;
+ @Input()
+ label: string;
+ @Input()
+ tipLabel: string;
+ @Input()
+ duration: number;
+ @Input()
+ autoHide: boolean;
+
+ instance: Rotate;
+
+ constructor(private map: MapComponent) {
+ // console.log('instancing aol-control-rotate');
+ }
+
+ ngOnInit() {
+ this.instance = new Rotate(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-rotate');
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/scaleline.component.ts b/projects/ng-openlayers/src/lib/controls/scaleline.component.ts
new file mode 100644
index 0000000..7853959
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/scaleline.component.ts
@@ -0,0 +1,26 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { ScaleLine } from 'ol/control';
+import { MapComponent } from '../map.component';
+import { Units } from 'ol/control/ScaleLine';
+
+@Component({
+ selector: 'aol-control-scaleline',
+ template: ` `,
+})
+export class ControlScaleLineComponent implements OnInit, OnDestroy {
+ @Input()
+ units: Units;
+
+ instance: ScaleLine;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new ScaleLine(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/zoom.component.ts b/projects/ng-openlayers/src/lib/controls/zoom.component.ts
new file mode 100644
index 0000000..0aee170
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/zoom.component.ts
@@ -0,0 +1,38 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { Zoom } from 'ol/control';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-control-zoom',
+ template: ` `,
+})
+export class ControlZoomComponent implements OnInit, OnDestroy {
+ @Input()
+ duration: number;
+ @Input()
+ zoomInLabel: string | HTMLElement;
+ @Input()
+ zoomOutLabel: string | HTMLElement;
+ @Input()
+ zoomInTipLabel: string;
+ @Input()
+ zoomOutTipLabel: string;
+ @Input()
+ delta: number;
+
+ instance: Zoom;
+
+ constructor(private map: MapComponent) {
+ // console.log('instancing aol-control-zoom');
+ }
+
+ ngOnInit() {
+ this.instance = new Zoom(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-zoom');
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/zoomslider.component.ts b/projects/ng-openlayers/src/lib/controls/zoomslider.component.ts
new file mode 100644
index 0000000..c7d11ba
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/zoomslider.component.ts
@@ -0,0 +1,34 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { ZoomSlider } from 'ol/control';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-control-zoomslider',
+ template: ` `,
+})
+export class ControlZoomSliderComponent implements OnInit, OnDestroy {
+ @Input()
+ className: string;
+ @Input()
+ duration: number;
+ @Input()
+ maxResolution: number;
+ @Input()
+ minResolution: number;
+
+ instance: ZoomSlider;
+
+ constructor(private map: MapComponent) {
+ // console.log('instancing aol-control-zoomslider');
+ }
+
+ ngOnInit() {
+ this.instance = new ZoomSlider(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-zoomslider');
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/controls/zoomtoextent.component.ts b/projects/ng-openlayers/src/lib/controls/zoomtoextent.component.ts
new file mode 100644
index 0000000..f53f7b1
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/controls/zoomtoextent.component.ts
@@ -0,0 +1,35 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { ZoomToExtent } from 'ol/control';
+import { MapComponent } from '../map.component';
+import { Extent } from 'ol/extent';
+
+@Component({
+ selector: 'aol-control-zoomtoextent',
+ template: ` `,
+})
+export class ControlZoomToExtentComponent implements OnInit, OnDestroy {
+ @Input()
+ className: string;
+ @Input()
+ label: string | HTMLElement;
+ @Input()
+ tipLabel: string;
+ @Input()
+ extent: Extent;
+
+ instance: ZoomToExtent;
+
+ constructor(private map: MapComponent) {
+ // console.log('instancing aol-control-zoomtoextent');
+ }
+
+ ngOnInit() {
+ this.instance = new ZoomToExtent(this);
+ this.map.instance.addControl(this.instance);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-control-zoomtoextent');
+ this.map.instance.removeControl(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/coordinate.component.ts b/projects/ng-openlayers/src/lib/coordinate.component.ts
new file mode 100644
index 0000000..38d5dc2
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/coordinate.component.ts
@@ -0,0 +1,80 @@
+import { Component, Optional, OnChanges, Input, SimpleChanges, OnInit } from '@angular/core';
+import { transform } from 'ol/proj';
+import { MapComponent } from './map.component';
+import { GeometryPointComponent } from './geom/geometrypoint.component';
+import { GeometryCircleComponent } from './geom/geometrycircle.component';
+import { ViewComponent } from './view.component';
+import { OverlayComponent } from './overlay.component';
+
+@Component({
+ selector: 'aol-coordinate',
+ template: ` `,
+})
+export class CoordinateComponent implements OnChanges, OnInit {
+ @Input()
+ x: number;
+ @Input()
+ y: number;
+ @Input()
+ srid = 'EPSG:3857';
+
+ private host: any;
+ private mapSrid = 'EPSG:3857';
+
+ constructor(
+ private map: MapComponent,
+ @Optional() viewHost: ViewComponent,
+ @Optional() geometryPointHost: GeometryPointComponent,
+ @Optional() geometryCircleHost: GeometryCircleComponent,
+ @Optional() overlayHost: OverlayComponent
+ ) {
+ // console.log('instancing aol-coordinate');
+ if (geometryPointHost !== null) {
+ this.host = geometryPointHost;
+ } else if (geometryCircleHost !== null) {
+ this.host = geometryCircleHost;
+ } else if (viewHost !== null) {
+ this.host = viewHost;
+ } else if (overlayHost !== null) {
+ this.host = overlayHost;
+ }
+ }
+
+ ngOnInit() {
+ this.map.instance.on('change:view', (e) => this.onMapViewChanged(e));
+ this.mapSrid = this.map.instance.getView().getProjection().getCode();
+ this.transformCoordinates();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ this.transformCoordinates();
+ }
+
+ private onMapViewChanged(event) {
+ this.mapSrid = event.target.get(event.key).getProjection().getCode();
+ this.transformCoordinates();
+ }
+
+ private transformCoordinates() {
+ let transformedCoordinates: number[];
+
+ if (this.srid === this.mapSrid) {
+ transformedCoordinates = [this.x, this.y];
+ } else {
+ transformedCoordinates = transform([this.x, this.y], this.srid, this.mapSrid);
+ }
+
+ switch (this.host.componentType) {
+ case 'geometry-point':
+ this.host.instance.setCoordinates(transformedCoordinates);
+ break;
+ case 'geometry-circle':
+ case 'view':
+ this.host.instance.setCenter(transformedCoordinates);
+ break;
+ case 'overlay':
+ this.host.instance.setPosition(transformedCoordinates);
+ break;
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/feature.component.ts b/projects/ng-openlayers/src/lib/feature.component.ts
new file mode 100644
index 0000000..03c7313
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/feature.component.ts
@@ -0,0 +1,35 @@
+import { Component, OnInit, OnDestroy, OnChanges, Input, SimpleChanges } from '@angular/core';
+import { Feature } from 'ol';
+import { SourceVectorComponent } from './sources/vector.component';
+
+@Component({
+ selector: 'aol-feature',
+ template: ` `,
+})
+export class FeatureComponent implements OnInit, OnDestroy, OnChanges {
+ @Input()
+ id: string | number | undefined;
+
+ public componentType = 'feature';
+ public instance: Feature;
+
+ constructor(private host: SourceVectorComponent) {}
+
+ ngOnInit() {
+ this.instance = new Feature();
+ if (this.id !== undefined) {
+ this.instance.setId(this.id);
+ }
+ this.host.instance.addFeature(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.host.instance.removeFeature(this.instance);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.instance) {
+ this.instance.setId(this.id);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/formats/format.component.ts b/projects/ng-openlayers/src/lib/formats/format.component.ts
new file mode 100644
index 0000000..74c60ef
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/formats/format.component.ts
@@ -0,0 +1,6 @@
+import Feature from 'ol/format/Feature';
+
+export class FormatComponent {
+ public instance: Feature;
+ public componentType = 'format';
+}
diff --git a/projects/ng-openlayers/src/lib/formats/mvt.component.ts b/projects/ng-openlayers/src/lib/formats/mvt.component.ts
new file mode 100644
index 0000000..0d880c2
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/formats/mvt.component.ts
@@ -0,0 +1,27 @@
+import { Component, forwardRef, Input } from '@angular/core';
+import { FormatComponent } from './format.component';
+import { MVT } from 'ol/format';
+import { FeatureClass } from 'ol/Feature';
+
+@Component({
+ selector: 'aol-format-mvt',
+ template: '',
+ providers: [{ provide: FormatComponent, useExisting: forwardRef(() => FormatMVTComponent) }],
+})
+export class FormatMVTComponent extends FormatComponent {
+ @Input()
+ featureClass: FeatureClass;
+ @Input()
+ geometryName: string;
+ @Input()
+ layerName: string;
+ @Input()
+ layers: string[];
+
+ instance: MVT;
+
+ constructor() {
+ super();
+ this.instance = new MVT(this);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/geometrycircle.component.ts b/projects/ng-openlayers/src/lib/geom/geometrycircle.component.ts
new file mode 100644
index 0000000..cdccb1d
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/geometrycircle.component.ts
@@ -0,0 +1,28 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { Circle } from 'ol/geom';
+import { SimpleGeometryComponent } from './simplegeometry.component';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-geometry-circle',
+ template: ` `,
+})
+export class GeometryCircleComponent extends SimpleGeometryComponent implements OnInit {
+ @Input()
+ get radius(): number {
+ return this.instance.getRadius();
+ }
+ set radius(radius: number) {
+ this.instance.setRadius(radius);
+ }
+
+ public componentType = 'geometry-circle';
+ public instance: Circle;
+
+ constructor(map: MapComponent, host: FeatureComponent) {
+ super(map, host);
+ // defaulting coordinates to [0,0]. To be overridden in child component.
+ this.instance = new Circle([0, 0]);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/geometrylinestring.component.ts b/projects/ng-openlayers/src/lib/geom/geometrylinestring.component.ts
new file mode 100644
index 0000000..23f9d1c
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/geometrylinestring.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { SimpleGeometryComponent } from './simplegeometry.component';
+import { MapComponent } from '../map.component';
+import { LineString } from 'ol/geom';
+
+@Component({
+ selector: 'aol-geometry-linestring',
+ template: ` `,
+})
+export class GeometryLinestringComponent extends SimpleGeometryComponent implements OnInit {
+ public componentType = 'geometry-linestring';
+ public instance: LineString;
+
+ constructor(map: MapComponent, host: FeatureComponent) {
+ super(map, host);
+ }
+
+ ngOnInit() {
+ this.instance = new LineString([
+ [0, 0],
+ [1, 1],
+ ]);
+ super.ngOnInit();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/geometrymultilinestring.component.ts b/projects/ng-openlayers/src/lib/geom/geometrymultilinestring.component.ts
new file mode 100644
index 0000000..92316cb
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/geometrymultilinestring.component.ts
@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { SimpleGeometryComponent } from './simplegeometry.component';
+import { MapComponent } from '../map.component';
+import { MultiLineString } from 'ol/geom';
+
+@Component({
+ selector: 'aol-geometry-multilinestring',
+ template: ` `,
+})
+export class GeometryMultiLinestringComponent extends SimpleGeometryComponent implements OnInit {
+ public componentType = 'geometry-multilinestring';
+ public instance: MultiLineString;
+
+ constructor(map: MapComponent, host: FeatureComponent) {
+ super(map, host);
+ }
+
+ ngOnInit() {
+ this.instance = new MultiLineString([
+ [
+ [0, 0],
+ [1, 1],
+ ],
+ ]);
+ super.ngOnInit();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/geometrymultipoint.component.ts b/projects/ng-openlayers/src/lib/geom/geometrymultipoint.component.ts
new file mode 100644
index 0000000..71414c2
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/geometrymultipoint.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { SimpleGeometryComponent } from './simplegeometry.component';
+import { MapComponent } from '../map.component';
+import { MultiPoint } from 'ol/geom';
+
+@Component({
+ selector: 'aol-geometry-multipoint',
+ template: ` `,
+})
+export class GeometryMultiPointComponent extends SimpleGeometryComponent implements OnInit {
+ public componentType = 'geometry-multipoint';
+ public instance: MultiPoint;
+
+ constructor(map: MapComponent, host: FeatureComponent) {
+ super(map, host);
+ }
+
+ ngOnInit() {
+ this.instance = new MultiPoint([
+ [0, 0],
+ [1, 1],
+ ]);
+ super.ngOnInit();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/geometrymultipolygon.component.ts b/projects/ng-openlayers/src/lib/geom/geometrymultipolygon.component.ts
new file mode 100644
index 0000000..d802b13
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/geometrymultipolygon.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { SimpleGeometryComponent } from './simplegeometry.component';
+import { MapComponent } from '../map.component';
+import { MultiPolygon } from 'ol/geom';
+
+@Component({
+ selector: 'aol-geometry-multipolygon',
+ template: ` `,
+})
+export class GeometryMultiPolygonComponent extends SimpleGeometryComponent implements OnInit {
+ public componentType = 'geometry-multipolygon';
+ public instance: MultiPolygon;
+
+ constructor(map: MapComponent, host: FeatureComponent) {
+ super(map, host);
+ }
+
+ ngOnInit() {
+ this.instance = new MultiPolygon([
+ [
+ [
+ [0, 0],
+ [1, 1],
+ [0, 1],
+ ],
+ ],
+ ]);
+ super.ngOnInit();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/geometrypoint.component.ts b/projects/ng-openlayers/src/lib/geom/geometrypoint.component.ts
new file mode 100644
index 0000000..23b1997
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/geometrypoint.component.ts
@@ -0,0 +1,23 @@
+import { Component, OnInit } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { SimpleGeometryComponent } from './simplegeometry.component';
+import { MapComponent } from '../map.component';
+import { Point } from 'ol/geom';
+
+@Component({
+ selector: 'aol-geometry-point',
+ template: ` `,
+})
+export class GeometryPointComponent extends SimpleGeometryComponent implements OnInit {
+ public componentType = 'geometry-point';
+ public instance: Point;
+
+ constructor(map: MapComponent, host: FeatureComponent) {
+ super(map, host);
+ }
+
+ ngOnInit() {
+ this.instance = new Point([0, 0]);
+ super.ngOnInit();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/geometrypolygon.component.ts b/projects/ng-openlayers/src/lib/geom/geometrypolygon.component.ts
new file mode 100644
index 0000000..8a1158f
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/geometrypolygon.component.ts
@@ -0,0 +1,29 @@
+import { Component, OnInit } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { SimpleGeometryComponent } from './simplegeometry.component';
+import { MapComponent } from '../map.component';
+import { Polygon } from 'ol/geom';
+
+@Component({
+ selector: 'aol-geometry-polygon',
+ template: ` `,
+})
+export class GeometryPolygonComponent extends SimpleGeometryComponent implements OnInit {
+ public componentType = 'geometry-polygon';
+ public instance: Polygon;
+
+ constructor(map: MapComponent, host: FeatureComponent) {
+ super(map, host);
+ }
+
+ ngOnInit() {
+ this.instance = new Polygon([
+ [
+ [0, 0],
+ [1, 1],
+ [0, 1],
+ ],
+ ]);
+ super.ngOnInit();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/geom/simplegeometry.component.ts b/projects/ng-openlayers/src/lib/geom/simplegeometry.component.ts
new file mode 100644
index 0000000..909da83
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/geom/simplegeometry.component.ts
@@ -0,0 +1,22 @@
+import { Input, OnInit, Directive } from '@angular/core';
+import { FeatureComponent } from '../feature.component';
+import { MapComponent } from '../map.component';
+import SimpleGeometry from 'ol/geom/SimpleGeometry';
+
+@Directive()
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export abstract class SimpleGeometryComponent implements OnInit {
+ @Input() srid: string;
+
+ public instance: SimpleGeometry;
+ public componentType = 'simple-geometry';
+
+ protected constructor(
+ protected map: MapComponent,
+ protected host: FeatureComponent
+ ) {}
+
+ ngOnInit() {
+ this.host.instance.setGeometry(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/graticule.component.ts b/projects/ng-openlayers/src/lib/graticule.component.ts
new file mode 100644
index 0000000..36fcb3f
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/graticule.component.ts
@@ -0,0 +1,57 @@
+import { Component, Input, AfterContentInit, OnChanges, SimpleChanges, OnDestroy } from '@angular/core';
+import { Graticule } from 'ol';
+import { Stroke } from 'ol/style';
+import { MapComponent } from './map.component';
+
+@Component({
+ selector: 'aol-graticule',
+ template: '',
+})
+export class GraticuleComponent implements AfterContentInit, OnChanges, OnDestroy {
+ @Input()
+ strokeStyle: Stroke;
+ @Input()
+ showLabels: boolean;
+ @Input()
+ lonLabelPosition: number;
+ @Input()
+ latLabelPosition: number;
+
+ instance: any;
+ public componentType = 'graticule';
+
+ constructor(private map: MapComponent) {}
+
+ ngOnChanges(changes: SimpleChanges) {
+ const properties: { [index: string]: any } = {};
+
+ if (!this.instance) {
+ return;
+ }
+
+ for (const key in changes) {
+ if (changes.hasOwnProperty(key)) {
+ properties[key] = changes[key].currentValue;
+ }
+ }
+
+ if (properties) {
+ this.instance = new Graticule(properties);
+ }
+ this.instance.setMap(this.map.instance);
+ }
+
+ ngAfterContentInit(): void {
+ this.instance = new Graticule({
+ strokeStyle: this.strokeStyle,
+ showLabels: this.showLabels,
+ lonLabelPosition: this.lonLabelPosition,
+ latLabelPosition: this.latLabelPosition,
+ });
+ this.instance.setMap(this.map.instance);
+ }
+
+ ngOnDestroy(): void {
+ this.instance.setMap(null);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/default.component.ts b/projects/ng-openlayers/src/lib/interactions/default.component.ts
new file mode 100644
index 0000000..842f072
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/default.component.ts
@@ -0,0 +1,46 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { defaults, Interaction } from 'ol/interaction';
+import { Collection } from 'ol';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-interaction-default',
+ template: '',
+})
+export class DefaultInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ altShiftDragRotate: boolean;
+ @Input()
+ onFocusOnly: boolean;
+ @Input()
+ doubleClickZoom: boolean;
+ @Input()
+ keyboard: boolean;
+ @Input()
+ mouseWheelZoom: boolean;
+ @Input()
+ shiftDragZoom: boolean;
+ @Input()
+ dragPan: boolean;
+ @Input()
+ pinchRotate: boolean;
+ @Input()
+ pinchZoom: boolean;
+ @Input()
+ zoomDelta: number;
+ @Input()
+ zoomDuration: number;
+
+ instance: Collection;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = defaults(this);
+ this.instance.forEach((i) => this.map.instance.addInteraction(i));
+ }
+
+ ngOnDestroy() {
+ this.instance.forEach((i) => this.map.instance.removeInteraction(i));
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/doubleclickzoom.component.ts b/projects/ng-openlayers/src/lib/interactions/doubleclickzoom.component.ts
new file mode 100644
index 0000000..b82d6c2
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/doubleclickzoom.component.ts
@@ -0,0 +1,27 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { DoubleClickZoom } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-interaction-doubleclickzoom',
+ template: '',
+})
+export class DoubleClickZoomInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ duration: number;
+ @Input()
+ delta: number;
+
+ instance: DoubleClickZoom;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new DoubleClickZoom(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/draganddrop.component.ts b/projects/ng-openlayers/src/lib/interactions/draganddrop.component.ts
new file mode 100644
index 0000000..9ecc6f9
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/draganddrop.component.ts
@@ -0,0 +1,31 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { DragAndDrop } from 'ol/interaction';
+import FeatureFormat from 'ol/format/Feature';
+import { MapComponent } from '../map.component';
+import { ProjectionLike } from 'ol/proj';
+
+@Component({
+ selector: 'aol-interaction-draganddrop',
+ template: '',
+})
+export class DragAndDropInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ formatConstructors: FeatureFormat[];
+ @Input()
+ projection: ProjectionLike;
+ @Input()
+ target: HTMLElement;
+
+ instance: DragAndDrop;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new DragAndDrop(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/dragbox.component.ts b/projects/ng-openlayers/src/lib/interactions/dragbox.component.ts
new file mode 100644
index 0000000..18b3374
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/dragbox.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnDestroy, OnInit, Input } from '@angular/core';
+import { DragBox } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+import { Condition } from 'ol/events/condition';
+import { EndCondition } from 'ol/interaction/DragBox';
+
+@Component({
+ selector: 'aol-interaction-dragbox',
+ template: '',
+})
+export class DragBoxInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ className: string;
+ @Input()
+ condition: Condition;
+ @Input()
+ boxEndCondition: EndCondition;
+
+ instance: DragBox;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new DragBox(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/dragpan.component.ts b/projects/ng-openlayers/src/lib/interactions/dragpan.component.ts
new file mode 100644
index 0000000..857d7d5
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/dragpan.component.ts
@@ -0,0 +1,29 @@
+import { Component, OnDestroy, OnInit, Input } from '@angular/core';
+import { DragPan } from 'ol/interaction';
+import Kinetic from 'ol/Kinetic';
+import { MapComponent } from '../map.component';
+import { Condition } from 'ol/events/condition';
+
+@Component({
+ selector: 'aol-interaction-dragpan',
+ template: '',
+})
+export class DragPanInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ condition: Condition;
+ @Input()
+ kinetic: Kinetic;
+
+ instance: DragPan;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new DragPan(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/dragrotate.component.ts b/projects/ng-openlayers/src/lib/interactions/dragrotate.component.ts
new file mode 100644
index 0000000..f1324d8
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/dragrotate.component.ts
@@ -0,0 +1,28 @@
+import { Component, OnDestroy, OnInit, Input } from '@angular/core';
+import { DragRotate } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+import { Condition } from 'ol/events/condition';
+
+@Component({
+ selector: 'aol-interaction-dragrotate',
+ template: '',
+})
+export class DragRotateInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ condition: Condition;
+ @Input()
+ duration: number;
+
+ instance: DragRotate;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new DragRotate(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/dragrotateandzoom.component.ts b/projects/ng-openlayers/src/lib/interactions/dragrotateandzoom.component.ts
new file mode 100644
index 0000000..779f042
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/dragrotateandzoom.component.ts
@@ -0,0 +1,28 @@
+import { Component, OnDestroy, OnInit, Input } from '@angular/core';
+import { DragRotateAndZoom } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+import { Condition } from 'ol/events/condition';
+
+@Component({
+ selector: 'aol-interaction-dragrotateandzoom',
+ template: '',
+})
+export class DragRotateAndZoomInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ condition: Condition;
+ @Input()
+ duration: number;
+
+ instance: DragRotateAndZoom;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new DragRotateAndZoom(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/dragzoom.component.ts b/projects/ng-openlayers/src/lib/interactions/dragzoom.component.ts
new file mode 100644
index 0000000..4454f3e
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/dragzoom.component.ts
@@ -0,0 +1,32 @@
+import { Component, OnDestroy, OnInit, Input } from '@angular/core';
+import { DragZoom } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+import { Condition } from 'ol/events/condition';
+
+@Component({
+ selector: 'aol-interaction-dragzoom',
+ template: '',
+})
+export class DragZoomInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ className: string;
+ @Input()
+ condition: Condition;
+ @Input()
+ duration: number;
+ @Input()
+ out: boolean;
+
+ instance: DragZoom;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new DragZoom(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/draw.component.ts b/projects/ng-openlayers/src/lib/interactions/draw.component.ts
new file mode 100644
index 0000000..afc3fcb
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/draw.component.ts
@@ -0,0 +1,84 @@
+import { Component, Input, OnDestroy, OnInit, EventEmitter, Output } from '@angular/core';
+import { MapComponent } from '../map.component';
+import { Draw } from 'ol/interaction';
+import { Collection, Feature } from 'ol';
+import { Vector } from 'ol/source';
+import { Style } from 'ol/style';
+import { DrawEvent, GeometryFunction } from 'ol/interaction/Draw';
+import { StyleFunction } from 'ol/style/Style';
+import { Condition } from 'ol/events/condition';
+import { Type } from 'ol/geom/Geometry';
+import { ObjectEvent } from 'ol/Object';
+import BaseEvent from 'ol/events/Event';
+
+@Component({
+ selector: 'aol-interaction-draw',
+ template: '',
+})
+export class DrawInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ clickTolerance?: number;
+ @Input()
+ features?: Collection;
+ @Input()
+ source?: Vector;
+ @Input()
+ snapTolerance?: number;
+ @Input()
+ type: Type;
+ @Input()
+ maxPoints?: number;
+ @Input()
+ minPoints?: number;
+ @Input()
+ finishCondition?: Condition;
+ @Input()
+ style?: Style | Style[] | StyleFunction;
+ @Input()
+ geometryFunction?: GeometryFunction;
+ @Input()
+ geometryName?: string;
+ @Input()
+ condition?: Condition;
+ @Input()
+ freehandCondition?: Condition;
+ @Input()
+ freehand?: boolean;
+ @Input()
+ wrapX?: boolean;
+
+ @Output()
+ olChange = new EventEmitter();
+ @Output()
+ olChangeActive = new EventEmitter();
+ @Output()
+ olDrawAbort = new EventEmitter();
+ @Output()
+ drawEnd = new EventEmitter();
+ @Output()
+ drawStart = new EventEmitter();
+ @Output()
+ olError = new EventEmitter();
+ @Output()
+ propertyChange = new EventEmitter();
+
+ instance: Draw;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new Draw(this);
+ this.instance.on('change', (event: DrawEvent) => this.olChange.emit(event));
+ this.instance.on('change:active', (event: ObjectEvent) => this.olChangeActive.emit(event));
+ this.instance.on('drawabort', (event: DrawEvent) => this.olDrawAbort.emit(event));
+ this.instance.on('drawend', (event: DrawEvent) => this.drawEnd.emit(event));
+ this.instance.on('drawstart', (event: DrawEvent) => this.drawStart.emit(event));
+ this.instance.on('error', (event: BaseEvent) => this.olError.emit(event));
+ this.instance.on('propertychange', (event: ObjectEvent) => this.propertyChange.emit(event));
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/keyboardpan.component.ts b/projects/ng-openlayers/src/lib/interactions/keyboardpan.component.ts
new file mode 100644
index 0000000..3ccd5fc
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/keyboardpan.component.ts
@@ -0,0 +1,27 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { KeyboardPan } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-interaction-keyboardpan',
+ template: '',
+})
+export class KeyboardPanInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ duration: number;
+ @Input()
+ pixelDelta: number;
+
+ instance: KeyboardPan;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new KeyboardPan(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/keyboardzoom.component.ts b/projects/ng-openlayers/src/lib/interactions/keyboardzoom.component.ts
new file mode 100644
index 0000000..cab5a4d
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/keyboardzoom.component.ts
@@ -0,0 +1,27 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { KeyboardZoom } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-interaction-keyboardpan',
+ template: '',
+})
+export class KeyboardZoomInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ duration: number;
+ @Input()
+ delta: number;
+
+ instance: KeyboardZoom;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new KeyboardZoom(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/modify.component.ts b/projects/ng-openlayers/src/lib/interactions/modify.component.ts
new file mode 100644
index 0000000..71e307f
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/modify.component.ts
@@ -0,0 +1,65 @@
+import { Component, OnDestroy, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { MapComponent } from '../map.component';
+import { Modify } from 'ol/interaction';
+import { Collection, Feature } from 'ol';
+import { Style } from 'ol/style';
+import { Vector } from 'ol/source';
+import { ModifyEvent } from 'ol/interaction/Modify';
+import { StyleFunction } from 'ol/style/Style';
+import { Condition } from 'ol/events/condition';
+import { ObjectEvent } from 'ol/Object';
+import { DrawEvent } from 'ol/interaction/Draw';
+import BaseEvent from 'ol/events/Event';
+
+@Component({
+ selector: 'aol-interaction-modify',
+ template: '',
+})
+export class ModifyInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ condition?: Condition;
+ @Input()
+ deleteCondition?: Condition;
+ @Input()
+ pixelTolerance?: number;
+ @Input()
+ style?: Style | Style[] | StyleFunction;
+ @Input()
+ features: Collection;
+ @Input()
+ wrapX?: boolean;
+ @Input()
+ source?: Vector;
+
+ @Output()
+ olChange = new EventEmitter();
+ @Output()
+ olChangeActive = new EventEmitter();
+ @Output()
+ olError = new EventEmitter();
+ @Output()
+ olModifyEnd = new EventEmitter();
+ @Output()
+ olModifyStart = new EventEmitter();
+ @Output()
+ propertyChange = new EventEmitter();
+
+ instance: Modify;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new Modify(this);
+ this.instance.on('change', (event: DrawEvent) => this.olChange.emit(event));
+ this.instance.on('change:active', (event: ObjectEvent) => this.olChangeActive.emit(event));
+ this.instance.on('error', (event: BaseEvent) => this.olError.emit(event));
+ this.instance.on('modifyend', (event: ModifyEvent) => this.olModifyEnd.emit(event));
+ this.instance.on('modifystart', (event: ModifyEvent) => this.olModifyStart.emit(event));
+ this.instance.on('propertychange', (event: ObjectEvent) => this.propertyChange.emit(event));
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/mousewheelzoom.component.ts b/projects/ng-openlayers/src/lib/interactions/mousewheelzoom.component.ts
new file mode 100644
index 0000000..39b3b9c
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/mousewheelzoom.component.ts
@@ -0,0 +1,29 @@
+import { Component, Input, OnDestroy, OnInit } from '@angular/core';
+import { MouseWheelZoom } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-interaction-mousewheelzoom',
+ template: '',
+})
+export class MouseWheelZoomInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ duration: number;
+ @Input()
+ timeout: number;
+ @Input()
+ useAnchor: boolean;
+
+ instance: MouseWheelZoom;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new MouseWheelZoom(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/pinchzoom.component.ts b/projects/ng-openlayers/src/lib/interactions/pinchzoom.component.ts
new file mode 100644
index 0000000..a48eee7
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/pinchzoom.component.ts
@@ -0,0 +1,27 @@
+import { Component, OnDestroy, OnInit, Input } from '@angular/core';
+import { PinchZoom } from 'ol/interaction';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-interaction-pinchzoom',
+ template: '',
+})
+export class PinchZoomInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ duration: number;
+ @Input()
+ constrainResolution: boolean;
+
+ instance: PinchZoom;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new PinchZoom(this);
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/select.component.ts b/projects/ng-openlayers/src/lib/interactions/select.component.ts
new file mode 100644
index 0000000..c5f722f
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/select.component.ts
@@ -0,0 +1,68 @@
+import { Component, OnDestroy, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { MapComponent } from '../map.component';
+import { Select } from 'ol/interaction';
+import { Layer } from 'ol/layer';
+import { Style } from 'ol/style';
+import { Collection, Feature } from 'ol';
+import { SelectEvent, FilterFunction } from 'ol/interaction/Select';
+import { StyleFunction } from 'ol/style/Style';
+import { Condition } from 'ol/events/condition';
+import { ObjectEvent } from 'ol/Object';
+import BaseEvent from 'ol/events/Event';
+
+@Component({
+ selector: 'aol-interaction-select',
+ template: '',
+})
+export class SelectInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ addCondition?: Condition;
+ @Input()
+ condition?: Condition;
+ @Input()
+ layers?: Layer[] | ((layer: Layer) => boolean);
+ @Input()
+ style?: Style | Style[] | StyleFunction;
+ @Input()
+ removeCondition?: Condition;
+ @Input()
+ toggleCondition?: Condition;
+ @Input()
+ multi?: boolean;
+ @Input()
+ features?: Collection;
+ @Input()
+ filter?: FilterFunction;
+ @Input()
+ wrapX?: boolean;
+
+ @Output()
+ olChange = new EventEmitter();
+ @Output()
+ olChangeActive = new EventEmitter();
+ @Output()
+ olError = new EventEmitter();
+ @Output()
+ propertyChange = new EventEmitter();
+ @Output()
+ olSelect = new EventEmitter();
+
+ instance: Select;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new Select(this);
+
+ this.instance.on('change', (event: SelectEvent) => this.olChange.emit(event));
+ this.instance.on('change:active', (event: ObjectEvent) => this.olChangeActive.emit(event));
+ this.instance.on('error', (event: BaseEvent) => this.olError.emit(event));
+ this.instance.on('propertychange', (event: ObjectEvent) => this.propertyChange.emit(event));
+ this.instance.on('select', (event: SelectEvent) => this.olSelect.emit(event));
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/interactions/translate.component.ts b/projects/ng-openlayers/src/lib/interactions/translate.component.ts
new file mode 100644
index 0000000..70ee6a3
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/interactions/translate.component.ts
@@ -0,0 +1,58 @@
+import { Component, OnDestroy, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { Translate } from 'ol/interaction';
+import { Collection, Feature } from 'ol';
+import { Layer } from 'ol/layer';
+import { TranslateEvent } from 'ol/interaction/Translate';
+import { MapComponent } from '../map.component';
+import BaseEvent from 'ol/events/Event';
+import { ObjectEvent } from 'ol/Object';
+
+@Component({
+ selector: 'aol-interaction-translate',
+ template: '',
+})
+export class TranslateInteractionComponent implements OnInit, OnDestroy {
+ @Input()
+ features?: Collection;
+ @Input()
+ layers?: Layer[] | ((layer: Layer) => boolean);
+ @Input()
+ hitTolerance?: number;
+
+ @Output()
+ olChange = new EventEmitter();
+ @Output()
+ olChangeActive = new EventEmitter();
+ @Output()
+ olError = new EventEmitter();
+ @Output()
+ propertyChange = new EventEmitter();
+ @Output()
+ translateEnd = new EventEmitter();
+ @Output()
+ translateStart = new EventEmitter();
+ @Output()
+ translating = new EventEmitter();
+
+ instance: Translate;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ this.instance = new Translate(this);
+
+ this.instance.on('change', (event: BaseEvent) => this.olChange.emit(event));
+ this.instance.on('change:active', (event: ObjectEvent) => this.olChangeActive.emit(event));
+ this.instance.on('error', (event: BaseEvent) => this.olError.emit(event));
+ this.instance.on('propertychange', (event: ObjectEvent) => this.propertyChange.emit(event));
+ this.instance.on('translateend', (event: TranslateEvent) => this.translateEnd.emit(event));
+ this.instance.on('translatestart', (event: TranslateEvent) => this.translateStart.emit(event));
+ this.instance.on('translating', (event: TranslateEvent) => this.translating.emit(event));
+
+ this.map.instance.addInteraction(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.map.instance.removeInteraction(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/layers/layer.component.ts b/projects/ng-openlayers/src/lib/layers/layer.component.ts
new file mode 100644
index 0000000..7f7f0b9
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/layers/layer.component.ts
@@ -0,0 +1,68 @@
+import { OnDestroy, OnInit, OnChanges, Input, SimpleChanges, Directive } from '@angular/core';
+import Event from 'ol/events/Event';
+import { MapComponent } from '../map.component';
+import { LayerGroupComponent } from './layergroup.component';
+import { Extent } from 'ol/extent';
+
+@Directive()
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export abstract class LayerComponent implements OnInit, OnChanges, OnDestroy {
+ @Input()
+ opacity: number;
+ @Input()
+ visible: boolean;
+ @Input()
+ extent: Extent;
+ @Input()
+ zIndex: number;
+ @Input()
+ minResolution: number;
+ @Input()
+ maxResolution: number;
+
+ @Input()
+ prerender: (evt: Event) => void;
+ @Input()
+ postrender: (evt: Event) => void;
+
+ public instance: any;
+ public componentType = 'layer';
+
+ protected constructor(protected host: MapComponent | LayerGroupComponent) {}
+
+ ngOnInit() {
+ if (this.prerender !== null && this.prerender !== undefined) {
+ this.instance.on('prerender', this.prerender);
+ }
+ if (this.postrender !== null && this.postrender !== undefined) {
+ this.instance.on('postrender', this.postrender);
+ }
+ this.host.instance.getLayers().push(this.instance);
+ }
+
+ ngOnDestroy() {
+ this.host.instance.getLayers().remove(this.instance);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const properties: { [index: string]: any } = {};
+ if (!this.instance) {
+ return;
+ }
+ for (const key in changes) {
+ if (changes.hasOwnProperty(key)) {
+ properties[key] = changes[key].currentValue;
+ if (key === 'prerender') {
+ this.instance.un('prerender', changes[key].previousValue);
+ this.instance.on('prerender', changes[key].currentValue);
+ }
+ if (key === 'postrender') {
+ this.instance.un('postrender', changes[key].previousValue);
+ this.instance.on('postrender', changes[key].currentValue);
+ }
+ }
+ }
+ // console.log('changes detected in aol-layer, setting new properties: ', properties);
+ this.instance.setProperties(properties, false);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/layers/layergroup.component.ts b/projects/ng-openlayers/src/lib/layers/layergroup.component.ts
new file mode 100644
index 0000000..00b0737
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/layers/layergroup.component.ts
@@ -0,0 +1,27 @@
+import { Component, OnDestroy, OnInit, SkipSelf, Optional } from '@angular/core';
+import { Group } from 'ol/layer';
+import { LayerComponent } from './layer.component';
+import { MapComponent } from '../map.component';
+
+@Component({
+ selector: 'aol-layer-group',
+ template: ` `,
+})
+export class LayerGroupComponent extends LayerComponent implements OnInit, OnDestroy {
+ public instance: Group;
+
+ constructor(
+ map: MapComponent,
+ @SkipSelf()
+ @Optional()
+ group?: LayerGroupComponent
+ ) {
+ super(group || map);
+ }
+
+ ngOnInit() {
+ // console.log(`creating ol.layer.Group instance with:`, this);
+ this.instance = new Group(this);
+ super.ngOnInit();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/layers/layerimage.component.ts b/projects/ng-openlayers/src/lib/layers/layerimage.component.ts
new file mode 100644
index 0000000..66a6d9c
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/layers/layerimage.component.ts
@@ -0,0 +1,38 @@
+import { Component, Input, OnChanges, OnInit, Optional, SimpleChanges } from '@angular/core';
+import { Image } from 'ol/layer';
+import { MapComponent } from '../map.component';
+import { LayerComponent } from './layer.component';
+import { LayerGroupComponent } from './layergroup.component';
+import { Extent } from 'ol/extent';
+
+@Component({
+ selector: 'aol-layer-image',
+ template: ` `,
+})
+export class LayerImageComponent extends LayerComponent implements OnInit, OnChanges {
+ @Input()
+ opacity: number;
+ @Input()
+ visible: boolean;
+ @Input()
+ extent: Extent;
+ @Input()
+ minResolution: number;
+ @Input()
+ maxResolution: number;
+ @Input()
+ zIndex: number;
+
+ constructor(map: MapComponent, @Optional() group?: LayerGroupComponent) {
+ super(group || map);
+ }
+
+ ngOnInit() {
+ this.instance = new Image(this);
+ super.ngOnInit();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ super.ngOnChanges(changes);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/layers/layertile.component.ts b/projects/ng-openlayers/src/lib/layers/layertile.component.ts
new file mode 100644
index 0000000..cfffe67
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/layers/layertile.component.ts
@@ -0,0 +1,30 @@
+import { Component, OnDestroy, OnInit, Input, Optional, OnChanges, SimpleChanges } from '@angular/core';
+import { Tile } from 'ol/layer';
+import { MapComponent } from '../map.component';
+import { LayerComponent } from './layer.component';
+import { LayerGroupComponent } from './layergroup.component';
+
+@Component({
+ selector: 'aol-layer-tile',
+ template: ` `,
+})
+export class LayerTileComponent extends LayerComponent implements OnInit, OnDestroy, OnChanges {
+ @Input()
+ preload: number;
+ @Input()
+ useInterimTilesOnError: boolean;
+
+ constructor(map: MapComponent, @Optional() group?: LayerGroupComponent) {
+ super(group || map);
+ }
+
+ ngOnInit() {
+ // console.log('creating ol.layer.Tile instance with:', this);
+ this.instance = new Tile(this);
+ super.ngOnInit();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ super.ngOnChanges(changes);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/layers/layervector.component.ts b/projects/ng-openlayers/src/lib/layers/layervector.component.ts
new file mode 100644
index 0000000..0683909
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/layers/layervector.component.ts
@@ -0,0 +1,39 @@
+import { Component, OnDestroy, OnInit, Input, Optional, OnChanges, SimpleChanges } from '@angular/core';
+import { MapComponent } from '../map.component';
+import { Vector } from 'ol/layer';
+import { Style } from 'ol/style';
+import { StyleFunction } from 'ol/style/Style';
+import { LayerComponent } from './layer.component';
+import { LayerGroupComponent } from './layergroup.component';
+
+@Component({
+ selector: 'aol-layer-vector',
+ template: ` `,
+})
+export class LayerVectorComponent extends LayerComponent implements OnInit, OnDestroy, OnChanges {
+ @Input()
+ renderBuffer: number;
+
+ @Input()
+ style: Style | Style[] | StyleFunction;
+
+ @Input()
+ updateWhileAnimating: boolean;
+
+ @Input()
+ updateWhileInteracting: boolean;
+
+ constructor(map: MapComponent, @Optional() group?: LayerGroupComponent) {
+ super(group || map);
+ }
+
+ ngOnInit() {
+ // console.log('creating ol.layer.Vector instance with:', this);
+ this.instance = new Vector(this);
+ super.ngOnInit();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ super.ngOnChanges(changes);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/layers/layervectortile.component.ts b/projects/ng-openlayers/src/lib/layers/layervectortile.component.ts
new file mode 100644
index 0000000..770aa6a
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/layers/layervectortile.component.ts
@@ -0,0 +1,44 @@
+import { Component, OnInit, Input, Optional, SimpleChanges, OnChanges } from '@angular/core';
+import { VectorTile } from 'ol/layer';
+import { Feature } from 'ol';
+import { Style } from 'ol/style';
+import { MapComponent } from '../map.component';
+import { LayerComponent } from './layer.component';
+import { LayerGroupComponent } from './layergroup.component';
+import { StyleFunction } from 'ol/style/Style';
+
+@Component({
+ selector: 'aol-layer-vectortile',
+ template: ` `,
+})
+export class LayerVectorTileComponent extends LayerComponent implements OnInit, OnChanges {
+ @Input()
+ renderBuffer: number;
+ @Input()
+ renderMode: any | string;
+ /* not marked as optional in the typings */
+ @Input()
+ renderOrder: (feature1: Feature, feature2: Feature) => number;
+ @Input()
+ style: Style | Style[] | StyleFunction;
+ @Input()
+ updateWhileAnimating: boolean;
+ @Input()
+ updateWhileInteracting: boolean;
+ @Input()
+ visible: boolean;
+
+ constructor(map: MapComponent, @Optional() group?: LayerGroupComponent) {
+ super(group || map);
+ }
+
+ ngOnInit() {
+ // console.log('creating ol.layer.VectorTile instance with:', this);
+ this.instance = new VectorTile(this);
+ super.ngOnInit();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ super.ngOnChanges(changes);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/map.component.ts b/projects/ng-openlayers/src/lib/map.component.ts
new file mode 100644
index 0000000..f6b4e9e
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/map.component.ts
@@ -0,0 +1,142 @@
+import {
+ Component,
+ OnInit,
+ ElementRef,
+ Input,
+ Output,
+ EventEmitter,
+ AfterViewInit,
+ SimpleChanges,
+ OnChanges,
+} from '@angular/core';
+import { Map } from 'ol';
+import MapBrowserEvent from 'ol/MapBrowserEvent';
+import MapEvent from 'ol/MapEvent';
+import { ObjectEvent } from 'ol/Object';
+import RenderEvent from 'ol/render/Event';
+import { Control } from 'ol/control';
+import { Interaction } from 'ol/interaction';
+import { DrawEvent } from 'ol/interaction/Draw';
+import BaseEvent from 'ol/events/Event';
+
+@Component({
+ selector: 'aol-map',
+ template: `
+
+
+ `,
+})
+export class MapComponent implements OnInit, AfterViewInit, OnChanges {
+ @Input()
+ width = '100%';
+ @Input()
+ height = '100%';
+ @Input()
+ pixelRatio: number;
+ @Input()
+ keyboardEventTarget: HTMLElement | string;
+ @Input()
+ loadTilesWhileAnimating: boolean;
+ @Input()
+ loadTilesWhileInteracting: boolean;
+ @Input()
+ logo: string | boolean;
+ @Input()
+ renderer: 'canvas' | 'webgl';
+
+ @Output()
+ olChange = new EventEmitter();
+ @Output()
+ olChangeLayerGroup = new EventEmitter();
+ @Output()
+ olChangeSize = new EventEmitter();
+ @Output()
+ olChangeTarget = new EventEmitter();
+ @Output()
+ olChangeView = new EventEmitter();
+ @Output()
+ olClick = new EventEmitter>();
+ @Output()
+ dblClick = new EventEmitter>();
+ @Output()
+ olError = new EventEmitter();
+ @Output()
+ loadEnd = new EventEmitter();
+ @Output()
+ loadStart = new EventEmitter();
+ @Output()
+ moveEnd = new EventEmitter();
+ @Output()
+ moveStart = new EventEmitter();
+ @Output()
+ pointerDrag = new EventEmitter>();
+ @Output()
+ pointerMove = new EventEmitter>();
+ @Output()
+ olPostCompose = new EventEmitter();
+ @Output()
+ olPostRender = new EventEmitter();
+ @Output()
+ olPreCompose = new EventEmitter();
+ @Output()
+ olPropertyChange = new EventEmitter();
+ @Output()
+ postRender = new EventEmitter();
+ @Output()
+ propertyChange = new EventEmitter();
+ @Output()
+ singleClick = new EventEmitter>();
+
+ public instance: Map;
+ public componentType = 'map';
+
+ // we pass empty arrays to not get default controls/interactions because we have our own directives
+ controls: Control[] = [];
+ interactions: Interaction[] = [];
+
+ constructor(private host: ElementRef) {}
+
+ ngOnInit() {
+ // console.log('creating ol.Map instance with:', this);
+ this.instance = new Map(this);
+ this.instance.setTarget(this.host.nativeElement.firstElementChild);
+ this.instance.on('change', (event: DrawEvent) => this.olChange.emit(event));
+ this.instance.on('change:layergroup', (event: ObjectEvent) => this.olChangeLayerGroup.emit(event));
+ this.instance.on('change:size', (event: ObjectEvent) => this.olChangeSize.emit(event));
+ this.instance.on('change:target', (event: ObjectEvent) => this.olChangeTarget.emit(event));
+ this.instance.on('change:view', (event: ObjectEvent) => this.olChangeView.emit(event));
+ this.instance.on('click', (event: MapBrowserEvent) => this.olClick.emit(event));
+ this.instance.on('dblclick', (event: MapBrowserEvent) => this.dblClick.emit(event));
+ this.instance.on('error', (event: BaseEvent) => this.olError.emit(event));
+ this.instance.on('loadend', (event: MapEvent) => this.loadEnd.emit(event));
+ this.instance.on('loadstart', (event: MapEvent) => this.loadStart.emit(event));
+ this.instance.on('moveend', (event: MapEvent) => this.moveEnd.emit(event));
+ this.instance.on('movestart', (event: MapEvent) => this.moveStart.emit(event));
+ this.instance.on('pointerdrag', (event: MapBrowserEvent) => this.pointerDrag.emit(event));
+ this.instance.on('pointermove', (event: MapBrowserEvent) => this.pointerMove.emit(event));
+ this.instance.on('postcompose', (event: RenderEvent) => this.olPostCompose.emit(event));
+ this.instance.on('postrender', (event: RenderEvent) => this.olPostRender.emit(event));
+ this.instance.on('postrender', (event: MapEvent) => this.postRender.emit(event));
+ this.instance.on('precompose', (event: RenderEvent) => this.olPreCompose.emit(event));
+ this.instance.on('propertychange', (event: ObjectEvent) => this.olPropertyChange.emit(event));
+ this.instance.on('singleclick', (event: MapBrowserEvent) => this.singleClick.emit(event));
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const properties: { [index: string]: any } = {};
+ if (!this.instance) {
+ return;
+ }
+ for (const key in changes) {
+ if (changes.hasOwnProperty(key)) {
+ properties[key] = changes[key].currentValue;
+ }
+ }
+ // console.log('changes detected in aol-map, setting new properties: ', properties);
+ this.instance.setProperties(properties, false);
+ }
+
+ ngAfterViewInit() {
+ this.instance.updateSize();
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/overlay.component.ts b/projects/ng-openlayers/src/lib/overlay.component.ts
new file mode 100644
index 0000000..55a98d2
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/overlay.component.ts
@@ -0,0 +1,50 @@
+import { Component, ContentChild, Input, OnDestroy, OnInit } from '@angular/core';
+import { MapComponent } from './map.component';
+import Overlay, { PanOptions, Positioning } from 'ol/Overlay';
+import { ContentComponent } from './content.component';
+
+@Component({
+ selector: 'aol-overlay',
+ template: '',
+})
+export class OverlayComponent implements OnInit, OnDestroy {
+ @ContentChild(ContentComponent, { static: true })
+ content: ContentComponent;
+
+ @Input()
+ id: number | string;
+ @Input()
+ offset: number[];
+ @Input()
+ positioning: Positioning;
+ @Input()
+ stopEvent: boolean;
+ @Input()
+ insertFirst: boolean;
+ @Input()
+ autoPan: boolean;
+ @Input()
+ autoPanAnimation: PanOptions;
+ @Input()
+ autoPanMargin: number;
+
+ componentType = 'overlay';
+ instance: Overlay;
+ element: HTMLElement;
+
+ constructor(private map: MapComponent) {}
+
+ ngOnInit() {
+ if (this.content) {
+ this.element = this.content.elementRef.nativeElement;
+ this.instance = new Overlay(this);
+ this.map.instance.addOverlay(this.instance);
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.instance) {
+ this.map.instance.removeOverlay(this.instance);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/bingmaps.component.ts b/projects/ng-openlayers/src/lib/sources/bingmaps.component.ts
new file mode 100644
index 0000000..fd50eb9
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/bingmaps.component.ts
@@ -0,0 +1,42 @@
+import { Component, Host, Input, OnInit, forwardRef } from '@angular/core';
+import { BingMaps } from 'ol/source';
+import { SourceComponent } from './source.component';
+import { LayerTileComponent } from '../layers/layertile.component';
+import { LoadFunction } from 'ol/Tile';
+
+@Component({
+ selector: 'aol-source-bingmaps',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceBingmapsComponent) }],
+})
+export class SourceBingmapsComponent extends SourceComponent implements OnInit {
+ @Input()
+ cacheSize: number;
+ @Input()
+ hidpi: boolean;
+ @Input()
+ culture: string;
+ @Input()
+ key: string;
+ @Input()
+ imagerySet: 'Road' | 'Aerial' | 'AerialWithLabels' | 'collinsBart' | 'ordnanceSurvey' = 'Aerial';
+ @Input()
+ maxZoom: number;
+ @Input()
+ reprojectionErrorThreshold: number;
+ @Input()
+ tileLoadFunction: LoadFunction;
+ @Input()
+ wrapX: boolean;
+
+ instance: BingMaps;
+
+ constructor(@Host() layer: LayerTileComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.instance = new BingMaps(this);
+ this.host.instance.setSource(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/cluster.component.ts b/projects/ng-openlayers/src/lib/sources/cluster.component.ts
new file mode 100644
index 0000000..4d041f9
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/cluster.component.ts
@@ -0,0 +1,54 @@
+import {
+ AfterContentInit,
+ Component,
+ ContentChild,
+ forwardRef,
+ Host,
+ Input,
+ OnChanges,
+ SimpleChanges,
+} from '@angular/core';
+import { Feature } from 'ol';
+import { Point } from 'ol/geom';
+import { Cluster, Vector } from 'ol/source';
+
+import { LayerVectorComponent } from '../layers/layervector.component';
+import { SourceComponent } from './source.component';
+import { SourceVectorComponent } from './vector.component';
+
+@Component({
+ selector: 'aol-source-cluster',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceClusterComponent) }],
+})
+export class SourceClusterComponent extends SourceComponent implements AfterContentInit, OnChanges {
+ @Input()
+ distance: number;
+ @Input()
+ geometryFunction?: (feature: Feature) => Point;
+ @Input()
+ wrapX?: boolean;
+
+ @ContentChild(SourceVectorComponent, { static: false })
+ sourceVectorComponent: SourceVectorComponent;
+
+ instance: Cluster;
+ source: Vector;
+
+ constructor(@Host() layer: LayerVectorComponent) {
+ super(layer);
+ }
+
+ ngAfterContentInit() {
+ this.source = this.sourceVectorComponent.instance;
+
+ this.instance = new Cluster(this);
+ this.host.instance.setSource(this.instance);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.instance && changes.hasOwnProperty('distance')) {
+ this.instance.setDistance(this.distance);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/geojson.component.ts b/projects/ng-openlayers/src/lib/sources/geojson.component.ts
new file mode 100644
index 0000000..834899c
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/geojson.component.ts
@@ -0,0 +1,36 @@
+import { Component, Host, Input, OnInit, forwardRef } from '@angular/core';
+import { LayerVectorComponent } from '../layers/layervector.component';
+import { SourceComponent } from './source.component';
+import FeatureFormat from 'ol/format/Feature';
+import { Vector } from 'ol/source';
+import { GeoJSON } from 'ol/format';
+import { ProjectionLike } from 'ol/proj';
+
+@Component({
+ selector: 'aol-source-geojson',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceGeoJSONComponent) }],
+})
+export class SourceGeoJSONComponent extends SourceComponent implements OnInit {
+ @Input()
+ defaultDataProjection: ProjectionLike;
+ @Input()
+ featureProjection: ProjectionLike;
+ @Input()
+ geometryName: string;
+ @Input()
+ url: string;
+
+ instance: Vector;
+ format: FeatureFormat;
+
+ constructor(@Host() layer: LayerVectorComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.format = new GeoJSON(this);
+ this.instance = new Vector(this);
+ this.host.instance.setSource(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/imagearcgisrest.component.ts b/projects/ng-openlayers/src/lib/sources/imagearcgisrest.component.ts
new file mode 100644
index 0000000..1da725b
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/imagearcgisrest.component.ts
@@ -0,0 +1,62 @@
+import {
+ Component,
+ EventEmitter,
+ forwardRef,
+ Host,
+ Input,
+ OnChanges,
+ OnInit,
+ Output,
+ SimpleChanges,
+} from '@angular/core';
+import ImageArcGISRest from 'ol/source/ImageArcGISRest';
+import { LayerImageComponent } from '../layers/layerimage.component';
+import { SourceComponent } from './source.component';
+import { ProjectionLike } from 'ol/proj';
+import { AttributionLike } from 'ol/source/Source';
+import { LoadFunction } from 'ol/Image';
+import { ImageSourceEvent } from 'ol/source/Image';
+
+@Component({
+ selector: 'aol-source-imagearcgisrest',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceImageArcGISRestComponent) }],
+})
+export class SourceImageArcGISRestComponent extends SourceComponent implements OnInit, OnChanges {
+ @Input() projection: ProjectionLike | string;
+ @Input() url: string;
+ @Input() attributions: AttributionLike;
+ @Input() crossOrigin?: string;
+ @Input() imageLoadFunction?: LoadFunction;
+ @Input() params?: { [k: string]: any };
+ @Input() ratio = 1.5;
+ @Input() resolutions?: number[];
+ @Input() wrapX?: boolean;
+
+ @Output()
+ imageLoadStart = new EventEmitter();
+ @Output()
+ imageLoadEnd = new EventEmitter();
+ @Output()
+ imageLoadError = new EventEmitter();
+
+ instance: ImageArcGISRest;
+
+ constructor(@Host() layer: LayerImageComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.instance = new ImageArcGISRest(this);
+ this.host.instance.setSource(this.instance);
+ this.instance.on('imageloadstart', (event: ImageSourceEvent) => this.imageLoadStart.emit(event));
+ this.instance.on('imageloadend', (event: ImageSourceEvent) => this.imageLoadEnd.emit(event));
+ this.instance.on('imageloaderror', (event: ImageSourceEvent) => this.imageLoadError.emit(event));
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.instance && changes.hasOwnProperty('params')) {
+ this.instance.updateParams(this.params);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/imagestatic.component.ts b/projects/ng-openlayers/src/lib/sources/imagestatic.component.ts
new file mode 100644
index 0000000..c3ab7e3
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/imagestatic.component.ts
@@ -0,0 +1,88 @@
+import {
+ Component,
+ Host,
+ Input,
+ forwardRef,
+ Output,
+ EventEmitter,
+ OnChanges,
+ SimpleChanges,
+ OnInit,
+} from '@angular/core';
+import { ImageStatic } from 'ol/source';
+import { SourceComponent } from './source.component';
+import { LayerImageComponent } from '../layers/layerimage.component';
+import { ProjectionLike } from 'ol/proj';
+import { Extent } from 'ol/extent';
+import { AttributionLike } from 'ol/source/Source';
+import { LoadFunction } from 'ol/Image';
+import { Size } from 'ol/size';
+import { ImageSourceEvent } from 'ol/source/Image';
+
+@Component({
+ selector: 'aol-source-imagestatic',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceImageStaticComponent) }],
+})
+export class SourceImageStaticComponent extends SourceComponent implements OnInit, OnChanges {
+ @Input()
+ projection: ProjectionLike | string;
+ @Input()
+ imageExtent: Extent;
+ @Input()
+ url: string;
+ @Input()
+ attributions: AttributionLike;
+ @Input()
+ crossOrigin?: string;
+ @Input()
+ imageLoadFunction?: LoadFunction;
+ @Input()
+ imageSize?: Size;
+
+ @Output()
+ imageLoadStart = new EventEmitter();
+ @Output()
+ imageLoadEnd = new EventEmitter();
+ @Output()
+ imageLoadError = new EventEmitter();
+
+ instance: ImageStatic;
+
+ constructor(@Host() layer: LayerImageComponent) {
+ super(layer);
+ }
+
+ setLayerSource(): void {
+ this.instance = new ImageStatic(this);
+ this.host.instance.setSource(this.instance);
+ this.instance.on('imageloadstart', (event: ImageSourceEvent) => this.imageLoadStart.emit(event));
+ this.instance.on('imageloadend', (event: ImageSourceEvent) => this.imageLoadEnd.emit(event));
+ this.instance.on('imageloaderror', (event: ImageSourceEvent) => this.imageLoadError.emit(event));
+ }
+
+ ngOnInit() {
+ this.setLayerSource();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const properties: { [index: string]: any } = {};
+ if (!this.instance) {
+ return;
+ }
+ for (const key in changes) {
+ if (changes.hasOwnProperty(key)) {
+ switch (key) {
+ case 'url':
+ this.url = changes[key].currentValue;
+ this.setLayerSource();
+ break;
+ default:
+ break;
+ }
+ properties[key] = changes[key].currentValue;
+ }
+ }
+ this.instance.setProperties(properties, false);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/imagewms.component.ts b/projects/ng-openlayers/src/lib/sources/imagewms.component.ts
new file mode 100644
index 0000000..3f2a3f3
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/imagewms.component.ts
@@ -0,0 +1,74 @@
+import {
+ Component,
+ Host,
+ Input,
+ OnChanges,
+ OnInit,
+ forwardRef,
+ SimpleChanges,
+ Output,
+ EventEmitter,
+} from '@angular/core';
+import { ImageWMS } from 'ol/source';
+import { LayerImageComponent } from '../layers/layerimage.component';
+import { SourceComponent } from './source.component';
+import { ProjectionLike } from 'ol/proj';
+import { AttributionLike } from 'ol/source/Source';
+import { LoadFunction } from 'ol/Image';
+import { ImageSourceEvent } from 'ol/source/Image';
+import { ServerType } from 'ol/source/wms';
+
+@Component({
+ selector: 'aol-source-imagewms',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceImageWMSComponent) }],
+})
+export class SourceImageWMSComponent extends SourceComponent implements OnChanges, OnInit {
+ @Input()
+ attributions: AttributionLike;
+ @Input()
+ crossOrigin: string;
+ @Input()
+ hidpi: boolean;
+ @Input()
+ serverType: ServerType;
+ @Input()
+ imageLoadFunction?: LoadFunction;
+ @Input()
+ params: { [key: string]: any };
+ @Input()
+ projection: ProjectionLike | string;
+ @Input()
+ ratio: number;
+ @Input()
+ resolutions: Array;
+ @Input()
+ url: string;
+
+ @Output()
+ imageLoadStart = new EventEmitter();
+ @Output()
+ imageLoadEnd = new EventEmitter();
+ @Output()
+ imageLoadError = new EventEmitter();
+
+ instance: ImageWMS;
+
+ constructor(@Host() layer: LayerImageComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.instance = new ImageWMS(this);
+ this.host.instance.setSource(this.instance);
+ this.instance.on('imageloadstart', (event: ImageSourceEvent) => this.imageLoadStart.emit(event));
+ this.instance.on('imageloadend', (event: ImageSourceEvent) => this.imageLoadEnd.emit(event));
+ this.instance.on('imageloaderror', (event: ImageSourceEvent) => this.imageLoadError.emit(event));
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.instance && changes.hasOwnProperty('params')) {
+ this.instance.updateParams(this.params);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/osm.component.ts b/projects/ng-openlayers/src/lib/sources/osm.component.ts
new file mode 100644
index 0000000..d768643
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/osm.component.ts
@@ -0,0 +1,62 @@
+import { AfterContentInit, Component, EventEmitter, forwardRef, Host, Input, Optional, Output } from '@angular/core';
+import { OSM } from 'ol/source';
+import { AttributionLike } from 'ol/source/Source';
+import { TileSourceEvent } from 'ol/source/Tile';
+import { LoadFunction } from 'ol/Tile';
+import { LayerTileComponent } from '../layers/layertile.component';
+import { SourceComponent } from './source.component';
+import { SourceXYZComponent } from './xyz.component';
+
+@Component({
+ selector: 'aol-source-osm',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceOsmComponent) }],
+})
+export class SourceOsmComponent extends SourceXYZComponent implements AfterContentInit {
+ @Input()
+ attributions: AttributionLike;
+ @Input()
+ cacheSize: number;
+ @Input()
+ crossOrigin: string;
+ @Input()
+ maxZoom: number;
+ @Input()
+ opaque: boolean;
+ @Input()
+ reprojectionErrorThreshold: number;
+ @Input()
+ tileLoadFunction: LoadFunction;
+ @Input()
+ url: string;
+ @Input()
+ wrapX: boolean;
+
+ @Output()
+ tileLoadStart = new EventEmitter();
+ @Output()
+ tileLoadEnd = new EventEmitter();
+ @Output()
+ tileLoadError = new EventEmitter();
+
+ instance: OSM;
+
+ constructor(
+ @Optional()
+ @Host()
+ protected layer?: LayerTileComponent
+ ) {
+ super(layer);
+ }
+
+ ngAfterContentInit() {
+ if (this.tileGridXYZ) {
+ this.tileGrid = this.tileGridXYZ.instance;
+ }
+ this.instance = new OSM(this);
+ this.instance.on('tileloadstart', (event: TileSourceEvent) => this.tileLoadStart.emit(event));
+ this.instance.on('tileloadend', (event: TileSourceEvent) => this.tileLoadEnd.emit(event));
+ this.instance.on('tileloaderror', (event: TileSourceEvent) => this.tileLoadError.emit(event));
+ this.register(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/raster.component.ts b/projects/ng-openlayers/src/lib/sources/raster.component.ts
new file mode 100644
index 0000000..b65073b
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/raster.component.ts
@@ -0,0 +1,68 @@
+import {
+ AfterContentInit,
+ Component,
+ ContentChild,
+ EventEmitter,
+ forwardRef,
+ Host,
+ Input,
+ Output,
+} from '@angular/core';
+import { Raster, Source } from 'ol/source';
+import { Operation, RasterSourceEvent } from 'ol/source/Raster';
+
+import { LayerImageComponent } from '../layers/layerimage.component';
+import { SourceComponent } from './source.component';
+
+@Component({
+ selector: 'aol-source-raster',
+ template: ` `,
+ providers: [
+ {
+ provide: SourceComponent,
+ useExisting: forwardRef(() => SourceRasterComponent),
+ },
+ ],
+})
+export class SourceRasterComponent extends SourceComponent implements AfterContentInit {
+ @Input()
+ operation?: Operation;
+ @Input()
+ threads?: number;
+ @Input()
+ lib?: any;
+ @Input()
+ operationType?: 'pixel' | 'image';
+
+ @Output()
+ beforeOperations = new EventEmitter();
+ @Output()
+ afterOperations = new EventEmitter();
+
+ instance: Raster;
+ sources: Source[] = [];
+
+ @ContentChild(SourceComponent, { static: false })
+ set source(sourceComponent: SourceComponent) {
+ this.sources = [sourceComponent.instance];
+ if (this.instance) {
+ // Openlayer doesn't handle sources update. Just recreate Raster instance.
+ this.init();
+ }
+ }
+
+ constructor(@Host() layer: LayerImageComponent) {
+ super(layer);
+ }
+
+ ngAfterContentInit() {
+ this.init();
+ }
+
+ init() {
+ this.instance = new Raster(this);
+ this.instance.on('beforeoperations', (event: RasterSourceEvent) => this.beforeOperations.emit(event));
+ this.instance.on('afteroperations', (event: RasterSourceEvent) => this.afterOperations.emit(event));
+ this.register(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/source.component.ts b/projects/ng-openlayers/src/lib/sources/source.component.ts
new file mode 100644
index 0000000..22e7201
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/source.component.ts
@@ -0,0 +1,28 @@
+import { Input, OnDestroy, Directive } from '@angular/core';
+import Source from 'ol/source/Source';
+
+import { LayerComponent } from '../layers/layer.component';
+
+@Directive()
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export abstract class SourceComponent implements OnDestroy {
+ @Input()
+ attributions: any;
+
+ public instance: Source;
+ public componentType = 'source';
+
+ protected constructor(protected host: LayerComponent) {}
+
+ ngOnDestroy() {
+ if (this.host && this.host.instance) {
+ this.host.instance.setSource(null);
+ }
+ }
+
+ protected register(s: Source) {
+ if (this.host) {
+ this.host.instance.setSource(s);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/tilejson.component.ts b/projects/ng-openlayers/src/lib/sources/tilejson.component.ts
new file mode 100644
index 0000000..f98a0ed
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/tilejson.component.ts
@@ -0,0 +1,25 @@
+import { Component, Host, Input, OnInit, forwardRef } from '@angular/core';
+import { TileJSON } from 'ol/source';
+import { LayerTileComponent } from '../layers/layertile.component';
+import { SourceComponent } from './source.component';
+
+@Component({
+ selector: 'aol-source-tilejson',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceTileJSONComponent) }],
+})
+export class SourceTileJSONComponent extends SourceComponent implements OnInit {
+ @Input()
+ url: string;
+
+ instance: TileJSON;
+
+ constructor(@Host() layer: LayerTileComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.instance = new TileJSON(this);
+ this.host.instance.setSource(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/tilewms.component.ts b/projects/ng-openlayers/src/lib/sources/tilewms.component.ts
new file mode 100644
index 0000000..9068ed4
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/tilewms.component.ts
@@ -0,0 +1,58 @@
+import { Component, Host, Input, OnChanges, OnInit, forwardRef, SimpleChanges } from '@angular/core';
+import { LayerTileComponent } from '../layers/layertile.component';
+import { SourceComponent } from './source.component';
+import { TileWMS } from 'ol/source';
+import TileGrid from 'ol/tilegrid/TileGrid';
+import { LoadFunction } from 'ol/Tile';
+import { ServerType } from 'ol/source/wms';
+
+@Component({
+ selector: 'aol-source-tilewms',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceTileWMSComponent) }],
+})
+export class SourceTileWMSComponent extends SourceComponent implements OnChanges, OnInit {
+ @Input()
+ cacheSize: number;
+ @Input()
+ crossOrigin: string;
+ @Input()
+ gutter: number;
+ @Input()
+ hidpi: boolean;
+ @Input()
+ params: { [key: string]: any };
+ @Input()
+ projection: string;
+ @Input()
+ reprojectionErrorThreshold: number;
+ @Input()
+ serverType: ServerType;
+ @Input()
+ tileGrid: TileGrid;
+ @Input()
+ tileLoadFunction: LoadFunction;
+ @Input()
+ url: string;
+ @Input()
+ urls: string[];
+ @Input()
+ wrapX: boolean;
+
+ instance: TileWMS;
+
+ constructor(@Host() layer: LayerTileComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.instance = new TileWMS(this);
+ this.host.instance.setSource(this.instance);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (this.instance && changes.hasOwnProperty('params')) {
+ this.instance.updateParams(this.params);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/tilewmts.component.ts b/projects/ng-openlayers/src/lib/sources/tilewmts.component.ts
new file mode 100644
index 0000000..d352c9d
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/tilewmts.component.ts
@@ -0,0 +1,117 @@
+import {
+ Component,
+ Host,
+ Input,
+ forwardRef,
+ AfterContentInit,
+ ContentChild,
+ SimpleChanges,
+ OnChanges,
+ Output,
+ EventEmitter,
+} from '@angular/core';
+import { LayerTileComponent } from '../layers/layertile.component';
+import { SourceComponent } from './source.component';
+import { TileGridWMTSComponent } from '../tilegridwmts.component';
+import { WMTS as SourceWMTS } from 'ol/source';
+import WMTS from 'ol/tilegrid/WMTS';
+import { ProjectionLike } from 'ol/proj';
+import { LoadFunction } from 'ol/Tile';
+import { TileSourceEvent } from 'ol/source/Tile';
+import { RequestEncoding } from 'ol/source/WMTS';
+
+@Component({
+ selector: 'aol-source-tilewmts',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceTileWMTSComponent) }],
+})
+export class SourceTileWMTSComponent extends SourceComponent implements AfterContentInit, OnChanges {
+ @Input()
+ cacheSize?: number;
+ @Input()
+ crossOrigin?: string;
+ @Input()
+ tileGrid: WMTS;
+ @Input()
+ projection: ProjectionLike;
+ @Input()
+ reprojectionErrorThreshold?: number;
+ @Input()
+ requestEncoding?: RequestEncoding | undefined;
+ @Input()
+ layer: string;
+ @Input()
+ style: string;
+ @Input()
+ tileClass?: any;
+ @Input()
+ tilePixelRatio?: number;
+ @Input()
+ version?: string;
+ @Input()
+ format?: string;
+ @Input()
+ matrixSet: string;
+ @Input()
+ dimensions?: any;
+ @Input()
+ url?: string;
+ @Input()
+ tileLoadFunction?: LoadFunction;
+ @Input()
+ urls?: string[];
+ @Input()
+ wrapX?: boolean;
+
+ @Output()
+ tileLoadStart = new EventEmitter();
+ @Output()
+ tileLoadEnd = new EventEmitter();
+ @Output()
+ tileLoadError = new EventEmitter();
+
+ @ContentChild(TileGridWMTSComponent, { static: false })
+ tileGridWMTS: TileGridWMTSComponent;
+
+ instance: SourceWMTS;
+
+ constructor(@Host() layer: LayerTileComponent) {
+ super(layer);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const properties: { [index: string]: any } = {};
+ if (!this.instance) {
+ return;
+ }
+ for (const key in changes) {
+ if (changes.hasOwnProperty(key)) {
+ switch (key) {
+ case 'url':
+ this.url = changes[key].currentValue;
+ this.setLayerSource();
+ break;
+ default:
+ break;
+ }
+ properties[key] = changes[key].currentValue;
+ }
+ }
+ this.instance.setProperties(properties, false);
+ }
+
+ setLayerSource(): void {
+ this.instance = new SourceWMTS(this);
+ this.instance.on('tileloadstart', (event: TileSourceEvent) => this.tileLoadStart.emit(event));
+ this.instance.on('tileloadend', (event: TileSourceEvent) => this.tileLoadEnd.emit(event));
+ this.instance.on('tileloaderror', (event: TileSourceEvent) => this.tileLoadError.emit(event));
+ this.host.instance.setSource(this.instance);
+ }
+
+ ngAfterContentInit(): void {
+ if (this.tileGridWMTS) {
+ this.tileGrid = this.tileGridWMTS.instance;
+ this.setLayerSource();
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/utfgrid.component.ts b/projects/ng-openlayers/src/lib/sources/utfgrid.component.ts
new file mode 100644
index 0000000..ba79294
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/utfgrid.component.ts
@@ -0,0 +1,26 @@
+import { Component, Host, Input, OnInit, forwardRef } from '@angular/core';
+import { SourceComponent } from './source.component';
+import { LayerTileComponent } from '../layers/layertile.component';
+import { UTFGrid } from 'ol/source';
+import { Config } from 'ol/source/TileJSON';
+
+@Component({
+ selector: 'aol-source-utfgrid',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceUTFGridComponent) }],
+})
+export class SourceUTFGridComponent extends SourceComponent implements OnInit {
+ @Input() tileJSON: Config;
+ @Input() url: string;
+
+ instance: UTFGrid;
+
+ constructor(@Host() layer: LayerTileComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.instance = new UTFGrid(this);
+ this.host.instance.setSource(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/vector.component.ts b/projects/ng-openlayers/src/lib/sources/vector.component.ts
new file mode 100644
index 0000000..6a58c90
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/vector.component.ts
@@ -0,0 +1,37 @@
+import { Component, Host, Input, OnInit, forwardRef } from '@angular/core';
+import { Vector } from 'ol/source';
+import Feature from 'ol/format/Feature';
+import { LayerVectorComponent } from '../layers/layervector.component';
+import { SourceComponent } from './source.component';
+import { LoadingStrategy } from 'ol/source/Vector';
+
+@Component({
+ selector: 'aol-source-vector',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceVectorComponent) }],
+})
+export class SourceVectorComponent extends SourceComponent implements OnInit {
+ @Input()
+ overlaps: boolean;
+ @Input()
+ useSpatialIndex: boolean;
+ @Input()
+ wrapX: boolean;
+ @Input()
+ url: string;
+ @Input()
+ format: Feature;
+ @Input()
+ strategy: LoadingStrategy;
+
+ instance: Vector;
+
+ constructor(@Host() layer: LayerVectorComponent) {
+ super(layer);
+ }
+
+ ngOnInit() {
+ this.instance = new Vector(this);
+ this.host.instance.setSource(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/vectortile.component.ts b/projects/ng-openlayers/src/lib/sources/vectortile.component.ts
new file mode 100644
index 0000000..e3ce85e
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/vectortile.component.ts
@@ -0,0 +1,56 @@
+import { Component, Host, Input, forwardRef, ContentChild, AfterContentInit } from '@angular/core';
+import { VectorTile } from 'ol/source';
+import Feature from 'ol/format/Feature';
+import TileGrid from 'ol/tilegrid/TileGrid';
+import { LayerVectorTileComponent } from '../layers/layervectortile.component';
+import { FormatComponent } from '../formats/format.component';
+import { TileGridComponent } from '../tilegrid.component';
+import { SourceComponent } from './source.component';
+import { ProjectionLike } from 'ol/proj';
+import { UrlFunction } from 'ol/Tile';
+
+@Component({
+ selector: 'aol-source-vectortile',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceVectorTileComponent) }],
+})
+export class SourceVectorTileComponent extends SourceComponent implements AfterContentInit {
+ @Input()
+ cacheSize: number;
+ @Input()
+ overlaps: boolean;
+ @Input()
+ projection: ProjectionLike;
+ @Input()
+ tilePixelRatio: number;
+ @Input()
+ tileUrlFunction: UrlFunction;
+ @Input()
+ url: string;
+ @Input()
+ urls: string[];
+ @Input()
+ wrapX: boolean;
+
+ @ContentChild(FormatComponent, { static: false })
+ formatComponent: FormatComponent;
+ @ContentChild(TileGridComponent, { static: false })
+ tileGridComponent: TileGridComponent;
+
+ public instance: VectorTile;
+ format: Feature;
+ tileGrid: TileGrid;
+
+ constructor(@Host() layer: LayerVectorTileComponent) {
+ super(layer);
+ }
+
+ /* need the children to construct the OL3 object */
+ ngAfterContentInit() {
+ this.format = this.formatComponent.instance;
+ this.tileGrid = this.tileGridComponent.instance;
+ // console.log('creating ol.source.VectorTile instance with:', this);
+ this.instance = new VectorTile(this);
+ this.host.instance.setSource(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/sources/xyz.component.ts b/projects/ng-openlayers/src/lib/sources/xyz.component.ts
new file mode 100644
index 0000000..a6478ff
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/sources/xyz.component.ts
@@ -0,0 +1,115 @@
+import {
+ AfterContentInit,
+ Component,
+ ContentChild,
+ EventEmitter,
+ forwardRef,
+ Host,
+ Input,
+ OnChanges,
+ Optional,
+ Output,
+ SimpleChanges,
+} from '@angular/core';
+import { Size } from 'ol/size';
+import { XYZ } from 'ol/source';
+import { TileSourceEvent } from 'ol/source/Tile';
+import { LoadFunction, UrlFunction } from 'ol/Tile';
+import TileGrid from 'ol/tilegrid/TileGrid';
+
+import { LayerTileComponent } from '../layers/layertile.component';
+import { TileGridComponent } from '../tilegrid.component';
+import { SourceComponent } from './source.component';
+
+@Component({
+ selector: 'aol-source-xyz',
+ template: ` `,
+ providers: [{ provide: SourceComponent, useExisting: forwardRef(() => SourceXYZComponent) }],
+})
+export class SourceXYZComponent extends SourceComponent implements AfterContentInit, OnChanges {
+ @Input()
+ cacheSize: number;
+ @Input()
+ crossOrigin: string;
+ @Input()
+ opaque: boolean;
+ @Input()
+ projection: string;
+ @Input()
+ reprojectionErrorThreshold: number;
+ @Input()
+ minZoom: number;
+ @Input()
+ maxZoom: number;
+ @Input()
+ tileGrid: TileGrid;
+ @Input()
+ tileLoadFunction?: LoadFunction;
+ @Input()
+ tilePixelRatio: number;
+ @Input()
+ tileSize: number | Size;
+ @Input()
+ tileUrlFunction?: UrlFunction;
+ @Input()
+ url: string;
+ @Input()
+ urls: string[];
+ @Input()
+ wrapX: boolean;
+
+ @ContentChild(TileGridComponent, { static: false })
+ tileGridXYZ: TileGridComponent;
+
+ @Output()
+ tileLoadStart = new EventEmitter();
+ @Output()
+ tileLoadEnd = new EventEmitter();
+ @Output()
+ tileLoadError = new EventEmitter();
+
+ instance: XYZ;
+
+ constructor(
+ @Optional()
+ @Host()
+ protected layer?: LayerTileComponent
+ ) {
+ super(layer);
+ }
+
+ ngAfterContentInit() {
+ if (this.tileGridXYZ) {
+ this.tileGrid = this.tileGridXYZ.instance;
+ }
+ this.init();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const properties: { [index: string]: any } = {};
+
+ if (!this.instance) {
+ return;
+ }
+ for (const key in changes) {
+ if (changes.hasOwnProperty(key)) {
+ properties[key] = changes[key].currentValue;
+ }
+ }
+
+ this.instance.setProperties(properties, false);
+ if (changes.hasOwnProperty('url')) {
+ this.init();
+ }
+ }
+
+ init() {
+ this.instance = new XYZ(this);
+
+ this.instance.on('tileloadstart', (event: TileSourceEvent) => this.tileLoadStart.emit(event));
+ this.instance.on('tileloadend', (event: TileSourceEvent) => this.tileLoadEnd.emit(event));
+ this.instance.on('tileloaderror', (event: TileSourceEvent) => this.tileLoadError.emit(event));
+
+ this.register(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/styles/circle.component.ts b/projects/ng-openlayers/src/lib/styles/circle.component.ts
new file mode 100644
index 0000000..dc4d1a1
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/styles/circle.component.ts
@@ -0,0 +1,58 @@
+import { Component, Input, Host, AfterContentInit, OnChanges, OnDestroy, SimpleChanges } from '@angular/core';
+import { Circle, Fill, Stroke } from 'ol/style';
+import { StyleComponent } from './style.component';
+
+@Component({
+ selector: 'aol-style-circle',
+ template: ` `,
+})
+export class StyleCircleComponent implements AfterContentInit, OnChanges, OnDestroy {
+ @Input()
+ fill: Fill;
+ @Input()
+ radius: number;
+ @Input()
+ snapToPixel: boolean;
+ @Input()
+ stroke: Stroke;
+
+ public componentType = 'style-circle';
+ public instance: Circle;
+
+ constructor(@Host() private host: StyleComponent) {}
+
+ /**
+ * WORK-AROUND: since the re-rendering is not triggered on style change
+ * we trigger a radius change.
+ * see openlayers #6233 and #5775
+ */
+ update() {
+ if (!!this.instance) {
+ // console.log('setting ol.style.Circle instance\'s radius');
+ this.instance.setRadius(this.radius);
+ }
+ this.host.update();
+ }
+
+ ngAfterContentInit() {
+ // console.log('creating ol.style.Circle instance with: ', this);
+ this.instance = new Circle(this);
+ this.host.instance.setImage(this.instance);
+ this.host.update();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (!this.instance) {
+ return;
+ }
+ if (changes.radius) {
+ this.instance.setRadius(changes.radius.currentValue);
+ }
+ // console.log('changes detected in aol-style-circle, setting new radius: ', changes['radius'].currentValue);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-style-circle');
+ this.host.instance.setImage(null);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/styles/fill.component.ts b/projects/ng-openlayers/src/lib/styles/fill.component.ts
new file mode 100644
index 0000000..38caace
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/styles/fill.component.ts
@@ -0,0 +1,68 @@
+import { Component, Input, Optional, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { Fill } from 'ol/style';
+import { StyleComponent } from './style.component';
+import { StyleCircleComponent } from './circle.component';
+import { StyleTextComponent } from './text.component';
+import { Color } from 'ol/color';
+import { ColorLike } from 'ol/colorlike';
+
+@Component({
+ selector: 'aol-style-fill',
+ template: ` `,
+})
+export class StyleFillComponent implements OnInit, OnChanges {
+ @Input()
+ color: Color | ColorLike;
+
+ public instance: Fill;
+ private readonly host: StyleComponent | StyleCircleComponent | StyleTextComponent;
+
+ constructor(
+ @Optional() styleHost: StyleComponent,
+ @Optional() styleCircleHost: StyleCircleComponent,
+ @Optional() styleTextHost: StyleTextComponent
+ ) {
+ if (!styleHost) {
+ throw new Error('aol-style-stroke must be a descendant of aol-style');
+ }
+ if (!!styleTextHost) {
+ this.host = styleTextHost;
+ } else if (!!styleCircleHost) {
+ this.host = styleCircleHost;
+ } else {
+ this.host = styleHost;
+ }
+ // console.log('creating aol-style-fill with: ', this);
+ }
+
+ ngOnInit() {
+ // console.log('creating ol.style.Fill instance with: ', this);
+ this.instance = new Fill(this);
+ switch (this.host.componentType) {
+ case 'style':
+ this.host.instance.setFill(this.instance);
+ // console.log('setting ol.style instance\'s fill:', this.host);
+ break;
+ case 'style-text':
+ this.host.instance.setFill(this.instance);
+ break;
+ case 'style-circle':
+ (this.host as StyleCircleComponent).fill = this.instance;
+ // console.log('setting ol.style.circle instance\'s fill:', this.host);
+ break;
+ default:
+ throw new Error('unknown host type: ' + this.host);
+ }
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (!this.instance) {
+ return;
+ }
+ if (changes.color) {
+ this.instance.setColor(changes.color.currentValue);
+ }
+ this.host.update();
+ // console.log('changes detected in aol-style-fill, setting new color: ', changes);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/styles/icon.component.ts b/projects/ng-openlayers/src/lib/styles/icon.component.ts
new file mode 100644
index 0000000..ed67c2a
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/styles/icon.component.ts
@@ -0,0 +1,80 @@
+import { Component, Input, Host, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { Icon } from 'ol/style';
+
+// TODO https://github.com/openlayers/openlayers/issues/12694
+// import IconAnchorUnits from 'ol/style/IconAnchorUnits';
+// import IconOrigin from 'ol/style/IconOrigin';
+import { StyleComponent } from './style.component';
+import { IconAnchorUnits, IconOrigin } from 'ol/style/Icon';
+
+@Component({
+ selector: 'aol-style-icon',
+ template: ` `,
+})
+export class StyleIconComponent implements OnInit, OnChanges {
+ @Input()
+ anchor: [number, number];
+ @Input()
+ anchorXUnits: IconAnchorUnits;
+ @Input()
+ anchorYUnits: IconAnchorUnits;
+ @Input()
+ anchorOrigin: IconOrigin;
+ @Input()
+ color: [number, number, number, number];
+ @Input()
+ crossOrigin: IconOrigin;
+ @Input()
+ img: HTMLCanvasElement | HTMLImageElement;
+ @Input()
+ offset: [number, number];
+ @Input()
+ offsetOrigin: IconOrigin;
+ @Input()
+ opacity: number;
+ @Input()
+ scale: number;
+ @Input()
+ snapToPixel: boolean;
+ @Input()
+ rotateWithView: boolean;
+ @Input()
+ rotation: number;
+ @Input()
+ size: [number, number];
+ @Input()
+ imgSize: [number, number];
+ @Input()
+ src: string;
+
+ public instance: Icon;
+
+ constructor(@Host() private host: StyleComponent) {}
+
+ ngOnInit() {
+ // console.log('creating ol.style.Icon instance with: ', this);
+ this.instance = new Icon(this);
+ this.host.instance.setImage(this.instance);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (!this.instance) {
+ return;
+ }
+ if (changes.opacity) {
+ this.instance.setOpacity(changes.opacity.currentValue);
+ }
+ if (changes.rotation) {
+ this.instance.setRotation(changes.rotation.currentValue);
+ }
+ if (changes.scale) {
+ this.instance.setScale(changes.scale.currentValue);
+ }
+ if (changes.src) {
+ this.instance = new Icon(this);
+ this.host.instance.setImage(this.instance);
+ }
+ this.host.update();
+ // console.log('changes detected in aol-style-icon: ', changes);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/styles/stroke.component.ts b/projects/ng-openlayers/src/lib/styles/stroke.component.ts
new file mode 100644
index 0000000..a67bfe7
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/styles/stroke.component.ts
@@ -0,0 +1,95 @@
+import { Component, Input, Optional, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { Stroke } from 'ol/style';
+import { StyleComponent } from './style.component';
+import { StyleCircleComponent } from './circle.component';
+import { StyleTextComponent } from './text.component';
+import { Color } from 'ol/color';
+import { ColorLike } from 'ol/colorlike';
+
+@Component({
+ selector: 'aol-style-stroke',
+ template: ` `,
+})
+export class StyleStrokeComponent implements OnInit, OnChanges {
+ @Input()
+ color: Color | ColorLike;
+ @Input()
+ lineCap: CanvasLineCap | undefined;
+ @Input()
+ lineDash: number[];
+ @Input()
+ lineJoin: CanvasLineJoin | undefined;
+ @Input()
+ miterLimit: number;
+ @Input()
+ width: number;
+
+ public instance: Stroke;
+ /* the typings do not have the setters */
+ private readonly host: StyleComponent | StyleCircleComponent | StyleTextComponent;
+
+ constructor(
+ @Optional() styleHost: StyleComponent,
+ @Optional() styleCircleHost: StyleCircleComponent,
+ @Optional() styleTextHost: StyleTextComponent
+ ) {
+ if (!styleHost) {
+ throw new Error('aol-style-stroke must be a descendant of aol-style');
+ }
+ if (!!styleTextHost) {
+ this.host = styleTextHost;
+ } else if (!!styleCircleHost) {
+ this.host = styleCircleHost;
+ } else {
+ this.host = styleHost;
+ }
+ // console.log('creating aol-style-stroke with: ', this);
+ }
+
+ ngOnInit() {
+ // console.log('creating ol.style.Stroke instance with: ', this);
+ this.instance = new Stroke(this);
+ switch (this.host.componentType) {
+ case 'style':
+ this.host.instance.setStroke(this.instance);
+ // console.log('setting ol.style instance\'s stroke:', this.host);
+ break;
+ case 'style-text':
+ this.host.instance.setStroke(this.instance);
+ break;
+ case 'style-circle':
+ (this.host as StyleCircleComponent).stroke = this.instance;
+ // console.log('setting ol.style.circle instance\'s stroke:', this.host);
+ break;
+ default:
+ throw new Error('unknown host type: ' + this.host);
+ // break;
+ }
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (!this.instance) {
+ return;
+ }
+ if (changes.color) {
+ this.instance.setColor(changes.color.currentValue);
+ }
+ if (changes.lineCap) {
+ this.instance.setLineCap(changes.lineCap.currentValue);
+ }
+ if (changes.lineDash) {
+ this.instance.setLineDash(changes.lineDash.currentValue);
+ }
+ if (changes.lineJoin) {
+ this.instance.setLineJoin(changes.lineJoin.currentValue);
+ }
+ if (changes.miterLimit) {
+ this.instance.setMiterLimit(changes.miterLimit.currentValue);
+ }
+ if (changes.width) {
+ this.instance.setWidth(changes.width.currentValue);
+ }
+ this.host.update();
+ // console.log('changes detected in aol-style-stroke, setting new properties: ', changes);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/styles/style.component.ts b/projects/ng-openlayers/src/lib/styles/style.component.ts
new file mode 100644
index 0000000..7952886
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/styles/style.component.ts
@@ -0,0 +1,48 @@
+import { Component, Input, Optional, OnInit } from '@angular/core';
+import { Fill, Image, Stroke, Style, Text } from 'ol/style';
+import { Geometry } from 'ol/geom';
+import { FeatureComponent } from '../feature.component';
+import { LayerVectorComponent } from '../layers/layervector.component';
+import { GeometryFunction } from 'ol/style/Style';
+
+@Component({
+ selector: 'aol-style',
+ template: ` `,
+})
+export class StyleComponent implements OnInit {
+ @Input()
+ geometry: string | Geometry | GeometryFunction;
+ @Input()
+ fill: Fill;
+ @Input()
+ image: Image;
+ @Input()
+ stroke: Stroke;
+ @Input()
+ text: Text;
+ @Input()
+ zIndex: number;
+
+ public instance: Style;
+ public componentType = 'style';
+ private readonly host: FeatureComponent | LayerVectorComponent;
+
+ constructor(@Optional() featureHost: FeatureComponent, @Optional() layerHost: LayerVectorComponent) {
+ // console.log('creating aol-style');
+ this.host = !!featureHost ? featureHost : layerHost;
+ if (!this.host) {
+ throw new Error('aol-style must be applied to a feature or a layer');
+ }
+ }
+
+ update() {
+ // console.log('updating style\'s host: ', this.host);
+ this.host.instance.changed();
+ }
+
+ ngOnInit() {
+ // console.log('creating aol-style instance with: ', this);
+ this.instance = new Style(this);
+ this.host.instance.setStyle(this.instance);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/styles/text.component.ts b/projects/ng-openlayers/src/lib/styles/text.component.ts
new file mode 100644
index 0000000..3412214
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/styles/text.component.ts
@@ -0,0 +1,78 @@
+import { Component, Input, Optional, OnInit, OnChanges, SimpleChanges } from '@angular/core';
+import { Text } from 'ol/style';
+import { StyleComponent } from './style.component';
+
+@Component({
+ selector: 'aol-style-text',
+ template: ` `,
+})
+export class StyleTextComponent implements OnInit, OnChanges {
+ @Input()
+ font: string | undefined;
+ @Input()
+ offsetX: number | undefined;
+ @Input()
+ offsetY: number | undefined;
+ @Input()
+ scale: number | undefined;
+ @Input()
+ rotateWithView: boolean | undefined;
+ @Input()
+ rotation: number | undefined;
+ @Input()
+ text: string | undefined;
+ @Input()
+ textAlign: CanvasTextAlign | undefined;
+ @Input()
+ textBaseLine: string | undefined;
+
+ public instance: Text;
+ public componentType = 'style-text';
+
+ constructor(@Optional() private host: StyleComponent) {
+ if (!host) {
+ throw new Error('aol-style-text must be a descendant of aol-style');
+ }
+ // console.log('creating aol-style-text with: ', this);
+ }
+
+ ngOnInit() {
+ // console.log('creating ol.style.Text instance with: ', this);
+ this.instance = new Text(this);
+ this.host.instance.setText(this.instance);
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (!this.instance) {
+ return;
+ }
+ if (changes.font) {
+ this.instance.setFont(changes.font.currentValue);
+ }
+ if (changes.offsetX) {
+ this.instance.setOffsetX(changes.offsetX.currentValue);
+ }
+ if (changes.offsetY) {
+ this.instance.setOffsetY(changes.offsetY.currentValue);
+ }
+ if (changes.scale) {
+ this.instance.setScale(changes.scale.currentValue);
+ }
+ if (changes.rotation) {
+ this.instance.setRotation(changes.rotation.currentValue);
+ }
+ if (changes.text) {
+ this.instance.setText(changes.text.currentValue);
+ }
+ if (changes.textAlign) {
+ this.instance.setTextAlign(changes.textAlign.currentValue);
+ }
+ if (changes.textBaseLine) {
+ this.instance.setTextBaseline(changes.textBaseLine.currentValue);
+ }
+ this.host.update();
+ // console.log('changes detected in aol-style-text, setting new properties: ', changes);
+ }
+
+ update() {}
+}
diff --git a/projects/ng-openlayers/src/lib/tilegrid.component.ts b/projects/ng-openlayers/src/lib/tilegrid.component.ts
new file mode 100644
index 0000000..aaf0a11
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/tilegrid.component.ts
@@ -0,0 +1,43 @@
+import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
+import { createXYZ } from 'ol/tilegrid';
+import TileGrid from 'ol/tilegrid/TileGrid';
+import { Extent } from 'ol/extent';
+import { Coordinate } from 'ol/coordinate';
+import { Size } from 'ol/size';
+
+@Component({
+ selector: 'aol-tilegrid',
+ template: '',
+})
+export class TileGridComponent implements OnInit, OnChanges {
+ @Input()
+ extent: Extent;
+ @Input()
+ maxZoom: number;
+ @Input()
+ minZoom: number;
+ @Input()
+ tileSize: number | Size;
+ @Input()
+ origin?: Coordinate;
+ @Input()
+ resolutions: number[];
+
+ instance: TileGrid;
+
+ ngOnInit() {
+ if (!this.resolutions) {
+ this.instance = createXYZ(this);
+ } else {
+ this.instance = new TileGrid(this);
+ }
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ if (!this.resolutions) {
+ this.instance = createXYZ(this);
+ } else {
+ this.instance = new TileGrid(this);
+ }
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/tilegridwmts.component.ts b/projects/ng-openlayers/src/lib/tilegridwmts.component.ts
new file mode 100644
index 0000000..d95459d
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/tilegridwmts.component.ts
@@ -0,0 +1,32 @@
+import { Component, Input, OnInit } from '@angular/core';
+import WMTS from 'ol/tilegrid/WMTS';
+import { TileGridComponent } from './tilegrid.component';
+import { Coordinate } from 'ol/coordinate';
+import { Size } from 'ol/size';
+
+@Component({
+ selector: 'aol-tilegrid-wmts',
+ template: '',
+})
+export class TileGridWMTSComponent extends TileGridComponent implements OnInit {
+ @Input()
+ origin?: Coordinate;
+ @Input()
+ origins?: Coordinate[];
+ @Input()
+ resolutions: number[];
+ @Input()
+ matrixIds: string[];
+ @Input()
+ sizes?: Size[];
+ @Input()
+ tileSizes?: Size[];
+ @Input()
+ widths?: number[];
+
+ instance: WMTS;
+
+ ngOnInit() {
+ this.instance = new WMTS(this);
+ }
+}
diff --git a/projects/ng-openlayers/src/lib/view.component.ts b/projects/ng-openlayers/src/lib/view.component.ts
new file mode 100644
index 0000000..ad52505
--- /dev/null
+++ b/projects/ng-openlayers/src/lib/view.component.ts
@@ -0,0 +1,127 @@
+import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges, EventEmitter, Output } from '@angular/core';
+import View from 'ol/View';
+import { MapComponent } from './map.component';
+import { ObjectEvent } from 'ol/Object';
+import { Extent } from 'ol/extent';
+import { Coordinate } from 'ol/coordinate';
+import { DrawEvent } from 'ol/interaction/Draw';
+import BaseEvent from 'ol/events/Event';
+
+@Component({
+ selector: 'aol-view',
+ template: ` `,
+})
+export class ViewComponent implements OnInit, OnChanges, OnDestroy {
+ @Input()
+ constrainRotation: boolean | number;
+ @Input()
+ enableRotation: boolean;
+ @Input()
+ extent: Extent;
+ @Input()
+ maxResolution: number;
+ @Input()
+ minResolution: number;
+ @Input()
+ maxZoom: number;
+ @Input()
+ minZoom: number;
+ @Input()
+ resolution: number;
+ @Input()
+ resolutions: number[];
+ @Input()
+ rotation: number;
+ @Input()
+ zoom: number;
+ @Input()
+ zoomFactor: number;
+ @Input()
+ center: Coordinate;
+ @Input()
+ projection: string;
+ @Input()
+ constrainOnlyCenter: boolean;
+ @Input()
+ smoothExtentConstraint: boolean;
+ @Input()
+ constrainResolution: boolean;
+ @Input()
+ smoothResolutionConstraint: boolean;
+ @Input()
+ showFullExtent: boolean;
+ @Input()
+ multiWorld: boolean;
+
+ @Input()
+ zoomAnimation = false;
+
+ @Output()
+ olChange = new EventEmitter();
+ @Output()
+ changeCenter = new EventEmitter();
+ @Output()
+ changeResolution = new EventEmitter();
+ @Output()
+ changeRotation = new EventEmitter();
+ @Output()
+ olError = new EventEmitter();
+ @Output()
+ propertyChange = new EventEmitter();
+
+ public instance: View;
+ public componentType = 'view';
+
+ constructor(private host: MapComponent) {}
+
+ ngOnInit() {
+ // console.log('creating ol.View instance with: ', this);
+ this.instance = new View(this);
+ this.host.instance.setView(this.instance);
+
+ this.instance.on('change', (event: DrawEvent) => this.olChange.emit(event));
+ this.instance.on('change:center', (event: ObjectEvent) => this.changeCenter.emit(event));
+ this.instance.on('change:resolution', (event: ObjectEvent) => this.changeResolution.emit(event));
+ this.instance.on('change:rotation', (event: ObjectEvent) => this.changeRotation.emit(event));
+ this.instance.on('error', (event: BaseEvent) => this.olError.emit(event));
+ this.instance.on('propertychange', (event: ObjectEvent) => this.propertyChange.emit(event));
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ const properties: { [index: string]: any } = {};
+ if (!this.instance) {
+ return;
+ }
+ for (const key in changes) {
+ if (changes.hasOwnProperty(key)) {
+ switch (key) {
+ case 'zoom':
+ /** Work-around: setting the zoom via setProperties does not work. */
+ if (this.zoomAnimation) {
+ this.instance.animate({ zoom: changes[key].currentValue });
+ } else {
+ this.instance.setZoom(changes[key].currentValue);
+ }
+ break;
+ case 'projection':
+ this.instance = new View(this);
+ this.host.instance.setView(this.instance);
+ break;
+ case 'center':
+ /** Work-around: setting the center via setProperties does not work. */
+ this.instance.setCenter(changes[key].currentValue);
+ break;
+ default:
+ break;
+ }
+ properties[key] = changes[key].currentValue;
+ }
+ }
+ // console.log('changes detected in aol-view, setting new properties: ', properties);
+ this.instance.setProperties(properties, false);
+ }
+
+ ngOnDestroy() {
+ // console.log('removing aol-view');
+ }
+}
diff --git a/projects/ng-openlayers/src/public-api.ts b/projects/ng-openlayers/src/public-api.ts
new file mode 100644
index 0000000..2ac2fc7
--- /dev/null
+++ b/projects/ng-openlayers/src/public-api.ts
@@ -0,0 +1,250 @@
+/*
+ * Public API Surface of ng-openlayers
+ */
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { SimpleGeometryComponent } from './lib/geom/simplegeometry.component';
+import { SourceComponent } from './lib/sources/source.component';
+import { ViewComponent } from './lib/view.component';
+import { GraticuleComponent } from './lib/graticule.component';
+import { LayerGroupComponent } from './lib/layers/layergroup.component';
+import { LayerImageComponent } from './lib/layers/layerimage.component';
+import { LayerTileComponent } from './lib/layers/layertile.component';
+import { MapComponent } from './lib/map.component';
+import { LayerVectorComponent } from './lib/layers/layervector.component';
+import { LayerVectorTileComponent } from './lib/layers/layervectortile.component';
+import { SourceOsmComponent } from './lib/sources/osm.component';
+import { SourceBingmapsComponent } from './lib/sources/bingmaps.component';
+import { SourceClusterComponent } from './lib/sources/cluster.component';
+import { SourceVectorComponent } from './lib/sources/vector.component';
+import { SourceXYZComponent } from './lib/sources/xyz.component';
+import { SourceTileWMTSComponent } from './lib/sources/tilewmts.component';
+import { SourceVectorTileComponent } from './lib/sources/vectortile.component';
+import { SourceTileWMSComponent } from './lib/sources/tilewms.component';
+import { SourceTileJSONComponent } from './lib/sources/tilejson.component';
+import { SourceGeoJSONComponent } from './lib/sources/geojson.component';
+import { SourceImageStaticComponent } from './lib/sources/imagestatic.component';
+import { SourceImageWMSComponent } from './lib/sources/imagewms.component';
+import { SourceImageArcGISRestComponent } from './lib/sources/imagearcgisrest.component';
+import { SourceRasterComponent } from './lib/sources/raster.component';
+import { FeatureComponent } from './lib/feature.component';
+import { GeometryCircleComponent } from './lib/geom/geometrycircle.component';
+import { GeometryLinestringComponent } from './lib/geom/geometrylinestring.component';
+import { GeometryMultiLinestringComponent } from './lib/geom/geometrymultilinestring.component';
+import { GeometryMultiPointComponent } from './lib/geom/geometrymultipoint.component';
+import { GeometryMultiPolygonComponent } from './lib/geom/geometrymultipolygon.component';
+import { GeometryPointComponent } from './lib/geom/geometrypoint.component';
+import { GeometryPolygonComponent } from './lib/geom/geometrypolygon.component';
+import { CoordinateComponent } from './lib/coordinate.component';
+import { CollectionCoordinatesComponent } from './lib/collectioncoordinates.component';
+import { StyleComponent } from './lib/styles/style.component';
+import { StyleCircleComponent } from './lib/styles/circle.component';
+import { StyleStrokeComponent } from './lib/styles/stroke.component';
+import { StyleIconComponent } from './lib/styles/icon.component';
+import { StyleFillComponent } from './lib/styles/fill.component';
+import { StyleTextComponent } from './lib/styles/text.component';
+import { DefaultControlComponent } from './lib/controls/default.component';
+import { ControlComponent } from './lib/controls/control.component';
+import { ControlAttributionComponent } from './lib/controls/attribution.component';
+import { ControlFullScreenComponent } from './lib/controls/fullscreen.component';
+import { ControlMousePositionComponent } from './lib/controls/mouseposition.component';
+import { ControlOverviewMapComponent } from './lib/controls/overviewmap.component';
+import { ControlRotateComponent } from './lib/controls/rotate.component';
+import { ControlScaleLineComponent } from './lib/controls/scaleline.component';
+import { ControlZoomComponent } from './lib/controls/zoom.component';
+import { ControlZoomSliderComponent } from './lib/controls/zoomslider.component';
+import { ControlZoomToExtentComponent } from './lib/controls/zoomtoextent.component';
+import { FormatMVTComponent } from './lib/formats/mvt.component';
+import { TileGridComponent } from './lib/tilegrid.component';
+import { TileGridWMTSComponent } from './lib/tilegridwmts.component';
+import { DefaultInteractionComponent } from './lib/interactions/default.component';
+import { DoubleClickZoomInteractionComponent } from './lib/interactions/doubleclickzoom.component';
+import { DragAndDropInteractionComponent } from './lib/interactions/draganddrop.component';
+import { DragBoxInteractionComponent } from './lib/interactions/dragbox.component';
+import { DragPanInteractionComponent } from './lib/interactions/dragpan.component';
+import { DragRotateInteractionComponent } from './lib/interactions/dragrotate.component';
+import { DragRotateAndZoomInteractionComponent } from './lib/interactions/dragrotateandzoom.component';
+import { DragZoomInteractionComponent } from './lib/interactions/dragzoom.component';
+import { MouseWheelZoomInteractionComponent } from './lib/interactions/mousewheelzoom.component';
+import { PinchZoomInteractionComponent } from './lib/interactions/pinchzoom.component';
+import { DrawInteractionComponent } from './lib/interactions/draw.component';
+import { KeyboardPanInteractionComponent } from './lib/interactions/keyboardpan.component';
+import { KeyboardZoomInteractionComponent } from './lib/interactions/keyboardzoom.component';
+import { SelectInteractionComponent } from './lib/interactions/select.component';
+import { ModifyInteractionComponent } from './lib/interactions/modify.component';
+import { TranslateInteractionComponent } from './lib/interactions/translate.component';
+import { OverlayComponent } from './lib/overlay.component';
+import { ContentComponent } from './lib/content.component';
+import { AttributionsComponent } from './lib/attributions.component';
+import { AttributionComponent } from './lib/attribution.component';
+import { SourceUTFGridComponent } from './lib/sources/utfgrid.component';
+import { LayerComponent } from './lib/layers/layer.component';
+
+export {
+ MapComponent,
+ ViewComponent,
+ GraticuleComponent,
+ LayerComponent,
+ LayerGroupComponent,
+ LayerImageComponent,
+ LayerTileComponent,
+ LayerVectorComponent,
+ LayerVectorTileComponent,
+ SourceComponent,
+ SourceOsmComponent,
+ SourceBingmapsComponent,
+ SourceClusterComponent,
+ SourceUTFGridComponent,
+ SourceVectorComponent,
+ SourceXYZComponent,
+ SourceVectorTileComponent,
+ SourceTileWMSComponent,
+ SourceTileWMTSComponent,
+ SourceTileJSONComponent,
+ SourceGeoJSONComponent,
+ SourceImageStaticComponent,
+ SourceImageWMSComponent,
+ SourceRasterComponent,
+ SourceImageArcGISRestComponent,
+ SimpleGeometryComponent,
+ FeatureComponent,
+ GeometryLinestringComponent,
+ GeometryMultiLinestringComponent,
+ GeometryMultiPointComponent,
+ GeometryMultiPolygonComponent,
+ GeometryPointComponent,
+ GeometryPolygonComponent,
+ GeometryCircleComponent,
+ CoordinateComponent,
+ CollectionCoordinatesComponent,
+ StyleComponent,
+ StyleCircleComponent,
+ StyleFillComponent,
+ StyleIconComponent,
+ StyleStrokeComponent,
+ StyleTextComponent,
+ DefaultControlComponent,
+ ControlComponent,
+ ControlAttributionComponent,
+ ControlFullScreenComponent,
+ ControlMousePositionComponent,
+ ControlOverviewMapComponent,
+ ControlRotateComponent,
+ ControlScaleLineComponent,
+ ControlZoomComponent,
+ ControlZoomSliderComponent,
+ ControlZoomToExtentComponent,
+ FormatMVTComponent,
+ TileGridComponent,
+ TileGridWMTSComponent,
+ DefaultInteractionComponent,
+ DoubleClickZoomInteractionComponent,
+ DragAndDropInteractionComponent,
+ DragBoxInteractionComponent,
+ DragPanInteractionComponent,
+ DragRotateInteractionComponent,
+ DragRotateAndZoomInteractionComponent,
+ DragZoomInteractionComponent,
+ MouseWheelZoomInteractionComponent,
+ PinchZoomInteractionComponent,
+ DrawInteractionComponent,
+ KeyboardPanInteractionComponent,
+ KeyboardZoomInteractionComponent,
+ SelectInteractionComponent,
+ ModifyInteractionComponent,
+ TranslateInteractionComponent,
+ OverlayComponent,
+ ContentComponent,
+ AttributionsComponent,
+ AttributionComponent,
+};
+
+const COMPONENTS = [
+ MapComponent,
+
+ ViewComponent,
+ GraticuleComponent,
+
+ LayerGroupComponent,
+ LayerImageComponent,
+ LayerTileComponent,
+ LayerVectorComponent,
+ LayerVectorTileComponent,
+
+ SourceOsmComponent,
+ SourceBingmapsComponent,
+ SourceClusterComponent,
+ SourceUTFGridComponent,
+ SourceVectorComponent,
+ SourceXYZComponent,
+ SourceVectorTileComponent,
+ SourceTileWMSComponent,
+ SourceTileWMTSComponent,
+ SourceTileJSONComponent,
+ SourceGeoJSONComponent,
+ SourceImageStaticComponent,
+ SourceImageWMSComponent,
+ SourceImageArcGISRestComponent,
+ SourceRasterComponent,
+
+ FeatureComponent,
+ GeometryLinestringComponent,
+ GeometryMultiLinestringComponent,
+ GeometryMultiPointComponent,
+ GeometryMultiPolygonComponent,
+ GeometryPointComponent,
+ GeometryPolygonComponent,
+ GeometryCircleComponent,
+ CoordinateComponent,
+ CollectionCoordinatesComponent,
+
+ StyleComponent,
+ StyleCircleComponent,
+ StyleFillComponent,
+ StyleIconComponent,
+ StyleStrokeComponent,
+ StyleTextComponent,
+
+ DefaultControlComponent,
+ ControlComponent,
+ ControlAttributionComponent,
+ ControlFullScreenComponent,
+ ControlMousePositionComponent,
+ ControlOverviewMapComponent,
+ ControlRotateComponent,
+ ControlScaleLineComponent,
+ ControlZoomComponent,
+ ControlZoomSliderComponent,
+ ControlZoomToExtentComponent,
+
+ FormatMVTComponent,
+ TileGridComponent,
+ TileGridWMTSComponent,
+
+ DefaultInteractionComponent,
+ DoubleClickZoomInteractionComponent,
+ DragAndDropInteractionComponent,
+ DragBoxInteractionComponent,
+ DragPanInteractionComponent,
+ DragRotateInteractionComponent,
+ DragRotateAndZoomInteractionComponent,
+ DragZoomInteractionComponent,
+ MouseWheelZoomInteractionComponent,
+ PinchZoomInteractionComponent,
+ DrawInteractionComponent,
+ SelectInteractionComponent,
+ ModifyInteractionComponent,
+ TranslateInteractionComponent,
+
+ OverlayComponent,
+ ContentComponent,
+ AttributionsComponent,
+ AttributionComponent,
+];
+
+@NgModule({
+ declarations: COMPONENTS,
+ imports: [CommonModule],
+ exports: COMPONENTS,
+})
+export class AngularOpenlayersModule {}
diff --git a/projects/ng-openlayers/src/test.ts b/projects/ng-openlayers/src/test.ts
new file mode 100644
index 0000000..8e2aa10
--- /dev/null
+++ b/projects/ng-openlayers/src/test.ts
@@ -0,0 +1,12 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'core-js/es7/reflect';
+import 'zone.js';
+import 'zone.js/testing';
+import { getTestBed } from '@angular/core/testing';
+import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
+ teardown: { destroyAfterEach: false },
+});
diff --git a/projects/ng-openlayers/tsconfig.lib.json b/projects/ng-openlayers/tsconfig.lib.json
new file mode 100644
index 0000000..95a4337
--- /dev/null
+++ b/projects/ng-openlayers/tsconfig.lib.json
@@ -0,0 +1,27 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "declaration": true,
+ "declarationMap": true,
+ "inlineSources": true,
+ "types": [],
+ "lib": [
+ "dom",
+ "es2018"
+ ]
+ },
+ "angularCompilerOptions": {
+ "skipLibCheck": true,
+ "skipTemplateCodegen": true,
+ "strictMetadataEmit": true,
+ "enableResourceInlining": true
+ },
+ "exclude": [
+ "src/test.ts",
+ "**/*.spec.ts"
+ ],
+ "include": [
+ "**/*.ts"
+ ]
+}
diff --git a/projects/ng-openlayers/tsconfig.lib.prod.json b/projects/ng-openlayers/tsconfig.lib.prod.json
new file mode 100644
index 0000000..2a2faa8
--- /dev/null
+++ b/projects/ng-openlayers/tsconfig.lib.prod.json
@@ -0,0 +1,9 @@
+{
+ "extends": "./tsconfig.lib.json",
+ "compilerOptions": {
+ "declarationMap": false
+ },
+ "angularCompilerOptions": {
+ "compilationMode": "partial"
+ }
+}
diff --git a/projects/ng-openlayers/tsconfig.spec.json b/projects/ng-openlayers/tsconfig.spec.json
new file mode 100644
index 0000000..75c900a
--- /dev/null
+++ b/projects/ng-openlayers/tsconfig.spec.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "types": [
+ "jasmine",
+ "node"
+ ]
+ },
+ "files": [
+ "src/test.ts"
+ ],
+ "include": [
+ "**/*.spec.ts",
+ "**/*.d.ts"
+ ]
+}
diff --git a/projects/ng-openlayers/tslint-prettier.json b/projects/ng-openlayers/tslint-prettier.json
new file mode 100644
index 0000000..12ebab2
--- /dev/null
+++ b/projects/ng-openlayers/tslint-prettier.json
@@ -0,0 +1,7 @@
+{
+ "extends": [
+ "./tslint.json",
+ "tslint-config-prettier",
+ "tslint-plugin-prettier"
+ ]
+}