forked from hpi-sam/digital-fuesim-manv
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathelement-manager.ts
132 lines (122 loc) · 4.67 KB
/
element-manager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import type { Immutable, JsonObject } from 'digital-fuesim-manv-shared';
import type { Feature } from 'ol';
import type { Geometry, Point } from 'ol/geom';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import type { Observable, Subject } from 'rxjs';
import { pairwise, startWith, takeUntil } from 'rxjs';
import { handleChanges } from 'src/app/shared/functions/handle-changes';
import { generateChangedProperties } from '../utility/generate-changed-properties';
/**
* Provides an Api to update a feature based on changes to an element (patient, vehicle, etc.).
*
* {@link Element} is the immutable JSON object (Patient, Vehicle, etc.)
* {@link Feature<FeatureType>} is the OpenLayers Feature that should be rendered to represent the {@link Element}.
*/
export abstract class ElementManager<
Element extends Immutable<JsonObject>,
FeatureType extends Geometry
> {
/**
* This should be called if a new element is added.
*/
public onElementCreated(element: Element) {
const feature = this.createFeature(element);
feature.set(featureElementKey, element);
}
/**
* This should be called if an element is deleted.
* It is not necessary to make sure wether the element has been created before or not.
*/
public onElementDeleted(element: Element): void {
const elementFeature = this.getFeatureFromElement(element);
if (!elementFeature) {
return;
}
this.deleteFeature(element, elementFeature);
}
/**
* This should be called if an element is changed.
*/
public onElementChanged(oldElement: Element, newElement: Element): void {
const elementFeature = this.getFeatureFromElement(oldElement);
if (!elementFeature) {
// If the element is not yet rendered on the map - we have to create it first
this.onElementCreated(newElement);
return;
}
const changedProperties = generateChangedProperties(
oldElement,
newElement
);
elementFeature.set(featureElementKey, newElement);
this.changeFeature(
oldElement,
newElement,
changedProperties,
elementFeature
);
}
/**
* Adds a new feature representing the {@link element} to the map.
*/
abstract createFeature(element: Element): Feature<FeatureType>;
/**
* Delete the {@link elementFeature} representing the {@link element} from the map.
*/
abstract deleteFeature(
element: Element,
elementFeature: Feature<FeatureType>
): void;
/**
* @param changedProperties The properties that have changed between the {@link oldElement} and the {@link newElement}
* @param elementFeature The openLayers feature that should be updated to reflect the changes
*/
abstract changeFeature(
oldElement: Element,
newElement: Element,
changedProperties: ReadonlySet<keyof Element>,
elementFeature: Feature<FeatureType>
): void;
abstract getFeatureFromElement(
element: Element
): Feature<FeatureType> | undefined;
public getElementFromFeature(feature: Feature<any>) {
return feature.get(featureElementKey);
}
/**
* @param renderBuffer The size of the largest symbol, line width or label on the highest zoom level.
*/
protected createElementLayer<LayerGeometry extends Geometry = Point>(
renderBuffer = 250
) {
return new VectorLayer({
// These two settings prevent clipping during animation/interaction but cause a performance hit -> disable if needed
updateWhileAnimating: true,
updateWhileInteracting: true,
renderBuffer,
source: new VectorSource<LayerGeometry>(),
});
}
protected registerChangeHandlers(
elementDictionary$: Observable<{ [id: string]: Element }>,
destroy$: Subject<void>,
createHandler?: (newElement: Element) => void,
deleteHandler?: (deletedElement: Element) => void,
changeHandler?: (oldElement: Element, newElement: Element) => void
) {
elementDictionary$
.pipe(startWith({}), pairwise(), takeUntil(destroy$))
.subscribe(([oldElementDictionary, newElementDictionary]) => {
handleChanges(oldElementDictionary, newElementDictionary, {
createHandler,
deleteHandler,
changeHandler,
});
});
}
}
/**
* The keys of the feature, where the type and most recent value of the respective element are saved to
*/
export const featureElementKey = 'element';