introduce feature management

This commit is contained in:
Fabian Jeglinski
2019-05-10 16:26:49 +02:00
parent 8c273f5d5e
commit f698beb068
14 changed files with 248 additions and 72 deletions
+5 -1
View File
@@ -18,13 +18,17 @@ import {
} from "@angular/material";
import {DragDropModule} from "@angular/cdk/drag-drop";
import { AddLayerComponent } from './add-layer-component/add-layer.component';
import {FeatureManagerComponent} from "./feature-management/feature-manager/feature-manager.component";
import {CustomColorEntryComponent} from "./feature-management/custom-color-entry/custom-color-entry.component";
@NgModule({
declarations: [
AppComponent,
MapComponent,
LeafletMapComponent,
AddLayerComponent
AddLayerComponent,
FeatureManagerComponent,
CustomColorEntryComponent
],
imports: [
BrowserModule,
+7
View File
@@ -0,0 +1,7 @@
/**
* An enum that presents all custom property keys
*/
export enum CustomProperties {
DEFAULT_COLOR_STROKE = 'default-style-stroke',
DEFAULT_COLOR_FILL = 'default-style-fill'
}
@@ -0,0 +1,11 @@
<div class="entry">
Default Color:
<div class="color-preview" [ngStyle]="{
'background-color': fillColorAsRgba,
'border-color': strokeColorAsRgba
}"></div>
<div class="controls">
<i class="material-icons" (click)="startEditDialog()">edit</i>
<i class="material-icons">delete</i>
</div>
</div>
@@ -0,0 +1,19 @@
.entry {
display: flex;
height: 3rem;
align-items: center;
margin: 0 0 .125rem 0;
border: 1px black solid;
padding: .5rem;
.controls {
margin-left: auto;
}
.color-preview {
margin-left: .5rem;
height: 2rem;
width: 2rem;
border: 2px solid;
}
}
@@ -0,0 +1,47 @@
import {Component, EventEmitter, Input, Output} from "@angular/core";
import {CustomProperties} from "../../enums/CustomProperties";
import {Utils} from "../../misc/Utils";
/**
* 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.
*/
@Component({
selector: 'lm-custom-color-entry',
templateUrl: 'custom-color-entry.component.html',
styleUrls: ['custom-color-entry.component.scss']
})
export class CustomColorEntryComponent {
_colors: any;
@Input() set colors(value: any) {
this._colors = value;
}
get colors(): any {
return this._colors;
}
get strokeColor(): number[] {
return this.colors[CustomProperties.DEFAULT_COLOR_STROKE];
}
get strokeColorAsRgba(): string {
return Utils.colorArrayToRgba(this.strokeColor);
}
get fillColor(): number[] {
return this.colors[CustomProperties.DEFAULT_COLOR_FILL];
}
get fillColorAsRgba(): string {
return Utils.colorArrayToRgba(this.fillColor);
}
@Output('colorChanged') colorChangeEmitter: EventEmitter<any> = new EventEmitter();
startEditDialog() {
this.colorChangeEmitter.emit(this.colors);
}
}
@@ -0,0 +1,28 @@
<div *ngIf="curFeature">
<div class="header">
<h4>Properties</h4>
</div>
<div *ngFor="let item of curProperties | keyvalue" class="entry">
{{item.key}}
<br>
{{item.value}}
<div class="controls">
<i class="material-icons">edit</i>
<i class="material-icons">delete</i>
</div>
</div>
<div class="header">
<h4>Custom Properties</h4>
</div>
<!--<div *ngFor="let item of curCustomProperties | keyvalue" class="entry">-->
<!-- {{item.key}}-->
<!-- <br>-->
<!-- {{item.value}}-->
<!-- <div class="controls">-->
<!-- <i class="material-icons">edit</i>-->
<!-- <i class="material-icons">delete</i>-->
<!-- </div>-->
<!--</div>-->
<lm-custom-color-entry [colors]="curCustomDefaultColor"
(colorChanged)="defaultColorChanged($event)"></lm-custom-color-entry>
</div>
@@ -0,0 +1,17 @@
.header,
.entry {
display: flex;
height: 3rem;
align-items: center;
margin: 0 0 .125rem 0;
border: 1px black solid;
padding: .5rem;
.controls {
margin-left: auto;
}
}
.header{
background-color: whitesmoke;
}
@@ -0,0 +1,66 @@
import {Component, Input} from "@angular/core";
// import {Feature} from 'ol/Feature';
import {Config} from "../../misc/Config";
import {CustomProperties} from "../../enums/CustomProperties";
import {Feature} from 'ol/Feature';
/**
* This component manages all properties of the current feature. It does not matter if it is a simple geo json
* property or a custom property (custom properties are special properties that are just understand by life map
* like color of the feature). All changes that were made to the properties are saved in the geo json of
* the RawLayer object from which the open layer feature based.
*/
@Component({
selector: 'lm-feature-manager',
templateUrl: 'feature-manager.component.html',
styleUrls: ['feature-manager.component.scss']
})
export class FeatureManagerComponent {
/**
* The feature that is selected to manage
*/
private _curFeature: Feature;
@Input() set curFeature(value: Feature) {
this._curFeature = value;
}
get curFeature(): Feature {
return this._curFeature;
}
/**
* Returns the properties of curFeature
*/
get curProperties(): any {
const properties = this.curFeature.getProperties();
delete properties.geometry;
delete properties[Config.GEO_JSON_PREFIX];
return properties;
}
/**
* Returns the custom properties of curFeature
*/
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 {
const colors = {};
colors[CustomProperties.DEFAULT_COLOR_STROKE] = this.curCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE];
colors[CustomProperties.DEFAULT_COLOR_FILL] = this.curCustomProperties[CustomProperties.DEFAULT_COLOR_FILL];
return colors;
}
defaultColorChanged(colors: any) {
console.log('Default colors changed!', colors);
}
}
+2 -10
View File
@@ -16,21 +16,13 @@
</div>
</div>
<!-- layer property area-->
<div class="layer-property-area">
<div>
<mat-card>
<mat-card-header>
<mat-card-title>Layer Properties</mat-card-title>
</mat-card-header>
<mat-card-content>
<div *ngFor="let item of currentFeatureInfo | keyvalue" class="entry">
{{item.key}}
<br>
{{item.value}}
<div class="controls">
<i class="material-icons">edit</i>
<i class="material-icons">delete</i>
</div>
</div>
<lm-feature-manager [curFeature]="cache.selectedFeature"></lm-feature-manager>
</mat-card-content>
</mat-card>
</div>
-19
View File
@@ -71,25 +71,6 @@
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
}
.layer-property-area {
.mat-card-content {
.entry {
display: flex;
height: 3rem;
align-items: center;
margin: 0 0 .125rem 0;
border: 1px black solid;
padding: .5rem;
.controls {
margin-left: auto;
}
}
}
}
}
.cdk-drag-preview {
+20 -33
View File
@@ -17,6 +17,7 @@ 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";
/**
* TODO: add description
@@ -29,6 +30,7 @@ import Fill from 'ol/style/Fill';
export class MapComponent implements OnInit {
@ViewChild('mapContainer') mapContainer: ElementRef<HTMLDivElement>;
@ViewChild(FeatureManagerComponent) featureManager: FeatureManagerComponent;
public map: Map;
public baseLayer: TileLayer;
@@ -49,15 +51,10 @@ export class MapComponent implements OnInit {
*/
public cursorCoordinates: string;
/**
* The properties of the current feature. The current layer is the layer beneath the cursor.
*/
public currentFeatureInfo: any;
/**
* A simple cache that holds different Features and styles
*/
private cache : { [key: string]: Feature | Style } = {
private cache: { [key: string]: Feature | Style } = {
/**
* Contains the feature that is currently selected
*/
@@ -76,12 +73,12 @@ export class MapComponent implements OnInit {
/**
* Default style for selected feature
*/
defaultSelectedStyle : null,
defaultSelectedStyle: null,
/**
* Default style for a highlighted feauture
*/
defaultHighlightStyle:null
defaultHighlightStyle: null
};
@@ -157,7 +154,7 @@ export class MapComponent implements OnInit {
const features = this.map.getFeaturesAtPixel(pixel);
if (features && features.length > 0) {
this.map.getTargetElement().style.cursor = 'pointer';
this.displayFeatureInfo(features[0]);
this.featureManager.curFeature = features[0];
this.highlightFeature(features[0]);
} else {
this.map.getTargetElement().style.cursor = '';
@@ -168,7 +165,7 @@ export class MapComponent implements OnInit {
// reset cache for feature and style
this.cache.lastHighlightedFeature = null;
this.cache.lastHighlightedStyle = null;
this.currentFeatureInfo = null;
this.featureManager.curFeature = null;
}
});
@@ -177,31 +174,21 @@ export class MapComponent implements OnInit {
const pixel = this.map.getEventPixel(evt.originalEvent);
const features = this.map.getFeaturesAtPixel(pixel);
if (this.cache.selectedFeature) {
// use lastHighlightedStyle because the original style is saved there
this.cache.selectedFeature.setStyle(this.cache.lastHighlightedStyle);
// this.styleCache['lastHighlightedStyle'] = null;
this.cache.selectedFeature = null;
} else if (features && features.length > 0) {
this.map.getTargetElement().style.cursor = '';
this.displayFeatureInfo(features[0]);
this.cache.selectedFeature = features[0];
this.cache.lastHighlightedFeature = null;
features[0].setStyle(this.cache.defaultSelectedStyle);
}
if (this.cache.selectedFeature) {
// use lastHighlightedStyle because the original style is saved there
this.cache.selectedFeature.setStyle(this.cache.lastHighlightedStyle);
// this.styleCache['lastHighlightedStyle'] = null;
this.cache.selectedFeature = null;
} else if (features && features.length > 0) {
this.map.getTargetElement().style.cursor = '';
this.featureManager.curFeature = features[0];
this.cache.selectedFeature = features[0];
this.cache.lastHighlightedFeature = null;
features[0].setStyle(this.cache.defaultSelectedStyle);
}
});
}
/**
* Prepared feature properties and shows them
* @param features
*/
private displayFeatureInfo(feature: Feature) {
const properties = feature.getProperties();
delete properties.geometry;
this.currentFeatureInfo = properties;
};
/**
* Highlights a Features
* @param feature
@@ -416,7 +403,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);
+2 -1
View File
@@ -1,3 +1,4 @@
export class Config{
static readonly GEO_JSON_PREFIX: string = 'GANE';
static readonly GEO_JSON_PREFIX: string = 'LM';
static readonly DEFAULT_FILL_OPACITY_TO_STROKE: number = 0.1;
}
+16
View File
@@ -25,4 +25,20 @@ export class Utils {
opacity
] : null;
}
public static colorArrayToRgba(colors: number[]): string{
let tmp = 'rgba(';
for(let i = 0; i < 4; i++){
const color = colors[i];
if(color){
tmp+= color+',';
}else {
tmp += '1';
}
}
// remove last comma
tmp = tmp.replace(/,\s*$/, "");
tmp += ')';
return tmp;
}
}
+8 -8
View File
@@ -7,6 +7,7 @@ import Stroke from 'ol/style/Stroke';
import Fill from 'ol/style/Fill';
import {Config} from "../misc/Config";
import {Feature} from 'ol/Feature';
import {CustomProperties} from "../enums/CustomProperties";
/**
* Represents a raw (not processed) map layer
@@ -51,11 +52,11 @@ export class RawLayer {
if (featureCustomProperties && featureCustomProperties['default-style-stroke']) {
feature.setStyle(new Style({
stroke: new Stroke({
color: featureCustomProperties['default-style-stroke'],
color: featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE],
width: 1
}),
fill: new Fill({
color: featureCustomProperties['default-style-fill']
color: featureCustomProperties[CustomProperties.DEFAULT_COLOR_FILL]
})
}));
}
@@ -78,10 +79,9 @@ export class RawLayer {
const features = RawLayer.geoJSONToFeatures(this.data);
features.forEach(feature => {
// save default color in feature properties
const properties = {
'default-style-stroke': color,
'default-style-fill': [...color.slice(0, 3), 0.1]
};
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);
@@ -93,8 +93,8 @@ export class RawLayer {
public getDefaultColor(): number[] {
const features = RawLayer.geoJSONToFeatures(this.data);
const featureCustomProperties = RawLayer.getCustomProperties(features[0]);
if (featureCustomProperties && featureCustomProperties['default-style-stroke']) {
return featureCustomProperties['default-style-stroke'];
if (featureCustomProperties && featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE]) {
return featureCustomProperties[CustomProperties.DEFAULT_COLOR_STROKE];
}
return null;
}