Add customization of networkmap
[ccsdk/features.git] / sdnr / wt / odlux / apps / networkMapApp / src / utils / mapLayers.ts
1 /**
2  * ============LICENSE_START========================================================================
3  * ONAP : ccsdk feature sdnr wt odlux
4  * =================================================================================================
5  * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved.
6  * =================================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software distributed under the License
13  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
14  * or implied. See the License for the specific language governing permissions and limitations under
15  * the License.
16  * ============LICENSE_END==========================================================================
17  */
18
19 import * as mapboxgl from 'mapbox-gl';
20 import { Feature } from 'model/Feature';
21 import { NetworkMapThemes, ThemeElement } from 'model/settings';
22
23 const fibreLinkColor = "#1154d9";
24 const microwaveLinkColor = "#039903";
25
26 class MapLayerService {
27
28     checkedLayers = false;
29     settings: NetworkMapThemes;
30     selectedTheme: string | null = null;
31
32     public addBaseSources = (map: mapboxgl.Map, selectedPoint: Feature | null, selectedLine: Feature | null) => {
33
34
35         // make sure the sources don't already exist
36         // (if the networkmap app gets opened quickly within short time periods, the prior sources might not be fully removed)
37
38         if (!map.getSource("lines")) {
39
40             map.addSource('lines', {
41                 type: 'geojson',
42                 data: { type: "FeatureCollection", features: [] }
43             });
44         }
45
46         if (!map.getSource("selectedLine")) {
47             const features = selectedLine !== null ? [selectedLine] : [];
48             map.addSource('selectedLine', {
49                 type: 'geojson',
50                 data: { type: "FeatureCollection", features: features }
51             });
52         }
53
54         if (!map.getSource("points")) {
55             map.addSource('points', {
56                 type: 'geojson',
57                 data: { type: "FeatureCollection", features: [] }
58             });
59         }
60
61         if (!map.getSource("selectedPoints")) {
62             const selectedPointFeature = selectedPoint !== null ? [selectedPoint] : [];
63             map.addSource('selectedPoints', {
64                 type: 'geojson',
65                 data: { type: "FeatureCollection", features: selectedPointFeature }
66
67             });
68         }
69
70         if (!map.getSource("alarmedPoints")) {
71             map.addSource("alarmedPoints", {
72                 type: 'geojson',
73                 data: { type: "FeatureCollection", features: [] }
74             });
75         }
76     }
77
78     private addCircleLayer = (map: mapboxgl.Map, id: string, source: string, circleColor: string, radius: number, strokeWidth: number, outerColor: string) => {
79
80         map.addLayer({
81             id: id,
82             source: source,
83             type: 'circle',
84             paint: {
85                 'circle-color': circleColor,
86                 'circle-radius': radius,
87                 'circle-stroke-width': strokeWidth,
88                 'circle-stroke-color': outerColor
89             }
90         });
91     }
92
93     private addLineLayer = (map: mapboxgl.Map, id: string, source: string, color: string, width: number, filter: string[]) => {
94
95         map.addLayer({
96             'id': id,
97             'type': 'line',
98             'source': source,
99             'layout': {
100                 'line-join': 'round',
101                 'line-cap': 'round'
102             },
103             'paint': {
104                 'line-color': color,
105                 'line-width': width
106             },
107             'filter': filter
108         });
109     }
110
111     private addIconLayer = (map: mapboxgl.Map, id: string, source: string, iconName: string, iconSize: number, filter: (string | string[])[]) => {
112         map.addLayer({
113             'id': id,
114             'type': 'symbol',
115             'source': source,
116             'layout': {
117                 'icon-allow-overlap': true,
118                 'icon-image': iconName,
119                 'icon-size': iconSize
120
121             },
122             'filter': filter,
123         });
124     }
125
126     /**
127      * Pick the correct theme based on user selection
128      */
129     private pickTheme = () => {
130         if (this.selectedTheme !== null) {
131             const result = this.settings.networkMapThemes.themes.find(el => el.key === this.selectedTheme);
132             if (result)
133                 return result;
134
135         }
136
137         return this.settings.networkMapThemes.themes[0];
138
139     }
140
141     public addBaseLayers = (map: mapboxgl.Map, themesettings?: ThemeElement) => {
142
143         const theme = !themesettings ? this.pickTheme() : themesettings;
144
145         this.addCommonLayers(map);
146
147         this.addCircleLayer(map, 'points', 'points', theme.site, 7, 1, '#fff');
148         this.addCircleLayer(map, 'selectedPoints', 'selectedPoints', theme.selectedSite, 9, 1, '#fff');
149         this.addCircleLayer(map, 'alarmedPoints', 'alarmedPoints', '#CC0000', 9, 1, '#fff');
150     }
151
152     public addIconLayers = (map: mapboxgl.Map, selectedSiteId?: string) => {
153
154         this.addCommonLayers(map);
155         this.createIconLayers(map, selectedSiteId);
156     }
157
158     private createIconLayers = (map: mapboxgl.Map, selectedSiteId?: string) => {
159
160         this.addIconLayer(map, 'point-lamps', 'points', 'lamp', 0.1, this.createFilter("street lamp", selectedSiteId));
161         this.addIconLayer(map, 'point-building', 'points', 'house', 0.1, this.createFilter("high rise building", selectedSiteId));
162         this.addIconLayer(map, 'point-data-center', 'points', 'data-center', 0.1, this.createFilter("data center", selectedSiteId));
163         this.addIconLayer(map, 'point-factory', 'points', 'factory', 0.2, this.createFilter("factory", selectedSiteId));
164
165
166         //select layers
167         this.addIconLayer(map, 'select-point-lamps', 'selectedPoints', 'lamp', 0.15, ['==', 'type', 'street lamp']);
168         this.addIconLayer(map, 'select-point-buildings', 'selectedPoints', 'house', 0.15, ['==', 'type', 'high rise building']);
169         this.addIconLayer(map, 'select-point-data-center', 'selectedPoints', 'data-center', 0.15, ['==', 'type', 'data center']);
170         this.addIconLayer(map, 'select-point-factory', 'selectedPoints', 'factory', 0.3, ['==', 'type', 'factory']);
171
172         //alarm layers
173         this.addIconLayer(map, 'point-lamps-alarm', 'alarmedPoints', 'lamp-red', 0.3, this.createFilter("street lamp"));
174         this.addIconLayer(map, 'point-building-alarm', 'alarmedPoints', 'house-red', 0.3, this.createFilter("high rise building"));
175         this.addIconLayer(map, 'point-data-center-alarm', 'alarmedPoints', 'data-center-red', 0.3, this.createFilter("data center"));
176         this.addIconLayer(map, 'point-factory-alarm', 'alarmedPoints', 'factory-red', 0.45, this.createFilter("factory"));
177
178         map.addLayer({
179             id: 'point-remaining',
180             source: 'points',
181             type: 'circle',
182             'filter': ['none', ['==', 'type', "high rise building"], ['==', 'type', "data center"], ['==', 'type', "factory"], ['==', 'type', "street lamp"]],
183             paint: {
184                 'circle-color': '#11b4da',
185                 'circle-radius': 7,
186                 'circle-stroke-width': 1,
187                 'circle-stroke-color': '#fff'
188             }
189         });
190     }
191
192     private addCommonLayers = (map: mapboxgl.Map, themesettings?: ThemeElement) => {
193
194         const theme = !themesettings ? this.pickTheme() : themesettings;
195
196         this.addLineLayer(map, 'microwave-lines', 'lines', theme.microwaveLink, 2, ['==', 'type', 'microwave']);
197         this.addLineLayer(map, 'fibre-lines', 'lines', theme.fiberLink, 2, ['==', 'type', 'fibre']);
198         this.addLineLayer(map, 'selectedLineMicrowave', 'selectedLine', theme.microwaveLink, 4, ['==', 'type', 'microwave']);
199         this.addLineLayer(map, 'selectedLineFibre', 'selectedLine', theme.fiberLink, 4, ['==', 'type', 'fibre']);
200     }
201
202     public removeBaseLayers = (map: mapboxgl.Map) => {
203
204         map.removeLayer("points");
205         map.removeLayer("lines");
206         map.removeLayer('selectedPoints');
207         map.removeLayer('selectedLine');
208     }
209
210     private removeIconLayers = (map: mapboxgl.Map) => {
211
212         map.removeLayer('point-building');
213         map.removeLayer('point-lamps');
214         map.removeLayer('point-data-center');
215         map.removeLayer('point-factory');
216         map.removeLayer('point-remaining');
217         map.removeLayer('select-point-data-center');
218         map.removeLayer('select-point-buildings');
219         map.removeLayer('select-point-lamps');
220         map.removeLayer('select-point-factory');
221         map.removeLayer('point-building-alarm');
222         map.removeLayer('point-lamps-alarm');
223         map.removeLayer('point-data-center-alarm');
224         map.removeLayer('point-factory-alarm');
225     }
226
227
228     private createFilter = (type: 'street lamp' | 'high rise building' | 'data center' | 'factory', selectedSiteId?: string) => {
229
230         return selectedSiteId === undefined ? ['==', 'type', type] : ["all", ['==', 'type', type], ['!=', 'id', selectedSiteId]]
231     }
232
233     public showIconLayers = (map: mapboxgl.Map, show: boolean, selectedSiteId?: string) => {
234
235         const zoom = map.getZoom();
236
237         if (show) {
238
239             if (zoom > 11) {
240
241                 const bounds = map.getBounds();
242
243                 if (map.getLayer('points') !== undefined && map.getLayer('point-lamps') === undefined && !this.checkedLayers) {
244
245                     // if sites don't have a type don't change layers to icons
246                     const elements = map.queryRenderedFeatures(undefined, {
247                         layers: ['points'], filter: ['has', 'type']
248                     });
249                     this.checkedLayers = true;
250
251                     if (elements.length > 0 && elements.length < 1000) {
252
253                         if (map.getLayer('point-lamps') === undefined) {
254                             map.removeLayer('points');
255                             map.setLayoutProperty('alarmedPoints', 'visibility', 'none');
256                             map.setLayoutProperty('selectedPoints', 'visibility', 'none');
257                             this.createIconLayers(map, selectedSiteId);
258                             //map.moveLayer('point-remaining','selectedPoints');
259
260                         }
261                     }
262                 }
263
264             } else {
265                 this.swapLayersBack(map);
266             }
267         } else {
268             this.swapLayersBack(map);
269         }
270     }
271
272     public swapLayersBack = (map: mapboxgl.Map) => {
273         this.checkedLayers = false;
274         const theme = this.pickTheme();
275
276         if (map.getLayer('selectedPoints') === undefined) {
277             this.addCircleLayer(map, 'selectedPoints', 'selectedPoints', theme.selectedSite, 9, 1, '#fff');
278
279         }
280
281         if (map.getLayer('alarmedPoints') === undefined) {
282             this.addCircleLayer(map, 'alarmedPoints', 'alarmedPoints', '#CC0000', 9, 1, '#fff');
283
284         }
285
286
287         if (map.getLayer('points') === undefined) {
288
289             map.setLayoutProperty('selectedPoints', 'visibility', 'visible');
290             map.setLayoutProperty('alarmedPoints', 'visibility', 'visible');
291             this.removeIconLayers(map);
292
293             this.addCircleLayer(map, 'points', 'points', theme.site, 7, 1, '#fff');
294
295
296             map.moveLayer('points', map.getLayer('selectedPoints').id);
297         }
298     }
299
300     public changeMapOpacity = (map: mapboxgl.Map, newValue: number) => {
301         const newOpacity = newValue / 100;
302         if (map) {
303             const tiles = map.getStyle().layers?.filter(el => el.id.includes("tiles"))
304             tiles?.forEach(layer => {
305                 if (layer.type === 'symbol') {
306                     map.setPaintProperty(layer.id, `icon-opacity`, newOpacity);
307                     map.setPaintProperty(layer.id, `text-opacity`, newOpacity);
308                 } else {
309                     map.setPaintProperty(layer.id, `${layer.type}-opacity`, newOpacity);
310                 }
311             })
312         }
313
314     }
315
316     public changeTheme = (map: mapboxgl.Map, themeName: string) => {
317         this.selectedTheme = themeName;
318         const theme = this.pickTheme();
319         if (theme && map.loaded()) {
320             map.setPaintProperty('points', 'circle-color', theme.site);
321             map.setPaintProperty('selectedPoints', 'circle-color', theme.selectedSite);
322             map.setPaintProperty('microwave-lines', 'line-color', theme.microwaveLink);
323             map.setPaintProperty('fibre-lines', 'line-color', theme.fiberLink);
324         }
325     }
326 }
327
328 const mapLayerService = new MapLayerService();
329 export default mapLayerService;
330