From 1a868116614dd9996c78e69941b537e9da19460b Mon Sep 17 00:00:00 2001 From: Aijana Schumann Date: Tue, 1 Feb 2022 13:18:42 +0100 Subject: Update ODLUX Updated to Material-ui 5, updated dashboard view, removed NetworkMap, LinkCalculator and LineOfSightApp, small bugfixes Issue-ID: CCSDK-3580 Signed-off-by: Aijana Schumann Change-Id: Id0fc148673e23a755cafc2be1c489248c38ff47c --- .../src/components/ConnectionErrorPoup.tsx | 40 --- .../lineOfSightApp/src/components/heightChart.tsx | 126 -------- .../apps/lineOfSightApp/src/components/map.tsx | 329 --------------------- .../src/components/mapContextMenu.tsx | 86 ------ .../apps/lineOfSightApp/src/components/mapInfo.tsx | 171 ----------- 5 files changed, 752 deletions(-) delete mode 100644 sdnr/wt/odlux/apps/lineOfSightApp/src/components/ConnectionErrorPoup.tsx delete mode 100644 sdnr/wt/odlux/apps/lineOfSightApp/src/components/heightChart.tsx delete mode 100644 sdnr/wt/odlux/apps/lineOfSightApp/src/components/map.tsx delete mode 100644 sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapContextMenu.tsx delete mode 100644 sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapInfo.tsx (limited to 'sdnr/wt/odlux/apps/lineOfSightApp/src/components') diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/ConnectionErrorPoup.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/components/ConnectionErrorPoup.tsx deleted file mode 100644 index 7d9339fc0..000000000 --- a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/ConnectionErrorPoup.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/** - * ============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 { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons" -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" -import { Paper, Typography } from "@material-ui/core" -import * as React from "react" - -type props = { reachable: boolean|null}; - - -const ConnectionErrorPoup: React.FunctionComponent = (props) => { - - return (props.reachable === false ? - -
-
Connection Error
- Service unavailable -
-
: null -) - -} - -export default ConnectionErrorPoup; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/heightChart.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/components/heightChart.tsx deleted file mode 100644 index 3030fe7dd..000000000 --- a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/heightChart.tsx +++ /dev/null @@ -1,126 +0,0 @@ -/** - * ============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 * as React from 'react'; - -import type { FC } from 'react'; -import * as d3 from 'd3'; - -import { useD3 } from "../hooks/d3"; -import { GPSProfileResult } from "../model/GPSProfileResult"; -import { max } from '../utils/math'; - -type HeightMapProps = { - data: GPSProfileResult[]; - dataMin: GPSProfileResult; - dataMax: GPSProfileResult; - width: number; - height: number; - heightPosA: number; - heightPosB: number; -} - -const HeightChart: FC = (props) => { - const { data, dataMin, dataMax, heightPosA, heightPosB } = props; - let ref: React.RefObject - - const drawSvg = () => { - ref = useD3( - (svg) => { - const margin = 100; - const width = Number(svg.attr("width")) - margin; - const height = Number(svg.attr("height")) - margin; - - // Add X axis - const x = d3.scaleBand() - .range([0, width]) - .domain(data.map(d => (`${d.gps.latitude},${d.gps.latitude}`))) - .padding(0.2); - - const maxHeight = max([dataMax.height, heightPosA, heightPosB], d => d) - - // Add Y axis - const y = d3.scaleLinear() - .domain([dataMin.height, maxHeight]) - .range([height, 0]); - - svg.append("g") - .attr('transform', `translate(${margin / 2}, ${margin / 2})`) - .call(d3.axisLeft(y)); - - // Bars - svg.selectAll("myBar") - .data(data) - .join("rect") - .attr('transform', `translate(${margin / 2}, ${margin / 2})`) - .attr("x", d => x(`${d.gps.latitude},${d.gps.latitude}`) || '') - .attr("y", d => y(d.height)) - .attr("width", x.bandwidth()) - .attr("fill", "#69b3a2b0") - .attr("height", d => height - y(d.height)) // always equal to 0 - - const firstX = `${data[0].gps.latitude},${data[0].gps.latitude}` - const lastX = `${data[data.length - 1].gps.latitude},${data[data.length - 1].gps.latitude}`; - - //add line - const x1 = x(firstX)!; - const x2 = x(lastX)!; - - svg.append("line") - .attr('transform', `translate(${margin / 2}, ${margin / 2})`) - .attr("x1", x1) - .attr("y1", y(props.heightPosA)) - .attr("x2", x2) - .attr("y2", y(props.heightPosB)) - - .style("stroke", "#88A") - .attr("stroke-width", "3px") - - //append circle on start and end - - svg.append("circle") - .attr('transform', `translate(${margin / 2}, ${margin / 2})`) - .attr('cx', x1) - .attr('cy', y(props.heightPosA)) - .attr('r', 10) - .attr('stroke', '#223b53') - .attr('fill', '#225ba3'); - - svg.append("circle") - .attr('transform', `translate(${margin / 2}, ${margin / 2})`) - .attr('cx', x2) - .attr('cy', y(props.heightPosB)) - .attr('r', 10) - .attr('stroke', '#223b53') - .attr('fill', '#225ba3'); - }, - [data] - ); - } - - drawSvg(); - - - - return ( - - - ); -} - -export { HeightChart }; diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/map.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/components/map.tsx deleted file mode 100644 index 6f29d5993..000000000 --- a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/map.tsx +++ /dev/null @@ -1,329 +0,0 @@ -/** - * ============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 * as React from 'react' -import * as mapboxgl from 'mapbox-gl'; -import { render } from 'react-dom'; -import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { OSM_STYLE, URL_BASEPATH } from '../config'; -import { GPSProfileResult } from '../model/GPSProfileResult'; -import MapContextMenu from './mapContextMenu'; -import { getGPSProfile } from '../services/heightService'; -import { max, min } from '../utils/math'; -import { HeightChart } from './heightChart'; -import { makeStyles } from '@material-ui/core'; -import { ClearSavedChartAction, SetChartAction, SetEndpointAction, SetHeightA, SetHeightB, SetMapCenterAction, SetStartPointAction } from '../actions/mapActions'; -import { LatLon } from '../model/LatLon'; -import MapInfo from './mapInfo'; -import { Height } from 'model/Height'; -import { PictureAsPdf } from '@material-ui/icons'; -import ConnectionErrorPoup from './ConnectionErrorPoup'; -import { addBaseLayer, addBaseSource, addPoint } from '../utils/map'; -import { SetReachableAction } from '../actions/commonActions'; - -import 'mapbox-gl/dist/mapbox-gl.css'; - -type mapProps = RouteComponentProps & Connect; - -const mapStateToProps = (state: IApplicationStoreState) => ({ - center: state.lineOfSight.map.center, - zoom: state.lineOfSight.map.zoom, - start: state.lineOfSight.map.start, - end: state.lineOfSight.map.end, - heightA: state.lineOfSight.map.heightA, - heightB: state.lineOfSight.map.heightB, - ready: state.lineOfSight.map.ready -}); - -const mapDispatchToProps = (dispatcher: IDispatcher) => ({ - ClearChartAction: () => dispatcher.dispatch(new ClearSavedChartAction), - SetMapPosition: (point: LatLon, zoom: number) => dispatcher.dispatch(new SetMapCenterAction(point, zoom)), - SetHeightStart: (height: Height) => dispatcher.dispatch(new SetHeightA(height)), - SetHeightEnd: (height: Height) => dispatcher.dispatch(new SetHeightB(height)), - setStartPosition: (position: LatLon|null) => dispatcher.dispatch(new SetStartPointAction(position)), - setEndPosition: (position: LatLon|null) => dispatcher.dispatch(new SetEndpointAction(position)), - setReachable : (reachable: boolean |null) => dispatcher.dispatch(new SetReachableAction(reachable)), - - - -}) - - -let map: mapboxgl.Map; - -const styles = makeStyles({ - chart: { - position: "absolute", - top: 0, - bottom: 0, - left: 0, - right: 0 - - } - }); - - -const Map: React.FC = (props) => { - - //const [start, setStart] = React.useState(); - //const [end, setEnd] = React.useState(); - const [data, setData] = React.useState(Number.NaN); - const [dataMin, setDataMin] = React.useState(); - const [dataMax, setDataMax] = React.useState(); - const [isMapLoaded, setMapLoaded] = React.useState(false); - - -const mapRef = React.useRef<{ map: mapboxgl.Map | null }>({ map: null }); -const mapContainerRef = React.useRef(null); - - - -const classes = styles(); - -const heightA = props.heightA !== null ? props.heightA.amsl + props.heightA.antennaHeight : 0; -const heightB = props.heightB !== null ? props.heightB.amsl + props.heightB.antennaHeight : 0; - -const {start, end} = props; - -const handleResize = () =>{ - - if (map) { - // wait a moment until resizing actually happened - window.setTimeout(() => map.resize(), 500); - } - -} - -//on mount -React.useEffect(()=>{ - - window.addEventListener("menu-resized", handleResize); - - - return () =>{ - console.log("unmount") - window.removeEventListener("menu-resized", handleResize); - - const center = mapRef.current.map?.getCenter(); - const mapZoom = mapRef.current.map?.getZoom(); - if(center){ - props.SetMapPosition({latitude: center.lat, longitude:center.lng}, mapZoom!); - } - - props.setReachable(null); - } - -},[]); - - - React.useEffect(()=>{ - - if(props.ready){ - setupMap(); - } - - },[props.ready]); - - React.useEffect(() => { - if (props.ready && isMapLoaded) { - drawChart(); - updateLosUrl(); - } - - }, [start, end, isMapLoaded]); - - const drawChart = () =>{ - if(start && end){ - - addBaseSource(map, 'route'); - addBaseLayer(map, 'route'); - - const json = `{ - "type": "Feature", - "properties": {}, - "geometry": { - "type": "LineString", - "coordinates": [ - [${start.longitude}, ${start.latitude}], - [${end.longitude}, ${end.latitude}] - ]} - }`; - - - - (map.getSource("route") as mapboxgl.GeoJSONSource).setData(JSON.parse(json)); - - - getGPSProfile({ latitude: start.latitude, longitude: start.longitude }, { latitude: end.latitude, longitude: end.longitude }).then(data => { - if (Array.isArray(data)) { - setDataMin(min(data, d => d.height)); - setDataMax(max(data, d => d.height)); - } - setData(data); - }); - } - else if (start || end){ - - const point = start!==null ? start: end!; - addBaseSource(map, 'route'); - addBaseLayer(map, 'route'); - addPoint(map, point); - - } - else { - //delete layers and source - //used instead of clearing source data because it has better performance - //(setting data to empty results in a noticable lag of line being cleared) - mapRef.current.map?.getLayer('line') && mapRef.current.map?.removeLayer('line') && mapRef.current.map?.removeLayer('points') && mapRef.current.map?.removeSource('route'); - - } - } - - const updateLosUrl = () =>{ - - if(start && end){ - - const locationPart = `lat1=${start.latitude}&lon1=${start.longitude}&lat2=${end.latitude}&lon2=${end.longitude}`; - - let heightPart = ''; - - if(props.heightA && props.heightB){ - heightPart = `&amslA=${props.heightA.amsl}&antennaHeightA=${props.heightA.antennaHeight}&amslB=${props.heightB.amsl}&antennaHeightB=${props.heightB.antennaHeight}`; - - } - - props.history.replace(`/${URL_BASEPATH}/los?${locationPart}${heightPart}`) - - }else if(!start && !end){ - props.history.replace(`/${URL_BASEPATH}`); - } - } - - - const updateHeightA = (value:number, value2: number) =>{ - props.SetHeightStart({amsl: value, antennaHeight: value2}); - } - - const updateHeightB = (value:number, value2: number) =>{ - props.SetHeightEnd({amsl: value, antennaHeight: value2}); - } - - const OnEndPosition = (position: mapboxgl.LngLat) =>{ - props.setEndPosition({latitude: position.lat, longitude: position.lng}) - } - - const OnStartPosition = (position: mapboxgl.LngLat) =>{ - props.setStartPosition({latitude: position.lat, longitude: position.lng}) - } - - - const setupMap = () => { - - let lat = props.center.latitude - let lon = props.center.longitude; - let zoom = props.zoom; - - map = new mapboxgl.Map({ - container: mapContainerRef.current!, - style: OSM_STYLE as any, - center: [lon, lat], - zoom: zoom, - accessToken: '' - }); - - mapRef.current.map = map; - - map.on('load', (ev) => { - - map.setMaxZoom(18); - setMapLoaded(true); - - //add source, layer - - addBaseSource(map, 'route'); - addBaseLayer(map, 'route'); - - }); - - let currentPopup: mapboxgl.Popup | null = null; - map.on('contextmenu', (e) => { - - if (currentPopup) - currentPopup.remove(); - - //change height if start/end changes - //??? -> show value? / reset after chart display? - - const popupNode = document.createElement("div"); - render( - { OnStartPosition(p); if (currentPopup) currentPopup.remove(); }} - onEnd={(p) => { OnEndPosition(p); if (currentPopup) currentPopup.remove(); }} - onHeightA={(p,p1)=> updateHeightA(p, p1)} - onHeightB={(p, p1)=> updateHeightB(p, p1)} />, - popupNode); - - currentPopup = new mapboxgl.Popup() - .setLngLat(e.lngLat) - .setDOMContent(popupNode) - .addTo(map); - }); - - map.on('moveend', mapMoveEnd); - - }; - - const mapMoveEnd = () =>{ - const mapZoom = Number(map.getZoom().toFixed(2)); - const lat = Number(map.getCenter().lat.toFixed(4)); - const lon = Number(map.getCenter().lng.toFixed(4)); - - props.SetMapPosition({latitude: lat, longitude: lon}, mapZoom); - - } - - - - return <> -
- - - - {typeof data === "object" - ? ( - < div className={classes.chart} onClick={() => { - setData(Number.NaN); - setDataMax(undefined); - setDataMin(undefined); - props.ClearChartAction(); - }}> - -
- ) - : null - } - - - -} - -export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Map)); - - diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapContextMenu.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapContextMenu.tsx deleted file mode 100644 index 0fc51cabf..000000000 --- a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapContextMenu.tsx +++ /dev/null @@ -1,86 +0,0 @@ -/** - * ============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, InputAdornment, makeStyles, TextField, Tooltip } from "@material-ui/core"; -import * as React from "react"; -import { FC, useEffect, useState } from "react"; -import { getGPSHeight } from "../services/heightService"; - -type MapContextMenuProps = { - pos: mapboxgl.LngLat; - onStart: (pos: mapboxgl.LngLat) => void; - onEnd: (pos: mapboxgl.LngLat) => void; - onHeightA: (height: number, antennaHeight: number) => void; - onHeightB: (height: number, antennaHeight: number) => void; - - } - - const styles = makeStyles({ - flexContainer: {display: "flex", flexDirection:"row"}, - textField:{width:60}, - button:{marginRight:5, marginTop:5, flexGrow:2} - }); - - const MapContextMenu: FC = (props) => { - const { pos, onStart, onEnd } = props; - const [height, setHeight] = useState(undefined); - const [value1, setValue1] = useState(''); - const [value2, setValue2] = useState(''); - - const classes = styles(); - - useEffect(() => { - getGPSHeight({ longitude: pos.lng, latitude: pos.lat }).then(setHeight); - }, [pos.lat, pos.lng]); - - const handleChangeHeight = (e:React.ChangeEvent, id: "heightA"|"heightB") =>{ - - //sanitize non numbers - const onlyNums = e.target.value.replace(/[^0-9]/g, ''); - - if(id==="heightA"){ - setValue1(onlyNums); - }else{ - setValue2(onlyNums); - } - } - - return ( -
-
Height: {height} m
-
-
- - - handleChangeHeight(e,"heightA")} InputProps={{endAdornment: m}}/> - -
-
- - - handleChangeHeight(e,"heightB")} InputProps={{endAdornment: m}}/> - -
-
- -
- ); - }; - - - export default MapContextMenu; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapInfo.tsx b/sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapInfo.tsx deleted file mode 100644 index 43a6e478a..000000000 --- a/sdnr/wt/odlux/apps/lineOfSightApp/src/components/mapInfo.tsx +++ /dev/null @@ -1,171 +0,0 @@ -/** - * ============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 { Accordion, AccordionDetails, AccordionSummary, makeStyles, Paper, Typography } from '@material-ui/core'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import { GPSProfileResult } from '../model/GPSProfileResult'; -import * as React from 'react'; -import { calculateDistanceInMeter } from '../utils/map'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; - -const mapStateToProps = (state: IApplicationStoreState) => ({ - center: state.lineOfSight.map.center, - zoom: state.lineOfSight.map.zoom, - start: state.lineOfSight.map.start, - end: state.lineOfSight.map.end, - heightA: state.lineOfSight.map.heightA, - heightB: state.lineOfSight.map.heightB, -}); - -const mapDispatchToProps = (dispatcher: IDispatcher) => ({ - - -}) - -type props = Connect & { - minHeight: GPSProfileResult | undefined; - maxHeight: GPSProfileResult | undefined; -}; - -const styles = (props: any) => makeStyles({ - accordion: {padding: 5, position: 'absolute', top: 10, width: props.width, marginLeft: 10, zIndex:1}, - container: { display: 'flex', flexDirection: "column", marginLeft:10, padding: 5 }, - caption:{width:'40%'}, - subTitleRow:{ width: '60%'}, - titleRowElement:{width: '40%', fontWeight: "bold"}, - secondRow:{width:'25%'}, - thirdRow:{width:'20%'} - }); - -const MapInfo: React.FC = (props) =>{ - - const [expanded, setExpanded] = React.useState(false); - const [width, setWidth] = React.useState(470); - const [length, setLength] = React.useState(); - - const classes = styles({width: width})(); - - const {start, end, center, zoom, heightA, heightB, minHeight, maxHeight} = props; - - React.useEffect(()=>{ - - if(start && end){ - setLength(calculateDistanceInMeter(start.latitude, start.longitude, end.latitude, end.longitude).toFixed(3)) - - }else{ - setLength(undefined) - } - - }, [start, end]) - - const handleChange = (event: any, isExpanded: boolean) => { - setExpanded(isExpanded); - }; - - - - - return - } - aria-controls="panel1a-content" - id="panel1a-header" - > - Map Info - - - - - Map Center - -
-
- Longitude{center.longitude}
-
- Latitude{center.latitude}
-
- Zoom {zoom}
- -
- Link - -
-
- Start - End -
- -
- - Longitude - {start?.longitude.toFixed(3)} - {end?.longitude.toFixed(3)}
- - -
- - Latitude - {start?.latitude.toFixed(3)} - {end?.latitude.toFixed(3)}
- - - -
- - Meassured height [m] - {heightA?.amsl} - {heightB?.amsl} -
- -
- - Antenna height [m] - {heightA?.antennaHeight} - {heightB?.antennaHeight} -
- -
- - Length [m] - {length} - -
- -
- - Max height @ position - {maxHeight? maxHeight.height+' m': ''} - {maxHeight?.gps.longitude.toFixed(3)} - {maxHeight?.gps.latitude.toFixed(3)} -
- -
- - Min height @ position - {minHeight? minHeight.height +' m': ''} - {minHeight?.gps.longitude.toFixed(3)} - {minHeight?.gps.latitude.toFixed(3)} -
- -
-
-
-} - -export default connect(mapStateToProps, mapDispatchToProps)(MapInfo); \ No newline at end of file -- cgit 1.2.3-korg