diff --git a/src/app/feature-management/custom-color-entry/custom-color-entry.component.html b/src/app/feature-management/custom-color-entry/custom-color-entry.component.html index fc5521a..02425ed 100644 --- a/src/app/feature-management/custom-color-entry/custom-color-entry.component.html +++ b/src/app/feature-management/custom-color-entry/custom-color-entry.component.html @@ -9,7 +9,7 @@ cpOutputFormat="hex" [cpOKButton]="true" [cpCancelButton]="true" - (colorPickerSelect)="selectedColor = $event" + (colorPickerSelect)="onColorPicked($event)" >edit delete diff --git a/src/app/feature-management/custom-color-entry/custom-color-entry.component.ts b/src/app/feature-management/custom-color-entry/custom-color-entry.component.ts index 0b474f4..eaa2c75 100644 --- a/src/app/feature-management/custom-color-entry/custom-color-entry.component.ts +++ b/src/app/feature-management/custom-color-entry/custom-color-entry.component.ts @@ -1,29 +1,41 @@ -import {Component, EventEmitter, Input, OnInit, Output} from "@angular/core"; -import {CustomProperties} from "../../enums/CustomProperties"; -import {Utils} from "../../misc/Utils"; -import {Config} from "../../misc/Config"; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {CustomProperties} from '../../enums/CustomProperties'; +import {Utils} from '../../misc/Utils'; +import {Config} from '../../misc/Config'; /** * This component represents an entry of the custom properties section inside the feature management and - * is responsible to show and makes changes to the color property. + * is responsible to show and makes changes to the color properties. */ @Component({ selector: 'lm-custom-color-entry', templateUrl: 'custom-color-entry.component.html', styleUrls: ['custom-color-entry.component.scss'] }) -export class CustomColorEntryComponent implements OnInit{ +export class CustomColorEntryComponent implements OnInit { - @Input() colors: any; + private _colors: any; + @Input() set colors(value: any) { + this._colors = value; + if (this._colors[CustomProperties.COLOR_STROKE]) { + this.isDefaultColor = false; + this.definePreviewStyle(); + } + } - private _selectedColor: any; - set selectedColor(value: any) { + get colors(): any { + return this._colors; + } + + + private _selectedColor: string; + set selectedColor(value: string) { this._selectedColor = value; this.applyColor(value); this.colorChangeEmitter.emit(this.colors); } - get selectedColor() :any{ + get selectedColor(): string { return this._selectedColor; } @@ -39,31 +51,30 @@ export class CustomColorEntryComponent implements OnInit{ return Utils.colorArrayToRgba(color); } - // - not default color properties + // - no default color properties get strokeColorAsRgba(): string { const color = this.colors[CustomProperties.COLOR_STROKE]; return Utils.colorArrayToRgba(color); } get fillColorAsRgba(): string { - const color = this.colors[CustomProperties.COLOR_FILL]; + const color = this.colors[CustomProperties.COLOR_FILL]; return Utils.colorArrayToRgba(color); } - @Output('colorChanged') colorChangeEmitter: EventEmitter = new EventEmitter(); /** * The color style of the color preview */ - previewStyle : any; + previewStyle: any; /** * A simple flag that is true, if the default color is active */ - isDefaultColor : boolean; + isDefaultColor: boolean = true; - ngOnInit(){ + ngOnInit() { this.definePreviewStyle(); } @@ -71,18 +82,16 @@ export class CustomColorEntryComponent implements OnInit{ * Returns the style for the preview. If a color was defined, this color is used. If not, the default color is used * instead. */ - definePreviewStyle(): any { + definePreviewStyle() { let styles = { 'border-color': this.defaultStrokeColorAsRgba, 'background-color': this.defaultFillColorAsRgba }; - this.isDefaultColor = true; - if (this.selectedColor) { + if (!this.isDefaultColor) { styles = { 'border-color': this.strokeColorAsRgba, 'background-color': this.fillColorAsRgba }; - this.isDefaultColor = false; } this.previewStyle = styles; } @@ -91,20 +100,30 @@ export class CustomColorEntryComponent implements OnInit{ * Saves the given hex value to colors object as not default color * @param value */ - applyColor(value: any) { + applyColor(value: string) { this.colors[CustomProperties.COLOR_STROKE] = Utils.hexToRgba(value); this.colors[CustomProperties.COLOR_FILL] = Utils.hexToRgba(value, Config.DEFAULT_FILL_OPACITY_TO_STROKE); this.definePreviewStyle(); } /** - * TODO add description + * Resets the custom color */ - resetColor(){ + resetColor() { this.colors[CustomProperties.COLOR_STROKE] = null; this.colors[CustomProperties.COLOR_FILL] = null; + this.isDefaultColor = true; this._selectedColor = null; this.definePreviewStyle(); + this.colorChangeEmitter.emit(this.colors); } + /** + * Eventhandler that is fired when a color is picked + * @param event + */ + onColorPicked(color: string) { + this.selectedColor = color; + this.isDefaultColor = false; + } } diff --git a/src/app/feature-management/feature-manager/feature-manager.component.html b/src/app/feature-management/feature-manager/feature-manager.component.html index 5571530..1fb95d1 100644 --- a/src/app/feature-management/feature-manager/feature-manager.component.html +++ b/src/app/feature-management/feature-manager/feature-manager.component.html @@ -23,8 +23,8 @@ - +
diff --git a/src/app/feature-management/feature-manager/feature-manager.component.ts b/src/app/feature-management/feature-manager/feature-manager.component.ts index 91fad8e..9568bc6 100644 --- a/src/app/feature-management/feature-manager/feature-manager.component.ts +++ b/src/app/feature-management/feature-manager/feature-manager.component.ts @@ -1,8 +1,8 @@ -import {Component, Input} from "@angular/core"; -// import {Feature} from 'ol/Feature'; -import {Config} from "../../misc/Config"; -import {CustomProperties} from "../../enums/CustomProperties"; +import {Component, Input, Output, EventEmitter} from '@angular/core'; +import {Config} from '../../misc/Config'; +import {CustomProperties} from '../../enums/CustomProperties'; import {Feature} from 'ol/Feature'; +import {RawLayer} from 'src/app/models/RawLayer'; /** * This component manages all properties of the current feature. It does not matter if it is a simple geo json @@ -29,6 +29,11 @@ export class FeatureManagerComponent { return this._curFeature; } + /** + * An event emitter that is fired if a feature is changed, + */ + @Output('featureChange') featureChangeEmitter: EventEmitter = new EventEmitter(); + /** * Returns the properties of curFeature */ @@ -37,7 +42,6 @@ export class FeatureManagerComponent { delete properties.geometry; delete properties[Config.GEO_JSON_PREFIX]; return properties; - } /** @@ -46,13 +50,12 @@ export class FeatureManagerComponent { get curCustomProperties(): any { const properties = this.curFeature.getProperties(); return properties[Config.GEO_JSON_PREFIX]; - } /** * Returns the default color properties of the current feature */ - get curCustomDefaultColor(): any { + get curFeatureColors(): any { const colors = {}; colors[CustomProperties.DEFAULT_COLOR_STROKE] = this.curCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE]; colors[CustomProperties.DEFAULT_COLOR_FILL] = this.curCustomProperties[CustomProperties.DEFAULT_COLOR_FILL]; @@ -61,8 +64,14 @@ export class FeatureManagerComponent { return colors; } - defaultColorChanged(colors: any) { - console.log('Default colors changed!', colors); + /** + * Event handler if the color set of a feature was changed + * @param colors + */ + onFeatureColorChanged(colors: any) { + RawLayer.saveCustomProperties(this.curFeature, colors); + this.curFeature.setStyle(RawLayer.determineColorStyle(colors)); + this.featureChangeEmitter.emit(this.curFeature); } } diff --git a/src/app/map/map.component.ts b/src/app/map/map.component.ts index 62dd618..9757dd8 100644 --- a/src/app/map/map.component.ts +++ b/src/app/map/map.component.ts @@ -5,19 +5,19 @@ import TileLayer from 'ol/layer/Tile'; import {fromLonLat, transform} from 'ol/proj'; import VectorLayer from 'ol/layer/Vector'; import {OSM} from 'ol/source'; -import {CdkDragDrop} from "@angular/cdk/drag-drop"; -import {MatDialog} from "@angular/material"; -import {AddLayerComponent} from "../add-layer-component/add-layer.component"; -import {RawLayer} from "../models/RawLayer"; +import {CdkDragDrop} from '@angular/cdk/drag-drop'; +import {MatDialog} from '@angular/material'; +import {AddLayerComponent} from '../add-layer-component/add-layer.component'; +import {RawLayer} from '../models/RawLayer'; import LayerGroup from 'ol/layer/Group'; -import {Collection} from "openlayers"; -import {EPSG} from "../enums/EPSG"; +import {Collection} from 'openlayers'; +import {EPSG} from '../enums/EPSG'; import {inAndOut} from 'ol/easing'; import {Feature} from 'ol/Feature'; import Style from 'ol/style/Style'; import Stroke from 'ol/style/Stroke'; import Fill from 'ol/style/Fill'; -import {FeatureManagerComponent} from "../feature-management/feature-manager/feature-manager.component"; +import {FeatureManagerComponent} from '../feature-management/feature-manager/feature-manager.component'; /** * TODO: add description @@ -60,6 +60,11 @@ export class MapComponent implements OnInit { */ selectedFeature: null, + // /** + // * The layer of the selected feature + // */ + // selectedLayer: null, + /** * The feature that was highlighted at least */ @@ -76,7 +81,7 @@ export class MapComponent implements OnInit { defaultSelectedStyle: null, /** - * Default style for a highlighted feauture + * Default style for a highlighted feature */ defaultHighlightStyle: null }; @@ -127,18 +132,18 @@ export class MapComponent implements OnInit { }) }); - this.addMapEventlistener(); - + this.addMapEventListener(); + this.addFeatureManagerEventListener(); // without this line the map is only visible after resizing window requestAnimationFrame(() => { this.map.updateSize(); - }) + }); } /** * Adds all map event listener */ - private addMapEventlistener() { + private addMapEventListener() { // get current cursor coordinates to transform and display them this.map.on('pointermove', (evt) => { const tmpCoords = transform(evt.coordinate, EPSG.openLayersDefault, EPSG.wgs84); @@ -152,6 +157,7 @@ export class MapComponent implements OnInit { } const pixel = this.map.getEventPixel(evt.originalEvent); const features = this.map.getFeaturesAtPixel(pixel); + if (features && features.length > 0) { this.map.getTargetElement().style.cursor = 'pointer'; this.featureManager.curFeature = features[0]; @@ -172,23 +178,45 @@ export class MapComponent implements OnInit { // handle feature selection this.map.on('click', (evt) => { const pixel = this.map.getEventPixel(evt.originalEvent); - const features = this.map.getFeaturesAtPixel(pixel); + // const features = this.map.getFeaturesAtPixel(pixel); + let selectedFeature; + // let selectedLayer; + // get the feature and the according layer + this.map.forEachFeatureAtPixel(pixel, (feature, layer) => { + selectedFeature = feature; + // selectedLayer = layer; + return true; + }); if (this.cache.selectedFeature) { // use lastHighlightedStyle because the original style is saved there - this.cache.selectedFeature.setStyle(this.cache.lastHighlightedStyle); - // this.styleCache['lastHighlightedStyle'] = null; + if (this.cache.lastHighlightedStyle) { + this.cache.selectedFeature.setStyle(this.cache.lastHighlightedStyle); + } this.cache.selectedFeature = null; - } else if (features && features.length > 0) { + // this.cache.selectedLayer = null; + } else if (selectedFeature) { this.map.getTargetElement().style.cursor = ''; - this.featureManager.curFeature = features[0]; - this.cache.selectedFeature = features[0]; + this.cache.selectedFeature = selectedFeature; + // this.cache.selectedLayer = selectedLayer; + this.featureManager.curFeature = this.cache.selectedFeature; this.cache.lastHighlightedFeature = null; - features[0].setStyle(this.cache.defaultSelectedStyle); + this.cache.selectedFeature.setStyle(this.cache.defaultSelectedStyle); } }); } + /** + * Adds all feature manager event listener + */ + private addFeatureManagerEventListener() { + this.featureManager.featureChangeEmitter.subscribe(() => { + // - reset selection values + // this.cache.selectedFeature = null; + this.cache.lastHighlightedStyle = null; + }); + } + /** * Highlights a Features * @param feature @@ -282,7 +310,7 @@ export class MapComponent implements OnInit { size: this.map.getSize(), duration: 888, easing: inAndOut - } + }; this.map.getView().fit(customLayer.getSource().getExtent(), options); } @@ -298,7 +326,7 @@ export class MapComponent implements OnInit { fill: new Fill({ color: 'rgba(245, 64, 41, 0.3)', }) - }) + }); this.cache.defaultHighlightStyle = new Style({ stroke: new Stroke({ @@ -308,7 +336,7 @@ export class MapComponent implements OnInit { fill: new Fill({ color: 'rgba(245, 64, 41, 0.1)', }) - }) + }); } /** @@ -316,22 +344,22 @@ export class MapComponent implements OnInit { */ private addMockData() { const data = { - "type": "FeatureCollection", - "name": "background", - "crs": { - "type": "name", - "properties": { - "name": "urn:ogc:def:crs:OGC:1.3:CRS84" + 'type': 'FeatureCollection', + 'name': 'background', + 'crs': { + 'type': 'name', + 'properties': { + 'name': 'urn:ogc:def:crs:OGC:1.3:CRS84' } }, - "features": [{ - "type": "Feature", - "properties": { - "name": "Layer 1" + 'features': [{ + 'type': 'Feature', + 'properties': { + 'name': 'Layer 1' }, - "geometry": { - "type": "MultiPolygon", - "coordinates": [ + 'geometry': { + 'type': 'MultiPolygon', + 'coordinates': [ [ [ [8.801864395832126, 53.14770721254275], @@ -344,13 +372,13 @@ export class MapComponent implements OnInit { } }, { - "type": "Feature", - "properties": { - "name": "Layer 2" + 'type': 'Feature', + 'properties': { + 'name': 'Layer 2' }, - "geometry": { - "type": "MultiPolygon", - "coordinates": [ + 'geometry': { + 'type': 'MultiPolygon', + 'coordinates': [ [ [ [8.807787400997881, 53.1477644198588], @@ -363,13 +391,13 @@ export class MapComponent implements OnInit { } }, { - "type": "Feature", - "properties": { - "name": "Layer 3" + 'type': 'Feature', + 'properties': { + 'name': 'Layer 3' }, - "geometry": { - "type": "MultiPolygon", - "coordinates": [ + 'geometry': { + 'type': 'MultiPolygon', + 'coordinates': [ [ [ [8.80207966021419, 53.14392919393571], @@ -382,13 +410,13 @@ export class MapComponent implements OnInit { } }, { - "type": "Feature", - "properties": { - "name": "Layer 4" + 'type': 'Feature', + 'properties': { + 'name': 'Layer 4' }, - "geometry": { - "type": "MultiPolygon", - "coordinates": [ + 'geometry': { + 'type': 'MultiPolygon', + 'coordinates': [ [ [ [8.808654874418039, 53.143631496039035], @@ -403,7 +431,7 @@ export class MapComponent implements OnInit { ] }; const rawLayer = new RawLayer('Mock Data Layer', JSON.stringify(data)); - rawLayer.saveDefaultColor([125, 125, 125]) + rawLayer.saveDefaultColor([125, 125, 125]); const customLayer = RawLayer.toVectorLayer(rawLayer); this.centerLayerOnMap(customLayer); this.customLayerCollection.push(customLayer); diff --git a/src/app/models/RawLayer.ts b/src/app/models/RawLayer.ts index b36dfbd..43f60cf 100644 --- a/src/app/models/RawLayer.ts +++ b/src/app/models/RawLayer.ts @@ -1,13 +1,13 @@ import VectorLayer from 'ol/layer/Vector'; import GeoJSON from 'ol/format/GeoJSON'; import {Vector as VectorSource} from 'ol/source'; -import {EPSG} from "../enums/EPSG"; +import {EPSG} from '../enums/EPSG'; import Style from 'ol/style/Style'; import Stroke from 'ol/style/Stroke'; import Fill from 'ol/style/Fill'; -import {Config} from "../misc/Config"; +import {Config} from '../misc/Config'; import {Feature} from 'ol/Feature'; -import {CustomProperties} from "../enums/CustomProperties"; +import {CustomProperties} from '../enums/CustomProperties'; /** * Represents a raw (not processed) map layer @@ -38,7 +38,7 @@ export class RawLayer { } /** - * Creates a VectorLayer instance from a RawLayer and applies the layer color to all features + * Creates a VectorLayer instance from a RawLayer and applies the layer or custom color to all features * @param rawLayer */ public static toVectorLayer(rawLayer: RawLayer): VectorLayer { @@ -46,21 +46,9 @@ export class RawLayer { const features = RawLayer.geoJSONToFeatures(rawLayer.data); // apply style to all features features.forEach(feature => { - // TODO - // determine if this feature has a own color! If so, do not apply default color and use own color. const featureCustomProperties = RawLayer.getCustomProperties(feature); - if (featureCustomProperties && featureCustomProperties['default-style-stroke']) { - feature.setStyle(new Style({ - stroke: new Stroke({ - color: featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE], - width: 1 - }), - fill: new Fill({ - color: featureCustomProperties[CustomProperties.DEFAULT_COLOR_FILL] - }) - })); - } - }) + feature.setStyle(this.determineColorStyle(featureCustomProperties)); + }); // add features to source vectorSource.addFeatures(features); @@ -72,33 +60,6 @@ export class RawLayer { }); } - /** - * Saves a default color property to geoJSON - */ - public saveDefaultColor(color: number[]) { - const features = RawLayer.geoJSONToFeatures(this.data); - features.forEach(feature => { - // save default color in feature properties - const properties = {}; - properties[CustomProperties.DEFAULT_COLOR_STROKE] = color; - properties[CustomProperties.DEFAULT_COLOR_FILL] = [...color.slice(0, 3), Config.DEFAULT_FILL_OPACITY_TO_STROKE]; - RawLayer.saveCustomProperties(feature, properties); - }) - this.data = RawLayer.featuresToGeoJSON(features); - } - - /** - * Reads the default color property from geoJSON - */ - public getDefaultColor(): number[] { - const features = RawLayer.geoJSONToFeatures(this.data); - const featureCustomProperties = RawLayer.getCustomProperties(features[0]); - if (featureCustomProperties && featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE]) { - return featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE]; - } - return null; - } - /** * Creates the features from geoJSON */ @@ -123,7 +84,7 @@ export class RawLayer { const newProperties = {}; // consider custom properties that were saved before const savedProperties = RawLayer.getCustomProperties(feature); - newProperties[Config.GEO_JSON_PREFIX] = {...savedProperties,...properties}; + newProperties[Config.GEO_JSON_PREFIX] = {...savedProperties, ...properties}; feature.setProperties(newProperties); } @@ -135,4 +96,63 @@ export class RawLayer { const featureProperties = feature.getProperties(); return featureProperties[Config.GEO_JSON_PREFIX]; } + + + /** + * Determines which color style should be applied. If no custom color property is provided, the default + * color is used. + * @param featureCustomProperties + */ + public static determineColorStyle(featureCustomProperties: { [key: string]: any }): Style { + if (featureCustomProperties && featureCustomProperties[CustomProperties.COLOR_STROKE]) { + return new Style({ + stroke: new Stroke({ + color: featureCustomProperties[CustomProperties.COLOR_STROKE], + width: 1 + }), + fill: new Fill({ + color: featureCustomProperties[CustomProperties.COLOR_FILL] + }) + }); + } else if (featureCustomProperties && featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE]) { + return new Style({ + stroke: new Stroke({ + color: featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE], + width: 1 + }), + fill: new Fill({ + color: featureCustomProperties[CustomProperties.DEFAULT_COLOR_FILL] + }) + }); + } else { + throw new Error('no color properties are defined'); + } + } + + /** + * Saves a default color property to geoJSON + */ + public saveDefaultColor(color: number[]) { + const features = RawLayer.geoJSONToFeatures(this.data); + features.forEach(feature => { + // save default color in feature properties + const properties = {}; + properties[CustomProperties.DEFAULT_COLOR_STROKE] = color; + properties[CustomProperties.DEFAULT_COLOR_FILL] = [...color.slice(0, 3), Config.DEFAULT_FILL_OPACITY_TO_STROKE]; + RawLayer.saveCustomProperties(feature, properties); + }); + this.data = RawLayer.featuresToGeoJSON(features); + } + + /** + * Reads the default color property from geoJSON + */ + public getDefaultColor(): number[] { + const features = RawLayer.geoJSONToFeatures(this.data); + const featureCustomProperties = RawLayer.getCustomProperties(features[0]); + if (featureCustomProperties && featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE]) { + return featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE]; + } + return null; + } }