diff options
author | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2021-05-07 13:56:52 +0200 |
---|---|---|
committer | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2021-05-07 13:56:52 +0200 |
commit | 2c1f3727a703634cf62c63cfc4a3d4bb30fe2d9c (patch) | |
tree | c8f9f47f11f7ff239ee9d71bcccc9236316f1729 | |
parent | 3cb21ac46f62e5c03afef0584af8472a3bb6b13b (diff) |
Add customization of networkmap
Add customization capabilities to the networkmap
Issue-ID: CCSDK-2938
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: Ibd3fe258d02939ea69b21de25569f20d9087f693
30 files changed, 1158 insertions, 557 deletions
diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/README.md b/sdnr/wt/odlux/apps/networkMapApp/icons/README.md index acfbcf823..85c75c4e9 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/README.md +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/README.md @@ -19,7 +19,7 @@ Copyright of icons is as followes: */ --> -datacenter.png, lamp.png, factory.png, datacenterred.png, lampred.png, factoryred.png, +datacenter.png, lamp.png and customize.png with all variations (different colors) Taken from MS Word diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts index bf398f5a4..9f956f813 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/apartment.png.d.ts @@ -1,2 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + declare const apartment: string; export default apartment;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png b/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png Binary files differnew file mode 100644 index 000000000..91dbf6824 --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png.d.ts new file mode 100644 index 000000000..16327f5af --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/customize.png.d.ts @@ -0,0 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +declare const customize: string; +export default customize;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts index a58a9f5af..dc7019766 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenter.png.d.ts @@ -1,2 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + declare const datacenter: string; export default datacenter;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts index 33f3061e2..74836be8d 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/datacenterred.png.d.ts @@ -1,2 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + declare const datacenterred: string; export default datacenterred;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts index b5c4f19d9..e341c5f95 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/factory.png.d.ts @@ -1,2 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + declare const factory: string; export default factory;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts index 1fac0a943..317ff7e85 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/factoryred.png.d.ts @@ -1,2 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + declare const factoryRed: string; export default factoryRed;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts index 9634b1275..eeedc7fcd 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/lamp.png.d.ts @@ -1,2 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + declare const lamp: string; export default lamp;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts b/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts index 12a8f91cb..b053df0b8 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/icons/lampred.png.d.ts @@ -1,2 +1,20 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + declare const lampred: string; export default lampred;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/App.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/App.tsx index 6caab5147..5840d1842 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/App.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/App.tsx @@ -17,7 +17,7 @@ */ import * as React from 'react'; -import Map from './components/map' +import Map from './components/map/map' import Details from './components/details/details' function MainView() { diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/actions/connectivityAction.ts b/sdnr/wt/odlux/apps/networkMapApp/src/actions/connectivityAction.ts index 8fcdc4c3b..63f52c8f9 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/actions/connectivityAction.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/actions/connectivityAction.ts @@ -33,6 +33,12 @@ export class IsTileServerReachableAction extends Action{ } } +export class IsBusycheckingConnectivityAction extends Action{ + constructor(public isBusy: boolean){ + super(); + } +} + export const verifyResponse = (response: Response) =>{ if(response.ok){ diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/actions/settingsAction.ts b/sdnr/wt/odlux/apps/networkMapApp/src/actions/settingsAction.ts new file mode 100644 index 000000000..5b8982368 --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/actions/settingsAction.ts @@ -0,0 +1,85 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { NetworkMapSettings, NetworkMapThemes, NetworkSettings } from '../model/settings'; +import { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; +import { settingsService } from '../services/settingsService'; + +export class SetMapSettingsAction extends Action{ + + constructor(public settings:NetworkMapSettings) { + super(); + } +} + +export class SetThemeSettingsAction extends Action{ + + constructor(public settings:NetworkMapThemes) { + super(); + } +} + +export class SetSettingsAction extends Action{ + + constructor(public settings:NetworkSettings) { + super(); + } +} + +export class SetBusyLoadingAction extends Action{ + + constructor(public busy: boolean) { + super(); + + } +} + + +export const getSettings = () => async (dispatcher: Dispatch) => { + dispatcher(new SetBusyLoadingAction(true)); + console.log("getting settings in action..") + settingsService.getMapSettings().then(result =>{ + if(result){ + if(result.networkMap && result.networkMapThemes){ + const mapSettings : NetworkSettings = { networkMap: result.networkMap, networkMapThemes: result.networkMapThemes} + dispatcher(new SetSettingsAction(mapSettings)); + }else if(result.networkMap){ + dispatcher(new SetMapSettingsAction(result)); + }else if(result.networkMapThemes){ + dispatcher(new SetThemeSettingsAction(result)); + } + } + else{ + console.warn("settings couldn't be loaded."); + } + dispatcher(new SetBusyLoadingAction(false)); + }); +} + +export const updateSettings = (mapSettings: NetworkMapSettings) => async (dispatcher: Dispatch) =>{ + + const result = await settingsService.updateMapSettings(mapSettings); + console.log("update settings"); + dispatcher(new SetMapSettingsAction(mapSettings)); + + console.log(result); + if(result){ + } + +} diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/customize/customizationView.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/customize/customizationView.tsx new file mode 100644 index 000000000..82e7b795b --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/customize/customizationView.tsx @@ -0,0 +1,291 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { Button, Grid, InputLabel, makeStyles, MenuItem, Select, Slider, TextField, Typography } from '@material-ui/core'; +import { NetworkMapSettings, ThemeElement } from '../../model/settings'; +import * as React from 'react' +import connect, { Connect, IDispatcher } from '../../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore'; +import { updateSettings } from '../../actions/settingsAction'; +import ThemeEntry from './themeElement' +import * as mapboxgl from 'mapbox-gl'; +import { OSM_STYLE } from '../../config'; +import mapLayerService from '../../utils/mapLayers'; +import { requestRest } from '../../../../../framework/src/services/restService'; +import { NavigateToApplication } from '../../../../../framework/src/actions/navigationActions'; + +type props = Connect<typeof mapProps, typeof mapDispatch>; +let map: mapboxgl.Map; +let myMapRef = React.createRef<HTMLDivElement>(); +const default_boundingbox = "12.882544785787754,52.21421979821472,13.775455214211949,52.80406241672602"; + + +const mapProps = (state: IApplicationStoreState) => ({ + settings: state.network.settings, +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + updateSettings: (mapSettings: NetworkMapSettings) => dispatcher.dispatch(updateSettings(mapSettings)), + navigateToApplication: (applicationName: string) => dispatcher.dispatch(new NavigateToApplication(applicationName)), + + +}); + +const styles = makeStyles({ + sectionMargin: { + marginTop: "30px", + marginBottom: "15px" + }, + elementMargin: { + + marginLeft: "10px" + } +}); + +const CustomizationView: React.FunctionComponent<props> = (props) => { + + const [opacity, setOpacity] = React.useState(Number(props.settings.mapSettings?.networkMap.tileOpacity) || 100); + const [theme, setTheme] = React.useState(props.settings.mapSettings?.networkMap.styling.theme || ''); + const [latitude, setLatitude] = React.useState<number>(Number(props.settings.mapSettings?.networkMap.startupPosition.latitude)|| 52.5); + const [longitude, setLongitude] = React.useState<number>(Number(props.settings.mapSettings?.networkMap.startupPosition.longitude)|| 13.35); + const [zoom, setZoom] = React.useState<number>(Number(props.settings.mapSettings?.networkMap.startupPosition.zoom) || 10); + + + //used to make opacity available within the map event-listeners + //(hook state values are snapshotted at initalization and not updated afterwards, thus use a ref here) + const myOpacityRef = React.useRef(opacity); + const setOpacityState = (data:any) => { + myOpacityRef.current = data; + setOpacity(data); + }; + + const classes = styles(); + const currentTheme = props.settings.themes.networkMapThemes.themes.find(el => el.key === theme); + + + React.useEffect(() => { + mapLayerService.settings = props.settings.themes; + + map = new mapboxgl.Map({ + container: myMapRef.current!, + style: OSM_STYLE as any, + center: [longitude, latitude], + zoom: zoom, + accessToken: '' + }); + + map.on('load', (ev) => { + + mapLayerService.addBaseSources(map, null, null); + if(props.settings.mapSettings?.networkMap.styling.theme !== theme){ + mapLayerService.addBaseLayers(map, currentTheme); + + }else{ + mapLayerService.addBaseLayers(map); + } + + mapLayerService.changeMapOpacity(map, myOpacityRef.current); + + getData(); + }); + + map.on('moveend', () => { + const center = map.getCenter(); + setZoom(Number(map.getZoom().toFixed(4))); + setLatitude(Number(center.lat.toFixed(4))); + setLongitude(Number(center.lng.toFixed(4))); + }); + + }, []); + + React.useEffect(() => { + recenterMap(); + }, [latitude, longitude, zoom]); + + const setState = () => { + if (props.settings.mapSettings?.networkMap.styling) { + setTheme(props.settings.mapSettings.networkMap.styling.theme); + mapLayerService.changeTheme(map, props.settings.mapSettings.networkMap.styling.theme); + } + + const propOpacity = props.settings.mapSettings?.networkMap.tileOpacity; + if (propOpacity) { + setOpacityState(propOpacity); + } + } + + React.useEffect(() => { + setState(); + }, [props.settings.mapSettings]); + + const onOpacityChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, newValue: number) => { + setOpacity(newValue); + mapLayerService.changeMapOpacity(map, newValue); + + }; + + const onChangeTheme = (e: any) => { + + const newTheme = e.target.value; + setTheme(newTheme); + mapLayerService.changeTheme(map, newTheme); + } + + const onCancel = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { + e.preventDefault(); + props.navigateToApplication("network"); + } + + const onSaveSettings = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { + e.preventDefault(); + + const updatedSettings: NetworkMapSettings = { + networkMap: { + tileOpacity: opacity.toString(), + styling: { theme: theme }, + startupPosition: { + latitude: latitude.toString(), + longitude: longitude.toString(), + zoom: zoom.toString() + } + } + }; + + console.log(updatedSettings); + + await props.updateSettings(updatedSettings) + props.navigateToApplication("network"); + + } + + const recenterMap = () => { + + if (!isNaN(latitude) && !isNaN(longitude) && !isNaN(zoom)) + + map.flyTo({ + center: [ + longitude, + latitude + ], zoom: zoom, + essential: false + }); + } + + + + const getData = () => { + + //get data of boundingbox from networkmap + + const links = requestRest<any>("/topology/network/links/geojson/" + default_boundingbox); + const sites = requestRest<any>("/topology/network/sites/geojson/" + default_boundingbox); + + Promise.all([links, sites]).then(results => { + if (map.getSource('lines')) { + (map.getSource('lines') as mapboxgl.GeoJSONSource).setData(results[0]); + } + + if (map.getSource('points')) { + (map.getSource('points') as mapboxgl.GeoJSONSource).setData(results[1]); + } + + if (map.getSource('selectedPoints')) { + (map.getSource('selectedPoints') as mapboxgl.GeoJSONSource).setData(results[1].features[0]); + } + }); + } + + /** + * Style property names to readable text + * @param text propretyName + * @returns readable text + */ + const styleText = (text: string) => { + const textParts = text.split(/(?=[A-Z])/); //split on uppercase character + const newText = textParts.join(" "); + return newText.charAt(0).toUpperCase() + newText.slice(1); + } + + + return (<> + <h3>Settings</h3> + <div style={{ display: 'flex', flexDirection: 'row', flexGrow: 1, height: "100%", position: 'relative' }}> + <div style={{ width: "60%", flexDirection: 'column', position:'relative' }}> + <Typography variant="body1" style={{ fontWeight: "bold" }} gutterBottom>Startup Position</Typography> + <div style={{ display: 'flex', flexDirection: 'row' }}> + <TextField type="number" value={latitude} onChange={(e) => setLatitude(e.target.value as any)} style={{ marginLeft: 10 }} label="Latitude" /> + <TextField type="number" value={longitude} onChange={(e) => setLongitude(e.target.value as any)} style={{ marginLeft: 5 }} label="Longitude" /> + <TextField type="number" value={zoom} onChange={(e) => setZoom(e.target.value as any)} style={{ marginLeft: 5 }} label="Zoom" /> + </div> + + <Typography className={classes.sectionMargin} variant="body1" style={{ fontWeight: "bold" }} gutterBottom> + Tile Opacity + </Typography> + <Grid className={classes.elementMargin} container spacing={2} style={{ width: '50%' }}> + <Grid item>0</Grid> + <Grid item xs> + <Slider color="secondary" min={0} max={100} value={opacity} onChange={onOpacityChange} aria-labelledby="continuous-slider" /> + </Grid> + <Grid item>100</Grid> + </Grid> + + <Typography className={classes.sectionMargin} variant="body1" style={{ fontWeight: "bold" }} gutterBottom> + Style of properties + </Typography> + <InputLabel id="theme-select-label">Theme</InputLabel> + <Select + className={classes.elementMargin} + value={theme} + onChange={onChangeTheme} + labelId="theme-select-label" + style={{ marginLeft: 10 }}> + { + props.settings.themes.networkMapThemes.themes.map(el => <MenuItem value={el.key}>{el.key}</MenuItem>) + } + + </Select> + + { + currentTheme && <div style={{ marginLeft: 60 }}> + { //skip the 'key' (theme name) entry + Object.keys(currentTheme).slice(1).map(el => <ThemeEntry text={styleText(el)} color={(currentTheme as any)[el]} />) + } + </div> + } + + + <div className={classes.sectionMargin} style={{ position: 'absolute', right: 0, top: '60%' }}> + <Button className={classes.elementMargin} variant="contained" + color="primary" onClick={onCancel}>Cancel</Button> + + <Button className={classes.elementMargin} variant="contained" + color="secondary" onClick={onSaveSettings}>Save</Button> + </div> + </div> + <div id="map" ref={myMapRef} style={{ width: "35%", height: "50%" }}> + + </div> + </div> + + </>) + +} + +export default connect(mapProps, mapDispatch)(CustomizationView); + + diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/customize/themeElement.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/customize/themeElement.tsx new file mode 100644 index 000000000..c991aaf63 --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/customize/themeElement.tsx @@ -0,0 +1,47 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { Typography } from '@material-ui/core'; +import * as React from 'react' + + +type props={ + color: string, + text: string +}; + +const ThemeEntry = (props: props) =>{ + + var circleStyle = { + padding:10, + margin:20, + backgroundColor: props.color, + borderRadius: "50%", + width:10, + height:10, + left:0, + top:0}; + + return <div style={{display: 'flex', flexDirection:'row'}}> + <div style={circleStyle} /> + <Typography variant="body1" style={{marginTop:24}}>{props.text}</Typography> + </div> + +} + +export default ThemeEntry;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/connectionInfo.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/connectionInfo.tsx index d1e2d978f..c1fdccfb8 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/connectionInfo.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/connectionInfo.tsx @@ -18,8 +18,8 @@ import * as React from 'react' -import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; -import connect, { IDispatcher, Connect } from "../../../../framework/src/flux/connect"; +import { IApplicationStoreState } from "../../../../../framework/src/store/applicationStore"; +import connect, { IDispatcher, Connect } from "../../../../../framework/src/flux/connect"; import { Paper, Typography } from "@material-ui/core"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/iconSwitch.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/iconSwitch.tsx index 8df1385d0..bb98cb467 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/iconSwitch.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/iconSwitch.tsx @@ -18,9 +18,9 @@ import * as React from 'react'; import { FormControlLabel, Switch, Paper } from "@material-ui/core"; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { SetIconSwitchAction } from '../actions/mapActions'; +import connect, { Connect, IDispatcher } from '../../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore'; +import { SetIconSwitchAction } from '../../actions/mapActions'; type props = Connect<typeof mapStateToProps, typeof mapDispatchToProps> & {visible: boolean} diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/map.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/map.tsx index 855e5cedf..9637d745e 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/map.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/map.tsx @@ -21,25 +21,28 @@ import * as mapboxgl from 'mapbox-gl'; import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { Site } from '../model/site'; -import { SelectSiteAction, ClearHistoryAction, SelectLinkAction } from '../actions/detailsAction'; -import { OSM_STYLE, URL_API, URL_BASEPATH, URL_TILE_API } from '../config'; -import { link } from '../model/link'; +import { Site } from '../../model/site'; +import { SelectSiteAction, ClearHistoryAction, SelectLinkAction } from '../../actions/detailsAction'; +import { OSM_STYLE, URL_API, URL_BASEPATH, URL_TILE_API } from '../../config'; +import { link } from '../../model/link'; import MapPopup from './mapPopup'; -import { SetPopupPositionAction, SelectMultipleLinksAction, SelectMultipleSitesAction } from '../actions/popupActions'; -import { Feature } from '../model/Feature'; -import { HighlightLinkAction, HighlightSiteAction, SetCoordinatesAction, SetStatistics } from '../actions/mapActions'; -import { addDistance, getUniqueFeatures, increaseBoundingBox } from '../utils/mapUtils'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import { SetPopupPositionAction, SelectMultipleLinksAction, SelectMultipleSitesAction } from '../../actions/popupActions'; +import { Feature } from '../../model/Feature'; +import { HighlightLinkAction, HighlightSiteAction, SetCoordinatesAction, SetStatistics } from '../../actions/mapActions'; +import { addDistance, getUniqueFeatures, increaseBoundingBox } from '../../utils/mapUtils'; +import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore'; +import connect, { IDispatcher, Connect } from '../../../../../framework/src/flux/connect'; import SearchBar from './searchBar'; -import { verifyResponse, IsTileServerReachableAction, handleConnectionError, setTileServerReachableAction } from '../actions/connectivityAction'; +import { verifyResponse, IsTileServerReachableAction, handleConnectionError, setTileServerReachableAction, IsBusycheckingConnectivityAction } from '../../actions/connectivityAction'; import ConnectionInfo from './connectionInfo' -import { showIconLayers, addBaseLayers, addBaseSources, addIconLayers } from '../utils/mapLayers'; +import mapLayerService from '../../utils/mapLayers'; import Statistics from './statistics'; import IconSwitch from './iconSwitch'; -import { addImages } from '../services/mapImagesService'; -import { PopupElement } from '../model/popupElements'; +import { addImages } from '../../services/mapImagesService'; +import { PopupElement } from '../../model/popupElements'; +import { Button } from '@material-ui/core'; +import { NavigateToApplication } from '../../../../../framework/src/actions/navigationActions'; +import customize from '../../../icons/customize.png'; type coordinates = { lat: number, lon: number, zoom: number } @@ -61,31 +64,52 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { } - componentDidMount() { + updateTheme(){ + mapLayerService.settings=this.props.settings.themes; + if(this.props.settings.mapSettings?.networkMap.styling.theme){ + mapLayerService.selectedTheme = this.props.settings.mapSettings?.networkMap.styling.theme; + } + } + + updateOpacity(){ + if(this.props.settings.mapSettings && this.props.settings.mapSettings.networkMap.tileOpacity){ + mapLayerService.changeMapOpacity(map, Number(this.props.settings.mapSettings.networkMap.tileOpacity)); + } + } + + async componentDidMount() { // resize the map, if menu gets collapsed window.addEventListener("menu-resized", this.handleResize); - // try if connection to tile + topologyserver is available + //pass themes to mapLayerService + this.updateTheme(); - fetch(URL_TILE_API + '/10/0/0.png') - .then(res => { - if (res.ok) { - this.setupMap(); - this.props.setTileServerLoaded(true); - } else { - this.props.setTileServerLoaded(false); - console.error("tileserver " + URL_TILE_API + " can't be reached."); - } - }) - .catch(err => { + // try if connection to tile + topologyserver are available + + try { + const tiles = await fetch(URL_TILE_API + '/10/0/0.png'); + if (tiles.ok) { + this.props.setTileServerLoaded(true); + }else{ this.props.setTileServerLoaded(false); - console.error("tileserver " + URL_TILE_API + " can't be reached."); - }); + } - fetch(URL_API + "/info/count/all") - .then(result => verifyResponse(result)) - .catch(error => this.props.handleConnectionError(error)); + } catch (error) { + this.props.setTileServerLoaded(false); + console.error("tileserver " + URL_TILE_API + " can't be reached."); + } + + try { + const topology = await fetch(URL_API + "/info/count/all"); + verifyResponse(topology); + } catch (error) { + this.props.handleConnectionError(error) + } + + //both done + this.props.setConnectivityCheck(false); + //map loaded in componentDidUpdate } setupMap = () => { @@ -94,6 +118,21 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { let lon = this.props.lon; let zoom = this.props.zoom; + if(this.props.settings.mapSettings){ + if(this.props.settings.mapSettings.networkMap.startupPosition.latitude){ + lat = Number(this.props.settings.mapSettings.networkMap.startupPosition.latitude) + } + + if(this.props.settings.mapSettings.networkMap.startupPosition.longitude){ + lon = Number(this.props.settings.mapSettings.networkMap.startupPosition.longitude) + } + + if(this.props.settings.mapSettings.networkMap.startupPosition.zoom){ + zoom = Number(this.props.settings.mapSettings.networkMap.startupPosition.zoom) + } + + } + const coordinates = this.extractCoordinatesFromUrl(); // override lat/lon/zoom with coordinates from url, if available if (this.areCoordinatesValid(coordinates)) { @@ -116,15 +155,17 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { const bbox = map.getBounds(); this.props.updateMapPosition(bbox.getCenter().lat, bbox.getCenter().lng, map.getZoom()) - addBaseSources(map, this.props.selectedSite, this.props.selectedLink); - + mapLayerService.addBaseSources(map, this.props.selectedSite, this.props.selectedLink); + addImages(map, (result: boolean)=>{ if(map.getZoom()>11) { - addIconLayers(map, this.props.selectedSite?.properties.id) + mapLayerService.addIconLayers(map, this.props.selectedSite?.properties.id) }else{ - addBaseLayers(map, this.props.selectedSite, this.props.selectedLink); + mapLayerService.addBaseLayers(map); } + this.updateOpacity(); + }); const boundingBox = increaseBoundingBox(map); @@ -229,7 +270,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { //switch icon layers if applicable - showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id); + mapLayerService.showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id); //update statistics const boundingBox = map.getBounds(); @@ -277,6 +318,29 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { componentDidUpdate(prevProps: mapProps, prevState: {}) { + //(load map) + //triggered if either settings were done loading or tile/topology server connectivity checked + if(prevProps.settings !== this.props.settings || this.props.isConnectivityCheckBusy !== prevProps.isConnectivityCheckBusy){ + + //update theme if settings changed + if(prevProps.settings !== this.props.settings){ + this.updateTheme(); + } + + //if everything done loading/reachable, load map + if(!this.props.isConnectivityCheckBusy && this.props.isTileServerReachable && !this.props.settings.isLoadingData && (prevProps.settings.isLoadingData !==this.props.settings.isLoadingData || prevProps.isConnectivityCheckBusy !== this.props.isConnectivityCheckBusy)){ + if(map == undefined){ + this.setupMap(); + } + else + if(map.getContainer() !== myRef.current){ + // reload map, because the current container (fresh div) doesn't hold the map and changing containers isn't supported + map.remove(); + this.setupMap(); + } + } + } + if (map !== undefined) { if (prevProps.selectedSite?.properties.id !== this.props.selectedSite?.properties.id) { @@ -361,7 +425,7 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { if (prevProps.showIcons !== this.props.showIcons) { if (map && map.getZoom() > 11) { - showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id); + mapLayerService.showIconLayers(map, this.props.showIcons, this.props.selectedSite?.properties.id); } } @@ -384,6 +448,9 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { componentWillUnmount(){ window.removeEventListener("menu-resized", this.handleResize); lastBoundingBox=null; + + // will be checked again on next load + this.props.setConnectivityCheck(true); } handleResize = () => { @@ -543,6 +610,9 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { return <> +{ + !this.props.settings.isLoadingData ? + <div id="map" style={{ width: "70%", position: 'relative' }} ref={myRef} > { this.state.isPopupOpen && @@ -552,7 +622,16 @@ class Map extends React.Component<mapProps, { isPopupOpen: boolean }> { <Statistics /> <IconSwitch visible={this.props.zoom>11} /> <ConnectionInfo /> + <Button + disabled={!this.props.isTopoServerReachable} + style={{'position': 'absolute', 'right':5, top:5, backgroundColor:'white'}} + onClick={e => this.props.navigateToApplication("network", "customize")} > + <img src={customize} /> + </Button> </div> + :<div style={{ width: "70%", position: 'relative' }} /> + + } </> } @@ -572,7 +651,9 @@ const mapStateToProps = (state: IApplicationStoreState) => ({ siteCount: state.network.map.statistics.sites, isTopoServerReachable: state.network.connectivity.isToplogyServerAvailable, isTileServerReachable: state.network.connectivity.isTileServerAvailable, - showIcons: state.network.map.allowIconSwitch + isConnectivityCheckBusy: state.network.connectivity.isBusy, + showIcons: state.network.map.allowIconSwitch, + settings: state.network.settings, }); const mapDispatchToProps = (dispatcher: IDispatcher) => ({ @@ -587,7 +668,10 @@ const mapDispatchToProps = (dispatcher: IDispatcher) => ({ updateMapPosition: (lat: number, lon: number, zoom: number) => dispatcher.dispatch(new SetCoordinatesAction(lat, lon, zoom)), setStatistics: (linkCount: string, siteCount: string) => dispatcher.dispatch(new SetStatistics(siteCount, linkCount)), setTileServerLoaded: (reachable: boolean) => dispatcher.dispatch(setTileServerReachableAction(reachable)), - handleConnectionError: (error: Error) => dispatcher.dispatch(handleConnectionError(error)) + handleConnectionError: (error: Error) => dispatcher.dispatch(handleConnectionError(error)), + navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path, "test3")), + setConnectivityCheck: (done: boolean) => dispatcher.dispatch(new IsBusycheckingConnectivityAction(done)), + }) export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Map));
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/mapPopup.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/mapPopup.tsx index 7435a0a3f..7a64f5a58 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/mapPopup.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/mapPopup.tsx @@ -18,14 +18,14 @@ import * as React from 'react'; import { Typography, Select, MenuItem, ClickAwayListener, Popper, Paper, FormGroup, Portal, Popover } from '@material-ui/core'; -import { SelectSiteAction, ClearHistoryAction, ClearDetailsAction } from '../actions/detailsAction'; -import { Site } from '../model/site'; -import { link } from '../model/link'; -import { URL_API } from '../config'; -import { HighlightLinkAction, HighlightSiteAction } from '../actions/mapActions'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; -import { verifyResponse, handleConnectionError } from '../actions/connectivityAction'; +import { SelectSiteAction, ClearHistoryAction, ClearDetailsAction } from '../../actions/detailsAction'; +import { Site } from '../../model/site'; +import { link } from '../../model/link'; +import { URL_API } from '../../config'; +import { HighlightLinkAction, HighlightSiteAction } from '../../actions/mapActions'; +import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore'; +import connect, { IDispatcher, Connect } from '../../../../../framework/src/flux/connect'; +import { verifyResponse, handleConnectionError } from '../../actions/connectivityAction'; diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/searchBar.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/searchBar.tsx index 2e698158d..45bc6092d 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/searchBar.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/searchBar.tsx @@ -20,16 +20,16 @@ import * as React from 'react'; import { makeStyles, Paper, InputBase, IconButton, Divider, Popover, Typography } from '@material-ui/core'; import SearchIcon from '@material-ui/icons/Search'; -import { URL_API } from '../config'; -import { isSite } from '../utils/utils'; -import { Site } from '../model/site'; -import { link } from '../model/link'; -import { SelectSiteAction, SelectLinkAction } from '../actions/detailsAction'; -import { HighlightLinkAction, HighlightSiteAction, ZoomToSearchResultAction } from '../actions/mapActions'; -import { calculateMidPoint } from '../utils/mapUtils'; -import { SetSearchValueAction } from '../actions/searchAction'; -import connect,{ Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { URL_API } from '../../config'; +import { isSite } from '../../utils/utils'; +import { Site } from '../../model/site'; +import { link } from '../../model/link'; +import { SelectSiteAction, SelectLinkAction } from '../../actions/detailsAction'; +import { HighlightLinkAction, HighlightSiteAction, ZoomToSearchResultAction } from '../../actions/mapActions'; +import { calculateMidPoint } from '../../utils/mapUtils'; +import { SetSearchValueAction } from '../../actions/searchAction'; +import connect,{ Connect, IDispatcher } from '../../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore'; diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/components/statistics.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/statistics.tsx index 4103d64f1..116b789d7 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/components/statistics.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/components/map/statistics.tsx @@ -20,8 +20,8 @@ import * as React from 'react'; import { Paper, Typography, Tooltip } from '@material-ui/core'; import InfoIcon from '@material-ui/icons/Info'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../../framework/src/store/applicationStore'; +import connect, { IDispatcher, Connect } from '../../../../../framework/src/flux/connect'; type props = Connect<typeof mapStateToProps, typeof mapDispatchToProps>; diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/handlers/connectivityReducer.ts b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/connectivityReducer.ts index 7214705e1..8ab82f2e9 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/handlers/connectivityReducer.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/connectivityReducer.ts @@ -17,12 +17,12 @@ */ import { IActionHandler } from "../../../../framework/src/flux/action"; -import { IsTopologyServerReachableAction, IsTileServerReachableAction } from "../actions/connectivityAction"; +import { IsTopologyServerReachableAction, IsTileServerReachableAction, IsBusycheckingConnectivityAction } from "../actions/connectivityAction"; -export type connectivityState = {isToplogyServerAvailable: boolean, isTileServerAvailable: boolean }; +export type connectivityState = {isToplogyServerAvailable: boolean, isTileServerAvailable: boolean, isBusy: boolean }; -const initialState: connectivityState = {isToplogyServerAvailable: true, isTileServerAvailable: true}; +const initialState: connectivityState = {isToplogyServerAvailable: true, isTileServerAvailable: true, isBusy: true}; export const ConnectivityReducer: IActionHandler<connectivityState> =(state=initialState, action)=> { @@ -32,6 +32,9 @@ export const ConnectivityReducer: IActionHandler<connectivityState> =(state=init else if (action instanceof IsTileServerReachableAction){ state = Object.assign({}, state, { isTileServerAvailable: action.reachable }); + }else if(action instanceof IsBusycheckingConnectivityAction){ + state = {...state, isBusy: action.isBusy} + } return state; diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/handlers/rootReducer.ts b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/rootReducer.ts index c9c475411..697dbd7a0 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/handlers/rootReducer.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/rootReducer.ts @@ -23,13 +23,15 @@ import { PopupsReducer, popupStoreState } from "./popupReducer"; import { MapReducer, mapState } from "./mapReducer"; import { SearchReducer, searchState } from "./searchReducer"; import { connectivityState, ConnectivityReducer } from './connectivityReducer'; +import { SettingsReducer, SettingsState } from './settingsReducer'; export interface INetworkAppStoreState{ details: DetailsStoreState, popup: popupStoreState, map: mapState, search: searchState, - connectivity: connectivityState + connectivity: connectivityState, + settings: SettingsState } declare module '../../../../framework/src/store/applicationStore' { @@ -43,7 +45,8 @@ const appHandler = { popup: PopupsReducer, map: MapReducer, search: SearchReducer, - connectivity: ConnectivityReducer}; + connectivity: ConnectivityReducer, + settings: SettingsReducer}; export const networkmapRootHandler = combineActionHandler<INetworkAppStoreState>(appHandler) diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/handlers/settingsReducer.ts b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/settingsReducer.ts new file mode 100644 index 000000000..977a379a0 --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/handlers/settingsReducer.ts @@ -0,0 +1,61 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { NetworkMapSettings, NetworkMapThemes } from "../model/settings"; +import { IActionHandler } from "../../../../framework/src/flux/action"; +import { SetBusyLoadingAction, SetMapSettingsAction, SetSettingsAction, SetThemeSettingsAction } from "../actions/settingsAction"; + +export type SettingsState = { + mapSettings: NetworkMapSettings|null, + themes: NetworkMapThemes, + isLoadingData: boolean +}; + + +const defaultThemes:NetworkMapThemes = {networkMapThemes:{themes: [ + + {key: "light", site: "#11b4da", selectedSite: "#116bda", fiberLink: "#1154d9", microwaveLink: "#039903"}, + {key: "dark", site: "#000000", selectedSite: "#6e6e6e", fiberLink: "#0a2a6b", microwaveLink: "#005200"}, +]}} + +const initialState: SettingsState = { + mapSettings: null, + themes: defaultThemes, + isLoadingData: true + +}; + +export const SettingsReducer: IActionHandler<SettingsState> = (state = initialState, action) => { + + if(action instanceof SetSettingsAction){ + state = { + isLoadingData: false, + mapSettings: {networkMap: action.settings.networkMap}, + themes:{networkMapThemes: {themes: action.settings.networkMapThemes.themes}} + }; + }else if(action instanceof SetMapSettingsAction){ + state={...state, mapSettings: action.settings}; + }else if(action instanceof SetThemeSettingsAction){ + state={...state, themes:{networkMapThemes: {themes: action.settings.networkMapThemes.themes}}}; + }else if(action instanceof SetBusyLoadingAction){ + state={...state, isLoadingData: action.busy}; + } + + return state; + +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/model/settings.ts b/sdnr/wt/odlux/apps/networkMapApp/src/model/settings.ts new file mode 100644 index 000000000..521f47ccc --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/model/settings.ts @@ -0,0 +1,34 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +export type NetworkMapSettings = { + networkMap: { + startupPosition: { latitude?: string, longitude?: string, zoom?: string }, + tileOpacity: string, + styling: { theme: string } } }; + +export type ThemeElement = { + key: string, + site: string, + selectedSite: string, + microwaveLink: string, + fiberLink: string}; + +export type NetworkMapThemes = {networkMapThemes: {themes: ThemeElement[]} }; + +export type NetworkSettings = NetworkMapSettings & NetworkMapThemes;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx b/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx index 3ce435f9b..e93368524 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx +++ b/sdnr/wt/odlux/apps/networkMapApp/src/pluginTransport.tsx @@ -30,17 +30,46 @@ import applicationApi from "../../../framework/src/services/applicationApi"; import { UpdateDetailsView } from "./actions/detailsAction"; import { findSiteToAlarm } from "./actions/mapActions"; import { URL_BASEPATH } from "./config"; +import { Redirect, Route, RouteComponentProps, Switch, withRouter } from "react-router-dom"; +import CustomizationView from "./components/customize/customizationView"; +import { getSettings } from "./actions/settingsAction"; +import connect, { Connect, IDispatcher } from "../../../framework/src/flux/connect"; +import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; -const App : React.SFC = (props) => { - return <MainView /> -}; +const mapProps = (state: IApplicationStoreState) => ({ +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + getSettings: () => dispatcher.dispatch(getSettings()) + +}); + + +const NetworkRouterApp = withRouter(connect(mapProps, mapDisp)((props: RouteComponentProps & Connect<typeof mapProps, typeof mapDisp>) => { + + React.useLayoutEffect(() => { + (async function waitFor() { + await props.getSettings(); + })(); + + }, []); + + //props.history.action = "POP"; + return ( + <Switch> + <Route path={`${props.match.path}/customize`} component={CustomizationView} /> + <Route path={`${props.match.path}`} component={MainView} /> + <Redirect to={`${props.match.path}`} /> + </Switch> + ) +})); export function register() { applicationManager.registerApplication({ name: URL_BASEPATH, // used as name of state as well icon: faMapMarked, rootActionHandler: networkmapRootHandler, - rootComponent: App, + rootComponent: NetworkRouterApp, menuEntry: "Network Map" }); } @@ -73,12 +102,13 @@ subscribe<ObjectNotification & IFormatedMessage>(["ObjectCreationNotification", })); */ +/* subscribe<FaultAlarmNotification & IFormatedMessage>("ProblemNotification", (fault => { const store = applicationApi && applicationApi.applicationStore; if (fault && store) { - store.dispatch(findSiteToAlarm(fault.nodeName)); + store.dispatch(findSiteToAlarm(fault.nodeName)); + - } -})); +}));*/ diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/services/settingsService.ts b/sdnr/wt/odlux/apps/networkMapApp/src/services/settingsService.ts new file mode 100644 index 000000000..2a2f09466 --- /dev/null +++ b/sdnr/wt/odlux/apps/networkMapApp/src/services/settingsService.ts @@ -0,0 +1,43 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ + +import { NetworkMapSettings, NetworkMapThemes, NetworkSettings } from "../model/settings"; +import { requestRest } from "../../../../framework/src/services/restService"; + + class SettingsService{ + + public getMapSettings = async () =>{ + const result = await requestRest<any>("/userdata", {method: "GET"}); + return result; + } + + public getMapThemes = async () =>{ + const result = await requestRest<NetworkMapThemes>("/userdata/networkMapThemes", {method: "GET"}); + return result; + } + + public updateMapSettings = async (newElement: NetworkMapSettings) =>{ + + const result = await requestRest<NetworkMapSettings>("/userdata/networkMap", {method: "PUT", body: JSON.stringify(newElement.networkMap)}); + return result; + } + + + } + + export const settingsService = new SettingsService();
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts b/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts index 1d4aa89ad..6dfd7983a 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts +++ b/sdnr/wt/odlux/apps/networkMapApp/src/utils/mapLayers.ts @@ -18,553 +18,313 @@ import * as mapboxgl from 'mapbox-gl'; import { Feature } from 'model/Feature'; +import { NetworkMapThemes, ThemeElement } from 'model/settings'; const fibreLinkColor = "#1154d9"; -const microwaveLinkColor="#039903"; +const microwaveLinkColor = "#039903"; -export const addBaseSources = (map: mapboxgl.Map, selectedPoint: Feature|null, selectedLine: Feature|null) =>{ - +class MapLayerService { - // make sure the sources don't already exist - // (if the networkmap app gets opened quickly within short time periods, the prior sources might not be fully removed) + checkedLayers = false; + settings: NetworkMapThemes; + selectedTheme: string | null = null; - if(!map.getSource("lines")){ - - map.addSource('lines', { - type: 'geojson', - data: { type: "FeatureCollection", features: [] } - }); - } - - if(!map.getSource("selectedLine")) - { - const features = selectedLine !== null ? [selectedLine] : []; - map.addSource('selectedLine', { - type: 'geojson', - data: { type: "FeatureCollection", features: features } - }); - } + public addBaseSources = (map: mapboxgl.Map, selectedPoint: Feature | null, selectedLine: Feature | null) => { - if(!map.getSource("points")) - { - map.addSource('points', { - type: 'geojson', - data: { type: "FeatureCollection", features: [] } - }); - } - if(!map.getSource("selectedPoints")) - { - const selectedPointFeature = selectedPoint !== null ? [selectedPoint] : []; - map.addSource('selectedPoints', { - type: 'geojson', - data: { type: "FeatureCollection", features: selectedPointFeature } - - }); - } + // make sure the sources don't already exist + // (if the networkmap app gets opened quickly within short time periods, the prior sources might not be fully removed) - if(!map.getSource("alarmedPoints")) - { - map.addSource("alarmedPoints", { - type: 'geojson', - data: {type:"FeatureCollection", features:[]} - }); - } -} - -export const addBaseLayers = (map: mapboxgl.Map, selectedPoint: Feature|null, selectedLine: Feature|null) => { - - addCommonLayers(map); + if (!map.getSource("lines")) { - map.addLayer({ - id: 'points', - source: 'points', - type: 'circle', - paint: { - 'circle-color': '#11b4da', - 'circle-radius': 7, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' + map.addSource('lines', { + type: 'geojson', + data: { type: "FeatureCollection", features: [] } + }); } - }); - - map.addLayer({ - id: 'selectedPoints', - source: 'selectedPoints', - type: 'circle', - paint: { - 'circle-color': '#116bda', - 'circle-radius': 9, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' + + if (!map.getSource("selectedLine")) { + const features = selectedLine !== null ? [selectedLine] : []; + map.addSource('selectedLine', { + type: 'geojson', + data: { type: "FeatureCollection", features: features } + }); } - }); - - map.addLayer({ - id: 'alarmedPoints', - source: 'alarmedPoints', - type: 'circle', - paint: { - 'circle-color': '#CC0000', - 'circle-radius': 9, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' + + if (!map.getSource("points")) { + map.addSource('points', { + type: 'geojson', + data: { type: "FeatureCollection", features: [] } + }); } - }); -} -export const addIconLayers = (map: mapboxgl.Map, selectedSiteId?: string) =>{ + if (!map.getSource("selectedPoints")) { + const selectedPointFeature = selectedPoint !== null ? [selectedPoint] : []; + map.addSource('selectedPoints', { + type: 'geojson', + data: { type: "FeatureCollection", features: selectedPointFeature } - addCommonLayers(map); - createIconLayers(map, selectedSiteId); -} + }); + } -const createIconLayers =(map: mapboxgl.Map, selectedSiteId?: string) =>{ - map.addLayer({ - 'id': 'point-lamps', - 'type': 'symbol', - 'source': 'points', - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'lamp', - 'icon-size': 0.1 - - }, - 'filter': createFilter("street lamp", selectedSiteId), - }); - - map.addLayer({ - 'id': 'point-building', - 'type': 'symbol', - 'source': 'points', - 'filter': createFilter("high rise building", selectedSiteId), - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'house', - 'icon-size': 0.1 + if (!map.getSource("alarmedPoints")) { + map.addSource("alarmedPoints", { + type: 'geojson', + data: { type: "FeatureCollection", features: [] } + }); } - }); - - map.addLayer({ - 'id': 'point-data-center', - 'type': 'symbol', - 'source': 'points', - 'filter': createFilter("data center", selectedSiteId), - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'data-center', - 'icon-size': 0.1 - } }); + } + + private addCircleLayer = (map: mapboxgl.Map, id: string, source: string, circleColor: string, radius: number, strokeWidth: number, outerColor: string) => { map.addLayer({ - 'id': 'point-factory', - 'type': 'symbol', - 'source': 'points', - 'filter': createFilter("factory", selectedSiteId), - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'factory', - 'icon-size': 0.2 + id: id, + source: source, + type: 'circle', + paint: { + 'circle-color': circleColor, + 'circle-radius': radius, + 'circle-stroke-width': strokeWidth, + 'circle-stroke-color': outerColor } }); + } - //select layers + private addLineLayer = (map: mapboxgl.Map, id: string, source: string, color: string, width: number, filter: string[]) => { map.addLayer({ - 'id': 'select-point-lamps', - 'type': 'symbol', - 'source': 'selectedPoints', + 'id': id, + 'type': 'line', + 'source': source, 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'lamp', - 'icon-size': 0.15 - + 'line-join': 'round', + 'line-cap': 'round' }, - 'filter': ['==', 'type', 'street lamp'], - }); - - map.addLayer({ - 'id': 'select-point-buildings', - 'type': 'symbol', - 'source': 'selectedPoints', - 'filter': ['==', 'type', 'high rise building'], - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'house', - 'icon-size': 0.15 - } - }); - - map.addLayer({ - 'id': 'select-point-data-center', - 'type': 'symbol', - 'source': 'selectedPoints', - 'filter': ['==', 'type', 'data center'], - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'data-center', - 'icon-size': 0.15 - } - }); - - map.addLayer({ - 'id': 'select-point-factory', - 'type': 'symbol', - 'source': 'selectedPoints', - 'filter': ['==', 'type', 'factory'], - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'factory', - 'icon-size': 0.3 - } + 'paint': { + 'line-color': color, + 'line-width': width + }, + 'filter': filter }); + } - //alarm layers - + private addIconLayer = (map: mapboxgl.Map, id: string, source: string, iconName: string, iconSize: number, filter: (string | string[])[]) => { map.addLayer({ - 'id': 'point-lamps-alarm', + 'id': id, 'type': 'symbol', - 'source': 'alarmedPoints', + 'source': source, 'layout': { 'icon-allow-overlap': true, - 'icon-image': 'lamp-red', - 'icon-size': 0.15 + 'icon-image': iconName, + 'icon-size': iconSize }, - 'filter': createFilter("street lamp"), - }); - - map.addLayer({ - 'id': 'point-building-alarm', - 'type': 'symbol', - 'source': 'alarmedPoints', - 'filter': createFilter("high rise building"), - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'house-red', - 'icon-size': 0.15 - } + 'filter': filter, }); + } - map.addLayer({ - 'id': 'point-data-center-alarm', - 'type': 'symbol', - 'source': 'alarmedPoints', - 'filter': createFilter("data center"), - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'data-center-red', - 'icon-size': 0.15 - } }); - - map.addLayer({ - 'id': 'point-factory-alarm', - 'type': 'symbol', - 'source': 'alarmedPoints', - 'filter': createFilter("factory"), - 'layout': { - 'icon-allow-overlap': true, - 'icon-image': 'factory-red', - 'icon-size': 0.3 - } - }); + /** + * Pick the correct theme based on user selection + */ + private pickTheme = () => { + if (this.selectedTheme !== null) { + const result = this.settings.networkMapThemes.themes.find(el => el.key === this.selectedTheme); + if (result) + return result; - map.addLayer({ - id: 'point-remaining', - source: 'points', - type: 'circle', - 'filter': ['none', ['==', 'type', "high rise building"], ['==', 'type', "data center"], ['==', 'type', "factory"], ['==', 'type', "street lamp"] ], - paint: { - 'circle-color': '#11b4da', - 'circle-radius': 7, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' } - }); -} - const addCommonLayers = (map: mapboxgl.Map) =>{ - - map.addLayer({ - 'id': 'microwave-lines', - 'type': 'line', - 'source': 'lines', - 'layout': { - 'line-join': 'round', - 'line-cap': 'round' - }, - 'paint': { - 'line-color': microwaveLinkColor, - 'line-width': 2 - }, - 'filter': ['==', 'type', 'microwave'] - }); - - map.addLayer({ - 'id': 'fibre-lines', - 'type': 'line', - 'source': 'lines', - 'layout': { - 'line-join': 'round', - 'line-cap': 'round' - }, - 'paint': { - 'line-color': fibreLinkColor, - 'line-width': 2 - }, - 'filter': ['==', 'type', 'fibre'] - }); - - map.addLayer({ - 'id': 'selectedLineMicrowave', - 'type': 'line', - 'source': 'selectedLine', - 'layout': { - 'line-join': 'round', - 'line-cap': 'round' - }, - 'paint': { - 'line-color': microwaveLinkColor, - 'line-width': 4 - }, - 'filter': ['==', 'type', 'microwave'] - }); - - map.addLayer({ - 'id': 'selectedLineFibre', - 'type': 'line', - 'source': 'selectedLine', - 'layout': { - 'line-join': 'round', - 'line-cap': 'round' - }, - 'paint': { - 'line-color': fibreLinkColor, - 'line-width': 4 - }, - 'filter': ['==', 'type', 'fibre'] - }); -} + return this.settings.networkMapThemes.themes[0]; -export const removeBaseLayers = (map: mapboxgl.Map) => { - - map.removeLayer("points"); - map.removeLayer("lines"); - map.removeLayer('selectedPoints'); - map.removeLayer('selectedLine'); -} - -const removeIconLayers = (map: mapboxgl.Map) =>{ - - map.removeLayer('point-building'); - map.removeLayer('point-lamps'); - map.removeLayer('point-data-center'); - map.removeLayer('point-factory'); - map.removeLayer('point-remaining'); - map.removeLayer('select-point-data-center'); - map.removeLayer('select-point-buildings'); - map.removeLayer('select-point-lamps'); - map.removeLayer('select-point-factory'); - map.removeLayer('point-building-alarm'); - map.removeLayer('point-lamps-alarm'); - map.removeLayer('point-data-center-alarm'); - map.removeLayer('point-factory-alarm'); -} - -let checkedLayers = false; + } -const createFilter = (type:'street lamp'|'high rise building'|'data center'|'factory', selectedSiteId?:string) =>{ + public addBaseLayers = (map: mapboxgl.Map, themesettings?: ThemeElement) => { - return selectedSiteId === undefined ? ['==', 'type', type] : ["all", ['==', 'type', type], ['!=', 'id', selectedSiteId]] -} + const theme = !themesettings ? this.pickTheme() : themesettings; -export const showIconLayers = (map: mapboxgl.Map, show: boolean, selectedSiteId?: string) => { + this.addCommonLayers(map); - const zoom = map.getZoom(); - - if(show){ - - if (zoom > 11) { + this.addCircleLayer(map, 'points', 'points', theme.site, 7, 1, '#fff'); + this.addCircleLayer(map, 'selectedPoints', 'selectedPoints', theme.selectedSite, 9, 1, '#fff'); + this.addCircleLayer(map, 'alarmedPoints', 'alarmedPoints', '#CC0000', 9, 1, '#fff'); + } - const bounds = map.getBounds(); + public addIconLayers = (map: mapboxgl.Map, selectedSiteId?: string) => { - if(map.getLayer('points')!== undefined && map.getLayer('point-lamps')===undefined && !checkedLayers){ + this.addCommonLayers(map); + this.createIconLayers(map, selectedSiteId); + } - // if sites don't have a type don't change layers to icons - const elements = map.queryRenderedFeatures( undefined,{ - layers: ['points'], filter:['has', 'type'] - }); - checkedLayers=true; + private createIconLayers = (map: mapboxgl.Map, selectedSiteId?: string) => { - if(elements.length>0 && elements.length<1000){ + this.addIconLayer(map, 'point-lamps', 'points', 'lamp', 0.1, this.createFilter("street lamp", selectedSiteId)); + this.addIconLayer(map, 'point-building', 'points', 'house', 0.1, this.createFilter("high rise building", selectedSiteId)); + this.addIconLayer(map, 'point-data-center', 'points', 'data-center', 0.1, this.createFilter("data center", selectedSiteId)); + this.addIconLayer(map, 'point-factory', 'points', 'factory', 0.2, this.createFilter("factory", selectedSiteId)); - if (map.getLayer('point-lamps') === undefined) { - map.removeLayer('points'); - map.setLayoutProperty('alarmedPoints', 'visibility', 'none'); - map.setLayoutProperty('selectedPoints', 'visibility', 'none'); - createIconLayers(map,selectedSiteId); - //map.moveLayer('point-remaining','selectedPoints'); - } - } - } - - } else { - swapLayersBack(map); - } -}else{ - swapLayersBack(map); -} -} + //select layers + this.addIconLayer(map, 'select-point-lamps', 'selectedPoints', 'lamp', 0.15, ['==', 'type', 'street lamp']); + this.addIconLayer(map, 'select-point-buildings', 'selectedPoints', 'house', 0.15, ['==', 'type', 'high rise building']); + this.addIconLayer(map, 'select-point-data-center', 'selectedPoints', 'data-center', 0.15, ['==', 'type', 'data center']); + this.addIconLayer(map, 'select-point-factory', 'selectedPoints', 'factory', 0.3, ['==', 'type', 'factory']); -export const swapLayersBack = (map: mapboxgl.Map) =>{ - checkedLayers=false; + //alarm layers + this.addIconLayer(map, 'point-lamps-alarm', 'alarmedPoints', 'lamp-red', 0.3, this.createFilter("street lamp")); + this.addIconLayer(map, 'point-building-alarm', 'alarmedPoints', 'house-red', 0.3, this.createFilter("high rise building")); + this.addIconLayer(map, 'point-data-center-alarm', 'alarmedPoints', 'data-center-red', 0.3, this.createFilter("data center")); + this.addIconLayer(map, 'point-factory-alarm', 'alarmedPoints', 'factory-red', 0.45, this.createFilter("factory")); - if(map.getLayer('selectedPoints') === undefined){ map.addLayer({ - id: 'selectedPoints', - source: 'selectedPoints', + id: 'point-remaining', + source: 'points', type: 'circle', + 'filter': ['none', ['==', 'type', "high rise building"], ['==', 'type', "data center"], ['==', 'type', "factory"], ['==', 'type', "street lamp"]], paint: { - 'circle-color': '#116bda', - 'circle-radius': 9, + 'circle-color': '#11b4da', + 'circle-radius': 7, 'circle-stroke-width': 1, 'circle-stroke-color': '#fff' } }); } - if(map.getLayer('alarmedPoints') === undefined){ - map.addLayer({ - id: 'alarmedPoints', - source: 'alarmedPoints', - type: 'circle', - paint: { - 'circle-color': '#CC0000', - 'circle-radius': 9, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' - } - }); + private addCommonLayers = (map: mapboxgl.Map, themesettings?: ThemeElement) => { + + const theme = !themesettings ? this.pickTheme() : themesettings; + + this.addLineLayer(map, 'microwave-lines', 'lines', theme.microwaveLink, 2, ['==', 'type', 'microwave']); + this.addLineLayer(map, 'fibre-lines', 'lines', theme.fiberLink, 2, ['==', 'type', 'fibre']); + this.addLineLayer(map, 'selectedLineMicrowave', 'selectedLine', theme.microwaveLink, 4, ['==', 'type', 'microwave']); + this.addLineLayer(map, 'selectedLineFibre', 'selectedLine', theme.fiberLink, 4, ['==', 'type', 'fibre']); } + public removeBaseLayers = (map: mapboxgl.Map) => { - if (map.getLayer('points') === undefined) { + map.removeLayer("points"); + map.removeLayer("lines"); + map.removeLayer('selectedPoints'); + map.removeLayer('selectedLine'); + } - map.setLayoutProperty('selectedPoints', 'visibility', 'visible'); - map.setLayoutProperty('alarmedPoints', 'visibility', 'visible'); - removeIconLayers(map); + private removeIconLayers = (map: mapboxgl.Map) => { + + map.removeLayer('point-building'); + map.removeLayer('point-lamps'); + map.removeLayer('point-data-center'); + map.removeLayer('point-factory'); + map.removeLayer('point-remaining'); + map.removeLayer('select-point-data-center'); + map.removeLayer('select-point-buildings'); + map.removeLayer('select-point-lamps'); + map.removeLayer('select-point-factory'); + map.removeLayer('point-building-alarm'); + map.removeLayer('point-lamps-alarm'); + map.removeLayer('point-data-center-alarm'); + map.removeLayer('point-factory-alarm'); + } - map.addLayer({ - id: 'points', - source: 'points', - type: 'circle', - paint: { - 'circle-color': '#11b4da', - 'circle-radius': 7, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' - } - }); - map.moveLayer('points', map.getLayer('selectedPoints').id); + private createFilter = (type: 'street lamp' | 'high rise building' | 'data center' | 'factory', selectedSiteId?: string) => { + + return selectedSiteId === undefined ? ['==', 'type', type] : ["all", ['==', 'type', type], ['!=', 'id', selectedSiteId]] } -} -const addClusterLayers = (map: mapboxgl.Map, data: any) => { - map.addSource('clusters', { - type: 'geojson', - data: data - }); - - map.addSource('selectedLine', { - type: 'geojson', - data: { type: "FeatureCollection", features: [] } - }); - - map.addSource('selectedPoints', { - type: 'geojson', - data: { type: "FeatureCollection", features: [] } - - }); - - map.addLayer({ - id: 'clusters', - type: 'circle', - source: 'clusters', - filter: ['has', 'count'], - paint: { - 'circle-color': [ - 'step', - ['get', 'count'], - '#51bbd6', - 100, - '#f1f075', - 750, - '#f28cb1' - ], - 'circle-radius': [ - 'step', - ['get', 'count'], - 20, - 100, - 30, - 750, - 40 - ] + public showIconLayers = (map: mapboxgl.Map, show: boolean, selectedSiteId?: string) => { + + const zoom = map.getZoom(); + + if (show) { + + if (zoom > 11) { + + const bounds = map.getBounds(); + + if (map.getLayer('points') !== undefined && map.getLayer('point-lamps') === undefined && !this.checkedLayers) { + + // if sites don't have a type don't change layers to icons + const elements = map.queryRenderedFeatures(undefined, { + layers: ['points'], filter: ['has', 'type'] + }); + this.checkedLayers = true; + + if (elements.length > 0 && elements.length < 1000) { + + if (map.getLayer('point-lamps') === undefined) { + map.removeLayer('points'); + map.setLayoutProperty('alarmedPoints', 'visibility', 'none'); + map.setLayoutProperty('selectedPoints', 'visibility', 'none'); + this.createIconLayers(map, selectedSiteId); + //map.moveLayer('point-remaining','selectedPoints'); + + } + } + } + + } else { + this.swapLayersBack(map); + } + } else { + this.swapLayersBack(map); } - }); - - - map.addLayer({ - id: 'cluster-count', - type: 'symbol', - source: 'clusters', - filter: ['has', 'count'], - layout: { - 'text-field': '{count}', - 'text-font': ['Roboto Bold'], - 'text-size': 12 + } + + public swapLayersBack = (map: mapboxgl.Map) => { + this.checkedLayers = false; + const theme = this.pickTheme(); + + if (map.getLayer('selectedPoints') === undefined) { + this.addCircleLayer(map, 'selectedPoints', 'selectedPoints', theme.selectedSite, 9, 1, '#fff'); + } - }); - - map.addLayer({ - 'id': 'selectedLine', - 'type': 'line', - 'source': 'selectedLine', - 'layout': { - 'line-join': 'round', - 'line-cap': 'round' - }, - 'paint': { - 'line-color': '#888', - 'line-width': 4 + + if (map.getLayer('alarmedPoints') === undefined) { + this.addCircleLayer(map, 'alarmedPoints', 'alarmedPoints', '#CC0000', 9, 1, '#fff'); + + } + + + if (map.getLayer('points') === undefined) { + + map.setLayoutProperty('selectedPoints', 'visibility', 'visible'); + map.setLayoutProperty('alarmedPoints', 'visibility', 'visible'); + this.removeIconLayers(map); + + this.addCircleLayer(map, 'points', 'points', theme.site, 7, 1, '#fff'); + + + map.moveLayer('points', map.getLayer('selectedPoints').id); } - }); - - map.addLayer({ - id: 'unclustered-points', - source: 'clusters', - filter: ['!', ['has', 'count'],], - type: 'circle', - paint: { - 'circle-color': '#11b4da', - 'circle-radius': 7, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' + } + + public changeMapOpacity = (map: mapboxgl.Map, newValue: number) => { + const newOpacity = newValue / 100; + if (map) { + const tiles = map.getStyle().layers?.filter(el => el.id.includes("tiles")) + tiles?.forEach(layer => { + if (layer.type === 'symbol') { + map.setPaintProperty(layer.id, `icon-opacity`, newOpacity); + map.setPaintProperty(layer.id, `text-opacity`, newOpacity); + } else { + map.setPaintProperty(layer.id, `${layer.type}-opacity`, newOpacity); + } + }) } - }); - - map.addLayer({ - id: 'selectedPoints', - source: 'selectedPoints', - type: 'circle', - paint: { - 'circle-color': '#116bda', - 'circle-radius': 9, - 'circle-stroke-width': 1, - 'circle-stroke-color': '#fff' + + } + + public changeTheme = (map: mapboxgl.Map, themeName: string) => { + this.selectedTheme = themeName; + const theme = this.pickTheme(); + if (theme && map.loaded()) { + map.setPaintProperty('points', 'circle-color', theme.site); + map.setPaintProperty('selectedPoints', 'circle-color', theme.selectedSite); + map.setPaintProperty('microwave-lines', 'line-color', theme.microwaveLink); + map.setPaintProperty('fibre-lines', 'line-color', theme.fiberLink); } - }); + } +} + +const mapLayerService = new MapLayerService(); +export default mapLayerService; -}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js b/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js index e0f16d0e7..3e80514f5 100644 --- a/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/networkMapApp/webpack.config.js @@ -139,7 +139,15 @@ module.exports = (env) => { "/yang-schema/": { target: "http://sdnr:8181", secure: false - }, + }, + "/userdata": { + target: "http://sdnr:8181", + secure: false + }, + "/userdata/": { + target: "http://sdnr:8181", + secure: false + }, "/oauth2/": { target: "http://sdnr:8181", secure: false diff --git a/sdnr/wt/odlux/odlux.properties b/sdnr/wt/odlux/odlux.properties index e2cc41d3a..36de5d4c3 100644 --- a/sdnr/wt/odlux/odlux.properties +++ b/sdnr/wt/odlux/odlux.properties @@ -8,5 +8,5 @@ odlux.apps.inventoryApp.buildno=96.078ad12(21/03/25) odlux.apps.linkCalculationApp.buildno=96.078ad12(21/03/25) odlux.apps.maintenanceApp.buildno=96.078ad12(21/03/25) odlux.apps.mediatorApp.buildno=96.078ad12(21/03/25) -odlux.apps.networkMapApp.buildno=96.078ad12(21/03/25) +odlux.apps.networkMapApp.buildno=102.acd1c0b(21/05/07) odlux.apps.permanceHistoryApp.buildno=81.1c38886(20/12/04) |